Xiao Sense Accelerometer Examples and Low Power

Hi! Thanks for testing my code that really helped. I have much more rudimentary method for detecting amps (see photo) but I tweaked my setup and picked apart my code. I think by using daCoders “combination” code for waking up the xiao sense from sleep and adding a delay(10) to my loop i have decreased the amps needing during awake and sleep function (I am now getting ~20-30 microamps during sleep). Again this is with non-mbed boards and using a rechargeable Lithium Cr2020 battery to detect foot steps per minute with the BLE sensor on your shoe. thanks again!


#include <LSM6DS3.h>
#include <nrf52840.h>
#include <Wire.h>
#include <bluefruit.h>
#include <Adafruit_LittleFS.h>

LSM6DS3 myIMU(I2C_MODE, 0x6A);  //I2C device address 0x6A


long previousMillisSLEEP = 0;  // last time steps checked level was checked, in ms
long currentMillisSLEEP = 0;

long previousMillisSTEPS = 0;

long previousSteps = 0;
long cadence = 0;
long previouscadence = 0;


float accX, accY, accZ, gX, gY, gZ;
int stepCount = 0;
bool UPDATED = false;
long currentMillisSTEPS = 0;
long changeInMilllisSTEPS = 0;


int stepcountforcadenceaverage = 0;
int totalcandenceforaverage = 0;


bool xbool = false;

int countdetect = 0;


const int SMOOTHING_WINDOW_SIZE = 80; // # samples

int _samples[SMOOTHING_WINDOW_SIZE];  // the readings from the analog input
int _curReadIndex = 0;                // the index of the current reading
int _sampleTotal = 0;                 // the running total
int _sampleAvg = 0;                   // the average

bool CONNECTEDtoble = false;

BLEService        RSC  = BLEService("1814");
BLECharacteristic RSCcharac = BLECharacteristic("2A53");

void setupAccelerometer()
{
  // Start with LSM6DS3 in disabled to save power
  myIMU.settings.gyroEnabled = 0;
  myIMU.settings.accelEnabled = 0;

  myIMU.begin();

  /* Values from the application note */

  myIMU.writeRegister(LSM6DS3_ACC_GYRO_CTRL1_XL, 0x60);    // Turn on the accelerometer
                                                           // ODR_XL = 416 Hz, FS_XL = ±2 g
  myIMU.writeRegister(LSM6DS3_ACC_GYRO_TAP_CFG1, 0x8E);    // Enable interrupts and tap detection on X, Y, Z-axis
  myIMU.writeRegister(LSM6DS3_ACC_GYRO_TAP_THS_6D, 0x8C);  // Set tap threshold
  myIMU.writeRegister(LSM6DS3_ACC_GYRO_INT_DUR2, 0x7F);    // Set Duration, Quiet and Shock time windows
  myIMU.writeRegister(LSM6DS3_ACC_GYRO_WAKE_UP_THS, 0x80); // Single & double-tap enabled (SINGLE_DOUBLE_TAP = 1)
  myIMU.writeRegister(LSM6DS3_ACC_GYRO_MD1_CFG, 0x08);     // Double-tap interrupt driven to INT1 pin

  return;
}

void setupWakeUpInterrupt()
{
  // Initially the system kept turning back on after system_off. Through trial and error
  // added the accelerator power down and that fixed the issue
  myIMU.writeRegister(LSM6DS3_ACC_GYRO_MD1_CFG, 0x00);    // Power down the accelerometer
  myIMU.writeRegister(LSM6DS3_ACC_GYRO_CTRL1_XL, 0x00);   // Disable double-tap interrupt

  // Set up the accelerometer for Wake-up interrupt.
  // Per the application note, use a two step set up to avoid spurious interrupts
  // Set up values are from the application note, and then adjusted for minimum power

  myIMU.writeRegister(LSM6DS3_ACC_GYRO_WAKE_UP_DUR, 0x00); // No duration
  myIMU.writeRegister(LSM6DS3_ACC_GYRO_WAKE_UP_THS, 0x02); // Set wake-up threshold
  myIMU.writeRegister(LSM6DS3_ACC_GYRO_TAP_CFG1, 0x80);    // Enable interrupts and apply slope filter; latch mode disabled
  myIMU.writeRegister(LSM6DS3_ACC_GYRO_CTRL1_XL, 0x70);    // Turn on the accelerometer
                                                           // ODR_XL = 833 Hz, FS_XL = ±2 g
  delay(4);                                                // Delay time per application note
  myIMU.writeRegister(LSM6DS3_ACC_GYRO_CTRL1_XL, 0xB0);    // ODR_XL = 1.6 Hz
  myIMU.writeRegister(LSM6DS3_ACC_GYRO_CTRL6_G, 0x10);     // High-performance operating mode disabled for accelerometer
  myIMU.writeRegister(LSM6DS3_ACC_GYRO_MD1_CFG, 0x20);     // Wake-up interrupt driven to INT1 pin

  // Set up the sense mechanism to generate the DETECT signal to wake from system_off
  pinMode(PIN_LSM6DS3TR_C_INT1, INPUT_PULLDOWN_SENSE);

  return;
}

void setup()
{
  //Serial.begin(9600); // open the serial port at 9600 bps:

  digitalWrite(LED_RED, LOW);
  digitalWrite(LED_GREEN, HIGH);

  setupAccelerometer();

  // Minimizes power when bluetooth is used
  NRF_POWER->DCDCEN = 1;


  Bluefruit.begin();
  Bluefruit.setName("RUNSTREAM002");

  // Set the connect/disconnect callback handlers
  Bluefruit.Periph.setConnectCallback(connect_callback);
  Bluefruit.Periph.setDisconnectCallback(disconnect_callback);

  setupRSC();
  startAdv();
}

void startAdv(void)
{
  // Advertising packet
  Bluefruit.Advertising.addFlags(BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE);
  Bluefruit.Advertising.addTxPower();
  Bluefruit.autoConnLed(false);

  Bluefruit.Advertising.addService(RSC);

  // Include Name
  Bluefruit.Advertising.addName();
  
  /* Start Advertising
   * - Enable auto advertising if disconnected
   * - Interval:  fast mode = 20 ms, slow mode = 152.5 ms
   * - Timeout for fast mode is 30 seconds
   * - Start(timeout) with timeout = 0 will advertise forever (until connected)
   * 
   * For recommended advertising interval
   * https://developer.apple.com/library/content/qa/qa1931/_index.html   
   */
  Bluefruit.Advertising.restartOnDisconnect(true);
  Bluefruit.Advertising.setInterval(32, 244);    // in unit of 0.625 ms
  Bluefruit.Advertising.setFastTimeout(30);      // number of seconds in fast mode
  Bluefruit.Advertising.start(0);                // 0 = Don't stop advertising after n seconds  
}


void setupRSC(void)
{
 
  RSC.begin();

  RSCcharac.setProperties(CHR_PROPS_NOTIFY);
  RSCcharac.setPermission(SECMODE_OPEN, SECMODE_NO_ACCESS);
  RSCcharac.setFixedLen(8);
  RSCcharac.setCccdWriteCallback(cccd_callback);  // Optionally capture CCCD updates
  RSCcharac.begin();
}

void connect_callback(uint16_t conn_handle)
{

  CONNECTEDtoble = true;
  // Get the reference to current connection
  BLEConnection* connection = Bluefruit.Connection(conn_handle);

  char central_name[32] = { 0 };
  connection->getPeerName(central_name, sizeof(central_name));


  digitalWrite(LED_GREEN, LOW);
  digitalWrite(LED_RED, HIGH);
}
/**
 * Callback invoked when a connection is dropped
 * @param conn_handle connection where this event happens
 * @param reason is a BLE_HCI_STATUS_CODE which can be found in ble_hci.h
 */
void disconnect_callback(uint16_t conn_handle, uint8_t reason)
{
  digitalWrite(LED_GREEN, HIGH);
  digitalWrite(LED_RED, LOW);

  CONNECTEDtoble = false;
  (void) conn_handle;
  (void) reason;


}

void cccd_callback(uint16_t conn_hdl, BLECharacteristic* chr, uint16_t cccd_value)
{

    // Check the characteristic this CCCD update is associated with in case
    // this handler is used for multiple CCCD records.
    if (chr->uuid == RSCcharac.uuid) {
        if (chr->notifyEnabled(conn_hdl)) {
            //Serial.println("Heart Rate Measurement 'Notify' enabled");
        } else {
            //Serial.println("Heart Rate Measurement 'Notify' disabled");
        }
    }
}


void loop()
{

     delay(10);
    // goToPowerOff();


  if(CONNECTEDtoble == true){

    previousMillisSLEEP = currentMillisSLEEP;




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

    accX = myIMU.readFloatAccelX();
    accY = myIMU.readFloatAccelY();
    accZ = myIMU.readFloatAccelZ();
    gX = myIMU.readFloatGyroX();
    gY = myIMU.readFloatGyroY();
    gZ = myIMU.readFloatGyroZ();

    //long magnitudeAcc = ((accX * accX) * (accY * accY) * (accZ * accZ));
    //long magnitudeGyro = ((gX * gX) * (gY * gY) * (gZ * gZ));

    countdetect++;
    
    // subtract the last reading:
    _sampleTotal = _sampleTotal - _samples[_curReadIndex];
  
    // read the sensor value
    _samples[_curReadIndex] = gY;
  
    // add the reading to the total:
    _sampleTotal = _sampleTotal + _samples[_curReadIndex];
  
    // advance to the next position in the array:
    _curReadIndex = _curReadIndex + 1;

    // if we're at the end of the array...
    if (_curReadIndex >= SMOOTHING_WINDOW_SIZE) {
          // ...wrap around to the beginning:
      _curReadIndex = 0;
    }

       // calculate the average:
    _sampleAvg = _sampleTotal / SMOOTHING_WINDOW_SIZE;
  

        if (xbool == false && _sampleAvg < -170   ) 
        {
           xbool = true;

        } 
        else if (xbool == true && _sampleAvg > -170   ) 
        {
              xbool=false;
              currentMillisSTEPS = millis();
              changeInMilllisSTEPS = currentMillisSTEPS - previousMillisSTEPS;
              updateSteps();
              previousMillisSTEPS = currentMillisSTEPS;
              //Serial.print("A SIGNIFICANT EVENT*******!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
        }


      currentMillisSTEPS = millis();
      changeInMilllisSTEPS = currentMillisSTEPS - previousMillisSTEPS;

  



        if (changeInMilllisSTEPS > 3000) 
        {
           cadence = 0;
           displayData();
           previousMillisSTEPS = currentMillisSTEPS;
        }
  }//end if connected
 

  long currentMillisSLEEP = millis();

  // if 1800000ms (3 minutes) have passed, check the steps level:
  if ((currentMillisSLEEP - previousMillisSLEEP >= 60000) && CONNECTEDtoble==false) {

      goToPowerOff();
  }



 
}


// //https://gitlab.utu.fi/tujupan/DTEK8081_source_codes/-/blob/15585c783735d98663ada2352cd9dc0f8c61e880/examples/ble_peripheral/ble_app_rscs/main.c
void updateSteps() {
  //     bool        is_inst_stride_len_present;                                 /**< True if Instantaneous Stride Length is present in the measurement. */
  //     bool        is_total_distance_present;                                  /**< True if Total Distance is present in the measurement. */
  //     bool        is_running;                                                 /**< True if running, False if walking. */
  //     uint16_t    inst_speed;                                                 /**< Instantaneous Speed. */
  //     uint8_t     inst_cadence;                                               /**< Instantaneous Cadence. */
  //     uint16_t    inst_stride_length;                                         /**< Instantaneous Stride Length. */
  //     uint32_t    total_distance;


  if (UPDATED == false) {
    UPDATED = true;
    cadence = 60000 / changeInMilllisSTEPS;

    if(cadence>(previouscadence*2) && previouscadence>10){
      //do nothing
    }else{


              if(stepcountforcadenceaverage >= 3)
              {
                cadence = totalcandenceforaverage/stepcountforcadenceaverage;
                stepcountforcadenceaverage = 0;
                totalcandenceforaverage = 0;
                displayData();

              }
              else
              {
                stepcountforcadenceaverage = stepcountforcadenceaverage+1;
                totalcandenceforaverage = totalcandenceforaverage +cadence;    
              }

        previouscadence=cadence;

    }

  }

  if (UPDATED == true) {
    delay(100);
    UPDATED = false;
  }
}

void displayData() {

  int running;
  if (cadence > 70) {
    running = 3;
  } else if (cadence > 60) {
    running = 2;
  } else if (cadence > 0) {
    running = 1;
  } else {
    running = 2;
  }
 
  uint8_t data[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };

  //static uint8_t data[4] = {0, 0, 0, 0};
  data[0] = 3;  //location. of foot. pod
  //data[1] = 0; // lowByte(CurrentSpeed);
  data[2] = running;  //0 nothing 1=walk 2=running
  data[3] = cadence;  //step count
  //data[4] = 0;
  data[5] = 0;  //always 0
  //data[6] = 0;
  data[7] = stepCount / 10;

  RSCcharac.notify(data, 8);  //convert number to hex);  // and update the stepcount level characteristic
}



void goToPowerOff()
{

  digitalWrite(LED_GREEN, HIGH);
  digitalWrite(LED_RED, HIGH);
  // Setup up double tap interrupt to wake back up
  setupWakeUpInterrupt();

  NRF_POWER->SYSTEMOFF=1; // Execution should not go beyond this
}
1 Like