YANO's digital garage

Copyright ©YANO All rights reserved. https://www.bravotouring.com/~yano/

Last-modified: 2024-03-20 (水)


[一語一絵/IT系]

fplug_for_linux / 2014-06-16 (月)

先日導入した[External]goto2048/fplug_for_linux

F-PLUG
F-PLUG

ゴミデータっぽい値が採れたりしたので改修にトライしていたが、シリアル通信のパラメータやリトライ手順など地道にプロトコル実装を固める事で、1週間以上連続で安定して(ペアリングも解除されずに)計測できる事を確認した。

ポイントとしては

  • RTS/CTSによるフロー制御
  • Rawモード送信
  • 非カノニカル受信
  • 応答が無い場合は要求を再送信
で、最終版のfpstatusは以下の通り。
#include <stdint.h>
#include <errno.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <termios.h>
#include <unistd.h>
#include <stdlib.h>
#include <time.h>
#include <string.h>

#include "fplug.h"

#undef  DEBUG
#define ENOTCONN_RETRY_MAX      10
#define MSG_RETRY_MAX           5

#define FPLUG_EXEC(fd,msg,buf)  fplug_exec((fd),(msg),sizeof((msg)),(buf),sizeof((buf)))

typedef unsigned char uchar;
static uint16_t tid = 0x0001;

void hunt_tid(uchar *buf)
{
    *(uint16_t *)&buf[2] = tid++;
}

typedef uchar uchar;

static void dump_message( char *mes, uchar *buf, int size  )
{
#ifdef DEBUG
    int cnt;
    printf( "%s", mes );
    for ( cnt=0; cnt<size; cnt++ ) {
        printf( "%02X,", buf[cnt] );
    }
    printf( "\n" );
#endif
    return;
}

int fplug_exec(int fd, uchar *req_msg, size_t req_len, uchar *res_buf, size_t bufsiz)
{
    struct timespec tmspec = { 0, 1000 * 1000 };
    int ret, i, j;
    uchar req_buf[BUFSIZ];

    memcpy(req_buf, req_msg, req_len);

    for (i = 0;i < MSG_RETRY_MAX; i++ ) {
        hunt_tid(req_buf);
        dump_message( "Command:", req_buf, req_len);
        for (ret = j = 0;j < ENOTCONN_RETRY_MAX; j++ ) {
            ret = write(fd, req_buf, req_len);
            if ( ret == req_len )
                break;
#ifdef DEBUG
printf("%s:%d; write()=%d,errno=%d\n", __FILE__, __LINE__, ret, errno);
#endif
            switch ( errno ){
            case ENOTCONN :
                tmspec.tv_nsec = 500 * 1000 * 1000;
                nanosleep(&tmspec, NULL);
                break;
            }
        }
        if ( ret <= 0 )
            break;

        for (j = 0;j < MSG_RETRY_MAX && !(ret = read(fd, res_buf, bufsiz)); j++ ) {
            tmspec.tv_sec = 0;
            tmspec.tv_nsec = (j+1) * 100 * 1000 * 1000;
            nanosleep(&tmspec, NULL);
        }
        if ( ret <= 0 ){
#ifdef DEBUG
printf("%s:%d; read()=%d,errno=%d\n", __FILE__, __LINE__, ret, errno);
#endif
            continue;
        }

        dump_message( "read:", res_buf, ret );
        if (4 < ret && !memcmp(req_buf, res_buf, 4)){   // TID match
#ifdef DEBUG
printf("%s:%d; read()=%d\n", __FILE__, __LINE__, ret);
#endif
            return ret;
        }
    }

    return -1;
}

int fplug_main( int fd, int mode )
{
    int cmd;
    int cnt;
    int ret;
    int i=0;
    uchar buf[BUFSIZ];
    uchar *szMode, szResult[BUFSIZ];
    time_t timer;
    struct tm *t;

    strcpy(szResult, "x");
    szMode = szResult;

    switch(mode) {
    case 'h':
        szMode = "FP_HUMID";
        for ( i = 0; i < MSG_RETRY_MAX && 0 < (ret = FPLUG_EXEC(fd, Humid, buf)); i++ ){
            uint16_t a = *(uint16_t *)&buf[ret-2];
            if ( 100 < a )
                continue;
            sprintf(szResult, "%d", a );
            break;
        }
        break;
    case 'i':
        szMode = "FP_ILLUM";
        for ( i = 0; i < MSG_RETRY_MAX && 0 < (ret = FPLUG_EXEC(fd, Illum, buf)); i++ ){
            uint16_t a = *(uint16_t *)&buf[ret-2];
            if ( 0xFFFD < a )
                continue;
            sprintf(szResult, "%d", a );
            break;
        }
        break;
    case 't':
        szMode = "FP_TEMP";
        for ( i = 0; i < MSG_RETRY_MAX && 0 < (ret = FPLUG_EXEC(fd, Temp, buf)); i++ ){
            int16_t a = *(int16_t *)&buf[ret-2];
            if ( a < -100 || 500 < a )
                continue;
            sprintf(szResult, "%.1f", (float)a / 10);
            break;
        }
        break;
    case 'w':
        szMode = "FP_WATT";
        for ( i = 0; i < MSG_RETRY_MAX && 0 < (ret = FPLUG_EXEC(fd, RWatt, buf)); i++ ){
            int16_t a = *(int16_t *)&buf[ret-2];
            if ( 2000 < a )
                continue;
            sprintf(szResult, "%.1f", (float)a / 10);
            break;
        }
        break;
    default:
        szMode = "FP_unknown";
        break;
    }
    printf( "%s=%s;", szMode, szResult);
}

int main(int argc, char *argv[])
{
    int fd, opt;
    struct termios oldtio, newtio;
    char cmdbuf[16];
    char *cmd = cmdbuf, *cmd_all = "with";

    memset(cmdbuf, 0x00, sizeof(cmdbuf));
    while ((opt = getopt(argc, argv, cmd_all)) != -1) {
        switch (opt) {
        case 'w':
        case 'i':
        case 't':
        case 'h':
            *cmd++ = tolower(opt);
            break;
        default: /* '?' */
            fprintf(stderr, "Usage: %s [Options] device\nOptions\t-w\tPower consumption(W)\n\t-i\tIlluminance\n\t-t\tTemperature\n\t-h\tHumidity\n", argv[0]);
            exit(EXIT_FAILURE);
        }
    }
    if ( argc <= optind ){
        fprintf(stderr, "Usage: %s [Options] device\nOptions\t-w\tPower consumption(W)\n\t-i\tIlluminance\n\t-t\tTemperature\n\t-h\tHumidity\n", argv[0]);
        exit(EXIT_FAILURE);
    }

    fd = open(argv[optind], O_RDWR);
    if ( fd < 0 ){
        perror(argv[optind]);
        return 0;
    }

    if ( !cmdbuf[0] )
        strcpy(cmdbuf, cmd_all);

    tcgetattr( fd, &oldtio );
    newtio = oldtio;
//  newtio.c_cflag = B9600 | CS8 | CLOCAL | CREAD;
    newtio.c_cflag = B9600 | CS8 | CLOCAL | CREAD | CRTSCTS;
    newtio.c_oflag = 0;         /* Rawモード出力 */
    newtio.c_lflag = 0;         /* 非カノニカル入力 */
    newtio.c_cc[VTIME]=0;       /* キャラクタ間タイマは未使用 */
    newtio.c_cc[VMIN]=0x00;     /* ブロック受信しない */
    tcflush(fd, TCIFLUSH);
    tcsetattr(fd, TCSANOW, &newtio);

    for ( cmd = cmdbuf; cmd && *cmd; cmd++ )
        fplug_main(fd, *cmd);

    printf( "\n");

    tcsetattr(fd, TCSANOW, &oldtio);
    close(fd);

    return 0;
}
これで期待通りにデータが採れるようになった。

【参照】
●富士通ビー・エス・シー http://www.bsc.fujitsu.com/
F-PLUG(エフプラグ)
F-PLUG メッセージ一覧
●Linux JF (Japanese FAQ) Project. http://archive.linux.or.jp/JF/
Serial-Programming-HOWTO.txt
●Amazon.co.jp https://www.amazon.co.jp/
富士通BSC F-PLUG115 電力・温度・湿度・照度測定機能つきプラグ ホワイト BSCESFP0103 4,900円