fwt_software/README.md

11 KiB

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, the debug console toggle, [Logging] level) gates ordinary messages: info (default) shows discrete events only; debug adds per-image saves; trace is the most verbose. At info an active capture is nearly silent.
  • Wire-trace categories (--trace, the trace console command, [Logging] trace) log every message exchanged with a subsystem, verbatim, and are independent of the level--trace serial shows all firmware serial traffic even at info. Categories are off by default (the camera one is high-rate). Only --log-level off silences 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.