Add RP examples

This commit is contained in:
robert
2026-05-21 03:21:16 -04:00
parent 87a0aa057d
commit ee8c6e9bb6
18 changed files with 1154 additions and 34 deletions
+29 -8
View File
@@ -1,20 +1,41 @@
# Environment setup: https://mongoose.ws/docs/getting-started/build-environment/
CWD ?= $(dir $(abspath $(lastword $(MAKEFILE_LIST))))
SDK_PATH ?= $(CWD)/pico-sdk
RM = rm -rf
MKBUILD = 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
.PHONY: build
build: pico-sdk
all build: build/firmware.uf2
build/firmware.uf2: pico_sdk_import.cmake mongoose.c mongoose.h
pico_sdk_import.cmake: $(SDK_PATH)
cp $(SDK_PATH)/external/$@ $@
mongoose.c mongoose.h:
cp ../../../$@ .
build/firmware.uf2: $(SDK_PATH) $(wildcard *.[ch]) CMakeLists.txt Makefile
$(MKBUILD)
cd build && cmake -G "Unix Makefiles" .. && make
cd build && PICO_SDK_PATH=$(SDK_PATH) cmake -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
flash: build/firmware.uf2
picotool load $< -f
clean:
$(RM) pico-sdk build
$(RM) build pico_sdk_import.cmake mongoose.c mongoose.h
fullclean: clean
$(RM) $(SDK_PATH)
$(SDK_PATH):
git clone -c advice.detachedHead=false --quiet --depth 1 -b 2.1.1 https://github.com/raspberrypi/pico-sdk $@
cd $@ && git submodule update --quiet --init
+34 -24
View File
@@ -3,30 +3,12 @@
#include "pico/stdlib.h"
#include "hardware/clocks.h"
#include "mongoose.h"
#include "driver_rp2040_rmii.h"
#include "mongoose.h"
#define CLKREFPIN 20 // either 20 or 22
static void fn(struct mg_connection *c, int ev, void *ev_data) {
if (ev == MG_EV_HTTP_MSG) {
mg_http_reply(c, 200, "", "ok\n");
}
}
int main(void) {
// use the RMII reference clock (50MHz) as our system clock
clock_configure_gpin(clk_sys, CLKREFPIN, 50 * MHZ, 50 * MHZ);
sleep_ms(100);
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
static void driver_init(struct mg_mgr *mgr) {
MG_INFO(("Init TCP/IP"));
// Initialise Mongoose network stack and specific driver
// Set consecutive GPIOs for RMII (tx and rx) and SMI function groups
@@ -47,11 +29,39 @@ int main(void) {
.driver = &mg_tcpip_driver_rp2040_rmii,
.driver_data = &driver_data,
};
mg_tcpip_init(&mgr, &mif);
mg_http_listen(&mgr, "http://0.0.0.0", fn, NULL); // HTTP listener
MG_INFO(("Init done, starting main loop"));
mg_tcpip_init(mgr, &mif);
}
for (;;) mg_mgr_poll(&mgr, 0); // Infinite event loop
static void http_ev_handler(struct mg_connection *c, int ev, void *ev_data) {
if (ev == MG_EV_HTTP_MSG) { // New HTTP request received
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
uint64_t tick = to_ms_since_boot(get_absolute_time());
if (mg_match(hm->uri, mg_str("/api/tick"), NULL)) {
mg_http_reply(c, 200, "", "{%m:%llu}\n", MG_ESC("tick"), tick);
} else {
mg_http_reply(c, 200, "", "Hi from Mongoose, tick %llu\n", tick);
}
}
}
// In an RTOS environment, give this task 8 KB of stack space.
static void run_mongoose(void) {
struct mg_mgr mgr;
mg_mgr_init(&mgr);
driver_init(&mgr);
mg_log_set(MG_LL_DEBUG); // Set log level
MG_INFO(("Hardware initialised, starting firmware..."));
mg_http_listen(&mgr, "http://0.0.0.0", http_ev_handler, NULL);
for (;;) {
mg_mgr_poll(&mgr, 1);
}
}
int main(void) {
// use the RMII reference clock (50MHz) as our system clock
clock_configure_gpin(clk_sys, CLKREFPIN, 50 * MHZ, 50 * MHZ);
sleep_ms(100);
stdio_init_all();
run_mongoose();
return 0;
}
-1
View File
@@ -1 +0,0 @@
../../../mongoose.c
-1
View File
@@ -1 +0,0 @@
../../../mongoose.h
@@ -0,0 +1,33 @@
cmake_minimum_required(VERSION 3.13)
include(pico-sdk/pico_sdk_init.cmake)
project(firmware)
pico_sdk_init()
add_executable(firmware
main.c
msc_disk.c
usb_descriptors.c
mongoose.c
pico-sdk/lib/tinyusb/lib/networking/rndis_reports.c)
target_include_directories(firmware PUBLIC
.
pico-sdk/lib/tinyusb/lib/networking)
target_link_libraries(firmware pico_stdlib pico_rand hardware_spi tinyusb_device)
pico_add_extra_outputs(firmware) # create map/bin/hex file etc.
pico_enable_stdio_usb(firmware 0) # Route stdio
pico_enable_stdio_uart(firmware 1) # to the UART
# Mongoose build flags in mongoose_config.h
# Example build options
add_definitions(-DHTTP_URL="http://0.0.0.0/")
# Extra build flags (enable if needed)
add_definitions(-DDUAL_CONFIG=0)
# Prevent extreme optimization from breaking OTA
target_compile_options(firmware PRIVATE -fno-tree-loop-distribute-patterns)
+41
View File
@@ -0,0 +1,41 @@
# Environment setup: https://mongoose.ws/docs/getting-started/build-environment/
CWD ?= $(dir $(abspath $(lastword $(MAKEFILE_LIST))))
SDK_PATH ?= $(CWD)/pico-sdk
RM = rm -rf
MKBUILD = test -d build || mkdir build
ifeq ($(OS),Windows_NT)
RM = cmd /C del /Q /F /S
MKBUILD = if not exist build mkdir build
endif
.PHONY: build
all build: build/firmware.uf2
build/firmware.uf2: pico_sdk_import.cmake mongoose.c mongoose.h
pico_sdk_import.cmake: $(SDK_PATH)
cp $(SDK_PATH)/external/$@ $@
mongoose.c mongoose.h:
cp ../../../$@ .
build/firmware.uf2: $(SDK_PATH) $(wildcard *.[ch]) CMakeLists.txt Makefile
$(MKBUILD)
cd build && PICO_SDK_PATH=$(SDK_PATH) cmake -G "Unix Makefiles" .. && make
flash: build/firmware.uf2
picotool load $< -f
clean:
$(RM) build pico_sdk_import.cmake mongoose.c mongoose.h
fullclean: clean
$(RM) $(SDK_PATH)
$(SDK_PATH):
git clone -c advice.detachedHead=false --quiet --depth 1 -b 2.1.1 https://github.com/raspberrypi/pico-sdk $@
cd $@ && git submodule update --quiet --init
+7
View File
@@ -0,0 +1,7 @@
# Device Dashboard via RNDIS on an RP2040
Your headless Raspberry Pi Pico-based hardware can also have a device dashboard on a web browser when you connect it to your computer via USB
- See detailed tutorial at https://mongoose.ws/tutorials/rp2040/pico-rndis-dashboard/
Note for Mac users: For this example to run on Mac, please set `DUAL_CONFIG=1` in `CMakeLists.txt` before building; this enables CDC-ECM. Please notice that Windows may not recognize the mass-storage device in that case.
+92
View File
@@ -0,0 +1,92 @@
// Copyright (c) 2022 Cesanta Software Limited
// All rights reserved
#include "mongoose.h"
#include "pico/stdlib.h"
#include "tusb.h"
static struct mg_tcpip_if *s_ifp;
uint8_t tud_network_mac_address[6] = {2, 2, 0x84, 0x6A, 0x96, 0};
static void blink_task(void *arg) { // Blink periodically
gpio_put(PICO_DEFAULT_LED_PIN, !gpio_get_out_level(PICO_DEFAULT_LED_PIN));
(void) arg;
}
bool tud_network_recv_cb(const uint8_t *buf, uint16_t len) {
mg_tcpip_qwrite((void *) buf, len, s_ifp);
// MG_INFO(("RECV %hu", len));
// mg_hexdump(buf, len);
tud_network_recv_renew();
return true;
}
void tud_network_init_cb(void) {}
uint16_t tud_network_xmit_cb(uint8_t *dst, void *ref, uint16_t arg) {
// MG_INFO(("SEND %hu", arg));
memcpy(dst, ref, arg);
return arg;
}
static size_t usb_tx(const void *buf, size_t len, struct mg_tcpip_if *ifp) {
if (!tud_ready()) return 0;
while (!tud_network_can_xmit(len)) tud_task();
tud_network_xmit((void *) buf, len);
(void) ifp;
return len;
}
static bool usb_poll(struct mg_tcpip_if *ifp, bool s1) {
(void) ifp;
tud_task();
return s1 ? tud_inited() && tud_ready() && tud_connected() : false;
}
static void driver_init(struct mg_mgr *mgr) {
struct mg_tcpip_driver driver = {.tx = usb_tx, .poll = usb_poll};
struct mg_tcpip_if mif = {.mac = {2, 0, 1, 2, 3, 0x77},
.ip = mg_htonl(MG_U32(192, 168, 3, 1)),
.mask = mg_htonl(MG_U32(255, 255, 255, 0)),
.enable_dhcp_server = true,
.driver = &driver,
.recv_queue.size = 4096};
s_ifp = &mif;
mg_tcpip_init(mgr, &mif);
tusb_init();
}
static void http_ev_handler(struct mg_connection *c, int ev, void *ev_data) {
if (ev == MG_EV_HTTP_MSG) { // New HTTP request received
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
uint64_t tick = to_ms_since_boot(get_absolute_time());
if (mg_match(hm->uri, mg_str("/api/tick"), NULL)) {
mg_http_reply(c, 200, "", "{%m:%llu}\n", MG_ESC("tick"), tick);
} else {
mg_http_reply(c, 200, "", "Hi from Mongoose, tick %llu\n", tick);
}
}
}
// In an RTOS environment, give this task 8 KB of stack space.
static void run_mongoose(void) {
struct mg_mgr mgr;
mg_mgr_init(&mgr);
driver_init(&mgr);
mg_log_set(MG_LL_DEBUG); // Set log level
MG_INFO(("Hardware initialised, starting firmware..."));
mg_timer_add(&mgr, 500, MG_TIMER_REPEAT, blink_task, &mgr);
mg_http_listen(&mgr, "http://0.0.0.0", http_ev_handler, NULL);
for (;;) {
mg_mgr_poll(&mgr, 1);
}
}
int main(void) {
gpio_init(PICO_DEFAULT_LED_PIN);
gpio_set_dir(PICO_DEFAULT_LED_PIN, GPIO_OUT);
stdio_init_all();
run_mongoose();
return 0;
}
@@ -0,0 +1,4 @@
#define MG_ARCH MG_ARCH_PICOSDK
#define MG_ENABLE_TCPIP 1
#define MG_ENABLE_TCPIP_DRIVER_INIT 0
+252
View File
@@ -0,0 +1,252 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2019 Ha Thach (tinyusb.org)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*/
// Modified by Cesanta Software
#include "tusb.h"
// Some MCU doesn't have enough 8KB SRAM to store the whole disk
enum
{
DISK_BLOCK_NUM = 16, // 8KB is the smallest size that Windows allows to mount
DISK_BLOCK_SIZE = 512
};
#define INDEX_CONTENTS \
"<head><meta http-equiv=\"Refresh\" content=\"0;url=http://192.168.3.1\"></head><body></body>"
// readonly
const uint8_t msc_disk0[DISK_BLOCK_NUM][DISK_BLOCK_SIZE] =
{
//------------- Block0: Boot Sector -------------//
// byte_per_sector = DISK_BLOCK_SIZE; fat12_sector_num_16 = DISK_BLOCK_NUM;
// sector_per_cluster = 1; reserved_sectors = 1;
// fat_num = 1; fat12_root_entry_num = 16;
// sector_per_fat = 1; sector_per_track = 1; head_num = 1; hidden_sectors = 0;
// drive_number = 0x80; media_type = 0xf8; extended_boot_signature = 0x29;
// filesystem_type = "FAT12 "; volume_serial_number = 0x1234; volume_label = "TinyUSB 0 ";
// FAT magic code at offset 510-511
{
0xEB, 0x3C, 0x90, 0x4D, 0x53, 0x44, 0x4F, 0x53, 0x35, 0x2E, 0x30, 0x00, 0x02, 0x01, 0x01, 0x00,
0x01, 0x10, 0x00, 0x10, 0x00, 0xF8, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x29, 0x34, 0x12, 0x00, 0x00, 'T' , 'i' , 'n' , 'y' , 'U' ,
'S' , 'B' , ' ' , 'C' , ' ' , ' ' , 0x46, 0x41, 0x54, 0x31, 0x32, 0x20, 0x20, 0x20, 0x00, 0x00,
// Zero up to 2 last bytes of FAT magic code
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x55, 0xAA
},
//------------- Block1: FAT12 Table -------------//
{
0xF8, 0xFF, 0xFF, 0xFF, 0x0F // // first 2 entries must be F8FF, third entry is cluster end of readme file
},
//------------- Block2: Root Directory -------------//
{
// first entry is volume label
'M' , 'o' , 'n' , 'g' , 'o' , 'o' , 's' , 'e' , ' ' , ' ' , ' ' , 0x08, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4F, 0x6D, 0x65, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// second entry is our file
'I' , 'N' , 'D' , 'E' , 'X' , ' ' , ' ' , ' ' , 'H' , 'T' , 'M' , 0x20, 0x00, 0xC6, 0x52, 0x6D,
0x65, 0x43, 0x65, 0x43, 0x00, 0x00, 0x88, 0x6D, 0x65, 0x43, 0x02, 0x00,
sizeof(INDEX_CONTENTS)-1, 0x00, 0x00, 0x00 // readme's files size (4 Bytes)
},
//------------- Block3: file Content -------------//
INDEX_CONTENTS
};
// Invoked to determine max LUN
uint8_t tud_msc_get_maxlun_cb(void)
{
return 1; // single LUN
}
// Invoked when received SCSI_CMD_INQUIRY
// Application fill vendor id, product id and revision with string up to 8, 16, 4 characters respectively
void tud_msc_inquiry_cb(uint8_t lun, uint8_t vendor_id[8], uint8_t product_id[16], uint8_t product_rev[4])
{
(void) lun; // use same ID for both LUNs
const char vid[] = "TinyUSB";
const char pid[] = "Mass Storage";
const char rev[] = "1.0";
memcpy(vendor_id , vid, strlen(vid));
memcpy(product_id , pid, strlen(pid));
memcpy(product_rev, rev, strlen(rev));
}
// Invoked when received Test Unit Ready command.
// return true allowing host to read/write this LUN e.g SD card inserted
bool tud_msc_test_unit_ready_cb(uint8_t lun)
{
(void) lun;
return true; // Flash/RAM disks are always ready
}
// Invoked when received SCSI_CMD_READ_CAPACITY_10 and SCSI_CMD_READ_FORMAT_CAPACITY to determine the disk size
// Application update block count and block size
void tud_msc_capacity_cb(uint8_t lun, uint32_t* block_count, uint16_t* block_size)
{
(void) lun;
*block_count = DISK_BLOCK_NUM;
*block_size = DISK_BLOCK_SIZE;
}
// Invoked when received Start Stop Unit command
// - Start = 0 : stopped power mode, if load_eject = 1 : unload disk storage
// - Start = 1 : active mode, if load_eject = 1 : load disk storage
bool tud_msc_start_stop_cb(uint8_t lun, uint8_t power_condition, bool start, bool load_eject)
{
(void) lun;
(void) power_condition;
if ( load_eject )
{
if (start)
{
// load disk storage
}else
{
// unload disk storage
}
}
return true;
}
// Callback invoked when received READ10 command.
// Copy disk's data to buffer (up to bufsize) and return number of copied bytes.
int32_t tud_msc_read10_cb(uint8_t lun, uint32_t lba, uint32_t offset, void* buffer, uint32_t bufsize)
{
// out of ramdisk
if ( lba >= DISK_BLOCK_NUM ) return -1;
(void) lun;
uint8_t const* addr = msc_disk0[lba] + offset;
memcpy(buffer, addr, bufsize);
return (int32_t) bufsize;
}
bool tud_msc_is_writable_cb (uint8_t lun)
{
(void) lun;
// readonly
return false;
}
// Callback invoked when received WRITE10 command.
// Process data in buffer to disk's storage and return number of written bytes
int32_t tud_msc_write10_cb(uint8_t lun, uint32_t lba, uint32_t offset, uint8_t* buffer, uint32_t bufsize)
{
// out of ramdisk
if ( lba >= DISK_BLOCK_NUM ) return -1;
#if 0
uint8_t* addr = (lun ? msc_disk1[lba] : msc_disk0[lba]) + offset;
memcpy(addr, buffer, bufsize);
#else // readonly
(void) lun; (void) lba; (void) offset; (void) buffer;
#endif
return (int32_t) bufsize;
}
// Callback invoked when received an SCSI command not in built-in list below
// - READ_CAPACITY10, READ_FORMAT_CAPACITY, INQUIRY, MODE_SENSE6, REQUEST_SENSE
// - READ10 and WRITE10 has their own callbacks
int32_t tud_msc_scsi_cb (uint8_t lun, uint8_t const scsi_cmd[16], void* buffer, uint16_t bufsize)
{
// read10 & write10 has their own callback and MUST not be handled here
void const* response = NULL;
int32_t resplen = 0;
// most scsi handled is input
bool in_xfer = true;
switch (scsi_cmd[0])
{
default:
// Set Sense = Invalid Command Operation
tud_msc_set_sense(lun, SCSI_SENSE_ILLEGAL_REQUEST, 0x20, 0x00);
// negative means error -> tinyusb could stall and/or response with failed status
resplen = -1;
break;
}
// return resplen must not larger than bufsize
if ( resplen > bufsize ) resplen = bufsize;
if ( response && (resplen > 0) )
{
if(in_xfer)
{
memcpy(buffer, response, (size_t) resplen);
}else
{
// SCSI output
}
}
return resplen;
}
@@ -0,0 +1,2 @@
#pragma once
#define SIZEOF_ETH_HDR 14
@@ -0,0 +1,12 @@
#pragma once
// Windows only supports RNDIS, and apparently doesn't like composite devices with two configurations.
// Mac only supports CDC-ECM, which we present as the second config in a dual configuration device
// Linux supports either RNDIS, CDC-ECM or CDC-NCM
#define CFG_TUD_ECM_RNDIS 1
#define CFG_TUD_MSC 1 // This enables mass storage
#define CFG_TUD_MSC_EP_BUFSIZE 512 // Also Add msc_disk.c and descritors
#define CFG_TUD_NCM 0
#define BOARD_DEVICE_RHPORT_NUM 0
#define BOARD_DEVICE_RHPORT_SPEED OPT_MODE_FULL_SPEED
#define CFG_TUSB_RHPORT0_MODE (OPT_MODE_DEVICE | BOARD_DEVICE_RHPORT_SPEED)
@@ -0,0 +1,271 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2019 Ha Thach (tinyusb.org)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*/
// Modified by Cesanta Software
#include "tusb.h"
/* A combination of interfaces must have a unique product id, since PC will save device driver after the first plug.
* Same VID/PID with different interface e.g MSC (first), then CDC (later) will possibly cause system error on PC.
*
* Auto ProductID layout's Bitmap:
* [MSB] NET | VENDOR | MIDI | HID | MSC | CDC [LSB]
*/
#define _PID_MAP(itf, n) ( (CFG_TUD_##itf) << (n) )
#define USB_PID (0x4000 | _PID_MAP(CDC, 0) | _PID_MAP(MSC, 1) | _PID_MAP(HID, 2) | \
_PID_MAP(MIDI, 3) | _PID_MAP(VENDOR, 4) | _PID_MAP(ECM_RNDIS, 5) | _PID_MAP(NCM, 5) )
// String Descriptor Index
enum
{
STRID_LANGID = 0,
STRID_MANUFACTURER,
STRID_PRODUCT,
STRID_SERIAL,
STRID_INTERFACE,
STRID_MAC
};
enum
{
ITF_NUM_CDC = 0,
ITF_NUM_CDC_DATA,
ITF_NUM_MSC,
ITF_NUM_TOTAL
};
enum
{
#if CFG_TUD_ECM_RNDIS
CONFIG_ID_RNDIS = 0,
#if DUAL_CONFIG
CONFIG_ID_ECM = 1,
#endif
#else
CONFIG_ID_NCM = 0,
#endif
CONFIG_ID_COUNT
};
//--------------------------------------------------------------------+
// Device Descriptors
//--------------------------------------------------------------------+
tusb_desc_device_t const desc_device =
{
.bLength = sizeof(tusb_desc_device_t),
.bDescriptorType = TUSB_DESC_DEVICE,
.bcdUSB = 0x0200,
// Use Interface Association Descriptor (IAD) device class
.bDeviceClass = TUSB_CLASS_MISC,
.bDeviceSubClass = MISC_SUBCLASS_COMMON,
.bDeviceProtocol = MISC_PROTOCOL_IAD,
.bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE,
.idVendor = 0xCafe,
.idProduct = USB_PID,
.bcdDevice = 0x0101,
.iManufacturer = STRID_MANUFACTURER,
.iProduct = STRID_PRODUCT,
.iSerialNumber = STRID_SERIAL,
.bNumConfigurations = CONFIG_ID_COUNT // multiple configurations
};
// Invoked when received GET DEVICE DESCRIPTOR
// Application return pointer to descriptor
uint8_t const * tud_descriptor_device_cb(void)
{
return (uint8_t const *) &desc_device;
}
//--------------------------------------------------------------------+
// Configuration Descriptor
//--------------------------------------------------------------------+
#define MAIN_CONFIG_TOTAL_LEN (TUD_CONFIG_DESC_LEN + TUD_MSC_DESC_LEN + TUD_RNDIS_DESC_LEN)
#define ALT_CONFIG_TOTAL_LEN (TUD_CONFIG_DESC_LEN + TUD_MSC_DESC_LEN + TUD_CDC_ECM_DESC_LEN)
#define NCM_CONFIG_TOTAL_LEN (TUD_CONFIG_DESC_LEN + TUD_MSC_DESC_LEN + TUD_CDC_NCM_DESC_LEN)
#if CFG_TUSB_MCU == OPT_MCU_LPC175X_6X || CFG_TUSB_MCU == OPT_MCU_LPC177X_8X || CFG_TUSB_MCU == OPT_MCU_LPC40XX
// LPC 17xx and 40xx endpoint type (bulk/interrupt/iso) are fixed by its number
// 0 control, 1 In, 2 Bulk, 3 Iso, 4 In, 5 Bulk etc ...
#define EPNUM_NET_NOTIF 0x81
#define EPNUM_NET_OUT 0x02
#define EPNUM_NET_IN 0x82
#define EPNUM_MSC_OUT 0x05
#define EPNUM_MSC_IN 0x85
#elif CFG_TUSB_MCU == OPT_MCU_SAMG || CFG_TUSB_MCU == OPT_MCU_SAMX7X
// SAMG & SAME70 don't support a same endpoint number with different direction IN and OUT
// e.g EP1 OUT & EP1 IN cannot exist together
#define EPNUM_NET_NOTIF 0x81
#define EPNUM_NET_OUT 0x02
#define EPNUM_NET_IN 0x83
#define EPNUM_MSC_OUT 0x04
#define EPNUM_MSC_IN 0x85
#else
#define EPNUM_NET_NOTIF 0x81
#define EPNUM_NET_OUT 0x02
#define EPNUM_NET_IN 0x82
#define EPNUM_MSC_OUT 0x03
#define EPNUM_MSC_IN 0x83
#endif
// Interface number, string index, EP Out & EP In address, EP size
# define MSCDESC TUD_MSC_DESCRIPTOR(ITF_NUM_MSC, 0, EPNUM_MSC_OUT, EPNUM_MSC_IN, 64)
#if CFG_TUD_ECM_RNDIS
static uint8_t const rndis_configuration[] =
{
// Config number (index+1), interface count, string index, total length, attribute, power in mA
TUD_CONFIG_DESCRIPTOR(CONFIG_ID_RNDIS+1, ITF_NUM_TOTAL, 0, MAIN_CONFIG_TOTAL_LEN, 0, 100),
// Interface number, string index, EP notification address and size, EP data address (out, in) and size.
TUD_RNDIS_DESCRIPTOR(ITF_NUM_CDC, STRID_INTERFACE, EPNUM_NET_NOTIF, 8, EPNUM_NET_OUT, EPNUM_NET_IN, CFG_TUD_NET_ENDPOINT_SIZE),
MSCDESC
};
#if DUAL_CONFIG
static uint8_t const ecm_configuration[] =
{
// Config number (index+1), interface count, string index, total length, attribute, power in mA
TUD_CONFIG_DESCRIPTOR(CONFIG_ID_ECM+1, ITF_NUM_TOTAL, 0, ALT_CONFIG_TOTAL_LEN, 0, 100),
// Interface number, description string index, MAC address string index, EP notification address and size, EP data address (out, in), and size, max segment size.
TUD_CDC_ECM_DESCRIPTOR(ITF_NUM_CDC, STRID_INTERFACE, STRID_MAC, EPNUM_NET_NOTIF, 64, EPNUM_NET_OUT, EPNUM_NET_IN, CFG_TUD_NET_ENDPOINT_SIZE, CFG_TUD_NET_MTU),
MSCDESC
};
#endif
#else
static uint8_t const ncm_configuration[] =
{
// Config number (index+1), interface count, string index, total length, attribute, power in mA
TUD_CONFIG_DESCRIPTOR(CONFIG_ID_NCM+1, ITF_NUM_TOTAL, 0, NCM_CONFIG_TOTAL_LEN, 0, 100),
// Interface number, description string index, MAC address string index, EP notification address and size, EP data address (out, in), and size, max segment size.
TUD_CDC_NCM_DESCRIPTOR(ITF_NUM_CDC, STRID_INTERFACE, STRID_MAC, EPNUM_NET_NOTIF, 64, EPNUM_NET_OUT, EPNUM_NET_IN, CFG_TUD_NET_ENDPOINT_SIZE, CFG_TUD_NET_MTU),
MSCDESC
};
#endif
// Configuration array: RNDIS and CDC-ECM
// - Windows only works with RNDIS
// - MacOS only works with CDC-ECM
// - Linux will work on both
static uint8_t const * const configuration_arr[2] =
{
#if CFG_TUD_ECM_RNDIS
[CONFIG_ID_RNDIS] = rndis_configuration
#if DUAL_CONFIG
, [CONFIG_ID_ECM ] = ecm_configuration
#endif
#else
[CONFIG_ID_NCM ] = ncm_configuration
#endif
};
// Invoked when received GET CONFIGURATION DESCRIPTOR
// Application return pointer to descriptor
// Descriptor contents must exist long enough for transfer to complete
uint8_t const * tud_descriptor_configuration_cb(uint8_t index)
{
return (index < CONFIG_ID_COUNT) ? configuration_arr[index] : NULL;
}
//--------------------------------------------------------------------+
// String Descriptors
//--------------------------------------------------------------------+
// array of pointer to string descriptors
static char const* string_desc_arr [] =
{
[STRID_LANGID] = (const char[]) { 0x09, 0x04 }, // supported language is English (0x0409)
[STRID_MANUFACTURER] = "TinyUSB", // Manufacturer
[STRID_PRODUCT] = "TinyUSB Device", // Product
[STRID_SERIAL] = "123456", // Serial
[STRID_INTERFACE] = "TinyUSB Network Interface" // Interface Description
// STRID_MAC index is handled separately
};
static uint16_t _desc_str[32];
// Invoked when received GET STRING DESCRIPTOR request
// Application return pointer to descriptor, whose contents must exist long enough for transfer to complete
uint16_t const* tud_descriptor_string_cb(uint8_t index, uint16_t langid)
{
(void) langid;
unsigned int chr_count = 0;
if (STRID_LANGID == index)
{
memcpy(&_desc_str[1], string_desc_arr[STRID_LANGID], 2);
chr_count = 1;
}
else if (STRID_MAC == index)
{
// Convert MAC address into UTF-16
for (unsigned i=0; i<sizeof(tud_network_mac_address); i++)
{
_desc_str[1+chr_count++] = "0123456789ABCDEF"[(tud_network_mac_address[i] >> 4) & 0xf];
_desc_str[1+chr_count++] = "0123456789ABCDEF"[(tud_network_mac_address[i] >> 0) & 0xf];
}
}
else
{
// Note: the 0xEE index string is a Microsoft OS 1.0 Descriptors.
// https://docs.microsoft.com/en-us/windows-hardware/drivers/usbcon/microsoft-defined-usb-descriptors
if ( !(index < sizeof(string_desc_arr)/sizeof(string_desc_arr[0])) ) return NULL;
const char* str = string_desc_arr[index];
// Cap at max char
chr_count = (uint8_t) strlen(str);
if ( chr_count > (TU_ARRAY_SIZE(_desc_str) - 1)) chr_count = TU_ARRAY_SIZE(_desc_str) - 1;
// Convert ASCII string into UTF-16
for (unsigned int i=0; i<chr_count; i++)
{
_desc_str[1+i] = str[i];
}
}
// first byte is length (including header), second byte is string type
_desc_str[0] = (uint16_t) ((TUSB_DESC_STRING << 8 ) | (2*chr_count + 2));
return _desc_str;
}
@@ -0,0 +1,24 @@
cmake_minimum_required(VERSION 3.13)
include(pico-sdk/pico_sdk_init.cmake)
project(firmware)
pico_sdk_init()
add_executable(firmware
main.c
mongoose.c
)
target_include_directories(firmware PUBLIC
.
)
target_link_libraries(firmware hardware_pio pico_stdlib pico_rand)
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
+40
View File
@@ -0,0 +1,40 @@
# Environment setup: https://mongoose.ws/docs/getting-started/build-environment/
CWD ?= $(dir $(abspath $(lastword $(MAKEFILE_LIST))))
SDK_PATH ?= $(CWD)/pico-sdk
RM = rm -rf
MKBUILD = test -d build || mkdir build
ifeq ($(OS),Windows_NT)
RM = cmd /C del /Q /F /S
MKBUILD = if not exist build mkdir build
endif
.PHONY: build
all build: build/firmware.uf2
build/firmware.uf2: pico_sdk_import.cmake mongoose.c mongoose.h
pico_sdk_import.cmake: $(SDK_PATH)
cp $(SDK_PATH)/external/$@ $@
mongoose.c mongoose.h:
cp ../../../$@ .
build/firmware.uf2: $(SDK_PATH) $(wildcard *.[ch]) CMakeLists.txt Makefile
$(MKBUILD)
cd build && PICO_SDK_PATH=$(SDK_PATH) cmake -G "Unix Makefiles" .. && make
flash: build/firmware.uf2
picotool load $< -f
clean:
$(RM) build pico_sdk_import.cmake mongoose.c mongoose.h
fullclean: clean
$(RM) $(SDK_PATH)
$(SDK_PATH):
git clone -c advice.detachedHead=false --quiet --depth 1 -b 2.1.1 https://github.com/raspberrypi/pico-sdk $@
cd $@ && git submodule update --quiet --init
+155
View File
@@ -0,0 +1,155 @@
// Copyright (c) 2025 Cesanta Software Limited
// All rights reserved
#include "pico/stdlib.h"
#include "hardware/pio.h"
extern void mg_delayms(unsigned int ms);
#define SPIPIO pio0
#define SPISM 0
#define DATAPIN 2
#define CLKPIN 3
#define CSPIN 1
#define PWRPIN 0
#define piospi_wrap_target 0
#define piospi_wrap 6
#define piospi_pio_version 0
#define piospi_offset_rx_ 7u
static const uint16_t piospi_program_instructions[] = {
// .wrap_target
0x80a0, // 0: pull block side 0
0xa027, // 1: mov x, osr side 0
0x6068, // 2: out null, 8 side 0
0x6001, // 3: out pins, 1 side 0
0x1043, // 4: jmp x--, 3 side 1
0xe000, // 5: set pins, 0 side 0
0xc020, // 6: irq wait 0 side 0
// .wrap
0xe080, // 7: set pindirs, 0 side 0
0x6020, // 8: out x, 32 side 0
0x4001, // 9: in pins, 1 side 0
0x1049, // 10: jmp x--, 9 side 1
0xe000, // 11: set pins, 0 side 0
0xe081, // 12: set pindirs, 1 side 0
0xc020, // 13: irq wait 0 side 0
0x0000, // 14: jmp 0 side 0
};
static const struct pio_program piospi_program = {
.instructions = piospi_program_instructions,
.length = 15,
.origin = -1,
.pio_version = 0,
#if PICO_PIO_VERSION > 0
.used_gpio_ranges = 0x0
#endif
};
static inline pio_sm_config piospi_program_get_default_config(uint offset) {
pio_sm_config c = pio_get_default_sm_config();
sm_config_set_wrap(&c, offset + piospi_wrap_target, offset + piospi_wrap);
sm_config_set_sideset(&c, 1, false, false);
return c;
}
static inline pio_sm_config piospi_init(PIO pio, uint sm, uint addr, uint data, uint clk) {
pio_gpio_init(pio, data);
pio_gpio_init(pio, clk);
pio_sm_set_pins_with_mask(pio, sm, 0, (1 << data) | (1 << clk));
pio_sm_config c = piospi_program_get_default_config(addr);
sm_config_set_out_pins(&c, data, 1);
sm_config_set_in_pins(&c, data);
sm_config_set_set_pins(&c, data, 1);
sm_config_set_sideset_pins(&c, clk);
pio_sm_set_consecutive_pindirs(pio, sm, data, 1, true);
pio_sm_set_consecutive_pindirs(pio, sm, clk, 1, true);
pio_sm_set_pindirs_with_mask(pio, sm, (1 << data) | (1 << clk), (1 << data) | (1 << clk));
sm_config_set_in_shift(&c, false, true, 8); // push bytes, MSB first, auto
#if PICO_RP2040
sm_config_set_clkdiv(&c, 18); // Run at 133/1 = <50MHz (2x data rate)
#else
sm_config_set_clkdiv(&c, 20); // Run at 150/1 = <50MHz (2x data rate)
#endif
return c;
}
static inline void piospi_tx(PIO pio, uint sm, pio_sm_config *c, uint addr, uint data) {
sm_config_set_fifo_join(c, PIO_FIFO_JOIN_TX);
sm_config_set_out_shift(c, false, true, 8); // pull bytes, MSB first, auto
pio_sm_init(pio, sm, addr, c);
}
static inline void piospi_rx(PIO pio, uint sm, pio_sm_config *c, uint addr, uint data) {
sm_config_set_fifo_join(c, PIO_FIFO_JOIN_NONE);
sm_config_set_out_shift(c, false, true, 32); // pull words, MSB first, auto
pio_sm_init(pio, sm, addr + piospi_offset_rx_, c);
}
static inline void piospi_done(PIO pio, uint sm) {
while(!pio_interrupt_get(pio, 0));
pio_sm_set_enabled(pio, sm, false);
pio_interrupt_clear(pio, 0);
}
static pio_sm_config s_piospi_sm_config;
static uint s_piospi_sm_addr;
void piospi_write(uint8_t *data, size_t len) {
size_t initial_len = len;
piospi_tx(SPIPIO, SPISM, &s_piospi_sm_config, s_piospi_sm_addr, DATAPIN);
__compiler_memory_barrier();
pio_sm_put(SPIPIO, SPISM, (len * 8) - 1); // bits to transmit
pio_sm_set_enabled(SPIPIO, SPISM, true);
while (len--) pio_sm_put_blocking(SPIPIO, SPISM, (*data++) << 24);
piospi_done(SPIPIO, SPISM);
}
// Read data block from SPI interface
void piospi_read(uint8_t *data, size_t len) {
piospi_rx(SPIPIO, SPISM, &s_piospi_sm_config, s_piospi_sm_addr, DATAPIN);
__compiler_memory_barrier();
pio_sm_put(SPIPIO, SPISM, (len * 8) - 1); // bits to receive
pio_sm_set_enabled(SPIPIO, SPISM, true);
while (len--) *data++ = pio_sm_get_blocking(SPIPIO, SPISM);
piospi_done(SPIPIO, SPISM);
}
void hwspecific_spi_init(void) {
gpio_init(PWRPIN);
gpio_set_dir(PWRPIN, GPIO_OUT);
gpio_put(PWRPIN, 0);
gpio_init(CSPIN);
gpio_set_dir(CSPIN, GPIO_OUT);
gpio_put(CSPIN, 0);
// init SPI pins so they are idle and stable during power up
gpio_init(DATAPIN);
gpio_set_dir(DATAPIN, GPIO_OUT);
gpio_put(DATAPIN, 0);
gpio_init(CLKPIN);
gpio_set_dir(CLKPIN, GPIO_OUT);
gpio_put(CLKPIN, 0);
mg_delayms(100);
gpio_put(CSPIN, 1);
gpio_put(PWRPIN, 1);
s_piospi_sm_addr = pio_add_program(SPIPIO, &piospi_program);
s_piospi_sm_config = piospi_init(SPIPIO, SPISM, s_piospi_sm_addr, DATAPIN, CLKPIN);
mg_delayms(50);
}
void hwspecific_spi_begin(void *arg) {
gpio_put(CSPIN, 0);
(void) arg;
}
// either write or read, not both
void hwspecific_spi_txn(void *arg, uint8_t *txdata, uint8_t *rxdata, size_t len) {
if (txdata != NULL) piospi_write(txdata, len);
if (rxdata != NULL) piospi_read(rxdata, len);
(void) arg;
}
void hwspecific_spi_end(void *arg) {
gpio_put(CSPIN, 1);
(void) arg;
}
+153
View File
@@ -0,0 +1,153 @@
// Copyright (c) 2025 Cesanta Software Limited
// All rights reserved
#include "mongoose.h"
#include "hal.h"
#define WIFI_SSID "YOUR_WIFI_NETWORK_NAME" // SET THIS!
#define WIFI_PASS "YOUR_WIFI_PASSWORD" // SET THIS!
static const struct mg_tcpip_spi spi = {NULL, hwspecific_spi_begin, hwspecific_spi_end, hwspecific_spi_txn};
#ifndef CYW43_RESOURCE_ATTRIBUTE
#define CYW43_RESOURCE_ATTRIBUTE
#endif
#include "pico-sdk/lib/cyw43-driver/firmware/w43439A0_7_95_49_00_combined.h"
#include "pico-sdk/lib/cyw43-driver/firmware/wifi_nvram_43439.h"
static const struct mg_tcpip_driver_cyw_firmware fw = {
(const uint8_t *)w43439A0_7_95_49_00_combined, (size_t)CYW43_WIFI_FW_LEN,
(const uint8_t *)wifi_nvram_4343, (size_t)sizeof(wifi_nvram_4343),
(const uint8_t *)(w43439A0_7_95_49_00_combined + sizeof(w43439A0_7_95_49_00_combined) - CYW43_CLM_LEN), (size_t)CYW43_CLM_LEN};
// 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_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_cyw_data *d = (struct mg_tcpip_driver_cyw_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_wifi_data *wifi = &((struct mg_tcpip_driver_cyw_data *) ifp->driver_data)->wifi;
wifi->apmode = false;
// simulate user selection of a network (2/2: actual connect)
bool res = mg_wifi_connect(wifi);
MG_INFO(("Manually connecting: %s", res ? "OK":"FAIL"));
if (res) {
state = CONNECTING;
} // 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_wifi_data *wifi = &((struct mg_tcpip_driver_cyw_data *) ifp->driver_data)->wifi;
bool res = mg_wifi_ap_start(wifi);
MG_INFO(("Disconnected"));
MG_INFO(("Manually starting AP: %s", res ? "OK":"FAIL"));
if (res) {
state = AP;
wifi->apmode = true;
}
}
break;
}
}
static struct mg_tcpip_driver_cyw_data d = {
{WIFI_SSID, WIFI_PASS, "mongoose", "mongoose", 0, 0, 0, 0, 10, true}, (struct mg_tcpip_spi *)&spi, (struct mg_tcpip_driver_cyw_firmware *)&fw, false};
static void http_ev_handler(struct mg_connection *c, int ev, void *ev_data) {
if (ev == MG_EV_HTTP_MSG) { // New HTTP request received
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
uint64_t tick = to_ms_since_boot(get_absolute_time());
if (mg_match(hm->uri, mg_str("/api/tick"), NULL)) {
mg_http_reply(c, 200, "", "{%m:%llu}\n", MG_ESC("tick"), tick);
} else {
mg_http_reply(c, 200, "", "Hi from Mongoose, tick %llu\n", tick);
}
}
}
static void driver_init(struct mg_mgr *mgr) {
d.wifi.apip = MG_IPV4(192, 168, 169, 1),
d.wifi.apmask = MG_IPV4(255, 255, 255, 0),
state = d.wifi.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 = (struct mg_tcpip_driver *)&mg_tcpip_driver_cyw,
.driver_data = (struct mg_tcpip_driver_cyw_data*)&d,
.fn = mif_fn,
// .recv_queue.size = 8192
};
mg_tcpip_init(mgr, &mif);
}
// In an RTOS environment, give this task 8 KB of stack space.
static void run_mongoose(void) {
struct mg_mgr mgr;
mg_mgr_init(&mgr);
driver_init(&mgr);
mg_log_set(MG_LL_DEBUG); // Set log level
MG_INFO(("Hardware initialised, starting firmware..."));
mg_http_listen(&mgr, "http://0.0.0.0", http_ev_handler, NULL);
for (;;) {
mg_mgr_poll(&mgr, 1);
}
}
int main(void) {
// initialize stdio
stdio_init_all();
hwspecific_spi_init();
run_mongoose();
return 0;
}
@@ -0,0 +1,5 @@
#define MG_ARCH MG_ARCH_PICOSDK
#define MG_ENABLE_TCPIP 1
#define MG_ENABLE_DRIVER_CYW 1
#define MG_ENABLE_TCPIP_DRIVER_INIT 0