XIAO nRF52840 Sense not working with some Seeed_Arduino_LSM6DS3 examples

I am trying to use the FreeFallDetect example from Seeed-Studio/Seeed_Arduino_LSM6DS3 and it is not detecting freefall. The Pedometer example works.

Using Seeed nRF52 mbed-enabled Boards v2.9.1

The HighLevelExample program shows constantly changing Accel and Gyro data even when the device is sitting still on a table.

Hi Greglarious, Welcome…

That being said , Yea they are not 100% on working examples around here, It’s NOT really managed just kind of thrown on a wiki and rarely updated. The User’s here are EXCELLENT though. The support is responsive but not from an Engineering POV.
I managed to learn allot about the IMU here is the free fall template I tweaked and works VERY well with the Expansion Board, but also on the chip alone. (see Video)
here is the code and pics & bonus video if there’s no Cockpit issue’s/. You know Between the Keyboard and the floor :smile:

GL :slight_smile:

Here is an example code that you can use to detect a light impact using the LSM6DS3 IMU 
and write the event information to the BLE characteristic value: , Check the comments for the values I've used successfully, You can WRITE them via the BLE connection and USE NRF connect to test. 
(best $10 bucs I ever spent) 
#include <ArduinoBLE.h>
#include <Arduino.h>
#include <LSM6DS3.h>
#include <Wire.h>
#include <U8x8lib.h>
LSM6DS3 IMU(I2C_MODE, 0x6A);  //I2C device address 0x6A  // IMU
#define int2Pin PIN_LSM6DS3TR_C_INT1
float aX, aY, aZ, gX, gY, gZ;
String timeDateStamp = (" __TIME____DATE__" );
float ftemperature = 25.0;
bool imuParked = false;
bool droppedFlag = false;
bool motionAlarm = true;
String imuOrientation = "upright";
double tapToOpen = true;
int numberOfTaps = 3;
const int appearance = (0x19);
int orientation = -1;                        // use -1 to indicate initial state
const int orientationUpdateInterval = 1000;  // update orientation every 5 seconds
unsigned long lastOrientationUpdate = 0;

// BLE Service and Characteristics
BLEService fallService("FDDA0001-7D3E-4988-B8E1-2E314F7A1D2A");
BLEStringCharacteristic fallCharacteristic("FDDA0002-7D3E-4988-B8E1-2E314F7A1D2A", BLERead | BLENotify, 20);
BLEFloatCharacteristic impactLevelCharacteristic("FDDA0003-7D3E-4988-B8E1-2E314F7A1D2A", BLERead | BLEWrite);

// Fall detection parameters
float impactThreshold = 1.5;  // G-force threshold for impact (default value) HEX 20 40 =10G, 20 41 =2.5 G, 10 40= 2.25G,80 3F 1.0G,C0 3F = 1.5G
const int impactTime = 250;   // Time (ms) to wait for impact
bool impactDetected = false;
unsigned long impactTimestamp;
unsigned long lastReadTimestamp = 0;
const int readInterval = 50; // Time interval (ms) to read acceleration data

//Create a instance of class
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

void setup() {
  delay(3000);  //relax...Get Ready for serial port
  Serial.println("Power ON ");  // Let's BEGIN!!
  Serial.println("Test program compiled on " __DATE__ " at "__TIME__);
  initdisplay();       // Sets Font and clears screen.

  if (!BLE.begin()) {
    Serial.println("Starting BLE failed!");
    while (1);
  testimu();           //sets begin and reads Temps to verify alive
  fallCharacteristic.writeValue("No Impact");
  Serial.println("BLE Fall Detector is ready");

void loop() {
  BLEDevice central = BLE.central();

  if (central) {
    Serial.print("Connected to central: ");
    impactDetected = false;

    while (central.connected()) {
      if (impactLevelCharacteristic.written()) {
        float newThreshold = impactLevelCharacteristic.value();
        if (newThreshold >= 0.5 && newThreshold <= 10.0) {
          impactThreshold = newThreshold;
          Serial.print("Updated impactThreshold to: ");


      unsigned long currentMillis = millis();
      if (currentMillis - lastReadTimestamp >= readInterval) {
        lastReadTimestamp = currentMillis;
        // read the acceleration data
        aX = IMU.readFloatAccelX();
        aY = IMU.readFloatAccelY();
        aZ = IMU.readFloatAccelZ();
        float magnitude = sqrt(aX * aX + aY * aY + aZ * aZ);

        if (magnitude > impactThreshold && !impactDetected) {
          impactDetected = true;
          impactTimestamp = millis();

        if (impactDetected && (millis() - impactTimestamp) > impactTime) {
          impactDetected = false;
          fallCharacteristic.writeValue("Impact Detected!");
          setLedRGB(true, false, false);  // Turn on RED LED
          Serial.println("Impact Detected!");
          u8x8.setCursor(5, 0);
          u8x8.print("Impact Detected!");

          fallCharacteristic.writeValue("No Impact");
          setLedRGB(false, false, false);  // Turn off any LED 's OFF
          Serial.println("No Impact");
          u8x8.setCursor(5, 0);
          u8x8.print("No Impact");


    Serial.print("Disconnected from central: ");
//end of main loop

bool checkForFall() {
  bool impact = false;
  unsigned long currentMillis = millis();
  if (currentMillis - lastReadTimestamp >= readInterval) {
    lastReadTimestamp = currentMillis;

    float ax = IMU.readFloatAccelX();
    float ay = IMU.readFloatAccelY();
    float az = IMU.readFloatAccelZ();
    float magnitude = sqrt(ax * ax + ay * ay + az * az);

    if (magnitude > impactThreshold && !impactDetected) {
      impactDetected = true;
      impactTimestamp = millis();

    if (impactDetected && (millis() - impactTimestamp) > impactTime) {
      impactDetected = false;
      impact = true;
  return impact;

void getOrientation() {
  // Check if it's time to update the orientation
  if (millis() - lastOrientationUpdate >= orientationUpdateInterval) {
    lastOrientationUpdate = millis();

    // read the acceleration data
    aX = IMU.readFloatAccelX();
    aY = IMU.readFloatAccelY();
    aZ = IMU.readFloatAccelZ();

    // determine the orientation
    int newOrientation;
    if (abs(aX) > abs(aY) && abs(aX) > abs(aZ)) {
      newOrientation = (aX > 0) ? 1 : 0;
    } else if (abs(aY) > abs(aZ)) {
      newOrientation = (aY > 0) ? 2 : 3;
    } else {
      newOrientation = (aZ > 0) ? 4 : 5;

    // print the orientation if it changed
    if (newOrientation != orientation) {
      Serial.print("3D Position:-->");
      u8x8.setCursor(0, 0);

      orientation = newOrientation;
      switch (orientation) {
        //Serial.println (orientation);
        case 0:
        case 1:
          Serial.println("Ass UP ");
          u8x8.print("Ass UP ");
        case 2:
          Serial.println("SIDEWAYS LEFT");
          u8x8.print("SIDEWAYS LEFT");
        case 3:
          Serial.println("SIDEWAYS RIGHT");
          u8x8.print("SIDEWAYS RIGHT");
        case 4:
          Serial.println("TOP UP");
          u8x8.print("TOP UP");
        case 5:
          Serial.println("TOP DOWN");
          u8x8.print("TOP DOWN");
    orientation = -1;  // use -1 to indicate initial state
void initdisplay() {
  pinMode(LEDR, OUTPUT);  // initialize the LED pin as an output:
  pinMode(LEDG, OUTPUT);  // initialize the LED pin as an output:
  pinMode(LEDB, OUTPUT);
  pinMode(LED_BUILTIN, OUTPUT);  // initialize the LED pin as an output:
  u8x8.setFlipMode(1);  // set number from 1 to 3, the screen word will rotary 180
  u8x8.setCursor(0, 0);
  u8x8.print("Power ON ");
void testimu() {
  if (IMU.begin() != 0) {  // Start IMU
    Serial.println("Device error");
    setLedRGB(false, true, false);  // Red
  } else {
    Serial.println("IMU GOOD");  //Serial.println("aX,aY,aZ,gX,gY,gZ");
    // //Thermometer
    Serial.print("Temperature: ");
    Serial.print((String) + IMU.readTempF() + "\u00b0");
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);


Thanks for the welcome and the example.
Your code is looking at the raw Accel and Gyro values. That works for me but I am having difficulty using the higher level IMU functions such as LSM6DS3_ACC_GYRO_WAKE_UP_SRC

Hi there,
The default for the “Wake_UP_THS(5B)” is 000000, so it should always be awake?

Yes I loaded it up (seeeds example) it gives the “Device OK !” and the “Success to Configure!” message but that’s it, NOT a BYTE of data ? (thankyou seeed for verifying my previous comment)
Something seems not right here, the description says for “Grove”, not to mention “2015”
Everything else is blank but the “by www.seeedstudio.com” WTH? Talk about “Garbage” IMO.

If it where a company I owned or operated, "quality " would extend to the software and examples, I would have comments by testers with “Clear and concise Instructions” I mean that’s why it’s there to help user’s or potential “CUSTOMERS were you get your MONEY from” to figure if this is a good fit for your project and NOT waste their time in getting the “BASICS”
But hay that’s just me, having worked at companies and acidemia that care. BUT I digress, :crazy_face::slight_smile:
I don’t believe the register values being written are all correct, and I don’t get it’s just reading the register, no Interrupt , seems hokkie IMO.
One of the resident experts can chime in, I’ll look more too.
GL :slight_smile:

The point of having an IMU such as the LSM6DS3 versus just a raw accelerometer/gyro is to offload processing from the controller onto the IMU chip for detecting events such as free fall.

From my experience that requires 3 things:

  1. configuring the IMU to trigger interrupts based on raw inputs and the circumstances you want to detect
  2. listening to the interrupts from the IMU and gathering data
  3. examining the resulting data from the interrupt to interpret it (usually just drop it into a volatile variable as you don’t want to do real work inside of the interrupt function)

It is unclear to me which step is the issue I am having now. I will start modifying the LSM6DS3 library with debug code to find that out now.

Yep, In this case I’m sure it’s the first TWO :slight_smile:
If it’s just polling the register, then it won’t work unless the IMU is configured properly.
I like using the interrupts cleaner and easier, when it works, Like on the Double Tap demo I posted a while back. I 'll experiment more…

There could be several reasons why the FreeFallDetect example is not working. Here are a few troubleshooting steps you can try:

Check the sensitivity settings: The LSM6DS3 has a configurable threshold for freefall detection. Make sure that the sensitivity is set correctly in the FreeFallDetect example code.

Check the orientation of the board: The LSM6DS3 has a built-in orientation detection feature that affects the interpretation of the accelerometer readings. Make sure that the board is oriented correctly according to the code.

Check the wiring: Double-check that the board is wired correctly and that all connections are secure.

Update the firmware: Make sure that you have the latest firmware installed on the board. Check the Seeed Studio website for any firmware updates for the nRF52 board.

Check for interference: Other nearby devices or electrical noise can interfere with the accelerometer readings. Try moving the board to a different location to see if this makes a difference.

If none of these steps resolve the issue, you may need to reach out to Seeed Studio support for further assistance.

Rachel Gomez

Thanks for the tips Rachel. Since the LSM6DS3 is built into the board on the XIAO nRF52840 Sense, wiring seems unlikely. The firmware is at latest revision. I am doubtful the issue is interference. Not sure exactly what orientation requirements the freefall example has and same with settings. Being an example, I think it is fair to expect it to work as provided or it is not a very good choice for an example.

I was hoppping seeed stuuudio suppport participated in this forum and would answer this thread but I guess I will have to try to reach out and contact them directly.

I recently tried using the TemperatureMonitor example from the XYZSensorLibrary on my ESP32 development board, but I encountered an issue. Despite following the instructions and wiring the sensor correctly, the temperature readings I obtained were consistently inaccurate. Even when the sensor was placed in a stable environment, the readings fluctuated significantly. I reviewed the documentation and ensured that I had the most up-to- date library version, but the problem persisted. I added additional debug statements to the code to monitor the sensor data, but couldn’t identify the source of the eatsleepsniff discrepancy. Feeling puzzled, I reached out to the online community forums specific to the XYZSensorLibrary and shared my experience. I’m eagerly awaiting any recommendations or insights that could help me troubleshoot this temperature monitoring issue. Regards

Hello there, sorry for hijacking your topic.
I’m desperately trying to find a simple tutorial for the fall detection (via interrupt?) but couldn’t find anything.
Your sample code seems promising but I can’t make it compile.
I fixed already the LEDR to LED_RED etc.
but I still have one (last) compilation error telling me

/home/…/Arduino/libraries/ArduinoBLE/src/utility/HCIUartTransport.cpp:35:2: error: #error “Unsupported board selected!”
35 | #error “Unsupported board selected!”
| ^~~~~
/home/…/Arduino/libraries/ArduinoBLE/src/utility/HCIUartTransport.cpp:101:40: error: ‘SerialHCI’ was not declared in this scope; did you mean ‘Serial’?
101 | HCIUartTransportClass HCIUartTransport(SerialHCI, 912600);
| ^~~~~~~~~
| Serial

So did you use a specific library for that serial connection? SerialHCI doesn’t seem to exist in my library manager. Any suggestion (or for other samples regarding this topic)?

Thanks for any help!

The Example posted Above Works with the latest Arduino 2.2.0 and the latest board 2.92 update as well.

Zip created at C:\Users\Dude\AppData\Local\Temp\arduino\sketches\2CB1FEE3AF60C4B6F2781C03897EA385/1sketch_aug29a_FREE_FALL_test.ino.zip

Using library ArduinoBLE at version 1.3.5 in folder: D:\Arduino_projects\libraries\ArduinoBLE 
Using library Seeed Arduino LSM6DS3 at version 2.0.3 in folder: D:\Arduino_projects\libraries\Seeed_Arduino_LSM6DS3 
Using library Wire in folder: C:\Users\Dude\AppData\Local\Arduino15\packages\Seeeduino\hardware\mbed\2.9.2\libraries\Wire (legacy)
Using library U8g2 at version 2.34.22 in folder: D:\Arduino_projects\libraries\U8g2 
Using library SPI in folder: C:\Users\Dude\AppData\Local\Arduino15\packages\Seeeduino\hardware\mbed\2.9.2\libraries\SPI (legacy)
"C:\\Users\\Dude\\AppData\\Local\\Arduino15\\packages\\Seeeduino\\tools\\arm-none-eabi-gcc\\7-2017q4/bin/arm-none-eabi-size" -A "C:\\Users\\Dude\\AppData\\Local\\Temp\\arduino\\sketches\\2CB1FEE3AF60C4B6F2781C03897EA385/1sketch_aug29a_FREE_FALL_test.ino.elf"
Sketch uses 339648 bytes (41%) of program storage space. Maximum is 811008 bytes.
Global variables use 71872 bytes (30%) of dynamic memory, leaving 165696 bytes for local variables. Maximum is 237568 bytes.

Once compiled and Uploaded It waits for the BLE connection and Displays it’s IMU’s Orientation (Up, Down, Flipped or Sides) Detecting over 1.5 G impact an a delay of
250ms. for NO-Impact. HTH
Works like the video.
GL :slight_smile: