further tests logging Canbus data to SD card using an ESP32 (used a 2.8" CYD with onboard ESP32 and SD reader)
to generate test data a ESP32 continuously transmited Canbus packets at 500kbits/sec - an incrementing count was transmitted on each packet so receiver could check for lost packets
/* ESP32 TWAI transmit continuously in loop() - hit <ENTER> to start/stop
This transmits a message every second.
Connect a CAN bus transceiver to the RX/TX pins.
For example: SN65HVD230
The API gives other possible speeds and alerts:
https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/peripherals/twai.html
created 27-06-2023 by Stephan Martin (designer2k2)
*/
#include "driver/twai.h"
#define PRINT 0
// Pins used to connect to CAN bus transceiver:
#define RX_PIN 16 //21
#define TX_PIN 17 //22
// Interval:
#define TRANSMIT_RATE_MS 1000
#define POLLING_RATE_MS 1000
static bool driver_installed = false;
unsigned long previousMillis = 0; // will store last time a message was send
void setup() {
// Start Serial:
Serial.begin(115200);
delay(2000);
Serial.println("ESP32 TWAI transmit continuously in loop()");
Serial.printf("sizeof(twai_message_t) %d\n", sizeof(twai_message_t));
// Initialize configuration structures using macro initializers
twai_general_config_t g_config = TWAI_GENERAL_CONFIG_DEFAULT((gpio_num_t)TX_PIN, (gpio_num_t)RX_PIN, TWAI_MODE_NORMAL);
twai_timing_config_t t_config = TWAI_TIMING_CONFIG_500KBITS(); //Look in the api-reference for other speed sets.
twai_filter_config_t f_config = TWAI_FILTER_CONFIG_ACCEPT_ALL();
// Install TWAI driver
if (twai_driver_install(&g_config, &t_config, &f_config) == ESP_OK) {
Serial.println("Driver installed");
} else {
Serial.println("Failed to install driver");
return;
}
// Start TWAI driver
if (twai_start() == ESP_OK) {
Serial.println("Driver started");
} else {
Serial.println("Failed to start driver");
return;
}
// Reconfigure alerts to detect TX alerts and Bus-Off errors
uint32_t alerts_to_enable = TWAI_ALERT_RX_DATA | TWAI_ALERT_TX_IDLE | TWAI_ALERT_TX_SUCCESS | TWAI_ALERT_TX_FAILED | TWAI_ALERT_ERR_PASS
| TWAI_ALERT_BUS_ERROR | TWAI_ALERT_RX_QUEUE_FULL;
if (twai_reconfigure_alerts(alerts_to_enable, NULL) == ESP_OK) {
Serial.println("CAN Alerts reconfigured");
} else {
Serial.println("Failed to reconfigure alerts");
return;
}
// TWAI driver is now successfully installed and started
driver_installed = true;
Serial.println("hit <ENTER> tostart/stop transmission of packets");
}
// transmit a message
static void send_message() {
static byte counter = 0;
// Send message
// Configure message to transmit
twai_message_t message;
message.identifier = 0x0F6;
message.data_length_code = 8;
for (int i = 0; i < 8; i++) {
message.data[i] = 0;
}
message.data[0] = counter++; // increment test counter
// Queue message for transmission
if (twai_transmit(&message, pdMS_TO_TICKS(1000)) == ESP_OK) {
if (PRINT) {
Serial.print("Message queued for transmission ");
if (message.extd) {
Serial.print(" Extended Format");
} else {
Serial.print(" Standard Format");
}
Serial.printf(" ID: %lx Byte: ", message.identifier);
if (!(message.rtr)) {
for (int i = 0; i < message.data_length_code; i++) {
Serial.printf(" 0x%02x", message.data[i]);
}
Serial.println("");
}
}
} else {
printf("Failed to queue message for transmission\n");
}
}
void loop() {
static unsigned long int messageNO = 0, count = 0;
static bool transmit = false;
static unsigned long timer1 = millis();
if (millis() - timer1 > 1000) {
timer1 += 1000;
Serial.print("NO ");
Serial.print(messageNO);
Serial.print(" count ");
Serial.println(count);
count = 0;
}
if (!driver_installed) {
// Driver not installed
delay(1000);
return;
}
// Check if alert happened
uint32_t alerts_triggered;
twai_read_alerts(&alerts_triggered, pdMS_TO_TICKS(POLLING_RATE_MS));
twai_status_info_t twaistatus;
twai_get_status_info(&twaistatus);
if (PRINT) {
// Handle alerts
if (alerts_triggered & TWAI_ALERT_ERR_PASS) {
Serial.println("Alert: TWAI controller has become error passive.");
}
if (alerts_triggered & TWAI_ALERT_BUS_ERROR) {
Serial.println("Alert: A (Bit, Stuff, CRC, Form, ACK) error has occurred on the bus.");
Serial.printf("Bus error count: %lu\n", twaistatus.bus_error_count);
}
if (alerts_triggered & TWAI_ALERT_TX_FAILED) {
Serial.println("Alert: The Transmission failed.");
Serial.printf("TX buffered: %lu\t", twaistatus.msgs_to_tx);
Serial.printf("TX error: %lu\t", twaistatus.tx_error_counter);
Serial.printf("TX failed: %lu\n", twaistatus.tx_failed_count);
}
if (alerts_triggered & TWAI_ALERT_TX_SUCCESS) {
Serial.print("Alert: The Transmission was successful.");
Serial.printf(": TX buffered: %lu\t\n", twaistatus.msgs_to_tx);
}
}
// transmit data when space entered on keyboard
if (Serial.available()) {
delay(2);
transmit = !transmit;
while (Serial.available()) Serial.read();
if(transmit)Serial.println("Transmission started");
else Serial.println("Transmission stopped");
}
// Send message
// unsigned long currentMillis = millis();
// if (currentMillis - previousMillis >= TRANSMIT_RATE_MS) {
// previousMillis = currentMillis;
if (transmit) {
messageNO++;
count++;
send_message();
}
// receiver
if (alerts_triggered & TWAI_ALERT_RX_QUEUE_FULL) {
Serial.println("Alert: The RX queue is full causing a received frame to be lost.");
Serial.printf("RX buffered: %lu\t", twaistatus.msgs_to_rx);
Serial.printf("RX missed: %lu\t", twaistatus.rx_missed_count);
Serial.printf("RX overrun %lu\n", twaistatus.rx_overrun_count);
}
}
serial monitor shows transmitting 3517 packets/second
ESP32 TWAI transmit continuously in loop()
sizeof(twai_message_t) 20
Driver installed
Driver started
CAN Alerts reconfigured
hit <ENTER> tostart/stop transmission of packets
NO 0 count 0
NO 0 count 0
NO 0 count 0
Transmission started
NO 3336 count 3334
NO 6853 count 3517
NO 10370 count 3517
NO 13887 count 3517
NO 17405 count 3518
NO 20922 count 3517
NO 24439 count 3517
NO 27956 count 3517
NO 31473 count 3517
NO 34990 count 3517
NO 38507 count 3517
NO 42025 count 3518
NO 45542 count 3517
NO 49059 count 3517
NO 52576 count 3517
NO 56093 count 3517
….
NO 1076073 count 3517
NO 1079590 count 3517
NO 1083107 count 3517
NO 1086624 count 3517
NO 1087752 count 1128
Transmission stopped
NO 1087752 count 0
NO 1087752 count 0
NO 1087752 count 0
the receiver receives binary packets and writes them to a file on an SD card - on a keypress the file is closed and the data read to check for errors
the TWAI library allocates a 500 packet FIFO (each packet is 20bytes)
receiver main.ino
/* ESP32 TWAI receive packets and save to SD card - check saved data
Connect a CAN bus transceiver to the RX/TX pins.
For example: SN65HVD230
The API gives other possible speeds and alerts:
https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/peripherals/twai.html
created 27-06-2023 by Stephan Martin (designer2k2)
*/
#include "driver/twai.h"
#include "SD_code.h"
#define PRINT 0
// Pins used to connect to CAN bus transceiver:
#define CAN_TX 22
#define CAN_RX 27
// Interval:
#define TRANSMIT_RATE_MS 1000
#define POLLING_RATE_MS 1000
static bool driver_installed = false;
unsigned long previousMillis = 0; // will store last time a message was send
void setup() {
// Start Serial:
Serial.begin(115200);
delay(2000);
Serial.println("\n\nESP32 TWAI receive packets and save to SD card - check saved data");
// Initialize configuration structures using macro initializers - allocate 25 receive buffers
twai_general_config_t g_config = { 0, TWAI_MODE_NORMAL, (gpio_num_t)CAN_TX, (gpio_num_t)CAN_RX, (gpio_num_t)-1, (gpio_num_t)-1, 5, 500 };
twai_timing_config_t t_config = TWAI_TIMING_CONFIG_500KBITS(); //Look in the api-reference for other speed sets.
twai_filter_config_t f_config = TWAI_FILTER_CONFIG_ACCEPT_ALL();
// Install TWAI driver
if (twai_driver_install(&g_config, &t_config, &f_config) == ESP_OK) {
Serial.println("Driver installed");
} else {
Serial.println("Failed to install driver");
return;
}
// Start TWAI driver
if (twai_start() == ESP_OK) {
Serial.println("Driver started");
} else {
Serial.println("Failed to start driver");
return;
}
// Reconfigure alerts to detect TX alerts and Bus-Off errors
uint32_t alerts_to_enable = TWAI_ALERT_RX_DATA | TWAI_ALERT_TX_IDLE | TWAI_ALERT_TX_SUCCESS | TWAI_ALERT_TX_FAILED | TWAI_ALERT_ERR_PASS
| TWAI_ALERT_BUS_ERROR | TWAI_ALERT_RX_QUEUE_FULL;
if (twai_reconfigure_alerts(alerts_to_enable, NULL) == ESP_OK) {
Serial.println("CAN Alerts reconfigured");
} else {
Serial.println("Failed to reconfigure alerts");
return;
}
// TWAI driver is now successfully installed and started
driver_installed = true;
SDsetup(); // Setup the SD for file IO
Serial.println("SD file opened saving received packets - hit <ENTER> read SD file and check");
}
static int messageNO = 0, errors = 0, overrun = 0, missedFrames=0;
// received message - display and check for lost packets
static void handle_rx_message(twai_message_t &message) {
static byte test = 0; // used as a test check for lost packets
messageNO++;
if (PRINT) { // print the received data?
if (message.extd)
Serial.printf("Received %d Extended Format", messageNO);
else
Serial.printf("Received %d Standard Format", messageNO);
Serial.printf(" ID: %lx Byte: ", message.identifier);
if (!(message.rtr)) {
for (int i = 0; i < message.data_length_code; i++) {
Serial.printf(" 0x%02x", message.data[i]);
}
Serial.printf(" errors %d\n", errors);
}
}
if (message.data[0] != test) { // check for lost messages
errors++;
Serial.printf("\nERROR! expaected %d received %d errors %d\n", test, message.data[0], errors);
test = message.data[0];
}
test++; // increment test ready for next packet
}
// loop waiting for incomming packets
void loop() {
while (1) {
// if <ENTER> read SD file and check for errors
if (Serial.available()) readCheck();
if (!driver_installed) {
// Driver not installed
delay(1000);
return;
}
// every 10 seconds print packet count
static unsigned long timer1 = millis();
if (millis() - timer1 > 10000) {
timer1 += 10000;
Serial.printf("NO %d err %d ovr %d missed %d\n", messageNO, errors, overrun, missedFrames);
}
// Check if alert happened
uint32_t alerts_triggered;
twai_read_alerts(&alerts_triggered, pdMS_TO_TICKS(POLLING_RATE_MS));
twai_status_info_t twaistatus;
twai_get_status_info(&twaistatus);
if (PRINT) { // print information?
// Handle alerts
if (alerts_triggered & TWAI_ALERT_ERR_PASS) {
Serial.println("Alert: TWAI controller has become error passive.");
}
if (alerts_triggered & TWAI_ALERT_BUS_ERROR) {
Serial.println("Alert: A (Bit, Stuff, CRC, Form, ACK) error has occurred on the bus.");
Serial.printf("Bus error count: %lu\n", twaistatus.bus_error_count);
}
if (alerts_triggered & TWAI_ALERT_TX_FAILED) {
Serial.println("Alert: The Transmission failed.");
Serial.printf("TX buffered: %lu\t", twaistatus.msgs_to_tx);
Serial.printf("TX error: %lu\t", twaistatus.tx_error_counter);
Serial.printf("TX failed: %lu\n", twaistatus.tx_failed_count);
}
if (alerts_triggered & TWAI_ALERT_TX_SUCCESS) {
Serial.print("Alert: The Transmission was successful.");
Serial.printf(": TX buffered: %lu\t\n", twaistatus.msgs_to_tx);
}
}
// receiver queue full?
if (alerts_triggered & TWAI_ALERT_RX_QUEUE_FULL) {
Serial.println("Alert: The RX queue is full causing a received frame to be lost.");
Serial.printf("RX buffered: %lu\t", twaistatus.msgs_to_rx);
Serial.printf("RX missed: %lu\t", missedFrames=twaistatus.rx_missed_count);
Serial.printf("RX overrun %lu\n", twaistatus.rx_overrun_count);
overrun += twaistatus.rx_overrun_count;
}
// Check if message is received
if (alerts_triggered & TWAI_ALERT_RX_DATA) {
// One or more messages received. Handle all.
twai_message_t message;
while (twai_receive(&message, 0) == ESP_OK) {
handle_rx_message(message);
SDwrite((byte *)&message, sizeof(message)); // save to SD card
}
}
}
}
// read SD card check for lost packets ?
void readCheck() {
Serial.println("\n\nSD card read check\nhit <ENTER> to swich ON/OFF display of data");
delay(2);
while (Serial.available()) Serial.read();
int errors = 0, frameCount = 0;
twai_message_t message;
bool printCheck = false; // no print of data
static byte test = 0; // test to check data read
SDfileClose();
SDopenReadFile(); // open file for reading
while (SDread((byte *)&message, sizeof(message)) == sizeof(message)) {
static unsigned long timer1 = millis();
if (millis() - timer1 > 1000) { // print count every 1 second
timer1 += 1000;
Serial.printf(" frameCount %d errors %d\n", frameCount, errors);
}
frameCount++;
if (Serial.available()) { // if <ENTER> switch print ON/OFF
delay(2);
printCheck = !printCheck;
while (Serial.available()) Serial.read();
}
if (printCheck) { // print packet contents?
if (message.extd)
Serial.printf("Received %d Extended Format", frameCount);
else
Serial.printf("Received %d Standard Format", frameCount);
Serial.printf(" ID: %lx Byte: ", message.identifier);
if (!(message.rtr)) {
for (int i = 0; i < message.data_length_code; i++) {
Serial.printf(" 0x%02x", message.data[i]);
}
Serial.printf(" errors %d\n", errors);
}
}
if (message.data[0] != test) { // check for lost packets
errors++;
Serial.printf("\nERROR! expected %d received %d errors %d\n ", test, message.data[0], errors);
test = message.data[0];
}
test++;
}
// filechecked
SDfileClose();
Serial.printf(" frameCount %d errors %d missedFrames %d\n", messageNO, errors, missedFrames);
while (1)
;
}
file SD_code.h (in same directory as above main.ino file)
void SDsetup();
void SDwrite(byte *data, int size) ;
void SDfileClose();
void SDopenReadFile() ;
int SDread(byte* buffer, int size) ;
file SD_code.cpp (in same directory as above main.ino file)
#include "FS.h"
#include "SD.h"
void listDir(fs::FS &fs, const char *dirname, uint8_t levels) {
Serial.printf("Listing directory: %s\n", dirname);
File root = fs.open(dirname);
if (!root) {
Serial.println("Failed to open directory");
return;
}
if (!root.isDirectory()) {
Serial.println("Not a directory");
return;
}
File file = root.openNextFile();
while (file) {
if (file.isDirectory()) {
Serial.print(" DIR : ");
Serial.println(file.name());
if (levels) {
listDir(fs, file.name(), levels - 1);
}
} else {
Serial.print(" FILE: ");
Serial.print(file.name());
Serial.print(" SIZE: ");
Serial.println(file.size());
}
file = root.openNextFile();
}
}
void createDir(fs::FS &fs, const char *path) {
Serial.printf("Creating Dir: %s\n", path);
if (fs.mkdir(path)) {
Serial.println("Dir created");
} else {
Serial.println("mkdir failed");
}
}
void removeDir(fs::FS &fs, const char *path) {
Serial.printf("Removing Dir: %s\n", path);
if (fs.rmdir(path)) {
Serial.println("Dir removed");
} else {
Serial.println("rmdir failed");
}
}
void renameFile(fs::FS &fs, const char *path1, const char *path2) {
Serial.printf("Renaming file %s to %s\n", path1, path2);
if (fs.rename(path1, path2)) {
Serial.println("File renamed");
} else {
Serial.println("Rename failed");
}
}
void deleteFile(fs::FS &fs, const char *path) {
Serial.printf("Deleting file: %s\n", path);
if (fs.remove(path)) {
Serial.println("File deleted");
} else {
Serial.println("Delete failed");
}
}
// setup SD card for writing to a file
char path[] = "/data.txt";
File file;
void SDsetup() {
Serial.begin(115200);
delay(2000);
Serial.println("\nSDcard file IO Write/read binary data");
if (!SD.begin(5)) {
Serial.println("Card Mount Failed");
return;
}
uint8_t cardType = SD.cardType();
if (cardType == CARD_NONE) {
Serial.println("No SD card attached");
return;
}
Serial.print("SD Card Type: ");
if (cardType == CARD_MMC) {
Serial.println("MMC");
} else if (cardType == CARD_SD) {
Serial.println("SDSC");
} else if (cardType == CARD_SDHC) {
Serial.println("SDHC");
} else {
Serial.println("UNKNOWN");
}
uint64_t cardSize = SD.cardSize() / (1024 * 1024);
Serial.printf("SD Card Size: %lluMB\n", cardSize);
listDir(SD, "/", 0);
// write binary data to file data.txt
Serial.printf("Writing file: %s\n", path);
file = SD.open(path, FILE_WRITE); // open file for writing
if (!file) {
Serial.println("Failed to open file for writing");
while (1)
;
}
}
// write data to file
void SDwrite(byte *data, int size) {
// write e binary data
int error = 0;
if ((error = file.write(data, size)) != size)
Serial.printf("error writing file %d\n", error);
}
// close the open file
void SDfileClose() {
file.close(); // close writiong file
Serial.printf("Used space: %lluMB\n", SD.usedBytes() / (1024 * 1024));
listDir(SD, "/", 0);
}
// open file for read check
File filein;
void SDopenReadFile() {
// read file and check data
Serial.printf("\n\nReading file: %s\n", path);
filein = SD.open(path); // open file for reading
if (!filein) {
Serial.println("Failed to open file for reading");
while (1)
;
}
}
// read bytes from fdile into buffer
int SDread(byte *buffer, int size) {
return filein.read(buffer, size); // read bytes from file
}
the following serial monitor output of the receiver shows that 1087752 data packets are stored on the SD card and read back OK showing no errors
ESP32 TWAI receive packets and save to SD card - check saved data
Driver installed
Driver started
CAN Alerts reconfigured
SDcard file IO Write/read binary data
SD Card Type: SDHC
SD Card Size: 29862MB
Listing directory: /
DIR : System Volume Information
FILE: data.txt SIZE: 0
Writing file: /data.txt
SD file opened saving received packets - hit <ENTER> read SD file and check
NO 9948 err 0 ovr 0 missed 0
NO 45120 err 0 ovr 0 missed 0
NO 80317 err 0 ovr 0 missed 0
NO 115464 err 0 ovr 0 missed 0
NO 150635 err 0 ovr 0 missed 0
NO 185807 err 0 ovr 0 missed 0
NO 220979 err 0 ovr 0 missed 0
NO 256151 err 0 ovr 0 missed 0
NO 291323 err 0 ovr 0 missed 0
NO 326494 err 0 ovr 0 missed 0
NO 361666 err 0 ovr 0 missed 0
NO 396838 err 0 ovr 0 missed 0
….
NO 924414 err 0 ovr 0 missed 0
NO 959586 err 0 ovr 0 missed 0
NO 994758 err 0 ovr 0 missed 0
NO 1029930 err 0 ovr 0 missed 0
NO 1065101 err 0 ovr 0 missed 0
SD card read check
hit <ENTER> to swich ON/OFF display of data
Used space: 222MB
Listing directory: /
DIR : System Volume Information
FILE: data.txt SIZE: 21755040
Reading file: /data.txt
frameCount 14451 errors 0
frameCount 28876 errors 0
frameCount 43212 errors 0
frameCount 57548 errors 0
frameCount 71884 errors 0
frameCount 86358 errors 0
…
frameCount 1021653 errors 0
frameCount 1036083 errors 0
frameCount 1050419 errors 0
frameCount 1064930 errors 0
frameCount 1079296 errors 0
Used space: 222MB
Listing directory: /
DIR : System Volume Information
FILE: data.txt SIZE: 21755040
frameCount 1087752 errors 0 missedFrames 0