mirror of
https://github.com/cesanta/mongoose.git
synced 2026-11-10 16:31:44 +00:00
Move ARCH_RP2040 --> ARCH_PICOSDK
This commit is contained in:
1 parent
7844d84798
commit
58327a1753
69 files changed
+15
-13
No files matched your search
@@ -0,0 +1,19 @@
|
||||
cmake_minimum_required(VERSION 3.13)
|
||||
include(pico-sdk/pico_sdk_init.cmake)
|
||||
|
||||
project(firmware)
|
||||
pico_sdk_init()
|
||||
|
||||
add_executable(firmware
|
||||
driver_rp2040_rmii.c
|
||||
main.c
|
||||
mongoose.c)
|
||||
|
||||
target_include_directories(firmware PUBLIC
|
||||
.)
|
||||
|
||||
target_link_libraries(firmware hardware_pio hardware_dma 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
|
||||
@@ -0,0 +1,20 @@
|
||||
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
|
||||
|
||||
build: pico-sdk
|
||||
$(MKBUILD)
|
||||
cd build && cmake -G "Unix Makefiles" .. && make
|
||||
|
||||
pico-sdk:
|
||||
git clone --depth 1 -b 1.4.0 https://github.com/raspberrypi/pico-sdk $@
|
||||
cd $@ && git submodule update --init
|
||||
|
||||
clean:
|
||||
$(RM) pico-sdk build
|
||||
@@ -0,0 +1 @@
|
||||
See detailed tutorial (including hardware connections) at https://mongoose.ws/tutorials/rp2040/pico-rmii/
|
||||
@@ -0,0 +1,336 @@
|
||||
// Copyright (c) 2022 Cesanta Software Limited
|
||||
// All rights reserved
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "hardware/dma.h"
|
||||
#include "hardware/pio.h"
|
||||
#include "pico/stdlib.h"
|
||||
|
||||
#include "driver_rp2040_rmii.h"
|
||||
#include "mongoose.h"
|
||||
|
||||
#define RXSM 0
|
||||
#define TXSM 1
|
||||
#define SMITXSM 2
|
||||
#define SMIRXSM 0 // pio1
|
||||
|
||||
static uint smi_wr_addr, smi_rd_addr;
|
||||
static int dma_rx;
|
||||
static int dma_tx;
|
||||
|
||||
static dma_channel_config dmacfg_rx;
|
||||
static dma_channel_config dmacfg_tx;
|
||||
|
||||
// Max frame size with VLAN tag + CRC and excluding preamble
|
||||
#define ETH_PKT_SIZE 1522
|
||||
|
||||
static uint8_t s_rxbuf[2][ETH_PKT_SIZE]; // ping-pong buffer
|
||||
static uint8_t s_txbuf[ETH_PKT_SIZE];
|
||||
static struct mg_tcpip_if *s_ifp; // MIP interface
|
||||
|
||||
#define rmii_tx_wrap_target 0
|
||||
#define rmii_tx_wrap 7
|
||||
|
||||
static const uint16_t rmii_tx_program_instructions[] = {
|
||||
// .wrap_target
|
||||
0xe000, // 0: set pins, 0 side 0
|
||||
0x80a0, // 1: pull block side 0
|
||||
0xe03d, // 2: set x, 29 side 0
|
||||
0xf101, // 3: set pins, 1 side 1 [1]
|
||||
0x1144, // 4: jmp x--, 4 side 1 [1]
|
||||
0xf103, // 5: set pins, 3 side 1 [1]
|
||||
0x7002, // 6: out pins, 2 side 1
|
||||
0x10e6, // 7: jmp !osre, 6 side 1
|
||||
// .wrap
|
||||
};
|
||||
|
||||
static const struct pio_program rmii_tx_program = {
|
||||
.instructions = rmii_tx_program_instructions,
|
||||
.length = 8,
|
||||
.origin = -1,
|
||||
};
|
||||
|
||||
static inline pio_sm_config rmii_tx_program_get_default_config(uint addr) {
|
||||
pio_sm_config c = pio_get_default_sm_config();
|
||||
sm_config_set_wrap(&c, addr + rmii_tx_wrap_target, addr + rmii_tx_wrap);
|
||||
sm_config_set_sideset(&c, 1, false, false);
|
||||
return c;
|
||||
}
|
||||
|
||||
static inline void rmii_tx_init(PIO pio, uint sm, uint addr, uint gpio) {
|
||||
pio_sm_config c = rmii_tx_program_get_default_config(addr);
|
||||
pio_gpio_init(pio, gpio);
|
||||
pio_gpio_init(pio, gpio + 1);
|
||||
pio_gpio_init(pio, gpio + 2);
|
||||
sm_config_set_out_pins(&c, gpio, 2);
|
||||
sm_config_set_set_pins(&c, gpio, 2);
|
||||
sm_config_set_sideset_pins(&c, gpio + 2);
|
||||
pio_sm_set_consecutive_pindirs(pio, sm, gpio, 3, true);
|
||||
sm_config_set_fifo_join(&c, PIO_FIFO_JOIN_TX);
|
||||
sm_config_set_out_shift(&c, true, true, 8); // pull bytes, LSB first, auto
|
||||
sm_config_set_clkdiv(&c,
|
||||
5); // Run at 50/5 = 10MHz (2x 2-bit nibble data rate)
|
||||
pio_sm_init(pio, sm, addr, &c);
|
||||
pio_sm_set_enabled(pio, sm, true);
|
||||
}
|
||||
|
||||
#define rmii_rx_wrap_target 0
|
||||
#define rmii_rx_wrap 10
|
||||
|
||||
static const uint16_t rmii_rx_program_instructions[] = {
|
||||
// .wrap_target
|
||||
0x2122, // 0: wait 0 pin, 2 [1]
|
||||
0x21a2, // 1: wait 1 pin, 2 [1]
|
||||
0x21a0, // 2: wait 1 pin, 0 [1]
|
||||
0x2121, // 3: wait 0 pin, 1 [1]
|
||||
0x21a1, // 4: wait 1 pin, 1 [1]
|
||||
0x4002, // 5: in pins, 2
|
||||
0x00c5, // 6: jmp pin, 5
|
||||
0x4002, // 7: in pins, 2
|
||||
0x00c5, // 8: jmp pin, 5
|
||||
0xa0c3, // 9: mov isr, null
|
||||
0xc020, // 10: irq wait 0
|
||||
// .wrap
|
||||
};
|
||||
|
||||
static const struct pio_program rmii_rx_program = {
|
||||
.instructions = rmii_rx_program_instructions,
|
||||
.length = 11,
|
||||
.origin = -1,
|
||||
};
|
||||
|
||||
static inline pio_sm_config rmii_rx_program_get_default_config(uint addr) {
|
||||
pio_sm_config c = pio_get_default_sm_config();
|
||||
sm_config_set_wrap(&c, addr + rmii_rx_wrap_target, addr + rmii_rx_wrap);
|
||||
return c;
|
||||
}
|
||||
|
||||
static inline void rmii_rx_init(PIO pio, uint sm, uint addr, uint gpio) {
|
||||
pio_sm_config c = rmii_rx_program_get_default_config(addr);
|
||||
pio_gpio_init(pio, gpio);
|
||||
pio_gpio_init(pio, gpio + 1);
|
||||
pio_gpio_init(pio, gpio + 2);
|
||||
sm_config_set_in_pins(&c, gpio);
|
||||
pio_sm_set_consecutive_pindirs(pio, sm, gpio, 3, false);
|
||||
sm_config_set_jmp_pin(&c, gpio + 2);
|
||||
sm_config_set_in_shift(&c, true, true, 8); // push bytes, LSB first, auto
|
||||
sm_config_set_fifo_join(&c, PIO_FIFO_JOIN_RX);
|
||||
sm_config_set_clkdiv(&c,
|
||||
5); // Run at 50/5 = 10MHz (2x 2-bit nibble data rate)
|
||||
pio_sm_init(pio, sm, addr, &c);
|
||||
pio_sm_set_enabled(pio, sm, true);
|
||||
}
|
||||
|
||||
#define smi_wr_wrap_target 0
|
||||
#define smi_wr_wrap 4
|
||||
|
||||
static const uint16_t smi_wr_program_instructions[] = {
|
||||
// .wrap_target
|
||||
0xe03f, // 0: set x, 31 side 0
|
||||
0xe101, // 1: set pins, 1 side 0 [1]
|
||||
0x1141, // 2: jmp x--, 1 side 1 [1]
|
||||
0x6101, // 3: out pins, 1 side 0 [1]
|
||||
0x1103, // 4: jmp 3 side 1 [1]
|
||||
// .wrap
|
||||
};
|
||||
|
||||
static const struct pio_program smi_wr_program = {
|
||||
.instructions = smi_wr_program_instructions,
|
||||
.length = 5,
|
||||
.origin = -1,
|
||||
};
|
||||
|
||||
static inline pio_sm_config smi_wr_program_get_default_config(uint addr) {
|
||||
pio_sm_config c = pio_get_default_sm_config();
|
||||
sm_config_set_wrap(&c, addr + smi_wr_wrap_target, addr + smi_wr_wrap);
|
||||
sm_config_set_sideset(&c, 1, false, false);
|
||||
return c;
|
||||
}
|
||||
|
||||
#define smi_rd_wrap_target 13
|
||||
#define smi_rd_wrap 13
|
||||
|
||||
static const uint16_t smi_rd_program_instructions[] = {
|
||||
0xe03f, // 0: set x, 31 side 0
|
||||
0xe101, // 1: set pins, 1 side 0 [1]
|
||||
0x1141, // 2: jmp x--, 1 side 1 [1]
|
||||
0xf02d, // 3: set x, 13 side 1
|
||||
0x6101, // 4: out pins, 1 side 0 [1]
|
||||
0x1144, // 5: jmp x--, 4 side 1 [1]
|
||||
0xe180, // 6: set pindirs, 0 side 0 [1]
|
||||
0xf101, // 7: set pins, 1 side 1 [1]
|
||||
0xa142, // 8: nop side 0 [1]
|
||||
0xf12f, // 9: set x, 15 side 1 [1]
|
||||
0xa042, // 10: nop side 0
|
||||
0x4001, // 11: in pins, 1 side 0
|
||||
0x114a, // 12: jmp x--, 10 side 1 [1]
|
||||
// .wrap_target
|
||||
0xf081, // 13: set pindirs, 1 side 1
|
||||
// .wrap
|
||||
};
|
||||
|
||||
static const struct pio_program smi_rd_program = {
|
||||
.instructions = smi_rd_program_instructions,
|
||||
.length = 14,
|
||||
.origin = -1,
|
||||
};
|
||||
|
||||
static inline pio_sm_config smi_rd_program_get_default_config(uint addr) {
|
||||
pio_sm_config c = pio_get_default_sm_config();
|
||||
sm_config_set_wrap(&c, addr + smi_rd_wrap_target, addr + smi_rd_wrap);
|
||||
sm_config_set_sideset(&c, 1, false, false);
|
||||
return c;
|
||||
}
|
||||
|
||||
static inline void smi_wr_init(PIO pio, uint sm, uint addr, uint gpio) {
|
||||
pio_gpio_init(pio, gpio);
|
||||
pio_gpio_init(pio, gpio + 1);
|
||||
pio_sm_config c = smi_wr_program_get_default_config(addr);
|
||||
sm_config_set_out_pins(&c, gpio, 1);
|
||||
sm_config_set_set_pins(&c, gpio, 1);
|
||||
sm_config_set_sideset_pins(&c, gpio + 1);
|
||||
pio_sm_set_consecutive_pindirs(pio, sm, gpio, 2, true);
|
||||
sm_config_set_out_shift(&c, false, true,
|
||||
16); // pull half-words, MSB first, auto
|
||||
sm_config_set_clkdiv(&c, 6); // Run at 50/6 = <10MHz (4x data rate)
|
||||
pio_sm_init(pio, sm, addr, &c);
|
||||
}
|
||||
|
||||
static inline void smi_rd_init(PIO pio, uint sm, uint addr, uint gpio) {
|
||||
pio_gpio_init(pio, gpio);
|
||||
pio_gpio_init(pio, gpio + 1);
|
||||
pio_sm_config c = smi_rd_program_get_default_config(addr);
|
||||
sm_config_set_in_pins(&c, gpio);
|
||||
sm_config_set_out_pins(&c, gpio, 1);
|
||||
sm_config_set_set_pins(&c, gpio, 1);
|
||||
sm_config_set_sideset_pins(&c, gpio + 1);
|
||||
pio_sm_set_consecutive_pindirs(pio, sm, gpio, 2, true);
|
||||
sm_config_set_out_shift(&c, false, true,
|
||||
16); // pull half-words, MSB first, auto
|
||||
sm_config_set_in_shift(&c, false, true,
|
||||
16); // push half-words, MSB first, auto
|
||||
sm_config_set_clkdiv(&c, 6); // Run at 50/6 = <10MHz (4x data rate)
|
||||
pio_sm_init(pio, sm, addr, &c);
|
||||
}
|
||||
|
||||
static void eth_write_phy(int addr, int reg, int val, uint8_t gpio) {
|
||||
uint16_t op = 0x5002 | (addr << 7) | (reg << 2); // b0101aaaaarrrrr10
|
||||
smi_wr_init(pio0, SMITXSM, smi_wr_addr, gpio);
|
||||
pio_sm_put(pio0, SMITXSM, op << 16);
|
||||
pio_sm_put(pio0, SMITXSM, (uint16_t) val << 16);
|
||||
pio_sm_set_enabled(pio0, SMITXSM, true);
|
||||
}
|
||||
|
||||
static uint16_t eth_read_phy(uint addr, uint reg, uint8_t gpio) {
|
||||
uint16_t op = 0x6000 | (addr << 7) | (reg << 2); // b0110aaaaarrrrr00
|
||||
smi_rd_init(pio1, SMIRXSM, smi_rd_addr, gpio);
|
||||
pio_sm_put(pio1, SMIRXSM, op << 16);
|
||||
pio_sm_set_enabled(pio1, SMIRXSM, true);
|
||||
uint16_t val = (uint16_t) pio_sm_get_blocking(pio1, SMIRXSM);
|
||||
pio_sm_set_enabled(pio1, SMIRXSM, false);
|
||||
return val;
|
||||
}
|
||||
|
||||
static void rx_irq(void);
|
||||
|
||||
static bool mg_tcpip_driver_rp2040_rmii_init(struct mg_tcpip_if *ifp) {
|
||||
struct mg_tcpip_driver_rp2040_rmii_data *d =
|
||||
(struct mg_tcpip_driver_rp2040_rmii_data *) ifp->driver_data;
|
||||
uint rx_sm_addr, tx_sm_addr;
|
||||
s_ifp = ifp;
|
||||
rx_sm_addr = pio_add_program(pio0, &rmii_rx_program);
|
||||
tx_sm_addr = pio_add_program(pio0, &rmii_tx_program);
|
||||
smi_wr_addr = pio_add_program(pio0, &smi_wr_program);
|
||||
smi_rd_addr = pio_add_program(pio1, &smi_rd_program);
|
||||
|
||||
dma_tx = dma_claim_unused_channel(true);
|
||||
dmacfg_tx = dma_channel_get_default_config(dma_tx);
|
||||
channel_config_set_read_increment(&dmacfg_tx, true); // read from memory
|
||||
channel_config_set_write_increment(&dmacfg_tx, false); // write to FIFO
|
||||
channel_config_set_dreq(&dmacfg_tx, pio_get_dreq(pio0, TXSM, true));
|
||||
channel_config_set_transfer_data_size(&dmacfg_tx,
|
||||
DMA_SIZE_8); // transfer bytes
|
||||
rmii_tx_init(pio0, TXSM, tx_sm_addr, d->tx0);
|
||||
|
||||
eth_write_phy(d->phy_addr, 4, 0x61,
|
||||
d->mdio); // Auto Negotiation Advertisement Register: enable
|
||||
// only 10BASE-T full and half duplex (two-pair), use
|
||||
// IEEE 802.3 format
|
||||
sleep_us(31); // give some time for PIO to work (64 bits @2.5MHz = 25.6 us)
|
||||
eth_write_phy(d->phy_addr, 0, 0x1000,
|
||||
d->mdio); // Basic Control Register: enable Auto-Negotiation
|
||||
sleep_us(31);
|
||||
|
||||
dma_rx = dma_claim_unused_channel(true);
|
||||
dmacfg_rx = dma_channel_get_default_config(dma_rx);
|
||||
channel_config_set_read_increment(&dmacfg_rx, false); // read from FIFO
|
||||
channel_config_set_write_increment(&dmacfg_rx,
|
||||
true); // write to memory, increment
|
||||
channel_config_set_dreq(&dmacfg_rx, pio_get_dreq(pio0, RXSM, false));
|
||||
channel_config_set_transfer_data_size(&dmacfg_rx,
|
||||
DMA_SIZE_8); // transfer bytes
|
||||
dma_channel_configure(dma_rx, &dmacfg_rx, s_rxbuf[0],
|
||||
((uint8_t *) &pio0->rxf[RXSM]) + 3, ETH_PKT_SIZE, true);
|
||||
pio_set_irq0_source_enabled(pio0, pis_interrupt0,
|
||||
true); // enable SM0 on PIO0_IRQ_0
|
||||
irq_set_exclusive_handler(7, rx_irq); // set handler for PIO0_IRQ_0
|
||||
irq_set_enabled(7, true); // enable it
|
||||
rmii_rx_init(pio0, RXSM, rx_sm_addr, d->rx0);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static size_t mg_tcpip_driver_rp2040_rmii_tx(const void *buf, size_t len,
|
||||
struct mg_tcpip_if *ifp) {
|
||||
dma_channel_wait_for_finish_blocking(dma_tx);
|
||||
memset(s_txbuf, 0, 60); // pre-pad
|
||||
memcpy(s_txbuf, buf, len);
|
||||
if (len < 60) len = 60; // pad
|
||||
uint32_t crc = mg_crc32(0, s_txbuf, len); // host is little-endian
|
||||
memcpy(s_txbuf + len, (uint8_t *) &crc, 4);
|
||||
len += 4;
|
||||
sleep_us(10); // enforce IFG in case software has been lightning fast...
|
||||
dma_channel_configure(dma_tx, &dmacfg_tx, ((uint8_t *) &pio0->txf[TXSM]) + 3,
|
||||
s_txbuf, len, true);
|
||||
return len;
|
||||
}
|
||||
|
||||
static uint32_t s_rxno;
|
||||
|
||||
// The max amount of time we should keep the PIO state machine stopped is the
|
||||
// IFG time (10us) and that includes irq latency
|
||||
static void rx_irq(void) {
|
||||
dma_channel_hw_t *hw = dma_channel_hw_addr(dma_rx);
|
||||
uint32_t rxno =
|
||||
(s_rxno + 1) &
|
||||
1; // 2 buffers, switch to the available one as fast as possible
|
||||
size_t len = ETH_PKT_SIZE - hw->transfer_count;
|
||||
dma_channel_abort(dma_rx);
|
||||
dma_channel_set_write_addr(dma_rx, s_rxbuf[rxno], true); // restart DMA
|
||||
pio_interrupt_clear(pio0,
|
||||
0); // ACK PIO IRQ so the state machine resumes receiving
|
||||
// NOTE(scaprile) Here we could check addressing to avoid queuing frames not
|
||||
// for us. The max amount of time we can linger here is what it takes for the
|
||||
// other buffer to fill (<8us) and that includes irq chaining
|
||||
if (len >= 64 && len <= ETH_PKT_SIZE)
|
||||
mg_tcpip_qwrite(s_rxbuf[s_rxno], len, s_ifp);
|
||||
s_rxno = rxno;
|
||||
}
|
||||
|
||||
static bool mg_tcpip_driver_rp2040_rmii_up(struct mg_tcpip_if *ifp) {
|
||||
struct mg_tcpip_driver_rp2040_rmii_data *d =
|
||||
(struct mg_tcpip_driver_rp2040_rmii_data *) ifp->driver_data;
|
||||
uint32_t bsr =
|
||||
eth_read_phy(d->phy_addr, 1, d->mdio); // Basic Status Register
|
||||
return (bsr & (1 << 2)) ? 1 : 0; // check Link Status flag
|
||||
}
|
||||
|
||||
struct mg_tcpip_driver mg_tcpip_driver_rp2040_rmii = {
|
||||
mg_tcpip_driver_rp2040_rmii_init,
|
||||
mg_tcpip_driver_rp2040_rmii_tx,
|
||||
NULL,
|
||||
mg_tcpip_driver_rp2040_rmii_up,
|
||||
};
|
||||
@@ -0,0 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
struct mg_tcpip_driver_rp2040_rmii_data {
|
||||
uint8_t rx0; // RX0, RX1, CRS_DV; consecutive GPIO pins
|
||||
uint8_t tx0; // TX0, TX1, TX-EN; consecutive GPIO pins
|
||||
uint8_t mdio; // MDIO, MDC; consecutive GPIO pins
|
||||
uint8_t phy_addr; // PHY address
|
||||
};
|
||||
|
||||
extern struct mg_tcpip_driver mg_tcpip_driver_rp2040_rmii;
|
||||
@@ -0,0 +1,57 @@
|
||||
// Copyright (c) 2022 Cesanta Software Limited
|
||||
// All rights reserved
|
||||
|
||||
#include "pico/stdlib.h"
|
||||
#include "hardware/clocks.h"
|
||||
|
||||
#include "mongoose.h"
|
||||
|
||||
#include "driver_rp2040_rmii.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
|
||||
|
||||
MG_INFO(("Init TCP/IP"));
|
||||
// Initialise Mongoose network stack and specific driver
|
||||
// Set consecutive GPIOs for RMII (tx and rx) and SMI function groups
|
||||
struct mg_tcpip_driver_rp2040_rmii_data driver_data = {
|
||||
// see driver_rp2040_rmii.h
|
||||
.rx0 = 6, // 6, 7, 8 : RX0, RX1, CRS_DV
|
||||
.tx0 = 10, // 10, 11, 12 : TX0, TX1, TX-EN
|
||||
.mdio = 14, // 14, 15 : MDIO, MDC
|
||||
.phy_addr = 1 // check your hardware, LAN8722 is 0 or 1
|
||||
};
|
||||
// Specify MAC address, 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 = {
|
||||
.mac = {2, 0, 1, 2, 3, 5},
|
||||
.ip = 0,
|
||||
.enable_mac_check = true,
|
||||
.enable_crc32_check = true,
|
||||
.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"));
|
||||
|
||||
for (;;) mg_mgr_poll(&mgr, 0); // Infinite event loop
|
||||
|
||||
return 0;
|
||||
}
|
||||
+1
@@ -0,0 +1 @@
|
||||
../../../mongoose.c
|
||||
+1
@@ -0,0 +1 @@
|
||||
../../../mongoose.h
|
||||
@@ -0,0 +1,5 @@
|
||||
#define MG_ARCH MG_ARCH_PICOSDK
|
||||
|
||||
#define MG_ENABLE_TCPIP 1
|
||||
#define MG_ENABLE_TCPIP_DRIVER_INIT 0
|
||||
#define MG_ENABLE_PACKED_FS 1
|
||||
@@ -0,0 +1,32 @@
|
||||
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
|
||||
net.c
|
||||
packed_fs.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)
|
||||
@@ -0,0 +1,20 @@
|
||||
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
|
||||
|
||||
build: pico-sdk
|
||||
$(MKBUILD)
|
||||
cd build && cmake -G "Unix Makefiles" .. && make
|
||||
|
||||
pico-sdk:
|
||||
git clone --depth 1 -b 1.4.0 https://github.com/raspberrypi/pico-sdk $@
|
||||
cd $@ && git submodule update --init
|
||||
|
||||
clean:
|
||||
$(RM) pico-sdk build
|
||||
@@ -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,81 @@
|
||||
// Copyright (c) 2022 Cesanta Software Limited
|
||||
// All rights reserved
|
||||
|
||||
#include "mongoose.h"
|
||||
#include "net.h"
|
||||
#include "pico/stdlib.h"
|
||||
#include "tusb.h"
|
||||
|
||||
static struct mg_tcpip_if *s_ifp;
|
||||
|
||||
const uint8_t tud_network_mac_address[6] = {2, 2, 0x84, 0x6A, 0x96, 0};
|
||||
|
||||
static void blink_cb(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_up(struct mg_tcpip_if *ifp) {
|
||||
(void) ifp;
|
||||
return tud_inited() && tud_ready() && tud_connected();
|
||||
}
|
||||
|
||||
static void fn(struct mg_connection *c, int ev, void *ev_data, void *fn_dta) {
|
||||
if (ev == MG_EV_HTTP_MSG) return mg_http_reply(c, 200, "", "ok\n");
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
gpio_init(PICO_DEFAULT_LED_PIN);
|
||||
gpio_set_dir(PICO_DEFAULT_LED_PIN, GPIO_OUT);
|
||||
stdio_init_all();
|
||||
|
||||
struct mg_mgr mgr; // Initialise Mongoose event manager
|
||||
mg_mgr_init(&mgr); // and attach it to the interface
|
||||
mg_timer_add(&mgr, 500, MG_TIMER_REPEAT, blink_cb, &mgr);
|
||||
|
||||
struct mg_tcpip_driver driver = {.tx = usb_tx, .up = usb_up};
|
||||
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();
|
||||
|
||||
MG_INFO(("Initialising application..."));
|
||||
web_init(&mgr);
|
||||
|
||||
MG_INFO(("Starting event loop"));
|
||||
for (;;) {
|
||||
mg_mgr_poll(&mgr, 0);
|
||||
tud_task();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
../../../mongoose.c
|
||||
@@ -0,0 +1 @@
|
||||
../../../mongoose.h
|
||||
@@ -0,0 +1,6 @@
|
||||
#define MG_ARCH MG_ARCH_PICOSDK
|
||||
|
||||
#define MG_ENABLE_TCPIP 1
|
||||
#define MG_ENABLE_TCPIP_DRIVER_INIT 0
|
||||
#define MG_ENABLE_PACKED_FS 1
|
||||
#define MG_ENABLE_POSIX_FS 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 @@
|
||||
../../device-dashboard/net.c
|
||||
@@ -0,0 +1 @@
|
||||
../../device-dashboard/net.h
|
||||
@@ -0,0 +1,2 @@
|
||||
#pragma once
|
||||
#define SIZEOF_ETH_HDR 14
|
||||
@@ -0,0 +1 @@
|
||||
../../device-dashboard/packed_fs.c
|
||||
@@ -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,26 @@
|
||||
cmake_minimum_required(VERSION 3.13)
|
||||
include(pico-sdk/pico_sdk_init.cmake)
|
||||
|
||||
project(firmware)
|
||||
pico_sdk_init()
|
||||
|
||||
add_executable(firmware
|
||||
main.c
|
||||
usb_descriptors.c
|
||||
mongoose.c
|
||||
net.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
|
||||
@@ -0,0 +1,20 @@
|
||||
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
|
||||
|
||||
build: pico-sdk
|
||||
$(MKBUILD)
|
||||
cd build && cmake -G "Unix Makefiles" .. && make
|
||||
|
||||
pico-sdk:
|
||||
git clone --depth 1 -b 1.4.0 https://github.com/raspberrypi/pico-sdk $@
|
||||
cd $@ && git submodule update --init
|
||||
|
||||
clean:
|
||||
$(RM) pico-sdk build
|
||||
@@ -0,0 +1,30 @@
|
||||
# Remote MQTT Device on an RP2040
|
||||
|
||||
Your headless Raspberry Pi Pico-based hardware can also be a remote gadget when you connect it to your computer via USB
|
||||
|
||||
For the USB specifics, see this related tutorial at https://mongoose.ws/tutorials/rp2040/pico-rndis-dashboard/
|
||||
|
||||
See this tutorial to control your device: https://mongoose.ws/tutorials/mqtt-dashboard/
|
||||
|
||||
For this to work, you need your computer to act as a router (gateway) and NAT for your device.
|
||||
|
||||
## Linux setup
|
||||
|
||||
Enable "masquerading"; the quick and simple options are:
|
||||
|
||||
``` bash
|
||||
sudo iptables --flush
|
||||
sudo iptables --table nat --flush
|
||||
sudo iptables --delete-chain
|
||||
sudo iptables --table nat --delete-chain
|
||||
|
||||
# Do masquerading
|
||||
sudo iptables -t nat -A POSTROUTING -o yourifc -j MASQUERADE
|
||||
# enable routing
|
||||
sudo echo 1 > /proc/sys/net/ipv4/ip_forward
|
||||
```
|
||||
|
||||
Where `yourifc` is the interface that is connected to your network
|
||||
|
||||
## MacOS setup
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
// Copyright (c) 2023 Cesanta Software Limited
|
||||
// All rights reserved
|
||||
|
||||
#define LED PICO_DEFAULT_LED_PIN
|
||||
|
||||
static inline int gpio_read(uint16_t pin) {
|
||||
return gpio_get_out_level(pin);
|
||||
}
|
||||
static inline void gpio_write(uint16_t pin, bool val) {
|
||||
gpio_put(pin, val);
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
// Copyright (c) 2022 Cesanta Software Limited
|
||||
// All rights reserved
|
||||
|
||||
#include "mongoose.h"
|
||||
#include "net.h"
|
||||
#include "pico/stdlib.h"
|
||||
#include "tusb.h"
|
||||
|
||||
bool hal_gpio_read(int pin) {
|
||||
return (pin >= 0 && pin <= 29) ? gpio_get_out_level((uint) pin) : false;
|
||||
}
|
||||
|
||||
bool hal_gpio_write(int pin, bool val) {
|
||||
if (pin >= 0 && pin <= 29) {
|
||||
gpio_put((uint) pin, val);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
int hal_led_pin(void) {
|
||||
return PICO_DEFAULT_LED_PIN;
|
||||
}
|
||||
|
||||
static struct mg_tcpip_if *s_ifp;
|
||||
|
||||
const uint8_t tud_network_mac_address[6] = {2, 2, 0x84, 0x6A, 0x96, 0};
|
||||
|
||||
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_up(struct mg_tcpip_if *ifp) {
|
||||
(void) ifp;
|
||||
return tud_inited() && tud_ready() && tud_connected();
|
||||
}
|
||||
|
||||
static void fn(struct mg_connection *c, int ev, void *ev_data, void *fn_dta) {
|
||||
if (ev == MG_EV_HTTP_MSG) return mg_http_reply(c, 200, "", "ok\n");
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
gpio_init(PICO_DEFAULT_LED_PIN);
|
||||
gpio_set_dir(PICO_DEFAULT_LED_PIN, GPIO_OUT);
|
||||
stdio_init_all();
|
||||
|
||||
struct mg_mgr mgr; // Initialise Mongoose event manager
|
||||
mg_mgr_init(&mgr); // and attach it to the interface
|
||||
|
||||
struct mg_tcpip_driver driver = {.tx = usb_tx, .up = usb_up};
|
||||
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,
|
||||
.enable_get_gateway = true,
|
||||
.driver = &driver,
|
||||
.recv_queue.size = 4096};
|
||||
s_ifp = &mif;
|
||||
mg_tcpip_init(&mgr, &mif);
|
||||
tusb_init();
|
||||
|
||||
MG_INFO(("Initialising application..."));
|
||||
web_init(&mgr);
|
||||
|
||||
MG_INFO(("Starting event loop"));
|
||||
for (;;) {
|
||||
mg_mgr_poll(&mgr, 0);
|
||||
tud_task();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
../../../mongoose.c
|
||||
@@ -0,0 +1 @@
|
||||
../../../mongoose.h
|
||||
@@ -0,0 +1,6 @@
|
||||
#define MG_ARCH MG_ARCH_PICOSDK
|
||||
|
||||
#define MG_ENABLE_TCPIP 1
|
||||
#define MG_ENABLE_TCPIP_DRIVER_INIT 0
|
||||
#define MG_ENABLE_PACKED_FS 1
|
||||
#define MG_ENABLE_POSIX_FS 0
|
||||
@@ -0,0 +1 @@
|
||||
../../mqtt-dashboard/device/net.c
|
||||
@@ -0,0 +1 @@
|
||||
../../mqtt-dashboard/device/net.h
|
||||
@@ -0,0 +1,2 @@
|
||||
#pragma once
|
||||
#define SIZEOF_ETH_HDR 14
|
||||
@@ -0,0 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
// Windows only supports RNDIS
|
||||
// Mac only supports CDC-ECM
|
||||
// Linux supports either RNDIS, CDC-ECM or CDC-NCM
|
||||
#define CFG_TUD_ECM_RNDIS 1
|
||||
#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,248 @@
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#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_TOTAL
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
#if CFG_TUD_ECM_RNDIS
|
||||
CONFIG_ID_RNDIS = 0,
|
||||
CONFIG_ID_ECM = 1,
|
||||
#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_RNDIS_DESC_LEN)
|
||||
#define ALT_CONFIG_TOTAL_LEN (TUD_CONFIG_DESC_LEN + TUD_CDC_ECM_DESC_LEN)
|
||||
#define NCM_CONFIG_TOTAL_LEN (TUD_CONFIG_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 etc ...
|
||||
#define EPNUM_NET_NOTIF 0x81
|
||||
#define EPNUM_NET_OUT 0x02
|
||||
#define EPNUM_NET_IN 0x82
|
||||
|
||||
#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
|
||||
|
||||
#else
|
||||
#define EPNUM_NET_NOTIF 0x81
|
||||
#define EPNUM_NET_OUT 0x02
|
||||
#define EPNUM_NET_IN 0x82
|
||||
#endif
|
||||
|
||||
#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),
|
||||
};
|
||||
|
||||
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),
|
||||
};
|
||||
|
||||
#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),
|
||||
};
|
||||
|
||||
#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,
|
||||
[CONFIG_ID_ECM ] = ecm_configuration
|
||||
#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,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 pico_rand 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/")
|
||||
|
||||
@@ -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
|
||||
@@ -0,0 +1,4 @@
|
||||
|
||||
# Mongoose on PICO W
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -0,0 +1,79 @@
|
||||
// Copyright (c) 2024 Cesanta Software Limited
|
||||
// All rights reserved
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#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;}
|
||||
@@ -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);
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
../../../mongoose.c
|
||||
@@ -0,0 +1 @@
|
||||
../../../mongoose.h
|
||||
@@ -0,0 +1,5 @@
|
||||
#define MG_ARCH MG_ARCH_PICOSDK
|
||||
|
||||
#define MG_ENABLE_TCPIP 1
|
||||
#define MG_ENABLE_TCPIP_DRIVER_INIT 0
|
||||
#define MG_ENABLE_PACKED_FS 1
|
||||
@@ -0,0 +1 @@
|
||||
../../device-dashboard/net.c
|
||||
@@ -0,0 +1 @@
|
||||
../../device-dashboard/net.h
|
||||
@@ -0,0 +1 @@
|
||||
../../device-dashboard/packed_fs.c
|
||||
@@ -0,0 +1,73 @@
|
||||
# This is a copy of <PICO_SDK_PATH>/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})
|
||||
@@ -0,0 +1,13 @@
|
||||
NAME = raspberry-pi-pico-w
|
||||
|
||||
$(NAME):
|
||||
git clone --depth 1 https://github.com/mongoose-examples/$(NAME)
|
||||
|
||||
build: | $(NAME)
|
||||
cp ./../../../mongoose.[ch] $(NAME)
|
||||
cp ./../../device-dashboard/packed_fs.c $(NAME)
|
||||
cp ./../../device-dashboard/net.[ch] $(NAME)
|
||||
make -C $(NAME) build
|
||||
|
||||
clean:
|
||||
rm -rf $(NAME)
|
||||
@@ -0,0 +1 @@
|
||||
See detailed tutorial at https://mongoose.ws/tutorials/rp2040/pico-w/
|
||||
Symlink
+1
@@ -0,0 +1 @@
|
||||
w5500-evb-pico-picosdk-builtin
|
||||
@@ -0,0 +1,19 @@
|
||||
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 net.c packed_fs.c)
|
||||
target_include_directories(firmware PUBLIC .)
|
||||
|
||||
target_link_libraries(firmware pico_stdlib hardware_spi pico_rand)
|
||||
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, for remote testing
|
||||
|
||||
# Mongoose build flags in mongoose_config.h
|
||||
|
||||
# Example build options
|
||||
add_definitions(-DHTTP_URL="http://0.0.0.0/")
|
||||
add_definitions(-DHTTPS_URL="https://0.0.0.0/")
|
||||
@@ -0,0 +1,34 @@
|
||||
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 example:
|
||||
true
|
||||
|
||||
build build/firmware.uf2: pico-sdk main.c net.c
|
||||
$(MKBUILD)
|
||||
cd build && cmake -G "Unix Makefiles" .. && make
|
||||
|
||||
pico-sdk:
|
||||
git clone --depth 1 -b 1.5.0 https://github.com/raspberrypi/pico-sdk $@
|
||||
cd $@ && git submodule update --init
|
||||
|
||||
flash: build/firmware.uf2
|
||||
picotool load $< -f
|
||||
|
||||
# Automated remote test. Requires env variable VCON_API_KEY set. See https://vcon.io/automated-firmware-tests/
|
||||
DEVICE_URL ?= https://dash.vcon.io/api/v3/devices/3
|
||||
update: build/firmware.uf2
|
||||
curl --fail-with-body -su :$(VCON_API_KEY) $(DEVICE_URL)/ota?uf2=1 --data-binary @$<
|
||||
|
||||
test: update
|
||||
curl --fail-with-body -su :$(VCON_API_KEY) $(DEVICE_URL)/tx?t=5 | tee /tmp/output.txt
|
||||
grep 'READY, IP:' /tmp/output.txt # Check for network init
|
||||
# grep 'MQTT connected' /tmp/output.txt # Check for MQTT connection success
|
||||
|
||||
clean:
|
||||
$(RM) pico-sdk build
|
||||
@@ -0,0 +1,3 @@
|
||||
# RP2040 + W5500 Ethernet chip
|
||||
|
||||
- See detailed tutorial (including hardware connections) at https://mongoose.ws/tutorials/rp2040/pico-w5500/
|
||||
@@ -0,0 +1,103 @@
|
||||
// Copyright (c) 2023 Cesanta Software Limited
|
||||
// All rights reserved
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "hardware/spi.h"
|
||||
#include "pico/rand.h"
|
||||
#include "pico/stdlib.h"
|
||||
#include "pico/unique_id.h"
|
||||
|
||||
#include "mongoose.h"
|
||||
#include "net.h"
|
||||
|
||||
enum { BLINK_PERIOD_MS = 1000 };
|
||||
enum { LED = 25, SPI_CS = 17, SPI_CLK = 18, SPI_TX = 19, SPI_RX = 16 }; // Pins
|
||||
|
||||
static void spi_begin(void *spi) {
|
||||
gpio_put(SPI_CS, 0);
|
||||
}
|
||||
static void spi_end(void *spi) {
|
||||
gpio_put(SPI_CS, 1);
|
||||
}
|
||||
static uint8_t spi_txn(void *spi, uint8_t byte) {
|
||||
uint8_t result = 0;
|
||||
spi_write_read_blocking(spi0, &byte, &result, 1);
|
||||
return result;
|
||||
}
|
||||
|
||||
bool mg_random(void *buf, size_t len) {
|
||||
for (size_t n = 0; n < len; n += sizeof(uint32_t)) {
|
||||
uint32_t r = get_rand_32();
|
||||
memcpy((char *) buf + n, &r, n + sizeof(r) > len ? len - n : sizeof(r));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static void timer_fn(void *arg) {
|
||||
gpio_put(PICO_DEFAULT_LED_PIN,
|
||||
!gpio_get_out_level(PICO_DEFAULT_LED_PIN)); // Blink LED
|
||||
struct mg_tcpip_if *ifp = arg; // And show
|
||||
const char *names[] = {"down", "up", "req", "ready"}; // network stats
|
||||
MG_INFO(("Ethernet: %s, IP: %M, rx:%u, tx:%u, dr:%u, er:%u",
|
||||
names[ifp->state], mg_print_ip4, &ifp->ip, ifp->nrecv, ifp->nsent,
|
||||
ifp->ndrop, ifp->nerr));
|
||||
}
|
||||
|
||||
static void eth_spi_init(void) {
|
||||
// Init SPI pins
|
||||
spi_init(spi0, 500 * 1000);
|
||||
gpio_set_function(SPI_RX, GPIO_FUNC_SPI); // MISO
|
||||
gpio_set_function(SPI_TX, GPIO_FUNC_SPI); // MOSI
|
||||
gpio_set_function(SPI_CLK, GPIO_FUNC_SPI); // CLK
|
||||
gpio_init(SPI_CS); // CS
|
||||
gpio_set_dir(SPI_CS, GPIO_OUT); // Set CS to output
|
||||
gpio_put(SPI_CS, 1); // And drive CS high (inactive)
|
||||
}
|
||||
|
||||
// Helper macro for MAC generation
|
||||
#define GENERATE_LOCALLY_ADMINISTERED_MAC(id) \
|
||||
{ 2, id[3], id[4], id[5], id[6], id[7] }
|
||||
|
||||
int main(void) {
|
||||
stdio_init_all();
|
||||
gpio_init(LED); // Setup board LED
|
||||
gpio_set_dir(LED, GPIO_OUT);
|
||||
eth_spi_init(); // Initialise SPI pins
|
||||
MG_INFO(("Starting ..."));
|
||||
|
||||
pico_unique_board_id_t board_id;
|
||||
pico_get_unique_board_id(&board_id);
|
||||
uint8_t *id = board_id.id;
|
||||
|
||||
struct mg_mgr mgr; // Initialise
|
||||
mg_mgr_init(&mgr); // Mongoose event manager
|
||||
mg_log_set(MG_LL_DEBUG); // Set log level
|
||||
|
||||
// Initialise Mongoose network stack
|
||||
struct mg_tcpip_spi spi = {NULL, spi_begin, spi_end, spi_txn};
|
||||
struct mg_tcpip_if mif = {.mac = GENERATE_LOCALLY_ADMINISTERED_MAC(id),
|
||||
// Uncomment below for static configuration:
|
||||
// .ip = mg_htonl(MG_U32(192, 168, 0, 223)),
|
||||
// .mask = mg_htonl(MG_U32(255, 255, 255, 0)),
|
||||
// .gw = mg_htonl(MG_U32(192, 168, 0, 1)),
|
||||
.driver = &mg_tcpip_driver_w5500,
|
||||
.driver_data = &spi};
|
||||
mg_tcpip_init(&mgr, &mif);
|
||||
mg_timer_add(&mgr, BLINK_PERIOD_MS, MG_TIMER_REPEAT, timer_fn, &mif);
|
||||
|
||||
MG_INFO(("MAC: %M. Waiting for IP...", mg_print_mac, mif.mac));
|
||||
while (mif.state != MG_TCPIP_STATE_READY) {
|
||||
mg_mgr_poll(&mgr, 0);
|
||||
}
|
||||
|
||||
MG_INFO(("Initialising application..."));
|
||||
web_init(&mgr);
|
||||
|
||||
MG_INFO(("Starting event loop"));
|
||||
for (;;) {
|
||||
mg_mgr_poll(&mgr, 1);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
../../../mongoose.c
|
||||
@@ -0,0 +1 @@
|
||||
../../../mongoose.h
|
||||
@@ -0,0 +1,8 @@
|
||||
#define MG_ARCH MG_ARCH_PICOSDK
|
||||
|
||||
#define MG_ENABLE_TCPIP 1
|
||||
#define MG_ENABLE_TCPIP_DRIVER_INIT 0
|
||||
#define MG_ENABLE_DRIVER_W5500 1
|
||||
#define MG_ENABLE_CUSTOM_RANDOM 1
|
||||
#define MG_ENABLE_PACKED_FS 1
|
||||
#define MG_ENABLE_POSIX_FS 0
|
||||
@@ -0,0 +1 @@
|
||||
../../device-dashboard/net.c
|
||||
@@ -0,0 +1 @@
|
||||
../../device-dashboard/net.h
|
||||
@@ -0,0 +1 @@
|
||||
../../device-dashboard/packed_fs.c
|
||||
Reference in new issue
Block a user