Skip to content

Commit da25f96

Browse files
committed
app: sensor: add fetching data from ccs811 and hm3301
Add support for CCS811 air quality sensor and HM3301 particulate matter sensor to the fire detection system alongside existing BME280. Changes: - Add CCS811 (CO2/VOC) and HM3301 (PM1.0/2.5/10) sensor support - Configure devicetree overlay for multi-sensor I2C bus setup - Implement environmental compensation for CCS811 using BME280 data - Add configurable CCS811_ENV_COMPENSATION with smart threshold updates - Refactor main.c into modular functions for better maintainability - Enable required sensor drivers in project configuration The environmental compensation feature improves CCS811 accuracy by updating its internal registers with temperature and humidity from BME280, using a 0.5°C/0.5%RH threshold to minimize I2C traffic. Refs: #9 Signed-off-by: Natalia Pluta <pluta.natalia.m@gmail.com>
1 parent 3977398 commit da25f96

4 files changed

Lines changed: 269 additions & 30 deletions

File tree

app/Kconfig

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,20 @@ menu "Zephyr"
1010
source "Kconfig.zephyr"
1111
endmenu
1212

13+
menu "Application Configuration"
14+
15+
config CCS811_ENV_COMPENSATION
16+
bool "Enable environmental compensation for CCS811"
17+
default y
18+
depends on CCS811 && BME280
19+
help
20+
Enable updating CCS811 environmental data register with
21+
temperature and humidity values from BME280 sensor.
22+
This improves accuracy of CO2 and VOC measurements by
23+
compensating for environmental conditions.
24+
25+
endmenu
26+
1327
module = APP
1428
module-str = APP
1529
source "subsys/logging/Kconfig.template.log_config"

app/boards/nrf9160dk_nrf9160.overlay

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,27 @@
2525
pinctrl-0 = <&i2c2_default_alt>;
2626
pinctrl-1 = <&i2c2_sleep_alt>;
2727
pinctrl-names = "default", "sleep";
28+
2829
bme280: bme280@77 {
2930
compatible = "bosch,bme280";
3031
reg = <0x77>;
3132
status = "okay";
3233
};
34+
35+
ccs811: ccs811@5b {
36+
compatible = "ams,ccs811";
37+
reg = <0x5b>;
38+
status = "okay";
39+
reset-gpios = <&gpio0 23 GPIO_ACTIVE_LOW>;
40+
irq-gpios = <&gpio0 24 GPIO_ACTIVE_LOW>;
41+
wake-gpios = <&gpio0 25 GPIO_ACTIVE_LOW>;
42+
};
43+
44+
hm3301: hm3301@40 {
45+
compatible = "seeed,hm330x";
46+
reg = <0x40>;
47+
status = "okay";
48+
};
3349
};
3450

3551
/ {

app/prj.conf

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,10 @@
66
CONFIG_LOG=y
77
CONFIG_BLINK=y
88

9-
# Enable I2C and sensor subsystem for BME280
9+
# Enable I2C and sensor subsystem for multiple sensors
1010
CONFIG_I2C=y
1111
CONFIG_SENSOR=y
1212
CONFIG_BME280=y
13+
CONFIG_CCS811=y
14+
CONFIG_CCS811_ENV_COMPENSATION=y
15+
CONFIG_HM330X=y

app/src/main.c

Lines changed: 235 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@
55

66
#include <zephyr/kernel.h>
77
#include <zephyr/drivers/sensor.h>
8+
#include <zephyr/drivers/i2c.h>
89
#include <zephyr/logging/log.h>
10+
#include <zephyr/drivers/sensor/ccs811.h>
911

1012
#include <app/drivers/blink.h>
1113

@@ -16,60 +18,264 @@ LOG_MODULE_REGISTER(main, CONFIG_APP_LOG_LEVEL);
1618
#define BLINK_PERIOD_MS_STEP 100U
1719
#define BLINK_PERIOD_MS_MAX 1000U
1820

19-
int main(void)
20-
{
21-
int ret;
22-
const struct device *sensor, *blink;
23-
struct sensor_value temp, press, hum;
21+
/* Function prototypes */
22+
static int init_sensors(const struct device **bme280, const struct device **ccs811,
23+
const struct device **hm3301, const struct device **blink);
24+
static int read_bme280_data(const struct device *sensor, struct sensor_value *temp,
25+
struct sensor_value *press, struct sensor_value *hum);
26+
static int read_ccs811_data(const struct device *sensor, struct sensor_value *co2,
27+
struct sensor_value *voc);
28+
static int read_hm3301_data(const struct device *sensor, struct sensor_value *pm1,
29+
struct sensor_value *pm25, struct sensor_value *pm10);
30+
static void log_sensor_data(const struct sensor_value *temp, const struct sensor_value *press,
31+
const struct sensor_value *hum, const struct sensor_value *co2,
32+
const struct sensor_value *voc, const struct sensor_value *pm1,
33+
const struct sensor_value *pm25, const struct sensor_value *pm10);
2434

25-
LOG_INF("Zephyr Fire Detection System %s", APP_VERSION_STRING);
35+
#ifdef CONFIG_CCS811_ENV_COMPENSATION
36+
static int update_ccs811_env_data(const struct device *ccs811_sensor,
37+
const struct sensor_value *temp, const struct sensor_value *hum);
38+
#endif
2639

27-
sensor = DEVICE_DT_GET(DT_NODELABEL(bme280));
28-
if (!device_is_ready(sensor)) {
40+
static int init_sensors(const struct device **bme280, const struct device **ccs811,
41+
const struct device **hm3301, const struct device **blink)
42+
{
43+
/* Initialize BME280 sensor */
44+
*bme280 = DEVICE_DT_GET(DT_NODELABEL(bme280));
45+
if (!device_is_ready(*bme280)) {
2946
LOG_ERR("BME280 sensor not ready");
30-
return 0;
47+
return -ENODEV;
3148
}
3249

33-
blink = DEVICE_DT_GET(DT_NODELABEL(blink_led));
34-
if (!device_is_ready(blink)) {
50+
k_sleep(K_MSEC(5000));
51+
52+
/* Initialize CCS811 sensor */
53+
*ccs811 = DEVICE_DT_GET(DT_NODELABEL(ccs811));
54+
if (!device_is_ready(*ccs811)) {
55+
LOG_ERR("CCS811 sensor not ready");
56+
return -ENODEV;
57+
}
58+
59+
/* Initialize HM3301 sensor */
60+
*hm3301 = DEVICE_DT_GET(DT_NODELABEL(hm3301));
61+
if (!device_is_ready(*hm3301)) {
62+
LOG_ERR("HM3301 sensor not ready");
63+
return -ENODEV;
64+
}
65+
66+
*blink = DEVICE_DT_GET(DT_NODELABEL(blink_led));
67+
if (!device_is_ready(*blink)) {
3568
LOG_ERR("Blink LED not ready");
36-
return 0;
69+
return -ENODEV;
3770
}
3871

39-
ret = blink_off(blink);
72+
int ret = blink_off(*blink);
4073
if (ret < 0) {
4174
LOG_ERR("Could not turn off LED (%d)", ret);
75+
return ret;
76+
}
77+
78+
LOG_INF("All sensors initialized successfully");
79+
return 0;
80+
}
81+
82+
static int read_bme280_data(const struct device *sensor, struct sensor_value *temp,
83+
struct sensor_value *press, struct sensor_value *hum)
84+
{
85+
int ret = sensor_sample_fetch(sensor);
86+
if (ret < 0) {
87+
LOG_ERR("Could not fetch BME280 sample (%d)", ret);
88+
return ret;
89+
}
90+
91+
ret = sensor_channel_get(sensor, SENSOR_CHAN_AMBIENT_TEMP, temp);
92+
if (ret < 0) {
93+
LOG_ERR("Could not get temperature (%d)", ret);
94+
return ret;
95+
}
96+
97+
ret = sensor_channel_get(sensor, SENSOR_CHAN_PRESS, press);
98+
if (ret < 0) {
99+
LOG_ERR("Could not get pressure (%d)", ret);
100+
return ret;
101+
}
102+
103+
ret = sensor_channel_get(sensor, SENSOR_CHAN_HUMIDITY, hum);
104+
if (ret < 0) {
105+
LOG_ERR("Could not get humidity (%d)", ret);
106+
return ret;
107+
}
108+
109+
return 0;
110+
}
111+
112+
static int read_ccs811_data(const struct device *sensor, struct sensor_value *co2,
113+
struct sensor_value *voc)
114+
{
115+
int ret = sensor_sample_fetch(sensor);
116+
if (ret < 0) {
117+
LOG_ERR("Could not fetch CCS811 sample (%d)", ret);
118+
return ret;
119+
}
120+
121+
ret = sensor_channel_get(sensor, SENSOR_CHAN_CO2, co2);
122+
if (ret < 0) {
123+
LOG_ERR("Could not get CO2 (%d)", ret);
124+
return ret;
125+
}
126+
127+
ret = sensor_channel_get(sensor, SENSOR_CHAN_VOC, voc);
128+
if (ret < 0) {
129+
LOG_ERR("Could not get VOC (%d)", ret);
130+
return ret;
131+
}
132+
133+
return 0;
134+
}
135+
136+
static int read_hm3301_data(const struct device *sensor, struct sensor_value *pm1,
137+
struct sensor_value *pm25, struct sensor_value *pm10)
138+
{
139+
int ret = sensor_sample_fetch(sensor);
140+
if (ret < 0) {
141+
LOG_ERR("Could not fetch HM3301 sample (%d)", ret);
142+
return ret;
143+
}
144+
145+
ret = sensor_channel_get(sensor, SENSOR_CHAN_PM_1_0, pm1);
146+
if (ret < 0) {
147+
LOG_ERR("Could not get PM1.0 (%d)", ret);
148+
return ret;
149+
}
150+
151+
ret = sensor_channel_get(sensor, SENSOR_CHAN_PM_2_5, pm25);
152+
if (ret < 0) {
153+
LOG_ERR("Could not get PM2.5 (%d)", ret);
154+
return ret;
155+
}
156+
157+
ret = sensor_channel_get(sensor, SENSOR_CHAN_PM_10, pm10);
158+
if (ret < 0) {
159+
LOG_ERR("Could not get PM10 (%d)", ret);
160+
return ret;
161+
}
162+
163+
return 0;
164+
}
165+
166+
static void log_sensor_data(const struct sensor_value *temp, const struct sensor_value *press,
167+
const struct sensor_value *hum, const struct sensor_value *co2,
168+
const struct sensor_value *voc, const struct sensor_value *pm1,
169+
const struct sensor_value *pm25, const struct sensor_value *pm10)
170+
{
171+
LOG_INF("BME280: Temp: %d.%06d C, Press: %d.%06d kPa, Hum: %d.%06d %%", temp->val1,
172+
temp->val2, press->val1, press->val2, hum->val1, hum->val2);
173+
174+
LOG_INF("CCS811: CO2: %d ppm, VOC: %d ppb", co2->val1, voc->val1);
175+
176+
LOG_INF("HM3301: PM1.0: %d μg/m³, PM2.5: %d μg/m³, PM10: %d μg/m³", pm1->val1, pm25->val1,
177+
pm10->val1);
178+
}
179+
180+
#ifdef CONFIG_CCS811_ENV_COMPENSATION
181+
static int update_ccs811_env_data(const struct device *ccs811_sensor,
182+
const struct sensor_value *temp, const struct sensor_value *hum)
183+
{
184+
/* Previous environmental values for smart updates */
185+
static struct sensor_value prev_temp = {0, 0};
186+
static struct sensor_value prev_hum = {0, 0};
187+
static bool env_data_initialized = false;
188+
189+
bool should_update = false;
190+
191+
/* Check if this is the first update */
192+
if (!env_data_initialized) {
193+
should_update = true;
194+
env_data_initialized = true;
195+
LOG_INF("Initializing CCS811 environmental data");
196+
} else {
197+
/* Check for significant changes (0.5°C or 0.5% RH threshold) */
198+
int32_t temp_diff = abs((temp->val1 * 1000000 + temp->val2) -
199+
(prev_temp.val1 * 1000000 + prev_temp.val2));
200+
int32_t hum_diff = abs((hum->val1 * 1000000 + hum->val2) -
201+
(prev_hum.val1 * 1000000 + prev_hum.val2));
202+
203+
/* Update if temperature changed by more than 0.5°C or humidity by more than 0.5% */
204+
if (temp_diff >= 500000 || hum_diff >= 500000) {
205+
should_update = true;
206+
LOG_DBG("Environmental change detected - updating CCS811");
207+
}
208+
}
209+
210+
if (should_update) {
211+
int ret = ccs811_envdata_update(ccs811_sensor, temp, hum);
212+
if (ret < 0) {
213+
LOG_ERR("Could not update CCS811 environmental data (%d)", ret);
214+
return ret;
215+
}
216+
217+
LOG_INF("CCS811 env data updated: T=%d.%02d°C, H=%d.%02d%%", temp->val1,
218+
temp->val2 / 10000, hum->val1, hum->val2 / 10000);
219+
220+
/* Store current values for next comparison */
221+
prev_temp = *temp;
222+
prev_hum = *hum;
223+
}
224+
225+
return 0;
226+
}
227+
#endif /* CONFIG_CCS811_ENV_COMPENSATION */
228+
229+
int main(void)
230+
{
231+
int ret;
232+
const struct device *bme280_sensor, *ccs811_sensor, *hm3301_sensor, *blink;
233+
struct sensor_value temp, press, hum, co2, voc, pm1, pm25, pm10;
234+
235+
LOG_INF("Zephyr Fire Detection System %s", APP_VERSION_STRING);
236+
237+
/* Get I2C device for scanning */
238+
const struct device *i2c_dev = DEVICE_DT_GET(DT_NODELABEL(i2c2));
239+
if (!device_is_ready(i2c_dev)) {
240+
LOG_ERR("I2C device not ready");
241+
return 0;
242+
}
243+
244+
/* Initialize all sensors */
245+
ret = init_sensors(&bme280_sensor, &ccs811_sensor, &hm3301_sensor, &blink);
246+
if (ret < 0) {
247+
LOG_ERR("Sensor initialization failed");
42248
return 0;
43249
}
44250

45251
while (1) {
46-
ret = sensor_sample_fetch(sensor);
252+
/* Read BME280 data (temperature, pressure, humidity) */
253+
ret = read_bme280_data(bme280_sensor, &temp, &press, &hum);
47254
if (ret < 0) {
48-
LOG_ERR("Could not fetch BME280 sample (%d)", ret);
49-
k_sleep(K_MSEC(1000));
50-
continue;
255+
LOG_ERR("Failed to read BME280 data");
51256
}
52257

53-
ret = sensor_channel_get(sensor, SENSOR_CHAN_AMBIENT_TEMP, &temp);
54-
if (ret < 0) {
55-
LOG_ERR("Could not get temperature (%d)", ret);
56-
continue;
258+
#ifdef CONFIG_CCS811_ENV_COMPENSATION
259+
/* Update CCS811 environmental data if BME280 reading was successful */
260+
if (ret == 0) {
261+
update_ccs811_env_data(ccs811_sensor, &temp, &hum);
57262
}
263+
#endif /* CONFIG_CCS811_ENV_COMPENSATION */
58264

59-
ret = sensor_channel_get(sensor, SENSOR_CHAN_PRESS, &press);
265+
/* Read CCS811 data (CO2 and VOC) */
266+
ret = read_ccs811_data(ccs811_sensor, &co2, &voc);
60267
if (ret < 0) {
61-
LOG_ERR("Could not get pressure (%d)", ret);
62-
continue;
268+
LOG_ERR("Failed to read CCS811 data");
63269
}
64270

65-
ret = sensor_channel_get(sensor, SENSOR_CHAN_HUMIDITY, &hum);
271+
/* Read HM3301 data (PM1.0, PM2.5, PM10) */
272+
ret = read_hm3301_data(hm3301_sensor, &pm1, &pm25, &pm10);
66273
if (ret < 0) {
67-
LOG_ERR("Could not get humidity (%d)", ret);
68-
continue;
274+
LOG_ERR("Failed to read HM3301 data");
69275
}
70276

71-
LOG_INF("BME280: Temp: %d.%06d C, Press: %d.%06d kPa, Hum: %d.%06d %%", temp.val1,
72-
temp.val2, press.val1, press.val2, hum.val1, hum.val2);
277+
/* Log all sensor data */
278+
log_sensor_data(&temp, &press, &hum, &co2, &voc, &pm1, &pm25, &pm10);
73279

74280
k_sleep(K_MSEC(1000));
75281
}

0 commit comments

Comments
 (0)