Hello Collin,
Thanks for open a window for asking your expert help. I am trying to build communication on CAN bus using your library.
My system: on one side, NVidia TX2 (with CAN controller CAN MCP2515T-I/ML and transceiver TJA1051TK/3); the other side Teensy 3.6 with "Dual CAN-Bus adapter for Teensy 3.5, 3.6" (https://www.tindie.com/products/Fusion/dual-can-bus-adapter-for-teensy-35-36/) which uses transceiver SN65HVD230D.
My first question is in the example "CANtest", as I understand it is same setup as me, why the enable pin is set to HIGH (which as Pawelsky point out the transceiver SN65HVD230D is in sleep mode)? Is slope mode you are using? Should I put pull down resistor for the enable pin?
I put an oscilloscope on the CANH and CANL, here is a picture of what I got:

If the hardware is of no problem. My problem is the communication on CAN bus always fails after some seconds. I had tried both teachtop's library and your library; I also tried your library and examples. Now I only succeed in the CANtest example with loopback.
On the TX2 side, CAN socket is used. I use a timer (40hz) to send command to read I2C altimeter (LW20/C): send command to CAN bus; delay 1ms; read CAN bus.
On the Teensy 3.6 side, I tried both scan mode and interrupt mode; interrupt mode will be a little bit better, but also failed after some reading the CAN bus (TX2 side). I will attach the code for the interrupt mode in the end. From the debug, it seems the mailboxes (TX) at the Teensy side are not released or the the data wrote to CAN bus from Teensy 3.6 is not read out from the TX2 side. I see there is a "txHandler", how to use it and what is this for? What is the event for this handler?
There are two extra headers for the LED and altimeter I work with. I put the function to read distance here. After this function is the main() of the Teensy 3.6.
static uint8_t getDefaultMedianDistance(byte addr, uint8_t **strCAN)
{
int i = 0;
uint8_t count = 0;
uint8_t str[8];
Wire.beginTransmission(addr);
Wire.write("?LD\r\n");
Wire.endTransmission();
delay(2); //must have a delay even only 1ms, or a lot of mess up
Wire.requestFrom(addr, (byte)12); // general call
while( Wire.available() ) {
char c = Wire.read();
//Serial.print(c);
if(i >= 5 && c != '\r' && c != '\n' && c != ' ' && c != '\0')
{
str[i - 5] = c;
count++;
}
i++;
}
//Serial.print('\n');
*strCAN = str;
return count;
}
The following is the main() of Teensy 3.6:
// -------------------------------------------------------------
// CANtest for Teensy 3.6 dual CAN bus
// by Collin Kidder, Based on CANTest by Pawelsky (based on CANtest by teachop)
//
// Both buses are left at default 250k speed and the second bus sends frames to the first
// to do this properly you should have the two buses linked together. This sketch
// also assumes that you need to set enable pins active. Comment out if not using
// enable pins or set them to your correct pins.
//
// This sketch tests both buses as well as interrupt driven Rx and Tx. There are only
// two Tx buffers by default so sending 5 at a time forces the interrupt driven system
// to buffer the final three and send them via interrupts. All the while all Rx frames
// are internally saved to a software buffer by the interrupt handler.
//
#include <Time.h>
#include "FlexCAN.h"
//#include <Wire.h>
#include <i2c_t3.h> // for multiple i2c ports
#include "BlinkM_funcs.h"
byte addr_BlinkM = 0x09; // address of BlinkM
#include "lw20_funcs.h"
byte addr_lw20 = 0x66;
#ifndef MK66FX1M0
#error "Teensy 3.6 with dual CAN bus is required to run this example"
#endif
class ExampleClass : public CANListener
{
public:
void printFrame(CAN_message_t &frame, int mailbox);
bool frameHandler(CAN_message_t &frame, int mailbox, uint8_t controller); //overrides the parent version so we can actually do something
void txHandler (int mailbox, uint8_t controller);
bool tx_completed_ = false;
};
void ExampleClass::printFrame(CAN_message_t &frame, int mailbox)
{
Serial.print("ID: ");
Serial.print(frame.id, HEX);
Serial.print(" Data: ");
for (int c = 0; c < frame.len; c++)
{
Serial.print(frame.buf[c], HEX);
Serial.write(' ');
}
Serial.write('\r');
Serial.write('\n');
Serial.print("mailbox: ");
Serial.print(mailbox);
Serial.write('\r');
Serial.write('\n');
}
void ExampleClass::txHandler (int mailbox, uint8_t controller)
{
tx_completed_ = true;
Serial.print("One write for mailbox and controller: ");
Serial.println(mailbox);
//Serial.print(", ");
//Serial.println(controller);
}
bool ExampleClass::frameHandler(CAN_message_t &frame, int mailbox, uint8_t controller)
{
CAN_message_t outMsg;
//printFrame(frame, mailbox);
switch (frame.id)
{
case 0x00: //BlinkM LED
{
switch (frame.buf[1])
{
case 'n':
{
BlinkM_setRGB(frame.buf[0], frame.buf[2], frame.buf[3], frame.buf[4]);
break;
}
case 'c':
{
break;
}
case 'h':
{
break;
}
case 'C':
{
break;
}
case 'H':
{
break;
}
case 'p':
{
break;
}
case 'o':
{
break;
}
case 'f':
{
break;
}
case 't':
{
break;
}
case 'W':
{
break;
}
case 'L':
{
break;
}
case 'A':
{
break;
}
case 'a':
{
outMsg.buf[0] = addr_BlinkM;
outMsg.buf[1] = BlinkM_getAddress(addr_BlinkM);
outMsg.ext = 0;
outMsg.id = 0x00;
//outMsg.timeout = 100;
outMsg.len = 2;
//tx_completed_ = false;
Can1.write(outMsg);
//while (!tx_completed_);
Serial.print("Address of BlinkM: 0x");
Serial.println(outMsg.buf[1]);
break;
}
case 'B':
{
break;
}
case 'Z':
{
int ver = BlinkM_getVersion(addr_BlinkM);
outMsg.buf[0] = addr_BlinkM;
outMsg.buf[1] = (ver >> 8) & 0xff;
outMsg.buf[2] = ver & 0xff;
outMsg.ext = 0;
outMsg.id = 0x00;
//outMsg.timeout = 100;
outMsg.len = 3;
//tx_completed_ = false;
Can1.write(outMsg);
//while (!tx_completed_);
Serial.print("BlinkM version: 0x");
Serial.print(outMsg.buf[1], HEX);
Serial.println(outMsg.buf[2], HEX);
break;
}
case 'R':
{
break;
}
case 'g':
{
byte r=0, g=0, b=0;
BlinkM_getRGBColor(addr_BlinkM, &r, &g, &b);
outMsg.buf[0] = addr_BlinkM;
outMsg.buf[1] = r;
outMsg.buf[2] = g;
outMsg.buf[3] = b;
Serial.print("r: 0x");
Serial.print(r, HEX);
Serial.print(", g: 0x");
Serial.print(g, HEX);
Serial.print(", b: 0x");
Serial.println(b, HEX);
outMsg.ext = 0;
outMsg.id = 0x00;
//outMsg.timeout = 100;
outMsg.len = 4;
//tx_completed_ = false;
Can1.write(outMsg);
//while (!tx_completed_);
break;
}
default:
{
break;
}
}
break;
}
case 0x01: // LW20 altimeter
{
String lw20CanCmd = "";
uint8_t *strCAN;
uint8_t strCANLen;
for(int i = 1; i < frame.len; i++)
lw20CanCmd += (char)frame.buf[i];
//Serial.print(lw20CanCmd);
//Serial.print('\n');
if(lw20CanCmd == "?LD")
{
strCANLen = getDefaultMedianDistance(addr_lw20, &strCAN) + 1;
//Serial.print("\noutMsg.buf: ");
for(int i = 1; i < strCANLen; i++)
{
outMsg.buf[i] = strCAN[i-1];
// Serial.print(outMsg.buf[i]);
}
//Serial.print('\n');
}
else if(lw20CanCmd == "?PN")
{
strCANLen = getProductName(addr_lw20, &strCAN) + 1;
//Serial.print("\noutMsg.buf: ");
for(int i = 1; i < strCANLen; i++)
{
outMsg.buf[i] = strCAN[i-1];
// Serial.print(outMsg.buf[i]);
}
//Serial.print('\n');
}
else if(lw20CanCmd == "?PS")
{
strCANLen = getSoftwareRev(addr_lw20, &strCAN) + 1;
//Serial.print("\noutMsg.buf: ");
for(int i = 1; i < strCANLen; i++)
{
outMsg.buf[i] = strCAN[i-1];
// Serial.print(outMsg.buf[i]);
}
//Serial.print('\n');
}
else if(lw20CanCmd == "?PF")
{
strCANLen = getFirmwareRev(addr_lw20, &strCAN) + 1;
//Serial.print("\noutMsg.buf: ");
for(int i = 1; i < strCANLen; i++)
{
outMsg.buf[i] = strCAN[i-1];
// Serial.print(outMsg.buf[i]);
}
//Serial.print('\n');
}
outMsg.ext = 0;
outMsg.id = 0x01;
//outMsg.timeout = 100;
outMsg.buf[0] = addr_lw20;
outMsg.len = strCANLen;
//tx_completed_ = false;
Can1.write(outMsg);
//while (!tx_completed_);
// Serial.print("outMsg.buf: ");
// for(int i = 1; i < strCANLen; i++)
// {
// Serial.print((char)outMsg.buf[i]);
// }
// Serial.print('\t');
// Serial.print("Length:");
// Serial.print(strCANLen);
// Serial.print('\n');
break;
}
default:
{
break;
}
}
return true;
}
ExampleClass exampleClass;
// -------------------------------------------------------------
void setup(void)
{
//Can0.setNumTxBoxes(8);
Can1.setNumTxBoxes(8);
//Can0.begin(1000000);
Can1.begin(1000000);
//if using enable pins on a transceiver they need to be set on
pinMode(2, OUTPUT);
pinMode(35, OUTPUT);
//digitalWrite(35, HIGH);
digitalWrite(2, LOW);
digitalWrite(35, LOW);
//Can0.attachObj(&exampleClass);
Can1.attachObj(&exampleClass);
exampleClass.attachGeneralHandler();
//exampleClass.attachMBHandler(2);
//exampleClass.attachMBHandler(14);
Wire.begin();
Serial.begin(115200);
delay(2000);
Serial.println(F("Teensy 3.6 dual CAN, BlinkM and LW20 altimeter."));
// init BlinkM funcs
BlinkM_begin();
BlinkM_off(addr_BlinkM);
// print address of BlinkMs
Serial.print("Address of BlinkM: 0x");
Serial.print(BlinkM_getAddress(addr_BlinkM), HEX);
Serial.println();
}
uint8_t j = 0;
uint32_t clk_count = 0;
unsigned long old_millis = 0;
unsigned long current_millis;
// -------------------------------------------------------------
void loop(void)
{
clk_count++;
if (clk_count > 1000000)
{
//Serial.println(now());//Returns the number of seconds since Jan 1 1970
current_millis = millis(); //Number of milliseconds since the program started (unsigned long)
Serial.println(millis() - old_millis);
old_millis = current_millis;
clk_count = 0;
}
//CAN_message_t inMsg, outMsg;
/*outMsg.buf[0] = 0x00;
outMsg.buf[1] = BlinkM_getAddress(0x09);
outMsg.ext = 0;
outMsg.id = 0x00;
//outMsg.timeout = 100;
outMsg.len = 2;
Can1.write(outMsg);
for(uint8_t i = 0; i < 8; i++, j++)
outMsg.buf[i] = j;
outMsg.ext = 0;
outMsg.id = 0x01;
//outMsg.timeout = 100;
outMsg.len = 8;
Can1.write(outMsg);*/
/*while (Can1.available())
//if (Can1.available())
{
}*/
}
Hello Collin,
Thanks for open a window for asking your expert help. I am trying to build communication on CAN bus using your library.
My system: on one side, NVidia TX2 (with CAN controller CAN MCP2515T-I/ML and transceiver TJA1051TK/3); the other side Teensy 3.6 with "Dual CAN-Bus adapter for Teensy 3.5, 3.6" (https://www.tindie.com/products/Fusion/dual-can-bus-adapter-for-teensy-35-36/) which uses transceiver SN65HVD230D.
My first question is in the example "CANtest", as I understand it is same setup as me, why the enable pin is set to HIGH (which as Pawelsky point out the transceiver SN65HVD230D is in sleep mode)? Is slope mode you are using? Should I put pull down resistor for the enable pin?
I put an oscilloscope on the CANH and CANL, here is a picture of what I got:

If the hardware is of no problem. My problem is the communication on CAN bus always fails after some seconds. I had tried both teachtop's library and your library; I also tried your library and examples. Now I only succeed in the CANtest example with loopback.
On the TX2 side, CAN socket is used. I use a timer (40hz) to send command to read I2C altimeter (LW20/C): send command to CAN bus; delay 1ms; read CAN bus.
On the Teensy 3.6 side, I tried both scan mode and interrupt mode; interrupt mode will be a little bit better, but also failed after some reading the CAN bus (TX2 side). I will attach the code for the interrupt mode in the end. From the debug, it seems the mailboxes (TX) at the Teensy side are not released or the the data wrote to CAN bus from Teensy 3.6 is not read out from the TX2 side. I see there is a "txHandler", how to use it and what is this for? What is the event for this handler?
There are two extra headers for the LED and altimeter I work with. I put the function to read distance here. After this function is the main() of the Teensy 3.6.
static uint8_t getDefaultMedianDistance(byte addr, uint8_t **strCAN)
{
int i = 0;
uint8_t count = 0;
uint8_t str[8];
Wire.beginTransmission(addr);
Wire.write("?LD\r\n");
Wire.endTransmission();
delay(2); //must have a delay even only 1ms, or a lot of mess up
Wire.requestFrom(addr, (byte)12); // general call
while( Wire.available() ) {
char c = Wire.read();
//Serial.print(c);
if(i >= 5 && c != '\r' && c != '\n' && c != ' ' && c != '\0')
{
str[i - 5] = c;
count++;
}
i++;
}
//Serial.print('\n');
*strCAN = str;
return count;
}
The following is the main() of Teensy 3.6:
// -------------------------------------------------------------
// CANtest for Teensy 3.6 dual CAN bus
// by Collin Kidder, Based on CANTest by Pawelsky (based on CANtest by teachop)
//
// Both buses are left at default 250k speed and the second bus sends frames to the first
// to do this properly you should have the two buses linked together. This sketch
// also assumes that you need to set enable pins active. Comment out if not using
// enable pins or set them to your correct pins.
//
// This sketch tests both buses as well as interrupt driven Rx and Tx. There are only
// two Tx buffers by default so sending 5 at a time forces the interrupt driven system
// to buffer the final three and send them via interrupts. All the while all Rx frames
// are internally saved to a software buffer by the interrupt handler.
//
#include <Time.h>
#include "FlexCAN.h"
//#include <Wire.h>
#include <i2c_t3.h> // for multiple i2c ports
#include "BlinkM_funcs.h"
byte addr_BlinkM = 0x09; // address of BlinkM
#include "lw20_funcs.h"
byte addr_lw20 = 0x66;
#ifndef MK66FX1M0
#error "Teensy 3.6 with dual CAN bus is required to run this example"
#endif
class ExampleClass : public CANListener
{
public:
void printFrame(CAN_message_t &frame, int mailbox);
bool frameHandler(CAN_message_t &frame, int mailbox, uint8_t controller); //overrides the parent version so we can actually do something
void txHandler (int mailbox, uint8_t controller);
bool tx_completed_ = false;
};
void ExampleClass::printFrame(CAN_message_t &frame, int mailbox)
{
Serial.print("ID: ");
Serial.print(frame.id, HEX);
Serial.print(" Data: ");
for (int c = 0; c < frame.len; c++)
{
Serial.print(frame.buf[c], HEX);
Serial.write(' ');
}
Serial.write('\r');
Serial.write('\n');
Serial.print("mailbox: ");
Serial.print(mailbox);
Serial.write('\r');
Serial.write('\n');
}
void ExampleClass::txHandler (int mailbox, uint8_t controller)
{
tx_completed_ = true;
Serial.print("One write for mailbox and controller: ");
Serial.println(mailbox);
//Serial.print(", ");
//Serial.println(controller);
}
bool ExampleClass::frameHandler(CAN_message_t &frame, int mailbox, uint8_t controller)
{
CAN_message_t outMsg;
//printFrame(frame, mailbox);
switch (frame.id)
{
case 0x00: //BlinkM LED
{
switch (frame.buf[1])
{
case 'n':
{
BlinkM_setRGB(frame.buf[0], frame.buf[2], frame.buf[3], frame.buf[4]);
// Serial.print("outMsg.buf: ");
// for(int i = 1; i < strCANLen; i++)
// {
// Serial.print((char)outMsg.buf[i]);
// }
// Serial.print('\t');
// Serial.print("Length:");
// Serial.print(strCANLen);
// Serial.print('\n');
}
return true;
}
ExampleClass exampleClass;
// -------------------------------------------------------------
void setup(void)
{
//Can0.setNumTxBoxes(8);
Can1.setNumTxBoxes(8);
//Can0.begin(1000000);
Can1.begin(1000000);
//if using enable pins on a transceiver they need to be set on
pinMode(2, OUTPUT);
pinMode(35, OUTPUT);
//digitalWrite(35, HIGH);
digitalWrite(2, LOW);
digitalWrite(35, LOW);
//Can0.attachObj(&exampleClass);
Can1.attachObj(&exampleClass);
exampleClass.attachGeneralHandler();
//exampleClass.attachMBHandler(2);
//exampleClass.attachMBHandler(14);
Wire.begin();
Serial.begin(115200);
delay(2000);
Serial.println(F("Teensy 3.6 dual CAN, BlinkM and LW20 altimeter."));
// init BlinkM funcs
BlinkM_begin();
BlinkM_off(addr_BlinkM);
// print address of BlinkMs
Serial.print("Address of BlinkM: 0x");
Serial.print(BlinkM_getAddress(addr_BlinkM), HEX);
Serial.println();
}
uint8_t j = 0;
uint32_t clk_count = 0;
unsigned long old_millis = 0;
unsigned long current_millis;
// -------------------------------------------------------------
void loop(void)
{
clk_count++;
if (clk_count > 1000000)
{
//Serial.println(now());//Returns the number of seconds since Jan 1 1970
current_millis = millis(); //Number of milliseconds since the program started (unsigned long)
Serial.println(millis() - old_millis);
old_millis = current_millis;
clk_count = 0;
}
//CAN_message_t inMsg, outMsg;
/*outMsg.buf[0] = 0x00;
outMsg.buf[1] = BlinkM_getAddress(0x09);
outMsg.ext = 0;
outMsg.id = 0x00;
//outMsg.timeout = 100;
outMsg.len = 2;
Can1.write(outMsg);
for(uint8_t i = 0; i < 8; i++, j++)
outMsg.buf[i] = j;
outMsg.ext = 0;
outMsg.id = 0x01;
//outMsg.timeout = 100;
outMsg.len = 8;
Can1.write(outMsg);*/
/*while (Can1.available())
//if (Can1.available())
{
}*/
}