You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

ESP32 MicroPython中uasyncio实现auto_pump与服务器并发运行问题求助

问题:ESP32 MicroPython中uasyncio并发运行auto_pump与服务器的问题

我在ESP32上使用Python 3.9.12和uasyncio V3开发MicroPython IoT后端应用,希望设备启动后auto_pump()能在后台持续运行,同时服务器等待客户端请求。

现有代码片段

import usocket as socket
import uasyncio as asyncio

d = DHT11(machine.Pin(23))
pump = machine.Pin(22, machine.Pin.OUT)

def get_sensor_data():
    #return data from sensors

async def handle_client(client_socket):
    request = client_socket.recv(1024).decode('utf-8')
    print('Request:', request)
    #manage different GET requests
    client_socket.send(response)
    client_socket.close()

async def start_server():
    #start server 
    while True:
        client_socket, client_addr = server_socket.accept()
        print('Client connected from', client_addr)
        await handle_client(client_socket)

async def auto_pump():
    while True:
        pump_state = get_pump_state()
        
        if pump_state == 1:
            pause_duration = get_pause_duration()
            print(f"Pompa spenta per {pause_duration} minuti")
            change_pump_state()
            await asyncio.sleep(get_pause_duration() * 60)
        else:
            pump_duration = get_pump_duration()
            print(f"Pompa accesa per {pump_duration} minuti")
            change_pump_state()
            await asyncio.sleep(get_pump_duration() * 60)

尝试的三种主函数实现及问题

第一种实现

async def main():
    pump_task = asyncio.create_task(auto_pump())
    server_task = asyncio.create_task(start_server())
    await asyncio.gather(server_task, pump_task)

asyncio.run(main())

问题:auto_pump()仅首次运行,直到服务器收到客户端连接时才会切换泵状态。

第二种实现

async def run_auto_pump():
    while True:
        await auto_pump()

asyncio.run(run_auto_pump())
asyncio.run(start_server())

问题:auto_pump()依旧无法持续运行,且asyncio.run()会阻塞到第一个任务结束,第二个任务永远不会启动。

第三种实现

if __name__ == "__main__":
    loop = asyncio.get_event_loop()
    loop.create_task(auto_pump())  
    loop.create_task(start_server())    
    loop.run_forever()

问题:仍未解决,auto_pump()的执行被服务器的阻塞操作打断。

问题根源

核心问题在于你使用了同步的usocket.accept()调用,这是一个阻塞式操作,会占用uasyncio的事件循环线程,导致事件循环无法切换到auto_pump()任务,直到有客户端连接触发accept()返回,才会继续执行auto_pump()中的后续代码。同理,client_socket.recv()也是同步阻塞操作,会进一步阻塞事件循环。

修复方案

使用uasyncio提供的异步TCP服务器API替代同步socket操作,确保所有IO操作都是异步的,让事件循环可以在等待客户端连接的同时,持续运行auto_pump()任务。

修正后的完整代码

import uasyncio as asyncio
from machine import Pin
from dht import DHT11

d = DHT11(Pin(23))
pump = Pin(22, Pin.OUT)

# 假设你已实现以下辅助函数
def get_sensor_data():
    # 返回传感器数据
    return {"temperature": d.temperature(), "humidity": d.humidity()}

def get_pump_state():
    # 返回当前泵状态(1为关闭,0为开启)
    return pump.value()

def get_pause_duration():
    # 返回泵暂停运行的时间(分钟)
    return 5

def get_pump_duration():
    # 返回泵持续运行的时间(分钟)
    return 1

def change_pump_state():
    # 切换泵的开关状态
    pump.value(not pump.value())

async def handle_client(reader, writer):
    # 使用uasyncio的StreamReader/StreamWriter进行异步IO
    request = await reader.read(1024)
    request_str = request.decode('utf-8')
    print('Request:', request_str)
    
    # 示例响应,可根据请求路径处理不同逻辑
    response = b'HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\n\r\nESP32 IoT Server'
    writer.write(response)
    await writer.drain()
    await writer.wait_closed()

async def start_server():
    # 创建异步TCP服务器,监听所有网卡的80端口
    server = await asyncio.start_server(handle_client, '0.0.0.0', 80)
    async with server:
        await server.serve_forever()

async def auto_pump():
    while True:
        pump_state = get_pump_state()
        
        if pump_state == 1:
            pause_duration = get_pause_duration()
            print(f"Pompa spenta per {pause_duration} minuti")
            change_pump_state()
            await asyncio.sleep(pause_duration * 60)
        else:
            pump_duration = get_pump_duration()
            print(f"Pompa accesa per {pump_duration} minuti")
            change_pump_state()
            await asyncio.sleep(pump_duration * 60)

async def main():
    # 创建两个异步任务并并发执行
    pump_task = asyncio.create_task(auto_pump())
    server_task = asyncio.create_task(start_server())
    await asyncio.gather(server_task, pump_task)

asyncio.run(main())

关键修改点

  1. 替换同步socket为异步服务器API:使用asyncio.start_server()创建异步服务器,该API不会阻塞事件循环,事件循环可以在等待客户端连接的同时调度auto_pump()任务。
  2. 异步IO处理客户端请求:改用StreamReader和StreamWriter替代同步socket对象,await reader.read()和await writer.drain()都是异步操作,不会阻塞事件循环。
  3. 移除阻塞式调用:彻底删除同步的usocket.accept()和client_socket.recv(),确保所有IO操作都通过uasyncio的异步接口完成。

内容的提问来源于stack exchange,提问作者Francesco Chiodo

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.22 22:35:56