はじめに
自宅でZabbixを動かしており、サーバーやネットワーク機器の状態を監視しています。
今回は新しく M5Stack ATOM Lite と ENV III を使い、室内の温度・湿度・気圧を取得してZabbixへ送信してみます。
第1回では、
- ATOM Lite + ENV IIIで温度・湿度・気圧を取得する
- ATOM LiteをWi-Fiに接続する
- 取得した値をZabbixへ送信する
- Zabbixのダッシュボードでグラフ化する
ところまでを行います。
モチベーション
自宅ではRaspberry Pi上でZabbixを稼働させており、サーバーやネットワーク機器などを監視しています。
せっかく24時間動いている監視環境があるので、IT機器の状態だけでなく、実世界の情報もZabbixに取り込んでみたいと思ったのが今回のきっかけです。
その第一歩として、身近で変化が分かりやすい「室内環境」を監視してみることにしました。
市販の温湿度計を置くだけでも現在の温度や湿度を知ることはできます。しかし、自分でセンサーから値を取得し、
センサー → Wi-Fi → Zabbix → グラフ
という一連の仕組みを作れば、IoT機器と既存の監視システムを組み合わせる方法を実際に試すことができます。
また、一度Zabbixに取り込んでしまえば、単に現在値を見るだけでなく、
- 長期間の変化を記録する
- 一定以上の温度や湿度を検知する
- センサーからデータが届かなくなったことを検知する
- 他のサーバー・ネットワーク監視と同じ画面で確認する
といったこともできそうです。
今後は屋外の温湿度測定などにも発展させたいと考えています。
使用したもの
今回使用したものは以下です。
- M5Stack ATOM Lite
- M5Stack ENV III Unit
- Groveケーブル
- USB Type-Cケーブル
- Arduino IDE
- Raspberry Pi(Zabbix Server稼働中)
- Wi-Fi環境
ENV IIIでは以下の値を取得できます。
- 温度
- 湿度
- 気圧
ATOM LiteとENV IIIはGroveケーブルで接続します。
全体構成
今回作る構成は以下です。
ENV III
│
│ I2C
▼
ATOM Lite
│
│ Wi-Fi
▼
自宅LAN
│
▼
Raspberry Pi
Zabbix Server
│
▼
Zabbix Dashboard
ATOM LiteがENV IIIから値を取得し、Wi-Fi経由でZabbix Serverへ送信します。
Arduino IDEの準備
Arduino IDEにM5Stackのボード定義を追加します。
Boards Managerで M5Stack を検索し、
M5Stack by M5Stack official
をインストールしました。
その後、ATOM LiteをUSBでPCへ接続し、ボードとして、
M5Atom
を選択します。
M5Unit-ENVライブラリをインストールする
次にArduino IDEのLibrary Managerから、
M5Unit-ENV
を検索します。
今回は、
M5Unit-ENV by M5Stack
をインストールしました。
これでENV IIIのSHT30(温湿度)とQMP6988(気圧)をArduinoから利用できるようになります。
まずATOM Liteへの書き込みを確認する
いきなりセンサーやZabbixとの通信を実装するのではなく、まずATOM Liteへ正常にプログラムを書き込めることを確認しました。
簡単なLED点滅プログラムを書き込みます。
#include <M5Atom.h>
void setup() {
M5.begin(true, false, true);
}
void loop() {
M5.dis.drawpix(0, CRGB::Green);
delay(1000);
M5.dis.drawpix(0, CRGB::Black);
delay(1000);
}
LEDが点滅すれば、Arduino IDEからATOM Liteへの書き込みは成功です。
Upload Speedで少しハマった
最初は高速なUpload Speedで書き込もうとしたところ、途中で esptool のエラーになりました。
私の環境ではArduino IDEの、
Tools
→ Upload Speed
を、
115200
に変更すると正常に書き込めました。
環境によると思いますが、書き込み時にエラーになる場合はUpload Speedを下げてみるとよさそうです。
ENV IIIから値を取得する
次にENV IIIをATOM LiteのGroveポートへ接続します。
まずはZabbixとの通信を考えず、センサーから値を取得できることを確認しました。
正常に取得できると、Arduino IDEのSerial Monitorに以下のように表示されます。
Temperature: 26.51 C
Humidity: 53.96 %
Pressure: 1010.33 hPa
--------------------
温度・湿度・気圧の3つが取得できました。
ここまで確認できたら、次にWi-Fi接続を追加します。
Wi-Fiの認証情報をsecrets.hへ分離する
Wi-FiのSSIDとパスワードをメインのソースコードへ直接書くのは避け、secrets.h に分離しました。
Arduino IDEでメインの .ino と同じスケッチ内に secrets.h を作成します。
#pragma once
const char* WIFI_SSID = "YOUR_WIFI_SSID";
const char* WIFI_PASSWORD = "YOUR_WIFI_PASSWORD";
実際には自宅Wi-FiのSSIDとパスワードを設定します。
GitHubやQiitaなどへソースコードを公開する場合、実際の secrets.h を誤って公開しないよう注意が必要です。
Wi-Fiへ接続する
ATOM LiteをWi-Fiへ接続します。
正常に接続できるとSerial Monitorに以下のように表示されます。
Wi-Fi connected.
IP address: 192.168.10.50
RSSI: -63 dBm
※IPアドレスは記事掲載用のダミーです。
ESP32自身がWi-Fiに接続できることを確認できました。
Zabbix Serverをホスト名で指定する
Zabbix ServerはRaspberry Pi上で動作しています。
ATOM LiteのプログラムへZabbix ServerのIPアドレスを直接書くこともできますが、DHCPでRaspberry PiのIPアドレスが変わった場合にプログラムの修正が必要になります。
そこで今回はmDNSを利用し、
zabbix-pi.local
というホスト名からZabbix Serverを名前解決するようにしました。
IPAddress zabbixIP;
if (WiFi.hostByName("zabbix-pi.local", zabbixIP)) {
Serial.print("Zabbix server resolved: ");
Serial.println(zabbixIP);
} else {
Serial.println("Failed to resolve Zabbix server");
}
正常に名前解決できると、
Zabbix server resolved: 192.168.10.10
のように表示されます。
※ホスト名・IPアドレスは記事掲載用のダミーです。
Zabbix Serverの10051/TCPをLAN側で待ち受ける
ATOM LiteからZabbix Serverへ接続しようとしたところ、Zabbix Serverの10051/TCPがlocalhostでしか待ち受けていませんでした。
確認します。
sudo ss -lntp | grep 10051
当初は、
127.0.0.1:10051
だけでした。
Zabbix Serverの設定を確認します。
sudo grep -n "^ListenIP" /etc/zabbix/zabbix_server.conf
以下のようになっていました。
ListenIP=127.0.0.1
今回はLAN内のATOM Liteから接続する必要があるため、Zabbix ServerのLAN側アドレスも追加します。
ListenIP=127.0.0.1,192.168.10.10
※ 192.168.10.10 は記事掲載用のダミーです。
設定後、Zabbix Serverを再起動します。
sudo systemctl restart zabbix-server
再度確認します。
sudo ss -lntp | grep 10051
以下の2つで待ち受けていればOKです。
127.0.0.1:10051
192.168.10.10:10051
今回はZabbixの10051/TCPをインターネットへ公開せず、自宅LAN内からのみ利用する構成にしています。
Zabbixにホストを作成する
次にZabbix側でATOM Lite用のホストを作成します。
今回は以下にしました。
ホスト名:M5Atom-Room
表示名:室内温湿度計
ホストグループは環境に合わせて任意のものを設定します。
今回はATOM LiteからZabbix Serverへ値を送信するため、通常のZabbix Agentは使用しません。
Zabbixトラッパーのアイテムを作成する
ホストに以下の3つのアイテムを作成します。
| 名前 | タイプ | キー | データ型 | 単位 |
|---|---|---|---|---|
| 室内温度 | Zabbixトラッパー | room.temperature |
数値(浮動小数) | °C |
| 室内湿度 | Zabbixトラッパー | room.humidity |
数値(浮動小数) | % |
| 室内気圧 | Zabbixトラッパー | room.pressure |
数値(浮動小数) | !hPa |
気圧については、最初は単位を hPa としていました。
しかしZabbixの単位の自動スケーリングによって、
1010 hPa
が、
1.01 KhPa
のような表示になったため、単位を、
!hPa
としました。
! を付けることで自動スケーリングを抑止し、
1010.33 hPa
のように表示できます。
ATOM Liteへ書き込んだプログラム
最終的にATOM Liteでは、
- ENV IIIを初期化
- Wi-Fiへ接続
- Zabbix Serverを名前解決
- 温度・湿度・気圧を取得
- Zabbix Serverの10051/TCPへ送信
- 60秒待機
- 繰り返し
という処理を行います。
secrets.h
#pragma once
const char* WIFI_SSID = "YOUR_WIFI_SSID";
const char* WIFI_PASSWORD = "YOUR_WIFI_PASSWORD";
M5Atom_ENV_Zabbix.ino
#include <M5Atom.h>
#include <Wire.h>
#include <WiFi.h>
#include "M5UnitENV.h"
#include "secrets.h"
SHT3X sht3x;
QMP6988 qmp;
const char* ZABBIX_HOST = "M5Atom-Room";
const char* ZABBIX_SERVER = "zabbix-pi.local";
IPAddress zabbixIP;
void setup() {
M5.begin(true, false, true);
Serial.begin(115200);
// ATOM Lite Grove Port
const int SDA_PIN = 26;
const int SCL_PIN = 32;
// QMP6988
if (!qmp.begin(&Wire, QMP6988_SLAVE_ADDRESS_L,
SDA_PIN, SCL_PIN, 400000U)) {
Serial.println("Couldn't find QMP6988");
while (1) {
delay(1000);
}
}
// SHT3X
if (!sht3x.begin(&Wire, SHT3X_I2C_ADDR,
SDA_PIN, SCL_PIN, 400000U)) {
Serial.println("Couldn't find SHT3X");
while (1) {
delay(1000);
}
}
// Wi-Fi
WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
Serial.print("Connecting to Wi-Fi");
while (WiFi.status() != WL_CONNECTED) {
Serial.print(".");
delay(500);
}
Serial.println();
Serial.println("Wi-Fi connected.");
Serial.print("IP address: ");
Serial.println(WiFi.localIP());
Serial.print("RSSI: ");
Serial.print(WiFi.RSSI());
Serial.println(" dBm");
// Zabbix Serverを名前解決
if (WiFi.hostByName(ZABBIX_SERVER, zabbixIP)) {
Serial.print("Zabbix server resolved: ");
Serial.println(zabbixIP);
} else {
Serial.println("Failed to resolve Zabbix server");
}
}
void sendToZabbix(float temperature,
float humidity,
float pressure) {
WiFiClient client;
Serial.println("Sending values to Zabbix...");
Serial.print("Connecting to Zabbix ");
Serial.print(zabbixIP);
Serial.println(":10051");
if (!client.connect(zabbixIP, 10051)) {
Serial.println("Connection to Zabbix failed");
return;
}
String json =
"{\"request\":\"sender data\",\"data\":["
"{\"host\":\"" + String(ZABBIX_HOST) +
"\",\"key\":\"room.temperature\",\"value\":\"" +
String(temperature, 2) + "\"},"
"{\"host\":\"" + String(ZABBIX_HOST) +
"\",\"key\":\"room.humidity\",\"value\":\"" +
String(humidity, 2) + "\"},"
"{\"host\":\"" + String(ZABBIX_HOST) +
"\",\"key\":\"room.pressure\",\"value\":\"" +
String(pressure, 2) + "\"}"
"]}";
uint64_t len = json.length();
// Zabbix Sender Protocol
client.write((const uint8_t*)"ZBXD\1", 5);
client.write((uint8_t*)&len, 8);
client.print(json);
Serial.println("Sent to Zabbix:");
Serial.println(json);
// Zabbixからのレスポンス待ち
unsigned long timeout = millis();
while (!client.available()) {
if (millis() - timeout > 5000) {
Serial.println("Zabbix response timeout");
client.stop();
return;
}
}
// Zabbixレスポンスのヘッダ
uint8_t header[13];
if (client.readBytes(header, 13) != 13) {
Serial.println("Invalid Zabbix response");
client.stop();
return;
}
String response;
while (client.available()) {
response += (char)client.read();
}
Serial.println("Zabbix response:");
Serial.println(response);
if (response.indexOf("\"response\":\"success\"") >= 0) {
Serial.println("Zabbix send SUCCESS");
}
client.stop();
}
void loop() {
if (sht3x.update()) {
Serial.println("Failed to read SHT3X");
delay(5000);
return;
}
float temperature = sht3x.cTemp;
float humidity = sht3x.humidity;
float pressure = qmp.calcPressure() / 100.0;
Serial.print("Temperature: ");
Serial.print(temperature);
Serial.println(" C");
Serial.print("Humidity: ");
Serial.print(humidity);
Serial.println(" %");
Serial.print("Pressure: ");
Serial.print(pressure);
Serial.println(" hPa");
// Wi-Fiが切断されていた場合
if (WiFi.status() != WL_CONNECTED) {
WiFi.reconnect();
}
// Zabbix Serverを名前解決
if (WiFi.hostByName(ZABBIX_SERVER, zabbixIP)) {
sendToZabbix(temperature, humidity, pressure);
} else {
Serial.println("Failed to resolve Zabbix server");
}
Serial.println("--------------------");
// 60秒間隔
delay(60000);
}
ホスト名、Wi-Fi情報などは記事掲載用に変更しています。
Zabbixへの送信を確認する
プログラムを書き込んでSerial Monitorを確認します。
正常に動作すると以下のようになります。
Wi-Fi connected.
IP address: 192.168.10.50
RSSI: -65 dBm
Zabbix server resolved: 192.168.10.10
Temperature: 25.19 C
Humidity: 55.59 %
Pressure: 1010.49 hPa
Sending values to Zabbix...
Connecting to Zabbix 192.168.10.10:10051
Zabbix response:
{"response":"success","info":"processed: 3; failed: 0; total: 3"}
Zabbix send SUCCESS
ここで、
processed: 3
failed: 0
total: 3
となっていれば、温度・湿度・気圧の3項目すべてをZabbixが受信しています。
Zabbixの最新データを確認する
Zabbixの、
監視データ
→ 最新データ
→ 室内温湿度計
を確認します。
以下の3項目が更新されるようになりました。
室内温度 25.31 °C
室内湿度 56.46 %
室内気圧 1010.33 hPa
約60秒間隔でATOM Liteから送信しているため、Zabbix側の最新データも定期的に更新されます。
グラフを作成する
今回は2種類のグラフを作成しました。
室内 温度・湿度
温度と湿度を1枚のグラフにします。
ただし単位が異なるので、
室内温度 → 左Y軸
室内湿度 → 右Y軸
としました。
これで左側に °C、右側に % が表示されます。
室内 気圧
気圧は約1000hPaと値のスケールが大きく異なるため、温湿度とは別グラフにしました。
室内気圧
のみを表示します。
ダッシュボードへ配置する
最後に作成したグラフをZabbixのダッシュボードへ配置しました。
ウィジェット追加から、
グラフ(クラシック)
を選択します。
以下の2つを配置しました。
室内 温度・湿度
室内 気圧
最終的に、
┌──────────────────────┬──────────────────────┐
│ 室内 温度・湿度 │ 室内 気圧 │
│ │ │
│ 温度 │ 気圧 │
│ 湿度 │ │
│ ~グラフ~ │ ~グラフ~ │
│ │ │
└──────────────────────┴──────────────────────┘
という形で、Zabbixのダッシュボードから室内環境を確認できるようになりました。
データ取得を開始した直後は、ダッシュボードの表示期間が長いと「データなし」のように見えることがあります。
その場合は表示期間を、
過去1時間
などにすると確認しやすくなります。
PCから外しても動作する
Arduino IDEはATOM Liteへプログラムを書き込むために使用しているだけなので、運用時にPCは必要ありません。
プログラムを書き込んだ後はPCからUSBケーブルを外し、USB電源アダプターなどから給電すれば、ATOM Lite単体で動作します。
USB電源
│
▼
ATOM Lite + ENV III
│
│ Wi-Fi
▼
Zabbix Server
電源を入れるとプログラムが最初から実行され、Wi-Fiへ接続してZabbixへの送信を再開します。
設置場所ではWi-Fiの電波が十分届いているか確認する必要があります。
まとめ
今回はM5Stack ATOM LiteとENV IIIを使って、
ENV III
↓
ATOM Lite
↓ Wi-Fi
Zabbix Server
↓
Zabbix Dashboard
という室内環境監視を作りました。
ENV IIIから取得した、
- 温度
- 湿度
- 気圧
を60秒間隔でZabbixへ送り、履歴として保存・グラフ化できるようになりました。
小さなATOM Liteから取得した実世界のデータが、普段サーバー監視に使っているZabbixへ流れていくのはなかなか面白いです。
Zabbixに取り込めたことで、ここからさらに監視らしい機能を追加できます。
今後やりたいこと
現状は「値を取得して記録する」ところまでなので、次は以下を試したいと思います。
- 一定時間データが来なかった場合に障害として検知する
- 室温が一定以上になった場合に警告する
- 湿度が一定以上・以下になった場合に警告する
- ATOM LiteのWi-Fi RSSIをZabbixへ送る
- 屋外にもセンサーを設置する
- 長期間の温湿度・気圧の変化を記録する
特に次回は、**「ATOM Liteから一定時間データが届かなかった場合にZabbixで障害として検知する」**ところをやってみたいと思います。