Using an Array to hold variable names

Hi all!

I've not tried using Arrays in Arduino before so I'm just trying to figure out how to use them properly. My goal is to make a 3 column array with the following data within:

Column 1 = A series of variable names
Column 2 = 1 or 0
Column 3 = One of two variables which points to some HEX information (MODE1 = 0x01, MODE3 = 0x03)

I would like to use the array in a function, where I can use an index variable to tell the code which row to pull from the array to use to make an OBD message request. To do this, once the row is known, I'd like to pull the PID reference (e.g PID_RPM which in this case = 0x0C). It will then pull the second column which tells the code whether this PID request should go ahead or skip to the next, then finally the 3rd column gives the message mode information. From here I'll drive my CAN message request with the information.

I tried using the code below to define the array, but it says I have too many initialisers... What am I doing wrong here, and can an array work in the way I describe?

List of PID references:

#define MODE1               0x01        //Show current data
#define MODE2               0x02        //Show freeze frame data
#define MODE3               0x03        //Show stored Diagnostic Trouble Codes
#define MODE4               0x04        //Clear Diagnostic Trouble Codes and stored values

#define PID_SUPPORTED       0x00
#define PID_MONITOR_STATUS  0x01
#define PID_SPEEDMPH        0x0D
#define PID_RPM             0x0C
#define PID_OIL_TEMP        0x5C
#define PID_COOLANT_TEMP    0x05
#define PID_INTAKE_TEMP     0x0F
#define PID_AIR_TEMP        0x46
#define PID_BATT_VOLTAGE    0x42
#define O2_VOLTAGE_1        0x24
#define O2_VOLTAGE_2        0x25
#define PID_FUEL_PRESSURE   0x0A
#define PID_FUEL_RATE       0x5E
#define PID_THROTTLE_POS    0x11

#define MODE1_RESPONSE      0x41
#define MODE3_RESPONSE      0x43
#define MODE4_RESPONSE      0x44
#define PID_REQUEST         0x7DF
#define PID_REPLY           0x7E8

Array initialisation:

//Col 1 = PID Reference, Col 2 = should this PID be requested, Col 3 = What mode should be run?
char *PIDArray [][3] = { {"PID_SPEEDMPH","PID_RPM","PID_OIL_TEMP","PID_COOLANT_TEMP","PID_INTAKE_TEMP","PID_AIR_TEMP","PID_BATT_VOLTAGE","PID_FUEL_PRESSURE","PID_FUEL_RATE","PID_THROTTLE_POS","PID_MONITOR_STATUS","0"},{"1","1","1","1","1","1","1","1","1","1","1","1"},{"MODE1","MODE1","MODE1","MODE1","MODE1","MODE1","MODE1","MODE1","MODE1","MODE1","MODE1","MODE3"} };

Pull from array:

PID == PIDArray[x][0];
MODE == PIDArray[x][2];
//Then I'll need a small loop to check the next rows for one with a 1 in column 2 for next time the code runs

Umm.. Maybe a different approach?

Typically in cases like this you would write a struct.

struct OBDInfo {
   char   variableName[15];
   bool   UnknownBoolean;
   word  aHexThing;
};

Or something along these lines..

Then you just make one array of these structs.

OBDInfo infoList[lots];

Then your index to that array gives you all three of your "columns".

Or maybe I just missed the point entirely. Donno'.

-jim lee

2 Likes

Thanks very much Jim! I've not heard of a struct before but that does look like a useful tool. So to use this, I need to first create my 11 or so structs (is there an efficient way to do this other than just 11 repeats of that code?). Once I have the structs then I make an array of the structs. If I want to change the unknownboolean variable, do I have to rebuild the struct with the 0 or 1 changed?

Well actually..

You first write the struct type as I showed you. (only one type) As you can see a struct is just a named group of variables. Basically the idea was that one struct held the information from any one line of your original arrays.

So you create the struct type then just allocate an array of them just as you would say an array of int, float or char.

struct myStruct {   // Decide what you want in your set of stuff per array item.
   bla
   bla
   bla
};

myStruct  infoList[numListItems];   // This makes the array of your struct things.

This make sense?

-jim lee

1 Like

Accessing a struct.

Lets say we did the original struct I showed you.

struct OBDInfo {
   char   variableName[15];
   bool   UnknownBoolean;
   word  aHexThing;
};

OBDInfo struct1;   // This creates one.

struct1.UnknownBoolean = true;   // This sets the UnknownBoolean in struct1 to true;

OBDInfo structList[5];   // This creates an array of 5 of these structs.

structList[3].UnknownBoolean = false;     // This sets the UnknownBoolean in struct[3] to false

-jim lee

1 Like

Thanks very much for your explanation Jim! I need to have a go at it to try to get my head around it so I'll have a go this afternoon and come back with some inevitable questions :smiley:

Hi Jim!

Ok I think I'm starting to understand it but there's a little more way to go. I have built this little test sketch to see how it works, and based off your reply added to some other info I found, I have created something that compiles:


/* OBD Data (from http://en.wikipedia.org/wiki/OBD-II_PIDs) */
word    MODE1              = 0x01;        //Show current data
word    MODE2              = 0x02;        //Show freeze frame data
word    MODE3              = 0x03;        //Show stored Diagnostic Trouble Codes
word    MODE4              = 0x04;        //Clear Diagnostic Trouble Codes and stored values

word    PID_SUPPORTED      = 0x00;
word    PID_MONITOR_STATUS = 0x01;
word    PID_SPEED          = 0x0D;
word    PID_RPM            = 0x0C;
word    PID_OIL_TEMP       = 0x5C;
word    PID_COOLANT_TEMP   = 0x05;
word    PID_INTAKE_TEMP    = 0x0F;
word    PID_AIR_TEMP       = 0x46;
word    PID_BATT_VOLTAGE   = 0x42;
word    O2_VOLTAGE_1       = 0x24;
word    O2_VOLTAGE_2       = 0x25;
word    PID_FUEL_PRESSURE  = 0x0A;
word    PID_FUEL_RATE      = 0x5E;
word    PID_THROTTLE_POS   = 0x11;

word    MODE1_RESPONSE     = 0x41;
word    MODE3_RESPONSE     = 0x43;
word    MODE4_RESPONSE     = 0x44;
word    PID_REQUEST        = 0x7DF;
word    PID_REPLY          = 0x7E8;

//Variables-------------
int RPM;
int SPEED;
int OIL_TEMP;
int COOLANT_TEMP;
int AIR_TEMP;
int INTAKE_TEMP;
int BATT_VOLTAGE;
int FUEL_PRESSURE;
int FUEL_RATE;
int THROTTLE_POS;


//Struct Init--------
struct OBD {
  word*   PID;
  bool   Active;
  word*   MODE;
  int*    Var;
};

OBD OBDArray[] = {{&PID_RPM, 1, &MODE1, &RPM},
  {&PID_SPEED, 1, &MODE1, &SPEED},
  {&PID_COOLANT_TEMP, 1, &MODE1, &COOLANT_TEMP}
};

byte Arraysize = sizeof(OBDArray) / sizeof(OBDArray[0]);

void setup() {
  Serial.begin(9600);
  OBDArray[1].Active = 0;

}

void loop() {
  Serial.println(Arraysize);
}

So I can roll through using the index from 0 to whatever the max row is, which is great. Does this seem like the right way to do it?

This is your struct..

//Struct Init--------
struct OBD {
  word*   PID;
  bool   Active;
  word*   MODE;
  int*    Var;
};

It seems to me that the PID & MODE variables you are saving are just numbers. You don't need pointers for saving those.

//Struct Init--------
struct OBD {
  word   PID;
  bool   Active;
  word   MODE;
  int*    Var;
};

// Making the initializer look more like...

OBD OBDArray[] = {{PID_RPM, 1, MODE1, &RPM},
  {PID_SPEED, 1, MODE1, &SPEED},
  {PID_COOLANT_TEMP, 1, MODE1, &COOLANT_TEMP}
};

Also, I think you could save some memory by using #define instead of how you are doing this...

// Example, this..

word    PID_SUPPORTED      = 0x00;
word    PID_MONITOR_STATUS = 0x01;
word    PID_SPEED          = 0x0D;

// becomes

#define PID_SUPPORTED        0x00
#define PID_MONITOR_STATUS   0x01
#define PID_SPEED            0x0D

Since they never change, you don't want to waste space saving them. As far as you are concerned, they will work the same way as before.

Now, as to the last variable Var. Do you really want every struct that uses COOLANT_TEMP have that pointing to the same global COOLANT_TEMP? Because the way you set it up does just that. Change one and all will reflect that change.

I'm not all that versed in CAN bus stuff so I'm shooting in the dark. The guy you want to talk to is @Power_Broker He's the one that could really help out here.

-jim lee

1 Like

Hi Jim, thanks for this - that's really helped! Yes I do want to use the global variable. In the example of RPM, I have 2 sources of data. Analogue from a hall effect sensor, or through CAN bus. At the moment I want to look in the table and find out whether I want to use CAN for the data stream for RPM or not. If I do, then I want to trigger my CAN function to request this PID from the network, then feed back the received data into the global RPM variable so I can then use this elsewhere in the code. If not then I will have another function which will switch on instead.

I seem to have got a working model working ok and receiving some simple data for 3 PIDs in sequence, but I am having trouble pushing the received data into the global variable.

I'll post the full code below, but essentially here is my problem:

If I serial print the message receiver code, then the data shows correctly in the monitor

Serial.print(CAN_Receive(OBDArray[Row].PID));

If I try to push it back into the global variable then it doesn't work (it compiles but if I serial print the RPM variable it is empty):

OBDArray[Row].Data = CAN_Receive(OBDArray[Row].PID);

My Struct is now like this:

struct OBD {
  String Name;
  word   PID;
  bool   Active;
  word   MODE;
  int*   Data;
};

What's going wrong here...?

Full Code:

//Testing out CAN Bus ONLY
#include <FlexCAN.h> //Can Bus

//CAN PIDs
/* Details from http://en.wikipedia.org/wiki/OBD-II_PIDs */
#define MODE1               0x01        //Show current data
#define MODE2               0x02        //Show freeze frame data
#define MODE3               0x03        //Show stored Diagnostic Trouble Codes
#define MODE4               0x04        //Clear Diagnostic Trouble Codes and stored values

#define PID_SUPPORTED       0x00
#define PID_MONITOR_STATUS  0x01
#define PID_SPEED           0x0D
#define PID_RPM             0x0C
#define PID_OIL_TEMP        0x5C
#define PID_COOLANT_TEMP    0x05
#define PID_INTAKE_TEMP     0x0F
#define PID_AIR_TEMP        0x46
#define PID_BATT_VOLTAGE    0x42
#define O2_VOLTAGE_1        0x24
#define O2_VOLTAGE_2        0x25
#define PID_FUEL_PRESSURE   0x0A
#define PID_FUEL_RATE       0x5E
#define PID_THROTTLE_POS    0x11

#define MODE1_RESPONSE      0x41
#define MODE3_RESPONSE      0x43
#define MODE4_RESPONSE      0x44
#define PID_REQUEST         0x7DF
#define PID_REPLY           0x7E8

//CAN Bus----------------------------------------
//DIAGNOSTICS*****
static uint8_t hex[17] = "0123456789abcdef";
// -------------------------------------------------------------
static void hexDump(uint8_t dumpLen, uint8_t *bytePtr)
{
  uint8_t working;
  while ( dumpLen-- ) {
    working = *bytePtr++;
    Serial.write( hex[ working >> 4 ] );
    Serial.write( hex[ working & 15 ] );
  }
  Serial.write('\r');
  Serial.write('\n');
}
//*****

byte PID_CALL;
byte PID;
byte MODE;
byte PID_RESPONSE;
CAN_message_t can_MsgRx, can_MsgTx;
bool CANWaiting = false;
int CAN_Retry = 0;

//Struct Init--------
struct OBD {
  String Name;
  word   PID;
  bool   Active;
  word   MODE;
  int*   Data;
};



//Variables-------------
int RPM;
int SPEED;
int OIL_TEMP;
int COOLANT_TEMP;
int AIR_TEMP;
int INTAKE_TEMP;
int BATT_VOLTAGE;
int FUEL_PRESSURE;
int FUEL_RATE;
int THROTTLE_POS;
//byte MONITOR_STATUS;
//byte MONITOR_STATUS_CAN;

//Trouble Codes----------------------------
int DTC = 2;
int numDTC;
int numDTC_CAN = 0;
int DTC1Raw;
int DTC2Raw;
char DTC1 [5];
char DTC2 [5];
int SeenDTC = 0;
int DTCp1;
int DTCp2;
int DTC_CLEARED = 0;

//---TIME & SPEED-----------------------------------
unsigned long lastmillis;
float timeseconds;

//For Serial/CAN
unsigned long CAN_Request_Millis = 10;
unsigned long CAN_Timeout = 1000; //CHANGE TO 100
unsigned long currentMillis = 0;
unsigned long CAN_Wait_Duration = 0;

//--------------Sensor Readings---------------------
int MonitorCodes;


//Struct Setup
int Row = 0;
OBD OBDArray[] = {{"RPM", PID_RPM, true, MODE1, &RPM},
  {"Speed", PID_SPEED, true, MODE1, &SPEED},
  {"Coolant Temp", PID_COOLANT_TEMP, false, MODE1, &COOLANT_TEMP}
};
byte Arraysize = sizeof(OBDArray) / sizeof(OBDArray[0]);

//====== END SET UP VARIABLES =================================================================================

//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

void setup(void) {

  Serial.begin(9600);
  Can0.begin(500000);
  delay(500);
  //Load EEPROM and update Active PIDs in table
}

//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

void loop(void) {
  currentMillis = millis();

  //=========READ CAN BUS=========
  Get_CAN_Data();
  //Print_CAN_Data();
  
}
//----------------------------------------------------------------------------------
//--------------------------END OF LOOP---------------------------------------------
//----------------------------------------------------------------------------------



//-------------------------START OF FUNCTIONS--------------------------------------

void NextPID() {
  //Start with next row unless at end of table, then start from 0
  for (int i = 0; i < (Arraysize-1); i++) {
    
    if ((i + Row) > (Arraysize - 1)) Row = 0;
    else Row = (Row + 1);

    if (OBDArray[Row].Active == true) break;
  }
}

void Print_CAN_Data(){
  Serial.print(RPM);
  Serial.print(" - ");
  Serial.print(SPEED);
  Serial.print(" - ");
  Serial.println(COOLANT_TEMP);
}

void Get_CAN_Data() {

  //Are we waiting for a response?
  if (CANWaiting == true) { //
    if (Can0.available() > 0) {//Is a response waiting?
      OBDArray[Row].Data = CAN_Receive(OBDArray[Row].PID);
      //Serial.println(RPM);
      CANWaiting = false; //Reset waiting flag
      NextPID();//Prepare next PID
    }

    else { //Still waiting - check for timeout
      CAN_Wait_Duration = currentMillis - CAN_Request_Millis;
      if (CAN_Wait_Duration > CAN_Timeout) //Waited too long
      {
        //How many times has this PID been tried?
        if (CAN_Retry == 3) {
          //Switch off this PID Request
          OBDArray[Row].Active = false;
          //Show Error
          Serial.print("OBD Error - ");
          Serial.println(OBDArray[Row].Name);
          //Try Next PID
          NextPID();
          CAN_Retry = 0;
          CANWaiting = false;
        }
        else {
          CAN_Retry ++; //Retry this PID
        }
      }
    }
  }

  if (CANWaiting == false) {
    //Request next Data
    CAN_Request(OBDArray[Row].MODE, OBDArray[Row].PID);
    CAN_Request_Millis = currentMillis;
    CANWaiting = true;
  }
}

void CAN_Request(byte CAN_MODE, byte PID_CALL) {
  can_MsgTx.ext = 0;
  can_MsgTx.id = PID_REQUEST;
  can_MsgTx.len = 8;

  can_MsgTx.buf[0] = 0x02; //Length in number of bytes of remaining data (2 bytes = 02, 3 bytes = 03 etc)
  can_MsgTx.buf[1] = CAN_MODE;
  can_MsgTx.buf[2] = PID_CALL;
  can_MsgTx.buf[3] = 0;
  can_MsgTx.buf[4] = 0;
  can_MsgTx.buf[5] = 0;
  can_MsgTx.buf[6] = 0;
  can_MsgTx.buf[7] = 0;

  can_MsgTx.flags.extended = 0;
  can_MsgTx.flags.remote = 0;
  //  can_MsgTx.timeout = 500;


  //Serial.print("CAN SENT: "); hexDump(8, can_MsgTx.buf); //Diagnostic - Dump message to Serial//@@@@@@@@@@@@@@@@@@@@@@@@@@@CAN DIAGNOSTICS@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
  //Serial.println(currentMillis / 10);
  Can0.write(can_MsgTx); //Send message
}

int CAN_Receive(byte PID_RESPONSE) { //CONSIDER CHECKING buf[0] (length - might be useful)
  while (Can0.available())
  {
    Can0.read(can_MsgRx);
    //Serial.print("CAN RECEIVED: "); hexDump(8, can_MsgRx.buf); //Diagnostic - Dump message to Serial useful explanation: https://www.csselectronics.com/screen/page/simple-intro-obd2-explained/language/en
    if (can_MsgRx.buf[1] == MODE3_RESPONSE) {
      numDTC_CAN = can_MsgRx.buf[2];

      if (numDTC_CAN > 0) {
        DTCp1 = can_MsgRx.buf[3];
        DTCp2 = can_MsgRx.buf[4];
        if (DTCp1 < 10) DTC1Raw = (DTCp1 * 100) + DTCp2;
        else DTC1Raw = (DTCp1 * 1000) + DTCp2;
        sprintf (DTC1, "P%04u", DTC1Raw);
      }
      else sprintf (DTC1, "");

      if (numDTC_CAN > 1) {
        DTCp1 = can_MsgRx.buf[5];
        DTCp2 = can_MsgRx.buf[6];
        if (DTCp1 < 10) DTC2Raw = (DTCp1 * 100) + DTCp2;
        else DTC2Raw = (DTCp1 * 1000) + DTCp2;
        sprintf (DTC2, "P%04u", DTC2Raw);
      }
      else sprintf (DTC2, "");
      return numDTC_CAN;
    }

    if ((can_MsgRx.id == PID_REPLY) && (can_MsgRx.buf[2] == PID_RESPONSE))
    {
      switch (can_MsgRx.buf[2]) //Switch means look at different cases
      { /* Details from http://en.wikipedia.org/wiki/OBD-II_PIDs */

        case PID_SPEED:         // A                  [km]  int SPEED =
          return  can_MsgRx.buf[3] * 1; //CHECK SPEED IS IN KPH AND CONVERSION!!!!
          break;

        case PID_RPM:              //   ((A*256)+B)/4    [RPM]
          return  ((can_MsgRx.buf[3] * 256) + can_MsgRx.buf[4]) / 4;
          break;

        case PID_OIL_TEMP:     //     A-40              [degree C]
          return  can_MsgRx.buf[3] - 40;
          break;

        case PID_COOLANT_TEMP:     //     A-40              [degree C]
          return  can_MsgRx.buf[3] - 40;
          break;

        case PID_INTAKE_TEMP:     //     A-40              [degree C]
          return  can_MsgRx.buf[3] - 40;
          break;

        case PID_AIR_TEMP:     //     A-40              [degree C]
          return  can_MsgRx.buf[3] - 40;
          break;

        case PID_BATT_VOLTAGE:            //
          return ((can_MsgRx.buf[3] * 256) + can_MsgRx.buf[4]);
          break;

        case PID_FUEL_PRESSURE:     //                   [kPa]
          return  can_MsgRx.buf[3] * 3;
          break;

        case PID_FUEL_RATE:     //                   [l/h]
          return  ((can_MsgRx.buf[3] * 256) + can_MsgRx.buf[4]);
          break;

        case PID_THROTTLE_POS:
          return int((can_MsgRx.buf[3] * 100) / 255);
          break;

        case PID_MONITOR_STATUS:
          return can_MsgRx.buf[3];// & can_MsgRx.buf[4] & can_MsgRx.buf[5];
          break;
      }
      return 1;
    }
  } // while
}

Looks to me like you are wanting to update the global value to a new value. BUT what you are actually doing is overwriting the address of the global data in the struct.

  1. You load the struct with an address of one of your globals.
  2. You want to update this global.. Try it this way.
*(OBDArray[Row].Data) = CAN_Receive(OBDArray[Row].PID);

This will update the gloabl int that Data is pointing to.

-jim lee

1 Like

It works! Wow, thanks very much Jim! I've learned something new and it works really well... Now to expand the test out to all of the PIDs I'm looking for then if all goes well I'll try integrating it into my dash code.

Thanks again for your help

You are very welcome!

Now, later on, when you get the idea that using delay() in your program may be a good idea? STOP RIGHT THERE! Toddle on back here and find a better way to approach writing software.

-jim lee

Haha thanks Jim - I will indeed remember this...

In case anyone is interested, here's my finished test code working really nicely:

//Testing out CAN Bus ONLY
#include <FlexCAN.h> //Can Bus

//======================START GLOBAL VARIABLES==============================
//------------Global Variables------------
int RPM;
int SPEED;
int OIL_TEMP;
int COOLANT_TEMP;
int AIR_TEMP;
int INTAKE_TEMP;
int BATT_VOLTAGE;
int FUEL_PRESSURE;
int FUEL_RATE;
int THROTTLE_POS;

//------------Global Time Variables------------
unsigned long currentMillis = 0;
//========================END GLOBAL VARIABLES==============================

//=====================START CAN BUS RELATED VARIABLES======================
//------------CAN PIDs------------
/* Details from http://en.wikipedia.org/wiki/OBD-II_PIDs */
#define MODE1               0x01        //Show current data
#define MODE2               0x02        //Show freeze frame data
#define MODE3               0x03        //Show stored Diagnostic Trouble Codes
#define MODE4               0x04        //Clear Diagnostic Trouble Codes and stored values

#define PID_SUPPORTED       0x00
#define PID_MONITOR_STATUS  0x01
#define PID_SPEED           0x0D
#define PID_RPM             0x0C
#define PID_OIL_TEMP        0x5C
#define PID_COOLANT_TEMP    0x05
#define PID_INTAKE_TEMP     0x0F
#define PID_AIR_TEMP        0x46
#define PID_BATT_VOLTAGE    0x42
#define O2_VOLTAGE_1        0x24
#define O2_VOLTAGE_2        0x25
#define PID_FUEL_PRESSURE   0x0A
#define PID_FUEL_RATE       0x5E
#define PID_THROTTLE_POS    0x11

#define MODE1_RESPONSE      0x41
#define MODE3_RESPONSE      0x43
#define MODE4_RESPONSE      0x44
#define PID_REQUEST         0x7DF
#define PID_REPLY           0x7E8
#define DTC_PID             0x00

//------------DIAGNOSTIC SERIAL DUMP------------
static uint8_t hex[17] = "0123456789abcdef";
static void hexDump(uint8_t dumpLen, uint8_t *bytePtr)
{
  uint8_t working;
  while ( dumpLen-- ) {
    working = *bytePtr++;
    Serial.write( hex[ working >> 4 ] );
    Serial.write( hex[ working & 15 ] );
  }
  Serial.write('\r');
  Serial.write('\n');
}

//------------CAN Variables------------
byte PID_CALL;
byte PID;
byte MODE;
byte PID_RESPONSE;
CAN_message_t can_MsgRx, can_MsgTx;
bool CANWaiting = false;
int CAN_Retry = 0;

//------------Structure Arrray Initialisation------------
struct OBD {
  String Name;
  word   PID;
  bool   Active;
  word   MODE;
  int*   Data;
  int    NoReply;
};

//------------DTC Variables------------
int DTC = 2;
int numDTC;
int DTC1Raw;
int DTC2Raw;
char DTC1 [5];
char DTC2 [5];
int SeenDTC = 0;
int DTCp1;
int DTCp2;
int DTC_CLEARED = 0;

//------------CAN Time Variables------------

//For Serial/CAN
unsigned long CAN_Request_Millis = 10;
unsigned long CAN_Timeout = 100; //CHANGE TO 100
unsigned long CAN_Wait_Duration = 0;

//------------Set Up Structure Array------------
int Row = 0;
OBD OBDArray[] = {{"RPM", PID_RPM, true, MODE1, &RPM, 0},
  {"Speed", PID_SPEED, true, MODE1, &SPEED, 0},
  {"Coolant Temp", PID_COOLANT_TEMP, true, MODE1, &COOLANT_TEMP, 0},
  {"Oil Temp", PID_OIL_TEMP, true, MODE1, &OIL_TEMP, 0},
  {"Air Temp", PID_AIR_TEMP, true, MODE1, &AIR_TEMP, 0},
  {"Intake Temp", PID_INTAKE_TEMP, true, MODE1, &INTAKE_TEMP, 0},
  {"Throttle Pos", PID_THROTTLE_POS, true, MODE1, &THROTTLE_POS, 0},
  {"Fuel Pressure", PID_FUEL_PRESSURE, true, MODE1, &FUEL_PRESSURE, 0},
  {"Fuel Rate", PID_FUEL_RATE, true, MODE1, &FUEL_RATE, 0},
  {"Batt Voltage", PID_BATT_VOLTAGE, true, MODE1, &BATT_VOLTAGE, 0},
  {"Trouble Codes", DTC_PID, true, MODE3, &numDTC, 0}
};
byte Arraysize = sizeof(OBDArray) / sizeof(OBDArray[0]);
//=====================END CAN BUS RELATED VARIABLES=====================

//====== END SET UP VARIABLES =================================================================================

void setup(void) {//+++++++++++++++++++++++++++++++++++++++START SETUP+++++++++++++++++++++++++++++++++++++++++++

  Serial.begin(9600);
  Can0.begin(500000);
  delay(500);
  //Load EEPROM and update Active PIDs in table
}//++++++++++++++++++++++++++++++++++++++++++++++++++++++++END SETUP+++++++++++++++++++++++++++++++++++++++++++++

void loop(void) {//+++++++++++++++++++++++++++++++++++++++START LOOP+++++++++++++++++++++++++++++++++++++++++++++
  currentMillis = millis();

  //----------READ CAN BUS----------
  Get_CAN_Data();
  Print_CAN_Data();

}//++++++++++++++++++++++++++++++++++++++++++++++++++++++++END LOOP++++++++++++++++++++++++++++++++++++++++++++++

//+++++++++++++++++++++++++++++++++++++++START FUNCTIONS+++++++++++++++++++++++++++++++++++++++++++++

//=====================================START CAN BUS FUNTIONS====================================================
//-------------GET CAN DATA-------------
void Get_CAN_Data() {

  //Are we waiting for a response?
  if (CANWaiting == true) { // IS IT THE RESPONSE WE'RE WAITING FOR?? IF SO, SAVE IT AND MOVE ON
    if (Can0.available() > 0) {//Is a response waiting?
      Can0.read(can_MsgRx);
      if ((can_MsgRx.id == PID_REPLY) && ((can_MsgRx.buf[2] == OBDArray[Row].PID) || can_MsgRx.buf[1] == MODE3_RESPONSE)) //Is this message the response we're looking for?
      {
        //Yes
        if (can_MsgRx.buf[1] == MODE3_RESPONSE) {//Is the message an error code?
          //Serial.print("CAN ERROR RECEIVED: "); hexDump(8, can_MsgRx.buf); //Diagnostic - Dump message to Serial useful explanation: https://www.csselectronics.com/screen/page/simple-intro-obd2-explained/language/en
          numDTC = CAN_ERR_Receive(can_MsgRx.buf[2], can_MsgRx.buf[3], can_MsgRx.buf[4], can_MsgRx.buf[5], can_MsgRx.buf[6]);
        }
        else {//Normal PID Received
          //Serial.print("CAN RECEIVED: "); hexDump(8, can_MsgRx.buf); //Diagnostic - Dump message to Serial useful explanation: https://www.csselectronics.com/screen/page/simple-intro-obd2-explained/language/en
          *(OBDArray[Row].Data) = CAN_Receive_Parse(can_MsgRx.buf[2], can_MsgRx.buf[3], can_MsgRx.buf[4]);
        }
        CANWaiting = false; //Reset waiting flag
        NextPID();//Prepare next PID
      }
    }

    else { //Still waiting - check for timeout
      CAN_Wait_Duration = currentMillis - CAN_Request_Millis;
      if (CAN_Wait_Duration > CAN_Timeout) //Waited too long
      {
        OBDArray[Row].NoReply ++; //Add to the no reply counter for this PID
        if (OBDArray[Row].NoReply > 100) { //if >100 no replys then this PID is not available, so stop requesting it and show an error
          //Switch off this PID Request
          OBDArray[Row].Active = false;
          //Show Error
          Serial.print("OBD Error - ");
          Serial.println(OBDArray[Row].Name);
          //Try Next PID
          NextPID();
          CAN_Retry = 0;
          CANWaiting = false;
        }
        //If it has just dropped a single packet then give it another try
        if (CAN_Retry == 2) {
          //Try Next PID
          NextPID();
          CAN_Retry = 0;
          CANWaiting = false;
        }
        else {
          CAN_Retry ++; //Retry this PID
        }
      }
    }
  }

  if (CANWaiting == false) {
    //Request next Data
    CAN_Request(OBDArray[Row].MODE, OBDArray[Row].PID);
    CAN_Request_Millis = currentMillis;
    CANWaiting = true;
  }
}

//-------------REQUEST CAN DATA-------------
void CAN_Request(byte CAN_MODE, byte PID_CALL) {
  can_MsgTx.ext = 0;
  can_MsgTx.id = PID_REQUEST;
  can_MsgTx.len = 8;

  can_MsgTx.buf[0] = 0x02; //Length in number of bytes of remaining data (2 bytes = 02, 3 bytes = 03 etc)
  can_MsgTx.buf[1] = CAN_MODE;
  can_MsgTx.buf[2] = PID_CALL;
  can_MsgTx.buf[3] = 0;
  can_MsgTx.buf[4] = 0;
  can_MsgTx.buf[5] = 0;
  can_MsgTx.buf[6] = 0;
  can_MsgTx.buf[7] = 0;

  can_MsgTx.flags.extended = 0;
  can_MsgTx.flags.remote = 0;

  //Serial.print("CAN SENT: "); hexDump(8, can_MsgTx.buf); //Diagnostic - Dump message to Serial//@@@@@@@@@@@@@@@@@@@@@@@@@@@CAN DIAGNOSTICS@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
  //Serial.println(currentMillis / 10);
  Can0.write(can_MsgTx); //Send message
}

//-------------NEXT PID TO RUN-------------
void NextPID() {
  //Start with next row unless at end of table, then start from 0
  for (int i = 0; i < (Arraysize - 1); i++) {

    if ((i + Row) == (Arraysize - 1)) Row = 0;
    else Row = (Row + 1);
    if (OBDArray[Row].Active == true) break;
  }
}

//-------------SERIAL PRINT CAN DATA-------------
void Print_CAN_Data() {
  Serial.print("RPM: ");
  Serial.print(RPM);
  Serial.print(" | SPEED: ");
  Serial.print(SPEED);
  Serial.print(" | COOLANT: ");
  Serial.print(COOLANT_TEMP);
  Serial.print(" | OIL: ");
  Serial.print(OIL_TEMP);
  Serial.print(" | AMBIENT: ");
  Serial.print(AIR_TEMP);
  Serial.print(" | INTAKE: ");
  Serial.print(INTAKE_TEMP);
  Serial.print(" | TPS: ");
  Serial.print(THROTTLE_POS);
  Serial.print(" | FUEL P: ");
  Serial.print(FUEL_PRESSURE);
  Serial.print(" | FUEL R: ");
  Serial.print(FUEL_RATE);
  Serial.print(" | BATT: ");
  Serial.print(BATT_VOLTAGE);
  Serial.print(" | DTC: ");
  Serial.println(numDTC);
}

//-------------PROCESS DTC INCOMING-------------
int CAN_ERR_Receive(byte pkt2, byte pkt3, byte pkt4, byte pkt5, byte pkt6) {
  numDTC = pkt2;

  if (numDTC > 0) {
    DTCp1 = pkt3;
    DTCp2 = pkt4;
    if (DTCp1 < 10) DTC1Raw = (DTCp1 * 100) + DTCp2;
    else DTC1Raw = (DTCp1 * 1000) + DTCp2;
    sprintf (DTC1, "P%04u", DTC1Raw);
  }
  else sprintf (DTC1, "");

  if (numDTC > 1) {
    DTCp1 = pkt5;
    DTCp2 = pkt6;
    if (DTCp1 < 10) DTC2Raw = (DTCp1 * 100) + DTCp2;
    else DTC2Raw = (DTCp1 * 1000) + DTCp2;
    sprintf (DTC2, "P%04u", DTC2Raw);
  }
  else sprintf (DTC2, "");
  return numDTC;
}

//-------------PROCESS NORMAL PID INCOMING-------------
int CAN_Receive_Parse(byte PID_Received, int Data_1, int Data_2) {
  switch (PID_Received) //Switch means look at different cases
  { /* Details from http://en.wikipedia.org/wiki/OBD-II_PIDs */

    case PID_SPEED:         // A                  [km]  int SPEED =
      return  Data_1 * 1; //CHECK SPEED IS IN KPH AND CONVERSION!!!!
      break;

    case PID_RPM:              //   ((A*256)+B)/4    [RPM]
      return  ((Data_1 * 256) + Data_2) / 4;
      break;

    case PID_OIL_TEMP:     //     A-40              [degree C]
      return  Data_1 - 40;
      break;

    case PID_COOLANT_TEMP:     //     A-40              [degree C]
      return  Data_1 - 40;
      break;

    case PID_INTAKE_TEMP:     //     A-40              [degree C]
      return  Data_1 - 40;
      break;

    case PID_AIR_TEMP:     //     A-40              [degree C]
      return  Data_1 - 40;
      break;

    case PID_BATT_VOLTAGE:            //
      return ((Data_1 * 256) + Data_2);
      break;

    case PID_FUEL_PRESSURE:     //                   [kPa]
      return  Data_1 * 3;
      break;

    case PID_FUEL_RATE:     //                   [l/h]
      return  ((Data_1 * 256) + Data_2) / 2;
      break;

    case PID_THROTTLE_POS:
      return int((Data_1 * 100) / 255);
      break;

    case PID_MONITOR_STATUS:
      return Data_1;// & Data_2 & can_MsgRx.buf[5];
      break;
  }
}
//=========================================END CAN BUS FUNCTIONS====================================================

This topic was automatically closed 120 days after the last reply. New replies are no longer allowed.