IMU generated interrupt servicing on XIAO BLE sense nrf52840 with mbed-enabled

I’m really struggling with triggering on IMU generated interrupts.

It seems that I am setting up the interrupt on the IMU correctly since the interrupt pin on the nrf is going up and down when I read it in the loop instead of in the ISR. However, the ISR appears to never be triggered or it’s otherwise stuck.

I initially started with sending data out using BLE and thought that I somehow encountered this bug: Only one pin interrupt supported · Issue #7 · arduino/ArduinoCore-nRF528x-mbedos · GitHub, so I went back to basing the ISR on the HighLevelExample and eventually even to the LowLevelExample to isolate the issue, attached below. I couldn’t even light up an LED in the ISR. Any ideas what’s wrong?

#include "LSM6DS3.h"
#include "Wire.h"
//#include "SPI.h"

uint16_t errorsAndWarnings = 0;
int16_t gyroX, gyroY, gyroZ, accX, accY, accZ;

//Create instance of LSM6DS3Core
LSM6DS3Core myIMU(I2C_MODE, 0x6A);    //I2C device address 0x6A
#define int1Pin PIN_LSM6DS3TR_C_INT1

void setup() {
    //Init Serial port
    while (!Serial);

    //Call .beginCore() to configure the IMU
    if (myIMU.beginCore() != 0) {
        Serial.print("\nDevice Error.\n");
    } else {
        Serial.print("\nDevice OK.\n");

    uint8_t dataToWrite = 0;  //Temporary variable

    //Setup the accelerometer******************************
    dataToWrite = 0; //Start Fresh!
//    dataToWrite |= LSM6DS3_ACC_GYRO_BW_XL_50Hz;
//    dataToWrite |= LSM6DS3_ACC_GYRO_FS_XL_8g;
    dataToWrite |= LSM6DS3_ACC_GYRO_ODR_XL_13Hz;

    //Now, write the patched together data
    errorsAndWarnings += myIMU.writeRegister(LSM6DS3_ACC_GYRO_CTRL1_XL, dataToWrite);

    //Set the ODR bit
    //errorsAndWarnings += myIMU.readRegister(&dataToWrite, LSM6DS3_ACC_GYRO_CTRL4_C);
    //dataToWrite &= ~((uint8_t)LSM6DS3_ACC_GYRO_BW_SCAL_ODR_ENABLED);
    errorsAndWarnings += myIMU.writeRegister(LSM6DS3_ACC_GYRO_INT1_CTRL, LSM6DS3_ACC_GYRO_INT1_DRDY_XL_ENABLED);

    Serial.println("Attaching IMU interrupt ...");
    pinMode(int1Pin, INPUT);
    attachInterrupt(digitalPinToInterrupt(int1Pin), interruptService, RISING);
 //   interrupts();

void loop() {
    //Get all parameters
    Serial.print("\nAccelerometer Counts:\n");
/*    //Acelerometer axis X
    if (myIMU.readRegisterInt16(&gyroX, LSM6DS3_ACC_GYRO_OUTX_L_XL) != 0) {
    Serial.print(" X = ");

    //Acelerometer axis Y
    Serial.print(" Y = ");

    //Acelerometer axis Z
    Serial.print(" Z = ");

    Serial.print("Total reported Errors and Warnings: ");

//    while(1);

void interruptService () {
//    digitalWrite(LED_BUILTIN, HIGH);
//    detachInterrupt(digitalPinToInterrupt(int1Pin));
    //Acelerometer axis X
    if (myIMU.readRegisterInt16(&gyroX, LSM6DS3_ACC_GYRO_OUTX_L_XL) != 0) {

    //Acelerometer axis Y
    if (myIMU.readRegisterInt16(&gyroY, LSM6DS3_ACC_GYRO_OUTY_L_XL) != 0) {

    //Acelerometer axis Z
    if (myIMU.readRegisterInt16(&gyroZ, LSM6DS3_ACC_GYRO_OUTZ_L_XL) != 0) {

I too had some understanding to gain, I use this and it helped,
I’m thinking the interrupt isn’t getting the right configuration or the sensitivity is too LOW.
The built in LED wasn’t defined?

// IMU Interrupt Example for XIAO BLE Sense
// This example shows how to configure LMSD6S3TR-C on XIAO BLE SENSE to interrupt
// on INT1 after a "Double Tap" was recognized.
// Additionally, the device goes into System OFF state, after 5 interrupts were
// received. Another "Double Tap" will wake up the device again.
// Original by chuck
//poatched and toasted by pjg

#include "LSM6DS3.h"
#include "Wire.h"
#include <U8x8lib.h>
LSM6DS3 myIMU(I2C_MODE, 0x6A); // IMU 
#define int1Pin PIN_LSM6DS3TR_C_INT1

U8X8_SSD1306_128X64_NONAME_HW_I2C u8x8(/* clock=*/ PIN_WIRE_SCL, /* data=*/ PIN_WIRE_SDA, /* reset=*/ U8X8_PIN_NONE);
 // OLEDs without Reset of the Display

const int buttonPin = 1;     // the number of the pushbutton pin
int buttonState = 0;         // variable for reading the pushbutton status
int BuzzerPin = A3;
uint8_t interruptCount = 0; // Amount of received interrupts
uint8_t prevInterruptCount = 0; // Interrupt Counter from last loop

void setup() {
	  delay(1000); //relax...
	  Serial.println("Processor came out of reset.\n");
    u8x8.setFlipMode(1);   // set number from 1 to 3, the screen word will rotary 180
    pinMode(LED_BUILTIN, OUTPUT);// initialize the LED pin as an output:
    pinMode(buttonPin, INPUT_PULLUP);// initialize the pushbutton pin as an input:
    pinMode(BuzzerPin, OUTPUT);
    pinMode(LEDR, OUTPUT);
    pinMode(LEDG, OUTPUT);
    pinMode(LEDB, OUTPUT);
    setLedRGB(false, false, true); // set blue led

    myIMU.settings.gyroEnabled = 0; // Gyro currently not used, disabled to save power 
    if (myIMU.begin() != 0) {
        Serial.println("IMU error");
    } else {
        Serial.println("IMU OK!");
    pinMode(int1Pin, INPUT);
    attachInterrupt(digitalPinToInterrupt(int1Pin), int1ISR, RISING);
    u8x8.setCursor(4, 0);
    u8x8.setCursor(5, 3);
    u8x8.setCursor(1, 6);

void loop() {
    setLedRGB(false, false, true); // reset led to blue only
    u8x8.setCursor(0, 2);
    Serial.print("\Iterrupt Counter: ");

    // if interrupt was received in this cycle
    if (interruptCount > prevInterruptCount) {
      Serial.println("\Interrupt received!");
      setLedRGB(false, true, false); // set green only
    prevInterruptCount = interruptCount;
    if (interruptCount >= 5) {
      // Trigger System OFF after 5 interrupts

// -------------------- System ------------------------- //

void goToPowerOff() {
  setLedRGB(false, false, false);
  Serial.println("Going to System OFF");
  u8x8.setCursor(0, 3);
  setupDoubleTapInterrupt(); // not needed here, if already applied..
  delay(1000); // delay seems important to apply settings, before going to System OFF
  //Ensure interrupt pin from IMU is set to wake up device
  nrf_gpio_cfg_sense_input(digitalPinToInterrupt(int1Pin), NRF_GPIO_PIN_PULLDOWN, NRF_GPIO_PIN_SENSE_HIGH);
  // Trigger System OFF

// -------------------- Interrupts ------------------------- //

void setupDoubleTapInterrupt() {
  uint8_t error = 0;
  uint8_t dataToWrite = 0;

  // Double Tap Config
  myIMU.writeRegister(LSM6DS3_ACC_GYRO_CTRL1_XL, 0x60); //* Acc = 416Hz (High-Performance mode)// Turn on the accelerometer
  // ODR_XL = 416 Hz, FS_XL = 2g
  myIMU.writeRegister(LSM6DS3_ACC_GYRO_TAP_CFG1, 0x8E);// INTERRUPTS_ENABLE, SLOPE_FDS// Enable interrupts and tap detection on X, Y, Z-axis
  myIMU.writeRegister(LSM6DS3_ACC_GYRO_TAP_THS_6D, 0x85);// Set tap threshold 8C
  myIMU.writeRegister(LSM6DS3_ACC_GYRO_INT_DUR2, 0x7F);// Set Duration, Quiet and Shock time windows 7F
  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

void int1ISR()

// -------------------- Utilities ------------------------- //

void setLedRGB(bool red, bool green, bool blue) {
  if (!blue) { digitalWrite(LEDB, HIGH); } else { digitalWrite(LEDB, LOW); }
  if (!green) { digitalWrite(LEDG, HIGH); } else { digitalWrite(LEDG, LOW); }
  if (!red) { digitalWrite(LEDR, HIGH); } else { digitalWrite(LEDR, LOW); }

prints the interrupt to the console as well as to display on the dev board. I posted a video of it in action for the sleep feature. using interrupts. workd GREAT!
GL :slight_smile:

Thanks. I looked at this code before and tried bits of it in one of my solutions. I also ran it more or less wholesale with modifications to remove the display and shutdown procedures. It works.

However, the difference between this use case and my case that in my case the interrupt could fire repeatedly and perhaps rather rapidly whereas in your case it takes a relatively long amount of time between the interrupts.

The module actually appears to be stuck so that the loop executes only once, similarly to what’s described here: I2C Interaction in ISR with 33 BLE and Nano Connect · Issue #301 · arduino/ArduinoCore-mbed · GitHub

Also your code “without” Wire.h might yield different results?

Took out the Wire.h which didn’t change anything.

The problem appears to be “myIMU.readRegisterInt16”. Perhaps it isn’t thread safe, etc. or there is a conflict between interrupts and i2c used to access the IMU, eg. interrupts used by i2c may be turned off while in the ISR.

I guess I’ll have to try going medieval, i.e. relatively low level for the ISR. That’s something I was going to do anyway for performance after knocking out a proof of concept at a high level. If I knew a little more about the embed lib used, maybe I’d look under the hood. myIMU doesn’t work for me at all in the non-embed framework. The

The IMU function will perform better when we use the “Seeed nrf52 mbed-enabled Boards Library”

statement in the Wiki is a euphemism.

It’s difficult at best to use i2c in ISRs. No SPI for the IMU on the Xiao, which although not without its own obstacles, would simplify low level access, not to mention make it faster. :anguished:

Oh well, I guess I’ll use a loop for the time being as a POC and then reimplement on a different board with SPI.

Using semaphores may be a viable solution/workaround: i2c with isr · Issue #2894 · espressif/arduino-esp32 · GitHub

Hi, Any idea how fast , I’m checking over the code and running a demo, I can tap the snot out of it and it keeps going…? doesn’t seem to pay attention to the lack of quiet time or I’m all wet on the settings for the IMU config regs.
I need to lower the threshold ( Tapping hard now, want softer more sensitive taps)and make it ignore 3 taps.
Fun stuff any input is welcome. :smile:
GL :v:

Would it be possible to get an interrupt also with tilt variation? How?

From the doc’s I believe YES , there are two interrupt registers I’ve tested using int 1 or 2 and attaching it to a pin. Have a look at the LSM6DS3 data sheet it’s 3 pages. You set the function and attach the pin. There are separate register setting for tilt only (inclination) afaik.
You try the high level example sketch it works on tilt as well.
GL :slight_smile:

The XIAO uses the LSM6DS3TR-C and not the LSM6DS3 AFAIR. However, though there are some differences between these two chips, I think it is the same in this case.

Take a look at FUNC_SRC (53h), FUNC_SRC2 (54h), WRIST_TILT_IA (55h), etc. and MD1_CFG (5Eh), MD2_CFG (5Fh) in the LSM6DS3TR-C datasheet.

You can probably also resolve the differences between the triggers in your ISR or elsewhere.

I see this in the two data sheets, Looks like FIFO and Android compatibility Diff.
 Smart FIFO up to 4 kbyte based on features set
 Android M compliant
 Smart FIFO up to 8 kbyte based on features set
 Compliant with Android K and L
It’s been discussed b4 HERE
I believe the library address the same functions.

我们制作的这个库的功能应该是可以应用在XIAO BLE上的,如有不能使用的功能,可以向我们提供相关反馈,谢谢。

or “The functions of the library we made should be applicable to XIAO BLE. If there are functions that cannot be used, please provide us with relevant feedback, thank you.”
Seed technical didn’t dispute so ?
GL :wink: