スマホで設定出来るXIAO ESP32C3を使った温度センサースケッチ




🔧 この装置でできること
機能 説明
✅ 温度測定 DS18B20センサーで周囲の温度を測定します。
✅ 異常通知 設定温度を超えたら Discord に通知が飛びます。
✅ データ記録 Ambient に自動的に温度を送信して記録されます。
✅ 省電力動作 測定と送信が終わると自動でスリープ状態になります。
✅ スマホ設定 スマホからWi-Fi接続・Discord通知・測定時間などが設定できます。

📱 スマホでの設定方法(初心者向けステップ)
ESP32の設定モードを起動する
ESP32に付いている「設定ボタン(GPIO10)」を押したまま電源を入れます。
赤色LEDが点灯したら「設定モード」になった合図です。
スマホでESP32に接続
スマホのWi-Fi設定を開き、「ESP32_Config」というWi-Fiに接続します(パスワード不要)。
接続するとインターネットは切れますが問題ありません。
ブラウザを開く
SafariやChromeを開いて、以下のアドレスを入力:
http://192.168.4.1
設定ページが表示されます
スマホ向けに見やすくデザインされた画面が出てきます。
各項目を入力・変更できます:
項目説明
個体ID識別用の名前(例:センサーA)
異常温度しきい値(℃)超えたらDiscord通知される温度
測定間隔(秒)何秒ごとに測定・送信を繰り返すか
Wi-Fi SSIDご家庭のWi-Fi名
Wi-Fiパスワードパスワード(非表示入力)
Ambient UserKeyAmbientのキー(クラウド送信用)
Discord WebhookDiscordの通知URL(送信先)
Discord送信を有効にする通知を送るかON/OFF




[保存] ボタンを押す
入力後、ページ下部の「保存」をタップ。
スマホ画面に「保存しました。再起動します...」と大きく表示されます。
その後ESP32は自動的に再起動し、測定運用が開始されます。
🧠 ESP32の内部の動作流れ(概要)
起動直後、ボタンが押されていたら設定モードへ(LED点灯)。
そうでなければ:
センサーに電源ON → 温度測定 → 電源OFF
異常温度や読み取り失敗ならDiscordに通知(有効な場合のみ)
Wi-Fi接続後、Ambientに温度を送信
すべて終わったらDeepSleepで省電力モードへ移行

💡 ヒント・注意事項
Ambient UserKey や Discord Webhook は最初に一度取得・設定すれば以後は不要。
測定間隔を短くしすぎると電池が早く切れます。5分(300秒)以上が推奨です。
Discord通知をOFFにしたい場合は、チェックボックスのチェックを外すだけ。
🛠️ 活用シーン例
農業現場でのハウス内温度モニタリング
倉庫の温度監視
サーバールームの簡易温度アラートシステム
メールではなくDiscordでリアルタイム通知したい用途
#include <WiFi.h>
#include <WebServer.h>
#include <Preferences.h>
#include <OneWire.h>
#include <DallasTemperature.h>
#include <Discord_WebHook.h>
#include <Ambient.h>
#include <esp_sleep.h>

#define CONFIG_BUTTON_PIN 10
#define LED_PIN 5

Preferences preferences;
WebServer server(80);
Discord_Webhook discord;
Ambient ambient;
WiFiClient client;

const int SENSOR_POWER_PIN = 20;
const int ONE_WIRE_PIN = 4;
OneWire oneWire(ONE_WIRE_PIN);
DallasTemperature sensors(&oneWire);

String kotaiID;
float thresholdTemp;
unsigned long sleepSec;
String WIFI_SSID;
String WIFI_PASSWORD;
String ambientUserKey;
String DISCORD_WEBHOOK;
bool ENABLE_DISCORD = true;

void handleRoot() {
  String html = "<!DOCTYPE html><html><head><meta charset='UTF-8'>";
  html += "<meta name='viewport' content='width=device-width, initial-scale=1'>";
  html += "<style>";
  html += "body, form, label, input, h2 { font-family: sans-serif; }";
  html += "body { padding: 20px; background-color: #f9f9f9; }";
  html += "form { background: #fff; padding: 20px; border-radius: 10px; box-shadow: 0 0 10px rgba(0,0,0,0.1); max-width: 600px; }";
  html += ".row { display: flex; align-items: center; margin-bottom: 12px; }";
  html += ".row label { width: 200px; font-weight: bold; }";
  html += ".row input[type='text'], .row input[type='number'], .row input[type='password'] { width: 200px; padding: 5px; }";
  html += "#discord { width: 100%; font-size: 12px; font-family: monospace; }";
  html += "</style>";
  html += "</head><body>";

  html += "<h2>ESP32温度センサー設定</h2><form method='POST' action='/save'>";

  html += "<div class='row'><label>個体ID</label><input type='text' name='id' value='" + kotaiID + "'></div>";
  html += "<div class='row'><label>異常温度閾値(℃)</label><input type='number' step='0.1' name='threshold' value='" + String(thresholdTemp) + "'></div>";
  html += "<div class='row'><label>測定間隔(秒)</label><input type='number' name='sleep' value='" + String(sleepSec) + "'></div>";
  html += "<div class='row'><label>WiFi SSID</label><input type='text' name='ssid' value='" + WIFI_SSID + "'></div>";
  html += "<div class='row'><label>WiFi パスワード</label><input type='password' name='pass' value='" + WIFI_PASSWORD + "'></div>";
  html += "<div class='row'><label>Ambient UserKey</label><input type='text' name='ambient' value='" + ambientUserKey + "'></div>";
  html += "<div class='row'><label>Discord Webhook</label></div>";
  html += "<div class='row'><input type='text' id='discord' name='discord' value='" + DISCORD_WEBHOOK + "'></div>";
  html += "<label style='margin-top:15px;'>Discord送信を有効にする</label><br>";
  html += "<input type='checkbox' name='enable_discord' value='1'";
  if (ENABLE_DISCORD) html += " checked";
  html += "> 有効";

  html += "<br><br><input type='submit' value='保存'>";
  html += "</form></body></html>";

  server.send(200, "text/html", html);
}

void handleSave() {
  kotaiID = server.arg("id");
  thresholdTemp = server.arg("threshold").toFloat();
  sleepSec = server.arg("sleep").toInt();
  WIFI_SSID = server.arg("ssid");
  WIFI_PASSWORD = server.arg("pass");
  ambientUserKey = server.arg("ambient");
  DISCORD_WEBHOOK = server.arg("discord");
  ENABLE_DISCORD = server.hasArg("enable_discord");

  preferences.begin("config", false);
  preferences.putString("kotaiID", kotaiID);
  preferences.putFloat("threshold", thresholdTemp);
  preferences.putULong("sleep", sleepSec);
  preferences.putString("ssid", WIFI_SSID);
  preferences.putString("pass", WIFI_PASSWORD);
  preferences.putString("ambient", ambientUserKey);
  preferences.putString("discord", DISCORD_WEBHOOK);
  preferences.putBool("enable_discord", ENABLE_DISCORD);
  preferences.end();

  server.send(200, "text/html; charset=UTF-8", "<!DOCTYPE html><html><head><meta charset='UTF-8'><meta name='viewport' content='width=device-width, initial-scale=1'><style>body{font-family:sans-serif;text-align:center;padding:40px;font-size:1.5em;}</style></head><body>保存しました。<br>再起動します...</body></html>");
  delay(1000);
  ESP.restart();
}

void startConfigPortal() {
  digitalWrite(LED_PIN, HIGH);
  WiFi.softAP("ESP32_Config");
  IPAddress IP = WiFi.softAPIP();
  server.on("/", handleRoot);
  server.on("/save", HTTP_POST, handleSave);
  server.begin();
  Serial.print("設定モード開始: ");
  Serial.println(IP);
  while (true) {
    server.handleClient();
    delay(10);
  }
}

void loadSettings() {
  preferences.begin("config", true);

  kotaiID = preferences.getString("kotaiID", "");
  if (kotaiID == "") kotaiID = "1";

  DISCORD_WEBHOOK = preferences.getString("discord", "");
  if (DISCORD_WEBHOOK == "") DISCORD_WEBHOOK = "https://discord.com/api/webhooks/YOURWEBHOOK";

  WIFI_SSID = preferences.getString("ssid", "");
  if (WIFI_SSID == "") WIFI_SSID = "YOURSSID";

  WIFI_PASSWORD = preferences.getString("pass", "");
  if (WIFI_PASSWORD == "") WIFI_PASSWORD = "YOURPASSWORD";

  ambientUserKey = preferences.getString("ambient", "");
  if (ambientUserKey == "") ambientUserKey = "YOURUSERKEY";

  sleepSec = preferences.getULong("sleep", 0);
  if (sleepSec == 0) sleepSec = 300;

  thresholdTemp = preferences.getFloat("threshold", -999.0);
  if (thresholdTemp < -100.0) thresholdTemp = 28.0;

  ENABLE_DISCORD = preferences.getBool("enable_discord", true);

  preferences.end();
}

void setup() {
  pinMode(LED_PIN, OUTPUT);
  digitalWrite(LED_PIN, LOW);
  Serial.begin(115200);
  loadSettings();

  pinMode(CONFIG_BUTTON_PIN, INPUT_PULLUP);
  pinMode(LED_PIN, OUTPUT);
  digitalWrite(LED_PIN, LOW);
  pinMode(SENSOR_POWER_PIN, OUTPUT);
  digitalWrite(SENSOR_POWER_PIN, LOW);
  pinMode(ONE_WIRE_PIN, INPUT_PULLUP);

  delay(100);

  if (digitalRead(CONFIG_BUTTON_PIN) == LOW) {
    Serial.println("設定ボタンが押されたため設定モードへ");
    startConfigPortal();
  }

  digitalWrite(SENSOR_POWER_PIN, HIGH);
  delay(100);
  sensors.begin();
  sensors.requestTemperatures();
  delay(750);
  float temperature = sensors.getTempCByIndex(0);
  Serial.println(String(temperature) + "℃");

  digitalWrite(SENSOR_POWER_PIN, LOW);

  bool sendToDiscord = false;
  String msg = "";

  if (temperature == -127.0) {
    msg = "⚠️ DS18B20 read failed. Temp: -127.0°C. Check sensor!";
    sendToDiscord = true;
  } else if (temperature > thresholdTemp) {
    msg = "🔥 異常温度: " + String(temperature) + "°C (ID:" + kotaiID + ")";
    sendToDiscord = true;
  }

  Serial.print("Wi-Fi接続中...");
  WiFi.begin(WIFI_SSID.c_str(), WIFI_PASSWORD.c_str());
  unsigned long t = millis();
  while (WiFi.status() != WL_CONNECTED && millis() - t < 10000) {
    delay(100);
  }

  if (WiFi.status() == WL_CONNECTED) {
    Serial.println("接続成功!");

    if (ENABLE_DISCORD && sendToDiscord && DISCORD_WEBHOOK.length() > 0) {
      Serial.println("Discordへ送信: " + msg);
      discord.begin(DISCORD_WEBHOOK);
      discord.send(msg);
    } else {
      Serial.println("Discord送信なし(無効または条件不一致)");
    }

    if (ambientUserKey.length() > 0) {
      char devKey[32], writeKey[20];
      uint8_t mac[6];
      esp_read_mac(mac, ESP_MAC_WIFI_STA);
      sprintf(devKey, "%02X:%02X:%02X:%02X:%02X:%02X",
              mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
      //Serial.printf("Device MAC (devKey): %s\r\n", devKey);

      unsigned int channelId;

      // Ambientのチャンネル情報を取得
      if (!ambient.getchannel(ambientUserKey.c_str(), devKey, channelId, writeKey, sizeof(writeKey), &client)) {
        Serial.printf("Ambient: チャンネル情報が取得できませんでした。デバイスキー (%s) を登録してください。\r\n", devKey);
        while (true) {
          delay(1000);
        }
      }

      for (int i = 0; i < sizeof(writeKey); i++) {
        if (writeKey[i] == '}' || writeKey[i] == '"') writeKey[i] = '\0';
      }
      ambient.begin(channelId, writeKey, &client);
      ambient.set(1, temperature);
      if (ambient.send()) {
        Serial.println("Ambient送信成功");
      } else {
        Serial.println("Ambient送信失敗");
      }
    } else {
      Serial.println("Ambient送信なし(UserKey未設定)");
    }
  } else {
    Serial.println("Wi-Fi接続失敗");
  }
  WiFi.disconnect(true);
  WiFi.mode(WIFI_OFF);
  digitalWrite(LED_PIN, LOW);  // 念のため明示的に消灯

  esp_sleep_enable_timer_wakeup(sleepSec * 1000000ULL);
  esp_deep_sleep_start();
}
  
void loop() {}
  • このエントリーをはてなブックマークに追加
  • follow us in feedly

この記事の著者

momo

1966年訓子府町生まれの訓子府育ち。玉葱や米、メロンを栽培する農家です。一眼レフを本格的に始めたのは2005年。仕事の時でもいつでもカメラを持ち歩く自称農場カメラマン。普段の生活を撮るのが主で、その他ストロボを使っての商品撮影、スタジオ撮影も。愛好家グループで年1回写真展を行っている。農機具の改造や作製、電子工作など、モノづくりが大好きです。

この著者の最新の記事

関連記事

コメント

  1. この記事へのコメントはありません。

  1. この記事へのトラックバックはありません。

2025年7月
 123456
78910111213
14151617181920
21222324252627
28293031  

カテゴリー

ページ上部へ戻る