I use this code to run a CAN-BUS between a ESP32-S3 and an ESP32 ("classical") with CAN-BUS tranceivers TJA1051 which uses this library https://github.com/handmade0octopus/ESP32-TWAI-CAN/tree/master
// project remarks:
// On a ESP32-S3-board with DUAL-USB-C-connector the one that is shown as USB-enhanced SERIAL CH434
// is the one that used by Serial.
// MACRO-START * MACRO-START * MACRO-START * MACRO-START * MACRO-START * MACRO-START *
// a detailed explanation how these macros work is given in this tutorial
// https://forum.arduino.cc/t/comfortable-serial-debug-output-short-to-write-fixed-text-name-and-content-of-any-variable-code-example/888298
#define dbg(myFixedText, variableName) \
Serial.print( F(#myFixedText " " #variableName"=") ); \
Serial.println(variableName);
#define dbgi(myFixedText, variableName,timeInterval) \
{ \
static unsigned long intervalStartTime; \
if ( millis() - intervalStartTime >= timeInterval ){ \
intervalStartTime = millis(); \
Serial.print( F(#myFixedText " " #variableName"=") ); \
Serial.println(variableName); \
} \
}
#define dbgc(myFixedText, variableName) \
{ \
static long lastState; \
if ( lastState != variableName ){ \
Serial.print( F(#myFixedText " " #variableName" changed from ") ); \
Serial.print(lastState); \
Serial.print( F(" to ") ); \
Serial.println(variableName); \
lastState = variableName; \
} \
}
#define dbgcf(myFixedText, variableName) \
{ \
static float lastState; \
if ( lastState != variableName ){ \
Serial.print( F(#myFixedText " " #variableName" changed from ") ); \
Serial.print(lastState); \
Serial.print( F(" to ") ); \
Serial.println(variableName); \
lastState = variableName; \
} \
}
// MACRO-END * MACRO-END * MACRO-END * MACRO-END * MACRO-END * MACRO-END * MACRO-END *
/*
Serial.begin(115200);
Serial.println("Setup-Start");
PrintFileNameDateTime();
BlinkHeartBeatLED(OnBoard_LED,250);
static unsigned long MyTestTimer;
if ( TimePeriodIsOver(MyTestTimer,1000) ) {
*/
#include <ESP32-TWAI-CAN.hpp>
// Showcasing simple use of ESP32-TWAI-CAN library driver.
// Default for ESP32
const byte CAN_TX = 5; // which means connect GPIO-Pin with this number with the Tx-output on the CAN-transeiver
const byte CAN_RX = 4; // which means connect GPIO-Pin with this number with the Rx-input on the CAN-transeiver
CanFrame rxFrame;
int myCounter;
unsigned long myLinksBlinkerTimer;
unsigned long myRechtsBlinkerTimer;
const byte linksBlinkerPin = 1;
const byte rechtsBlinkerPin = 2;
const byte fahrlichtPin = 42;
const byte linksBlinkerBitFilter = 0b00000001;
const byte rechtsBlinkerBitFilter = 0b00000010;
const byte FahrlichtBitFilter = 0b00000100;
const byte linksBlinkerBitClear = ~linksBlinkerBitFilter;
const byte rechtsBlinkerBitClear = ~rechtsBlinkerBitFilter;
const byte FahrlichtBitClear = ~FahrlichtBitFilter;
const byte onBoard_LED = 38;
void setup() {
digitalWrite(fahrlichtPin, LOW);
digitalWrite(linksBlinkerPin, LOW);
digitalWrite(rechtsBlinkerPin, LOW);
pinMode(linksBlinkerPin, OUTPUT);
pinMode(rechtsBlinkerPin, OUTPUT);
pinMode(fahrlichtPin, OUTPUT);
Serial.begin(115200);
Serial.println("Setup-Start");
PrintFileNameDateTime();
Serial.println();
Serial.print("linksBlinkerBitFilter binär:");
Serial.println(linksBlinkerBitFilter, BIN);
Serial.print("linksBlinkerBitClear binär:");
Serial.println(linksBlinkerBitClear, BIN);
// You can set custom size for the queues - those are default
byte QueueSize = 5;
ESP32Can.setRxQueueSize(QueueSize);
ESP32Can.setTxQueueSize(QueueSize);
Serial.print("ESP32Can.setRxQueueSize(");
Serial.print(QueueSize);
Serial.println(")");
Serial.print("ESP32Can.setTxQueueSize(");
Serial.print(QueueSize);
Serial.println(")");
int CAN_Speed = 500;
Serial.print("ESP32Can.begin(ESP32Can.convertSpeed(");
Serial.print(CAN_Speed);
Serial.print("), CAN_TX=");
Serial.print(CAN_TX);
Serial.print(", CAN_RX=");
Serial.print(CAN_RX);
Serial.print(", 10, 10) )");
Serial.println();
if (ESP32Can.begin(ESP32Can.convertSpeed(CAN_Speed), CAN_TX, CAN_RX, 10, 10) ) {
Serial.println("CAN bus started successfully!");
}
else {
Serial.println("starting CAN bus failed!");
}
}
void loop() {
BlinkHeartBeatLED(onBoard_LED, 250);
static byte links;
static byte rechts;
static unsigned long MyTestTimer;
/*
if ( TimePeriodIsOver(myLinksBlinkerTimer, 400) ) {
digitalWrite(linksBlinkerPin, !digitalRead(linksBlinkerPin) );
}
if ( TimePeriodIsOver(myRechtsBlinkerTimer, 250) ) {
digitalWrite(rechtsBlinkerPin, !digitalRead(rechtsBlinkerPin) );
}
*/
// You can set custom timeout, default is 1000 milliseconds
if (ESP32Can.readFrame(rxFrame, 100)) {
// Comment out if too many frames
Serial.printf("Received frame: %03X \r\n", rxFrame.identifier);
if (rxFrame.identifier == 0x7FF) { // Standard OBD2 frame responce ID
//dbgc("LR",rxFrame.data[7]);
// links = rxFrame.data[7] & 0b00000001; // xxy
links = rxFrame.data[7] & linksBlinkerBitFilter;
dbgc("L",links);
if ( links == 1) {
digitalWrite(linksBlinkerPin, HIGH);
//Serial.println("links HIGH");
}
else {
digitalWrite(linksBlinkerPin, LOW);;
//Serial.println("links LOW");
}
rechts = rxFrame.data[7] & 0b00000010;
dbgc("R",rechts);
if (rechts == 0b00000010) {
digitalWrite(rechtsBlinkerPin, HIGH);
//Serial.println("rechts HIGH");
}
else {
digitalWrite(rechtsBlinkerPin, LOW);;
//Serial.println("rechts LOW");
}
}
}
if ( TimePeriodIsOver(MyTestTimer, 1000) ) {
myCounter++;
if (myCounter > 999) {
myCounter = 0;
}
sendCanFrame('A', myCounter);
}
}
void sendCanFrame(uint8_t obdId, int Number) {
char myDigitBuffer[10] = " ";
itoa(Number, myDigitBuffer, 10); // itoa convert integer to ASCII-coded char-array
CanFrame myDataFrame = { 0 };
//obdFrame.identifier = 0x7DF; // Default OBD2 address;
myDataFrame.identifier = 0x7FF;
myDataFrame.extd = 0;
myDataFrame.data_length_code = 8;
myDataFrame.data[0] = myDigitBuffer[0];
myDataFrame.data[1] = myDigitBuffer[1];
myDataFrame.data[2] = myDigitBuffer[2];
myDataFrame.data[3] = 'H';
myDataFrame.data[4] = 'e';
myDataFrame.data[5] = 'l';
myDataFrame.data[6] = 'l';
myDataFrame.data[7] = 'o';
// Accepts both pointers and references
printCanFrame(myDataFrame);
ESP32Can.writeFrame(myDataFrame); // timeout defaults to 1 ms
}
void printCanFrame(CanFrame p_CAN_Frame) {
Serial.println("sending CAN-frame");
Serial.print("identifier=");
Serial.print(p_CAN_Frame.identifier, HEX);
Serial.print(" frame length=");
Serial.print(p_CAN_Frame.data_length_code);
Serial.print(" data as ASCII-Code#");
for (byte IdxNr = 0; IdxNr < 8; IdxNr++) {
Serial.print(char(p_CAN_Frame.data[IdxNr]) );
}
Serial.print("#");
Serial.println();
}
// helper-functions
void PrintFileNameDateTime() {
Serial.println( F("Code running comes from file ") );
Serial.println( F(__FILE__) );
Serial.print( F(" compiled ") );
Serial.print( F(__DATE__) );
Serial.print( F(" ") );
Serial.println( F(__TIME__) );
}
// easy to use helper-function for non-blocking timing
boolean TimePeriodIsOver (unsigned long &startOfPeriod, unsigned long TimePeriod) {
unsigned long currentMillis = millis();
if ( currentMillis - startOfPeriod >= TimePeriod ) {
// more time than TimePeriod has elapsed since last time if-condition was true
startOfPeriod = currentMillis; // a new period starts right here so set new starttime
return true;
}
else return false; // actual TimePeriod is NOT yet over
}
void BlinkHeartBeatLED(int IO_Pin, int BlinkPeriod) {
static unsigned long MyBlinkTimer;
pinMode(IO_Pin, OUTPUT);
if ( TimePeriodIsOver(MyBlinkTimer, BlinkPeriod) ) {
digitalWrite(IO_Pin, !digitalRead(IO_Pin) );
}
}
ESP32-code
// MACRO-START * MACRO-START * MACRO-START * MACRO-START * MACRO-START * MACRO-START *
// a detailed explanation how these macros work is given in this tutorial
// https://forum.arduino.cc/t/comfortable-serial-debug-output-short-to-write-fixed-text-name-and-content-of-any-variable-code-example/888298
#define dbg(myFixedText, variableName) \
Serial.print( F(#myFixedText " " #variableName"=") ); \
Serial.println(variableName);
#define dbgi(myFixedText, variableName,timeInterval) \
{ \
static unsigned long intervalStartTime; \
if ( millis() - intervalStartTime >= timeInterval ){ \
intervalStartTime = millis(); \
Serial.print( F(#myFixedText " " #variableName"=") ); \
Serial.println(variableName); \
} \
}
#define dbgc(myFixedText, variableName) \
{ \
static long lastState; \
if ( lastState != variableName ){ \
Serial.print( F(#myFixedText " " #variableName" changed from ") ); \
Serial.print(lastState); \
Serial.print( F(" to ") ); \
Serial.println(variableName); \
lastState = variableName; \
} \
}
#define dbgcf(myFixedText, variableName) \
{ \
static float lastState; \
if ( lastState != variableName ){ \
Serial.print( F(#myFixedText " " #variableName" changed from ") ); \
Serial.print(lastState); \
Serial.print( F(" to ") ); \
Serial.println(variableName); \
lastState = variableName; \
} \
}
// MACRO-END * MACRO-END * MACRO-END * MACRO-END * MACRO-END * MACRO-END * MACRO-END *
/*
Serial.begin(115200);
Serial.println("Setup-Start");
PrintFileNameDateTime();
BlinkHeartBeatLED(OnBoard_LED,250);
static unsigned long MyTestTimer;
if ( TimePeriodIsOver(MyTestTimer,1000) ) {
*/
#include <ESP32-TWAI-CAN.hpp>
#include <SafeString.h>
// Showcasing simple use of ESP32-TWAI-CAN library driver.
// Default for ESP32
const byte CAN_TX = 5; // which means connect GPIO-Pin with this number with the Tx-output on the CAN-transeiver
const byte CAN_RX = 4; // which means connect GPIO-Pin with this number with the Rx-input on the CAN-transeiver
CanFrame rxFrame;
int myCounter;
cSF(rxData_SS, 16);
unsigned long myLinksBlinkerTimer;
unsigned long myRechtsBlinkerTimer;
boolean linksBlinkerAn = false;
boolean rechtsBlinkerAn = false;
const byte linksBlinkerBitFilter = 0b00000001;
const byte rechtsBlinkerBitFilter = 0b00000010;
const byte FahrlichtBitFilter = 0b00000100;
const byte linksBlinkerBitClear = ~linksBlinkerBitFilter;
const byte rechtsBlinkerBitClear = ~rechtsBlinkerBitFilter;
const byte FahrlichtBitClear = ~FahrlichtBitFilter;
const byte linksBlinkerPin = 26;
const byte rechtssBlinkerPin = 27;
const byte switchedOn = LOW;
const byte switchedOff = HIGH;
void setup() {
Serial.begin(115200);
Serial.println("Setup-Start");
PrintFileNameDateTime();
Serial.println();
pinMode(linksBlinkerPin, INPUT_PULLUP);
pinMode(rechtssBlinkerPin, INPUT_PULLUP);
// You can set custom size for the queues - those are default
byte QueueSize = 5;
ESP32Can.setRxQueueSize(QueueSize);
ESP32Can.setTxQueueSize(QueueSize);
Serial.print("ESP32Can.setRxQueueSize(");
Serial.print(QueueSize);
Serial.println(")");
Serial.print("ESP32Can.setTxQueueSize(");
Serial.print(QueueSize);
Serial.println(")");
int CAN_Speed = 500;
Serial.print("ESP32Can.begin(ESP32Can.convertSpeed(");
Serial.print(CAN_Speed);
Serial.print("), CAN_TX=");
Serial.print(CAN_TX);
Serial.print(", CAN_RX=");
Serial.print(CAN_RX);
Serial.print(", 10, 10) )");
Serial.println();
if (ESP32Can.begin(ESP32Can.convertSpeed(CAN_Speed), CAN_TX, CAN_RX, 10, 10) ) {
Serial.println("CAN bus started successfully!");
}
else {
Serial.println("starting CAN bus failed!");
}
dbg("IO", digitalRead(linksBlinkerPin) );
dbg("IO", digitalRead(rechtssBlinkerPin) );
}
void loop() {
static unsigned long MyTestTimer;
dbgc("IO", digitalRead(linksBlinkerPin) );
dbgc("IO", digitalRead(rechtssBlinkerPin) );
if ( TimePeriodIsOver(myLinksBlinkerTimer, 1000) ) {
linksBlinkerAn = !linksBlinkerAn;
}
if ( TimePeriodIsOver(myRechtsBlinkerTimer, 250) ) {
//rechtsBlinkerAn = !rechtsBlinkerAn;
}
if ( TimePeriodIsOver(MyTestTimer, 50) ) {
//myCounter++;
if (myCounter > 999) {
myCounter = 0;
}
//sendCanFrame('A', myCounter);
}
// You can set custom timeout, default is 1000 milliseconds
if (ESP32Can.readFrame(rxFrame, 100)) {
// Comment out if too many frames
//Serial.printf("Received frame: %03X \r\n", rxFrame.identifier);
if (rxFrame.identifier == 0x7FF) { // Standard OBD2 frame responce ID
rxData_SS = "";
for (byte i = 0; i < 8; i++) {
rxData_SS += char( rxFrame.data[i] );
}
Serial.println(rxData_SS);
}
}
}
void sendCanFrame(uint8_t obdId, int Number) {
char myDigitBuffer[10] = " ";
itoa(Number, myDigitBuffer, 10); // itoa convert integer to ASCII-coded char-array
CanFrame myDataFrame = { 0 };
//obdFrame.identifier = 0x7DF; // Default OBD2 address;
myDataFrame.identifier = 0x7FF;
myDataFrame.extd = 0;
myDataFrame.data_length_code = 8;
myDataFrame.data[0] = myDigitBuffer[0];
myDataFrame.data[1] = myDigitBuffer[1];
myDataFrame.data[2] = myDigitBuffer[2];
myDataFrame.data[3] = 'H';
myDataFrame.data[4] = 'e';
myDataFrame.data[5] = 'l';
myDataFrame.data[6] = 'l';
if (linksBlinkerAn) {
//myDataFrame.data[7] = myDataFrame.data[7] | 0b00000001;
myDataFrame.data[7] = myDataFrame.data[7] | linksBlinkerBitFilter;
}
else {
myDataFrame.data[7] = myDataFrame.data[7] & linksBlinkerBitClear; // xxy
}
if (rechtsBlinkerAn) {
myDataFrame.data[7] = myDataFrame.data[7] | rechtsBlinkerBitFilter;
}
else {
myDataFrame.data[7] = myDataFrame.data[7] & rechtsBlinkerBitClear;
}
dbgc("LR", myDataFrame.data[7]);
//myDataFrame.data[7] = 'o';
// Accepts both pointers and references
printCanFrame(myDataFrame);
ESP32Can.writeFrame(myDataFrame); // timeout defaults to 1 ms
}
void printCanFrame(CanFrame p_CAN_Frame) {
Serial.println("sending CAN-frame");
Serial.print("identifier=");
Serial.print(p_CAN_Frame.identifier, HEX);
Serial.print(" frame length=");
Serial.print(p_CAN_Frame.data_length_code);
Serial.print(" data as ASCII-Code#");
for (byte IdxNr = 0; IdxNr < 8; IdxNr++) {
Serial.print(char(p_CAN_Frame.data[IdxNr]) );
}
Serial.print("#");
Serial.println();
}
// helper-functions
void PrintFileNameDateTime() {
Serial.println( F("Code running comes from file ") );
Serial.println( F(__FILE__) );
Serial.print( F(" compiled ") );
Serial.print( F(__DATE__) );
Serial.print( F(" ") );
Serial.println( F(__TIME__) );
}
// easy to use helper-function for non-blocking timing
boolean TimePeriodIsOver (unsigned long &startOfPeriod, unsigned long TimePeriod) {
unsigned long currentMillis = millis();
if ( currentMillis - startOfPeriod >= TimePeriod ) {
// more time than TimePeriod has elapsed since last time if-condition was true
startOfPeriod = currentMillis; // a new period starts right here so set new starttime
return true;
}
else return false; // actual TimePeriod is NOT yet over
}
void BlinkHeartBeatLED(int IO_Pin, int BlinkPeriod) {
static unsigned long MyBlinkTimer;
pinMode(IO_Pin, OUTPUT);
if ( TimePeriodIsOver(MyBlinkTimer, BlinkPeriod) ) {
digitalWrite(IO_Pin, !digitalRead(IO_Pin) );
}
}