先日導入したgoto2048/fplug_for_linux。
F-PLUG |
ゴミデータっぽい値が採れたりしたので改修にトライしていたが、シリアル通信のパラメータやリトライ手順など地道にプロトコル実装を固める事で、1週間以上連続で安定して(ペアリングも解除されずに)計測できる事を確認した。
ポイントとしては
- RTS/CTSによるフロー制御
- Rawモード送信
- 非カノニカル受信
- 応答が無い場合は要求を再送信
#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円