如何通过esp32在外地实现电脑的远程开机
本文最后更新于 2026-09-02,文章内容可能已经过时。
在本文会讲述如何通过esp32开发板实现在外地(如学校)等地方实现电脑的WOL唤醒。
材料
esp32开发板
一个域名(免费的二级域名即可)
一、开启电脑的WOL唤醒功能
在windows系统中右键开始菜单选择设备管理器
在设备管理器中找到自己有线网卡设备,并双击进入
在电源管理中取消勾选“允许计算机关闭此设备以节约电源”

在高级中打开“魔术包唤醒”和“关机网络唤醒”
打开控制面板,找到电源选项,点击选择电源按钮的功能,点击更改当前不可用的设置,取消勾选启用快速启动,保证电脑能够被正常唤醒。
最后,开机进入bios,在bios中打来类似Wake-on-LAN的选项(不同主板名称可能不同)
下图引用华硕官方wol开启教程,仅供参考
二、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这时候就可以通过创建的控制按钮远程控制电脑开机了