記事の推し
・BluetoothSerial通信で複数数値データ転送と測定トリガーで読み間違いを減らした
・M5AtomUとENVIIIでI2Cで簡単に温湿度気圧測定
・PC側のPythonで温湿度気圧データをリアルタイム描画とファイル出力
・連続測定でトンガの衝撃波を記録できるかも...
・M5AtomUはraspi4のUSBに挿して、ArduinoIDEで開発
・PythonはPC上(ペアリング済)のVscodeで開発・実行
・BluetoothSerial利用なので、開発できた時点でUSBから電源供給すれば、電波が届く範囲なら自由に計測ができる
・BME280センサーと同時測定してみた
・記録は、(毎日)世代管理できるようにした
M5AtomU側
# include "Wire.h"
# include "UNIT_ENV.h"
SHT3X sht30;
QMP6988 qmp6988;
# include "BluetoothSerial.h"
# if !defined(CONFIG_BT_ENABLED) || !defined(CONFIG_BLUEDROID_ENABLED)
# error Bluetooth is not enabled! Please run `make menuconfig` to and enable it
# endif
BluetoothSerial SerialBT;
void setup() {
Serial.begin(115200);
Wire.begin(26, 32); //21, 22); SDA, SCLのpin番号
qmp6988.init();
SerialBT.begin("ESP32test"); //Bluetooth device name
Serial.println("The device started, now you can pair it with bluetooth!");
}
void loop() {
if (sht30.get() != 0) {
return;
}
Serial.printf("T\r\n"); //測定開始位置の特定のため
Serial.printf("%2.2f\r\n", sht30.cTemp);
Serial.printf("%0.2f\r\n", sht30.humidity);
Serial.printf("%0.2f\r\n", qmp6988.calcPressure());
delay(2000); //2秒間隔で測定のため、応答速度を考慮
}
PC側pythonプログラム
import serial
import time
import matplotlib.pyplot as plt
import numpy as np
import csv
COM="COM3"
bitRate=115200
ser = serial.Serial(COM, bitRate, timeout=0.1)
with open('temp_hum_press.csv', 'w', newline='') as csvfile:
fieldnames = ['Time', 'Temperature', 'Humidity', 'Pressure']
spamwriter = csv.DictWriter(csvfile, fieldnames=fieldnames)
spamwriter.writeheader()
fig, (ax1, ax2, ax3) = plt.subplots(3, 1, figsize=(8,16))
x = np.arange(0,1000,1)
y = 0*np.arange(0,1000,1)
data_start = ser.readline()
while data_start != b'T\r\n':
data_start = ser.readline()
#print(data_start)
data_temp = ser.readline()
data_humid = ser.readline()
data_pressure = ser.readline()
print(data_temp)
fd_temp = float(data_temp.decode())
fd_humid = float(data_humid.decode())
fd_pressure = float(data_pressure.decode())
ax1.plot(x,y+fd_temp, lw = 2)
ax2.plot(x,y+fd_humid, lw = 2)
ax3.plot(x,y+fd_pressure, lw = 2)
ax1.set_ylim(fd_temp-10,fd_temp+10)
ax2.set_ylim(fd_humid-10,fd_humid+10)
ax3.set_ylim(fd_pressure-50,fd_pressure+50)
ax1.plot(0, fd_temp, color = 'red', marker = 'o',linestyle = '-', markersize = 3, label = "Temperature")
ax2.plot(0, fd_humid,color = 'blue', marker = 'o',linestyle = '-', markersize = 3, label = "Humidity")
ax3.plot(0, fd_pressure,color = 'black', marker = 'o',linestyle = '-', markersize = 3, label = "Pressure")
ax1.legend()
ax2.legend()
ax3.legend()
plt.grid(True)
plt.pause(1)
start = time.time()
tm0 = start
tm = start
while 1:
try:
data_start = ser.readline()
while data_start != b'T\r\n':
data_start = ser.readline()
#print(data_start)
data_temp = ser.readline()
data_humid = ser.readline()
data_pressure = ser.readline()
fd_temp = float(data_temp.decode())
fd_humid = float(data_humid.decode())
fd_pressure = float(data_pressure.decode())
except:
#time.sleep(1)
print('error')
spamwriter.writerow({'Time': time.ctime(time.time()), 'Temperature': fd_temp, 'Humidity': fd_humid, 'Pressure': fd_pressure})
print( time.ctime(time.time()), fd_temp, fd_humid, fd_pressure)
ax1.plot(tm-start, fd_temp,color = 'red', marker = 'o',linestyle = '-', markersize = 3, label = "Temperature")
ax2.plot(tm-start, fd_humid,color = 'blue', marker = 'o',linestyle = '-', markersize = 3, label = "Humidity")
ax3.plot(tm-start, fd_pressure,color = 'black', marker = 'o',linestyle = '-', markersize = 3, label = "Pressure")
plt.pause(0.05)
while tm-tm0 < 2:
tm = time.time()
tm0 = tm
print('program end')
ser.close()
測定の様子
最終的なプログラム
測定値が不安定なので、以下では、ENVIIIとBME280を同時測定して並べてみました。
AtomU測定側プログラム
#include "Wire.h"
# include "UNIT_ENV.h"
# include "Seeed_BME280.h"
SHT3X sht30;
QMP6988 qmp6988;
BME280 bme280;
# include "BluetoothSerial.h"
# if !defined(CONFIG_BT_ENABLED) || !defined(CONFIG_BLUEDROID_ENABLED)
# error Bluetooth is not enabled! Please run `make menuconfig` to and enable it
# endif
BluetoothSerial SerialBT;
float tmp =0.0;
float hum =0.0;
float pressure =0.0;
float tmp_ =0.0;
float hum_ =0.0;
float pressure_ =0.0;
void setup() {
Serial.begin(115200);
Wire.begin(26, 32); //21, 22);
qmp6988.init();
bme280.init();
SerialBT.begin("ESP32test"); //Bluetooth device name
Serial.println("The device started, now you can pair it with bluetooth!");
}
void loop() {
pressure = bme280.getPressure(); //qmp6988.calcPressure();
tmp = bme280.getTemperature(); //sht30.cTemp;
hum = bme280.getHumidity(); //sht30.humidity;
pressure_ = qmp6988.calcPressure();
if (sht30.get() == 0) {
tmp_ = sht30.cTemp;
hum_ = sht30.humidity;
}else{
tmp_ = 0.0;
hum_ = 0.0;
}
Serial.print(tmp);
Serial.print(hum);
Serial.print(pressure);
Serial.print('\n');
Serial.print(tmp_);
Serial.print(hum_);
Serial.print(pressure_);
Serial.print('\n');
SerialBT.printf("T\r\n");
SerialBT.printf("%2.1f\r\n", tmp);
SerialBT.printf("%2.0f\r\n", hum);
SerialBT.printf("%2.0f\r\n", pressure);
SerialBT.printf("%2.1f\r\n", tmp_);
SerialBT.printf("%2.0f\r\n", hum_);
SerialBT.printf("%2.0f\r\n", pressure_);
delay(10000);
}
上記のコードを動かすには、BME280センサーを利用しているため、ArduinoIDEの右上の▼をクリックして、以下のコードを追加します。
【参考】
Seeed-Studio/Grove_BME280
Seeed_288BME.cpp
# include "Seeed_BME280.h"
bool BME280::init(int i2c_addr) {
uint8_t retry = 0;
uint8_t chip_id = 0;
_devAddr = i2c_addr;
Wire.begin();
while ((retry++ < 5) && (chip_id != 0x60)) {
chip_id = BME280Read8(BME280_REG_CHIPID);
#ifdef BMP280_DEBUG_PRINT
Serial.print("Read chip ID: ");
Serial.println(chip_id);
#endif
delay(100);
}
if (chip_id != 0x60){
Serial.println("Read Chip ID fail!");
return false;
}
dig_T1 = BME280Read16LE(BME280_REG_DIG_T1);
dig_T2 = BME280ReadS16LE(BME280_REG_DIG_T2);
dig_T3 = BME280ReadS16LE(BME280_REG_DIG_T3);
dig_P1 = BME280Read16LE(BME280_REG_DIG_P1);
dig_P2 = BME280ReadS16LE(BME280_REG_DIG_P2);
dig_P3 = BME280ReadS16LE(BME280_REG_DIG_P3);
dig_P4 = BME280ReadS16LE(BME280_REG_DIG_P4);
dig_P5 = BME280ReadS16LE(BME280_REG_DIG_P5);
dig_P6 = BME280ReadS16LE(BME280_REG_DIG_P6);
dig_P7 = BME280ReadS16LE(BME280_REG_DIG_P7);
dig_P8 = BME280ReadS16LE(BME280_REG_DIG_P8);
dig_P9 = BME280ReadS16LE(BME280_REG_DIG_P9);
dig_H1 = BME280Read8(BME280_REG_DIG_H1);
dig_H2 = BME280Read16LE(BME280_REG_DIG_H2);
dig_H3 = BME280Read8(BME280_REG_DIG_H3);
dig_H4 = (BME280Read8(BME280_REG_DIG_H4) << 4) | (0x0F & BME280Read8(BME280_REG_DIG_H4 + 1));
dig_H5 = (BME280Read8(BME280_REG_DIG_H5 + 1) << 4) | (0x0F & BME280Read8(BME280_REG_DIG_H5) >> 4);
dig_H6 = (int8_t)BME280Read8(BME280_REG_DIG_H6);
writeRegister(BME280_REG_CONTROLHUMID, 0x05); //Choose 16X oversampling
writeRegister(BME280_REG_CONTROL, 0xB7); //Choose 16X oversampling
return true;
}
float BME280::getTemperature(void) {
int32_t var1, var2;
int32_t adc_T = BME280Read24(BME280_REG_TEMPDATA);
// Check if the last transport successed
if (!isTransport_OK) {
return 0;
}
adc_T >>= 4;
var1 = (((adc_T >> 3) - ((int32_t)(dig_T1 << 1))) *
((int32_t)dig_T2)) >> 11;
var2 = (((((adc_T >> 4) - ((int32_t)dig_T1)) *
((adc_T >> 4) - ((int32_t)dig_T1))) >> 12) *
((int32_t)dig_T3)) >> 14;
t_fine = var1 + var2;
float T = (t_fine * 5 + 128) >> 8;
return T / 100;
}
uint32_t BME280::getPressure(void) {
int64_t var1, var2, p;
// Call getTemperature to get t_fine
getTemperature();
// Check if the last transport successed
if (!isTransport_OK) {
return 0;
}
int32_t adc_P = BME280Read24(BME280_REG_PRESSUREDATA);
adc_P >>= 4;
var1 = ((int64_t)t_fine) - 128000;
var2 = var1 * var1 * (int64_t)dig_P6;
var2 = var2 + ((var1 * (int64_t)dig_P5) << 17);
var2 = var2 + (((int64_t)dig_P4) << 35);
var1 = ((var1 * var1 * (int64_t)dig_P3) >> 8) + ((var1 * (int64_t)dig_P2) << 12);
var1 = (((((int64_t)1) << 47) + var1)) * ((int64_t)dig_P1) >> 33;
if (var1 == 0) {
return 0; // avoid exception caused by division by zero
}
p = 1048576 - adc_P;
p = (((p << 31) - var2) * 3125) / var1;
var1 = (((int64_t)dig_P9) * (p >> 13) * (p >> 13)) >> 25;
var2 = (((int64_t)dig_P8) * p) >> 19;
p = ((p + var1 + var2) >> 8) + (((int64_t)dig_P7) << 4);
return (uint32_t)p / 256;
}
uint32_t BME280::getHumidity(void) {
int32_t v_x1_u32r, adc_H;
// Call getTemperature to get t_fine
getTemperature();
// Check if the last transport successed
if (!isTransport_OK) {
return 0;
}
adc_H = BME280Read16(BME280_REG_HUMIDITYDATA);
v_x1_u32r = (t_fine - ((int32_t)76800));
v_x1_u32r = (((((adc_H << 14) - (((int32_t)dig_H4) << 20) - (((int32_t)dig_H5) * v_x1_u32r)) + ((
int32_t)16384)) >> 15) * (((((((v_x1_u32r * ((int32_t)dig_H6)) >> 10) * (((v_x1_u32r * ((int32_t)dig_H3)) >> 11) + ((
int32_t)32768))) >> 10) + ((int32_t)2097152)) * ((int32_t)dig_H2) + 8192) >> 14));
v_x1_u32r = (v_x1_u32r - (((((v_x1_u32r >> 15) * (v_x1_u32r >> 15)) >> 7) * ((int32_t)dig_H1)) >> 4));
v_x1_u32r = (v_x1_u32r < 0 ? 0 : v_x1_u32r);
v_x1_u32r = (v_x1_u32r > 419430400 ? 419430400 : v_x1_u32r);
return (uint32_t)(v_x1_u32r >> 12) / 1024.0;
}
float BME280::calcAltitude(float pressure) {
if (!isTransport_OK) {
return 0;
}
float A = pressure / 101325;
float B = 1 / 5.25588;
float C = pow(A, B);
C = 1.0 - C;
C = C / 0.0000225577;
return C;
}
uint8_t BME280::BME280Read8(uint8_t reg) {
Wire.beginTransmission(_devAddr);
Wire.write(reg);
Wire.endTransmission();
Wire.requestFrom(_devAddr, 1);
// return 0 if slave didn't response
if (Wire.available() < 1) {
isTransport_OK = false;
return 0;
} else {
isTransport_OK = true;
}
return Wire.read();
}
uint16_t BME280::BME280Read16(uint8_t reg) {
uint8_t msb, lsb;
Wire.beginTransmission(_devAddr);
Wire.write(reg);
Wire.endTransmission();
Wire.requestFrom(_devAddr, 2);
// return 0 if slave didn't response
if (Wire.available() < 2) {
isTransport_OK = false;
return 0;
} else {
isTransport_OK = true;
}
msb = Wire.read();
lsb = Wire.read();
return (uint16_t) msb << 8 | lsb;
}
uint16_t BME280::BME280Read16LE(uint8_t reg) {
uint16_t data = BME280Read16(reg);
return (data >> 8) | (data << 8);
}
int16_t BME280::BME280ReadS16(uint8_t reg) {
return (int16_t)BME280Read16(reg);
}
int16_t BME280::BME280ReadS16LE(uint8_t reg) {
return (int16_t)BME280Read16LE(reg);
}
uint32_t BME280::BME280Read24(uint8_t reg) {
uint32_t data;
Wire.beginTransmission(_devAddr);
Wire.write(reg);
Wire.endTransmission();
Wire.requestFrom(_devAddr, 3);
// return 0 if slave didn't response
if (Wire.available() < 3) {
isTransport_OK = false;
return 0;
} else if (isTransport_OK == false) {
isTransport_OK = true;
if (!init(_devAddr)) {
#ifdef BMP280_DEBUG_PRINT
Serial.println("Device not connected or broken!");
#endif
}
}
data = Wire.read();
data <<= 8;
data |= Wire.read();
data <<= 8;
data |= Wire.read();
return data;
}
void BME280::writeRegister(uint8_t reg, uint8_t val) {
Wire.beginTransmission(_devAddr); // start transmission to device
Wire.write(reg); // send register address
Wire.write(val); // send value to write
Wire.endTransmission(); // end transmission
}
同様にタブを追加して、書き込みます。
Seeed_BME280.h
# pragma once
# ifndef _SEEED_BME280_H_
# define _SEEED_BME280_H_
# include <Arduino.h>
# include <Wire.h>
# define BME280_ADDRESS 0x76
# define BME280_REG_DIG_T1 0x88
# define BME280_REG_DIG_T2 0x8A
# define BME280_REG_DIG_T3 0x8C
# define BME280_REG_DIG_P1 0x8E
# define BME280_REG_DIG_P2 0x90
# define BME280_REG_DIG_P3 0x92
# define BME280_REG_DIG_P4 0x94
# define BME280_REG_DIG_P5 0x96
# define BME280_REG_DIG_P6 0x98
# define BME280_REG_DIG_P7 0x9A
# define BME280_REG_DIG_P8 0x9C
# define BME280_REG_DIG_P9 0x9E
# define BME280_REG_DIG_H1 0xA1
# define BME280_REG_DIG_H2 0xE1
# define BME280_REG_DIG_H3 0xE3
# define BME280_REG_DIG_H4 0xE4
# define BME280_REG_DIG_H5 0xE5
# define BME280_REG_DIG_H6 0xE7
# define BME280_REG_CHIPID 0xD0
# define BME280_REG_VERSION 0xD1
# define BME280_REG_SOFTRESET 0xE0
# define BME280_REG_CAL26 0xE1
# define BME280_REG_CONTROLHUMID 0xF2
# define BME280_REG_CONTROL 0xF4
# define BME280_REG_CONFIG 0xF5
# define BME280_REG_PRESSUREDATA 0xF7
# define BME280_REG_TEMPDATA 0xFA
# define BME280_REG_HUMIDITYDATA 0xFD
class BME280 {
public:
bool init(int i2c_addr = BME280_ADDRESS);
float getTemperature(void);
uint32_t getPressure(void);
uint32_t getHumidity(void);
float calcAltitude(float pressure);
private:
int _devAddr;
bool isTransport_OK;
// Calibration data
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;
uint8_t dig_H1;
int16_t dig_H2;
uint8_t dig_H3;
int16_t dig_H4;
int16_t dig_H5;
int8_t dig_H6;
int32_t t_fine;
// private functions
uint8_t BME280Read8(uint8_t reg);
uint16_t BME280Read16(uint8_t reg);
uint16_t BME280Read16LE(uint8_t reg);
int16_t BME280ReadS16(uint8_t reg);
int16_t BME280ReadS16LE(uint8_t reg);
uint32_t BME280Read24(uint8_t reg);
void writeRegister(uint8_t reg, uint8_t val);
};
# endif
COM="COM3"
bitRate=115200
ser = serial.Serial(COM, bitRate, timeout=0.1)
def measure_(filename):
with open(filename, 'w', newline='') as csvfile:
fieldnames = ['Time', 'Temperature', 'Humidity', 'Pressure']
spamwriter = csv.DictWriter(csvfile, fieldnames=fieldnames)
spamwriter.writeheader()
fig, (ax1, ax2, ax3) = plt.subplots(3, 1, figsize=(8,16))
x = np.arange(0,1000,1)
y = 0*np.arange(0,1000,1)
data_start = ser.readline()
while data_start != b'T\r\n':
data_start = ser.readline()
print(data_start)
data_temp = ser.readline()
data_humid = ser.readline()
data_pressure = ser.readline()
data_temp_ = ser.readline()
data_humid_ = ser.readline()
data_pressure_ = ser.readline()
print(data_temp)
fd_temp = float(data_temp.decode())
fd_humid = float(data_humid.decode())
fd_pressure = float(data_pressure.decode())
fd_temp_ = float(data_temp_.decode())
fd_humid_ = float(data_humid_.decode())
fd_pressure_ = float(data_pressure_.decode())
ax1.plot(x,y+fd_temp, lw = 2)
ax2.plot(x,y+fd_humid, lw = 2)
ax3.plot(x,y+fd_pressure, lw = 2)
ax1.set_ylim(fd_temp-10,fd_temp+10)
ax2.set_ylim(fd_humid-20,fd_humid+20)
ax3.set_ylim(fd_pressure-500,fd_pressure+500)
ax1.plot(0, fd_temp, color = 'red', marker = 'o',linestyle = '-', markersize = 3, label = "Temperature")
ax2.plot(0, fd_humid,color = 'red', marker = 'o',linestyle = '-', markersize = 3, label = "Humidity")
ax3.plot(0, fd_pressure,color = 'red', marker = 'o',linestyle = '-', markersize = 3, label = "Pressure")
ax1.plot(0, fd_temp_, color = 'blue', marker = 'o',linestyle = '-', markersize = 3, label = "Temperature_")
ax2.plot(0, fd_humid_,color = 'blue', marker = 'o',linestyle = '-', markersize = 3, label = "Humidity_")
ax3.plot(0, fd_pressure_,color = 'blue', marker = 'o',linestyle = '-', markersize = 3, label = "Pressure_")
ax1.legend()
ax2.legend()
ax3.legend()
plt.grid(True)
plt.pause(1)
start = time.time()
tm0 = start
tm = start
while 1:
try:
data_start = ser.readline()
while data_start != b'T\r\n':
data_start = ser.readline()
#print(data_start)
data_temp = ser.readline()
data_humid = ser.readline()
data_pressure = ser.readline()
data_temp_ = ser.readline()
data_humid_ = ser.readline()
data_pressure_ = ser.readline()
fd_temp = float(data_temp.decode())
fd_humid = float(data_humid.decode())
fd_pressure = float(data_pressure.decode())
fd_temp_ = float(data_temp_.decode())
fd_humid_ = float(data_humid_.decode())
fd_pressure_ = float(data_pressure_.decode())
except:
#time.sleep(1)
print('error')
spamwriter.writerow({'Time': time.ctime(time.time()), 'Temperature': fd_temp, 'Humidity': fd_humid, 'Pressure': fd_pressure})
print( time.ctime(time.time()), fd_temp, fd_humid, fd_pressure)
print( time.ctime(time.time()), fd_temp_, fd_humid_, fd_pressure_)
ax1.plot(tm-start, fd_temp,color = 'red', marker = 'o',linestyle = '-', markersize = 3, label = "Temperature")
ax2.plot(tm-start, fd_humid,color = 'red', marker = 'o',linestyle = '-', markersize = 3, label = "Humidity")
ax3.plot(tm-start, fd_pressure,color = 'red', marker = 'o',linestyle = '-', markersize = 3, label = "Pressure")
ax1.plot(tm-start, fd_temp_,color = 'blue', marker = 'o',linestyle = '-', markersize = 3, label = "Temperature_")
ax2.plot(tm-start, fd_humid_,color = 'blue', marker = 'o',linestyle = '-', markersize = 3, label = "Humidity_")
ax3.plot(tm-start, fd_pressure_,color = 'blue', marker = 'o',linestyle = '-', markersize = 3, label = "Pressure_")
plt.pause(0.05)
figfilename = 'fig_Temp_Humid_Pres_'+ str(datetime.date.today())+ '.png'
plt.savefig(figfilename)
while tm-tm0 < 10:
tm = time.time()
tm0 = tm
filename1 = 'temp_hum_press' + str(datetime.date.today())+ '.csv'
if filename != filename1:
print(filename, filename1)
break
else:
continue
return
if name == 'main':
filename1 = 'temp_hum_press' + str(datetime.date.today())+ '.csv'
while 1:
filename = 'temp_hum_press' + str(datetime.date.today())+ '.csv'
if filename == filename1:
measure_(filename)
else:
filename1 = 'temp_hum_press' + str(datetime.date.today())+ '.csv'
print('program end')
ser.close()
# 測定結果
上記では、長期間計測が可能なように、毎日ファイルを更新して結果を残すこととしている。
結果は、以下のとおりのグラフが得られた
横軸は、秒である。二つの測定値がほぼ平行に動いているのがわかる。
なお、測定は窓際でソーラー充電器から給電し、データはBlootooth_serialでPCに転送してPC側で保存している。また、データは、ENVIIIのデータのみ保存している。
#### fig_Temp_Humid_Pres_2022-01-29.png

#### fig_Temp_Humid_Pres_2022-01-28.png
