This document specifies the Hexagonal Software Architecture (Ports & Adapters) of the tracked_bot firmware for the ATmega328P microcontroller (16 MHz), the directory layout, the contract header specifications (core/interfaces/), and the implementation of SOLID design principles, Command Pattern dispatching, and safety interlocks in Embedded C.
core/interfaces/ & core/error_handler/)tracked_motion) & Soft-Start Slew Limitingled_status)The firmware is structured according to Ports & Adapters (Hexagonal Architecture) and SOLID principles tailored for resource-constrained 8-bit AVR microcontrollers:
core/interfaces/.core/ defines what it requires to operate (imotor.h for motor actuator control, icomm.h for byte-stream communication, motion_types.h for kinematic states and status codes).core/ have zero dependencies on AVR-specific hardware headers (<avr/io.h>, <avr/interrupt.h>) or MCU peripheral registers.PROGMEM, pgm_read_byte) are abstracted in progmem_compat.h with native x86/ARM fallbacks, allowing core algorithms (kinematics, slew limiter, CRC-8, binary packet FSM, ring buffer) to compile and execute natively in PC unit tests.drivers/ - OCP - Open/Closed Principle):
core/interfaces/.drivers/ without modifying a single line of domain code in core/.command_dispatcher.h) to independent, single-purpose command handlers (cmd_motion, cmd_speed, cmd_stop, cmd_ping, cmd_status, cmd_diag, cmd_err_clear).error_handler_t (SYSTEM_MODE_NORMAL, SYSTEM_MODE_DEGRADED, SYSTEM_MODE_PANIC).error_handler_is_motion_allowed() immediately inhibits all motor actuation.wdt_enable(WDTO_2S)). Hard reset occurs if the superloop stalls for more than 2.0 seconds.failsafe_watchdog_t): Periodic 20 ms timer monitor. If no valid control command is decoded within the heartbeat window (500 ms default), motors are automatically brought to an immediate halt.main.c):
main.c file serves as the single Composition Root of the application.motion_status_t (MOTION_STATUS_OK, MOTION_STATUS_INVALID_ARG, MOTION_STATUS_ERROR) instead of unchecked returns.NULL pointer checks and argument boundary validations.Each functional component is isolated into a dedicated sub-directory containing its corresponding .c and .h pair:
sw/
βββ README.md βββ This document (Firmware architecture & design guide)
βββ protocol.md βββ Binary Interface Control Document (ICD specification)
βββ scripts/
β βββ setup_udev.sh βββ Linux udev rules & /dev/tracked_bot persistent symlink
β
βββ trackedbot/
βββ main.c βββ Composition Root (Dependency Injection, Superloop, WDT)
β
βββ core/ βββ HARDWARE-AGNOSTIC DOMAIN LOGIC (Pure C99)
β βββ interfaces/ βββ Abstract Ports & Cross-Platform Contracts
β β βββ icomm.h - Bidirectional communication stream port (comm_stream_t)
β β βββ imotor.h - Actuator motor channel port (motor_handle_t, motor_dir_t)
β β βββ motion_types.h - Locomotion commands, kinematic states & return status codes
β β βββ progmem_compat.h - Cross-platform Flash storage (PROGMEM) abstraction macros
β β
β βββ command_parser/ βββ 5-State Binary Serial Frame Parser & Dispatcher
β β βββ command_parser.h/.c - FSM byte stream parser (SYNC, MSG_ID, LEN, PAYLOAD, CRC8)
β β βββ command_dispatcher.h/.c - Command router, parameter validator & activity notifier
β β βββ binary_protocol_types.h - Wire constants, message IDs, payload lengths & structures
β β βββ command/ βββ Command Pattern Execution Handlers
β β βββ cmd_motion.h/.c - CMD_ID_MOTION (0x01): Track trajectory handler
β β βββ cmd_speed.h/.c - CMD_ID_SET_SPEED (0x02): PWM drive speed handler
β β βββ cmd_stop.h/.c - CMD_ID_STOP (0x03): Immediate kinematic halt handler
β β βββ cmd_ping.h/.c - CMD_ID_PING (0x04): Heartbeat keepalive handler
β β βββ cmd_status.h/.c - CMD_ID_GET_STATUS (0x10): Telemetry query handler
β β βββ cmd_diag.h/.c - CMD_ID_GET_DIAG (0x11): Extended diagnostics handler
β β βββ cmd_err_clear.h/.c - CMD_ID_ERR_CLEAR (0x12): Fault reset handler
β β
β βββ error_handler/ βββ Centralized System Health & Safety Interlocks
β β βββ error_handler.h/.c - Error reporting, fault latching, operating mode manager
β β βββ error_types.h - System modes, error diagnostic codes & safety reactions
β β
β βββ telemetry_reporter/ βββ Outbound Binary Telemetry Frame Serializer
β β βββ telemetry_reporter.h/.c - Formats ACK, NACK, PONG, STATUS (7B) & DIAG (9B) frames
β β
β βββ crc8/ βββ CRC-8 Checksum Engine (Polynomial 0x07)
β β βββ crc8.h/.c - Hardware-friendly bitwise & byte-wise CRC-8 calculation
β β
β βββ tracked_motion/ βββ Differential Tracked Skid-Steering Kinematics
β β βββ tracked_motion.h/.c - Skid-steering kinematics, PROGMEM lookup, slew limiter integration
β β
β βββ slew_limiter/ βββ Soft-Start / Slew-Rate Acceleration Ramp
β β βββ slew_limiter.h/.c - Rate-limited PWM increment stepping (prevents brownouts)
β β
β βββ failsafe_watchdog/ βββ Software Communication Safety Watchdog
β β βββ failsafe_watchdog.h/.c - 500 ms heartbeat timeout monitor, auto-stop on comm loss
β β
β βββ ring_buffer/ βββ Single-Producer Single-Consumer Circular FIFO
β βββ ring_buffer.h/.c - Lock-free ring buffer for ISR-to-user stream decoupling
β
βββ drivers/ βββ HARDWARE ADAPTERS (AVR Peripherals & Shield Pins)
β βββ l293d_shield/ βββ Adafruit-compatible L293D Motor Shield Adapter
β β βββ l293d_shield.h/.c - Timer0/Timer2 PWM (OCR0A/OCR2A) & H-bridge imotor adapter
β β
β βββ uart_avr/ βββ ATmega328P USART0 Peripheral Stream Adapter
β β βββ uart_avr.h/.c - Interrupt-driven RX/TX ring buffers, icomm adapter
β β
β βββ shift_reg/ βββ 74HC595 Direction Latch Bit-Bang Driver
β β βββ shift_reg.h/.c - Serial bit-bang shift register control for motor shield
β β
β βββ timer_avr/ βββ Timer1 1 ms System Timebase Driver
β β βββ timer_avr.h/.c - CTC mode 1 ms millisecond counter & interval utilities
β β
β βββ led_status/ βββ Diagnostic Status LED Driver (PB5 / D13)
β βββ led_status.h/.c - Non-blocking optical cadences via PINB5 single-cycle toggle
β
βββ build/ βββ MODULAR AVR-GCC BUILD SYSTEM
βββ Makefile - Master build target orchestration (all, flash, clean)
βββ sources.mk - Enumeration of all C source compilation units
βββ subdir.mk - Per-subfolder compilation rules
βββ objects.mk - Object and dependency file collections
βββ cflags.mk - Compiler flags (-mmcu=atmega328p, -DF_CPU=16000000UL, -Wall)
βββ csflags.mk - Size calculation flags (avr-size)
βββ ocflags.mk - Object copy flags for ihex extraction (avr-objcopy)
βββ odflags.mk - Disassembly and listing flags (avr-objdump)
βββ tools.mk - Toolchain executable definitions (avr-gcc, avrdude, etc.)
core/interfaces/ & core/error_handler/)Abstract struct-of-function-pointers and enumerated domain types decouple domain interactors from physical hardware peripherals:
icomm.h β Bidirectional Byte Stream PortAbstracts any character-oriented communication stream (USART0, USB-CDC, SPI, virtual ring buffer):
typedef struct {
void *context;
bool (*available)(void *context);
uint8_t (*read)(void *context);
void (*write)(void *context, uint8_t byte);
void (*write_str)(void *context, const char *str);
} comm_stream_t;
imotor.h β Motor Actuator PortAbstracts an individual DC motor channel regardless of the underlying H-bridge IC or PWM timer:
typedef enum {
MOTOR_DIR_STOP = 0,
MOTOR_DIR_FORWARD,
MOTOR_DIR_BACKWARD
} motor_dir_t;
typedef struct {
void *context;
motion_status_t (*set_direction)(void *context, motor_dir_t dir);
motion_status_t (*set_speed)(void *context, uint8_t speed);
motion_status_t (*stop)(void *context);
} motor_handle_t;
motion_types.h β Kinematics & Motion Status CodesProvides standard enumeration types for locomotion directions, vehicle states, and return statuses:
typedef enum {
MOTION_STATUS_OK = 0,
MOTION_STATUS_ERROR,
MOTION_STATUS_INVALID_ARG
} motion_status_t;
typedef enum {
MOTION_STOP = 0,
MOTION_FORWARD,
MOTION_BACKWARD,
MOTION_TURN_LEFT,
MOTION_TURN_RIGHT,
MOTION_SPIN_LEFT,
MOTION_SPIN_RIGHT
} motion_cmd_t;
typedef enum {
MOTION_STATE_IDLE = 0,
MOTION_STATE_MOVING,
MOTION_STATE_FAILSAFE
} motion_state_t;
error_types.h β System Modes & Safety DiagnosticsDefines operating health states, standardized error codes, and safety reaction policies:
typedef enum {
SYSTEM_MODE_NORMAL = 0, /**< Nominal operation; all motion and telemetry permitted. */
SYSTEM_MODE_DEGRADED, /**< Fault active; motors locked or communication restricted. */
SYSTEM_MODE_PANIC /**< Critical fault; safe shutdown or hardware reset pending. */
} system_mode_t;
typedef enum {
ERR_CODE_NONE = 0,
ERR_CODE_INIT_UART,
ERR_CODE_INIT_MOTORS,
ERR_CODE_INIT_TIMER,
ERR_CODE_INIT_PARSER,
ERR_CODE_INIT_FAILSAFE,
ERR_CODE_COMM_RX_OVERFLOW,
ERR_CODE_COMM_TX_OVERFLOW,
ERR_CODE_CMD_SYNTAX_ERROR,
ERR_CODE_CMD_UNKNOWN,
ERR_CODE_FAILSAFE_TRIGGERED,
ERR_CODE_HARDWARE_FAULT,
ERR_CODE_COMM_CRC
} error_code_t;
progmem_compat.h β Cross-Platform Flash Memory StorageProvides transparent PROGMEM macros for Harvard AVR architectures while ensuring host testability:
#if defined(__AVR__)
#include <avr/pgmspace.h>
#else
#define PROGMEM
#define PSTR(s) (s)
#define pgm_read_byte(addr) (*(const uint8_t *)(addr))
#define pgm_read_word(addr) (*(const uint16_t *)(addr))
#define pgm_read_ptr(addr) (*(void *const *)(addr))
#endif
To satisfy the Open/Closed Principle (OCP) and eliminate fragile global macros, hardware-specific constants are strictly confined to their respective adapter modules:
DDRD, PORTD, DDRB, PORTB) and Timer0 / Timer2 fast PWM configuration (TCCR0A, TCCR0B, OCR0A, TCCR2A, TCCR2B, OCR2A) are defined solely inside drivers/l293d_shield/l293d_shield.c. For electrical pinout details, see ../hw/motor_shield.md.PD4), latch (PB4), serial data (PB0), and output enable (PD7) are isolated inside drivers/shift_reg/shift_reg.c.UBRR0H, UBRR0L), control registers (UCSR0A, UCSR0B, UCSR0C), and interrupt vectors (USART_RX_vect, USART_UDRE_vect) are encapsulated within drivers/uart_avr/uart_avr.c. For serial UART hardware details, see ../hw/main_board.md.TCCR1B, OCR1A = 249), prescaler 64, and the 1 ms tick interrupt (TIMER1_COMPA_vect) are encapsulated within drivers/timer_avr/timer_avr.c.DDRB5), output latch (PORTB5), and the single-cycle hardware toggle via input register PINB5 are encapsulated within drivers/led_status/led_status.c.tracked_motion) & Soft-Start Slew LimitingThe tracked_motion module manages differential track velocities for the T101 mechanical chassis. Direction mapping is defined in a Flash-resident lookup table (S_KINEMATICS_TABLE in PROGMEM), yielding zero-RAM overhead and $O(1)$ constant-time execution:
| Motion Command | Left Track Direction | Right Track Direction | Left Active | Right Active | Maneuver Description |
|---|---|---|---|---|---|
MOTION_STOP |
MOTOR_DIR_STOP |
MOTOR_DIR_STOP |
False | False | Immediate zero-PWM standstill |
MOTION_FORWARD |
MOTOR_DIR_FORWARD |
MOTOR_DIR_FORWARD |
True | True | Straight forward drive |
MOTION_BACKWARD |
MOTOR_DIR_BACKWARD |
MOTOR_DIR_BACKWARD |
True | True | Straight reverse drive |
MOTION_TURN_LEFT |
MOTOR_DIR_STOP |
MOTOR_DIR_FORWARD |
False | True | Pivot turn (left stopped, right forward) |
MOTION_TURN_RIGHT |
MOTOR_DIR_FORWARD |
MOTOR_DIR_STOP |
True | False | Pivot turn (left forward, right stopped) |
MOTION_SPIN_LEFT |
MOTOR_DIR_BACKWARD |
MOTOR_DIR_FORWARD |
True | True | Counter-rotation spin left ($R = 0$) |
MOTION_SPIN_RIGHT |
MOTOR_DIR_FORWARD |
MOTOR_DIR_BACKWARD |
True | True | Counter-rotation spin right ($R = 0$) |
slew_limiter)Directly switching motor PWM from 0 to 255 produces massive inrush currents (up to 2.5 A per motor at stall), causing battery voltage sag that can brown out the ATmega328P and overheat the L293D drivers (see ../hw/power_supply.md):
slew_limiter ramps current motor speed toward the target speed in fixed increment steps executed every 20 ms.MOTION_STOP command or a failsafe timeout occurs, the slew limiter is bypassed to guarantee an immediate zero-latency stop.The firmware utilizes a deterministic binary serial protocol specified in protocol.md. Packets use a compact 4-part structure with a maximum payload of 16 bytes:
flowchart LR
SYNC["<b>SYNC</b><br/>1 Byte<br/><code>0xAA</code>"]
ID["<b>MSG_ID</b><br/>1 Byte<br/><code>0x01..0xFF</code>"]
LEN["<b>LENGTH</b><br/>1 Byte<br/><code>0..16</code>"]
PAYLOAD["<b>PAYLOAD</b><br/>0..16 Bytes<br/>Command / Telemetry"]
CRC["<b>CRC-8</b><br/>1 Byte<br/><code>0x00..0xFF</code>"]
SYNC --- ID --- LEN --- PAYLOAD --- CRC
subgraph CRC_SCOPE ["<b>CRC-8 Calculation Scope (Polynomial 0x07)</b>"]
direction LR
ID
LEN
PAYLOAD
end
classDef syncStyle fill:#991b1b,stroke:#f87171,stroke-width:2px,color:#ffffff;
classDef hdrStyle fill:#312e81,stroke:#818cf8,stroke-width:2px,color:#ffffff;
classDef payloadStyle fill:#0369a1,stroke:#38bdf8,stroke-width:2px,color:#ffffff;
classDef crcStyle fill:#7c2d12,stroke:#fb923c,stroke-width:2px,color:#ffffff;
class SYNC syncStyle;
class ID,LEN hdrStyle;
class PAYLOAD payloadStyle;
class CRC crcStyle;
style CRC_SCOPE fill:none,stroke:#fb923c,stroke-width:2px,stroke-dasharray: 4 4
[MSG_ID, LENGTH, PAYLOAD...] via crc8_update().command_parser_t)The parser consumes bytes non-blockingly from comm_stream_t using a 5-state FSM:
stateDiagram-v2
[*] --> PARSER_STATE_WAIT_SYNC
PARSER_STATE_WAIT_SYNC --> PARSER_STATE_READ_ID : byte == 0xAA (SYNC)
PARSER_STATE_WAIT_SYNC --> PARSER_STATE_WAIT_SYNC : byte != 0xAA
PARSER_STATE_READ_ID --> PARSER_STATE_READ_LEN : Store msg_id
PARSER_STATE_READ_LEN --> PARSER_STATE_CHECK_CRC : len == 0
PARSER_STATE_READ_LEN --> PARSER_STATE_READ_PAYLOAD : 0 < len <= 16
PARSER_STATE_READ_LEN --> PARSER_STATE_WAIT_SYNC : len > 16 (Send NACK)
PARSER_STATE_READ_PAYLOAD --> PARSER_STATE_CHECK_CRC : index == len
PARSER_STATE_READ_PAYLOAD --> PARSER_STATE_READ_PAYLOAD : index < len
PARSER_STATE_CHECK_CRC --> PARSER_STATE_WAIT_SYNC : CRC Match -> Dispatch Command
PARSER_STATE_CHECK_CRC --> PARSER_STATE_WAIT_SYNC : CRC Mismatch -> Send NACK
When a frame is verified, command_dispatcher_execute() verifies payload length bounds, feeds the failsafe watchdog via on_command_activity(), and executes the corresponding Command handler:
| MSG_ID | Identifier | Target Handler | Description | Outbound Response |
|---|---|---|---|---|
0x01 |
CMD_ID_MOTION |
cmd_motion_execute() |
Set drive trajectory (motion_cmd_t) |
RESP_ID_ACK / NACK |
0x02 |
CMD_ID_SET_SPEED |
cmd_speed_execute() |
Set PWM drive speed ($0 \dots 255$) | RESP_ID_ACK / NACK |
0x03 |
CMD_ID_STOP |
cmd_stop_execute() |
Immediate zero-PWM stop | RESP_ID_ACK / NACK |
0x04 |
CMD_ID_PING |
cmd_ping_execute() |
Keepalive heartbeat query | RESP_ID_PONG (0x84) |
0x10 |
CMD_ID_GET_STATUS |
cmd_status_execute() |
Query consolidated status | RESP_ID_STATUS (7B) |
0x11 |
CMD_ID_GET_DIAG |
cmd_diag_execute() |
Query full diagnostic telemetry | RESP_ID_DIAG (9B) |
0x12 |
CMD_ID_ERR_CLEAR |
cmd_err_clear_execute() |
Clear active fault latch & count | RESP_ID_ACK / NACK |
USART_RX_vect): Incoming UART bytes are pushed into a lock-free Single-Producer Single-Consumer (SPSC) circular queue (ring_buffer_t). The main loop pulls characters without blocking.USART_UDRE_vect): Outbound frames formatted by telemetry_reporter are buffered in s_tx_ring. The Data Register Empty interrupt feeds the hardware UDR0 register in the background, ensuring zero CPU wait states.The error_handler_t module manages system safety and operational state:
SYSTEM_MODE_NORMAL: All subsystems nominal; motion and telemetry fully enabled.SYSTEM_MODE_DEGRADED: Non-critical fault or communications warning active; motion is safely locked.SYSTEM_MODE_PANIC: Critical hardware or initialization failure; all actuation halted.In the main superloop, motor actuation is guarded by an explicit domain policy check:
if (error_handler_is_motion_allowed(&s_error_handler)) {
(void)tracked_motion_update(&s_tracked_motion, (uint32_t)FAILSAFE_CHECK_INTERVAL_MS);
} else {
(void)tracked_motion_stop(&s_tracked_motion);
}
telemetry_reporter)RESP_ID_STATUS (0x90, 7 Bytes): [mode, last_err, count_hi, count_lo, mot_state, mot_cmd, speed]RESP_ID_DIAG (0x91, 9 Bytes): Consolidated status (7B) + failsafe_flags (1B) + motion_lock (1B).RESP_ID_ACK (0x80, 1 Byte): [cmd_id]RESP_ID_NACK (0x81, 2 Bytes): [cmd_id, error_code_t]RESP_ID_PONG (0x84, 0 Bytes): Heartbeat confirmation.led_status)The led_status module provides non-blocking visual diagnostics on the onboard LED (Arduino pin 13 / ATmega328P PB5), driven directly by the active system mode:
PINB5 toggles the underlying PORTB5 output latch in a single 16 MHz clock cycle (62.5 ns) without read-modify-write overhead.| Operating Mode | Frequency | ON Time | OFF Time | Visual Indication |
|---|---|---|---|---|
SYSTEM_MODE_NORMAL |
1.0 Hz | 100 ms | 900 ms | Calm, healthy heartbeat |
SYSTEM_MODE_DEGRADED |
5.0 Hz | 100 ms | 100 ms | Rapid warning flash |
SYSTEM_MODE_PANIC |
10.0 Hz | 50 ms | 50 ms | Urgent emergency strobe |
The firmware build system is located in sw/trackedbot/build/ and is organized into modular Makefile fragments:
Install the AVR-GCC toolchain and flashing utility:
sudo apt-get install gcc-avr binutils-avr avr-libc avrdude make
To flash without root privileges and establish a persistent device node (/dev/tracked_bot), execute the included configuration script:
sudo bash sw/scripts/setup_udev.sh
Compile the project to generate the binary ELF, Intel HEX image, and assembly listing:
cd sw/trackedbot/build
make all
Program the ATmega328P via the persistent /dev/tracked_bot symlink at 115200 baud:
make flash
(Override default port or baud rate via make flash PORT=/dev/ttyUSB0 BAUD=115200)
make clean
graph TD
subgraph CompositionRoot ["Composition Root"]
MAIN["main.c"]
end
subgraph CoreLayer ["sw/trackedbot/core/ (Hardware-Agnostic Domain Logic)"]
subgraph Interfaces ["interfaces/ (Ports & Contracts)"]
ICOMM["icomm.h<br/>(comm_stream_t)"]
IMOTOR["imotor.h<br/>(motor_handle_t)"]
TYPES["motion_types.h<br/>(motion_cmd_t, motion_status_t)"]
PROGMEM_COMPAT["progmem_compat.h"]
end
subgraph ParserSubsystem ["command_parser/"]
PARSER["command_parser.h/.c<br/>(5-State FSM)"]
DISPATCHER["command_dispatcher.h/.c"]
CMD_TYPES["binary_protocol_types.h"]
CMDS["command/cmd_*.h/.c<br/>(Command Handlers)"]
end
ERR["error_handler/<br/>(error_types.h)"]
TELEM["telemetry_reporter/"]
CRC["crc8/"]
TM["tracked_motion/"]
SLEW["slew_limiter/"]
WATCHDOG["failsafe_watchdog/"]
RB["ring_buffer/"]
end
subgraph DriversLayer ["sw/trackedbot/drivers/ (AVR Hardware Adapters)"]
L293D["l293d_shield/"]
UART["uart_avr/"]
TIMER["timer_avr/"]
SHIFT["shift_reg/"]
LED["led_status/"]
end
%% Dependencies of Core onto Interfaces and Collaborators
TM --> IMOTOR
TM --> TYPES
TM --> SLEW
TM --> PROGMEM_COMPAT
PARSER --> DISPATCHER
PARSER --> CRC
PARSER --> TELEM
DISPATCHER --> CMDS
DISPATCHER --> TM
DISPATCHER --> ERR
DISPATCHER --> WATCHDOG
CMDS --> TM
CMDS --> ERR
CMDS --> TELEM
TELEM --> ICOMM
TELEM --> CRC
WATCHDOG --> TM
WATCHDOG --> TYPES
%% Dependencies of Drivers onto Interfaces and Core
L293D --> IMOTOR
L293D --> SHIFT
UART --> ICOMM
UART --> RB
LED --> ERR
%% Composition Root wires everything via Dependency Injection
MAIN --> TM
MAIN --> PARSER
MAIN --> WATCHDOG
MAIN --> ERR
MAIN --> TELEM
MAIN --> L293D
MAIN --> UART
MAIN --> TIMER
MAIN --> LED
classDiagram
class comm_stream_t {
+void* context
+bool (*available)(void* ctx)
+uint8_t (*read)(void* ctx)
+void (*write)(void* ctx, uint8_t b)
+void (*write_str)(void* ctx, const char* s)
}
class motor_handle_t {
+void* context
+set_direction(void* ctx, motor_dir_t dir) motion_status_t
+set_speed(void* ctx, uint8_t speed) motion_status_t
+stop(void* ctx) motion_status_t
}
class slew_limiter_t {
+uint8_t current_val
+uint8_t target_val
+uint8_t rate_per_step
+slew_limiter_update() motion_status_t
}
class tracked_motion_t {
-motor_handle_t left_motor
-motor_handle_t right_motor
-slew_limiter_t speed_limiter
+set_command(motion_cmd_t cmd) motion_status_t
+set_speed(uint8_t speed) motion_status_t
+update(uint16_t delta_ms) motion_status_t
+stop() motion_status_t
}
class command_parser_t {
-command_dispatcher_t dispatcher
-parser_state_t state
-uint8_t payload_buffer[16]
+command_parser_process() motion_status_t
}
class command_dispatcher_t {
-tracked_motion_t* tracked_motion
-comm_stream_t stream
-error_handler_t* error_handler
-failsafe_watchdog_t* failsafe
+command_dispatcher_execute() bool
}
class error_handler_t {
-system_mode_t mode
-error_code_t last_error
-uint16_t error_count
+error_handler_report() motion_status_t
+error_handler_is_motion_allowed() bool
}
class uart_avr {
+uart_avr_init(baud) bool
+uart_avr_get_stream() comm_stream_t
}
class l293d_shield {
+l293d_init() motion_status_t
+l293d_get_motor_handle(ch, inv) motor_handle_t
}
uart_avr ..|> comm_stream_t : Produces Adapter
l293d_shield ..|> motor_handle_t : Produces Adapter
command_parser_t *-- command_dispatcher_t : Composes
command_dispatcher_t --> comm_stream_t : Consumes
command_dispatcher_t --> tracked_motion_t : Controls
command_dispatcher_t --> error_handler_t : Reports/Queries
tracked_motion_t *-- slew_limiter_t : Composes
tracked_motion_t --> motor_handle_t : Controls Left & Right
sequenceDiagram
autonumber
actor Client as Host Controller (UART)
participant USART as USART0 (uart_avr)
participant Parser as command_parser (FSM)
participant CRC as crc8
participant Dispatcher as command_dispatcher
participant CmdMotion as cmd_motion
participant Watchdog as failsafe_watchdog
participant TM as tracked_motion
participant Telem as telemetry_reporter
participant Slew as slew_limiter
participant L293D as l293d_shield
participant Motors as DC Motors M3 & M1
Client->>USART: Binary Frame: [0xAA, 0x01, 0x01, 0x01, CRC8]
Note over USART: USART_RX_vect pushes incoming bytes to Ring Buffer
loop Superloop Polling Cycle
Parser->>USART: comm_available(&stream)
USART-->>Parser: true
Parser->>USART: comm_read(&stream)
Note over Parser: FSM: WAIT_SYNC -> READ_ID (0x01) -> READ_LEN (1) -> READ_PAYLOAD -> CHECK_CRC
Parser->>CRC: crc8_update() over [0x01, 0x01, 0x01]
CRC-->>Parser: Checksum Match
Parser->>Dispatcher: command_dispatcher_execute(CMD_ID_MOTION, payload, 1)
Dispatcher->>Watchdog: on_activity() -> failsafe_feed()
Dispatcher->>CmdMotion: cmd_motion_execute()
CmdMotion->>TM: tracked_motion_set_command(MOTION_FORWARD)
TM->>Slew: slew_limiter_set_target(target_speed)
TM->>L293D: motor_set_direction(left, FORWARD) & motor_set_direction(right, FORWARD)
CmdMotion->>Telem: telemetry_reporter_send_ack(stream, CMD_ID_MOTION)
Telem->>CRC: Compute CRC-8 for RESP_ID_ACK (0x80)
Telem->>USART: comm_write() frame [0xAA, 0x80, 0x01, 0x01, CRC]
Note over USART: USART_UDRE_vect transmits frame asynchronously
Note over TM: Periodic 20ms Tick -> tracked_motion_update()
TM->>Slew: slew_limiter_update() -> Smoothly steps PWM duty cycle
TM->>L293D: motor_set_speed(OCR0A = cur_speed, OCR2A = cur_speed)
L293D->>Motors: Smooth PWM acceleration drives tracks forward
end
/dev/tracked_bot port symlinks and flashing permissions.OCR0A/OCR2A), 74HC595 direction multiplexer, and M3 (Left Track) / M1 (Right Track) split-chip mapping.EXT_PWR vs. USB), motor EMI filtering, and battery current budgets.