Electrónica‎ > ‎Arduino‎ > ‎

OBDII HC-05

Com este projecto o objectivo é ligar um módulo bluetooth HC-05 (Slave\Master) a um adaptador bluetooth OBDII (On-board diagnostics) ELM327, desta forma posso trabalhar as informação que o meu carro dá através deste protocolo de comunicação automóvel. 

Tendo como base o projecto feito com o módulo bluetooth HC-06 em BT HC-06, que funciona apenas em modo Slave, começamos por ligar o módulo HC-05 da seguinte forma:

 

 MÓDULO HC-05 ARDUINO
 Pino VCC Arduino 3.3v
 Pino GRND Arduino GRND
 Pino TXT  Arduino RXD
 Pino RXD  Arduino TXT através de 'voltage divider'

 Pino KEYUsado para entrar em modo AT (de configuração do módulo), para entrar temos de ligar este pino também a 3.3v. 
Em operação normal do módulo (Data Mode), este pino pode estar desligado. 
   
Configurar o módulo HC-05, usando o modo AT, para conseguirmos estabelecer ligação ao ELM327:
  • Para entrar em modo AT temos de, com o módulo desligado, elevar o pino 'Key' da placa (ou o pino34 / PI011 directamente do módulo) ligando-o aos 3.3v
  • Ligamos depois o módulo HC-05, o status led  vai piscar de modo diferente, agora só precisamos de enviar comandos através de uma comunicação serial (com qualquer terminal existente, neste projecto foi usado um Arduino e o respectivo IDE)
Código utilizado:

#include <SoftwareSerial.h> //Para ligar o módulo HC-05 noutros pinos que não o 0 e 1 (RX\TX)

#define RxD 7                //Arduino pin connected to Tx of HC-05
#define TxD 8                //Arduino pin connected to Rx of HC-05

SoftwareSerial blueToothSerial(RxD,TxD); //3.3v entre o pino 8 do Arduino e o RX do módulo

void setup()
{
  // inicializar as 2 portas serial:
  Serial.begin(38400);          //Para o arduino comunicar com o PC 
  blueToothSerial.begin(38400);  //-> HC-05 AT mode baud rate is 38400
  
   pinMode(RxD, INPUT);
   pinMode(TxD, OUTPUT);
     
 } 
 
void loop() {
  
  if (blueToothSerial.available()) {         //PORTA DO BLUETOOTH RX + TX (7 e 8) | Escrever as respostas do módulo
    int inByte = blueToothSerial.read();
    Serial.write((char)inByte); 
   }
  

  if (Serial.available()) {                   // Ler a porta Serial do Arduino para depois enviar os comandos ao módulo
    int inByte = Serial.read();
    blueToothSerial.print((char)inByte);     //Na janela Serial do IDE temos de escolher a opção "Both NL & CR" e o baud de 38400 
   
 //blueToothSerial.print("\r\n"); //No HC-05 todos os comandos têm de acabar assim
  }
}

//-> PARA ENTRAR EM MODO DE AT COMMAND TEM DE SE ALTERAR BAUD PARA:
//  blueToothSerial.begin(38400);

//-----------------------------------------------
//-> DEPOIS DE ESTAR COMO MASTER COLOCAR CONFIGs DE SERIAL PORTS ASSIM PARA COMUNICAR EM MODO NORMAL:

//  Serial.begin(38400); //Para o arduino comunicar com o PC 
//  blueToothSerial.begin(9600);//HC-05 AT mode baud rate is 38400
//  -----------------------------------------------

Para testar a ligação ao módulo podemos apenas escrever o comando 'AT' e a resposta tem de ser 'OK'.

Para o nosso projecto temos de usar os seguintes comandos:

AT                 -> Testar a ligação
AT+VERSION -> Para sabermos a versão do módulo
AT+ROLE=1 -> Configurar o módulo como MASTER (0 é Slave)
AT+CMODE=0 -> Para estabelecer ligação com o último dispositivo (automaticamente)
AT+INIT       -> Inicializar
AT+INQ        -> Pesquisar outros equipamentos bluetooth
AT+INQC      -> Terminar a pesquisa
AT+RNAME   -> Perguntar o nome de um determinado equipamento bluetooth (através do seu endereço)
AT+FSAD      -> Pesquisar o referido equipamento na lista de autenticação de módulos  autenticados anteriormente (se existir)
AT+BIND      -> Para vincular um equipamento ao módulo HC-05
AT+PAIR       -> Efectuar o emparelhamento com o equipamento pretendido
AT+LINK       -> Estabelecer ligação

Foi efectuado um teste com sucesso de emparelhamento e ligação ao módulo HC-06. Para utilizarmos os comandos acima temos só de perceber os endereços MAC dos dispositivos a ligar. Depois de efectuarmos a pesquisa com o comando "AT+INQ" e se o módulo HC-06 estiver ligado, vamos obter na lista o respectivo endereço, neste caso é o 00:12:11:24:93:70. Para usarmos este endereço nos nossos comandos temos de o adaptar desta forma: 12,11,249370 (eliminados os primeiros zeros e separando-o por vírgulas).

Podemos por exemplo perguntar o nome do dispositivo assim: AT+RNAME?12,11,249370, a resposta neste caso foi "linvor"
Resumo dos comandos e respostas deste teste:

AT
OK
AT+ROLE?
+ROLE:0
OK
AT+ROLE=1
OK
AT+INQ
ERROR
AT+INIT
OK
AT+INQ
(...)
+INQ:00:12:11:24:93:70 
(...)
AT+INQC
OK
AT+RNAME?12,11,249370 
+RNAME:linvor
OK
AT+FSAD=12,11,249370
OK
AT+PAIR=12,11,249370,10     -> o número 10 é o tempo em que espera por uma resposta
OK
AT+LINK=12,11,249370
OK

Como exemplo dos comandos a enviar, para comunicar com outro dispositivo bluetooth, podemos ver esta imagem (de outro utilizar na internet):

Para estabelecer ligação com o nosso módulo OBDII ELM327, temos de ter como base a sua informação:
OBDII Bluetooth ELM327
PIN: 6789
MAC: 001109150673

Atenção: Para que consigamos emparelhar e estabelecer ligação, o módulo HC-05 tem de ter a mesma password (ou pairing code) que o módulo Slave ELM327, o comando para alterar no HC-05 é este: AT+PIN6789

Com o módulo ELM327 ligado e com o código acima no Arduino, foi efectuada uma pesquisa de equipamentos e na lista apareceu o endereço do módulo Slave: 11:9:150673,1F00,7FFF, que ficará assim: 11,09,150673,20 (o nome do dispositivo é CHX).

Uma vez que o módulo HC-05 já está configurado como Master (ROLE=1), com a password 6789 e no modo CMODE=0, temos apenas de testar vincular o ELM327 e estabelecer ligação (nas próximas vezes assim que ligarmos ambos os módulos (HC-05 e ELM327) a ligação será feita automaticamente).

(Apenas precisamos de entrar no modo AT no HC-05 uma vez e para definir as configurações desejadas (como master, vínculo, o modo CMODE e a password), pois para o emparelhamento e ligação o módulo não pode estar em Data Mode mas sim em  Command Mode (AT) elevando o pino Key para Hight)

+INQ:11:9:150673,1F00,7FFF
OK
+RNAME:CHX
OK
+BIND:11:9:150673

Comandos enviados:

AT+INIT
OK
AT+INQ
(...)
+INQ:11:9:150673,1F00,7FFF
(...)
AT+INQC
OK
AT+RNAME?11,09,150673,20
+RNAME:CHX
OK
AT+BIND=11,09,150673,20
OK
AT+FSAD=11,09,150673,20
OK
AT+PAIR=11,09,150673,20,10
OK
AT+LINK=11,09,150673,20
OK

Para o envio destes comandos o código utilizado foi adaptado no void loop() para fazer uma pequena separação do código enviado e das respostas recebidas do ELM327:

void loop() {

//PARA ENVIAR COMANDOS PARA O OBDII:

  // Ler a porta Serial do Arduino
  if (Serial.available()) {
    char inByte = Serial.read();
    blueToothSerial.print(inByte);    //JANELA SERIAL PARA "Carriage return" (\r) e 38400 de baud rate
    
}
 
//PARA ENVIAR COMANDOS RECEBIDOS DO OBDII PARA O SERIAL MONITOR DO IDE:
 
if (blueToothSerial.available()) {
  char recvChar;
  boolean prompt;
  prompt=false;

    while(!prompt){                                     //while no prompt 
       while ((blueToothSerial.available()>0) && (!prompt)){       //while there is data and not prompt
        recvChar = blueToothSerial.read();            //read from elm
        Serial.print(recvChar); 
        if (recvChar==62) { prompt=true; Serial.println(""); Serial.println("------");}    //if received char is '>' then prompt is true
      }
   
     }
  
       
  }
   
    
}

Como teste, após emparelhamento e ligação,  podemos enviar o comando de Reset do ELM327 "ATZ":
Com este comando o ELM327 faz reset e apresenta a versão do firmware, podemos também testar enviar "ATRV" e como resposta é indicada a voltagem da alimentação/bateria.

Para uma lista completa dos comandos do ELM327 podemos aceder a este documento


Para facilitar a ligação e configuração do módulo HC-05 ao ELM327, podemos utilizar este último em casa sem ser necessário estar ligado ao carro. Temos de o ligar apenas a uma fonte de 12v, pelos pinos Vcc e Ground, tendo em conta a seguinte imagem:

 

Pino 4 do OBDII é o GROUND
Pino 16 do OBDII é o VCC (12v)

Após estas configurações temos então o módulo HC-05 pronto para estabelecer ligação automaticamente com o módulo slave ELM327, em Data Mode e sempre que se ligarem ambos os módulos.


################# LIGAÇÃO OBDII #################


Agora vamos aceder ao módulo ELM327, obter e trabalhar a informação que o carro nos dá. O objectivo é apresentar os seguintes dados num display de segmentos:
  • Valor actual das rotações por minuto (Engine RPM)
  • Valor da temperatura (Engine coolant temperature)
  • Velocidade em tempo real (Vehicle speed)
  • Percentagem do valor binário\torque actual do motor (Engine load value)
  • Voltagem da bateria 
Para começarmos a usar o ELM327 o mesmo tem de ser inicializado com os estes comandos ("\r" é obrigatório no final de cada comando):
"ATZ\r"        //Reset do módulo 
"ATSP0\r"      //Para detectar automaticamente o protocolo OBD usado pelo carro

Depois de inicializado podemos perguntar ao carro quais os parâmetros que disponibiliza (On-board diagnostics Parameter IDs), para isso enviamos os seguintes comandos:
"0100\r"       // Para obter os PID´s de 00-19
"0120\r"       // Para obter os PID´s de 20-39
"0140\r"       // Para obter os PID´s de 40-77
Geralmente estes comandos enviam-se apenas uma vez para o mesmo carro, pois apenas precisamos saber esta informação ao início (e que podemos guardar num ficheiro).

Na Wikipedia estão disponíveis todos os PID´s e as respectivas fórmulas\cálculos necessários: http://en.wikipedia.org/wiki/OBD-II_PIDs

Para o carro em questão (Clio III) e para confirmar os PID´s 00-19 disponíveis, temos de enviar o comando referido ("0100\r") e descodificar a resposta obtida (em Hexadecimal): 41 00 BE 3E B8 11

Com a ajuda do link indicado da Wikipedia, temos de converter todos os caracteres, após o 41 00, em código binário ficando assim:

B: 1011
E: 1110
3: 0011
E: 1110
B: 1011
8: 1000
1: 0001
1: 0001

Depois é só juntar todos os 1 (disponível) e 0 (indisponível) e confirmar quais são os PID´s, tendo em conta a tabela oficial disponível na Wikipedia. Por exemplo:

Para o modo 0x:
 PID: 010203 04 05 06 07 08 
 B,E,etc. 1 0 1 1 1 1 1 0

Continuando a tabela teríamos assim todos os PID´s de 00-19 suportados, neste exemplo apenas fomos até ao PID #8. No site podemos calcular automaticamente os PID´s, temos de escrever o resultado BE 3E B8 11 e depois escolher o modo 0, vamos ter a seguinte lista:

01 01 4 Number of trouble codes and I/M info Bit encoded. See below.
01 03 2 Fuel system status Bit encoded. See below.
01 04 1 Calculated engine load value 0 100 % A*100/255
01 05 1 Engine coolant temperature -40 215 °C A-40
01 06 1 Short term fuel % trim—Bank 1 -100 (Rich) 99.22 (Lean) % 0.7812 * (A-128)
01 07 1 Long term fuel % trim—Bank 1 -100 (Rich) 99.22 (Lean) % 0.7812 * (A-128)
01 0B 1 Intake manifold pressure 0 255 kPa (absolute) A
01 0C 2 Engine RPM 0 16,383.75 rpm ((A*256)+B)/4
01 0D 1 Vehicle speed 0 255 km/h A
01 0E 1 Timing advance -64 63.5 ° relative to #1 cylinder A/2 - 64
01 0F 1 Intake air temperature -40 215 °C A-40
01 11 1 Throttle position 0 100 % A*100/255
01 13 1 Oxygen sensors present [A0..A3] == Bank 1, Sensors 1-4. [A4..A7] == Bank 2...
01 14 2 Bank 1, Sensor 1: Oxygen sensor voltage, Short term fuel trim 00 1.2799.2 Volts% A * 0.005(B-128) * 0.7812 (if B==0xFF, sensor is not used in trim calc)
01 15 2 Bank 1, Sensor 2:Oxygen sensor voltage,Short term fuel trim 00 1.27599.2 Volts% A * 0.005(B-128) * 0.7812 (if B==0xFF, sensor is not used in trim calc)
01 1C 1 OBD standards this vehicle conforms to Bit encoded. See below.
01 20 4 PIDs supported 21-40 Bit encoded [A7..D0] == [PID 0x21..PID 0x40]

Confirmamos assim que conseguimos obter os valores pretendidos (rpm, temperatura, velocidade, binário) pois temos os respectivos PID´s disponíveis. 


Temos agora que enviar os comandos pretendidos e calcular se necessário o resultado:

 COMANDO Exemplo de RESPOSTA
RPM:     010C\r 41 0C 04 70
Temp:   0105\r 41 05 5A
Veloc.:  010D\r 41 0D 00
Load:     0104\r 41 04 ??

Alguns cálculos estão disponíveis aqui nestes exemplos: http://www.obdsol.com/articles/obd-software-development/reading-real-time-data/


Para testar foi utilizado um serial display Lcd 16X2 e um display de segmentos TM1638 com o seguinte código:

//Código adaptado do original http://forum.arduino.cc/index.php?topic=149453.0 feito por Kostas Kokoras 
//Code adapted from the original http://forum.arduino.cc/index.php?topic=149453.0 made ??by Kostas Kokoras

#include <math.h> //Para ter a função de arrendondamento (ROUND())
#include <TM1638.h>             //Biblioteca para o display de segmentos
#include <SoftwareSerial.h>     //Biblioteca para usar outros pinos de RX\TX para o módulo Bluetooth
#include <Timer.h>              //Biblioteca usada para obter a informação do OBDII através do método 'pulse' e não do delay normal do Arduino.


TM1638 module(6, 5, 4);        //Module data pin 6, clock pin 5 and strobe pin 4
String name = "RPm OBD2";      //Pata a mensagem inicial 
unsigned int rpmleds;         //holds the 8 leds values 

#define RxD 7                //Arduino pin connected to Tx of HC-05
#define TxD 8                //Arduino pin connected to Rx of HC-05

SoftwareSerial blueToothSerial(RxD,TxD); //SoftwareSerial mySerial(RX,TX); //3.3v entre o pino 8 do Arduino e o RX do módulo

char inByte;
boolean obd_error_flag;      //Variable for OBD connection error
#define OBD_CMD_RETRIES 3    //Number of retries for each OBD command in case of not receive prompt '>' char

//Variáveis para o procedimento de RPM:
boolean rpm_error_flag;      //Variable for RPM error
boolean rpm_retries;         //Variable for RPM cmd retries
unsigned int rpm;            //Variables for RPM
#define RPM_CMD_RETRIES 5    //Number of retries for RPM command
char recvCharLCD;
Timer tRPM;

//Variáveis para o procedimento de Temperatura:
boolean temp_error_flag;      //Variable for Temperatura error
boolean temp_retries;         //Variable for Temperatura cmd retries
int temp,temp_to_disp;        //Variables for Temperatura
#define TEMP_CMD_RETRIES 5    //Number of retries for Temperatura command

//Variáveis para o procedimento de Velocidade:
boolean velocidade_error_flag;      //Variable for Velocidade error
boolean velocidade_retries;         //Variable for Velocidade cmd retries
unsigned int velocidade;             //Variables for VelocidadePM
#define VELOCIDADE_CMD_RETRIES 5    //Number of retries for Velocidade command

//Variáveis para o procedimento de Load:
boolean load_error_flag;      //Variable for LoadLo error
boolean load_retries;         //Variable for Load cmd retries
int load;//Variables 
#define LOAD_CMD_RETRIES 5    //Number of retries for Load command

//Para o MENU:
byte oldbuttons; //DISPLAY SEGMENTOS
int Modo=0;

byte rpmdata = 0;                   // marker that new data are available
byte tempdata = 0;                  // marker that new data are available
byte velocidadedata = 0;            // marker that new data are available
byte loaddata = 0;                  // marker that new data are available

Timer tTemp;                        
Timer tVelocidade;
Timer tLoad;

void setup()
{

 //TESTE DISPLAY SEGMENTOS\LEDS: (4 Leds: RED \ 4 Leds: GREEN):
  module.setLED(TM1638_COLOR_RED, 0);  // set LED number x to red
  module.setLED(TM1638_COLOR_RED, 1);  // set LED number x to red
  module.setLED(TM1638_COLOR_RED, 2);  // set LED number x to red
  module.setLED(TM1638_COLOR_RED, 3);  // set LED number x to red
  module.setLED(TM1638_COLOR_GREEN, 4); // set LED number x to green
  module.setLED(TM1638_COLOR_GREEN, 5); // set LED number x to green
  module.setLED(TM1638_COLOR_GREEN, 6); // set LED number x to green
  module.setLED(TM1638_COLOR_GREEN, 7); // set LED number x to green

module.clearDisplay(); //Limpar os segmentos:
module.setupDisplay(true, 7); // where 7 is intensity (from 0~7) - to turn on every segment AND all the LEDs, use the following
delay(500);
module.setDisplayToString(name,0,0,FONT_DEFAULT); //Mensagem Inicial
delay(3000);
module.setLEDs(0b00000000 | 0b00000000 << 8);    //Desligar todos os LEDs

// initialize both serial ports:
 Serial.begin(9600);              //Serial LCD Itead (9600)
 blueToothSerial.begin(38400);    //HC-05 AT mode baud rate is 38400

 pinMode(RxD, INPUT);
 pinMode(TxD, OUTPUT);
   
 tRPM.every(250,rpm_calc);//Every 250ms read RPM value from OBD
 tTemp.every(250,temp_calc);//Every 250ms read TEMP value from OBD
 tVelocidade.every(250,velocidade_calc);//Every 250ms read VELOCIDADE value from OBD
 tLoad.every(250,load_calc);//Every 250ms read LOAD value from OBD

//Informação inicial no Serial LCD ITEAD 16x2:    
  Serial.print("sc;");             
    delay(10);
  Serial.print("sb1;");             
    delay(10);
  Serial.print("sd0,0;");        //ROW,COLUMN yy,xx
    delay(10);
  Serial.print("ssOBDII;");
   delay(10);
   
Serial.print("sd1,0;");        
delay(10);
Serial.print("ssa iniciar...;");
delay(1000);
Serial.print("sc;");             
delay(10);
Serial.print("sd0,0;");delay(10);
Serial.print("ssFASE;");delay(10);
Serial.print("sd1,0;");delay(10);
Serial.print("ss1/2;");delay(10);
  
obd_error_flag=false;     // obd error flag is false

send_OBD_cmd("ATZ");      //send to OBD ATZ, reset
delay(1000);

//Serial.print("sd1,0;");delay(10);
//Serial.print("ss2/7;");delay(10);
//send_OBD_cmd("ATE0");   // ECHO OFF (Para o OBDII não devolver os mesmos comandos enviados)
//delay(1000);

Serial.print("sd1,0;");delay(10);
Serial.print("ss2/2;");delay(10);
send_OBD_cmd("ATSP0");    //send ATSP0, protocol auto
delay(2000);

//Para quado se quiser obter os PID´s disponíveis;
//Serial.print("sd1,0;");delay(10);
//Serial.print("ss4/7;");delay(10);
//send_OBD_cmd("0100");     //send 0100, retrieve available pid's 00-19
//delay(2000);
//
//Serial.print("sd1,0;");delay(10);
//Serial.print("ss5/7;");delay(10);
//send_OBD_cmd("0120");     //send 0120, retrieve available pid's 20-39
//delay(2000);
//
//Serial.print("sd1,0;");delay(10);
//Serial.print("ss6/7;");delay(10);
//send_OBD_cmd("0140");     //send 0140, retrieve available pid's 40-??
//delay(2000);

//Teste para obter valor de RPM:
//Serial.print("sd1,0;");delay(10);
//Serial.print("ss7/7;");delay(10);
//send_OBD_cmd("010C1");    //send 010C1, RPM cmd
//delay(1000);
//Serial.print("sc;"); delay(10);
//Serial.print("sd0,0;"); delay(10);
//Serial.print("ssRPM:;"); delay(10);

//Menu inicial:
  Serial.print("sc;");            
    delay(10);
  Serial.print("sd0,0;");        //ROW,COLUMN: yy,xx
    delay(10);
  Serial.print("ssEscolha o MODO:;"); 
   delay(50);
  Serial.print("sd1,0;");        //ROW,COLUMN: yy,xx
   delay(50);
  Serial.print("ss1,2,3,4;"); 
   delay(50);
   
 
//----------------------------------------------------------//
//---------------------Send OBD Command---------------------//
//--------------------for initialitation--------------------//

void send_OBD_cmd(char *obd_cmd){

  char recvChar;
  
  boolean prompt;
  int retries;
 
   if (!(obd_error_flag)){                                        //if no OBD connection error
    
    prompt=false;
    retries=0;
    while((!prompt) && (retries<OBD_CMD_RETRIES)){                //while no prompt and not reached OBD cmd retries
      blueToothSerial.print(obd_cmd);                             //send OBD cmd
      blueToothSerial.print("\r");                                //send cariage return

      while (blueToothSerial.available() <= 0);                   //wait while no data from ELM
      
      while ((blueToothSerial.available()>0) && (!prompt)){       //while there is data and not prompt
        recvChar = blueToothSerial.read();          //read from elm
        Serial.println(recvChar);
       recvCharLCD+=recvChar;
       if (recvChar==62) prompt=true;            //if received char is '>' then prompt is true
     }
      retries=retries+1;                                          //increase retries
      delay(2000);
    }
    
    if (retries>=OBD_CMD_RETRIES) {                               // if OBD cmd retries reached
      obd_error_flag=true;          // obd error flag is true
       digitalWrite(13,HIGH);
    }
  }
}
 
 
void SerialLcdDisplay(String Mode, String Valor)
{
  Serial.print("sc;");            
    delay(10);
  Serial.print("sd0,0;");        //ROW,COLUMN: yy,xx
    delay(10);
  Serial.print("ss"+ Mode + ";"); 
   delay(50);
  Serial.print("sd1,10;");        //ROW,COLUMN: yy,xx
   delay(50);
  Serial.print("ss"+ Valor + ";"); 
   delay(50);
}
 
 
 
void loop() {
  
byte buttons=module.getButtons();
 
if (buttons != 0) {
  if (buttons != oldbuttons) {
      oldbuttons = buttons;

 switch(buttons)
 {
case 1:
//Serial.println("BOTAO 1"); delay(50);
Modo=1;
module.clearDisplay(); //CLEAR
SerialLcdDisplay("MODO1 - RPM", 0);
 break;
 case 2:
//Serial.println("BOTAO 2"); delay(50);
Modo=2;
module.clearDisplay(); //CLEAR
SerialLcdDisplay("MODO2 - TEMP", "C");
 break;
 case 4:
//Serial.println("BOTAO 3"); delay(50);
Modo=3;
module.clearDisplay(); //CLEAR
SerialLcdDisplay("MODO3 - VELOC.", "Km");
 break;
 case 8:
//Serial.println("BOTAO 4"); delay(50);
Modo=4;
module.clearDisplay(); //CLEAR
SerialLcdDisplay("MODO4 - LOAD", "%");
 break;
 case 16:
//Serial.println("BOTAO 5"); delay(50); //NÃO USADO
 break;
 case 32:
//Serial.println("BOTAO 6"); delay(50); //NÃO USADO
 break;
 case 64: 
//Serial.println("BOTAO 7"); delay(50); //NÃO USADO
 break;
 case 128:
//Serial.println("BOTAO 8"); delay(50); //NÃO USADO
 break;
 }
} }  
  
  
if (Modo==1) { //RPM
tRPM.update();  //update of timer for calling rpm_calc
if (rpmdata!=rpm){
    rpmdata=rpm;
    
   rpmleds = map(rpm,950,2600,0,8);    
   module.setDisplayToDecNumber(rpm, 0, false); // RPM ATUAL NO DISPLAY DE SEGMENTOS

   if (rpmleds==0){            
     module.setLEDs(0b00000000 | 0b00000000 << 8); //Todos desligados
   }
   if (rpmleds==1){
     module.setLEDs(0b00000000 | 0b00000001<< 8 ); //Da Direita para a esquerda no código e da esquerda para a direita no Display| 1º led da esquerda VERDE   
   }
   if (rpmleds==2){
     module.setLEDs(0b00000000 | 0b00000011<< 8 ); // 2 leds da esquerda VERDE  
   }
   if (rpmleds==3){
     module.setLEDs(0b00000000 | 0b00000111<< 8 ); // 3 leds da esquerda VERDE  
   }
   if (rpmleds==4){
     module.setLEDs(0b00000000 | 0b00001111<< 8); // 4 leds da esquerda VERDE  
   }
   if (rpmleds==5){
     module.setLEDs(0b00000000 | 0b00011111 << 8); // 5 leds da esquerda VERDE  
   }
   if (rpmleds==6){
     module.setLEDs(0b00100000 | 0b00011111<< 8 ); // 5 leds da esquerda VERDE + 1 VERMELHO  
   }
   if (rpmleds==7){
     module.setLEDs(0b01100000 | 0b00011111<<8 ); // 5 leds da esquerda VERDE + 2 VERMELHO  
   }
   if (rpmleds==8){
     //module.setLEDs(0b11100000 | 0b000011111<<8 ); // 5 leds da esquerda VERDE + 3 VERMELHO  
     module.setLEDs(0b11111111 | 0b111111111<<8 );}  //TODOS OS LEDS LIGADOS a VERDE + VERMELHO
   } 
}

if (Modo==2) { //TEMPERATURA
tTemp.update();  //update of timer for calling temperatura_calc

if (tempdata!=temp){
    tempdata=temp;
      
   rpmleds = map(temp,10,200,0,8);    // Colocar potênciometro para definir valor |distributes the TEMPERATURA level to the 8 leds 
   module.setDisplayToDecNumber(temp, 0, false); // TEMPERATURA ATUAL NO DISPLAY DE SEGMENTOS
   //ou---> module.setDisplayToString (String(temp) + " C", 0, false); 
    
   if (rpmleds==0){            
     module.setLEDs(0b00000000 | 0b00000000 << 8); //Todos desligados
   }
   if (rpmleds==1){
     module.setLEDs(0b00000000 | 0b00000001<< 8 ); //Da Direita para a esquerda no código e da esquerda para a direita no Display| 1º led da esquerda VERDE   
   }
   if (rpmleds==2){
     module.setLEDs(0b00000000 | 0b00000011<< 8 ); // 2 leds da esquerda VERDE  
   }
   if (rpmleds==3){
     module.setLEDs(0b00000000 | 0b00000111<< 8 ); // 3 leds da esquerda VERDE  
   }
   if (rpmleds==4){
     module.setLEDs(0b00000000 | 0b00001111<< 8); // 4 leds da esquerda VERDE  
   }
   if (rpmleds==5){
     module.setLEDs(0b00000000 | 0b00011111 << 8); // 5 leds da esquerda VERDE  
   }
   if (rpmleds==6){
     module.setLEDs(0b00100000 | 0b00011111<< 8 ); // 5 leds da esquerda VERDE + 1 VERMELHO  
   }
   if (rpmleds==7){
     module.setLEDs(0b01100000 | 0b00011111<<8 ); // 5 leds da esquerda VERDE + 2 VERMELHO  
   }
   if (rpmleds==8){
     //module.setLEDs(0b11100000 | 0b000011111<<8 ); // 5 leds da esquerda VERDE + 3 VERMELHO  
     module.setLEDs(0b11111111 | 0b111111111<<8 );} //TODOS OS LEDS LIGADOS a VERDE + VERMELHO
   } 
}

if (Modo==3) { //VELOCIDADE
tVelocidade.update();  //update of timer for calling velocidade_calc

if (velocidadedata!=velocidade){
    velocidadedata=velocidade;
      
   rpmleds = map(velocidade,10,210,0,8);    // Colocar potênciometro para definir valor |distributes the VELOCIDADE level to the 8 leds 

    module.setDisplayToDecNumber(velocidade, 0, false); // RPM ATUAL NO DISPLAY DE SEGMENTOS

   if (rpmleds==0){            
     module.setLEDs(0b00000000 | 0b00000000 << 8); //Todos desligados
   }
   if (rpmleds==1){
     module.setLEDs(0b00000000 | 0b00000001<< 8 ); //Da Direita para a esquerda no código e da esquerda para a direita no Display| 1º led da esquerda VERDE   
   }
   if (rpmleds==2){
     module.setLEDs(0b00000000 | 0b00000011<< 8 ); // 2 leds da esquerda VERDE  
   }
   if (rpmleds==3){
     module.setLEDs(0b00000000 | 0b00000111<< 8 ); // 3 leds da esquerda VERDE  
   }
   if (rpmleds==4){
     module.setLEDs(0b00000000 | 0b00001111<< 8); // 4 leds da esquerda VERDE  
   }
   if (rpmleds==5){
     module.setLEDs(0b00000000 | 0b00011111 << 8); // 5 leds da esquerda VERDE  
   }
   if (rpmleds==6){
     module.setLEDs(0b00100000 | 0b00011111<< 8 ); // 5 leds da esquerda VERDE + 1 VERMELHO  
   }
   if (rpmleds==7){
     module.setLEDs(0b01100000 | 0b00011111<<8 ); // 5 leds da esquerda VERDE + 2 VERMELHO  
   }
   if (rpmleds==8){
     //module.setLEDs(0b11100000 | 0b000011111<<8 ); // 5 leds da esquerda VERDE + 3 VERMELHO  
     module.setLEDs(0b11111111 | 0b111111111<<8 );} //TODOS OS LEDS LIGADOS a VERDE + VERMELHO
   } 
}

if (Modo==4) { //ENGINE LOAD %
tLoad.update();  //update of timer for calling load_calc

if (loaddata!=load){
    loaddata=load;
      
   rpmleds = map(load,10,100,0,8);    // Colocar potênciometro para definir valor |distributes the LOAD level to the 8 leds 
   module.setDisplayToDecNumber(load, 0, false); 

   if (rpmleds==0){            
     module.setLEDs(0b00000000 | 0b00000000 << 8); //Todos desligados
   }
   if (rpmleds==1){
     module.setLEDs(0b00000000 | 0b00000001<< 8 ); //Da Direita para a esquerda no código e da esquerda para a direita no Display| 1º led da esquerda VERDE   
   }
   if (rpmleds==2){
     module.setLEDs(0b00000000 | 0b00000011<< 8 ); // 2 leds da esquerda VERDE  
   }
   if (rpmleds==3){
     module.setLEDs(0b00000000 | 0b00000111<< 8 ); // 3 leds da esquerda VERDE  
   }
   if (rpmleds==4){
     module.setLEDs(0b00000000 | 0b00001111<< 8); // 4 leds da esquerda VERDE  
   }
   if (rpmleds==5){
     module.setLEDs(0b00000000 | 0b00011111 << 8); // 5 leds da esquerda VERDE  
   }
   if (rpmleds==6){
     module.setLEDs(0b00100000 | 0b00011111<< 8 ); // 5 leds da esquerda VERDE + 1 VERMELHO  
   }
   if (rpmleds==7){
     module.setLEDs(0b01100000 | 0b00011111<<8 ); // 5 leds da esquerda VERDE + 2 VERMELHO  
   }
   if (rpmleds==8){
     //module.setLEDs(0b11100000 | 0b000011111<<8 ); // 5 leds da esquerda VERDE + 3 VERMELHO  
     module.setLEDs(0b11111111 | 0b111111111<<8 );} //TODOS OS LEDS LIGADOS a VERDE + VERMELHO
   } 
}
 
}

//-----------------------------------------------------//
//----------Retrieve RPM value from OBD----------------//
//---------convert it to readable number---------------//
void rpm_calc(){
   boolean prompt,valid;  
   char recvChar;
   char bufin[15];
   int i;
   

  if (!(obd_error_flag)){                                   //if no OBD connection error
  
     valid=false;
     prompt=false;
     blueToothSerial.print("010C1");                        //send to OBD PID command 010C is for RPM, the last 1 is for ELM to wait just for 1 respond (see ELM datasheet)
     blueToothSerial.print("\r");                           //send to OBD cariage return char
     while (blueToothSerial.available() <= 0);              //wait while no data from ELM
     i=0;
     while ((blueToothSerial.available()>0) && (!prompt)){  //if there is data from ELM and prompt is false
       recvChar = blueToothSerial.read();                   //read from ELM
       if ((i<15)&&(!(recvChar==32))) {                     //the normal respond to previus command is 010C1/r41 0C ?? ??>, so count 15 chars and ignore char 32 which is space   | GRCBYTE: ele ignora o "/r"
         bufin[i]=recvChar;                                 //put received char in bufin array
         i=i+1;                                             //increase i
       }  
       if (recvChar==62) prompt=true;                       //if received char is 62 which is '>' then prompt is true, which means that ELM response is finished 
     }

    if ((bufin[6]=='4') && (bufin[7]=='1') && (bufin[8]=='0') && (bufin[9]=='C')){ //if first four chars after our command is 410C
      valid=true;                                                                  //then we have a correct RPM response
    } else {
      valid=false;                                                                 //else we dont
    }
    if (valid){                                                                    //in case of correct RPM response
      rpm_retries=0;                                                               //reset to 0 retries
      rpm_error_flag=false;                                                        //set rpm error flag to false
      
     //start calculation of real RPM value
     //RPM is coming from OBD in two 8bit(bytes) hex numbers for example A=0B and B=6C
     //the equation is ((A * 256) + B) / 4, so 0B=11 and 6C=108
     //so rpm=((11 * 256) + 108) / 4 = 731 a normal idle car engine rpm
      rpm=0;                                                                                            
      for (i=10;i<14;i++){                              //in that 4 chars of bufin array which is the RPM value
        if ((bufin[i]>='A') && (bufin[i]<='F')){        //if char is between 'A' and 'F'
          bufin[i]-=55;                                 //'A' is int 65 minus 55 gives 10 which is int value for hex A
        } 
         
        if ((bufin[i]>='0') && (bufin[i]<='9')){        //if char is between '0' and '9'
          bufin[i]-=48;                                 //'0' is int 48 minus 48 gives 0 same as hex
        }
        
        rpm=(rpm << 4) | (bufin[i] & 0xf);              //shift left rpm 4 bits and add the 4 bits of new char
       
      }
      rpm=rpm >> 2;                                     //finaly shift right rpm 2 bits, rpm=rpm/4
    }
      
    }
    if (!valid){                                              //in case of incorrect RPM response
      rpm_error_flag=true;                                    //set rpm error flag to true
      rpm_retries+=1;                                         //add 1 retry
      rpm=0;     
      //set rpm to 0
      //Serial.println("RPM_ERROR");
      if (rpm_retries>=RPM_CMD_RETRIES) obd_error_flag=true;  //if retries reached RPM_CMD_RETRIES limit then set obd error flag to true
    }
    
      if ((obd_error_flag==true)){rpm=0; rpm_retries=0;}                              //if no OBD connection error !GrcByte!
   
}

//[TEMPERATURA & VELOCIDADE & ENGINE LOAD]:

//TEMPERATURA:
void temp_calc(){
   boolean prompt,valid;  
   char recvChar;
   char bufin[12];
   int i;

  if (!(obd_error_flag)){                                   //if no OBD connection error
     valid=false;
     prompt=false;
     blueToothSerial.print("0105");                        //send to OBD PID command 0105 is for Temperature
     blueToothSerial.print("\r");                           //send to OBD cariage return char
     while (blueToothSerial.available() <= 0);              //wait while no data from ELM
     i=0;
     while ((blueToothSerial.available()>0) && (!prompt)){  //if there is data from ELM and prompt is false
       recvChar = blueToothSerial.read();                   //read from ELM
       if ((i<12)&&(!(recvChar==32))) {                     //the normal respond to previus command is 0105/r41 05 ??>, so count 12 chars and ignore char 32 which is space
         bufin[i]=recvChar;                                 //put received char in bufin array
         i=i+1;                                             //increase i
       }  
       if (recvChar==62) prompt=true;                       //if received char is 62 which is '>' then prompt is true, which means that ELM response is finished 
     }

    if ((bufin[5]=='4') && (bufin[6]=='1') && (bufin[7]=='0') && (bufin[8]=='5')){ //if first four chars after our command is 4105
      valid=true;                                                                  //then we have a correct TEMP response
    } else {
      valid=false;                                                                 //else we dont
    }
    if (valid){                                                                    //in case of correct TEMP response
      temp_retries=0;                                                               //reset to 0 retries
      temp_error_flag=false;                                                        //set rpm error flag to false
      
// Cálculo
String tempHex(bufin[9]); 
String tempHex2(bufin[10]); 
String tempHexTotal=tempHex+tempHex2;
temp=hexToDec(tempHexTotal);
temp-=40; 
     }
    if (!valid){                                              //in case of incorrect TEMP response
      temp_error_flag=true;                                    //set rpm error flag to true
      temp_retries+=1;                                         //add 1 retry
      temp=0;                      //set temperatura to 0
            
      //Serial.println("RPM_ERROR");
      if (temp_retries>=TEMP_CMD_RETRIES) obd_error_flag=true;  //if retries reached TEMP_CMD_RETRIES limit then set obd error flag to true
    }    
      if ((obd_error_flag==true)){temp=0; temp_retries=0;}                              //if no OBD connection error !GrcByte!
   
}
}

//VELOCIDADE:
void velocidade_calc(){
   boolean prompt,valid;  
   char recvChar;
   char bufin[12];
   int i;

  if (!(obd_error_flag)){                                   //if no OBD connection error
 
     valid=false;
     prompt=false;
     blueToothSerial.print("010D");                        //send to OBD PID command 010D is for VELOCIDADE
     blueToothSerial.print("\r");                           //send to OBD cariage return char
     while (blueToothSerial.available() <= 0);              //wait while no data from ELM
     i=0;
     while ((blueToothSerial.available()>0) && (!prompt)){  //if there is data from ELM and prompt is false
       recvChar = blueToothSerial.read();                   //read from ELM
       if ((i<12)&&(!(recvChar==32))) {                     //the normal respond to previus command is 010D/r41 0D ??>, so count 12 chars and ignore char 32 which is space
         bufin[i]=recvChar;                                 //put received char in bufin array
         i=i+1;                                             //increase i
       }  
       if (recvChar==62) prompt=true;                       //if received char is 62 which is '>' then prompt is true, which means that ELM response is finished 
     }

    if ((bufin[5]=='4') && (bufin[6]=='1') && (bufin[7]=='0') && (bufin[8]=='D')){ //if first four chars after our command is 410D
      valid=true;                                                                  //then we have a correct VELOCIDADE response
    } else {
      valid=false;                                                                 //else we dont
    }
    if (valid){                                                                    //in case of correct VELOCIDADE response
      velocidade_retries=0;                                                               //reset to 0 retries
      velocidade_error_flag=false;                                                        //set rpm error flag to false
      
//Cáluclo:
String velocidadeHex(bufin[9]); 
String velocidadeHex2(bufin[10]); 
String velocidadeHexTotal=velocidadeHex+velocidadeHex2;
velocidade=hexToDec(velocidadeHexTotal);

     }
    if (!valid){                                              //in case of incorrect VELOCIDADE response
      velocidade_error_flag=true;                                    //set rpm error flag to true
      velocidade_retries+=1;                                         //add 1 retry
      velocidade=0;                                                  //set velocidade to 0
      //Serial.println("RPM_ERROR");
      if (velocidade_retries>=VELOCIDADE_CMD_RETRIES) obd_error_flag=true;  //if retries reached VELOCIDADE_CMD_RETRIES limit then set obd error flag to true
    }    
      if ((obd_error_flag==true)){velocidade=0; velocidade_retries=0;}                              //if no OBD connection error !GrcByte!
   
}
}

//ENGINE LOAD
void load_calc(){
   boolean prompt,valid;  
   char recvChar;
   char bufin[12];
   int i;
  
  if (!(obd_error_flag)){                                   //if no OBD connection error
     valid=false;
     prompt=false;
     blueToothSerial.print("0104");                        //send to OBD PID command 0104 is for LOAD
     blueToothSerial.print("\r");                           //send to OBD cariage return char
     while (blueToothSerial.available() <= 0);              //wait while no data from ELM
     i=0;
     while ((blueToothSerial.available()>0) && (!prompt)){  //if there is data from ELM and prompt is false
       recvChar = blueToothSerial.read();                   //read from ELM
       if ((i<12)&&(!(recvChar==32))) {                     //the normal respond to previus command is 0104/r41 04 ??>, so count 12 chars and ignore char 32 which is space
         bufin[i]=recvChar;                                 //put received char in bufin array
         i=i+1;                                             //increase i
       }  
       if (recvChar==62) prompt=true;                       //if received char is 62 which is '>' then prompt is true, which means that ELM response is finished 
     }

    if ((bufin[5]=='4') && (bufin[6]=='1') && (bufin[7]=='0') && (bufin[8]=='4')){ //if first four chars after our command is 410D
      valid=true;                                                                  //then we have a correct LOAD response
    } else {
      valid=false;                                                                 //else we dont
    }
    if (valid){                                                                    //in case of correct LOAD response
      load_retries=0;                                                               //reset to 0 retries
      load_error_flag=false;                                                        //set LOAD error flag to false
      
//Cálculo:
String loadHex(bufin[9]); 
String loadHex2(bufin[10]); 
String loadHexTotal=loadHex+loadHex2;
int DecimalDecode=hexToDec(loadHexTotal);
load=round((float(DecimalDecode)/255)*100); //Arredonda e devolve valor final
     }
    if (!valid){                                              //in case of incorrect LOAD response
      load_error_flag=true;                                    //set LOAD error flag to true
      load_retries+=1;                                         //add 1 retry
      load=0;                                                  //set LOAD to 0
      //Serial.println("RPM_ERROR");
      if (load_retries>=LOAD_CMD_RETRIES) obd_error_flag=true;  //if retries reached LOAD_CMD_RETRIES limit then set obd error flag to true
    }    
      if ((obd_error_flag==true)){load=0; load_retries=0;}                              //if no OBD connection error !GrcByte!   
}
}

// Converting from Hex to Decimal:
// NOTE: This function can handle a positive hex value from 0 - 65,535 (a four digit hex string).
//       For larger/longer values, change "unsigned int" to "long" in both places.
unsigned int hexToDec(String hexString) {
  
  unsigned int decValue = 0;
  int nextInt;
  
  for (int i = 0; i < hexString.length(); i++) {
    
    nextInt = int(hexString.charAt(i));
    if (nextInt >= 48 && nextInt <= 57) nextInt = map(nextInt, 48, 57, 0, 9);
    if (nextInt >= 65 && nextInt <= 70) nextInt = map(nextInt, 65, 70, 10, 15);
    if (nextInt >= 97 && nextInt <= 102) nextInt = map(nextInt, 97, 102, 10, 15);
    nextInt = constrain(nextInt, 0, 15);
    
    decValue = (decValue * 16) + nextInt;
  }
  
  return decValue;
}

Teste apenas com valor de RPM:




Comments