Thingy91x ADXL367 Motion Detection Only Triggers Once After Reboot

Environment
Hardware: Thingy:91 X (nRF9151)
SDK: nRF Connect SDK v2.8.0
Zephyr: v4.2.99
Driver: ADXL367 with interrupt-based activity detection
Configuration: CONFIG_ADXL367_TRIGGER_GLOBAL_THREAD=y

Problem Description

Motion detection triggers successfully once after reboot, but then stops working completely. Subsequent motion events are not detected until the device is power cycled.

Expected behavior: Continuous motion detection with callbacks on each motion event
Actual behavior: First motion event works, then silence forever

Current Configuration

// Activity detection settings
CONFIG_ADXL367_ACTIVITY_DETECTION_MODE=y
CONFIG_ADXL367_INACTIVITY_DETECTION_MODE=n
CONFIG_ADXL367_ACTIVITY_THRESHOLD=50 // 50 * 250mg = 12.5g threshold
CONFIG_ADXL367_ACTIVITY_TIME=1 // 1 sample at 100Hz
CONFIG_ADXL367_TRIGGER_GLOBAL_THREAD=y

// Using DEFAULT mode (not LINKED/LOOPED) for activity-only detection

Application Code

static void motion_trigger_handler(const struct device *dev,
const struct sensor_trigger *trigger)
{
LOG_INF("Motion detected! (Activity interrupt)");
// ... callback logic
}

int motion_enable(void)
{
struct sensor_trigger trig = {
.type = SENSOR_TRIG_THRESHOLD,
.chan = SENSOR_CHAN_ACCEL_XYZ,
};

return sensor_trigger_set(accel_dev, &trig, motion_trigger_handler);
}

Debugging Done

Verified interrupt re-enable logic: Modified adxl367_trigger.c to ensure GPIO interrupt is always re-enabled even on STATUS register read failures
Checked processing mode: Using ADXL367_DEFAULT mode (not LINKED) since inactivity detection is disabled
Verified GPIO configuration: INT1 pin configured correctly, first interrupt fires successfully
Added logging: Confirmed STATUS register reads complete and handlers are called

Observations

First motion event: Interrupt fires, handler called, logs appear
Second motion event onwards: No interrupt, no handler, complete silence
After power cycle: First motion works again, then stops

Questions

Does the ADXL367 require special acknowledgment of activity interrupts beyond reading the STATUS register?
Should activity detection be configured differently for continuous triggering?
Are there any undocumented register settings needed for the interrupt to re-arm automatically?
Could this be related to the activity timer (ADXL367_TIME_ACT) with a value of 1 sample?

Request

Has anyone successfully implemented continuous motion detection with the ADXL367 on Zephyr? Any insights into what might cause the interrupt to stop firing after the first trigger would be greatly appreciated.

Parents
  • Hello,

    I am not entirely sure what help we may provide here, it may seem as something that analog devices may need to help you with.

    I guess the first question from me is whether you have measured on a logic analyzer the state of the INT1 pin? Does it indeed indicate an interrupt, or is the problem entirely on the configuration of the adxl side? If it's entirely on the adxl side, then maybe try at generating interrupts on both activity and inactivity detection.

    Kenneth

  • Hi Kenneth,

    Thanks for getting back to me. I don't have a logic analyzer, but I've already tried quite a bit of debugging.

    Regarding your suggestion to try both activity and inactivity detection - I did that. Same issue - works once, then stops.

    I've actually been through several iterations:
    - Tried LINKED mode with both activity + inactivity enabled - still locks up
    - Created a custom driver using DEFAULT mode instead of LINKED - still locks up
    - Fixed the interrupt re-enable path to ensure it always runs even on errors - still locks up

    The pattern is always the same: first motion event works perfectly, then nothing. Power cycle and it works once again.

    I've modified the Zephyr driver's trigger handler to guarantee the GPIO interrupt gets re-enabled, and I've confirmed the STATUS register read succeeds. But after that first trigger, it's like the INT1 pin never asserts again.

    What's puzzling is that the GPIO interrupt re-enable call succeeds, and the first callback worked fine. Makes me wonder if there's something about how the ADXL367 re-arms its interrupt that isn't documented.

    Has anyone at Nordic actually tested continuous motion detection with this sensor? Or are there any known quirks with the ADXL367's interrupt behavior that might not be obvious from the datasheet?

    I can provide logs or register dumps if that would help.

    Thanks
  • I don't have any experience with this accelerometer no, but asking AI I can find some discussions around this topic:

    Try with a significant higher CONFIG_ADXL367_ACTIVITY_THRESHOLD value, if the range isn't changed in the driver, then a value of 50 could correspond to a very low value, potentially triggering continuously active.

    Try to enable an inactivity with with a short timeout, so that after first motion the sensor will self-clear its awake state.

    Try with ADXL367_REFERENCED_ACTIVITY_DETECTION_MODE=n.

    Initially try with a very high threshold (low sensitivity) and gradually reduce the threshold runtime to see if that works.

    Kenneth

Reply Children
  • There's basically no way to make it work properly. I'm expecting a solution, this should be a high priority issue. It's clearly not working and it's most definitely not a skill or effort issue. It's DOA with non proper support in the sdk

  • It is suspicious that there is no connection between P0.11 of the nRF9151 and INT1 of the ADXL367 in the Thingy:91X devicetree overlay. I modified the overlay to add this connection.

    On the actual board, INT1 is not only connected to the nRF9151 but also to the nRF5340. I believe the corresponding nRF5340 pin is left floating. Unfortunately, this cannot be verified with a logic analyzer because there is no test point and the trace is routed on the inner layers.

  • Additionally, the AI insists that the Threshold Activity Register and Threshold Inactivity Register use a scale factor of 7.8 mg/LSB (for the +-2g rangebut I could not find any reference to this value in the datasheet.

  • Hi! I also want to use this accelerometer, here is what works for me (based on the accel_trig sample in zephyr, running on a thingy91x' ADXL367)

    # boards/thingy91x_nrf9151_ns.conf

    ```c
    CONFIG_ADXL367_ACTIVITY_DETECTION_MODE=y
    CONFIG_ADXL367_INACTIVITY_DETECTION_MODE=n
    CONFIG_ADXL367_ACTIVITY_THRESHOLD=50
    CONFIG_ADXL367_ACTIVITY_TIME=10
    CONFIG_ADXL367_TRIGGER_GLOBAL_THREAD=y
    ```

    # boards/thingy91x_nrf9151_ns.overlay

    ```c

    / {
        aliases {
            accel0 = &accel;
        };
    };

    &accel {
        status="okay";
    };

    ```

    # src/main.c

    ```c

    /*
     * Copyright 2024 NXP
     * Copyright (c) 2018 Phytec Messtechnik GmbH
     *
     * SPDX-License-Identifier: Apache-2.0
     */

    #include <zephyr/kernel.h>
    #include <zephyr/drivers/sensor.h>
    #include <stdio.h>

    K_SEM_DEFINE(sem, 0, 1); /* starts off "not available" */

    static void tap_trigger_handler(const struct device *dev, const struct sensor_trigger *trigger)
    {
        ARG_UNUSED(trigger);
        printf("TAP detected==========================================================\n\n\n\n\n");

        if (sensor_sample_fetch_chan(dev, SENSOR_CHAN_ACCEL_XYZ) < 0) {
            printf("ERROR: SENSOR_CHAN_ACCEL_XYZ fetch failed\n");
        }

        k_sem_give(&sem);
    }

    int main(void)
    {
        struct sensor_value data[3];
        const struct device *const dev = DEVICE_DT_GET(DT_ALIAS(accel0));

        struct sensor_trigger trig = {
            .type = SENSOR_TRIG_DATA_READY,
            .chan = SENSOR_CHAN_ACCEL_XYZ,
        };

        if (!device_is_ready(dev)) {
            printf("Device %s is not ready\n", dev->name);
            return 0;
        }


        trig.type = SENSOR_TRIG_THRESHOLD;
        trig.chan = SENSOR_CHAN_ACCEL_XYZ;
        if (sensor_trigger_set(dev, &trig, tap_trigger_handler) < 0) {
            printf("Could not set tap trigger\n");
            return 0;
        }


        while (1) {
            k_sem_take(&sem, K_FOREVER);
            sensor_channel_get(dev, SENSOR_CHAN_ACCEL_XYZ, data);

            /* Print accel x,y,z data */
            printf("%16s [m/s^2]:    (%12.6f, %12.6f, %12.6f)\n", dev->name,
                   sensor_value_to_double(&data[0]), sensor_value_to_double(&data[1]),
                   sensor_value_to_double(&data[2]));
        }
    }

    ```

    The sensitivity is just about right to not trigger while the thingy91x is sitting on my desk while I'm typing, but as soon as I pick up the device it triggers a few times.

Related