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:

 

   

Configurar o módulo HC-05, usando o modo AT, para conseguirmos estabelecer ligação ao ELM327:

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 já 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

Info sobre os comandos existentes: https://docs.google.com/viewer?a=v&pid=sites&srcid=ZGVmYXVsdGRvbWFpbnxncmNieXRlcHJvamVjdG9zfGd4OjFmNDJjYjUyZWFhYzRlYTU

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:

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:

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:

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: