MySensors Library & Examples  2.3.2
AirQualitySensor.ino
1 /*
2  * The MySensors Arduino library handles the wireless radio link and protocol
3  * between your home built sensors/actuators and HA controller of choice.
4  * The sensors forms a self healing radio network with optional repeaters. Each
5  * repeater and gateway builds a routing tables in EEPROM which keeps track of the
6  * network topology allowing messages to be routed to nodes.
7  *
8  * Created by Henrik Ekblad <[email protected]>
9  * Copyright (C) 2013-2019 Sensnology AB
10  * Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors
11  *
12  * Documentation: http://www.mysensors.org
13  * Support Forum: http://forum.mysensors.org
14  *
15  * This program is free software; you can redistribute it and/or
16  * modify it under the terms of the GNU General Public License
17  * version 2 as published by the Free Software Foundation.
18  *
19  *******************************
20  *
21  * DESCRIPTION
22  *
23  * Connect the MQ2 sensor as follows :
24  *
25  * A H A >>> 5V
26  * B >>> A0
27  * H >>> GND
28  * B >>> 10K ohm >>> GND
29  *
30  * Contribution: epierre
31  * Based on http://sandboxelectronics.com/?p=165
32  * License: Attribution-NonCommercial-ShareAlike 3.0 Unported (CC BY-NC-SA 3.0)
33  * Modified by HEK to work in 1.4
34  *
35  */
36 
37 // Enable debug prints to serial monitor
38 #define MY_DEBUG
39 
40 // Enable and select radio type attached
41 #define MY_RADIO_RF24
42 //#define MY_RADIO_NRF5_ESB
43 //#define MY_RADIO_RFM69
44 //#define MY_RADIO_RFM95
45 
46 #include <MySensors.h>
47 
48 #define CHILD_ID_MQ 0
49 /************************Hardware Related Macros************************************/
50 #define MQ_SENSOR_ANALOG_PIN (0) //define which analog input channel you are going to use
51 #define RL_VALUE (5) //define the load resistance on the board, in kilo ohms
52 #define RO_CLEAN_AIR_FACTOR (9.83) //RO_CLEAR_AIR_FACTOR=(Sensor resistance in clean air)/RO,
53 //which is derived from the chart in datasheet
54 /***********************Software Related Macros************************************/
55 #define CALIBARAION_SAMPLE_TIMES (50) //define how many samples you are going to take in the calibration phase
56 #define CALIBRATION_SAMPLE_INTERVAL (500) //define the time interval(in milliseconds) between each samples in the
57 //calibration phase
58 #define READ_SAMPLE_INTERVAL (50) //define how many samples you are going to take in normal operation
59 #define READ_SAMPLE_TIMES (5) //define the time interval(in milliseconds) between each samples in
60 //normal operation
61 /**********************Application Related Macros**********************************/
62 #define GAS_LPG (0)
63 #define GAS_CO (1)
64 #define GAS_SMOKE (2)
65 /*****************************Globals***********************************************/
66 uint32_t SLEEP_TIME = 30000; // Sleep time between reads (in milliseconds)
67 //VARIABLES
68 float Ro = 10000.0; // this has to be tuned 10K Ohm
69 int val = 0; // variable to store the value coming from the sensor
70 uint16_t lastMQ = 0;
71 float LPGCurve[3] = {2.3,0.21,-0.47}; //two points are taken from the curve.
72 //with these two points, a line is formed which is "approximately equivalent"
73 //to the original curve.
74 //data format:{ x, y, slope}; point1: (lg200, 0.21), point2: (lg10000, -0.59)
75 float COCurve[3] = {2.3,0.72,-0.34}; //two points are taken from the curve.
76 //with these two points, a line is formed which is "approximately equivalent"
77 //to the original curve.
78 //data format:{ x, y, slope}; point1: (lg200, 0.72), point2: (lg10000, 0.15)
79 float SmokeCurve[3] = {2.3,0.53,-0.44}; //two points are taken from the curve.
80 //with these two points, a line is formed which is "approximately equivalent"
81 //to the original curve.
82 //data format:{ x, y, slope}; point1: (lg200, 0.53), point2:(lg10000,-0.22)
83 
84 
85 MyMessage msg(CHILD_ID_MQ, V_LEVEL);
86 
87 void setup()
88 {
89  Ro = MQCalibration(
90  MQ_SENSOR_ANALOG_PIN); //Calibrating the sensor. Please make sure the sensor is in clean air
91 }
92 
94 {
95  // Send the sketch version information to the gateway and Controller
96  sendSketchInfo("Air Quality Sensor", "1.0");
97 
98  // Register all sensors to gateway (they will be created as child devices)
99  present(CHILD_ID_MQ, S_AIR_QUALITY);
100 }
101 
102 void loop()
103 {
104  uint16_t valMQ = MQGetGasPercentage(MQRead(MQ_SENSOR_ANALOG_PIN)/Ro,GAS_CO);
105  Serial.println(val);
106 
107  Serial.print("LPG:");
108  Serial.print(MQGetGasPercentage(MQRead(MQ_SENSOR_ANALOG_PIN)/Ro,GAS_LPG) );
109  Serial.print( "ppm" );
110  Serial.print(" ");
111  Serial.print("CO:");
112  Serial.print(MQGetGasPercentage(MQRead(MQ_SENSOR_ANALOG_PIN)/Ro,GAS_CO) );
113  Serial.print( "ppm" );
114  Serial.print(" ");
115  Serial.print("SMOKE:");
116  Serial.print(MQGetGasPercentage(MQRead(MQ_SENSOR_ANALOG_PIN)/Ro,GAS_SMOKE) );
117  Serial.print( "ppm" );
118  Serial.print("\n");
119 
120  if (valMQ != lastMQ) {
121  send(msg.set((int16_t)ceil(valMQ)));
122  lastMQ = ceil(valMQ);
123  }
124 
125  sleep(SLEEP_TIME); //sleep for: sleepTime
126 }
127 
128 /****************** MQResistanceCalculation ****************************************
129 Input: raw_adc - raw value read from adc, which represents the voltage
130 Output: the calculated sensor resistance
131 Remarks: The sensor and the load resistor forms a voltage divider. Given the voltage
132  across the load resistor and its resistance, the resistance of the sensor
133  could be derived.
134 ************************************************************************************/
135 float MQResistanceCalculation(int raw_adc)
136 {
137  return ( ((float)RL_VALUE*(1023-raw_adc)/raw_adc));
138 }
139 
140 /***************************** MQCalibration ****************************************
141 Input: mq_pin - analog channel
142 Output: Ro of the sensor
143 Remarks: This function assumes that the sensor is in clean air. It use
144  MQResistanceCalculation to calculates the sensor resistance in clean air
145  and then divides it with RO_CLEAN_AIR_FACTOR. RO_CLEAN_AIR_FACTOR is about
146  10, which differs slightly between different sensors.
147 ************************************************************************************/
148 float MQCalibration(int mq_pin)
149 {
150  int i;
151  float inVal=0;
152 
153  for (i=0; i<CALIBARAION_SAMPLE_TIMES; i++) { //take multiple samples
154  inVal += MQResistanceCalculation(analogRead(mq_pin));
155  delay(CALIBRATION_SAMPLE_INTERVAL);
156  }
157  inVal = inVal/CALIBARAION_SAMPLE_TIMES; //calculate the average value
158 
159  inVal = inVal/RO_CLEAN_AIR_FACTOR; //divided by RO_CLEAN_AIR_FACTOR yields the Ro
160  //according to the chart in the datasheet
161 
162  return inVal;
163 }
164 /***************************** MQRead *********************************************
165 Input: mq_pin - analog channel
166 Output: Rs of the sensor
167 Remarks: This function use MQResistanceCalculation to calculate the sensor resistance (Rs).
168  The Rs changes as the sensor is in the different concentration of the target
169  gas. The sample times and the time interval between samples could be configured
170  by changing the definition of the macros.
171 ************************************************************************************/
172 float MQRead(int mq_pin)
173 {
174  int i;
175  float rs=0;
176 
177  for (i=0; i<READ_SAMPLE_TIMES; i++) {
178  rs += MQResistanceCalculation(analogRead(mq_pin));
179  delay(READ_SAMPLE_INTERVAL);
180  }
181 
182  rs = rs/READ_SAMPLE_TIMES;
183 
184  return rs;
185 }
186 
187 /***************************** MQGetGasPercentage **********************************
188 Input: rs_ro_ratio - Rs divided by Ro
189  gas_id - target gas type
190 Output: ppm of the target gas
191 Remarks: This function passes different curves to the MQGetPercentage function which
192  calculates the ppm (parts per million) of the target gas.
193 ************************************************************************************/
194 int MQGetGasPercentage(float rs_ro_ratio, int gas_id)
195 {
196  if ( gas_id == GAS_LPG ) {
197  return MQGetPercentage(rs_ro_ratio,LPGCurve);
198  } else if ( gas_id == GAS_CO ) {
199  return MQGetPercentage(rs_ro_ratio,COCurve);
200  } else if ( gas_id == GAS_SMOKE ) {
201  return MQGetPercentage(rs_ro_ratio,SmokeCurve);
202  }
203 
204  return 0;
205 }
206 
207 /***************************** MQGetPercentage **********************************
208 Input: rs_ro_ratio - Rs divided by Ro
209  pcurve - pointer to the curve of the target gas
210 Output: ppm of the target gas
211 Remarks: By using the slope and a point of the line. The x(logarithmic value of ppm)
212  of the line could be derived if y(rs_ro_ratio) is provided. As it is a
213  logarithmic coordinate, power of 10 is used to convert the result to non-logarithmic
214  value.
215 ************************************************************************************/
216 int MQGetPercentage(float rs_ro_ratio, float *pcurve)
217 {
218  return (pow(10,( ((log(rs_ro_ratio)-pcurve[1])/pcurve[2]) + pcurve[0])));
219 }
MyMessage is used to create, manipulate, send and read MySensors messages.
Definition: MyMessage.h:289
bool sendSketchInfo(const char *name, const char *version, const bool requestEcho=false)
API declaration for MySensors.
bool send(MyMessage &msg, const bool requestEcho=false)
bool present(const uint8_t sensorId, const mysensors_sensor_t sensorType, const char *description="", const bool requestEcho=false)
void setup()
Called after node initialises but before main loop.
int8_t sleep(const uint32_t sleepingMS, const bool smartSleep=false)
void presentation()
Node presentation.
MyMessage & set(const void *payload, const size_t length)
Set entire payload.
void loop()
Main loop.