SS-Sauron/Heimdall-Wake-On-Lan-ESP32
GitHub: SS-Sauron/Heimdall-Wake-On-Lan-ESP32
一款 ESP32 MQTT 远程电源管理固件,通过 Wake-on-LAN 唤醒局域网设备并提供 TOTP 认证与 OPSEC 隐蔽功能。
Stars: 2 | Forks: 0

# 👁 HEIMDALL
**网络守护者**
*时刻注视。永不沉睡。随时唤醒沉睡之物。*
[](https://github.com/SS-Sauron/Heimdall/releases)
[](https://github.com/SS-Sauron/Heimdall/actions/workflows/build.yml)
[](https://ss-sauron.github.io/Heimdall-Wake-On-Lan-ESP32/)
[](https://idf.espressif.com/)
[](https://www.espressif.com/)
[](LICENSE)
[](https://mqtt.org/)
一款紧凑的 **ESP32** 固件,它在你的 MQTT broker 上默默值守,拦截唤醒命令,并向本地网络中沉睡的机器广播 **Wake-on-LAN magic packet**。
只需通过内置的 captive portal 配置一次——然后就可以忘记它的存在。直到你需要它为止。
## ✦ 功能简介
```
(OTA Updates)
[ HTTP/OTA Server ] ◄── TLS ── [ Heimdall / ESP32 ] ──── UDP ────► [ Target PC ]
▲ │ ▲
TLS └──────────── ICMP ────────────┘
│ (Ping Feedback) │
│ │
[ MQTT Broker ] ─────────────────┴───────────────── TLS ────────────┘
(Sleep Listener)
```
Heimdall 是一款专为 ESP32 打造的完整远程电源管理与网络安全工具。开箱即用,它提供:
- **Wake-on-LAN**:向任何本地子网广播 magic packet 以唤醒沉睡的 PC。
- **远程 PC 休眠**:包含一个配套的监听脚本,可通过 MQTT 安全地让你的 PC 重新进入休眠状态。
- **GPIO 控制**:允许你远程切换连接到 ESP32 的物理继电器或指示灯。
- **隐蔽性与 OPSEC**:可配置为在完全不可见的状态下运行,包含 MAC 欺骗、虚假主机名、通过 HMAC 派生的 MQTT topic 以及 TOTP 身份验证,以防止未经授权的网络控制。
## ✦ 功能特性
| 图标 | 功能特性 |
|---|---|
| 🌐 | **Captive Portal 配置** — 连接,配置,完成。支持在 iOS、Android 和 Windows 上进行完整的 DNS 重定向 |
| 🔐 | **三种构建预设** — STANDARD(完整版)、HARDENED(OPSEC + 便利性)、HARDENED STEALTH(最小足迹) |
| 🔑 | **TOTP 身份验证** — 符合 RFC 6238 标准。每个唤醒命令都需要有效的一次性代码(仅限 HARDENED 版本) |
| 🕵️ | **身份混淆** — MAC 欺骗、通用设备主机名、通过 HMAC 派生的不透明 MQTT topic(仅限 HARDENED 版本) |
| 📡 | **动态广播** — 在 runtime 计算正确的广播地址。适用于任何子网 |
| 🔄 | **支持 OTA** — 双槽分区表,失败时自动回滚 |
| 🛡️ | **自愈式 WiFi** — 区分错误密码与临时中断。在路由器重启期间绝不会弹回到设置模式 |
| 🏷️ | **自定义主机名** — 设置你自己的设备名称,并与 DHCP、mDNS 和 NetBIOS 同步 |
| 🏓 | **Ping 反馈** — 可选通过 ICMP echo 请求确认 PC 唤醒状态 |
| 🔌 | **GPIO 输出控制** — 远程切换特定的 ESP32 引脚(例如用于物理继电器)。在 HARDENED 构建版本中受 TOTP 保护 |
| 🔒 | **SecureOn 密码** — 唤醒需要将 6 字节 SecureOn 密码附加到 magic packet 的现代主板 |
| 💤 | **PC 休眠伴侣** — 包含一个跨平台伴侣脚本,可通过 MQTT 远程安全地让你的 PC 休眠 |
| 💡 | **状态 LED** — 提供 portal、连接中、就绪和已发送唤醒状态的视觉反馈 |
| 🔁 | **崩溃循环检测** — 统计 RTC memory 中连续的固件崩溃次数,并在连续发生 3 次 panic 后自动回退到 captive portal(计数器在断电时重置) |
| 🌍 | **Web Flasher** — 通过 USB 直接从浏览器刷入固件。无需 IDE 或工具链 |
## ✦ 构建配置
| | STANDARD | HARDENED | HARDENED STEALTH |
|---|:---:|:---:|:---:|
| Captive Portal 配置 | ✅ | ✅ | ✅ |
| OTA 双槽分区 | ✅ | ✅ | ✅ |
| 自愈式 WiFi 恢复 | ✅ | ✅ | ✅ |
| 动态广播地址 | ✅ | ✅ | ✅ |
| Ping 反馈 (ICMP) | ✅ | ✅ | ❌ |
| GPIO 输出控制 | ✅ | ✅ | ❌ |
| SecureOn 密码 | ✅ | ✅ | ✅ |
| 状态 LED 反馈 | ✅ | ✅ | ❌ |
| MQTT 响应 / 存在状态 | ✅ | ✅ | ❌ |
| TOTP 命令身份验证 | ❌ | ✅ | ✅ |
| HMAC 派生 MQTT topic | ❌ | ✅ | ✅ |
| MAC 地址欺骗 | ❌ | ✅ | ✅ |
| 主机名混淆 | ❌ | ✅ | ✅ |
有关源码构建命令和自定义,请参阅[构建配置](docs/build_profiles.md)。
## ✦ 硬件要求
- **ESP32**(经典版)开发板
- 连接到你的本地 WiFi 网络
- 可访问 MQTT broker(本地或云端,端口 1883 或 8883 TLS)
- *(可选)* 默认使用 ESP32 的内置 LED 进行视觉状态反馈(出于隐蔽性考虑可禁用)
就是这样。无需任何额外组件。
## ✦ 物理设置
**在正常运行期间,Heimdall 不需要通过 USB 连接到你的 PC。**
由于 Wake-on-LAN 使用无线广播机制,ESP32 只需要两样东西即可工作:
1. 电源(例如标准的 USB 充电器)。
2. 与你休眠 PC 连接到同一个本地网络子网的连接。
你可以将 ESP32 插在房间角落、沙发后面,甚至完全不同房间的插座上。当来自 MQTT broker 的命令到达时,ESP32 会通过你的 WiFi 广播一个“Magic Packet”,然后你的路由器会将其通过以太网线(或 WiFi)转发,以唤醒你沉睡的 PC。
## ✦ Web Flasher(无需 IDE)
安装 Heimdall 最简单的方法是使用官方的 Web Flasher 直接从你的浏览器进行。你不需要安装任何开发工具,也不需要从源码编译任何东西。
**[🚀 启动 Heimdall Web Flasher](https://ss-sauron.github.io/Heimdall-Wake-On-Lan-ESP32/)**
1. 通过 USB 将你的 ESP32 连接到计算机。
2. 在受支持的浏览器(Chrome、Edge 或 Opera)中打开上面的链接。
3. 选择所需的构建配置(**Standard**、**Hardened (Full)** 或 **Hardened Stealth**),然后点击 **Connect**。
4. 选择你的 ESP32 的 COM 端口。
5. 点击 **Install Heimdall** 并等待刷入完成。
刷入后,跳转到下面的快速开始中的**第 4 步**,连接到 captive portal 并配置你的凭证。
## ✦ 前置条件(从源码构建)
在构建之前,请确保已在你的 shell 中安装并导出了 ESP-IDF 环境。
| 要求 | 版本 | 备注 |
|---|---|---|
| [ESP-IDF](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/get-started/) | v6.0.1 | 使用官方安装程序或 `install.sh` 流程 |
| Python | 3.8+ | 由 ESP-IDF 工具设置安装 |
| Xtensa ESP32 toolchain | ESP-IDF 管理 | 由 `install.sh` / ESP-IDF 安装程序安装 |
| IDF Component Manager | ESP-IDF 内置 | 在构建期间解析管理的组件 |
使用以下命令检查活动环境:
```
idf.py --version
```
## ✦ 快速开始(从源码构建)
**1. 克隆项目**
```
git clone https://github.com/SS-Sauron/Heimdall.git
cd Heimdall
idf.py set-target esp32
```
**2. 选择构建配置(可选)**
默认配置为 STANDARD。要切换到 HARDENED 或从源码构建 Stealth,请参阅[构建配置](docs/build_profiles.md)。构建前的快速覆盖方法:
```
idf.py menuconfig
```
导航到 **WoL Relay → Build Profile**,选择配置,保存并退出。
对于不使用 menuconfig 的 Hardened 或 Stealth:
```
# Hardened (完整版)
SDKCONFIG_DEFAULTS="sdkconfig.defaults;sdkconfig.hardened" idf.py build
# Hardened Stealth
SDKCONFIG_DEFAULTS="sdkconfig.defaults;sdkconfig.hardened;sdkconfig.hardened.stealth" idf.py build
```
**3. 构建、刷入并监控**
```
idf.py build flash monitor
```
**4. 连接到 portal**
首次启动时,Heimdall 会创建一个受 WPA2 保护的 WiFi 接入点。启用串行配置输出后,SSID 和永久 portal 密码将打印到串口监视器中。从你的手机或笔记本电脑连接——配置页面会在 iOS、Android 和 Windows 上自动打开。
保存后,设备将重启进入中继模式。使用下面的成功启动日志来确认 WiFi、TLS 和 MQTT 是否全部正常工作。
**5. 发送唤醒命令**
将目标机器的 MAC 地址作为纯文本 payload 发布到 Heimdall 的命令 topic:
```
AA:BB:CC:DD:EE:FF
```
*(如果你启用了 Ping 反馈,你可以选择包含目标 IP。有关 payload 格式,请参阅 **使用与操作**)。*
你的机器就会被唤醒。
向同一个命令 topic 发布不同的目标 MAC,即可唤醒同一个局域网上其他支持 WoL 的设备。Heimdall 不会存储单一的目标 PC MAC;payload 会为每个命令选择目标。
## ✦ MQTT Broker
Heimdall 可与托管或本地 MQTT broker 配合使用。对于基于 TLS 的 MQTT 使用端口 `8883`,或者在受信任的本地网络上对于普通 MQTT 使用端口 `1883`。
示例托管 broker 包括 [HiveMQ Cloud](https://www.hivemq.com/mqtt-cloud-broker/) 和 [Adafruit IO](https://io.adafruit.com)。这些仅作为示例;任何兼容的 MQTT broker 应该都可以工作。
在 portal 中,输入 broker 主机名,例如:
```
example-cluster.s1.eu.hivemq.cloud
```
不要在 broker 字段中包含凭证、路径或真实的机密信息。诸如 `mqtts://`、`mqtt://`、`https://` 和 `tcp://` 之类的 scheme 前缀会被接受,但在存储前会被去除。
## ✦ 首次配置启动
```
I (645) main: Not provisioned — starting captive portal
I (646) main: Portal password:
(permanent — same after every reset)
I (660) portal: Portal AP SSID: NETGEAR-XXXXXX
I (663) portal: Portal AP Auth: WPA2
I (1109) portal: SoftAP started SSID: NETGEAR-XXXXXX Auth: WPA2 DHCP opt-114: http://192.168.4.1/
I (1117) dns_server: Listening on UDP port 53 — redirect → 192.168.4.1
I (1122) dns_server: Started — redirecting all A queries to 192.168.4.1
I (1135) portal: HTTP server ready on port 80
I (1135) portal: Portal ready — waiting for credentials
I (10709) esp_netif_lwip: DHCP server assigned IP to a client, IP is: 192.168.4.2
I (123605) portal: Stripped scheme prefix from broker URL: 'https://xxx.eu.hivemq.cloud' → 'xxx.eu.hivemq.cloud'
I (123618) storage: Credentials saved successfully
I (123619) storage: Hostname saved: test-relay-name
I (137113) portal: Credentials saved — rebooting into relay mode
```
## ✦ 中继成功启动
在配置了有效的 WiFi 和 MQTT 凭证后,下一次重启应跳过 portal 并进入中继模式。重要的成功标志包括:WiFi 获取 IP 地址、TLS 验证 broker 证书、MQTT 连接成功,以及确认命令 topic 订阅。
```
I (646) identity: Hostname loaded from NVS: test-relay
I (649) main: Credentials found — starting relay
I (2875) wifi_sta: Got IP:
I (2876) main: WiFi connected
I (2879) main: Starting MQTT relay
I (2881) opsec: Command topic: wol/
I (2885) opsec: Status topic: wol//s
I (2887) opsec: Log topic: wol//l
I (2893) mqtt_relay: Connecting to MQTT broker: mqtts://.hivemq.cloud:8883
I (2900) mqtt_relay: MQTT credential lengths: username= password=
I (2906) mqtt_relay: TLS hostname verification/SNI: enabled via broker URI hostname
I (2917) mqtt_relay: MQTT client started — relay is active
I (3702) esp-x509-crt-bundle: Certificate validated
I (4895) mqtt_relay: MQTT connected — subscribing to: wol/
I (5002) mqtt_relay: Subscription confirmed (msg_id=22951)
```
如果日志显示到 `MQTT connected` 和 `Subscription confirmed`,则 Heimdall 已上线,并正在上面显示的命令 topic 上等待唤醒命令。
## ✦ 使用与操作
一旦 Heimdall 成功配置并连接到你的 MQTT broker,你就可以开始发送唤醒命令了。
### ✦ 查找你的命令 Topic
Heimdall 订阅和发布的 topic 取决于所选的构建配置。响应分为 **Status** topic (`/s`) 用于保留的机器可读状态,以及 **Log** topic (`/l`) 用于未保留的人类可读诊断信息。
**STANDARD 构建**
```
Command: wol/
Status: wol//s
Log: wol//l
```
`` 是在串口监视器中打印的 ESP32 station MAC。
**HARDENED 构建**
```
Command: <16-character-hmac-topic>
Status: <16-character-hmac-topic-base>/s
Log: <16-character-hmac-topic-base>/l
```
HARDENED topic 是由设备 MAC 和配置期间生成的密钥派生出的不透明字符串。它们在中继启动期间打印到串口监视器。
### ✦ 让 PC 休眠
Heimdall 包含一个跨平台伴侣脚本(`sleep_listener.py` 和 `sleep_listener.exe`),可在你的目标 PC 上静默运行。当它通过你的 MQTT broker 接收到特定的休眠命令时,会立即触发操作系统休眠(或挂起)。
有关完整的设置说明,请参阅 **[PC 休眠监听指南](docs/pc_sleep_listener.md)**。
### ✦ 使用触发脚本
代码库包含了现成的 Bash 脚本,可为你格式化 MQTT payload 并处理 `mosquitto_pub` 命令。
**前提条件:** 你必须安装 `mosquitto-clients`(如果使用 hardened 脚本,还需安装 `oathtool`)。
**STANDARD 脚本用法:**
```
./scripts/wake_standard.sh [user] [pass]
```
*示例:* `./scripts/wake_standard.sh mqtt.example.com 8883 "wol/AA:BB:CC:11:22:33" 99:88:77:66:55:44`
**HARDENED 脚本用法(带 TOTP):**
```
./scripts/wake_hardened.sh [user] [pass]
```
*示例:* `./scripts/wake_hardened.sh mqtt.example.com 8883 "a1b2c3d4e5f6g7h8" 99:88:77:66:55:44 "JBSWY3DPEHPK3PXP"`
### ✦ Ping 反馈(可选)
如果你的固在编译时启用了 `CONFIG_WOL_PING_FEEDBACK=y`,你可以在命令中包含目标 PC 的 IP 地址。Heimdall 将 ping 该机器,并在其成功启动或超时时发布警报。
**Standard 构建 (JSON):**
发送 JSON payload 代替纯文本:
```
{"mac":"AA:BB:CC:DD:EE:FF", "ip":"192.168.1.100"}
```
**Hardened 构建 (字符串):**
将 IP 地址作为第 4 段附加在 MAC 和 TOTP 之后:
```
AA:BB:CC:DD:EE:FF:123456:192.168.1.100
```
### ✦ 响应 Payload
在发送 magic packet 后(Standard 和 Hardened Full 构建),Heimdall 会向 **Status** topic (`/s`) 发布确认信息:
```
{
"mac": "AA:BB:CC:DD:EE:FF",
"status": "sent"
}
```
系统诊断信息(如 uptime 和剩余堆内存)会定期发布,并在执行唤醒命令时发布到 **Log** topic (`/l`):
```
{
"free_heap": 187432,
"uptime_s": 3672
}
```
如果请求了 Ping 反馈,一旦机器启动(或超时),你将在 **Status** topic 上收到*第二条*消息:
```
{
"mac": "AA:BB:CC:DD:EE:FF",
"status": "awake",
"boot_time_s": 14
}
```
| 字段 | 描述 |
|---|---|
| `mac` | 发送 Wake-on-LAN magic packet 的目标 MAC 地址。 |
| `status` | `sent`(已广播 WoL)、`awake`(ping 成功)、`timeout`(ping 失败)、`ota_start`(OTA 开始)、`ota_ok`(OTA 完成)或 `ota_fail`(OTA 错误)。 |
| `reason` |(仅限 Status topic)解释 OTA 更新失败原因的描述性字符串。 |
| `boot_time_s` | 从广播 WoL 到第一次成功收到 ping 回复之间经过的秒数。 |
| `free_heap` |(仅限 Log topic)ESP32 上可用的 RAM 字节数。可用于监控设备健康状况。 |
| `uptime_s` |(仅限 Log topic)自上次重启以来 ESP32 持续运行的总时间(秒)。 |
### ✦ GPIO 输出控制
如果你的固件在编译时启用了 `CONFIG_WOL_GPIO_COMMANDS=y`,你可以通过向主命令 topic 发布 JSON payload 来控制 ESP32 上的特定 GPIO 引脚。
只能控制 `CONFIG_WOL_GPIO_ALLOWED_PINS` 中列出的引脚。
**Standard 构建 (JSON):**
```
{"action":"gpio", "pin":4, "level":1}
```
**Hardened 构建 (带 TOTP 的 JSON):**
```
{"action":"gpio", "pin":4, "level":1, "totp":123456}
```
Heimdall 将向 **Status** topic (`/s`) 发布回确认信息:
```
{"action":"gpio", "pin":4, "level":1, "status":"ok"}
```
### ✦ TOTP 设置(仅限 HARDENED)
启用 TOTP 后,每个唤醒命令都必须在目标 MAC 地址后附加一个有效的 6 位时间验证码:
```
AA:BB:CC:DD:EE:FF:123456
```
TOTP 种子在配置期间生成,并在 portal 机密页面上**一次性**显示为 Base32 值以及 `otpauth://` URI。请立即保存。如果丢失,请恢复出厂设置并重新配置设备以生成新种子。
任何兼容 RFC 6238 的身份验证器应用或触发脚本都可以根据该种子生成代码。
### ✦ 状态 LED 模式
Heimdall 默认使用 ESP32 的内置 LED (GPIO2) 提供实时的视觉反馈。你也可以选择将外部 LED 连接到自定义引脚,或者在配置中完全禁用 LED,以适用于隐蔽部署。
| 状态 | LED 模式 | 描述 |
|---|---|---|
| **Portal 模式** | 快速闪烁(~2.5Hz) | 等待你连接到 Heimdall WiFi 设置 portal |
| **连接中** | 缓慢呼吸(~0.5Hz) | 正在尝试连接 WiFi 和 MQTT broker |
| **就绪** | 常亮 | 已连接并正在主动监听唤醒命令 |
| **已发送唤醒** | 快速闪烁 6 次(50ms) | Magic packet 已发送 —— 自动返回就绪状态 |
## ✦ Over-The-Air (OTA) 更新
Heimdall 使用带有自动回滚功能的双槽 OTA 分区表。如果新固件在启动时崩溃或未能连接到 MQTT broker,bootloader 会自动恢复到上一个正常工作的槽位。
**MQTT 触发的 OTA 拉取**是主要的更新方法。向 `/ota` 发布 JSON payload:
```
{"url":"https://example.com/heimdall.bin"}
```
HARDENED 构建需要 TOTP 代码:
```
{"url":"https://example.com/heimdall.bin","totp":123456}
```
设备在此过程中会向 status topic 发布 `ota_start`、`ota_ok` 和 `ota_fail` 状态事件。有关完整的 payload 参考、进度事件、并发行为和 HTTP 注意事项,请参阅技术文档中的[通过 MQTT 进行 OTA 更新](docs/README.md#ota-updates-via-mqtt)部分。
你也可以通过以下方式手动更新:
- **[Web Flasher](https://ss-sauron.github.io/Heimdall-Wake-On-Lan-ESP32/)** — 通过 USB 直接从浏览器刷入。无需工具。
- **`idf.py flash`** — 使用 ESP-IDF 工具链从源码重新构建并通过 USB 刷入。
回滚保证适用于所有方法:一旦新固件成功连接到你的 MQTT broker 并订阅了命令 topic,它就会调用 `esp_ota_mark_app_valid_cancel_rollback()` 来永久提交更新。
## ✦ 恢复出厂设置
要清除存储的凭证并返回配置模式:
1. 按住 **BOOT** 按钮 5 秒钟。
2. 在串口监视器中等待恢复出厂设置确认信息。
3. 设备会擦除 `wol` NVS namespace 并重启进入 captive portal。
这在正常中继操作期间以及配置失误后均有效。
## ✦ 项目结构
```
Heimdall/
├── components/
│ ├── dns_server/ # Captive portal DNS redirect
│ ├── identity/ # MAC spoofing & hostname obfuscation
│ ├── mqtt_relay/ # MQTT client & WoL dispatch core
│ ├── opsec/ # HMAC topics, TOTP, SNTP
│ ├── ota/ # MQTT-triggered HTTPS OTA pull
│ ├── portal/ # Provisioning web server
│ ├── status_led/ # Visual status LED feedback
│ ├── storage/ # NVS credential persistence
│ ├── wifi_sta/ # WiFi station with self-healing
│ └── wol/ # Magic packet builder & broadcaster (SecureOn support)
├── docs/ # Extended documentation and Web Flasher UI (GitHub Pages)
├── experimental/ # Sandbox for upcoming features and design files
├── main/ # Boot sequence & orchestration
├── resources/ # README images and visual design assets
├── scripts/ # MQTT trigger helpers, Windows WoL setup, OTA push helper,
│ # and PC sleep companion (sleep_listener.py / .exe)
├── .clangd # IDE language server config (clangd + ESP-IDF/GCC compatibility)
├── partitions.csv # OTA-ready dual-slot partition table
├── sdkconfig.defaults # Baseline Kconfig configuration for STANDARD profile
├── sdkconfig.hardened # Kconfig overrides for the HARDENED profile
├── sdkconfig.hardened.stealth # Stealth toggles layered on HARDENED
└── sleep_listener.spec # PyInstaller spec used to build the sleep_listener.exe binary
```
## ✦ CI/CD Pipeline
每次推送到 `main` 分支都会触发自动化的 GitHub Actions 工作流,该工作流会:
- **构建** 使用官方 Espressif ESP-IDF Docker 镜像并行构建 STANDARD、HARDENED 和 HARDENED STEALTH 固件配置。
- **部署** 将 Web Flasher 部署到 GitHub Pages,将最新编译的 `.bin` 文件与 flasher UI 打包在一起,以便用户始终可以从浏览器刷入最新代码。
每个 Git 标签 (`v1.0.0`) 还会额外:
- **创建 GitHub Release** 并将编译好的 `heimdall-standard.bin`、`heimdall-hardened.bin` 和 `heimdall-hardened-stealth.bin` 作为可下载的制品附加上去。
## ✦ 贡献
欢迎提交 Bug 报告、功能请求和 Pull Request。在进行较大更改之前,请参阅 [CONTRIBUTING.md](CONTRIBUTING.md)。
## ✦ 构建所用技术
[](https://en.wikipedia.org/wiki/C99)
[](https://www.freertos.org/)
[](https://tls.mbed.org/)
[](https://savannah.nongnu.org/projects/lwip/)
*Heimdall 守望着 Bifrost —— 连接各个领域的桥梁。*
*而这个项目守护着你的网络。同样的工作,更小的开发板。*
**[ [文档](docs/) · [发布版本](https://github.com/SS-Sauron/Heimdall/releases) · [许可证](LICENSE) ]**
标签:ESP32, OTA升级, TOTP认证, Wake-on-LAN, 后端开发, 固件开发, 底层编程, 物联网, 逆向工具