戻る

ESP32フィールド観測システム(温度・湿度・PIR・RSSI)
完全仕様書(コピペ再現版)

作成日: 2026-01-05

1. システム概要

本書は、ESP32(WROOM/DevKit系 または ESP32-C3)複数台をフィールドに設置し、DHT11(温度・湿度)とPIR(人感)を計測して、WiFi経由でRaspberry Pi 5のMosquitto(MQTT)へ送信、Node-RED Dashboard(/ui)でグラフ表示し、外付けSSDへCSV保存するシステムの「完全再現」手順です。

初心者が、手順どおりに環境を作り、コードをカット&ペーストして同じシステムを再現できるよう、配線・設定・コード・Node-REDフローJSONをすべて含めます。

2. 構成(全体アーキテクチャ)

データの流れ:センサー(DHT11/PIR)→ ESP32(WiFi)→ MQTT(Mosquitto)→ Node-RED → Dashboard(/ui)+ CSV保存(SSD)

トピック設計:

  ・sensors/<NODE>/dht11  : {node,temp,hum,pir,rssi}

  ・sensors/<NODE>/pir    : {node,pir,rssi}(変化時のみ)

NODEは field01 / field02 / field03 を想定します。複数台運用では NODE 名だけを変えて同じスケッチを書き込みます。

3. 必要機材

フィールド側(各ノード=1台あたり):ESP32(WROOM/DevKit系 または ESP32-C3)、DHT11、PIR、ジャンパ線、電源。

サーバ側:Raspberry Pi 5、Mosquitto、Node-RED(Dashboard)、外付けSSD(例:/media/PI_SHARE_500G)。

4. 配線(ESP32側)

本書では以下のGPIOを使用します(あなたの既存構成に合わせています)。

  ・DHT11 DATA → GPIO4

  ・PIR OUT    → GPIO2

VCC/GNDは各モジュール仕様に従い、ESP32のGNDは必ず共通にします。

5. 事前準備(Raspberry Pi:Mosquitto / Node-RED / SSD)

5.1 MQTT動作確認(例)

mosquitto_sub -h localhost -t “sensors/#” -v

5.2 SSD保存先フォルダの準備(例)

sudo mkdir -p /media/PI_SHARE_500G/sensor_logs
sudo chown -R $USER:$USER /media/PI_SHARE_500G/sensor_logs

6. Arduino IDE 設定(Windows 11)

6.1 ボードマネージャ:ESP32 by Espressif Systems をインストール

6.2 ライブラリ:PubSubClient、DHT sensor library(Adafruit)

6.3 ボード選択:WROOM/DevKitは該当するESP32 DevKit、C3はXIAO ESP32C3等(S3と混同しない)

7. ESP32 スケッチ(WiFi + MQTT:WROOM/DevKit用)

/*
  ===== フィールド観測ノード(WiFi + MQTT)安定版 =====
  – DHT11(温度・湿度)と PIR(人感)を読み取り、MQTTで送信します。
  – RSSI(WiFi受信感度, dBm)も同時に送ります。0 に近いほど強い(例:-50は強い、-90は弱い)。
  – 送信先トピック:
      sensors/<NODE>/dht11  : {node,temp,hum,pir,rssi}
      sensors/<NODE>/pir    : {node,pir,rssi}(変化時のみ)
  – 複数台運用:NODE 名だけ field01/02/03 に変えて焼きます。

/*
  ===== フィールド観測ノード(WiFi + MQTT)=====
*/

#include <WiFi.h>
#include <PubSubClient.h>
#include <DHT.h>

// --- 配線(GPIO番号で指定)---
#define DHTPIN  4
#define PIRPIN  2
#define DHTTYPE DHT11

// --- このノードの名前(field01/02/03 など)---
const char* NODE = "field01";

// --- WiFi ---
const char* WIFI_SSID = "YOUR_WIFI_SSID";
const char* WIFI_PASS = "YOUR_WIFI_PASSWORD";

// --- MQTT(あなたのRasPi)---
const char* MQTT_HOST = "192.168.50.150";
const int   MQTT_PORT = 1883;

WiFiClient espClient;
PubSubClient mqtt(espClient);
DHT dht(DHTPIN, DHTTYPE);

void connectWiFi() {
  WiFi.mode(WIFI_STA);
  WiFi.begin(WIFI_SSID, WIFI_PASS);

  Serial.print("WiFi connecting");
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println();
  Serial.print("WiFi connected. IP=");
  Serial.println(WiFi.localIP());
}

void connectMQTT() {
  while (!mqtt.connected()) {
    // ★clientIDを一意に(事故防止)
    String clientId = String(NODE) + "-" + String((uint32_t)ESP.getEfuseMac(), HEX);

    Serial.print("MQTT connecting as ");
    Serial.print(clientId);
    Serial.print(" ... ");

    if (mqtt.connect(clientId.c_str())) {
      Serial.println("OK");
    } else {
      Serial.print("FAIL rc=");
      Serial.println(mqtt.state());
      delay(1000);
    }
  }
}

void setup() {
  Serial.begin(115200);
  delay(200);

  // ★PIR安定化:まずはこれ推奨(合わなければ INPUT_PULLUP を試す)
  pinMode(PIRPIN, INPUT_PULLDOWN);

  dht.begin();

  connectWiFi();
  mqtt.setServer(MQTT_HOST, MQTT_PORT);
  connectMQTT();

  Serial.print("NODE="); Serial.println(NODE);
}

void loop() {
  // ★WiFi復帰(屋外運用で重要)
  if (WiFi.status() != WL_CONNECTED) connectWiFi();

  if (!mqtt.connected()) connectMQTT();
  mqtt.loop();

  static unsigned long lastDhtMs = 0;
  static int lastPir = -1;

  int pir = digitalRead(PIRPIN);
  int rssi = WiFi.RSSI();

  // PIR:変化時のみ
  if (pir != lastPir) {
    lastPir = pir;
    String topic = String("sensors/") + NODE + "/pir";
    String payload = String("{") +
      "\"node\":\"" + NODE + "\"," +
      "\"pir\":" + String(pir) + "," +
      "\"rssi\":" + String(rssi) +
    "}";

    mqtt.publish(topic.c_str(), payload.c_str());
  }

  // DHT:5秒ごと
  if (millis() - lastDhtMs > 5000) {
    lastDhtMs = millis();

    float t = dht.readTemperature();
    float h = dht.readHumidity();

    if (isnan(t) || isnan(h)) {
      Serial.println("DHT read failed");
      return;
    }

    String topic = String("sensors/") + NODE + "/dht11";
    String payload = String("{") +
      "\"node\":\"" + NODE + "\"," +
      "\"temp\":" + String(t, 1) + "," +
      "\"hum\":" + String(h, 1) + "," +
      "\"pir\":" + String(pir) + "," +
      "\"rssi\":" + String(rssi) +
    "}";

    mqtt.publish(topic.c_str(), payload.c_str());
    Serial.println(payload);
  }
}

2)ESP32-C3(例:Seeed Studio XIAO ESP32C3)

ESP32-C3 向け。基本コードはESP32と同じですが、ボードによって ‘D2/D3’ 表記がある場合でも内部はGPIO番号です。
例として、DHT=GPIO4, PIR=GPIO2で記載します。

/*
  ===== フィールド観測ノード(WiFi + MQTT)=====
  - DHT11(温度・湿度)と PIR(人感)を読み取り、MQTTで送信
  - RSSI(WiFi受信感度, dBm)も同時に送信
  - topics:
      sensors/<NODE>/dht11 : {node,temp,hum,pir,rssi}
      sensors/<NODE>/pir   : {node,pir,rssi}  ※変化時のみ
*/

#include <WiFi.h>
#include <PubSubClient.h>
#include <DHT.h>

// --- 配線(GPIO番号で指定)---
#define DHTPIN  D3   // D3 = GPIO4
#define PIRPIN  D2   // D2 = GPIO2
#define DHTTYPE DHT11

// --- ノード名(field01/field02/field03)---
const char* NODE = "field01";

// --- WiFi ---
const char* WIFI_SSID = "ASUS_76";
const char* WIFI_PASS = "59ygi7dkd@";

// --- MQTT(RasPi)---
const char* MQTT_HOST = "192.168.50.150";
const int   MQTT_PORT = 1883;

WiFiClient espClient;
PubSubClient mqtt(espClient);
DHT dht(DHTPIN, DHTTYPE);

// ---- WiFi接続(復帰できるように関数化)----
void connectWiFi() {
  WiFi.mode(WIFI_STA);
  WiFi.begin(WIFI_SSID, WIFI_PASS);

  Serial.print("WiFi connecting");
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println();
  Serial.print("WiFi connected. IP=");
  Serial.println(WiFi.localIP());
}

// ---- MQTT接続(clientID衝突防止)----
void connectMQTT() {
  while (!mqtt.connected()) {
    // ★複数台で確実にユニークにする(NODE + チップID)
    // ESP32-C3でも ESP.getEfuseMac() が使えます
    String clientId = String(NODE) + "-" + String((uint32_t)ESP.getEfuseMac(), HEX);

    Serial.print("MQTT connecting as ");
    Serial.print(clientId);
    Serial.print(" ... ");

    if (mqtt.connect(clientId.c_str())) {
      Serial.println("OK");
    } else {
      Serial.print("FAIL rc=");
      Serial.println(mqtt.state());
      delay(1000);
    }
  }
}

void setup() {
  Serial.begin(115200);
  delay(200);

  // ★PIR安定化:まずはこれ推奨(合わなければ INPUT_PULLUP を試す)
  pinMode(PIRPIN, INPUT_PULLDOWN);

  dht.begin();

  connectWiFi();
  mqtt.setServer(MQTT_HOST, MQTT_PORT);
  connectMQTT();

  Serial.print("NODE="); Serial.println(NODE);
}

void loop() {
  // ★WiFiが切れたら復帰(屋外運用で重要)
  if (WiFi.status() != WL_CONNECTED) {
    connectWiFi();
  }

  // MQTT再接続
  if (!mqtt.connected()) connectMQTT();
  mqtt.loop();

  static unsigned long lastDhtMs = 0;
  static int lastPir = -1;

  int pir = digitalRead(PIRPIN);
  int rssi = WiFi.RSSI();

  // 1) PIR:変化時のみ送信
  if (pir != lastPir) {
    lastPir = pir;

    String topic = String("sensors/") + NODE + "/pir";
    String payload = String("{") +
      "\"node\":\"" + NODE + "\"," +
      "\"pir\":" + String(pir) + "," +
      "\"rssi\":" + String(rssi) +
    "}";

    mqtt.publish(topic.c_str(), payload.c_str());
  }

  // 2) DHT:5秒ごと
  if (millis() - lastDhtMs > 5000) {
    lastDhtMs = millis();

    float t = dht.readTemperature();
    float h = dht.readHumidity();

    if (isnan(t) || isnan(h)) {
      Serial.println("DHT read failed");
      return;
    }

    String topic = String("sensors/") + NODE + "/dht11";
    String payload = String("{") +
      "\"node\":\"" + NODE + "\"," +
      "\"temp\":" + String(t, 1) + "," +
      "\"hum\":" + String(h, 1) + "," +
      "\"pir\":" + String(pir) + "," +
      "\"rssi\":" + String(rssi) +
    "}";

    mqtt.publish(topic.c_str(), payload.c_str());
    Serial.println(payload);
  }
}

9. Node-RED フローJSON(/ui + PIR安定化 + RSSI棒グラフ + セッションCSV)


インポート:Node-RED右上メニュー → Import → Clipboard → 下のJSONを貼り付け → Import → Deploy

Dashboard: http://<RasPiのIP>:1880/nodered/ui

使い方

  1. Node-RED画面右上メニュー → ImportClipboard
    下のJSONを貼り付け → Import
  2. Deploy
  3. Dashboard:http://<RasPiのIP>:1880/red/ui (あなたの環境に合わせること)

10. 保存データ(CSV)の確認方法

保存先(例)

/media/PI_SHARE_500G/sensor_logs/

基本確認(Raspberry Pi)

1) ファイル一覧(サイズと更新時刻)

   ls -lh /media/PI_SHARE_500G/sensor_logs

2) 追記されているか(末尾を表示)

   tail -n 5 /media/PI_SHARE_500G/sensor_logs/field01.csv

3) 先頭を表示(ヘッダがある場合や形式確認)

   head -n 5 /media/PI_SHARE_500G/sensor_logs/field01.csv

4) 行数(データ件数の目安)

   wc -l /media/PI_SHARE_500G/sensor_logs/field01.csv

5) 更新が止まっていないか(更新時刻の変化を見る)

   watch -n 2 ‘ls -lh /media/PI_SHARE_500G/sensor_logs’

Node-RED側の確認ポイント

・fileノードの警告(プロパティ未設定)が出ていないこと

・createDir=false の場合、事前に sensor_logs フォルダが存在すること

・書き込み権限:Node-RED実行ユーザがそのフォルダに書き込めること(例:ls -ld で確認)

11. CSV 1行データの読み方

例:

2026-01-03T08:01:29.080Z,field01,17.0,51.0,0,-90

この1行は、カンマ区切り(CSV)で次の順番です。

1) timestamp:日時(ISO 8601)。末尾のZはUTCを意味します。

2) node:ノード名(field01/field02/field03 など)。

3) temp:温度(℃)。DHT11は分解能が粗く、誤差も大きい点に注意。

4) hum:湿度(%)。同様に誤差やばらつきがあります。

5) pir:人感(0/1)。0=非検知、1=検知。環境ノイズで1が続く場合は設置・電源・配線・Pull設定、またはNode-RED側のRBE/Delay等で安定化します。

6) rssi:WiFi受信感度(dBm)。0に近いほど強い(例:-50は強い、-90は弱い)。

時刻の注意(UTC→日本時間)

Node-REDの例では new Date().toISOString() を使うためUTCになります。日本時間(JST=UTC+9)で見たい場合:

・CSVはUTCのまま保存し、解析時にJSTへ変換(推奨:後処理が楽)

・またはNode-RED側でJSTの文字列にして保存(例:functionノードでtoLocaleString(‘ja-JP’, { timeZone: ‘Asia/Tokyo’ }))

12. RSSI(受信感度)の目安

RSSIは WiFi の受信信号強度(dBm)です。数値は「0に近いほど強い」ことを意味します(例:-50dBmは強い、-90dBmは弱い)。

本システムでは、各ESP32がMQTT送信時点で WiFi.RSSI() を取得し、payloadに rssi として含めています。

目安(環境により変動します)

・-30 ~ -55 dBm:非常に良い(近距離/見通し良好)
・-56 ~ -67 dBm:良い(安定しやすい)
・-68 ~ -75 dBm:普通(運用可能だが遮蔽物で不安定になり得る)
・-76 ~ -82 dBm:弱め(遅延・再送・切断が出やすいことがある)
・-83 ~ -90 dBm:かなり弱い(途切れやすい。設置位置・アンテナ・中継検討)
・-91 dBm以下:限界付近(接続維持が難しいことが多い)

注意

・RSSIは瞬間値で揺れます。Node-REDのグラフで「傾向」を見てください(平均的にどの範囲にいるか)。
・屋外は天候・植生・人や車の動きで変動します。設置後に数時間~1日ログを取り、最悪値も確認します。
・RSSIが悪いのに通信が安定している場合もあります(再送やスループットの要求が低い場合)。逆もあります。

[
    {
        "id": "0bb5efaeb009f607",
        "type": "tab",
        "label": "WiFi MQTT DHT11 PIR RSSI (Direct)",
        "disabled": false,
        "info": ""
    },
    {
        "id": "7ce9febfdb39df7e",
        "type": "ui_switch",
        "z": "0bb5efaeb009f607",
        "name": "Record ON/OFF",
        "label": "記録(セッションCSV)",
        "tooltip": "ONでセッションファイル作成→保存開始 / OFFで保存停止",
        "group": "d890fb2cd2532e42",
        "order": 1,
        "width": 6,
        "height": 1,
        "passthru": true,
        "decouple": "false",
        "topic": "save",
        "style": "",
        "onvalue": "true",
        "onvalueType": "bool",
        "onicon": "",
        "oncolor": "",
        "offvalue": "false",
        "offvalueType": "bool",
        "officon": "",
        "offcolor": "",
        "animate": true,
        "x": 200,
        "y": 40,
        "wires": [
            [
                "7ec46caad5fc991f"
            ]
        ]
    },
    {
        "id": "5eb9b9a0cde1c535",
        "type": "ui_text",
        "z": "0bb5efaeb009f607",
        "group": "d890fb2cd2532e42",
        "order": 2,
        "width": 6,
        "height": 1,
        "name": "Session filename",
        "label": "保存ファイル",
        "format": "{{msg.payload}}",
        "layout": "row-spread",
        "x": 520,
        "y": 40,
        "wires": []
    },
    {
        "id": "7ec46caad5fc991f",
        "type": "function",
        "z": "0bb5efaeb009f607",
        "name": "Start/Stop session (create filename + header)",
        "func": "// UI switch: true=ON / false=OFF\nconst on = !!msg.payload;\nflow.set(\"saveEnabled\", on);\n\nif (!on) {\n  flow.set(\"saveFile\", null);\n  return [{ payload: \"(停止中)\" }, null];\n}\n\n// ON → セッションファイル名生成\nconst d = new Date();\nconst pad = n => String(n).padStart(2, \"0\");\nconst fname = `/media/PI_SHARE_500G/sensor_logs/session_${d.getFullYear()}${pad(d.getMonth()+1)}${pad(d.getDate())}_${pad(d.getHours())}${pad(d.getMinutes())}${pad(d.getSeconds())}.csv`;\n\nflow.set(\"saveFile\", fname);\n\n// ヘッダを書き込む(fileノードへ)\nconst headerMsg = {\n  filename: fname,\n  payload: \"timestamp,node,temp,hum,pir,rssi,linkAgeSec\\n\"\n};\n\n// UIに表示\nconst uiMsg = { payload: fname };\n\nreturn [uiMsg, headerMsg];",
        "outputs": 2,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 540,
        "y": 80,
        "wires": [
            [
                "5eb9b9a0cde1c535"
            ],
            [
                "c836fc9f41ab07b8"
            ]
        ]
    },
    {
        "id": "c4b785b6fe0a1ddb",
        "type": "mqtt in",
        "z": "0bb5efaeb009f607",
        "name": "MQTT IN sensors/+/dht11",
        "topic": "sensors/+/dht11",
        "qos": "0",
        "datatype": "auto",
        "broker": "mqtt_broker_local",
        "nl": false,
        "rap": true,
        "rh": 0,
        "inputs": 0,
        "x": 180,
        "y": 160,
        "wires": [
            [
                "9ec43c1ba4f0c075"
            ]
        ]
    },
    {
        "id": "2a57ba05046325ad",
        "type": "mqtt in",
        "z": "0bb5efaeb009f607",
        "name": "MQTT IN sensors/+/pir",
        "topic": "sensors/+/pir",
        "qos": "0",
        "datatype": "auto",
        "broker": "mqtt_broker_local",
        "nl": false,
        "rap": true,
        "rh": 0,
        "inputs": 0,
        "x": 170,
        "y": 220,
        "wires": [
            [
                "3cad5acdab48233c"
            ]
        ]
    },
    {
        "id": "9ec43c1ba4f0c075",
        "type": "json",
        "z": "0bb5efaeb009f607",
        "name": "JSON parse (dht11)",
        "property": "payload",
        "action": "",
        "pretty": false,
        "x": 410,
        "y": 160,
        "wires": [
            [
                "c8d1e69c861f5f5d",
                "4ea616ecf2208591",
                "24dd0282be683c6d"
            ]
        ]
    },
    {
        "id": "3cad5acdab48233c",
        "type": "json",
        "z": "0bb5efaeb009f607",
        "name": "JSON parse (pir)",
        "property": "payload",
        "action": "",
        "pretty": false,
        "x": 400,
        "y": 220,
        "wires": [
            [
                "1e286ef4f09b153e",
                "4ea616ecf2208591"
            ]
        ]
    },
    {
        "id": "c8d1e69c861f5f5d",
        "type": "function",
        "z": "0bb5efaeb009f607",
        "name": "Validate & Split (temp/hum/rssi/pir)",
        "func": "// msg.payload: { node, temp, hum, pir, rssi }\nconst p = msg.payload;\nif (!p || !p.node) return null;\n\nconst temp = Number(p.temp);\nconst hum  = Number(p.hum);\nconst pir  = Number(p.pir);\nconst rssi = Number(p.rssi);\n\nif (Number.isNaN(temp) || Number.isNaN(hum)) return null;\n\n// output1: temp, output2: hum, output3: rssi, output4: pir\nreturn [\n  { topic: p.node, payload: temp },\n  { topic: p.node, payload: hum  },\n  { topic: p.node, payload: rssi },\n  { topic: p.node, payload: pir  }\n];",
        "outputs": 4,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 700,
        "y": 160,
        "wires": [
            [
                "064335ecca504ccb"
            ],
            [
                "3465a1591bf153cb"
            ],
            [
                "b8782fbd6166b427"
            ],
            [
                "81cd58e386d41fce"
            ]
        ]
    },
    {
        "id": "1e286ef4f09b153e",
        "type": "function",
        "z": "0bb5efaeb009f607",
        "name": "Validate PIR event (topic=node, payload=pir)",
        "func": "// msg.payload: { node, pir, rssi? }\nconst p = msg.payload;\nif (!p || !p.node) return null;\n\nconst pir = Number(p.pir);\nif (Number.isNaN(pir)) return null;\n\nmsg.topic = p.node;\nmsg.payload = pir;\nreturn msg;",
        "outputs": 1,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 720,
        "y": 220,
        "wires": [
            [
                "f7d8443264e2faa7",
                "a53a2f38942ef6a5"
            ]
        ]
    },
    {
        "id": "f7d8443264e2faa7",
        "type": "rbe",
        "z": "0bb5efaeb009f607",
        "name": "PIR: Block unless value changes (RBE)",
        "func": "rbe",
        "gap": "",
        "start": "",
        "inout": "out",
        "property": "payload",
        "x": 1010,
        "y": 220,
        "wires": [
            [
                "a53a2f38942ef6a5"
            ]
        ]
    },
    {
        "id": "4ea616ecf2208591",
        "type": "function",
        "z": "0bb5efaeb009f607",
        "name": "LinkHealth: update lastSeen + emit age(sec)",
        "func": "// dht11/pir の受信が来たら lastSeen を更新し、\n// field01/02/03 の「最終受信からの経過秒」を出力します。\n\nconst now = Date.now();\n\n// どのノードの受信か判定\nconst p = msg.payload;\nconst nodeName = (p && p.node) ? p.node : msg.topic;\nif (!nodeName) return null;\n\nflow.set(`lastSeen_${nodeName}`, now);\n\nconst out = [];\n[\"field01\",\"field02\",\"field03\"].forEach(n => {\n  const last = flow.get(`lastSeen_${n}`);\n  const sec = last ? Math.floor((now - last) / 1000) : 999;\n  out.push({ topic: n, payload: sec });\n});\n\n// 1出力に複数メッセージとして返す\nreturn [out];",
        "outputs": 1,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 740,
        "y": 300,
        "wires": [
            [
                "bab6e4143c16f51e"
            ]
        ]
    },
    {
        "id": "24dd0282be683c6d",
        "type": "function",
        "z": "0bb5efaeb009f607",
        "name": "CSV: append (session file, if enabled)",
        "func": "// msg.payload: { node, temp, hum, pir, rssi }\n// 記録ONのときだけ、セッションCSVへ追記します。\n\nconst enabled = !!flow.get(\"saveEnabled\");\nif (!enabled) return null;\n\nconst fname = flow.get(\"saveFile\");\nif (!fname) return null;\n\nconst p = msg.payload;\nif (!p || !p.node) return null;\n\nconst ts = new Date().toISOString();\n\n// LinkAge(秒)も一緒に保存\nconst last = flow.get(`lastSeen_${p.node}`);\nconst ageSec = last ? Math.floor((Date.now() - last) / 1000) : 999;\n\nmsg.filename = fname;\nmsg.payload = `${ts},${p.node},${p.temp},${p.hum},${p.pir},${p.rssi},${ageSec}\\n`;\nreturn msg;",
        "outputs": 1,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 720,
        "y": 360,
        "wires": [
            [
                "c836fc9f41ab07b8"
            ]
        ]
    },
    {
        "id": "c836fc9f41ab07b8",
        "type": "file",
        "z": "0bb5efaeb009f607",
        "name": "Append CSV to SSD (session)",
        "filename": "filename",
        "filenameType": "msg",
        "appendNewline": false,
        "createDir": true,
        "overwriteFile": "false",
        "encoding": "none",
        "x": 1040,
        "y": 360,
        "wires": [
            []
        ]
    },
    {
        "id": "064335ecca504ccb",
        "type": "switch",
        "z": "0bb5efaeb009f607",
        "name": "Route temp by node",
        "property": "topic",
        "propertyType": "msg",
        "rules": [
            {
                "t": "eq",
                "v": "field01",
                "vt": "str"
            },
            {
                "t": "eq",
                "v": "field02",
                "vt": "str"
            },
            {
                "t": "eq",
                "v": "field03",
                "vt": "str"
            }
        ],
        "checkall": "true",
        "repair": false,
        "outputs": 3,
        "x": 980,
        "y": 120,
        "wires": [
            [
                "abc96ec235c454fc"
            ],
            [
                "930a350aad89efde"
            ],
            [
                "7db7f2316125ce97"
            ]
        ]
    },
    {
        "id": "3465a1591bf153cb",
        "type": "switch",
        "z": "0bb5efaeb009f607",
        "name": "Route hum by node",
        "property": "topic",
        "propertyType": "msg",
        "rules": [
            {
                "t": "eq",
                "v": "field01",
                "vt": "str"
            },
            {
                "t": "eq",
                "v": "field02",
                "vt": "str"
            },
            {
                "t": "eq",
                "v": "field03",
                "vt": "str"
            }
        ],
        "checkall": "true",
        "repair": false,
        "outputs": 3,
        "x": 970,
        "y": 160,
        "wires": [
            [
                "1f569dc36776e9fb"
            ],
            [
                "e30fa7e8502946bd"
            ],
            [
                "16c1b84196473112"
            ]
        ]
    },
    {
        "id": "b8782fbd6166b427",
        "type": "switch",
        "z": "0bb5efaeb009f607",
        "name": "Route rssi by node",
        "property": "topic",
        "propertyType": "msg",
        "rules": [
            {
                "t": "eq",
                "v": "field01",
                "vt": "str"
            },
            {
                "t": "eq",
                "v": "field02",
                "vt": "str"
            },
            {
                "t": "eq",
                "v": "field03",
                "vt": "str"
            }
        ],
        "checkall": "true",
        "repair": false,
        "outputs": 3,
        "x": 970,
        "y": 200,
        "wires": [
            [
                "54c74b5e71151041"
            ],
            [
                "b5a2fbcc68cb2a76"
            ],
            [
                "69d6559ba6f7c4d5"
            ]
        ]
    },
    {
        "id": "81cd58e386d41fce",
        "type": "switch",
        "z": "0bb5efaeb009f607",
        "name": "Route pir (state, from dht11) by node",
        "property": "topic",
        "propertyType": "msg",
        "rules": [
            {
                "t": "eq",
                "v": "field01",
                "vt": "str"
            },
            {
                "t": "eq",
                "v": "field02",
                "vt": "str"
            },
            {
                "t": "eq",
                "v": "field03",
                "vt": "str"
            }
        ],
        "checkall": "true",
        "repair": false,
        "outputs": 3,
        "x": 1020,
        "y": 240,
        "wires": [
            [
                "e2bcf0f9addbec9e"
            ],
            [
                "5be35f5951381d5e"
            ],
            [
                "8519542856c1c396"
            ]
        ]
    },
    {
        "id": "a53a2f38942ef6a5",
        "type": "switch",
        "z": "0bb5efaeb009f607",
        "name": "Route PIR event by node",
        "property": "topic",
        "propertyType": "msg",
        "rules": [
            {
                "t": "eq",
                "v": "field01",
                "vt": "str"
            },
            {
                "t": "eq",
                "v": "field02",
                "vt": "str"
            },
            {
                "t": "eq",
                "v": "field03",
                "vt": "str"
            }
        ],
        "checkall": "true",
        "repair": false,
        "outputs": 3,
        "x": 1210,
        "y": 260,
        "wires": [
            [
                "e2bcf0f9addbec9e"
            ],
            [
                "5be35f5951381d5e"
            ],
            [
                "8519542856c1c396"
            ]
        ]
    },
    {
        "id": "bab6e4143c16f51e",
        "type": "switch",
        "z": "0bb5efaeb009f607",
        "name": "Route LinkAge (sec) by node",
        "property": "topic",
        "propertyType": "msg",
        "rules": [
            {
                "t": "eq",
                "v": "field01",
                "vt": "str"
            },
            {
                "t": "eq",
                "v": "field02",
                "vt": "str"
            },
            {
                "t": "eq",
                "v": "field03",
                "vt": "str"
            }
        ],
        "checkall": "true",
        "repair": false,
        "outputs": 3,
        "x": 1020,
        "y": 320,
        "wires": [
            [
                "bfcebff627fbd598"
            ],
            [
                "21a2c80e4473c969"
            ],
            [
                "c44a47261cfd1734"
            ]
        ]
    },
    {
        "id": "abc96ec235c454fc",
        "type": "ui_chart",
        "z": "0bb5efaeb009f607",
        "name": "Temp field01",
        "group": "427d7248a54e7bfc",
        "order": 1,
        "width": 0,
        "height": 0,
        "label": "Temperature (°C)",
        "chartType": "line",
        "legend": "false",
        "xformat": "HH:mm:ss",
        "interpolate": "linear",
        "nodata": "",
        "dot": false,
        "ymin": "",
        "ymax": "",
        "removeOlder": "12",
        "removeOlderUnit": "3600",
        "cutout": 0,
        "useOneColor": false,
        "useUTC": true,
        "outputs": 1,
        "x": 1450,
        "y": 120,
        "wires": [
            []
        ]
    },
    {
        "id": "1f569dc36776e9fb",
        "type": "ui_chart",
        "z": "0bb5efaeb009f607",
        "name": "Hum field01",
        "group": "427d7248a54e7bfc",
        "order": 2,
        "width": 0,
        "height": 0,
        "label": "Humidity (%)",
        "chartType": "line",
        "legend": "false",
        "xformat": "HH:mm:ss",
        "interpolate": "linear",
        "nodata": "",
        "dot": false,
        "ymin": "",
        "ymax": "",
        "removeOlder": "12",
        "removeOlderUnit": "3600",
        "cutout": 0,
        "useOneColor": false,
        "useUTC": true,
        "outputs": 1,
        "x": 1440,
        "y": 160,
        "wires": [
            []
        ]
    },
    {
        "id": "e2bcf0f9addbec9e",
        "type": "ui_chart",
        "z": "0bb5efaeb009f607",
        "name": "PIR field01",
        "group": "427d7248a54e7bfc",
        "order": 3,
        "width": 0,
        "height": 0,
        "label": "PIR (0/1)",
        "chartType": "line",
        "legend": "false",
        "xformat": "HH:mm:ss",
        "interpolate": "step",
        "nodata": "",
        "dot": false,
        "ymin": "0",
        "ymax": "1",
        "removeOlder": "12",
        "removeOlderUnit": "3600",
        "cutout": 0,
        "useOneColor": false,
        "useUTC": true,
        "outputs": 1,
        "x": 1440,
        "y": 240,
        "wires": [
            []
        ]
    },
    {
        "id": "54c74b5e71151041",
        "type": "ui_chart",
        "z": "0bb5efaeb009f607",
        "name": "RSSI field01",
        "group": "427d7248a54e7bfc",
        "order": 4,
        "width": 0,
        "height": 0,
        "label": "RSSI (dBm)",
        "chartType": "line",
        "legend": "false",
        "xformat": "HH:mm:ss",
        "interpolate": "linear",
        "nodata": "",
        "dot": false,
        "ymin": "-100",
        "ymax": "-30",
        "removeOlder": "12",
        "removeOlderUnit": "3600",
        "cutout": 0,
        "useOneColor": false,
        "useUTC": true,
        "outputs": 1,
        "x": 1440,
        "y": 200,
        "wires": [
            []
        ]
    },
    {
        "id": "bfcebff627fbd598",
        "type": "ui_chart",
        "z": "0bb5efaeb009f607",
        "name": "LinkAge field01",
        "group": "427d7248a54e7bfc",
        "order": 5,
        "width": 0,
        "height": 0,
        "label": "LinkAge (sec)  ※最終受信からの秒",
        "chartType": "bar",
        "legend": "false",
        "xformat": "HH:mm:ss",
        "interpolate": "linear",
        "nodata": "",
        "dot": false,
        "ymin": "0",
        "ymax": "60",
        "removeOlder": "12",
        "removeOlderUnit": "3600",
        "cutout": 0,
        "useOneColor": true,
        "useUTC": true,
        "outputs": 1,
        "x": 1460,
        "y": 280,
        "wires": [
            []
        ]
    },
    {
        "id": "930a350aad89efde",
        "type": "ui_chart",
        "z": "0bb5efaeb009f607",
        "name": "Temp field02",
        "group": "143d906e362ee630",
        "order": 1,
        "width": 0,
        "height": 0,
        "label": "Temperature (°C)",
        "chartType": "line",
        "legend": "false",
        "xformat": "HH:mm:ss",
        "interpolate": "linear",
        "nodata": "",
        "dot": false,
        "ymin": "",
        "ymax": "",
        "removeOlder": "12",
        "removeOlderUnit": "3600",
        "cutout": 0,
        "useOneColor": false,
        "useUTC": true,
        "outputs": 1,
        "x": 1450,
        "y": 320,
        "wires": [
            []
        ]
    },
    {
        "id": "e30fa7e8502946bd",
        "type": "ui_chart",
        "z": "0bb5efaeb009f607",
        "name": "Hum field02",
        "group": "143d906e362ee630",
        "order": 2,
        "width": 0,
        "height": 0,
        "label": "Humidity (%)",
        "chartType": "line",
        "legend": "false",
        "xformat": "HH:mm:ss",
        "interpolate": "linear",
        "nodata": "",
        "dot": false,
        "ymin": "",
        "ymax": "",
        "removeOlder": "12",
        "removeOlderUnit": "3600",
        "cutout": 0,
        "useOneColor": false,
        "useUTC": true,
        "outputs": 1,
        "x": 1440,
        "y": 360,
        "wires": [
            []
        ]
    },
    {
        "id": "5be35f5951381d5e",
        "type": "ui_chart",
        "z": "0bb5efaeb009f607",
        "name": "PIR field02",
        "group": "143d906e362ee630",
        "order": 3,
        "width": 0,
        "height": 0,
        "label": "PIR (0/1)",
        "chartType": "line",
        "legend": "false",
        "xformat": "HH:mm:ss",
        "interpolate": "step",
        "nodata": "",
        "dot": false,
        "ymin": "0",
        "ymax": "1",
        "removeOlder": "12",
        "removeOlderUnit": "3600",
        "cutout": 0,
        "useOneColor": false,
        "useUTC": true,
        "outputs": 1,
        "x": 1440,
        "y": 440,
        "wires": [
            []
        ]
    },
    {
        "id": "b5a2fbcc68cb2a76",
        "type": "ui_chart",
        "z": "0bb5efaeb009f607",
        "name": "RSSI field02",
        "group": "143d906e362ee630",
        "order": 4,
        "width": 0,
        "height": 0,
        "label": "RSSI (dBm)",
        "chartType": "line",
        "legend": "false",
        "xformat": "HH:mm:ss",
        "interpolate": "linear",
        "nodata": "",
        "dot": false,
        "ymin": "-100",
        "ymax": "-30",
        "removeOlder": "12",
        "removeOlderUnit": "3600",
        "cutout": 0,
        "useOneColor": false,
        "useUTC": true,
        "outputs": 1,
        "x": 1440,
        "y": 400,
        "wires": [
            []
        ]
    },
    {
        "id": "21a2c80e4473c969",
        "type": "ui_chart",
        "z": "0bb5efaeb009f607",
        "name": "LinkAge field02",
        "group": "143d906e362ee630",
        "order": 5,
        "width": 0,
        "height": 0,
        "label": "LinkAge (sec)  ※最終受信からの秒",
        "chartType": "bar",
        "legend": "false",
        "xformat": "HH:mm:ss",
        "interpolate": "linear",
        "nodata": "",
        "dot": false,
        "ymin": "0",
        "ymax": "60",
        "removeOlder": "12",
        "removeOlderUnit": "3600",
        "cutout": 0,
        "useOneColor": true,
        "useUTC": true,
        "outputs": 1,
        "x": 1460,
        "y": 480,
        "wires": [
            []
        ]
    },
    {
        "id": "7db7f2316125ce97",
        "type": "ui_chart",
        "z": "0bb5efaeb009f607",
        "name": "Temp field03",
        "group": "0a3d104aa2f4e548",
        "order": 1,
        "width": 0,
        "height": 0,
        "label": "Temperature (°C)",
        "chartType": "line",
        "legend": "false",
        "xformat": "HH:mm:ss",
        "interpolate": "linear",
        "nodata": "",
        "dot": false,
        "ymin": "",
        "ymax": "",
        "removeOlder": "12",
        "removeOlderUnit": "3600",
        "cutout": 0,
        "useOneColor": false,
        "useUTC": true,
        "outputs": 1,
        "x": 1450,
        "y": 520,
        "wires": [
            []
        ]
    },
    {
        "id": "16c1b84196473112",
        "type": "ui_chart",
        "z": "0bb5efaeb009f607",
        "name": "Hum field03",
        "group": "0a3d104aa2f4e548",
        "order": 2,
        "width": 0,
        "height": 0,
        "label": "Humidity (%)",
        "chartType": "line",
        "legend": "false",
        "xformat": "HH:mm:ss",
        "interpolate": "linear",
        "nodata": "",
        "dot": false,
        "ymin": "",
        "ymax": "",
        "removeOlder": "12",
        "removeOlderUnit": "3600",
        "cutout": 0,
        "useOneColor": false,
        "useUTC": true,
        "outputs": 1,
        "x": 1440,
        "y": 560,
        "wires": [
            []
        ]
    },
    {
        "id": "8519542856c1c396",
        "type": "ui_chart",
        "z": "0bb5efaeb009f607",
        "name": "PIR field03",
        "group": "0a3d104aa2f4e548",
        "order": 3,
        "width": 0,
        "height": 0,
        "label": "PIR (0/1)",
        "chartType": "line",
        "legend": "false",
        "xformat": "HH:mm:ss",
        "interpolate": "step",
        "nodata": "",
        "dot": false,
        "ymin": "0",
        "ymax": "1",
        "removeOlder": "12",
        "removeOlderUnit": "3600",
        "cutout": 0,
        "useOneColor": false,
        "useUTC": true,
        "outputs": 1,
        "x": 1440,
        "y": 640,
        "wires": [
            []
        ]
    },
    {
        "id": "69d6559ba6f7c4d5",
        "type": "ui_chart",
        "z": "0bb5efaeb009f607",
        "name": "RSSI field03",
        "group": "0a3d104aa2f4e548",
        "order": 4,
        "width": 0,
        "height": 0,
        "label": "RSSI (dBm)",
        "chartType": "line",
        "legend": "false",
        "xformat": "HH:mm:ss",
        "interpolate": "linear",
        "nodata": "",
        "dot": false,
        "ymin": "-100",
        "ymax": "-30",
        "removeOlder": "12",
        "removeOlderUnit": "3600",
        "cutout": 0,
        "useOneColor": false,
        "useUTC": true,
        "outputs": 1,
        "x": 1440,
        "y": 600,
        "wires": [
            []
        ]
    },
    {
        "id": "c44a47261cfd1734",
        "type": "ui_chart",
        "z": "0bb5efaeb009f607",
        "name": "LinkAge field03",
        "group": "0a3d104aa2f4e548",
        "order": 5,
        "width": 0,
        "height": 0,
        "label": "LinkAge (sec)  ※最終受信からの秒",
        "chartType": "bar",
        "legend": "false",
        "xformat": "HH:mm:ss",
        "interpolate": "linear",
        "nodata": "",
        "dot": false,
        "ymin": "0",
        "ymax": "60",
        "removeOlder": "12",
        "removeOlderUnit": "3600",
        "cutout": 0,
        "useOneColor": true,
        "useUTC": true,
        "outputs": 1,
        "x": 1460,
        "y": 680,
        "wires": [
            []
        ]
    },
    {
        "id": "d890fb2cd2532e42",
        "type": "ui_group",
        "name": "Control",
        "tab": "3b1f1ee7541e680e",
        "order": 1,
        "disp": true,
        "width": "12",
        "collapse": false
    },
    {
        "id": "mqtt_broker_local",
        "type": "mqtt-broker",
        "name": "localhost",
        "broker": "127.0.0.1",
        "port": "1883",
        "clientid": "",
        "usetls": false,
        "protocolVersion": "4",
        "keepalive": "60",
        "cleansession": true,
        "birthTopic": "",
        "birthQos": "0",
        "birthPayload": "",
        "closeTopic": "",
        "closeQos": "0",
        "closePayload": "",
        "willTopic": "",
        "willQos": "0",
        "willPayload": ""
    },
    {
        "id": "427d7248a54e7bfc",
        "type": "ui_group",
        "name": "field01",
        "tab": "3b1f1ee7541e680e",
        "order": 2,
        "disp": true,
        "width": "4",
        "collapse": false
    },
    {
        "id": "143d906e362ee630",
        "type": "ui_group",
        "name": "field02",
        "tab": "3b1f1ee7541e680e",
        "order": 3,
        "disp": true,
        "width": "4",
        "collapse": false
    },
    {
        "id": "0a3d104aa2f4e548",
        "type": "ui_group",
        "name": "field03",
        "tab": "3b1f1ee7541e680e",
        "order": 4,
        "disp": true,
        "width": "4",
        "collapse": false
    },
    {
        "id": "3b1f1ee7541e680e",
        "type": "ui_tab",
        "name": "Field Sensors (WiFi/MQTT)",
        "icon": "dashboard",
        "disabled": false,
        "hidden": false
    },
    {
        "id": "1bbc5b47cb74479c",
        "type": "global-config",
        "env": [],
        "modules": {
            "node-red-dashboard": "3.6.5"
        }
    }
]