Custom CAN gateway / helper module for a Ford vehicle, sitting on both the HS-CAN (powertrain, 500 kbit/s on CAN1) and MM-CAN (multimedia, 125 kbit/s on CAN2) buses. It listens to factory traffic, mirrors useful vehicle state, and injects a few convenience features the stock setup lacks.
- MCU: STM32F105RBT6 (Cortex-M3, 128 KB flash / 64 KB RAM), dual bxCAN
- Clock: 25 MHz HSE → 72 MHz PLL
- I/O: status LED (PC12), CAN transceiver enable/sleep (PB12/PB13), two switched outputs — SW (PC10, push-pull) and SW2 (PC11, open-drain)
- Vehicle state mirror — decodes ignition, gear/shifter position, coolant
temperature, battery voltage/current/SoC, cruise state, and audio-on from
HS-CAN and MM-CAN broadcast frames (
inc/can.hdocuments all the frame layouts:0x030SWM buttons,0x0C0PCM status,0x0D0gear,0x0E0shifter,0x150BMS current,0x2F0coolant,0x435battery voltage,0x1E6ACM audio,0x3B3body/ignition…). - IPC print (
src/ipc_print.c) — periodically renders coolant temp, SoC, battery current and voltage as a text string and pushes it to the instrument cluster's navigation text area over ISO-TP on MM-CAN (0x2C0), i.e. live telemetry on the stock IPC display. - EQ presets (
src/filters.c) — translates APIM light/EQ selections (0x3DA) into ACM equalizer-set commands (0x1DC). - ACC→CC button remap (
fixACCbuttonsinsrc/filters.c) — for cars where the steering wheel has adaptive-cruise buttons but the PCM only runs standard cruise/limiter: intercepts SWM frames (0x030) and re-emits them with the CC bits set — LIM →CC_Lim, RES+ →CC_Reswhen cruise is in standby with a set speed, otherwiseCC_Set_Plus. Each press is retransmitted (up to 20× at 5 ms) until the PCM's cruise state visibly changes, with edge detection so a held button fires once. - Switched outputs — SW follows reverse gear (shifter position R); SW2 follows audio-on/ignition (e.g. powering a wireless dongle), with a delayed off.
- Mini UDS server on
0x707/0x70F:0x11ECUReset (via IWDG),0x3ETesterPresent (stops IPC printing for diagnostics),0x2FInputOutputControlByIdentifier (manual SW2 control). - ISO-TP transmit with FlowControl handling for multi-frame messages.
FreeRTOS (heap_4) on libopencm3. RX ISRs decode known broadcast frames
directly into shared state (Car, Cruise); everything else is queued to
can_rx_task (filters/UDS). Transmit goes through canTxQueue and
can_tx_task with per-message pacing delay. Hardware CAN ID-list filters
limit RX load to the IDs of interest.
src/
main.c clocks, task creation, LED task, bootloader VTOR
can.c CAN init, ISRs, rx/tx tasks, ISO-TP, UDS handler
filters.c eqPresets, fixACCbuttons
ipc_print.c telemetry text to instrument cluster
gpio.c LED, transceiver, switched outputs
inc/ headers, incl. all CAN frame bitfield layouts (can.h)
rtos/ FreeRTOS kernel (CM3 port)
libopencm3/ HAL (built via make TARGETS=stm32/f1)
Requires arm-none-eabi-gcc, CMake ≥ 3.9, Ninja.
make # Release build -> cmake-build-release/opencanbox.bin
make releasebl # bootloader-offset build -> cmake-build-releasebl/opencanbox.bin
make debug # Debug build (semihosting enabled)
make cleanEach build produces opencanbox.{elf,hex,bin,map}. The Makefile wraps CMake
and shares the cmake-build-* directories with CLion.
make releaselinks withlinker.ld— flash at0x08000000, runs standalone.make releasebllinks withlinkerbl.ld(-DBOOTLOADER=1) — image based at0x08001800andSCB->VTORrelocated accordingly, for running under can-uds-bootloader, a small STM32 bootloader occupying the first 6 KB that allows reflashing over CAN via UDS (ISO-14229): programmingSession → RequestDownload / TransferData / RequestTransferExit. The repo ships aflash.pyclient (udsoncan + can-isotp), soreleaseblimages can be flashed in-car without opening the module.
Debug builds use semihosting (initialise_monitor_handles); Release defines
NDEBUG and skips it.