diff --git a/examples/rp2040/pico-w-sdk-builtin/CMakeLists.txt b/examples/rp2040/pico-w-sdk-builtin/CMakeLists.txt new file mode 100644 index 00000000..035f8264 --- /dev/null +++ b/examples/rp2040/pico-w-sdk-builtin/CMakeLists.txt @@ -0,0 +1,29 @@ +cmake_minimum_required(VERSION 3.13) +include(pico-sdk/pico_sdk_init.cmake) + +project(firmware) +pico_sdk_init() + +add_executable(firmware + driver_pico-w.c + main.c + mongoose.c + net.c + packed_fs.c +) + +target_include_directories(firmware PUBLIC + . +) + +target_link_libraries(firmware hardware_pio hardware_dma pico_stdlib cyw43_driver_picow) +pico_add_extra_outputs(firmware) # create map/bin/hex file etc. + +pico_enable_stdio_usb(firmware 1) # Route stdio +pico_enable_stdio_uart(firmware 0) # to USB + +# Mongoose build flags in mongoose_config.h + +# Example build options +add_definitions(-DHTTP_URL="http://0.0.0.0/") + diff --git a/examples/rp2040/pico-w-sdk-builtin/Makefile b/examples/rp2040/pico-w-sdk-builtin/Makefile new file mode 100644 index 00000000..1149396c --- /dev/null +++ b/examples/rp2040/pico-w-sdk-builtin/Makefile @@ -0,0 +1,22 @@ +RM = rm -rf +MKBUILD = rm -rf build && mkdir -p build # test -d build || mkdir build +ifeq ($(OS),Windows_NT) + RM = cmd /C del /Q /F /S + MKBUILD = if not exist build mkdir build +endif + +all example: + true + +build: pico-sdk build/firmware.uf2 + +build/firmware.uf2: + $(MKBUILD) + cd build && cmake -G "Unix Makefiles" .. && make + +pico-sdk: + git clone --depth 1 -b 1.5.1 https://github.com/raspberrypi/pico-sdk $@ + cd $@ && git submodule update --init + +clean: + $(RM) pico-sdk build diff --git a/examples/rp2040/pico-w-sdk-builtin/README.md b/examples/rp2040/pico-w-sdk-builtin/README.md new file mode 100644 index 00000000..c32e5f6b --- /dev/null +++ b/examples/rp2040/pico-w-sdk-builtin/README.md @@ -0,0 +1,4 @@ + +# Mongoose on PICO W + + diff --git a/examples/rp2040/pico-w-sdk-builtin/cyw43_configport.h b/examples/rp2040/pico-w-sdk-builtin/cyw43_configport.h new file mode 100644 index 00000000..21c38695 --- /dev/null +++ b/examples/rp2040/pico-w-sdk-builtin/cyw43_configport.h @@ -0,0 +1,164 @@ +/* + * Copyright (c) 2022 Raspberry Pi (Trading) Ltd. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +// This header is included by cyw43_driver to setup its environment + +// THIS FILE HAS BEEN EDITED, ORIGINAL FILE IN PICO-SDK + +#ifndef _CYW43_CONFIGPORT_H +#define _CYW43_CONFIGPORT_H + +#include "boards/pico_w.h" +#include "hardware/gpio.h" +#include "pico/error.h" +#include "pico/time.h" + +#ifdef __cplusplus +extern "C" { +#endif + +// Use our own TCP/IP stack +#define CYW43_LWIP 0 + +#ifndef CYW43_HOST_NAME +#define CYW43_HOST_NAME "Mongoose" +#endif + +#ifndef CYW43_GPIO +#define CYW43_GPIO 1 +#endif + +#ifndef CYW43_LOGIC_DEBUG +#define CYW43_LOGIC_DEBUG 0 +#endif + +#ifndef CYW43_USE_OTP_MAC +#define CYW43_USE_OTP_MAC 1 +#endif + +#ifndef CYW43_NO_NETUTILS +#define CYW43_NO_NETUTILS 1 +#endif + +#ifndef CYW43_IOCTL_TIMEOUT_US +#define CYW43_IOCTL_TIMEOUT_US 1000000 +#endif + +#ifndef CYW43_USE_STATS +#define CYW43_USE_STATS 0 +#endif + +// todo should this be user settable? +#ifndef CYW43_HAL_MAC_WLAN0 +#define CYW43_HAL_MAC_WLAN0 0 +#endif + +#ifndef STATIC +#define STATIC static +#endif + +#ifndef CYW43_USE_SPI +#define CYW43_USE_SPI 1 +#endif + +#ifndef CYW43_SPI_PIO +#define CYW43_SPI_PIO 1 +#endif + +#ifndef CYW43_CHIPSET_FIRMWARE_INCLUDE_FILE +#if CYW43_ENABLE_BLUETOOTH +#define CYW43_CHIPSET_FIRMWARE_INCLUDE_FILE "wb43439A0_7_95_49_00_combined.h" +#else +#define CYW43_CHIPSET_FIRMWARE_INCLUDE_FILE "w43439A0_7_95_49_00_combined.h" +#endif +#endif + +#ifndef CYW43_WIFI_NVRAM_INCLUDE_FILE +#define CYW43_WIFI_NVRAM_INCLUDE_FILE "wifi_nvram_43439.h" +#endif + +// Note, these are negated, because cyw43_driver negates them before returning! +#define CYW43_EPERM (-PICO_ERROR_NOT_PERMITTED) // Operation not permitted +#define CYW43_EIO (-PICO_ERROR_IO) // I/O error +#define CYW43_EINVAL (-PICO_ERROR_INVALID_ARG) // Invalid argument +#define CYW43_ETIMEDOUT (-PICO_ERROR_TIMEOUT) // Connection timed out + +#define CYW43_NUM_GPIOS CYW43_WL_GPIO_COUNT + +#define cyw43_hal_pin_obj_t uint + +// get the number of elements in a fixed-size array +#define CYW43_ARRAY_SIZE(a) count_of(a) + +static inline uint32_t cyw43_hal_ticks_us(void) { + return time_us_32(); +} + +static inline uint32_t cyw43_hal_ticks_ms(void) { + return to_ms_since_boot(get_absolute_time()); +} + +static inline int cyw43_hal_pin_read(cyw43_hal_pin_obj_t pin) { + return gpio_get(pin); +} + +static inline void cyw43_hal_pin_low(cyw43_hal_pin_obj_t pin) { + gpio_clr_mask(1 << pin); +} + +static inline void cyw43_hal_pin_high(cyw43_hal_pin_obj_t pin) { + gpio_set_mask(1 << pin); +} + +#define CYW43_HAL_PIN_MODE_INPUT (GPIO_IN) +#define CYW43_HAL_PIN_MODE_OUTPUT (GPIO_OUT) + +#define CYW43_HAL_PIN_PULL_NONE (0) +#define CYW43_HAL_PIN_PULL_UP (1) +#define CYW43_HAL_PIN_PULL_DOWN (2) + +static inline void cyw43_hal_pin_config(cyw43_hal_pin_obj_t pin, uint32_t mode, uint32_t pull, __unused uint32_t alt) { + assert((mode == CYW43_HAL_PIN_MODE_INPUT || mode == CYW43_HAL_PIN_MODE_OUTPUT) && alt == 0); + gpio_set_dir(pin, mode); + gpio_set_pulls(pin, pull == CYW43_HAL_PIN_PULL_UP, pull == CYW43_HAL_PIN_PULL_DOWN); +} + +void cyw43_hal_get_mac(int idx, uint8_t buf[6]); + +void cyw43_hal_generate_laa_mac(int idx, uint8_t buf[6]); + + +// documentation is king +#define CYW43_THREAD_ENTER +#define CYW43_THREAD_EXIT +#define CYW43_THREAD_LOCK_CHECK + +// ??? and the ';' at the end is needed +#define CYW43_SDPCM_SEND_COMMON_WAIT (void) 0; +#define CYW43_DO_IOCTL_WAIT (void) 0; + +#define cyw43_delay_ms sleep_ms +#define cyw43_delay_us sleep_us +static inline void cyw43_schedule_internal_poll_dispatch(void (*func)(void)){ + (void)func; // do nothing, we'll call it anyway +} + +#define CYW43_POST_POLL_HOOK + +// Allow malloc and free to be changed +#ifndef cyw43_malloc +#define cyw43_malloc malloc +#endif +#ifndef cyw43_free +#define cyw43_free free +#endif + +#ifdef __cplusplus +} +#endif + + +#endif diff --git a/examples/rp2040/pico-w-sdk-builtin/driver_pico-w.c b/examples/rp2040/pico-w-sdk-builtin/driver_pico-w.c new file mode 100644 index 00000000..3743214b --- /dev/null +++ b/examples/rp2040/pico-w-sdk-builtin/driver_pico-w.c @@ -0,0 +1,79 @@ +// Copyright (c) 2024 Cesanta Software Limited +// All rights reserved + +#include +#include +#include + +#include "pico/stdlib.h" +#include "pico/unique_id.h" +#include "cyw43.h" +#include "cyw43_country.h" + +#include "mongoose.h" +#include "driver_pico-w.h" + + +static struct mg_tcpip_if *s_ifp; + +static bool mg_tcpip_driver_pico_w_init(struct mg_tcpip_if *ifp) { + struct mg_tcpip_driver_pico_w_data *d = (struct mg_tcpip_driver_pico_w_data *) ifp->driver_data; + s_ifp = ifp; + // initialize WiFi chip and connect to network + cyw43_init(&cyw43_state); + cyw43_wifi_set_up(&cyw43_state, CYW43_ITF_STA, true, CYW43_COUNTRY_WORLDWIDE); + cyw43_wifi_join(&cyw43_state, strlen(d->ssid), d->ssid, strlen(d->pass), d->pass, CYW43_AUTH_WPA2_AES_PSK, NULL, CYW43_ITF_STA); + cyw43_wifi_get_mac(&cyw43_state, CYW43_ITF_STA, ifp->mac); + return true; +} + +static size_t mg_tcpip_driver_pico_w_tx(const void *buf, size_t len, + struct mg_tcpip_if *ifp) { + return cyw43_send_ethernet(&cyw43_state, CYW43_ITF_STA, len, buf, false) ? 0 : len; + (void) ifp; +} + +static bool mg_tcpip_driver_pico_w_up(struct mg_tcpip_if *ifp) { + return cyw43_wifi_link_status(&cyw43_state, CYW43_ITF_STA) == CYW43_LINK_JOIN ? 1 : 0; +} + +void driver_pico_w_poll(void) { + cyw43_poll(); +} + +struct mg_tcpip_driver mg_tcpip_driver_pico_w = { + mg_tcpip_driver_pico_w_init, + mg_tcpip_driver_pico_w_tx, + NULL, + mg_tcpip_driver_pico_w_up, +}; + +void cyw43_cb_tcpip_init(cyw43_t *self, int itf) {} +void cyw43_cb_tcpip_deinit(cyw43_t *self, int itf) {} +void cyw43_cb_tcpip_set_link_up(cyw43_t *self, int itf) {} +void cyw43_cb_tcpip_set_link_down(cyw43_t *self, int itf) {} + +// Called once per outstanding frame during a call to cyw43_poll +void cyw43_cb_process_ethernet(void *cb_data, int itf, size_t len, const uint8_t *buf) { + if (itf != CYW43_ITF_STA) return; + mg_tcpip_qwrite((void *) buf, len, s_ifp); + (void) cb_data; +} + +// Return mac address +void cyw43_hal_get_mac(__unused int idx, uint8_t buf[6]) { + memcpy(buf, cyw43_state.mac, 6); +} + +// Generate a mac address if one is not set in otp +void cyw43_hal_generate_laa_mac(__unused int idx, uint8_t buf[6]) { + pico_unique_board_id_t board_id; + MG_DEBUG(("No MAC in cyw43 OTP, generated from board id")); + pico_get_unique_board_id(&board_id); + memcpy(buf, &board_id.id[2], 6); + buf[0] &= (uint8_t)~0x1; // unicast + buf[0] |= 0x2; // locally administered +} + +// there's life beyond lwIP +void pbuf_copy_partial(void){(void)0;} diff --git a/examples/rp2040/pico-w-sdk-builtin/driver_pico-w.h b/examples/rp2040/pico-w-sdk-builtin/driver_pico-w.h new file mode 100644 index 00000000..ce0e4756 --- /dev/null +++ b/examples/rp2040/pico-w-sdk-builtin/driver_pico-w.h @@ -0,0 +1,10 @@ +#pragma once + +struct mg_tcpip_driver_pico_w_data { + char *ssid; + char *pass; +}; + +extern struct mg_tcpip_driver mg_tcpip_driver_pico_w; + +void driver_pico_w_poll(void); diff --git a/examples/rp2040/pico-w-sdk-builtin/main.c b/examples/rp2040/pico-w-sdk-builtin/main.c new file mode 100644 index 00000000..bda3da0c --- /dev/null +++ b/examples/rp2040/pico-w-sdk-builtin/main.c @@ -0,0 +1,52 @@ +// Copyright (c) 2024 Cesanta Software Limited +// All rights reserved + +#include "pico/stdlib.h" + +#include "mongoose.h" +#include "net.h" + +#include "driver_pico-w.h" + +#define WIFI_SSID "yourWiFiSSID" +#define WIFI_PASS "yourWiFiPassword" + + +int main(void) { + // initialize stdio + stdio_init_all(); + + struct mg_mgr mgr; // Initialise Mongoose event manager + mg_mgr_init(&mgr); // and attach it to the interface + mg_log_set(MG_LL_DEBUG); // Set log level + + // Initialise WiFi creds + struct mg_tcpip_driver_pico_w_data driver_data = { + .ssid = WIFI_SSID, + .pass = WIFI_PASS + }; + // 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 + }; + + mg_tcpip_init(&mgr, &mif); + MG_INFO(("Init done, starting main loop")); + + MG_INFO(("Initialising application...")); + web_init(&mgr); + + MG_INFO(("Starting event loop")); + for (;;) { + driver_pico_w_poll(); + mg_mgr_poll(&mgr, 0); + } + + return 0; +} + diff --git a/examples/rp2040/pico-w-sdk-builtin/mongoose.c b/examples/rp2040/pico-w-sdk-builtin/mongoose.c new file mode 120000 index 00000000..5e522bbc --- /dev/null +++ b/examples/rp2040/pico-w-sdk-builtin/mongoose.c @@ -0,0 +1 @@ +../../../mongoose.c \ No newline at end of file diff --git a/examples/rp2040/pico-w-sdk-builtin/mongoose.h b/examples/rp2040/pico-w-sdk-builtin/mongoose.h new file mode 120000 index 00000000..ee4ac823 --- /dev/null +++ b/examples/rp2040/pico-w-sdk-builtin/mongoose.h @@ -0,0 +1 @@ +../../../mongoose.h \ No newline at end of file diff --git a/examples/rp2040/pico-w-sdk-builtin/mongoose_config.h b/examples/rp2040/pico-w-sdk-builtin/mongoose_config.h new file mode 100644 index 00000000..25ac94af --- /dev/null +++ b/examples/rp2040/pico-w-sdk-builtin/mongoose_config.h @@ -0,0 +1,5 @@ +#define MG_ARCH MG_ARCH_RP2040 + +#define MG_ENABLE_TCPIP 1 +#define MG_ENABLE_TCPIP_DRIVER_INIT 0 +#define MG_ENABLE_PACKED_FS 1 diff --git a/examples/rp2040/pico-w-sdk-builtin/net.c b/examples/rp2040/pico-w-sdk-builtin/net.c new file mode 120000 index 00000000..fe0e6f06 --- /dev/null +++ b/examples/rp2040/pico-w-sdk-builtin/net.c @@ -0,0 +1 @@ +../../device-dashboard/net.c \ No newline at end of file diff --git a/examples/rp2040/pico-w-sdk-builtin/net.h b/examples/rp2040/pico-w-sdk-builtin/net.h new file mode 120000 index 00000000..9de896ef --- /dev/null +++ b/examples/rp2040/pico-w-sdk-builtin/net.h @@ -0,0 +1 @@ +../../device-dashboard/net.h \ No newline at end of file diff --git a/examples/rp2040/pico-w-sdk-builtin/packed_fs.c b/examples/rp2040/pico-w-sdk-builtin/packed_fs.c new file mode 120000 index 00000000..e06bf092 --- /dev/null +++ b/examples/rp2040/pico-w-sdk-builtin/packed_fs.c @@ -0,0 +1 @@ +../../device-dashboard/packed_fs.c \ No newline at end of file diff --git a/examples/rp2040/pico-w-sdk-builtin/pico_sdk_import.cmake b/examples/rp2040/pico-w-sdk-builtin/pico_sdk_import.cmake new file mode 100644 index 00000000..65f8a6f7 --- /dev/null +++ b/examples/rp2040/pico-w-sdk-builtin/pico_sdk_import.cmake @@ -0,0 +1,73 @@ +# 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 () + +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") + +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 master + GIT_SUBMODULES_RECURSE FALSE + ) + else () + FetchContent_Declare( + pico_sdk + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG master + ) + 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})