fplug_for_linux

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

F-PLUG

F-PLUG

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

電力は期待通り採れたので動作確認はOKとしたのだが、fptest温度(Temperature)が採れない事がある事に気付いて調べてみたところ、応答メッセージが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/

OBDNマガジン http://obdnmagazine.blogspot.jp/

ロードバイクときどきiPad/Airなblog http://blogs.yahoo.co.jp/beachinside

Amazon.co.jp https://www.amazon.co.jp/