Commit 17f53c08 authored by user2684's avatar user2684
Browse files

Add support for MQ gas sensor #36

parent 15fc8d41
......@@ -154,6 +154,12 @@ void Sensor::before() {
onBefore();
}
// call the sensor-specific implementation of setup
void Sensor::setup() {
if (_pin == -1) return;
onSetup();
}
// call the sensor-specific implementation of loop
void Sensor::loop(const MyMessage & message) {
if (_pin == -1) return;
......@@ -278,12 +284,16 @@ void SensorAnalogInput::setRangeMax(int value) {
_range_max = value;
}
// what do to during setup
// what do to during before
void SensorAnalogInput::onBefore() {
// prepare the pin for input
pinMode(_pin, INPUT);
}
// what do to during setup
void SensorAnalogInput::onSetup() {
}
// what do to during loop
void SensorAnalogInput::onLoop() {
// read the input
......@@ -375,12 +385,16 @@ void SensorThermistor::setOffset(float value) {
_offset = value;
}
// what do to during setup
// what do to during before
void SensorThermistor::onBefore() {
// set the pin as input
pinMode(_pin, INPUT);
}
// what do to during setup
void SensorThermistor::onSetup() {
}
// what do to during loop
void SensorThermistor::onLoop() {
// read the voltage across the thermistor
......@@ -423,12 +437,16 @@ void SensorThermistor::onReceive(const MyMessage & message) {
SensorDigitalInput::SensorDigitalInput(int child_id, int pin): Sensor(child_id, pin) {
}
// what do to during setup
// what do to during before
void SensorDigitalInput::onBefore() {
// set the pin for input
pinMode(_pin, INPUT);
}
// what do to during setup
void SensorDigitalInput::onSetup() {
}
// what do to during loop
void SensorDigitalInput::onLoop() {
// read the value
......@@ -459,6 +477,7 @@ void SensorDigitalInput::onReceive(const MyMessage & message) {
SensorDigitalOutput::SensorDigitalOutput(int child_id, int pin): Sensor(child_id, pin) {
}
// what do to during before
void SensorDigitalOutput::onBefore() {
// set the pin as output and initialize it accordingly
pinMode(_pin, OUTPUT);
......@@ -467,6 +486,10 @@ void SensorDigitalOutput::onBefore() {
_value_int = _initial_value;
}
// what do to during setup
void SensorDigitalOutput::onSetup() {
}
// setter/getter
void SensorDigitalOutput::setInitialValue(int value) {
_initial_value = value;
......@@ -560,12 +583,16 @@ SensorDHT::SensorDHT(int child_id, int pin, DHT* dht, int sensor_type, int dht_t
}
}
// what do to during setup
// what do to during before
void SensorDHT::onBefore() {
// initialize the dht library
_dht->begin();
}
// what do to during setup
void SensorDHT::onSetup() {
}
// what do to during loop
void SensorDHT::onLoop() {
// temperature sensor
......@@ -627,12 +654,16 @@ SensorSHT21::SensorSHT21(int child_id, int sensor_type): Sensor(child_id,A2) {
}
}
// what do to during setup
// what do to during before
void SensorSHT21::onBefore() {
// initialize the library
Wire.begin();
}
// what do to during setup
void SensorSHT21::onSetup() {
}
// what do to during loop
void SensorSHT21::onLoop() {
// temperature sensor
......@@ -708,13 +739,17 @@ int SensorSwitch::getInitial() {
return _initial;
}
// what do to during setup
// what do to during before
void SensorSwitch::onBefore() {
// initialize the value
if (_mode == RISING) _value_int = LOW;
else if (_mode == FALLING) _value_int = HIGH;
}
// what do to during setup
void SensorSwitch::onSetup() {
}
// what do to during loop
void SensorSwitch::onLoop() {
// wait to ensure the the input is not floating
......@@ -775,10 +810,14 @@ SensorDs18b20::SensorDs18b20(int child_id, int pin, DallasTemperature* sensors,
_sensors = sensors;
}
// what do to during setup
// what do to during before
void SensorDs18b20::onBefore() {
}
// what do to during setup
void SensorDs18b20::onSetup() {
}
// what do to during loop
void SensorDs18b20::onLoop() {
// request the temperature
......@@ -814,11 +853,15 @@ SensorBH1750::SensorBH1750(int child_id): Sensor(child_id,A4) {
_lightSensor = new BH1750();
}
// what do to during setup
// what do to during before
void SensorBH1750::onBefore() {
_lightSensor->begin();
}
// what do to during setup
void SensorBH1750::onSetup() {
}
// what do to during loop
void SensorBH1750::onLoop() {
// request the light level
......@@ -852,12 +895,16 @@ SensorMLX90614::SensorMLX90614(int child_id, Adafruit_MLX90614* mlx, int sensor_
setValueType(TYPE_FLOAT);
}
// what do to during setup
// what do to during before
void SensorMLX90614::onBefore() {
// initialize the library
_mlx->begin();
}
// what do to during setup
void SensorMLX90614::onSetup() {
}
// what do to during loop
void SensorMLX90614::onLoop() {
float temperature = _sensor_type == 0 ? _mlx->readAmbientTempC() : _mlx->readObjectTempC();
......@@ -907,9 +954,12 @@ SensorBME280::SensorBME280(int child_id, Adafruit_BME280* bme, int sensor_type):
}
}
// what do to during setup
// what do to during before
void SensorBME280::onBefore() {
// initialize the library
}
// what do to during setup
void SensorBME280::onSetup() {
}
// what do to during loop
......@@ -965,6 +1015,152 @@ void SensorBME280::onReceive(const MyMessage & message) {
}
#endif
/*
* SensorMQ
*/
SensorMQ::SensorMQ(int child_id, int pin): Sensor(child_id,pin) {
setPresentation(S_AIR_QUALITY);
setType(V_LEVEL);
}
//setter/getter
void SensorMQ::setRlValue(float value) {
_rl_value = value;
}
void SensorMQ::setRoValue(float value) {
_ro = value;
}
void SensorMQ::setCleanAirFactor(float value) {
_ro_clean_air_factor = value;
}
void SensorMQ::setCalibrationSampleTimes(int value) {
_calibration_sample_times = value;
}
void SensorMQ::setCalibrationSampleInterval(int value){
_calibration_sample_interval = value;
}
void SensorMQ::setReadSampleTimes(int value) {
_read_sample_times = value;
}
void SensorMQ::setReadSampleInterval(int value) {
_read_sample_interval = value;
}
void SensorMQ::setLPGCurve(float *value) {
_LPGCurve[0] = value[0];
_LPGCurve[2] = value[1];
_LPGCurve[2] = value[2];
}
void SensorMQ::setCOCurve(float *value) {
_COCurve[0] = value[0];
_COCurve[2] = value[1];
_COCurve[2] = value[2];
}
void SensorMQ::setSmokeCurve(float *value) {
_SmokeCurve[0] = value[0];
_SmokeCurve[2] = value[1];
_SmokeCurve[2] = value[2];
}
// what do to during before
void SensorMQ::onBefore() {
// prepare the pin for input
pinMode(_pin, INPUT);
}
// what do to during setup
void SensorMQ::onSetup() {
_ro = _MQCalibration();
onLoop();
}
// what do to during loop
void SensorMQ::onLoop() {
if (_pin == -1) return;
// calculate rs/ro
float mq = _MQRead()/_ro;
// calculate the ppm
float lpg = _MQGetGasPercentage(mq,_gas_lpg);
float co = _MQGetGasPercentage(mq,_gas_co);
float smoke = _MQGetGasPercentage(mq,_gas_smoke);
// assign to the value the requested gas
uint16_t value;
if (_target_gas == _gas_lpg) value = lpg;
if (_target_gas == _gas_co) value = co;
if (_target_gas == _gas_smoke) value = smoke;
#if DEBUG == 1
Serial.print(F("MQ I="));
Serial.print(_child_id);
Serial.print(F(" V="));
Serial.print(value);
Serial.print(F(" LPG="));
Serial.print(lpg);
Serial.print(F(" CO="));
Serial.print(co);
Serial.print(F(" SMOKE="));
Serial.println(smoke);
#endif
// store the value
_value_int = (int16_t)ceil(value);
}
// what do to as the main task when receiving a message
void SensorMQ::onReceive(const MyMessage & message) {
onLoop();
}
// returns the calculated sensor resistance
float SensorMQ::_MQResistanceCalculation(int raw_adc) {
return ( ((float)_rl_value*(1023-raw_adc)/raw_adc));
}
// This function assumes that the sensor is in clean air
float SensorMQ::_MQCalibration() {
int i;
float val=0;
//take multiple samples
for (i=0; i< _calibration_sample_times; i++) {
val += _MQResistanceCalculation(analogRead(_pin));
delay(_calibration_sample_interval);
}
//calculate the average value
val = val/_calibration_sample_times;
//divided by RO_CLEAN_AIR_FACTOR yields the Ro
val = val/_ro_clean_air_factor;
//according to the chart in the datasheet
return val;
}
// This function use MQResistanceCalculation to caculate the sensor resistenc (Rs).
float SensorMQ::_MQRead() {
int i;
float rs=0;
for (i=0; i<_read_sample_times; i++) {
rs += _MQResistanceCalculation(analogRead(_pin));
delay(_read_sample_interval);
}
rs = rs/_read_sample_times;
return rs;
}
// This function passes different curves to the MQGetPercentage function which calculates the ppm (parts per million) of the target gas.
int SensorMQ::_MQGetGasPercentage(float rs_ro_ratio, int gas_id) {
if ( gas_id == _gas_lpg ) {
return _MQGetPercentage(rs_ro_ratio,_LPGCurve);
} else if ( gas_id == _gas_co) {
return _MQGetPercentage(rs_ro_ratio,_COCurve);
} else if ( gas_id == _gas_smoke) {
return _MQGetPercentage(rs_ro_ratio,_SmokeCurve);
}
return 0;
}
// returns ppm of the target gas
int SensorMQ::_MQGetPercentage(float rs_ro_ratio, float *pcurve) {
return (pow(10,( ((log10(rs_ro_ratio)-pcurve[1])/pcurve[2]) + pcurve[0])));
}
/*******************************************
NodeManager
*/
......@@ -1059,6 +1255,7 @@ int NodeManager::registerSensor(int sensor_type, int pin, int child_id) {
else if (sensor_type == SENSOR_ANALOG_INPUT) return registerSensor(new SensorAnalogInput(child_id, pin));
else if (sensor_type == SENSOR_LDR) return registerSensor(new SensorLDR(child_id, pin));
else if (sensor_type == SENSOR_THERMISTOR) return registerSensor(new SensorThermistor(child_id, pin));
else if (sensor_type == SENSOR_MQ) return registerSensor(new SensorMQ(child_id, pin));
#endif
#if MODULE_DIGITAL_INPUT == 1
else if (sensor_type == SENSOR_DIGITAL_INPUT) return registerSensor(new SensorDigitalInput(child_id, pin));
......@@ -1305,6 +1502,12 @@ void NodeManager::setup() {
#if SERVICE_MESSAGES == 1
_send(_msg.set("STARTED"));
#endif
// run setup for all the registered sensors
for (int i = 0; i < 255; i++) {
if (_sensors[i] == 0) continue;
// call each sensor's setup()
_sensors[i]->setup();
}
}
// run the main function for all the register sensors
......
......@@ -41,7 +41,7 @@
#define EEPROM_SLEEP_UNIT 4
// define NodeManager version
#define VERSION 1.3
#define VERSION 1.4
/************************************
* Include user defined configuration settings
......@@ -97,7 +97,7 @@
#define BATTERY_CHILD_ID 201
#endif
// Enable this module to use one of the following sensors: SENSOR_ANALOG_INPUT, SENSOR_LDR, SENSOR_THERMISTOR
// Enable this module to use one of the following sensors: SENSOR_ANALOG_INPUT, SENSOR_LDR, SENSOR_THERMISTOR, SENSOR_MQ
#ifndef MODULE_ANALOG_INPUT
#define MODULE_ANALOG_INPUT 0
#endif
......@@ -147,6 +147,8 @@
#define SENSOR_LDR 2
// Thermistor sensor, return the temperature based on the attached thermistor
#define SENSOR_THERMISTOR 3
// MQ2 air quality sensor
#define SENSOR_MQ 19
#endif
#if MODULE_DIGITAL_INPUT == 1
// Generic digital sensor, return a pin's digital value
......@@ -194,7 +196,7 @@
// MLX90614 sensor, contactless temperature sensor
#define SENSOR_BME280 18
#endif
// last Id: 18
// last Id: 19
/***********************************
Libraries
*/
......@@ -301,10 +303,12 @@ class Sensor {
// define what to do at each stage of the sketch
virtual void before();
virtual void presentation();
virtual void setup();
virtual void loop(const MyMessage & message);
virtual void receive(const MyMessage & message);
// abstract functions, subclasses need to implement
virtual void onBefore() = 0;
virtual void onSetup() = 0;
virtual void onLoop() = 0;
virtual void onReceive(const MyMessage & message) = 0;
protected:
......@@ -353,6 +357,7 @@ class SensorAnalogInput: public Sensor {
void setRangeMax(int value);
// define what to do at each stage of the sketch
void onBefore();
void onSetup();
void onLoop();
void onReceive(const MyMessage & message);
protected:
......@@ -391,6 +396,7 @@ class SensorThermistor: public Sensor {
void setOffset(float value);
// define what to do at each stage of the sketch
void onBefore();
void onSetup();
void onLoop();
void onReceive(const MyMessage & message);
protected:
......@@ -409,6 +415,7 @@ class SensorDigitalInput: public Sensor {
SensorDigitalInput(int child_id, int pin);
// define what to do at each stage of the sketch
void onBefore();
void onSetup();
void onLoop();
void onReceive(const MyMessage & message);
};
......@@ -425,6 +432,7 @@ class SensorDigitalOutput: public Sensor {
void setPulseWidth(int value);
// define what to do at each stage of the sketch
void onBefore();
void onSetup();
void onLoop();
void onReceive(const MyMessage & message);
protected:
......@@ -460,6 +468,7 @@ class SensorDHT: public Sensor {
SensorDHT(int child_id, int pin, DHT* dht, int sensor_type, int dht_type);
// define what to do at each stage of the sketch
void onBefore();
void onSetup();
void onLoop();
void onReceive(const MyMessage & message);
protected:
......@@ -479,6 +488,7 @@ class SensorSHT21: public Sensor {
SensorSHT21(int child_id, int sensor_type);
// define what to do at each stage of the sketch
void onBefore();
void onSetup();
void onLoop();
void onReceive(const MyMessage & message);
protected:
......@@ -514,6 +524,7 @@ class SensorSwitch: public Sensor {
int getInitial();
// define what to do at each stage of the sketch
void onBefore();
void onSetup();
void onLoop();
void onReceive(const MyMessage & message);
protected:
......@@ -548,6 +559,7 @@ class SensorDs18b20: public Sensor {
SensorDs18b20(int child_id, int pin, DallasTemperature* sensors, int index);
// define what to do at each stage of the sketch
void onBefore();
void onSetup();
void onLoop();
void onReceive(const MyMessage & message);
protected:
......@@ -566,6 +578,7 @@ class SensorBH1750: public Sensor {
SensorBH1750(int child_id);
// define what to do at each stage of the sketch
void onBefore();
void onSetup();
void onLoop();
void onReceive(const MyMessage & message);
protected:
......@@ -582,6 +595,7 @@ class SensorMLX90614: public Sensor {
SensorMLX90614(int child_id, Adafruit_MLX90614* mlx, int sensor_type);
// define what to do at each stage of the sketch
void onBefore();
void onSetup();
void onLoop();
void onReceive(const MyMessage & message);
protected:
......@@ -599,6 +613,7 @@ class SensorBME280: public Sensor {
SensorBME280(int child_id, Adafruit_BME280* bme, int sensor_type);
// define what to do at each stage of the sketch
void onBefore();
void onSetup();
void onLoop();
void onReceive(const MyMessage & message);
protected:
......@@ -607,6 +622,61 @@ class SensorBME280: public Sensor {
};
#endif
/*
SensorMQ
*/
class SensorMQ: public Sensor {
public:
SensorMQ(int child_id, int pin);
// define the target gas whose ppm has to be returned. 0: LPG, 1: CO, 2: Smoke (default: 1);
void setTargetGas(int value);
// define the load resistance on the board, in kilo ohms (default: 1);
void setRlValue(float value);
// define the Ro resistance on the board (default: 10000);
void setRoValue(float value);
// Sensor resistance in clean air (default: 9.83);
void setCleanAirFactor(float value);
// define how many samples you are going to take in the calibration phase (default: 50);
void setCalibrationSampleTimes(int value);
// define the time interal(in milisecond) between each samples in the cablibration phase (default: 500);
void setCalibrationSampleInterval(int value);
// define how many samples you are going to take in normal operation (default: 50);
void setReadSampleTimes(int value);
// define the time interal(in milisecond) between each samples in the normal operations (default: 5);
void setReadSampleInterval(int value);
// set the LPGCurve array (default: {2.3,0.21,-0.47})
void setLPGCurve(float *value);
// set the COCurve array (default: {2.3,0.72,-0.34})
void setCOCurve(float *value);
// set the SmokeCurve array (default: {2.3,0.53,-0.44})
void setSmokeCurve(float *value);
// define what to do at each stage of the sketch
void onBefore();
void onSetup();
void onLoop();
void onReceive(const MyMessage & message);
protected:
float _rl_value = 1.0;
float _ro_clean_air_factor = 9.83;
int _calibration_sample_times = 50;
int _calibration_sample_interval = 500;
int _read_sample_interval = 50;
int _read_sample_times = 5;
float _ro = 10000.0;
float _LPGCurve[3] = {2.3,0.21,-0.47};
float _COCurve[3] = {2.3,0.72,-0.34};
float _SmokeCurve[3] = {2.3,0.53,-0.44};
float _MQResistanceCalculation(int raw_adc);
float _MQCalibration();
float _MQRead();
int _MQGetGasPercentage(float rs_ro_ratio, int gas_id);
int _MQGetPercentage(float rs_ro_ratio, float *pcurve);
int _gas_lpg = 0;
int _gas_co = 1;
int _gas_smoke = 2;
int _target_gas = _gas_co;
};
/***************************************
NodeManager: manages all the aspects of the node
*/
......
......@@ -29,7 +29,7 @@ void before() {
/*
* Register below your sensors
*/
nodeManager.registerSensor(SENSOR_MQ,A1);
/*
* Register above your sensors
......
......@@ -76,7 +76,7 @@ Those NodeManager's directives in the `config.h` file control which module/libra
// if enabled, a battery sensor will be created at BATTERY_CHILD_ID and will report vcc voltage together with the battery level percentage
#define BATTERY_SENSOR 1
// Enable this module to use one of the following sensors: SENSOR_ANALOG_INPUT, SENSOR_LDR, SENSOR_THERMISTOR
// Enable this module to use one of the following sensors: SENSOR_ANALOG_INPUT, SENSOR_LDR, SENSOR_THERMISTOR, SENSOR_MQ
#define MODULE_ANALOG_INPUT 1
// Enable this module to use one of the following sensors: SENSOR_DIGITAL_INPUT
#define MODULE_DIGITAL_INPUT 1
......@@ -196,6 +196,7 @@ SENSOR_HTU21D | HTU21D sensor, return temperature/humidity based on the attached
SENSOR_BH1750 | BH1750 sensor, return light level in lux
SENSOR_MLX90614 | MLX90614 contactless temperature sensor, return ambient and object temperature
SENSOR_BME280 | BME280 sensor, return temperature/humidity/pressure based on the attached BME280 sensor
SENSOR_MQ | MQ sensor, return ppm of the target gas
To register a sensor simply call the NodeManager instance with the sensory type and the pin the sensor is conncted to. For example:
~~~c
......@@ -213,6 +214,8 @@ If you want to create a custom sensor and register it with NodeManager so it can
~~~c
// define what to do during before() to setup the sensor
void onBefore();
// define what to do during setup() by executing the sensor's main task
void onSetup();
// define what to do during loop() by executing the sensor's main task
void onLoop();
// define what to do during receive() when the sensor receives a message
......@@ -258,7 +261,7 @@ The following methods are available for all the sensors:
void setForceUpdate(int value);
// the value type of this sensor (default: TYPE_INTEGER)
void setValueType(int value);
// for float values, set the float precision (default: 2)
// for float values, set the float precision (default: 2)
void setFloatPrecision(int value);
// optionally sleep interval in milliseconds before sending each message to the radio network (default: 0)
void setSleepBetweenSend(int value);
......@@ -306,6 +309,32 @@ Each sensor class can expose additional methods.
void setOffset(float value);
~~~
#### SensorMQ
~~~c
// define the target gas whose ppm has to be returned. 0: LPG, 1: CO, 2: Smoke (default: 1);
void setTargetGas(int value);
// define the load resistance on the board, in kilo ohms (default: 1);
void setRlValue(float value);
// define the Ro resistance on the board (default: 10000);
void setRoValue(float value);
// Sensor resistance in clean air (default: 9.83);
void setCleanAirFactor(float value);
// define how many samples you are going to take in the calibration phase (default: 50);
void setCalibrationSampleTimes(int value);
// define the time interal(in milisecond) between each samples in the cablibration phase (default: 500);
void setCalibrationSampleInterval(int value);
// define how many samples you are going to take in normal operation (default: 50);
void setReadSampleTimes(int value);
// define the time interal(in milisecond) between each samples in the normal operations (default: 5);
void setReadSampleInterval(int value);
// set the LPGCurve array (default: {2.3,0.21,-0.47})
void setLPGCurve(float *value);
// set the COCurve array (default: {2.3,0.72,-0.34})
void setCOCurve(float *value);
// set the SmokeCurve array (default: {2.3,0.53,-0.44})
void setSmokeCurve(float *value);
~~~
#### SensorDigitalOutput / SensorRelay / SensorLatchingRelay
~~~c
// set how to initialize the output (default: LOW)
......
......@@ -5,8 +5,8 @@
* Sketch configuration
*/