The shell sample, and a two-board rig for testing on hardware

samples/shell is `rb9704` on the shell with callbacks that print what
arrives, a CRC-32 and the start of each message from the network. Board
overlays for native_sim (the modem a host serial port, a pty or a real
9704 on USB), NUCLEO-U575ZI-Q, XIAO ESP32S3 and ESP32-S3-DevKitC.

tests/rig/bridge copies bytes between a board's USB serial port and a
UART, so two boards wired together stand in for a USB-serial adapter: one
runs the sample, the other bridges its modem UART to the host, where the
fake modem answers. Overlays for a Nucleo and a XIAO, either way round.
This commit is contained in:
scottp committed 2026-10-09 12:48:16 +11:00
1 parent c0e92a7f02
commit dbed9bbc88
17 files changed
+350

No files matched your search

+5
View File
@@ -0,0 +1,5 @@
# SPDX-License-Identifier: MIT
cmake_minimum_required(VERSION 3.20.0)
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
project(rb9704_shell)
target_sources(app PRIVATE src/main.c)
@@ -0,0 +1,24 @@
/* ESP32-S3-DevKitC: the modem on uart1, GPIO17 (TX) and GPIO18 (RX). */
&pinctrl {
uart1_rb9704: uart1_rb9704 {
group1 {
pinmux = <UART1_TX_GPIO17>;
output-high;
};
group2 {
pinmux = <UART1_RX_GPIO18>;
bias-pull-up;
};
};
};
&uart1 {
pinctrl-0 = <&uart1_rb9704>;
pinctrl-names = "default";
current-speed = <230400>;
status = "okay";
rockblock {
compatible = "rock7,rockblock-9704";
};
};
+2
View File
@@ -0,0 +1,2 @@
# One log backend on native_sim, the shell's, as on a board.
CONFIG_LOG_BACKEND_NATIVE_POSIX=n
+14
View File
@@ -0,0 +1,14 @@
/* native_sim: the modem is a host serial device, the fake modem's pty
* (python/rb9704tools/fakemodem.py) or a real RockBLOCK on USB. Name it at
* run time: ./zephyr.exe --modem_uart_port=/dev/pts/N (the node name below). */
/ {
modem_uart: modem_uart {
compatible = "zephyr,native-tty-uart";
current-speed = <230400>;
status = "okay";
rockblock {
compatible = "rock7,rockblock-9704";
};
};
};
@@ -0,0 +1,2 @@
# One log backend on native_sim, the shell's, as on a board.
CONFIG_LOG_BACKEND_NATIVE_POSIX=n
@@ -0,0 +1,14 @@
/* native_sim: the modem is a host serial device, the fake modem's pty
* (python/rb9704tools/fakemodem.py) or a real RockBLOCK on USB. Name it at
* run time: ./zephyr.exe --modem_uart_port=/dev/pts/N (the node name below). */
/ {
modem_uart: modem_uart {
compatible = "zephyr,native-tty-uart";
current-speed = <230400>;
status = "okay";
rockblock {
compatible = "rock7,rockblock-9704";
};
};
};
@@ -0,0 +1,16 @@
/* NUCLEO-U575ZI-Q: the modem on USART3, PD8 (TX) and PD9 (RX).
*
* On this board those pins share solder bridges with the ST-LINK's virtual
* COM port; check UM2861 before wiring. To test without a modem, the fake
* modem (python/rb9704tools/fakemodem.py) on a 3.3 V USB-serial adapter
* wired to the same pins stands in for it. */
&usart3 {
pinctrl-0 = <&usart3_tx_pd8 &usart3_rx_pd9>;
pinctrl-names = "default";
current-speed = <230400>;
status = "okay";
rockblock {
compatible = "rock7,rockblock-9704";
};
};
@@ -0,0 +1,29 @@
/* Seeed XIAO ESP32S3: the modem on uart1, D6 (GPIO43, TX) and D7 (GPIO44,
* RX). The console stays on the native USB. */
&pinctrl {
uart1_rb9704: uart1_rb9704 {
group1 {
pinmux = <UART1_TX_GPIO43>;
output-high;
};
group2 {
pinmux = <UART1_RX_GPIO44>;
bias-pull-up;
};
};
};
&uart0 {
status = "disabled";
};
&uart1 {
pinctrl-0 = <&uart1_rb9704>;
pinctrl-names = "default";
current-speed = <230400>;
status = "okay";
rockblock {
compatible = "rock7,rockblock-9704";
};
};
+12
View File
@@ -0,0 +1,12 @@
CONFIG_SERIAL=y
CONFIG_SHELL=y
CONFIG_LOG=y
# One log backend, the shell's: with the UART one as well every line
# printed twice.
CONFIG_LOG_BACKEND_UART=n
CONFIG_RB9704=y
CONFIG_RB9704_SHELL=y
# Long `rb9704 send` lines, typed or pasted.
CONFIG_SHELL_CMD_BUFF_SIZE=512
CONFIG_SHELL_BACKEND_SERIAL_RX_RING_BUFFER_SIZE=256
CONFIG_CRC=y
+65
View File
@@ -0,0 +1,65 @@
/* SPDX-License-Identifier: MIT
*
* The rb9704 shell sample: `rb9704` on the console, and every message from
* the network, every finished send and every signal change printed as it
* happens. The same image is what the tests in tests/ drive, on native_sim
* against a fake modem and on a board against a fake or a real one.
*/
#include <zephyr/kernel.h>
#include <zephyr/logging/log.h>
#include <ctype.h>
#include <zephyr/sys/crc.h>
#include <rb9704/rb9704.h>
LOG_MODULE_REGISTER(sample, LOG_LEVEL_INF);
static void on_mo_done(uint16_t topic, uint16_t id, int result, void *user)
{
ARG_UNUSED(user);
LOG_INF("mo done topic %u id %u result %d", topic, id, result);
}
static void on_mt(const struct rb9704_mt *mt, void *user)
{
/* The whole message's CRC and its start: a long one in a single log
* line would not survive every board's log buffer. */
char text[65];
size_t n = MIN(mt->len, sizeof(text) - 1);
ARG_UNUSED(user);
for (size_t i = 0; i < n; i++) {
text[i] = isprint(mt->data[i]) ? mt->data[i] : '.';
}
text[n] = '\0';
LOG_INF("mt topic %u id %u len %u crc %08x: %s", mt->topic, mt->id, (unsigned int)mt->len,
crc32_ieee(mt->data, mt->len), text);
}
static void on_signal(uint8_t bars, bool visible, void *user)
{
ARG_UNUSED(user);
LOG_INF("signal %u bars%s", bars, visible ? "" : ", not visible");
}
static void on_provisioning(const struct rb9704_topic *topics, uint8_t n, void *user)
{
ARG_UNUSED(user);
LOG_INF("provisioned, %u topic(s), the first %u", n, n > 0 ? topics[0].id : 0);
}
static const struct rb9704_callbacks cb = {
.mo_done = on_mo_done,
.mt = on_mt,
.signal = on_signal,
.provisioning = on_provisioning,
};
int main(void)
{
rb9704_set_callbacks(&cb);
LOG_INF("rb9704 shell sample: `rb9704 begin`, then `rb9704 info`");
return 0;
}
+5
View File
@@ -0,0 +1,5 @@
# SPDX-License-Identifier: MIT
cmake_minimum_required(VERSION 3.20.0)
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
project(rb9704_bridge)
target_sources(app PRIVATE src/main.c)
@@ -0,0 +1,18 @@
/* host: USART1, the ST-LINK's virtual COM port. target: USART2 on PD5/PD6,
* the CN9 UART block, wired to the other board's modem UART. Both at the
* modem's 230400. */
/ {
aliases {
bridge-host = &usart1;
bridge-target = &usart2;
};
};
&usart1 {
current-speed = <230400>;
};
&usart2 {
current-speed = <230400>;
fifo-enable;
};
@@ -0,0 +1,33 @@
/* host: the native USB serial port. target: uart1 on D8 (GPIO7, TX) and D9
* (GPIO8, RX), wired to the other board's modem UART. 230400, the modem's
* rate; the USB side has none. */
/ {
aliases {
bridge-host = &usb_serial;
bridge-target = &uart1;
};
};
&spi2 {
status = "disabled"; /* the board puts it on GPIO7 to 9 */
};
&pinctrl {
uart1_bridge: uart1_bridge {
group1 {
pinmux = <UART1_TX_GPIO7>;
output-high;
};
group2 {
pinmux = <UART1_RX_GPIO8>;
bias-pull-up;
};
};
};
&uart1 {
pinctrl-0 = <&uart1_bridge>;
pinctrl-names = "default";
current-speed = <230400>;
status = "okay";
};
+9
View File
@@ -0,0 +1,9 @@
CONFIG_SERIAL=y
CONFIG_UART_INTERRUPT_DRIVEN=y
CONFIG_RING_BUFFER=y
# Both UARTs carry the bridged bytes; nothing else may print on them.
CONFIG_CONSOLE=n
CONFIG_UART_CONSOLE=n
CONFIG_PRINTK=n
CONFIG_LOG=n
CONFIG_BOOT_BANNER=n
+57
View File
@@ -0,0 +1,57 @@
/* SPDX-License-Identifier: MIT
*
* A two-way UART bridge, for the test rig: every byte in on `host` goes out
* on `target`, and the other way round. With `host` a board's USB serial
* port and `target` a UART wired to another board's modem pins, the fake
* modem on the host answers that board as a modem would. The aliases are
* the board overlay's. */
#include <zephyr/kernel.h>
#include <zephyr/device.h>
#include <zephyr/drivers/uart.h>
#include <zephyr/sys/ring_buffer.h>
static const struct device *const host = DEVICE_DT_GET(DT_ALIAS(bridge_host));
static const struct device *const target = DEVICE_DT_GET(DT_ALIAS(bridge_target));
RING_BUF_DECLARE(to_target, 4096);
RING_BUF_DECLARE(to_host, 4096);
static void isr(const struct device *dev, void *user)
{
struct ring_buf *out = user; /* bytes read here go into this ring */
const struct device *other = dev == host ? target : host;
struct ring_buf *back = out == &to_target ? &to_host : &to_target;
uint8_t b[64];
while (uart_irq_update(dev) && uart_irq_is_pending(dev)) {
if (uart_irq_rx_ready(dev)) {
int n = uart_fifo_read(dev, b, sizeof(b));
if (n > 0) {
ring_buf_put(out, b, n);
uart_irq_tx_enable(other);
}
}
if (uart_irq_tx_ready(dev)) {
uint8_t *p;
uint32_t n = ring_buf_get_claim(back, &p, 64);
if (n == 0) {
uart_irq_tx_disable(dev);
} else {
int w = uart_fifo_fill(dev, p, n);
ring_buf_get_finish(back, w > 0 ? w : 0);
}
}
}
}
int main(void)
{
uart_irq_callback_user_data_set(host, isr, &to_target);
uart_irq_callback_user_data_set(target, isr, &to_host);
uart_irq_rx_enable(host);
uart_irq_rx_enable(target);
return 0;
}
+13
View File
@@ -0,0 +1,13 @@
/* The shell sample on a NUCLEO-U575ZI-Q in the test rig: the modem UART on
* USART2, PD5 (TX) and PD6 (RX) on the CN9 UART block, wired to the bridge
* board's target UART (bridge/). The shell stays on the ST-LINK's port.
* west build -b nucleo_u575zi_q samples/shell -- \
* -DDTC_OVERLAY_FILE=$PWD/tests/rig/nucleo_u575zi_q_rig.overlay */
&usart2 {
current-speed = <230400>;
fifo-enable;
rockblock {
compatible = "rock7,rockblock-9704";
};
};
+32
View File
@@ -0,0 +1,32 @@
/* The shell sample on a XIAO ESP32S3 in the test rig: the modem UART on
* D8 (GPIO7, TX) and D9 (GPIO8, RX), wired to the bridge board's target
* UART (bridge/). Use it in place of the sample's own XIAO overlay:
* west build -b xiao_esp32s3/esp32s3/procpu samples/shell -- \
* -DDTC_OVERLAY_FILE=$PWD/tests/rig/xiao_esp32s3_rig.overlay */
&spi2 {
status = "disabled"; /* the board puts it on GPIO7 to 9 */
};
&pinctrl {
uart1_rig: uart1_rig {
group1 {
pinmux = <UART1_TX_GPIO7>;
output-high;
};
group2 {
pinmux = <UART1_RX_GPIO8>;
bias-pull-up;
};
};
};
&uart1 {
pinctrl-0 = <&uart1_rig>;
pinctrl-names = "default";
current-speed = <230400>;
status = "okay";
rockblock {
compatible = "rock7,rockblock-9704";
};
};