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;
I am testing your code.
ReplyDeleteAdvice if you make any advance ;)
Hi,
ReplyDeleteHow to add PID with mode other than mode 1?
Best regards.
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