/
Using LPS22HH and LIS2DW12 Sensors

Using LPS22HH and LIS2DW12 Sensors

1. Overview

This application note describes how to configure LPS22HH and LIS2DW12 sensors on the SOM-NRF9151 baseboard in Zephyr BSP and use them in the user application.

2. LPS22HH and LIS2DW12 sensors in Zephyr BSP

2.1. Configuring LPS22HH Barometer

The LPS22HH device is declared in nrf9151som_nrf9151_ns.dts as a child node of the i2c2 bus:

&i2c2 { status = "okay"; lps22hh: lps22hh@5c { compatible = "st,lps22hh"; reg = <0x5c>; drdy-gpios = <&gpio0 5 GPIO_ACTIVE_HIGH>; }; };

This node configures the following parameters:

Parameter

Value

Description

Parameter

Value

Description

compatible

"st,lps22hh"

Enables the lps22hh.c driver and associates it with the device.

reg

<0x5c>

LPS22HH slave address on the I2C bus, which will be used by the driver to communicate with the barometer.

drdy-gpios

<&gpio0 5 GPIO_ACTIVE_HIGH>

Host SoC GPIO, that will be used for LPS22HH interrupts.

2.2. Configuring LIS2DW12 Accelerometer

The LIS2DW12 device is declared in nrf9151som_nrf9151_ns.dts as a child node of the i2c2 bus:

&i2c2 { status = "okay"; lis2dw12: lis2dw12@19 { compatible = "st,lis2dw12"; reg = <0x19>; irq-gpios = <&gpio0 7 GPIO_ACTIVE_HIGH>, <&gpio0 6 GPIO_ACTIVE_HIGH>; int-pin = <1>; tap-threshold = <1>, <1>, <1>; }; };

This node configures the following parameters:

Parameter

Value

Description

Parameter

Value

Description

compatible

"st,lis2dw12"

Enables the lis2dw12.c driver and associates it with the device.

reg

<0x19>

LIS2DW12 slave address on the I2C bus, which will be used by the driver to communicate with the IMU.

irq-gpios

<&gpio0 7 GPIO_ACTIVE_HIGH>, <&gpio0 6 GPIO_ACTIVE_HIGH>

Host SoC GPIO, that will be used for LIS2DW12 interrupts.

int-pin

<1>

Select DRDY pin number (1 or 2).

This number represents which of the two interrupt pins (INT1 or INT2) the drdy line is attached to. This property is not mandatory and if not present it defaults to 1 which is the configuration at power-up.

tap-threshold

<1>, <1>, <1>

Tap X/Y/Z axes threshold. Default is power-up configuration.

Thresholds to start the tap-event detection procedure on the X/Y/Z axes.
Threshold values for each axis are unsigned 5-bit corresponding to a 2g acceleration full-scale range. A threshold value equal to zero corresponds to disable the tap detection on that axis.

For example, if you want to set the threshold for X to 12, for Z to 14 and want to disable tap detection on Y, you should specify in Device Tree, which is equivalent to X = 12 * 2g/32 = 750mg and Z = 14 * 2g/32 = 875mg.

3. Controlling Sensors in User Application

3.1. Sensor C-binding API

The Customized Asset Tracker v2 application makes use of the Zephyr Sensor API, which is defined in zephyr/drivers/sensor.h:

Function

Description

Comments

__syscall int sensor_attr_set(const struct device *dev, enum sensor_channel chan, enum sensor_attribute attr, const struct sensor_value *val);

Set an attribute for a sensor.

dev - pointer to the sensor device.

chan - the channel the attribute belongs to, if any. Some attributes may only be set for all channels of a device, depending on device capabilities.

attr - the attribute to set.

val - the value to set the attribute to.

Returns 0 if successful, negative errno code if failure.

__syscall int sensor_attr_get(const struct device *dev, enum sensor_channel chan, enum sensor_attribute attr, const struct sensor_value *val);

Get an attribute for a sensor.

dev - pointer to the sensor device.

chan - the channel the attribute belongs to, if any. Some attributes may only be set for all channels of a device, depending on device capabilities.

attr - the attribute to get.

val - pointer to where to store the attribute

Returns 0 if successful, negative errno code if failure.

__syscall int sensor_sample_fetch(const struct device *dev);

Fetch a sample from the sensor and store it in an internal driver buffer.

dev - pointer to the sensor device.

Returns 0 if successful, negative errno code if failure.

Read all of a sensor's active channels and, if necessary, perform any additional operations necessary to make the values useful. The user may then get individual channel values by calling sensor_channel_get().

The function blocks until the fetch operation is complete. Since the function communicates with the sensor device, it is unsafe to call it in an ISR if the device is connected via I2C or SPI.

__syscall int sensor_sample_fetch_chan(const struct device *dev, enum sensor_channel type);

Fetch a sample from the sensor and store it in an internal driver buffer.

dev - pointer to the sensor device.

type - the channel that needs updated.

Returns 0 if successful, negative errno code if failure.

Read and compute compensation for one type of sensor data (magnetometer, accelerometer, etc). The user may then get individual channel values by sensor_channel_get().

This is mostly implemented by multi function devices enabling reading at different sampling rates. The function blocks until the fetch operation is complete.

Since the function communicates with the sensor device, it is unsafe to call it in an ISR if the device is connected via I2C or SPI.

__syscall int sensor_channel_get(const struct device *dev, enum sensor_channel chan, struct sensor_value *val);

Get a reading from a sensor device.

dev - pointer to the sensor device
chan - the channel to read
val - pointer to the variable to store the value.

Return a useful value for a particular channel, from the driver's internal data. Before calling this function, a sample must be obtained by calling sensor_sample_fetch() or sensor_sample_fetch_chan(). It is guaranteed that two subsequent calls of this function for the same channels will yield the same value, if sensor_sample_fetch() or sensor_sample_fetch_chan() has not been called in the meantime.

static inline int sensor_trigger_set(const struct device *dev, const struct sensor_trigger *trig, sensor_trigger_handler_t handler);

Activate a sensor's trigger and set the trigger handle.

dev - pointer to the sensor device.
trig - the trigger to activate.
handler - the function that should be called when the trigger fires.

Returns 0 if successful, negative errno code if failure.

The handler will be called from a thread, so I2C or SPI operations are safe. However, the thread's stack is limited and defined by the driver. It is currently up to the caller to ensure that the handler does not overflow the stack.

The user-allocated trigger will be stored by the driver as a pointer, rather than a copy, and passed back to the handler. This enables the handler to use CONTAINER_OF to retrieve a context pointer when the trigger is embedded in a larger struct and requires that the trigger is not allocated on the stack.

The Customized Asset Tracker v2 application also implements C-binding helper API to read the sensors data and configure external sensors in ext_sensors.* and src/addons/sensors:

Function

Description

Comments

int ext_sensors_accelerometer_threshold_set(double threshold, bool upper);

Set the threshold that triggers callback on accelerometer data.

threshold - Variable that sets the accelerometer threshold value in m/s2. Must be a value larger than 0 and smaller than the configured range (default 2G: ~= 19.6133 m/s2).
upper - Flag indicating if the given threshold is for activity detection (true) or inactivity detection (false).

Returns 0 on success or negative error value on failure.

int ext_sensors_accelerometer_trigger_callback_set(bool enable);

Enable or disable accelerometer trigger handler.

enable - Flag that enables or disables callback triggers from the accelerometer.

Returns 0 on success or negative error value on failure.

int ext_sensors_accelerometer_set_full_scale(void);

Set accelerometer full scale.

Returns 0 on success or negative error value on failure.

int ext_sensors_temperature_get(double *temp);

Get temperature from library.

temp - Pointer to variable containing temperature in celcius.

Returns 0 on success or negative error value on failure.

int ext_sensors_pressure_get(double *press);

Get pressure from library.

press - Pointer to variable containing atmospheric pressure in kilopascal.

Returns 0 on success or negative error value on failure.

void lis2dw12_init();

Initialize tap the LIS2DW12 accelerometer.

 

void lis2dw12_configure_tap_handler(const struct device *dev);

Initialize tap handler for LIS2DW12 accelerometer.

dev - Pointer to the LIS2DW12 device structure.

void lps22hh_trigger_init()

Initialize trigger for the LPS22HH barometer.

 

3.2. LPS22HH Shell Commands

The Customized Asset Tracker v2 application provides a set of Zephyr shell commands for LPS22HH barometer, defined in the lps22hh_shell.c file:

Command

Parameters

Comments

lps22hh get

 

Prints out LPS22HH readings to the serial console in the following format:

uart:~$ lps22hh get Pressure: <pressure> kPa Temperature: <temp> C

lps22hh set

<1..200>

Set LPS22HH sampling frequency (1..200)

lps22hh set

<lc|ln>

Set LPS22HH power mode (lc - low current, ln - low noise)

3.3. LIS2DW12 Shell Commands

The Customized Asset Tracker v2 application provides a set of Zephyr shell commands for LIS2DW12 accelerometer, defined in the lis2dw12_shell.c file:

Command

Parameters

Comments

lis2dw12 get

 

Prints out LIS2DW12 readings to the serial console in the following format:

uart:~$ lis2dw12 get accel x:<X axis acceleration> ms/2 y:<Y axis acceleration> ms/2 z:<Z axis acceleration> ms/2 Trigger count: 0

lis2dw12 set

<Hz>

Set LIS2DW12 IMU sampling frequency (default is 12)

lis2dw12 set

<1..4>

Set LIS2DW12 IMU power mode (1 - high performance, 2..4 low power mode)

lis2dw12 init

 

Set LIS2DW12 data-ready handler on INT2

lis2dw12 init_tap

 

Set LIS2DW12 tap handler on INT1

lis2dw12 init

 

Remove LIS2DW12 data-ready handler from INT2

3.4. Sending LPS22HH Data to nRF Cloud

The LPS22HH data is being collected and sent to the nRF Cloud by the sensor and data modules, defined in sensor_module.c and data_module.c:

  1. When the sensor module receives APP_EVT_DATA_GET, it calls the environmental_data_get() function to collect accelerometer data that should be sent to nRF Cloud.

  2. environmental_data_get() reads the temperature and pressure readings by calling ext_sensors_temperature_get() and ext_sensors_pressure_get(), then it generates the sensor_module_event, which contains the accelerometer and gyroscope data, timestamp and SENSOR_EVT_ENVIRONMENTAL_DATA_READY event type.

  3. The data module receives the sensor_module_event from the sensor module and enqueues the IMU data to be sent to nRF Cloud.

The IMU data is being sent to the nRF Cloud in JSON format via MQTT transport:

{ "appId": "TEMP", "messageType": "DATA", "ts": 1743807100960, "data": "34.86" },

After receiving the message from the device, nRF Cloud converts it into the following JSON structure:

{ "topic": "prod/8ac47cbc-cc7f-43d0-bb92-de294d73db3e/m/d/50343959-3733-44f0-8015-1a1481b18ed5/d2c", "deviceId": "50343959-3733-44f0-8015-1a1481b18ed5", "receivedAt": "2025-04-04T22:51:43.823Z", "message": { "appId": "TEMP", "messageType": "DATA", "ts": 1743807100960, "data": "34.86" }, "tenantId": "8ac47cbc-cc7f-43d0-bb92-de294d73db3e" },

The event and data structures used by the sensor and data modules have the following definitions:

/** @brief Sensor module event. */ struct sensor_module_event { /** Sensor module application event header. */ struct app_event_header header; /** Sensor module event type. */ enum sensor_module_event_type type; union { /** Variable that contains sensor readings. */ struct sensor_module_data sensors; /** Variable that contains acceleration data. */ struct sensor_module_accel_data accel; /** Variable that contains impact data. */ struct sensor_module_impact_data impact; /** Variable that contains battery level data. */ struct sensor_module_batt_lvl_data bat; /** Module ID, used when acknowledging shutdown requests. */ uint32_t id; /** Code signifying the cause of error. */ int err; } data; };
/** @brief Structure used to provide environmental data. */ struct sensor_module_data { /** Uptime when the data was sampled. */ int64_t timestamp; /** Temperature in Celsius degrees. */ double temperature; /** Humidity in percentage. */ double humidity; /** Atmospheric pressure in kilopascal. */ double pressure; /** BSEC air quality in Indoor-Air-Quality (IAQ) index. * If -1, the value is not provided. */ int bsec_air_quality; };
struct cloud_data_sensors { /** Environmental sensors timestamp. UNIX milliseconds. */ int64_t env_ts; /** Temperature in celcius. */ double temperature; /** Humidity level in percentage. */ double humidity; /** Atmospheric pressure in kilopascal. */ double pressure; /** BSEC Air quality in Indoor-Air-Quality (IAQ) index. * If -1, the value is not provided. */ int bsec_air_quality; /** Flag signifying that the data entry is to be encoded. */ bool queued : 1; };

4. Validating Sensor Operations

4.1. Validating LPS22HH Readings and Interrupts:

  1. From the nRF9151 serial console, read the barometer data using the lps22hh get command.

  2. Observe pressure change data from recent measurement and temperature output on console:

    uart:~$ lps22hh get Pressure: 100.741 kPa Temperature: 25.67 C uart:~$ lps22hh get Pressure: 100.748 kPa Temperature: 25.66 C
  3. From the nRF9151 serial console, read the GPIO interrupt counter for P0.05:

    uart:~$ gpio_interrupt 0.05 P0.05: 0
  4. Trigger a barometer high pressure event, as follows:

    1. Place the board with the barometer inside a plastic bag.

    2. Insert the pipe from an air can into the bag.

    3. Seal the bag firmly using the scotch tape to prevent any air from escaping.

    4. Trigger the air can to release air into the bag, gradually increasing the pressure inside.

    5. Press on the bag to further increase the pressure surrounding the barometer.

  5. Read the GPIO interrupt counter for P0.05; verify that the number of interrupts has increased by 1:

    uart:~$ gpio_interrupt 0.05 P0.05: 1

4.2. Validating Accelerometer Readings and Interrupts:

  1. From the nRF9151 serial console, read the accelerometer data using the lis2dw12 get command:

  2. Observe accelerometer data output on console:

    uart:~$ lis2dw12 get accel x:-0.306281 ms/2 y:0.459421 ms/2 z:9.801001 ms/2 Trigger count: 0
  3. Read the GPIO interrupt counter for P0.7:

    uart:~$ gpio_interrupt 0.7 P0.7: 0
  4. Tap on the accelerometer to trigger an accelerometer event.

  5. Read the GPIO interrupt counter for P0.7; verify that the number of interrupts has increased by 1:

    uart:~$ gpio_interrupt 1.8 P1.8: 1
  6. Read the GPIO interrupt counter for P0.6:

    uart:~$ gpio_interrupt 0.6 P0.6: 0
  7. Re-configure the accelerometer to trigger data-ready events on INT2:

    uart:~$ lis2dw12 init Setting data-ready handler on INT2
  8. Wait 10 seconds.

  9. Read the GPIO interrupt counter for P0.6; verify that the number of interrupts has increased.

    uart:~$ gpio_interrupt 0.6 P0.6: 9
  10. Reset the board by long-pressing (a 10+ seconds press) the user push-button.

4.3. Validating LPS22HH Integration with nRF Cloud

  1. Check the board uuid from the nRF9151 console (it is assumed that the nRF Device provisioning procedure has been performed earlier):

    uart:~$ nrf_provisioning uuid 50343959-3733-4c71-806b-202470cee0bf
  2. Check that the board has successfully connected to the cloud over LTE:

    [00:00:02.824,615] <inf> app_event_manager: MODEM_EVT_LTE_CONNECTING [00:00:04.110,168] <inf> app_event_manager: MODEM_EVT_LTE_CELL_UPDATE [00:00:15.095,916] <inf> app_event_manager: MODEM_EVT_LTE_CONNECTED [00:00:15.234,283] <inf> cloud_module: DEVICE CERTIFICATE IS PRESENT [00:00:15.236,907] <inf> app_event_manager: CLOUD_EVT_CONNECTING [00:00:15.237,426] <inf> app_event_manager: MODEM_EVT_LTE_PSM_UPDATE [00:00:15.237,915] <inf> app_event_manager: DATA_EVT_DATE_TIME_OBTAINED [00:00:22.080,871] <inf> net_mqtt: Connect completed
  3. Login to the nRF Cloud and go to the DEVICE MANAGEMENT / Devices page. Find the board by the uuid ID and open the board page.

  4. Click the Manage Cards button and check the Device Data checkbox so that the Device Data card is displayed on the board page.

  5. In the Device Data card, check the Last connected attribute to make sure that the board has recently connected to the cloud.

  6. Wait 110 seconds for the device data to be updated on the cloud. The default timeout of 110 seconds can be reduced for convenience: click the View Config button, then Edit Configuration. If activeMode is false then the timeout is no greater than movementResolution, otherwise, it is no greater than activeWaitTime.

  7. From the nRF9151 console, confirm that the device data have been sent to the cloud:

    [00:02:16.997,283] <inf> app_event_manager: DATA_EVT_DATA_READY [00:02:17.002,197] <inf> app_event_manager: DATA_EVT_DATA_SEND_BATCH [00:02:17.002,868] <inf> app_event_manager: CLOUD_EVT_DATA_SEND_QOS
  8. In the Device Data card on the nRF Cloud, verify that the Temperature and Air Pressure attributes has just been updated and has a proper value:

    image-20250228-144540.png
  9. Click the Show Temperature card icon to see the graphical representation of the temperature values on the time scale:

    image-20250228-144324.png
  10. Click the View historical data icon and select appropriate time interval to see the temperature history:

    image-20250228-144446.png
  11. Click the Show Air Pressure card icon to see the graphical representation of the air pressure values on the time scale:

    image-20250228-144617.png
  12. Click the View historical data icon and select appropriate time interval to see the air pressure history:

    image-20250228-144703.png

4.4. Validating Accelerometer Integration with nRF Cloud

  1. Login to the nRF Cloud and go to the DEVICE MANAGEMENT / Devices page. Find the board by the uuid ID and open the board page.

  2. Press the “View Config” button.

  3. Press “Edit configuration” in “Device Configuration” window opened:

    image-20250228-144944.png
  4. Change the "accThreshAct" and “accThreshInAct” parameters to 1:

    image-20250228-145330.png
  5. Press the “Commit” button.

  6. Wait for the board to connect to the cloud and download new configuration. Note that this process may take up to 5 minutes, since the Asset Tracker by default is configured to connect to the cloud once in 300 seconds. The “Reported” field will update when the board applies the new configuration.

  7. Verify updated sensors data arrived on nRF9151 console:

    [00:00:16.598,602] <inf> app_event_manager: CLOUD_EVT_CONFIG_RECEIVED ... [00:00:16.604,461] <inf> sensor_module: Received sensors configuration data
  8. Shake the board.

  9. Observe the activity detected message on nRF5340 console:

    [00:02:36.250,457] <inf> ext_sensors: Activity detected
  10. Observe the activity detected message on nRF9151 console:

    [00:02:42.888,946] <inf> app_event_manager: SENSOR_EVT_MOVEMENT_ACTIVITY_DETECTED

Related content