The Stock Firmware's Limitations

The default firmware on many commercial e-scooters, while functional, often imposes limitations. These can include restricted speed caps, predefined acceleration curves, and a lack of advanced diagnostic information. For enthusiasts and developers, these limitations can be frustrating, hindering the exploration of the scooter's full potential. The proprietary nature of these firmwares also means that users are largely at the mercy of the manufacturer for any updates or feature additions, which are often infrequent or non-existent.

This project began with a desire to break free from these constraints. The goal was not just to bypass existing limits but to gain complete control over the scooter's core operations. This level of control allows for fine-tuning performance, implementing custom safety features, and even integrating new functionalities not originally envisioned by the manufacturer. The choice of Rust as the reimplementation language was driven by its focus on safety, performance, and low-level control, making it an ideal candidate for embedded systems development where reliability is paramount.

E-scooter mainboard with custom firmware flashing setup

Reverse Engineering the E-Scooter's Core

The initial phase involved meticulous reverse engineering of the existing firmware. This process typically starts with identifying the microcontroller unit (MCU) on the scooter's mainboard. Once identified, the next step is to find ways to extract the original firmware. This often requires specialized hardware, such as a programmer or a logic analyzer, to interface with the MCU's debug ports (like SWD or JTAG) or to read directly from its flash memory. The extracted binary is then subjected to disassembly and static analysis. Tools like Ghidra or IDA Pro are invaluable here, helping to deconstruct the machine code into human-readable assembly language. This allows for the identification of key functions, data structures, and communication protocols.

Understanding the communication between different components is crucial. E-scooters typically have a mainboard, a battery management system (BMS), and a display unit, all communicating over a bus, often I2C or UART. Analyzing these communication packets, sometimes by intercepting them with a logic analyzer during normal operation, reveals how commands are sent and data is received. For instance, understanding the specific byte sequences that trigger acceleration or braking is essential for reimplementing those functions correctly. This phase is akin to deciphering an alien language, piecing together meaning from raw symbols and patterns.

Rebuilding the Firmware in Rust

With a solid understanding of the original firmware's logic and the scooter's hardware interfaces, the next step is to rewrite the firmware. Rust's suitability for embedded systems comes from its zero-cost abstractions, memory safety guarantees without a garbage collector, and excellent tooling. The process involves setting up a Rust development environment for the specific target MCU. This often requires using a Rust Embedded HAL (Hardware Abstraction Layer) and potentially writing custom drivers for specific peripherals if they are not covered by existing crates.

The core of the reimplementation involves translating the identified functionalities from the disassembled firmware into Rust code. This includes:

  • Motor Control: Implementing the Pulse Width Modulation (PWM) signals to control motor speed and torque based on throttle input. This requires careful timing and understanding of the motor controller's requirements.
  • Battery Management Interface: Reading battery voltage, current, and state of charge from the BMS. This data is critical for safe operation and for displaying accurate battery information.
  • User Interface: Handling input from the throttle and brake levers, and sending data to the display unit (e.g., speed, battery level, error codes).
  • Safety Features: Reimplementing or enhancing safety checks, such as over-current protection, over-voltage protection, and thermal monitoring.

The surprising detail here is not just the complexity but the elegance with which Rust can manage these low-level operations. The language's features, like `unsafe` blocks for hardware interaction and robust error handling mechanisms, allow developers to write code that is both performant and auditable. Building a complete firmware from scratch, even with the aid of reverse engineering, is a significant undertaking, demanding patience and a deep understanding of both software and hardware.

Testing and Deployment

Once the Rust firmware is compiled, it needs to be flashed onto the e-scooter's MCU. This is typically done using the same hardware interface used for extraction, like an SWD debugger. The first boot-up of custom firmware is always a moment of tense anticipation. Initial testing focuses on basic functionality: does the motor spin? Do the brakes engage? Is the display showing sensible data? Iterative testing and debugging are essential. The ability to log output or use a debugger connected to the scooter's board is invaluable for diagnosing issues. For instance, if the motor isn't responding as expected, examining the PWM output signals with an oscilloscope can reveal problems with the control loop implementation.

The real power of custom firmware becomes apparent when testing enhanced features. This could involve implementing a 'boost' mode that temporarily allows for higher acceleration, or a 'cruise control' feature. Each new feature requires careful calibration and testing to ensure it operates reliably and safely under various conditions. The process is not just about making the scooter faster or more powerful; it's about creating a more responsive, informative, and personalized riding experience. This project demonstrates that with sufficient technical skill and the right tools, even complex embedded systems can be understood, modified, and improved.