December 2022

Feather Transportation Logger


Functional Requirements

  • Transportation duration of two days
  • 1.5 G max acceleration in any direction
  • Employ level crossing to trigger data recording.   +/-0.25 G threshold for acceleration, +/-18 deg threshold for tilt,   < 32 °F and > 105 ° F threshold for temperature  

 

Hardware

 

Firmware

The code uses a level crossing algorithm, monitoring conditions during transport and recording data for all sensors to the micro SD card when:

  • 3-axis maximum acceleration exceeding 0.25 G
  • Tilt exceeding 18 degrees
  • Temperature when it is < 32 °F or > 105 °F

When any of the above thresholds are exceeded, all sensor data is recorded to the micro SD card. The acceleration and gyroscope data is sampled as quickly as possible. Every one second, the max, min, and avg is calculated for each of the three acceleration and gyroscope axis.   Follosing that, the tilt (pitch, roll, yaw) is calculated, and the temperature assessed relative to the 32 °F BS 105 °F thresholds.  

The sketch below was run for seven days with the level crossing being manually exceeded once or more per day in order to cause the data including the 4400 mAh Lithium Ion battery voltage to be recorded.   I estimate the battery life at 80% discharge to be 95 hours for four days.  

 


 /*

  M4 Express ATSAMD51 Cortex M4 microcontroller
  9DOF IMU - Adafruit ISM330DHCX + LIS3MDL FeatherWing
  Datalogger + RTC FeatherWing
  Adafruit Sensirion SHT40 Temperature & Humidity Sensor
  SparkFun Qwiic / Stemma QT FeatherWing (for SHT40)
  Li 4400 mAh 3.7 V battery
  
  Monitors conditions during transport and records to mSD card:
    3-axis maximum acceleration exceeding 0.25 G
    Tilt exceeding 18 degrees
    Temperature when it is < 32 °F or > 105 °F

  When any of the above thresholds are exceeded, all sensor data is
  recorded to the micro SD card.  
  The acceleration and gyroscope data is sampled as quickly as possible.
  Every one second, the max, min, and avg is calculated for each of the
  three acceleration and gyroscope axis.  Follosing that, the tilt
  (pitch, roll, yaw) is calculated, and the temperature assessed relative
  to the 32 °F BS 105 °F thresholds.  
  
  9 DOF IMU - Accelerometer / Gyroscope / Magnetometer 

  Before using this sketch, you must calibrate the sensors using:
    sensor_calib_zero-offset_imu_accel_gyro_mag.ino

  and then update the following with the results:

    xyzReading accelOffset = {0.0, 0.0, 0.0};
    xyzReading gyroOffset = {0.0, 0.0, 0.0};
    xyzReading magHardIronOffset = {0.0, 0.0, 0.0};
    xyzReading magSoftIronScale = {0.0, 0.0, 0.0};
    
*/

const byte pin_built_in_led = 13;  

void blinkERR(byte ledPIN){
  // S-O-S
  const int S = 150, O = 300;
  for(int i = 3; i>0; i--){
    digitalWrite(ledPIN, HIGH);
    delay(S);
    digitalWrite(ledPIN, LOW);
    delay(S);
  }    
  delay(200);
  for(int i = 3; i>0; i--){
    digitalWrite(ledPIN, HIGH);
    delay(O);
    digitalWrite(ledPIN, LOW);
    delay(O);
  }    
  delay(200);
  for(int i = 3; i>0; i--){
    digitalWrite(ledPIN, HIGH);
    delay(S);
    digitalWrite(ledPIN, LOW);
    delay(S);
  }    
  delay(200);
} // blinkERR()


/////////////////////////////////////////////////////////////////////////
// SHT40 temperature & humidity sensor
// Install library "Adafruit SHT4X"
// Typical read duration is 11 ms

#include "Adafruit_SHT4x.h"
Adafruit_SHT4x sht4 = Adafruit_SHT4x();

/////////////////////////////////////////////////////////////////////////
// SD

// NOTE: Typical SD write & flush takes 20 to 30 ms

// Arduino standard SD library
#include <SPI.h>
#include <SD.h>
// 'chip/secondary select' (SS) (also called CS)
//    M4 Express ATSAMD51 Cortex M4 pin 10
const int SDchipSelect = 10;
File sdfile;

/////////////////////////////////////////////////////////////////////////
// Adafruit LSM6DS (ISM330DHCX) + LIS3MDL

//Install these libraries:
//  Adafruit_Sensor
//  Adafruit LSM6DS   LSM6DSOX, ISM330DHCX, LSM6DSO32, LSM6DS33   
//                    https://github.com/adafruit/Adafruit_LSM6DS
//  Adafruit LIS3MDL  https://github.com/adafruit/Adafruit_LIS3MDL

#include <Adafruit_LIS3MDL.h>   // magnetometer 
Adafruit_LIS3MDL magn;
//For LIS3MDL: Examples -> Adafruit LIS3MDL -> lis3mdl_demo

// LSM6DS library for accel, gyro, temp
// See library folder 'Adafruit_LSM6DS' for include options, and https://learn.adafruit.com/lsm6dsox-and-ism330dhc-6-dof-imu?view=all
#include <Adafruit_ISM330DHCX.h>
Adafruit_ISM330DHCX imu;
//For ISM330DHCX: File -> Examples -> Adafruit LSM6DS -> adafruit_ism330dhcx_test

#include <Wire.h>
#include <Adafruit_Sensor.h>

#define MAGNETOMETER_EXISTS true

unsigned long samples = 0;

float degPerSec(float radPerSec) {
  // Ref: 0.006 rad/s = 0.35 deg/s
  float f = radPerSec * 180.0 / M_PI;
  return f;
}

struct xyzReading {
  float x;
  float y;
  float z;
};

xyzReading accelMax = {0.0, 0.0, 0.0};
xyzReading accelMin = {0.0, 0.0, 0.0};
xyzReading accelAvg = {0.0, 0.0, 0.0};
// Update accelOffset by running: 'sensor_calib_zero-offset_imu_accel_gyro_mag.ino'
xyzReading accelOffset = {0.076220, 0.194032, 0.038702};
xyzReading accelZero = {0.0, 0.0, 9.80665};

xyzReading gyroMax = {0.0, 0.0, 0.0};
xyzReading gyroMin = {0.0, 0.0, 0.0};
xyzReading gyroAvg = {0.0, 0.0, 0.0};
// Update gyroOffset by running: 'sensor_calib_zero-offset_imu_accel_gyro_mag.ino'
xyzReading gyroOffset = {-0.005534, 0.008544, 0.008752};
xyzReading gyroZero = {0.0, 0.0, 0.0};

#if MAGNETOMETER_EXISTS
  // corrections below in micro-Tesla (uT)
  xyzReading magHardIronOffset = {-1.3666, -5.5759, 2.3239};
  xyzReading magSoftIronScale = {0.9859, 0.9723, 1.0448};

  // If you have the magnetic declination to correct for the difference between
  // magnetic north and geographic north, then revise the next line to reflect
  // that offset. 
  float mag_decl = 0.0;
  
  float magnetometerHeading(float raw_mag_x, float raw_mag_y) {
    // raw_mag_x and raw_mag_y in micro-Tesla (uT)
    // If mag_decl = 0.0, then returns the magnetic heading in degrees,
    // otherwise the geographic heading in degrees. 
    
      // Calculate angle for heading, assuming the magnetometer is parallel
      // to the ground and +Y points toward the magnetic heading of interest.
      //float heading = -1 * (atan2(raw_mag_x, raw_mag_y) * 180.0) / M_PI;
      
      // Calculate angle for heading, assuming the magnetometer is parallel
      // to the ground and +X points toward the magnetic heading of interest.
      float heading = -1 * (atan2(raw_mag_y, raw_mag_x) * 180.0) / M_PI;
  
      // Apply any magnetic declination to get the geographic heading
      heading += mag_decl;
      
      // Convert heading to 0..360 degrees
      if (heading < 0) {
        heading += 360.0;
      }
  
      return heading;
      
  } // magnetometerHeading()  
#endif

struct LevelCrossLowHiAbs {
  float a_x_hi;
  float a_y_hi;
  float a_z_hi;
  float r_hi;
  float p_hi;
  float t_lo;
  float t_hi;
};
// +/-0.25 G (2.5 m/s^2) threshold for acceleration, 
// +/-18 deg threshold for tilt,   
// < 32 °F and > 105 ° F threshold for temperature
LevelCrossLowHiAbs level_cross = {
  {2.5},
  {2.5},
  {12.3},
  {18.0},
  {18.0},
  {32.0},
  {105.0},
};


void sampleData() {
  // Continuously sample data as fast as loop() will allow.
  // Record the data to accelMax, accelMin, accelAvg 
  // (using the offset correction), and also update 'samples'.
  // See also TimerA().
  
  // Below varies by library. 
  sensors_event_t a;  // m/s^2
  sensors_event_t g;  // rad/s
  sensors_event_t t;  // °C
  sensors_event_t m;  // micro-Tesla (uT)
  imu.getEvent(&a, &g, &t);
  // sensors_event_t accel, mag, gyro, temp, dist, lux, press, RH, I, V
  //sensors_event_t a, m, g;
  //gyro.getEvent(&g);
  //accelmagn.getEvent(&a, &m);

  accelAvg.x += a.acceleration.x + accelOffset.x;
  accelAvg.y += a.acceleration.y + accelOffset.y;
  accelAvg.z += a.acceleration.z + accelOffset.z;

  gyroAvg.x += g.gyro.x + gyroOffset.x;
  gyroAvg.y += g.gyro.y + gyroOffset.y;
  gyroAvg.z += g.gyro.z + gyroOffset.z;

  if (samples == 0) {
    accelMax.x = a.acceleration.x + accelOffset.x;
    accelMax.y = a.acceleration.y + accelOffset.y;
    accelMax.z = a.acceleration.z + accelOffset.z;
    gyroMax.x = g.gyro.x + gyroOffset.x;
    gyroMax.y = g.gyro.y + gyroOffset.y;
    gyroMax.z = g.gyro.z + gyroOffset.z;
    
    accelMin.x = a.acceleration.x + accelOffset.x;
    accelMin.y = a.acceleration.y + accelOffset.y;
    accelMin.z = a.acceleration.z + accelOffset.z;
    gyroMin.x = g.gyro.x + gyroOffset.x;
    gyroMin.y = g.gyro.y + gyroOffset.y;
    gyroMin.z = g.gyro.z + gyroOffset.z;
  } else {
    accelMax.x = max(a.acceleration.x + accelOffset.x, accelMax.x);
    accelMax.y = max(a.acceleration.y + accelOffset.x, accelMax.y);
    accelMax.z = max(a.acceleration.z + accelOffset.z, accelMax.z);
    
    gyroMax.x = max(g.gyro.x + gyroOffset.x, gyroMax.x);
    gyroMax.y = max(g.gyro.y + gyroOffset.y, gyroMax.y);
    gyroMax.z = max(g.gyro.z + gyroOffset.z, gyroMax.z);

    accelMin.x = min(a.acceleration.x + accelOffset.x, accelMin.x);
    accelMin.y = min(a.acceleration.y + accelOffset.y, accelMin.y);
    accelMin.z = min(a.acceleration.z + accelOffset.z, accelMin.z);

    gyroMin.x = min(g.gyro.x + gyroOffset.x, gyroMin.x);
    gyroMin.y = min(g.gyro.y + gyroOffset.y, gyroMin.y);
    gyroMin.z = min(g.gyro.z + gyroOffset.z, gyroMin.z);
  }
  samples++;

} //  sampleData()

//////////////////////////////////////////////////////////////////////////////
// TimerA
// 1000000 us = 1000 ms = 1 sec = 1 Hz
const unsigned long timerAinterval = 1000;  
unsigned long timerAlap = millis();  // timer

void timerA() {
  //  Timer A
  
  if (timerAlap > millis())  timerAlap = millis();
  if (millis() - timerAlap > timerAinterval) { 
  
    accelAvg.x = accelAvg.x / samples;
    accelAvg.y = accelAvg.y / samples;
    accelAvg.z = accelAvg.z / samples;

    gyroAvg.x = gyroAvg.x / samples;
    gyroAvg.y = gyroAvg.y / samples;
    gyroAvg.z = gyroAvg.z / samples;

    // Below varies by library. 
    sensors_event_t a;  // m/s^2
    sensors_event_t g;  // rad/s
    sensors_event_t t;  // °C
    sensors_event_t m;  // micro-Tesla (uT)
    imu.getEvent(&a, &g, &t);
    magn.getEvent(&m);  

    // Determine if the sensor is at rest...
    if ((accelAvg.z > accelZero.z*0.95 && accelAvg.z < accelZero.z*1.05) || fabs(accelMax.x)-fabs(accelMin.x) < 1.0 || fabs(accelMax.y)-fabs(accelMin.y) < 1.0 ) {
      // Sensor is not in motion and is in the standard orientation (+z = 9.81 m/s^2)    
      // atan2() returns a value in the range of -PI to +PI radians (+/- 180 deg)
      double roll = atan2(-1*accelAvg.x ,(sqrt((pow(accelAvg.y,2)) + (pow(accelAvg.z,2)))));    // radians
      double pitch = atan2 (accelAvg.y ,(sqrt((pow(accelAvg.x,2)) + (pow(accelAvg.z,2)))));     // radians
      // Convert roll & pitch to degrees.  +/- 0..180
      roll *= 180.0 / M_PI;
      pitch *= 180.0 / M_PI;
      // SHT40 read takes 3 ms
      sensors_event_t humidity, temp;
      sht4.getEvent(&humidity, &temp);// populate temp and humidity objects with fresh data
      float t = temp.temperature*9.0/5.0+32.0;
      float rh = humidity.relative_humidity;
      #if MAGNETOMETER_EXISTS
        float Yh = ((m.magnetic.y - magHardIronOffset.y) * magSoftIronScale.y * cos(roll)) - ((m.magnetic.z - magHardIronOffset.z) * magSoftIronScale.z * sin(roll));
        float Xh = ((m.magnetic.x - magHardIronOffset.x) * magSoftIronScale.x * cos(pitch))+((m.magnetic.y - magHardIronOffset.y) * magSoftIronScale.y * sin(roll)*sin(pitch)) + ((m.magnetic.z - magHardIronOffset.z) * magSoftIronScale.z * cos(roll) * sin(pitch));
        double yaw = atan2(Yh, Xh) * 180.0 / M_PI;  // degrees 0 to 360
        // Check for level crosssing.. 
        if (fabs(accelMax.x) > level_cross.a_x_hi || fabs(accelMax.y) > level_cross.a_y_hi || fabs(accelMax.z) > level_cross.a_z_hi || fabs(roll) > level_cross.r_hi || fabs(pitch) > level_cross.p_hi || fabs(t) > level_cross.t_hi || t < level_cross.t_lo) {
          digitalWrite(pin_built_in_led, HIGH);
          //One or more level cross thresholds exceeded.  
          Serial.print("\t"); Serial.print(accelAvg.x,2); Serial.print("\t"); Serial.print(accelAvg.y,2); Serial.print("\t"); Serial.print(accelAvg.z,2); Serial.print("\t\t"); 
          Serial.print(roll,2); Serial.print("\t"); Serial.print(pitch,2); Serial.print("\t"); Serial.print(yaw,2); Serial.print("\t\t");
          Serial.print(magnetometerHeading((m.magnetic.x - magHardIronOffset.x) * magSoftIronScale.x, (m.magnetic.y - magHardIronOffset.y) * magSoftIronScale.y),1); Serial.print("\t\t");
          Serial.print(t,2); Serial.print("\t"); Serial.print(rh,2); Serial.println("\t\t");          
          
          Serial.println("");
          // Write the data to the SD card.
          sdfile.print(millis()); sdfile.print(";");
          sdfile.print(accelAvg.x,3); sdfile.print(";");
          sdfile.print(accelAvg.y,3); sdfile.print(";");
          sdfile.print(accelAvg.z,3); sdfile.print(";");
          sdfile.print(roll,3); sdfile.print(";");
          sdfile.print(pitch,3); sdfile.print(";");
          sdfile.print(yaw,3); sdfile.print(";");
          sdfile.print(magnetometerHeading((m.magnetic.x - magHardIronOffset.x) * magSoftIronScale.x, (m.magnetic.y - magHardIronOffset.y) * magSoftIronScale.y),1); sdfile.print(";");
          sdfile.print(t,1); sdfile.print(";");
          sdfile.print(rh,1); sdfile.println(";");         
          // Execute a flush() to insure it is written since no sdfile.close() will be issued.
          sdfile.flush();  
          digitalWrite(pin_built_in_led, LOW);
        }     
      #endif
    } else {
      Serial.println("\tSensor in motion");
    } // sensor motion detection   

    accelAvg.x = 0; accelAvg.y = 0; accelAvg.z = 0;
    accelMin.x = 0; accelMin.y = 0; accelMin.z = 0;
    accelMax.x = 0; accelMax.y = 0; accelMax.z = 0;
    gyroAvg.x = 0; gyroAvg.y = 0; gyroAvg.z = 0;
    gyroMin.x = 0; gyroMin.y = 0; gyroMin.z = 0;
    gyroMax.x = 0; gyroMax.y = 0; gyroMax.z = 0;
    samples = 0;
    
    timerAlap = millis(); // reset the timer
  }
} // timerA()

/////////////////////////////////////////////////////////////////////////



void setup() {

  pinMode(pin_built_in_led, OUTPUT);
  digitalWrite(pin_built_in_led, LOW);

  Serial.begin(115200);
  while (!Serial) {
    digitalWrite(pin_built_in_led, HIGH);
    delay(1);
    digitalWrite(pin_built_in_led, LOW);
  }
  Serial.println("\nSerial ready\n");
  
  //////////////////////////////////////////////////////////////////////////////
  // micro SD card AF P/N 254
  if (!SD.begin(SDchipSelect)) {
    Serial.println("SD card failed, or not present");
    while (1) {
      blinkERR(pin_built_in_led);
    }
  }
  Serial.println("SD card initialized.");
  // Create a filename reference to a file that doesn't exist 'ANALOG00.TXT'..'ANALOG99.TXT'
  char filename[15];
  strcpy(filename, "/ANALOG00.TXT");
  for (uint8_t i = 0; i < 100; i++) {
    filename[7] = '0' + i/10;
    filename[8] = '0' + i%10;
    // create if does not exist, do not open existing, write, sync after write
    if (SD.exists(filename)){
      Serial.print("File '");
      Serial.print(filename);
      Serial.println("' already exists");
    } else {
      Serial.print("New file will be '");
      Serial.print(filename);
      Serial.println("'");
      break;
    }
  }
  // Open file on SD Card for writing
  sdfile = SD.open(filename, FILE_WRITE);
  if (! sdfile) {
    Serial.print("ERROR - unable to create '");
    Serial.print(filename); Serial.println("'");
    while (1);
  }
  Serial.println("");
  #if MAGNETOMETER_EXISTS
    sdfile.println("m/s^2\tX\tY\tZ\t\tRoll\tPitch\tYaw deg\t\tMagnetic deg\t\tTemp F\t\tRH %");
  #else
    sdfile.println("m/s^2\tX\tY\tZ\t\tRoll\tPitch\t\tTemp F\t\tRH %");
  #endif
  // Execute a flush() to insure it is written since no sdfile.close() will be issued.
  sdfile.flush();  

  //////////////////////////////////////////////////////////////////////////////
  // SHT40 temperature & humidity sensor
  
  Serial.println("\nAdafruit SHT4x temperature & humidity sensor initialization..");
  // Warning: If SHT40 initialization fails, power cycle of device required.
  if (! sht4.begin()) {
    Serial.println("SHT4x sensor initialization error!");
    while (1) blinkERR(pin_built_in_led);
  }
  Serial.print("SHT4x sensor serial number 0x");
  Serial.println(sht4.readSerial(), HEX);
  // Set the sensor precision (higher precision = longer read time)
  sht4.setPrecision(SHT4X_LOW_PRECISION);
  switch (sht4.getPrecision()) {
     case SHT4X_HIGH_PRECISION: 
       Serial.println("High precision");
       break;
     case SHT4X_MED_PRECISION: 
       Serial.println("Med precision");
       break;
     case SHT4X_LOW_PRECISION: 
       Serial.println("Low precision");
       break;
  }
  // Set the heater.  Higher heat = longer read times and more power consumption.
  sht4.setHeater(SHT4X_NO_HEATER);
  switch (sht4.getHeater()) {
     case SHT4X_NO_HEATER: 
       Serial.println("No heater");
       break;
     case SHT4X_HIGH_HEATER_1S: 
       Serial.println("High heat for 1 second");
       break;
     case SHT4X_HIGH_HEATER_100MS: 
       Serial.println("High heat for 0.1 second");
       break;
     case SHT4X_MED_HEATER_1S: 
       Serial.println("Medium heat for 1 second");
       break;
     case SHT4X_MED_HEATER_100MS: 
       Serial.println("Medium heat for 0.1 second");
       break;
     case SHT4X_LOW_HEATER_1S: 
       Serial.println("Low heat for 1 second");
       break;
     case SHT4X_LOW_HEATER_100MS: 
       Serial.println("Low heat for 0.1 second");
       break;
  }
  Serial.println("");

  //////////////////////////////////////////////////////////////////////////////
  // Adafruit LSM6DS (ISM330DHCX) + LIS3MDL  (modify based on the sensor being used)
  // Initialize  
  Serial.println("LSM6DS (ISM330DHCX) initialization...");
  delay(2000);
  while (!imu.begin_I2C()) {
    Serial.println("LSM6DS (ISM330DHCX) initialization failure");
    blinkERR(pin_built_in_led);
    delay(1000);
  }
  Serial.println("LSM6DS (ISM330DHCX) initialized");

  imu.setAccelRange(LSM6DS_ACCEL_RANGE_16_G);
  Serial.print("Accelerometer range set to: ");
  switch (imu.getAccelRange()) {
  case LSM6DS_ACCEL_RANGE_2_G:
    Serial.println("+-2G");
    break;
  case LSM6DS_ACCEL_RANGE_4_G:
    Serial.println("+-4G");
    break;
  case LSM6DS_ACCEL_RANGE_8_G:
    Serial.println("+-8G");
    break;
  case LSM6DS_ACCEL_RANGE_16_G:
    Serial.println("+-16G");
    break;
  }

  // imu.setGyroRange(LSM6DS_GYRO_RANGE_250_DPS);
  Serial.print("Gyro range set to: ");
  switch (imu.getGyroRange()) {
  case LSM6DS_GYRO_RANGE_125_DPS:
    Serial.println("125 degrees/s");
    break;
  case LSM6DS_GYRO_RANGE_250_DPS:
    Serial.println("250 degrees/s");
    break;
  case LSM6DS_GYRO_RANGE_500_DPS:
    Serial.println("500 degrees/s");
    break;
  case LSM6DS_GYRO_RANGE_1000_DPS:
    Serial.println("1000 degrees/s");
    break;
  case LSM6DS_GYRO_RANGE_2000_DPS:
    Serial.println("2000 degrees/s");
    break;
  case ISM330DHCX_GYRO_RANGE_4000_DPS:
    Serial.println("4000 degrees/s");
    break;
  }

  imu.setAccelDataRate(LSM6DS_RATE_104_HZ);
  Serial.print("Accelerometer data rate set to: ");
  switch (imu.getAccelDataRate()) {
  case LSM6DS_RATE_SHUTDOWN:
    Serial.println("0 Hz");
    break;
  case LSM6DS_RATE_12_5_HZ:
    Serial.println("12.5 Hz");
    break;
  case LSM6DS_RATE_26_HZ:
    Serial.println("26 Hz");
    break;
  case LSM6DS_RATE_52_HZ:
    Serial.println("52 Hz");
    break;
  case LSM6DS_RATE_104_HZ:
    Serial.println("104 Hz");
    break;
  case LSM6DS_RATE_208_HZ:
    Serial.println("208 Hz");
    break;
  case LSM6DS_RATE_416_HZ:
    Serial.println("416 Hz");
    break;
  case LSM6DS_RATE_833_HZ:
    Serial.println("833 Hz");
    break;
  case LSM6DS_RATE_1_66K_HZ:
    Serial.println("1.66 KHz");
    break;
  case LSM6DS_RATE_3_33K_HZ:
    Serial.println("3.33 KHz");
    break;
  case LSM6DS_RATE_6_66K_HZ:
    Serial.println("6.66 KHz");
    break;
  }

  imu.setGyroDataRate(LSM6DS_RATE_104_HZ);
  Serial.print("Gyro data rate set to: ");
  switch (imu.getGyroDataRate()) {
  case LSM6DS_RATE_SHUTDOWN:
    Serial.println("0 Hz");
    break;
  case LSM6DS_RATE_12_5_HZ:
    Serial.println("12.5 Hz");
    break;
  case LSM6DS_RATE_26_HZ:
    Serial.println("26 Hz");
    break;
  case LSM6DS_RATE_52_HZ:
    Serial.println("52 Hz");
    break;
  case LSM6DS_RATE_104_HZ:
    Serial.println("104 Hz");
    break;
  case LSM6DS_RATE_208_HZ:
    Serial.println("208 Hz");
    break;
  case LSM6DS_RATE_416_HZ:
    Serial.println("416 Hz");
    break;
  case LSM6DS_RATE_833_HZ:
    Serial.println("833 Hz");
    break;
  case LSM6DS_RATE_1_66K_HZ:
    Serial.println("1.66 KHz");
    break;
  case LSM6DS_RATE_3_33K_HZ:
    Serial.println("3.33 KHz");
    break;
  case LSM6DS_RATE_6_66K_HZ:
    Serial.println("6.66 KHz");
    break;
  }

  imu.configInt1(false, false, true); // accelerometer DRDY on INT1
  imu.configInt2(false, true, false); // gyro DRDY on INT2

  // Adafruit LIS3MDL magnetometer 
  if (!magn.begin_I2C()) {
    Serial.println("LIS3MDL initialization failure");
    while (1) blinkERR(pin_built_in_led);
  }
  Serial.println("LIS3MDL initialized");
  magn.setPerformanceMode(LIS3MDL_MEDIUMMODE);
  Serial.print("Performance mode set to: ");
  switch (magn.getPerformanceMode()) {
    case LIS3MDL_LOWPOWERMODE: Serial.println("Low"); break;
    case LIS3MDL_MEDIUMMODE: Serial.println("Medium"); break;
    case LIS3MDL_HIGHMODE: Serial.println("High"); break;
    case LIS3MDL_ULTRAHIGHMODE: Serial.println("Ultra-High"); break;
  }

  magn.setOperationMode(LIS3MDL_CONTINUOUSMODE);
  Serial.print("Operation mode set to: ");
  // Single shot mode will complete conversion and go into power down
  switch (magn.getOperationMode()) {
    case LIS3MDL_CONTINUOUSMODE: Serial.println("Continuous"); break;
    case LIS3MDL_SINGLEMODE: Serial.println("Single mode"); break;
    case LIS3MDL_POWERDOWNMODE: Serial.println("Power-down"); break;
  }

  magn.setDataRate(LIS3MDL_DATARATE_155_HZ);
  // You can check the datarate by looking at the frequency of the DRDY pin
  Serial.print("Data rate set to: ");
  switch (magn.getDataRate()) {
    case LIS3MDL_DATARATE_0_625_HZ: Serial.println("0.625 Hz"); break;
    case LIS3MDL_DATARATE_1_25_HZ: Serial.println("1.25 Hz"); break;
    case LIS3MDL_DATARATE_2_5_HZ: Serial.println("2.5 Hz"); break;
    case LIS3MDL_DATARATE_5_HZ: Serial.println("5 Hz"); break;
    case LIS3MDL_DATARATE_10_HZ: Serial.println("10 Hz"); break;
    case LIS3MDL_DATARATE_20_HZ: Serial.println("20 Hz"); break;
    case LIS3MDL_DATARATE_40_HZ: Serial.println("40 Hz"); break;
    case LIS3MDL_DATARATE_80_HZ: Serial.println("80 Hz"); break;
    case LIS3MDL_DATARATE_155_HZ: Serial.println("155 Hz"); break;
    case LIS3MDL_DATARATE_300_HZ: Serial.println("300 Hz"); break;
    case LIS3MDL_DATARATE_560_HZ: Serial.println("560 Hz"); break;
    case LIS3MDL_DATARATE_1000_HZ: Serial.println("1000 Hz"); break;
  }
  
  magn.setRange(LIS3MDL_RANGE_4_GAUSS);
  Serial.print("Range set to: ");
  switch (magn.getRange()) {
    case LIS3MDL_RANGE_4_GAUSS: Serial.println("+-4 gauss"); break;
    case LIS3MDL_RANGE_8_GAUSS: Serial.println("+-8 gauss"); break;
    case LIS3MDL_RANGE_12_GAUSS: Serial.println("+-12 gauss"); break;
    case LIS3MDL_RANGE_16_GAUSS: Serial.println("+-16 gauss"); break;
  }

  magn.setIntThreshold(500);
  magn.configInterrupt(false, false, true, // enable z axis
                          true, // polarity
                          false, // don't latch
                          true); // enabled!

  
  //////////////////////////////////////////////////////////////////////////////

  Serial.println("\nSetup complete\n");
  timerAlap = millis(); // reset the timer
  samples = 0;

  randomSeed(millis());

  Serial.println("\nSetup complete\n");

  #if MAGNETOMETER_EXISTS
    Serial.println("m/s^2\tX\tY\tZ\t\tRoll\tPitch\tYaw deg\t\tMag deg\t\tTemp F\tRH %");
  #else
    Serial.println("m/s^2\tX\tY\tZ\t\tRoll\tPitch\t\tTemp F\tRH %");
  #endif
} // setup()


void loop() {

  sampleData();
  
  timerA();
  
} // loop()

 

Under Development

 


Do you need help developing or customizing a IoT product for your needs?   Send me an email requesting a free one hour phone / web share consultation.  

 

The information presented on this website is for the author's use only.   Use of this information by anyone other than the author is offered as guidelines and non-professional advice only.   No liability is assumed by the author or this web site.