# FireWatchTower — 2-Axis Gimbal Controller Firmware for a 2-axis motorised gimbal (yaw + pitch) running on an Arduino-compatible AVR microcontroller. Built around the TMC5160 stepper driver chip, with encoder feedback, automatic homing, thermal management, and a serial command interface. --- ## Hardware | Component | Details | |---|---| | MCU | ATmega32U4 (Arduino Leonardo-compatible) | | Motor driver | TMC5160 × 2 (one per axis) | | Communication | SPI @ 4 MHz, Mode 3 | | Temperature sensor | DHT11 | | Fan control | PWM output + tachometer input | ### Pin assignments | Pin | Function | |---|---| | 8 | TMC5160 CS — Yaw | | 7 | TMC5160 EN — Yaw | | 1 | TMC5160 CS — Pitch | | 0 | TMC5160 EN — Pitch | | 9 | DHT11 data | | 10 | Fan PWM output | | 11 | Fan tachometer input | ### Axis parameters | Parameter | Yaw | Pitch | |---|---|---| | Steps per revolution | 177,000 | 500,000 | | Gear ratio | 739.56 | 739.56 | | Default speed (VMAX) | 50,000 | 250,000 | | Global current scaler | 80 | 50 | --- ## Project structure ``` src/ ├── main.cpp ├── config/ │ ├── Constants.h — tuning values, thresholds, timing │ └── Pins.h — pin assignments ├── motor/ │ ├── MotorConfig.h — axis IDs, EEPROM layout, gimbal_status enum │ ├── MotorAxis.h/.cpp — per-axis state and TMC5160 register interface │ ├── MotorDriver.h/.cpp— SPI init, register config, enable/disable │ └── Homing.h/.cpp — homing state machines (non-blocking) ├── serial/ │ └── CommandParser.h/.cpp — serial command protocol ├── thermal/ │ └── ThermalManager.h/.cpp — DHT11 reading and fan PWM control └── hal/ └── SPI_HAL.cpp — TMC-API SPI callbacks (chip select per axis) ``` --- ## Building This project uses [PlatformIO](https://platformio.org/). **Build:** ```bash pio run ``` **Build and upload:** ```bash pio run --target upload ``` **Clean:** ```bash pio run --target clean ``` **Serial monitor:** ```bash pio device monitor --baud 115200 ``` --- ## Axis lifecycle Each axis progresses through the following states after power-on: ``` BOOT → RESET → ENC_INIT → INITIALIZED → AUTO / MANUAL ↓ ERROR (homing failed) ``` - **BOOT** — initial state, hardware not yet configured - **RESET** — registers written, motor enabled, ready to home - **ENC_INIT** — homing sequence in progress - **INITIALIZED** — endstop limits known, axis ready for operation - **AUTO** — stepping through positions automatically - **MANUAL** — responding to direct move commands - **ERROR** — homing failed; send `r` to reset and try again --- ## Homing Each axis must be homed before normal operation. Send `q` over serial to start. **Yaw** uses stallGuard (encoder deviation detection) as a virtual endstop — the motor drives into a mechanical stop, the TMC5160 detects the current spike, and the position is recorded. No physical switches required. **Pitch** uses physical hardware endstop switches. The TMC5160 latches the position (XLATCH) the instant a switch triggers, giving an accurate limit reading. Once both limits are found, the axis validates the measured travel range and moves to the centre position. --- ## Serial command protocol Connect at **115200 baud**. Commands are single characters sent over the serial port. **Format:** `[y|p][d],` - Axis specifier (optional): `y` = yaw only, `p` = pitch only, omit = both - Base specifier (optional): `d` after axis = decimal arguments (default is hex) ### Command reference | Command | Description | Arguments | |---|---|---| | `r` | Reset: re-init registers and enable coils | — | | `q` | Start homing sequence | — | | `!` | Disable motor coils (free-wheel) | — | | `*` | Enable motor coils (holding torque) | — | | `m` | Move to absolute position | `, ` | | `;` | Overwrite XACTUAL register directly | `` | | `s` | Stop (decelerate to zero) | — | | `b` | Clear encoder deviation warning flag | — | | `t` | Sync encoder to actual position | — | | `e` | Clear stallGuard event flag (both axes) | — | | `w` | Set stallGuard threshold | `` | | `u` | Enter AUTO stepping mode | `` (must be > 2) | | `h` | Save heading calibration to EEPROM | `0, ` | | `y` | Load heading calibration from EEPROM | `0` | | `d` | Enter MANUAL mode | — | | `p` | Advance yaw one step (AUTO mode) | — | | `v` | Set yaw speed | `` | | `+` | Set yaw right limit to current position | — | | `-` | Set yaw left limit to current position | — | | `/` | Set both yaw limits manually | `, ` | | `?` | Print current yaw endstop positions | — | ### Examples ``` q — home both axes qy — home yaw only md 500000,35000 — move both axes to position 500000 at speed 35000 (decimal) my 1F400,8888 — move yaw to 0x1F400 at speed 0x8888 (hex, default) !p — disable pitch motor w 60 — set stallGuard threshold to 60 on both axes ``` --- ## Serial status output Every 250 ms the firmware prints a status line for each axis: **Yaw:** ``` YAW: , , , , , , ``` **Pitch:** ``` PITCH: , , , , , , , , ``` | Field | Description | |---|---| | `X_enc` | Encoder position (external encoder) | | `X_act` | Step counter position (internal) | | `X_err` | Tracking error (X_act − X_enc) | | `dev_warn` | Encoder deviation warning flag | | `sg_event` | StallGuard event flag | | `sg_status` | StallGuard status bit | | `sg_stop` | StallGuard stop flag | | `endstop_l` | Left endstop state (pitch only) | | `endstop_r` | Right endstop state (pitch only) | --- ## Thermal management The DHT11 sensor is read every 250 ms. The fan PWM output is controlled proportionally: - Below 25 °C: fan off - Above 25 °C: PWM = (temperature − 25) × 20, capped at 255 --- ## Dependencies Managed automatically by PlatformIO: - `TMC-API` — Trinamic vendor library for TMC5160 register access - `DHT sensor library` — Adafruit DHT11/22 driver - `Adafruit Unified Sensor` - `SPI` — Arduino built-in - `EEPROM` — Arduino built-in