Add radio simulation

This commit is contained in:
cpq committed 2026-11-05 22:23:06 +00:00
1 parent 9337a78cd0
commit d39519b92f
66 files changed
+5659 -1000

No files matched your search

+6 -1
View File
@@ -14,6 +14,7 @@ CFLAGS += -mcpu=cortex-m33 -mthumb -mfloat-abi=hard -mfpu=fpv5-sp-d16 $(CFLAGS_E
LDFLAGS ?= -Tlink.ld -nostdlib -nostartfiles --specs=nano.specs -lc -lm -lgcc -Wl,--gc-sections
SOURCES = main.c hal.c $(MGDIR)/mongoose.c
SOURCES += $(MGDIR)/tutorials/mqtt/mqtt-client/mongoose_mqtt.c
SOURCES += cmsis_mcxw71/MCXW/MCXW716C/gcc/startup_MCXW716C.S
@@ -23,9 +24,13 @@ firmware.bin: firmware.elf
arm-none-eabi-objcopy -O binary $< $@
arm-none-eabi-size --format=berkeley $<
firmware.elf: cmsis_core cmsis_mcxw71 $(SOURCES) $(wildcard *.h) link.ld Makefile
firmware.elf: cmsis_core cmsis_mcxw71 $(SOURCES) $(wildcard *.h) $(MGDIR)/mongoose.h link.ld Makefile mesh-config
arm-none-eabi-gcc $(SOURCES) $(CFLAGS) $(LDFLAGS) -o $@
# Rebuild when overriding the board address or timing.
.PHONY: mesh-config
mesh-config:
cmsis_core:
git clone -q -c advice.detachedHead=false --depth 1 -b 5.9.0 https://github.com/ARM-software/CMSIS_5 $@
+36 -14
View File
@@ -9,7 +9,7 @@
// 802.15.4 register setup. Then select the legacy 802.15.4 demodulator and
// CCA from RX digital. See PhyHwInit() in NXP ieee_802_15_4 PHY sources,
// PhyPacketProcessor.c
bool hal_radio_init(void) {
void hal_radio_init(void) {
uint32_t stat = RFMC_RF2P4GHZ_STAT_RST_STAT_MASK |
RFMC_RF2P4GHZ_STAT_LP_ACK_STAT_MASK;
size_t i, n = 1000000;
@@ -20,7 +20,10 @@ bool hal_radio_init(void) {
RFMC_RF2P4GHZ_CTRL_RST_MASK); // Release radio reset, no low power
while ((RFMC->RF2P4GHZ_STAT & stat) != 0 ||
(RFMC->XO_STAT & RFMC_XO_STAT_XTAL_RDY_MASK) == 0) {
if (--n == 0) return false; // Radio did not wake up
if (--n == 0) {
MG_ERROR(("Radio init failed")); // Radio did not wake up
return;
}
}
RADIO_CTRL->LL_CTRL = RADIO_CTRL_LL_CTRL_ACTIVE_LL_MASK;
RFMC->RF2P4GHZ_CTRL |=
@@ -31,17 +34,17 @@ bool hal_radio_init(void) {
XCVR_MISC->XCVR_CTRL &= ~XCVR_MISC_XCVR_CTRL_DEMOD_SEL_MASK;
XCVR_MISC->XCVR_CTRL |= XCVR_MISC_XCVR_CTRL_DEMOD_SEL(2);
XCVR_ZBDEMOD->CCA_LQI_SRC |= XCVR_ZBDEMOD_CCA_LQI_SRC_CCA1_FROM_RX_DIG(1);
return true;
}
// Random bytes for Mongoose, MG_ENABLE_CUSTOM_RANDOM. Entropy is radio noise:
// receiver RSSI, sampled while the radio receives, mixed with SysTick by
// SHA-256. Good enough for random addresses and nonces, not a certified TRNG
bool mg_random(void *buf, size_t len) {
static uint32_t counter;
uint8_t digest[32], *p = (uint8_t *) buf;
// Random 32 bits, for mg_random(). Entropy is radio noise: receiver RSSI,
// sampled while the radio receives, mixed with SysTick by SHA-256. One hash
// fills a pool of 8 words. Good enough for random addresses and nonces, not
// a certified TRNG
uint32_t hal_rng_read(void) {
static uint32_t counter, pool[8];
static size_t avail;
size_t i, n;
while (len > 0) {
if (avail == 0) {
mg_sha256_ctx ctx;
mg_sha256_init(&ctx);
counter++;
@@ -51,11 +54,10 @@ bool mg_random(void *buf, size_t len) {
mg_sha256_update(&ctx, (uint8_t *) sample, sizeof(sample));
for (n = 0; n < 50; n++) __NOP(); // Let the RSSI update
}
mg_sha256_final(digest, &ctx);
n = len < sizeof(digest) ? len : sizeof(digest);
memcpy(p, digest, n), p += n, len -= n;
mg_sha256_final((uint8_t *) pool, &ctx);
avail = sizeof(pool) / sizeof(pool[0]);
}
return true;
return pool[--avail];
}
// Added to minimise the bin size: avoid pulling printf and friends
@@ -81,6 +83,26 @@ uint64_t hal_get_tick(void) {
return s_ticks;
}
uint64_t mg_micros(void);
// SysTick runs at the core clock and reloads every millisecond. Sample
// the tick counter and the sub-millisecond counter in one critical section.
uint64_t mg_micros(void) {
uint32_t primask = __get_PRIMASK(), val, pending;
uint64_t ms;
__disable_irq();
ms = hal_get_tick();
pending = SCB->ICSR & SCB_ICSR_PENDSTSET_Msk;
val = SysTick->VAL;
if (pending != (SCB->ICSR & SCB_ICSR_PENDSTSET_Msk)) {
pending = SCB_ICSR_PENDSTSET_Msk;
val = SysTick->VAL;
}
__set_PRIMASK(primask);
if (pending != 0 && val != 0) ms++;
return ms * 1000 + ((uint64_t) (SysTick->LOAD - val) * 1000) /
(SysTick->LOAD + 1);
}
uint32_t SystemCoreClock;
// Called by the startup code before .data and .bss are initialised, so it
+54 -8
View File
@@ -17,11 +17,14 @@ uint64_t hal_get_tick(void);
bool hal_timer_expired(volatile uint64_t *t, uint64_t period, uint64_t now);
size_t hal_ram_used(void);
size_t hal_ram_free(void);
bool hal_radio_init(void); // Set up the 2.4 GHz transceiver for 802.15.4
void hal_radio_init(void); // Set up the 2.4 GHz transceiver for 802.15.4
uint32_t hal_rng_read(void); // Random 32 bits, radio noise. See hal.c
extern uint32_t SystemCoreClock; // CPU clock, Hz. Set by hal_clock_init()
#define BIT(x) (1UL << (x))
#define PIN(port, num) ((((port) - 'A') << 8) | (num))
#define PINNO(pin) (pin & 255)
#define PINPORT(pin) (pin >> 8)
#define CLRSET(reg, clear, set) ((reg) = ((reg) & ~(clear)) | (set))
#define SIRC_FREQUENCY 6000000UL // Slow IRC (FRO6M), Hz
@@ -92,15 +95,53 @@ static inline void hal_periph_clock(volatile uint32_t *mrcc, uint32_t src) {
__DSB();
}
// LPUART1 is wired to the MCU-Link debug probe USB serial port.
// TX = PTC3, RX = PTC2, both pin mux alternative 3. See board.h, pin_mux.c
static inline void hal_uart_init(LPUART_Type *uart, unsigned long baud) {
// PORT, the pin multiplexer, of a pin. Ports A..C
static inline PORT_Type *hal_pin_port(uint16_t pin) {
PORT_Type *ports[] = {PORTA, PORTB, PORTC};
return ports[PINPORT(pin)];
}
// GPIO of a pin. Ports A..C
static inline GPIO_Type *hal_pin_gpio(uint16_t pin) {
GPIO_Type *gpios[] = {GPIOA, GPIOB, GPIOC};
return gpios[PINPORT(pin)];
}
// Route a pin to function mux: 1 GPIO, others see the datasheet pinout.
// Clocks its PORT: MRCC_PORTA..C are consecutive
static inline void hal_pin_mux(uint16_t pin, uint32_t mux) {
hal_periph_clock(&MRCC->MRCC_PORTA + PINPORT(pin), 0);
hal_pin_port(pin)->PCR[PINNO(pin)] = PORT_PCR_MUX(mux);
}
static inline void hal_gpio_output(uint16_t pin) {
hal_pin_mux(pin, 1);
hal_periph_clock(&MRCC->MRCC_GPIOA + PINPORT(pin), 0); // Consecutive, too
hal_pin_gpio(pin)->PDDR |= BIT(PINNO(pin));
}
static inline void hal_gpio_write(uint16_t pin, bool val) {
if (val) {
hal_pin_gpio(pin)->PSOR = BIT(PINNO(pin));
} else {
hal_pin_gpio(pin)->PCOR = BIT(PINNO(pin));
}
}
static inline void hal_gpio_toggle(uint16_t pin) {
hal_pin_gpio(pin)->PTOR = BIT(PINNO(pin));
}
// LPUART1, on tx and rx pins, pin mux alternative 3. On FRDM-MCXW71 it is
// wired to the MCU-Link debug probe USB serial port: TX PTC3, RX PTC2, see
// board.h, pin_mux.c
static inline void hal_uart_init(LPUART_Type *uart, uint16_t tx, uint16_t rx,
unsigned long baud) {
uint32_t src = MRCC_SRC_FRO192M, freq = hal_firc_freq();
uint32_t best_osr = 16, best_sbr = 1, best_err = UINT32_MAX, osr;
if (freq == 0) src = MRCC_SRC_FRO6M, freq = SIRC_FREQUENCY; // FIRC off
hal_periph_clock(&MRCC->MRCC_PORTC, 0);
PORTC->PCR[2] = PORT_PCR_MUX(3); // RX
PORTC->PCR[3] = PORT_PCR_MUX(3); // TX
hal_pin_mux(rx, 3);
hal_pin_mux(tx, 3);
hal_periph_clock(&MRCC->MRCC_LPUART1, src);
// Find the oversampling ratio (4..32) that gives the lowest baud rate error
for (osr = 4; osr <= 32; osr++) {
@@ -141,6 +182,11 @@ static inline void hal_wdog_disable(void) {
while ((WDOG0->CS & WDOG_CS_RCS_MASK) == 0) (void) 0;
}
// Random numbers come from radio noise, see hal_rng_read(): nothing to set
// up here. The radio must be running, see hal_radio_init()
static inline void hal_rng_init(void) {
}
static inline void hal_sys_init(void) {
SCB->CPACR |= ((3UL << 10 * 2) | (3UL << 11 * 2)); // Enable FPU
hal_wdog_disable();
@@ -33,6 +33,9 @@ SECTIONS {
_ebss = .;
} > sram
/* Health record, see MG_HEALTH_RAM: not cleared, survives a warm reset */
.mg_health (NOLOAD) : { KEEP(*(.mg_health .mg_health*)) } > sram
. = ALIGN(8);
_end = .; /* Start of heap, see _sbrk() in hal.c */
}
+35 -11
View File
@@ -1,24 +1,42 @@
// Copyright (c) 2026 Cesanta Software Limited
// All rights reserved
//
// IPv4 over IEEE 802.15.4 on FRDM-MCXW71, Mongoose built-in TCP/IP stack.
// The radio carries Ethernet frames, channel 15, PAN ID 0xabcd. With the
// FRDM-RW612 eth_radio_bridge tutorial, gets an address from the LAN's DHCP
// server and is reachable from the LAN. Serves pages on ports 80 and 443
#include "hal.h"
#include "mongoose.h"
#ifndef UART_DEBUG
#define UART_DEBUG LPUART1 // MCU-Link USB serial port
#define UART_DEBUG_TX_PIN PIN('C', 3)
#define UART_DEBUG_RX_PIN PIN('C', 2)
#endif
#define LED1 PIN('A', 19)
#define LED2 PIN('A', 20)
#define LED3 PIN('A', 21)
static void log_fn(char ch, void *param) {
hal_uart_write_buf(param, &ch, 1);
}
static void blink_task(void) {
static uint64_t blink_timer = 0;
if (hal_timer_expired(&blink_timer, 500, hal_get_tick())) {
hal_gpio_toggle(LED2);
}
}
uint64_t mg_millis(void) {
return hal_get_tick();
}
bool mg_random(void *buf, size_t len) {
for (size_t n = 0; n < len; n += sizeof(uint32_t)) {
uint32_t r = hal_rng_read();
memcpy((char *) buf + n, &r, n + sizeof(r) > len ? len - n : sizeof(r));
}
return true;
}
// Self-signed P-256 certificate and key for the HTTPS listener. Created by:
// openssl ecparam -genkey -name prime256v1 -noout -out key.pem
// openssl req -x509 -key key.pem -out cert.pem -days 3650 -subj /CN=mg
@@ -70,16 +88,21 @@ static void http_ev_handler(struct mg_connection *c, int ev, void *ev_data) {
}
int main(void) {
hal_clock_init();
hal_uart_init(UART_DEBUG, 115200);
mg_log_set_fn(log_fn, UART_DEBUG);
MG_HEALTH_INIT();
if (!hal_radio_init()) MG_ERROR(("Radio init failed"));
hal_clock_init();
hal_uart_init(UART_DEBUG, UART_DEBUG_TX_PIN, UART_DEBUG_RX_PIN, 115200);
mg_log_set_fn(log_fn, UART_DEBUG);
hal_rng_init();
hal_radio_init();
hal_gpio_output(LED1), hal_gpio_write(LED1, true); // Active low
hal_gpio_output(LED2), hal_gpio_write(LED2, true); // Active low
hal_gpio_output(LED3), hal_gpio_write(LED3, true); // Active low
struct mg_mgr mgr;
mg_mgr_init(&mgr);
mg_http_listen(&mgr, "http://0.0.0.0:80", http_ev_handler, NULL);
mg_http_listen(&mgr, "https://0.0.0.0:443", http_ev_handler, NULL);
mg_http_listen(&mgr, "http://0.0.0.0", http_ev_handler, NULL);
mg_http_listen(&mgr, "https://0.0.0.0", http_ev_handler, NULL);
mg_mqtt_init(&mgr);
MG_INFO(("Initialised. CPU clock: %lu MHz", SystemCoreClock / 1000000));
@@ -89,6 +112,7 @@ int main(void) {
for (;;) {
mg_mgr_poll(&mgr, 0);
mg_mqtt_poll(&mgr);
blink_task();
}
return 0;
@@ -1,9 +1,10 @@
#pragma once
#include "MCXW716C.h"
// See https://mongoose.ws/docs/getting-started/build-environment/
// See https://mongoose.ws/docs/getting-started/build-options/
#define MG_ARCH MG_ARCH_ARMGCC
#define MG_TLS MG_TLS_BUILTIN
#define MQTT_CLIENT_ID "w71" // For tutorials/mqtt/mqtt-client
#define MG_OTA MG_OTA_MCXW71
#define MG_ENABLE_TCPIP 1
@@ -21,3 +22,12 @@
(mac)[0] = 2, (mac)[1] = b_[1], (mac)[2] = b_[2], (mac)[3] = b_[3]; \
(mac)[4] = b_[4], (mac)[5] = b_[5]; \
} while (0)
// Crash report support. Health record lives in the .mg_health region, see link.ld
#define MG_ENABLE_HEALTH 1
#define MG_HEALTH_RAM __attribute__((section(".mg_health")))
// mdash.net device management service support
#define MG_ENABLE_MDASH 0
#define MG_MDASH_KEY "..." // Set this - mDash device token
#define MG_FIRMWARE_VERSION "w71.1.0.0"
@@ -17,6 +17,7 @@ CFLAGS += -mcpu=cortex-m33+nodsp -mthumb -mfloat-abi=hard -mfpu=fpv5-sp-d16 $(CF
LDFLAGS ?= -Tlink.ld -nostdlib -nostartfiles --specs=nano.specs -lc -lgcc -Wl,--gc-sections
SOURCES = main.c hal.c radio.c startup.c flexspi.c flash_config.c $(MGDIR)/mongoose.c
SOURCES += cmsis_rw612/devices/RW612/drivers/fsl_clock.c
SOURCES += cmsis_rw612/devices/RW612/drivers/fsl_trng.c
SOURCES += cmsis_rw612/devices/RW612/drivers/fsl_reset.c
@@ -35,9 +36,13 @@ firmware.bin: firmware.elf
arm-none-eabi-objcopy -O binary $< $@
arm-none-eabi-size --format=berkeley $<
firmware.elf: cmsis_core cmsis_rw612 $(RADIO_FW) $(SOURCES) $(wildcard *.h) link.ld Makefile mongoose_config.h
firmware.elf: cmsis_core cmsis_rw612 $(RADIO_FW) $(SOURCES) $(wildcard *.h) $(MGDIR)/mongoose.h link.ld Makefile mongoose_config.h mesh-config
arm-none-eabi-gcc $(SOURCES) $(CFLAGS) $(LDFLAGS) -o $@
# Rebuild when overriding the board address or timing.
.PHONY: mesh-config
mesh-config:
cmsis_core:
git clone -q -c advice.detachedHead=false --depth 1 -b 5.9.0 https://github.com/ARM-software/CMSIS_5 $@
@@ -56,7 +61,7 @@ show_symbols_sorted_by_size: firmware.bin
arm-none-eabi-readelf -sW firmware.elf | sed 1,3d | sed 's,^.*: ,,' | tr -s ' ' | cut -d ' ' -f 2,7 | sort -nk1 | grep -v '^0 '
flash: firmware.bin
(echo 'loadfile firmware.elf'; echo 'reset'; echo 'exit';) | JLinkExe -device RW612 -if SWD -speed 4000 -autoconnect 1 -NoGui 1 -ExitOnError 1
(echo 'loadfile firmware.elf'; echo 'reset'; echo 'go'; echo 'exit';) | JLinkExe -device RW612 -if SWD -speed 4000 -autoconnect 1 -NoGui 1 -ExitOnError 1
# JRun --nogui --device RW612 --if SWD firmware.elf
clean:
@@ -26,6 +26,26 @@ uint64_t hal_get_tick(void) {
return s_ticks;
};
uint64_t mg_micros(void);
// SysTick runs at the core clock and reloads every millisecond. Sample
// the tick counter and the sub-millisecond counter in one critical section.
uint64_t mg_micros(void) {
uint32_t primask = __get_PRIMASK(), val, pending;
uint64_t ms;
__disable_irq();
ms = hal_get_tick();
pending = SCB->ICSR & SCB_ICSR_PENDSTSET_Msk;
val = SysTick->VAL;
if (pending != (SCB->ICSR & SCB_ICSR_PENDSTSET_Msk)) {
pending = SCB_ICSR_PENDSTSET_Msk;
val = SysTick->VAL;
}
__set_PRIMASK(primask);
if (pending != 0 && val != 0) ms++;
return ms * 1000 + ((uint64_t) (SysTick->LOAD - val) * 1000) /
(SysTick->LOAD + 1);
}
uint32_t SystemCoreClock = SYS_FREQUENCY;
int hal_radio_err; // Result of hal_radio_init()
void SystemInit(void) { // Called automatically by startup code
@@ -152,16 +152,22 @@ static inline void hal_gpio_toggle(uint16_t pin) {
#define MG_HAL_SYSTICK MG_HAL_SYSTICK_NONE
#endif
static inline void hal_uart_init(USART_Type *uart, unsigned long baud) {
if (uart == USART0) {
static inline void hal_uart_init(USART_Type *uart, unsigned long baud,
uint16_t tx_pin, uint16_t rx_pin) {
if (uart == USART0 && tx_pin == PIN(0, 3) &&
rx_pin == PIN(0, 2)) {
IO_MUX_SetPinMux(IO_MUX_FC0_USART_DATA);
} else if (uart == USART1) {
} else if (uart == USART1 && tx_pin == PIN(0, 8) &&
rx_pin == PIN(0, 9)) {
IO_MUX_SetPinMux(IO_MUX_FC1_USART_DATA);
} else if (uart == USART2) {
} else if (uart == USART2 && tx_pin == PIN(0, 14) &&
rx_pin == PIN(0, 13)) {
IO_MUX_SetPinMux(IO_MUX_FC2_USART_DATA);
} else if (uart == USART3) {
} else if (uart == USART3 && tx_pin == PIN(0, 26) &&
rx_pin == PIN(0, 24)) {
IO_MUX_SetPinMux(IO_MUX_FC3_USART_DATA);
} else if (uart == USART14) {
} else if (uart == USART14 && tx_pin == PIN(1, 24) &&
rx_pin == PIN(1, 25)) {
IO_MUX_SetPinMux(IO_MUX_FC14_USART_DATA);
} else {
return;
+123
View File
@@ -0,0 +1,123 @@
// Copyright (c) 2026 Cesanta Software Limited
// All rights reserved
#include "hal.h"
#include "mongoose.h"
#define LED_RADIO PIN(0, 12) // Green, active low
#define LED_LAN PIN(0, 0) // Blue, active low
#define LED_RED PIN(0, 1) // Red, active low
static uint64_t s_radio_led_until;
// Cumulative totals. Up: radio -> LAN; down: LAN -> radio queue.
static uint64_t s_frames_up, s_frames_down, s_bytes_up, s_bytes_down;
static uint64_t s_radio_sent, s_radio_received;
static size_t (*s_radio_tx)(struct mg_tcpip_if *, const void *, size_t);
static size_t radio_tx(struct mg_tcpip_if *ifp, const void *buf, size_t len) {
size_t sent = s_radio_tx(ifp, buf, len);
if (sent != 0) s_radio_sent++;
return sent;
}
static size_t eth_up(const void *buf, size_t len, struct mg_tcpip_if *ifp) {
size_t sent = mg_tcpip_driver_rw612.tx(buf, len, ifp);
if (sent != 0) s_frames_up++, s_bytes_up += sent;
return sent;
}
static size_t eth_down(struct mg_mesh_node *node, const void *buf, size_t len) {
size_t queued = mg_mesh_default.tx(node, buf, len);
if (queued != 0) s_frames_down++, s_bytes_down += queued;
return queued;
}
static void radio_rx(struct mg_mesh_node *node, void *buf, size_t len) {
s_radio_received++;
s_radio_led_until = mg_millis() + 2;
mg_mesh_default.rx(node, buf, len);
}
static void log_fn(char ch, void *arg) {
hal_uart_write_buf(arg, &ch, 1);
}
uint64_t mg_millis(void) {
return hal_get_tick();
}
int main(void) {
static struct mg_tcpip_driver_rw612_data eth_data;
static struct mg_tcpip_if eth;
static struct mg_tcpip_driver eth_driver;
static char eth_queue[8192];
static struct mg_tcpip_driver_rw612w_data radio_data;
static struct mg_tcpip_if radio;
static struct mg_mesh mesh;
uint32_t lan_received = 0;
uint64_t lan_led_until = 0;
uint64_t link_timer = 0, stats_timer = 0;
hal_clock_init();
hal_rng_init();
hal_gpio_output(LED_RADIO);
hal_gpio_output(LED_LAN);
hal_gpio_output(LED_RED);
hal_gpio_write(LED_RADIO, true);
hal_gpio_write(LED_LAN, true);
hal_gpio_write(LED_RED, true);
hal_uart_init(USART3, 115200, PIN(0, 26), PIN(0, 24));
mg_log_set_fn(log_fn, USART3);
hal_ethernet_init();
mesh = mg_mesh_default;
mesh.tx = eth_down;
mesh.rx = radio_rx; // Observe radio reception without modifying the driver.
radio_data.strategy = &mesh;
radio.driver = &mg_tcpip_driver_rw612w;
radio.driver_data = &radio_data;
if (!radio.driver->init(&radio)) return 1;
s_radio_tx = radio_data.mesh->tx;
radio_data.mesh->tx = radio_tx;
eth_data.mdc_cr = MG_DRIVER_MDC_CR;
eth_data.mdc_holdtime = MG_DRIVER_MDC_HOLDTIME;
eth_data.phy_addr = MG_TCPIP_PHY_ADDR;
eth_driver = mg_tcpip_driver_rw612;
eth_driver.tx = eth_up;
eth.driver = &eth_driver;
eth.driver_data = &eth_data;
memcpy(eth.mac, radio.mac, 6);
mg_queue_init(&eth.recv_queue, eth_queue, sizeof(eth_queue));
if (!eth.driver->init(&eth)) return 1;
ENET->RCR |= ENET_RCR_PROM_MASK; // Receive frames addressed to radio nodes.
radio_data.mesh->lan = &eth;
radio_data.mesh->next_tick = 0;
MG_INFO(("Ethernet/radio bridge, channel %u", MG_802154_CHANNEL));
stats_timer = mg_millis() + 5000;
for (;;) {
uint64_t now = mg_millis();
if (mg_timer_expired(&link_timer, 100, mg_millis())) {
eth.state = eth.driver->poll(&eth, true) ? MG_TCPIP_STATE_UP
: MG_TCPIP_STATE_DOWN;
}
radio.driver->poll(&radio, false);
if (mg_timer_expired(&stats_timer, 5000, now)) {
MG_INFO(
("ETH frames up/down %llu/%llu, bytes up/down "
"%llu/%llu, radio frames sent/rcvd %llu/%llu",
s_frames_up, s_frames_down, s_bytes_up, s_bytes_down, s_radio_sent,
s_radio_received));
}
if (eth.nrecv != lan_received) {
lan_received = eth.nrecv;
lan_led_until = now + 2;
}
hal_gpio_write(LED_RADIO, now >= s_radio_led_until);
hal_gpio_write(LED_LAN, now >= lan_led_until);
}
return 0;
}
@@ -0,0 +1,13 @@
#pragma once
// See https://mongoose.ws/docs/getting-started/build-environment/
#define MG_ARCH MG_ARCH_ARMGCC
#define MG_ENABLE_TCPIP 1
#define MG_ENABLE_DRIVER_RW612 1 // Ethernet
#define MG_ENABLE_DRIVER_RW612W 1 // Mesh bridge over raw 802.15.4
#define MG_ENABLE_CUSTOM_MILLIS 1
// The bridge initializes and polls its two L2 interfaces directly.
#define MG_ENABLE_TCPIP_DRIVER_INIT 0
@@ -1,101 +0,0 @@
// Copyright (c) 2026 Cesanta Software Limited
// All rights reserved
//
// Ethernet <-> IEEE 802.15.4 radio bridge. Radio frames carry Ethernet frames
// split into pieces, see mg_l2_802154_tx(). Radio nodes, e.g. the
// FRDM-MCXW71 minimal tutorial, are on the Ethernet LAN: they get IPv4
// addresses from its DHCP server. The bridge itself has no IP address.
// Everything from the radio goes to Ethernet. From Ethernet, only broadcasts
// and frames to radio nodes go to the radio, it is much slower
#include "hal.h"
#include "mongoose.h"
#define UART_DEBUG USART3
static void log_fn(char ch, void *param) {
hal_uart_write_buf(param, &ch, 1);
}
uint64_t mg_millis(void) {
return hal_get_tick();
}
// MAC addresses of radio nodes, learned from frames they send
static uint8_t s_nodes[8][6];
static size_t s_next;
// Remember a radio node's MAC address, if new
static void learn(const uint8_t *mac) {
size_t i, n = sizeof(s_nodes) / sizeof(s_nodes[0]);
for (i = 0; i < n && memcmp(s_nodes[i], mac, 6) != 0; i++) (void) 0;
if (i < n) return;
memcpy(s_nodes[s_next], mac, 6);
s_next = (s_next + 1) % n;
MG_INFO(("Radio node %M", mg_print_mac, mac));
}
// Is an Ethernet frame for the radio: broadcast, or to a radio node
static bool for_radio(const uint8_t *dst) {
size_t i, n = sizeof(s_nodes) / sizeof(s_nodes[0]);
if (memcmp(dst, "\xff\xff\xff\xff\xff\xff", 6) == 0) return true;
for (i = 0; i < n && memcmp(s_nodes[i], dst, 6) != 0; i++) (void) 0;
return i < n;
}
int main(void) {
static struct mg_tcpip_driver_rw612_data eth_data = {
.mdc_cr = MG_DRIVER_MDC_CR, .phy_addr = MG_TCPIP_PHY_ADDR};
static struct mg_l2_802154_data radio_data = {
.channel = MG_L2_802154_CHANNEL, .pan_id = MG_L2_802154_PAN_ID};
static struct mg_tcpip_if eth = {.driver = &mg_tcpip_driver_rw612,
.driver_data = &eth_data};
static struct mg_tcpip_if radio = {.driver = &mg_tcpip_driver_rw612_154,
.driver_data = &radio_data};
static char eth_queue[8192]; // Filled by the Ethernet IRQ handler
static uint8_t frame[1540];
uint8_t uid[FSL_OCOTP_UID_LENGTH], digest[32];
uint32_t uid_len = sizeof(uid);
uint64_t timer = 0;
char *p;
size_t n;
hal_clock_init();
hal_uart_init(UART_DEBUG, 115200);
mg_log_set_fn(log_fn, UART_DEBUG);
if (hal_radio_err) {
MG_ERROR(("Radio firmware failed to start, step %d", hal_radio_err));
}
// MAC addresses made from the chip unique ID
memset(uid, 0, sizeof(uid));
OCOTP_ReadUniqueID(uid, &uid_len);
mg_sha256(digest, uid, sizeof(uid));
memcpy(eth.mac, digest, 6);
memcpy(radio.mac, digest + 6, 6);
eth.mac[0] = radio.mac[0] = 0x02; // Locally administered, unicast
hal_ethernet_init();
mg_queue_init(&eth.recv_queue, eth_queue, sizeof(eth_queue));
if (!eth.driver->init(&eth)) MG_ERROR(("Ethernet init failed"));
if (!radio.driver->init(&radio)) MG_ERROR(("Radio init failed"));
ENET->RCR |= ENET_RCR_PROM_MASK; // Receive frames for radio nodes, too
MG_INFO(("Bridge started, radio channel %d", radio_data.channel));
for (;;) {
if ((n = mg_queue_next(&eth.recv_queue, &p)) > 0) { // Ethernet -> radio
if (n >= 14 && for_radio((uint8_t *) p)) radio.driver->tx(p, n, &radio);
mg_queue_del(&eth.recv_queue, n);
}
if ((n = radio.driver->rx(frame, sizeof(frame), &radio)) >= 14) {
learn(frame + 6); // Radio -> Ethernet
eth.driver->tx(frame, n, &eth);
}
if (mg_timer_expired(&timer, 1000, mg_millis())) { // Ethernet link
bool up = eth.driver->poll(&eth, true);
eth.state = up ? MG_TCPIP_STATE_UP : MG_TCPIP_STATE_DOWN;
}
}
return 0;
}
@@ -1,10 +0,0 @@
#pragma once
// See https://mongoose.ws/docs/getting-started/build-environment/
#define MG_ARCH MG_ARCH_ARMGCC
#define MG_ENABLE_TCPIP 1
#define MG_ENABLE_DRIVER_RW612 1 // Ethernet
#define MG_ENABLE_DRIVER_RW612_154 1 // 802.15.4 radio
#define MG_ENABLE_TCPIP_DRIVER_INIT 0 // main.c drives both, no TCP/IP stack
#define MG_ENABLE_CUSTOM_MILLIS 1
+12 -6
View File
@@ -147,16 +147,22 @@ static inline void hal_gpio_toggle(uint16_t pin) {
#define MG_HAL_SYSTICK MG_HAL_SYSTICK_NONE
#endif
static inline void hal_uart_init(USART_Type *uart, unsigned long baud) {
if (uart == USART0) {
static inline void hal_uart_init(USART_Type *uart, unsigned long baud,
uint16_t tx_pin, uint16_t rx_pin) {
if (uart == USART0 && tx_pin == PIN(0, 3) &&
rx_pin == PIN(0, 2)) {
IO_MUX_SetPinMux(IO_MUX_FC0_USART_DATA);
} else if (uart == USART1) {
} else if (uart == USART1 && tx_pin == PIN(0, 8) &&
rx_pin == PIN(0, 9)) {
IO_MUX_SetPinMux(IO_MUX_FC1_USART_DATA);
} else if (uart == USART2) {
} else if (uart == USART2 && tx_pin == PIN(0, 14) &&
rx_pin == PIN(0, 13)) {
IO_MUX_SetPinMux(IO_MUX_FC2_USART_DATA);
} else if (uart == USART3) {
} else if (uart == USART3 && tx_pin == PIN(0, 26) &&
rx_pin == PIN(0, 24)) {
IO_MUX_SetPinMux(IO_MUX_FC3_USART_DATA);
} else if (uart == USART14) {
} else if (uart == USART14 && tx_pin == PIN(1, 24) &&
rx_pin == PIN(1, 25)) {
IO_MUX_SetPinMux(IO_MUX_FC14_USART_DATA);
} else {
return;
+1 -1
View File
@@ -96,7 +96,7 @@ int main(void) {
hal_gpio_write(LED2, true);
hal_gpio_output(LED3);
hal_gpio_write(LED3, true);
hal_uart_init(UART_DEBUG, 115200);
hal_uart_init(UART_DEBUG, 115200, PIN(0, 26), PIN(0, 24));
mg_log_set_fn(log_fn, UART_DEBUG);
hal_ethernet_init();