20120709

Arduino + Canbus shield + GPS Receiver


Ingredients:
Also required:

  • soldering iron
  • patience & basic soldering skills (to solder the headers onto the Canbus shield)
  • arduino software
  • computer, car/van etc.

Things to keep in mind:

  • when you want to stop logging: press on the joystick until the leds go out so that the file on the sd card is properly closed
  • make sure to reset if you plugin before starting the engine, not all pids are available at that point
  • with the engine off my engine runs at 280 rpm...
  • powered from the canbus, so i disconnect before leaving the vehicle in case it would drain the battery


Still todo:

  • solder on the lcd display, build an interface that shows fuel consumption
  • see if there is a more efficient way to log all available sensors (on startup  check which pids are available and then log all of the available ones)
  • print a case
  • sleep mode: if the engine is off (or rpm is 280 for a couple of times in a row), switch all leds off, switch off the gps (if possible), check every n seconds if the engine was started etc.





The code (still in progress...):

/* Welcome to the ECU Reader project. This sketch uses the Canbus library.
It requires the CAN-bus shield for the Arduino. This shield contains the MCP2515 CAN controller and the MCP2551 CAN-bus driver.
A connector for an EM406 GPS receiver and an uSDcard holder with 3v level convertor for use in data logging applications.
The output data can be displayed on a serial LCD.


The SD test functions requires a FAT16 formated card with a text file of LOG00.TXT in the card.




SK Pang Electronics www.skpang.co.uk
v4.0 04-03-12 Updated for Arduino 1.0
v3.0 21-02-11  Use library from Adafruit for sd card instead.


*/


#include <SD.h>        /* Library from Adafruit.com */
//#include <SdFatUtil.h>
#include <SoftwareSerial.h>
#include <Canbus.h>


Sd2Card card;
SdVolume volume;
SdFile root;
SdFile file;


/* Define Joystick connection */
#define UP     A1
#define RIGHT  A2
#define DOWN   A3
#define CLICK  A4
#define LEFT   A5


char buffer[512];  //Data will be temporarily stored to this buffer before being written to the file
char tempbuf[15];
int read_size=0;   //Used as an indicator for how many characters are read from the file
int D10 = 10;
int LED2 = 8;
int LED3 = 7;




SoftwareSerial mySerial =  SoftwareSerial(4, 5);


//#define COMMAND 0xFE
#define powerpin 4
#define GPSRATE 4800
//#define GPSRATE 38400


// GPS parser for 406a
#define BUFFSIZ 90 // plenty big
//char buffer[BUFFSIZ];
char *parseptr;
char buffidx;
uint32_t tmp;
unsigned char gpsexit;





void setup() {
  pinMode(LED2, OUTPUT); 
  pinMode(LED3, OUTPUT); 

  digitalWrite(LED2, LOW);
  pinMode(UP,INPUT);
  pinMode(DOWN,INPUT);
  pinMode(LEFT,INPUT);
  pinMode(RIGHT,INPUT);
  pinMode(CLICK,INPUT);


  digitalWrite(UP, HIGH);       /* Enable internal pull-ups */
  digitalWrite(DOWN, HIGH);
  digitalWrite(LEFT, HIGH);
  digitalWrite(RIGHT, HIGH);
  digitalWrite(CLICK, HIGH);
  
  mySerial.begin(GPSRATE);
  
  delay(1000); 
  
//  sLCD.begin(9600);              /* Setup serial LCD and clear the screen */
//  clear_lcd();

//  sLCD.print("CANBUS");
//  sLCD.write(COMMAND);
//  sLCD.write(LINE1); 
//  sLCD.print("Logging");
  
  //loop();
}



void loop(void)
{
 // clear_lcd();
  
  //if(Canbus.init(CANSPEED_500))  /* Initialise MCP2515 CAN controller at the specified speed */
 // {
 //   sLCD.print("CAN Init ok");
 // } else
 // {
 //   sLCD.print("Can't init CAN");
 // } 
   Canbus.init(CANSPEED_500);
   
  delay(500);
//  clear_lcd(); 
//  sLCD.print("Init SD card");  
  delay(500);
//  clear_lcd(); 
//  sLCD.print("Press J/S click");  
//  sLCD.write(COMMAND);
 // sLCD.write(LINE1);                     /* Move LCD cursor to line 1 */
//   sLCD.print("to Stop"); 
  


  // anything goes wrong, wait forever...
  
  // initialize the SD card at SPI_HALF_SPEED to avoid bus errors with
  // breadboards.  use SPI_FULL_SPEED for better performance.
  if (!card.init(SPI_HALF_SPEED,9)) while(1);
  
  // initialize a FAT volume
  if (!volume.init(&card)) while(1);
  
  // open the root directory
  if (!root.openRoot(&volume)) while(1);


  // create a new file
  char name[] = "LOG00.TXT";
  for (uint8_t i = 0; i < 100; i++) {
    name[3] = i/10 + '0';
    name[4] = i%10 + '0';
    if (file.open(&root, name, O_CREAT | O_EXCL | O_WRITE)) break;
  }


  if (!file.isOpen()) while(1);
 // Serial.print("Writing to: ");
//  Serial.println(name);
  // write header


  int supported_pids[]={PID_SUPPORTED_00,PID_SUPPORTED_20,PID_SUPPORTED_40,PID_SUPPORTED_60,PID_SUPPORTED_80,PID_SUPPORTED_A0,PID_SUPPORTED_C0};
    
  file.print("PIDs supported:");
  file.println();  


  
  for (uint8_t i=0; i<(sizeof(supported_pids)/sizeof(int)); i++)
  {
   if(Canbus.ecu_req(supported_pids[i],buffer) == 1)         
   {
    file.print(buffer);
    }
   file.print(',');
  }
  
  file.print("Logging started....");
  file.println();  
  
  int pids[]={ENGINE_RPM,VEHICLE_SPEED,ENGINE_COOLANT_TEMP,THROTTLE,CALC_ENG_LOAD_VAL,FUEL_PRESSURE,INTAKE_MAN_ABS_PRESS,INTAKE_AIR_TEMP,RUNTIME_SINCE_START,FUEL_LEVEL_INPUT,BAR_PRESS,ENG_OIL_TEMP,ENG_FUEL_RATE,DRIVER_DEMAND_ENG_TORQ,ACTUAL_ENG_TORQ};


  while(1)    /* Main logging loop */
  {
   /* gps */
   gpsexit=0;
   digitalWrite(LED2, HIGH);
   while(gpsexit==0)
   { 
    readline();
    if ((strncmp(buffer, "$GPRMC",6) == 0) or  (strncmp(buffer, "$GPGGA",6) == 0))
    {
     digitalWrite(LED2, LOW);
     gpsexit=1;
    }     
   }   
     
   file.print(buffer);
   file.println();  


   /* canbus &*/   digitalWrite(LED3, HIGH);
   for (uint8_t i=0; i<(sizeof(pids)/sizeof(int)); i++)
   {
    if(Canbus.ecu_req(pids[i],buffer) == 1)
    {
     file.print(buffer);
    } 
    file.print(',');
   }   
   file.println();  
   
   digitalWrite(LED3, LOW); 

   if (digitalRead(CLICK) == 0)
   {  /* Check for Click button */
    file.print("Logging ended....");
    file.println();  
    file.close();
    while(1);
   }
  }
}


void readline(void) {
  char c;
  
  buffidx = 0; // start at begninning
  while (1) {
      c=mySerial.read();
      if (c == -1)
        continue;
  //    Serial.print(c);
      if (c == '\n')
        continue;
      if ((buffidx == BUFFSIZ-1) || (c == '\r')) {
        buffer[buffidx] = 0;
        return;
      }
      buffer[buffidx++]= c;
  }
}

Modified Canbus library (only showing the bits between "switch(message.data[2])" and "}message_ok = 1;"):


{   /* Details from http://en.wikipedia.org/wiki/OBD-II_PIDs */
case ENGINE_RPM:   //   ((A*256)+B)/4    [RPM]
engine_data =  ((message.data[3]*256) + message.data[4])/4;
sprintf(buffer,"%04d rpm",(int) engine_data);
break;

case ENGINE_COOLANT_TEMP: // A-40  [degree C]
engine_data =  message.data[3] - 40;
sprintf(buffer,"%03d C",(int) engine_data);
break;

case VEHICLE_SPEED: // A  [km]
engine_data =  message.data[3];
sprintf(buffer,"%03d km",(int) engine_data);
break;


case MAF_SENSOR:   // ((256*A)+B) / 100  [g/s]
engine_data =  ((message.data[3]*256) + message.data[4])/100;
sprintf(buffer,"%d g/s",(int) engine_data);
break;


case O2_VOLTAGE:     // A * 0.005   (B-128) * 100/128 
engine_data = message.data[3]*0.005;
sprintf(buffer,"%d v",(int) engine_data);

break;

case THROTTLE: // Throttle Position
engine_data = (message.data[3]*100)/255;
sprintf(buffer,"%03d %%",(int) engine_data);
break;


case PID_SUPPORTED_00:
sprintf(buffer,"00 %d %d",message.data[3],message.data[4]);
break;

case PID_SUPPORTED_20:
sprintf(buffer,"20 %d %d",message.data[3],message.data[4]);
break;

case PID_SUPPORTED_40:
sprintf(buffer,"40 %d %d",message.data[3],message.data[4]);
break;

case PID_SUPPORTED_60:
sprintf(buffer,"60 %d %d",message.data[3],message.data[4]);
break;

case PID_SUPPORTED_80:
sprintf(buffer,"80 %d %d",message.data[3],message.data[4]);
break;

case PID_SUPPORTED_A0:
sprintf(buffer,"A0 %d %d",message.data[3],message.data[4]);
break;


case PID_SUPPORTED_C0:
sprintf(buffer,"C0 %d %d",message.data[3],message.data[4]);
break;

case CALC_ENG_LOAD_VAL:
engine_data =  (message.data[3]*100)/256;
sprintf(buffer,"%d %%",(int) engine_data);
break;


case FUEL_PRESSURE:
engine_data =  (message.data[3]*3);
sprintf(buffer,"%d kPa ",(int) engine_data);
break;


case INTAKE_MAN_ABS_PRESS :
engine_data =  (message.data[3]*1);
sprintf(buffer,"%d   kPa ",(int) engine_data);
break;


case INTAKE_AIR_TEMP:
engine_data =  (message.data[3]-40);
sprintf(buffer,"%d C",(int) engine_data);
break;


case RUNTIME_SINCE_START:  
engine_data =  ((message.data[3]*256) + message.data[4]);
sprintf(buffer,"%d s",(int) engine_data);
break;


case FUEL_LEVEL_INPUT:
engine_data =  (message.data[3]*100)/256;
sprintf(buffer,"%d %%",(int) engine_data);
break;


case BAR_PRESS:
engine_data =  (message.data[3]);
sprintf(buffer,"%d kPa",(int) engine_data);
break;


case ENG_OIL_TEMP:
engine_data =  (message.data[3]-40);
sprintf(buffer,"%d C",(int) engine_data);
break;


case ENG_FUEL_RATE:
engine_data =  (message.data[3]*100)*0.05;
sprintf(buffer,"%d L/h",(int) engine_data);
break;


case DRIVER_DEMAND_ENG_TORQ:
engine_data =  (message.data[3]-125);
sprintf(buffer,"%d %%",(int) engine_data);
break;


case ACTUAL_ENG_TORQ:
engine_data =  (message.data[3]-125);
sprintf(buffer,"%d %%",(int) engine_data);
break;


case ENG_REF_TORQ:  
engine_data =  ((message.data[3]*256) + message.data[4]);
sprintf(buffer,"%d Nm",(int) engine_data);
break;



3 comments:

  1. I am testing your code.
    Advice if you make any advance ;)

    ReplyDelete
  2. Hi,
    How to add PID with mode other than mode 1?

    Best regards.

    ReplyDelete
  3. Good question... sorry don't know, this project is somewhat on hold for me (hence the late response), i only get the first four bits of info from my car - the rest isn't supported :<

    ReplyDelete