ENV II UNITで取得した温度・湿度・気圧のデータをPrometheusに送信する方法を紹介します。
💡Prometheusサーバーに送信する方法(PUSH型)とPrometheusサーバーから取得する(PULL型)の2つの方法を紹介します。
環境
- Platform IO 6.1.3
ENV II UNITの値の取得
まずはENV II UNITから値を取得する方法を解説します。
必要なライブラリー
- m5stack/M5Unit-ENV
- adafruit/Adafruit Unified Sensor
- adafruit/Adafruit BMP280 Library
[env:m5stack-core2]
platform = espressif32
board = m5stack-core2
framework = arduino
lib_deps =
m5stack/M5Core2
HttpClient
WiFi
adafruit/Adafruit Unified Sensor@^1.1.6
adafruit/Adafruit BMP280 Library@^2.6.3
m5stack/M5Unit-ENV@^0.0.5
monitor_speed = 115200
プログラムの解説
- include宣言
#include "M5_ENV.h"
#include "Adafruit_Sensor.h"
#include <Adafruit_BMP280.h>
- 初期化
SHT3X sht30;
Adafruit_BMP280 bme;
float tmp = 0.0;
float hum = 0.0;
float pressure = 0.0;
- bmeライブラリーはbeginが必要ですので、
setup()
内で以下を実行
while (!bme.begin(0x76))
{
}
- センサー値の取得。気圧はPaで取得できるので、100分の1してhPaに変換しています。
pressure = bme.readPressure() / 100;
sht30.get();
tmp = sht30.cTemp;
hum = sht30.humidity;
ENV II UNITからの値取得は以上です。
💡サンプルコードはこちらです。
Prometheus remote write endpointへ送信する方法(サンプルプログラムの確認)
Grafana Labs製のライブラリーを使用します。(こちら)
必要なライブラリー
- grafana/PrometheusArduino
- grafana/PromLokiTransport
[env:m5stack-core2]
platform = espressif32
board = m5stack-core2
framework = arduino
lib_deps =
m5stack/[email protected]
[email protected]
[email protected]
grafana/[email protected]
grafana/[email protected]
monitor_speed = 115200
プログラムの解説
サンプルがありますので、これをそのまま使います。
- include
config.hにはWi-FiやPrometheusの送信先の値を設定します。
#include "config.h"
#include <PromLokiTransport.h>
#include <PrometheusArduino.h>
- 初期化
サンプルでは、TimeSeries
として、uptime_milliseconds_total
とheap_free_bytes
を送信します。2つの値を送信するので、WriteRequest req
の引数は2
を指定しています。
PromLokiTransport transport;
PromClient client(transport);
// Create a write request for 2 series.
WriteRequest req(2);
// Define a TimeSeries which can hold up to 5 samples, has a name of `uptime_milliseconds`
TimeSeries ts1(5, "uptime_milliseconds_total", "{job=\"esp32-test\",host=\"esp32\"}");
// Define a TimeSeries which can hold up to 5 samples, has a name of `heap_free_bytes`
TimeSeries ts2(5, "heap_free_bytes", "{job=\"esp32-test\",host=\"esp32\",foo=\"bar\"}");
- setup処理
Wi-Fi接続や、Prometheus送信先の設定を行います。
// Configure and start the transport layer
transport.setWifiSsid(WIFI_SSID);
transport.setWifiPass(WIFI_PASSWORD);
transport.setDebug(Serial); // Remove this line to disable debug logging of the client.
if (!transport.begin()) {
Serial.println(transport.errmsg);
while (true) {};
}
// Configure the client
client.setUrl(URL);
client.setPath((char*)PATH);
client.setPort(PORT);
client.setDebug(Serial); // Remove this line to disable debug logging of the client.
if (!client.begin()) {
Serial.println(client.errmsg);
while (true) {};
}
// Add our TimeSeries to the WriteRequest
req.addTimeSeries(ts1);
req.addTimeSeries(ts2);
req.setDebug(Serial); // Remove this line to disable debug logging of the write request serialization and compression.
- 送信処理
送信処理はバッチ送信する形のサンプルとなっており、5件データを一時蓄積した後、まとめて送信して言います。
if (!ts1.addSample(time, millis())) {
Serial.println(ts1.errmsg);
}
if (!ts2.addSample(time, freeMemory())) {
Serial.println(ts2.errmsg);
}
loopCounter++;
if (loopCounter >= 4) {
// Send data
loopCounter = 0;
PromClient::SendResult res = client.send(req);
if (!res == PromClient::SendResult::SUCCESS) {
Serial.println(client.errmsg);
// Note: additional retries or error handling could be implemented here.
// the result could also be:
// PromClient::SendResult::FAILED_DONT_RETRY
// PromClient::SendResult::FAILED_RETRYABLE
}
// Batches are not automatically reset so that additional retry logic could be implemented by the library user.
// Reset batches after a succesful send.
ts1.resetSamples();
ts2.resetSamples();
}
💡簡単に送信できるライブラリーが用意されています。
方法1:ENV II UNITの値をPrometheus remote write endpointへ送信
上記サンプルを改変し、ENV II UNITの値を送信します。
必要なライブラリー
- m5stack/M5Unit-ENV
- adafruit/Adafruit Unified Sensor
- adafruit/Adafruit BMP280 Library
- grafana/PrometheusArduino
- grafana/PromLokiTransport
[env:m5stack-core2]
platform = espressif32
board = m5stack-core2
framework = arduino
lib_deps =
m5stack/[email protected]
[email protected]
[email protected]
m5stack/[email protected]
adafruit/Adafruit Unified [email protected]
adafruit/Adafruit BMP280 [email protected]
grafana/[email protected]
grafana/[email protected]
monitor_speed = 115200
- 送信処理
PrometheusArduinoのサンプルをベースに以下のように修正します。
WriteRequest req
の引数を3
(値の数)にします。第2引数はバッファーサイズの指定で、デフォルト値は512ですが、増やす必要がありますので、1024
を指定します。
あとは、TimeSeriesを3つ定義し、WriteRequestに追加。ENV II UNITで取得した値をaddSampleして送信します。
WriteRequest req(3, 1024);
TimeSeries ts3(5, "m5core2_pressure", "{job=\"unit ii\",host=\"m5core2\"}");
TimeSeries ts4(5, "m5core2_tmp", "{job=\"unit ii\",host=\"m5core2\"}");
TimeSeries ts5(5, "m5core2_hum", "{job=\"unit ii\",host=\"m5core2\"}");
req.addTimeSeries(ts3);
req.addTimeSeries(ts4);
req.addTimeSeries(ts5);
pressure = bme.readPressure() / 100;
sht30.get();
tmp = sht30.cTemp;
hum = sht30.humidity;
if (!ts3.addSample(time, pressure)) {
Serial.println(ts3.errmsg);
}
if (!ts4.addSample(time, tmp)) {
Serial.println(ts4.errmsg);
}
if (!ts5.addSample(time, hum)) {
Serial.println(ts5.errmsg);
}
Prometheusサーバーを起動(Remote Write有効化)
Dockerを使用して構築しました。
Remote Writeを有効にするには、--web.enable-remote-write-receiver
引数を指定します。
prometheus.ymlはデフォルト値のままでOKです。(未指定だとエラーとなりました。)
docker run --rm \
-p 9090:9090 \
-v `pwd`/prometheus.yml:/prometheus/prometheus.yml \
prom/prometheus --web.enable-remote-write-receiver
- prometheus.yml
## my global config
global:
scrape_interval: 15s # Set the scrape interval to every 15 seconds. Default is every 1 minute.
evaluation_interval: 15s # Evaluate rules every 15 seconds. The default is every 1 minute.
# scrape_timeout is set to the global default (10s).
## Alertmanager configuration
alerting:
alertmanagers:
- static_configs:
- targets:
# - alertmanager:9093
## Load rules once and periodically evaluate them according to the global 'evaluation_interval'.
rule_files:
# - "first_rules.yml"
# - "second_rules.yml"
## A scrape configuration containing exactly one endpoint to scrape:
## Here it's Prometheus itself.
scrape_configs:
# The job name is added as a label `job=<job_name>` to any timeseries scraped from this config.
- job_name: 'prometheus'
# metrics_path defaults to '/metrics'
# scheme defaults to 'http'.
static_configs:
- targets: ['localhost:9090']
💡ENV II UNITの値をPrometheusサーバーに送信できました。
方法2:ENV II UNITの値をPrometheusの/metricsエンドポイントで公開する
次の方法はM5Core2でWebサーバーを起動し、/metricsで公開します。こちらはPrometheusに特化したライブラリーは不要です。 また、Prometheus側から探しやすいように、mDNSを使用して名前解決ができるようにします。
必要なライブラリー
ENV II UNIT関連のみです
- m5stack/M5Unit-ENV
- adafruit/Adafruit Unified Sensor
- adafruit/Adafruit BMP280 Library
[env:m5stack-core2]
platform = espressif32
board = m5stack-core2
framework = arduino
lib_deps =
m5stack/M5Core2
HttpClient
WiFi
adafruit/Adafruit Unified Sensor@^1.1.6
adafruit/Adafruit BMP280 Library@^2.6.3
m5stack/M5Unit-ENV@^0.0.5
monitor_speed = 115200
プログラムの解説
- Include
ENV II UNIT関連の他に、WebサーバーとmDNSに必要なものをInclude
#include <M5Core2.h>
#include <WiFi.h>
#include <WebServer.h>
#include <ESPmDNS.h>
#include "M5_ENV.h"
#include "Adafruit_Sensor.h"
#include <Adafruit_BMP280.h>
- 初期化
Webサーバーを定義します。9090
は使用するポート番号です。
WebServer server(9090);
SHT3X sht30;
Adafruit_BMP280 bme;
float tmp = 0.0;
float hum = 0.0;
float pressure = 0.0;
- setup (mDNS設定)
mDNSを有効化し、m5core2.local
で検索できるようにするにはsetupで以下の処理を行います。
if (MDNS.begin("m5core2"))
{
Serial.println("MDNS responder started");
}
- setup (Webサーバー)
/metrics
へリクエストが来たら、handleRoot
メソッドが呼び出されるように指定した後、begin()します。
server.on("/metrics", handleRoot);
server.begin();
handleRootメソッドでは、ENV II UNITの値を取得し、HTTPレスポンスを作成し返却します。
pressure = bme.readPressure() / 100;
sht30.get();
tmp = sht30.cTemp;
hum = sht30.humidity / 100;
String metrics_p = "m5core2_pressure " + String(pressure);
String metrics_t = "m5core2_temperature " + String(tmp);
String metrics_h = "m5core2_humidity " + String(hum);
String metrics = metrics_p + "\n" + metrics_t + "\n" + metrics_h + "\n";
server.send(200, "text/plain", metrics);
Prometheusサーバーを起動(mDNSで名前解決させ、Prometheusからscrapeさせる)
オフィシャルのDocker Imageでは、mDNSが動作しないようですので、docker run時にホスト側で解決したIPアドレスを渡すことにしました。(ホスト側にavahi-utils
が必要となります)
docker run --rm \
-p 9090:9090 \
-v `pwd`/prometheus.yml:/prometheus/prometheus.yml \
--add-host "m5core2.local:`avahi-resolve-host-name -n m5core2.local | awk '{print $2}'`" \
prom/prometheus
- prometheus.yml
## my global config
global:
scrape_interval: 15s # Set the scrape interval to every 15 seconds. Default is every 1 minute.
evaluation_interval: 15s # Evaluate rules every 15 seconds. The default is every 1 minute.
# scrape_timeout is set to the global default (10s).
## Alertmanager configuration
alerting:
alertmanagers:
- static_configs:
- targets:
# - alertmanager:9093
## Load rules once and periodically evaluate them according to the global 'evaluation_interval'.
rule_files:
# - "first_rules.yml"
# - "second_rules.yml"
## A scrape configuration containing exactly one endpoint to scrape:
## Here it's Prometheus itself.
scrape_configs:
# The job name is added as a label `job=<job_name>` to any timeseries scraped from this config.
- job_name: 'prometheus'
# metrics_path defaults to '/metrics'
# scheme defaults to 'http'.
static_configs:
- targets: ['localhost:9090']
- job_name: 'prometheus_m5core2'
static_configs:
- targets: ['m5core2.local:9090']
💡ENV II UNITの値をPrometheusサーバーから取得できました。
参考
https://create.arduino.cc/projecthub/425297/webservers-on-esp32-edffef https://github.com/m5stack/M5Core2/blob/master/examples/Advanced/WIFI/mDNS_Find/mDNS_Find.ino https://github.com/m5stack/M5Unit-ENV/blob/master/examples/Unit_ENVII_M5Core2/Unit_ENVII_M5Core2.ino