Need some advice on OBD application

Hello all,
As always, I'm not sure if this is the right section & if it isn't, please feel free to move it.

Anyway, I am working on a sketch that queries the OBD port on my 2023 Toyota Tacoma for info on transmission temps, battery voltage, and a couple of other values, and processes the data into human-readable values that display on a 4" TFT.

After a quest worthy of Sir Lancelot, I have the relevant codes for my truck. They are as follows:

Battery voltage:
Header 701 Mode 22 PID 1F42
Returns 2 bytes

Transmission pan temp.
Header 701 Mode 22 PID 1627
Returns one byte

torque converter return oil temp.
Header 701 Mode 22 PID 1628
Returns one byte

Engine coolant temp.
Header 701 Mode 22 PID 1F05
Returns one byte

Transmission current gear.
Header 701 Mode 22 PID 1621
Returns one byte

torque converter lock status
Header 701 Mode 22 PID 1620
Returns one byte

The sketch has a struct that holds the data to be transmitted:

struct canMsg1;
  canMsg1.can_id  = 0x16;
  canMsg1.can_dlc = 8;
  canMsg1.data[0] = 0x00;
  canMsg1.data[1] = 0x00;
  canMsg1.data[2] = 0x00;
  canMsg1.data[3] = 0x00;
  canMsg1.data[4] = 0x00;
  canMsg1.data[5] = 0x00;
  canMsg1.data[6] = 0x00;
  canMsg1.data[7] = 0x00;

//The send message command:
mcp2515.sendMessage(&canMsg1);

My first question is where do I place the OBD header (always 701)? The first struct value is the ID, which I assume is the mode (always 22). The dlc is the length, I think. I don't know if that is the value of the expected return, or just leave it at 8. There is a lot of confusing information out there, it's akin to listening for the sound of a pin dropping on a pillow in the middle of an AC/DC concert. Any and all advice is appreciated. TIA.

Gary

Edit: I'm happy to include any other information needed. The entire sketch is cumbersome, being mostly touchscreen and display stuff.

make a copy and reduce it without touchscreen and display but with serial output

1 Like

Ok, here is something that will compile with all the display stuff stripped out:

#include <mcp2515.h>

#define NUMBER_OF_PIDS 6
#define PID_ENGINE_COOLANT_TEMPERATURE 0x1F05
#define PID_TRANSMISSION_OIL_PAN_TEMPERATURE 0x1627
#define PID_TORQUE_CONVERTER_OIL_RETURN_TEMPERATURE 0x1628
#define PID_BATTERY_VOLTAGE 0x1F42
#define PID_CURRENT_GEAR 0x1621
#define PID_TORQUE_CONVERTER_LOCK_STATUS 0x1620
//=============================================================================================================================================================================================
//=============================================================================================================================================================================================
//Global variables
unsigned char pids[] = {PID_ENGINE_COOLANT_TEMPERATURE, PID_TRANSMISSION_OIL_PAN_TEMPERATURE, PID_TORQUE_CONVERTER_OIL_RETURN_TEMPERATURE, PID_BATTERY_VOLTAGE, PID_CURRENT_GEAR, PID_TORQUE_CONVERTER_LOCK_STATUS};
struct can_frame canMsg;
struct can_frame canMsgOutgoing;

//=============================================================================================================================================================================================
MCP2515 mcp2515(10);

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void setup() 
{
  Serial.begin(9600); 
  mcp2515.reset();
  mcp2515.setBitrate(CAN_500KBPS);
  mcp2515.setNormalMode();  
  
 } 
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//============================================================================================================================================================================================
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void loop() 
{ 

   for(int i = 0; i < NUMBER_OF_PIDS; i++)
    {
     requestDataOBD(pids[i]); 
     ReadFromCanBus(pids[i]);
     delay(500); 
    }
 delay(5000);
 Serial.println(); 
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void requestDataOBD(unsigned char pid) {
  canMsgOutgoing.can_id  = 0x2BD;   // request 701 = 0x2BD(hex)
  canMsgOutgoing.can_dlc = 8;       // length of data frame
  canMsgOutgoing.can_mode = 0x16;   // Mode 22
  canMsgOutgoing.can_pid = pid;     // OBD PID that we are requesting
  canMsgOutgoing.data[0] = 0x00;    // zeros
  canMsgOutgoing.data[1] = 0x00;    
  canMsgOutgoing.data[2] = 0x00;    
  canMsgOutgoing.data[3] = 0x00;   
  canMsgOutgoing.data[4] = 0x00;
  canMsgOutgoing.data[5] = 0x00;
  canMsgOutgoing.data[6] = 0x00;
  canMsgOutgoing.data[7] = 0x00;
  mcp2515.sendMessage(&canMsgOutgoing);
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void ReadFromCanBus(unsigned char pid)
{
  if (mcp2515.readMessage(&canMsg) == MCP2515::ERROR_OK) {
    Serial.print(pid, HEX);
    Serial.print(", ");
    Serial.print(canMsg.can_id, HEX); // print ID
    Serial.print(", "); 
    Serial.print(canMsg.can_dlc, HEX); // print DLC
    Serial.print(", ");
    //Serial.print(canMsg.can_pid, HEX); // print PID
    //Serial.print(", ");
    
    for (int i = 0; i < canMsg.can_dlc; i++)  {  // print the data
      Serial.print(canMsg.data[i],HEX);
      Serial.print(", ");
    }

    //btt vgearSerial.println(); 
    Serial.println(); 
        
  }

}

I also modified the library file can.h


#ifndef CAN_H_
#define CAN_H_

#include <stdint.h>


typedef unsigned char __u8;
typedef unsigned short __u16;
typedef unsigned long __u32;


/* special address description flags for the CAN_ID */
#define CAN_EFF_FLAG 0x80000000UL /* EFF/SFF is set in the MSB */
#define CAN_RTR_FLAG 0x40000000UL /* remote transmission request */
#define CAN_ERR_FLAG 0x20000000UL /* error message frame */

/* valid bits in CAN ID for frame formats */
#define CAN_SFF_MASK 0x000007FFUL /* standard frame format (SFF) */
#define CAN_EFF_MASK 0x1FFFFFFFUL /* extended frame format (EFF) */
#define CAN_ERR_MASK 0x1FFFFFFFUL /* omit EFF, RTR, ERR flags */

/*
 * Controller Area Network Identifier structure
 *
 * bit 0-28 : CAN identifier (11/29 bit)
 * bit 29   : error message frame flag (0 = data frame, 1 = error message)
 * bit 30   : remote transmission request flag (1 = rtr frame)
 * bit 31   : frame format flag (0 = standard 11 bit, 1 = extended 29 bit)
 */
typedef __u32 canid_t;

#define CAN_SFF_ID_BITS     11
#define CAN_EFF_ID_BITS     29

/* CAN payload length and DLC definitions according to ISO 11898-1 */
#define CAN_MAX_DLC 8
#define CAN_MAX_DLEN 8

struct can_frame {
     canid_t can_id;  /* 32 bit CAN_ID + EFF/RTR/ERR flags */
    __u8     can_mode;   
    __u8     can_pid; 
    __u8     can_dlc; /* frame payload length in byte (0 .. CAN_MAX_DLEN) */
    __u8     data[CAN_MAX_DLEN] __attribute__((aligned(8)));
};

#endif /* CAN_H_ */

So I updated the sketch above, and now I get a return, but I still have something messed up. It returns

7941, 45A, 8, 0, 5A, 4, 0, 44, 1, 0, 0, 0,  //Engine coolant temp.
5671, 45A, 8, 0, 5A, 4, 0, 44, 1, 0, 0, 0,  //Transmission pan temp.
5672, 4E0, 8, 0, 24, 0, 44, 1, 0, 0, 0, 0,  //Torque converter oil return temp.
8002, 45A, 8, 0, 5A, 4, 0, 44, 1, 0, 0, 0,  //Battery voltage
5665, 4E0, 8, 0, 24, 0, 44, 1, 0, 0, 0, 0, //Current gear 


I ordered a logic analyzer, and will attempt to capture some frames, both to and from the canbus to see what I am missing.

where is array defined?

Sorry. I forgot that. I added it to the sketch above.

one PID is now missing

Yep. Forgot that one too. Fixed it.

does it compile now?
what if you comment out mcp2515_defs.h line?

Let me check...

struct can_frame {
    canid_t can_id;  /* 32 bit CAN_ID + EFF/RTR/ERR flags */
    __u8    can_dlc; /* frame payload length in byte (0 .. CAN_MAX_DLEN) */
    __u8    data[CAN_MAX_DLEN] __attribute__((aligned(8)));
};

'struct can_frame' has no member named 'can_mode'; ...

It's hard to keep this all straight and not forget anything. Sorry about that. I modified the can.h library file as well (see above).

It does compile with only the single library with the modified library file.

why you edit the lib? do you think the CAN bus of your truck will self adopting to your wishes?
and i have no edited lib. sketch contains many errors, where you get such monster?

I don't think anything, just experimenting. This was the first time I got a reply from the bus.

how? i can't even compile your sketch

Don't worry about it. I have a logic analyzer coming, and I'll figure it out eventually. Thanks for the input.

I was hoping someone could shed some light on Toyota's canbus methodology. Online information about it is scarce, as apparently this information is more closely guarded than the Epstein Client list. Toyota changed things around 2018 or so, adding a central hub that everything has to be routed through. There may also be security keys involved. Nobody seems to know for sure, or at least the ones that know aren't saying anything. It seems the only way is to analyze the frames and try to figure out the syntax that way.

I would just run the simple library code example for the 2515 , asking for one PID and get that working . Then move on .

What is your wiring diagram, module, Arduino used .

I am using this canbus shield on an uno with a TFT touchscreen.
I made a custom circuit board to allow everything to stack neatly. That all seems to work fine; it is just deciphering the protocol that is hanging me up. I also have one of these OBD readers bluetoothed to my phone through the Torque Pro app. I have a Y splitter cable that I have not used yet. At this point, I hope to use the logic analyzer to capture frames being sent and received. I know the codes being sent, just not the precise format. I have the feeling that it is the asking for the data that I am messing up, not getting the result I am looking for...OR, the system is getting the correct command, and it is in the reading, parsing, and calculating the returned frame that is wrong....or both.

For instance, I know the data the Torque Pro app is sending to request the transmission temperature on thermistor #1 is:

OBD Header       Mode         PID
   701            22          1627

and the first byte returned is used to calculate the value via the formula A-40, yielding the temperature in Degrees C

So, if I plug the above 701, 22, 1627 into the structure to send, referencing a diagram I saw online that I can't find now to save my soul, I get back:

5671, 45A, 8, 0, 5A, 4, 0, 44, 1, 0, 0, 0,

As near as I can tell, 5A (90, DEC) s the first byte sent back, yielding 50 degrees C as the formula result, which is not even close. I believe the request should return just one data byte, yet I see a 44 and a 1 in the returned frame, indeed, I see the same values in every returned frame.
I hope all this makes sense....

....and yes, I will follow your advice and focus on only one value from now on until I have it solved.

The examples give you clue to the format . Which includes the header , length of data field , data .
The calculation to be carried out on the returned value depends upon that PID .

Examples

And…This