IFCT - Improved Flexcan Teensy Library

Status
Not open for further replies.
Ok. Works fine for me now. Two things I messed up on that caused it to stop: 1) left the fifo code in the loop and 2) should have had the onreceive last;

Now I am getting the following:
Code:
*** MB 0  LEN: 8 EXT: 0 REMOTE: 0 TS: 41938 ID: 105 Buffer: 28 44 7F 44 0 0 C4 41 
Pressure: 0.00  Temperature: 0.00
*** MB 1  LEN: 8 EXT: 0 REMOTE: 0 TS: 47165 ID: 1 Buffer: 0 3C 1C 46 0 3C 1C 46 
lat: 0.000000  long: 0.00
*** MB 1  LEN: 8 EXT: 0 REMOTE: 0 TS: 52356 ID: 2 Buffer: 0 0 40 40 0 0 40 41 
fType: 0.00  nSV: 0.00
*** MB 1  LEN: 8 EXT: 0 REMOTE: 0 TS: 57547 ID: 3 Buffer: DC 1 2F 43 B D7 A3 3C 
head: 0.00  spd: 0.00
 
Your MB0/MB1 callbacks seem to be processing on default variable initialization (0)

myMSG seems like it supposed to update those vars, because I see the data printout from myMSG being intact, I don't think your conversion routine is setup properly
 
I am not sure what I just did. Was cleaning up code and did something dumb. Now I only getting;
Code:
*** MB 0  LEN: 8 EXT: 0 REMOTE: 0 TS: 56905 ID: 105 Buffer: 38 2D 7F 44 B8 1E C7 41 
Pressure: 0.00  Temperature: 0.00
*** MB 1  LEN: 8 EXT: 0 REMOTE: 0 TS: 57022 ID: 1 Buffer: 0 0 C6 42 0 0 C6 42 
lat: 0.000000  long: 0.00
*** MB 1  LEN: 8 EXT: 0 REMOTE: 0 TS: 57270 ID: 3 Buffer: 0 0 0 0 0 0 0 0 
head: 0.00  spd: 0.00

Yeah have to check the conversion routine. Put together on the fly just to test. Wanted to make sure I had CAN working first.
 
T3.5 seems to be "locking" up in your last function, probably an overflow, i put a "return" at the top so it never runs and i can reprogram it fine. If i let it as-is i have to push program button on t3.5
so there must be an overflow in your last function
 
Hmmm.. Going to check whats going on after diner. Cooking again tonight.

The last function is the conversion. Was trying to do make a function so that I wouldn't need to repeat it. I am making can_msg_t msg global so I don't have to keep passing. Beside I kept running into conversion errors when I tried :)
 
you can pass only if you add "const" to the overload

Code:
void func([COLOR="#FF0000"]const[/COLOR] CAN_message_t &msg) {}
 
You could do it this way:
Code:
  convert2Float(msg);
Code:
void convert2Float(const CAN_message_t &frame) {
  uint8_t data[sizeof(float)];
  uint8_t data1[sizeof(float)];

  Serial.print("C2F_FRAME: ");
  for (uint8_t i = 0; i < 8; i++) {
    if ( i < 4 ) data[i] = frame.buf[i];
    else data1[i] = frame.buf[i];
    Serial.print(i); Serial.print(" ");
  }
}

the freezes seem to be caused by using memcpy, i commented them out and its running fine... (T3.5)
 
Last edited:
You could also use a union, this doesn't freeze:
Code:
void convert2Float(const CAN_message_t &frame) {
  union ifct {
    float val;
    uint8_t b[4];
  };
  ifct data, data1;

  Serial.print("C2F_FRAME: ");

  for (uint8_t i = 0; i < 8; i++) {
    if ( i < 4 ) data[i] = frame.buf[i];
    else data1[i] = frame.buf[i];
    Serial.print(i); Serial.print(" ");
  }

  Serial.print("VAL1: "); Serial.print(data.val);
  Serial.print("\tVAL2: "); Serial.println(data1.val);
}

OUTPUT:
Code:
C2F_FRAME: 227 132 161 195 76 125 169 137 VAL1: -323.04	VAL2: 0.00

because my code sends random data, i couldnt test the float stuff as its either "ovf" (overflow) on print, an actual float, or plain 0.00.
However you could do same but in reverse at your master end for sending, create an ifct union and name (ifct data), set the float (data.val = 0.01f), and then send the bytes data.b[0] -> data.b[3] to the node
 
Hi tony

Gave your union example a shot and its throwing the following error:
Code:
C:\Users\CyberPalin\Documents\Arduino\CANFIFO.ino\CANFIFO.ino.ino: In function 'void convert2Float(float, float, const CAN_message_t&)':

C:\Users\CyberPalin\Documents\Arduino\CANFIFO.ino\CANFIFO.ino.ino:141:11: error: no match for 'operator[]' (operand types are 'convert2Float(float, float, const CAN_message_t&)::ifct' and 'uint8_t {aka unsigned char}')

       data[i] = frame.buf[i];

           ^

C:\Users\CyberPalin\Documents\Arduino\CANFIFO.ino\CANFIFO.ino.ino:144:12: error: no match for 'operator[]' (operand types are 'convert2Float(float, float, const CAN_message_t&)::ifct' and 'uint8_t {aka unsigned char}')

       data1[i] = frame.buf[i];

            ^

Exact same function no modes. Any ideas?


UPDATE: SOLVED. Has to be data.b

Final UPDATE:
Had to make a minor change to the functions but it works fine. I also resolved the issue I had with no ID2 coming through. I added a delay(1) between writes. Not major issue since the sensor updates are quite a bit longer anyway.

Code:
*** MB 0  LEN: 8 EXT: 0 REMOTE: 0 TS: 56257 ID: 105 Buffer: B2 1E 7F 44 0 0 C8 41 
C2F_FRAME: Pressure: 1020.48  Temperature: 25.00
*** MB 1  LEN: 8 EXT: 0 REMOTE: 0 TS: 58263 ID: 1 Buffer: 0 0 C6 42 0 0 C6 42 
C2F_FRAME: lat: 99.000000  long: 99.00
*** MB 1  LEN: 8 EXT: 0 REMOTE: 0 TS: 60267 ID: 2 Buffer: 0 0 40 40 0 0 30 41 
C2F_FRAME: fType: 3.00  nSV: 11.00
*** MB 1  LEN: 8 EXT: 0 REMOTE: 0 TS: 62271 ID: 3 Buffer: 0 0 0 0 59 39 34 3C 
C2F_FRAME: head: 0.00  spd: 0.01
 
Last edited:
it depends on how fast your handling the MB interrupt, remember your asking for IDs 1-3, so when you send all 3 to a multi-id mailbox, you catch the first, but maybe your not done “processing it” before 2 comes, and no other mailbox is set to capture it, the hardware will “toss it out”. looks like you were getting 1 and 3 fine, so while processing frame #1 you lose frame #2 but you exited fast enough to process frame 3.
 
I added an additional convert function to go from int32's to 4 bytes so I could preserve the accuracy of the of lat and long. So here is my final results:
Code:
*** MB 0  LEN: 8 EXT: 0 REMOTE: 0 TS: 46014 ID: 105 Buffer: CA 8F 7E 44 52 B8 BE 41 
Pressure: 1018.25  Temperature: 23.84
*** MB 1  LEN: 8 EXT: 0 REMOTE: 0 TS: 48021 ID: 1 Buffer: 8F BB 4D 18 C7 C6 0 D4 
lat: 40.77474594  long: -73.81465912
*** MB 1  LEN: 8 EXT: 0 REMOTE: 0 TS: 50025 ID: 2 Buffer: 3 0 0 0 C 0 0 0 
fType: 3  nSV: 12
*** MB 1  LEN: 8 EXT: 0 REMOTE: 0 TS: 52029 ID: 3 Buffer: 0 0 0 0 B D7 A3 3D 
head: 0.00  spd: 0.08


In case anyone is interested here are the two sketches that I used:

SENDER:

Code:
#include <IFCT.h>
#include <Wire.h>
#include "globals.h"
#include <Adafruit_Sensor.h>
#include <Adafruit_BME280.h>

#define SEALEVELPRESSURE_HPA (1013.25)

Adafruit_BME280 bme; // I2C

#include <ublox2.h>
#include "Streaming.h"
#include <string>

// a uBlox object, which is on Teensy hardware
// GPS serial port
UBLOX GPS(GPShwSERIAL);

// the uBlox data structure
gpsData uBloxData;

struct gps rtk;

CAN_message_t msg; // setup a storage buffer

void setup() {
  Serial.begin(115200);
  delay(5000);
  
    bool status;
    // default settings
    // (you can also pass in a Wire library object like &Wire2)
    status = bme.begin();  
    if (!status) {
        Serial.println("Could not find a valid BME280 sensor, check wiring!");
        while (1);
    }

  //GPS SETUP
  // -- AutoBauding test --
  // Try communication with the GPS
  // receiver at 9600 baud, default settings
  // then set GPS UART Baud to 460800
  GPS.begin(9600);
  GPS.SetGPSbaud(460800, true);
  GPS.end();
  GPS.begin(460800);
  
  GPS.SetRATE(200, false);                 // Navigation/Measurement Rate Settings, e.g. 100ms => 10Hz, 200 => 5.00Hz, 1000ms => 1Hz, 10000ms => 0.1Hz
  // Possible Configurations:
  // 60=>16.67Hz, 64=>15.63Hz, 72=>13.89Hz, 80=>12.50Hz, 100=>10.00Hz, 125=>8.00Hz, 200=>5.00Hz, 250=>4.00Hz, 500=>2.00Hz
  // 800=>1.25Hz, 1000=>1.00Hz, 2000=>0.50Hz, 4000=>0.25Hz, 10000=>0.10Hz, 20000=>0.05Hz, 50000=>0.02Hz

  // NOTE: Dis_all_NMEA -strongly suggest changing RX buffer to 255 or more,*otherwise you will miss ACKs*on serial monitor
  GPS.Dis_all_NMEA_Child_MSGs(false);       // Disable All NMEA Child Messages Command

  GPS.SetNAV5(4, false);                    // Set Dynamic platform model Navigation Engine Settings (0:portable, 2: stationary, 3:pedestrian, Etc)
  // Possible Configurations
  // 0: portable, 2: stationary, 3: pedestrian, 4: automotive, 5: sea, 6: airborne with <1g, 7: airborne with <2g
  // 8: airborne with <4g, 9: wrist worn watch (not supported in protocol v.less than 18)

  // ### Periodic auto update ON,OFF Command ###
  GPS.Ena_NAV_PVT(true);                    // Enable periodic auto update NAV_PVT
  //GPS.Dis_NAV_PVT(false);                 // Disable periodic auto update NAV_PVT

    Can0.setMB(MB0, TX);  //Add ,IDE for extended frames.
    Can0.setMB(MB1, TX);  //Add ,IDE for extended frames.


  delay(2500);
    Serial.println("-- Default Test --");
    Serial.println();

}

void loop() {

  msg.flags.extended = 0;  //1 = extended frame 
  msg.flags.remote = 0;
  msg.len = 8;
  delay(2);
  //Send Pressure and Temperature from BME280
  msg.id = 0x69;
  //setup two msg buffer (convert float to uint8's
  convertFloat(bme.readPressure()/100.0F, bme.readTemperature());
  Can0.write(MB0, msg);

  if(GPS.read(&uBloxData)) {
    rtk.iTOW = uBloxData.iTOW;
    rtk.fixType = uBloxData.fixType;
    rtk.numSV = uBloxData.numSV;
    rtk.pDOP = uBloxData.pDOP * 100;
    rtk.lat1 = uBloxData.lat;
    rtk.lon1 = uBloxData.lon;
    rtk.heading = uBloxData.heading;
    rtk.gSpeed = uBloxData.gSpeed;
    rtk.hMSL = uBloxData.hMSL;
  } 

  delay(2);
 //Sends GPS data from the M8N
  //Serial.println(rtk.lat1, 6);
  msg.id = 0x01;
  convertInt(rtk.lat1*1e7, rtk.lon1*1e7);
  //Serial.print("ID1 TX Status:"); 
  //Serial.println(Can0.write(MB1, msg));
  Can0.write(MB1, msg);

  delay(2);
  msg.id = 0x02;
  convertInt(rtk.fixType, rtk.numSV); 
  //Serial.print("ID2 TX Status:"); 
  //Serial.println(Can0.write(MB1, msg));
  Can0.write(MB1, msg);

  delay(2);
  msg.id = 0x03;
  convertFloat(rtk.heading, (float) rtk.gSpeed);
  //Serial.print("ID3 TX Status:"); 
  //Serial.println(Can0.write(MB1, msg));
  Can0.write(MB1, msg);


  static uint32_t _timer = millis();
  if ( millis() - _timer > 4000 ) {
    pinMode(13, OUTPUT); digitalWrite(13, !digitalRead(13));
    Serial.print("*HEARTBEAT* "); Serial.println(millis());
    Serial.println(rtk.lat1, 6);
    //Can0.mailboxStatus();
    _timer = millis();
  }

}

void convertFloat(float a, float b){
    char data[sizeof(float)];
    memcpy(data, &a, sizeof a);    // send data
    char data1[sizeof(float)];
    memcpy(data1, &b, sizeof b);    // send data
    for(uint8_t i = 0; i<4; i++){
      msg.buf[i] = data[i];
      msg.buf[i+4] = data1[i];
    }
}

void convertInt(int32_t a, int32_t b){
    char data[sizeof(int32_t)];
    memcpy(data, &a, sizeof a);    // send data
    char data1[sizeof(int32_t)];
    memcpy(data1, &b, sizeof b);    // send data
    for(uint8_t i = 0; i<4; i++){
      msg.buf[i] = data[i];
      msg.buf[i+4] = data1[i];
    }
}

RECEIVER:
Code:
#include <IFCT.h>
  float pressure, temp;
  int32_t lat, lon,  fType, nSV;
  float head, spd;
  CAN_message_t msg; // setup a temporary storage buffer

void setup() {
  Serial.begin(115200);
  delay(1000);

  Can0.setMB(MB0, RX);  //add ,IDE for extended frames
  Can0.enableMBInterrupt(MB0); // <--- enable per mailbox interrupt
  Can0.setMB(MB1, RX);  //add ,IDE for extended frames
  Can0.enableMBInterrupt(MB1); // <--- enable per mailbox interrupt

  //Set up mailbox filters
  Can0.setMBFilter(REJECT_ALL); // blocks all traffic from entering mailbox (ACCEPT_ALL restores default behaviour)
  Can0.setMBFilter(MB0, 0x69);  // MB0 accecpts BME280 data
  Can0.setMBFilterRange(MB1, 0x01, 0x03);  // MB1 accepts GPS data

  //Can0.onReceive(myMSG);
  Can0.onReceive(MB0, MB0cb); // set MB0 to receive and use MB0cb interrupt
  Can0.onReceive(MB1, MB1cb);

}

void loop() {

  static uint32_t _timer = millis();
  if ( millis() - _timer > 4000 ) {
    pinMode(13, OUTPUT); digitalWrite(13, !digitalRead(13));
    Serial.print("*HEARTBEAT* "); Serial.println(millis());
    _timer = millis();
    Can0.mailboxStatus();
  }

}

void MB1cb(const CAN_message_t &msg) { // <--- mailbox 1 callback
  Serial.print("*** MB "); Serial.print(msg.mb);
  Serial.print("  LEN: "); Serial.print(msg.len);
  Serial.print(" EXT: "); Serial.print(msg.flags.extended);
  Serial.print(" REMOTE: "); Serial.print(msg.rtr);
  Serial.print(" TS: "); Serial.print(msg.timestamp);
  Serial.print(" ID: "); Serial.print(msg.id);
  Serial.print(" Buffer: ");
  for ( uint8_t i = 0; i < msg.len; i++ ) {
    Serial.print(msg.buf[i], HEX); Serial.print(" ");
  } Serial.println();

  if(msg.id == 0x01) {
    convert2Int(lat, lon, msg);
    Serial.print("lat: "); Serial.print(lat/1e7,8);
    Serial.print("  long: "); Serial.println(lon/1e7,8);
  } else if(msg.id == 0x02){
    convert2Int(fType, nSV, msg);
    Serial.print("fType: "); Serial.print(fType);
    Serial.print("  nSV: "); Serial.println(nSV);

  } else if(msg.id == 0x03) {
    convert2Float(head, spd, msg);
    Serial.print("head: "); Serial.print(head,2);
    Serial.print("  spd: "); Serial.println(spd);
    Serial.println();
  }
}

void MB0cb(const CAN_message_t &msg) { // <--- mailbox 1 callback
  Serial.print("*** MB "); Serial.print(msg.mb);
  Serial.print("  LEN: "); Serial.print(msg.len);
  Serial.print(" EXT: "); Serial.print(msg.flags.extended);
  Serial.print(" REMOTE: "); Serial.print(msg.rtr);
  Serial.print(" TS: "); Serial.print(msg.timestamp);
  Serial.print(" ID: "); Serial.print(msg.id);
  Serial.print(" Buffer: ");
  for ( uint8_t i = 0; i < msg.len; i++ ) {
    Serial.print(msg.buf[i], HEX); Serial.print(" ");
  } Serial.println();

  convert2Float(pressure, temp, msg);
  Serial.print("Pressure: "); Serial.print(pressure,2);
  Serial.print("  Temperature: "); Serial.println(temp);
}



void myMSG(const CAN_message_t &msg) { // global callback
  Serial.print("**MB "); Serial.print(msg.mb);
  Serial.print("  LEN: "); Serial.print(msg.len);
  Serial.print(" EXT: "); Serial.print(msg.flags.extended);
  Serial.print(" REMOTE: "); Serial.print(msg.rtr);
  Serial.print(" TS: "); Serial.print(msg.timestamp);
  Serial.print(" ID: "); Serial.print(msg.id);
  Serial.print(" Buffer: ");
  for ( uint8_t i = 0; i < msg.len; i++ ) {
    Serial.print(msg.buf[i], HEX); Serial.print(" ");
  } Serial.println();

      convert2Float(pressure, temp, msg);
      Serial.print(pressure);
      Serial.print(", "); Serial.println(temp);
}

void convert2Float(float &a, float &c, const CAN_message_t &frame) {
  union ifct {
    float val;
    uint8_t b[4];
  };
  ifct data, data1;

  //Serial.print("C2F_FRAME: ");

  for (uint8_t i = 0; i < 4; i++) {
      data.b[i] = frame.buf[i];
      data1.b[i] = frame.buf[i+4];
  }


  //Serial.print("VAL1: "); Serial.print(data.val);
  //Serial.print("\tVAL2: "); Serial.println(data1.val);
  a = data.val;
  c = data1.val;
}

void convert2Int(int32_t &a, int32_t &c, const CAN_message_t &frame) {
  union ifct {
    int32_t val;
    uint8_t b[4];
  };
  ifct data, data1;

  //Serial.print("C2F_FRAME: ");

  for (uint8_t i = 0; i < 4; i++) {
      data.b[i] = frame.buf[i];
      data1.b[i] = frame.buf[i+4];
  }


  //Serial.print("VAL1: "); Serial.print(data.val);
  //Serial.print("\tVAL2: "); Serial.println(data1.val);
  a = data.val;
  c = data1.val;
}
 
Hi Tony,
I rewrote the sender sketch to only send GPS data when available and BME280 data every 100 ms. I also got rid of the memcpy in the convert functions (finally):
Code:
#include <IFCT.h>
#include <Wire.h>
#include "globals.h"
#include <Adafruit_Sensor.h>
#include <Adafruit_BME280.h>

#define SEALEVELPRESSURE_HPA (1013.25)

Adafruit_BME280 bme; // I2C

#include <ublox2.h>
#include "Streaming.h"
#include <string>

// a uBlox object, which is on Teensy hardware
// GPS serial port
UBLOX GPS(GPShwSERIAL);

// the uBlox data structure
gpsData uBloxData;

struct gps rtk;

CAN_message_t msg; // setup a storage buffer

void setup() {
  Serial.begin(115200);
  delay(5000);
  
    bool status;
    // default settings
    // (you can also pass in a Wire library object like &Wire2)
    status = bme.begin();  
    if (!status) {
        Serial.println("Could not find a valid BME280 sensor, check wiring!");
        while (1);
    }

  //GPS SETUP
  // -- AutoBauding test --
  // Try communication with the GPS
  // receiver at 9600 baud, default settings
  // then set GPS UART Baud to 460800
  GPS.begin(9600);
  GPS.SetGPSbaud(460800, true);
  GPS.end();
  GPS.begin(460800);
  
  GPS.SetRATE(200, false);                 // Navigation/Measurement Rate Settings, e.g. 100ms => 10Hz, 200 => 5.00Hz, 1000ms => 1Hz, 10000ms => 0.1Hz
  // Possible Configurations:
  // 60=>16.67Hz, 64=>15.63Hz, 72=>13.89Hz, 80=>12.50Hz, 100=>10.00Hz, 125=>8.00Hz, 200=>5.00Hz, 250=>4.00Hz, 500=>2.00Hz
  // 800=>1.25Hz, 1000=>1.00Hz, 2000=>0.50Hz, 4000=>0.25Hz, 10000=>0.10Hz, 20000=>0.05Hz, 50000=>0.02Hz

  // NOTE: Dis_all_NMEA -strongly suggest changing RX buffer to 255 or more,*otherwise you will miss ACKs*on serial monitor
  GPS.Dis_all_NMEA_Child_MSGs(false);       // Disable All NMEA Child Messages Command

  GPS.SetNAV5(4, false);                    // Set Dynamic platform model Navigation Engine Settings (0:portable, 2: stationary, 3:pedestrian, Etc)
  // Possible Configurations
  // 0: portable, 2: stationary, 3: pedestrian, 4: automotive, 5: sea, 6: airborne with <1g, 7: airborne with <2g
  // 8: airborne with <4g, 9: wrist worn watch (not supported in protocol v.less than 18)

  // ### Periodic auto update ON,OFF Command ###
  GPS.Ena_NAV_PVT(true);                    // Enable periodic auto update NAV_PVT
  //GPS.Dis_NAV_PVT(false);                 // Disable periodic auto update NAV_PVT

    Can0.setMB(MB0, TX);  //Add ,IDE for extended frames.
    Can0.setMB(MB1, TX);  //Add ,IDE for extended frames.


  delay(2500);
    Serial.println("-- Default Test --");
    Serial.println();

}

void loop() {

  static uint32_t _timer0 = millis();
  if ( millis() - _timer0 > 100 ) {
    msg.flags.extended = 0;  //1 = extended frame 
    msg.flags.remote = 0;
    msg.len = 8;
    //Send Pressure and Temperature from BME280
    msg.id = 0x69;
    //setup two msg buffer (convert float to uint8's
    convertFloat(bme.readPressure()/100.0F, bme.readTemperature(), msg);
    Can0.write(MB0, msg);
    _timer0 = millis();
  }
  
  if(GPS.read(&uBloxData)) {
    rtk.iTOW = uBloxData.iTOW;
    rtk.fixType = uBloxData.fixType;
    rtk.numSV = uBloxData.numSV;
    rtk.pDOP = uBloxData.pDOP * 100;
    rtk.lat1 = uBloxData.lat;
    rtk.lon1 = uBloxData.lon;
    rtk.heading = uBloxData.heading;
    rtk.gSpeed = uBloxData.gSpeed;
    rtk.hMSL = uBloxData.hMSL;

    sendGPS();
  } 

  static uint32_t _timer = millis();
  if ( millis() - _timer > 4000 ) {
    pinMode(13, OUTPUT); digitalWrite(13, !digitalRead(13));
    Serial.print("*HEARTBEAT* "); Serial.println(millis());
    Serial.println(rtk.lat1, 6);
    //Can0.mailboxStatus();
    _timer = millis();
  }

}

void sendGPS(){
  msg.flags.extended = 0;  //1 = extended frame 
  msg.flags.remote = 0;
  msg.len = 8;
  //Sends GPS data from the M8N
  msg.id = 0x01;
  convertInt(rtk.lat1*1e7, rtk.lon1*1e7, msg);
  //Serial.print("ID1 TX Status:"); 
  //Serial.println(Can0.write(MB1, msg));
  Can0.write(MB1, msg);

  delay(2);
  msg.id = 0x02;
  convertInt(rtk.fixType, rtk.numSV, msg);
  //Serial.print("ID2 TX Status:"); 
  //Serial.println(Can0.write(MB1, msg));
  Can0.write(MB1, msg);

  delay(2);
  msg.id = 0x03;
  convertFloat(rtk.heading, (float) rtk.gSpeed, msg);
  //Serial.print("ID3 TX Status:"); 
  //Serial.println(Can0.write(MB1, msg));
  Can0.write(MB1, msg);

}


void convertFloat(float s_data, float s_data1, CAN_message_t &frame){
  union ifct {
    float val;
    uint8_t b[4];
  };
  ifct data, data1;

  //Serial.print("C2F_FRAME: ");

  data.val = s_data;
  data1.val = s_data1;
  for (uint8_t i = 0; i < 4; i++) {
      frame.buf[i] = data.b[i];
      frame.buf[i+4] = data1.b[i];
  }
}

void convertInt(int32_t s_data, int32_t s_data1, CAN_message_t &frame) {
  union ifct {
    int32_t val;
    uint8_t b[4];
  };
  ifct data, data1;

  //Serial.print("C2F_FRAME: ");

  data.val = s_data;
  data1.val = s_data1;
  for (uint8_t i = 0; i < 4; i++) {
      frame.buf[i] = data.b[i];
      frame.buf[i+4] = data1.b[i];
  }
}

Sample output:
Code:
*** MB 0  LEN: 8 EXT: 0 REMOTE: 0 TS: 6313 ID: 105 Buffer: D2 A6 7B 44 E1 7A C4 41 
Pressure: 1006.61  Temperature: 24.56
*** MB 0  LEN: 8 EXT: 0 REMOTE: 0 TS: 44780 ID: 105 Buffer: 54 AA 7B 44 E1 7A C4 41 
Pressure: 1006.66  Temperature: 24.56
*** MB 1  LEN: 8 EXT: 0 REMOTE: 0 TS: 41355 ID: 1 Buffer: FE BD 4D 18 36 C4 0 D4 
lat: 40.77481079  long: -73.81472778
*** MB 1  LEN: 8 EXT: 0 REMOTE: 0 TS: 43359 ID: 2 Buffer: 3 0 0 0 9 0 0 0 
fType: 3  nSV: 9
*** MB 1  LEN: 8 EXT: 0 REMOTE: 0 TS: 45363 ID: 3 Buffer: 0 0 0 0 A6 9B 44 3C 
head: 0.00  spd: 0.01

*** MB 0  LEN: 8 EXT: 0 REMOTE: 0 TS: 17709 ID: 105 Buffer: 32 A9 7B 44 66 66 C4 41 
Pressure: 1006.64  Temperature: 24.55
*** MB 0  LEN: 8 EXT: 0 REMOTE: 0 TS: 56174 ID: 105 Buffer: 13 AA 7B 44 66 66 C4 41 
Pressure: 1006.66  Temperature: 24.55
*** MB 1  LEN: 8 EXT: 0 REMOTE: 0 TS: 45931 ID: 1 Buffer: FF BD 4D 18 33 C4 0 D4 
lat: 40.77481079  long: -73.81472778
*** MB 1  LEN: 8 EXT: 0 REMOTE: 0 TS: 47935 ID: 2 Buffer: 3 0 0 0 9 0 0 0 
fType: 3  nSV: 9
*** MB 1  LEN: 8 EXT: 0 REMOTE: 0 TS: 49939 ID: 3 Buffer: 0 0 0 0 9E EF 27 3D 
head: 0.00  spd: 0.04

*** MB 0  LEN: 8 EXT: 0 REMOTE: 0 TS: 29104 ID: 105 Buffer: 93 A8 7B 44 E1 7A C4 41 
Pressure: 1006.63  Temperature: 24.56
*** MB 0  LEN: 8 EXT: 0 REMOTE: 0 TS: 2030 ID: 105 Buffer: 32 A9 7B 44 66 66 C4 41 
Pressure: 1006.64  Temperature: 24.55
*** MB 1  LEN: 8 EXT: 0 REMOTE: 0 TS: 48478 ID: 1 Buffer: FF BD 4D 18 33 C4 0 D4 
lat: 40.77481079  long: -73.81472778
*** MB 1  LEN: 8 EXT: 0 REMOTE: 0 TS: 50482 ID: 2 Buffer: 3 0 0 0 9 0 0 0 
fType: 3  nSV: 9
*** MB 1  LEN: 8 EXT: 0 REMOTE: 0 TS: 52486 ID: 3 Buffer: 0 0 0 0 1B 2F DD 3C 
head: 0.00  spd: 0.03
 
Updates added this week and posted on github:

1) Can0.read(msg) has been updated to only pull messages from non-interrupt enabled mailboxes.
2) Can0.events() changes the way the interrupts work when directly handling the data.

Circular_Buffer library was added to the library which gives you the option to directly fire a callback and have the data be dealt with your callback (unbuffered), or have the ISR buffer the data into the Circular_Buffer arrays for firing from loop() mode

To switch between both, just use or don't use the .events() in your main loop. .events() will fire the callbacks and read directly from the queue, not using .events() will allow direct message interrupt --> callback functionality without using buffers

.events() mode will also allow you to continue to queue interrupt based messages even if your routine is slow in your callback :)

Tony
 
Ok. Just gave it a try and it works like a charm. One of the problems I was having with GPS Lat/Lon data was precision. Couldn't send as floats or int32. Wrote a function to send as doubles but had to add 2 more messages plus 1 for iTOW. That's 5 ids on one mailbox. I also removed all delays from the sending sketch on the T3.5 and the receiving sketch using events() kept up like a charm. Here is a sample of the new output layout I am using (just to see the data):
Code:
Press    Temp      TOW              LAT           LON        FIX       SV      Head    Spd
1012.45	24.53	1307513066	40.7747196	-73.8146183	3	11	0.00	0.07
1012.47	24.53	1307513070	40.7747196	-73.8146182	3	11	0.00	0.02
1012.41	24.53	1307513073	40.7747197	-73.8146182	3	11	0.00	0.01
1012.44	24.53	1307513076	40.7747198	-73.8146181	3	11	0.00	0.01
1012.49	24.53	1307513079	40.7747199	-73.8146181	3	11	0.00	0.03
1012.43	24.53	1307513082	40.7747200	-73.8146181	3	11	0.00	0.03
1012.50	24.53	1307513098	40.7747205	-73.8146177	3	11	0.00	0.04
1012.47	24.53	1307513101	40.7747205	-73.8146176	3	11	0.00	0.01
1012.48	24.53	1307513104	40.7747208	-73.8146175	3	11	0.00	0.02
1012.44	24.53	1307513107	40.7747209	-73.8146173	3	11	0.00	0.04
1012.46	24.53	1307513110	40.7747210	-73.8146172	3	11	0.00	0.02

For reference here are the sketches:
Sending:
Code:
#include <IFCT.h>
#include <Wire.h>
#include "globals.h"
#include <Adafruit_Sensor.h>
#include <Adafruit_BME280.h>

#define SEALEVELPRESSURE_HPA (1013.25)

Adafruit_BME280 bme; // I2C

#include <ublox2.h>
#include "Streaming.h"
#include <string>

// a uBlox object, which is on Teensy hardware
// GPS serial port
UBLOX GPS(GPShwSERIAL);

// the uBlox data structure
gpsData uBloxData;

struct gps rtk;

CAN_message_t msg; // setup a storage buffer

void setup() {
  Serial.begin(115200);
  delay(5000);
  
    bool status;
    // default settings
    // (you can also pass in a Wire library object like &Wire2)
    status = bme.begin();  
    if (!status) {
        Serial.println("Could not find a valid BME280 sensor, check wiring!");
        while (1);
    }

  //GPS SETUP
  // -- AutoBauding test --
  // Try communication with the GPS
  // receiver at 9600 baud, default settings
  // then set GPS UART Baud to 460800
  GPS.begin(9600);
  GPS.SetGPSbaud(460800, true);
  GPS.end();
  GPS.begin(460800);
  
  GPS.SetRATE(100, false);                 // Navigation/Measurement Rate Settings, e.g. 100ms => 10Hz, 200 => 5.00Hz, 1000ms => 1Hz, 10000ms => 0.1Hz
  // Possible Configurations:
  // 60=>16.67Hz, 64=>15.63Hz, 72=>13.89Hz, 80=>12.50Hz, 100=>10.00Hz, 125=>8.00Hz, 200=>5.00Hz, 250=>4.00Hz, 500=>2.00Hz
  // 800=>1.25Hz, 1000=>1.00Hz, 2000=>0.50Hz, 4000=>0.25Hz, 10000=>0.10Hz, 20000=>0.05Hz, 50000=>0.02Hz

  // NOTE: Dis_all_NMEA -strongly suggest changing RX buffer to 255 or more,*otherwise you will miss ACKs*on serial monitor
  GPS.Dis_all_NMEA_Child_MSGs(false);       // Disable All NMEA Child Messages Command

  GPS.SetNAV5(4, false);                    // Set Dynamic platform model Navigation Engine Settings (0:portable, 2: stationary, 3:pedestrian, Etc)
  // Possible Configurations
  // 0: portable, 2: stationary, 3: pedestrian, 4: automotive, 5: sea, 6: airborne with <1g, 7: airborne with <2g
  // 8: airborne with <4g, 9: wrist worn watch (not supported in protocol v.less than 18)

  // ### Periodic auto update ON,OFF Command ###
  GPS.Ena_NAV_PVT(true);                    // Enable periodic auto update NAV_PVT
  //GPS.Dis_NAV_PVT(false);                 // Disable periodic auto update NAV_PVT

    Can0.setMB(MB0, TX);  //Add ,IDE for extended frames.
    Can0.setMB(MB1, TX);  //Add ,IDE for extended frames.


  delay(2500);
    Serial.println("-- Default Test --");
    Serial.println();

}

void loop() {

  static uint32_t _timer0 = millis();
  if ( millis() - _timer0 > 100 ) {
    msg.flags.extended = 0;  //1 = extended frame 
    msg.flags.remote = 0;
    msg.len = 8;
    //Send Pressure and Temperature from BME280
    msg.id = 0x69;
    //setup two msg buffer (convert float to uint8's
    convertFloat(bme.readPressure()/100.0F, bme.readTemperature(), msg);
    Can0.write(MB0, msg);
    _timer0 = millis();
  }

  if(GPS.read(&uBloxData)) {
    rtk.iTOW = uBloxData.iTOW;
    rtk.fixType = uBloxData.fixType;
    rtk.numSV = uBloxData.numSV;
    rtk.pDOP = uBloxData.pDOP * 100;
    rtk.lat1 = uBloxData.lat;
    rtk.lon1 = uBloxData.lon;
    rtk.heading = uBloxData.heading;
    rtk.gSpeed = uBloxData.gSpeed;
    rtk.hMSL = uBloxData.hMSL;

    if((rtk.iTOW - rtk.ts) > 0 ) {
      Serial.println(rtk.lat1, 7);
      sendGPS();
    }

    rtk.ts = rtk.iTOW;
  } 

  static uint32_t _timer = millis();
  if ( millis() - _timer > 4000 ) {
    pinMode(13, OUTPUT); digitalWrite(13, !digitalRead(13));
    Serial.print("*HEARTBEAT* "); Serial.println(millis());
    //Can0.mailboxStatus();
    _timer = millis();
  }

}

void sendGPS(){
  msg.flags.extended = 0;  //1 = extended frame 
  msg.flags.remote = 0;
  msg.len = 8;
  //Sends GPS data from the M8N
  msg.id = 0x01;
  convertDouble(rtk.lat1, msg);
  //Serial.print("ID1 TX Status:"); 
  //Serial.println(Can0.write(MB1, msg));
  Can0.write(MB1, msg);

  //delay(2);
  msg.id = 0x02;
  convertDouble(rtk.lon1, msg);
  //Serial.print("ID2 TX Status:"); 
  //Serial.println(Can0.write(MB1, msg));
  Can0.write(MB1, msg);
  
  //delay(2);
  msg.id = 0x03;
  convertInt(rtk.fixType, rtk.numSV, msg);
  //Serial.print("ID2 TX Status:"); 
  //Serial.println(Can0.write(MB1, msg));
  Can0.write(MB1, msg);

  //delay(2);
  msg.id = 0x04;
  convertFloat(rtk.heading, (float) rtk.gSpeed, msg);
  //Serial.print("ID3 TX Status:"); 
  //Serial.println(Can0.write(MB1, msg));
  Can0.write(MB1, msg);

  //delay(2);
  msg.id = 0x05;
  convertFloat(rtk.iTOW, 0, msg);
  //Serial.print("ID3 TX Status:"); 
  //Serial.println(Can0.write(MB1, msg));
  Can0.write(MB1, msg);

}


void convertFloat(float s_data, float s_data1, CAN_message_t &frame){
  union ifct {
    float val;
    uint8_t b[4];
  };
  ifct data, data1;

  //Serial.print("C2F_FRAME: ");

  data.val = s_data;
  data1.val = s_data1;
  for (uint8_t i = 0; i < 4; i++) {
      frame.buf[i] = data.b[i];
      frame.buf[i+4] = data1.b[i];
  }
}

void convertInt(int32_t s_data, int32_t s_data1, CAN_message_t &frame) {
  union ifct {
    int32_t val;
    uint8_t b[4];
  };
  ifct data, data1;

  //Serial.print("C2F_FRAME: ");

  data.val = s_data;
  data1.val = s_data1;
  for (uint8_t i = 0; i < 4; i++) {
      frame.buf[i] = data.b[i];
      frame.buf[i+4] = data1.b[i];
  }
}

void convertDouble(double s_data, CAN_message_t &frame){
  union ifct {
    double val;
    uint8_t b[8];
  };
  ifct data;

  //Serial.print("C2F_FRAME: ");

  data.val = s_data;
  for (uint8_t i = 0; i < 8; i++) {
      frame.buf[i] = data.b[i];
  }
}

Receiving:
Code:
#include <IFCT.h>
  float pressure, temp, head, spd;
  int32_t iTow, fType, nSV, tmp;
  double lat, lon;
  
  CAN_message_t msg; // setup a temporary storage buffer
  uint8_t gps_msg_cmplt = 0;

void setup() {
  Serial.begin(115200);
  delay(1000);
  
  Can0.setMB(MB0, RX);  //add ,IDE for extended frames
  Can0.enableMBInterrupt(MB0); // <--- enable per mailbox interrupt
  Can0.setMB(MB1, RX);  //add ,IDE for extended frames
  Can0.enableMBInterrupt(MB1); // <--- enable per mailbox interrupt

  //Set up mailbox filters
  Can0.setMBFilter(REJECT_ALL); // blocks all traffic from entering mailbox (ACCEPT_ALL restores default behaviour)
  Can0.setMBFilter(MB0, 0x69);  // MB0 accecpts BME280 data
  Can0.setMBFilterRange(MB1, 0x01, 0x05);  // MB1 accepts GPS data

  //Can0.onReceive(myMSG);
  Can0.onReceive(MB0, MB0cb); // set MB0 to receive and use MB0cb interrupt
  Can0.onReceive(MB1, MB1cb);

  Serial.print("Press(mb) \t Temp(degC) \t Lat \t Lon \t fType \t #SV \t Head \t Spd");  

}

void loop() {
  Can0.events();

  if(gps_msg_cmplt == 1){
    Serial.print(pressure,2); Serial.print("\t"); Serial.print(temp);
    Serial.print("\t"); Serial.print(iTow);
    Serial.print("\t"); Serial.print(lat,7); Serial.print("\t"); Serial.print(lon,7);
    Serial.print("\t"); Serial.print(fType); Serial.print("\t"); Serial.print(nSV);
    Serial.print("\t"); Serial.print(head,2);Serial.print("\t"); Serial.println(spd);
    gps_msg_cmplt = 0;
  }
  
  static uint32_t _timer = millis();
  if ( millis() - _timer > 4000 ) {
    pinMode(13, OUTPUT); digitalWrite(13, !digitalRead(13));
    //Serial.print("*HEARTBEAT* "); Serial.println(millis());
    _timer = millis();
  }
}

void MB1cb(const CAN_message_t &msg) { // <--- mailbox 1 callback

  //print_msg(msg);
  
  if(msg.id == 0x01) {
    convert2Double(lat, msg);
    //Serial.print("lat: "); Serial.print(lat/1e7,8);
    //Serial.print("  long: "); Serial.println(lon/1e7,8);
  } else if(msg.id == 0x02){
    convert2Double(lon, msg);  
  } else if(msg.id == 0x03){
    convert2Int(fType, nSV, msg);
    //Serial.print("fType: "); Serial.print(fType);
    //Serial.print("  nSV: "); Serial.println(nSV);
  } else if(msg.id == 0x04) {
    convert2Float(head, spd, msg);
    //Serial.print("head: "); Serial.print(head,2);
    //Serial.print("  spd: "); Serial.println(spd);
    //Serial.println();
  } else if(msg.id == 0x05){
    convert2Int(iTow, tmp, msg);
    gps_msg_cmplt = 1;
  }
}

void MB0cb(const CAN_message_t &msg) { // <--- mailbox 1 callback

  //print_msg(msg);

  convert2Float(pressure, temp, msg);
  //Serial.print("Pressure: "); Serial.print(pressure,2);
  //Serial.print("  Temperature: "); Serial.println(temp);
}



void myMSG(const CAN_message_t &msg) { // global callback
  print_msg(msg);
}


void print_msg(const CAN_message_t &frame){
  Serial.print("*** MB "); Serial.print(frame.mb);
  Serial.print("  LEN: "); Serial.print(frame.len);
  Serial.print(" EXT: "); Serial.print(frame.flags.extended);
  Serial.print(" REMOTE: "); Serial.print(frame.rtr);
  Serial.print(" TS: "); Serial.print(frame.timestamp);
  Serial.print(" ID: "); Serial.print(frame.id);
  Serial.print(" Buffer: ");
  for ( uint8_t i = 0; i < frame.len; i++ ) {
    Serial.print(frame.buf[i], HEX); Serial.print(" ");
  } Serial.println();
}

void convert2Float(float &a, float &c, const CAN_message_t &frame) {
  union ifct {
    float val;
    uint8_t b[4];
  };
  ifct data, data1;

  //Serial.print("C2F_FRAME: ");

  for (uint8_t i = 0; i < 4; i++) {
      data.b[i] = frame.buf[i];
      data1.b[i] = frame.buf[i+4];
  }


  //Serial.print("VAL1: "); Serial.print(data.val);
  //Serial.print("\tVAL2: "); Serial.println(data1.val);
  a = data.val;
  c = data1.val;
}

void convert2Int(int32_t &a, int32_t &c, const CAN_message_t &frame) {
  union ifct {
    int32_t val;
    uint8_t b[4];
  };
  ifct data, data1;

  //Serial.print("C2F_FRAME: ");

  for (uint8_t i = 0; i < 4; i++) {
      data.b[i] = frame.buf[i];
      data1.b[i] = frame.buf[i+4];
  }


  //Serial.print("VAL1: "); Serial.print(data.val);
  //Serial.print("\tVAL2: "); Serial.println(data1.val);
  a = data.val;
  c = data1.val;
}

void convert2Double(double &a, const CAN_message_t &frame) {
  union ifct {
    double val;
    uint8_t b[8];
  };
  ifct data;

  //Serial.print("C2F_FRAME: ");

  for (uint8_t i = 0; i < 8; i++) {
      data.b[i] = frame.buf[i];
  }


  //Serial.print("VAL1: "); Serial.print(data.val);
  //Serial.print("\tVAL2: "); Serial.println(data1.val);
  a = data.val;
}
 
IFCT and ODD II Implementation?

I couldn't resist the lure of trying to hook up the Teensy to my car (Hyundai Sonata). So I ordered a ODB cable which should get here today or tomorrow, but in the mean time I started looking at some of the ODB2 implementations out there and of course Wikipedia.

Based on what I read (I am really new to ODB and CAN) I think the following sketch may work assuming my car uses the same addresses for requesting and receiving data. If you have any suggestions please let me know:

Code:
#include <IFCT.h>

  CAN_message_t msg_tx; // setup a storage buffer
  CAN_message_t msg_rx; // setup a receive storage buffer

#define ENGINE_COOLANT_TEMP 0x05   //returns 1 bytes, A-40, -40 to 215C 
#define ENGINE_RPM          0x0C   //2 bytes, (256A+B)/4, 0-16,383.75 RPM
#define VEHICLE_SPEED       0x0D   //1 byte, 0-255 km/h
#define OIL_TEMP            0x5C   //calcluated value A-40, returns 1 byte,
                                   //range -40 to 210C, 
#define ECM_Voltage         0x42   //2 bytes, 0-65.535, (256A+B)/1000

/*
  Response id from wiki article
    "Typically the engine or main ECU responds at ID 7E8h. 
    Other modules, like the hybrid controller or battery controller in a Prius, respond at 07E9, 07EA, 07EB, etc. "

  dta: 0x02, 0x01, PID_CODE
  1st byte Number ofadditionaldata bytes (SAE Standard)
  2nd byte is the Mode, for real time data you use Mode 14
  3rd byte is the PID_Code
  Bytes 4-8 value of the specified parameter starting at Byte 0
  
  See https://en.wikipedia.org/wiki/OBD-II_PIDs

  For recieve:
  byte 0: Number of additional data bytes: 3 to 6
  byte 1: Custom mode. Same as query, except that 40h is added to 
    the mode value. So: 41h = show current data;
    42h = freeze frame; etc.
  byte 2: PID code (e.g.: 05 = Engine coolant temperature)
  byte 3: value of the specified parameter, byte 0
  byte 4: value, byte 1 (optional)
  byte 5: value, byte 2 (optional)
  byte 6: value, byte 3 (optional)
  byte 7: not used (may be 00h or 55h)
  
*/  
#define PID_REQUEST    0x7DF
#define PID_REPLY0     0x7E8
#define PID_REPLY1     0x7E9
#define PID_REPLY2     0x7EA
#define PID_REPLY3     0x7EB

void setup() {
  Serial.begin(115200);
  delay(2000);

  Can0.setBaudRate(50000);  //set baud to 500K
  Can0.enableFIFO(1); // turn on fifo
  Can0.enableFIFOInterrupt(0); //  <---- interrupt off, we use pollFIFO // FIFO -------------->>>>>>>>
  Can0.onReceive(myMSG);

  msg_tx.flags.extended = 0;
  msg_tx.flags.remote = 0;
  msg_tx.len = 8;
  msg_tx.buf[3] = 0;
  msg_tx.buf[4] = 0;
  msg_tx.buf[5] = 0;
  msg_tx.buf[6] = 0;
  msg_tx.buf[7] = 0;

}

void loop() {

  static uint32_t _timer = millis();
  if ( millis() - _timer > 4000 ) {
    pinMode(13, OUTPUT); digitalWrite(13, !digitalRead(13));
    Serial.print("*HEARTBEAT* "); Serial.println(millis());
    _timer = millis();
  }

  //Send request for Engine RPMs
  msg_tx.id = PID_REQUEST;
  msg_tx.buf[0] = 0x02;
  msg_tx.buf[1] = 0x01;
  msg_tx.buf[2] = ENGINE_RPM;

  int msg_tx_status = Can0.write(msg_tx);
  while(Can0.read(msg_rx)){
    delay(1);
  }
  
}

void myMSG(const CAN_message_t &msg_rx) { // global callback
  char buffer[30];
  
  Serial.print("MB "); Serial.print(msg_rx.mb);
  Serial.print("  LEN: "); Serial.print(msg_rx.len);
  Serial.print(" EXT: "); Serial.print(msg_rx.flags.extended);
  Serial.print(" REMOTE: "); Serial.print(msg_rx.rtr);
  Serial.print(" TS: "); Serial.print(msg_rx.timestamp);
  Serial.print(" ID: "); Serial.print(msg_rx.id);
  Serial.print(" Buffer: ");
  for ( uint8_t i = 0; i < msg_rx.len; i++ ) {
    Serial.print(msg_rx.buf[i], HEX); Serial.print(" ");
  } Serial.println();

  float rpm =  ((msg_rx.buf[3]*256) + msg_rx.buf[4])/4;
  sprintf(buffer,"%d rpm ",(int) rpm);
  
}
Again this is just a test to see if it works :)


Thanks
Mike
 
the read() function doesnt goto callback but you should check the id field of return before running sprintf as it could be any id, or the engine which is sending other data not just rpm (in case you did multiple requests)

you can also request up to 3 engine datas in a single frame as well :)

if youll use read() youll need to handle the data where the delay(1) is otherwise enable interrupt on FIFO in setup and you’ll get all car data in callback
 
Update:

Added FIFO filtering support, with automatic masking capability as well.

Usage: (by default you have 8 filters for FIFO)
Code:
Can0.setFIFOFilter(REJECT_ALL); /* initially, we set all the filters to ignore all traffic */
Can0.setFIFOFilter(0, 0x2C, STD); /* allow first filter to accept Standard ID 0x2C */
Can0.setFIFOFilter(1, 0x2C, EXT); /* allow second filter to accept Extended ID 0x2C */
Can0.setFIFOFilter(2, 1, 3, STD); /* allow 3rd filter to accept IDs 0x01 and 0x03, Standard ID */
Can0.setFIFOFilterRange(3, 1, 11, EXT); /* allow the 4th filter to accept Extended ID range from 0x01 -> 0x0B */
 
IFCT and ODD II Implementation?

I did some mods to the sketch as a first test. I should be specifically printing Engine RPM but also dumping out all data in the call back:

Code:
#include <IFCT.h>

  CAN_message_t msg_tx; // setup a storage buffer
  CAN_message_t msg_rx; // setup a receive storage buffer

#define ENGINE_COOLANT_TEMP 0x05   //returns 1 bytes, A-40, -40 to 215C 
#define ENGINE_RPM          0x0C   //2 bytes, (256A+B)/4, 0-16,383.75 RPM
#define VEHICLE_SPEED       0x0D   //1 byte, 0-255 km/h
#define OIL_TEMP            0x5C   //calcluated value A-40, returns 1 byte,
                                   //range -40 to 210C, 
#define ECM_Voltage         0x42   //2 bytes, 0-65.535, (256A+B)/1000

char buffer[30];

/*
  Response id from wiki article
    "Typically the engine or main ECU responds at ID 7E8h. 
    Other modules, like the hybrid controller or battery controller in a Prius, respond at 07E9, 07EA, 07EB, etc. "

  dta: 0x02, 0x01, PID_CODE
  1st byte Number ofadditionaldata bytes (SAE Standard)
  2nd byte is the Mode, for real time data you use Mode 14
  3rd byte is the PID_Code
  Bytes 4-8 value of the specified parameter starting at Byte 0
  
  See https://en.wikipedia.org/wiki/OBD-II_PIDs

  For recieve:
  byte 0: Number of additional data bytes: 3 to 6
  byte 1: Custom mode. Same as query, except that 40h is added to 
    the mode value. So: 41h = show current data;
    42h = freeze frame; etc.
  byte 2: PID code (e.g.: 05 = Engine coolant temperature)
  byte 3: value of the specified parameter, byte 0
  byte 4: value, byte 1 (optional)
  byte 5: value, byte 2 (optional)
  byte 6: value, byte 3 (optional)
  byte 7: not used (may be 00h or 55h)
  
*/  
#define PID_REQUEST    0x7DF
#define PID_REPLY0     0x7E8
#define PID_REPLY1     0x7E9
#define PID_REPLY2     0x7EA
#define PID_REPLY3     0x7EB

void setup() {
  Serial.begin(115200);
  delay(2000);


  Can0.setBaudRate(50000);  //set baud to 500K
  Can0.enableFIFO(1); // turn on fifo
  Can0.enableFIFOInterrupt(1); //  <---- interrupt off, we use pollFIFO // FIFO -------------->>>>>>>>
  Can0.setMB(MB0, TX);
  Can0.setMB(MB1, RX);
  Can0.setMB(MB2, RX);
  Can0.setMB(MB3, RX);
  
  Can0.onReceive(myMSG);

  msg_tx.flags.extended = 0;
  msg_tx.flags.remote = 0;
  msg_tx.len = 8;
  msg_tx.buf[3] = 0;
  msg_tx.buf[4] = 0;
  msg_tx.buf[5] = 0;
  msg_tx.buf[6] = 0;
  msg_tx.buf[7] = 0;

}

void loop() {

  static uint32_t _timer = millis();
  if ( millis() - _timer > 4000 ) {
    pinMode(13, OUTPUT); digitalWrite(13, !digitalRead(13));
    Serial.print("*HEARTBEAT* "); Serial.println(millis());
    _timer = millis();
  }

  //Send request for Engine RPMs
  msg_tx.id = PID_REQUEST;
  msg_tx.buf[0] = 0x02;
  msg_tx.buf[1] = 0x01;
  msg_tx.buf[2] = ENGINE_RPM;

  int msg_tx_status = Can0.write(msg_tx);

  while(Can0.read(msg_rx)){
    if(msg_rx.buf[2] == ENGINE_RPM){
      float rpm =  ((msg_rx.buf[3]*256) + msg_rx.buf[4])/4;
      sprintf(buffer,"%d rpm \n",(int) rpm);
    }
  }
}

void myMSG(const CAN_message_t &msg) { // global callback
  Serial.print("**** MB "); Serial.print(msg.mb);
  Serial.print("  LEN: "); Serial.print(msg.len);
  Serial.print(" EXT: "); Serial.print(msg.flags.extended);
  Serial.print(" REMOTE: "); Serial.print(msg.rtr);
  Serial.print(" TS: "); Serial.print(msg.timestamp);
  Serial.print(" ID: "); Serial.print(msg.id);
  Serial.print(" Buffer: ");
  for ( uint8_t i = 0; i < msg_rx.len; i++ ) {
    Serial.print(msg.buf[i], HEX); Serial.print(" ");
  } Serial.println();
 
}
 
setMB wont work on FIFO unless the MB’s are above or equal to MB8, FIFO takes over the first 8 mailboxes

your write will actually be going out MB8 since its first transmit mailbox, MB0 wasnt set because its owned by FIFO
Also because of this, you dont have any actual reception mailboxes, so fifo gets your ids and itll goto the interrupt callback, read() is not actually working at all since it doesnt touch interrupt driven fifo or mailboxes

what you could do to collect ECU data in a single mailbox is:

Code:
Can0.setMB(8,RX); /* standard id collection mailbox */
Can0.setMBFilterRange(MB8,0x7DF,7EB) /* accept that data range */
Can0.enableMBInterrupt(MB8); /* enable MB interrupt */

This would get ECU specific data to your callback before it enters the FIFO
 
setMB wont work on FIFO unless the MB’s are above or equal to MB8, FIFO takes over the first 8 mailboxes
Ok guess I had it backwards thought it was the other way around. So,
Code:
 MB 0 - 5 are FIFO
MB6-7 are for FIFO filter
and finally
MB 8-15 are free.


Think I will do it the way you suggested and give it a whirl. Would recommend using events as well?


Mike

EDIT: Guess I will have to remember a couple of other things as well:
1. setMRP(0) will have FIFO filters processed before mailboxes, 1 has MB processed first which is default
2. setRFFN sets the number of filters with a max of 32. I'll let u explain this when u implement the function :)
 
Last edited:
yes events() will enable the queue system to clear the mailbox faster in case your callbacks take too long in processing, the design might change a bit but events() most likely will stay.
 
Hi Tony.

Finally got it working on the receive side now to test the tx side.

I wrote a small test sketch for the T3.5 that sends the RPM PID every heartbeat and random data every 500ms:
Code:
#include <IFCT.h>

CAN_message_t msg; // setup a storage buffer

void setup() {
  Serial.begin(115200);
  delay(2000);
  
    Serial.println("-- ODBII Test --");
    Serial.println();

}

void loop() {

  static uint32_t _timer0 = millis();
  if ( millis() - _timer0 > 500 ) {
  delay(20);

  msg.flags.extended = 0;
  msg.flags.remote = 0;
  msg.len = 8;
  msg.id = 0x7E8;
  msg.buf[0] = 0x02;
  msg.buf[1] = 0;
  msg.buf[2] = random(0, 255);
  msg.buf[3] = random(0, 255);
  msg.buf[4] = random(0, 255);
  msg.buf[5] = random(0, 255);
  msg.buf[6] = random(0, 255);
  msg.buf[7] = random(0, 255);
  Can0.write(msg);
  _timer0 = millis();
  }

  static uint32_t _timer = millis();
  if ( millis() - _timer > 4000 ) {
    pinMode(13, OUTPUT); digitalWrite(13, !digitalRead(13));
    Serial.print("*HEARTBEAT* "); Serial.println(millis());
    //Can0.mailboxStatus();
    msg.flags.extended = 0;
    msg.flags.remote = 0;
    msg.len = 8;
    msg.id = 0x7E8;
    msg.buf[0] = 0x02;
    msg.buf[1] = 0;
    msg.buf[2] = 0x0C;
    msg.buf[3] = 0x10;
    msg.buf[4] = 0x15;
    msg.buf[5] = 0;
    msg.buf[6] = 0;
    msg.buf[7] = 0;
    Can0.write(msg);
    //Can0.mailboxStatus();
    _timer = millis();
  }

}
On the master side I incorporated your changes:
Code:
#include <IFCT.h>

  CAN_message_t msg_tx; // setup a storage buffer
  CAN_message_t msg; // setup a receive storage buffer

#define ENGINE_COOLANT_TEMP 0x05   //returns 1 bytes, A-40, -40 to 215C 
#define ENGINE_RPM          0x0C   //2 bytes, (256A+B)/4, 0-16,383.75 RPM
#define VEHICLE_SPEED       0x0D   //1 byte, 0-255 km/h
#define OIL_TEMP            0x5C   //calcluated value A-40, returns 1 byte,
                                   //range -40 to 210C, 
#define ECM_Voltage         0x42   //2 bytes, 0-65.535, (256A+B)/1000

/*
  Response id from wiki article
    "Typically the engine or main ECU responds at ID 7E8h. 
    Other modules, like the hybrid controller or battery controller in a Prius, respond at 07E9, 07EA, 07EB, etc. "

  dta: 0x02, 0x01, PID_CODE
  1st byte Number ofadditionaldata bytes (SAE Standard)
  2nd byte is the Mode, for real time data you use Mode 14
  3rd byte is the PID_Code
  Bytes 4-8 value of the specified parameter starting at Byte 0
  
  See https://en.wikipedia.org/wiki/OBD-II_PIDs

  For recieve:
  byte 0: Number of additional data bytes: 3 to 6
  byte 1: Custom mode. Same as query, except that 40h is added to 
    the mode value. So: 41h = show current data;
    42h = freeze frame; etc.
  byte 2: PID code (e.g.: 05 = Engine coolant temperature)
  byte 3: value of the specified parameter, byte 0
  byte 4: value, byte 1 (optional)
  byte 5: value, byte 2 (optional)
  byte 6: value, byte 3 (optional)
  byte 7: not used (may be 00h or 55h)
  
*/  
#define PID_REQUEST    0x7DF
#define PID_REPLY0     0x7E8
#define PID_REPLY1     0x7E9
#define PID_REPLY2     0x7EA
#define PID_REPLY3     0x7EB

void setup() {
  Serial.begin(115200);
  delay(2000);

  //Can0.setBaudRate(50000);  //set baud to 500K
  Can0.enableFIFO(1);
  Can0.setMB(MB8,RX); /* standard id collection mailbox */
  Can0.setMBFilterRange(MB8,0x7DF,0x7EB); /* accept that data range */
  Can0.enableMBInterrupt(MB8); /* enable MB interrupt */

  Can0.onReceive(MB8, myMSG);

  msg_tx.flags.extended = 0;
  msg_tx.flags.remote = 0;
  msg_tx.len = 8;
  msg_tx.buf[3] = 0;
  msg_tx.buf[4] = 0;
  msg_tx.buf[5] = 0;
  msg_tx.buf[6] = 0;
  msg_tx.buf[7] = 0;

}

void loop() {
  Can0.events();
  static uint32_t _timer = millis();
  if ( millis() - _timer > 4000 ) {
    pinMode(13, OUTPUT); digitalWrite(13, !digitalRead(13));
    Serial.print("*HEARTBEAT* "); Serial.println(millis());
    _timer = millis();
  }

  //Send request for Engine RPMs
  msg_tx.id = PID_REQUEST;
  msg_tx.buf[0] = 0x02;
  msg_tx.buf[1] = 0x01;
  msg_tx.buf[2] = ENGINE_RPM;

  //int msg_tx_status = Can0.write(msg_tx);

}

void myMSG(const CAN_message_t &msg) { // global callback
  Serial.print("**** MB "); Serial.print(msg.mb);
  Serial.print("  LEN: "); Serial.print(msg.len);
  Serial.print(" EXT: "); Serial.print(msg.flags.extended);
  Serial.print(" REMOTE: "); Serial.print(msg.rtr);
  Serial.print(" TS: "); Serial.print(msg.timestamp);
  Serial.print(" ID: "); Serial.print(msg.id);
  Serial.print(" Buffer: ");
  for ( uint8_t i = 0; i < msg.len; i++ ) {
    Serial.print(msg.buf[i], HEX); Serial.print(" ");
  } Serial.println();

  if(msg.buf[2] == ENGINE_RPM){
      float rpm =  ((msg.buf[3]*256) + msg.buf[4])/4;
      Serial.println(rpm);
  }
}
it basically reads the stream data from T3.5 and prints the RPM when it gets encountered.


Code:
*HEARTBEAT* 6301
**** MB 8  LEN: 8 EXT: 0 REMOTE: 0 TS: 25917 ID: 2024 Buffer: 2 0 D 8A 7 C8 EE 35 
**** MB 8  LEN: 8 EXT: 0 REMOTE: 0 TS: 22636 ID: 2024 Buffer: 2 0 E5 F1 49 A0 36 98 
**** MB 8  LEN: 8 EXT: 0 REMOTE: 0 TS: 48962 ID: 2024 Buffer: 2 0 C 10 15 0 0 0 
RPM: 1029.00
**** MB 8  LEN: 8 EXT: 0 REMOTE: 0 TS: 19354 ID: 2024 Buffer: 2 0 8 FE 47 85 47 E0 
**** MB 8  LEN: 8 EXT: 0 REMOTE: 0 TS: 16071 ID: 2024 Buffer: 2 0 5 E2 D0 84 24 D7 
**** MB 8  LEN: 8 EXT: 0 REMOTE: 0 TS: 12789 ID: 2024 Buffer: 2 0 45 FB A5 EF B6 19 
**** MB 8  LEN: 8 EXT: 0 REMOTE: 0 TS: 9509 ID: 2024 Buffer: 2 0 7 45 1F 86 7A B7 
**** MB 8  LEN: 8 EXT: 0 REMOTE: 0 TS: 6227 ID: 2024 Buffer: 2 0 5E 58 2B 55 41 E9 
*HEARTBEAT* 10302
**** MB 8  LEN: 8 EXT: 0 REMOTE: 0 TS: 2945 ID: 2024 Buffer: 2 0 2C 9F 1F E4 33 FD 
**** MB 8  LEN: 8 EXT: 0 REMOTE: 0 TS: 65200 ID: 2024 Buffer: 2 0 40 D6 94 A0 CD 5D 
**** MB 8  LEN: 8 EXT: 0 REMOTE: 0 TS: 61917 ID: 2024 Buffer: 2 0 F0 B4 77 99 C1 34 
**** MB 8  LEN: 8 EXT: 0 REMOTE: 0 TS: 52312 ID: 2024 Buffer: 2 0 C 10 15 0 0 0 
RPM: 1029.00
**** MB 8  LEN: 8 EXT: 0 REMOTE: 0 TS: 58635 ID: 2024 Buffer: 2 0 16 3B 46 D7 2D 38 
**** MB 8  LEN: 8 EXT: 0 REMOTE: 0 TS: 55353 ID: 2024 Buffer: 2 0 F1 C7 28 C B4 61 
**** MB 8  LEN: 8 EXT: 0 REMOTE: 0 TS: 52070 ID: 2024 Buffer: 2 0 48 28 C6 52 E B8 
**** MB 8  LEN: 8 EXT: 0 REMOTE: 0 TS: 48786 ID: 2024 Buffer: 2 0 C5 75 75 2A CF CE 
**** MB 8  LEN: 8 EXT: 0 REMOTE: 0 TS: 45503 ID: 2024 Buffer: 2 0 1E 62 4D 2C BF 98 
*HEARTBEAT* 14303

Will play with it some more later.
 
IFCT and ODD II Implementation

Hi all

After a frustrating day getting the connector wiring wrong and dealing with the heat I finally was able to get obd data from the Hyundai Sonata 2006. Had to restructure the sketch to accept all MB though. Unfortunately the data doesn't make any sense to me when compared to the standard. Here is a sample:
Code:
**** MB 0  LEN: 8 EXT: 0 REMOTE: 0 TS: 35261 ID: 1349 Buffer: DC 2E 0 8C A5 0 A6 0 
**** MB 0  LEN: 8 EXT: 0 REMOTE: 0 TS: 35376 ID: 672 Buffer: 8 0 66 1C 14 1 15 0 
**** MB 0  LEN: 8 EXT: 0 REMOTE: 0 TS: 35495 ID: 608 Buffer: 36 36 36 28 FF EA 0 0 
**** MB 0  LEN: 8 EXT: 0 REMOTE: 0 TS: 35613 ID: 1695 Buffer: 3C 71 0 0 0 0 0 0 
**** MB 0  LEN: 8 EXT: 0 REMOTE: 0 TS: 53736 ID: 608 Buffer: 36 36 36 28 FF EA 0 0 
**** MB 0  LEN: 8 EXT: 0 REMOTE: 0 TS: 53854 ID: 1087 Buffer: 0 40 50 FE 46 BB C 0 
**** MB 0  LEN: 8 EXT: 0 REMOTE: 0 TS: 53973 ID: 1088 Buffer: FF FF 0 AA AA AA AA AA 
**** MB 0  LEN: 8 EXT: 0 REMOTE: 0 TS: 55055 ID: 339 Buffer: 0 21 0 FF 0 FF 0 0 
**** MB 0  LEN: 8 EXT: 0 REMOTE: 0 TS: 57185 ID: 790 Buffer: 45 36 8F D 36 28 0 7C 
**** MB 0  LEN: 8 EXT: 0 REMOTE: 0 TS: 57299 ID: 809 Buffer: B 98 7F 8 11 2E 0 21 
**** MB 0  LEN: 8 EXT: 0 REMOTE: 0 TS: 57416 ID: 640 Buffer: 10 0 84 98 1B 1D 66 4E

This is a snippet but the grouping seems to keep repeating.

Thanks
Mike
 
Yup, thats normal. The groupings and repetitive broadcasts are normal, this shows your connections are fine. The data you see is the traffic scrolling off the multiple modules on the car, not just the ECU.

You could now talk to the ECU provided its the correct 11/29bit identifier or even parse the live traffic in the stream (which needs to be decoded(lots of patience)), it definately contains RPM data, speedometer, and many other things, even a bit for the AC compressor status is there too (at least on mine) :)
 
Hello all, I've updated the library to support FIFO filtering tables B & C, auto masking is done with Tables A & B, I omitted to include auto masking for TableC as since all extended and standard IDs are partial, it would make no sense really.

As per the datasheet:

RXIDA
— Rx Frame Identifier (Format A)
Specifies an ID to be used as acceptance criteria for the FIFO. In the standard frame
format, only the 11 most significant bits (29 to 19) are used for frame identification. In
the extended frame format, all bits are used.
RXIDB_0, RXIDB_1
— Rx Frame Identifier (Format B)
Specifies an ID to be used as acceptance criteria for the FIFO. In the standard frame
format, the 11 most significant bits (a full standard ID) (29 to 19 and 13 to 3) are used for
frame identification. In the extended frame format, all 14 bits of the field are compared to
the 14 most significant bits of the received ID.
RXIDC_0, RXIDC_1, RXIDC_2, RXIDC_3
— Rx Frame Identifier (Format C)
Specifies an ID to be used as acceptance criteria for the FIFO. In both standard and
extended frame formats, all 8 bits of the field are compared to the 8 most significant bits
of the received ID.

In the library, tables A & B support range based ID inputs in the library, TableC accepts 4 ID's extended or standard
You can still use mailbox filtering for mailboxes above the FIFO table that are available.
 
Status
Not open for further replies.
Back
Top