前回の記事Raspberry Piで温度と湿度を取得する!HDC1000用のプログラムつき!でラズパイから温度データを取得できるようになりました。
今回は定期的にそのデータをMySQLに登録できるようにしたいと思います。
今回行いたいこと!
今回行いたいことを整理しておきます(ง°̀ロ°́)ง
- MySQLに温度データを登録できるようにする。
- 定期的に温度取得用のプログラムを実行してMySLQLにデータを登録する。
どうやって温度データをMySQLに登録するの?
MySQLへのデータ登録するためのライブラリが公開されているのでそのライブラリを使ったMySQLへデータを登録できるクラスを作ろうと思います。
前回作製したHDC1000のクラスでは温度を取得できるようになりました。
こちらと組み合わせて、温度を取得し同時にMySQLにデータを登録するプログラムを作製したいと思います。
どうやって定期的にプログラムを実行するの?
作製するプログラムは実行ファイル形式なので定期的に呼んで上げる必要があります。
ラズパイはLinuxなのでLinuxでスケジュール実行に良く利用されるcrontabを使うと良さそうです。
こちらで実装しているサンプルも後ほどご紹介します。
温度データをMySQLに登録するプログラム
メイン関数
クラスHdc1000/MySqlInterfaceを作って使っています。
オプションはデバック用に色々作って見ました。ここでは “-add”がMySQLにデータを登録しています。
MySqlInterfaceのRegisterメソッドで実際登録しています。その際にはHdc1000クラスからGetTemperature/GetHumidityメソッドで取得した温度と湿度データを登録します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 |
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <time.h> #include "MySqlInterface.hpp" #include "i2cHdc1000.hpp" enum OPERATION { NONE, ALLHISTORY, SPECIFICPERIOD }; int main(int argc, char *argv[]) { // current time time_t now = time(NULL); struct tm *pnow = localtime(&now); char strDateNow[16]; char strTimeNow[16]; sprintf(strDateNow, "%04d-%02d-%02d", pnow->tm_year+1900, pnow->tm_mon+1, pnow->tm_mday); sprintf(strTimeNow, "%02d:%02d:%02d", pnow->tm_hour, pnow->tm_min, pnow->tm_sec); // for query period char strDayStart[32] = ""; char strDayEnd[32] = ""; char strTimeStart[32] = ""; char strTimeEnd[32] = ""; // initialization of classes and operation Hdc1000 hdc1000; MySqlInterface mySql; OPERATION operation = NONE; // mode confirmation for(int i = 1; i < argc; i++) { // Show current data if(strcmp("-showcurrent", argv[i]) == 0) { printf("%s %s %4.2f %4.2f\n", strDateNow, strTimeNow, hdc1000.GetTemperature(), hdc1000.GetHumidity()); return 0; } // Register in MySQL DB else if(strcmp("-add", argv[i]) == 0) { if(mySql.Connect()) exit(-1); mySql.Register(strDateNow, strTimeNow, hdc1000.GetTemperature(), hdc1000.GetHumidity()); mySql.Close(); return 0;; } else if(strcmp("-allhistory", argv[i]) == 0) { operation = ALLHISTORY; break; } else if(strcmp("-period", argv[i]) == 0) { operation = SPECIFICPERIOD; } else if(strcmp("-startday", argv[i]) == 0) { strcpy(strDayStart, argv[i+1]); } else if(strcmp("-endday", argv[i]) == 0) { strcpy(strDayEnd, argv[i+1]); } else if(strcmp("-starttime", argv[i]) == 0) { strcpy(strTimeStart, argv[i+1]); } else if(strcmp("-endtime", argv[i]) == 0) { strcpy(strTimeEnd, argv[i+1]); } } // Operations switch(operation) { //Show All history. Should be avoided.. case ALLHISTORY: if(mySql.Connect()) exit(-1); mySql.History(); mySql.Close(); break; case SPECIFICPERIOD: if(mySql.Connect()) exit(-1); mySql.History(strDayStart, strTimeStart, strDayEnd, strTimeEnd); mySql.Close(); break; case NONE: printf("no valid argument\n"); break; } return 0; } |
MySQLのガスケットクラス
MySqlInterfaceのクラスはこちらです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
#ifndef MYSQL_H #define MYSQL_H #include <mysql/mysql.h> #define SQL_SERV "localhost" #define SQL_USER "Your_MySQL_User_Name" #define SQL_PSWD "Your_MySQL_User_Password" #define SQL_DB "Your_MySQL_DB_Name" #define SQL_TBL "Your_MySQL_DB_Table_Name" class MySqlInterface { public: MySqlInterface(); virtual ~MySqlInterface(); int Connect(); int Close(void); int Register(char *date, char *time, float temperature, float humidity); int Query(char *sql_str); int History(); int History(char *day_start, char *day_end); int History(char *day_start, char *time_start, char *day_end, char *time_end); public: MYSQL *con; MYSQL_RES *resp; MYSQL_ROW row; }; #endif |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 |
#include <stdio.h> #include <stdlib.h> #include <string.h> #include "MySqlInterface.hpp" MySqlInterface::MySqlInterface() { con = mysql_init(NULL); } int MySqlInterface::Connect(void) { if(!mysql_real_connect(con, SQL_SERV, SQL_USER, SQL_PSWD, SQL_DB, 0, NULL, 0)) { printf("SQL initialization is failed\n"); return -1; } return 0; } int MySqlInterface::Close() { mysql_close(con); return 0; } int MySqlInterface::Register( char *date, char *time, float temperature, float humidity) { char sqlStr[256]; sprintf(sqlStr, "INSERT INTO %s VALUES(\'%s\', \'%s\', %4.2f, %4.2f)", SQL_TBL, date, time, temperature, humidity); if(mysql_query(con, sqlStr)){ mysql_close(con); printf("failed to register\n"); return -1; } return 0; } int MySqlInterface::Query(char *sqlStr) { if(mysql_query(con, sqlStr)){ mysql_close(con); return -1; } resp = mysql_use_result(con); while((row = mysql_fetch_row(resp)) != NULL){ printf("%s %s Temp:%s Humi:%s\n", row[0], row[1], row[2], row[3]); } return 0; } int MySqlInterface::History() { char sqlStr[256]; sprintf(sqlStr, "select * from %s", SQL_TBL); Query(sqlStr); return 0; } int MySqlInterface::History(char *day_start, char *day_end) { char sqlStr[256]; sprintf(sqlStr, "select * from %s where DATE>=\'%s\' and DATE<=\'%s\'", SQL_TBL, day_start, day_end); Query(sqlStr); return 0; } int MySqlInterface::History( char *day_start, char *time_start, char *day_end, char *time_end) { char sqlStr[256]; char sqlStrTmp[128]; bool found = false; sprintf(sqlStr, "select * from %s", SQL_TBL); // null check and put format when found if(strcmp(day_start, "") != 0) { sprintf(sqlStrTmp, "DATE>=\'%s\'", day_start); sprintf(sqlStr, "%s where %s", sqlStr, sqlStrTmp); found = true; } if(strcmp(day_end, "") != 0) { sprintf(sqlStrTmp, "DATE<=\'%s\'", day_end); if(!found) sprintf(sqlStr, "%s where %s", sqlStr, sqlStrTmp); else sprintf(sqlStr, "%s and %s", sqlStr, sqlStrTmp); found = true; } if(strcmp(time_start, "") != 0) { sprintf(sqlStrTmp, "TIME>=\'%s\'", time_start); if(!found) sprintf(sqlStr, "%s where %s", sqlStr, sqlStrTmp); else sprintf(sqlStr, "%s and %s", sqlStr, sqlStrTmp); found = true; } if(strcmp(time_end, "") != 0) { sprintf(sqlStrTmp, "TIME<=\'%s\'", time_end); if(!found) sprintf(sqlStr, "%s where %s", sqlStr, sqlStrTmp); else sprintf(sqlStr, "%s and %s", sqlStr, sqlStrTmp); found = true; } printf("sql %s\n", sqlStr); Query(sqlStr); return 0; } MySqlInterface::~MySqlInterface() { } |
HDC1000のプログラム
HDC1000のプログラムはすでにご紹介していますが、ここにも一応貼っておきます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 |
#ifndef HDC1000_H #define HDC1000_H /* HDC1000 register map Pointer Name Reset Description value 0x00 Temperature 0x0000 Temperature measurement output 0x01 Humidity 0x0000 Relative Humidity measurement output 0x02 Configuration 0x1000 HDC1000 configuration and status 0xFB Serial ID device dependent First 2 bytes of the serial ID of the part 0xFC Serial ID device dependent Mid 2 bytes of the serial ID of the part 0xFD Serial ID device dependent Last byte bit of the serial ID of the part 0xFE Manufacturer ID 0x5449 ID of Texas Instruments 0xFF Device ID 0x1000 ID of HDC1000 device */ #define HDC1000_I2C_ADDRESS 0x40 #define HDC1000_TEMPERATURE 0x00 #define HDC1000_HUMIDITY 0x01 #define HDC1000_CONFIGURATION 0x02 #define HDC1000_SERIAL_ID_1 0xFB #define HDC1000_SERIAL_ID_2 0xFC #define HDC1000_SERIAL_ID_3 0xFD #define HDC1000_MANUFACTURER_ID 0xFE #define HDC1000_DEVICE_ID 0xFF #define HDC1000_CONFIG_RST 0x80 #define HDC1000_CONFIG_HEAT 0x20 #define HDC1000_CONFIG_MODE 0x10 #define HDC1000_CONFIG_BTST 0x08 #define HDC1000_CONFIG_TRES 0x04 #define HDC1000_CONFIG_HRES_11 0x02 #define HDC1000_CONFIG_HRES_01 0x01 #define HDC1000_CONFIG_RSV 0x00 #define HDC1000_RDY_PIN 0x4 class Hdc1000 { public: Hdc1000(); virtual ~Hdc1000(); float GetTemperature(); float GetHumidity(); private: float temperature; float humidity; int fd; int SetRegister(unsigned char reg); int GetMeasuredData(); }; #endif |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 |
#include <stdio.h> #include <unistd.h> #include <wiringPi.h> #include <wiringPiI2C.h> #include "i2cHdc1000.hpp" Hdc1000::Hdc1000() : temperature(0.0), humidity(0.0) { // To call GPIO without re-mapping wiringPiSetupGpio(); // Set Pin dedicating for ready Input pinMode(HDC1000_RDY_PIN, INPUT); // Get File handle descriptor fd = wiringPiI2CSetup(HDC1000_I2C_ADDRESS); } float Hdc1000::GetTemperature() { // Set target register and get measured data after it is ready SetRegister(HDC1000_TEMPERATURE); temperature = (float)GetMeasuredData(); //Convert from HDC1000 specification temperature = temperature / 65536.0 * 165.0 - 40.0; return temperature; } float Hdc1000::GetHumidity() { // Set target register and get measured data after it is ready SetRegister(HDC1000_HUMIDITY); humidity = (float)GetMeasuredData(); //Convert from HDC1000 specification humidity = humidity / 65536.0 * 100.0; return humidity; } int Hdc1000::GetMeasuredData() { unsigned char buff[2]; // Wait until the device is ready while(digitalRead(HDC1000_RDY_PIN) == HIGH); //Read from file descriptor read(fd, buff, sizeof(unsigned char) * 2); return (buff[0] << 8) | buff[1]; } int Hdc1000::SetRegister(unsigned char reg) { return wiringPiI2CWrite(fd, reg); } Hdc1000::~Hdc1000() { } |
Makefile
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
OBJS = MySqlInterface.o i2cHdc1000.o main.o TARGET = hdc1000WithSql CC = g++ CFLAGS = -Wall -lmysqlclient -L/usr/lib64/mysql -lwiringPi .SUFFIXES: .cpp .c .o $(TARGET): $(OBJS) $(CC) $(OBJS) -o $(TARGET) $(CFLAGS) .c.o: $< $(CC) -c $(CFLAGS) $< .cpp.o: $< $(CC) -c $(CFLAGS) $< clean: rm -f $(OBJS) $(TARGET) |
これでmakeを実行してください。
プログラムを定期的に実行しよう
プログラムができたのでスケジュール実行をしたいと思います。
こちらに詳しく書いてあるので簡単にまとめますね。
crontabってなに?
そもそもcronですが、unixやLinuxなどでcron daemonを使いバックグラウンドで一定間隔で自動的に実行されることを許されているタスクです
Crontab(CRON TABle)はcronタスクを持つファイルで、このファイルに記載することでcronタスクは実行されます。
cronタスク(job / schedule)には設定、日付、時間を設定できます。
Crontabのコマンド
crontab -e crontabファイルを編集します。
crontab -l cronタスクを確認できます。
crontab -r cronのファイルを削除します。
crontabの記載方法
crontab -e
でcrontabファイルを編集します。
crontabファイルの記載方法についてご紹介します。
crontabファイル5つの6つフィールドがあります。分、時間、月の日、月、曜日、最後に実行したいコマンドです。
* * * * * command to be executed
– – – – –
| | | | |
| | | | +—– day of week (0 – 6) (Sunday=0)
| | | +——- month (1 – 12)
| | +——— day of month (1 – 31)
| +———– hour (0 – 23)
+————- min (0 – 59)
*がワイルドカードとして使うことができます。
crontabのサンプル
hdc1000WithSqlというプログラムを作製して、/usr/local/binに置きました。
毎時間ごとに実行したいのでminのフィールドに00を設定しています。引数 “-add”で SQLに登録する設定です。
00 * * * * /usr/local/bin/hdc1000WithSql -add
おわりに
MySQLに温度データを定期的に実行できるようになりました。次回は登録したデータをブラウザ経由でインタラクティブに表示できるようにしたいと思います。
記事を読んでいただいてありがとうございます。この記事がいいなと思ったら下記のSNSボタンのクリックをお願いします。励みになります😁