Xiao Sense Accelerometer Examples and Low Power

@aaronfrom just wondering how you got on with making BLE sketches low power, I didn’t have any luck with @faizannazir code, need to dig into it a bit more. will post where i’m at shortly.
thanks

Hi,
I used non-mbed boards and was able to get this BLE foot step tracker (located on your foot) to work. I tested it and in deep sleep mode it uses 48 microamps and ~3 milliamps when awake. It wakes up with double tap. It runs on a rechargeable Lithium CR2020 battery. It works perfectly at first but, after sleeping for a few days it wont wake up again and I don’t know why. Hope this helps, and if you can figure out why it won’t wake up again please let me know.

PS sorry the code is not very pretty…

////##############################################################################
////#Seeed nRF52 Boards ----- !!!!!!!!!  ONLY VERSION 1.1.5 WORKS  !!!!!!!!!
/////https://files.seeedstudio.com/arduino/package_seeeduino_boards_index.json
////##############################################################################
//#include <Adafruit_SPIFlash.h>
#include <LSM6DS3.h>
#include <nrf52840.h>
#include <Wire.h>

#include <bluefruit.h>
#include <Adafruit_LittleFS.h>
#include <InternalFileSystem.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;

long stopcount = 0;

long stepONE = 0;
long stepTWO = 0;
long stepTHREE = 0;
long stepFOUR = 0;
long stepFIVE = 0;
long stepSIX = 0;
long stepSEVEN = 0;
long stepEIGHT = 0;

long intervalONE = 0;
long intervalTWO = 0;
long intervalTHREE = 0;
long intervalFOUR = 0;
long intervalFIVE = 0;
long intervalSIX = 0;
long intervalSEVEN = 0;
long intervalEIGHT = 0;


float accX, accY, accZ, gX, gY, gZ;
const float accelerationThreshold = 26;  // threshold of significant in G's
int stepsTaken = 0;
int stepCount = 0;
int STOPcount = 0;
bool STEP = false;
bool UPDATED = false;
int updateClearCount = 0;
long currentMillisSTEPS = 0;
long changeInMilllisSTEPS = 0;
long previousMilllisFOOTONGROUND = 0;
long changeInMilllisFOOTONGROUND = 0;
long STARTMilllisSWING = 0;
long ENDMilllisSWING = 0;
long changeMilllisSWING = 0;

int stepcountforcadenceaverage = 0;
int totalcandenceforaverage = 0;

bool stepup = false;
bool topofstep = false;
bool xbool = false;
bool ybool = false;
bool zbool = false;
int countdetect = 0;
long totalmagnitude = 0;
int swingcount = 0;

const int SMOOTHING_WINDOW_SIZE = 80; // 25 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;

/* HRM Service Definitions
 * Heart Rate Monitor Service:  0x180D
 * Heart Rate Measurement Char: 0x2A37
 * Body Sensor Location Char:   0x2A38
 */
BLEService        hrms = BLEService(UUID16_SVC_HEART_RATE);
BLECharacteristic hrmc = BLECharacteristic(UUID16_CHR_HEART_RATE_MEASUREMENT);
BLECharacteristic bslc = BLECharacteristic(UUID16_CHR_BODY_SENSOR_LOCATION);
BLEService        RSC  = BLEService("1814");
BLECharacteristic RSCcharac = BLECharacteristic("2A53");

                 // remote clients will be able to get notifications if this characteristic changes

BLEDis bledis;    // DIS (Device Information Service) helper class instance
BLEBas blebas;    // BAS (Battery Service) helper class instance

uint8_t  bps = 0;



//Adafruit_FlashTransport_QSPI flashTransport;
//LSM6DS3 myIMU; // Default is I2C mode
//Create a instance of class LSM6DS3



// void QSPIF_sleep(void)
// {
//   flashTransport.begin();
//   flashTransport.runCommand(0xB9);
//   flashTransport.end();
// }

void setupWakeUpInterrupt()
{
  // 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
*/

 /* Values that use the highest low power setting (52Hz) with resulting physical tap detection parameters that are
  * close as possible to the application note values.
  */
  
  myIMU.writeRegister(LSM6DS3_ACC_GYRO_CTRL1_XL, 0x30);    // Turn on the accelerometer
                                                           // ODR_XL = 52 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, 0x10);    // 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_CTRL6_G, 0x10);     // High-performance operating mode disabled for accelerometer
  myIMU.writeRegister(LSM6DS3_ACC_GYRO_MD1_CFG, 0x08);     // Double-tap interrupt driven to INT1 pin

  // Set up the sense mechanism to generate the DETECT signal to wake from system_off
  // No need to attach a handler, if just waking with the GPIO input.
  pinMode(PIN_LSM6DS3TR_C_INT1, INPUT_PULLDOWN_SENSE);

  return;
}

void setup()
{
  pinMode(LED_RED, OUTPUT);
  pinMode(LED_GREEN, OUTPUT);
  
  digitalWrite(LED_RED, LOW);
  digitalWrite(LED_GREEN, HIGH);

  // //QSPIF_sleep();
      // if (myIMU.begin() != 0) {
      //  Serial.println("Device error");
      // } else {
      //  Serial.println("aX,aY,aZ,gX,gY,gZ");
      // }

  ////////////////////////////////////////////////////////////////////
  //RED LIGHT TURNS ON WHEN THE DEVICE IS ON
  ////////////////////////////////////////////////////////////////////

  // Flash green to see power on, reset, and wake from system_off
  //digitalWrite(LED_RED, LOW);
  //delay(1000);
  //digitalWrite(LED_GREEN, HIGH);;

  // Minimizes power when bluetooth is used
  NRF_POWER->DCDCEN = 1;
  NRF_POWER->TASKS_LOWPWR = 1;
  uint32_t dcdcen = NRF_POWER->DCDCEN;
  // Enter LOWPWR mode (LOWPWR is recommended for most applications)
  //NRF_POWER->TASKS_LOWPWR = 1;
  

// Initialise the Bluefruit module
  //Serial.println("Initialise the Bluefruit nRF52 module");
  Bluefruit.begin();


  Bluefruit.setName("RUNSTREAM002");





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

  ///////////////////// Configure and Start the Device Information Service
  //Serial.println("Configuring the Device Information Service");
  //bledis.setManufacturer("Cyborg Syndicate, LLC");
  //bledis.setModel("RUNSTREAM Footpod");

  //bledis.begin();

  // Start the BLE Battery Service and set it to 100%
  //Serial.println("Configuring the Battery Service");
  blebas.begin();

  //const int VOL_PIN = A0;

  //enable battery measuring
  //pinMode(VBAT_ENABLE, OUTPUT);
//  pinMode(A6, INPUT);
////  int battery = analogRead(A6);
  //int batteryLevel = map(battery, 0, 1023, 0, 100);
  //int value = analogRead( VOL_PIN );
  //int volt = value * 3.3 / 1023.0;


    // Battery Level setup
  //pinMode(P0_31, OUTPUT);
  //digitalWrite(P0_31, LOW);
  //pinMode(VBAT_ENABLE, OUTPUT);
  pinMode(PIN_VBAT, INPUT);
  digitalWrite(PIN_VBAT, LOW);
  const double vRef = 3.3; // Assumes 3.3V regulator output is ADC reference voltage
  const unsigned int numReadings = 1024; // 10-bit ADC readings 0-1023, so the factor is 1024
  unsigned int adcCount = analogRead(PIN_VBAT);
  double adcVoltage = (adcCount * vRef) / numReadings;
  double vBat = adcVoltage*1510.0/510.0; // Voltage divider from Vbat to ADC
  double batlife = (adcVoltage/3.3)*100;

  //printf("adcCount = %3u = 0x%03X, adcVoltage = %.3fV, vBat = %.3f\n",
  //           adcCount, adcCount, adcVoltage, vBat);
  //int Vadc = analogRead(VBAT_ENABLE);

  //float batteryLevel = ((510e3 + 1000e3) / 510e3) * 2.4 * Vadc / 4096;

   //digitalWrite(VBAT_ENABLE, LOW);
  blebas.write(batlife);

  // Setup the Heart Rate Monitor service using
  // BLEService and BLECharacteristic classes
  //Serial.println("Configuring the Heart Rate Monitor Service");
  //setupHRM();

  setupRSC();


  // Setup the advertising packet(s)
  //Serial.println("Setting up the advertising payload(s)");
  startAdv();

  //Serial.println("Ready Player One!!!");
  //Serial.println("\nAdvertising");
}



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


  // Include HRM Service UUID
  //Bluefruit.Advertising.addService(hrms);


  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 loop()
{
      //Serial.println("main loop looping");

  // FreeRTOS will automatically put the system in system_on sleep mode here
  //delay(180000); // Measure idle loop power during this period

  
  //ALL LIGHTS TURN OFF JUST BEFORE DEVICE GOES TO SLEEP
  // Flash red before system_off sleep
  // digitalWrite(LED_RED, LOW);
  // delay(1000);
  // digitalWrite(LED_RED, HIGH);


  if(CONNECTEDtoble == true){

  //if ( Bluefruit.connected() ) {
    // pinMode(PIN_VBAT, INPUT);
    // digitalWrite(PIN_VBAT, LOW);
    // const double vRef = 3.3; // Assumes 3.3V regulator output is ADC reference voltage
    // const unsigned int numReadings = 1024; // 10-bit ADC readings 0-1023, so the factor is 1024
    // unsigned int adcCount = analogRead(PIN_VBAT);
    // double adcVoltage = (adcCount * vRef) / numReadings;
    // double vBat = adcVoltage*1510.0/510.0; // Voltage divider from Vbat to ADC
    // double batlife = (adcVoltage/3.3)*100;
    // printf("adcCount = %3u = 0x%03X, adcVoltage = %.3fV, vBat = %.3f, balife = %.1f\n",
    //          adcCount, adcCount, adcVoltage, vBat, batlife);
    // Serial.println(" connected ");

    //uint8_t hrmdata[2] = { 0b00000110, bps++ };           // Sensor connected, increment BPS value
    
    //CONNECTEDtoble = true;
    // Note: We use .notify instead of .write!
    // If it is connected but CCCD is not enabled
    // The characteristic's value is still updated although notification is not sent
    // if ( hrmc.notify(hrmdata, sizeof(hrmdata)) ){
    //   Serial.print("Heart Rate Measurement updated to: "); Serial.println(bps); 
    // }else{
    //   Serial.println("ERROR: Notify not set in the CCCD or not connected!");
    // }

    //reset sleep timer
    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++;
     /*
      Serial.print(accX);
      Serial.print(" ");
      Serial.print(accY);
      Serial.print(" ");   
      Serial.print(accZ);
      Serial.print(" ");
      Serial.print(gX);
      Serial.print(" ");
      Serial.print(gY);
      Serial.print(" ");
      Serial.print(gZ);
      Serial.print(" ");
      Serial.print(magnitudeAcc);
      Serial.print(" ");
      Serial.print(magnitudeGyro);
      Serial.print(" ");
      Serial.print(_sampleAvg);
      Serial.println(" ");
     */
       // 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();
  }

  // Only send update once per second
  //delay(10);


}//end loop



void setupHRM(void)
{
  // Configure the Heart Rate Monitor service
  // See: https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.service.heart_rate.xml
  // Supported Characteristics:
  // Name                         UUID    Requirement Properties
  // ---------------------------- ------  ----------- ----------
  // Heart Rate Measurement       0x2A37  Mandatory   Notify
  // Body Sensor Location         0x2A38  Optional    Read
  // Heart Rate Control Point     0x2A39  Conditional Write       <-- Not used here
  hrms.begin();

  // Note: You must call .begin() on the BLEService before calling .begin() on
  // any characteristic(s) within that service definition.. Calling .begin() on
  // a BLECharacteristic will cause it to be added to the last BLEService that
  // was 'begin()'ed!

  // Configure the Heart Rate Measurement characteristic
  // See: https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.characteristic.heart_rate_measurement.xml
  // Properties = Notify
  // Min Len    = 1
  // Max Len    = 8
  //    B0      = UINT8  - Flag (MANDATORY)
  //      b5:7  = Reserved
  //      b4    = RR-Internal (0 = Not present, 1 = Present)
  //      b3    = Energy expended status (0 = Not present, 1 = Present)
  //      b1:2  = Sensor contact status (0+1 = Not supported, 2 = Supported but contact not detected, 3 = Supported and detected)
  //      b0    = Value format (0 = UINT8, 1 = UINT16)
  //    B1      = UINT8  - 8-bit heart rate measurement value in BPM
  //    B2:3    = UINT16 - 16-bit heart rate measurement value in BPM
  //    B4:5    = UINT16 - Energy expended in joules
  //    B6:7    = UINT16 - RR Internal (1/1024 second resolution)
  hrmc.setProperties(CHR_PROPS_NOTIFY);
  hrmc.setPermission(SECMODE_OPEN, SECMODE_NO_ACCESS);
  hrmc.setFixedLen(2);
  //hrmc.setCccdWriteCallback(cccd_callback);  // Optionally capture CCCD updates
  hrmc.begin();
  uint8_t hrmdata[2] = { 0b00000110, 0x40 }; // Set the characteristic to use 8-bit values, with the sensor connected and detected
  hrmc.write(hrmdata, 2);

  // Configure the Body Sensor Location characteristic
  // See: https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.characteristic.body_sensor_location.xml
  // Properties = Read
  // Min Len    = 1
  // Max Len    = 1
  //    B0      = UINT8 - Body Sensor Location
  //      0     = Other
  //      1     = Chest
  //      2     = Wrist
  //      3     = Finger
  //      4     = Hand
  //      5     = Ear Lobe
  //      6     = Foot
  //      7:255 = Reserved
  bslc.setProperties(CHR_PROPS_READ);
  bslc.setPermission(SECMODE_OPEN, SECMODE_NO_ACCESS);
  bslc.setFixedLen(1);
  bslc.begin();
  bslc.write8(2);    // Set the characteristic to 'Wrist' (2)
}


void setupRSC(void)
{
  // Configure the Heart Rate Monitor service
  // See: https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.service.heart_rate.xml
  // Supported Characteristics:
  // Name                         UUID    Requirement Properties
  // ---------------------------- ------  ----------- ----------
  // Heart Rate Measurement       0x2A37  Mandatory   Notify
  // Body Sensor Location         0x2A38  Optional    Read
  // Heart Rate Control Point     0x2A39  Conditional Write       <-- Not used here
  RSC.begin();

  // Note: You must call .begin() on the BLEService before calling .begin() on
  // any characteristic(s) within that service definition.. Calling .begin() on
  // a BLECharacteristic will cause it to be added to the last BLEService that
  // was 'begin()'ed!

  // Configure the Heart Rate Measurement characteristic
  // See: https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.characteristic.heart_rate_measurement.xml
  // Properties = Notify
  // Min Len    = 1
  // Max Len    = 8
  //    B0      = UINT8  - Flag (MANDATORY)
  //      b5:7  = Reserved
  //      b4    = RR-Internal (0 = Not present, 1 = Present)
  //      b3    = Energy expended status (0 = Not present, 1 = Present)
  //      b1:2  = Sensor contact status (0+1 = Not supported, 2 = Supported but contact not detected, 3 = Supported and detected)
  //      b0    = Value format (0 = UINT8, 1 = UINT16)
  //    B1      = UINT8  - 8-bit heart rate measurement value in BPM
  //    B2:3    = UINT16 - 16-bit heart rate measurement value in BPM
  //    B4:5    = UINT16 - Energy expended in joules
  //    B6:7    = UINT16 - RR Internal (1/1024 second resolution)
  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();
  // uint8_t hrmdata[2] = { 0b00000110, 0x40 }; // Set the characteristic to use 8-bit values, with the sensor connected and detected
  // RSCcharac.write(hrmdata, 2);

 

}

void connect_callback(uint16_t conn_handle)
{

  myIMU.begin();

  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));

  //Serial.print("Connected to ");
  //Serial.println(central_name);

  digitalWrite(LED_GREEN, HIGH);
  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)
{
  myIMU.settings.gyroEnabled = 0;
  myIMU.settings.accelEnabled = 0;

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

  //Serial.print("Disconnected, reason = 0x"); Serial.println(reason, HEX);
  //Serial.println("Advertising!");

  //digitalWrite(LED_GREEN, HIGH);
  //digitalWrite(LED_RED, HIGH);
}

void cccd_callback(uint16_t conn_hdl, BLECharacteristic* chr, uint16_t cccd_value)
{
    // Display the raw request packet
    //Serial.print("CCCD Updated: ");
    //Serial.printBuffer(request->data, request->len);
    //Serial.print(cccd_value);
    //Serial.println("");

    // 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");
        }
    }
}

//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;
        //Serial.print("steps taken: ");
        //Serial.println(stepsTaken);
        //Serial.print("delta time: ");
        //Serial.println(changeInMilllisSTEPS);
    }

  }




  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;
  }
  //byte data[4] = {0, 0, 0, 0};
  //byte data[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
  //uint8_t data[8] = { 0b00000110, 0x40 }; // Set the characteristic to use 8-bit values, with the sensor connected and detected
  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;

  //RSCchar.setValue(data, 4);
  //RSCchar->writeValue(data, 8);
  //RSCcharac.writeValue(data, 8);  //convert number to hex);  // and update the stepcount level characteristic
  //RSCchar.readValue(data,8); //convert number to hex);  // and update the stepcount level characteristic
  RSCcharac.notify(data, 8);  //convert number to hex);  // and update the stepcount level characteristic

  // RSCchar.setValue(data, 8);
  //  RSCchar.notify();
  //Serial.print("Cadence: ");
  //Serial.println(cadence);
}



void goToPowerOff() {
  Bluefruit.Advertising.stop();


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

  // Setup up double tap interrupt to wake back up
  setupWakeUpInterrupt();

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

}


Hi there,
So I’ve compiled and run your sketch and with the Power Profiler II and the Nrf_connect for desktop BLE, I see when powered on it’s advertising and connectable AOK. However The PPKII shows the sleep is no-where near the posted numbers. I still see 20ma. during sleep? (see pics)


after a minute(60-sec) , BLE Scan shows it goes offline. not connectable.

So I’m betting the Battery is Dead or below a functional level.
here is the Wake_Up which takes more than a double tap BTW,
It does register a step and the BLE notify works sending the data.


the PPK shows it’s never reaching deep sleep.


I’m using a CR2450 coin cell FYI.
I think maybe when you sleep , you need to change the IMU from STEP cadence to ONLY Double-Tap awakable? if that make sense. perhaps it will be lower, YMMV
HTH
GL :slight_smile: PJ :v:

PS. Battery Monitor Service Appears to work well.
I went back and loaded BSP 1.1.5 and same but slightly lower Power with that, still not great!
It’s running for a day , see if it’s alive tonight?

I know this thread is somewhat old. But I figure you or others might have some input here. In lieu of using a physical tilt switch interrupt on a GPIO pin, I am looking at using the accelerometer to interrupt sleep on a specific “tilt” angle (range). In this case, using X axis if it exceeds a specific range (e.g wake when X > ,.2). For reference this is a title project to wake up the nrf52840 sense when a handle opens a valve (tilt degree). and measure the time duration until back to the off position < .2 I also need to include background vibrations not causing a false wakeup (think debounce) but I think I can avoid it simply by changing the range (e.g. .25 in stead of .2)

Hi there,
Sure, The tilt switch can be a back up or limit max tilt switch. The Accelerometer is capable of Detecting Tilt in one or more planes, Using a Filter will give you just YAW and Pitch as well.
You can look at the orientation portion of the demo code on here for Wake up from Sleep on Motion or Fall.
HTH
GL :slight_smile: PJ :v:

Ok thanks. I don’t want to wake up unless I’m + 2-4 degrees off TDC. So I’m assuming the filters are the threshold for a wake Interrupt.

I’m still working with that XAIO touch display - and pretty well migrated all my code to non-mbed. Haven’t got the Arduino GFX working vs the TFTeSPI library (using the lvgl for touch using square line studio)

Hi there,
AFAIK , the kalman filter I think it is , is used to take the raw accelerometer & gyro data and give back the Pitch and Yaw. you calculate the tilt angle. There is no Magnetometer so it’s not an IMU* persay. , The thresholds basically set sensitivities etc. and generate the interrupt when data is available. But Generally,
Accelerometers can be used for measuring both dynamic and static measurements of
acceleration. Tilt is a static measurement where gravity is the acceleration being measured.
The highest degree resolution of a tilt measurement, a low-g, high sensitivity accelerometer is desired.
The Xiao has the “LSM6DS3TR-C” capable of Significant motion and tilt function according to data sheet. So most of the work is done for you. accelerometers are good
solutions for XY and XYZ tilt sensing and will experience
acceleration in the range of +1g to -1g as the device is tilted
from -90 degrees to +90 degrees. based on 1g = 9.8 m/s

How you mount it will be key. Check the demo on Park Demo on here, the orientation portion
can be used to show the tilt angle , then just set the interrupt to occur if it’s exceeded.
for a door handle the Y is fixed, more or less, to the origin. So the X axis vs, the ground plane or my choice , I think LOL :thinking:Z would do it.

like this

noting the position of the IMU* on the Xiao.(page 18) LSM6DS3TR-C
ps from pg 17 By default, the AWT algorithm is applied to the positive X-axis.
HTH
GL :slight_smile: PJ :v:

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
}

Cool. I wrote some code the PUC which also has an accelerometer for this specific use case. Didnt have a sleep/wake per se. I will be mounting consistently on one of the axis’s as it till sit in a handle. This way I will only need to focus on one axis not 3. Makes life way easier and I will mount it on a axis that top dead center

1 Like

Hi there,
Cool, Whichever you choose , just keep in mind the Acceleration moment is on the Axis to Gravity Towards the ground. You get it no problem.
GL :slight_smile: PJ

1 Like

So what if I have a couple beers, I bump the wall but don’t fall down :slight_smile:

LOL, Well check out the Motion and Fall demo,
“Impact Detected” by the sudden lack of acceleration and change in direction from +/- for the window defined and the G’s for the Wall Bounce :crazy_face:
The impact detection allows for any axis.
GL :slight_smile: PJ
Everything in Moderation, Too! :v:

1 Like