Skip to content

Commit 37d3ea9

Browse files
committed
Merge pull request #862 from pguyot/w40/picow-networking-add-ap-connection
Add AP mode support for Pico-W Add the ability to configure the Pico-W wireless chipset in AP mode. There is no DHCP server yet and stations cannot obtain an IP. Also it is possible to configure the network driver in AP+STA mode, however this does not work. It is still unclear whether this is a limitation of the chipset. These changes are made under both the "Apache 2.0" and the "GNU Lesser General Public License 2.1 or later" license terms (dual license). SPDX-License-Identifier: Apache-2.0 OR LGPL-2.1-or-later
2 parents 540a075 + e0cd982 commit 37d3ea9

File tree

5 files changed

+217
-22
lines changed

5 files changed

+217
-22
lines changed

examples/erlang/rp2040/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,3 +27,4 @@ pack_uf2(pico_blink pico_blink)
2727
pack_uf2(pico_rtc pico_rtc)
2828
pack_uf2(picow_blink picow_blink)
2929
pack_uf2(picow_wifi_sta picow_wifi_sta)
30+
pack_uf2(picow_wifi_ap picow_wifi_ap)
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
%
2+
% This file is part of AtomVM.
3+
%
4+
% Copyright 2023 Paul Guyot <[email protected]>
5+
%
6+
% Licensed under the Apache License, Version 2.0 (the "License");
7+
% you may not use this file except in compliance with the License.
8+
% You may obtain a copy of the License at
9+
%
10+
% http://www.apache.org/licenses/LICENSE-2.0
11+
%
12+
% Unless required by applicable law or agreed to in writing, software
13+
% distributed under the License is distributed on an "AS IS" BASIS,
14+
% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
% See the License for the specific language governing permissions and
16+
% limitations under the License.
17+
%
18+
% SPDX-License-Identifier: Apache-2.0 OR LGPL-2.1-or-later
19+
%
20+
21+
-module(picow_wifi_ap).
22+
23+
-export([start/0]).
24+
25+
start() ->
26+
Config = [
27+
{ap, [
28+
%% If an SSID is not specified, AtomVM will default to atomvm-<hexmac>
29+
%% where <hexmac> is the hexadecimal representation of the factory-supplied
30+
%% MAC address of the Pico-W device.
31+
%% If a password is not specified, the AP network will be open, with
32+
%% no encryption or authentication (strongly discouraged)
33+
{ap_started, fun ap_started/0},
34+
{sta_connected, fun sta_connected/1},
35+
{sta_ip_assigned, fun sta_ip_assigned/1},
36+
{sta_disconnected, fun sta_disconnected/1}
37+
]}
38+
],
39+
case network:start(Config) of
40+
{ok, _Pid} ->
41+
timer:sleep(infinity);
42+
Error ->
43+
erlang:display(Error)
44+
end.
45+
46+
ap_started() ->
47+
io:format("AP started.\n").
48+
49+
sta_connected(Mac) ->
50+
io:format("STA connected with mac ~w\n", [Mac]).
51+
52+
sta_disconnected(Mac) ->
53+
io:format("STA disconnected with mac ~w\n", [Mac]).
54+
55+
sta_ip_assigned(Address) ->
56+
io:format("STA assigned address ~p\n", [Address]).

src/platforms/rp2040/pico_sdk_import.cmake

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,14 +58,14 @@ if (NOT PICO_SDK_PATH)
5858
FetchContent_Declare(
5959
pico_sdk
6060
GIT_REPOSITORY https://github.com/pguyot/pico-sdk # revert to raspberrypi once PR 1101 is merged
61-
GIT_TAG v1.5.1+conditional-variables # revert to master once PR 1101 is merged
61+
GIT_TAG v1.5.1+conditional-variables+cyw43-assoc-callback # revert to master once pico-sdk PR 1101 is merged + cyw43-driver PR 91
6262
GIT_SUBMODULES_RECURSE FALSE
6363
)
6464
else ()
6565
FetchContent_Declare(
6666
pico_sdk
6767
GIT_REPOSITORY https://github.com/pguyot/pico-sdk # revert to raspberrypi once PR 1101 is merged
68-
GIT_TAG v1.5.1+conditional-variables # revert to master once PR 1101 is merged
68+
GIT_TAG v1.5.1+conditional-variables+cyw43-assoc-callback # revert to master once pico-sdk PR 1101 is merged + cyw43-driver PR 91
6969
)
7070
endif ()
7171

src/platforms/rp2040/src/lib/lwipopts.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@
4848
#define LWIP_NETIF_LINK_CALLBACK 1
4949
#define LWIP_NETIF_HOSTNAME 1
5050
#define LWIP_NETCONN 0
51+
#define CYW43_ASSOC_CALLBACK 1
5152
#define MEM_STATS 0
5253
#define SYS_STATS 0
5354
#define MEMP_STATS 0

src/platforms/rp2040/src/lib/networkdriver.c

Lines changed: 157 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,9 @@
3838
#define PORT_REPLY_SIZE (TUPLE_SIZE(2) + REF_SIZE)
3939

4040
static const char *const ap_atom = ATOM_STR("\x2", "ap");
41+
static const char *const ap_sta_connected_atom = ATOM_STR("\x10", "ap_sta_connected");
42+
static const char *const ap_sta_disconnected_atom = ATOM_STR("\x13", "ap_sta_disconnected");
43+
static const char *const ap_started_atom = ATOM_STR("\xA", "ap_started");
4144
static const char *const psk_atom = ATOM_STR("\x3", "psk");
4245
static const char *const ssid_atom = ATOM_STR("\x4", "ssid");
4346
static const char *const sta_atom = ATOM_STR("\x3", "sta");
@@ -63,13 +66,16 @@ struct NetworkDriverData
6366
uint32_t owner_process_id;
6467
uint64_t ref_ticks;
6568
int link_status;
69+
int stas_count;
70+
uint8_t *stas_mac;
6671
};
6772

6873
// Callbacks do not allow for user data
6974
// netif->state is actually pointing to &cyw43_state
7075
static struct NetworkDriverData *driver_data;
7176

7277
static void network_driver_netif_status_cb(struct netif *netif);
78+
static void network_driver_cyw43_assoc_cb(bool assoc);
7379

7480
static term tuple_from_addr(Heap *heap, uint32_t addr)
7581
{
@@ -129,6 +135,38 @@ static void send_got_ip(struct netif *netif)
129135
END_WITH_STACK_HEAP(heap, driver_data->global);
130136
}
131137

138+
static void send_ap_started()
139+
{
140+
// {Ref, ap_started}
141+
BEGIN_WITH_STACK_HEAP(PORT_REPLY_SIZE, heap);
142+
{
143+
send_term(&heap, globalcontext_make_atom(driver_data->global, ap_started_atom));
144+
}
145+
END_WITH_STACK_HEAP(heap, driver_data->global);
146+
}
147+
148+
static void send_atom_mac(term atom, uint8_t *mac)
149+
{
150+
// {Ref, {ap_connected | ap_disconnected, <<1,2,3,4,5,6>>}}
151+
BEGIN_WITH_STACK_HEAP(PORT_REPLY_SIZE + TUPLE_SIZE(2) + TERM_BINARY_HEAP_SIZE(6), heap);
152+
{
153+
term mac_term = term_from_literal_binary(mac, 6, &heap, driver_data->global);
154+
term reply = port_heap_create_tuple2(&heap, atom, mac_term);
155+
send_term(&heap, reply);
156+
}
157+
END_WITH_STACK_HEAP(heap, driver_data->global);
158+
}
159+
160+
static void send_ap_sta_connected(uint8_t *mac)
161+
{
162+
send_atom_mac(globalcontext_make_atom(driver_data->global, ap_sta_connected_atom), mac);
163+
}
164+
165+
static void send_ap_sta_disconnected(uint8_t *mac)
166+
{
167+
send_atom_mac(globalcontext_make_atom(driver_data->global, ap_sta_disconnected_atom), mac);
168+
}
169+
132170
static term start_sta(term sta_config, GlobalContext *global)
133171
{
134172
term ssid_term = interop_kv_get_value(sta_config, ssid_atom, global);
@@ -171,32 +209,124 @@ static term start_sta(term sta_config, GlobalContext *global)
171209
return OK_ATOM;
172210
}
173211

174-
static term start_ap(term ap_config, GlobalContext *glb)
212+
static char *get_default_device_name()
175213
{
176-
UNUSED(ap_config);
177-
UNUSED(glb);
178-
return BADARG_ATOM;
214+
uint8_t mac[6];
215+
int err = cyw43_wifi_get_mac(&cyw43_state, CYW43_ITF_STA, mac);
216+
if (err) {
217+
return NULL;
218+
}
219+
220+
size_t buf_size = strlen("atomvm-") + 12 + 1;
221+
char *buf = malloc(buf_size);
222+
if (IS_NULL_PTR(buf)) {
223+
return NULL;
224+
}
225+
snprintf(buf, buf_size,
226+
"atomvm-%02x%02x%02x%02x%02x%02x", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
227+
return buf;
179228
}
180229

181-
static void network_driver_netif_status_cb(struct netif *netif)
230+
static void network_driver_cyw43_assoc_cb(bool assoc)
182231
{
183-
if (netif == &cyw43_state.netif[CYW43_ITF_STA]) {
184-
// We don't really need to lock to call cyw43_tcpip_link_status
185-
// However, we take advantage of this lock to protect driver_data->link_status.
186-
cyw43_arch_lwip_begin();
187-
int link_status = cyw43_tcpip_link_status(&cyw43_state, CYW43_ITF_STA);
188-
int previous_link_status = driver_data->link_status;
189-
driver_data->link_status = link_status;
190-
cyw43_arch_lwip_end();
191-
if (link_status != previous_link_status) {
192-
if (link_status == CYW43_LINK_DOWN) {
193-
send_sta_disconnected();
194-
} else if (link_status == CYW43_LINK_JOIN) {
195-
send_sta_connected();
196-
} else if (link_status == CYW43_LINK_UP) {
197-
send_got_ip(netif);
232+
UNUSED(assoc);
233+
234+
int max_stas;
235+
cyw43_wifi_ap_get_max_stas(&cyw43_state, &max_stas);
236+
uint8_t *new_macs = malloc(6 * max_stas);
237+
int nb_stas;
238+
cyw43_wifi_ap_get_stas(&cyw43_state, &nb_stas, new_macs);
239+
// Determine new macs.
240+
for (int i = 0; i < nb_stas; i++) {
241+
bool new_mac = true;
242+
for (int j = 0; j < driver_data->stas_count; j++) {
243+
if (memcmp(&driver_data->stas_mac[6 * j], &new_macs[6 * i], 6) == 0) {
244+
new_mac = false;
245+
break;
198246
}
199247
}
248+
if (new_mac) {
249+
send_ap_sta_connected(&new_macs[6 * i]);
250+
}
251+
}
252+
// Determine old macs
253+
for (int j = 0; j < driver_data->stas_count; j++) {
254+
bool old_mac = true;
255+
for (int i = 0; i < nb_stas; i++) {
256+
if (memcmp(&driver_data->stas_mac[6 * j], &new_macs[6 * i], 6) == 0) {
257+
old_mac = false;
258+
break;
259+
}
260+
}
261+
if (old_mac) {
262+
send_ap_sta_disconnected(&driver_data->stas_mac[6 * j]);
263+
}
264+
}
265+
free(driver_data->stas_mac);
266+
new_macs = realloc(new_macs, 6 * nb_stas);
267+
driver_data->stas_mac = new_macs;
268+
driver_data->stas_count = nb_stas;
269+
}
270+
271+
static term start_ap(term ap_config, GlobalContext *global)
272+
{
273+
term ssid_term = interop_kv_get_value(ap_config, ssid_atom, global);
274+
term pass_term = interop_kv_get_value(ap_config, psk_atom, global);
275+
276+
//
277+
// Check parameters
278+
//
279+
char *ssid = NULL;
280+
if (term_is_invalid_term(ssid_term)) {
281+
ssid = get_default_device_name();
282+
} else {
283+
int ok = 0;
284+
ssid = interop_term_to_string(ssid_term, &ok);
285+
if (!ok || IS_NULL_PTR(ssid)) {
286+
return BADARG_ATOM;
287+
}
288+
}
289+
char *psk = NULL;
290+
if (!term_is_invalid_term(pass_term)) {
291+
int ok = 0;
292+
psk = interop_term_to_string(pass_term, &ok);
293+
if (strlen(psk) < 8) {
294+
free(ssid);
295+
return BADARG_ATOM;
296+
}
297+
if (!ok) {
298+
free(ssid);
299+
return BADARG_ATOM;
300+
}
301+
}
302+
303+
uint32_t auth = (psk == NULL) ? CYW43_AUTH_OPEN : CYW43_AUTH_WPA2_AES_PSK;
304+
cyw43_state.assoc_cb = network_driver_cyw43_assoc_cb;
305+
cyw43_arch_enable_ap_mode(ssid, psk, auth);
306+
send_ap_started();
307+
free(ssid);
308+
free(psk);
309+
310+
return OK_ATOM;
311+
}
312+
313+
static void network_driver_netif_status_cb(struct netif *netif)
314+
{
315+
// We don't really need to lock to call cyw43_tcpip_link_status
316+
// However, we take advantage of this lock to protect driver_data->link_status.
317+
cyw43_arch_lwip_begin();
318+
int link_status = cyw43_tcpip_link_status(&cyw43_state, CYW43_ITF_STA);
319+
int previous_link_status = driver_data->link_status;
320+
driver_data->link_status = link_status;
321+
cyw43_arch_lwip_end();
322+
if (link_status != previous_link_status) {
323+
if (link_status == CYW43_LINK_DOWN) {
324+
send_sta_disconnected();
325+
} else if (link_status == CYW43_LINK_JOIN) {
326+
send_sta_connected();
327+
} else if (link_status == CYW43_LINK_UP) {
328+
send_got_ip(netif);
329+
}
200330
}
201331
}
202332

@@ -210,11 +340,15 @@ static void start_network(Context *ctx, term pid, term ref, term config)
210340

211341
if (driver_data == NULL) {
212342
driver_data = malloc(sizeof(struct NetworkDriverData));
343+
driver_data->stas_mac = NULL;
213344
}
214345
driver_data->global = ctx->global;
215346
driver_data->owner_process_id = term_to_local_process_id(pid);
216347
driver_data->ref_ticks = term_to_ref_ticks(ref);
217348
driver_data->link_status = CYW43_LINK_DOWN;
349+
free(driver_data->stas_mac);
350+
driver_data->stas_count = 0;
351+
driver_data->stas_mac = NULL;
218352

219353
//
220354
// Get the STA and AP config, if set
@@ -317,6 +451,9 @@ void network_driver_destroy(GlobalContext *global)
317451
{
318452
UNUSED(global);
319453

454+
if (driver_data) {
455+
free(driver_data->stas_mac);
456+
}
320457
free(driver_data);
321458
driver_data = NULL;
322459
}

0 commit comments

Comments
 (0)