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 "st_things.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 float mg811_co2_slope; // refer to datasheet, mg811 co2 slope value
49 static sensor_mg811_t mg_sensor;
51 co2_sensor_data_t co2_sensor;
53 extern int32_t g_co2_sensor_value;
54 extern bool g_switch_is_on;
56 extern int resource_read_adc_mcp3008(int ch_num, unsigned int *out_value); /* resource_adc_mcp3008.c */
57 extern int resource_adc_mcp3008_init(void); /* resource_adc_mcp3008.c */
59 static int _get_sensor_parameter(int index);
62 * initial co2 mg811 sensor for ppm caculation
64 static void _init_co2_mg811_set(float zero_volts, float max_volts)
66 sensor_mg811_t *sen = &mg_sensor;
69 if (zero_volts == 0.f)
70 zero_volts = VOLT_V_ZERO;
72 max_volts = VOLT_V_MAX;
74 sen->zero_point_volts = zero_volts;
75 sen->max_point_volts = max_volts;
76 reaction_volts = VOLT_V_REACT;
77 mg811_co2_slope = reaction_volts / (POINT_X_ZERO - POINT_X_MAX);
78 #if defined(__DEBUG__)
79 DBG("CO2Volage zero_volts: %.f mV, max_volts: %.f mV, reaction %.f mV",
80 sen->zero_point_volts * 1000., sen->max_point_volts * 1000., reaction_volts * 1000.);
81 DBG("mg811_ppm: %.3f V, %.3f", POINT_X_ZERO, mg811_co2_slope);
86 * get ppm value in co2 mg811 sensor
88 static int _get_co2_mg811_ppm(float volts)
91 sensor_mg811_t *sen = &mg_sensor;
94 volts = volts / DC_GAIN;
95 if (!(volts <= sen->zero_point_volts && volts >= sen->max_point_volts)) {
96 if ((debug++ % 10) == 0) {
97 DBG("wrong input %.f mV, voltage range %.f ~ %.f (400 ppm ~ 10000 ppm)",
98 volts * 1000., sen->zero_point_volts * 1000., sen->max_point_volts * 1000.);
100 if (volts < sen->max_point_volts) {
105 log_volts = (volts - sen->zero_point_volts) / mg811_co2_slope;
107 return pow(10, log_volts + POINT_X_ZERO);
111 * get adc to co2 sensor analog value
113 static short resource_get_co2_sensor_analog(int ch_num)
116 unsigned int out_value = 0;
117 float sensor_value = 0;
119 ret = resource_read_adc_mcp3008(ch_num, &out_value);
122 sensor_value = ((float)out_value * 4.) * (SPI_REF_VOLT / SPI_MAX_VOLT); // 10bit -> 12bit, calibration adc volt
124 return (short)sensor_value;
128 * update to average co2 ppm value
130 int resource_update_co2_sensor_value(void)
132 co2_sensor_data_t *sensorp = &co2_sensor;
133 float sensor_value = 0;
134 float sensor_fvalue = 0;
137 static int debug = 0;
140 if (sensorp->bsize < ADC_MAX_SIZE)
141 size = sensorp->bsize;
146 for (n = 0; n < size; n++) {
148 sensor_value += (float)sensorp->sensor_value[n];
151 sensor_value = sensor_value / (float)size;
153 sensor_fvalue = (sensor_value * ADC_REF_VOLT) / ADC_MAX_VOLT;
154 percentage = _get_co2_mg811_ppm(sensor_fvalue / 1000.f);
156 if (percentage < 0 || percentage >= 10000) {
157 if ((debug++ % 5) == 0)
158 DBG("sensor: %.f, volt: %.2f mV, CO2: %d ppm", sensor_value, sensor_fvalue, percentage);
165 * initial adc funtion
167 void resource_init_co2_sensor(void)
169 float zero_volts; // measurement mutimeter voltage (mV)
171 zero_volts = (float)_get_sensor_parameter(0) / 1000.0;
172 if (zero_volts == 0.0)
173 zero_volts = DEFAULT_ZERO_VOLTS;
175 _init_co2_mg811_set(zero_volts/DC_GAIN, (zero_volts - DEFAULT_RANGE_VOLTS)/DC_GAIN);
180 * 0: calibration min voltage
181 * 1: calibration max voltage (no used)
182 * 2: notification loop count (default 100 is 1000msec delay)
184 int _get_sensor_parameter(int index)
190 char path[MAX_PATH_LEN];
191 char *app_data_path = NULL;
193 app_data_path = app_get_data_path();
194 sprintf(path, "%s%s", app_data_path, CO2_DATA);
196 if((fp = fopen(path, "r")) == NULL) {
197 ERR("Error: [%s] can't open adc data file", path);
200 return (int)(DEFAULT_ZERO_VOLTS * 1000);
202 fgets(buffer, 16, fp);
204 sscanf(buffer, "%d %d %d", &volts[0], &volts[1], &volts[2]);
205 DBG("get parameter: %s, zero: %d, max: %d, count: %d", buffer, volts[0], volts[1], volts[2]);
215 void resource_set_sensor_parameter(int zero_volts)
219 char path[MAX_PATH_LEN];
220 char *app_data_path = NULL;
222 app_data_path = app_get_data_path();
223 sprintf(path, "%s%s", app_data_path, CO2_DATA);
225 if((fp = fopen(path, "w+")) == NULL) {
226 ERR("ERROR: can't fopen file: %s", CO2_DATA);
229 memset(buffer, 0, sizeof(buffer));
230 sprintf(buffer, "%d %d %d", zero_volts, zero_volts - (int)(DEFAULT_RANGE_VOLTS * 1000), DEFAULT_NOTIFY_TIME);
237 * get co2 sensor percentage (ppm)
239 int resource_get_co2_sensor_parameter(void)
241 int param = _get_sensor_parameter(0);
247 * main thread funtion to get sensor data
249 void *thread_sensor_main(void *arg)
255 co2_sensor_data_t *sensorp = &co2_sensor;
257 DBG("%s starting...\n", __func__);
259 memset((void *)sensorp, 0, sizeof(co2_sensor_data_t));
260 resource_init_co2_sensor();
262 ret = resource_adc_mcp3008_init();
263 DBG("resource_adc_mcp3008_init ret: %d", ret);
267 if (thread_done) break;
269 sensor_value = resource_get_co2_sensor_analog(pin);
270 if (sensor_value >= 0)
273 sensorp->sensor_value[sensorp->index++] = sensor_value;
274 if (sensorp->index >= ADC_MAX_SIZE)
276 if (sensorp->bsize < ADC_MAX_SIZE)
281 usleep(10); // 10usec
285 if (++err_count >= 100)
287 memset((void *)sensorp, 0, sizeof(co2_sensor_data_t));
289 usleep(10 * 1000); // 10msec
292 DBG("%s exiting...\n", __func__);
293 pthread_exit((void *) 0);
296 void *thread_sensor_notify(void *arg)
301 count = _get_sensor_parameter(2);
303 count = DEFAULT_NOTIFY_TIME;
305 if (thread_done) break;
307 if (nloop++ >= count) {
309 // notify sensor value to server
310 co2_sensor_data_t *sensorp = &co2_sensor;
312 if (g_co2_sensor_value > 0 && g_co2_sensor_value < 10000)
313 DBG("CO2 value: %d, count: %d", g_co2_sensor_value, sensorp->count);
320 st_things_notify_observers(RES_CAPABILITY_AIRQUALITYSENSOR);
322 usleep(10 * 1000); // 10msec
324 DBG("%s exiting...\n", __func__);
325 pthread_exit((void *) 0);