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");
    	}
    }

  • Thanks for reply. I had a look at the source code that you have suggested. I have learnt a couple of things:

    1. Only 2 triggers are created for the ADXL362 (motion and stationary). No trigger for data ready for some reason

    2. SENSOR_CHAN_ACCEL_XYZ is not supported for setting UPPER_THRESH and LOWER_THRESH

    After adjusting the source code a little bit. See my current full source 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>
    
    #define LOG_LEVEL CONFIG_LOG_DEFAULT_LEVEL
    #include <zephyr/logging/log.h>
    LOG_MODULE_REGISTER(main);
    
    struct sensor_value data[3];
    
    /* Convert to s/m2 depending on the maximum measured range used for adxl362. */
    #if IS_ENABLED(CONFIG_ADXL362_ACCEL_RANGE_2G)
    #define ADXL362_RANGE_MAX_M_S2 19.6133
    #elif IS_ENABLED(CONFIG_ADXL362_ACCEL_RANGE_4G)
    #define ADXL362_RANGE_MAX_M_S2 39.2266
    #elif IS_ENABLED(CONFIG_ADXL362_ACCEL_RANGE_8G)
    #define ADXL362_RANGE_MAX_M_S2 78.4532
    #endif
    
    /* This is derived from the sensitivity values in the datasheet. */
    #define ADXL362_THRESHOLD_RESOLUTION_DECIMAL_MAX 2000
    
    #if IS_ENABLED(CONFIG_ADXL362_ACCEL_ODR_12_5)
    #define ADXL362_TIMEOUT_MAX_S 5242.88
    #elif IS_ENABLED(CONFIG_ADXL362_ACCEL_ODR_25)
    #define ADXL362_TIMEOUT_MAX_S 2621.44
    #elif IS_ENABLED(CONFIG_ADXL362_ACCEL_ODR_50)
    #define ADXL362_TIMEOUT_MAX_S 1310.72
    #elif IS_ENABLED(CONFIG_ADXL362_ACCEL_ODR_100)
    #define ADXL362_TIMEOUT_MAX_S 655.36
    #elif IS_ENABLED(CONFIG_ADXL362_ACCEL_ODR_200)
    #define ADXL362_TIMEOUT_MAX_S 327.68
    #elif IS_ENABLED(CONFIG_ADXL362_ACCEL_ODR_400)
    #define ADXL362_TIMEOUT_MAX_S 163.84
    #endif
    
    #define ADXL362_TIMEOUT_RESOLUTION_MAX 65536
    
    #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 int ext_sensors_accelerometer_threshold_set(double threshold, bool upper);
    
    static void trigger_handler(const struct device *dev, const struct sensor_trigger *trig)
    {
    	int err = 0;
    	switch (trig->type)
    	{
    
    	case SENSOR_TRIG_MOTION:
    	{
    		if (sensor_sample_fetch(dev) < 0)
    		{
    			printk("Sample fetch error \n");
    			return;
    		}
    
    		err = sensor_channel_get(dev, SENSOR_CHAN_ACCEL_XYZ, &data[0]);
    		if (err)
    		{
    			printk("sensor_channel_get, error: %d \n", err);
    			return;
    		}
    
    		printk("x: %.1f, y: %.1f, z: %.1f (m/s^2)\n",
    			   sensor_value_to_double(&data[0]),
    			   sensor_value_to_double(&data[1]),
    			   sensor_value_to_double(&data[2]));
    
    		printk("Inactivity detected\n");
    		break;
    	}
    
    	case SENSOR_TRIG_STATIONARY:
    	{
    		if (sensor_sample_fetch(dev) < 0)
    		{
    			printk("Sample fetch error \n");
    			return;
    		}
    
    		err = sensor_channel_get(dev, SENSOR_CHAN_ACCEL_XYZ, &data[0]);
    		if (err)
    		{
    			printk("sensor_channel_get, error: %d \n", err);
    			return;
    		}
    
    		printk("x: %.1f, y: %.1f, z: %.1f (m/s^2)\n",
    			   sensor_value_to_double(&data[0]),
    			   sensor_value_to_double(&data[1]),
    			   sensor_value_to_double(&data[2]));
    
    		printk("Activity detected\n");
    		break;
    	}
    
    	default:
    		printk("Unknown trigger %u \n",(trig->type));
    	}
    }
    
    void main(void)
    {
    
    	if (!device_is_ready(adxl1362_sens))
    	{
    		printk("sensor: device %s not ready.\n", adxl1362_sens->name);
    		return 0;
    	}
    
    	ext_sensors_accelerometer_threshold_set(3, true);
    	ext_sensors_accelerometer_threshold_set(0.5, false);
    
    	if (IS_ENABLED(CONFIG_ADXL362_TRIGGER))
    	{
    		printk("Configuring triggers\n");
    		struct sensor_trigger trig_motion = {
    			.chan = SENSOR_CHAN_ACCEL_XYZ,
    			.type = SENSOR_TRIG_MOTION,
    		};
    		if (sensor_trigger_set(adxl1362_sens, &trig_motion, trigger_handler))
    		{
    			printk("SENSOR_TRIG_MOTION set error\n");
    		}
    
    		struct sensor_trigger trig_stationary = {
    			.chan = SENSOR_CHAN_ACCEL_XYZ,
    			.type = SENSOR_TRIG_STATIONARY,
    		};
    		if (sensor_trigger_set(adxl1362_sens, &trig_stationary, trigger_handler))
    		{
    			printk("SENSOR_TRIG_STATIONARY set error\n");
    		}
    	}
    }
    
    static int ext_sensors_accelerometer_threshold_set(double threshold, bool upper)
    {
    	int err, input_value;
    	double range_max_m_s2 = ADXL362_RANGE_MAX_M_S2;
    
    	if ((threshold > range_max_m_s2) || (threshold <= 0.0))
    	{
    		LOG_ERR("Invalid %s threshold value: %f", upper ? "activity" : "inactivity", threshold);
    		return -ENOTSUP;
    	}
    
    	/* Convert threshold value into 11-bit decimal value relative
    	 * to the configured measuring range of the accelerometer.
    	 */
    	threshold = (threshold *
    				 (ADXL362_THRESHOLD_RESOLUTION_DECIMAL_MAX / range_max_m_s2));
    
    	/* Add 0.5 to ensure proper conversion from double to int. */
    	threshold = threshold + 0.5;
    	input_value = (int)threshold;
    
    	if (input_value >= ADXL362_THRESHOLD_RESOLUTION_DECIMAL_MAX)
    	{
    		input_value = ADXL362_THRESHOLD_RESOLUTION_DECIMAL_MAX - 1;
    	}
    	else if (input_value < 0)
    	{
    		input_value = 0;
    	}
    
    	const struct sensor_value data = {
    		.val1 = input_value};
    
    	enum sensor_attribute attr = upper ? SENSOR_ATTR_UPPER_THRESH : SENSOR_ATTR_LOWER_THRESH;
    
    	/* SENSOR_CHAN_ACCEL_XYZ is not supported by the driver in this case. */
    	err = sensor_attr_set(adxl1362_sens,
    						  SENSOR_CHAN_ACCEL_X,
    						  attr,
    						  &data);
    	if (err)
    	{
    		LOG_ERR("Failed to set accelerometer threshold value");
    		LOG_ERR("Device: %s, error: %d",
    				adxl1362_sens->name, err);
    
    		return err;
    	}
    	return 0;
    }

    And my proj.conf:

    CONFIG_CBPRINTF_FP_SUPPORT=y
    
    # 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_ACCEL_RANGE_2G=y
    CONFIG_ADXL362_ACCEL_ODR_400=y

    The serial terminal logs

    :

    As I move the device, multiple unknown triggers fire at once (maybe like 5 or something).  Also, some unknown triggers are triggered once when the device is kept still.

    I have looked through the code multiple times and cannot find any issues regarding the triggers. I believe they are set correctly:

    	if (IS_ENABLED(CONFIG_ADXL362_TRIGGER))
    	{
    		printk("Configuring triggers\n");
    		struct sensor_trigger trig_motion = {
    			.chan = SENSOR_CHAN_ACCEL_XYZ,
    			.type = SENSOR_TRIG_MOTION,
    		};
    		if (sensor_trigger_set(adxl1362_sens, &trig_motion, trigger_handler))
    		{
    			printk("SENSOR_TRIG_MOTION set error\n");
    		}
    
    		struct sensor_trigger trig_stationary = {
    			.chan = SENSOR_CHAN_ACCEL_XYZ,
    			.type = SENSOR_TRIG_STATIONARY,
    		};
    		if (sensor_trigger_set(adxl1362_sens, &trig_stationary, trigger_handler))
    		{
    			printk("SENSOR_TRIG_STATIONARY set error\n");
    		}
    	}

    static void trigger_handler(const struct device *dev, const struct sensor_trigger *trig)
    {
    	int err = 0;
    	switch (trig->type)
    	{
    
    	case SENSOR_TRIG_MOTION:
    	{
    		if (sensor_sample_fetch(dev) < 0)
    		{
    			printk("Sample fetch error \n");
    			return;
    		}
    
    		err = sensor_channel_get(dev, SENSOR_CHAN_ACCEL_XYZ, &data[0]);
    		if (err)
    		{
    			printk("sensor_channel_get, error: %d \n", err);
    			return;
    		}
    
    		printk("x: %.1f, y: %.1f, z: %.1f (m/s^2)\n",
    			   sensor_value_to_double(&data[0]),
    			   sensor_value_to_double(&data[1]),
    			   sensor_value_to_double(&data[2]));
    
    		printk("Inactivity detected\n");
    		break;
    	}
    
    	case SENSOR_TRIG_STATIONARY:
    	{
    		if (sensor_sample_fetch(dev) < 0)
    		{
    			printk("Sample fetch error \n");
    			return;
    		}
    
    		err = sensor_channel_get(dev, SENSOR_CHAN_ACCEL_XYZ, &data[0]);
    		if (err)
    		{
    			printk("sensor_channel_get, error: %d \n", err);
    			return;
    		}
    
    		printk("x: %.1f, y: %.1f, z: %.1f (m/s^2)\n",
    			   sensor_value_to_double(&data[0]),
    			   sensor_value_to_double(&data[1]),
    			   sensor_value_to_double(&data[2]));
    
    		printk("Activity detected\n");
    		break;
    	}
    
    	default:
    		printk("Unknown trigger %u \n",(trig->type));
    	}
    }

    I have also updated the code in the repository so you can have a look at the project files: https://github.com/krupis/thingy91_motion_trigger

    Perhaps you are able to detect any mistakes in my code and have a clue why I would be getting this unknown trigger even though I have defined my motion and stationary triggers

Reply
  • Thanks for reply. I had a look at the source code that you have suggested. I have learnt a couple of things:

    1. Only 2 triggers are created for the ADXL362 (motion and stationary). No trigger for data ready for some reason

    2. SENSOR_CHAN_ACCEL_XYZ is not supported for setting UPPER_THRESH and LOWER_THRESH

    After adjusting the source code a little bit. See my current full source 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>
    
    #define LOG_LEVEL CONFIG_LOG_DEFAULT_LEVEL
    #include <zephyr/logging/log.h>
    LOG_MODULE_REGISTER(main);
    
    struct sensor_value data[3];
    
    /* Convert to s/m2 depending on the maximum measured range used for adxl362. */
    #if IS_ENABLED(CONFIG_ADXL362_ACCEL_RANGE_2G)
    #define ADXL362_RANGE_MAX_M_S2 19.6133
    #elif IS_ENABLED(CONFIG_ADXL362_ACCEL_RANGE_4G)
    #define ADXL362_RANGE_MAX_M_S2 39.2266
    #elif IS_ENABLED(CONFIG_ADXL362_ACCEL_RANGE_8G)
    #define ADXL362_RANGE_MAX_M_S2 78.4532
    #endif
    
    /* This is derived from the sensitivity values in the datasheet. */
    #define ADXL362_THRESHOLD_RESOLUTION_DECIMAL_MAX 2000
    
    #if IS_ENABLED(CONFIG_ADXL362_ACCEL_ODR_12_5)
    #define ADXL362_TIMEOUT_MAX_S 5242.88
    #elif IS_ENABLED(CONFIG_ADXL362_ACCEL_ODR_25)
    #define ADXL362_TIMEOUT_MAX_S 2621.44
    #elif IS_ENABLED(CONFIG_ADXL362_ACCEL_ODR_50)
    #define ADXL362_TIMEOUT_MAX_S 1310.72
    #elif IS_ENABLED(CONFIG_ADXL362_ACCEL_ODR_100)
    #define ADXL362_TIMEOUT_MAX_S 655.36
    #elif IS_ENABLED(CONFIG_ADXL362_ACCEL_ODR_200)
    #define ADXL362_TIMEOUT_MAX_S 327.68
    #elif IS_ENABLED(CONFIG_ADXL362_ACCEL_ODR_400)
    #define ADXL362_TIMEOUT_MAX_S 163.84
    #endif
    
    #define ADXL362_TIMEOUT_RESOLUTION_MAX 65536
    
    #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 int ext_sensors_accelerometer_threshold_set(double threshold, bool upper);
    
    static void trigger_handler(const struct device *dev, const struct sensor_trigger *trig)
    {
    	int err = 0;
    	switch (trig->type)
    	{
    
    	case SENSOR_TRIG_MOTION:
    	{
    		if (sensor_sample_fetch(dev) < 0)
    		{
    			printk("Sample fetch error \n");
    			return;
    		}
    
    		err = sensor_channel_get(dev, SENSOR_CHAN_ACCEL_XYZ, &data[0]);
    		if (err)
    		{
    			printk("sensor_channel_get, error: %d \n", err);
    			return;
    		}
    
    		printk("x: %.1f, y: %.1f, z: %.1f (m/s^2)\n",
    			   sensor_value_to_double(&data[0]),
    			   sensor_value_to_double(&data[1]),
    			   sensor_value_to_double(&data[2]));
    
    		printk("Inactivity detected\n");
    		break;
    	}
    
    	case SENSOR_TRIG_STATIONARY:
    	{
    		if (sensor_sample_fetch(dev) < 0)
    		{
    			printk("Sample fetch error \n");
    			return;
    		}
    
    		err = sensor_channel_get(dev, SENSOR_CHAN_ACCEL_XYZ, &data[0]);
    		if (err)
    		{
    			printk("sensor_channel_get, error: %d \n", err);
    			return;
    		}
    
    		printk("x: %.1f, y: %.1f, z: %.1f (m/s^2)\n",
    			   sensor_value_to_double(&data[0]),
    			   sensor_value_to_double(&data[1]),
    			   sensor_value_to_double(&data[2]));
    
    		printk("Activity detected\n");
    		break;
    	}
    
    	default:
    		printk("Unknown trigger %u \n",(trig->type));
    	}
    }
    
    void main(void)
    {
    
    	if (!device_is_ready(adxl1362_sens))
    	{
    		printk("sensor: device %s not ready.\n", adxl1362_sens->name);
    		return 0;
    	}
    
    	ext_sensors_accelerometer_threshold_set(3, true);
    	ext_sensors_accelerometer_threshold_set(0.5, false);
    
    	if (IS_ENABLED(CONFIG_ADXL362_TRIGGER))
    	{
    		printk("Configuring triggers\n");
    		struct sensor_trigger trig_motion = {
    			.chan = SENSOR_CHAN_ACCEL_XYZ,
    			.type = SENSOR_TRIG_MOTION,
    		};
    		if (sensor_trigger_set(adxl1362_sens, &trig_motion, trigger_handler))
    		{
    			printk("SENSOR_TRIG_MOTION set error\n");
    		}
    
    		struct sensor_trigger trig_stationary = {
    			.chan = SENSOR_CHAN_ACCEL_XYZ,
    			.type = SENSOR_TRIG_STATIONARY,
    		};
    		if (sensor_trigger_set(adxl1362_sens, &trig_stationary, trigger_handler))
    		{
    			printk("SENSOR_TRIG_STATIONARY set error\n");
    		}
    	}
    }
    
    static int ext_sensors_accelerometer_threshold_set(double threshold, bool upper)
    {
    	int err, input_value;
    	double range_max_m_s2 = ADXL362_RANGE_MAX_M_S2;
    
    	if ((threshold > range_max_m_s2) || (threshold <= 0.0))
    	{
    		LOG_ERR("Invalid %s threshold value: %f", upper ? "activity" : "inactivity", threshold);
    		return -ENOTSUP;
    	}
    
    	/* Convert threshold value into 11-bit decimal value relative
    	 * to the configured measuring range of the accelerometer.
    	 */
    	threshold = (threshold *
    				 (ADXL362_THRESHOLD_RESOLUTION_DECIMAL_MAX / range_max_m_s2));
    
    	/* Add 0.5 to ensure proper conversion from double to int. */
    	threshold = threshold + 0.5;
    	input_value = (int)threshold;
    
    	if (input_value >= ADXL362_THRESHOLD_RESOLUTION_DECIMAL_MAX)
    	{
    		input_value = ADXL362_THRESHOLD_RESOLUTION_DECIMAL_MAX - 1;
    	}
    	else if (input_value < 0)
    	{
    		input_value = 0;
    	}
    
    	const struct sensor_value data = {
    		.val1 = input_value};
    
    	enum sensor_attribute attr = upper ? SENSOR_ATTR_UPPER_THRESH : SENSOR_ATTR_LOWER_THRESH;
    
    	/* SENSOR_CHAN_ACCEL_XYZ is not supported by the driver in this case. */
    	err = sensor_attr_set(adxl1362_sens,
    						  SENSOR_CHAN_ACCEL_X,
    						  attr,
    						  &data);
    	if (err)
    	{
    		LOG_ERR("Failed to set accelerometer threshold value");
    		LOG_ERR("Device: %s, error: %d",
    				adxl1362_sens->name, err);
    
    		return err;
    	}
    	return 0;
    }

    And my proj.conf:

    CONFIG_CBPRINTF_FP_SUPPORT=y
    
    # 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_ACCEL_RANGE_2G=y
    CONFIG_ADXL362_ACCEL_ODR_400=y

    The serial terminal logs

    :

    As I move the device, multiple unknown triggers fire at once (maybe like 5 or something).  Also, some unknown triggers are triggered once when the device is kept still.

    I have looked through the code multiple times and cannot find any issues regarding the triggers. I believe they are set correctly:

    	if (IS_ENABLED(CONFIG_ADXL362_TRIGGER))
    	{
    		printk("Configuring triggers\n");
    		struct sensor_trigger trig_motion = {
    			.chan = SENSOR_CHAN_ACCEL_XYZ,
    			.type = SENSOR_TRIG_MOTION,
    		};
    		if (sensor_trigger_set(adxl1362_sens, &trig_motion, trigger_handler))
    		{
    			printk("SENSOR_TRIG_MOTION set error\n");
    		}
    
    		struct sensor_trigger trig_stationary = {
    			.chan = SENSOR_CHAN_ACCEL_XYZ,
    			.type = SENSOR_TRIG_STATIONARY,
    		};
    		if (sensor_trigger_set(adxl1362_sens, &trig_stationary, trigger_handler))
    		{
    			printk("SENSOR_TRIG_STATIONARY set error\n");
    		}
    	}

    static void trigger_handler(const struct device *dev, const struct sensor_trigger *trig)
    {
    	int err = 0;
    	switch (trig->type)
    	{
    
    	case SENSOR_TRIG_MOTION:
    	{
    		if (sensor_sample_fetch(dev) < 0)
    		{
    			printk("Sample fetch error \n");
    			return;
    		}
    
    		err = sensor_channel_get(dev, SENSOR_CHAN_ACCEL_XYZ, &data[0]);
    		if (err)
    		{
    			printk("sensor_channel_get, error: %d \n", err);
    			return;
    		}
    
    		printk("x: %.1f, y: %.1f, z: %.1f (m/s^2)\n",
    			   sensor_value_to_double(&data[0]),
    			   sensor_value_to_double(&data[1]),
    			   sensor_value_to_double(&data[2]));
    
    		printk("Inactivity detected\n");
    		break;
    	}
    
    	case SENSOR_TRIG_STATIONARY:
    	{
    		if (sensor_sample_fetch(dev) < 0)
    		{
    			printk("Sample fetch error \n");
    			return;
    		}
    
    		err = sensor_channel_get(dev, SENSOR_CHAN_ACCEL_XYZ, &data[0]);
    		if (err)
    		{
    			printk("sensor_channel_get, error: %d \n", err);
    			return;
    		}
    
    		printk("x: %.1f, y: %.1f, z: %.1f (m/s^2)\n",
    			   sensor_value_to_double(&data[0]),
    			   sensor_value_to_double(&data[1]),
    			   sensor_value_to_double(&data[2]));
    
    		printk("Activity detected\n");
    		break;
    	}
    
    	default:
    		printk("Unknown trigger %u \n",(trig->type));
    	}
    }

    I have also updated the code in the repository so you can have a look at the project files: https://github.com/krupis/thingy91_motion_trigger

    Perhaps you are able to detect any mistakes in my code and have a clue why I would be getting this unknown trigger even though I have defined my motion and stationary triggers

Children
  • UPDATE

    I have figured out what was an issue regarding unknown triggers.

    The issue was I was setting the upper and lower threshold triggers before setting up trigger handlers

    The below is not correct:

    void main(void)
    {
    
    	if (!device_is_ready(adxl1362_sens))
    	{
    		printk("sensor: device %s not ready.\n", adxl1362_sens->name);
    		return 0;
    	}
    
    	if (IS_ENABLED(CONFIG_ADXL362_TRIGGER))
    	{
    		ext_sensors_accelerometer_threshold_set(10.0, true);
    		ext_sensors_accelerometer_threshold_set(1.5, false);
    	
    		printk("Configuring triggers\n");
    		struct sensor_trigger trig_motion = {
    			.chan = SENSOR_CHAN_ACCEL_XYZ,
    			.type = SENSOR_TRIG_MOTION,
    		};
    		if (sensor_trigger_set(adxl1362_sens, &trig_motion, trigger_handler))
    		{
    			printk("SENSOR_TRIG_MOTION set error\n");
    		}
    
    		struct sensor_trigger trig_stationary = {
    			.chan = SENSOR_CHAN_ACCEL_XYZ,
    			.type = SENSOR_TRIG_STATIONARY,
    		};
    		if (sensor_trigger_set(adxl1362_sens, &trig_stationary, trigger_handler))
    		{
    			printk("SENSOR_TRIG_STATIONARY set error\n");
    		}
    
    	}
    }

    This is correct:

    void main(void)
    {
    
    	if (!device_is_ready(adxl1362_sens))
    	{
    		printk("sensor: device %s not ready.\n", adxl1362_sens->name);
    		return 0;
    	}
    
    	if (IS_ENABLED(CONFIG_ADXL362_TRIGGER))
    	{
    		printk("Configuring triggers\n");
    		struct sensor_trigger trig_motion = {
    			.chan = SENSOR_CHAN_ACCEL_XYZ,
    			.type = SENSOR_TRIG_MOTION,
    		};
    		if (sensor_trigger_set(adxl1362_sens, &trig_motion, trigger_handler))
    		{
    			printk("SENSOR_TRIG_MOTION set error\n");
    		}
    
    		struct sensor_trigger trig_stationary = {
    			.chan = SENSOR_CHAN_ACCEL_XYZ,
    			.type = SENSOR_TRIG_STATIONARY,
    		};
    		if (sensor_trigger_set(adxl1362_sens, &trig_stationary, trigger_handler))
    		{
    			printk("SENSOR_TRIG_STATIONARY set error\n");
    		}
    
    		ext_sensors_accelerometer_threshold_set(10.0, true);
    		ext_sensors_accelerometer_threshold_set(1.5, false);
    	}
    }

    So now I am able to trigger MOTION and STATIONARY events:

  • Hi,

    Your experiments and code examples were quite valuable for me.

    There still is a detail in the setup that I don't quite understand.
    You're monitoring for the beginning of a movement with the SENSOR_TRIG_MOTION trigger and then immediately register the end of the movement with the SENSOR_TRIG_STATIONARY trigger.

    Are these two events the only thing of interest for your implementation? I mean, in an example closer to the real world, sampling of continuous movement after the initial registering is also of interest. And sampling usually stops when the stationary trigger is met.

    I'm asking this because I'm trying to implement the described scenario (measuring vibrations, where samples are enclosed by the two said triggers) but two things bug me (I'm not very experienced in embedded programming, yet):

    1. 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);
    2. 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)?

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

    Best Regards,

    Ivan

  • 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

Related