Project: Arnie (lidar-guided mobile robot firmware) Author: Anton Chernov Date: 2026-06-21 Version: 0.1.0
Arnie is the firmware for a differential-drive mobile robot built around a STM32F103C8T6 (Blue Pill) MCU. The robot senses its surroundings with a Delta-2B rotating laser radar and its orientation with an HMC5883L magnetometer, drives two brushed DC motors through a custom H-bridge, and runs every activity as a task under the AcroSched cooperative scheduler.
This document captures the functional and non-functional requirements.
Each requirement has a stable identifier (REQ-NNN) and explicit acceptance
criteria so derived documents (test plans, datasheets, design notes) can refer
to them unambiguously.
Arnie shall target the STM32F103C8T6 (ARM Cortex-M3) on the Blue Pill board.
Acceptance criteria:
- SYSCLK is 72 MHz from the 8 MHz HSE via PLL ×9; APB1 = 36 MHz, APB2 = 72 MHz.
- Flash latency, prefetch and vector-table relocation are configured before
main()inSystemInit(). - The firmware fits the 64 KB Flash / 20 KB RAM budget with margin.
Arnie shall build cleanly with both Arm Compiler 6 (AC6) and GCC (arm-none-eabi)
through the CMSIS-Toolbox (cbuild).
Acceptance criteria:
cbuild arnie.csolution.yml --rebuild --toolchain AC6reports2 succeeded, 0 failed(Debug + Release).cbuild arnie.csolution.yml --rebuild --toolchain GCCreports the same.- Toolchain-specific options live in
for-compilerblocks; sources are shared unchanged between toolchains. - No dependency on libm (transcendental math is approximated in-tree).
Arnie shall run all periodic and event-driven work as tasks under the AcroSched cooperative scheduler.
Acceptance criteria:
- The 1 ms tick is produced by
SysTick_Handler()incrementingvolatile AcroTick_t sys_tick(BSP-owned, not the library). - Start-up order is
bspStart()→acroInit(&sys_tick)→acroAddTask(...)→acroRun()(never returns). - Every scheduler API call is checked; a failure routes to
Error_Handler()via theCHECK_STATUS()macro. Deliberate discards useIGNORE_RETURN(). - The scheduler library is linked per-compiler (
acrosched.libfor AC6,libacrosched.afor GCC); its headers are vendored inlib/inc/.
Arnie shall provide a platform BSP that brings up clocks, GPIO, the timebase, fault exceptions and basic I/O services.
Acceptance criteria:
SystemInit()configures clocks, GPIO and the USARTs beforemain(); it does not enable SysTick or global interrupts.bspStart()(called frommain()) starts the 1 ms SysTick, enables the configurable fault exceptions (MemManage/BusFault/UsageFault) and global interrupts.- The BSP exposes
bspGetTick(),get_system_core_clock(),reset_mcu(), LED control and UART transmit helpers.
Arnie shall receive and decode the Delta-2B laser radar stream and expose a polar obstacle map.
Acceptance criteria:
- The radar is received on USART1 at 230400 8N1 over circular DMA1 Channel 5; the link is simplex (radar → MCU).
- A byte-fed state machine validates the frame header, length, type, command and 16-bit checksum before accepting a frame.
- Measurements are folded into a 360-sector polar histogram (minimum distance per 1° sector); a completed revolution (start-angle wrap) publishes a scan.
- The driver exposes scan data, scan count, rotation speed and fault status.
Arnie shall measure heading using an HMC5883L magnetometer.
Acceptance criteria:
- The sensor is on I2C1 (PB6 SCL / PB7 SDA), standard mode 100 kHz, using the module's own pull-ups.
- The driver verifies the device identity ('H','4','3'); on mismatch or bus error it raises a fault flag and skips further reads.
- A sample yields raw X/Y/Z counts and a heading in deci-degrees (0..3599).
- The heading uses an in-tree four-quadrant arctangent approximation; no libm dependency.
Arnie shall provide a human-readable debug console independent of the sensor links.
Acceptance criteria:
- The console is USART2 TX on PA2, 115200 8N1 (PCLK1 = 36 MHz, BRR = 313).
- It does not share pins with the lidar (which occupies both USART1 lines).
- The heartbeat task periodically emits scan count, front distance, radar speed and heading.
No task shall block the dispatcher indefinitely.
Acceptance criteria:
- All peripheral polling loops (e.g. I2C status waits) are bounded by a timeout; on timeout the operation aborts and reports failure.
- Bus faults set a fault flag rather than spinning forever.
- Drivers expose a
*Process()-style entry that performs one bounded unit of work per dispatcher visit.
Arnie shall use static allocation only; dynamic allocation (malloc/free)
is forbidden in application and driver code.
Acceptance criteria:
- Buffers (DMA buffer, histograms, parser context) are statically allocated.
- No heap usage appears in BSP, lidar, compass or application sources.
Arnie shall maintain a single authoritative pin / timer / DMA allocation map.
Acceptance criteria:
- Every peripheral pin, timer channel and DMA channel in use is recorded with its function and any conflict resolution.
- New peripherals are assigned from the documented free-pin list only.
- The map is kept consistent with the implemented code.
All sources shall follow the project style guide and carry Doxygen documentation.
Acceptance criteria:
- File headers, section banners, naming conventions, single entry/exit and
declarations-at-block-top follow
.github/copilot-instructions.md. - Lines stay within 80 ± 2 columns.
- Public
.hdeclarations carry full Doxygen blocks;.cdefinitions carry the/** @fn name */tag. - Cortex-M hardware is accessed through CMSIS structures, never raw addresses.
Arnie shall version each source file independently using SemVer in its header block.
Acceptance criteria:
- Each
.c/.hheader carries@version MAJOR.MINOR.PATCH. - The first
@dateis the file creation date and is never altered on edits; the second@date @showdateis the Doxygen build date. - A meaningful change bumps the file's
@version.
Arnie shall drive two brushed DC motors through a custom H-bridge (2× IR2184 per motor) using sign-magnitude PWM.
Target: Motion phase
Acceptance criteria:
- Motor A PWM on TIM1 (PA8/PA11), Motor B PWM on TIM4 (PB8/PB9), ~20 kHz.
- Hardware enable/stop (SD) lines default to the safe OFF state on reset.
- Direction is selected by swapping which leg is PWM'd; bootstrap refresh is preserved on the switching leg.
Arnie shall measure wheel speed from single-channel tachometer encoders.
Target: Motion phase
Acceptance criteria:
- Tacho A on TIM2_CH1 (PA0), Tacho B on TIM3_CH1 (PA6) via input capture.
- Speed is derived by the period method; direction is not available (single-channel).
Arnie shall regulate wheel speed with a PID controller on a fixed tick.
Target: Motion phase
Acceptance criteria:
- The control loop runs at a fixed rate (SysTick-derived) as a scheduler task.
- Setpoint, measured speed and output are observable over the debug console.
Arnie shall apply hard-iron and soft-iron correction to the magnetometer.
Target: Navigation phase
Acceptance criteria:
- Calibration offsets/scales are applied to raw X/Y/Z before heading computation.
- A documented procedure produces the calibration constants from logged data.
Arnie shall compensate the heading for chassis tilt once an accelerometer / inertial reference is available.
Target: Navigation phase
Acceptance criteria:
- Heading remains accurate within the specified tilt range.
- Falls back to flat-plane heading when no tilt reference is present.
Arnie shall combine the polar lidar map and heading to navigate while avoiding obstacles.
Target: Navigation phase
Acceptance criteria:
- A navigation task consumes the published scan and heading to produce motor setpoints.
- A minimum stand-off distance is enforced; the robot stops or turns rather than collide.
Arnie shall enable an independent watchdog refreshed from the dispatcher.
Target: Hardening phase
Acceptance criteria:
- The IWDG is refreshed once per dispatcher iteration via the AcroSched watchdog hook.
- A stalled dispatcher (no refresh) resets the MCU within the configured timeout.