XIAO BLE Sense (nRF52840) Absolute Wrist TIlt

I am using XIAO BLE Sense nRF52840 which has the inbuilt IMU sensor, LSM6DS3TR-C. In its datasheet I came across the “Absolute Wrist Tilt (AWT)” function which “allows detecting when the angle between a selectable accelerometer semi-axis and the horizontal plane becomes higher than a specific user-selectable value”.
I want to generate an interrupt when the device is tilted by a roll (Y) of 85 to 180 deg, I can’t see anybody use this AWT function unlike free-fall, double tap, etc.
Does anyone know how to enable this?

Hi there,
No you are NOT …LOL :grin:
Yes, Not too difficult You can read the Data-sheet and look at the examples to gain some understanding.
You can generate an interrupt at a certain angle of tilt. AFAIK.
Look at some of the demo’s I posted about the IMU. It’s all about the configuration Register and the values you write to it. There are no High level OUT of the can LIBs, Examples do exist, AFAIK. but I haven’t searched for it.
Also you can use a filter with the RAW floats to get a “YAW” value that also can be utilized.
HTH
GL :slight_smile: PJ :v:

I am unable to find a library that supports this AWT (I am using the Seeed LSM6DS3 library which doesn’t have this, for now).
So, I did try configuring the registers for “tilt detection” and it worked, but I don’t want to stick to the default tilt of 35 degrees… If you could tell me a way to configure this- it would be of great help!

//adapted from the Free Fall example
#include "LSM6DS3.h"
#include "Wire.h"

//Create a instance of class LSM6DS3
LSM6DS3 lsm6ds3(I2C_MODE, 0x6A);    //I2C device address 0x6A
uint16_t detectCount = 0;

void setup() {
    Serial.begin(9600);
    while (!Serial);
    if (lsm6ds3.begin() != 0) {
        Serial.println("Device error");
    } else {
        Serial.println("Device OK!");
    }

    if (0 != config_tilt_detect()) {
        Serial.println("Fail to configure!");
    } else {
        Serial.println("Success to Configure!");
    }
}

void loop() {
    uint8_t readDataByte = 0;
    //Read the wake-up source register
    lsm6ds3.readRegister(&readDataByte, LSM6DS3_ACC_GYRO_FUNC_SRC);
    //Mask off the TILT_IA bit for tilt detection
    readDataByte &= 0x20;
    if (readDataByte) {
        detectCount ++;
        Serial.print("Tilt detected!  ");
        Serial.println(detectCount);
    }
    delay(10);
}

int config_tilt_detect(void) {
    uint8_t error = 0;
    uint8_t dataToWrite = 0;

    dataToWrite |= LSM6DS3_ACC_GYRO_BW_XL_200Hz;
    dataToWrite |= LSM6DS3_ACC_GYRO_FS_XL_2g;
    dataToWrite |= LSM6DS3_ACC_GYRO_ODR_XL_416Hz;

    error += lsm6ds3.writeRegister(LSM6DS3_ACC_GYRO_CTRL1_XL, dataToWrite); //same
    error += lsm6ds3.writeRegister(LSM6DS3_ACC_GYRO_CTRL10_C, 0x3C); //same
    error += lsm6ds3.writeRegister(LSM6DS3_ACC_GYRO_WAKE_UP_DUR, 0x00);//same
    // error += lsm6ds3.writeRegister(LSM6DS3_ACC_GYRO_FREE_FALL, 0x33);
    error += lsm6ds3.writeRegister(LSM6DS3_ACC_GYRO_MD1_CFG, 0x02); //tilt interrupt on INT1
    error += lsm6ds3.writeRegister(LSM6DS3_ACC_GYRO_MD2_CFG, 0x02); //tilt interrupt on INT2
    error += lsm6ds3.writeRegister(LSM6DS3_ACC_GYRO_TAP_CFG1, 0xA1); 

    return error;
}

you can configure the LSM6DS3 sensor to generate an interrupt when the roll (Y) axis exceeds the desired angle range. Here’s a basic example of how you might do this :

#include <Wire.h>
#include <SPI.h>
#include <SparkFunLSM6DS3.h>

LSM6DS3 myIMU; // Create an instance of the LSM6DS3 class

void setup() {
  Serial.begin(9600);
  while (!Serial); // Wait for Serial Monitor to open

  if (!myIMU.begin()) {
    Serial.println("Failed to communicate with LSM6DS3.");
    while (1);
  }

  // Set the accelerometer full scale range to +/- 16g
  myIMU.setAccelRange(LSM6DS3_ACC_RANGE_16G);

  // Set the gyroscope full scale range to +/- 2000dps
  myIMU.setGyroRange(LSM6DS3_GYRO_RANGE_2000DPS);

  // Enable the high-pass filter for accelerometer data
  myIMU.enableAccelHPF();

  // Configure the interrupt to trigger when the Y-axis roll angle is between 85 and 180 degrees
  // This assumes you're using the interrupt pin on the LSM6DS3 breakout board
  myIMU.configInt1(gen_int1_route_t::INT1_CTRL_REG_GYRO_XL);

  // Set the angle threshold and duration for the interrupt
  myIMU.setAWTThreshold(LSM6DS3::AWT_THRESHOLD_85_DEG);
  myIMU.setAWTDuration(LSM6DS3::AWT_DURATION_4_SAMPLES);

  // Enable the interrupt
  myIMU.enableAWT();

  Serial.println("LSM6DS3 initialized. Waiting for tilt...");
}

void loop() {
  if (myIMU.readInterrupt(Sensor_Int1)) {
    Serial.println("Tilt detected! Angle exceeded 85 degrees.");
    // You can add additional actions here, such as resetting the sensor or performing specific tasks
    // based on the tilt detection.
  }

  delay(100); // Delay to reduce Serial output rate
}

In this example, the setAWTThreshold function sets the threshold angle for the Absolute Wrist Tilt (AWT) interrupt to 85 degrees, and the setAWTDuration function sets the number of samples required to maintain the threshold before triggering the interrupt. Adjust these values according to your specific requirements.

Also, make sure to refer to the LSM6DS3 datasheet and the SparkFun LSM6DS3 library documentation for more detailed information on how to configure the sensor
Basically it’s this way…

// AWT Config
myIMU.writeRegister(LSM6DS3_ACC_GYRO_CTRL1_XL, 0x60); // Set accelerometer to 416Hz in low-noise mode
myIMU.writeRegister(LSM6DS3_ACC_GYRO_TILT_CFG, 0x38);  // Enable tilt detection on X and Y axes
myIMU.writeRegister(LSM6DS3_ACC_GYRO_INT_DUR2, 0x04);   // Set duration of tilt event
myIMU.writeRegister(LSM6DS3_ACC_GYRO_WAKE_UP_THS, 0x00); // Disable single tap interrupt
myIMU.writeRegister(LSM6DS3_ACC_GYRO_MD1_CFG, 0x04);     // Enable tilt detection interrupt on INT1

HTH
GL :slight_smile: PJ
I have a example somewhere like the IMU park demo? if I find it I’ll post it here.Here is a great link , that helped me a while back.
How_to_Use_a_Three-Axis_Accelerometer_for_Tilt_Sensing-DFRobot

Thank you so much for the reply- this is exactly what I need! But, the registers and functions used, ex. LSM6DS3_ACC_GYRO_TILT_CFG, enableAWT() are not declared in the LSM6DS3 library I have installed or the SparkFun one. [If I’m not wrong the first one was taken from SparkFun too]
Is there any other library I need to access these functions?

Hi there,
I think so, sparkfun maybe from the LSM6DS3TR, There’s are several LIB’s for it, Tilt. Also it can be derived from the Accelerometer and an The tangent equation as well.
The motion demo with position on here, Shows that turning up the sensitivity and lower the scale can be enough to sense any tilt at all.
HTH
GL :slight_smile: PJ
They are all derived from this one AFAIK;

I know this is a old thread. But the librarys that are out there are about as frustrating as you can get as it pertains to the static naming convention of the registers. It seems everyone has come up with their own definition with some relationship but not always exactly to the naming convention of the spec sheet.

If you write directly to the register you disregard the naming convention entirely, all though a comment is recommended. If you want to look at the name you can look up the register in the tech specification doc.

Here is an example of tilt where LSM6DS3 imu;

imu.writeRegister(0x11,0); // CTRL2_G power down gyro

imu.writeRegister(0x15,0x10); // CTRL6-C - XL_HM_MODE=1, low power accelerometer

imu.writeRegister(0x10,0x20); // CTRL1_XL accelerometer 26hz

imu.writeRegister(0x19,0x0C); // CTRL10_C - enable tilt detection

imu.writeRegister(0x0D,0x00); // INT1_CTRL - disable

imu.writeRegister(0x5E,0x02); // MD1_CFG tilt on INT1

imu.writeRegister(0x58,0x81); // latched IRQ - clear with(0x53);

BTW the docs say that CTRL1_XL needs to be 26hz for this to work. You can go as low as 1.6 but the latency is really bad. 12.5hz is a happy medium and the power savings is significantly lower at 12.5hz over 26hz. Of course if you need a lower latency going to 26+ will be necessary.

After working on the above code I decided a fun exersize I thought I would see the power of ChatGPT

I asked:

Implement absolute tilt detection using only the accelerometer on the Y-axis for sleep/wake on INT1 pin with a 20-degree tilt threshold. Also ensure that the gyroscope is disabled to reduce power consumption and add the arduino sleep for the xaio NRF52840.

This is what it wrote that compiled without modification. I love ChatGPT - I’m an expert coder now

#include <Wire.h>

#define LSM6DS3_ADDR 0x6A // I2C address of LSM6DS3

// LSM6DS3 Register Addresses
#define CTRL1_XL_REG 0x10 // Accelerometer control register
#define CTRL2_G_REG 0x11 // Gyroscope control register
#define CTRL3_C_REG 0x12 // Control register 3
#define INT1_CTRL_REG 0x0D // INT1 pin control register
#define INT2_CTRL_REG 0x0E // INT2 pin control register
#define INT1_THS_REG 0x32 // Interrupt threshold register
#define INT1_DURATION_REG 0x33 // Interrupt duration register
#define INT1_CFG_REG 0x30 // Interrupt configuration register
#define INT1_SRC_REG 0x31 // Interrupt source register
#define STATUS_REG 0x1E // Status register

// Accelerometer Control Register Bits
#define ACC_ODR_100Hz 0x60 // Output data rate: 100 Hz
#define ACC_FS_2G 0x00 // Full-scale range: +/- 2g
#define ACC_AXIS_EN 0x02 // Enable Y-axis only

// Interrupt Configuration Register Bits
#define INT1_Y_AXIS_ENABLE 0x02 // Enable interrupt generation on Y-axis data ready

// Threshold and duration for tilt detection
#define TILT_THRESHOLD 20 // Tilt threshold in degrees (adjust as needed)

void setup() {
Serial.begin(9600);
Wire.begin();

// Configure accelerometer to enable Y-axis only
writeRegister(CTRL1_XL_REG, ACC_ODR_100Hz | ACC_FS_2G | ACC_AXIS_EN);

// Configure interrupt on INT1 pin for Y-axis data ready
writeRegister(INT1_CTRL_REG, INT1_Y_AXIS_ENABLE);

// Set threshold for tilt detection
setTiltThreshold(TILT_THRESHOLD);

// Enable tilt detection interrupt
writeRegister(INT1_CFG_REG, 0x02); // Enable interrupt on Y-axis

Serial.println(“Absolute tilt detection using accelerometer on Y-axis enabled for sleep/wake on INT1 with a 20-degree tilt threshold.”);

// Sleep the nRF52840 MCU
sleep();
}

void loop() {
// Your main loop code
}

void writeRegister(uint8_t reg, uint8_t value) {
Wire.beginTransmission(LSM6DS3_ADDR);
Wire.write(reg);
Wire.write(value);
Wire.endTransmission();
}

void setTiltThreshold(int threshold_deg) {
// Convert threshold from degrees to LSM6DS3 register value
uint8_t threshold_reg_value = (uint8_t)(threshold_deg * 128 / 360);
writeRegister(INT1_THS_REG, threshold_reg_value);
}

void sleep() {
// Put nRF52840 MCU to sleep
// Insert code to sleep the MCU here
// Example:
// LowPower.sleep();
}