# Fire Gimbal Control (Staeffelsberg) Real-time control software for an automated **fire-watch gimbal**. A C++17 program runs on a tower-mounted PC, rotates a pan gimbal carrying up to four industrial cameras, captures a 360° panorama for wildfire detection, compresses each frame to JPEG XL, and reports to a ground station over MQTT. This is the deployment for the **Staeffelsberg** fire-watch tower (FWT). The same codebase serves all towers; the tower identity comes from `config.ini`. The code is organized around an **SDK-independent core** (`fgc_core`: config, logging, capture scheduler, parsers) and **swappable I/O implementations** behind three interfaces — motor controller, control channel, and camera source — each with a real and a mock/null version. This makes the system deployable on any machine and runnable **without hardware or an MQTT broker** for development. State is held in memory (mutex-guarded), configuration is read once from an INI file, images are written to the filesystem as `.jxl`, and telemetry flows over MQTT. There is no database. ## Architecture at a glance ``` ┌──────────────────────── fgc_core (no SDKs) ─────────────────────────┐ │ Config · Logger · CaptureScheduler · TelemetryParser · CommandParser │ └───────▲─────────────────▲──────────────────▲────────────────────────┘ │ IMotorController │ IControlChannel │ ICameraSource ┌───────────┴──────┐ ┌────────┴─────────┐ ┌──────┴────────────────┐ real │ SerialMotorCtrl │ │ MqttControlChannel│ │ VimbaCameraSource │ (WITH_VIMBA/WITH_MQTT) mock │ MockMotorCtrl │ │ NullControlChannel│ │ MockCameraSource │ └──────────────────┘ └──────────────────┘ └──────────┬────────────┘ frames ▼ ImagePipeline → .jxl + CamEvent ``` See [docs/architecture.md](docs/architecture.md) for the full design. ## Build ```bash cmake -B build # configure (needs the Vimba X SDK + Paho for a full build) cmake --build build # -> build/fire_gimbal_control # Development build with NO proprietary SDKs (mocks only): cmake -B build -DWITH_VIMBA=OFF -DWITH_MQTT=OFF cmake --build build ``` Dependencies and the Vimba X SDK setup are in [docs/build-and-setup.md](docs/build-and-setup.md). ## Run ```bash # Real deployment (needs cameras, motor MCU on the configured serial port, MQTT broker): scripts/run.sh --start scripts/run.sh --init --start # find endstops first # Development, no hardware or broker: scripts/run.sh --mock-serial --mock-camera --no-mqtt --start --log-level debug ``` `run.sh` resolves the binary relative to the repo, so it works from any checkout; extra args are forwarded to `fire_gimbal_control`. On launch the program starts the motor controller, opens the camera(s), starts the image pipeline, connects the control channel (continuing in **degraded mode** if the broker is unreachable), then enters a control loop. Without `--start` it idles until you type `start`. Stop with `exit` or Ctrl-D. Config is resolved from `--config `, then `$FGC_CONFIG`, `./config.ini`, the executable's directory, and `$XDG_CONFIG_HOME/fire_gimbal_control/config.ini`. Copy the template to get started: ```bash cp config/config.example.ini config.ini # then edit ``` ### Command-line options | Flag | Effect | |------|--------| | `-c, --config ` | Use a specific `config.ini` (otherwise the search path above). | | `-i, --init` | Run the endstop-finding init sequence before entering the loop. | | `-s, --start` | Begin capturing immediately on launch (else wait for the `start` command). | | `-d, --demo` | Demo mode: copy a placeholder `.jxl` instead of encoding live frames. | | `--no-mqtt` | Disable MQTT; use a null control channel (overrides `enable_mqtt`). | | `--mock-camera` | Use a simulated camera — no Vimba hardware needed. | | `--mock-serial` | Use a simulated motor controller — no serial hardware needed. | | `--log-level ` | `trace`, `debug`, `info`, `warn`, `error`, or `off`. | | `-h, --help` | Print options and exit. | CLI flags override the matching `[Features]` keys in `config.ini`. ### Interactive commands While running, the program reads commands from stdin (one per line): | Command | Action | |---------|--------| | `start` | Start the capture cycle. | | `stop` | Stop capturing (the gimbal stays powered and homed). | | `debug` | Toggle debug-level logging on/off. | | `set fps ` | Capture rate, in images per second. | | `set camera fps ` | Camera sensor frame rate. | | `set camera jxlq ` | JPEG XL quality as butteraugli **distance** (lower = higher quality / larger files). | | `set camera jxle ` | JPEG XL encoder effort (higher = slower, smaller). | | `set camera display <0\|1>` | Toggle the local preview window. | | `set motorctl ` | Send a raw command string straight to the motor controller. | | `exit` | Stop everything and quit (Ctrl-D also works). | A typical first bring-up: `scripts/run.sh --init` to home the gimbal, then type `start` once you've confirmed telemetry looks sane; adjust `set fps` / `set camera jxlq` live as needed. See [docs/configuration.md](docs/configuration.md) for the full `config.ini` reference. ## Test ```bash cmake -B build -DWITH_VIMBA=OFF -DWITH_MQTT=OFF -DBUILD_TESTING=ON cmake --build build ctest --test-dir build --output-on-failure ``` The unit tests cover the core (config, paths, telemetry/command parsers, capture scheduler) and need no SDKs. ## Deploy to the LattePanda The LattePanda is the machine wired to the gimbal and camera, so it builds and runs there. `deploy.sh` rsyncs the codebase over ssh and runs the cmake build remotely (mirrors `../firmware/deploy.sh`). ```bash cp .deploy.env.example .deploy.env # then edit REMOTE_HOST / REMOTE_DIR ./deploy.sh --check-deps # verify the device has OpenCV/Boost/libjxl ./deploy.sh # rsync + remote configure + build ./deploy.sh --run # ... then run it over ssh (RUN_ARGS, ctrl-c to stop) ./deploy.sh --clean --run # wipe remote build dir first, then build + run ``` `config.ini` and `images/` are device-local and never overwritten by a deploy; the first deploy seeds `config.ini` from `config/config.example.ini` if the device has none. One-time device prerequisites (Ubuntu): `sudo apt-get install -y build-essential cmake git libopencv-dev libboost-program-options-dev libjxl-dev`, plus the Vimba X SDK under `/opt/VimbaX` for `-DWITH_VIMBA=ON`. ## Documentation | Document | Contents | |----------|----------| | [docs/architecture.md](docs/architecture.md) | Components, interfaces, threading, data flow, capture state machine | | [docs/build-and-setup.md](docs/build-and-setup.md) | CMake, dependencies, options, Vimba X / Paho, host setup | | [docs/deployment.md](docs/deployment.md) | Step-by-step deploy to the gimbal LattePanda (systemd) | | [docs/configuration.md](docs/configuration.md) | `config.ini` keys, CLI flags, console commands | | [docs/mqtt-api.md](docs/mqtt-api.md) | MQTT topic catalog and payloads | | [docs/modules-reference.md](docs/modules-reference.md) | Per-file reference and data structures | | [docs/known-issues.md](docs/known-issues.md) | Status of past issues + remaining caveats | ## Repository layout ``` CMakeLists.txt Build (options: WITH_VIMBA, WITH_MQTT, BUILD_TESTING) cmake/ FindVmb.cmake (Vimba X), Paho.cmake (FetchContent) config/ config.example.ini (real config.ini is gitignored) include/fgc/ Public headers: interfaces, Config, Logger, scheduler, impls mock/ Mock/null implementations src/ core/ Config, Logger, Paths, parsers, CaptureScheduler, Application serial/ SerialMotorController mqtt/ MqttControlChannel camera/ VimbaCameraSource, ImagePipeline, JpegXlEncoder main.cpp Thin entry point (CLI -> Application) tests/ doctest unit tests scripts/ run.sh + systemd unit template ini.c / ini.h Bundled inih INI parser ``` ## Ownership Internal tooling for the GGS fire-watch tower network. No license file is present in the repository.