Nano 33 100Hz+ SD Card Datalogging

Are there any solutions for onboard IMU datalogging to SD Card at 100Hz or better on the Nano 33 Platform, with consistent samples (10ms or less)?

The thread below discusses the issue (which explains the standard 13Hz logging) but without any detailed solutions to solve the problem.
https://forum.arduino.cc/index.php?topic=642496.0

I have also tried to get the SdFat LowLatencyLogger working, which should perform better by using buffers but have topped out at 83Hz, as explained further in the following thread, maybe someone with a Nano 33 may have a better understanding to what the problem is.
https://forum.arduino.cc/index.php?topic=692540.0

Has anyone had the LowLatencyLogger working with their Nano 33’s or used an alternate method to achieve greater speed, such as different code or assigning the pins through mbed if that is possible with the LowLatencyLogger.

I have all three of the Nano 33’s but have only tried it so far with the BLE guessing they would all behave the same. Any advice or suggestions would be greatly appreciated.

Thanks
Matt

When you keep the file open you should be able to write the data at the speed the sensor can provide it. The ArduinoLSM9DS1 library sets the data rate for the accelerometer to about 100 Hz. See

Here is a simple example that reads the data at that speed and creates a new file every 60 seconds.

#include <SPI.h>
#include <SD.h>
#include <Arduino_LSM9DS1.h>

const int chipSelect = 10;

#define FILE_INTERVALL       60000
#define FILE_NAME_BASE       "DATA"
#define FILE_NAME_EXTENSION  ".log"

void setup()
{
  Serial.begin( 9600 );
  while ( !Serial );

  Serial.print( "Initializing SD card..." );

  if ( !SD.begin( chipSelect ) )
  {
    Serial.println( "card failed, or not present" );
    while ( 1 );
  }
  Serial.println( " card initialized." );

  if ( !IMU.begin() )
  {
    Serial.println( "Failed to initialize IMU!" );
    while ( 1 );
  }

  Serial.print( "Accelerometer sample rate = " );
  Serial.print( IMU.accelerationSampleRate() );
  Serial.println( " Hz" );
}

void loop()
{
  String dataString = "";
  static String fileName = "";
  static File dataFile;
  static long previousMillis = -100000;
  static int sampleCount = 0;

  unsigned long currentMillis = millis();
  if ( currentMillis - previousMillis > FILE_INTERVALL )
  {
    previousMillis = currentMillis;
    if ( dataFile )
    {
      dataFile.close();
      Serial.print( "File closed: " );
      Serial.print( fileName );
      Serial.print( " (" );
      Serial.print( sampleCount );
      Serial.println( " Samples)" );
      sampleCount = 0;
    }

    int fileNumber = 0;
    do
    {
      fileNumber++;
      fileName = FILE_NAME_BASE + String( fileNumber ) + FILE_NAME_EXTENSION;
    }
    while ( SD.exists( fileName ) == true );

    dataFile = SD.open( fileName, FILE_WRITE );

    Serial.print( "New File: " );
    Serial.println( fileName );
  }

  if ( IMU.accelerationAvailable() )
  {
    float x, y, z;
    sampleCount++;

    IMU.readAcceleration( x, y, z );

    dataString += String( x );
    dataString += ",";
    dataString += String( y );
    dataString += ",";
    dataString += String( z );

    if ( dataFile )
    {
      dataFile.println( dataString );
    }
  }
}

If you would want to run this for a long time you would need to make some small changes to keep the time constant. Right now the time to create new files is part of the 60 seconds. So over time you would loose samples. But I wanted to keep the example simple.

Thanks for the code Klaus, the Razor IMU also uses the string method although extremely verbose. I have that module also but it has inconsistent writes to SD which the SdFat logger addresses.

Whilst the frequent 60ms latency and occasional larger bleeps won’t be an issue in many situations, the project I am working on has rapid bursts of acceleration/deceleration and I need to log this motion hence the preference for a buffered solution. 80Hz would probably be sufficient but if it’s possible to have a consistent solution at 100Hz or more then that would be good and can then use it in my automotive hobbies, measuring gear changes etc.

The accelerometer speed can be changed with the control registers and with a max of 952 Hz it should be good enough to measure the gear changes of various DCT boxes and before/after modding gains.

I guess we are limited to 80Hz with the SdFat logger at least, maybe a code mod or other code will do better. The best solution would sort out the mbed issues as the SdFat logger has no problems at 1000+Hz on other platforms.

matt_g_01:
The accelerometer speed can be changed with the control registers and with a max of 952 Hz it should be good enough to measure the gear changes of various DCT boxes and before/after modding gains.

Could you post your code for the accelerometer?

What format do you need for the data? RAW binary data could save some bandwidth, but would require some data formatting on the PC side.

In the LSM9DS1.cpp:

  //stock settings for Gyro(CTRL_REG1_G) and Accelerometer(CTRL_REG6_XL) 
  //writeRegister(LSM9DS1_ADDRESS, LSM9DS1_CTRL_REG1_G, 0x78); // 119 Hz, 2000 dps, 16 Hz BW
  //writeRegister(LSM9DS1_ADDRESS, LSM9DS1_CTRL_REG6_XL, 0x70); // 119 Hz, 4G
   
  writeRegister(LSM9DS1_ADDRESS, LSM9DS1_CTRL_REG1_G, 0x00); // power down Gyro
  //writeRegister(LSM9DS1_ADDRESS, LSM9DS1_CTRL_REG6_XL, 0x00); // power down Accelerometer

  //writeRegister(LSM9DS1_ADDRESS, LSM9DS1_CTRL_REG1_G, 0x20); //  10 Hz, 245 dps, 100 Hz Low Pass Cutoff 

  //writeRegister(LSM9DS1_ADDRESS, LSM9DS1_CTRL_REG1_G, 0xa3); // 238 Hz, 245 dps, 100 Hz Low Pass Cutoff 
  //writeRegister(LSM9DS1_ADDRESS, LSM9DS1_CTRL_REG6_XL, 0x85); // 238 Hz, 2G, filter BW 211 Hz
  
  //writeRegister(LSM9DS1_ADDRESS, LSM9DS1_CTRL_REG6_XL, 0xA0); // 476 Hz, 2G, filter BW 408 Hz
  
  //writeRegister(LSM9DS1_ADDRESS, LSM9DS1_CTRL_REG1_G, 0xc3); // Max 952 Hz,  245 dps, 100 Hz Low Pass Cutoff 
  writeRegister(LSM9DS1_ADDRESS, LSM9DS1_CTRL_REG6_XL, 0xc4); // Max 952 Hz, 2G, filter BW 408 Hz
  
  //Magnetometer Control Registers
  writeRegister(LSM9DS1_ADDRESS_M, LSM9DS1_CTRL_REG1_M, 0xb4); // Temperature compensation enable, medium performance, 20 Hz
  //writeRegister(LSM9DS1_ADDRESS_M, LSM9DS1_CTRL_REG1_M, 0x00); // lowest settings
  writeRegister(LSM9DS1_ADDRESS_M, LSM9DS1_CTRL_REG2_M, 0x00); // 4 Gauss - Lowest
  writeRegister(LSM9DS1_ADDRESS_M, LSM9DS1_CTRL_REG3_M, 0x00); // Continuous conversion mode

These are a few values I put in there for testing, calculated from the corresponding tables in the lsm9ds1 datasheet, such as Table 66. CTRL_REG6_XL register for the Accelerometer, then converting the required binary from the table to hex in the code.

If you change the G, DPS and Gauss then the corresponding values in the calculation also need changing in the following classes:

For Acc in LSM9DS1Class::readAcceleration, if using 2G, replace the 4 with 2 :

  x = data[0]; // * 2.0 / 32768.0;
  y = data[1]; // * 2.0 / 32768.0;
  z = data[2]; // * 2.0 / 32768.0;

Then do the same within:
LSM9DS1Class::readGyroscope and
LSM9DS1Class::readMagneticField
to match the values chosen for DPS and Gauss.

For the current project I'll probably make do with 80Hz from the lowlatencylogger as it is consistent with timing, but for later in the year will likely try to get something going that is faster.

You might want to check update of the LSM9DS1 library.
There are functions that give control of all the ODR and FullScale settings. The values returned by getGyroODR, getAccelODR and getMagnetODR are the actual values that were measured during the call to the corresponding set...function.
The LSM9DS1 datasheet claims the maximum ODR is 952 on setting 6 for the gyroscope and the accelerometer. But it turns out that this maximum is almost equal to setting 5 @476Hz and with a lot more noise. Very nice is the fast ODR @400Hz for the magnetometer.