Hello, currently working on a ESP32 Adafruit feather running FreeRTOS in an effort to log accel. data @ 1000 Hz. LIS3DH accel hooked up using I2C, SD card hooked up using SPI (Feather hat RTC+SD card). I've used the vtaskdelay successfully to get data at 100 to about 400 Hz when things start going bad. Ideally Task "GetData" gets data from the LIS3DH via I2C @ 5000Hz. Then writes to queue and TAsk "SDWrite" takes data via pointers to SD Card. I have a 3rd Task "SDflush" to run a file.flush() on the system to make sure its written. I can always get data but as it gets faster it writes the same values up to 16 times in a row and the fastest it achieves is 2000 usec (about 500 Hz).
Things I've tried:
- remove vtaskdelay(): data timing jumps around and average Hz actually gets lower
- keep an eye on Queue: Queue never seems to even get close to filling up, SDwrite task works pretty fast
- log.flush(): adjust time, doesn't seem to have an effect
- removed size of data structure: didn't seem to have any effect on lag
I think I may not be getting data fast enough, but data rate is set to 5000Hz and I2C should definitely be fast enough?
Here is a data file read out with vTaskDelay(1):
439540,0.00000,0.57475,9.31093
441540,0.00000,0.45980,9.31093
449541,-0.11495,0.57475,8.96608
449541,-0.11495,0.57475,8.96608
449541,-0.11495,0.57475,8.96608
449541,-0.11495,0.57475,8.96608
451536,-0.11495,0.57475,9.19598
453541,-0.11495,0.34485,9.31093
455541,-0.11495,0.22990,9.42588
457541,-0.22990,0.34485,9.31093
459541,-0.22990,0.34485,9.19598
461541,-0.22990,0.45980,9.42588
463541,-0.45980,0.34485,9.31093
465541,-0.11495,0.45980,9.54083
467541,-0.11495,0.57475,9.31093
469541,-0.11495,0.80465,9.42588
471541,0.11495,0.45980,9.42588
473541,-0.11495,0.34485,9.42588
481540,0.00000,0.34485,9.42588
481540,0.00000,0.34485,9.42588
481540,0.00000,0.34485,9.42588
481540,0.00000,0.34485,9.42588
You can see "hiccups" of same data logged.
Code Part 1:
/ Data logger: For measuring acceleration from LIS3DH
// Hardware: Adafruit ESP32, Adalogger feather+RTC, 1x LIS3DH accels (Currently, 2x in future)
// Created: May 12 2020
// Updated:
// Uses SPI for SD, I2C for Accels, hoping for 1000 Hz. sampling
// files are saves text files, csv = NNNNNNNN.TXT
// See ReadME and photos for additional hook up info
//Use ESP32 duo core
const int TaskCore1 = 1;
const int TaskCore0 = 0;
//Libraries
#include <Wire.h>
#include <SPI.h>
#include <SdFat.h>
#include <Adafruit_LIS3DH.h>
#include <Adafruit_Sensor.h>
#include <stdio.h>
#include "esp_system.h" //This inclusion configures the peripherals in the ESP system.
//#include "freertos/Arduino_FreeRTOS.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/timers.h"
#include "freertos/event_groups.h"
#include "freertos/queue.h"
//#include "freertos/stream_buffer.h"
//------------------------------------------------------------------------------
// SD file definitions
const uint8_t sdChipSelect = 33;
SdFat sd;
SdFile file;
File logfile;
//------------------------------------------------------------------------------
// Queue definitions
// data type for Queue item
struct Data_t {
uint32_t usec;
float valueX;
float valueY;
float valueZ;
} xData_t;
//Declare Queue data type for FreeRTOS
QueueHandle_t DataQueue = NULL;
//TickType_t pdMS_TO_TICKS(uint32_t millis);
//TickType_t intervalTicks = pdMS_TO_TICKS(1);
// interval between points in units of 1000 usec
const uint16_t intervalTicks = 1;
//------------------------------------------------------------------------------
// Accel Lis3dh definitions, I2C
// Sensor I2C
Adafruit_LIS3DH lis = Adafruit_LIS3DH();
// Used for hardware & software SPI
// hardware SPI 1 LIS3DH->Feather: Power to Vin, Gnd to Gnd, SCL->SCK, SDA->MOSI, SDO->MOSO, CS->CS 14/15
//#define LIS3DH_CS 14 //ESP32: 14/A6 , Cortex m0: 5, Use for upper accel, hbar, seatpost, etc.
//#define LIS3DH_CS2 15 //ESP32: 15/A8, Cortex m0: 9, Use for lower accel, axles, etc.
// Sensor 1
//Adafruit_LIS3DH lis = Adafruit_LIS3DH(LIS3DH_CS);
// Sensor 2
//Adafruit_LIS3DH lis2 = Adafruit_LIS3DH(LIS3DH_CS2);
//------------------------------------------------------------------------------
// define two tasks for Sensor Data and SD Write
void TaskGetData( void *pvParameters );
void TaskSDWrite( void *pvParameters );
void TaskSDFlush( void *pvParameters );
//------------------------------------------------------------------------------
// Start the scheduler so the created tasks start executing. Need this for ESP32???
void vTaskStartScheduler();
// the setup function runs once when you press reset or power the board
void setup() {
// initialize serial communication at 115200 bits per second:
//Serial.begin(115200);
//Outputs, Pins, Buttons, Etc.
pinMode(13, OUTPUT); //set Built in LED to show writing on SD Card
//ACCEL Setup and RUN
if (! lis.begin(0x18)) { // change this to 0x19 for alternative i2c address
Serial.println("Couldnt start");
while (1) yield();
}
Serial.println("LIS3DH found!");
// Set accel range
lis.setRange(LIS3DH_RANGE_16_G); // 2, 4, 8 or 16 G!
//lis2.setRange(LIS3DH_RANGE_16_G);
// Set DataRate
lis.setDataRate(LIS3DH_DATARATE_LOWPOWER_5KHZ); //OPTIONS: LIS3DH_DATARATE_400_HZ, LIS3DH_DATARATE_LOWPOWER_1K6HZ, LIS3DH_DATARATE_LOWPOWER_5KHZ
//lis2.setDataRate(LIS3DH_DATARATE_LOWPOWER_5KHZ);
//Setup I2C speed
//Wire.setClock(400000);
// SD CARD SETUP ====================================================================
// see if the card is present and can be initialized: (Use highest SD clock possible, but lower if has error, 15 Mhz works, possible to go to to 25 Mhz if sample rate is low enough
if (!sd.begin(sdChipSelect, SD_SCK_MHZ(25))) {
Serial.println("Card init. failed!");
//error(2);
}
// Create filename scheme ====================================================================
char filename[15];
// Setup filename to be appropriate for what you are testing
strcpy(filename, "/DATA00.TXT");
for (uint8_t i = 0; i < 100; i++) {
filename[5] = '0' + i/10;
filename[6] = '0' + i%10;
// create if does not exist, do not open existing, write, sync after write
if (! sd.exists(filename)) {
break;
}
}
// Create file and prepare it ============================================================
logfile = sd.open(filename, O_CREAT | O_WRITE ); // | O_TRUNC);
if( ! logfile ) {
Serial.print("Couldnt create ");
Serial.println(filename);
//error(3);
}
Serial.print("Writing to ");
Serial.println(filename);
pinMode(13, OUTPUT);
Serial.println("Ready!");
//Queue Setup
DataQueue = xQueueCreate(5000, sizeof( &xData_t ));
if(DataQueue == NULL){
Serial.println("Error Creating the Queue");
}
// Setup up Tasks and where to run ============================================================
// Now set up tasks to run independently.
xTaskCreatePinnedToCore(
TaskGetData
, "Get Data from Accel to Queue" // A name just for humans
, 2048 // This stack size can be checked & adjusted by reading the Stack Highwater
, NULL
, 3 // Priority, with 3 (configMAX_PRIORITIES - 1) being the highest, and 0 being the lowest.
, NULL
, TaskCore0);
xTaskCreatePinnedToCore(
TaskSDWrite
, "Get Data from Queue"
, 2048 // Stack size
, NULL
, 2 // Priority
, NULL
, TaskCore1);
xTaskCreatePinnedToCore(
TaskSDFlush
, "Write Data to Card"
, 1024 // Stack size
, NULL
, 1 // Priority
, NULL
, TaskCore1);
}
void loop()
{
// Empty. Things are done in Tasks.
/*--------------------------------------------------*/
/*---------------------- Tasks ---------------------*/
/*--------------------------------------------------*/
}