MySensors Library & Examples  2.3.2-36-g2e00bf6
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-2020 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 //#define MY_PJON
46 
47 #include <MySensors.h>
48 
49 #define CHILD_ID_MQ 0
50 /************************Hardware Related Macros************************************/
51 #define MQ_SENSOR_ANALOG_PIN (0) //define which analog input channel you are going to use
52 #define RL_VALUE (5) //define the load resistance on the board, in kilo ohms
53 #define RO_CLEAN_AIR_FACTOR (9.83) //RO_CLEAR_AIR_FACTOR=(Sensor resistance in clean air)/RO,
54 //which is derived from the chart in datasheet
55 /***********************Software Related Macros************************************/
56 #define CALIBARAION_SAMPLE_TIMES (50) //define how many samples you are going to take in the calibration phase
57 #define CALIBRATION_SAMPLE_INTERVAL (500) //define the time interval(in milliseconds) between each samples in the
58 //calibration phase
59 #define READ_SAMPLE_INTERVAL (50) //define how many samples you are going to take in normal operation
60 #define READ_SAMPLE_TIMES (5) //define the time interval(in milliseconds) between each samples in
61 //normal operation
62 /**********************Application Related Macros**********************************/
63 #define GAS_LPG (0)
64 #define GAS_CO (1)
65 #define GAS_SMOKE (2)
66 /*****************************Globals***********************************************/
67 uint32_t SLEEP_TIME = 30000; // Sleep time between reads (in milliseconds)
68 //VARIABLES
69 float Ro = 10000.0; // this has to be tuned 10K Ohm
70 int val = 0; // variable to store the value coming from the sensor
71 uint16_t lastMQ = 0;
72 float LPGCurve[3] = {2.3,0.21,-0.47}; //two points are taken from the curve.
73 //with these two points, a line is formed which is "approximately equivalent"
74 //to the original curve.
75 //data format:{ x, y, slope}; point1: (lg200, 0.21), point2: (lg10000, -0.59)
76 float COCurve[3] = {2.3,0.72,-0.34}; //two points are taken from the curve.
77 //with these two points, a line is formed which is "approximately equivalent"
78 //to the original curve.
79 //data format:{ x, y, slope}; point1: (lg200, 0.72), point2: (lg10000, 0.15)
80 float SmokeCurve[3] = {2.3,0.53,-0.44}; //two points are taken from the curve.
81 //with these two points, a line is formed which is "approximately equivalent"
82 //to the original curve.
83 //data format:{ x, y, slope}; point1: (lg200, 0.53), point2:(lg10000,-0.22)
84 
85 
86 MyMessage msg(CHILD_ID_MQ, V_LEVEL);
87 
88 void setup()
89 {
90  Ro = MQCalibration(
91  MQ_SENSOR_ANALOG_PIN); //Calibrating the sensor. Please make sure the sensor is in clean air
92 }
93 
95 {
96  // Send the sketch version information to the gateway and Controller
97  sendSketchInfo("Air Quality Sensor", "1.0");
98 
99  // Register all sensors to gateway (they will be created as child devices)
100  present(CHILD_ID_MQ, S_AIR_QUALITY);
101 }
102 
103 void loop()
104 {
105  uint16_t valMQ = MQGetGasPercentage(MQRead(MQ_SENSOR_ANALOG_PIN)/Ro,GAS_CO);
106  Serial.println(val);
107 
108  Serial.print("LPG:");
109  Serial.print(MQGetGasPercentage(MQRead(MQ_SENSOR_ANALOG_PIN)/Ro,GAS_LPG) );
110  Serial.print( "ppm" );
111  Serial.print(" ");
112  Serial.print("CO:");
113  Serial.print(MQGetGasPercentage(MQRead(MQ_SENSOR_ANALOG_PIN)/Ro,GAS_CO) );
114  Serial.print( "ppm" );
115  Serial.print(" ");
116  Serial.print("SMOKE:");
117  Serial.print(MQGetGasPercentage(MQRead(MQ_SENSOR_ANALOG_PIN)/Ro,GAS_SMOKE) );
118  Serial.print( "ppm" );
119  Serial.print("\n");
120 
121  if (valMQ != lastMQ) {
122  send(msg.set((int16_t)ceil(valMQ)));
123  lastMQ = ceil(valMQ);
124  }
125 
126  sleep(SLEEP_TIME); //sleep for: sleepTime
127 }
128 
129 /****************** MQResistanceCalculation ****************************************
130 Input: raw_adc - raw value read from adc, which represents the voltage
131 Output: the calculated sensor resistance
132 Remarks: The sensor and the load resistor forms a voltage divider. Given the voltage
133  across the load resistor and its resistance, the resistance of the sensor
134  could be derived.
135 ************************************************************************************/
136 float MQResistanceCalculation(int raw_adc)
137 {
138  return ( ((float)RL_VALUE*(1023-raw_adc)/raw_adc));
139 }
140 
141 /***************************** MQCalibration ****************************************
142 Input: mq_pin - analog channel
143 Output: Ro of the sensor
144 Remarks: This function assumes that the sensor is in clean air. It use
145  MQResistanceCalculation to calculates the sensor resistance in clean air
146  and then divides it with RO_CLEAN_AIR_FACTOR. RO_CLEAN_AIR_FACTOR is about
147  10, which differs slightly between different sensors.
148 ************************************************************************************/
149 float MQCalibration(int mq_pin)
150 {
151  int i;
152  float inVal=0;
153 
154  for (i=0; i<CALIBARAION_SAMPLE_TIMES; i++) { //take multiple samples
155  inVal += MQResistanceCalculation(analogRead(mq_pin));
156  delay(CALIBRATION_SAMPLE_INTERVAL);
157  }
158  inVal = inVal/CALIBARAION_SAMPLE_TIMES; //calculate the average value
159 
160  inVal = inVal/RO_CLEAN_AIR_FACTOR; //divided by RO_CLEAN_AIR_FACTOR yields the Ro
161  //according to the chart in the datasheet
162 
163  return inVal;
164 }
165 /***************************** MQRead *********************************************
166 Input: mq_pin - analog channel
167 Output: Rs of the sensor
168 Remarks: This function use MQResistanceCalculation to calculate the sensor resistance (Rs).
169  The Rs changes as the sensor is in the different concentration of the target
170  gas. The sample times and the time interval between samples could be configured
171  by changing the definition of the macros.
172 ************************************************************************************/
173 float MQRead(int mq_pin)
174 {
175  int i;
176  float rs=0;
177 
178  for (i=0; i<READ_SAMPLE_TIMES; i++) {
179  rs += MQResistanceCalculation(analogRead(mq_pin));
180  delay(READ_SAMPLE_INTERVAL);
181  }
182 
183  rs = rs/READ_SAMPLE_TIMES;
184 
185  return rs;
186 }
187 
188 /***************************** MQGetGasPercentage **********************************
189 Input: rs_ro_ratio - Rs divided by Ro
190  gas_id - target gas type
191 Output: ppm of the target gas
192 Remarks: This function passes different curves to the MQGetPercentage function which
193  calculates the ppm (parts per million) of the target gas.
194 ************************************************************************************/
195 int MQGetGasPercentage(float rs_ro_ratio, int gas_id)
196 {
197  if ( gas_id == GAS_LPG ) {
198  return MQGetPercentage(rs_ro_ratio,LPGCurve);
199  } else if ( gas_id == GAS_CO ) {
200  return MQGetPercentage(rs_ro_ratio,COCurve);
201  } else if ( gas_id == GAS_SMOKE ) {
202  return MQGetPercentage(rs_ro_ratio,SmokeCurve);
203  }
204 
205  return 0;
206 }
207 
208 /***************************** MQGetPercentage **********************************
209 Input: rs_ro_ratio - Rs divided by Ro
210  pcurve - pointer to the curve of the target gas
211 Output: ppm of the target gas
212 Remarks: By using the slope and a point of the line. The x(logarithmic value of ppm)
213  of the line could be derived if y(rs_ro_ratio) is provided. As it is a
214  logarithmic coordinate, power of 10 is used to convert the result to non-logarithmic
215  value.
216 ************************************************************************************/
217 int MQGetPercentage(float rs_ro_ratio, float *pcurve)
218 {
219  return (pow(10,( ((log(rs_ro_ratio)-pcurve[1])/pcurve[2]) + pcurve[0])));
220 }
sendSketchInfo
bool sendSketchInfo(const char *name, const char *version, const bool requestEcho=false)
loop
void loop()
Main loop.
Definition: AirQualitySensor.ino:103
presentation
void presentation()
Node presentation.
Definition: AirQualitySensor.ino:94
MyMessage::set
MyMessage & set(const void *payload, const size_t length)
Set entire payload.
send
bool send(MyMessage &msg, const bool requestEcho=false)
present
bool present(const uint8_t sensorId, const mysensors_sensor_t sensorType, const char *description="", const bool requestEcho=false)
setup
void setup()
Called after node initialises but before main loop.
Definition: AirQualitySensor.ino:88
sleep
int8_t sleep(const uint32_t sleepingMS, const bool smartSleep=false)
MySensors.h
API declaration for MySensors.
MyMessage
MyMessage is used to create, manipulate, send and read MySensors messages.
Definition: MyMessage.h:289