Interfacing nRF91xx with ADXL367 over SPI using Zephyr RTOS
If you’re working with Nordic’s nRF91xx series and want to add motion sensing, the Analog Devices ADXL367 is a fantastic choice. It’s an…
Interfacing nRF91xx with ADXL367 over SPI using Zephyr RTOS
If you’re working with Nordic’s nRF91xx series and want to add motion sensing, the Analog Devices ADXL367 is a fantastic choice. It’s an ultra-low-power, three-axis accelerometer, perfect for applications like wearables, asset tracking, and always-on health monitoring.
In this guide, we’ll set up SPI communication between the nRF91xx and ADXL367 using Zephyr RTOS APIs, verify the connection, and read real motion data from the sensor.
You can use any of the nrf91xx. In my Case, I am using the nrf9151 Stratus Pro from Conexio.
Why SPI with ADXL367?
The ADXL367 supports both I²C and SPI, but SPI offers:
- Faster throughput for continuous data streams.
- Full-duplex transfers.
- Better signal integrity at higher speeds.
- Straightforward multi-sensor integration with separate chip-select lines.
👉 Reference: ADXL367 Datasheet (Analog Devices)
Hardware Setup Connections (nRF91xx ↔ ADXL367)
- SCK → SPI SCK
- MOSI → SPI MOSI
- MISO → SPI MISO
- CS → Chip Select (active low)
- VDD/VDDIO → 1.8V / 3.3V power (match ADXL367 supply)
- GND → Ground
For nRF9151 Stratus Pro
- The Stratus Pro breaks out SPI pins(You can use any, as they are configurable in the .overlay file)on its expansion headers.
- Use Jumper wires or a sensor expansion connector to hook up ADXL367.
- Power the sensor from VDD_3V3 or VDD_1V8, depending on your ADXL367 breakout board.
👉 Reference: Stratus Pro Documentation
Configuring SPI in Zephyr
First, define the SPI bus and device tree overlay (e.g., overlay.dts):
/*
* Author: Shahzaib Ali <shahzaibali.isb@gmail.com>
*
* SPDX-License-Identifier: Apache-2.0
*/
&spi1 {
status = "okay";
pinctrl-0 = <&spi1_default>;
pinctrl-names = "default";
cs-gpios = <&gpio0 6 GPIO_ACTIVE_LOW>;
adxl367: adxl367@0 {
compatible = "adi,adxl367";
reg = <0>;
spi-max-frequency = <8000000>;
};
};
&pinctrl {
spi1_default: spi1_default {
group1 {
psels = <NRF_PSEL(SPIM_MISO, 0, 4)>,
<NRF_PSEL(SPIM_MOSI, 0, 3)>,
<NRF_PSEL(SPIM_SCK, 0, 5)>;
};
};
};
Then you have to add the configs in the prj.conf
CONFIG_STDOUT_CONSOLE=y
CONFIG_GPIO=y
# Adding SPI
CONFIG_SPI=y
# Add the accelerometer
CONFIG_SENSOR=y
CONFIG_ADXL367=y
CONFIG_CBPRINTF_FP_SUPPORT=y
# newlibc for printing floats
CONFIG_NEWLIB_LIBC=y
CONFIG_NEWLIB_LIBC_FLOAT_PRINTF=y
# Enable Zephyr application to be booted by MCUboot
CONFIG_BOOTLOADER_MCUBOOT=y
👉 Reference: Zephyr DeviceTree Guide
Zephyr SPI API Basics
In Zephyr, SPI transfers are handled with the spi_transceive() or spi_write() / spi_read() functions.
/*
* Author: Shahzaib Ali <shahzaibali.isb@gmail.com>
*/
#include <stdio.h>
#include <zephyr/kernel.h>
#include <zephyr/device.h>
#include <zephyr/drivers/sensor.h>
static void fetch_and_display(const struct device *sensor)
{
int ret;
struct sensor_value accel[3];
ret = sensor_sample_fetch(sensor);
if (ret < 0)
{
printf("Sensor sample fetch failed: %d\n", ret);
k_msleep(500);
}
/* Get acceleration data */
ret = sensor_channel_get(sensor, SENSOR_CHAN_ACCEL_XYZ, accel);
if (ret < 0)
{
printf("Cannot read sensor channels: %d\n", ret);
k_msleep(500);
}
/* Display acceleration data */
printf("ACCEL: X=%d.%06d, Y=%d.%06d, Z=%d.%06d\n",
accel[0].val1, accel[0].val2,
accel[1].val1, accel[1].val2,
accel[2].val1, accel[2].val2);
}
int main(void)
{
printf("ADXL367 Accelerometer sensor example for %s\n", CONFIG_BOARD);
const struct device *sensor = DEVICE_DT_GET_ANY(adi_adxl367); // This gets the sensor info from the overlay file, you can add any sensor name here that you want to use
if (sensor == NULL)
{
printf("No ADXL367 device found\n");
return 0;
}
if (!device_is_ready(sensor))
{
printf("ADXL367 device %s is not ready\n", sensor->name);
return 0;
}
printf("Found ADXL367 device: %s\n", sensor->name);
/* Main loop to read and display sensor data */
while (true)
{
/* Fetch the sensor samples */
fetch_and_display(sensor);
k_sleep(K_MSEC(2000));
}
}
👉 Reference: Zephyr SPI API Docs
Reading the Device ID
The ADXL367’s Device ID register (0x00) should return 0xAD.
uint8_t tx_buf[3] = {0x0B, 0x00, 0x00}; // Read command, address 0x00, dummy
uint8_t rx_buf[3] = {0};
struct spi_buf tx_spi_buf = { .buf = tx_buf, .len = sizeof(tx_buf) };
struct spi_buf_set tx_set = { .buffers = &tx_spi_buf, .count = 1 };
struct spi_buf rx_spi_buf = { .buf = rx_buf, .len = sizeof(rx_buf) };
struct spi_buf_set rx_set = { .buffers = &rx_spi_buf, .count = 1 };
int ret = spi_transceive(spi_dev, &spi_cfg, &tx_set, &rx_set);
if (ret == 0) {
printk("Device ID: 0x%02X\n", rx_buf[2]);
} else {
printk("SPI transfer failed\n");
}
Expected output:
Device ID: 0xAD
👉 Reference: ADXL367 Register Map
Reading XYZ Acceleration Data
Registers for acceleration data:
- X LSB: 0x0E
- X MSB: 0x0F
- Y LSB: 0x10
- Y MSB: 0x11
- Z LSB: 0x12
- Z MSB: 0x13
uint8_t tx_buf_data[7] = {0x0B, 0x0E, 0, 0, 0, 0, 0};
uint8_t rx_buf_data[7] = {0};
struct spi_buf tx_spi_buf_data = { .buf = tx_buf_data, .len = sizeof(tx_buf_data) };
struct spi_buf_set tx_set_data = { .buffers = &tx_spi_buf_data, .count = 1 };
struct spi_buf rx_spi_buf_data = { .buf = rx_buf_data, .len = sizeof(rx_buf_data) };
struct spi_buf_set rx_set_data = { .buffers = &rx_spi_buf_data, .count = 1 };
ret = spi_transceive(spi_dev, &spi_cfg, &tx_set_data, &rx_set_data);
if (ret == 0) {
int16_t x = (rx_buf_data[2] | (rx_buf_data[3] << 8));
int16_t y = (rx_buf_data[4] | (rx_buf_data[5] << 8));
int16_t z = (rx_buf_data[6] | (rx_buf_data[7] << 8));
printk("X=%d, Y=%d, Z=%d\n", x, y, z);
}
👉 Reference: Analog Devices ADXL367 GitHub Driver
Debugging Tips
- If you only get
0x00→ check CS polarity and SPI mode. - If readings look noisy → lower SPI frequency (start with 500 kHz).
- Use a logic analyzer to check signals.
- Always confirm Device ID (
0xAD) first.
Real Applications
With nRF91xx handling connectivity (LTE-M / NB-IoT / GNSS) and ADXL367 providing motion data, this setup enables:
- IoT asset tracking with motion triggers.
- Wearable activity monitors.
- Remote vibration monitoring.
- Health and fitness IoT devices.
👉 Reference: nRF Connect SDK Docs
Conclusion
Using Zephyr RTOS, setting up SPI between nRF91xx and ADXL367 is straightforward. Start by verifying the Device ID, configure the accelerometer, and then fetch XYZ acceleration values.
With this working, you’ve built a low-power, LTE-connected motion sensing system ready for IoT and wearable applications.
👉 Reference: Zephyr RTOS Official Docs
메타데이터
- post_id
- 55e4ea70ea44
- slug
- interfacing-nrf91xx-with-adxl367-over-spi-using-zephyr-rtos-55e4ea70ea44
- url
- https://medium.com/@shahzaibali.isb/interfacing-nrf91xx-with-adxl367-over-spi-using-zephyr-rtos-55e4ea70ea44
- canonical_url
- https://medium.com/@shahzaibali.isb/interfacing-nrf91xx-with-adxl367-over-spi-using-zephyr-rtos-55e4ea70ea44
- author_url
- https://medium.com/@shahzaibali.isb
- status
- ok
- fetched_at
- 2026-07-28 02:41:04