1 /*
2  * Copyright (c) 2018 Peter Bigot Consulting, LLC
3  * Copyright (c) 2018 Linaro Ltd.
4  *
5  * SPDX-License-Identifier: Apache-2.0
6  */
7 
8 #include <zephyr/kernel.h>
9 #include <zephyr/device.h>
10 #include <zephyr/drivers/sensor.h>
11 #include <zephyr/sys/printk.h>
12 #include <zephyr/drivers/sensor/ccs811.h>
13 #include <stdio.h>
14 
15 static bool app_fw_2;
16 
now_str(void)17 static const char *now_str(void)
18 {
19 	static char buf[16]; /* ...HH:MM:SS.MMM */
20 	uint32_t now = k_uptime_get_32();
21 	unsigned int ms = now % MSEC_PER_SEC;
22 	unsigned int s;
23 	unsigned int min;
24 	unsigned int h;
25 
26 	now /= MSEC_PER_SEC;
27 	s = now % 60U;
28 	now /= 60U;
29 	min = now % 60U;
30 	now /= 60U;
31 	h = now;
32 
33 	snprintf(buf, sizeof(buf), "%u:%02u:%02u.%03u",
34 		 h, min, s, ms);
35 	return buf;
36 }
37 
do_fetch(const struct device * dev)38 static int do_fetch(const struct device *dev)
39 {
40 	struct sensor_value co2, tvoc, voltage, current;
41 	int rc = 0;
42 	int baseline = -1;
43 
44 #ifdef CONFIG_APP_MONITOR_BASELINE
45 	rc = ccs811_baseline_fetch(dev);
46 	if (rc >= 0) {
47 		baseline = rc;
48 		rc = 0;
49 	}
50 #endif
51 	if (rc == 0) {
52 		rc = sensor_sample_fetch(dev);
53 	}
54 	if (rc == 0) {
55 		const struct ccs811_result_type *rp = ccs811_result(dev);
56 
57 		sensor_channel_get(dev, SENSOR_CHAN_CO2, &co2);
58 		sensor_channel_get(dev, SENSOR_CHAN_VOC, &tvoc);
59 		sensor_channel_get(dev, SENSOR_CHAN_VOLTAGE, &voltage);
60 		sensor_channel_get(dev, SENSOR_CHAN_CURRENT, &current);
61 		printk("\n[%s]: CCS811: %u ppm eCO2; %u ppb eTVOC\n",
62 		       now_str(), co2.val1, tvoc.val1);
63 		printk("Voltage: %d.%06dV; Current: %d.%06dA\n", voltage.val1,
64 		       voltage.val2, current.val1, current.val2);
65 #ifdef CONFIG_APP_MONITOR_BASELINE
66 		printk("BASELINE %04x\n", baseline);
67 #endif
68 		if (app_fw_2 && !(rp->status & CCS811_STATUS_DATA_READY)) {
69 			printk("STALE DATA\n");
70 		}
71 
72 		if (rp->status & CCS811_STATUS_ERROR) {
73 			printk("ERROR: %02x\n", rp->error);
74 		}
75 	}
76 	return rc;
77 }
78 
79 #ifndef CONFIG_CCS811_TRIGGER_NONE
80 
trigger_handler(const struct device * dev,const struct sensor_trigger * trig)81 static void trigger_handler(const struct device *dev,
82 			    const struct sensor_trigger *trig)
83 {
84 	int rc = do_fetch(dev);
85 
86 	if (rc == 0) {
87 		printk("Triggered fetch got %d\n", rc);
88 	} else if (-EAGAIN == rc) {
89 		printk("Triggered fetch got stale data\n");
90 	} else {
91 		printk("Triggered fetch failed: %d\n", rc);
92 	}
93 }
94 
95 #endif /* !CONFIG_CCS811_TRIGGER_NONE */
96 
do_main(const struct device * dev)97 static void do_main(const struct device *dev)
98 {
99 	while (true) {
100 		int rc = do_fetch(dev);
101 
102 		if (rc == 0) {
103 			printk("Timed fetch got %d\n", rc);
104 		} else if (-EAGAIN == rc) {
105 			printk("Timed fetch got stale data\n");
106 		} else {
107 			printk("Timed fetch failed: %d\n", rc);
108 			break;
109 		}
110 		k_msleep(1000);
111 	}
112 }
113 
main(void)114 int main(void)
115 {
116 	const struct device *const dev = DEVICE_DT_GET_ONE(ams_ccs811);
117 	struct ccs811_configver_type cfgver;
118 	int rc;
119 
120 	if (!device_is_ready(dev)) {
121 		printk("Device %s is not ready\n", dev->name);
122 		return 0;
123 	}
124 
125 	printk("device is %p, name is %s\n", dev, dev->name);
126 
127 	rc = ccs811_configver_fetch(dev, &cfgver);
128 	if (rc == 0) {
129 		printk("HW %02x; FW Boot %04x App %04x ; mode %02x\n",
130 		       cfgver.hw_version, cfgver.fw_boot_version,
131 		       cfgver.fw_app_version, cfgver.mode);
132 		app_fw_2 = (cfgver.fw_app_version >> 8) > 0x11;
133 	}
134 
135 #ifdef CONFIG_APP_USE_ENVDATA
136 	struct sensor_value temp = { CONFIG_APP_ENV_TEMPERATURE };
137 	struct sensor_value humidity = { CONFIG_APP_ENV_HUMIDITY };
138 
139 	rc = ccs811_envdata_update(dev, &temp, &humidity);
140 	printk("ENV_DATA set for %d Cel, %d %%RH got %d\n",
141 	       temp.val1, humidity.val1, rc);
142 #endif
143 
144 #ifdef CONFIG_CCS811_TRIGGER
145 	struct sensor_trigger trig = { 0 };
146 
147 #ifdef CONFIG_APP_TRIGGER_ON_THRESHOLD
148 	printk("Triggering on threshold:\n");
149 	if (rc == 0) {
150 		struct sensor_value thr = {
151 			.val1 = CONFIG_APP_CO2_MEDIUM_PPM,
152 		};
153 		rc = sensor_attr_set(dev, SENSOR_CHAN_CO2,
154 				     SENSOR_ATTR_LOWER_THRESH,
155 				     &thr);
156 		printk("L/M threshold to %d got %d\n", thr.val1, rc);
157 	}
158 	if (rc == 0) {
159 		struct sensor_value thr = {
160 			.val1 = CONFIG_APP_CO2_HIGH_PPM,
161 		};
162 		rc = sensor_attr_set(dev, SENSOR_CHAN_CO2,
163 				     SENSOR_ATTR_UPPER_THRESH,
164 				     &thr);
165 		printk("M/H threshold to %d got %d\n", thr.val1, rc);
166 	}
167 	trig.type = SENSOR_TRIG_THRESHOLD;
168 	trig.chan = SENSOR_CHAN_CO2;
169 #elif defined(CONFIG_APP_TRIGGER_ON_DATAREADY)
170 	printk("Triggering on data ready\n");
171 	trig.type = SENSOR_TRIG_DATA_READY;
172 	trig.chan = SENSOR_CHAN_ALL;
173 #else
174 #error Unhandled trigger on
175 #endif
176 	if (rc == 0) {
177 		rc = sensor_trigger_set(dev, &trig, trigger_handler);
178 	}
179 	if (rc == 0) {
180 #ifdef CONFIG_APP_TRIGGER_ON_DATAREADY
181 		while (true) {
182 			k_sleep(K_FOREVER);
183 		}
184 #endif
185 	}
186 	printk("Trigger installation got: %d\n", rc);
187 #endif /* CONFIG_CCS811_TRIGGER */
188 	if (rc == 0) {
189 		do_main(dev);
190 	}
191 	return 0;
192 }
193