README says what the module adds to Ground Control's library and what it works around, and how to use it. JSPR.md is the protocol as the library speaks it, from its source, with its surprises. CLOUDLOOP.md is account to first message, from WII6's IRIDIUM.md. TESTING.md is the levels, the rig, and what has passed: Python, native_sim, an ESP32-S3 and an STM32U5 against the fake, and free checks against a real 9704.
4.4 KiB
Testing
One fake modem, three places to run the code against it, and a few free checks against a real modem.
| Level | What runs | Against | Needs |
|---|---|---|---|
| Python | Ground Control's Python binding, the rb9704 command line, the fake itself |
the fake on a pty | Python 3.10 to 3.14 |
| Host Zephyr | this module in the shell sample on native_sim |
the fake on a pty, or a real modem on USB | a Zephyr workspace |
| Board | the same sample on an ESP32-S3, STM32U5 or any board | the fake on a USB-serial port wired to the board's modem pins | a board and a wire |
The native_sim and board runs are the same tests (python/tests/test_zephyr.py)
through a different console, so a behaviour seen on one is checked on the
other.
The fake modem
python/rb9704tools/fakemodem.py, rb9704-fake on the command line. It
implements JSPR as JSPR.md describes it: the three exchanges rbBegin
needs, the identity and signal getters, provisioning, MO in segments of
any size with the CRC checked, MT in segments the library can take, and
on command a refusal, a failed MO, a signal change or a change of
provisioning. It writes every whole MO it receives as an event line, so a
test checks the bytes that reached "the network".
What it cannot tell you is what a real modem does that the library's
source does not show: unsolicited lines at odd moments, timing, MT
segment sizes. That is what test_real.py and the hardware are for.
Running them
python/install.sh
# Python level: the fake, the command line, Ground Control's binding
python/.venv/bin/pytest -q python/tests
# Host Zephyr: build the sample for native_sim, then
west build -b native_sim/native/64 samples/shell -d build/native
python/.venv/bin/pytest -q python/tests --zephyr-exe build/native/zephyr/zephyr.exe
# Board: flash the sample with its modem UART wired to a USB-serial
# adapter (3.3 V), then the board's shell port and the adapter's port
python/.venv/bin/pytest -q python/tests/test_zephyr.py \
--board-console /dev/ttyACM0 --board-modem /dev/ttyUSB0
# A real modem, free checks only (identity and signal, never a message)
python/.venv/bin/pytest -q python/tests/test_real.py --real-modem /dev/ttyUSB0 \
[--zephyr-exe build/native/zephyr/zephyr.exe]
Every option has an environment variable as well: RB9704_ZEPHYR_EXE,
RB9704_BOARD_CONSOLE, RB9704_BOARD_MODEM, RB9704_REAL_MODEM.
On native_sim the modem UART is a zephyr,native-tty-uart node
(samples/shell/boards/native_sim.overlay); the tests give it the fake's
pty with -modem_uart_port=. The same option takes a real modem's
/dev/ttyUSB0, which is how host Zephyr talks to real hardware.
The rig: two boards, one wire
No USB-serial adapter to hand? Two boards with a UART wired between them
will do: one runs the sample, the other runs tests/rig/bridge, which
copies bytes between that UART and its own USB serial port, where the
fake answers.
host --USB-- bridge board --UART-- board under test --USB-- host
(tests/rig/bridge) (samples/shell)
fake modem on this port pytest drives this shell
The overlays in tests/rig/ are for a NUCLEO-U575ZI-Q (USART2 on PD5 and
PD6) and a Seeed XIAO ESP32S3 (D8 and D9) wired to each other, either way
round:
| Under test | Bridge | --board-console |
--board-modem |
|---|---|---|---|
XIAO ESP32S3, -DDTC_OVERLAY_FILE=tests/rig/xiao_esp32s3_rig.overlay |
Nucleo, tests/rig/bridge |
the XIAO's USB | the Nucleo's ST-LINK port |
Nucleo, -DDTC_OVERLAY_FILE=tests/rig/nucleo_u575zi_q_rig.overlay |
XIAO, tests/rig/bridge |
the Nucleo's ST-LINK port | the XIAO's USB |
The ESP32-S3 sits in its ROM loader after west flash until it is reset;
toggle RTS on its USB port, or press reset, before the tests.
What has passed
On 2026-10-09, Zephyr v4.4.0, Ground Control's library v1.0.1, the binding 1.0.2:
| Level | Tests | Result |
|---|---|---|
| Python: fake, command line, binding | 16 | all pass |
native_sim/native/64 against the fake |
12 | all pass |
| XIAO ESP32S3 through the rig | 12 | all pass |
| NUCLEO-U575ZI-Q through the rig | 12 | all pass |
| a real 9704 (firmware v1.5.14), binding and native_sim, free checks | 2 | all pass |
Not yet: a message through a real modem and satellite, which costs
airtime and needs the sky (CLOUDLOOP.md, "The order to test in"), and
the power pins on a board that has them wired.