2026年2月16日 星期一

DISCORD ESP32

DISCORD  ESP32 


 

    準備工作:取得 Discord Webhook URL

  1. 開啟 Discord 並進入你的伺服器。

  2. 進入頻道設定(齒輪圖示) > 整合 (Integrations) > Webhook

  3. 點擊「建立 Webhook」,設定好名字後點擊 「複製 Webhook URL」





main.ino

#include <WiFi.h>
#include <HTTPClient.h>
#include <WiFiClientSecure.h> // 必須包含此庫以處理 HTTPS
#include <ArduinoJson.h>
#include <DHT.h>

// --- Discord Webhook 設定 ---
// 請務必確認此 URL 正確且包含 ID 與 Token
const char* DISCORD_WEBHOOK_URL = "https://discord.com/api/webhooks/1472888818762907763/SpUB8clIuuvq54_ZCtEDaYaVRiDSPQJNIiX80JbWleFP8fUA79WqnXnJEeSBDC4-yfVk";

// --- Wokwi 模擬 Wi-Fi 憑證 ---
const char* WIFI_SSID     = "Wokwi-GUEST";
const char* WIFI_PASSWORD = "";

// --- 硬體腳位定義 ---
#define DHTPIN 15
#define DHTTYPE DHT22
#define LED_PIN 2
DHT dht(DHTPIN, DHTTYPE);

/**
 * @brief 向 Discord 發送訊息 (支援 HTTPS)
 */
void sendDiscordMessage(String content) {
    if (WiFi.status() != WL_CONNECTED) {
        Serial.println("WiFi 未連線,無法發送訊息");
        return;
    }

    WiFiClientSecure *client = new WiFiClientSecure;
    if(client) {
        // 重要:在 Wokwi 中必須跳過 SSL 憑證檢查
        client->setInsecure();

        HTTPClient http;
        Serial.print("\n[HTTP] 開始發送至 Discord...");
       
        if (http.begin(*client, DISCORD_WEBHOOK_URL)) {
            http.addHeader("Content-Type", "application/json");

            StaticJsonDocument<512> doc;
            doc["content"] = content;

            String requestBody;
            serializeJson(doc, requestBody);
           
            int httpResponseCode = http.POST(requestBody);
           
            if (httpResponseCode > 0) {
                Serial.printf(" 狀態碼: %d\n", httpResponseCode);
                if (httpResponseCode == 204) {
                    Serial.println("✅ Discord 訊息發送成功!");
                } else if (httpResponseCode == 400) {
                    Serial.println("❌ 錯誤 400:請檢查 Webhook URL 或 JSON 格式");
                    Serial.println(http.getString()); // 印出 Discord 的錯誤訊息
                }
            } else {
                Serial.printf(" 失敗,錯誤原因: %s\n", http.errorToString(httpResponseCode).c_str());
            }
            http.end();
        } else {
            Serial.println("無法連接伺服器");
        }
        delete client; // 釋放記憶體
    }
}

void setup() {
    Serial.begin(115200);
    pinMode(LED_PIN, OUTPUT);
    dht.begin();

    // 啟動提示
    digitalWrite(LED_PIN, HIGH); delay(200);
    digitalWrite(LED_PIN, LOW);  delay(200);

    // 1. 連接 Wi-Fi
    WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
    Serial.print("連線至 Wi-Fi...");
    while (WiFi.status() != WL_CONNECTED) {
        delay(500);
        Serial.print(".");
    }
    Serial.println("\nWi-Fi 已連線! IP: " + WiFi.localIP().toString());

    // 2. 初始讀取數據並發送
    delay(2000); // 等待 DHT 穩定
    float h = dht.readHumidity();
    float t = dht.readTemperature();

    if (isnan(h) || isnan(t)) {
        Serial.println("DHT22 讀取失敗!");
        sendDiscordMessage("⚠️ **ESP32 警告**:無法讀取 DHT22 感測器數據!");
    } else {
        String report = "🚀 **ESP32 監控站啟動**\n🌡️ 溫度: `" + String(t, 1) + "°C`\n💧 濕度: `" + String(h, 1) + "%`";
        sendDiscordMessage(report);
       
        if (t > 30.0) digitalWrite(LED_PIN, HIGH);
    }
}
void loop() {
    static unsigned long lastUpdate = 0;
    // 將更新頻率稍微拉長到 60 秒,避免被 Discord 判定為惡意攻擊
    if (millis() - lastUpdate > 60000) {
        lastUpdate = millis();
       
        float t = dht.readTemperature();
        float h = dht.readHumidity();
       
        if (!isnan(t) && !isnan(h)) {
            // 先確認 WiFi 是否還在,不在就重連
            if (WiFi.status() != WL_CONNECTED) {
                WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
                return;
            }
           
            String msg = "🔄 定時回報 - 溫度: " + String(t, 1) + "°C, 濕度: " + String(h, 1) + "%";
            sendDiscordMessage(msg);
        }
    }
}

arduino_secrets.h

#define SECRET_SSID "Wokwi-GUEST"
#define SECRET_PASS ""
//Copy the webhook url here:
#define SECRET_WEBHOOK "https://discord.com/api/webhooks/1472888818762907763/SpUB8clIuuvq54_ZCtEDaYaVRiDSPQJNIiX80JbWleFP8fUA79WqnXnJEeSBDC4-yfVk"
#define SECRET_TTS "false"

discord.h

#include <WiFi.h>
#include <WiFiMulti.h>
#include <HTTPClient.h>
#include <WiFiClientSecure.h>

const char ssid[] = SECRET_SSID;    // Network SSID (name)
const char pass[] = SECRET_PASS;    // Network password (use for WPA, or use as key for WEP)
const String discord_webhook = SECRET_WEBHOOK;
const String discord_tts = SECRET_TTS;

WiFiMulti WiFiMulti;

void connectWIFI() {
  WiFiMulti.addAP(ssid, pass);
  WiFi.mode(WIFI_STA);
  Serial.print("[WiFi] Connecting to: ");
  Serial.println(ssid);
  // wait for WiFi connection
  while ((WiFiMulti.run() != WL_CONNECTED)) {
    Serial.print(".");
  }
  Serial.println("[WiFi] Connected");
}

void sendDiscord(String content) {
  WiFiClientSecure *client = new WiFiClientSecure;
  // WiFiClientSecure *client;

  if (client) {
    client->setInsecure(); // Disable SSL certificate verification
    {
      HTTPClient https;
      Serial.println("[HTTP] Connecting to Discord...");
      Serial.println("[HTTP] Message: " + content);
      Serial.println("[HTTP] TTS: " + discord_tts);
      if (https.begin(*client, discord_webhook)) {  // HTTPS
        // start connection and send HTTP header
        https.addHeader("Content-Type", "application/json");
        int httpCode = https.POST("{\"content\":\"" + content + "\",\"tts\":" + discord_tts +"}");

        // httpCode will be negative on error
        if (httpCode > 0) {
          // HTTP header has been send and Server response header has been handled
          Serial.print("[HTTP] Status code: ");
          Serial.println(httpCode);

          // file found at server
          if (httpCode == HTTP_CODE_OK || httpCode == HTTP_CODE_MOVED_PERMANENTLY) {
            String payload = https.getString();
            Serial.print("[HTTP] Response: ");
            Serial.println(payload);
          }
        } else {
          Serial.print("[HTTP] Post... failed, error: ");
          Serial.println(https.errorToString(httpCode).c_str());
        }

        https.end();
      } else {
        Serial.printf("[HTTP] Unable to connect\n");
      }

      // End extra scoping block
    }

    delete client;
  } else {
    Serial.println("[HTTP] Unable to create client");
  }
}





import machine
import dht
import network
import time
import urequests as requests
import ujson
import os
import gc

# --- 設定區 ---
WIFI_SSID = "Wokwi-GUEST"
WIFI_PASS = ""

# Discord Webhook 設定
DISCORD_ID = "1472888818762907763"
DISCORD_TOKEN = "SpUB8clIuuvq54_ZCtEDaYaVRiDSPQJNIiX80JbWleFP8fUA79WqnXnJEeSBDC4-yfVk"
URL = "https://discord.com/api/webhooks/{}/{}".format(DISCORD_ID, DISCORD_TOKEN)

# 硬體定義
led_pin = machine.Pin(2, machine.Pin.OUT)     # 你的 LED 腳位 (GPIO 2)
dht_sensor = dht.DHT22(machine.Pin(15))      # 你的 DHT22 腳位 (GPIO 15)
button_pin = machine.Pin(35, machine.Pin.IN, machine.Pin.PULL_UP) # 按鈕 (GPIO 35)

button_down = False

# --- 網路連線 ---
def connect_wifi():
    wifi = network.WLAN(network.STA_IF)
    wifi.active(True)
    if not wifi.isconnected():
        print('正在連線到 WiFi...')
        wifi.connect(WIFI_SSID, WIFI_PASS)
        while not wifi.isconnected():
            time.sleep(1)
            print('.', end='')
    return wifi

# --- 按鈕中斷處理 ---
def button_handler(pin):
    global button_down
    # 簡單去彈跳處理
    button_down = True

# 綁定中斷
button_pin.irq(trigger=machine.Pin.IRQ_FALLING, handler=button_handler)

# --- 發送 Discord 訊息 ---
def send_discord_message(content):
    connect_wifi()
    gc.collect() # 釋放記憶體,防止 SSL 連線記憶體不足
   
    # 修正重點:處理換行符號以符合 JSON 規範,避免 400 錯誤
    # Discord 的 JSON body 中,換行符號必須被轉義為 \\n
    json_content = content.replace('\n', '\\n')
    payload = '{"content": "' + json_content + '"}'
   
    headers = {
        'Content-Type': 'application/json'
    }

    try:
        print("\n 訊息發送中...")
        # 設定 timeout 防止模擬器連線卡死
        response = requests.post(URL, data=payload.encode('utf-8'), headers=headers, timeout=10)
       
        print("Discord 回應狀態碼:", response.status_code)
       
        if response.status_code == 400:
            print("❌ 格式錯誤原因:", response.text)
        elif response.status_code == 204:
            print("✅ 訊息發送成功!")
           
        response.close()
    except Exception as e:
        print("❌ 網路發送異常 (可能是 SSL EOF):", e)
   
    gc.collect()

# --- 主程式 ---
if __name__ == '__main__':
    print("系統啟動: " + os.uname().sysname)
    led_pin.value(0)
   
    # 啟動時發送一次通知
    send_discord_message("🚀 ESP32 MicroPython 監控站已啟動!")

    while True:
        if button_down:
            print("\n偵測到按鈕按下,讀取數據中...")
            led_pin.value(1) # 處理時亮燈
           
            try:
                dht_sensor.measure()
                temp = dht_sensor.temperature()
                humi = dht_sensor.humidity()
               
                # 建立多行訊息
                msg = "📊 **環境監測數據**\n🌡️ 溫度: {:.1f}°C\n💧 濕度: {:.1f}%".format(temp, humi)
                print(msg)
               
                # 發送至 Discord
                send_discord_message(msg)
               
                # 簡易 LED 控制邏輯:溫度大於 30 度維持亮燈
                if temp <= 30:
                    led_pin.value(0)
                else:
                    print("⚠️ 溫度過高!")
                   
            except OSError as e:
                print("DHT22 讀取失敗:", e)
                send_discord_message("⚠️ 無法讀取感測器數據")
                led_pin.value(0)
           
            # 處理完畢,重置按鈕狀態
            button_down = False
           
        # 讓 CPU 休息一下
        time.sleep_ms(200)

DISCORD ESP32

DISCORD  ESP32        準備工作:取得 Discord Webhook URL 開啟 Discord 並進入你的伺服器。 進入頻道設定(齒輪圖示) > 整合 (Integrations) > Webhook 。 點擊「建立 Webhook」,設定...