Banner publicitario de PCBWay

Comunicación Bluetooth con HC-05, OLED128x64 I2C y ELM327

torres.electronico

Well-known-Alfil
buenas, como va?
estoy armando una interface de comunicacion entre un arduino nano (+ HC05 + OLED128x64 I2c) y el ELM327 y estoy teniendo problemas con el HC-05...

Como no queria depender de librerias, arranque con un formato de comandos de comunicacion estandar del ELM327 y note que no tenia respuesta alguna (hice mas de 7 versiones distintas y nada...)
CSS:
#include <U8glib.h>
#include <SoftwareSerial.h>

// ------------------- OLED SH1106 -------------------
U8GLIB_SH1106_128X64 u8g(U8G_I2C_OPT_NONE);

// ------------------- HC-05 (SoftwareSerial) -------------------
SoftwareSerial HC05(10, 11); // RX, TX

// ------------------- Variables OBD -------------------
int rpm = 0;
float boost = 0;
float tempRefrig = 0;
float voltaje = 0;

// ------------------- Setup -------------------
void setup() {
  Serial.begin(115200);  // Debug PC
  HC05.begin(9600);      // HC-05 maestro ya configurado

  // Debug inicial en OLED
  u8g.firstPage();
  do {
    u8g.setFont(u8g_font_6x10);
    u8g.drawStr(0,0,"DEBUG INICIAL");
    u8g.drawStr(0,15,"HC-05 configurado a 9600");
    u8g.drawStr(0,30,"Rol: Maestro");
    u8g.drawStr(0,45,"Nombre: HC05_OBD");
  } while(u8g.nextPage());
  delay(1000);

  // Inicialización ELM327
  sendELM("ATZ");
  sendELM("ATE0");
  sendELM("ATL0");
  sendELM("ATS0");
  sendELM("ATH0");
  sendELM("ATSP0");
}

// ------------------- Loop -------------------
void loop() {
  // Leer PIDs reales desde ELM327
  rpm = leerPID("010C");        // RPM
  boost = leerPID("010B")/10.0; // Boost bar
  tempRefrig = leerPID("0105"); // Temp C
  voltaje = leerPID("012F")/10.0; // Voltaje

  // Mostrar en pantalla
  u8g.firstPage();
  do {
    u8g.setFont(u8g_font_6x10);
    u8g.drawStr(0,0,"RPM / BOOST / TEMP / VOLT");

    char buf[20];
    sprintf(buf,"RPM: %d", rpm);
    u8g.drawStr(0,15,buf);
    sprintf(buf,"Boost: %.2f bar", boost);
    u8g.drawStr(0,30,buf);
    sprintf(buf,"Temp: %.1f C", tempRefrig);
    u8g.drawStr(0,45,buf);
    sprintf(buf,"Volt: %.1f V", voltaje);
    u8g.drawStr(0,60,buf);
  } while(u8g.nextPage());

  delay(500);
}

// ------------------- Funciones -------------------

// Enviar comando al ELM327 y mostrar debug
void sendELM(String cmd) {
  HC05.println(cmd);
  delay(300);

  String resp = "";
  unsigned long t = millis();
  while(millis()-t < 300) {
    while(HC05.available()) {
      char c = HC05.read();
      resp += c;
    }
  }

  Serial.print("CMD: "); Serial.print(cmd);
  Serial.print(" RESP: "); Serial.println(resp);

  // Debug en OLED
  u8g.firstPage();
  do {
    u8g.setFont(u8g_font_6x10);
    u8g.drawStr(0,0,("CMD: "+cmd).c_str());
    u8g.drawStr(0,15,("RESP: "+resp).c_str());
  } while(u8g.nextPage());

  delay(200);
}

// ------------------- Leer PID simple -------------------
int leerPID(String pid) {
  HC05.println(pid);
  delay(200);

  String resp = "";
  unsigned long t = millis();
  while(millis()-t < 300) {
    while(HC05.available()) {
      char c = HC05.read();
      if(c != '\r' && c != '\n') resp += c;
    }
  }
  resp.trim();
  resp.toUpperCase();

  // Extraer los últimos 2-4 bytes de la respuesta
  if(resp.length() >= 4) {
    int a = strtoul(resp.substring(resp.length()-4,resp.length()-2).c_str(),NULL,16);
    int b = strtoul(resp.substring(resp.length()-2).c_str(),NULL,16);
    return a*256 + b;
  }
  return 0;
}

llegue al punto de dudar si realmente me estaba respondiendo el HC-05, asi que arme el siguiente sketch para ver si podía configurar el modulo bluetooth y de ahi, usando el monitor configurar manualmente todo para que quede guardada la mac y despues se conecte solo:

CSS:
#include <SoftwareSerial.h>
SoftwareSerial BT(10,11); // RX,TX

long bauds[] = {9600, 38400};
void setup(){
  Serial.begin(115200);
  Serial.println(F("=== HC-05 AUTO TEST ==="));
  delay(200);
  for(int i=0;i<2;i++){
    long b = bauds[i];
    Serial.print("Probar baud "); Serial.println(b);
    BT.end();
    delay(50);
    BT.begin(b);
    delay(200);
    // enviar AT simple y AT+VERSION?
    Serial.print("-> Enviando 'AT' a "); Serial.println(b);
    BT.print("AT\r\n");
    delay(400);
    readDump();
    Serial.print("-> Enviando 'AT+VERSION?' a "); Serial.println(b);
    BT.print("AT+VERSION?\r\n");
    delay(600);
    readDump();
    Serial.println("-------------------------");
    delay(300);
  }
  Serial.println("FIN PRUEBA. Ahora escribe comandos (monitor -> BT).");
}

void loop(){
  if (Serial.available()) BT.write(Serial.read()); // lo que escribas va a HC-05
  if (BT.available()) Serial.write(BT.read());     // lo que responda HC-05 va al monitor
}

void readDump(){
  unsigned long t = millis();
  String r="";
  while(millis()-t < 800){
    while(BT.available()){
      char c = (char)BT.read();
      r += c;
    }
  }
  if(r.length()) {
    Serial.print(" RESPUESTA: '"); Serial.print(r); Serial.println("'");
  } else {
    Serial.println(" RESPUESTA: (sin datos)");
  }
}

los comandos implementados fueron:
AT
AT+NAME=HC05_OBD
AT+PSWD=1234
AT+ROLE=1
AT+UART=9600,0,0 (probe casi todas)
AT+CMODE=0
AT+BIND=1C,A1,35,69,8D,C5
AT+LINK=1C,A1,35,69,8D,C5

AT+RESET

y nada....

AT
AT+ROLE=1
AT+CMODE=0
AT+INQM=1,9,48
AT+INIT

AT+INQ

espere ver la mac y nunca respondio...asi que me fije desde una app del telefono la mac y...

AT+PAIR=1C,A1,35,69,8D,C5,20
AT+BIND=1C,A1,35,69,8D,C5
AT+LINK=1C,A1,35,69,8D,C5

AT+RESET

Todo esto me dio errores (0/6y16)... la verdad no recuerdo exactamente el orden, pero en fin... Intente hacer algo de emparejamiento automatico:

CSS:
#include <SoftwareSerial.h>

SoftwareSerial BT(10, 11);  // RX, TX

void setup() {
  Serial.begin(9600);
  Serial.println("=== HC-05 Auto-Link ELM327 ===");

  BT.begin(38400); // Modo AT completo

  delay(1000);
  enviar("AT");
  enviar("AT+RESET");
  enviar("AT+ROLE=1");    // Modo maestro
  enviar("AT+CMODE=1");   // Permitir conexión con cualquier dispositivo
  enviar("AT+INIT");
  enviar("AT+INQ");       // Buscar dispositivos cercanos
  delay(5000);

  Serial.println("Intentando conectar al ELM327...");
  enviar("AT+LINK=1C,A1,35,69,8D,C5");  // tu dirección (ajustar si cambia)
  delay(5000);

  Serial.println("Listo. Si LED del HC-05 queda fijo, se conectó al ELM327.");
  Serial.println("Podés cargar ahora el sketch principal.");
}

void loop() {
  if (BT.available()) Serial.write(BT.read());
  if (Serial.available()) BT.write(Serial.read());
}

void enviar(String cmd) {
  Serial.print(">> "); Serial.println(cmd);
  BT.print(cmd); BT.print("\r\n");
  delay(500);
  while (BT.available()) Serial.write(BT.read());
}
y seguia sin comunicacion... Al parecer, este modulo es muy chino y viene con una version de firmware muy vieja (eso interpreto de los foros que lei) y no reconoce muchos comandos... la cosa es que no pude hacerlo funcionar, asi que cai en una libreria :rolleyes: (ELMDuino) y tampoco funciono...

CSS:
#include <Wire.h>
#include <U8glib.h>
#include <SoftwareSerial.h>
#include "ELMduino.h"

// --- OLED U8glib ---
U8GLIB_SH1106_128X64 u8g(U8G_I2C_OPT_NONE);

// --- SoftwareSerial para HC-05 / ELM327 ---
#define ELM_RX 10
#define ELM_TX 11
SoftwareSerial elmSerial(ELM_RX, ELM_TX);
ELM327 myELM327;

// --- Variables OBD ---
float rpmVal = 0;
float speed = 0;
float coolantTemp = 0;
float batteryV = 0;
float boostKPa = 0;

unsigned long lastUpdate = 0;
const unsigned long updateInterval = 500; // ms
unsigned long lastDataTime = 0;
const unsigned long timeoutComm = 3000; // ms antifreeze

typedef enum { ENG_RPM, SPEED, COOLANT_TEMP, BATT_VOLT, BOOST } obd_pid_states;
obd_pid_states obd_state = ENG_RPM;

// --- Setup ---
void setup() {
  Serial.begin(115200);
  Wire.begin();
  delay(250);

  // Inicializar OLED
  Serial.println("Iniciando OLED...");
  u8g.firstPage();
  do {
    u8g.setFont(u8g_font_6x10);
    u8g.drawStr(10, 30, "OLED OK!");
  } while(u8g.nextPage());
  Serial.println("OLED inicializado correctamente");

  // Inicializar ELM327
  elmSerial.begin(38400); // coincide con HC-05 configurado
  myELM327.begin(elmSerial);
  lastDataTime = millis();
  Serial.println("Esperando datos del ELM327...");
}

// --- Loop ---
void loop() {
  unsigned long currentMillis = millis();
  if(currentMillis - lastUpdate >= updateInterval){
    lastUpdate = currentMillis;
    bool newData = false;

    // --- Lectura PID ---
    switch(obd_state){
      case ENG_RPM:
        rpmVal = myELM327.rpm();
        obd_state = SPEED;
        newData = true; break;
      case SPEED:
        speed = myELM327.kph();
        obd_state = COOLANT_TEMP;
        newData = true; break;
      case COOLANT_TEMP:
        coolantTemp = myELM327.engineCoolantTemp();
        obd_state = BATT_VOLT;
        newData = true; break;
      case BATT_VOLT:
        batteryV = myELM327.batteryVoltage();
        obd_state = BOOST;
        newData = true; break;
      case BOOST:
        boostKPa = myELM327.manifoldPressure();
        obd_state = ENG_RPM;
        newData = true; break;
    }

    if(newData) lastDataTime = millis();

    // --- Dibujar en pantalla ---
    u8g.firstPage();
    do {
      drawData();
    } while(u8g.nextPage());
  }
}

// --- Función para dibujar datos ---
void drawData() {
  char buf[20];
  u8g.setFont(u8g_font_6x10);

  // Fila 1: Voltaje
  sprintf(buf, "Volt: %.1f V", batteryV);
  u8g.drawStr(0, 10, buf);

  // Fila 2: RPM
  sprintf(buf, "RPM: %.0f", rpmVal);
  u8g.drawStr(0, 22, buf);

  // Fila 3: Temp
  sprintf(buf, "Temp: %.0f C", coolantTemp);
  u8g.drawStr(0, 34, buf);

  // Fila 4: Boost
  if(millis() - lastDataTime > timeoutComm){
    u8g.drawStr(0, 46, "Perdida de comunicacion");
  } else {
    sprintf(buf, "Boost: %.0f kPa", boostKPa);
    u8g.drawStr(0, 46, buf);
  }
}

estoy suponiendo que el problema esta en el modulo bluetooth... si alguien experimento lo mismo y me puede dar una mano, le agradezco de antemano... La otra que me queda, es implementar un esp32 que tengo guardadito como oro por ahi, pero es caro para este proyecto...
El hardware en esta beta es sencillo:
-Arduino NANO
-Oled SH1106 I2c
-HC-05
 
Intenta primero comunicarte vía Bluetooth con tu teléfono, para poder asegurarte que estás configurando todo correctamente, luego intentas con el ELM.
 
Asegúrate que el ELM327 no esté conectado a ningún otro dispositivo mientras intentas esta conexión, parece que lo conectaste previamente a algún otro celular o teléfono, desactiva el Bluetooth y luego asegúrate que haya tiempos de espera entre estos comandos.

Una vez logres la conexión debes configurar el ELM327 mediante los correspondientes comandos AT para establecer el estándar ISO y así sucesivamente, para ello debes seguir una secuencia para su respectiva conexión:

MAC Address del ELM327 = 1C,A1,35,69,8D,C5

AT+RESET
AT+ROLE=1
AT+CMODE=0
AT+INIT
AT+BIND=1C,A1,35,69,8D,C5
AT+PAIR=1C,A1,35,69,8D,C5,20
AT+LINK=1C,A1,35,69,8D,C5


Si esto no te funciona es porque el HC-05 debe tener algún problema o conflicto tal como lo comentas, y lo mas factible es que pruebes con otro teléfono y otro HC-05, con su respectiva secuencia.
 

Adjuntos

  • HC-05 AT Command.pdf
    91.2 KB · Visitas: 3
  • HC-05 Comadandos.pdf
    83.7 KB · Visitas: 3
Última edición:
Asegúrate que el ELM327 no esté conectado a ningún otro dispositivo mientras intentas esta conexión, parece que lo conectaste previamente a algún otro celular o teléfono, desactiva el Bluetooth y luego asegúrate que haya tiempos de espera entre estos comandos.

Una vez logres la conexión debes configurar el ELM327 mediante los correspondientes comandos AT para establecer el estándar ISO y así sucesivamente, para ello debes seguir una secuencia para su respectiva conexión:

MAC Address del ELM327 = 1C,A1,35,69,8D,C5

AT+RESET
AT+ROLE=1
AT+CMODE=0
AT+INIT
AT+BIND=1C,A1,35,69,8D,C5
AT+PAIR=1C,A1,35,69,8D,C5,20
AT+LINK=1C,A1,35,69,8D,C5


Si esto no te funciona es porque el HC-05 debe tener algún problema o conflicto tal como lo comentas, y lo mas factible es que pruebes con otro teléfono y otro HC-05, con su respectiva secuencia.
millon de gracias por el aporte. Efectivamente comprobe que tengo unos HC-05 clones con firmware reducido. Son copias muy malas, asi que opte por seguir el proyecto directamente con el bluetooth nativo del ESP32... Me resulto mucho mas practico y sencillo por el momento, ya que la librería ELMDuino, ya viene justamente preparada para trabajar específicamente con un ESP32 y su Bluetooth. Hoy justamente habia terminado de pulir una de las betas y con tanta mala suerte rompí el TFT :facepalm:
WhatsApp Image 2025-11-04 at 5.05.59 PM.jpeg

Asi que ahora voy a editar la base que ya tengo y trabajarlo por el momento con el monitor serial para poder trabajar la parte de los PIDs estandar que soporta el ELM327 (RPM, tension de bateria, temperatura del refrigerante, rpm, entre otros mas) y los PIDs propietarios (presion del turbo, presion de aceite, entre otros) que son los PIDs especificos que emplean las marcas de vehiculos y el llamado a la lectura se hace con otros comandos de la libreria... Termino de depurar las 5 betas que tengo :ROFLMAO: y ni bien pueda terminar de dejar tan solo una perfecta, ahi vamos por la publicacion de una base para que despues puedan replicarlo y averiguando los PID de determinadas marcas,modificar los llamados y tener un circuito a medida de la necesidad de cada uno... Como no tengo todos los PIDs de la NISSAN FRONTIER 4x4 diesel modelo 2008, use google y me meti en varios foros de los cuales pude sacar algunos numeros medios raros de algunas funciones, asi que arme este sketch para rastrear cual es el correcto

CSS:
#include <BluetoothSerial.h>
#include <ELMduino.h>

BluetoothSerial SerialBT;
ELM327 myELM327;

#define ELM_NAME "OBDII"                       // Cambia según el nombre de tu ELM327
const char* ELM327_MAC = "1C:A1:35:69:8D:C5";  // Esta es la direccion MAC de mi ELM327

// Estructura y lista de PIDs
struct PIDTest { const char* pid; const char* desc; };
PIDTest testPIDs[] =
                   {
                    {"210D", "Boost/Presión Turbo (Nissan Consult)"},
                    {"2133", "Presión de Aceite Motor (Consult)"},
                    {"2142", "Temperatura de Aceite Motor (Consult)"},
                    {"2161", "Sensor Presión de Aceite alternativo"},
                    {"2134", "Presión circuito alternativo"},
                    {"0142", "Voltaje ECU/Batería estándar"},
                    {"0B",   "MAP/Presión colector admisión estándar"},
                   };
const int N_PIDS = sizeof(testPIDs) / sizeof(testPIDs[0]);
float kPaToPSI(float kpa) { return kpa / 6.895; }

void setup()
    {
     Serial.begin(115200);
     Serial.println("Iniciando...");
     if (!SerialBT.connect(ELM327_MAC))
        {
         Serial.println("No se pudo conectar al ELM327 por MAC");
         while (1);
        }
     Serial.println("Conectado al ELM327!");

     if (!myELM327.begin(SerialBT, false, 2000))
        {
         Serial.println("No se pudo inicializar comunicación con ELM327");
         while (1);
        }
     Serial.println("ELM327 inicializado correctamente!");
    }

void loop() 
     {
      Serial.println("--- PRUEBA DE PIDs EXTENDIDOS ---");
      for (int i = 0; i < N_PIDS; i++) 
          {
           Serial.print("Consultando PID: "); Serial.print(testPIDs[i].pid);
           Serial.print(" ["); Serial.print(testPIDs[i].desc); Serial.println("]");
          // 1. Enviamos el comando
          myELM327.sendCommand(testPIDs[i].pid);
         // 2. Recuperamos el resultado "payload" y lo mostramos como string
         Serial.print("Respuesta cruda: ");
         Serial.println(myELM327.payload);
        // 3. Si quiero convertir a valor numérico, tengo que parsear el payload aca
        Serial.println("-----------------------------");
       delay(1500);
      }
    Serial.println("Reiniciando barrido en 15 segundos...");
   delay(15000);
}

Cómo funciona el sketch? Consulta todos los PIDs (estándar + extendidos/propietarios) uno por uno y los muestra en el monitor serial: PID, descripción, valor en kPa, valor en PSI y respuesta cruda (un string que despues tengo que meter unas lineas mas para desglosar)...
Básicamente la idea es ver si obtengo un valor válido en PSI que cambia según las condiciones de la camioneta, y con eso verifico que ese es el PID correcto para mi variable. Mañana comento mas al respevto, y es bienvenido todo aquel que se quiera sumar al proyecto.... igualmente, voy a mostrar las cosas que intente con el nano y el hc-05 por si alguien quiere probar con su hc-05. saludos
 
Atrás
Arriba