<< Back to WORTHLESS.DEV
FARDRIVER BLE PROTOCOL
Complete Technical Documentation

CONTROLDM2E7 Binary Protocol Analysis

PROJECT OVERVIEW
Status: Complete reverse engineering of Fardriver CONTROLDM2E7 BLE protocol
Method: C# decompilation + live protocol capture
Parameters: 300+ documented with full conversion algorithms

This documentation covers the complete BLE protocol used by Fardriver motor controllers. The protocol was reverse-engineered through systematic analysis of the decompiled C# ProControlPage source code combined with live packet captures.

What's documented:

SAFETY WARNING: This protocol controls high-power motor systems. Improper use can cause equipment damage, injury, or death. Always implement proper safety systems and validation.
BINARY PROTOCOL SPECIFICATION
BLE Connection Details:
Parameter Value Notes
Device Name CONTROLDM2E7 Advertised BLE name
Service UUID 0000ffe0-0000-1000-8000-00805f9b34fb Standard Nordic UART service
Characteristic UUID 0000ffec-0000-1000-8000-00805f9b34fb Single bidirectional characteristic
Write Method writeWithoutResponse No acknowledgment, responses via notifications
MTU Size 20 bytes All packets fit in single MTU
Packet Structure (16 bytes total):
[HEADER][ADDRESS][DATA_PAYLOAD][CRC16] HEADER (1 byte): 0xAA (fixed) ADDRESS (1 byte): Parameter address (0x00-0xFF) DATA_PAYLOAD (12 bytes): Parameter data CRC16 (2 bytes): Custom CRC validation [CRC_HI][CRC_LO]
Command Types:
Type Format Purpose
Read Command AA [ADDR] [ADDR×13] [CRC_HI] [CRC_LO] Request parameter value
Response AA [ADDR] [DATA_BYTES] [CRC_HI] [CRC_LO] Return requested data
Write Command AA [C0+LEN] [ADDR] [ADDR] [DATA...] [CRC] Set parameter value
Write Response AA [ADDR] [STATUS] [RESERVED...] [CRC] Confirm write operation
CRC16 Algorithm (Custom Fardriver Implementation):
Initial Values: crc_hi = 0x3C, crc_lo = 0x7F FOR each byte in packet[0] to packet[13]: index = (crc_hi ^ byte) & 0xFF crc_hi = (crc_lo ^ crc_table_hi[index]) & 0xFF crc_lo = crc_table_lo[index] packet[14] = crc_hi packet[15] = crc_lo
PARAMETER ADDRESS MAP
300+ parameters organized into functional groups. Each address contains 16-byte packet with specific field layouts.
Real-time Telemetry (High Priority):
Address Parameter Data Format Update Rate
0xE2 RPM + Status Block RPM(2-3), Gear(4), Status(5), Modulation(6-7), Errors(8-11) 10Hz continuous
0xE8 Power Telemetry Voltage(2-3)÷10, Current(4-5)÷4, Temps(6-9), Throttle(10-11)÷20 10Hz continuous
0xEE 3-Phase Currents Phase_A(2-3)÷4, Phase_B(4-5)÷4, Phase_C(8-9)÷4 (signed) 5Hz continuous
0xFA Switch Status Brake_Bits(4), EABS_Bits(5), Switch_Matrix(6-13) 20Hz polling
Motor Configuration (0x00-0x2F):
Address Parameter Data Format Range/Notes
0x12 Motor Specs Block LD(2-3), AlarmDelay(4-5), PolePairs(6), MaxSpeed(8-9), RatedPower(10-11), RatedVoltage(12-13)÷10 Multiple motor parameters packed in single address
0x18 Motor Speed & Current Block RatedSpeed(2-3), MaxLineCurr(4-5)÷4, FollowConfig(6[0-1]), ECOConfig(6[2-3]), WeakA(6[4-5]), LQ(8-9), BattRatedCap(10-11), IntRes(12-13) Speed ratings and current limits with configuration bits
0x1E Status Flags & Protection FwReRatio(2-3), LowVolProtect(4-5)÷10, CustomCode0(6), CustomCode1(7), RelayDelay(8-9), ModelDate(10-12), ModelTime(13-15) Contains 16 status flag bits in RelayDelay field - see Status Flags section
0x24 Voltage Protection Block Minutes(2), Hours(3), HighVolProtect(4-5)÷10, CustomMaxLineCurr(6-7)÷4, CustomMaxPhaseCurr(8-9)÷4, LowSpeed(10-11), BackSpeed(12-13) Overvoltage protection and custom current limits
0x2A Speed Control Block MidSpeed(2-3), Max_Dec(4-5), FreeThrottle(6), MaxPhaseCurr(8-9)÷4, SpeedAnalog(10-11), Max_Acc(12-13) Speed control parameters and acceleration limits
0x30 Current Control Block StopBackCurr(2-3), MaxBackCurr(4-5), LowSpeedLineCurr_cfg(6), MidSpeedLineCurr_cfg(7), LowSpeedPhaseCurr_cfg(8), MidSpeedPhaseCurr_cfg(9), BlockTime(10-11), SpdPulseNum(12-13) Regenerative braking and speed-dependent current limits
0x63 Advanced Parameters Block ENMaxLineCurr(2-3), ENMaxPhaseCurr(4-5), MOTORDIA(6), TempCoeff(8-9) Enhanced current limits and motor temperature coefficient
Advanced Configuration (0x60-0x9F):
Address Parameter Data Format Range/Notes
0x69 Boost & Park Control BstXhBcP(2-3), FwReSdhSdl(4-5), ChgFdSeatVol(6-7), ParaIndex(12), SpecialCode(13) Boost timing and park mode configuration
0x75 General Parameters GPara0(10-11) contains multiple bit-packed settings EnModify bits in GPara0[12-13], BlueKey flag in GPara0[15]
0x81 Throttle Configuration ThrottleInsert(10-11), various throttle response parameters Throttle insertion detection and response settings
Status Flags Decoding (Address 0x1E RelayDelay Field):
Bit Position Flag Name Description C# Field Reference
0 BCEnable Side Stand Enable (RelayDelay & 1)
1 SeatEnable Seat Sensor Enable (RelayDelay >> 1 & 1)
2 PEnable Park Mode Enable (RelayDelay >> 2 & 1)
3 AutoBackP Auto Return to Park Enable (RelayDelay >> 3 & 1)
4 CruiseEnable Cruise Control Enable (RelayDelay >> 4 & 1)
5 EABSEnable Electronic ABS Enable (RelayDelay >> 5 & 1)
6 TuixingEnable Push Assist Enable (RelayDelay >> 6 & 1)
7 ForseTheft Force Anti-theft Mode (RelayDelay >> 7 & 1)
8 OverSpeedAlarm Overspeed Alarm Enable (RelayDelay >> 8 & 1)
9 ParkDisableBreak Brake Won't Release Park (RelayDelay >> 9 & 1)
10 RememberGear Gear Memory Enable (RelayDelay >> 10 & 1)
14 BackEnable Reverse Function Enable (RelayDelay >> 14 & 1)
15 RelayDelay1S 1 Second Relay Delay (RelayDelay >> 15 & 1)
Data Conversion Matrix:
Parameter Type Raw Format Conversion Example
Battery/System Voltage uint16 little-endian raw_value ÷ 10 720 → 72.0V
Line/Phase Current uint16/int16 little-endian raw_value ÷ 4 400 → 100.0A
Throttle Voltage uint16 little-endian raw_value ÷ 20 90 → 4.50V
Temperature int16 little-endian (signed) raw_value (direct) 50 → 50°C, -10 → -10°C
Speed Ratios uint8 (raw_value × 100) ÷ 128 96 → 75.0%
Timing Parameters uint16 little-endian raw_value ÷ 500 1000 → 2.0s
REAL-TIME TELEMETRY PARSING
RPM & Status Block (0xE2):
# Example: AA E2 40 0C 04 00 80 03 25 00 33 00 4A 00 1B 2C Bytes 2-3: RPM = (0x0C << 8) | 0x40 = 3136 RPM Byte 4: Gear = 4 (current gear setting) Byte 5: Status flags = 0x00 (no errors) Bytes 6-7: Modulation = (0x03 << 8) | 0x80 = 896 (PWM level) Bytes 8-11: Error flags (bit field encoding)
Power Telemetry (0xE8):
# Example: AA E8 D0 02 90 01 32 00 28 00 5A 00 FF FF 8A 7B Bytes 2-3: Voltage = 720 ÷ 10 = 72.0V Bytes 4-5: Current = 400 ÷ 4 = 100.0A Bytes 6-7: Temp1 = 50°C (controller) Bytes 8-9: Temp2 = 40°C (motor/ambient) Bytes 10-11: Throttle = 90 ÷ 20 = 4.5V
3-Phase Currents (0xEE):
# Example: AA EE 64 00 C8 00 00 00 90 01 32 00 FF FF A5 B3 Bytes 2-3: Phase A = 100 ÷ 4 = 25.0A Bytes 4-5: Phase B = 200 ÷ 4 = 50.0A Bytes 8-9: Phase C = 400 ÷ 4 = 100.0A (Signed values: positive = forward, negative = regen)
PARAMETER WRITE SYSTEM
CRITICAL: Writing ≠ Saving! Parameters written to RAM are temporary and lost on power cycle. You must execute save commands to make changes permanent.
Write Command Format:
# Structure: [AA][LENGTH_CODE][ADDRESS][ADDRESS][DATA][CRC16] LENGTH_CODE = 0xC0 + total_data_length Example: 0xC4 = 4 bytes (address + address + 2 data bytes) # Write MaxSpeed = 5000 RPM to address 0x15: AA C4 15 15 88 13 [CRC_HI] [CRC_LO] 0x1388 = 5000 in little-endian format
Save Categories & Commands:
# Parameter Save Categories (discovered through reverse engineering): Motor Config (0x10-0x1F): AA C0 A0 5A A5 3C C3 F0 0F 55 AA 00 00 [CRC] Protection (0x20-0x2F): AA C0 A1 A5 5A C3 3C 0F F0 AA 55 00 00 [CRC] Throttle/Control (0x30-0x3F): AA C0 A2 3C C3 5A A5 F0 0F 55 AA 00 00 [CRC] PID Parameters (0x00-0x0F): AA C0 A3 C3 3C A5 5A 0F F0 AA 55 00 00 [CRC] Save operations take 2-5 seconds and MUST complete successfully!
Complete Write+Save Process:
1. Read current parameter (for backup) 2. Write new value to RAM 3. Verify write success (status = 0x00) 4. Test new parameter behavior 5. Execute appropriate save command for category 6. Wait for save response (up to 5000ms) 7. Verify persistence after save
PROTOCOL DESIGN PROBLEMS
This protocol is a masterclass in how NOT to design a communication protocol. Here are the major problems discovered during reverse engineering:
Inconsistent Data Scaling:
Parameter Scale Factor Problem
Battery Voltage ÷10 Different from throttle voltage (÷20) - no consistency
Current Values ÷4 Arbitrary choice, 0.25A resolution too coarse for low currents
Speed Ratios ×100÷128 Results in percentages that don't align with decimals
Timing Values ÷500 Completely arbitrary, 0.002s resolution is oddly specific
Address Space Chaos:
# No logical organization whatsoever: Motor Configuration scattered across: 0x12: Kitchen sink of multiple params 0x14: Just pole pairs (why separate?) 0x15: Rated speed (why not with 0x12?) 0x16: Max speed (different from rated speed?) Protection Settings randomly placed: 0x1A: Low voltage protect 0x1B: Low voltage restore (at least next to 0x1A) 0x20: High voltage protect (WHY JUMP TO 0x20?!) 0x7A: Motor temp protect (completely different area) 0x7C: Controller temp protect (no organization)
CRC Algorithm Stupidity:
# Instead of using standard CRC16-CCITT, they invented their own: STANDARD CRC16-CCITT: Used by thousands of protocols FARDRIVER SPECIAL: Custom initial values (0x3C7F), custom polynomial RESULT: ✗ Cannot use standard CRC libraries ✗ Must implement custom lookup tables ✗ No interoperability ✗ Debugging is harder ✓ Achieves absolutely nothing beneficial
Packet Structure Inconsistencies:
# Different formats for no reason: READ COMMANDS: Address repeated 13 times (bytes 1-13) WRITE COMMANDS: Address repeated only twice (bytes 2-3) RESPONSES: Address appears once (byte 1) # Mixed data types in single parameters: Address 0x1E contains BOTH: - RelayDelay timing value (milliseconds) - Status flags (boolean bits) You cannot change timing without affecting flags! You cannot change flags without affecting timing!
Error Handling Disasters:
# Error responses are about as reliable as a broken clock: DOCUMENTED STATUS CODES: 0x00 = Success 0x01 = Validation Failed 0x02 = Read-only Parameter 0xFF = General Failure ACTUAL BEHAVIOR: ✗ Controller returns 0x00 even when write fails ✗ Some invalid writes are silently ignored ✗ Some valid writes are randomly rejected ✗ Controller sometimes just doesn't respond at all ✗ No way to detect stale data
The "Special" Addresses:
# Some addresses behave completely differently because reasons: ADDRESS 0x08: Sending read to 0x08 returns data from 0x12 Why? Nobody knows! Just more special case handling. ADDRESS 0x0E: Sending read to 0x0E returns data marked as 0x0C Response claims to be from different address than requested. TELEMETRY: Some update continuously, others only on request No pattern or logic to which is which. WRITE vs READ: Some addresses can be written but not read Some can be read but not written Trial and error is the only way to find out
The Result: A protocol that works, but barely. Every implementation is a house of cards built around countless inconsistencies and design failures. The fact that anyone successfully implements this protocol is a testament to human persistence and the power of reverse engineering.
SAFETY DISCLAIMER
SAFETY DISCLAIMER: Motor controllers operate high-power systems that can cause injury or death. Always implement proper safety systems, parameter validation, and emergency stops. Test in controlled environments. The authors assume no responsibility for damages or injuries.