adafruit / Adafruit_CircuitPython_ESP32SPI

ESP32 as wifi with SPI interface
MIT License
103 stars 75 forks source link

Add context manager for socket #196

Closed anecdata closed 7 months ago

anecdata commented 7 months ago

Modeled after the WIZnet library (without the added complexities of its internal device and socket management).

Tested with Adafruit CircuitPython 9.0.0-rc.1 on 2024-03-14; Adafruit PyPortal with samd51j20, and:

  1. TCP socket client [HTTP] using adafruit_connection_manager
  2. UDP socket client using adafruit_connection_manager
  3. UDP socket server using adafruit_connection_manager
  4. UDP socket server using adafruit_connection_manager and asyncio: 8 tasks, each bind to a port, then each time a task receives a packet it exits the with and restarts that server. Client hits random ports at random intervals.

In each case, repeatedly executed the with block to check for closing and re-opening the sockets. Ran esp32spi debug mode (checking socket number) and enough iterations to demonstrate that sockets were properly closed (rather than using new sockets until they ran out). I haven't written a context manager before, and there may be subtleties I'm not thinking of with more complex uses, so I'd welcome any feedback with changes or other suggested tests.

Server code for 4th test... ```py import time import os import asyncio import board from digitalio import DigitalInOut from adafruit_esp32spi import adafruit_esp32spi import adafruit_connection_manager PORTS = (5000, 5001, 5002, 5003, 5004, 5005, 5006, 5007,) time.sleep(3) # wait for serial spi = board.SPI() esp32_cs = DigitalInOut(board.ESP_CS) esp32_ready = DigitalInOut(board.ESP_BUSY) esp32_reset = DigitalInOut(board.ESP_RESET) radio = adafruit_esp32spi.ESP_SPIcontrol(spi, esp32_cs, esp32_ready, esp32_reset, debug=False) print("Connecting to Wifi...", end=" ") radio.connect({"ssid": os.getenv("WIFI_SSID"), "password": os.getenv("WIFI_PASSWORD")}) print(f'{radio.pretty_ip(radio.ip_address)}') # get the socket pool from connection manager socket = adafruit_connection_manager.get_radio_socketpool(radio) async def udpserver(port): while True: with socket.socket(type=socket.SOCK_DGRAM) as s: print(f'Starting UDP server with sock={s._socknum} on {port=}') radio.start_server(port, s._socknum, conn_mode=radio.UDP_MODE) await asyncio.sleep(0) while True: numbytes_avail = radio.socket_available(s._socknum) if numbytes_avail: print(f'Reading UDP socket with sock={s._socknum} on {port=} avail={numbytes_avail}', end=" ") bytes_read = radio.socket_read(s._socknum, numbytes_avail) print(f'{bytes_read}') break await asyncio.sleep(0) async def main(): tasks = [] for taskno in range(len(PORTS)): tasks.append(asyncio.create_task(udpserver(PORTS[taskno]))) await asyncio.gather(*tasks) asyncio.run(main()) ```
Client code for 4th test... ```py import time import random import subprocess host = '192.168.6.39' while True: port = random.randint(5000, 5007) packet = f'UDP packet to {host}:{port}' print(f'Sending {packet}') ps = subprocess.Popen(('echo', f'{packet}'), stdout=subprocess.PIPE) output = subprocess.check_output(('nc', '-u', '-w', '1', f'{host}', f'{port}'), stdin=ps.stdout) ps.wait() time.sleep(random.uniform(0.1, 1.0)) ```
Sample client output for 4th test... ``` Sending UDP packet to 192.168.6.39:5003 Sending UDP packet to 192.168.6.39:5003 Sending UDP packet to 192.168.6.39:5000 Sending UDP packet to 192.168.6.39:5005 Sending UDP packet to 192.168.6.39:5001 Sending UDP packet to 192.168.6.39:5002 Sending UDP packet to 192.168.6.39:5004 ```
Sample server output for 4th test... ``` Connecting to Wifi... 192.168.6.39 Starting UDP server with sock=0 on port=5000 Starting UDP server with sock=1 on port=5001 Starting UDP server with sock=2 on port=5002 Starting UDP server with sock=3 on port=5003 Starting UDP server with sock=4 on port=5004 Starting UDP server with sock=5 on port=5005 Starting UDP server with sock=6 on port=5006 Starting UDP server with sock=7 on port=5007 Reading UDP socket with sock=3 on port=5003 avail=32 b'UDP packet to 192.168.6.39:5003\n' Starting UDP server with sock=3 on port=5003 Reading UDP socket with sock=3 on port=5003 avail=32 b'UDP packet to 192.168.6.39:5003\n' Starting UDP server with sock=3 on port=5003 Reading UDP socket with sock=0 on port=5000 avail=32 b'UDP packet to 192.168.6.39:5000\n' Starting UDP server with sock=0 on port=5000 Reading UDP socket with sock=5 on port=5005 avail=32 b'UDP packet to 192.168.6.39:5005\n' Starting UDP server with sock=5 on port=5005 Reading UDP socket with sock=1 on port=5001 avail=32 b'UDP packet to 192.168.6.39:5001\n' Starting UDP server with sock=1 on port=5001 Reading UDP socket with sock=2 on port=5002 avail=32 b'UDP packet to 192.168.6.39:5002\n' Starting UDP server with sock=2 on port=5002 Reading UDP socket with sock=4 on port=5004 avail=32 b'UDP packet to 192.168.6.39:5004\n' Starting UDP server with sock=4 on port=5004 ```