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