ESP32 with ELM327

Hello Guys,

I am currently trying to connect an ESP32 with my car and therefore using the ELM327 Bluetooth Adapter.

My cars coolant temperature gauge is not working as it's supposed to and thats why I want to display it on a small 128x64 display. I can get the esp32 to connect to the ELM327 but no matter what command I try to send I will always get the same answer from the ELM327 wich is " 4F 42 44 49 49" and can be translated to "OBDII". Can someone try to help me find out why I get this response and how I can improve my code to run properly?
The PID for the coolant temperature is 01 05 .
I am new to microcontrollers and do a lot of coding with ChatGPT.
Here is my code.

#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <BLEDevice.h>
#include <BLEUtils.h>
#include <BLEScan.h>
#include <BLEAdvertisedDevice.h>

#define SCREEN_WIDTH 128 
#define SCREEN_HEIGHT 64 
#define OLED_RESET -1  

Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);

BLEClient* pClient;
BLERemoteService* pRemoteService;
BLERemoteCharacteristic* pRemoteCharacteristic;
BLEScan* pBLEScan;
BLEAdvertisedDevice* myDevice = nullptr;

void initializeDisplay() 
{
    if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) 
    {
        Serial.println(F("SSD1306 allocation failed"));
        for(;;); 
    }
    display.clearDisplay();
    display.setTextSize(1);      
    display.setTextColor(SSD1306_WHITE); 
    display.setCursor(0, 0);     
    display.println("Initializing BLE...");
    display.display();
}

void initializeBLE() 
{
    Serial.println("Initializing BLE...");
    BLEDevice::init("");
    pClient = BLEDevice::createClient();
    pBLEScan = BLEDevice::getScan();
    pBLEScan->setActiveScan(true);
    Serial.println("BLE client initialized.");
}

void connectToELM327() 
{
    Serial.println("Starting scan for BLE devices...");
    display.clearDisplay();
    display.setCursor(0, 0);
    display.println("Scanning for BLE...");
    display.display();

    BLEScanResults* foundDevices = pBLEScan->start(5);
    bool elm327Found = false;

    for (int i = 0; i < foundDevices->getCount(); i++) 
    {
        BLEAdvertisedDevice device = foundDevices->getDevice(i);
        if (device.getAddress().toString() == "1c:a1:35:69:8d:c5") 
        {
            Serial.println("ELM327 found!");
            display.println("ELM327 found!");
            display.display();
            myDevice = new BLEAdvertisedDevice(device);
            elm327Found = true;
            break;
        }
    }

    if (!elm327Found) 
    {
        Serial.println("ELM327 not found. Retrying in 2 seconds...");
        display.println("ELM327 not found.");
        display.display();
        delay(2000);
        return;
    }

    Serial.println("Trying to connect to ELM327...");
    display.println("Connecting to ELM327...");
    display.display();
    
    if (!pClient->connect(myDevice)) 
    {
        Serial.println("Connection to ELM327 failed.");
        display.println("Connection failed");
        display.display();
        delay(5000);
        return;
    }

    Serial.println("Connected to ELM327!");
    display.println("Connected!");
    display.display();

    pRemoteService = pClient->getService(BLEUUID("00001800-0000-1000-8000-00805f9b34fb"));
    if (!pRemoteService) 
    {
        Serial.println("OBD-II Service not found. Disconnecting.");
        display.println("Service not found.");
        display.display();
        pClient->disconnect();
        return;
    }
    
    pRemoteCharacteristic = pRemoteService->getCharacteristic(BLEUUID("00002a00-0000-1000-8000-00805f9b34fb"));
    if (!pRemoteCharacteristic) 
    {
        Serial.println("Characteristic not found. Disconnecting.");
        display.println("Characteristic not found.");
        display.display();
        pClient->disconnect();
        return;
    }
    Serial.println("Characteristic connected!");
    display.println("Characteristic connected!");
    display.display();
}

void sendCommand(String command) 
{
    Serial.print("Sending command: ");
    Serial.println(command);
    pRemoteCharacteristic->writeValue(command.c_str(), command.length());
    
    delay(500); 

    if (pRemoteCharacteristic->canRead()) 
    {
        String response = pRemoteCharacteristic->readValue();
        Serial.print("Response received: ");
        Serial.println(response);
    } 
    else 
    {
        Serial.println("The characteristic cannot be read.");
    }
}

void setup() 
{
    Serial.begin(115200);
    initializeDisplay();
    initializeBLE();
    connectToELM327();
}

void loop() 
{
    if (!pClient->isConnected()) 
    {
        Serial.println("No connection to ELM327. Restarting...");
        connectToELM327();
        delay(5000);
        return;
    }

    sendCommand("AT Z\r");  // Reset command
    sendCommand("AT DP\r");  // Query protocol
    String obdCommand = "01 05";
    sendCommand(obdCommand);

    delay(5000);  
}

Best regards!

Update 20.04.2026 :

I did invest some time in this project and managed to get it working. Thanks to a lot of AI and Claude!!

The following code reads the coolant temperature of my 2000 Audi TT 8N via the ELM327 and sends it to the ESP32 via Bluetooth (BLE) which send it to a small 1.28 inch OLED Display.

/*
 * ESP32 ELM327 BLE OBD2 Coolant Temperature Reader with OLED Display
 *
 * This code connects an ESP32 via Bluetooth Low Energy (BLE) to an
 * ELM327 Mini adapter and reads the coolant temperature from an OBD2 simulator.
 * The temperature is shown on a 1.3" OLED display (128x64 I2C).
 *
 * Required libraries:
 * - BLEDevice (included in ESP32 Arduino Core)
 * - Adafruit SSD1306
 * - Adafruit GFX Library
 *
 * Hardware:
 * - ESP32 Development Board
 * - ELM327 Mini (BLE Version)
 * - OBD Simulator-B-V1.5
 * - 1.3" OLED I2C Display 128x64
 *
 * OLED wiring:
 * VCC → 3.3V (or 5V)
 * GND → GND
 * SCL → GPIO 22
 * SDA → GPIO 21
 */

#include <BLEDevice.h>
#include <BLEUtils.h>
#include <BLEScan.h>
#include <BLEAdvertisedDevice.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

// ===== OLED Display Configuration =====
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define OLED_RESET -1  // Reset pin (or -1 if not used)
#define SCREEN_ADDRESS 0x3C  // I2C address (usually 0x3C or 0x3D)

Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);

// ===== Configuration: Choose the protocol =====
// Change this line to test different protocols:
// Option 1: ISO 15765-4 CAN (recommended, modern vehicles)
// #define OBD_PROTOCOL "ATSP6"
// #define PROTOCOL_NAME "ISO 15765-4 CAN (11bit, 500kbaud)"

// Option 2: ISO 9141-2 (older vehicles)
 #define OBD_PROTOCOL "ATSP3"
 #define PROTOCOL_NAME "ISO 9141-2"

// Option 3: ISO 14230-4 KWP (fast init) - WORKS with your simulator!
//#define OBD_PROTOCOL "ATSP5"
//#define PROTOCOL_NAME "ISO 14230-4 KWP fast"

// Option 4: ISO 14230-4 KWP (5 baud init) - Alternative if Option 3 is not stable
// #define OBD_PROTOCOL "ATSP4"
// #define PROTOCOL_NAME "ISO 14230-4 KWP 5baud"

// ===== BLE UUIDs for ELM327 =====
// Standard UUIDs for ELM327 BLE devices (Serial Port Profile)
#define SERVICE_UUID           "0000fff0-0000-1000-8000-00805f9b34fb"
#define CHARACTERISTIC_UUID_TX "0000fff1-0000-1000-8000-00805f9b34fb" // ESP32 receives here
#define CHARACTERISTIC_UUID_RX "0000fff2-0000-1000-8000-00805f9b34fb" // ESP32 sends here

// ===== Global Variables =====
static BLEAdvertisedDevice* elmDevice = nullptr;
static BLEClient* pClient = nullptr;
static BLERemoteCharacteristic* pTxCharacteristic = nullptr;
static BLERemoteCharacteristic* pRxCharacteristic = nullptr;

static bool deviceConnected = false;
static bool doConnect = false;
static String receivedData = "";

// ===== Callback for BLE Scan =====
class MyAdvertisedDeviceCallbacks: public BLEAdvertisedDeviceCallbacks {
    void onResult(BLEAdvertisedDevice advertisedDevice) {
        Serial.print("BLE device found: ");
        Serial.println(advertisedDevice.toString().c_str());
        
        // Search for ELM327 in device name (common names: "OBDII", "ELM327", "OBD2")
        String deviceName = String(advertisedDevice.getName().c_str());
        
        if (deviceName.indexOf("OBD") != -1 ||
            deviceName.indexOf("ELM") != -1 ||
            deviceName.indexOf("obd") != -1 ||
            deviceName.indexOf("elm") != -1) {
            
            Serial.println(">>> ELM327 device found!");
            BLEDevice::getScan()->stop();
            elmDevice = new BLEAdvertisedDevice(advertisedDevice);
            doConnect = true;
        }
    }
};

// ===== Callback for received data (Notifications) =====
static void notifyCallback(BLERemoteCharacteristic* pBLERemoteCharacteristic,
                           uint8_t* pData, size_t length, bool isNotify) {
    // Convert received data to string
    for (int i = 0; i < length; i++) {
        receivedData += (char)pData[i];
    }
    
    // Debug output of raw data
    //Serial.print("Received: ");
    //Serial.println(receivedData);
}

// ===== Callback for connection status =====
class MyClientCallback : public BLEClientCallbacks {
    void onConnect(BLEClient* pclient) {
        Serial.println("✓ Connected to ELM327");
        deviceConnected = true;
    }

    void onDisconnect(BLEClient* pclient) {
        Serial.println("✗ Connection to ELM327 lost");
        deviceConnected = false;
        
        // Show connection loss on display
        display.clearDisplay();
        display.setTextSize(1);
        display.setCursor(5, 20);
        display.println("Connection");
        display.setCursor(5, 35);
        display.println("lost!");
        display.display();
    }
};

// ===== Function: Establish connection to ELM327 =====
bool connectToELM327() {
    Serial.println("\n=== Connection setup started ===");
    
    // Create BLE client
    pClient = BLEDevice::createClient();
    Serial.println("✓ BLE client created");
    
    // Set callback for connection status
    pClient->setClientCallbacks(new MyClientCallback());
    
    // Connect to ELM327
    Serial.print("Connecting to ");
    Serial.println(elmDevice->getAddress().toString().c_str());
    
    if (!pClient->connect(elmDevice)) {
        Serial.println("✗ ERROR: Connection failed");
        return false;
    }
    Serial.println("✓ Connection established");
    
    // Retrieve service
    BLERemoteService* pRemoteService = pClient->getService(SERVICE_UUID);
    if (pRemoteService == nullptr) {
        Serial.println("✗ ERROR: Service not found");
        pClient->disconnect();
        return false;
    }
    Serial.println("✓ Service found");
    
    // Retrieve TX characteristic (ESP32 receives)
    pTxCharacteristic = pRemoteService->getCharacteristic(CHARACTERISTIC_UUID_TX);
    if (pTxCharacteristic == nullptr) {
        Serial.println("✗ ERROR: TX characteristic not found");
        pClient->disconnect();
        return false;
    }
    Serial.println("✓ TX characteristic found");
    
    // Retrieve RX characteristic (ESP32 sends)
    pRxCharacteristic = pRemoteService->getCharacteristic(CHARACTERISTIC_UUID_RX);
    if (pRxCharacteristic == nullptr) {
        Serial.println("✗ ERROR: RX characteristic not found");
        pClient->disconnect();
        return false;
    }
    Serial.println("✓ RX characteristic found");
    
    // Enable notifications for incoming data
    if (pTxCharacteristic->canNotify()) {
        pTxCharacteristic->registerForNotify(notifyCallback);
        Serial.println("✓ Notifications enabled");
    }
    
    Serial.println("=== Connection successful ===\n");
    return true;
}

// ===== Function: Send OBD command =====
void sendOBDCommand(String command) {
    if (!deviceConnected) {
        Serial.println("✗ Not connected - command will not be sent");
        return;
    }
    
    receivedData = ""; // Clear receive buffer
    command += "\r"; // Append carriage return (ELM327 expects this)
    
    //Serial.print("Sending command: ");
    //Serial.println(command);
    
    pRxCharacteristic->writeValue(command.c_str(), command.length());
}

// ===== Function: Wait for response =====
String waitForResponse(int timeoutMs = 2000) {
    unsigned long startTime = millis();
    
    while (millis() - startTime < timeoutMs) {
        // Check if data ends with '>' (ELM327 prompt)
        if (receivedData.indexOf('>') != -1) {
            String response = receivedData;
            receivedData = "";
            return response;
        }
        delay(10);
    }
    
    Serial.println("⚠ Timeout waiting for response");
    return receivedData;
}

// ===== Function: Initialize ELM327 =====
bool initializeELM327() {
    Serial.println("\n=== ELM327 Initialization ===");
    
    // Reset
    sendOBDCommand("ATZ");
    delay(2500); // Extra time for complete reset
    String response = waitForResponse(3000);
    Serial.println(response);
    
    // Wait until ELM327 is really ready
    delay(500);
    
    // Turn off echo (for cleaner responses)
    sendOBDCommand("ATE0");
    delay(500);
    response = waitForResponse();
    Serial.print("Echo off: ");
    Serial.println(response);
    
    // Remove spaces from responses
    sendOBDCommand("ATS0");
    delay(500);
    response = waitForResponse();
    Serial.print("Spaces off: ");
    Serial.println(response);
    
    // Turn off linefeed
    sendOBDCommand("ATL0");
    delay(500);
    response = waitForResponse();
    
    // IMPORTANT: Set protocol MANUALLY instead of auto-detection
    Serial.print("Setting protocol to ");
    Serial.print(PROTOCOL_NAME);
    Serial.println("...");
    
    sendOBDCommand(OBD_PROTOCOL);
    delay(2000); // Longer wait time for protocol setup
    response = waitForResponse(2500);
    Serial.print("Protocol set: ");
    Serial.println(response);
    
    // Turn off headers (can help with some simulators)
    sendOBDCommand("ATH0");
    delay(500);
    response = waitForResponse();
    Serial.print("Headers off: ");
    Serial.println(response);
    
    // Set adaptive timing to auto (IMPORTANT for ISO14230-4!)
    sendOBDCommand("ATAT2");
    delay(500);
    response = waitForResponse();
    Serial.print("Adaptive timing: ");
    Serial.println(response);
    
    // Set timeout to a higher value for ISO14230-4
    // ATST FF = 255 × 4ms = 1020ms timeout
    sendOBDCommand("ATSTFF");
    delay(500);
    response = waitForResponse();
    Serial.print("Timeout set: ");
    Serial.println(response);
    
    // Allow long messages (important for some protocols)
    sendOBDCommand("ATAL");
    delay(500);
    response = waitForResponse();
    Serial.print("Long messages: ");
    Serial.println(response);
    
    // Enable keep alive - prevents connection drop!
    // Automatically sends small packets to maintain connection
    sendOBDCommand("ATKW");
    delay(500);
    response = waitForResponse();
    Serial.print("Keep Alive Status: ");
    Serial.println(response);
    
    // Query used protocol
    sendOBDCommand("ATDPN");
    delay(500);
    response = waitForResponse();
    Serial.print(">>> Used protocol number: ");
    Serial.println(response);
    
    sendOBDCommand("ATDP");
    delay(500);
    response = waitForResponse();
    Serial.print(">>> Protocol name: ");
    Serial.println(response);
    
    // Voltage test (checks if OBD connection is established)
    sendOBDCommand("ATRV");
    delay(500);
    response = waitForResponse();
    Serial.print("Voltage: ");
    Serial.println(response);
    
    // IMPORTANT: Initial "dummy" bus initialization
    // This ensures the bus is really connected
    Serial.println("\n>>> Performing initial bus connection <<<");
    sendOBDCommand("0100");
    delay(1500);
    response = waitForResponse(5000); // Long timeout for first connection
    Serial.print("Initial bus response: ");
    Serial.println(response);
    
    // If BUS INIT ERROR, try again
    if (response.indexOf("ERROR") != -1 || response.indexOf("UNABLE") != -1) {
        Serial.println("Bus init failed, retrying...");
        delay(1000);
        
        // Set protocol again
        sendOBDCommand(OBD_PROTOCOL);
        delay(2000);
        waitForResponse(2500);
        
        // Try again
        sendOBDCommand("0100");
        delay(1500);
        response = waitForResponse(5000);
        Serial.print("Second attempt: ");
        Serial.println(response);
    }
    
    Serial.println("✓ ELM327 initialized");
    Serial.println("=== Initialization complete ===\n");
    
    // Short pause before normal queries start
    delay(1000);
    
    return true;
}

// ===== Function: Read coolant temperature =====
float readCoolantTemperature() {
    //Serial.println("--- Reading coolant temperature ---");
    
    // OBD2 Mode 01 PID 05: Engine Coolant Temperature
    sendOBDCommand("0105");
    delay(800); // Longer wait for ISO14230-4
    
    String response = waitForResponse(4000); // Longer timeout for ISO14230-4
    
    //Serial.print("Full response: [");
    //Serial.print(response);
    //Serial.println("]");
    
    // Check if an error was returned
    if (response.indexOf("NO DATA") != -1) {
        Serial.println("✗ No data - reinitializing bus...");
        
        // Complete bus reinitialization
        sendOBDCommand("ATZ");
        delay(2000);
        waitForResponse(3000);
        
        // Set protocol again
        sendOBDCommand(OBD_PROTOCOL);
        delay(1500);
        waitForResponse(2000);
        
        // Adaptive timing
        sendOBDCommand("ATAT2");
        delay(500);
        waitForResponse();
        
        Serial.println("Bus reinitialized - retrying...");
        
        // Try again
        sendOBDCommand("0105");
        delay(800);
        response = waitForResponse(4000);
        
        Serial.print("New response after reset: [");
        Serial.print(response);
        Serial.println("]");
        
        // If still NO DATA, give up
        if (response.indexOf("NO DATA") != -1 || response.indexOf("UNABLE") != -1) {
            Serial.println("✗ Still no data after reset");
            return -999.0;
        }
    }
    
    if (response.indexOf("UNABLE TO CONNECT") != -1) {
        Serial.println("✗ Connection to OBD bus failed - attempting reconnect");
        
        // Similar reset as for NO DATA
        sendOBDCommand("ATZ");
        delay(2000);
        waitForResponse(3000);
        
        sendOBDCommand(OBD_PROTOCOL);
        delay(1500);
        waitForResponse(2000);
        
        return -999.0;
    }
    
    if (response.indexOf("BUS INIT") != -1 && response.indexOf("ERROR") != -1) {
        Serial.println("✗ Bus initialization failed");
        return -999.0;
    }
    
    if (response.indexOf("?") != -1) {
        Serial.println("✗ Unknown command");
        return -999.0;
    }
    
    // Parse response: format is "41 05 XX" where XX is the hex value
    // Temperature = XX - 40 (in °C)
    
    // Remove spaces for easier parsing
    response.replace(" ", "");
    response.replace("\r", "");
    response.replace("\n", "");
    response.replace(">", "");
    
    int pidPos = response.indexOf("4105");
    if (pidPos == -1) {
        Serial.println("✗ Invalid response or PID not supported");
        Serial.print("Cleaned response was: ");
        Serial.println(response);
        return -999.0;
    }
    
    // Extract hex value after "4105" (2 characters)
    String hexValue = response.substring(pidPos + 4, pidPos + 6);
    hexValue.trim();
    
    if (hexValue.length() < 2) {
        Serial.println("✗ Hex value too short");
        return -999.0;
    }
    
    // Convert hex to decimal
    int tempValue = (int)strtol(hexValue.c_str(), NULL, 16);
    
    // Calculate temperature (formula: value - 40)
    float temperature = tempValue - 40.0;
    
    //Serial.print("Raw data (Hex): ");
    //Serial.println(hexValue);
    //Serial.print("Decimal value: ");
    //Serial.println(tempValue);
    //Serial.print("✓ Coolant temperature: ");
    Serial.print(temperature);
    Serial.println(" °C");
    displayTemperature(temperature);
    
    return temperature;
}

// ===== Function: Display temperature on OLED =====
void displayTemperature(float temperature) {
    display.clearDisplay();
    
    // Check if temperature is valid
    if (temperature <= -999.0) {
        // Show error message
        display.setTextSize(2);
        display.setTextColor(SSD1306_WHITE);
        display.setCursor(10, 20);
        display.println("ERROR");
        display.setTextSize(1);
        display.setCursor(5, 45);
        display.println("No connection");
    } else {
        // Show temperature large in the center
        display.setTextSize(4);
        display.setTextColor(SSD1306_WHITE);
        
        // Center temperature
        String tempStr = String((int)temperature);
        int16_t x = (SCREEN_WIDTH - (tempStr.length() * 24)) / 2;
        
        display.setCursor(x, 15);
        display.print((int)temperature);
        
        // Degree symbol and C
        display.setTextSize(2);
        display.setCursor(x + (tempStr.length() * 24), 20);
        display.print("o");
        display.setCursor(x + (tempStr.length() * 24) + 12, 25);
        display.print("C");
        
        // "Coolant" text at the bottom
        display.setTextSize(1);
        display.setCursor(20, 55);
        display.print("Coolant");
    }
    
    display.display();
}

// ===== Setup =====
void setup() {
    Serial.begin(115200);
    Serial.println("\n\n========================================");
    Serial.println("ESP32 ELM327 BLE OBD2 Reader with OLED");
    Serial.println("========================================\n");
    
    // ===== Initialize OLED Display =====
    Serial.println("Initializing OLED display...");
    if(!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS)) {
        Serial.println("✗ ERROR: OLED display not found!");
        Serial.println("Check wiring:");
        Serial.println("VCC → 3.3V, GND → GND");
        Serial.println("SCL → GPIO 22, SDA → GPIO 21");
        for(;;); // Infinite loop
    }
    
    Serial.println("✓ OLED display initialized");
    
    // Welcome screen
    display.clearDisplay();
    display.setTextSize(2);
    display.setTextColor(SSD1306_WHITE);
    display.setCursor(15, 10);
    display.println("OBD2");
    display.setCursor(10, 30);
    display.println("Reader");
    display.setTextSize(1);
    display.setCursor(20, 52);
    display.println("Starting...");
    display.display();
    delay(2000);
    
    // Initialize BLE
    Serial.println("Initializing BLE...");
    BLEDevice::init("ESP32_OBD_Reader");
    
    // Search screen
    display.clearDisplay();
    display.setTextSize(1);
    display.setCursor(5, 25);
    display.println("Searching ELM327...");
    display.display();
    
    // Start BLE scan
    BLEScan* pBLEScan = BLEDevice::getScan();
    pBLEScan->setAdvertisedDeviceCallbacks(new MyAdvertisedDeviceCallbacks());
    pBLEScan->setInterval(1349);
    pBLEScan->setWindow(449);
    pBLEScan->setActiveScan(true);
    
    Serial.println("Starting BLE scan (10 seconds)...");
    pBLEScan->start(10, false);
}

// ===== Main Loop =====
void loop() {
    // If device was found, connect
    if (doConnect) {
        if (connectToELM327()) {
            doConnect = false;
            
            // Initialize ELM327
            if (initializeELM327()) {
                Serial.println("Ready to read!\n");
            }
        } else {
            doConnect = false;
            Serial.println("Connection failed. Restarting in 5 seconds...");
            delay(5000);
            ESP.restart();
        }
    }
    
    // If connected, read temperature every 5 seconds
    if (deviceConnected) {
        // Run a test on first iteration
        static bool firstRun = true;
        if (firstRun) {
            Serial.println("\n>>> Running test query <<<");
            
            // Test: Query supported PIDs (0100)
            sendOBDCommand("0100");
            delay(1000);
            String testResponse = waitForResponse(3000);
            Serial.print("Test response (0100): ");
            Serial.println(testResponse);
            
            delay(1000);
            firstRun = false;
        }
        
        readCoolantTemperature();
        //Serial.println(); // Empty line for better readability
        delay(500); // 500ms works with your setup
    }
    
    // Only reconnect if really disconnected AND was previously connected
    // (not on first start)
    static bool wasConnected = false;
    if (!deviceConnected && wasConnected) {
        Serial.println("Connection lost! Trying to reconnect...");
        
        // Error screen
        display.clearDisplay();
        display.setTextSize(1);
        display.setCursor(5, 20);
        display.println("Connection");
        display.setCursor(5, 35);
        display.println("lost!");
        display.display();
        
        delay(5000);
        ESP.restart();
    }
    
    // Save status
    if (deviceConnected) {
        wasConnected = true;
    }
}

The problems I had was to find out wich OBD protocol my car was using and to simulate my car to not always get my car started. To find out wich OBD protocol my car uses I send this command AT DP via an app for OBD "Terminal" which replies with the used protocol by the ELM327. To simulate my car I bought an OBD simulator on aliexpress which lets you simulate your car with some values e.g. rpm, speed, temperature. I bought this one without the display.

I also made an upgraded version of that where I can read more values of my car such as rpm, speed, battery voltage, intake air temperature and so on. To display these Values nicely, I bought the Nextion Display (round 2.1'' Link ) and made a "GUI". I also 3D-printed a mount for it to fit into the air vent of my car. The Display being touch allows me to click trough the values. Thanks to the tutorial by UPIR. That video got me to buy that display.

Here is a video that shows how it works.

elm327_esp32.mov.zip (6,6 MB)

Let me know when you got any questions or just use Claude ai. It got the whole project to work which ChatGPT was not able to.

To see if it's the same because you suspect the 'meter' is bad ?

Is an ELM327 BT or BLE ? (Maybe your module does both?)

Yes, I know that the 'meter' in my car is "broken" because I can read the right coolant temperature when I connect the ELM327 to my phone with an OBDII app. My plan is to also show the engine rpm and some other sensors on the display, but I am seeing that as the next step.
I can't tell if the module can do both, BT and BLE but I think BLE is working fine.

I am at the same point as you trying to do it with BLE and I get the connection with the equipment but the data is not correct when reading, any news?

Hi,
I have not had the time yet to work further on it. But I will let you know when I am achieving any progress. At least I am not the only one struggling with sending and receiving.
I think I will buy an OBD2 simulator wich makes it easier to get a result and I don’t have to get in my car every time.
I can recommend you this video for some advice.
https://youtu.be/L3ufJTFX2v0?si=OISUzcPEOP1tAXQQ

Let me know if you’re making progress…

How were you able to get the uuid? The issue might be related to trying to acess an incorrect service. I've found in other forums that they were trying to acess 00001101-0000-1000-8000-00805F9B34FB, but when I tried to connect with my phone using a bluetooth terminal was only able to find FFF1, and FFF2. Not exactly sure which one should I use

Hey @Mattei were you able to solve that riddle? I'm also trying to connect my ELM but only see FFF1 and FFF2, same as you. Banging my head on the wall, since I don't understand it.