Python: the rb9704 command line, a fake modem, and the tests
rb9704 drives a 9704 on USB and the Cloudloop account behind it, moved from WII6's host/rockblock with its project names taken out. rb9704-fake speaks JSPR on a pty or a serial port: everything the library asks, MO in segments with the CRC checked, MT, signal, provisioning, refusals and failures on command. The tests run against the fake at every level: the fake itself, the command line, Ground Control's Python binding, the shell sample on native_sim, and the sample on a board (test_zephyr.py, one suite through two consoles). test_real.py makes free checks against a real modem.
This commit is contained in:
1 parent
dbed9bbc88
commit
917522d1f4
12 files changed
+1861
No files matched your search
@@ -0,0 +1,94 @@
|
||||
# rockblock9704-tools: the RockBLOCK 9704 from a computer
|
||||
|
||||
Two commands and a test suite, in one Python package:
|
||||
|
||||
- `rb9704`, a command line for a RockBLOCK 9704 plugged into USB and for
|
||||
the Cloudloop account behind it, for proving the modem, the account and
|
||||
the delivery path with no firmware in the loop;
|
||||
- `rb9704-fake`, a fake modem that speaks JSPR on a pty or a serial port,
|
||||
for testing anything that drives a 9704 without one;
|
||||
- `tests/`, which run Ground Control's own Python binding, Zephyr on
|
||||
`native_sim`, and Zephyr on a board against the fake
|
||||
(`../docs/TESTING.md`).
|
||||
|
||||
`../docs/CLOUDLOOP.md` is the account, the provisioning and the order to
|
||||
test things in; this is the tools.
|
||||
|
||||
## Install
|
||||
|
||||
```sh
|
||||
python/install.sh # python/.venv, this package, rockblock9704, pytest
|
||||
python/install.sh --udev # ... and keep ModemManager off the port (sudo)
|
||||
```
|
||||
|
||||
Or by hand: `pip install -e 'python[modem,test]'`. Python 3.10 to 3.14,
|
||||
which is where `rockblock9704` has wheels (Linux x86_64 and aarch64,
|
||||
macOS, Windows). Without the `modem` extra, the fake modem, `jspr`,
|
||||
`provision` and the cloud verbs work and the library verbs do not.
|
||||
|
||||
## Verbs
|
||||
|
||||
| Verb | Costs | What it does |
|
||||
| --- | --- | --- |
|
||||
| `ports` | | USB serial ports, the FT234XD (`0403:6015`) first |
|
||||
| `info [--json]` | | hardware and firmware, serial, IMEI, ICCID, SIM, temperature, signal |
|
||||
| `signal [--watch S] [--every S]` | | signal bars 0 to 5, once or for S seconds with a summary |
|
||||
| `provision [--timeout S]` | | the setup `begin` does, then signal and provisioning every 30 s until topics appear |
|
||||
| `jspr [--setup] [LINE ...]` | | raw JSPR: the lines given, or a prompt; every answer and unsolicited line printed |
|
||||
| `send --text/--hex/--file [--topic N]` | airtime | one MO, blocking, through the library |
|
||||
| `listen [--out DIR] [--for S]` | | MT as it arrives, each written to `DIR/MT-<utc>.bin` |
|
||||
| `session --text/--hex/--file [--quiet S]` | airtime | one MO, then MT until S seconds pass with nothing new |
|
||||
| `cloud ping` | | the token works |
|
||||
| `cloud things` | | every Thing on the account, with its Certus subscriber id |
|
||||
| `cloud mo [--thing ID] [--hours H]` | | message records for a Thing over the last H hours |
|
||||
| `cloud msg ID` | | one message record and its Lingo form |
|
||||
| `cloud mt --text/--hex/--file [--thing ID]` | airtime | queue an MT on `IMT_TOPIC_RAW` |
|
||||
| `cloud status ID` | | an MT's delivery status |
|
||||
|
||||
The verbs that cost airtime print the size and ask; `--yes` skips the
|
||||
question and is the only way to send from a script.
|
||||
|
||||
Options that go with any verb, before or after it:
|
||||
|
||||
- `-p PORT`: the serial port. Otherwise `$RB9704_PORT`, otherwise the one
|
||||
9704 bridge attached (`0403:6015`); two attached is an error.
|
||||
- `--log FILE`: append one JSON line per event (`{"t": ..., "ev": ...}`,
|
||||
UTC), for the bench log.
|
||||
|
||||
The cloud verbs read the token from `$CLOUDLOOP_TOKEN` or
|
||||
`~/.config/rb9704/cloudloop.token`, and the Thing from `--thing` or
|
||||
`$CLOUDLOOP_THING`.
|
||||
|
||||
## The fake modem
|
||||
|
||||
```sh
|
||||
rb9704-fake # a pty; prints READY /dev/pts/N
|
||||
rb9704-fake --port /dev/ttyUSB0 # a real port, e.g. an adapter wired to a board
|
||||
rb9704-fake --topics 244,313 # the provisioning it reports
|
||||
```
|
||||
|
||||
It answers everything Ground Control's library asks (`../docs/JSPR.md`),
|
||||
takes MO messages segment by segment and checks their CRC, and on a
|
||||
command on stdin sends an MT, a signal change, a refusal or a failed MO.
|
||||
The commands and the events it prints are in `rb9704tools/fakemodem.py`'s
|
||||
docstring. Run it in a process of its own beside the library's Python
|
||||
binding, which holds the GIL through every blocking call.
|
||||
|
||||
## How it is built
|
||||
|
||||
- The modem verbs (`info`, `signal`, `send`, `listen`, `session`) use
|
||||
Ground Control's `rockblock9704`, a binding of the C library this
|
||||
repository builds into Zephyr. `listen` and `session` use its
|
||||
callback API, polled every 10 ms as the library asks, with the
|
||||
callbacks only recording and the loop doing the work.
|
||||
- `provision` and `jspr` speak JSPR over pyserial, because the library
|
||||
asks for the provisioning only inside a send. `Jspr.setup` repeats
|
||||
`rbBegin`'s sequence: an active API version, the internal SIM, the
|
||||
state set active. JSPR lines are `<code> <target> <json>` ending in CR;
|
||||
299 is unsolicited.
|
||||
- The cloud verbs are plain `urllib` POSTs to `api.cloudloop.com`, every
|
||||
argument in the query string as the API's Swagger
|
||||
(<https://api.cloudloop.com/swagger/index.html>) describes it.
|
||||
|
||||
Only one program can hold the port: `jspr` and the library verbs cannot
|
||||
run at once.
|
||||
Reference in new issue
Block a user