artarch
Works Posts Make Category Tags About

センサデータを influxdb に集めて grafana でみる

Published Apr 8, 2017 by Ato Araki in やってみた, 備忘録 at https://blog.artarch.net/notes/device/grafana-influxdb-arduino/

Table of Contents

  • grafana
  • sample
  • データ収集で得られたものの一例
    • M5Stack で表示
    • トンガの火山噴火の衝撃波らしきものの観測

Arduino でセンサ値を InfluxDB へ入れつつ、 Grafana で参照する内容を少しまとめ

grafana

https://grafana.com/

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

See Also

  • MadgwickFilter + MPU9250 で遊ぶ
  • Train Controller
  • DigisparkとLM61で温度を測る
  • Raspberry Piに挿せるモータードライバを試す
  • Pomodoro Timer
  • 第三級アマチュア無線技士(3アマ)を受験しました

LastModified: 2022-01-16T11:00:00Z

© 2023 Ato Araki