-
Notifications
You must be signed in to change notification settings - Fork 1.3k
wifi.radio Access Point modes #4650
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
Conversation
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.
This looks good to me! I do like the idea of treating sta and ap modes independently. So you would be able to do AP only mode if you only start the AP.
Just a note: I can't think of any material downsides to using STA+AP mode for achieving AP mode. There is some resource load, I don't know how significant, a data structure or two. But STA+AP can be used just like AP-only, by just not invoking any STA functionality (scan, ping, connect, etc.) The trick about AP-only mode is that STA currently starts by default, so if we want to allow AP-only as opposed to STA+AP, there are two ways I can think of:
Both options add some coordination complexity that I need to investigate. BTW, I haven't actually gotten a separate station connected to the AP yet, hopefully will get some time this weekend. Addendum: I think there's a 3rd way to handle STA, without an explicit new start_station() user API needed at the top of Bottom line: I could use some architectural direction on the API / how we want these modes to behave for users, particularly on startup. Addendum 2: The nomenclature is quickly getting out of hand too - functions and variables. I've been adding Addendum 3 (from the future): Went with Option 2 above - full independence with auto-STA at init. |
Connection to AP (open wifi) working now:
CircuitPython STA+AP SSID "Bob" running a UDP socket server:
macOS wifi STA connected to CircuitPython AP SSID "Bob", Ethernet off, running CPython UDP socket client:
I think this is doing what I think it's doing. Don't worry, I won't spam every little test result :p but this was the first successful connection to the AP, with socket stuff running on top and working fine. Pleasantly surprised. Addendum: Other authmodes should be fine, tested WIFI_AUTH_WPA2_PSK successfully. Note that the ESP32-S2 can operate on only one channel at a time. If STA is connected, AP will use the same channel. If STA is not connected, AP can choose its channel. Needs more testing, only once I think I saw a connected station getting stopped when starting an AP with a different channel. But trying it more, the AP just uses the same channel as the STA. |
Current status: STA starts up by default in STA and AP can be independently started and stopped, generally with no disruption to the other. wifi mode is retained through transition from There are four wifi modes:
There's more fleshing out to do... additional AP params in |
Latest commit allows sequences like this:
|
…es since 0 is out of range, but just to be sure, default channel to 1
A couple of comments on
|
I think this is ready to come out of draft. I had little idea what I was getting into, and I don't know what I don't know. It needs careful review and test, I'm sure others will find novel ways to exercise the code ;-). An oddity... there's nothing in the Espressif NETIF or WiFi APIs to get the IP address of connected stations. Maybe I'm missing something obvious. It is possible to get their MAC address and an
Probably not a big deal when the AP is also the "server" in the IP sense since clients usually need to be directed to the server anyway, bit if it's say a UDP client, it would need to know the IP address of the UDP server running on a connected Station. The AP has default IP address Function/variable naming is a little out of control for mirror AP/Station items, suggestions welcome. I can't help think that the mode change functions could be more efficient than a bunch of |
@anecdata I could help with some testing, this is intended to work with the ESP32S2 and Pyportal? |
I'm happy to merge this quickly. It'll end up in 7.x which will need lots of testing anyway. Getting folks to try it out is the easiest way to find the issues. :-) |
jposada202020 Thank you! I have no idea about stubs. I can post some code (I'll edit this comment in a couple of minutes). The API is pretty simple. ESP32-S2-only as the Access Point. Anything can be a wifi station to it. The first step to testing is to start the AP, then connect some external station to it. That doesn't actually do anthing though, so as a second step I've been running some TCP or UDP stuff over that connection... that's a little more work to set up, but the same as on any other pair of devices. Some basic annotated code: # ESP32-S2 wifi radio SoftAP changes:
#
# the key concept is `mode`, one of: Station (STA), Access Point (AP), STA+AP, (NONE)
# before now, ESP32-S2 wifi was always in Station mode, ready to scan or connect to an AP
# now Station and AP can be independently controlled, and the ESP32-S2 can be either, both, or neither
# note that AP mode isn't a router; it's typically going to be an IP server endpoint to IP clients
#
# mode is also independent of `enabled` state, which turns wifi on or off
# mode can be changed when wifi is enabled or not enabled
#
# the maximum number of connected stations is currently 4
import wifi
import ipaddress
import socketpool
import time
from secrets import secrets
# at this point, the ESP32-S2 is running as a station (init default), and if desired can connect to any AP
# the following two lines are not new APIs, but they are useful to test in combination with mode changes:
wifi.radio.enabled = False # turns wifi off, mode is retained or can be changed while not enabled
wifi.radio.enabled = True # turns wifi back on
print("Wi-Fi Enabled?", wifi.radio.enabled)
print(dir(wifi.radio)) # useful reference
print("Stopping the (default) station...")
wifi.radio.stop_station() # now the device is in NONE mode, neither Station nor Access Point
# print("(Re-)Starting the station...")
# wifi.radio.start_station() # would restart the station and later you would have both Station and AP running
# start the AP, `channel` of your choosing, `authmode` of your choosing:
# The Access Point IP address will be 192.168.4.1
# ...if that collides with your LAN, you may need to isolate the external station from your LAN
print("Starting AP...")
wifi.radio.start_ap("Bob", "YourUncle", channel=6, authmode=wifi.radio.WPA2)
# connect from some client(s), check their interfaces to verify the wi-fi is connected, channel, authmode,...
# Fudge an HTTP response back to a browser on a connected wifi station
HOST = "" # see below
PORT = 80
TIMEOUT = None
BACKLOG = 2
MAXBUF = 1024
# get some sockets
pool = socketpool.SocketPool(wifi.radio)
print("AP IP Address:", wifi.radio.ipv4_address_ap)
print(" Gateway:", wifi.radio.ipv4_gateway_ap)
print(" Subnet:", wifi.radio.ipv4_subnet_ap)
HOST = str(wifi.radio.ipv4_address_ap)
print("Create TCP Server socket", (HOST, PORT))
s = pool.socket(pool.AF_INET, pool.SOCK_STREAM)
s.settimeout(TIMEOUT)
s.bind((HOST, PORT))
s.listen(BACKLOG)
print("Listening")
inbuf = bytearray(MAXBUF)
while True:
print("Accepting connections")
conn, addr = s.accept()
conn.settimeout(TIMEOUT)
print("Accepted from", addr)
size = conn.recv_into(inbuf, MAXBUF)
print("Received", size, "bytes")
print(inbuf[:size])
outbuf = b"HTTP/1.0 200 OK\r\n" + \
b"Connection: close\r\n" + \
b"\r\n" + \
b"<html>Hello, world!<pre>" + \
inbuf[:size] + \
b"</pre></html>"
conn.send(outbuf)
print("Sent", len(outbuf), "bytes")
conn.close()
# the rest is exercise left for the reader, could try some socket stuff
# (some examples at <https://github.com/anecdata/Socket>)
# or anything else that's convenient to set up.
print("Stopping the AP...")
wifi.radio.stop_ap() # close down the shop
# </fin> You should be able to do virtually any sequences of:
|
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.
The code structure looks good to me. One comment about the security enum values. Looks good otherwise.
Preliminary testing done in Adafruit CircuitPython 7.0.0-alpha.1-853-gcbe1aa74f on 2021-04-26; Adafruit Metro ESP32S2 with ESP32S2 Test Code>>> import wifi
>>> import ipaddress
>>> import socketpool
>>> import time
>>> wifi.radio.enabled = True
>>> wifi.radio.start_ap("Bob", "YourUncle", channel=6, authmode=wifi.radio.WPA2_PSK) Result |
@anecdata I could try different stuff but it has been a while since I do AP/Internet stuff, even with the help of your repo code... last time I was using flask, so you could put a date on that :) |
@jposada202020 Looks good, thanks! "without internet" lol, I was wondering how hard it would be to write a router or at least a packet forwarder, or if it's even possible in this environment. It would be equally useful to get confirmation that these changes didn't break various folks' existing station scan and connect code. If you have existing wifi stuff that still works fine on this version, that would be a good sign :-) |
I updated the code above to make it a little more interesting. Run it, then connect to the new AP and try in a browser to go to: |
Ok will try. Thanks for the code. :) @anecdata There you go! |
import wifi
from wifi import AuthMode
wifi.radio.start_ap("ssid", "password", authmode=[AuthMode.WPA, AuthMode.WPA2, AuthMode.PSK]) |
@tannewt Some of the authmodes are not currently supported for AP... should we remove them or comment them out in |
An exception should be raised as that is port specific. From the idf docs, I see only |
I would have thought so too, but the WPA3 modes fail with the same error in the DEBUG console as with WEP. And we also haven't implemented Enterprise. So the only working AP authmodes currently are: WPA, WPA2, WPA-WPA2, and Open. |
Yup! just checked, only these authmodes are supported. Thanks for pointing that out. |
100% agree |
I can add the exception in |
oh, you did it already @microdev1 , nice! are we ready to merge then? Tested most valid and invalid combinations of SSID, password, and AuthMode. Behaved as expected. |
{ MP_ROM_QSTR(MP_QSTR_stop_station), MP_ROM_PTR(&wifi_radio_stop_station_obj) }, | ||
{ MP_ROM_QSTR(MP_QSTR_stop_ap), MP_ROM_PTR(&wifi_radio_stop_ap_obj) }, | ||
{ MP_ROM_QSTR(MP_QSTR_start_ap), MP_ROM_PTR(&wifi_radio_start_ap_obj) }, | ||
|
||
{ MP_ROM_QSTR(MP_QSTR_connect), MP_ROM_PTR(&wifi_radio_connect_obj) }, | ||
// { MP_ROM_QSTR(MP_QSTR_connect_to_enterprise), MP_ROM_PTR(&wifi_radio_connect_to_enterprise_obj) }, | ||
|
||
{ MP_ROM_QSTR(MP_QSTR_ap_info), MP_ROM_PTR(&wifi_radio_ap_info_obj) }, |
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.
ap_info
is a bit confusing with the new api, should this be re-named?
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.
Yeah, I mentioned earlier that naming is an issue. There's a natural confusion between AP-things related to the AP an ESP32-S2 Station is connected to, vs. the ESP32-S2 as an AP. Changing it would break existing code, but now's the time if we're going to do that. Maybe connected_ap_info
or remote_ap_info
? Whatever we do, RTD and examples could be beefed up to clarify usage.
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.
We can do this in a follow up PR too.
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.
This looks good to me! Thank you!
Hello.I would like to ask how to open the AP module of IOTS2 module?Has this module been released yet? |
It's part of the |
Rough early implementation based on comments in #4246...
Current Assumptions: [outdated]
ping
requires connection to an AP just like any other socket-based activity.WIFI_EVENT_
base.netif
is created at wifi init, and destroyed at resetCurrent Status: [outdated]
wifi.radio.start_ap(ssid, pw)
starts the AP, and the AP is then visible to other devices on the network due to broadcasting beacon frameswifi.radio.get_mac_address_ap
returns the proper AP MAC address (STA MAC + 1)wifi.radio.enabled = boolean_value
toggles the wifi (AP & STA) between started and stopped; modes are retained, connections are droppedOpen Questions:
WIFI_EVENT_AP_STA[DIS]CONNECTED
,WIFI_EVENT_AP_PROBEREQRECVED
,...