# rubo A C framework for commanding a small robot — Raspberry Pi Pico WH, two motors driving four wheels, a black-and-white camera — over USB, WiFi or BLE, using a small command language. The hardware isn't attached yet, so the whole stack runs on the host against a simulated HAL. The language, the framer and the interpreter are exercised by tests with no Pico in the loop. ## Specifications | Document | Covers | |----------|--------| | [`docs/grammar.md`](docs/grammar.md) | the language: callables, procedures, named arguments, `REPEAT`, the rules that keep it safe | | [`docs/flow.md`](docs/flow.md) | transport, framing, the envelope, admission, execution, abort and failsafe | Read those before changing anything here. Most of what looks like an odd decision in the code is a decision recorded in one of them. ## Build and test ```sh cmake -S . -B build cmake --build build ./build/test_rubo # or: ctest --test-dir build ``` ## Drive it ```sh ./build/rubo_host --trace ``` `--trace` prints motor state changes. Try: ``` PING STAT SQUARE side=400 DEF ZIGZAG $deg=20 $reps=3 [ REPEAT $reps [ TURN dir=LEFT deg=$deg; TURN dir=RIGHT deg=$deg ] ] ZIGZAG deg=15 ABORT ``` `--script` makes it read piped input as a well-behaved client would: one outstanding statement at a time, waiting for `DONE` or `ERR` before sending the next (docs/flow.md §9). ```sh ./build/rubo_host --script < commands.txt ``` ## Layout ``` include/rubo/rubo.h public interface: limits, error codes, framer, engine include/rubo/hal_sim.h simulated hardware, host only src/lexer.c tokeniser (grammar.md §3) src/parser.c recursive descent, validation (grammar.md §4, §5) src/registry.c callable table and arena (grammar.md §2) src/sym.c identifier interning src/exec.c resumable interpreter (flow.md §8) src/framer.c bracket-balance framing (flow.md §4) src/session.c envelope and admission (flow.md §5, §7, §9) src/natives.c built-in callables (grammar.md §8) src/stdlib.c firmware procedures, in the language itself src/hal_sim.c simulated motors and camera host/main.c the main loop from flow.md §8.3 host/transport_stdio.c non-blocking stdio transport tests/test_rubo.c host tests ``` ## Porting to the Pico Two things, and nothing else: 1. a `rubo_transport_t` over USB CDC, lwIP or BLE NUS — `read()` must never block; 2. a `rubo_hal_t` driving the real motors. `rubo_core` is target-independent and does not change. That is the point of the layering in docs/flow.md §2, and it is why the language is testable today. ## Status Working: the language end to end (definition, named arguments, defaults, `REPEAT`, procedures calling procedures), framing, the envelope, admission, abort, the deadman, and a simulated HAL. Not built yet: the Pico transport and HAL, the camera `DAT`/`DEND` bulk path (docs/flow.md §11), and `IF`/`ELIF`, which is reserved in the grammar and lands with the sensor layer (docs/grammar.md §7).