1
- // ESP32_RET_SD
1
+ // ESP32_RET_SD (internally known as ESP32_RET_SD_v1)
2
2
// ESP32 Reverse Engineering Tool with SD CAN bus datalogger for SavvyCAN
3
3
// Based on Collin Kidder (https://github.com/collin80) fantastic A0RET (https://github.com/collin80/A0RET)
4
- // Updated by frank @ motorvate Jan 1/2024 to add standalone recording of all CAN data to the SD card in SavvyCAN format
4
+ // Updated by frank @ motorvate Feb 1/2025
5
+
6
+ // MotorvateDIY CAN LOGGER v1.0 PCB average current draw: Wifi: ~80mA @ 12v or 1W, with SD card: ~90ma @ 12v, or 1.1W
5
7
6
8
// Main Goal: Easy to use, Plug and Play CAN bus datalogging
7
9
// If an SD card *IS* detected at boot up, all CAN bus data is recorded to the SD card, in SavvyCAN CSV format for offline processing
8
- // If an SD card *IS NOT* detected, a WIFI access point is created for SavvyCAN to connect to and receive or send CAN frames
10
+ // If an SD card *IS NOT* detected at boot up , a WIFI access point is created for SavvyCAN to connect to and receive or send CAN frames
9
11
10
- // compatable with Arduino core 2.0.x and 3.0 .x
12
+ // compatable with Arduino core 2.0.x and 3.1 .x
11
13
// Now compiles using default 1.2MB APP partition
12
14
13
15
// With a focus on "easy of use", an offline mode (CAN to SD) has been added to the SavvyCAN mode (CAN to Wifi)
17
19
// Removed: Bluetooth
18
20
// Removed: Digital and Analog I/O
19
21
20
- // Added: saves all CAN data to SD card in SavvyCAN CSV format just by plugging into the OBD port for offline processing/reverse engineering
22
+ // Added: Saving all CAN data to SD card in SavvyCAN CSV format just by plugging into the OBD port for offline processing/reverse engineering
21
23
// Added: when saving to SD card, the ESP32 builtin LED is toggled every 250 (CAN_RX_LED_TOGGLE) CAN frames (set in config.h)
22
24
// Added: getting timestamp as soon as possible, see gvret_comm.cpp line 558 and esp32_can_builtin.cpp line 398
23
25
24
26
// Instructions for use:
25
27
// in config.h:
26
- // set SD card pins on lines 43-46. Uses VSPI default pins of: SCK 18, MISO 19, MOSI 23, CS 5
27
- // set CAN Tx/Rx pins on lines 50-51. Defaults to CAN_TX 17, CAN_RX 16
28
- // set CAN0 speed on line 54. Default speed is 500,000 bits/sec
29
- // set SSID and password on lines 57-58. Default SSID: ESP32_RET_SD, password motorvate
28
+ // set SD card pins on lines 43-46
29
+ // set CAN Tx/Rx pins on lines 50-51
30
+ // set SSID and password on lines 54-55
30
31
// Compile and upload to ESP32!
31
- // full video instructions at youtube .com/motorvateDIY
32
+ // full video instructions at YouTube .com/motorvateDIY
32
33
33
34
/*
34
35
A0RET.ino
@@ -100,9 +101,9 @@ Preferences nvPrefs;
100
101
101
102
WiFiManager wifiManager;
102
103
103
- GVRET_Comm_Handler serialGVRET; // gvret protocol over the serial to USB connection
104
- GVRET_Comm_Handler wifiGVRET; // GVRET over the wifi telnet port
105
- CANManager canManager; // keeps track of bus load and abstracts away some details of how things are done
104
+ GVRET_Comm_Handler serialGVRET; // gvret protocol over the serial to USB connection
105
+ GVRET_Comm_Handler wifiGVRET; // GVRET over the wifi telnet port
106
+ CANManager canManager; // keeps track of bus load and abstracts away some details of how things are done
106
107
107
108
SerialConsole console;
108
109
@@ -115,14 +116,15 @@ QueueHandle_t SD_Queue;
115
116
// initializes all the system EEPROM values. Chances are this should be broken out a bit but
116
117
// there is only one checksum check for all of them so it's simple to do it all here.
117
118
// NOTE: IF PREFS ERROR, USE ? AND SET ONE ITEM. This writes the SSID< WPA2KEY and ELM327-BT to flash
118
- void loadSettings () {
119
- settings.CAN0Speed = CAN_SPEED_500K;
119
+ void loadSettings ()
120
+ {
121
+ settings.CAN0Speed = 500000 ;
120
122
settings.CAN0_Enabled = true ;
121
123
settings.CAN0ListenOnly = false ;
122
124
settings.useBinarySerialComm = false ;
123
- settings.logLevel = 1 ; // info
124
- settings.wifiMode = 2 ; // Wifi defaults to creating an AP
125
- settings.systemType = 0 ; // ESP32 with single CAN bus
125
+ settings.logLevel = 1 ; // info
126
+ settings.wifiMode = 2 ; // Wifi defaults to creating an AP
127
+ settings.systemType = 0 ; // ESP32 with single CAN bus
126
128
settings.CAN1Speed = 500000 ;
127
129
// below needs strcpy to copy the short string into the larger string 32/64
128
130
strcpy (settings.SSID , SSID_NAME);
@@ -139,49 +141,60 @@ void loadSettings() {
139
141
SysSettings.sdToggle = false ;
140
142
SysSettings.txToggle = false ;
141
143
SysSettings.rxToggle = true ;
142
- SysSettings.numBuses = 1 ; // Currently we support CAN0
144
+ SysSettings.numBuses = 1 ; // Currently we support CAN0
143
145
SysSettings.isWifiActive = false ;
144
146
SysSettings.isWifiConnected = false ;
145
147
}
146
148
147
149
uint32_t chipId = 0 ;
148
150
149
- String getDefaultMacAddress () {
151
+ String getDefaultMacAddress ()
152
+ {
150
153
151
154
String mac = " " ;
152
155
153
- unsigned char mac_base[6 ] = { 0 };
156
+ unsigned char mac_base[6 ] = {0 };
154
157
155
- if (esp_efuse_mac_get_default (mac_base) == ESP_OK) {
156
- char buffer[18 ]; // 6*2 characters for hex + 5 characters for colons + 1 character for null terminator
158
+ if (esp_efuse_mac_get_default (mac_base) == ESP_OK)
159
+ {
160
+ char buffer[18 ]; // 6*2 characters for hex + 5 characters for colons + 1 character for null terminator
157
161
sprintf (buffer, " %02X:%02X:%02X:%02X:%02X:%02X" , mac_base[0 ], mac_base[1 ], mac_base[2 ], mac_base[3 ], mac_base[4 ], mac_base[5 ]);
158
162
mac = buffer;
159
163
}
160
164
161
165
return mac;
162
166
}
163
167
164
- String getInterfaceMacAddress (esp_mac_type_t interface) {
168
+ String getInterfaceMacAddress (esp_mac_type_t interface)
169
+ {
165
170
166
171
String mac = " " ;
167
172
168
- unsigned char mac_base[6 ] = { 0 };
173
+ unsigned char mac_base[6 ] = {0 };
169
174
170
- if (esp_read_mac (mac_base, interface) == ESP_OK) {
171
- char buffer[18 ]; // 6*2 characters for hex + 5 characters for colons + 1 character for null terminator
175
+ if (esp_read_mac (mac_base, interface) == ESP_OK)
176
+ {
177
+ char buffer[18 ]; // 6*2 characters for hex + 5 characters for colons + 1 character for null terminator
172
178
sprintf (buffer, " %02X:%02X:%02X:%02X:%02X:%02X" , mac_base[0 ], mac_base[1 ], mac_base[2 ], mac_base[3 ], mac_base[4 ], mac_base[5 ]);
173
179
mac = buffer;
174
180
}
175
181
176
182
return mac;
177
183
}
178
184
179
- void setup () {
185
+ void setup ()
186
+ {
187
+ // allow supply voltage to stabalize
188
+ // delay(250);
180
189
181
190
Serial.begin (115200 );
182
191
192
+ // allow serial to setup and to prevent SD card header being sent to SD card on noisey power up
193
+ // delay(250);
194
+
183
195
// display ESP32 details
184
- for (int i = 0 ; i < 17 ; i = i + 8 ) {
196
+ for (int i = 0 ; i < 17 ; i = i + 8 )
197
+ {
185
198
chipId |= ((ESP.getEfuseMac () >> (40 - i)) & 0xff ) << i;
186
199
}
187
200
@@ -209,43 +222,55 @@ void setup() {
209
222
spi.begin (SD_SCK, SD_MISO, SD_MOSI, SD_CS);
210
223
211
224
// set SPI clock speed to 30 MHz
212
- if (!SD.begin (SD_CS, spi, 30000000 )) {
225
+ if (!SD.begin (SD_CS, spi, 30000000 ))
226
+ {
213
227
Serial.print (" \n SD card mount failed, " );
214
228
}
215
229
216
230
uint8_t cardType = SD.cardType ();
217
231
218
- if (cardType == CARD_NONE) {
232
+ if (cardType == CARD_NONE)
233
+ {
219
234
Serial.println (" no SD card found" );
220
235
SD_card_present = false ;
221
- } else {
236
+ }
237
+ else
238
+ {
222
239
SD_card_present = true ;
223
240
224
241
// SD card Queue creation only if SD card detected
225
242
SD_Queue = xQueueCreate (20 , sizeof (SD_buffer));
226
- if (SD_Queue == NULL ) {
243
+ if (SD_Queue == NULL )
244
+ {
227
245
Serial.println (" Error Creating the SD Queue" );
228
246
}
229
247
230
248
// create task to read xQueue and write to SD card
231
249
xTaskCreatePinnedToCore (
232
- TaskSDWrite, // task function to call
233
- " SD Write" , // name of task for optional eports
234
- 2048 , // Stack depth/size in bytes
235
- NULL , // pvParameters
236
- 2 , // Priority 1, same as setup() and loop(), now priority of 2
237
- &xSDWrite, // task handle
238
- // NULL, // no task handle
239
- 1 ); // core to use for this task
250
+ TaskSDWrite, // task function to call
251
+ " SD Write" , // name of task for optional eports
252
+ 2048 , // Stack depth/size in bytes
253
+ NULL , // pvParameters
254
+ 2 , // Priority 1, same as setup() and loop(), now priority of 2
255
+ &xSDWrite, // task handle
256
+ // NULL, // no task handle
257
+ 1 ); // core to use for this task
240
258
241
259
Serial.print (" SD card type: " );
242
- if (cardType == CARD_MMC) {
260
+ if (cardType == CARD_MMC)
261
+ {
243
262
Serial.println (" MMC" );
244
- } else if (cardType == CARD_SD) {
263
+ }
264
+ else if (cardType == CARD_SD)
265
+ {
245
266
Serial.println (" SDSC" );
246
- } else if (cardType == CARD_SDHC) {
267
+ }
268
+ else if (cardType == CARD_SDHC)
269
+ {
247
270
Serial.println (" SDHC" );
248
- } else {
271
+ }
272
+ else
273
+ {
249
274
Serial.println (" UNKNOWN" );
250
275
}
251
276
@@ -254,9 +279,10 @@ void setup() {
254
279
Serial.printf (" SD Card Size: %llu MB\n " , SD.cardSize () / (1024 * 1024 ));
255
280
Serial.printf (" Available space: %llu MB\n " , (SD.totalBytes () - SD.usedBytes ()) / (1024 * 1024 ));
256
281
257
- // create a unique LogFile filename
258
- int fn = 0 ;
259
- do {
282
+ // create a unique LogFile filename, start at 1
283
+ int fn = 1 ;
284
+ do
285
+ {
260
286
sprintf (LogFileName, " /CAN_LOG_%d.csv" , fn++);
261
287
} while (SD.exists (LogFileName));
262
288
LogFile = SD.open (LogFileName, FILE_APPEND);
@@ -267,36 +293,32 @@ void setup() {
267
293
268
294
Serial.printf (" CAN log filename: %s\n " , LogFileName);
269
295
270
- } // end SD_card_present
296
+ } // end SD_card_present
271
297
272
298
// sets CAN speed, LED pins, WIFI, SSID, etc
273
299
loadSettings ();
274
300
275
301
// starts CAN
276
302
canManager.setup ();
277
303
278
-
279
- if (SD_card_present) {
280
- Serial.println (" *** Recording CAN bus to SD card ***" );
281
- } else {
282
- Serial.println (" *** Ready for SavvyCAN Network (GVRET) connection ***" );
283
- }
284
-
304
+ // setup wifi
285
305
SysSettings.isWifiConnected = false ;
286
306
287
- // allow CAN bus to start up before starting wifi
288
- delay (250 );
289
-
290
- // setup wifi
291
- wifiManager.setup ();
307
+ if (!SD_card_present)
308
+ {
309
+ // allow CAN bus to start up before starting wifi
310
+ delay (250 );
311
+ wifiManager.setup ();
312
+ }
292
313
}
293
314
294
315
/*
295
316
Send a fake frame out USB and maybe to file to show where the mark was triggered at. The fake frame has bits 31 through 3
296
317
set which can never happen in reality since frames are either 11 or 29 bit IDs. So, this is a sign that it is a mark frame
297
318
and not a real frame. The bottom three bits specify which mark triggered.
298
319
*/
299
- void sendMarkTriggered (int which) {
320
+ void sendMarkTriggered (int which)
321
+ {
300
322
CAN_FRAME frame;
301
323
frame.id = 0xFFFFFFF8ull + which;
302
324
frame.extended = true ;
@@ -317,7 +339,8 @@ Any bytes between checksum and 0xF1 are thrown away
317
339
Yes, this should probably have been done more neatly but this way is likely to be the
318
340
fastest and safest with limited function calls
319
341
*/
320
- void loop () {
342
+ void loop ()
343
+ {
321
344
// uint32_t temp32;
322
345
bool isConnected = false ;
323
346
int serialCnt;
@@ -326,17 +349,24 @@ void loop() {
326
349
/* if (Serial)*/ isConnected = true ;
327
350
328
351
canManager.loop ();
329
- wifiManager.loop ();
352
+
353
+ // only start softAP is SD card is not present
354
+ if (!SD_card_present)
355
+ {
356
+ wifiManager.loop ();
357
+ }
330
358
331
359
size_t wifiLength = wifiGVRET.numAvailableBytes ();
332
360
size_t serialLength = serialGVRET.numAvailableBytes ();
333
361
334
362
size_t maxLength = (wifiLength > serialLength) ? wifiLength : serialLength;
335
363
336
364
// If the max time has passed or the buffer is almost filled then send buffered data out
337
- if ((micros () - lastFlushMicros > SER_BUFF_FLUSH_INTERVAL) || (maxLength > (WIFI_BUFF_SIZE - 40 ))) {
365
+ if ((micros () - lastFlushMicros > SER_BUFF_FLUSH_INTERVAL) || (maxLength > (WIFI_BUFF_SIZE - 40 )))
366
+ {
338
367
lastFlushMicros = micros ();
339
- if (serialLength > 0 ) {
368
+ if (serialLength > 0 )
369
+ {
340
370
341
371
if (SD_card_present)
342
372
// saves CAN frames to uSD card in SavvyCAN CSV format
@@ -346,45 +376,48 @@ void loop() {
346
376
// copy serialGVRET.getBufferedBytes() > transmitBuffer to SD_buffer
347
377
memcpy (&SD_buffer, serialGVRET.getBufferedBytes (), serialLength);
348
378
349
- if (xQueueSend (SD_Queue, (void *)&SD_buffer, 10 * portTICK_PERIOD_MS) != pdPASS) {
379
+ if (xQueueSend (SD_Queue, (void *)&SD_buffer, 10 * portTICK_PERIOD_MS) != pdPASS)
380
+ {
350
381
Serial.println (" xQueueSend to SD_Queue 10ms timeout error" );
351
382
}
352
383
353
- // flush SD card every 20ms
354
384
LogFile.flush ();
355
385
356
386
serialGVRET.clearBufferedBytes ();
357
- // only toggle Rx LED when writing to SD card, regardless of CAN activity
358
-
359
- // toggles every 20ms x 100 = 2 seconds
360
- // toggleRXLED();
361
- // below works, but blinks too fast
362
- // toggleSD_LED();
363
- } else {
387
+ }
388
+ else
389
+ {
364
390
serialGVRET.clearBufferedBytes ();
365
391
}
366
392
}
367
393
368
- if (wifiLength > 0 ) {
394
+ if (wifiLength > 0 )
395
+ {
369
396
wifiManager.sendBufferedData ();
370
397
}
371
398
}
372
399
373
400
serialCnt = 0 ;
374
- while ((Serial.available () > 0 ) && serialCnt < 128 ) {
401
+ while ((Serial.available () > 0 ) && serialCnt < 128 )
402
+ {
375
403
serialCnt++;
376
404
in_byte = Serial.read ();
377
405
serialGVRET.processIncomingByte (in_byte);
378
406
}
379
407
}
380
408
381
409
// write data to SD card
382
- void TaskSDWrite (void *pvParameters) {
383
- for (;;) {
410
+ void TaskSDWrite (void *pvParameters)
411
+ {
412
+ for (;;)
413
+ {
384
414
// blocks/waits 1000 ms to receive data from SD_Queue
385
- if (xQueueReceive (SD_Queue, &SD_buffer, 1000 * portTICK_PERIOD_MS) != pdPASS) {
415
+ if (xQueueReceive (SD_Queue, &SD_buffer, 1000 * portTICK_PERIOD_MS) != pdPASS)
416
+ {
386
417
Serial.println (" SD queue receive error: 1000ms second timeout." );
387
- } else {
418
+ }
419
+ else
420
+ {
388
421
LogFile.print (SD_buffer);
389
422
}
390
423
}
0 commit comments