-
Notifications
You must be signed in to change notification settings - Fork 17
Home
Welcome to the ION E-Bike Battery Emulator Wiki. This is a documentation effort based on a large collaborative analysis from the pedelec community to reverse-engineer and revive the ION motor system used in older Dutch e-bikes, such as those from Sparta, Batavus, and Koga.
- Introduction
- Project Goals
- ION System Architecture
- BOW Bus Protocol & Communication
- Reverse Engineering the Communication
- Hardware Requirements
- To-Do List: Build an ION Battery Emulator Board
- ESP32 Setup & Configuration
- Software & Tools
- BowTool
- NiMH vs Li-Ion Batteries
- Safety Considerations
- Motor Feedback Signals
- CU2 vs CU3 Display Comparison
- Logging Best Practices
- Emulator Development Checklist
- Protocol State Diagram
- Firmware Flashing Notes (ESP32)
- Frequently Asked Questions (FAQ)
- Community & Support
- Glossary
- Versioning
- Suggested Tools and Repos
- License
- Current Progress
- Known Issues & Challenges
- Resources & References
- Contributing
- Contributors
The ION system, once a mainstream drive unit in the Netherlands, has become obsolete due to aging batteries and proprietary designs. While the mechanical and electrical parts of the e-bikes remain functional, users are often left with unusable systems. This project explores the development of an emulator for the ION battery and display, allowing the reuse of these bikes with custom lithium-ion batteries and open-source controllers.
- Replace the original ION battery with a custom Li-Ion pack.
- Understand and emulate the serial communication protocol.
- Enable full functionality of the motor (including assist modes).
- Use inexpensive hardware (ESP32, USB UART) for emulation.
- Document everything for community use and development.
Note: While the most commonly documented motor is the Toprun MMU1, there may be multiple versions or models used across Sparta, Batavus, and Koga bikes that employ the ION system. These motors are all believed to support the BOW bus protocol and share similar communication behavior, but may differ in firmware, response timing, or handshake validation routines.
Further documentation of alternative ION-compatible motors is encouraged if identified.
Components:
- Motor: Toprun (MMU1), integrated controller.
- Battery: PMU1 or PMU2, typically NiMH or early Li-Ion, integrated BMS.
- Display: CU2 (round), CU3 (rectangular), with limited feedback.
- Communication: Single-wire bus (half-duplex UART at ~9600 baud).
Communication Topology: All components communicate over a shared single-wire bus. Data is exchanged in a serialized protocol with distinct packet headers, payloads, and checksums.
The ION system uses a single-wire serial protocol commonly referred to as the BOW bus. This protocol is shared across all system components β motor, battery, display, and external tools like BowTool or ESP32 emulators. It runs at 9600 baud and follows a defined message structure.
10 C1 31 00 00 01 FF 31
| Byte(s) | Description | Value |
|---|---|---|
10 |
Start Byte | Constant |
C1 |
Source (Motor or Tool) | 0xC1 |
31 |
Command (Write EEPROM) | 0x31 |
00 00 |
Start Address | 0x0000 |
01 |
Length | 1 byte |
FF |
Data to write | 0xFF |
31 |
Checksum | XOR of above |
10 2B 04 01 2E
| Byte(s) | Description | Value |
|---|---|---|
10 |
Start Byte | Constant |
2B |
Source (Battery or Motor) | 0x2B |
04 |
Command (Status Response) | 0x04 |
01 |
Payload (Assist Level = Low) | 0x01 |
2E |
Checksum | XOR |
10 C1 30 00 00 01 00 30
| Byte(s) | Description | Value |
|---|---|---|
10 |
Start Byte | Constant |
C1 |
Source (Motor or Tool) | 0xC1 |
30 |
Command (Read EEPROM) | 0x30 |
00 00 |
Start Address | 0x0000 |
01 |
Length | 1 byte |
00 |
Padding / reserved | 0x00 |
30 |
Checksum | XOR of above |
10 21 03 01 23
| Byte(s) | Description | Value |
|---|---|---|
10 |
Start Byte | Constant |
21 |
Source (Display) | 0x21 |
03 |
Command (Request Data) | 0x03 |
01 |
Payload: Assist Level | e.g. Low
|
23 |
Checksum | XOR |
Let's take the example message:
10 C1 08 11 22 33 08 AA
Breakdown:
| Byte(s) | Description | Value |
|---|---|---|
10 |
Start Byte | Constant |
C1 |
Source Address (Motor) | 0xC1 |
08 |
Command Code (Handshake Request) | 0x08 |
11 22 33 |
Handshake Payload (token/seed) | Variable |
08 |
Echoed Command Code or Sub-ID | 0x08 |
AA |
Checksum | XOR of bytes |
The exact function of each byte can vary slightly by command. Many commands echo their ID later in the payload for validation.
π‘ Checksum is typically XOR of all previous bytes in the packet. Incorrect checksum causes the receiver to ignore the packet.
Basic Protocol Format:
[Start Byte] [Source] [Target] [Command] [Data ...] [Checksum]
Message Types:
-
0x00: Alive ping (no payload) -
0x01: Command with payload -
0x02: Response with payload -
0x03: Response no payload (ack) -
0x04: Status check (no payload)
Example Command:
-
10 C1 21 22 01 6F: Sent from Battery to Display, requesting status/interaction.
Behavior Observed:
- Alive messages (0x00) rotate between components.
- Motor initiates handshake via command
0x08, expects response containing specific byte sequence. - Battery logs and reuses data from previous shutdown for validation.
- Display does not send alive pings, only responses.
-
Motor-to-Battery handshake:
- Motor sends
0x08request with values. - Battery replies with
0x2B 08 ...using values previously received in0x09at shutdown.
- Motor sends
-
Assist levels:
- User button presses are logged as changes in specific bytes (e.g., 00 -> 01, 02, 03).
- Motor doesnβt respond unless full interaction between battery and display is valid.
- Some data may be signed or encrypted.
- Byte 5 of some messages acts like a rolling counter.
- Potential use of serialized handshake token or challenge-response for security.
Below is a complete list of components typically used to build a circuit board for emulating the ION system using an ESP32:
- 1Γ ESP32 microcontroller board (e.g. DevKitC, WROOM, ESP32 SuperMini, or TTGO)
- 1Γ USB-to-UART bridge (if not integrated)
- 1Γ DC-DC buck converter (26V to 5V or 3.3V output)
- 1Γ Fuse (e.g. 1A fast-blow)
- 1Γ TVS diode (e.g. SMAJ26CA)
- 1Γ 220kΞ© resistor
- 1Γ 47kΞ© resistor
- 1Γ NPN transistor (e.g. BC547 or 2N3904 for TX gating)
- 1Γ 10kΞ© resistor (for transistor base)
- Optional: 1Γ logic-level MOSFET (for active bus driving or isolation)
- 1Γ Diode (1N4148 or similar for TX protection)
- 1Γ 1kΞ© series resistor (TX line isolation)
- 1Γ 10kΞ© pull-up resistor (optional for RX stability)
- 1Γ 2- or 3-pin screw terminal or JST for bus and power
- 1β2Γ LEDs with current-limiting resistors
- 1Γ Push button (optional for reset/mode)
-
1Γ SD card module (for UART log storage)
-
1Γ OLED or TFT display (TTGO-style boards)
-
1Γ 3.3V LDO regulator (if not onboard)
-
Microcontroller: ESP32 preferred (UART + Wi-Fi for debugging).
-
Sniffing tools:
- USB-UART Converter (e.g., FTDI, CP2102).
- Resistor divider for voltage level matching (e.g., 220k to 47k).
-
Power Supply: 26V DC (lab supply or battery).
-
Optional: Oscilloscope to visualize waveform/timing.
-
Gather Parts
- Order ESP32 board (DevKit, TTGO, ESP32 SuperMini, etc.)
- Acquire resistors, transistor, diode, connectors, etc. (see Hardware Requirements)
-
Design Circuit
- Draw schematic with UART tap, TX buffer, and power handling
- Include voltage divider and optional MOSFET/LEDs
-
Breadboard Prototype (Optional)
- Assemble basic working version for testing
- Validate signal levels and pinout
-
Build PCB (optional)
- Create PCB layout in KiCad or Fritzing
- Order from manufacturer (e.g. JLCPCB, OSH Park)
-
Build the Firmware
- Clone example projects from GitHub (e.g., void-spark/ion1)
- Implement UART receive and response logic in Arduino or PlatformIO
- Define packet formats, timing, and assist level behavior
- Incorporate startup handshake and alive pings
- Use logging to fine-tune responses during interaction with motor/display
-
Flash Firmware
- Use Arduino IDE or PlatformIO to upload UART sniffer/emulator code
- Modify handshake logic if needed
-
Connect to Bike
- Tap into display/motor wiring via bus line
- Use lab power supply or real battery to simulate voltage
-
Debug & Monitor
- Use HTerm, Serial Monitor, or Telnet for log output
- Adjust timing/response based on feedback
-
Optimize & Share
- Log packets, refine firmware
- Publish schematic/code for community use
-
Gather Parts
- Order ESP32 board (DevKit, TTGO, etc.)
- Acquire resistors, transistor, diode, connectors, etc. (see Hardware Requirements)
-
Design Circuit
- Draw schematic with UART tap, TX buffer, and power handling
- Include voltage divider and optional MOSFET/LEDs
-
Breadboard Prototype (Optional)
- Assemble basic working version for testing
- Validate signal levels and pinout
-
Build PCB (optional)
- Create PCB layout in KiCad or Fritzing
- Order from manufacturer (e.g. JLCPCB, OSH Park)
-
Flash Firmware
- Use Arduino IDE or PlatformIO to upload UART sniffer/emulator code
- Modify handshake logic if needed
-
Connect to Bike
- Tap into display/motor wiring via bus line
- Use lab power supply or real battery to simulate voltage
-
Debug & Monitor
- Use HTerm, Serial Monitor, or Telnet for log output
- Adjust timing/response based on feedback
-
Optimize & Share
- Log packets, refine firmware
- Publish schematic/code for community use
The ESP32 is used as the main controller to both emulate and sniff bus traffic. It provides flexibility, real-time processing, and wireless debugging options.
The following ESP32 boards are commonly used in the ION emulator community:
- ESP32 DevKitC (Espressif Official): Standard, breadboard-friendly.
- ESP32 NodeMCU / DOIT Dev Boards: Common with CP2102/CH340 chips.
- TTGO T-Display / T-Beam: Adds onboard screen or GPS; more complex.
- ESP32-WROOM modules: Embedded in custom PCBs or permanent installations.
- Estardyn ESP32-C3 SuperMini: Very small ESP32-C3 board.
Some users experiment with ESP32-C3 boards. These are RISC-V based, low-power versions of ESP32s. They can work, but with limitations:
Pros:
- Small, power-efficient
- Cheap and widely available
Cons:
- Only one UART (usually used for USB logging)
- Requires software serial (bit-banging) or UART reassignment
Tested ESP32-C3 Boards:
- LILYGO TTGO T-OI Plus (C3)
- ESP32-C3-DevKitM-1
- WEMOS D1 Mini ESP32-C3
- M5Stamp C3
- Ai-Thinker ESP-C3-32S Kit
- Estardyn ESP32-C3 SuperMini
β οΈ Tip: Use GPIO20 and GPIO21 for software UART on some C3 boards, and validate stable operation at 9600 baud.
The ESP32 is used as the main controller to both emulate and sniff bus traffic. It provides flexibility, real-time processing, and wireless debugging options.
- GPIO17 = RX (connected to voltage divider tap)
- GPIO16 = TX (connected to motor bus via diode or resistor)
- 3.3V and GND from ESP32 to voltage divider
To safely read the 26V bus:
26V Bus
|
[220k]
|
|------> RX (ESP32 GPIO17)
|
[ 47k ]
|
GND
- Written in C++ or Arduino framework
- Reads raw UART data
- Decodes frames based on ION protocol format
- Can replay captured handshake sequences
- Optionally logs to serial monitor or SD card
#define RX_PIN 17
#define TX_PIN 16
void setup() {
Serial.begin(115200);
Serial2.begin(9600, SERIAL_8N1, RX_PIN, TX_PIN);
}
void loop() {
if (Serial2.available()) {
uint8_t b = Serial2.read();
Serial.printf("%02X ", b);
}
}- Use Wi-Fi + Telnet for real-time monitoring
- Use hardware serial (Serial1/Serial2) for clean separation
- Add CRC checks to validate message structure
- Emulate display and battery behavior to trigger motor startup
- Intercept button presses and respond accordingly
- Forward or generate valid speed signals
- HTerm: Terminal software for capturing raw UART traffic.
- Python: Used for parsing and analyzing hex dumps.
-
Excel Analysis: Shared spreadsheet (
ION UART communication.xlsx) with color-coded logs. - GitHub Projects:
BowTool is a diagnostic and configuration utility originally developed by or for the ION system to read out and configure battery firmware parameters.
- A Windows-based tool (often distributed as
bowtool.exe) used by professionals and enthusiasts to interface with ION battery packs. - It communicates through an RS232 serial port or USB-UART adapter.
- Offers access to internal BMS data, EEPROM contents, fault codes, and battery history.
- Read battery cycle count, temperature data, and charge levels.
- Check error codes and reset soft faults (Like E0003).
- Identify firmware versions and hardware IDs.
- Extract EEPROM data for reverse engineering.
- Connect the ION battery to your PC using a USB-to-Serial interface.
- Set up proper COM port settings (usually 9600 baud, 8N1).
- Launch
bowtool.exe(if available). - Choose the appropriate port and begin scanning.
- Browse or export battery info from the interface.
The ION system has evolved across two generations of battery technology, both with unique characteristics and challenges for emulation:
- Used in early ION bikes (e.g. PMU1).
- Typically 24-cell packs (1.2V nominal per cell).
- Nominal pack voltage: 28.8V, fully charged: ~34V.
- Heavy and less energy dense.
- Includes simple internal BMS with temperature sensing and slow charging logic.
- Known to slowly degrade and self-discharge over time.
- Original BMS may contain EEPROM data required for handshake with the motor controller.
- Used in later ION bikes (e.g. PMU2).
- Typically 7S or 8S configuration (3.6β4.2V per cell).
- Nominal pack voltage: 25.2V (7S) or 29.6V (8S).
- Lighter and higher energy density.
- More sophisticated BMS with cell balancing, over/under-voltage protection, and history tracking.
- Often contain firmware that validates the pack to the system using serial numbers or tokens.
- Voltage matching is critical β emulate a realistic range for the motor to accept power.
- Handshake data must be preserved or mimicked between shutdown/startup.
- Thermal sensors and current sense lines may be required for full functionality.
- Older NiMH-based bikes may be easier to spoof, but newer Li-Ion systems may perform firmware checks.
π οΈ Tip: Use an adjustable lab power supply or a custom-built Li-Ion pack to mimic NiMH voltages for testing without the original battery.
Working with e-bike battery systems, especially custom-built Li-Ion packs, introduces electrical and thermal risks. Always take the following precautions:
- Use a BMS (Battery Management System) with overcurrent and undervoltage protection.
- Include fuses in series with the power line to prevent short-circuit damage.
- Avoid working on live systems; disconnect the pack before rewiring or flashing.
- Do not charge unbalanced Li-Ion packs without protection.
- Monitor temperature and current during charge/discharge cycles.
The motor controller provides several outputs and feedback lines, including:
- Speed signal (Hall-based): Pulses that can be measured by the display.
- Assist signal (PWM or data): Used internally by the motor logic.
- Voltage sense: The battery may monitor back EMF from the motor.
For full emulation, it's useful to understand and, if needed, simulate these feedback lines.
| Feature | CU2 (Round) | CU3 (Rectangular) |
|---|---|---|
| Display Shape | Circular | Rectangular |
| Serial Number Binding | Sometimes ignored | Often strictly checked |
| Protocol Behavior | Identical (mostly) | Identical |
| Assist Button Mapping | Simple up/down | Same, but layout varies |
| Compatibility | High | May require valid token |
- Use HTerm or similar serial logging tools that support timestamping.
- Include idle time or user actions in your annotations.
- Label source (e.g. Motor β Battery) for each packet.
- Use spreadsheets to sort and highlight repeating patterns.
- Save logs in raw hex + ASCII format for decoding tools.
| Feature | Required? | Notes |
|---|---|---|
| Voltage Emulation | β | Use lab supply or BMS-controlled pack |
| Handshake Token Retention | β | Needed between shutdown/startup cycles |
| Serial Number Emulation | Required for CU3 and some motors | |
| Assist Level Request/Response | β | Required to enable motor support |
| Alive and Ping Message Rotation | β | Maintains sync between components |
| Log File Generation | Optional | Helps with debugging and development |
Below is a simplified state diagram showing the flow of communication during system operation:
[Startup]
β
[Battery β Display] β Alive Ping
β
[Motor β Battery] β Handshake Init (0x08)
β
[Battery β Motor] β Handshake Reply
β
[Display β Battery] β Assist Level Request
β
[Motor Engaged] if valid token + assist level
π‘ This loop continues as long as the system is powered and correctly emulated.
For flashing firmware onto ESP32 (DevKit, WROOM, or C3):
- Use ESP32 board manager from Espressif
- Select the right board (e.g., ESP32 Dev Module or ESP32C3 Dev Module)
- Upload via USB
- Create
platformio.iniwith appropriate board - Use built-in terminal to build/flash
- Example config:
[env:esp32dev]
platform = espressif32
board = esp32dev
framework = arduino
monitor_speed = 115200
Q: Why doesnβt the motor engage even after startup? A: You may be missing a valid handshake or assist level response. Also verify the serial number logic if using CU3.
Q: Do I need to emulate both display and battery? A: In some cases, yes β especially if the motor expects full traffic and assist commands from the display.
Q: Can I use USB UART adapter instead of ESP32? A: For sniffing/logging β yes. For real-time emulation β no, you need something programmable like ESP32.
Q: My bike turns off after 5 seconds β what now? A: It may expect continuous alive pings or motor-side acknowledgment. Check for byte 0x00 rotations.
Join the ongoing conversation, ask questions, or contribute your findings:
- Pedelec-Forum.de - ION Thread
- GitHub Discussions (in respective repos)
- Community forks and firmware testing
If there's interest, a Discord or Matrix room could be started!
| Term | Definition |
|---|---|
| BOW | Battery On-Wire; the UART-based communication protocol used in ION systems |
| CU2/CU3 | Types of ION displays (CU2 = round, CU3 = rectangular) |
| ESP32 | Microcontroller used for emulation and logging |
| BMS | Battery Management System controlling charge, discharge, and safety |
| UART | Universal Asynchronous Receiver-Transmitter (serial communication) |
| PMU1/2 | Battery models used in older/newer ION systems (NiMH vs. Li-Ion) |
Last updated: April 9, 2025
This documentation is released under the Creative Commons Attribution 4.0 International (CC BY 4.0) license. You are free to share and adapt the material, as long as appropriate credit is given.
- Partial emulation of display-battery communication is working.
- Display responds to button input and updates assist level.
- Motor provides speed feedback but does not engage support.
- Handshake logic between display and motor is likely not fully reproduced.
- ESP32-based emulator developed for testing.
- Motor may require cryptographic validation of messages.
- Lack of manufacturer software makes configuration difficult.
- Display serial number appears to be verified by the motor.
- Potentially unique keys per device chain (Display β Battery β Motor).
- CU2 vs CU3 display compatibility dependent on battery firmware.
- GitHub repos from Infant, Thi3rryzz, and stancecoke
- Pedelec-Forum thread: "Zweiter FrΓΌhling fΓΌr ION-Antrieb"
- Python scripts for decoding UART logs
- EEPROM and microcontroller fuse analysis
- Logs from multiple bike setups and conditions
Please join the effort! Share logs, reverse-engineering tools, and insights.
- Fork and clone the GitHub repo.
- Submit logs from working ION systems.
- Help decode unknown messages.
- Test emulator setups.
- mooiweertje β documentation review, insights, and testing support
- hochsitzcola β extensive logging, protocol analysis, ESP32 firmware
- Mike747 β hardware testing and community feedback
- InfantEudora β original sparta_ion repository and reverse-engineering insights
- Thi3rryzz β GitHub contributions and refinement
- void-spark β Python log decoders, CU3 compatibility tests
- TyraMisoux β deep technical insights into AVR bootloaders and system security
- Everyone on the Pedelec-Forum who shared logs, ideas, and support
This documentation is an evolving community effort. Contributions, corrections, and new discoveries are welcome!