Discovery 3 CAN bus sniffing series
- Part 1: OBD pins, incorrect wire colours, and the DIY cable trap
- Part 2: Bringing up the LilyGO ESP32 CAN board
- Part 3: Debugging when CAN frames stay at zero
- Part 4: Corrected wiring and the first real CAN frames
- Part 5: Finding the first likely suspension height values
Important correction: the supplied OBD pigtail colour chart was inaccurate. On this cable, continuity testing showed OBD pin 6 = Green, OBD pin 14 = Green/White, and OBD pin 5 = Light Blue.
Part 4 was the point where the Discovery 3 CAN bus sniffer finally started receiving real frames. The next job was to stop looking at a wall of hex and start taking labelled captures tied to a known vehicle action.
For the first pass, I chose the air suspension height states because they are easy to command from the cabin and have clear physical positions: access height, normal height, and off-road height. That gives the parser something useful to compare. If a byte moves consistently from access to normal to off-road, it is at least worth investigating.
The capture set
The working setup from Part 4 stayed the same:
Green -> CAN-H
Green/White -> CAN-L
Light Blue -> GND
TX=GPIO27
RX=GPIO26
500 kbit/s
I then collected both stable captures and transition captures. The stable captures were taken once the vehicle had settled at a known height. The transition captures covered the movement between states.
The stable files were:
access-height.txt
normal-height.txt
offroad-height.txt
The transition files were:
lowering-offroad-to-normal.txt
lowering-normal-to-access.txt
The stable capture summaries were:
access-height.txt frames=8841 unique_ids=27 duration_ms=46704
normal-height.txt frames=1820 unique_ids=26 duration_ms=8759
offroad-height.txt frames=9630 unique_ids=27 duration_ms=46514
The normal-height capture is shorter than the other two, so this is not enough data to call anything decoded. It is enough to find the first strong candidate.
Using d3can.py candidates
I added a small helper command to compare labelled captures:
python .\d3can.py candidates normal=.\normal-height.txt access=.\access-height.txt offroad=.\offroad-height.txt --limit 15
The idea is simple: group frames by CAN ID, inspect byte and word fields, then rank fields whose mean values separate between the labelled states. This does not prove what a signal means. It only gives a shortlist of frames worth watching more closely.
One of the strongest results was extended CAN ID:
0x12E9E6A0
This ID was already present in the first successful passive capture from Part 4, but the labelled suspension captures made it much more interesting.
The payloads that stand out
The stable samples for 0x12E9E6A0 were:
Access: 00 95 00 99 02 90 00 9D
Normal: 00 CA 00 CA 06 BE 00 CC
Offroad: 00 F6 00 FA 08 EF 00 F7
Four bytes immediately stand out: bytes 1, 3, 5, and 7 if counted from zero. They all rise as the suspension moves upward:
Byte 1: Access=0x95 Normal=0xCA Offroad=0xF6
Byte 3: Access=0x99 Normal=0xCA Offroad=0xFA
Byte 5: Access=0x90 Normal=0xBE Offroad=0xEF
Byte 7: Access=0x9D Normal=0xCC Offroad=0xF7
In decimal, that is:
Byte 1: 149 -> 202 -> 246
Byte 3: 153 -> 202 -> 250
Byte 5: 144 -> 190 -> 239
Byte 7: 157 -> 204 -> 247
That monotonic movement is exactly the sort of pattern I was hoping to find. It looks like four height-like values in one eight-byte payload, which makes it a plausible suspension height or corner-height frame.
Why this is only a candidate
There are a few reasons not to overclaim this yet.
- The capture set is still small.
- The normal-height capture is shorter than the access and off-road captures.
- The values have not yet been tied to individual corners.
- The units and scaling are unknown.
- Other nearby or related frames may carry status, target height, calibration, or filtered values.
So the right conclusion is not “decoded”. The right conclusion is: 0x12E9E6A0 is the first strong suspension-height candidate, and bytes 1, 3, 5, and 7 deserve focused testing.
Next tests
The next round should repeat the stable captures and add deliberate corner-specific changes where possible. Useful tests would include:
- Repeat access, normal, and off-road captures in the same order and in reverse order.
- Watch
0x12E9E6A0live during height changes. - Compare transition traces to see whether the four candidate bytes ramp smoothly or step between values.
- Add known load or jack tests only if they can be done safely and without confusing the suspension ECU.
- Look for a matching target-height or status frame near the same events.
The useful shift here is that the project now has a concrete frame to chase. Instead of scanning every ID equally, the next capture session can focus on proving or disproving 0x12E9E6A0.
Leave a Reply