Skip to content

Repository files navigation

vanmoof-s5-decomp

Clean-room rebuild of the VanMoof S5 / A5 firmware (internal codename XS5) from decompilations, producing buildable source trees that re-emit binary-equivalent (or behaviour-equivalent) images.

Sibling project to vanmoof-s3-decomp. The S5 is a generation newer and architecturally very different: instead of a handful of small MCUs on a serial bus, it runs a Linux application processor (i.MX8 class), a cellular modem (nRF9160), a BLE SoC (nRF5x, Zephyr), and a fleet of Cortex-M sub-ECUs on a CAN bus. This repo decomposes that stack target by target.

Status: first target active. The repo documents the firmware layout and each target's container format / MCU. user_ecu/ is the first target under reconstruction: ~121 VanMoof functions translated to C (all compile clean for Cortex-M4F), the FreeRTOS/libgcc/SDK code identified and deferred as vendor, and the static frontier exhausted. The boot/clocks, I²C + CAN comms, FOTA/storage, device registry, the handlebar buttons, and the LED control chain are documented under user_ecu/docs/. The remaining per-target subdirectories are created as work on each begins.

Firmware source

The reference image is the v1.5.0-main FOTA bundle (build 20240129, version 1.5.0), located at:

../VanMooof-Firmware/SA5/
├── v1.5.0-main/
│   └── v1.5.0-main                ← gzip → tar → SquashFS rootfs (the i.MX8 Linux system, ~57 MB)
└── v1.5.0-main_device_files/      ← the individual per-ECU images + manifest.txt

manifest.txt is the authoritative list of components shipped in this release (Filename,Device,Date,Time,Major,Minor,Patch,Type,AllowSkip,DontRollback). Rendered readable (all main images share build 2024-01-29 14:52:22 except motor_control at 15:42:39; the battery/charger blobs are vendor untagged):

Device Version Type Build Skippable No-rollback
imx8_bridge 1.5.0 main 2024-01-29 14:52:22 no no
ble 1.5.0 main 2024-01-29 14:52:22 no no
modem 1.5.0 main 2024-01-29 14:52:22 yes no
motor_sensor 1.5.0 main 2024-01-29 14:52:22 no no
elock 1.5.0 main 2024-01-29 14:52:22 no no
user_ecu 1.5.0 main 2024-01-29 14:52:22 no no
frontlight 1.5.0 main 2024-01-29 14:52:22 no no
rearlight 1.5.0 main 2024-01-29 14:52:22 no no
eshifter 1.5.0 main 2024-01-29 14:52:22 no no
motor_control 1.5.0 main 2024-01-29 15:42:39 no no
power_control 1.5.0 main 2024-01-29 14:52:22 no no
power_pedal 1.5.0 main 2024-01-29 14:52:22 no no
battery_primary_panasonic 1.4.256 untagged yes yes
charger_liteon_normal 1.8.0 untagged yes yes
charger_liteon_speed 1.2.0 untagged yes yes

AllowSkip = the updater may skip this image; DontRollback = it must not be rolled back to an earlier version (set on the vendor battery/charger blobs).

Targets

Container formats and sizes below are confirmed from the image headers; MCU part numbers marked (tbc) are inferred from the vector layout / SDK and are to be confirmed in Ghidra.

Application processor (Linux)

Component Image Format Size Status
main v1.5.0-mainVM-XS5_FOTA gzip → tar → SquashFS (zlib) ~57 MB documented — i.MX8M Nano, NXP 5.4-zeus + meta-vmxs5; rootfs unpacked, service graph + MQTT bus + OEM delta mapped

The main module is the bike's Linux brain (hostname vmxs5mainecu): an i.MX8M Nano running NXP i.MX Release Distro 5.4-zeus with a VanMoof application layer (meta-vmxs5) — a Go gateway to AWS IoT plus a C++ service suite (power, ride, ux, tracking, monitor, update, logging) wired together over a loopback MQTT bus, bridging BLE, the nRF9160 modem, and the CAN fleet. Full analysis under main/docs/:

Doc Covers
version.md firmware tag, OS base, kernel/toolchain, build provenance
oem-differences.md what VanMoof added vs the stock NXP image (the vmxs5-* packages)
architecture.md SoC, boot/partitions, service graph, bike ↔ cloud data flow
hardware.md i.MX8 peripherals — charger/gauge ICs, the two batteries, RTC/IMU, CAN/SPI/serial
services.md per-service / per-binary inventory (Go gateway + C++ suite + bridges)
power/ reconstruction of power — battery/charger state machine + OD/CAN telemetry + MQTT wiring
mqtt-bus.md the internal MQTT bus + the BLE role-based ACL
can-bus.md the CAN wire protocol (vm library) — frame format, 29-bit ID encoding, the OD address map (verified)
kernel-modules.md the two out-of-tree .kos — cryptodev + Jailhouse (both vendor)
update.md the two OTA paths — i.MX8 A/B self-update + peripheral (CAN/SMP) updater
fota-image.md Pegatron FOTA container format, unpack recipe, eMMC A/B layout

Wireless SoCs (Zephyr + MCUboot, magic 0x96f3b83d)

Component Image MCU Size Status
ble ble.*.bin Nordic nRF52840 (tbc), Zephyr + SoftDevice Controller ~273 KB active — base fixed to 0x23000, 2015 fns; 53 VanMoof fns (auth/ble/settings/findmy-glue) all reconstructed to C, rest vendor (incl. 116 Apple Find My, deferred)
modem modem.*.bin Nordic nRF9160 (LTE-M/NB-IoT + GNSS), Zephyr ~302 KB pending

Both carry an MCUboot image header (0x96f3b83d); the payload is a Zephyr/nRF Connect SDK application. modem also ships a vendor modem firmware (mfw_nrf9160_1.3.1.zip) in the device-files directory.

Sub-ECUs (ARM Cortex-M, raw vector table)

All start with a Cortex-M vector table (initial SP 0x2000_8000, except user_ecu at 0x2001_0000). Part numbers (tbc) via Ghidra. Each in-house image carries a VMFW ("VanMoof FirmWare") header at 0x134 (right after the vector table): magic, version/CRC, the image length at 0x140, and the build __DATE__/__TIME__ (Jan 29 2024 14:50:32 for this release). The header is shared by imx8_bridge, elock, eshifter, the lights, motor_sensor, the power boards, and user_ecu — but not ble/modem (MCuboot 0x96f3b83d) or motor_control (header 0x000008aa). The updater's per-page CRC flashing keys off it (see main/docs/update.md).

Component Image Role Size Status
user_ecu user_ecu.*.bin handlebar controller — Cortex-M4F, FreeRTOS; handlebar buttons + cluster LEDs, sensors, USB-C phone-charge monitor; I²C+CRC-8 + CAN comms ~106 KB active — 192 fns analyzed, ~121 reconstructed to C; boot/comms/control/FOTA/buttons/LED documented
imx8_bridge imx8_bridge.*.bin SPI(i.MX8)↔CAN(fleet) bridge — Cortex-M + FreeRTOS (CanTX); discrete MCU (likely the lpc55sxx SPI satellite), 61-IRQ NVIC, SP 0x2000_8000 ~24 KB scoped — static first-look done; Ghidra pending
motor_control motor_control.*.bin motor controller (non-standard header 0x000008aa) ~25 KB pending
motor_sensor motor_sensor.*.bin motor position/torque sensing ~25 KB pending
power_control power_control.*.bin power management — NXP Cortex-M4F, FreeRTOS; power-mode state machine, rail sequencing, NTC sensing, Bosch M_CAN comms ~29 KB active — 84 fns classified, 15 VanMoof fns reconstructed to C; rest vendor (FreeRTOS / NXP HAL / libgcc)
power_pedal power_pedal.*.bin pedal-assist sensing ~28 KB pending
elock elock.*.bin electronic frame lock ~27 KB pending
eshifter eshifter.*.bin e-shifter (auto gearbox) ~29 KB pending
frontlight frontlight.*.bin front light ~28 KB pending
rearlight rearlight.*.bin rear light ~27 KB pending

user_ecu documentation

The first reconstructed target is documented under user_ecu/docs/:

Doc Covers
architecture.md what the firmware does — boot, RTOS startup, tasks, control/math, the two comms paths, subsystems, reconstruction status
hardware.md MCU identity, memory/peripheral map, clock tree, SRAM globals, the USB-C phone-charge power monitor
protocol.md the I²C wire framing + CRC-8, and the CAN/device command dispatch (inbound command frames, device-record commands, outbound framing, event/status records, the comm-port frame path)
buttons.md the handlebar buttons — GPIO scan, debounce, and exactly what CAN data each press/release sends
led_control.md the LED control chain end-to-end with CAN/I²C worked examples
progress.md per-function tracker (source of truth for what's left)

CAN bus: the on-MCU command/event framing is documented in protocol.md and buttons.md; the on-wire CAN ID/DLC mapping is off-image (it lives in the application processor's upper protocol layer, not in any sub-ECU image).

Peripheral / vendor firmware

Component Image Vendor Size Status
battery_primary_panasonic battery_primary_panasonic.0.0.1.4.256.*.bin Panasonic BMS — encrypted (a _DECRYPTED.bin is present); header "(C) 2021 Energy Company of Panasonic Group" ~98 KB pending
charger_liteon_normal charger_liteon_normal.0.0.1.8.0.*.bin LiteON charger (normal) ~23 KB pending
charger_liteon_speed charger_liteon_speed.0.0.1.2.0.*.bin LiteON charger (speed) ~23 KB pending

Planned repository layout

Mirrors vanmoof-s3-decomp: one self-contained subdirectory per target, each created as work begins.

vanmoof-s5-decomp/
├── README.md
├── .gitignore
├── Makefile                ← top-level dispatcher (added with the first buildable target)
├── main/                   ← i.MX8 Linux rootfs analysis (SquashFS unpack, service map)
├── ble/                    ← nRF5x BLE app (Zephyr/MCUboot)
├── modem/                  ← nRF9160 cellular app (Zephyr/MCUboot)
├── user_ecu/               ← main vehicle controller
├── imx8_bridge/            ← i.MX8 ↔ ECU bus gateway
├── motor_control/          ├ motor_sensor/
├── power_control/          ├ power_pedal/
├── elock/  eshifter/  frontlight/  rearlight/
├── battery/                ← Panasonic BMS (encrypted)
├── charger/                ← LiteON chargers
├── tools/                  ← cross-target tooling (FOTA unpack, MCUboot/CAN helpers)
└── reference/              ← shared datasheets, CAN/protocol notes, pin maps

Each target subdirectory follows the S3 convention: src/, include/, docs/ (progress.md, hardware.md, protocol.md), ghidra/exports/, and a linker script + Makefile.

Legal

This is a clean-room interoperability project. OEM firmware images are not redistributed in this repository — extract them from a FOTA bundle or flash dump of a bike you own.

Reverse engineering for interoperability is permitted under EU Software Directive 2009/24/EC Art. 6 and US DMCA §1201(f). The reconstructed source in this repository is original work derived from analysis of the OEM image and publicly available documentation.

No warranty. Flashing reconstructed firmware to a bike will brick or damage hardware if it is wrong. Do not flash to a bike you depend on. Use a spare PCB or hardware-in-the-loop simulator.

About

VanMoof S5/A5 Decompilation

Topics

Resources

Stars

Watchers

Forks

Contributors

Languages