214 lines
6.3 KiB
Markdown
214 lines
6.3 KiB
Markdown
# 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:** `<cmd>[y|p][d]<arg1>,<arg2>`
|
||
|
||
- 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 | `<position>, <speed>` |
|
||
| `;` | Overwrite XACTUAL register directly | `<value>` |
|
||
| `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 | `<sgt>` |
|
||
| `u` | Enter AUTO stepping mode | `<num_steps>` (must be > 2) |
|
||
| `h` | Save heading calibration to EEPROM | `0, <heading_value>` |
|
||
| `y` | Load heading calibration from EEPROM | `0` |
|
||
| `d` | Enter MANUAL mode | — |
|
||
| `p` | Advance yaw one step (AUTO mode) | — |
|
||
| `v` | Set yaw speed | `<vmax>` |
|
||
| `+` | Set yaw right limit to current position | — |
|
||
| `-` | Set yaw left limit to current position | — |
|
||
| `/` | Set both yaw limits manually | `<right>, <left>` |
|
||
| `?` | 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: <X_enc>, <X_act>, <X_err>, <dev_warn>, <sg_event>, <sg_status>, <sg_stop>
|
||
```
|
||
|
||
**Pitch:**
|
||
```
|
||
PITCH: <X_enc>, <X_act>, <X_err>, <dev_warn>, <sg_event>, <sg_status>, <sg_stop>, <endstop_l>, <endstop_r>
|
||
```
|
||
|
||
| 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 |