本文最后更新于 2026-09-02,文章内容可能已经过时。

在本文会讲述如何通过esp32开发板实现在外地(如学校)等地方实现电脑的WOL唤醒。

材料

  • esp32开发板

  • 一个域名(免费的二级域名即可)

一、开启电脑的WOL唤醒功能

在windows系统中右键开始菜单选择设备管理器

QQ20260902-163650.png在设备管理器中找到自己有线网卡设备,并双击进入

屏幕截图 2026-09-02 163943.png在电源管理中取消勾选“允许计算机关闭此设备以节约电源”

屏幕截图 2026-09-02 164122.png

在高级中打开“魔术包唤醒”和“关机网络唤醒”

QQ20260902-164407.png打开控制面板,找到电源选项,点击选择电源按钮的功能,点击更改当前不可用的设置,取消勾选启用快速启动,保证电脑能够被正常唤醒。

最后,开机进入bios,在bios中打来类似Wake-on-LAN的选项(不同主板名称可能不同)

下图引用华硕官方wol开启教程,仅供参考7492f906-2f58-4e46-b6b9-e967ea660d53.bmp

二、ESP32的配置

方法一(适用于有公网ipv6):

在esp32中刷入入MicroPython固件,并写入以下实例代码:

# boot.py
import network
import socket
import time
from machine import Pin

WIFI_SSID = "改成自己的WiFi名称"
WIFI_PASSWORD = "改为自己的wifi密码"
TARGET_MAC = "改成自己电脑的mac地址" 
wlan = network.WLAN(network.STA_IF)
wlan.active(True)
wlan.connect(WIFI_SSID, WIFI_PASSWORD)

while not wlan.isconnected():
    time.sleep(0.5)
print('IP:', wlan.ifconfig()[0])
def send_wol():
    mac = bytes.fromhex(TARGET_MAC.replace(':', ''))
    packet = b'\xff' * 6 + mac * 16
    
    sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
    sock.sendto(packet, ('255.255.255.255', 9))
    sock.close()
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind(('', 80))
s.listen(5)
print('唤醒地址: http://' + wlan.ifconfig()[0] + '/wake')
while True:
    conn, addr = s.accept()
    request = conn.recv(1024).decode()
    
    if '/wake' in request:
        send_wol()
        conn.send(b'HTTP/1.1 200 OK\r\n\r\nOK')
    else:
        conn.send(b'HTTP/1.1 200 OK\r\n\r\nSend to /wake')
    
    conn.close()

之后进入路由器后台,以华为路由器为例,找到ddns服务,绑定域名到esp32(ddns配置教程见后续发布文章)

最后,只需要在浏览器的地址栏输入http://[你的域名/wake即可远程唤醒

方法二(适用于没有ipv6)、

此方法需要有一台有公网ip的服务器

首先,需要在服务器上安装Mosquitto MQTT服务(以Ubuntu为例)在终端中输入以下指令来安装

sudo apt install -y mosquitto mosquitto-clients

安装完毕后,输入以下指令来配置密码文件(要将your_username换成你想要创建账户的名称)

sudo mosquitto_passwd -c /etc/mosquitto/passwd your_username

随后,输入以下指令来编辑配置文件

sudo nano /etc/mosquitto/mosquitto.conf

在配置文件中添加以下内容

# 监听端口
listener 1883 0.0.0.0
# 密码文件路径
password_file /etc/mosquitto/passwd

使用以下命令重启服务

sudo systemctl restart mosquitto

注意,一定要在服务器防火墙开放1883端口

在esp32上,可以使用以下示例代码(这段代码是我用ai生成后自己稍微改了一下,可能有些小问题)

# main.py - ESP32 MQTT远程开机系统(完整稳定版)
import network
import time
import socket
import struct
import machine
import gc
import ubinascii
from machine import WDT, Pin

# ============ 配置信息 ============
# WiFi配置
WIFI_SSID = "你的WiFi名称"
WIFI_PASSWORD = "你的WiFi密码"

# MQTT服务器配置
MQTT_SERVER = "你的公网服务器IP"
MQTT_PORT = 1883
MQTT_USER = "your_username"
MQTT_PASSWORD = "your_password"
MQTT_CLIENT_ID = b"esp32_wol_client"
MQTT_TOPIC_COMMAND = b"home/wol/command"
MQTT_TOPIC_STATUS = b"home/wol/status"

# 目标电脑信息
TARGET_MAC = "AA:BB:CC:DD:EE:FF"  # 改成你的电脑MAC地址
BROADCAST_IP = "192.168.1.255"     # 改成你的局域网广播地址

# ============ 稳定性配置 ============
WDT_TIMEOUT = 30000          # 看门狗超时30秒
HEARTBEAT_INTERVAL = 30      # 心跳间隔30秒
MAX_ERROR_COUNT = 5          # 最大连续错误次数
RECONNECT_DELAY = 5          # 重连延迟5秒
MEMORY_THRESHOLD = 20000     # 内存阈值(字节)
WIFI_CHECK_INTERVAL = 60     # WiFi检查间隔60秒

# ============ 简易MQTT客户端实现 ============
class MQTTClient:
    def __init__(self, client_id, server, port=1883, user=None, password=None, keepalive=60):
        self.client_id = client_id
        self.server = server
        self.port = port
        self.user = user
        self.password = password
        self.keepalive = keepalive
        self.sock = None
        self.pid = 0
        self.cb = None
        self.connected = False
        self.last_ping = 0

    def _send_str(self, s):
        if isinstance(s, str):
            s = s.encode()
        self.sock.write(struct.pack("!H", len(s)))
        self.sock.write(s)

    def _recv_len(self):
        n = 0
        sh = 0
        while True:
            b = self.sock.read(1)[0]
            n |= (b & 0x7f) << sh
            if not b & 0x80:
                return n
            sh += 7

    def set_callback(self, f):
        self.cb = f

    def connect(self):
        try:
            if self.sock:
                try:
                    self.sock.close()
                except:
                    pass
            
            print("正在连接MQTT服务器...")
            self.sock = socket.socket()
            self.sock.settimeout(10)
            addr = socket.getaddrinfo(self.server, self.port)[0][-1]
            self.sock.connect(addr)
            
            # 构建CONNECT包
            premsg = bytearray(b"\x10\0\0\0\0\0")
            msg = bytearray(b"\x04MQTT\x04\x02\0\0")
            
            sz = 10 + 2 + len(self.client_id)
            msg[6] = 0  # clean session
            if self.user is not None:
                sz += 2 + len(self.user) + 2 + len(self.password)
                msg[6] |= 0xC0
            if self.keepalive:
                msg[7] |= self.keepalive >> 8
                msg[8] |= self.keepalive & 0x00FF
            
            i = 1
            while sz > 0x7f:
                premsg[i] = (sz & 0x7f) | 0x80
                sz >>= 7
                i += 1
            premsg[i] = sz
            
            self.sock.write(premsg, i + 2)
            self.sock.write(msg)
            
            # 发送客户端ID
            self._send_str(self.client_id)
            
            # 发送用户名密码
            if self.user is not None:
                self._send_str(self.user)
                self._send_str(self.password)
            
            # 等待CONNACK
            resp = self.sock.read(4)
            if len(resp) != 4 or resp[3] != 0:
                print(f"连接失败,错误码: {resp[3] if len(resp) == 4 else '未知'}")
                self.connected = False
                return False
            
            self.connected = True
            self.last_ping = time.time()
            print("MQTT连接成功!")
            return True
            
        except Exception as e:
            print(f"MQTT连接异常: {e}")
            self.connected = False
            return False

    def disconnect(self):
        try:
            if self.sock:
                self.sock.write(b"\xe0\0")
                self.sock.close()
        except:
            pass
        self.connected = False

    def ping(self):
        try:
            if self.sock and self.connected:
                self.sock.write(b"\xc0\0")
                self.last_ping = time.time()
                return True
        except:
            pass
        self.connected = False
        return False

    def publish(self, topic, msg, retain=False, qos=0):
        try:
            if isinstance(topic, str):
                topic = topic.encode()
            if isinstance(msg, str):
                msg = msg.encode()
                
            pkt = bytearray(b"\x30\0\0\0")
            pkt[0] |= qos << 1 | retain
            sz = 2 + len(topic) + len(msg)
            if qos > 0:
                sz += 2
            
            i = 1
            while sz > 0x7f:
                pkt[i] = (sz & 0x7f) | 0x80
                sz >>= 7
                i += 1
            pkt[i] = sz
            
            self.sock.write(pkt, i + 1)
            self._send_str(topic)
            if qos > 0:
                self.pid += 1
                struct.pack_into("!H", pkt, 0, self.pid)
                self.sock.write(pkt, 2)
            self.sock.write(msg)
            return True
        except Exception as e:
            print(f"发布失败: {e}")
            self.connected = False
            return False

    def subscribe(self, topic, qos=0):
        try:
            if isinstance(topic, str):
                topic = topic.encode()
                
            pkt = bytearray(b"\x82\0\0\0")
            self.pid += 1
            struct.pack_into("!BH", pkt, 1, 2 + 2 + len(topic) + 1, self.pid)
            self.sock.write(pkt)
            self._send_str(topic)
            self.sock.write(b"\x00")  # qos 0
            print(f"已订阅主题: {topic.decode()}")
            return True
        except Exception as e:
            print(f"订阅失败: {e}")
            return False

    def check_msg(self):
        try:
            if not self.sock or not self.connected:
                return None
            self.sock.setblocking(False)
            return self.wait_msg()
        except:
            return None

    def wait_msg(self):
        try:
            res = self.sock.read(1)
            if res is None or res == b"":
                return None
            if res == b"\xd0":  # PINGRESP
                self.sock.read(1)
                return None
            
            op = res[0]
            if op & 0xf0 != 0x30:
                return op
            
            sz = self._recv_len()
            topic_len = self.sock.read(2)
            topic_len = (topic_len[0] << 8) | topic_len[1]
            topic = self.sock.read(topic_len)
            sz -= topic_len + 2
            
            if op & 6:
                pid = self.sock.read(2)
                sz -= 2
            
            msg = self.sock.read(sz)
            
            if self.cb is not None:
                self.cb(topic, msg)
            
            return op
        except Exception as e:
            return None

# ============ 全局变量 ============
wdt = None
mqtt_client = None
error_count = 0
led = None
wlan = None

# ============ LED指示灯 ============
def init_led():
    """初始化LED指示灯"""
    global led
    try:
        led = Pin(2, Pin.OUT)  # 板载LED
        led.value(1)  # 默认关闭(ESP32板载LED是低电平点亮)
    except Exception as e:
        print(f"LED初始化失败: {e}")
        led = None

def blink_led(times, delay=0.2):
    """LED闪烁"""
    global led
    if led is None:
        return
    try:
        for i in range(times):
            led.value(0)  # 点亮
            time.sleep(delay)
            led.value(1)  # 熄灭
            time.sleep(delay)
    except:
        pass

# ============ WiFi管理 ============
def reset_wifi():
    """完全重置WiFi"""
    global wlan
    try:
        if wlan is None:
            wlan = network.WLAN(network.STA_IF)
        
        # 关闭WiFi
        wlan.active(False)
        time.sleep(2)
        
        # 重新激活
        wlan.active(True)
        time.sleep(2)
        
        # 断开连接
        if wlan.isconnected():
            wlan.disconnect()
            time.sleep(1)
        
        print("WiFi已重置")
        return wlan
    except Exception as e:
        print(f"WiFi重置失败: {e}")
        return None

def connect_wifi():
    """增强版WiFi连接"""
    global wlan
    print("\n=== 开始连接WiFi ===")
    
    # 重置WiFi
    wlan = reset_wifi()
    if wlan is None:
        return False
    
    print(f"目标WiFi: {WIFI_SSID}")
    
    # 连接WiFi
    try:
        wlan.connect(WIFI_SSID, WIFI_PASSWORD)
    except Exception as e:
        print(f"WiFi连接请求失败: {e}")
        return False
    
    # 等待连接
    timeout = 30
    while not wlan.isconnected() and timeout > 0:
        time.sleep(1)
        timeout -= 1
        print('.', end='')
        
        # 每5秒打印一次状态
        if timeout % 5 == 0 and timeout < 30:
            status = wlan.status()
            print(f"\nWiFi状态码: {status}")
            
            # 状态码说明
            status_codes = {
                network.STAT_IDLE: "空闲",
                network.STAT_CONNECTING: "连接中",
                network.STAT_WRONG_PASSWORD: "密码错误",
                network.STAT_NO_AP_FOUND: "找不到AP",
                network.STAT_CONNECT_FAIL: "连接失败",
                network.STAT_GOT_IP: "已获取IP"
            }
            if status in status_codes:
                print(f"状态: {status_codes[status]}")
    print()
    
    if wlan.isconnected():
        # 获取IP信息
        ip_info = wlan.ifconfig()
        print("WiFi连接成功!")
        print(f"IP地址: {ip_info[0]}")
        print(f"子网掩码: {ip_info[1]}")
        print(f"网关: {ip_info[2]}")
        print(f"DNS: {ip_info[3]}")
        
        # 关闭WiFi省电模式(重要!)
        try:
            wlan.config(pm=wlan.PM_NONE)
            print("已关闭WiFi省电模式")
        except:
            pass
        
        return True
    else:
        status = wlan.status()
        print(f"WiFi连接失败,状态码: {status}")
        
        # 打印状态说明
        if status == network.STAT_WRONG_PASSWORD:
            print("错误:WiFi密码错误")
        elif status == network.STAT_NO_AP_FOUND:
            print("错误:找不到WiFi网络")
        elif status == network.STAT_CONNECT_FAIL:
            print("错误:连接失败")
        
        return False

def check_wifi_health():
    """检查WiFi健康状态"""
    global wlan, mqtt_client
    
    if wlan is None:
        wlan = network.WLAN(network.STA_IF)
    
    if not wlan.isconnected():
        print("WiFi已断开,尝试重连...")
        if connect_wifi():
            # WiFi恢复,重连MQTT
            if mqtt_client and mqtt_client.connect():
                mqtt_client.subscribe(MQTT_TOPIC_COMMAND)
                mqtt_client.publish(MQTT_TOPIC_STATUS, "online")
            return True
        return False
    
    # 检查IP是否有效
    ip = wlan.ifconfig()[0]
    if ip == '0.0.0.0' or ip == '':
        print("IP无效,重置WiFi...")
        return connect_wifi()
    
    return True

# ============ WOL功能 ============
def send_wol():
    """发送Wake-on-LAN魔术包"""
    try:
        print(f"\n准备发送WOL包...")
        print(f"目标MAC: {TARGET_MAC}")
        print(f"广播地址: {BROADCAST_IP}")
        
        # 将MAC地址转换为字节
        mac_bytes = bytes.fromhex(TARGET_MAC.replace(':', '').replace('-', ''))
        
        # 创建魔术包:6个0xFF + 16个MAC地址
        magic_packet = b'\xff' * 6 + mac_bytes * 16
        
        # 创建UDP socket
        sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
        sock.settimeout(2)
        
        # 发送到多个端口
        success = False
        for port in [9, 7, 0]:
            try:
                sock.sendto(magic_packet, (BROADCAST_IP, port))
                print(f"已发送到端口 {port}")
                success = True
                time.sleep(0.1)
            except Exception as e:
                print(f"端口 {port} 发送失败: {e}")
        
        sock.close()
        
        if success:
            print("WOL包发送成功!")
            blink_led(3, 0.1)  # LED闪烁3次表示成功
            return True
        else:
            print("WOL包发送失败!")
            blink_led(5, 0.1)  # LED闪烁5次表示失败
            return False
        
    except Exception as e:
        print(f"WOL发送异常: {e}")
        blink_led(5, 0.1)
        return False

# ============ MQTT回调 ============
def mqtt_callback(topic, msg):
    """处理接收到的MQTT消息"""
    global error_count, wdt
    try:
        print(f"\n收到MQTT消息: {topic.decode()} -> {msg.decode()}")
        
        if topic == MQTT_TOPIC_COMMAND:
            if msg in [b'wake', b'on', b'1', b'boot', b'power']:
                print("执行开机命令...")
                if send_wol():
                    mqtt_client.publish(MQTT_TOPIC_STATUS, "success")
                    print("已发送成功状态")
                else:
                    mqtt_client.publish(MQTT_TOPIC_STATUS, "failed")
                    print("已发送失败状态")
            else:
                print(f"未知命令: {msg}")
        
        error_count = 0  # 重置错误计数
        
        # 喂狗
        if wdt:
            wdt.feed()
        
    except Exception as e:
        print(f"回调处理错误: {e}")

# ============ 内存管理 ============
def check_memory():
    """检查内存使用情况"""
    gc.collect()
    free_mem = gc.mem_free()
    alloc_mem = gc.mem_alloc()
    total = free_mem + alloc_mem
    
    print(f"内存状态: {free_mem}/{total} 字节可用 ({free_mem*100//total}%)")
    
    if free_mem < MEMORY_THRESHOLD:
        print("内存不足,执行深度垃圾回收")
        gc.collect()
        return False
    return True

# ============ 系统健康检查 ============
def system_check():
    """系统健康检查"""
    global error_count, mqtt_client
    
    print("\n=== 系统健康检查 ===")
    
    # 1. 检查WiFi
    if not check_wifi_health():
        error_count += 1
        print(f"WiFi检查失败,错误计数: {error_count}")
        return False
    
    # 2. 检查MQTT连接
    if mqtt_client and not mqtt_client.connected:
        print("MQTT断开,尝试重连...")
        if mqtt_client.connect():
            mqtt_client.subscribe(MQTT_TOPIC_COMMAND)
            mqtt_client.publish(MQTT_TOPIC_STATUS, "online")
            print("MQTT重连成功")
        else:
            error_count += 1
            print(f"MQTT重连失败,错误计数: {error_count}")
            return False
    
    # 3. 检查心跳
    if mqtt_client and mqtt_client.connected:
        if time.time() - mqtt_client.last_ping > HEARTBEAT_INTERVAL:
            if not mqtt_client.ping():
                error_count += 1
                print(f"心跳失败,错误计数: {error_count}")
                return False
    
    # 4. 检查内存
    check_memory()
    
    # 所有检查通过
    error_count = 0
    print("健康检查通过")
    return True

# ============ 看门狗 ============
def init_watchdog():
    """初始化看门狗"""
    global wdt
    try:
        wdt = WDT(timeout=WDT_TIMEOUT)
        print(f"看门狗已启动(超时: {WDT_TIMEOUT}ms)")
        return True
    except Exception as e:
        print(f"看门狗初始化失败: {e}")
        return False

# ============ 主程序 ============
def main():
    global mqtt_client, error_count, wdt
    
    print("=" * 50)
    print("ESP32 MQTT远程开机系统")
    print("版本: 3.0 稳定版")
    print("=" * 50)
    
    # 初始化组件
    print("\n初始化系统组件...")
    init_led()
    if not init_watchdog():
        print("警告: 看门狗未启动")
    
    # 连接WiFi
    if not connect_wifi():
        print("\n首次WiFi连接失败,3秒后重启...")
        blink_led(5, 0.1)
        time.sleep(3)
        machine.reset()
        return
    
    # 创建MQTT客户端
    print("\n初始化MQTT客户端...")
    mqtt_client = MQTTClient(
        client_id=MQTT_CLIENT_ID,
        server=MQTT_SERVER,
        port=MQTT_PORT,
        user=MQTT_USER,
        password=MQTT_PASSWORD,
        keepalive=60
    )
    
    # 设置回调
    mqtt_client.set_callback(mqtt_callback)
    
    # 连接MQTT
    if not mqtt_client.connect():
        print("首次MQTT连接失败,3秒后重启...")
        blink_led(5, 0.1)
        time.sleep(3)
        machine.reset()
        return
    
    # 订阅主题
    if mqtt_client.subscribe(MQTT_TOPIC_COMMAND):
        print("订阅成功")
    
    # 发布在线状态
    mqtt_client.publish(MQTT_TOPIC_STATUS, "online")
    print("\n设备已上线!")
    blink_led(2, 0.2)  # 启动成功,LED闪烁2次
    
    # 主循环
    print("\n进入主循环...")
    print("=" * 50)
    
    last_wifi_check = time.time()
    last_memory_check = time.time()
    last_status_publish = time.time()
    
    while True:
        try:
            # 1. 喂看门狗
            if wdt:
                wdt.feed()
            
            # 2. 处理MQTT消息
            if mqtt_client and mqtt_client.connected:
                mqtt_client.check_msg()
            
            # 3. 定期检查WiFi(每60秒)
            if time.time() - last_wifi_check > WIFI_CHECK_INTERVAL:
                print("\n定期WiFi检查...")
                if not check_wifi_health():
                    print("WiFi异常,尝试恢复...")
                    if connect_wifi():
                        # WiFi恢复,重连MQTT
                        if mqtt_client.connect():
                            mqtt_client.subscribe(MQTT_TOPIC_COMMAND)
                            mqtt_client.publish(MQTT_TOPIC_STATUS, "online")
                last_wifi_check = time.time()
            
            # 4. 定期检查MQTT连接
            if mqtt_client and not mqtt_client.connected:
                print("\nMQTT断开,尝试重连...")
                if mqtt_client.connect():
                    mqtt_client.subscribe(MQTT_TOPIC_COMMAND)
                    mqtt_client.publish(MQTT_TOPIC_STATUS, "online")
                    error_count = 0
                else:
                    error_count += 1
            
            # 5. 定期发送心跳(每30秒)
            if mqtt_client and mqtt_client.connected:
                if time.time() - mqtt_client.last_ping > HEARTBEAT_INTERVAL:
                    if not mqtt_client.ping():
                        print("心跳失败")
                        error_count += 1
            
            # 6. 定期检查内存(每5分钟)
            if time.time() - last_memory_check > 300:
                check_memory()
                last_memory_check = time.time()
            
            # 7. 定期发布在线状态(每5分钟)
            if time.time() - last_status_publish > 300:
                if mqtt_client and mqtt_client.connected:
                    mqtt_client.publish(MQTT_TOPIC_STATUS, "online")
                    last_status_publish = time.time()
            
            # 8. 错误计数检查
            if error_count >= MAX_ERROR_COUNT:
                print(f"\n错误次数过多({error_count}),执行重启...")
                if mqtt_client:
                    mqtt_client.publish(MQTT_TOPIC_STATUS, "error_restart")
                time.sleep(2)
                machine.reset()
            
            # 短暂延时
            time.sleep(0.1)
            
        except KeyboardInterrupt:
            print("\n\n手动停止程序")
            if mqtt_client:
                mqtt_client.publish(MQTT_TOPIC_STATUS, "offline")
                mqtt_client.disconnect()
            break
            
        except MemoryError:
            print("\n内存错误,执行重启...")
            time.sleep(1)
            machine.reset()
            
        except Exception as e:
            print(f"\n未捕获异常: {e}")
            error_count += 1
            time.sleep(RECONNECT_DELAY)
            
            # 如果错误太多,重启
            if error_count >= MAX_ERROR_COUNT:
                print(f"错误过多({error_count}),重启设备...")
                machine.reset()

# ============ 启动 ============
if __name__ == '__main__':
    try:
        main()
    except Exception as e:
        print(f"程序崩溃: {e}")
        time.sleep(5)
        machine.reset()

到这一步就已经配置完成了,我们可以在手机上下载MQTT Client这个软件来链接服务器

下载后打开软件进行配置

服务器: xxx.xxx.xxx.xxx
端口: 1883
用户名: your_username
密码: your_password

创建控制按钮

发布主题: home/wol/command
发布消息: wake

订阅状态

订阅主题: home/wol/status

这时候就可以通过创建的控制按钮远程控制电脑开机了