-
Notifications
You must be signed in to change notification settings - Fork 75
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Improve native wifi API compatibility #199
Conversation
Tested on: import time
import os
import board
import digitalio
import wifi
from adafruit_esp32spi import adafruit_esp32spi
time.sleep(3)
# native wifi
print("\nNative wifi...\n")
radio = wifi.radio
# radio.disconnect() not implemented; use radio.enabled = False or radio.stop_station()
print(f'{radio.connect(os.getenv("WIFI_SSID"), os.getenv("WIFI_PASSWORD"))=}')
print(f'{radio.connected=}')
print(f'{radio.ipv4_address=}')
# ESP32SPI
print("\nESP32SPI...\n")
spi = board.SPI()
esp32_cs = digitalio.DigitalInOut(board.D13)
esp32_reset = digitalio.DigitalInOut(board.D12)
esp32_ready = digitalio.DigitalInOut(board.D11)
radio = adafruit_esp32spi.ESP_SPIcontrol(spi, esp32_cs, esp32_ready, esp32_reset)
# new APIs
print(f'NEW {radio.connect(os.getenv("WIFI_SSID"), os.getenv("WIFI_PASSWORD"))=}')
print(f'{radio.connected=}')
print(f'{radio.ipv4_address=}')
print(f'\nradio.disconnect()=')
print(f'NEW {radio.connect(os.getenv("WIFI_SSID"), os.getenv("WIFI_PASSWORD"), timeout=10)=}')
print(f'{radio.connected=}')
print(f'{radio.ipv4_address=}')
# compatibility:
print(f'\nradio.disconnect()=')
print(f'ORIG {radio.connect_AP(os.getenv("WIFI_SSID"), os.getenv("WIFI_PASSWORD"))=}')
print(f'{radio.connect_AP(os.getenv("WIFI_SSID"), os.getenv("WIFI_PASSWORD"))=}')
print(f'{radio.is_connected=}')
print(f'{radio.ip_address=}')
print(f'{radio.pretty_ip(radio.ip_address)=}')
print(f'{radio.status=}') # 3 == WL_CONNECTED
print(f'\nradio.disconnect()=')
print(f'ORIG {radio.connect_AP(os.getenv("WIFI_SSID"), os.getenv("WIFI_PASSWORD"), timeout_s=10)=}')
print(f'{radio.is_connected=}')
print(f'{radio.ip_address=}')
print(f'{radio.pretty_ip(radio.ip_address)=}')
print(f'{radio.status=}') # 3 == WL_CONNECTED
# these should never disagree:
# esp.connected
# esp.is_connected
# esp.status == 3: # "WL_CONNECTED"
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since there are still a number of differences between this API and Radio
, I was thinking about making it upward compatible for a time, change all the examples, add new Radio
-compatible API calls, and then remove the old ones. For instance, ESP32SPI has create_AP()
, Radio
has start_ap()
, etc. Radio
also has stop_ap()
, etc.; this does not, but could it?
This could be done in the existing class, or we could make a new wrapper class
around adafruit_esp32spi
, calling it, say radio
, and mimic wifi.radio
. This would increase the code size, though.
It would grow during the overlap interval. Yes, there are a number of functions that could be normalized ...though not all params would necessarily match in either direction. When it gets into the socket stuff... that's a whole other ballgame. |
NINA could be a bottleneck for some missing functions (most NINA functions are by now exposed in ESP32SPI, but not 100%). For example, no stop_AP in NINA, but maybe could fudge it by setting an invalid AP and ignore the error (or just esp.reset) ...could get a little weird. |
@dhalbert do you know if we added the wrapper if it would fit in all the places it's frozen? |
Yes, I'd be worried about that, so maybe we have to do it all at once. But we'd need to measure the sizes. |
@anecdata Another thing I was thinking about was, for a transition period, making |
I had thought about that, and it was my first inclination. Then it seemed like the other vestiges of the |
I will take a look at the guides tomorrow and see how much needs to be changed. |
I did a quick Github search on the guides repo, and it looks pretty prevalent there. I should be able to work up a code option tomorrow for handling both cases, I've seen it in libraries for other situations. |
I am willing to change all the guides examples coincident with the library release. |
Hmmm, I'm thinking. Do we really want to name it from adafruit_esp32spi import radio
??? = radio(spi_bus, esp32_cs, esp32_ready, esp32_reset) or add a imoprt adafruit_esp32spi
radio = adafruit_esp32spi.radio(spi_bus, esp32_cs, esp32_ready, esp32_reset) And this started as a thought if we want to rename |
Just in case we want to support def connect(self, *args, timeout=10):
"""Connect to an access point with given name and password."""
if len(args) == 2:
ssid, password = args
elif len(args) == 1:
if isinstance(args[0], dict): # secrets
ssid, password = args[0]["ssid"], args[0]["password"]
elif isinstance(args[0], str): # open AP
ssid, password = args[0], None
else:
raise ConnectionError("Invalid credentials format")
else:
raise ConnectionError("Invalid credentials format")
self.connect_AP(ssid, password, timeout_s=timeout) ( It handles all three cases:
|
I was also thinking whether we might just start a new library that uses the low-level SPI code here but has much more of a |
Should I close this in favor of one for the alternatives for some future |
I personally would love to see this go in as is, and then build a better one... Happy to go update docs |
No, I think we can do this as is. I will look tomorrow about how many guides need to be edited, and plan time tomorrow or soon after to merge this, push the release and then edit the guides immediately. |
OK, this is a problem: ESP32SPI is frozen into several boards: PyPortal, Titano, and MatrixPortal M4, and the i.MX 101x boards. Those boards need a frozen version because they don't have enough RAM to support a non-frozen version for other than small programs. If we change the API now, and change the Guide code, then the examples will no longer work on those boards with 9.0.x stable builds. So I propose:
|
So like this: def connect(self, ssid, password, timeout=10):
"""Connect to an access point with given name and password."""
# previous versions of connect were passed in a secrets dict.
# temporarily continue supporting this
if isinstance(ssid, dict):
ssid = ssid["ssid"]
password = ssid["password"]
self.connect_AP(ssid, password, timeout_s=timeout) |
@justmobilize I think your version would require a second argument in the case when just a dict was passed in. See #199 (comment). I think that could also be done with |
Also, finished testing. The only thing I might add is updating mac address:
and so would do:
|
True, I like |
OK, showing my ignorance, but if password is a kwarg in the function def, will it work if passed as a non-keyword arg? edit:: n/m, TIL >>> def myfunc(a, b=None, t=10):
... print(a, b, t)
>>> myfunc("ssid", "password", 10)
ssid password 10 |
Is this what you mean? (EDIT: fixed example) >>> def connect(ssid, password=None, timeout=10):
... print(ssid, password, timeout)
...
>>> connect("myssid")
myssid None 10
>>> connect("myssid", "mypassword")
myssid mypassword 10
>>> connect()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: connect() missing 1 required positional argument: 'ssid' |
Yes, forgot about the whole * and kw-only thing, thanks. |
@anecdata, I fixed my example above. |
Shouldn't it be keyword-only? |
Oh, it may break backwards compatibility if it fully matches native. |
We can fix that when we remove the dict capability and rewrite the examples and Guide code. |
OK, this is odd, I'm almost certain it used to work: print(f'MAC (actual) {radio.MAC_address_actual=}') prints:
edit: probably nobody noticed since it's almost always iterated and converted to hex, I'll fix that while I'm in there |
…crets` dict, `ssid, password`, and `ssid`-only (open) all work as before.
Co-authored-by: Justin Myers <justmobilize@users.noreply.github.com>
(I fixed |
@justmobilize would you mind re-testing in case I missed something? |
Yup. On it |
@anecdata looks good. Added this test to what you have above:
|
Yes, good point, I didn't update the original posted test code when we changed the direction. Also tested a local open network: |
Co-authored-by: Justin Myers <justmobilize@users.noreply.github.com>
secrets
and improve native wifi API compatibility.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I added some more documentation about connect()
. Thanks @anecdata and @justmobilize for all your work on this. It was tricky to decide what to do.
Updating https://github.com/adafruit/Adafruit_CircuitPython_ESP32SPI to 8.1.0 from 8.0.0: > Merge pull request adafruit/Adafruit_CircuitPython_ESP32SPI#199 from anecdata/connect Updating https://github.com/adafruit/Adafruit_CircuitPython_AzureIoT to 2.6.0 from 2.5.18: > Merge pull request adafruit/Adafruit_CircuitPython_AzureIoT#65 from justmobilize/fix-esp32spi Updating https://github.com/adafruit/Adafruit_CircuitPython_Bundle/circuitpython_library_list.md to NA from NA: > Updated download stats for the libraries
•
connect
now matches (subset of) native wifi API, and deprecates the old secrets-dict-based API,but does still return theand (usually) returnsesp.status
instead of the nativewifi
None
None
.• new
ipv4_address
matches native wifi API• new
connected
matches native wifi APIThis allows the following code, regardless of whether the board has native
wifi
, or an ESP32SPI (Airlift) co-processor:esp32spi_simpletest.py
works fine: it putssettings.toml
credentials into asecrets
dict, then passes the elements of that dict to theconnect_AP()
param fields. If we want to encourage the native methods, or deprecate the old methods someday, the examples could be changed.