Arduino でセンサ値を InfluxDB へ入れつつ、 Grafana で参照する内容を少しまとめ
grafana
sample
アナログ値をwifi経由でデータを入れてみる
※ サンプルです。証明書などの準備が必要なのでこのままコンパイルできません。
#include <Wire.h>
#include "SparkFunBME280.h"
#include "SparkFunCCS811.h"
#include <ESP8266WiFi.h>
#define CCS811_ADDR 0x5B //Default I2C Address
//#define CCS811_ADDR 0x5A //Alternate I2C Address
#define PIN_NOT_WAKE 5
const char* ssid = "wifi";
const char* password = "pass";
static const char* host = "<address your influxdb server>";
static const int httpsPort = 443;
const char* db_name = "test";
const char* db_user = "esp8266";
const char* db_password = "test";
//Global sensor objects
CCS811 myCCS811(CCS811_ADDR);
BME280 myBME280;
WiFiClientSecure client;
extern const unsigned char ca_cert[] PROGMEM;
extern const unsigned int ca_cert_len;
unsigned long counter = 0;
void setup()
{
Serial.begin(115200);
Serial.println();
Serial.println();
Serial.println();
Serial.println("Apply BME280 data to CCS811 for compensation.");
Wire.begin();
//This begins the CCS811 sensor and prints error status of .beginWithStatus()
CCS811Core::CCS811_Status_e returnCode = myCCS811.beginWithStatus();
Serial.print("CCS811 begin exited with: ");
Serial.println(myCCS811.statusString(returnCode));
returnCode = myCCS811.setDriveMode(2); // 10s
Serial.print("CCS811 setDriveMode exited with: ");
Serial.println(myCCS811.statusString(returnCode));
//Initialize BME280
//For I2C, enable the following and disable the SPI section
myBME280.settings.commInterface = I2C_MODE;
myBME280.settings.I2CAddress = 0x76;
myBME280.settings.runMode = 3; //Normal mode
myBME280.settings.tStandby = 0;
myBME280.settings.filter = 4;
myBME280.settings.tempOverSample = 5;
myBME280.settings.pressOverSample = 5;
myBME280.settings.humidOverSample = 5;
//Calling .begin() causes the settings to be loaded
delay(10); //Make sure sensor had enough time to turn on. BME280 requires 2ms to start up.
myBME280.begin();
pinMode(LED_BUILTIN, OUTPUT);
Serial.println("connecting...");
WiFi.hostname("environment-monitor-workspace");
WiFi.begin(ssid, password);
connectWifi();
syncTime();
bool res = client.setCACert_P(ca_cert, ca_cert_len);
if (!res) {
Serial.println("Failed to load root CA certificate!");
while (true) {
yield();
}
}
if (!client.connect(host, httpsPort)) {
Serial.println("connection failed");
return;
}
//client.keepAlive();
client.keepAlive(TCP_DEFAULT_KEEPALIVE_IDLE_SEC, TCP_DEFAULT_KEEPALIVE_INTERVAL_SEC, TCP_DEFAULT_KEEPALIVE_COUNT);
if (client.verifyCertChain(host)) {
//Serial.println("* SSL certificate verify ok");
} else {
Serial.println("* ERROR: SSL certificate verify failed");
client.stop();
return;
}
client.stop();
}
void connectWifi() {
Serial.println("connecting...");
while (WiFi.status() != WL_CONNECTED) {
delay(500);
digitalWrite(LED_BUILTIN, LOW);
Serial.print(".");
delay(10);
digitalWrite(LED_BUILTIN, HIGH);
}
Serial.println();
Serial.print("Connected to ");
Serial.println(ssid);
Serial.print("IP address: ");
Serial.println(WiFi.localIP());
}
void loop()
{
//Check to see if data is available
if (myCCS811.dataAvailable())
{
Serial.println();
//Calling this function updates the global tVOC and eCO2 variables
myCCS811.readAlgorithmResults();
//printInfoSerial fetches the values of tVOC and eCO2
printInfoSerial();
float BMEtempC = myBME280.readTempC();
float BMEhumid = myBME280.readFloatHumidity();
//This sends the temperature data to the CCS811
myCCS811.setEnvironmentalData(BMEhumid, BMEtempC);
}
else if (myCCS811.checkForStatusError())
{
//If the CCS811 found an internal error, print it.
printSensorError();
}
Serial.print(".");
delay(100); //Wait for next reading
}
//---------------------------------------------------------------
void printInfoSerial()
{
//getCO2() gets the previously read data from the library
uint16_t co2 = myCCS811.getCO2();
Serial.print(" eCO2 : ");
Serial.print(co2);
Serial.println(" ppm");
//getTVOC() gets the previously read data from the library
uint16_t tvoc = myCCS811.getTVOC();
Serial.print(" TVOC : ");
Serial.print(tvoc);
Serial.println(" ppb");
float temperature = myBME280.readTempC();
Serial.print(" Temperature: ");
Serial.print(temperature, 2);
Serial.println(" C");
float pressure = myBME280.readFloatPressure() / 100.0;
Serial.print(" Pressure : ");
Serial.print(pressure, 4);
Serial.println(" hPa");
Serial.print(" Altitude : ");
Serial.print(myBME280.readFloatAltitudeMeters(), 2);
Serial.println(" m");
float humidity = myBME280.readFloatHumidity();
Serial.print(" Humidity : ");
Serial.print(humidity, 2);
Serial.println(" %");
Serial.println();
sendDataToInfluxdb(temperature, pressure, humidity, co2, tvoc);
}
//printSensorError gets, clears, then prints the errors
//saved within the error register.
void printSensorError()
{
uint8_t error = myCCS811.getErrorRegister();
if ( error == 0xFF ) //comm error
{
Serial.println("Failed to get ERROR_ID register.");
}
else
{
Serial.print("Errcharor: ");
if (error & 1 << 5) Serial.print("HeaterSupply");
if (error & 1 << 4) Serial.print("HeaterFault");
if (error & 1 << 3) Serial.print("MaxResistance");
if (error & 1 << 2) Serial.print("MeasModeInvalid");
if (error & 1 << 1) Serial.print("ReadRegInvalid");
if (error & 1 << 0) Serial.print("MsgInvalid");
Serial.println();
}
}
// keepalive https://forum.arduino.cc/index.php?topic=372714.0
void sendDataToInfluxdb(float temperature, float pressure, float humidity, uint16_t co2, uint16_t tvoc) {
if (!client.connected()) {
if (!client.connect(host, httpsPort)) {
Serial.println("connection failed");
connectWifi();
return;
}
}
counter++;
String payload = "environment_monitor,location=workspace temperature=" + String(temperature) +
",pressure=" + String(pressure) +
",humidity=" + String(humidity) +
",eCO2=" + String(co2) +
",TVOC=" + String(tvoc) +
",counter=" + String(counter);
Serial.println(payload.c_str());
String url = String("/write?db=") + db_name + "&u=" + db_user + "&p=" + db_password;
client.print(String("POST ") + url + " HTTP/1.1\r\n" +
"Host: " + host + "\r\n" +
"User-Agent: esp8266 envmon\r\n" +
"Accept: */*\r\n");
client.print("Content-Length: ");
client.println(payload.length());
client.println("Content-Type: application/x-www-form-urlencoded");
client.println("Connection: keep-alive");
//client.println("Connection: close");
client.println();
client.println(payload);
client.println();
client.println();
unsigned long timeout = millis();
while (!client.available()) {
if (millis() - timeout > 10000) {
Serial.println(">>> Client Timeout !");
client.stop();
delay(1000);
return;
}
}
while (client.connected()) {
//while (client.connected()) {
String line = client.readStringUntil('\n');
//Serial.print("< ");
//Serial.println(line);
if (line == "\r") {
break;
}
}
// while (client.available()) {
// String line = client.readStringUntil('\n');
// Serial.print("< ");
// Serial.println(line);
// }
//client.stop();
//Serial.println("* done");
}
#define JST 3600*9
void syncTime() {
configTime(JST, 0, "time.google.com", "ntp.nict.jp");
time_t now = time(nullptr);
while (now < JST * 2) {
delay(500);
Serial.print(".");
now = time(nullptr);
}
struct tm timeinfo;
gmtime_r(&now, &timeinfo);
Serial.print("Current time: ");
Serial.print(asctime(&timeinfo));
}
データ収集で得られたものの一例
M5Stack で表示
grafanaのグラフ(室内の温湿度情報)をM5stackに表示できた。ちょっとテンション上がる pic.twitter.com/3Lu3TwtuDd
— Ato Araki (@atotto) May 23, 2021
トンガの火山噴火の衝撃波らしきものの観測
我が家の気圧計もバッチリ捉えてたhttps://t.co/N9ZJWbsuAZ pic.twitter.com/Uep5j7Dloe
— Ato Araki (@atotto) January 16, 2022