Can Bus Shield [RISOLTO]

Buongiorno a tutti,
ho da poco acquistato uno shield cinese per analizzare i dati in CAN-Bus della mia vettura.
Vorrei creare una applicazione embedded capace di sniffare i dati dal CAN e mostrarli su un display secondario.
Per farlo ho deciso di utilizzare questa scheda http://www.elecfreaks.com/estore/can-bus-shield.html semplicemente per una questione di costo estremamente ridotto ~13€ comprese spedizioni.
A differenza di quelle della Sparkufn o Seedstudio, variano i pin del connettore a 9Pin, per il resto sembrano perfettamente uguali.
Connetto lo shield semplicemente utilizzando i due pin Can_H e Can_L.
Utilizzo lo sketch di esempio per analizzare i dati presenti sul Can di un furgone Nissan :

#include <SPI.h>
#include "mcp_can.h"


// the cs pin of the version after v1.1 is default to D9
// v0.9b and v1.0 is default D10
const int SPI_CS_PIN = 10;

MCP_CAN CAN(SPI_CS_PIN);                                    // Set CS pin

void setup()
{
    Serial.begin(115200);

    while (CAN_OK != CAN.begin(CAN_500KBPS))              // init can bus : baudrate = 500k
    {
        Serial.println("CAN BUS Shield init fail");
        Serial.println(" Init CAN BUS Shield again");
        delay(100);
    }
    Serial.println("CAN BUS Shield init ok!");
}


void loop()
{
    unsigned char len = 0;
    unsigned char buf[8];

    if(CAN_MSGAVAIL == CAN.checkReceive())            // check if data coming
    {
        CAN.readMsgBuf(&len, buf);    // read data,  len: data length, buf: data buf

        unsigned char canId = CAN.getCanId();
        
        Serial.println("-----------------------------");
        Serial.print("Get data from ID: ");
        Serial.println(canId, HEX);

        for(int i = 0; i<len; i++)    // print the data
        {
            Serial.print(buf[i], HEX);
            Serial.print("\t");
        }
        Serial.println();
    }
}

Quello che ottengo è una serie di dati elencati in base all ID fornito dal CAN.
Tra tutti questi non riesco a trovare quelli che per ovvie ragioni sono standard su tutte le vetture, come i Giri motori (PID 0x0C) o i dati del sensore MAF (PID 0x10)…su questo particolare veicolo riesco a trovare solo il pid relativo alla velocità (PID 0x0D).
Tutti i riferimenti possono essere trovati qui
So che i valori viaggiano sull CAN sia per studi effettuati in precedenza sia per un confronto con uno strumento professionale capace di leggere l’OBD con il quale riesco a leggere più di 20 informazioni.
L’ho provato anche su una Smart ma ottengo una situazione simile, con la differenza che su quest’altro veicolo leggo solo la temperatura dell’acqua.
Sapreste darmi qualche consiglio per ottenere quello che cerco?
Ringrazio chiunque riesca ad aiutarmi
— ricordati di usare i tag CODE non i QUOTE. Ho corretto io :wink: - gpb01

Non conosco ne le librerie ne le funzioni ne la shield ma nello steck vedo la variabile len inizializzata a zero e poi passata alla funzione CAN.readMsgBuf().Non dovresti passargli un valore positivo al massimo pari alla dimensione del buffer per poter funzionare?

mario55: vedo la variabile len inizializzata a zero e poi passata alla funzione CAN.readMsgBuf().Non dovresti passargli un valore positivo al massimo pari alla dimensione del buffer per poter funzionare?

No, è giusto così ... vedi che passa il puntatore a len ? Perché è la funzione chiamata che ritorna dentro len il numero di caratteri messi dentro a buf[].

Guglielmo

Rebel_88: A differenza di quelle della Sparkufn o Seedstudio, variano i pin del connettore a 9Pin, per il resto sembrano perfettamente uguali.

In realtà, se guardi lo schema di quella di SparkFun, vedi che sulla loro hai DUE possibili configurazioni di pin (C*AN e OBD-II*) ... ... una identica alla tua (OBD-II) con CAN-H sul pin 3, CAN-L sul pin 5 e GND sul pin 2, l'altra (CAN) ha il CAN-H sul pin 7, il CAN-L sul pin 2 ed il GND sul pin 3.

Quindi ... tu stai usando il lo standard OBD-II.

Guglielmo

La libreria a corredo di quella scheda fornisce due esempi ... receive e send ... ... perché, per cominciare, non usi l'esempio di receive che si suppone essere funzionante, visto che viene dato a corredo della libreria ?

Guglielmo

Buongiorno gpb01, grazie per le risposte, non avevo notato quella differenza negli schemi.

n realtà, se guardi lo schema di quella di SparkFun, vedi che sulla loro hai DUE possibili configurazioni di pin (CAN e OBD-II) ... ... una identica alla tua (OBD-II) con CAN-H sul pin 3, CAN-L sul pin 5 e GND sul pin 2, l'altra (CAN) ha il CAN-H sul pin 7, il CAN-L sul pin 2 ed il GND sul pin 3.

Quindi ... tu stai usando il lo standard OBD-II.

Però io sto utilizzando una coppia di cavi direttamente all'uscita di CAN_H e CAN_L...quindi non il connettore DB9.

Forse ho sbagliato qualcosa nella teoria, da quello che so : l'OBD II è uno standard sottoinsieme dello standard CAN, dall' OBD io posso fare la diagnosi dei problemi ma contemporaneamente acquisire un certo numero di dati in transito(come giri motore, velocità temperatura dei liquidi...). Mentre il CAN è lo standard principale ed ogni vettura può avere dei campi CAN propri di ogni marca o modello. Ho sbagliato?

Tornando alla shield, ho provato la libreria receive ma ottengo gli stessi dati dell'altro codice, ovvero mi fornisce la lunghezza dei dati del frame ricevuto, ho aggiunto l'individuazione del PID, e in fine i byte ricevuti. Tra questi mancano quelli che io reputo standard cioè giri motore, maf, ecc... Ora provo a confrontare gli schemi della Sparkfun con quelli del mio shield in modo da permettere l'analisi del CAN, perchè il mio scopo è creare una mia scheda da poter integrare in un modulo e non l'utilizzo dello shield. Sono aperto ad ogni suggerimento o indicazione.

Rebel_88: Sono aperto ad ogni suggerimento o indicazione.

Purtroppo NON sono un esperto di CAN/OBD II ... quindi sarà meglio aspettare qualcuno del settore ... ::)

Guglielmo

Salve a tutti,
sono riuscito a risolvere il mio problema e quindi ho pensato di postare la soluzione momentanea.
Semplicemente non utilizzo la libreria Canbus.cpp, o meglio non utilizzo le funzioni per ricevere dati che sono presenti in quella libreria.
Ho utilizzato direttamente l’accesso all’mcp2515 in questo modo :

#include <Canbus.h>  // don't forget to include these
#include <defaults.h>
#include <global.h>
#include <mcp2515.h>
#include <mcp2515_defs.h>
#include <pids.h>
#include <stdio.h>


char buffer[456];
char data_buffer[456];
//unsigned char pid=0x0C; //test per verificare 0x0C
int CAN_data=0;
int data[255];
/* data[255] è un array di parametri 
 * 0x0C = 12 data[12] = RPM  
 * 0x0D = 13 data[13] = SPEED
 * 0x0E = 14 data[14] = TIMING ADVANCE
 * 0x10 = 16 data[16] = MAF
 * 0x11 = 17 data[17] = THROTTLE POSITION
 * 0x45 = 69 data[69] = RELATIVE THROTTLE POSITION 
 * 0x04 = 4  data[4] = ENGINE LOAD
 * 0x5C = 92 data[92] = OIL TEMPERATURE
 */
unsigned char pid[7] ={4, 12, 13, 14, 16, 17, 92}; //pid presenti sulla mia auto
int i=0;

void setup() {
  Serial.begin(9600);

  /* AVAILABLE OPTION FOR CAN SPEED ARE :
   * CANSPEED_125 //CAN speed at 125Kbps
   * CANSPEED_250 //CAN speed at 250Kbps
   * CANSPEED_500 //CAN speed at 500Kbps
   */
  if(Canbus.init(CANSPEED_500))  /* Initialise MCP2515 CAN controller at the specified speed */
  {
    Serial.println("CAN Init ok");
  } else
  {
    Serial.println("Can't init CAN");
  } 
   
  delay(1000);
}

//********************************Main Loop*********************************//

void loop()
{ 
  tCAN message;
  //char *buffer;
  
  float engine_data;
  int timeout = 0;
  char message_ok = 0;
  // Prepair message
  message.id = PID_REQUEST;
  message.header.rtr = 0;
  message.header.length = 8;
  message.data[0] = 0x02;
  message.data[1] = 0x01;
  message.data[2] = pid[i];
  message.data[3] = 0x00;
  message.data[4] = 0x00;
  message.data[5] = 0x00;
  message.data[6] = 0x00;
  message.data[7] = 0x00;           
  

  mcp2515_bit_modify(CANCTRL, (1<<REQOP2)|(1<<REQOP1)|(1<<REQOP0), 0);
  
  //Serial.print("PID : "); Serial.print(pid,HEX); Serial.print("\t");
  
  if (mcp2515_send_message(&message)) {
  }
  
  while(timeout < 4000)
  {
    timeout++;
        if (mcp2515_check_message()) 
        {
          

          if (mcp2515_get_message(&message)) 
          {
              if((message.id == PID_REPLY) && (message.data[2] == pid[i])) // Check message is the reply and its the right PID
              {
                
                //Serial.print ("PID: ");Serial.print(message.data[2],HEX);Serial.print("\t");
               /* Serial.print(message.data[0],HEX);Serial.print("\t");
                Serial.print(message.data[1],HEX);Serial.print("\t");
                Serial.print(message.data[2],HEX);Serial.print("\t");
                Serial.print(message.data[3],HEX);Serial.print("\t");
                Serial.print(message.data[4],HEX);Serial.print("\t");
                Serial.print(message.data[5],HEX);Serial.print("\t");
                Serial.print(message.data[6],HEX);Serial.print("\t");
                Serial.println(message.data[7],HEX);
                */
                Conversione(pid[i], message.data[3],message.data[4],message.data[5],message.data[6]);
               // Serial.println(((message.data[3]* 256) +  message.data[4])/4);
               //Serial.write(Conversione(pid,buffer));
              }      
          }
        }
    
  }
  
i++;
  if(i>5)
    i=0;
  //Serial.println();
  //Print(); 
   delay(50);

}

void Conversione (unsigned char pid,  char A, char B, char C, char D)
{
  
  switch(pid)
  {
    case ENGINE_RPM:  //   ((A*256)+B)/4    [RPM]
      data[12] = ((A* 256) +  B)/4;
      //Serial.print(data[12]);Serial.print(" rpm");
    break;

    case VEHICLE_SPEED:    // A          [km]
      data[13] =  A;
      //Serial.print(data[13]);Serial.println("km");
    break;

    case MAF_SENSOR:         // ((256*A)+B) / 100  [g/s]
      data[16] =  ((A*256) + B)/100;
      //Serial.print(data[16]);Serial.println(" g/s");
     break;
    
     case THROTTLE:       // Throttle Position
     data[17] = (A*100)/255;
     //Serial.print(data[16]);Serial.println(" %");
     break;

     case ENGINE_LOAD:    //Engine Load
     data[4] = (A*100)/255;
     //Serial.print(data[17]);Serial.println(" %");
     break;

     case ENGINE_COOLANT_TEMP:  //A-40
     data[5] = A-40;
     //Serial.print(data[5]);Serial.println(" °C");
     break;

     case ENGINE_OIL_TEMP:   //  A - 40
     data[92] = A-40;
     //Serial.print(data[92]);Serial.println(" °C");
     break;

     case TIMING_ADVANCE: //(A-128)/2
     data[14] = (A-128)/2;
     //Serial.print(data[14]);Serial.println(" °");
     break;
     
    }
    Print();
  }

void Print()
{
  /* data[255] è un array di parametri 
 * 0x0C = 12 data[12] = RPM  
 * 0x0D = 13 data[13] = SPEED
 * 0x0E = 14 data[14] = TIMING ADVANCE
 * 0x10 = 16 data[16] = MAF
 * 0x11 = 17 data[17] = THROTTLE POSITION
 * 0x04 = 4  data[4] = ENGINE LOAD
 * 0x5C = 92 data[92] = OIL TEMPERATURE
 */
  Serial.println("PARAMETRI VETTURA");
  Serial.println("*************************************************");
  
  Serial.print("CARICO MOTORE          :"); Serial.print(data[4]);
  Serial.print("\t");
  Serial.print("POSIZIONE ACCELARATORE : ");Serial.println(data[17]);
  
  Serial.print("GIRI MOTORE            :"); Serial.print(data[12]);
  Serial.print("\t");
  Serial.print("VELOCITA'              : ");Serial.println(data[13]);

  Serial.print("ANTICIPO               :"); Serial.print(data[14]);
  Serial.print("\t");
  Serial.print("MAF                    : ");Serial.println(data[16]);
  
  
  }

In questo modo sono riuscito a trovare quali sono i pid che posso leggere da una vettura in mio possesso.

A questo punto ho dei piccolissimi problemi di algoritmo.
Per prima cosa vorrei convertire da esadecimale a binario 4 byte che ricevo da una trama del CAN bus, ad esempio se ricevo BE 1F A8 13 voglio convertirla in 1 011 1110 0001 1111 1010 1000 0001 0011, esiste una funzione già presente in arduino?

Come secondo problema ho una stampa a monitor errata del frame in data[14] cioè TIMING_ADVANCE. Dalla lettura dei dati RAW il mio frame ha la forma :
3 41 D 0 FF FF FF FF
dove il quarto byte che chiamo A = 0 è quello che devo utilizzare per la conversione dell’unità di misura secondo la formula :
(A-128)/2
(in base a quanto riportato su OBD-II PIDs - Wikipedia)
quindi il suo range varia da -64 a 63.5 °, invece ottengo a schermo dei valori attorno a -100.
Secondo voi sto sbagliando qualcosa nel formato dei dati?