計測プログラムをC言語で書く
本連載第3回(前回)の計測プログラムの周期を秒単位で変更できるようにし、Raspberry Piに取り付けたスイッチを押し続けることでシャットダウンするプログラムを書いてみた。前回の改訂版ということでファイル名は「lps_get2」としている(今回もプログラムに無駄があるのはご容赦いただきたい)。
前回のプログラムは引数というものを入れてなかったので「main(void)」となっていたが、今回はコマンドからの文字を受け付ける。引数は計測間隔で、何も入れてなければ1秒単位、数字が入れば〇秒単位で計測。なお、プログラム簡略化のため、引数に数字以外を入力した場合を考慮しておらず、開始時にスイッチが押された状態ではプログラムを即終了するようにしている。どうしてこのような仕様にしたのかは後で説明しよう。
プログラムの実行結果は、ファイルを開いてそこに記録する。本連載では、Rasbian OSの標準ユーザー「pi」を使ってきたので、piユーザーのホームディレクトリ「/home/pi」下に、「月日時分.csv」というファイルを作った。
今回のプログラムでは、正確に○秒単位の計測を行うために、マイクロ秒単位で停止する「usleep」コマンドを併用している。計測間隔-1秒の「sleep」コマンドに続けて、ループがスタートするマイクロ秒単位の時刻から、次の「x.000秒」が来るタイミングまで、usleepコマンドで停止させている。完成版のプログラムは「1/100秒」単位までしか記録していないので、表示上の誤差は最初の1行目以外は出ないはずだ。
また、スイッチを長押し(2計測周期を押しっぱなし)すると、気圧センサーの計測を終了する。あらにファイルを閉じて(から念のために3秒ほど待って)、シャットダウンコマンドを呼び出すようになっている。
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <unistd.h>
#include <linux/i2c-dev.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <stdio.h>
#include <wiringPi.h>
#include <sys/time.h>
#define GPIO23 23
#define LPS_ADDRESS (0x5c)
#define WHO_AM_I (0x0f)
#define CTRL_REG1 (0x20)
#define STATUS_REG (0x27)
#define PRESS_L (0x28)
#define PRESS_M (0x29)
#define PRESS_H (0x2a)
#define TEMP_L (0x2b)
#define TEMP_H (0x2c)
// 関数プロトタイプ宣言。
void I2C_write(unsigned char rs, unsigned char data, int fd);
unsigned char I2C_read(unsigned char rs, int fd);
int main(int argc, char *argv[])
{
int lpt;
char *i2cFileName = "/dev/i2c-1";
int LpsAddress = LPS_ADDRESS;
long int press;
short int temp;
float press_d,temp_d;
time_t timer;
struct tm *t_st;
FILE *fp;
char filename[256];
struct timeval t_us;
long timeus;
int i1,i2,rcycle;
//計測間隔設定。なしの場合は1
if(argc==1) rcycle=1;
else rcycle=atoi(argv[1]);
// GPIO初期設定 開始時にボタンが押されていたら即時終了
if(wiringPiSetupGpio() == -1) return 1;
pinMode(GPIO23, INPUT);
pullUpDnControl(GPIO23 , PUD_UP);
i1=digitalRead(GPIO23);
if (i1 == 0) return 1;
// 出力ファイルオープン
// /home/pi下に月日時分.csvでオープン
time(&timer);
t_st = localtime(&timer);
sprintf(filename,"/home/pi/%02d%02d%02d%02d.csv",t_st->tm_mon+1,t_st->tm_mday,t_st->tm_hour,t_st->tm_min);
if ((fp = fopen(filename,"w")) == NULL) {
fprintf(stderr , "File open failed.\n");
return 1;
}
// I2CポートをRead/Write属性でオープン。
if ((lpt = open(i2cFileName, O_RDWR)) < 0)
{
fprintf(stderr , "Faild to open i2c port\n");
exit(1);
}
// 通信先アドレスの設定。
if (ioctl(lpt, I2C_SLAVE, LpsAddress) < 0)
{
fprintf(stderr , "Unable to get bus access to talk to slave\n");
exit(1);
}
if (I2C_read(WHO_AM_I,lpt) != (0xbb))
{
fprintf(stderr , "Unable find LPS331:0F:%2.2x\n",I2C_read(WHO_AM_I,lpt));
exit(1);
}
I2C_write(CTRL_REG1,0xe0,lpt); // output retio 12.5/12.5Hz
usleep(100000);
//計測ループ
while(1){
i2=i1;
i1=digitalRead(GPIO23);
time(&timer);
t_st = localtime(&timer);
gettimeofday( &t_us,NULL);
timeus = 1000000 - t_us.tv_usec;
fprintf(fp,"%d/%02d/%02d %02d:%02d:%02d.%02.2ld , ",t_st->tm_year+1900,t_st->tm_mon+1,t_st->tm_mday,t_st->tm_hour,t_st->tm_min,t_st->tm_sec,t_us.tv_usec/10000);
press = ( (I2C_read(PRESS_H,lpt) << 16) | (I2C_read(PRESS_M,lpt) << 8) | I2C_read(PRESS_L,lpt) );
press_d = press / 4096.0 ;
temp = ( (I2C_read(TEMP_H,lpt) << 8) | I2C_read(TEMP_L,lpt) );
temp_d = 42.5 + (temp / 480.0);
fprintf(fp,"%4.4f , ",press_d);
fprintf(fp,"%3.3f\n",temp_d);
if(i1==i2 && i1==0) break; // 計測2回ともスイッチが入っていたら終了
sleep(rcycle-1); // 次回ほぼ.000ぐらいのタイミングになるように調整
usleep(timeus);
}
//終了処理
I2C_write(CTRL_REG1,0x00,lpt); // 気圧センサーDown
fclose(fp);
sleep(2);
system ("sudo shutdown -h now");
return 0;
}
void I2C_write(unsigned char rs, unsigned char data, int fd)
{
unsigned char buf[2];
buf[0] = rs;
buf[1] = data;
if (write(fd, buf, 2) != 2)
{
fprintf(stderr , "Error writeing to i2c slave1\n");
}
}
unsigned char I2C_read(unsigned char rs, int fd)
{
unsigned char buf[2];
buf[0] = rs;
if (write(fd, buf, 1) != 1)
{
fprintf(stderr , "Error writeing to i2c slave1\n");
}
if (read(fd, buf, 1) != 1)
{
fprintf(stderr , "Error reading to i2c slave1\n");
}
return buf[0];
}