2019/06/02
前回の続きです。今回はESP8266に接続している温湿度気圧センサ(BME280)のデータをRaspi Zeroに飛ばすお話です。
プログラムは以下の通りで、132〜258行目はセンサからのデータ収集部分、266行目以降は前回お話ししたFTP通信部分です。センサデータ収集プログラムは
以前のblogでお話しした内容、FTP通信プログラムは
前回の内容をそのまま参考にしました。機能ごとにソースコードを分けずに引っ付けて長くなってしまいましたが、まずは動作検証まで。
#include <ESP8266WiFi.h>
#include <Wire.h>
#include <FS.h>
#define AP_SSID "Raspi_AP"
#define AP_PW "drbobt123"
#define BME280_ADDRESS 0x76
WiFiServer server( 80 );
//FTPサーバーと接続するための設定_
#define FTP_TO "192.168.4.1" // FTP 送信先のIPアドレス
#define FTP_USER "pi" // FTP ユーザ名
#define FTP_PASS "raspberry" // FTP パスワード
#define FTP_DIR "/home/pi/Common" // FTP ディレクトリ
#define FILENAME "/mes.csv" // FTPサーバーに送るファイル名(保存するファイル名)
unsigned long int hum_raw,temp_raw,pres_raw;
signed long int t_fine;
double temp_act = 0.0, press_act = 0.0,hum_act=0.0;
int sec, minute, hr, mintemp;
char temperature[16];
char atmospheric_pressure[16];
char humidity[16];
char uptime[16];
char filename[16];
uint16_t dig_T1;
int16_t dig_T2;
int16_t dig_T3;
uint16_t dig_P1;
int16_t dig_P2;
int16_t dig_P3;
int16_t dig_P4;
int16_t dig_P5;
int16_t dig_P6;
int16_t dig_P7;
int16_t dig_P8;
int16_t dig_P9;
int8_t dig_H1;
int16_t dig_H2;
int8_t dig_H3;
int16_t dig_H4;
int16_t dig_H5;
int8_t dig_H6;
void setup()
{
// For STA use
WiFi.mode(WIFI_STA);
WiFi.begin(AP_SSID, AP_PW);
while (WiFi.status() != WL_CONNECTED) { // 接続に成功するまで待つ
Serial.print(".");
delay(500); // 待ち時間処理
}
server.begin();
uint8_t osrs_t = 1; //Temperature oversampling x 1
uint8_t osrs_p = 1; //Pressure oversampling x 1
uint8_t osrs_h = 1; //Humidity oversampling x 1
uint8_t mode = 3; //Normal mode
uint8_t t_sb = 5; //Tstandby 1000ms
uint8_t filter = 0; //Filter off
uint8_t spi3w_en = 0; //3-wire SPI Disable
uint8_t ctrl_meas_reg = (osrs_t << 5) | (osrs_p << 2) | mode;
uint8_t config_reg = (t_sb << 5) | (filter << 2) | spi3w_en;
uint8_t ctrl_hum_reg = osrs_h;
mintemp = -1;
// Sensor data Communication
Serial.begin(115200);
Wire.begin(4,5);
writeReg(0xF2,ctrl_hum_reg);
writeReg(0xF4,ctrl_meas_reg);
writeReg(0xF5,config_reg);
readTrim();
SPIFFS.begin();
}
void loop()
{
signed long int temp_cal;
unsigned long int press_cal,hum_cal;
delay(60000);
readData();
sec = millis() / 1000;
minute = sec / 60;
hr = minute / 60;
temp_cal = calibration_T(temp_raw);
press_cal = calibration_P(pres_raw);
hum_cal = calibration_H(hum_raw);
temp_act = (double)temp_cal / 100.0;
press_act = (double)press_cal / 100.0;
hum_act = (double)hum_cal / 1024.0;
sprintf(uptime,"%02d:%02d",hr, minute % 60);
Serial.print(uptime);
Serial.print("->");
Serial.print("TEMP : ");
Serial.print(temp_act);
Serial.print(" DegC PRESS : ");
Serial.print(press_act);
Serial.print(" hPa HUM : ");
Serial.print(hum_act);
Serial.println(" %");
File f = SPIFFS.open(FILENAME, "a");
f.print(uptime);
f.print(",");
f.print(temp_act);
f.print(",");
f.print(press_act);
f.print(",");
f.println(hum_act);
f.close();
if(minute!=mintemp){
mintemp = minute;
byte ret = doFTP(FILENAME); // FTPで送信する
if (ret) {
Serial.print("FTP Err :");
Serial.println(ret);
delay(3000);
}
}
}
void readTrim()
{
uint8_t data[32],i=0;
Wire.beginTransmission(BME280_ADDRESS);
Wire.write(0x88);
Wire.endTransmission();
Wire.requestFrom(BME280_ADDRESS,24);
while(Wire.available()){
data[i] = Wire.read();
i++;
}
Wire.beginTransmission(BME280_ADDRESS);
Wire.write(0xA1);
Wire.endTransmission();
Wire.requestFrom(BME280_ADDRESS,1);
data[i] = Wire.read();
i++;
Wire.beginTransmission(BME280_ADDRESS);
Wire.write(0xE1);
Wire.endTransmission();
Wire.requestFrom(BME280_ADDRESS,7);
while(Wire.available()){
data[i] = Wire.read();
i++;
}
dig_T1 = (data[1] << 8) | data[0];
dig_T2 = (data[3] << 8) | data[2];
dig_T3 = (data[5] << 8) | data[4];
dig_P1 = (data[7] << 8) | data[6];
dig_P2 = (data[9] << 8) | data[8];
dig_P3 = (data[11]<< 8) | data[10];
dig_P4 = (data[13]<< 8) | data[12];
dig_P5 = (data[15]<< 8) | data[14];
dig_P6 = (data[17]<< 8) | data[16];
dig_P7 = (data[19]<< 8) | data[18];
dig_P8 = (data[21]<< 8) | data[20];
dig_P9 = (data[23]<< 8) | data[22];
dig_H1 = data[24];
dig_H2 = (data[26]<< 8) | data[25];
dig_H3 = data[27];
dig_H4 = (data[28]<< 4) | (0x0F & data[29]);
dig_H5 = (data[30] << 4) | ((data[29] >> 4) & 0x0F);
dig_H6 = data[31];
}
void writeReg(uint8_t reg_address, uint8_t data)
{
Wire.beginTransmission(BME280_ADDRESS);
Wire.write(reg_address);
Wire.write(data);
Wire.endTransmission();
}
void readData()
{
int i = 0;
uint32_t data[8];
Wire.beginTransmission(BME280_ADDRESS);
Wire.write(0xF7);
Wire.endTransmission();
Wire.requestFrom(BME280_ADDRESS,8);
while(Wire.available()){
data[i] = Wire.read();
i++;
}
pres_raw = (data[0] << 12) | (data[1] << 4) | (data[2] >> 4);
temp_raw = (data[3] << 12) | (data[4] << 4) | (data[5] >> 4);
hum_raw = (data[6] << 8) | data[7];
}
signed long int calibration_T(signed long int adc_T)
{
signed long int var1, var2, T;
var1 = ((((adc_T >> 3) - ((signed long int)dig_T1<<1))) * ((signed long int)dig_T2)) >> 11;
var2 = (((((adc_T >> 4) - ((signed long int)dig_T1)) * ((adc_T>>4) - ((signed long int)dig_T1))) >> 12) * ((signed long int)dig_T3)) >> 14;
t_fine = var1 + var2;
T = (t_fine * 5 + 128) >> 8;
return T;
}
unsigned long int calibration_P(signed long int adc_P)
{
signed long int var1, var2;
unsigned long int P;
var1 = (((signed long int)t_fine)>>1) - (signed long int)64000;
var2 = (((var1>>2) * (var1>>2)) >> 11) * ((signed long int)dig_P6);
var2 = var2 + ((var1*((signed long int)dig_P5))<<1);
var2 = (var2>>2)+(((signed long int)dig_P4)<<16);
var1 = (((dig_P3 * (((var1>>2)*(var1>>2)) >> 13)) >>3) + ((((signed long int)dig_P2) * var1)>>1))>>18;
var1 = ((((32768+var1))*((signed long int)dig_P1))>>15);
if (var1 == 0)
{
return 0;
}
P = (((unsigned long int)(((signed long int)1048576)-adc_P)-(var2>>12)))*3125;
if(P<0x80000000)
{
P = (P << 1) / ((unsigned long int) var1);
}
else
{
P = (P / (unsigned long int)var1) * 2;
}
var1 = (((signed long int)dig_P9) * ((signed long int)(((P>>3) * (P>>3))>>13)))>>12;
var2 = (((signed long int)(P>>2)) * ((signed long int)dig_P8))>>13;
P = (unsigned long int)((signed long int)P + ((var1 + var2 + dig_P7) >> 4));
return P;
}
unsigned long int calibration_H(signed long int adc_H)
{
signed long int v_x1;
v_x1 = (t_fine - ((signed long int)76800));
v_x1 = (((((adc_H << 14) -(((signed long int)dig_H4) << 20) - (((signed long int)dig_H5) * v_x1)) +
((signed long int)16384)) >> 15) * (((((((v_x1 * ((signed long int)dig_H6)) >> 10) *
(((v_x1 * ((signed long int)dig_H3)) >> 11) + ((signed long int) 32768))) >> 10) + (( signed long int)2097152)) *
((signed long int) dig_H2) + 8192) >> 14));
v_x1 = (v_x1 - (((((v_x1 >> 15) * (v_x1 >> 15)) >> 7) * ((signed long int)dig_H1)) >> 4));
v_x1 = (v_x1 < 0 ? 0 : v_x1);
v_x1 = (v_x1 > 419430400 ? 419430400 : v_x1);
return (unsigned long int)(v_x1 >> 12);
}
void sleep() {
Serial.println("F Err"); // ファイルを呼び出せなかった時のエラー表示
}
// FTP Communication
#define FTP_WAIT 1
#define BUFFER_SIZE 128
byte doFTP(char *filename){
char ftpBuf[BUFFER_SIZE];
WiFiClient client;
WiFiClient dclient;
int i;
File file = SPIFFS.open(FILENAME,"r");
if(!file){
Serial.println("SPIFFS open fail");
return 11;
}
Serial.println("SPIFFS opened");
if (client.connect(FTP_TO,21)) {
Serial.println("Command connected");
}
if(eRcv(client,ftpBuf)) return 21;
sprintf(ftpBuf,"USER %s\r\n",FTP_USER);
client.print(ftpBuf);
delay(FTP_WAIT);
Serial.print(ftpBuf);
if(eRcv(client,ftpBuf)) return 22;
sprintf(ftpBuf,"PASS %s\r\n",FTP_PASS);
client.print(ftpBuf);
delay(FTP_WAIT);
Serial.println("PASS");
if(eRcv(client,ftpBuf)) return 23;
client.print("Type I\r\n");
delay(FTP_WAIT);
Serial.println("Type i");
if(eRcv(client,ftpBuf)) return 25;
client.print("PASV\r\n");
delay(FTP_WAIT);
Serial.println("PASV");
delay(100);
if(eRcv(client,ftpBuf)) return 26;
char *tStr = strtok(ftpBuf,"(,");
if(tStr == NULL) return 27;
int array_pasv[6];
for (i = 0; i < 6; i++) {
tStr = strtok(NULL,"(,");
array_pasv[i] = atoi(tStr);
if(tStr == NULL){
Serial.println("Bad PASV Answer");
return 28;
}
}
unsigned int hiPort,loPort;
hiPort = array_pasv[4] << 8;
loPort = array_pasv[5] & 255;
Serial.print("Data port: ");
hiPort = hiPort | loPort;
Serial.println(hiPort);
if (dclient.connect(FTP_TO,hiPort)) {
Serial.println("Data connected");
}else{
Serial.println("Data connection failed");
client.stop();
file.close();
return 31;
}
sprintf(ftpBuf,"STOR %s%s\r\n",FTP_DIR,filename);
client.print(ftpBuf);
delay(FTP_WAIT);
Serial.print(ftpBuf);
if(eRcv(client,ftpBuf)){
dclient.stop();
file.close();
return 32;
}
Serial.println("Writing");
i=0;
while(file.available()){
ftpBuf[i]=file.read();
i++;
if(i >= BUFFER_SIZE){
if(!dclient.connected()) break;
dclient.write( (byte *)ftpBuf, BUFFER_SIZE);
i=0;
Serial.print(".");
delay(1);
}
}
if(i > 0){
if(dclient.connected()){
dclient.write((byte *)ftpBuf, i);
}
}
dclient.stop();
Serial.println("Data disconnected");
if(eRcv(client,ftpBuf)) return 33;
client.print("QUIT\r\n");
delay(FTP_WAIT);
Serial.println("QUIT");
client.stop();
Serial.println("Command disconnected");
file.close();
Serial.println("SPIFFS closed");
return 0;
}
byte eRcv(WiFiClient &client,char *ftpBuf){
byte thisByte,i=0,len=0;
while(!client.available()){
delay(FTP_WAIT);
if(!client.connected()){
Serial.println("FTP:eRcv:disC");
return 11;
}
i++;
if(i>1000){ // 200ms以上必要
Serial.println("FTP:eRcv:noRes");
return 12;
}
}
while(client.connected()){
if(!client.available()){
delay(FTP_WAIT);
if(!client.available()) break;
}
thisByte = client.read();
if(thisByte==(byte)'\r');
else if(thisByte==(byte)'\n'){
Serial.write('>');
Serial.println(ftpBuf);
if(ftpBuf[0] >= '4'){
client.print("QUIT\r\n");
delay(FTP_WAIT);
Serial.println("QUIT");
return 1;
}
if(len>3 && ftpBuf[3] == ' '){
return 0;
}
len = 0;
}else if(len < BUFFER_SIZE - 1 ){
ftpBuf[len] = thisByte;
len++;
ftpBuf[len] = 0;
}
}
return 0;
}
void efail(WiFiClient &client){
byte thisByte = 0;
client.print("QUIT\r\n");
delay(FTP_WAIT);
while(!client.available()){
if(!client.connected()) return;
delay(1);
}
while(client.available()){
thisByte = client.read();
Serial.write(thisByte);
}
client.stop();
Serial.println("Command disconnected");
}
実行時のArduinoのシリアルモニタはこんな感じです。1分おきにデータ収集して、そのデータをSPIFFSに追加書き込みし、そのファイルをRaspi ZeroにFTPで飛ばします。

Raspi Zeroには確実にファイル転送され、保存されていました。

ファイルの中身は以下の通り。左側から経過時間(1分おき)、温度、気圧、湿度の順番です。

ファイル保存されるのは、現時刻ではなく経過時間ですが、Raspi Zeroにファイル保存された時刻と経過時間から、データ収集した時刻を計算できました。採取したデータを表計算ソフトでグラフ化すると以下の通りです。



CSVファイルがRaspi Zeroにリアルタイムに入ってくるので、Webアプリでグラフ化するのも容易にできそうです。今回は行いませんが・・。センサの数を増やして、それらのデータをRaspi Zeroに集約すると面白いかもしれません。「簡便な自宅IoTシステム」のできあがりです。