AirLift support for wifi, socketpool, and ssl modules#10786
AirLift support for wifi, socketpool, and ssl modules#10786dhalbert wants to merge 15 commits intoadafruit:mainfrom
Conversation
get/set hostname power mgmt, ip config; compiles wip compiles SSID connection working fix authmode in scanned results getaddrinfo fix incorrect const in socket recv API wip: progress on socket ops wip wip; removing heap ops; still need to do sock ops HTTP fetches work wip ssl HTTPS fetching works wip: manage nina client and server UDP working both ways
b5d687b to
7caad84
Compare
c8d516d to
a490203
Compare
|
Some test code I have been running on a PyPortal, which is basically like the typical WiFi test program you find in the guides. import os
import board
from digitalio import DigitalInOut
import displayio
import ipaddress
import ssl
import wifi
import socketpool
import adafruit_requests
import sys
import time
displayio.release_displays()
# URLs to fetch from
HTTP_TEXT_URL = "http://wifitest.adafruit.com/testwifi/index.html"
HTTPS_TEXT_URL = "https://www.example.org"
HTTPS_TEXT_URL = "https://www.adafruit.com/api/quotes.php"
JSON_QUOTES_URL = "https://www.adafruit.com/api/quotes.php"
JSON_STARS_URL = "https://api.github.com/repos/adafruit/circuitpython"
print("wifi AirLift test")
wifi.radio.init_airlift(
board.SPI(),
DigitalInOut(board.ESP_CS),
DigitalInOut(board.ESP_BUSY),
DigitalInOut(board.ESP_RESET)
)
#### device info test
mac_address = ':'.join(f"{i:02x}" for i in wifi.radio.mac_address)
print(f"MAC address: {mac_address}")
print("AirLift Firmware version:", wifi.radio.version)
print(f"Hostname: {wifi.radio.hostname}")
wifi.radio.hostname = "another-name"
print(f"New hostname: {wifi.radio.hostname}")
print(f"{wifi.radio.ipv4_dns=}")
#### WiFi scan test
print("Available WiFi networks:")
for network in wifi.radio.start_scanning_networks():
print(network)
wifi.radio.stop_scanning_networks()
#### Connect and info test
print(f"Connecting to {os.getenv('CIRCUITPY_WIFI_SSID')}")
wifi.radio.connect(os.getenv("CIRCUITPY_WIFI_SSID"), os.getenv("CIRCUITPY_WIFI_PASSWORD"))
print(f"Connected to {os.getenv('CIRCUITPY_WIFI_SSID')}")
print(f"My IP address: {wifi.radio.ipv4_address}")
print(f"{wifi.radio.ap_info=}")
#### Ping test
ping_ip = ipaddress.IPv4Address("8.8.8.8")
ping = wifi.radio.ping(ip=ping_ip)
if ping is None:
print("Couldn't ping 'google.com' successfully")
else:
# convert s to ms
print(f"Pinging 'google.com' took: {ping * 1000} ms")
pool = socketpool.SocketPool(wifi.radio)
print(f"{pool.getaddrinfo("adafruit.com", 80)=}")
#### UDP test
# address = ("192.168.1.83", 12000)
# udp_socket = pool.socket(pool.AF_INET, pool.SOCK_DGRAM)
# udp_socket.settimeout(1.0)
# buf = bytearray(1024)
# for _ in range(3):
# print("UDP loop")
# message = str(time.time()).encode("utf-8")
# udp_socket.sendto(message, address)
# try:
# # Get echoed message
# length, server = udp_socket.recvfrom_into(buf)
# print("Echo: ", buf[0:length], "from", server)
# except Exception as e:
# print("Catching", type(e), e, "no reponse")
# time.sleep(2)
# udp_socket.close()
requests = adafruit_requests.Session(pool, ssl.create_default_context())
#### HTTP test
start = time.monotonic()
print(f"Fetching text from {HTTP_TEXT_URL}")
response = requests.get(HTTP_TEXT_URL)
print("-" * 80)
print(response.text)
print("-" * 40)
response.close()
print(f"fetch: {time.monotonic() -start} secs")
print()
#### HTTPS test
start = time.monotonic()
print(f"Fetching text from {HTTPS_TEXT_URL}")
response = requests.get(HTTPS_TEXT_URL)
print("-" * 80)
print(response.text)
print("-" * 40)
response.close()
print(f"fetch: {time.monotonic() -start} secs")
print()
#### HTTPS JSON test
# print(f"Fetching json from {JSON_QUOTES_URL}")
# response = requests.get(JSON_QUOTES_URL)
# print("-" * 80)
# print(response.json())
# print("-" * 40)
# print()
# print(f"Fetching and parsing json from {JSON_STARS_URL}")
# response = requests.get(JSON_STARS_URL)
# print("-" * 80)
# print(f"CircuitPython GitHub Stars: {response.json()['stargazers_count']}")
# print("-" * 40)
print("Done")A UDP server to run on a host computer for testing:import socket
server_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
server_socket.bind(("", 12000))
while True:
message, address = server_socket.recvfrom(1024)
print("Received:, message) |
|
I have an iperf3 implementation which you might want to test with: https://github.com/bablokb/circuitpython-iperf |
|
@anecdata @RetiredWizard @Neradoc may be of interest to you |
|
I either don't understand frozen modules or I built this wrong. I was assuming that all the libraries included in the frozen directory would be available without having to install an mpy version in the lib folder. I built the Makerfabs S3 TFT board but the connection_manager library isn't seen unless I put the file on the board. When I did put the connection_manager and requests library on the board the SSL library wasn't available. I was able to fix this by adding Once I made the parameter change WiFi seemed to work fine on the makerfabs board. I'll do a little more testing though. After the makerfabs, I'll try a couple RP2xx0 board boards. Where there any particular boards or functionality you thought I should be looking at? |
|
@RetiredWizard Sorry! I forgot I only turned this on for SAMD51J20 boards. It is not |
|
Ah ok, I think the only thing I have that comes close is the matrix portal M4 (j19). let me know if/when you want me to run some tests on that. I can also check other WiFi boards just to make sure this update doesn't cause issues. Just let me know how I can help 😁 |
3b0028a to
a769568
Compare
There was a problem hiding this comment.
What are these frozen changes for? (Just a first glance at this. I haven't looked at the devices code yet.
There was a problem hiding this comment.
This was meant to synchronize with main re frozen modules. I'm not sure what happened. I'll remove them.
b0d4e3e to
52dd100
Compare
52dd100 to
8acde80
Compare
|
Since Airlift isn't compatible with ethernet, does this imply that the Wiznet Eth-breakouts can't be used anymore? |
I'll have to figure this out -- the problem is that two versions of This is why this is a draft :) . I could change the airlift support import airlift_wifi as wifi
import airlift_socketpool as socketpool
import airlift_ssl as sslBut then Or perhaps |
This sounds like core code taking on some of what A small consideration: using two radio types on a project. I suspect wifi/airlift + ethernet would be more in demand than wifi + airlift. Or at least the ability to use airlift or ethernet on a native wifi board (that may have been chosen for its other features). |
|
I loaded the matrix_portal_m4 artifact and it partially worked with my programs.
Here are the results from running your test code: Click to expandAdafruit CircuitPython 10.1.0-beta.1-31-g7decafea35 on 2026-02-05; Adafruit Matrix Portal M4 with samd51j19 >>> import dantest wifi AirLift test >cmd GET_MACADDR_CMD (22) >>param #0 (length: 1) --x ff --d 255 --c "�"cmd SET_HOSTNAME (16)
MAC address: 88:9a:06:86:e6:94
AirLift Firmware version: 1.2.2
New hostname: another-name
<response #1 (length 18) --x 74 77 69 6c 69 67 68 74 7a 6f 6e 65 2d 67 75 65 73 74 --d 116 119 105 108 105 103 104 116 122 111 110 101 45 103 117 101 115 116 --c "twilightzone-guest" <response #2 (length 14) --x 56 65 72 69 7a 6f 6e 5f 58 37 42 37 4a 51 --d 86 101 114 105 122 111 110 95 88 55 66 55 74 81 --c "Verizon_X7B7JQ" <response #3 (length 14) --x 56 65 72 69 7a 6f 6e 5f 58 37 42 37 4a 51 --d 86 101 114 105 122 111 110 95 88 55 66 55 74 81 --c "Verizon_X7B7JQ" <response #4 (length 4) --x 48 6f 6d 65 --d 72 111 109 101 --c "Home"
Network(ssid='twilightzone', bssid=b'\xe00\xd16]H', rssi=-67, channel=11, country='', authmode=(wifi.AuthMode.PSK, wifi.AuthMode.WPA, wifi.AuthMode.WPA2))
Traceback (most recent call last): |
|
Thanks for the testing. In my own testing on Fruit Jam, I found that a first HTTP request works, and a second one fails -- it doesn't fetch the last chunk of data from the host. I have not yet debugged this. It worked fine on SAMD. I'm not sure if this is an ESP32 vs ESP32-C6 difference or a SAMD vs RP2xxx difference. |
|
I'm looking my comment over and besides the mis-diagnosed connection_manager issue, it looks like I need to figure out how to update the airlift firmware on this device as well. I'll take a look at that and retest. Sorry for the noise.... |
|
Doing some random testing, noticed ntp doesn't work: line 91 is: I also noticed I needed to call then and then it worked |
|
After updating the airlift firmware, I'm no longer seeing the safe mode crashes and the DNS error I needed to try/except around in your test program no longer fails. The test program still ends at the same point but the message is slightly different: After my changes to the test script this is line 59: |
|
Regarding connection manager, as long as it has a way of knowing which radio it is, it can import things correctly. I actually have a dual wifi project that's connected to two different SSIDs (esp32s3 + airlift) |
Implement wifi/socketpool/ssl for Airlift (NINA-FW) co-processors.
Starting this as a draft, for testing purposes. Tested with Adafruit NINA-FW 3.4.0.
GET_STATE_TCP_CMDandAVAIL_DATA_TCP_CMD. The failure is that the ready line from the AirLift is in the wrong state. This may be some failure or race condition on the AirLift side. I have some other ideas for investigating this as well. Adding some delays did not help. Error is repeated:devices/airlift.wifi.radio.init_airlift()added to specify pins to use.CIRCUITPY_{WIFI,SSL,SOCKETPOOL}further refined asCIRCUITPY_*_NATIVEandCIRCUITPY_*_AIRLIFT. Compiled guards changed appropriately.common_hal_socketpool_socket_bind()return type fromsize_ttoint.socketpoolfunction names which were not named canonically.common_hal_wifi_radio_get_version(().common_hal_socketpool_socket_recv_into()andsocketpool_socket_recv_into()buffer args wereconstand should not have been.-flto=autoinstead of-flto=jobserverto fix spurious message about jobserver.Freeverb,c(found while consolidating some error messages).try_lock().IPv4Address. Simplify some conversion code.elem_print_helper()routines inshared_bindings/util.cto make printing objects easier. Used to give more info when printing aNetworkobject.port_malloc_check()andport_realloc_check()routines. These are not used now, but were added when it looked like I needed to do port mallocs for talking to the AirLift.There were a lot more commits with some dead ends and snapshot commits but I rebased to only a few.