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:
TCP socket client [HTTP] using adafruit_connection_manager
UDP socket client using adafruit_connection_manager
UDP socket server using adafruit_connection_manager
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
```
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: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 ```