IoTセンサーデータをLEDパネルにリアルタイム表示!ESP32とMQTTで作る可視化システム
はじめに
以前、IoT Printer なる奇妙奇天烈なソリューションという記事で、「デジタル × アナログ」の融合をご紹介してきました。今回は、同じくアナログ的な表示方法でありながら、よりモダンな「LED パネル表示」にチャレンジします。
IoT という文脈では、データをスマホアプリや Web ブラウザで表示するというのが連想されるのではないでしょうか?しかし、もっと身近な場所でパッと見てすぐ分かるディスプレイがあったら便利ですよね。
今回は、ESP32 マイコンとLED マトリックスパネルを使って、LoRaWAN 対応温度湿度センサーデバイスELmote® EM-ELHT01のデータをリアルタイムで表示するシステムを作ってみましたので、簡単にご紹介したいと思います。
まずは、デモ動画をご覧ください。
このプロジェクトで実現できること
このシステムを作ると、以下のようなことができるようになります。
2. リアルタイム更新:データが更新されると、すぐにディスプレイに反映
3. 色分け表示:データの種類ごとに色を変えて、見やすく表示
4. 時刻表示:現在時刻も併せて表示し、データの新しさが分かる
5. コンパクト設計:デスクに置けるサイズで、場所を取らない
使用する機器について
LoRaWAN 対応 温度湿度センサーデバイス
温度湿度センサーデバイスとして、LoRaWAN 対応温度湿度センサーデバイスELmote® EM-ELHT01を利用します。
本製品は、温度・湿度センサーが本体に内蔵されています。さらに、それ以外のセンサー(温度、照度、PIR など)を外付けすることが可能です。 また、データログ機能が備わっており、LoRaWAN 通信エリア外でセンシングされたデータを、後から LoRaWAN 通信経由で取得することができます。
ESP32-LEDPRO-R2 マイコンボード
今回、LED パネルを利用することを目的に開発されたESP32-LEDPRO-R2というマイコンボードを利用します。
ESP32-LEDPRO-R2 の特徴:
- ESP32 ベースで、インターネット接続が簡単
- 温度・湿度センサー(AHT21)を内蔵
- 加速度センサー(KXTJ3)も内蔵
- LED パネル制御に最適化された設計
- 16MB の大容量フラッシュメモリ搭載
P4 64x32 LED マトリックスパネル
表示用には、64x32 の LED マトリックスパネルを使用します。 これは、パネルを連結していくことで、大きなディスプレイにもなるもののようです。
LED パネルの特徴:
- フルカラー表示対応(1677 万色)
- 屋内での視認性が良好
- HUB75 インターフェースで接続が簡単
- 比較的安価で入手可能
システムの全体像
システム全体の流れは以下のようになります。
[遠隔地のLoRaWANセンサー]
↓
[The Things Stack (LoRaWANサーバー)]
↓
[MQTT ブローカー]
↓
[ESP32-LEDPRO-R2] ← [内蔵センサー]
↓
[LEDマトリックスパネル表示]
簡単に言うと、「遠くにあるセンサーのデータをインターネット経由で取得し、手元のセンサーデータと一緒に LED パネルに表示する」というシステムです。
開発環境の準備
PlatformIO の使用
今回は、ファームウェア開発のために Arduino IDE の代わりに、PlatformIOを使用しました。ArduinoIDE よりも高機能で、ライブラリ管理が簡単です。
必要なライブラリ:
- ESP32 HUB75 LED MATRIX PANEL:LED パネル制御用
- Adafruit GFX Library:グラフィック描画用
- Adafruit AHTX0:温度湿度センサー用
- PubSubClient:MQTT 通信用
- ArduinoJson:JSON データ解析用
- NTPClient:時刻同期用
主要機能の実装
1. WiFi 接続(複数ネットワーク対応)
自宅とオフィスなど、複数の WiFi ネットワークに自動で接続できるよう設定します。
// WiFi認証情報の定義
const char* WIFI_SSID_HOME = "HomeNetwork";
const char* WIFI_PASSWORD_HOME = "homepassword";
const char* WIFI_SSID_OFFICE = "OfficeNetwork";
const char* WIFI_PASSWORD_OFFICE = "officepassword";
void setupWiFi() {
// 1つ目のWiFiに接続を試す
WiFi.begin(WIFI_SSID_HOME, WIFI_PASSWORD_HOME);
delay(5000);
// 接続できなかったら2つ目を試す
if (WiFi.status() != WL_CONNECTED) {
WiFi.begin(WIFI_SSID_OFFICE, WIFI_PASSWORD_OFFICE);
delay(5000);
}
if (WiFi.status() == WL_CONNECTED) {
Serial.println("WiFi接続完了!");
Serial.print("IPアドレス: ");
Serial.println(WiFi.localIP());
}
}
2. The Things Stack MQTT 接続
MQTTを使って、遠隔地のセンサーデータを受信します。MQTT は「メッセージキューイング」の仕組みで、IoT でよく使われる通信方式です。
The Things Stack は、LoRaWAN ネットワークサーバーとして、受信したセンサーデータを MQTT 経由で配信する機能を提供しています。
// MQTT設定
const char* mqtt_server = "eu1.cloud.thethings.network"; //利用しているTTS Sandboxのクラスタで置き換え
const char* mqtt_user = "{application-id}@ttn"; // 実際のアプリケーションIDに置き換え
const char* mqtt_password = "your-api-key"; // APIキーを設定
const char* mqtt_topic_uplink = "v3/{application-id}/devices/{device-id}/up"; // 実際のアプリケーションIDに置き換え
// グローバル変数
WiFiClient espClient;
PubSubClient mqttClient(espClient);
float remote_temperature = 0.0;
float remote_humidity = 0.0;
bool mqtt_data_received = false;
// MQTT接続設定
void setupMQTT() {
mqttClient.setServer(mqtt_server, 1883);
mqttClient.setCallback(callback);
mqttClient.setBufferSize(4096); // The Things Stackの大きなJSONデータに対応
}
// MQTT接続処理
void connectMQTT() {
while (!mqttClient.connected()) {
Serial.print("The Things Stack MQTTサーバーに接続中...");
// クライアントIDは一意である必要があります
String clientId = "ESP32-" + String(random(0xffff), HEX);
if (mqttClient.connect(clientId.c_str(), mqtt_user, mqtt_password)) {
Serial.println("接続成功!");
// uplinkトピックをサブスクライブ
if (mqttClient.subscribe(mqtt_topic_uplink)) {
Serial.print("トピックをサブスクライブしました: ");
Serial.println(mqtt_topic_uplink);
}
} else {
Serial.print("接続失敗, rc=");
Serial.print(mqttClient.state());
Serial.println(" 5秒後に再試行...");
delay(5000);
}
}
}
// コールバック関数でJSONデータを解析
void callback(char* topic, byte* payload, unsigned int length) {
Serial.print("メッセージ受信 [");
Serial.print(topic);
Serial.println("]");
// JSONデータを解析
DynamicJsonDocument doc(4096);
DeserializationError error = deserializeJson(doc, payload, length);
if (error) {
Serial.print("JSONパースエラー: ");
Serial.println(error.c_str());
return;
}
// The Things Stackのデコード済みペイロードからデータを取得
if (doc["uplink_message"]["decoded_payload"]["TempC_SHT"]) {
remote_temperature = doc["uplink_message"]["decoded_payload"]["TempC_SHT"];
remote_humidity = doc["uplink_message"]["decoded_payload"]["Hum_SHT"];
mqtt_data_received = true;
Serial.println("新しいセンサーデータを受信:");
Serial.print("温度: ");
Serial.print(remote_temperature);
Serial.print("℃, 湿度: ");
Serial.print(remote_humidity);
Serial.println("%");
}
}
3. 正確な時刻の取得
インターネット経由で正確な時刻を取得し、日本時間で表示します。
// 日本の時刻サーバーから時刻を取得
WiFiUDP ntpUDP;
NTPClient timeClient(ntpUDP, "ntp.nict.jp", 9 * 3600, 60000);
void setupTime() {
timeClient.begin();
timeClient.update();
Serial.println("時刻同期完了");
}
String getFormattedDate() {
time_t rawTime = timeClient.getEpochTime();
struct tm * timeInfo = localtime(&rawTime);
char buffer[11];
strftime(buffer, sizeof(buffer), "%Y/%m/%d", timeInfo);
return String(buffer);
}
4. LED パネルへの表示
取得したデータを色分けして、見やすく LED パネルに表示します。
// 色の定義
uint16_t COLOR_ORANGE = dma_display->color565(255, 165, 0);
uint16_t COLOR_YELLOW = dma_display->color565(255, 255, 0);
uint16_t COLOR_CYAN = dma_display->color565(0, 255, 255);
uint16_t COLOR_WHITE = dma_display->color565(255, 255, 255);
void updateLEDDisplay() {
// 画面をクリア
dma_display->clearScreen();
// 1行目:日付をオレンジ色で表示
dma_display->setTextColor(COLOR_ORANGE);
dma_display->setCursor(0, 0);
dma_display->print(getFormattedDate());
// 2行目:時刻を黄色で表示
dma_display->setTextColor(COLOR_YELLOW);
dma_display->setCursor(0, 8);
dma_display->print(timeClient.getFormattedTime());
// 3行目:遠隔センサーをシアン色で表示
dma_display->setTextColor(COLOR_CYAN);
dma_display->setCursor(0, 16);
dma_display->print("Remote:");
dma_display->print(remote_temperature, 1);
dma_display->print("C ");
dma_display->print(remote_humidity, 0);
dma_display->print("%");
// データ受信状態の表示
if (mqtt_data_received) {
dma_display->print(" *"); // 新しいデータを受信した印
mqtt_data_received = false; // フラグをリセット
}
// 4行目:ローカルセンサーを白色で表示
dma_display->setTextColor(COLOR_WHITE);
dma_display->setCursor(0, 24);
dma_display->print("Local:");
dma_display->print(localTemp, 1);
dma_display->print("C ");
dma_display->print(localHumi, 0);
dma_display->print("%");
}
実際の表示例
LED パネルには、以下のように色分けされた情報が表示されます。
2025/08/18 ← オレンジ色(日付)
12:10:40 ← 黄色(現在時刻)
Remote:27.6C 46% ← シアン色(遠隔センサー)
Local:29.2C 39% ← 白色(手元のセンサー)
色分けの意味
- オレンジ色:時間情報(日付)- 目立つ色で時間軸を明確に
- 黄色:時間情報(時刻)- 更新頻度が高い情報
- シアン色:遠隔データ - 涼しげな色で「遠く」をイメージ
- 白色:ローカルデータ - 基本色で「手元」を表現
色分けすることで、パッと見てどのデータか分かるようになっています。
システムの動作フロー
システムは以下の流れで動作します。
1. WiFi に自動接続
2. インターネットから正確な時刻を取得
3. MQTT サーバーに接続
4. LED パネルの初期化
1. 手元のセンサーから温度・湿度を読み取り
2. MQTT で遠隔センサーのデータ受信を待機
3. LED パネルの表示を更新(0.25 秒間隔)
4. 時刻を再同期(1 分間隔)
応用アイデア
このシステムをベースに、様々な用途に応用できます。
- リビングに置いて室内外の温度差を確認
- 子供部屋と寝室の環境監視
- ペットの居る部屋の温度管理
- 複数フロアの環境監視
- 会議室・校庭・体育館・教室などの温度把握・空調管理
- サーバールームの温度監視
- 温室とビニールハウスの同時監視
- 複数の畑の環境比較
- 屋内育苗と屋外栽培の環境差確認
まとめ
今回は、ESP32-LEDPRO-R2 と LED マトリックスパネルを使って、LoRaWAN 温度湿度センサーのデータをリアルタイム表示するシステムを作成しました。
このプロジェクトのポイント:
- 手軽に始められる:比較的安価な部品で構築可能
- 実用性が高い:複数拠点のデータを一箇所で確認できる
- 拡張性がある:センサーの種類や表示内容を簡単に追加可能
- 学習効果が高い:WiFi、MQTT、時刻同期など、IoT の基本技術を習得できる
前回のプリンタ出力では、データがアップリンクされる度にレシートに値が出力されました。今回は、PC モニターではなく、動的に更新される情報表示が可能な安価な LED パネルを活用しました。それぞれに適した用途があり、どちらも「デジタルデータの物理的な可視化」という点で共通しています。
皆さんも、身近な場所にこんな便利なディスプレイを設置してみませんか?