2 * Copyright (c) 2018 Samsung Electronics Co., Ltd.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an AS IS BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
20 #include <app_common.h>
21 #include "smartthings_resource.h"
22 #include "resource/resource_co2_sensor.h"
25 #define CO2_DATA "co2_data" // save co2 data
27 #define MAX_PATH_LEN 128
28 #define DC_GAIN (8.500) // refer to schematic, (R2 + R3) / R2 = 8.5
29 #define DEFAULT_ZERO_VOLTS (2.950)
30 #define DEFAULT_RANGE_VOLTS (0.400) // refer to datasheet
31 #define DEFAULT_NOTIFY_TIME (100) // 1000msec
33 #define ADC_PIN 0 // adc pin number
34 #define ADC_ERROR (-9999) // adc read error
35 #define ADC_MAX_VOLT (4096.f) // 12bit resolution
36 #define ADC_REF_VOLT (3300.f + 20.f)
38 #define SPI_MAX_VOLT (3200.f)
39 #define SPI_REF_VOLT (3000.f)
41 #define POINT_X_ZERO (2.60206) // the start point, log(400)=2.6020606
42 #define POINT_X_MAX (4.000) // the start point, log(10000)=4.0
43 #define VOLT_V_ZERO (0.3245) // refer to datasheet, 400ppm(V)
44 #define VOLT_V_MAX (0.2645) // refer to datasheet, 10000ppm(V)
45 #define VOLT_V_REACT (VOLT_V_ZERO - VOLT_V_MAX)
47 static const char* RES_CAPABILITY_AIRQUALITYSENSOR = "/capability/airQualitySensor/main/0";
48 static const char* PROP_AIRQUALITY = "airQuality";
50 static float mg811_co2_slope; // refer to datasheet, mg811 co2 slope value
51 static sensor_mg811_t mg_sensor;
53 co2_sensor_data_t co2_sensor;
55 extern int32_t g_co2_sensor_value;
56 extern bool g_switch_is_on;
57 extern smartthings_resource_h st_handle;
59 extern int resource_read_adc_mcp3008(int ch_num, unsigned int *out_value); /* resource_adc_mcp3008.c */
60 extern int resource_adc_mcp3008_init(void); /* resource_adc_mcp3008.c */
62 static int _get_sensor_parameter(int index);
65 * initial co2 mg811 sensor for ppm caculation
67 static void _init_co2_mg811_set(float zero_volts, float max_volts)
69 sensor_mg811_t *sen = &mg_sensor;
72 if (zero_volts == 0.f)
73 zero_volts = VOLT_V_ZERO;
75 max_volts = VOLT_V_MAX;
77 sen->zero_point_volts = zero_volts;
78 sen->max_point_volts = max_volts;
79 reaction_volts = VOLT_V_REACT;
80 mg811_co2_slope = reaction_volts / (POINT_X_ZERO - POINT_X_MAX);
81 #if defined(__DEBUG__)
82 _D("CO2Volage zero_volts: %.f mV, max_volts: %.f mV, reaction %.f mV",
83 sen->zero_point_volts * 1000., sen->max_point_volts * 1000., reaction_volts * 1000.);
84 _D("mg811_ppm: %.3f V, %.3f", POINT_X_ZERO, mg811_co2_slope);
89 * get ppm value in co2 mg811 sensor
91 static int _get_co2_mg811_ppm(float volts)
94 sensor_mg811_t *sen = &mg_sensor;
97 volts = volts / DC_GAIN;
98 if (!(volts <= sen->zero_point_volts && volts >= sen->max_point_volts)) {
99 if ((debug++ % 10) == 0) {
100 _D("wrong input %.f mV, voltage range %.f ~ %.f (400 ppm ~ 10000 ppm)",
101 volts * 1000., sen->zero_point_volts * 1000., sen->max_point_volts * 1000.);
103 if (volts < sen->max_point_volts) {
108 log_volts = (volts - sen->zero_point_volts) / mg811_co2_slope;
110 return pow(10, log_volts + POINT_X_ZERO);
114 * get adc to co2 sensor analog value
116 static short resource_get_co2_sensor_analog(int ch_num)
119 unsigned int out_value = 0;
120 float sensor_value = 0;
122 ret = resource_read_adc_mcp3008(ch_num, &out_value);
125 sensor_value = ((float)out_value * 4.) * (SPI_REF_VOLT / SPI_MAX_VOLT); // 10bit -> 12bit, calibration adc volt
127 return (short)sensor_value;
131 * update to average co2 ppm value
133 int resource_update_co2_sensor_value(void)
135 co2_sensor_data_t *sensorp = &co2_sensor;
136 float sensor_value = 0;
137 float sensor_fvalue = 0;
140 static int debug = 0;
143 if (sensorp->bsize < ADC_MAX_SIZE)
144 size = sensorp->bsize;
149 for (n = 0; n < size; n++) {
151 sensor_value += (float)sensorp->sensor_value[n];
154 sensor_value = sensor_value / (float)size;
156 sensor_fvalue = (sensor_value * ADC_REF_VOLT) / ADC_MAX_VOLT;
157 percentage = _get_co2_mg811_ppm(sensor_fvalue / 1000.f);
159 if (percentage < 0 || percentage >= 10000) {
160 if ((debug++ % 5) == 0)
161 _D("sensor: %.f, volt: %.2f mV, CO2: %d ppm", sensor_value, sensor_fvalue, percentage);
168 * initial adc funtion
170 void resource_init_co2_sensor(void)
172 float zero_volts; // measurement mutimeter voltage (mV)
174 zero_volts = (float)_get_sensor_parameter(0) / 1000.0;
175 if (zero_volts == 0.0)
176 zero_volts = DEFAULT_ZERO_VOLTS;
178 _init_co2_mg811_set(zero_volts/DC_GAIN, (zero_volts - DEFAULT_RANGE_VOLTS)/DC_GAIN);
183 * 0: calibration min voltage
184 * 1: calibration max voltage (no used)
185 * 2: notification loop count (default 100 is 1000msec delay)
187 int _get_sensor_parameter(int index)
193 char path[MAX_PATH_LEN];
194 char *app_data_path = NULL;
196 app_data_path = app_get_data_path();
197 sprintf(path, "%s%s", app_data_path, CO2_DATA);
199 if((fp = fopen(path, "r")) == NULL) {
200 _E("Error: [%s] can't open adc data file", path);
203 return (int)(DEFAULT_ZERO_VOLTS * 1000);
205 fgets(buffer, 16, fp);
207 sscanf(buffer, "%d %d %d", &volts[0], &volts[1], &volts[2]);
208 _D("get parameter: %s, zero: %d, max: %d, count: %d", buffer, volts[0], volts[1], volts[2]);
218 void resource_set_sensor_parameter(int zero_volts)
222 char path[MAX_PATH_LEN];
223 char *app_data_path = NULL;
225 app_data_path = app_get_data_path();
226 sprintf(path, "%s%s", app_data_path, CO2_DATA);
228 if((fp = fopen(path, "w+")) == NULL) {
229 _E("ERROR: can't fopen file: %s", CO2_DATA);
232 memset(buffer, 0, sizeof(buffer));
233 sprintf(buffer, "%d %d %d", zero_volts, zero_volts - (int)(DEFAULT_RANGE_VOLTS * 1000), DEFAULT_NOTIFY_TIME);
240 * get co2 sensor percentage (ppm)
242 int resource_get_co2_sensor_parameter(void)
244 int param = _get_sensor_parameter(0);
250 * notify sensor value to cloud
252 static int notify_sensor_value(void)
254 int error = SMARTTHINGS_RESOURCE_ERROR_NONE;
256 smartthings_payload_h resp_payload = NULL;
258 // send notification to cloud server
259 error = smartthings_payload_create(&resp_payload);
260 if (error != SMARTTHINGS_RESOURCE_ERROR_NONE || !resp_payload) {
261 _E("smartthings_payload_create() failed, [%d]", error);
265 error = smartthings_payload_set_int(resp_payload, PROP_AIRQUALITY, g_co2_sensor_value);
266 if (error != SMARTTHINGS_RESOURCE_ERROR_NONE) {
267 _E("smartthings_payload_set_int() failed, [%d]", error);
271 error = smartthings_resource_notify(st_handle, RES_CAPABILITY_AIRQUALITYSENSOR, resp_payload);
272 if (error != SMARTTHINGS_RESOURCE_ERROR_NONE) {
273 _E("smartthings_resource_notify() failed, [%d]", error);
277 if (smartthings_payload_destroy(resp_payload)) {
278 _E("smartthings_payload_destroy() failed");
286 * main thread function to get sensor data
288 void *thread_sensor_main(void *arg)
294 co2_sensor_data_t *sensorp = &co2_sensor;
296 _D("%s starting...\n", __func__);
298 memset((void *)sensorp, 0, sizeof(co2_sensor_data_t));
299 resource_init_co2_sensor();
301 ret = resource_adc_mcp3008_init();
302 _D("resource_adc_mcp3008_init ret: %d", ret);
306 if (thread_done) break;
308 sensor_value = resource_get_co2_sensor_analog(pin);
309 if (sensor_value >= 0)
312 sensorp->sensor_value[sensorp->index++] = sensor_value;
313 if (sensorp->index >= ADC_MAX_SIZE)
315 if (sensorp->bsize < ADC_MAX_SIZE)
320 usleep(10); // 10usec
324 if (++err_count >= 100)
326 memset((void *)sensorp, 0, sizeof(co2_sensor_data_t));
328 usleep(10 * 1000); // 10msec
331 _D("%s exiting...\n", __func__);
332 pthread_exit((void *) 0);
335 void *thread_sensor_notify(void *arg)
340 count = _get_sensor_parameter(2);
342 count = DEFAULT_NOTIFY_TIME;
344 if (thread_done) break;
346 if (nloop++ >= count) {
348 // notify sensor value to server
349 co2_sensor_data_t *sensorp = &co2_sensor;
351 if (g_co2_sensor_value > 0 && g_co2_sensor_value < 10000)
352 _D("CO2 value: %d, count: %d", g_co2_sensor_value, sensorp->count);
358 if (g_switch_is_on) {
359 notify_sensor_value();
362 usleep(10 * 1000); // 10msec
364 _D("%s exiting...\n", __func__);
365 pthread_exit((void *) 0);