Can-Bus shield, BMW and modified library problems

So I bought a Sparkfun Can-Bus shield for use with my Uno and trying to get it to display a variety of engine parameters from my 2008 BMW 335i. Eventually they will go on a LCD but for now, just testing the code on the Serial Monitor. Using a OBD2 to DB9 adapter cable plugged into the shield, I have successfully read the data from the example code provided by Sparkfun's Canbus.h library. However, because I want to display more parameters than the sample code provides I've been trying to modify this library to accommodate these extra readings (oil temp, fuel pressure, engine run time, fuel flow). I have successfully modified the code for engine run time but the other three readings won't return a value. When I run the debugging code, the first readings for these three parameters are blank and after the loop goes around once, the next reading displays the data from the preceding functioning parameter. It's as if the buffer is not getting the updated reading and re-printing the last good one. Are the PIDs that I've added not valid? I thought they were an industry standard. Why was I able to get the engine timer (Hex 0x1F) to work but not the others? Thanks in advance. I'll be really embarrassed if the answer is a simple one.

Here is the main sketch:

#include <Canbus.h>
#include <defaults.h>
#include <global.h>
#include <mcp2515.h>
#include <mcp2515_defs.h>
#include <ODB2Parser.h>
#include <ODB2Response.h>
#include <pids.h>


#include <Canbus.h>

char UserInput;
int data;
char buffer[456];  //Data will be temporarily stored to this buffer before being written to the file

//********************************Setup Loop*********************************//

void setup(){
Serial.begin(9600);
Serial.println("CAN-Bus Demo");

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); 

}

void loop(){

 Canbus.ecu_req(FUEL_FLOW, buffer);
 Serial.print(" FF: ");
 Serial.print(buffer);
 delay(150);

 Canbus.ecu_req(FUEL_PRESSURE, buffer);
 Serial.print(" Fp: ");
 Serial.print(buffer);
 delay(150);

 Canbus.ecu_req(VEHICLE_SPEED, buffer);
 Serial.print("Spd: ");
 Serial.print(buffer);
 delay(150);

 Canbus.ecu_req(ENGINE_RPM, buffer);
 Serial.print("  RPM: ");
 Serial.print(buffer);
 delay(150);

 Canbus.ecu_req(ENGINE_COOLANT_TEMP, buffer);
 Serial.print(" Cool: ");
 Serial.print(buffer);
 delay(150);

 Canbus.ecu_req(OIL_TEMPERATURE, buffer);
 Serial.print(" Ot: ");
 Serial.print(buffer);
 delay(150);

 Canbus.ecu_req(ENGINE_TIME, buffer);
 Serial.print(" Time: ");
 Serial.println(buffer);
 delay(150);

}

Here is a portion of the Canbus.cpp. For brevity, I only added the switch function to the code below. Let me know if it's necessary to see the whole .cpp file to better diagnose my problem

char CanbusClass::ecu_req(unsigned char pid,  char *buffer) 
{
	tCAN message;
	float engine_data;

	if(CanbusClass::ecu_req(pid, &message))
	{
		switch(message.data[2])
		{   
                /* 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,"%d rpm ",(int) engine_data);
		break;
	
		case ENGINE_COOLANT_TEMP: 	// 	A-40			  [degree C]
		engine_data = message.data[3] - 40;
		sprintf(buffer,"%d degC",(int) engine_data);
	        break;
	
		case VEHICLE_SPEED: 		// A				  [km]
		engine_data =  message.data[3];
		sprintf(buffer,"%d 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 (if B==0xFF, sensor is not used in trim calc)
		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,"%d %% ",(int) engine_data);
		break;
                
            case OIL_TEMPERATURE:				// Oil Temperature added to Library, not working
            engine_data = message.data[3] - 40;
            sprintf(buffer,"%d degF",(int) engine_data);
            break;
                
            case ENGINE_TIME:				// Engine Time added to Library, functional
            engine_data = ((message.data[3]*256)+ message.data[4]);
            sprintf(buffer,"%d sec",(int) engine_data);
            break;

            case FUEL_PRESSURE:				// Fuel Pressure added to Library, not working
            engine_data = (message.data[3]*3);
            sprintf(buffer,"%d psi",(int) engine_data);
            break;

            case FUEL_FLOW:				// Fuel Flow added to Library, not working
            engine_data = ((message.data[3]*256)+ message.data[4])/20;
            sprintf(buffer,"%d gal/hr",(int) engine_data);
            break;
	
		}
        
		return 1;
	}

 	return 0;
}

Here is the pids.h file I also modified. I added the hex ID from the wikipedia article on OBD-II PIDs. This is also where I got the formulas for the parameters I've added.

#ifndef pids__h
#define pids__h

#define PID_REQUEST                   0x7DF
#define PID_REPLY                     0x7E8

#define ENGINE_COOLANT_TEMP           0x05
#define ENGINE_RPM                    0x0C
#define VEHICLE_SPEED                 0x0D
#define MAF_SENSOR                    0x10
#define O2_VOLTAGE                    0x14
#define FUEL_LEVEL                    0x2F
#define THROTTLE                      0x11
#define REL_ACCELERATOR_PEDAL_POS     0x5A
#define ACCELERATOR_PEDAL_POS_D       0x49
#define ACCELERATOR_PEDAL_POS_E       0x4A
#define ACCELERATOR_PEDAL_POS_F       0x4B
#define OIL_TEMPERATURE               0x5C //added to original Library, not working
#define ENGINE_TIME                   0x1F //added to original Library, functional
#define FUEL_PRESSURE                 0x0A //added to original Library, not working
#define FUEL_FLOW                     0x5E //added to original Library, not working

#endif

Hi there, are you able to display all the PID's at the same time on the Serial monitor?

Could you please upload your full Canbus.cpp file. I see it is not the same as the one downloaded from Sparkfun. Did you alter it ?

I am working on a similar project.

Jeffreykruger:
Hi there, are you able to display all the PID's at the same time on the Serial monitor?

I am attempting to display 7 PIDs at a time on the serial monitor with a 150 delay between readings. 4 of them work fine but the other 3 readings will not return a value. I modified the original sketch attached at the top of this post to isolate these 3 problem readings. Here is what came from the serial monitor:

CAN-Bus Demo
CAN Init ok
Ot:  FF:  Fp: 
Ot:  FF:  Fp: 
Ot:  FF:  Fp: 
Ot:  FF:  Fp: 
Ot:  FF:  Fp: 
Ot:  FF:  Fp: 
Ot:  FF:  Fp: 
Ot:  FF:  Fp: 
Ot:  FF:  Fp: 
Ot:  FF:  Fp: 
Ot:  FF:  Fp: 
Ot:

Again, I compile the sketch with no problems and get good readings from the other PIDs. Also, "Ot" = oil temperature, "FF" = Fuel flow and "Fp" = fuel pressure.

Very strange to see those results. Perhaps your car does not support the PID's?

I am using the same sketch as you on my Ford Fiesta 2013. However when I try display all PIDs on the serial monitor they become very erratic for some reason. I see your main code but could you please share with me your full canbus.cpp file so I can see how it differed from the original one I've been using.

Jeffreykruger:
Very strange to see those results. Perhaps your car does not support the PID's?

I am using the same sketch as you on my Ford Fiesta 2013. However when I try display all PIDs on the serial monitor they become very erratic for some reason. I see your main code but could you please share with me your full canbus.cpp file so I can see how it differed from the original one I've been using.

Here is the full .cpp file. I only added 4 additional cases to the switch function. Only the ENGINE_TIME case is working (besides the default ones). Also, I've discovered that when I switch around the PID Hex's, the problem follows the PID. So I know my code is good and as you mentioned, it seems that the code is not recognizing these particular PIDs. How can that be? It's my understanding that these are industry standard codes. What is it about these PIDs (oil temp, fuel rate and fuel pressure) that make them unable for the shield to read???? I got the PIDs for these problem 3 ones from the same place I got the ENGINE_TIME ones, so I know it's a valid code. What am I missing?

/**
 * 
 *
 * Copyright (c) 2008-2009  All rights reserved.
 */

#if ARDUINO>=100
#include <Arduino.h> // Arduino 1.0
#else
#include <Wprogram.h> // Arduino 0022
#endif
#include <stdint.h>
#include <avr/pgmspace.h>

#include <stdio.h>
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
#include "pins_arduino.h"
#include <inttypes.h>
#include "global.h"
#include "mcp2515.h"
#include "defaults.h"
#include "Canbus.h"
#include "pids.h"





/* C++ wrapper */
CanbusClass::CanbusClass() {

 
}
char CanbusClass::message_rx(unsigned char *buffer) {
		tCAN message;
	
		if (mcp2515_check_message()) {
		
			
			// Lese die Nachricht aus dem Puffern des MCP2515
			if (mcp2515_get_message(&message)) {
				buffer[0] = message.data[0];
				buffer[1] = message.data[1];
				buffer[2] = message.data[2];
				buffer[3] = message.data[3];
				buffer[4] = message.data[4];
				buffer[5] = message.data[5];
				buffer[6] = message.data[6];
				buffer[7] = message.data[7];																															
			}
			else {
			//	PRINT("Kann die Nachricht nicht auslesen\n\n");
			}
		}

}

char CanbusClass::message_tx(void) {
	tCAN message;

	// einige Testwerte
	message.id = 0x7DF;
	message.header.rtr = 0;
	message.header.length = 8;
	message.data[0] = 0x02;
	message.data[1] = 0x01;
	message.data[2] = 0x05;
	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);
		
	if (mcp2515_send_message(&message)) {
		return 1;
	}
	else {
		//	PRINT("Fehler: konnte die Nachricht nicht auslesen\n\n");
		return 0;
	}
	return 1;
 
}


char CanbusClass::ecu_req(unsigned char pid,  tCAN *message) 
{
	int timeout = 0;
	// Prepare 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;
	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);

	if (mcp2515_send_message(message)) {
	}
	
	while(timeout < 12000)
	{
		timeout++;
		if (mcp2515_check_message()) 
		{
			if (mcp2515_get_message(message)) 
			{
					if((message->id == PID_REPLY) && (message->data[2] == pid))	// Check message is the reply and its the right PID
					{
						return 1;
					}

			}
		}
	}
 	return 0;
}


char CanbusClass::ecu_req(unsigned char pid,  char *buffer) 
{
	tCAN message;
	float engine_data;

	if(CanbusClass::ecu_req(pid, &message))
	{
		switch(message.data[2])
		{   /* 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,"%d rpm ",(int) engine_data);
			break;
	
			case ENGINE_COOLANT_TEMP: 	// 	A-40			  [degree C]
			engine_data = message.data[3] - 40;
			sprintf(buffer,"%d degC",(int) engine_data);
	        break;
	
			case VEHICLE_SPEED: 		// A				  [km]
			engine_data =  message.data[3];
			sprintf(buffer,"%d 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 (if B==0xFF, sensor is not used in trim calc)
			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,"%d %% ",(int) engine_data);
			break;
                
            case OIL_TEMPERATURE:				// Oil Temperature added to Library, not working
            engine_data = message.data[3] - 40;
            sprintf(buffer,"%d degF",(int) engine_data);
            break;
                
            case ENGINE_TIME:				// Engine Time added to Library, functional
            engine_data = ((message.data[3]*256)+ message.data[4]);
            sprintf(buffer,"%d sec",(int) engine_data);
            break;

            case FUEL_PRESSURE:				// Fuel Pressure added to Library, not working
            engine_data = (message.data[3]*3);
            sprintf(buffer,"%d psi",(int) engine_data);
            break;

            case FUEL_FLOW:				// Fuel Flow added to Library, not working
            engine_data = ((message.data[3]*256)+ message.data[4])/20;
            sprintf(buffer,"%d gal/hr",(int) engine_data);
            break;
	
		}
        
		return 1;
	}

 	return 0;
}



char CanbusClass::init(unsigned char speed) {

  return mcp2515_init(speed);
 
}

CanbusClass Canbus;

Thanks very much!!

Yes it all seems legit and the calculations match that on the Pid wiki page. Perhaps you could google around and see exactly what PID's are supported in your car. I will test them on my car tonight and will let you know the result :slight_smile:

Jeffreykruger:
Thanks very much!!

Yes it all seems legit and the calculations match that on the Pid wiki page. Perhaps you could google around and see exactly what PID's are supported in your car. I will test them on my car tonight and will let you know the result :slight_smile:

Yes, please let me know. I'd be interested to know if you can view oil t, fuel f and fuel p using the .cpp code I pasted above. If you can, then I'll know that the problem is with BMW's PIDs.

Thanks for you help.

I am struggling to compile the code.

fatal error: ODB2Parser.h: No such file or directory

Do you know where I can get this sketch or library from ?

Jeffreykruger:
I am struggling to compile the code.

fatal error: ODB2Parser.h: No such file or directory

Do you know where I can get this sketch or library from ?

The library I downloaded and installed is found here:

I modified the SparkFun_Can_Demo.ino to simply print results to the serial monitor. As I already mentioned, I added additional code to Canbus.cpp and pids.h which is posted above.