【问题标题】:using usocket seems to halt the loop (micropython)使用 usocket 似乎停止了循环(micropython)
【发布时间】:2021-04-15 05:06:04
【问题描述】:

我正在尝试为 ESP32 板编写一个简单的程序。 我的主程序相当简单,它必须循​​环运行。 另一方面,设备还需要能够以非常简单的响应来响应 HTTP 请求。

这是我的尝试(https://randomnerdtutorials.com/micropython-esp32-esp8266-bme280-web-server/ 的返工):

try:
  import usocket as socket
except:
  import socket

from micropython import const
import time

REFRESH_DELAY = const(60000) #millisecondi


def do_connect():
    import network
    wlan = network.WLAN(network.STA_IF)
    wlan.active(True)
    if not wlan.isconnected():
        print('connecting to network...')
        wlan.config(dhcp_hostname=HOST)
        wlan.connect('SSID', 'PSWD')
        while not wlan.isconnected():
            pass
    print('network config:', wlan.ifconfig())

import json
import esp
esp.osdebug(None)

import gc
gc.collect()
    
do_connect()

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind((HOST, SENSOR_SCKT_PORT))
s.listen(5)



prevRun = 0
i = 0

while True:
    print("iteration #"+str(i))
    i += 1
    
    # run every 60 seconds
    curRun = int(round(time.time() * 1000))
    
    if curRun - prevRun >= REFRESH_DELAY:
        prevRun = curRun
        # MAIN PROGRAM
        #   ......
        # whole bunch of code
        #  ....


    # run continuously:
    try:
        if gc.mem_free() < 102000:
            gc.collect()
        conn, addr = s.accept()
        conn.settimeout(3.0)
        print('Got a connection from %s' % str(addr))
        request = conn.recv(1024)
        conn.settimeout(None)
        request = str(request)
        #print('Content = %s' % request)
        
        measurements = 'some json stuff'
        
        conn.send('HTTP/1.1 200 OK\n')
        conn.send('Content-Type: text/html\n')
        conn.send('Connection: close\n\n')
        conn.send(measurements)
        conn.close()
    
    except OSError as e:
        conn.close()
        print('Connection closed')

我只得到迭代 #0,然后while True 循环停止。 如果我用 HTTP 请求 ping 这个服务器,我得到一个正确的响应,并且循环前进到迭代 #1 和 #2(不知道为什么它认为我用 2 个请求 ping 它)。 所以看起来 socket.listen(5) 正在停止 while 循环。

有什么办法可以避免这种情况吗? 还有其他解决方案吗? 我不认为线程在这里是一个选项。

【问题讨论】:

    标签: loops sockets micropython


    【解决方案1】:

    问题是s.accept() 是一个阻塞调用......它在收到连接之前不会返回。这就是它暂停循环的原因。

    最简单的解决方案可能是在调用s.accept()之前检查连接是否正在等待;您可以使用select.selectselect.poll 执行此操作。我更喜欢select.poll API,它最终看起来像这样:

    import esp
    import gc
    import json
    import machine
    import network
    import select
    import socket
    import time
    
    from micropython import const
    
    HOST = '0.0.0.0'
    SENSOR_SCKT_PORT = const(1234)
    REFRESH_DELAY = const(60000)  # milliseconds
    
    
    def wait_for_connection():
        print('waiting for connection...')
        wlan = network.WLAN(network.STA_IF)
        while not wlan.isconnected():
            machine.idle()
        print('...connected. network config:', wlan.ifconfig())
    
    
    esp.osdebug(None)
    gc.collect()
    
    wait_for_connection()
    
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.bind((HOST, SENSOR_SCKT_PORT))
    s.listen(5)
    
    poll = select.poll()
    poll.register(s, select.POLLIN)
    
    prevRun = 0
    i = 0
    
    while True:
        print("iteration #"+str(i))
        i += 1
    
        # run every 60 seconds
        curRun = int(round(time.time() * 1000))
    
        if curRun - prevRun >= REFRESH_DELAY:
            prevRun = curRun
            # MAIN PROGRAM
            #   ......
            # whole bunch of code
            #  ....
    
        # run continuously:
        try:
            if gc.mem_free() < 102000:
                gc.collect()
    
            events = poll.poll(100)
            if events:
                conn, addr = s.accept()
                conn.settimeout(3.0)
                print('Got a connection from %s' % str(addr))
                request = conn.recv(1024)
                conn.settimeout(None)
                request = str(request)
                # print('Content = %s' % request)
    
                measurements = 'some json stuff'
    
                conn.send('HTTP/1.1 200 OK\n')
                conn.send('Content-Type: text/html\n')
                conn.send('Connection: close\n\n')
                conn.send(measurements)
                conn.close()
        except OSError:
            conn.close()
            print('Connection closed')
    

    您会注意到,我对您的代码进行了一些改动,以使其在我的设备上运行并满足我的风格;首先,我已经删除了大部分 do_connect 方法,并将所有 imports 放在文件顶部。

    唯一真正的变化是:

    • 我们创建一个select.poll() 对象:

      poll = select.poll()
      
    • 我们要求它监视 s 变量的 POLLIN 事件:

      poll.register(s, select.POLLIN)
      
    • 在尝试处理连接之前,我们会检查是否有任何连接处于挂起状态:

      events = poll.poll(100)
      if events:
          conn, addr = s.accept()
          conn.settimeout(3.0)
          [...]
      

    完成这些更改后,运行代码并发出请求将如下所示:

    iteration #0
    iteration #1
    iteration #2
    iteration #3
    iteration #4
    iteration #5
    iteration #6
    Got a connection from ('192.168.1.169', 54392)
    iteration #7
    iteration #8
    iteration #9
    iteration #10
    

    请注意,如此处所写,您的循环将至少每 100 毫秒迭代一次(您可以通过将调用的超时时间更改为 poll.poll() 来控制它)。


    注意:以上是在运行 MicroPython v1.13-268-gf7aafc062 的 esp8266 设备(Wemos D1 克隆)上测试的。

    【讨论】:

    • 非常感谢!这看起来很完美!如果我的代码看起来像垃圾,我深表歉意,我刚刚删除了一堆其他东西。我试图将导入和 gc.collect() 交织在一起,因为我在某处读到这是有益的。我还没有测试它,因为我目前无法访问 REPL。董事会似乎很忙,但软件工作不正常。如果没有 REPL 访问权限,我将无法调试它。我会告诉你的。
    • 是否可以使用这种方法监听 2 个不同的端口?
    • 当然。您可以使用轮询对象注册多个套接字,poll.poll 的返回值是(obj, event, ...) 元组的列表(请参阅the docs),因此您可以判断哪个套接字接收到事件(以及什么样的事件,如果您'正在监视多种事件类型)。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-01-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多