ESP32 ALDL Dashboard
An Android application built using Jetpack Compose to display real-time engine telemetry from a 1986 Pontiac Fiero 2.8L V6 (1227170 ECM). The app connects to a Bluetooth serial device named ESP32-ALDL and decodes a raw binary stream of 27-byte frames containing ECM data packets under the $24 mask specifications.
Technical Specifications
Data Stream Frame Structure
The ESP32 reads the raw 160-baud unidirectional ALDL stream from the ECM and encapsulates it in a 27-byte packet transmitted over Bluetooth SPP.
| Byte Index | Length | Value / Field | Description |
|---|---|---|---|
| 0 - 1 | 2 bytes | AA 55 |
Frame Sync Header (added by ESP32) |
| 2 - 26 | 25 bytes | Raw Data Payload | 1227170 ECM data stream (0-indexed indices 0 to 24) |
Parameter Offsets & Decoding Formulas
The telemetry parameters are parsed from the $24 / $24A ECM mask definitions (24-INT10.ads). In TunerPro's .ads file, the byte numbers are 1-indexed (e.g. btByteNumber = 4 maps to raw payload byte index 3).
1. Primary Sensors & Measurements
| Parameter | Payload Index | Raw Size | Formula / Conversion | Units |
|---|---|---|---|---|
| IAC Position | Index 3 (Byte 4) | 8-bit | Value = Raw |
Steps |
| Coolant Temp | Index 4 (Byte 5) | 8-bit | $C = (Raw \times 0.75) - 40$F = (Raw \times 1.35) - 40 |
°C / °F |
| Vehicle Speed | Index 5 (Byte 6) | 8-bit | Value = Raw (Operation 3) |
MPH |
| MAP | Index 6 (Byte 7) | 8-bit | $Volts = Raw \times 0.019608$kPa = (Raw \times 0.369) + 10.354 |
Volts / kPa |
| Engine Speed | Index 7 (Byte 8) | 8-bit | RPM = Raw \times 25 |
RPM |
| TPS | Index 8 (Byte 9) | 8-bit | Volts = Raw \times 0.019608 |
Volts |
| Integrator (INT) | Index 9 (Byte 10) | 8-bit | Value = Raw |
— |
| O2 Sensor | Index 10 (Byte 11) | 8-bit | mV = Raw \times 4.44 |
mV |
| Battery Voltage | Index 17 (Byte 18) | 8-bit | Volts = Raw \times 0.1 |
Volts |
| BLM | Index 18 (Byte 19) | 8-bit | Value = Raw |
— |
| Rich/Lean Crosses | Index 19 (Byte 20) | 8-bit | Value = Raw |
Crosses |
| Spark Advance | Index 20 (Byte 21) | 8-bit | Degrees = Raw \times 0.351563 |
Degrees |
| EGR Duty Cycle | Index 21 (Byte 22) | 8-bit | Percent = Raw \times 0.392157 |
% |
| Manifold Air Temp (MAT) | Index 22 (Byte 23) | 8-bit | Linear Table Interpolation (see below) | °C / °F |
| Base Pulse Width (BPW) | Index 23-24 (Byte 24-25) | 16-bit | $Raw = (HighByte \ll 8) \vert LowByte$ms = Raw \times 0.015259 |
Milliseconds (ms) |
2. MAT Linear Interpolation Tables
The Manifold Air Temperature (MAT) is read from Index 22. It is mapped to degrees C or F using interpolation curves specified in tables 52 and 53:
- Celsius (
MAT C- Table 52):0->200.0,12->150.0,13->145.0,14->140.0,16->135.0,18->130.0,21->125.0,23->120.0,26->115.0,30->110.0,34->105.0,39->100.0,44->95.0,50->90.0,56->85.0,64->80.0,72->75.0,81->70.0,92->65.0,102->60.0,114->55.0,126->50.0,139->45.0,152->40.0,165->35.0,177->30.0,189->25.0,199->20.0,209->15.0,218->10.0,225->5.0,231->0.0,237->-5.0,241->-10.0,245->-15.0,247->-20.0,250->-25.0,251->-30.0,255->-40.0
- Fahrenheit (
MAT F- Table 53):0->392.0,12->302.0,13->293.0,14->284.0,16->275.0,18->266.0,21->257.0,23->248.0,26->239.0,30->230.0,34->221.0,39->212.0,44->203.0,50->194.0,56->185.0,64->176.0,72->167.0,81->158.0,92->149.0,102->140.0,114->131.0,126->122.0,139->113.0,152->104.0,165->95.0,177->86.0,189->77.0,199->68.0,209->59.0,218->50.0,225->41.0,231->32.0,237->23.0,241->14.0,245->5.0,247->-4.0,250->-13.0,251->-22.0,255->-40.0
3. Stored Fault Trouble Codes
Malfunction Indicator Codes are mapped as bit flags across three specific status bytes:
Codes Byte 1 (Payload Index 11 / Byte 12)
- Bit 7: Code 12 - Crank Sensor / System Check
- Bit 6: Code 13 - O2 Sensor
- Bit 5: Code 14 - Coolant High Temp
- Bit 4: Code 15 - Coolant Low Temp
- Bit 3: Code 21 - TPS Voltage High
- Bit 2: Code 22 - TPS Voltage Low
- Bit 1: Code 23 - MAT Voltage Low
- Bit 0: Code 24 - Vehicle Speed Sensor (VSS)
Codes Byte 2 (Payload Index 12 / Byte 13)
- Bit 7: Code 25 - MAT Voltage High
- Bit 5: Code 32 - EGR System
- Bit 4: Code 33 - MAP Sensor High
- Bit 3: Code 34 - MAP Sensor Low
- Bit 2: Code 35 - IAC Position
- Bit 0: Code 42 - Electronic Spark Timing (EST)
Codes Byte 3 (Payload Index 13 / Byte 14)
- Bit 7: Code 43 - Electronic Spark Control (Knock Sensor)
- Bit 6: Code 44 - O2 Sensor Lean Exhaust
- Bit 5: Code 45 - O2 Sensor Rich Exhaust
- Bit 4: Code 51 - PROM Error
- Bit 3: Code 52 - Cal-Pack Error
- Bit 2: Code 53 - System Battery Over-Voltage
- Bit 0: Code 55 - ADU Error
4. Engine Status & Bit Flags
Misc Byte 1 (Payload Index 14 / Byte 15)
- Bit 1: BLM Enabled (1 = Yes)
- Bit 3: Quasi Pulse Mode (1 = Yes)
- Bit 4: Async Pulse Mode (1 = Yes)
- Bit 6: Rich/Lean Status (1 = Rich, 0 = Lean)
- Bit 7: Loop Status (1 = Closed, 0 = Open)
Misc Byte 2 (Payload Index 15 / Byte 16)
- Bit 5: A/C Status (0 = Enabled, 1 = Disabled/Idle)
- Bit 7: Park/Neutral Switch (1 = Park/Neutral, 0 = In Gear)
Misc Byte 3 (Payload Index 16 / Byte 17)
- Bit 0: A/C Clutch Command (1 = Enabled)
- Bit 2: Torque Converter Clutch (TCC) (1 = Locked)
- Bit 5: Power Steering Cramp Switch (1 = Active)
App Features & Architecture
- Bluetooth Thread Management:
- Queries for paired devices and connects directly to
"ESP32-ALDL"over standard SPP RFCOMM sockets. - Robust frame synchronization: buffers incoming streams and aligns on the double-byte header
0xAA 0x55.
- Queries for paired devices and connects directly to
- State Management:
- State flows from the Bluetooth thread through Kotlin
StateFlowto Compose UI.
- State flows from the Bluetooth thread through Kotlin
- Modern Dash Dashboard UI:
- Dynamic animations for RPM and TPS sweeps.
- Metrics displayed in responsive tiles with custom indicator colors (e.g. engine loop states, fuel trims, battery health).
- Live trouble code alert panel.
- Diagnostic pane displaying real-time raw hex buffers for physical signal verification.