今回は、ハードウェア制御部はC言語で、Web画面部はPerlで書きます。Perlからsystem関数でハードウェア制御部を呼び出すイメージです。
とうことで、今回はハードウェア制御部だけの話を書きます。CGIは次回に。。。
ソースは以下のようになります。コマンドを実行することで電源制御命令を出します。HA端子的に言えば電源制御パルスを出すということですね。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <termios.h>
#include <time.h>
#define DEV_NAME "/dev/ttyUSB1" // デバイスファイル名
#define BAUD_RATE B38400 // RS232C通信ボーレート
#define BUFF_SIZE 512 // 適当
// シリアルポートの初期化
int serial_init(int fd)
{
int ret;
struct termios tio;
memset(&tio,0,sizeof(tio));
tio.c_cflag = CS8 | CLOCAL | CREAD;
tio.c_cc[VTIME] = 0;
// ボーレートの設定
ret=cfsetispeed(&tio,BAUD_RATE);
if(ret!=0)return ret;
ret=cfsetospeed(&tio,BAUD_RATE);
if(ret!=0)return ret;
// デバイスに設定を行う
ret=tcsetattr(fd,TCSANOW,&tio);
if(ret!=0)return ret;
}
int main(int argc,char *argv[]){
int fd;
// デバイスファイル(シリアルポート)オープン
fd = open(DEV_NAME,O_RDWR);
if(fd<0){
// デバイスの open() に失敗したら
perror(argv[1]);
printf("open error.\n");
exit(1);
}
// シリアルポートの初期化
if(serial_init(fd)<0){
printf("serial init error.\n");
}
unsigned char buffer[BUFF_SIZE];
memset(buffer,0,BUFF_SIZE);
write(fd,buffer,BUFF_SIZE);
close(fd);
return 0;
}
ポイントとしては、シリアルポートもファイルI/Oとして扱うことでしょうか。ファイル関連システムコールだけでは扱えない部分はtcsetattr()などを用いています。
前回の説明では、300bpsで3バイト送信しておりましたが、このソースでは38400bpsで512バイト送信しています。何故かというと、うまく300bpsに設定できなかったからです。シェルから実行する分には300bpsに設定できていたのですが、CGIから実行すると無視されてしまいます。なんかしらのセキュリティの設定のせいだとは思うのですが、セキュリティを外すよりは、あきらめて初期値の38400bpsのままにしちゃうほうが安全かなと思った次第です。
38400bpsで100msのパルスを出すには384バイト送信すればいいのですが、512バイト送信しています。これはもう気分だけの問題です。エアコン側との相性もあると思うので、自作される場合は調整してみてください。
もう一つ、入力側の制御コマンドを示します。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <termios.h>
#include <time.h>
#include <sys/ioctl.h>
#define DEV_NAME "/dev/ttyUSB1" // デバイスファイル名
int main(int argc,char *argv[]){
int fd;
// デバイスファイル(シリアルポート)オープン
fd = open(DEV_NAME,O_RDWR);
if(fd<0){
// デバイスの open() に失敗したら
perror(argv[1]);
printf("open error.\n");
exit(1);
}
int lstat;
ioctl( fd, TIOCMGET, &lstat );
if( lstat & TIOCM_DSR ){
printf("1\n");
}else{
printf("0\n");
}
close(fd);
return 0;
}
実行すると、エアコンのON/OFF状態を標準出力に出します。ONなら1、OFFなら0です。これのポイントも、ファイルI/Oとして扱う点ですかね。まったく同じですね。
入力も出力もまとめて1本のプログラムにして、引数で動作を変えるほうがよいかもしれません。デバイスファイル名が2か所に出てくるのが気持ち悪いですしね。
しかし、引数のパースが面倒なので、別々のプログラムにしてしまいました。別々のプログラムのほうが読みやすいのではないかと思います。