|
|
||
|---|---|---|
| .claude | ||
| bin/x64/Release | ||
| build-notui | ||
| build-tui | ||
| cmake | ||
| config | ||
| docs | ||
| include/fgc | ||
| scripts | ||
| src | ||
| tests | ||
| .deploy.env | ||
| .deploy.env.example | ||
| .gitignore | ||
| CMakeLists.txt | ||
| README.md | ||
| deploy.sh | ||
| ini.c | ||
| ini.h | ||
| main.cpp | ||
README.md
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 for the full design.
Build
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.
Run
# 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
# Same, but with the full-screen terminal dashboard:
scripts/run.sh --tui --mock-serial --mock-camera --no-mqtt
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 <path>, then $FGC_CONFIG, ./config.ini, the executable's directory, and
$XDG_CONFIG_HOME/fire_gimbal_control/config.ini. Copy the template to get started:
cp config/config.example.ini config.ini # then edit
Command-line options
| Flag | Effect |
|---|---|
-c, --config <path> |
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. |
--tui / --no-tui |
Show the full-screen terminal dashboard / force the headless console (overrides [UI] enable_tui). |
--log-level <lvl> |
trace, debug, info, warn, error, or off. |
--trace <cats> |
Verbatim wire tracing; comma list of serial,mqtt,camera,control,all,none. |
-h, --help |
Print options and exit. |
CLI flags override the matching [Features] / [Logging] 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 <n> |
Capture rate, in images per second. |
set camera fps <n> |
Camera sensor frame rate. |
set camera jxlq <d> |
JPEG XL quality as butteraugli distance (lower = higher quality / larger files). |
set camera jxle <n> |
JPEG XL encoder effort (higher = slower, smaller). |
set camera display <0|1> |
Toggle the local preview window. |
set motorctl <raw> |
Send a raw command string straight to the motor controller. |
trace <cat> [on|off] |
Enable/disable a wire-trace category live (e.g. trace serial, trace mqtt off). |
trace all / trace off |
Enable every category / silence all of them. |
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 for the full config.ini reference.
Verbosity & wire tracing
Two independent knobs control output:
- Log level (
--log-level, thedebugconsole toggle,[Logging] level) gates ordinary messages:info(default) shows discrete events only;debugadds per-image saves;traceis the most verbose. Atinfoan active capture is nearly silent. - Wire-trace categories (
--trace, thetraceconsole command,[Logging] trace) log every message exchanged with a subsystem, verbatim, and are independent of the level —--trace serialshows all firmware serial traffic even atinfo. Categories are off by default (the camera one is high-rate). Only--log-level offsilences them.
Lines are tagged by category with a TX/RX direction, so a single subsystem is easy to follow
or grep:
14:02:01.234 [SERIAL] TX MOVE -90,0
14:02:01.250 [SERIAL] RX ST Y:A,982,969,80084000,0,8,8,Se P:A,...
14:02:02.000 [MQTT] PUB GGS/FWT/Tower/StatusCode 0
14:02:02.500 [CAMERA] TX trigger cam0
14:02:02.910 [CAMERA] RX frame cam0 1936x1216 7064576B
14:02:02.000 [CONTROL] sweep -> grid yaw=-90 pitch=0
Example — watch only firmware serial traffic on the device:
./deploy.sh --run with RUN_ARGS="--start --trace serial".
Test
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).
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 | Components, interfaces, threading, data flow, capture state machine |
| docs/build-and-setup.md | CMake, dependencies, options, Vimba X / Paho, host setup |
| docs/deployment.md | Step-by-step deploy to the gimbal LattePanda (systemd) |
| docs/configuration.md | config.ini keys, CLI flags, console commands |
| docs/mqtt-api.md | MQTT topic catalog and payloads |
| docs/modules-reference.md | Per-file reference and data structures |
| 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, scan.csv (real config.ini is gitignored)
include/fgc/ Public headers: interfaces, Config, Logger, scheduler, impls
mock/ Mock/null implementations
src/
core/ Config, Logger, Paths, parsers, Geometry, ScanGrid, 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.