How to configure Trigger for ADXL362 using sensor library

I am studying a little bit about the sensor library. Unfortunately, I haven't been able to find any good documentation about sensor library, what function calls are available for each sensor and etc... Is it available?

Anyways, my current problem is with ADXL362 accelerometer. I am trying to set a trigger (instead of polling data like in accel_polling project available at zephyr\samples\sensor\accel_polling)

I want to only read data when the movement threshold has been triggered. 

However, it is not fully clear how can this be achieved and could not find any useful information online about it.

My project repository:

github.com/.../thingy91_motion_trigger

I am currently testing on the Thingy 91 board and running the following code:

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

K_SEM_DEFINE(sem, 0, 1);

static const enum sensor_channel channels[] = {
	SENSOR_CHAN_ACCEL_X,
	SENSOR_CHAN_ACCEL_Y,
	SENSOR_CHAN_ACCEL_Z,
};

#define DEFAULT_ADXL362_NODE DT_ALIAS(adxl362)
BUILD_ASSERT(DT_NODE_HAS_STATUS(DEFAULT_ADXL362_NODE, okay),
			 "ADXL362 not specified in DT");

// DEVICE TREE STRUCTURE
const struct device *adxl1362_sens = DEVICE_DT_GET(DEFAULT_ADXL362_NODE);


static void trigger_handler(const struct device *dev,
			    const struct sensor_trigger *trig)
{
	switch (trig->type) {
	case SENSOR_TRIG_DATA_READY:
		if (sensor_sample_fetch(dev) < 0) {
			printk("Sample fetch error\n");
			return;
		}
		k_sem_give(&sem);
		break;
	case SENSOR_TRIG_THRESHOLD:
		printk("Threshold trigger\n");
		break;
	default:
		printk("Unknown trigger\n");
	}
}

void main(void)
{

	struct sensor_value accel[3];


	if (!device_is_ready(adxl1362_sens))
	{
		printk("sensor: device %s not ready.\n", adxl1362_sens->name);
		return 0;
	}
	else
	{
		printk("sensor: device %s ready.\n", adxl1362_sens->name);
	}


	if (IS_ENABLED(CONFIG_ADXL362_TRIGGER)) {
		struct sensor_trigger trig = { .chan = SENSOR_CHAN_ACCEL_X };

		trig.type = SENSOR_TRIG_THRESHOLD;
		if (sensor_trigger_set(adxl1362_sens, &trig, trigger_handler)) {
			printk("Trigger set error\n");
			return;
		}

		trig.type = SENSOR_TRIG_DATA_READY;
		if (sensor_trigger_set(adxl1362_sens, &trig, trigger_handler)) {
			printk("Trigger set error\n");
		}
	}

	while (true) {
		if (IS_ENABLED(CONFIG_ADXL362_TRIGGER)) {
			k_sem_take(&sem, K_FOREVER);
		} else {
			k_sleep(K_MSEC(1000));
			if (sensor_sample_fetch(adxl1362_sens) < 0) {
				printk("Sample fetch error\n");
				return;
			}
		}

		if (sensor_channel_get(adxl1362_sens, SENSOR_CHAN_ACCEL_X, &accel[0]) < 0) {
			printk("Channel get error\n");
			return;
		}

		if (sensor_channel_get(adxl1362_sens, SENSOR_CHAN_ACCEL_Y, &accel[1]) < 0) {
			printk("Channel get error\n");
			return;
		}

		if (sensor_channel_get(adxl1362_sens, SENSOR_CHAN_ACCEL_Z, &accel[2]) < 0) {
			printk("Channel get error\n");
			return;
		}

		printk("x: %.1f, y: %.1f, z: %.1f (m/s^2)\n",
		       sensor_value_to_double(&accel[0]),
		       sensor_value_to_double(&accel[1]),
		       sensor_value_to_double(&accel[2]));
	}
}

my prj.conf is as following:

CONFIG_CBPRINTF_FP_SUPPORT=y


# Enable Edge Impulse dependencies
CONFIG_CPP=y
CONFIG_STD_CPP11=y
CONFIG_FP16=n


# SPI
CONFIG_SPI=y
CONFIG_SPI_NRFX=y
CONFIG_MAIN_STACK_SIZE=4096


# LOG
CONFIG_LOG=y

# ADXL362
CONFIG_SENSOR=y
CONFIG_ADXL362=y
CONFIG_ADXL362_TRIGGER_GLOBAL_THREAD=y
CONFIG_ADXL362_INTERRUPT_MODE=1
CONFIG_ADXL362_ABS_REF_MODE=1
CONFIG_ADXL362_ACTIVITY_THRESHOLD=200


and my thingy91_nr9160_ns.overlay:

/ {
    aliases {
            adxl372 = &adxl372;
            adxl362 = &adxl362;
    };
};

After flasing the nRF9160 on the Thingy 91 with the source code shown above, I am getting the following error:

À*** Booting nRF Connect SDK v2.5.0 ***
sensor: device adxl362@0 ready.
[00:00:00.270,568] [1B][1;31m<err> ADXL362: Unsupported sensor trigger[1B][0m
Trigger set error

I would appreciate if someone could help me:

  1. By sharing sensor library documentation (especially what function calls are available for each sensor and how to use them correctly)
  2. Figure out what could be the issue with the code and why I am getting unsupported sensor trigger issue
Parents
  • UPDATE:

    After digging through the source code of the ADXL362 driver (zephyr\drivers\sensor\adxl362)

    I have discovered that SENSOR_TRIG_THRESHOLD is not supported.

    See the adxl362_trigger_set function (adxl362_trigger.c):

    int adxl362_trigger_set(const struct device *dev,
    			const struct sensor_trigger *trig,
    			sensor_trigger_handler_t handler)
    {
    	struct adxl362_data *drv_data = dev->data;
    	const struct adxl362_config *config = dev->config;
    	uint8_t int_mask, int_en, status_buf;
    
    	if (!config->interrupt.port) {
    		return -ENOTSUP;
    	}
    
    	switch (trig->type) {
    	case SENSOR_TRIG_MOTION:
    		k_mutex_lock(&drv_data->trigger_mutex, K_FOREVER);
    		drv_data->act_handler = handler;
    		drv_data->act_trigger = trig;
    		k_mutex_unlock(&drv_data->trigger_mutex);
    		int_mask = ADXL362_INTMAP1_ACT;
    		/* Clear activity and inactivity interrupts */
    		adxl362_get_status(dev, &status_buf);
    		break;
    	case SENSOR_TRIG_STATIONARY:
    		k_mutex_lock(&drv_data->trigger_mutex, K_FOREVER);
    		drv_data->inact_handler = handler;
    		drv_data->inact_trigger = trig;
    		k_mutex_unlock(&drv_data->trigger_mutex);
    		int_mask = ADXL362_INTMAP1_INACT;
    		/* Clear activity and inactivity interrupts */
    		adxl362_get_status(dev, &status_buf);
    		break;
    	case SENSOR_TRIG_DATA_READY:
    		k_mutex_lock(&drv_data->trigger_mutex, K_FOREVER);
    		drv_data->drdy_handler = handler;
    		drv_data->drdy_trigger = trig;
    		k_mutex_unlock(&drv_data->trigger_mutex);
    		int_mask = ADXL362_INTMAP1_DATA_READY;
    		adxl362_clear_data_ready(dev);
    		break;
    	default:
    		LOG_ERR("Unsupported sensor trigger");
    		return -ENOTSUP;
    	}
    
    	if (handler) {
    		int_en = int_mask;
    	} else {
    		int_en = 0U;
    	}
    
    	return adxl362_reg_write_mask(dev, ADXL362_REG_INTMAP1, int_mask, int_en);
    }

    So I have modified my code slightly, removed the SENSOR_TRIG_THRESHOLD  and added only those trigger methods that are supported. See my updated main.c below:

    #include <stdio.h>
    #include <stdlib.h>
    #include <zephyr/kernel.h>
    #include <zephyr/drivers/spi.h>
    #include <zephyr/device.h>
    #include <zephyr/drivers/sensor.h>
    
    K_SEM_DEFINE(sem, 0, 1);
    
    static const enum sensor_channel channels[] = {
    	SENSOR_CHAN_ACCEL_X,
    	SENSOR_CHAN_ACCEL_Y,
    	SENSOR_CHAN_ACCEL_Z,
    };
    
    #define DEFAULT_ADXL362_NODE DT_ALIAS(adxl362)
    BUILD_ASSERT(DT_NODE_HAS_STATUS(DEFAULT_ADXL362_NODE, okay),
    			 "ADXL362 not specified in DT");
    
    // DEVICE TREE STRUCTURE
    const struct device *adxl1362_sens = DEVICE_DT_GET(DEFAULT_ADXL362_NODE);
    
    static void trigger_handler(const struct device *dev,
    							const struct sensor_trigger *trig)
    {
    	switch (trig->type)
    	{
    	case SENSOR_TRIG_DATA_READY:
    	{
    		printk("SENSOR_TRIG_DATA_READY\n");
    		if (sensor_sample_fetch(dev) < 0)
    		{
    			printk("Sample fetch error\n");
    			return;
    		}
    		k_sem_give(&sem);
    		break;
    	}
    	case SENSOR_TRIG_MOTION:
    	{
    		printk("SENSOR_TRIG_MOTION\n");
    		break;
    	}
    
    	case SENSOR_TRIG_STATIONARY:
    	{
    		printk("SENSOR_TRIG_STATIONARY\n");
    		break;
    	}
    
    	default:
    		printk("Unknown trigger\n");
    	}
    }
    
    void main(void)
    {
    
    	struct sensor_value accel[3];
    
    	if (!device_is_ready(adxl1362_sens))
    	{
    		printk("sensor: device %s not ready.\n", adxl1362_sens->name);
    		return 0;
    	}
    	else
    	{
    		printk("sensor: device %s ready.\n", adxl1362_sens->name);
    	}
    
    
    
    	if (IS_ENABLED(CONFIG_ADXL362_TRIGGER))
    	{
    		struct sensor_trigger trig = {.chan = SENSOR_CHAN_ACCEL_XYZ};
    
    		trig.type = SENSOR_TRIG_DATA_READY;
    		if (sensor_trigger_set(adxl1362_sens, &trig, trigger_handler))
    		{
    			printk("SENSOR_TRIG_DATA_READY set error\n");
    		}
    
    		trig.type = SENSOR_TRIG_MOTION;
    		if (sensor_trigger_set(adxl1362_sens, &trig, trigger_handler))
    		{
    			printk("SENSOR_TRIG_MOTION set error\n");
    		}
    
    		trig.type = SENSOR_TRIG_STATIONARY;
    		if (sensor_trigger_set(adxl1362_sens, &trig, trigger_handler))
    		{
    			printk("SENSOR_TRIG_STATIONARY set error\n");
    		}
    
    
    	}
    
    	while (true)
    	{
    		if (IS_ENABLED(CONFIG_ADXL362_TRIGGER))
    		{
    			k_sem_take(&sem, K_FOREVER);
    		}
    
    		if (sensor_channel_get(adxl1362_sens, SENSOR_CHAN_ACCEL_X, &accel[0]) < 0)
    		{
    			printk("Channel get error\n");
    			return;
    		}
    
    		if (sensor_channel_get(adxl1362_sens, SENSOR_CHAN_ACCEL_Y, &accel[1]) < 0)
    		{
    			printk("Channel get error\n");
    			return;
    		}
    
    		if (sensor_channel_get(adxl1362_sens, SENSOR_CHAN_ACCEL_Z, &accel[2]) < 0)
    		{
    			printk("Channel get error\n");
    			return;
    		}
    
    		printk("x: %.1f, y: %.1f, z: %.1f (m/s^2)\n",
    			   sensor_value_to_double(&accel[0]),
    			   sensor_value_to_double(&accel[1]),
    			   sensor_value_to_double(&accel[2]));
    	}
    }

    Once I run the code, the following logs are being printed:

    ð*** Booting nRF Connect SDK v2.5.0 ***
    sensor: device adxl362@0 ready.
    SENSOR_TRIG_STATIONARY

    I have tried to move the Thingy91 board but nothing is triggered. I believe I am getting closed to figuring out how to properly set this up. I just need to figure out how to trigger SENSOR_TRIG_MOTION and SENSOR_TRIG_DATA_READY. Do I need any additional setup to get it to work?

    Perhaps I am not handling the events in trigger_handler correctly?

    static void trigger_handler(const struct device *dev,
    							const struct sensor_trigger *trig)
    {
    	switch (trig->type)
    	{
    	case SENSOR_TRIG_DATA_READY:
    	{
    		printk("SENSOR_TRIG_DATA_READY\n");
    		if (sensor_sample_fetch(dev) < 0)
    		{
    			printk("Sample fetch error\n");
    			return;
    		}
    		k_sem_give(&sem);
    		break;
    	}
    	case SENSOR_TRIG_MOTION:
    	{
    		printk("SENSOR_TRIG_MOTION\n");
    		break;
    	}
    
    	case SENSOR_TRIG_STATIONARY:
    	{
    		printk("SENSOR_TRIG_STATIONARY\n");
    		break;
    	}
    
    	default:
    		printk("Unknown trigger\n");
    	}
    }

  • The data in the Serial Terminal shows that the two triggers are registered immediately one after the other regardless for how long I'm shaking the Thingy (i.e. there is constant sequence of pairs of coordinates (movement+stationary) - stationary is registered even when I'm moving the device);

    This sounds like you have trigger threshold set wrong. Did you do sensor_attr_set to set trigger threshold?

    I'm not sure how to interpret the continuous movement in the context of Zephyr/the sensor driver:
    1. is there a separate trigger to be used (I've seen someone using 
      SENSOR_TRIG_DATA_READY in another code sample ...)?
      b. or maybe some kind of loop has to be employed in the specific case?
      c. or another OS related mechanism should be used (an interrupt, or something)?

    You can use accelerometer without any triggers. The sample project is available at zephyr/samples/sensor/accel_polling

    However, for detecting vibrations you could just set trigger thresholds (lets say you want to detect 5G change). You can configure trigger to only activate SENSOR_TRIG_MOTION when the acceleration change detected is larger than 5G.

    Sorry if I hijacked your question but as you said - the documentation is quite scarce ...

    Not an issue at all. Although I must agree that documentation regarding triggers is quite difficult to understand and there are not so many sample projects to look at.

    If you use my Thingy 91, you should try this project:

    https://github.com/krupis/thingy91_motion_trigger

    Although it is not fully clear to me why the trigger is only available for X axis on the ADXL362. How can I trigger the MOTION event when I am accelerating in Y axis instead of X?


    [00:00:57.210,723] [1B][0m<dbg> main: trigger_handler: Inactivity detected[1B][0m
    x: -0.3, y: -13.7, z: 1.8 (m/s^2)
    [00:00:57.281,250] [1B][0m<dbg> main: trigger_handler: Activity detected[1B][0m
    x: -2.4, y: -12.2, z: 0.5 (m/s^2)
    [00:00:57.290,039] [1B][0m<dbg> main: trigger_handler: Inactivity detected[1B][0m
    x: -0.9, y: -7.1, z: 0.1 (m/s^2)
    [00:00:57.337,005] [1B][0m<dbg> main: trigger_handler: Activity detected[1B][0m
    x: -0.4, y: -6.2, z: -0.7 (m/s^2)
    [00:00:57.363,464] [1B][0m<dbg> main: trigger_handler: Inactivity detected[1B][0m
    x: -1.0, y: -12.6, z: 0.6 (m/s^2)
    [00:00:57.454,528] [1B][0m<dbg> main: trigger_handler: Activity detected[1B][0m
    x: -0.9, y: -12.8, z: 0.5 (m/s^2)
    [00:00:57.457,458] [1B][0m<dbg> main: trigger_handler: Inactivity detected[1B][0m
    x: -1.1, y: -7.3, z: -3.5 (m/s^2)
    [00:00:57.601,562] [1B][0m<dbg> main: trigger_handler: Activity detected[1B][0m
    x: -0.4, y: -7.5, z: -3.0 (m/s^2)
    [00:00:57.610,351] [1B][0m<dbg> main: trigger_handler: Inactivity detected[1B][0m
    x: 0.2, y: -3.1, z: -8.1 (m/s^2)
    [00:00:57.757,202] [1B][0m<dbg> main: trigger_handler: Activity detected[1B][0m
    x: 0.4, y: -3.1, z: -7.8 (m/s^2)
    [00:00:57.760,162] [1B][0m<dbg> main: trigger_handler: Inactivity detected[1B][0m
    x: 0.5, y: -4.6, z: -13.7 (m/s^2)
    [00:00:57.851,196] [1B][0m<dbg> main: trigger_handler: Activity detected[1B][0m
    x: 0.1, y: -1.7, z: -9.8 (m/s^2)
    [00:00:57.859,985] [1B][0m<dbg> main: trigger_handler: Inactivity detected[1B][0m

    I hope to hear back from  regarding this

  • If you use my Thingy 91, you should try this project:

    https://github.com/krupis/thingy91_motion_trigger

    Yes, I based my part of my experiments on your repo ... By the way, the code in the repo is behind your last comment here - when you moved the threshold setting after the triggers definitions.

    This sounds like you have trigger threshold set wrong. Did you do sensor_attr_set to set trigger threshold?

    I didn't set any attributes - I never saw you setting such neither in the committed configs, nor in the code. I'll play around with the thresholds first thing when I start fixing my setup.

    You can use accelerometer without any triggers. The sample project is available at zephyr/samples/sensor/accel_polling

    Yes, I've had the thought of trying a hybrid approach - with triggers to catch the start and the end of the movement and to poll the sensor in between. I'm sure there could be a more optimised approach but at least I'll have to see what happens with this one first.

    Although it is not fully clear to me why the trigger is only available for X axis on the ADXL362. How can I trigger the MOTION event when I am accelerating in Y axis instead of X?

    Yes, we really need someone more experienced on this one.

    By the way, looking again at your serial terminal output with the measurements - if you mark them with which trigger they belong to, I guess you might see the same as me - pairs of movement+stationary measurements. 

    Don't mind the "timestamp" - it's made up. this result is from just moving the Thingy 30-40 cm on the table.

    Regards,

    Ivan

  • Hi,

    I see no one still manged to answer your last question. You most probably answered some of it for yourself. I also an on and off trying to understand the concepts here.

    There is something in particular that leaves me wondering.

    While playing with the code (the thresholds for the triggers mostly) I saw that in the output there is always a pair of the two triggers reported - always. There is always MOVED+STOPPED;MOVED+STOPPED;MOVED+STOPPED;MOVED+STOPPED; ...

    They seem inseparable, regardless of the threshold values. My expectation was that if I move the thingy for several seconds I'd have a chain of MOVED;MOVED;MOVED;MOVED;MOVED; ... and then at the end just one STOPPED; trigger reported. Obviously I don't quite understand how the sensor works even though I started to dig in the official datasheet too.

    Anyway, my question is: since your code is an adaptation of the ext_sensors.c (from the Asset Tracker repository), you copied the constants but actually you never used the ones for inactivity (the timeouts - ADXL362_TIMEOUT_MAX_S and ADXL362_TIMEOUT_RESOLUTION_MAX). You also didn't use the ext_sensors_inactivity_timeout_set(double inact_time) function when defining the triggers. And I wonder why - they seem important for the case? Or are they?

    Regards,

    Ivan

  • Hi again ...

    On your question:

    Although it is not fully clear to me why the trigger is only available for X axis on the ADXL362. How can I trigger the MOTION event when I am accelerating in Y axis instead of X?

    I'm stating the obvious (you most probably have seen it ... line 164 in your min.c file) but this just seems o be a limitation in the driver's API. When you try to use the unsupported constant the motion is still registered but for every trigger handling there is an error reported (I think it was -22: EINVAL). And I guess that if the limitation is really tied to the X axis then the easiest "hack" would be to position the Thingy in such a way that the movement happens on its X axis.

    As for the pairs of activity/inactivity that bothered me, I think I figured out what's happening. When motion is detected (the acceleration has passed the upper threshold) at some point the movement starts happening with nearly constant speed. This means that even with the sensor moving the acceleration is practically 0. Hence the inactivity detected.
    I'm not sure if my thinking is correct but I posed the scenario to MS's Copilot and it seemingly "agreed". It even suggested some code for the trigger handler with a flag being raised to maintain the state of movement while it happens but I'm not sure this solves the case with the constant speed.
    I mean, obviously every company that sells smart watches solved the motion detection case and it should be something relatively simple, yet I still can't figure it out. :D 

    Cheers!

  • While playing with the code (the thresholds for the triggers mostly) I saw that in the output there is always a pair of the two triggers reported - always. There is always MOVED+STOPPED;MOVED+STOPPED;MOVED+STOPPED;MOVED+STOPPED; .


    When you move the device suddenly, its acceleration goes beyond threshold hence triggering movement event. After that, if its no longer accelerating, it will trigger stopped event (as you have already figuring out in your latest post).


    If you move the device whilst increasing its acceleration (not velocity), then you will never get stopped signal (start by moving the device slowly and increase the acceleration as you move it further).

    I mean, obviously every company that sells smart watches solved the motion detection case and it should be something relatively simple, yet I still can't figure it out. :D 

    The easiest method to detect movement at constant speed is by using GPS (the combination of accelerometer+GPS is what most smart devices use).

Reply
  • While playing with the code (the thresholds for the triggers mostly) I saw that in the output there is always a pair of the two triggers reported - always. There is always MOVED+STOPPED;MOVED+STOPPED;MOVED+STOPPED;MOVED+STOPPED; .


    When you move the device suddenly, its acceleration goes beyond threshold hence triggering movement event. After that, if its no longer accelerating, it will trigger stopped event (as you have already figuring out in your latest post).


    If you move the device whilst increasing its acceleration (not velocity), then you will never get stopped signal (start by moving the device slowly and increase the acceleration as you move it further).

    I mean, obviously every company that sells smart watches solved the motion detection case and it should be something relatively simple, yet I still can't figure it out. :D 

    The easiest method to detect movement at constant speed is by using GPS (the combination of accelerometer+GPS is what most smart devices use).

Children
No Data
Related