From 189bef61d36bbd0ca254d066b77a1086eddd97e2 Mon Sep 17 00:00:00 2001 From: "Sergio R. Caprile" Date: Mon, 24 Mar 2025 17:49:40 -0300 Subject: [PATCH] Add support for AP in Pico 2 W (valid for Pico W) --- .../CMakeLists.txt | 21 +++ .../Makefile | 17 ++ .../main.c | 145 ++++++++++++++++++ .../mongoose.c | 1 + .../mongoose.h | 1 + .../mongoose_config.h | 26 ++++ .../pico_sdk_import.cmake | 84 ++++++++++ 7 files changed, 295 insertions(+) create mode 100644 tutorials/pico-sdk/pico-2-w-picosdk-baremetal-builtin-ap/CMakeLists.txt create mode 100644 tutorials/pico-sdk/pico-2-w-picosdk-baremetal-builtin-ap/Makefile create mode 100644 tutorials/pico-sdk/pico-2-w-picosdk-baremetal-builtin-ap/main.c create mode 120000 tutorials/pico-sdk/pico-2-w-picosdk-baremetal-builtin-ap/mongoose.c create mode 120000 tutorials/pico-sdk/pico-2-w-picosdk-baremetal-builtin-ap/mongoose.h create mode 100644 tutorials/pico-sdk/pico-2-w-picosdk-baremetal-builtin-ap/mongoose_config.h create mode 100644 tutorials/pico-sdk/pico-2-w-picosdk-baremetal-builtin-ap/pico_sdk_import.cmake diff --git a/tutorials/pico-sdk/pico-2-w-picosdk-baremetal-builtin-ap/CMakeLists.txt b/tutorials/pico-sdk/pico-2-w-picosdk-baremetal-builtin-ap/CMakeLists.txt new file mode 100644 index 00000000..187998d2 --- /dev/null +++ b/tutorials/pico-sdk/pico-2-w-picosdk-baremetal-builtin-ap/CMakeLists.txt @@ -0,0 +1,21 @@ +cmake_minimum_required(VERSION 3.13) +include(pico_sdk_import.cmake) + +project(firmware) +pico_sdk_init() + +add_executable(firmware + main.c + mongoose.c +) + +target_include_directories(firmware PUBLIC + . +) + +target_link_libraries(firmware pico_stdlib pico_rand pico_cyw43_driver pico_cyw43_arch_none) +pico_add_extra_outputs(firmware) # create map/bin/hex file etc. + +pico_enable_stdio_usb(firmware 1) # Route stdio + +add_definitions(-DMG_ENABLE_TCPIP_DRIVER_INIT=0) diff --git a/tutorials/pico-sdk/pico-2-w-picosdk-baremetal-builtin-ap/Makefile b/tutorials/pico-sdk/pico-2-w-picosdk-baremetal-builtin-ap/Makefile new file mode 100644 index 00000000..1c7bc11e --- /dev/null +++ b/tutorials/pico-sdk/pico-2-w-picosdk-baremetal-builtin-ap/Makefile @@ -0,0 +1,17 @@ +CWD = $(realpath $(PWD)) +SDK_PATH ?= $(CWD)/pico-sdk + +all example: + true + +build: build/firmware.uf2 + +build/firmware.uf2: pico-sdk main.c + mkdir -p build && cd build && PICO_SDK_PATH=$(SDK_PATH) cmake -DPICO_BOARD="pico2_w" -G "Unix Makefiles" .. && make + +pico-sdk: + git clone --depth 1 -b 2.1.0 https://github.com/raspberrypi/pico-sdk $@ + cd $@ && git submodule update --init + +clean: + rm -rf build pico-sdk diff --git a/tutorials/pico-sdk/pico-2-w-picosdk-baremetal-builtin-ap/main.c b/tutorials/pico-sdk/pico-2-w-picosdk-baremetal-builtin-ap/main.c new file mode 100644 index 00000000..47e77dd4 --- /dev/null +++ b/tutorials/pico-sdk/pico-2-w-picosdk-baremetal-builtin-ap/main.c @@ -0,0 +1,145 @@ +// Copyright (c) 2022 Cesanta Software Limited +// All rights reserved + +#include "mongoose.h" + + +#define WIFI_SSID "YOUR_WIFI_NETWORK_NAME" // SET THIS! +#define WIFI_PASS "YOUR_WIFI_PASSWORD" // SET THIS! + +static uint32_t s_ip, s_mask; + + +// mif user states +enum {AP, SCANNING, STOPPING_AP, CONNECTING, READY}; +static unsigned int state; + +static void mif_fn(struct mg_tcpip_if *ifp, int ev, void *ev_data) { + // TODO(): should we include this inside ifp ? add an fn_data ? + if (ev == MG_TCPIP_EV_ST_CHG) { + MG_INFO(("State change: %u", *(uint8_t *) ev_data)); + } + switch(state) { + case AP: // we are in AP mode, wait for a user connection to trigger a scan or a connection to a network + if (ev == MG_TCPIP_EV_ST_CHG && *(uint8_t *) ev_data == MG_TCPIP_STATE_UP) { + MG_INFO(("Access Point started")); + s_ip = ifp->ip, ifp->ip = MG_IPV4(192, 168, 169, 1); + s_mask = ifp->mask, ifp->mask = MG_IPV4(255, 255, 255, 0); + ifp->enable_dhcp_client = false; + ifp->enable_dhcp_server = true; + } else if (ev == MG_TCPIP_EV_ST_CHG && *(uint8_t *) ev_data == MG_TCPIP_STATE_READY) { + MG_INFO(("Access Point READY !")); + + // simulate user request to scan for networks + bool res = mg_wifi_scan(); + MG_INFO(("Starting scan: %s", res ? "OK":"FAIL")); + if (res) state = SCANNING; + } + break; + case SCANNING: + if (ev == MG_TCPIP_EV_WIFI_SCAN_RESULT) { + struct mg_wifi_scan_bss_data *bss = (struct mg_wifi_scan_bss_data *) ev_data; + MG_INFO(("BSS: %.*s (%u) (%M) %d dBm %u", bss->SSID.len, bss->SSID.buf, bss->channel, mg_print_mac, bss->BSSID, (int) bss->RSSI, bss->security)); + } else if (ev == MG_TCPIP_EV_WIFI_SCAN_END) { + struct mg_tcpip_driver_pico_w_data *d = (struct mg_tcpip_driver_pico_w_data *) ifp->driver_data; + MG_INFO(("Wi-Fi scan finished")); + + // simulate user selection of a network (1/2: stop AP) + bool res = mg_wifi_ap_stop(); + MG_INFO(("Manually stopping AP: %s", res ? "OK":"FAIL")); + if (res) state = STOPPING_AP; + // else we have a hw/fw problem + } + break; + case STOPPING_AP: + if (ev == MG_TCPIP_EV_ST_CHG && *(uint8_t *) ev_data == MG_TCPIP_STATE_DOWN) { + struct mg_tcpip_driver_pico_w_data *d = (struct mg_tcpip_driver_pico_w_data *) ifp->driver_data; + d->apmode = false; + + // simulate user selection of a network (2/2: actual connect) + bool res = mg_wifi_connect(d->ssid, d->pass); + MG_INFO(("Manually connecting: %s", res ? "OK":"FAIL")); + if (res) { + state = CONNECTING; + ifp->ip = s_ip; + ifp->mask = s_mask; + if (ifp->ip == 0) ifp->enable_dhcp_client = true; + ifp->enable_dhcp_server = false; + } // else manually start AP as below + } + break; + case CONNECTING: + if (ev == MG_TCPIP_EV_ST_CHG && *(uint8_t *) ev_data == MG_TCPIP_STATE_READY) { + MG_INFO(("READY!")); + state = READY; + + // simulate user code disconnection and go back to AP mode (1/2: disconnect) + bool res = mg_wifi_disconnect(); + MG_INFO(("Manually disconnecting: %s", res ? "OK":"FAIL")); + } else if (ev == MG_TCPIP_EV_WIFI_CONNECT_ERR) { + MG_ERROR(("Wi-Fi connect failed")); + // manually start AP as below + } + break; + case READY: + // go back to AP mode after a disconnection (simulation 2/2), you could retry + if (ev == MG_TCPIP_EV_ST_CHG && *(uint8_t *) ev_data == MG_TCPIP_STATE_DOWN) { + struct mg_tcpip_driver_pico_w_data *d = (struct mg_tcpip_driver_pico_w_data *) ifp->driver_data; + bool res = mg_wifi_ap_start(d->apssid, d->appass, d->apchannel); + MG_INFO(("Disconnected")); + MG_INFO(("Manually starting AP: %s", res ? "OK":"FAIL")); + if (res) { + state = AP; + d->apmode = true; + } + } + break; + } +} + + +int main(void) { + // initialize stdio + stdio_init_all(); + sleep_ms(4000); + + struct mg_mgr mgr; // Initialise Mongoose event manager + mg_mgr_init(&mgr); // and attach it to the interface + + // Initialise WiFi creds + struct mg_tcpip_driver_pico_w_data driver_data = { + .ssid = WIFI_SSID, + .pass = WIFI_PASS, + .apssid = "mongoose", + .appass = "eightchars", + .security = 0, + .apsecurity = 0, + .apchannel = 10, + .apmode = true + }; + + state = driver_data.apmode ? AP : CONNECTING; + + // Initialise Mongoose network stack + // Either set use_dhcp or enter a static config. + // For static configuration, specify IP/mask/GW in network byte order + struct mg_tcpip_if mif = { + .ip = 0, + .driver = &mg_tcpip_driver_pico_w, + .driver_data = &driver_data, + .recv_queue.size = 8192, + .fn = mif_fn + }; + + mg_tcpip_init(&mgr, &mif); + MG_INFO(("Init done, starting main loop")); + + MG_INFO(("Initialising application...")); + + MG_INFO(("Starting event loop")); + for (;;) { + mg_mgr_poll(&mgr, 0); + } + + return 0; +} diff --git a/tutorials/pico-sdk/pico-2-w-picosdk-baremetal-builtin-ap/mongoose.c b/tutorials/pico-sdk/pico-2-w-picosdk-baremetal-builtin-ap/mongoose.c new file mode 120000 index 00000000..5e522bbc --- /dev/null +++ b/tutorials/pico-sdk/pico-2-w-picosdk-baremetal-builtin-ap/mongoose.c @@ -0,0 +1 @@ +../../../mongoose.c \ No newline at end of file diff --git a/tutorials/pico-sdk/pico-2-w-picosdk-baremetal-builtin-ap/mongoose.h b/tutorials/pico-sdk/pico-2-w-picosdk-baremetal-builtin-ap/mongoose.h new file mode 120000 index 00000000..ee4ac823 --- /dev/null +++ b/tutorials/pico-sdk/pico-2-w-picosdk-baremetal-builtin-ap/mongoose.h @@ -0,0 +1 @@ +../../../mongoose.h \ No newline at end of file diff --git a/tutorials/pico-sdk/pico-2-w-picosdk-baremetal-builtin-ap/mongoose_config.h b/tutorials/pico-sdk/pico-2-w-picosdk-baremetal-builtin-ap/mongoose_config.h new file mode 100644 index 00000000..5b7630ac --- /dev/null +++ b/tutorials/pico-sdk/pico-2-w-picosdk-baremetal-builtin-ap/mongoose_config.h @@ -0,0 +1,26 @@ +#pragma once + +// See https://mongoose.ws/documentation/#build-options +#define MG_ARCH MG_ARCH_PICOSDK +#define MG_OTA MG_OTA_PICOSDK + +#define MG_ENABLE_TCPIP 1 +#define MG_ENABLE_DRIVER_PICO_W 1 +#define MG_ENABLE_PACKED_FS 1 + +// For static IP configuration, define MG_TCPIP_{IP,MASK,GW} +// By default, those are set to zero, meaning that DHCP is used +// +// #define MG_TCPIP_IP MG_IPV4(192, 168, 1, 10) +// #define MG_TCPIP_GW MG_IPV4(192, 168, 1, 1) +// #define MG_TCPIP_MASK MG_IPV4(255, 255, 255, 0) + +// MAC address is read from OTP by the driver + +// Set your Wi-Fi credentials +// Using a build-time constant: +// #define MG_SET_WIFI_CREDS(ssid, pass) do { *ssid = "this"; *pass = "that"; } while (0) +// +// Using a custom function: +extern void main_setcreds(char **ssid, char **pass); +#define MG_SET_WIFI_CREDS(ssid, pass) main_setcreds(ssid, pass) diff --git a/tutorials/pico-sdk/pico-2-w-picosdk-baremetal-builtin-ap/pico_sdk_import.cmake b/tutorials/pico-sdk/pico-2-w-picosdk-baremetal-builtin-ap/pico_sdk_import.cmake new file mode 100644 index 00000000..a0721d0d --- /dev/null +++ b/tutorials/pico-sdk/pico-2-w-picosdk-baremetal-builtin-ap/pico_sdk_import.cmake @@ -0,0 +1,84 @@ +# This is a copy of /external/pico_sdk_import.cmake + +# This can be dropped into an external project to help locate this SDK +# It should be include()ed prior to project() + +if (DEFINED ENV{PICO_SDK_PATH} AND (NOT PICO_SDK_PATH)) + set(PICO_SDK_PATH $ENV{PICO_SDK_PATH}) + message("Using PICO_SDK_PATH from environment ('${PICO_SDK_PATH}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT} AND (NOT PICO_SDK_FETCH_FROM_GIT)) + set(PICO_SDK_FETCH_FROM_GIT $ENV{PICO_SDK_FETCH_FROM_GIT}) + message("Using PICO_SDK_FETCH_FROM_GIT from environment ('${PICO_SDK_FETCH_FROM_GIT}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_PATH} AND (NOT PICO_SDK_FETCH_FROM_GIT_PATH)) + set(PICO_SDK_FETCH_FROM_GIT_PATH $ENV{PICO_SDK_FETCH_FROM_GIT_PATH}) + message("Using PICO_SDK_FETCH_FROM_GIT_PATH from environment ('${PICO_SDK_FETCH_FROM_GIT_PATH}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_TAG} AND (NOT PICO_SDK_FETCH_FROM_GIT_TAG)) + set(PICO_SDK_FETCH_FROM_GIT_TAG $ENV{PICO_SDK_FETCH_FROM_GIT_TAG}) + message("Using PICO_SDK_FETCH_FROM_GIT_TAG from environment ('${PICO_SDK_FETCH_FROM_GIT_TAG}')") +endif () + +if (PICO_SDK_FETCH_FROM_GIT AND NOT PICO_SDK_FETCH_FROM_GIT_TAG) + set(PICO_SDK_FETCH_FROM_GIT_TAG "master") + message("Using master as default value for PICO_SDK_FETCH_FROM_GIT_TAG") +endif() + +set(PICO_SDK_PATH "${PICO_SDK_PATH}" CACHE PATH "Path to the Raspberry Pi Pico SDK") +set(PICO_SDK_FETCH_FROM_GIT "${PICO_SDK_FETCH_FROM_GIT}" CACHE BOOL "Set to ON to fetch copy of SDK from git if not otherwise locatable") +set(PICO_SDK_FETCH_FROM_GIT_PATH "${PICO_SDK_FETCH_FROM_GIT_PATH}" CACHE FILEPATH "location to download SDK") +set(PICO_SDK_FETCH_FROM_GIT_TAG "${PICO_SDK_FETCH_FROM_GIT_TAG}" CACHE FILEPATH "release tag for SDK") + +if (NOT PICO_SDK_PATH) + if (PICO_SDK_FETCH_FROM_GIT) + include(FetchContent) + set(FETCHCONTENT_BASE_DIR_SAVE ${FETCHCONTENT_BASE_DIR}) + if (PICO_SDK_FETCH_FROM_GIT_PATH) + get_filename_component(FETCHCONTENT_BASE_DIR "${PICO_SDK_FETCH_FROM_GIT_PATH}" REALPATH BASE_DIR "${CMAKE_SOURCE_DIR}") + endif () + # GIT_SUBMODULES_RECURSE was added in 3.17 + if (${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.17.0") + FetchContent_Declare( + pico_sdk + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG} + GIT_SUBMODULES_RECURSE FALSE + ) + else () + FetchContent_Declare( + pico_sdk + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG} + ) + endif () + + if (NOT pico_sdk) + message("Downloading Raspberry Pi Pico SDK") + FetchContent_Populate(pico_sdk) + set(PICO_SDK_PATH ${pico_sdk_SOURCE_DIR}) + endif () + set(FETCHCONTENT_BASE_DIR ${FETCHCONTENT_BASE_DIR_SAVE}) + else () + message(FATAL_ERROR + "SDK location was not specified. Please set PICO_SDK_PATH or set PICO_SDK_FETCH_FROM_GIT to on to fetch from git." + ) + endif () +endif () + +get_filename_component(PICO_SDK_PATH "${PICO_SDK_PATH}" REALPATH BASE_DIR "${CMAKE_BINARY_DIR}") +if (NOT EXISTS ${PICO_SDK_PATH}) + message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' not found") +endif () + +set(PICO_SDK_INIT_CMAKE_FILE ${PICO_SDK_PATH}/pico_sdk_init.cmake) +if (NOT EXISTS ${PICO_SDK_INIT_CMAKE_FILE}) + message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' does not appear to contain the Raspberry Pi Pico SDK") +endif () + +set(PICO_SDK_PATH ${PICO_SDK_PATH} CACHE PATH "Path to the Raspberry Pi Pico SDK" FORCE) + +include(${PICO_SDK_INIT_CMAKE_FILE})