refactor: migrate firmware from Arduino to native ESP-IDF implementation with CMake configuration

This commit is contained in:
2026-06-12 13:48:35 +01:00
parent 619ec20ca1
commit f5ec189dc4
6 changed files with 254 additions and 94 deletions
+95 -21
View File
@@ -1,20 +1,36 @@
# ESP32 ALDL Wireless Bridge
**ESP32 firmware for reading GM 160-baud PWM ALDL data from a 19861988 Pontiac Fiero 2.8L V6 (1227170 ECM) and streaming it wirelessly over Bluetooth SPP.**
**ESP32 native C firmware using ESP-IDF for reading GM 160-baud PWM ALDL data from a 19861988 Pontiac Fiero 2.8L V6 (1227170 ECM) and streaming it wirelessly over Bluetooth SPP.**
This project turns an ESP32 into a reliable, low-latency wireless ALDL interface. It decodes the raw 160-baud PWM signal in real time and forwards clean 25-byte data frames over Bluetooth to the companion Android app ([esp32-aldl-android](https://git.i3omb.com/gronod/esp32-aldl-android)).
It is implemented as a native ESP-IDF application in C, removing the overhead of the Arduino framework.
---
## Features
- **Real-time 160-baud PWM ALDL decoding** — Custom bit-banged decoder running in a high-priority FreeRTOS task
- **Native C ESP-IDF Implementation** — Built directly on ESP-IDF v5.2+ for maximum performance and low memory footprint
- **Real-time 160-baud PWM ALDL decoding** — Custom bit-banged decoder running in a high-priority FreeRTOS task (`aldlDecodeTask`)
- **Robust pulse handling** — Handles glitches, merged pulses, and idle gaps gracefully
- **Hard frame synchronization** — Prepends `0xAA 0x55` header for reliable packet alignment on the receiving side
- **Bluetooth SPP (Serial Port Profile)** — Appears as a classic Bluetooth serial device named `ESP32-ALDL`
- **Decoupled architecture** — ISR Ring buffer Decoder task Bluetooth transmit queue
- **Status reporting** — Periodic status output over USB serial
- **Low CPU usage** on the decoder core while maintaining timing accuracy
- **Bluedroid Bluetooth Classic SPP** — Connects directly via Serial Port Profile (SPP) using "Just Works" Secure Simple Pairing (SSP) configuration
- **Decoupled RTOS Architecture** — ISR -> Ring Buffer -> Decoder task -> Bluetooth transmit queue -> BT TX task
- **Periodic status reporting** — Diagnostics reported over ESP-IDF log outputs (`ESP_LOGI`)
---
## Project Structure
```
esp32-aldl/
├── CMakeLists.txt # Top-level CMake build configuration
├── sdkconfig.defaults # Kconfig options (enabling BT, SPP, HZ=1000)
├── README.md # Project documentation
└── main/
├── CMakeLists.txt # Main component build script
└── main.c # C application logic (decoder and Bluetooth SPP)
```
---
@@ -22,34 +38,92 @@ This project turns an ESP32 into a reliable, low-latency wireless ALDL interface
- **ECM**: GM 1227170 (19861988 Pontiac Fiero 2.8L V6)
- **ALDL Mode**: `$24` / `$24A` mask (25-byte continuous broadcast frames)
- **Microcontroller**: ESP32 (any dev board with sufficient GPIO — tested on ESP32-WROOM-32 and ESP32-S3)
- **Bluetooth**: Classic Bluetooth (BR/EDR) — works with most Android devices
- **Microcontroller**: ESP32 (Classic ESP32 with Classic Bluetooth capability)
- **Bluetooth**: Classic Bluetooth (BR/EDR)
---
## Pin Connections
| Signal | ESP32 Pin | Notes |
|--------------|---------------|--------------------------------------------|
| ALDL Data In | **GPIO 4** | Connect to ALDL pin **M** (data line) |
| GND | GND | Must share ground with the car/ECU |
| Signal | ESP32 Pin | Notes |
|--------------|---------------|-----------------------------------------------|
| ALDL Data In | **GPIO 4** | Connect to ALDL pin **M** (data line) |
| GND | GND | Must share ground with the car/ECU |
| 5V / 3.3V | — | **Do not** power the ESP32 from the ALDL line |
> **Important**: The ALDL line is a 5V PWM signal. The ESP32 GPIO is 3.3V tolerant for input in most cases, but a simple voltage divider or level shifter (e.g., 1kΩ + 2kΩ) is recommended for long-term reliability.
> [!WARNING]
> The ALDL line is a 5V PWM signal. Since ESP32 GPIO pins are not 5V-tolerant, a simple voltage divider (e.g., 1kΩ + 2kΩ) or a bidirectional level shifter is required to step down the signal to 3.3V for long-term hardware reliability.
---
## Prerequisites & Installation
To build and compile the firmware, you need the official **ESP-IDF v5.2.1** toolchain installed.
1. Clone ESP-IDF (v5.2.1 stable release):
```bash
mkdir -p ~/esp
git clone -b v5.2.1 --recursive https://github.com/espressif/esp-idf.git ~/esp/esp-idf
```
2. Run the toolchain installation:
```bash
cd ~/esp/esp-idf
./install.sh esp32
```
3. Initialize the environment:
```bash
source ~/esp/esp-idf/export.sh
```
4. *(Optional)* If `cmake` or `ninja` are missing in your environment, install them inside the active Python virtual environment:
```bash
pip install cmake ninja
```
---
## Building and Flashing
Once the toolchain environment is loaded, execute the following commands in the `esp32-aldl` root workspace folder:
### 1. Set Build Target
```bash
idf.py set-target esp32
```
This command generates the build directories and loads settings from `sdkconfig.defaults` (enabling Bluetooth SPP and setting the FreeRTOS clock frequency to 1000 Hz).
### 2. Compile Project
```bash
idf.py build
```
This builds the bootloader, partition table, ESP-IDF drivers, Bluetooth stack, and the main application code, producing `build/esp32-aldl.bin`.
### 3. Flash & Monitor
Flash the firmware to the ESP32 and open the terminal console to view serial output (replace `/dev/ttyUSB0` with your target serial port):
```bash
idf.py -p /dev/ttyUSB0 flash monitor
```
To exit the serial monitor, press `Ctrl + ]`.
---
## How It Works
### Signal Decoding
The firmware uses an interrupt service routine (ISR) triggered on any edge of GPIO 4. Pulse widths are measured using the ESP32 high-resolution timer and pushed into a lock-free ring buffer.
The firmware registers an Interrupt Service Routine (ISR) triggered on any edge (both rising and falling) of **GPIO 4**. Pulse widths are calculated in microseconds using `esp_timer_get_time()` and pushed to a volatile ring buffer.
A dedicated decoder task (`aldlDecodeTask`) processes the pulse stream:
- Classifies pulses as `0`, `1`, glitch, merged pulse, or idle gap
- Reconstructs bytes using the standard 160-baud ALDL PWM encoding
- Assembles complete 25-byte frames
- Validates and enqueues valid frames
The high-priority `aldlDecodeTask` pops pulses from the ring buffer:
- Glitches under `300us` are discarded.
- Idle gaps over `13500us` reset the decoder.
- Pulses are classified into Logical `0` (approx. `1.11ms`), Logical `1` (approx. `4.16ms`), or Merged pulses.
- Decoded bits are reconstructed into 25-byte frames.
### Bluetooth Transmission
A separate task (`btTransmitTask`) pulls decoded frames from a FreeRTOS queue and transmits them over Bluetooth SPP with a 2-byte hard sync header:
When a 25-byte frame is fully received, it is pushed to `bt_queue`.
The `btTransmitTask` waits on the queue:
- Prepend the 2-byte hard-sync header (`0xAA 0x55`).
- Sends the packet (`27 bytes` total) over the Bluetooth Classic Serial Port Profile connection via the `esp_spp_write()` API.
- The device advertises itself under the classic Bluetooth name `ESP32-ALDL`.