YANO's digital garage

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

Last-modified: 2023-01-25 (水)


[一語一絵/IT系]

fplug_for_linux / 2014-05-23 (金)

先日調達した[External]富士通ビー・エス・シー[External]F-PLUG(エフプラグ)

F-PLUG
F-PLUG

データの取得は一般公開されている[External]F-PLUG メッセージ一覧に従って一般的なRFCOMMの接続手順をベースに実装すれば良いのだが、まずは動作確認を含めて[External]OBDNマガジン[External]goto2048/fplug_for_linuxを利用してみた。

電力は期待通り採れたので動作確認はOKとしたのだが、fptest温度(Temperature)が採れない事がある事に気付いて調べてみたところ、応答メッセージが[External]F-PLUG メッセージ一覧の仕様と違って1バイト短い事がわかった。湿度、照度は大丈夫そうなのだが、ひとまずデータの最後から2バイトに注目するように改修した。

yano@ML110G7:~/software/fplug_for_linux$ sudo fptest
1.Get Temperature
2.Get Humidity
3.Illumination
4.Watt(Realtime)
5.Exit
Input command no ->1
Temp Command:10,81,00,00,0E,F0,00,00,11,00,62,01,E0,00,
ret size=15
Temp Status:10,81,00,00,00,00,0E,F0,00,72,01,E0,02,0E,01,
Temperature=27.0

1.Get Temperature
2.Get Humidity
3.Illumination
4.Watt(Realtime)
5.Exit
Input command no ->4
ret size=16
Realtime Wattmeter:10,81,00,00,00,22,00,0E,F0,00,72,01,E2,02,73,02,
Watt(Realtime)=62.7W

fpstatusもsensors.cronからデータを取得できるよう同様に改修してみたのだが、応答メッセージの受信以前に要求メッセージの送信がうまくいかない事が多い事がわかった。調べてみると"Transport endpoint is not connected"が発生しているので、ENOTCONNの場合はwriteをリトライするように対処。

ついでに一度の接続で電力、温度、湿度、照度を立て続けに取得できるように改造。

#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 <time.h>
#include <string.h>

#include "fplug.h"

#define DEOJ_TOKEN "\x0E\xF0\x00\x72"
#define DEOJ_TOKEN_LEN 4
static unsigned char *
memmem(unsigned char *a, size_t a_len, unsigned char *b, size_t b_len)
{
    int i;

    for (i = 0; i < (a_len - b_len); i++ ){
        if ( a[i] != *b )
            continue;
        if ( !memcmp(&a[i], b, b_len) )
            return &a[i];
    }
    return NULL;
}

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

int fp_main( int, int );

int main(int argc, char *argv[])
{
    int fd;
    struct termios oldtio, newtio;
    char *cmd;

    if ( argc == 1 ) {
        printf("Usage: fpstatus <option>\n");
        return(0);
    }
    if ( *argv[1] == '-' ) argv[1]++;

    fd = open(SERIAL_PORT, O_RDWR);
    if ( fd < 0 ){
        perror("open(/dev/rfcomm0)");
        return 0;
    }

    tcgetattr( fd, &oldtio );
    newtio = oldtio;
    newtio.c_cflag = B9600 | CS8 | CLOCAL | CREAD;
    tcsetattr(fd, TCSANOW, &newtio);

    for ( cmd = argv[1]; cmd && *cmd; cmd++ )
        fp_main(fd, *cmd);

    printf( "\n");

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

    return 0;
}

typedef unsigned char uchar;
size_t fplug_request(int fd, uchar *req_buf,size_t req_len,uchar *res_buf, size_t bufsiz)
{
    struct timespec tmspec = { 0, 1000 * 1000 };
    int ret, i;

    dump_message( "Command:", req_buf, req_len);
    for (ret = i = 0;i < 10; i++ ) {
        tmspec.tv_nsec = i * 100 * 1000 * 1000;
        ret = write(fd, req_buf, req_len);
        if ( ret == req_len )
                break;
// printf("write()=%d,errno=%d\n", ret, errno);
        switch ( errno ){
        case ENOTCONN :
                nanosleep(&tmspec, NULL);
                break;
        }
    }
    ret = read(fd, res_buf, bufsiz);
    dump_message( "read:", res_buf, ret );
    return ret;
}

int fp_main( int fd, int mode )
{
    int cmd;
    int cnt;
    int a=0;
    double f;
    int ret;
    int i=0;
    unsigned char buf[BUFSIZ];
    time_t timer;
    struct tm *t;

    switch(mode) {
        case 't':
            while (1) {
                ret = fplug_request(fd, Temp, sizeof(Temp), buf, BUFSIZ);
                if (memmem(buf, ret, DEOJ_TOKEN, DEOJ_TOKEN_LEN)) break;
                if ( i++ >10 ) return(0);
            }
            a = *(int16_t *)&buf[ret-2];
            printf( "FP_TEMP=%.1f;", (float)a / 10);
            break;
        case 'h':
            while (1) {
                ret = fplug_request(fd, Humid, sizeof(Humid), buf, BUFSIZ);
                if (memmem(buf, ret, DEOJ_TOKEN, DEOJ_TOKEN_LEN)) break;
                if ( i++ >10 ) return(0);
            }
            a = *(uint16_t *)&buf[ret-2];
            printf( "FP_HUMID=%d;", a );
            break;
        case 'i':
            while (1) {
                ret = fplug_request(fd, Illum, sizeof(Illum), buf, BUFSIZ);
                if (memmem(buf, ret, DEOJ_TOKEN, DEOJ_TOKEN_LEN)) break;
                if ( i++ >10 ) return(0);
            }
            a = *(uint16_t *)&buf[ret-2];
            printf( "FP_ILLUM=%d;", a );
            break;
        case 'w':
            while (1) {
                ret = fplug_request(fd, RWatt, sizeof(RWatt), buf, BUFSIZ);
                if (memmem(buf, ret, DEOJ_TOKEN, DEOJ_TOKEN_LEN)) break;
                if ( i++ >10 ) return(0);
            }
            a = *(uint16_t *)&buf[ret-2];
            printf( "FP_WATT=%.1f;", (float)a / 10);
            break;
        default:
            break;
    }
}
更にread待ちにならないようにpselect監視を挟んでみたのだが、期待通りに受信してもpselectでキャッチできないので、素直にwriteできたらreadするよう着地。あとrootでないと/dev/rfcomm0が"Permission denied"になるのでseteuidするようにしてみたのだが、うまくいかないという問題が残っているものの、取り敢えず期待通りに温度、湿度、照度を含めてデータは採れるようになった。

【参照】
●富士通ビー・エス・シー http://www.bsc.fujitsu.com/
F-PLUG(エフプラグ)
F-PLUG メッセージ一覧
●OBDNマガジン http://obdnmagazine.blogspot.jp/
富士通ビー・エス・シー F-PLUG [AX3][A6] 2013年3月25日
●ロードバイクときどきiPad/Airなblog http://blogs.yahoo.co.jp/beachinside
F-PLUG(電力/明るさ/温度/湿度測定プラグ)を買って来た 2013年8月13日
●Amazon.co.jp https://www.amazon.co.jp/
富士通BSC F-PLUG115 電力・温度・湿度・照度測定機能つきプラグ ホワイト BSCESFP0103 4,900円