mirror of
https://github.com/cesanta/mongoose.git
synced 2026-10-01 05:52:32 +07:00
Add RP examples
This commit is contained in:
@@ -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
|
||||
|
||||
|
||||
@@ -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 +0,0 @@
|
||||
../../../mongoose.c
|
||||
@@ -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)
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user