1 /*
2 * Copyright (C) 2018 Sensirion Inc.
3 * Author: Johannes Winkelmann, jwi@sensirion.com
4 *
5 * Driver for SCD30
6 * Based on SGP30 driver, and arduino-scd library
7 */
8
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <string.h>
12 #include "aos/kernel.h"
13 #include "sensor_drv_api.h"
14 #include "sensor_hal.h"
15
16 #define POLYNOMIAL 0x131
17
18 #define SCD30_I2C_SLAVE_ADDR 0x61
19
20 #define SCD30_ADDR_TRANS(n) ((n) << 1)
21 #define SCD30_I2C_ADDR SCD30_ADDR_TRANS(SCD30_I2C_SLAVE_ADDR)
22
23 #define CMD_CONT_MEASUREMENT_LEN 5
24 static uint8_t CMD_CONT_MEASUREMENT[CMD_CONT_MEASUREMENT_LEN] = {
25 0x00, 0x10, 0x00, 0x00, 0x81
26 };
27
28 // Note: in this library, we're hardcoding the interval to 2 seconds
29 #define CMD_SET_INTERVAL_LEN 5
30 static uint8_t CMD_SET_INTERVAL[CMD_SET_INTERVAL_LEN] = { 0x46, 0x00,
31 0x00, 0x01,
32 0xE3 };
33 #define CMD_STOP_MEASUREMENT_LEN 2
34 static const uint8_t CMD_STOP_MEASUREMENT[CMD_STOP_MEASUREMENT_LEN] = { 0x01,
35 0x04 };
36
37 #define CMD_READ_MEASUREMENT_LEN 2
38 #define CMD_READ_MEASUREMENT_RESULT_LEN 18
39 static uint8_t CMD_READ_MEASUREMENT[CMD_READ_MEASUREMENT_LEN] = { 0x03,
40 0x00 };
41
42 #define CMD_DATA_READY_LEN 2
43 #define CMD_DATA_READY_RESULT_LEN 4
44 static uint8_t CMD_DATA_READY[CMD_DATA_READY_LEN] = { 0x02, 0x02 };
45
46 /*
47 * default port = 3
48 * Use "GLOBAL_DEFINES += SENSIRION_SCD30_PORT=2" in a Makefile to override
49 */
50 #ifndef SENSIRION_SCD30_PORT
51 #define SENSIRION_SCD30_PORT 3
52 #endif /* SENSIRION_SCD30_PORT */
53
54 i2c_dev_t scd30_ctx = {
55 .port = SENSIRION_SCD30_PORT,
56 .config.address_width = 8,
57 .config.freq = 100000,
58 .config.dev_addr = SCD30_I2C_ADDR,
59 };
60
61 static float g_scd30_most_recent_value = 0;
62
SCD30_CalcCrc(uint8_t data[],uint8_t nbrOfBytes)63 static uint8_t SCD30_CalcCrc(uint8_t data[], uint8_t nbrOfBytes)
64 {
65 uint8_t bit; // bit mask
66 uint8_t crc = 0xFF; // calculated checksum
67 uint8_t byteCtr; // byte counter
68
69 // calculates 8-Bit checksum with given polynomial
70 for(byteCtr = 0; byteCtr < nbrOfBytes; byteCtr++)
71 {
72 crc ^= (data[byteCtr]);
73 for(bit = 8; bit > 0; --bit)
74 {
75 if(crc & 0x80) crc = (crc << 1) ^ POLYNOMIAL;
76 else crc = (crc << 1);
77 }
78 }
79
80 return crc;
81 }
scd30_convert_to_float(uint8_t * data)82 static float scd30_convert_to_float(uint8_t *data)
83 {
84 void* addr;
85 uint32_t val = (((uint32_t)data[0] << 24) | ((uint32_t)data[1] << 16) |
86 ((uint32_t)data[3] << 8) | ((uint32_t)data[4]));
87 addr = (void*)(&val);
88 return *(float *)addr;
89 }
90
scd30_delay_ms(uint32_t delay_time)91 static void scd30_delay_ms(uint32_t delay_time)
92 {
93 // TODO: krhino_task_sleep is currently blocking; therefore, I implemented
94 // a busy wait loop
95 //
96 // krhino_task_sleep(krhino_ms_to_ticks(delay_time));
97 uint32_t start_ms = aos_now_ms();
98 while (aos_now_ms() - start_ms < delay_time) {
99 // idle
100 }
101 }
102
drv_scd30_cmd_write(i2c_dev_t * drv,uint8_t * cmd,uint8_t len)103 static int drv_scd30_cmd_write(i2c_dev_t* drv, uint8_t* cmd, uint8_t len)
104 {
105 return hal_i2c_master_send(drv, drv->config.dev_addr, cmd, len, AOS_WAIT_FOREVER);
106 }
107
drv_scd30_result_read(i2c_dev_t * drv,uint8_t * data,uint16_t size)108 static int drv_scd30_result_read(i2c_dev_t* drv, uint8_t *data, uint16_t size)
109 {
110 if (data == NULL || size == 0)
111 return -1;
112
113 return hal_i2c_master_recv(drv, drv->config.dev_addr, data, size, AOS_WAIT_FOREVER);
114 }
115
scd30_check_data_ready(i2c_dev_t * drv)116 static int scd30_check_data_ready(i2c_dev_t* drv)
117 {
118 bool dataReady = false;
119 int ret = drv_scd30_cmd_write(drv, CMD_DATA_READY, CMD_DATA_READY_LEN);
120
121 if (ret != 0) {
122 return 1;
123 } else {
124 uint8_t buf[CMD_DATA_READY_RESULT_LEN] = { 0 };
125 ret = drv_scd30_result_read(drv, buf, CMD_DATA_READY_RESULT_LEN);
126 if (ret != 0) {
127 return 2;
128 } else {
129 dataReady = (buf[1] == 1);
130 }
131 }
132 return dataReady;
133 }
134
drv_scd30_read_raw_data(i2c_dev_t * drv,integer_data_t * pdata)135 static int drv_scd30_read_raw_data(i2c_dev_t *drv, integer_data_t *pdata)
136 {
137 int ret = 0;
138 uint8_t data[CMD_READ_MEASUREMENT_RESULT_LEN] = {0};
139
140 ret = scd30_check_data_ready(drv);
141 if (ret) {
142 ret = drv_scd30_cmd_write(drv, CMD_READ_MEASUREMENT, CMD_READ_MEASUREMENT_LEN);
143 if (unlikely(ret)) {
144 return ret;
145 }
146 ret = drv_scd30_result_read(drv, data, CMD_READ_MEASUREMENT_RESULT_LEN);
147 if (unlikely(ret)) {
148 return ret;
149 }
150 g_scd30_most_recent_value = scd30_convert_to_float(data);
151 }
152
153 pdata->data = g_scd30_most_recent_value;
154
155 return ret;
156 }
157
drv_scd30_init_sensor(i2c_dev_t * drv)158 static int drv_scd30_init_sensor(i2c_dev_t* drv)
159 {
160 int ret = 0;
161
162 CMD_SET_INTERVAL[4] = SCD30_CalcCrc(&CMD_SET_INTERVAL[2],2);
163 ret = drv_scd30_cmd_write(drv, CMD_SET_INTERVAL, CMD_SET_INTERVAL_LEN);
164 if (unlikely(ret)) {
165 return ret;
166 }
167 // workaround for firmware bug in early samples
168 scd30_delay_ms(100);
169 CMD_CONT_MEASUREMENT[4] = SCD30_CalcCrc(&CMD_CONT_MEASUREMENT[2],2);
170 return drv_scd30_cmd_write(drv, CMD_CONT_MEASUREMENT, CMD_CONT_MEASUREMENT_LEN);
171 }
172
173
174
drv_co2_sensirion_scd30_irq_handle(void)175 static void drv_co2_sensirion_scd30_irq_handle(void)
176 {
177 /* no handle so far */
178 }
179
drv_co2_sensirion_scd30_open(void)180 static int drv_co2_sensirion_scd30_open(void)
181 {
182 LOG("%s %s successfully \n", SENSOR_STR, __func__);
183
184 return 0;
185 }
186
drv_co2_sensirion_scd30_close(void)187 static int drv_co2_sensirion_scd30_close(void)
188 {
189 LOG("%s %s successfully \n", SENSOR_STR, __func__);
190
191 return 0;
192 }
193
drv_co2_sensirion_scd30_read(void * buf,size_t len)194 static int drv_co2_sensirion_scd30_read(void *buf, size_t len)
195 {
196 int ret = 0;
197 const size_t size = sizeof(integer_data_t);
198 integer_data_t * pdata = (integer_data_t *)buf;
199
200 if (buf == NULL){
201 return -1;
202 }
203
204 if (len < size){
205 return -1;
206 }
207
208 ret = drv_scd30_read_raw_data(&scd30_ctx, pdata);
209 if (ret != 0)
210 return -1;
211
212 pdata->timestamp = aos_now_ms();
213
214 return (int)size;
215 }
216
drv_co2_sensirion_scd30_write(const void * buf,size_t len)217 static int drv_co2_sensirion_scd30_write(const void *buf, size_t len)
218 {
219 (void)buf;
220 (void)len;
221 return 0;
222 }
223
drv_co2_sensirion_scd30_ioctl(int cmd,unsigned long arg)224 static int drv_co2_sensirion_scd30_ioctl(int cmd, unsigned long arg)
225 {
226 switch (cmd) {
227 case SENSOR_IOCTL_GET_INFO:{
228 /* fill the dev info here */
229 dev_sensor_info_t *info = (dev_sensor_info_t *)arg;
230 info->model = "SCD30";
231 //info->unit = ppm;
232 }break;
233 default:
234 return -1;
235 }
236
237 LOG("%s %s successfully \n", SENSOR_STR, __func__);
238 return 0;
239 }
240
241
drv_co2_sensirion_scd30_init(void)242 int drv_co2_sensirion_scd30_init(void)
243 {
244 int ret = 0;
245 sensor_obj_t sensor_temp;
246 memset(&sensor_temp, 0, sizeof(sensor_temp));
247
248 ret = drv_scd30_init_sensor(&scd30_ctx);
249 if (unlikely(ret)) {
250 return -1;
251 }
252
253 /* fill the sensor_temp obj parameters here */
254 sensor_temp.tag = TAG_DEV_CO2;
255 sensor_temp.path = dev_co2_path;
256 sensor_temp.io_port = I2C_PORT;
257 sensor_temp.open = drv_co2_sensirion_scd30_open;
258 sensor_temp.close = drv_co2_sensirion_scd30_close;
259 sensor_temp.read = drv_co2_sensirion_scd30_read;
260 sensor_temp.write = drv_co2_sensirion_scd30_write;
261 sensor_temp.ioctl = drv_co2_sensirion_scd30_ioctl;
262 sensor_temp.irq_handle = drv_co2_sensirion_scd30_irq_handle;
263
264 ret = sensor_create_obj(&sensor_temp);
265 if (unlikely(ret)) {
266 return -1;
267 }
268
269 LOG("%s %s successfully \n", SENSOR_STR, __func__);
270 return 0;
271 }
272
273 SENSOR_DRV_ADD(drv_co2_sensirion_scd30_init);
274