From fe318a78d2c65a894042b79b704d5ebbcfe2148f Mon Sep 17 00:00:00 2001 From: Noel Eck Date: Tue, 6 Sep 2016 08:40:37 -0700 Subject: [PATCH] mqx: C gas sensors mq2-mq9 Implement a single source file for the following: * mq2: H2/LPG/CH4/CO/alcohol/smoke/propane/etc * mq3: Alcohol/benzine/CH4/hexane/LPG/CO * mq4: CNG (mostly methane) * mq5: H2/LPG/CH4/CO/alcohol * mq6: LPG (mostly propane/butane) * mq7: CO * mq8: H * mq9: CO/CH4/LPG The documentation for these sensors notes that they do not read an exact gas concentration. Some of the sensors have examples for how to calibrate with a 'clean air environment' for outputing ppm, but this is not the case for all mqx sensors. Because of this, the output can be read as a normalized output (0.0->1.0), raw volts (normalized * aRef), or a scaled/offset voltage. Signed-off-by: Noel Eck --- examples/c/CMakeLists.txt | 1 + examples/c/mqx.c | 86 +++++++++++++++++++++ src/gas/CMakeLists.txt | 13 ++-- src/gas/gas.cxx | 2 +- src/gas/mqx.c | 127 +++++++++++++++++++++++++++++++ src/gas/mqx.h | 153 ++++++++++++++++++++++++++++++++++++++ src/gas/mqx_fti.c | 122 ++++++++++++++++++++++++++++++ 7 files changed, 498 insertions(+), 6 deletions(-) create mode 100644 examples/c/mqx.c create mode 100644 src/gas/mqx.c create mode 100644 src/gas/mqx.h create mode 100644 src/gas/mqx_fti.c diff --git a/examples/c/CMakeLists.txt b/examples/c/CMakeLists.txt index ef2aa9dd..03d74762 100644 --- a/examples/c/CMakeLists.txt +++ b/examples/c/CMakeLists.txt @@ -95,6 +95,7 @@ add_example (urm37-uart) add_example (hka5) add_example (dfrorp) add_example (vdiv) +add_example (mqx) # Custom examples add_custom_example (nmea_gps_i2c-example-c nmea_gps_i2c.c nmea_gps) diff --git a/examples/c/mqx.c b/examples/c/mqx.c new file mode 100644 index 00000000..72d02974 --- /dev/null +++ b/examples/c/mqx.c @@ -0,0 +1,86 @@ +/* + * Author: Jon Trulson + * Copyright (c) 2016 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include +#include + +#include "mqx.h" + +bool shouldRun = true; + +void sig_handler(int signo) +{ + if (signo == SIGINT) + shouldRun = false; +} + +int main() +{ + signal(SIGINT, sig_handler); + + //! [Interesting] + + // Instantiate a mqx sensor on analog pin A0 + mqx_context sensor = mqx_init(0); + + if (!sensor) + { + printf("mqx_init() failed.\n"); + return -1; + } + + // Set the aref, scale, and offset + mqx_set_aref(sensor, 5.0); + mqx_set_scale(sensor, 1.0); + mqx_set_offset(sensor, -.1); + printf("aRef: %0.03f scale: %0.03f offset: %0.03f\n\n", + mqx_get_aref(sensor), + mqx_get_scale(sensor), + mqx_get_offset(sensor)); + + // Every half a second, sample the sensor output + while (shouldRun) + { + float normalized = 0.0; + float raw_volts = 0.0; + float volts = 0.0; + + mqx_get_normalized(sensor, &normalized); + mqx_get_raw_volts(sensor, &raw_volts); + mqx_get_volts(sensor, &volts); + + printf("Normalized output: %0.03f, raw mqx sensor output: %0.03f v " + "adjusted output: %0.03f v\n", normalized, raw_volts, volts); + + usleep(500000); + } + + //! [Interesting] + + printf("Exiting\n"); + + mqx_close(sensor); + + return 0; +} diff --git a/src/gas/CMakeLists.txt b/src/gas/CMakeLists.txt index 76b36cab..235772c9 100644 --- a/src/gas/CMakeLists.txt +++ b/src/gas/CMakeLists.txt @@ -1,5 +1,8 @@ -set (libname "gas") -set (libdescription "Gas sensors") -set (module_src ${libname}.cxx mq2.cxx mq3.cxx mq4.cxx mq5.cxx mq6.cxx mq7.cxx mq8.cxx mq9.cxx tp401.cxx) -set (module_hpp ${libname}.hpp mq2.hpp mq3.hpp mq4.hpp mq5.hpp mq6.hpp mq7.hpp mq8.hpp mq9.hpp tp401.hpp) -upm_module_init() +upm_mixed_module_init (NAME gas + DESCRIPTION "Gas sensors" + C_HDR mqx.h + C_SRC mqx.c + CPP_HDR gas.hpp mq2.hpp mq3.hpp mq4.hpp mq5.hpp mq6.hpp mq7.hpp mq8.hpp mq9.hpp tp401.hpp + CPP_SRC gas.cxx mq2.cxx mq3.cxx mq4.cxx mq5.cxx mq6.cxx mq7.cxx mq8.cxx mq9.cxx tp401.cxx + FTI_SRC mqx_fti.c + REQUIRES mraa) diff --git a/src/gas/gas.cxx b/src/gas/gas.cxx index 1fd0a497..06855aeb 100644 --- a/src/gas/gas.cxx +++ b/src/gas/gas.cxx @@ -65,7 +65,7 @@ int Gas::findThreshold (thresholdContext* ctx, unsigned int threshold, uint16_t * buffer, int len) { long sum = 0; - for (unsigned int i = 0; i < len; i++) { + for (int i = 0; i < len; i++) { sum += buffer[i]; } diff --git a/src/gas/mqx.c b/src/gas/mqx.c new file mode 100644 index 00000000..9a4ef77d --- /dev/null +++ b/src/gas/mqx.c @@ -0,0 +1,127 @@ +/* + * Author: + * Copyright (c) 2015 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include +#include + +#include "mqx.h" + +mqx_context mqx_init(int16_t pin) +{ + mqx_context dev = (mqx_context) malloc(sizeof(struct _mqx_context)); + + if (dev == NULL) + return NULL; + + /* Init aio pin */ + dev->aio = mraa_aio_init(pin); + + if (dev->aio == NULL) { + free(dev); + return NULL; + } + + /* Set the ADC ref, scale, and offset defaults */ + dev->m_aRef = 5.0; + dev->m_scale = 1.0; + dev->m_offset = 0.0; + + return dev; +} + +void mqx_close(mqx_context dev) +{ + mraa_aio_close(dev->aio); + free(dev); +} + +upm_result_t mqx_set_aref(const mqx_context dev, float aref) +{ + dev->m_aRef = aref; + return UPM_SUCCESS; +} + +upm_result_t mqx_set_scale(const mqx_context dev, float scale) +{ + dev->m_scale = scale; + return UPM_SUCCESS; +} + +upm_result_t mqx_set_offset(const mqx_context dev, float offset) +{ + dev->m_offset = offset; + return UPM_SUCCESS; +} + +float mqx_get_aref(const mqx_context dev) +{ + return dev->m_aRef; +} + +float mqx_get_scale(const mqx_context dev) +{ + return dev->m_scale; +} + +float mqx_get_offset(const mqx_context dev) +{ + return dev->m_offset; +} + +upm_result_t mqx_get_normalized(const mqx_context dev, float *value) +{ + *value = mraa_aio_read_float(dev->aio); + if (*value < 0) + return UPM_ERROR_OPERATION_FAILED; +} + +upm_result_t mqx_get_raw_volts(const mqx_context dev, float *value) +{ + *value = mraa_aio_read_float(dev->aio); + if (*value < 0) + return UPM_ERROR_OPERATION_FAILED; + + /* Scale by the ADC reference voltage */ + *value *= dev->m_aRef; + + return UPM_SUCCESS; +} + +upm_result_t mqx_get_volts(const mqx_context dev, float *value) +{ + *value = mraa_aio_read_float(dev->aio); + if (*value < 0) + return UPM_ERROR_OPERATION_FAILED; + + /* Apply raw scale */ + *value *= dev->m_scale; + + /* Scale to aRef */ + *value *= dev->m_aRef; + + /* Apply the offset in volts */ + *value += dev->m_offset; + + return UPM_SUCCESS; +} diff --git a/src/gas/mqx.h b/src/gas/mqx.h new file mode 100644 index 00000000..2430d79d --- /dev/null +++ b/src/gas/mqx.h @@ -0,0 +1,153 @@ +/* + * Author: + * Copyright (c) 2015 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#pragma once + +#include "upm.h" +#include "mraa/aio.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * The mqx gas sensor driver can be used to read a wide range of sensors. The + * list below shows the currently supported sensors: + * + * mq2: H2/LPG/CH4/CO/alcohol/smoke/propane/etc + * LPG/propane 200-5000 ppm + * butane 300-5000 ppm + * CH4 5000-2000 ppm + * H2 300-5000 ppm + * alcohol 100-2000 ppm + * mq3: Alcohol/benzine/CH4/hexane/LPG/CO .05-10 mg/L + * mq4: CNG (mostly methane) 200-10000 ppm + * mq5: H2/LPG/CH4/CO/alcohol 200-10000 ppm + * mq6: LPG (mostly propane/butane) 200-10000 ppm + * mq7: CO 20-2000 ppm + * mq8: H 100-10000 ppm + * mq9: CO/CH4/LPG 200-1000/10000/10000 ppm + * + */ + +/** + * driver context + */ +typedef struct _mqx_context { + /* mraa aio pin context */ + mraa_aio_context aio; + /* Analog voltage reference */ + float m_aRef; + /* Scale */ + float m_scale; + /* Offset in sensor units */ + float m_offset; +} *mqx_context; + +/** + * Initialize analog sensor + * @param pin Analog pin + * @return sensor context + */ +mqx_context mqx_init(int16_t pin); + +/** + * Analog sensor destructor + * @param sensor context pointer + */ +void mqx_close(mqx_context dev); + +/** + * Set ADC reference voltage + * @param dev sensor context pointer + * @param aref ADC reference voltage + * @return Function result code + */ +upm_result_t mqx_set_aref(const mqx_context dev, float aref); + +/** + * Set sensor scale. This scale is applied to the return value: + * counts = counts * scale + * @param dev sensor context pointer + * @param scale count scale value used + * @return Function result code + */ +upm_result_t mqx_set_scale(const mqx_context dev, float scale); + +/** + * Set sensor offset. This offset is applied to the return value: + * counts = counts + offset + * @param dev sensor context pointer + * @param offset count offset value used + * @return Function result code + */ +upm_result_t mqx_set_offset(const mqx_context dev, float offset); + +/** + * Get sensor aref + * @param dev sensor context pointer + * @return Sensor ADC reference voltage + */ +float mqx_get_aref(const mqx_context dev); + +/** + * Get sensor scale + * @param dev sensor context pointer + * @return Sensor scale + */ +float mqx_get_scale(const mqx_context dev); + +/** + * Get sensor offset + * @param dev sensor context pointer + * @return Sensor offset + */ +float mqx_get_offset(const mqx_context dev); + +/** + * Read normalized value for sensor + * @param dev sensor context pointer + * @param *value Normalized value (0.0 -> 1.0) + * @return Function result code + */ +upm_result_t mqx_get_normalized(const mqx_context dev, float *value); + +/** + * Read raw voltage from the sensor + * @param dev sensor context pointer + * @param *value Raw sensor voltage + * @return Function result code + */ +upm_result_t mqx_get_raw_volts(const mqx_context dev, float *value); + +/** + * Read scaled/offset voltage from the sensor + * @param dev sensor context pointer + * @param *value Adjusted sensor voltage + * @return Function result code + */ +upm_result_t mqx_get_volts(const mqx_context dev, float *value); +#ifdef __cplusplus +} +#endif diff --git a/src/gas/mqx_fti.c b/src/gas/mqx_fti.c new file mode 100644 index 00000000..443f635e --- /dev/null +++ b/src/gas/mqx_fti.c @@ -0,0 +1,122 @@ +/* + * Author: + * Copyright (c) 2015 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include +#include + +#include "mqx.h" + +#include "upm_fti.h" +#include "fti/upm_sensor.h" +#include "fti/upm_raw.h" + +/** + * This file implements the Function Table Interface (FTI) for this sensor + */ + +const char upm_mqx_name[] = "MQX"; +const char upm_mqx_description[] = "Analog gas sensor (mqx)"; +const upm_protocol_t upm_mqx_protocol[] = {UPM_ANALOG}; +const upm_sensor_t upm_mqx_category[] = {UPM_RAW}; + +// forward declarations +void* upm_mqx_init_str(const char* protocol, const char* params); +void upm_mqx_close(void* dev); +const upm_sensor_descriptor_t upm_mqx_get_descriptor(); +upm_result_t upm_mqx_set_offset(const void* dev, float offset); +upm_result_t upm_mqx_set_scale(const void* dev, float scale); +upm_result_t upm_mqx_get_value(const void* dev, float *value); + + +/* This sensor implementes 2 function tables */ +/* 1. Generic base function table */ +static const upm_sensor_ft ft_gen = +{ + .upm_sensor_init_name = &upm_mqx_init_str, + .upm_sensor_close = &upm_mqx_close, + .upm_sensor_get_descriptor = &upm_mqx_get_descriptor +}; + +/* 2. RAW function table */ +static const upm_raw_ft ft_raw = +{ + .upm_raw_set_offset = &upm_mqx_set_offset, + .upm_raw_set_scale = &upm_mqx_set_scale, + .upm_raw_get_value = &upm_mqx_get_value +}; + +const void* upm_mqx_get_ft(upm_sensor_t sensor_type) +{ + switch(sensor_type) + { + case UPM_SENSOR: + return &ft_gen; + case UPM_RAW: + return &ft_raw; + default: + return NULL; + } +} + +const upm_sensor_descriptor_t upm_mqx_get_descriptor() +{ + /* Fill in the descriptor */ + upm_sensor_descriptor_t usd; + usd.name = upm_mqx_name; + usd.description = upm_mqx_description; + usd.protocol_size = 1; + usd.protocol = upm_mqx_protocol; + usd.category_size = 1; + usd.category = upm_mqx_category; + + return usd; +} + +void* upm_mqx_init_str(const char* protocol, const char* params) +{ + fprintf(stderr, "String initialization - not implemented, using ain0: %s\n", __FILENAME__); + + return mqx_init(0); +} + +void upm_mqx_close(void* dev) +{ + mqx_close((mqx_context)dev); +} + +upm_result_t upm_mqx_get_value(const void* dev, float *value) +{ + return mqx_get_volts((mqx_context)dev, value); +} + +upm_result_t upm_mqx_set_offset(const void* dev, float offset) +{ + return mqx_set_offset((mqx_context)dev, offset); +} + +upm_result_t upm_mqx_set_scale(const void* dev, float scale) +{ + return mqx_set_scale((mqx_context)dev, scale); +} +