方案 3:手机控制 ESP32

import socket

import network

import machine

import time

from machine import Pin

全局变量

led = Pin(2, Pin.OUT) # 初始化GPIO2为输出模式

led.value(0) # 默认熄灭LED

def start_ap():

"""启动ESP32的AP模式"""

ap = network.WLAN(network.AP_IF)

ap.active(False)

time.sleep(0.5)

ap.active(True)

AP配置

ssid = 'ESP32_APTest'

password = '12345678'

ap.config(

essid=ssid,

password=password,

authmode=3, # WPA2-PSK

max_clients=10

)

等待AP启动完成

while not ap.active():

time.sleep(0.1)

print('='*20)

print('AP模式已启动')

print('SSID:', ssid)

print('密码:', password)

ip = ap.ifconfig()0

print('IP地址:', ip) # 通常是192.168.4.1

print('='*20)

return ip

def udp_server():

"""启动UDP服务,监听指令控制LED"""

udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

udp_socket.bind(("0.0.0.0", 7788))

udp_socket.settimeout(1) # 超时1秒,避免阻塞

while True:

try:

接收UDP数据

recv_data, sender_info = udp_socket.recvfrom(1024)

cmd = recv_data.decode("utf-8").strip()

执行指令

if cmd == "light on":

led.value(1)

print("来自{}的指令:点亮LED".format(sender_info))

udp_socket.sendto(b"success: LED ON", sender_info)

elif cmd == "light off":

led.value(0)

print("来自{}的指令:熄灭LED".format(sender_info))

udp_socket.sendto(b"success: LED OFF", sender_info)

else:

udp_socket.sendto(b"error: unknown command", sender_info)

兼容110和116两种超时错误码,不打印正常超时

except OSError as e:

err_code = e.args0

if err_code in (110, 116):

continue

else:

print("UDP服务异常: {}".format(e))

continue

except Exception as e:

print("UDP服务其他错误: {}".format(e))

continue

def web_server(ip):

"""改进版Web服务器:精确解析HTTP请求,返回标准响应"""

启动TCP Web服务器

web_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

web_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

web_socket.bind(("0.0.0.0", 80))

web_socket.listen(5)

设置服务器accept超时,避免永久阻塞

web_socket.settimeout(3)

print("API服务器已启动,地址: http://{}".format(ip))

while True:

conn = None

try:

接受客户端连接(最多等待3秒)

conn, addr = web_socket.accept()

print("客户端连接: {}".format(addr))

为每个连接设置接收超时2秒

conn.settimeout(2)

接收HTTP请求(最多1024字节)

request = conn.recv(1024).decode("utf-8").strip()

if not request:

continue

解析请求行

request_line = request.split('\r\n')0 # 第一行是请求行

parts = request_line.split(' ')

if len(parts) < 2:

无效请求

response = "HTTP/1.1 400 Bad Request\r\nContent-Length: 0\r\n\r\n"

conn.send(response.encode("utf-8"))

continue

method, path = parts0, parts1

仅处理GET请求

if method != 'GET':

response = "HTTP/1.1 405 Method Not Allowed\r\nAllow: GET\r\nContent-Length: 0\r\n\r\n"

conn.send(response.encode("utf-8"))

continue

精确匹配路径

if path == '/light/on':

led.value(1)

body = "LED已点亮"

response = "HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\nContent-Length: {}\r\nAccess-Control-Allow-Origin: *\r\n\r\n{}".format(len(body), body)

elif path == '/light/off':

led.value(0)

body = "LED已熄灭"

response = "HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\nContent-Length: {}\r\nAccess-Control-Allow-Origin: *\r\n\r\n{}".format(len(body), body)

else:

body = "API Not Found"

response = "HTTP/1.1 404 Not Found\r\nContent-Type: text/plain\r\nContent-Length: {}\r\n\r\n{}".format(len(body), body)

发送响应

conn.send(response.encode("utf-8"))

except OSError as e:

忽略超时异常,继续循环

err_code = e.args0

if err_code in (110, 116):

continue

else:

print("Web服务器OS错误: {}".format(e))

except Exception as e:

print("Web服务器其他错误: {}".format(e))

finally:

确保连接被关闭

if conn:

try:

conn.close()

except:

pass

def main():

"""主函数:启动所有服务"""

启动AP模式

ip = start_ap()

启动UDP服务(新建线程)

import _thread

_thread.start_new_thread(udp_server, ())

启动Web服务器(主线程)

web_server(ip)

if name == "main":

main()

<!DOCTYPE html>

<html lang="zh-CN">

<head>

<meta charset="UTF-8">

<meta name="viewport" content="width=device-width, initial-scale=1.0">

<title>ESP32 LED控制</title>

<style>

body {

font-family: Arial, sans-serif;

max-width: 400px;

margin: 50px auto;

text-align: center;

}

.control-btn {

width: 150px;

height: 60px;

font-size: 20px;

margin: 10px;

border: none;

border-radius: 8px;

cursor: pointer;

color: white;

}

#onBtn {

background-color: #4CAF50;

}

#offBtn {

background-color: #f44336;

}

#status {

margin-top: 20px;

font-size: 18px;

color: #333;

}

</style>

</head>

<body>

<h1>ESP32 LED控制器</h1>

<button id="onBtn" class="control-btn">点亮LED</button>

<button id="offBtn" class="control-btn">熄灭LED</button>

<div id="status">等待操作...</div>

<script>

// ESP32的实际IP地址(AP模式下通常是192.168.4.1)

const esp32_ip = "192.168.4.1";

// 发送请求到ESP32的API接口

async function sendCommand(cmd) {

try {

const response = await fetch(`http://${esp32_ip}/light/${cmd}`);

const result = await response.text();

document.getElementById('status').textContent = `操作结果: ${result}`;

} catch (err) {

document.getElementById('status').textContent = `操作失败: 请检查网络和ESP32 IP`;

console.error(err);

}

}

// 绑定按钮事件

document.getElementById('onBtn').addEventListener('click', () => {

sendCommand("on");

});

document.getElementById('offBtn').addEventListener('click', () => {

sendCommand("off");

});

</script>

</body>

</html>

相关推荐
我是一棵无人问荆的小草3 小时前
stm32f103单片机的NVIC分组2介绍
stm32·单片机·嵌入式硬件
紫幽3 小时前
从 0 用 Vue 做屏并生成 LVGL 单片机代码(完整源码备忘)
前端·单片机
智者知已应修善业4 小时前
【51单片机数码管逐位显示1-8余位不亮】2024-10-23
c语言·经验分享·笔记·嵌入式硬件·51单片机
Be for thing5 小时前
【嵌入式成长3】STC89C51外部中断|中断原理、寄存器、中断服务函数
单片机·嵌入式硬件·学习
科芯创展6 小时前
1A,5.5VIN,同步升降压恒流芯片,XZ3442
单片机
恒锐丰-罗生7 小时前
率能半导体 SS6840 单通道 H 桥电机驱动芯片深度解析
驱动开发·嵌入式硬件·硬件工程
9稳8 小时前
基于PLC的小车运料装卸控制系统设计
开发语言·网络·数据库·单片机·嵌入式硬件
拾知_H8 小时前
STM32+FreeRTOS基础配置
stm32·嵌入式硬件·freertos
智购科技自动贩卖机8 小时前
自动售货机硬件主控方案选型实战:单片机、树莓派、ESP32怎么选?
人工智能·单片机·嵌入式硬件·物联网·网络协议·yolo·架构
LCG元9 小时前
STM32 多通道 ADC + DMA 采集实战:数据错位、读数跳动的根因与滤波选型
stm32·单片机·嵌入式硬件