Skip to main content

Zephyr RTOS support

The Zephyr RTOS provides support for the sensors and SD-card through a set of drivers. This enables the use of the environmental FeatherWing with e.g. an Icarus IoT Board. Examples on enabling and using the sensors with Zephyr is shown in the following sections.

Enabling the SI7060 temperature sensor

The SI7060 temperature sensor can be used with Zephyr by adding a node to the device-tree overlay of your project and enabling the driver in your configuration file. Follow these steps to enable the SI7060 on the Icarus IoT Board:

  1. Navigate to your project and create/enter the boards directory.
  2. Create a overlay file with the exact name of your board. E.g: actinius_icarus_ns.overlay.
  3. Copy and paste the following device-tree node to the overlay file (change the i2c bus according to your board):
&i2c2 {
si7060: si7060@32 {
compatible = "silabs,si7060";
reg = <0x32>;
};
};
  1. Add the following configuration options to your prj.conf file:
CONFIG_I2C=y
CONFIG_SENSOR=y
CONFIG_SI7060=y

Enabling the OPT3002 ambient light sensor

The OPT3002 ambient light sensor can be used with Zephyr by adding a node to the device-tree overlay of your project and enabling the driver in your configuration file. Follow these steps to enable the OPT3002 on the Icarus IoT Board:

  1. Navigate to your project and create/enter the boards directory.
  2. Create a overlay file with the exact name of your board. E.g: actinius_icarus_ns.overlay.
  3. Copy and paste the following device-tree node to the overlay file (change the i2c bus according to your board):
&i2c2 {
opt3002: opt3002@44 {
compatible = "ti,opt3001";
reg = <0x44>;
};
};
  1. Add the following configuration options to your prj.conf file:
CONFIG_I2C=y
CONFIG_SENSOR=y
CONFIG_OPT3001=y

Enabling the BME688 sensors

The BME688 gas sensors can be used with Zephyr by adding a node to the device-tree overlay of your project and enabling the driver in your configuration file. See the Zephyr sample /ncs/zephyr/samples/sensor/bme680/ or follow these steps to enable the BME688 sensors on the Icarus IoT Board:

  1. Navigate to your project and create/enter the boards directory.
  2. Create a overlay file with the exact name of your board. E.g: actinius_icarus_ns.overlay.
  3. Copy and paste the following device-tree node to the overlay file (change the i2c bus according to your board). Only add the second BME688 node if you have a environmental FeatherWing with 2 BME688 sensors:
&i2c2 {
bme688_1: bme688-1@76 {
compatible = "bosch,bme680";
reg = <0x76>;
};

/* Node definition for the second BME688 sensor (optional) */
bme688_2: bme688-2@77 {
compatible = "bosch,bme680";
reg = <0x77>;
};
};
  1. Add the following configuration options to your prj.conf file:
CONFIG_I2C=y
CONFIG_SENSOR=y
CONFIG_BME680=y

Example code for reading environmental FeatherWing sensor data

The following configuration and code shows you how to setup the Icarus IoT Board to read data from each sensor and print it to a serial monitor. You can use this example as a reference for your own project.

  1. Navigate to your project and create/enter the boards directory.
  2. Create a overlay file with the filename: actinius_icarus_ns.overlay.
  3. Copy and paste the following device-tree node to the overlay file:
&i2c2 {
/* Node definition for the main BME688 sensor */
bme688_1: bme688-1@76 {
compatible = "bosch,bme680";
reg = <0x76>;
};

/* Node definition for the second BME688 sensor (optional) */
bme688_2: bme688-2@77 {
compatible = "bosch,bme680";
reg = <0x77>;
};

/* Node definition for the OPT3002 sensor */
opt3002: opt3002@44 {
compatible = "ti,opt3001";
reg = <0x44>;
};

/* Node definition for the SI7060 sensor */
si7060: si7060@32 {
compatible = "silabs,si7060";
reg = <0x32>;
};
};
  1. Add the following configuration options to your prj.conf file:
CONFIG_MAIN_STACK_SIZE=8192
CONFIG_HEAP_MEM_POOL_SIZE=16384

CONFIG_STDOUT_CONSOLE=y
CONFIG_NEWLIB_LIBC=y
CONFIG_NEWLIB_LIBC_FLOAT_PRINTF=y

CONFIG_LOG=y
CONFIG_LOG_BACKEND_UART=y

CONFIG_GPIO=y
CONFIG_I2C=y

# Sensor configuration
CONFIG_SENSOR=y
CONFIG_OPT3001=y
CONFIG_SI7060=y
CONFIG_BME680=y

CONFIG_BOOTLOADER_MCUBOOT=y # Needed for DFU
  1. Add the following code to your main.c file:
#include <zephyr/kernel.h>
#include <zephyr/device.h>
#include <zephyr/devicetree.h>
#include <zephyr/drivers/sensor.h>
#include <stdio.h>

static const struct device *bme_1 = DEVICE_DT_GET(DT_NODELABEL(bme688_1));
static const struct device *bme_2 = DEVICE_DT_GET(DT_NODELABEL(bme688_2));
static const struct device *si = DEVICE_DT_GET(DT_NODELABEL(si7060));
static const struct device *opt = DEVICE_DT_GET(DT_NODELABEL(opt3002));

static bool is_second_bme688_present;

void print_bme688_values(const struct device *dev)
{
struct sensor_value temp_raw;
double temp;

struct sensor_value press_raw;
double press;

struct sensor_value humidity_raw;
double humidity;

struct sensor_value gas_res_raw;
double gas_res;

sensor_sample_fetch(dev);

sensor_channel_get(dev, SENSOR_CHAN_AMBIENT_TEMP, &temp_raw);
sensor_channel_get(dev, SENSOR_CHAN_PRESS, &press_raw);
sensor_channel_get(dev, SENSOR_CHAN_HUMIDITY, &humidity_raw);
sensor_channel_get(dev, SENSOR_CHAN_GAS_RES, &gas_res_raw);

temp = sensor_value_to_double(&temp_raw);
press = sensor_value_to_double(&press_raw);
humidity = sensor_value_to_double(&humidity_raw);
gas_res = sensor_value_to_double(&gas_res_raw);

printf("BME688_1 T: %f; P: %f; H: %f; G: %f\n", temp, press, humidity, gas_res);
}

void print_si7060_value(const struct device *dev)
{
struct sensor_value temp_raw;
double temp;

sensor_sample_fetch(dev);
sensor_channel_get(dev, SENSOR_CHAN_AMBIENT_TEMP, &temp_raw);
temp = sensor_value_to_double(&temp_raw);
printf("SI7060 T: %f\n", temp);
}

void print_opt3002_value(const struct device *dev)
{
struct sensor_value light_raw;
double light;

sensor_sample_fetch(dev);
sensor_channel_get(dev, SENSOR_CHAN_LIGHT, &light_raw);
light = sensor_value_to_double(&light_raw);
printf("OPT3002 LUX: %f\n", light);
}

void main(void)
{
/* wait for all sensors to become ready */
bool all_sensors_ready = false;
while (!all_sensors_ready) {
all_sensors_ready = true;

if (!device_is_ready(bme_1)) {
printk("BME688_1 is not ready\n");
all_sensors_ready = false;
}

if (!device_is_ready(bme_2)) {
printk("BME688_2 is not ready\n");
// do not consider the second BME688 missing an error
} else {
is_second_bme688_present = true;
}

if (!device_is_ready(si)) {
printk("SI7060 is not ready\n");
all_sensors_ready = false;
}

if (!device_is_ready(opt)) {
printk("OPT3002 is not ready\n");
all_sensors_ready = false;
}

k_sleep(K_MSEC(500));
}

uint32_t sample_count = 0;
while (true) {
sample_count++;
printk("\n\nSample count: %u\n", sample_count);

print_bme688_values(bme_1);

if (is_second_bme688_present) {
print_bme688_values(bme_2);
}

print_si7060_value(si);

print_opt3002_value(opt);

k_sleep(K_MSEC(3500));
}
}
  1. Build the code. Either through the nRF Connect SDK extension for Visual Studio Code or by using West. For more information, see the Building and application page.

  2. Flash the binary to your device using the VS Code + JLink, West + Jlink, the Actinius I/O Serial Programmer or mcumgr. For more information, see the Flashing an application page.