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.
181 lines
7.6 KiB
Markdown
181 lines
7.6 KiB
Markdown
# rockblock9704-zephyr
|
|
|
|
The RockBLOCK 9704, Ground Control's Iridium Certus modem, on Zephyr: a
|
|
module that builds Ground Control's own C library for any board with a
|
|
UART, gives it the one thread it needs, and offers a small API, a shell
|
|
command and a devicetree binding. With it come host tools in Python and a
|
|
fake modem, so the same tests run against Ground Control's Python binding,
|
|
against Zephyr on `native_sim`, and against Zephyr on a board.
|
|
|
|
The 9704 sends and receives IMT messages of up to 100 kB through the
|
|
Iridium network, and speaks JSPR, JSON lines over a 230400 8N1 UART. There
|
|
is no AT mode; it is not a 9602 or 9603.
|
|
|
|
## Ground Control's library
|
|
|
|
This module does not reimplement or fork the protocol. Ground Control's
|
|
repository, [rock7/RockBLOCK-9704](https://github.com/rock7/RockBLOCK-9704)
|
|
(MIT), is pinned untouched in `west.yml` at `modules/lib/rockblock9704`,
|
|
and `CMakeLists.txt` compiles its portable core. What each part does:
|
|
|
|
| | Ground Control's library | This module |
|
|
| --- | --- | --- |
|
|
| JSPR, segmenting, CRC, provisioning check | yes | uses it unchanged |
|
|
| Platforms | Linux, Windows, Arduino serial presets | a Zephyr serial port, on the UART in devicetree |
|
|
| Threads | none: no locking, shared static buffers | one worker thread owns every call into it |
|
|
| API | blocking calls, or async calls with callbacks and `rbPoll()` | requests with a timeout each, callbacks from the worker, a status snapshot |
|
|
| Polling | the caller calls `rbPoll()`, one line per call | the worker wakes on a byte from the modem, so an idle modem costs nothing |
|
|
| Power pins | `rbBeginGpio` with libgpiod, Linux only | `power-gpios`, `iridium-enable-gpios`, `booted-gpios` in devicetree |
|
|
| Python | `rockblock9704` on PyPI, a binding of the C | the `rb9704` command line uses it; the fake modem tests it |
|
|
|
|
The library's source shows several things a caller has to work around;
|
|
`docs/JSPR.md`, section 8, lists them all. The ones this module handles:
|
|
|
|
- **Provisioning is asked once and kept for good**, a miss included. A
|
|
modem provisioned after the first send would refuse every topic until
|
|
a reboot. Before a send whose topic is not in what the library kept,
|
|
and after the modem refuses one, the module clears the library's cache
|
|
so it asks again: one free JSPR round trip.
|
|
- **The blocking calls stop working once callbacks are registered.** The
|
|
module uses the async path only.
|
|
- **A query during a send swallows the modem's segment requests**, since
|
|
the reader discards lines it is not waiting for. Info and signal
|
|
requests are refused with `-EBUSY` while a message is moving.
|
|
- **No locking.** Every library call is on the worker thread.
|
|
|
|
Others are for upstream to fix, and are listed in `docs/JSPR.md` for that:
|
|
`setState` waits for a misspelt target, a line with no status code reads
|
|
past its buffer, MT segment data is cut at 1446 characters, and the
|
|
Python binding never releases the GIL.
|
|
|
|
## Using it
|
|
|
|
In the application's `west.yml`; the import brings Ground Control's
|
|
library, pinned, at the path the module looks in, and nothing else:
|
|
|
|
```yaml
|
|
- name: rockblock9704-zephyr
|
|
url: https://github.com/SH3D/rockblock9704-zephyr
|
|
revision: v0.1.0
|
|
path: modules/lib/rockblock9704-zephyr
|
|
import:
|
|
name-allowlist:
|
|
- rockblock9704
|
|
```
|
|
|
|
In the board overlay, a node on the modem's UART. The pins are optional;
|
|
without them the modem is taken to be powered:
|
|
|
|
```dts
|
|
&usart3 {
|
|
current-speed = <230400>;
|
|
status = "okay";
|
|
|
|
rockblock {
|
|
compatible = "rock7,rockblock-9704";
|
|
power-gpios = <&gpiob 0 GPIO_ACTIVE_HIGH>; /* P_EN */
|
|
iridium-enable-gpios = <&gpiob 1 GPIO_ACTIVE_HIGH>; /* I_EN */
|
|
booted-gpios = <&gpiob 2 GPIO_ACTIVE_HIGH>; /* I_BTD */
|
|
};
|
|
};
|
|
```
|
|
|
|
`CONFIG_RB9704` follows the node. Then `<rb9704/rb9704.h>`:
|
|
|
|
```c
|
|
static void mo_done(uint16_t topic, uint16_t id, int result, void *user)
|
|
{
|
|
/* result 0: the network has it */
|
|
}
|
|
|
|
static void mt(const struct rb9704_mt *m, void *user)
|
|
{
|
|
/* m->data, m->len: copy what you need, it is acknowledged after */
|
|
}
|
|
|
|
static const struct rb9704_callbacks cb = { .mo_done = mo_done, .mt = mt };
|
|
|
|
rb9704_set_callbacks(&cb);
|
|
rb9704_begin(K_SECONDS(30));
|
|
rb9704_send(RB9704_TOPIC_RAW, buf, len, K_SECONDS(10)); /* costs airtime */
|
|
rb9704_send(RB9704_TOPIC_RAW, buf, len, K_NO_WAIT); /* queues, never blocks */
|
|
```
|
|
|
|
Every request takes a timeout and waits no longer; with `K_NO_WAIT` a send
|
|
queues and returns `-EINPROGRESS`, and its outcome, a refusal included,
|
|
comes through `mo_done`. Callbacks run on the worker thread. One modem per
|
|
system: the library keeps global state.
|
|
|
|
| Kconfig | Default | |
|
|
| --- | --- | --- |
|
|
| `CONFIG_RB9704_MESSAGE_MAX` | 2048 | largest message each way; three static buffers of this size |
|
|
| `CONFIG_RB9704_WORKER_STACK_SIZE` | 8192 | measured: the library parks 1.5 kB on the stack in `rbPoll()` |
|
|
| `CONFIG_RB9704_WORKER_PRIORITY` | 8 | |
|
|
| `CONFIG_RB9704_RX_RING_SIZE` | 8192 | the library's longest JSPR line |
|
|
| `CONFIG_RB9704_POWER` | with `power-gpios` | drive the pins |
|
|
| `CONFIG_RB9704_SHELL` | with `CONFIG_SHELL` | `rb9704` on the shell |
|
|
| `CONFIG_RB9704_UPSTREAM_DIR` | empty | where Ground Control's library is, if not where `west.yml` puts it |
|
|
|
|
## The shell
|
|
|
|
```
|
|
rb9704 begin | end | power on|off
|
|
rb9704 status what is known, without asking the modem
|
|
rb9704 info IMEI, serial, versions, ICCID, SIM, temperature
|
|
rb9704 signal bars now
|
|
rb9704 send <topic> <text> | sendhex <topic> <hex> costs airtime
|
|
rb9704 spy on|off every JSPR line, both ways
|
|
```
|
|
|
|
`samples/shell` is the shell plus callbacks that print what arrives.
|
|
|
|
## Host tools
|
|
|
|
`python/` is `rb9704` (the modem and Cloudloop from a computer), `rb9704-fake`
|
|
(a fake modem on a pty or a serial port), and the tests. `python/README.md`
|
|
has every verb; `docs/CLOUDLOOP.md` is the account, the provisioning and
|
|
the order to test a new modem in.
|
|
|
|
## Testing
|
|
|
|
```sh
|
|
python/install.sh
|
|
python/.venv/bin/pytest -q python/tests # Python
|
|
python/.venv/bin/pytest -q python/tests --zephyr-exe <native_sim zephyr.exe>
|
|
python/.venv/bin/pytest -q python/tests/test_zephyr.py \
|
|
--board-console /dev/ttyACM0 --board-modem /dev/ttyUSB0 # a board
|
|
python/.venv/bin/pytest -q python/tests/test_real.py --real-modem /dev/ttyUSB0
|
|
```
|
|
|
|
`docs/TESTING.md` has the levels, the two-board rig that needs no
|
|
USB-serial adapter, and what has passed: the Python level, `native_sim`,
|
|
an ESP32-S3 and an STM32U5 against the fake, and free checks against a
|
|
real 9704 from Python and from `native_sim`.
|
|
|
|
## Boards
|
|
|
|
Any board with a free UART. The sample has overlays for `native_sim`,
|
|
NUCLEO-U575ZI-Q, Seeed XIAO ESP32S3 and ESP32-S3-DevKitC, and has run on
|
|
the first three.
|
|
|
|
## Layout
|
|
|
|
```
|
|
CMakeLists.txt, Kconfig, zephyr/module.yml the module
|
|
dts/bindings/rock7,rockblock-9704.yaml the devicetree binding
|
|
include/rb9704/rb9704.h the API
|
|
src/ serial port, shims, worker, power, shell
|
|
samples/shell/ the shell sample, with board overlays
|
|
tests/rig/ the two-board test rig
|
|
python/ rb9704, rb9704-fake, the tests
|
|
docs/JSPR.md the protocol as the library speaks it
|
|
docs/CLOUDLOOP.md account to first message
|
|
docs/TESTING.md the tests and how to run them
|
|
west.yml the pins
|
|
```
|
|
|
|
## Licence
|
|
|
|
MIT, as Ground Control's library is. The serial port and worker began in
|
|
the OGLAS satellite bench and were carried through WII6 before moving here.
|