Commit 1ac1b5f0 authored by user2684's avatar user2684
Browse files

Add support for ML8511 UV sensor #37

parent 17f53c08
...@@ -4,6 +4,30 @@ ...@@ -4,6 +4,30 @@
#include "NodeManager.h" #include "NodeManager.h"
/***************************************
Global functions
*/
// return vcc in V
float getVcc() {
// Measure Vcc against 1.1V Vref
#if defined(__AVR_ATmega32U4__) || defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
ADMUX = (_BV(REFS0) | _BV(MUX4) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1));
#elif defined (__AVR_ATtiny24__) || defined(__AVR_ATtiny44__) || defined(__AVR_ATtiny84__)
ADMUX = (_BV(MUX5) | _BV(MUX0));
#elif defined (__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__)
ADMUX = (_BV(MUX3) | _BV(MUX2));
#else
ADMUX = (_BV(REFS0) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1));
#endif
// Vref settle
delay(70);
// Do conversion
ADCSRA |= _BV(ADSC);
while (bit_is_set(ADCSRA, ADSC)) {};
// return Vcc in mV
return (float)((1125300UL) / ADC) / 1000;
}
/*************************************** /***************************************
PowerManager PowerManager
...@@ -58,6 +82,7 @@ void PowerManager::powerOff() { ...@@ -58,6 +82,7 @@ void PowerManager::powerOff() {
digitalWrite(_vcc_pin, LOW); digitalWrite(_vcc_pin, LOW);
} }
/****************************************** /******************************************
Sensors Sensors
*/ */
...@@ -429,6 +454,205 @@ void SensorThermistor::onReceive(const MyMessage & message) { ...@@ -429,6 +454,205 @@ void SensorThermistor::onReceive(const MyMessage & message) {
onLoop(); onLoop();
} }
/*
SensorML8511
*/
// contructor
SensorML8511::SensorML8511(int child_id, int pin): Sensor(child_id, pin) {
// set presentation, type and value type
setPresentation(S_UV);
setType(V_UV);
setValueType(TYPE_FLOAT);
}
// what do to during before
void SensorML8511::onBefore() {
// set the pin as input
pinMode(_pin, INPUT);
}
// what do to during setup
void SensorML8511::onSetup() {
onLoop();
}
// what do to during loop
void SensorML8511::onLoop() {
// read the voltage
int uvLevel = analogRead(_pin);
int refLevel = getVcc()*1024/3.3;
//Use the 3.3V power pin as a reference to get a very accurate output value from sensor
float outputVoltage = 3.3 / refLevel * uvLevel;
//Convert the voltage to a UV intensity level
float uvIntensity = _mapfloat(outputVoltage, 0.99, 2.8, 0.0, 15.0);
#if DEBUG == 1
Serial.print(F("UV I="));
Serial.print(_child_id);
Serial.print(F(" V="));
Serial.print(outputVoltage);
Serial.print(F(" I="));
Serial.println(uvIntensity);
#endif
// store the value
_value_float = uvIntensity;
}
// what do to as the main task when receiving a message
void SensorML8511::onReceive(const MyMessage & message) {
onLoop();
}
// The Arduino Map function but for floats
float SensorML8511::_mapfloat(float x, float in_min, float in_max, float out_min, float out_max) {
return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
}
/*
* 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();
}
// 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])));
}
/* /*
SensorDigitalInput SensorDigitalInput
*/ */
...@@ -1015,150 +1239,6 @@ void SensorBME280::onReceive(const MyMessage & message) { ...@@ -1015,150 +1239,6 @@ void SensorBME280::onReceive(const MyMessage & message) {
} }
#endif #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])));
}
/******************************************* /*******************************************
...@@ -1256,6 +1336,7 @@ int NodeManager::registerSensor(int sensor_type, int pin, int child_id) { ...@@ -1256,6 +1336,7 @@ int NodeManager::registerSensor(int sensor_type, int pin, int child_id) {
else if (sensor_type == SENSOR_LDR) return registerSensor(new SensorLDR(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_THERMISTOR) return registerSensor(new SensorThermistor(child_id, pin));
else if (sensor_type == SENSOR_MQ) return registerSensor(new SensorMQ(child_id, pin)); else if (sensor_type == SENSOR_MQ) return registerSensor(new SensorMQ(child_id, pin));
else if (sensor_type == SENSOR_ML8511) return registerSensor(new SensorML8511(child_id, pin));
#endif #endif
#if MODULE_DIGITAL_INPUT == 1 #if MODULE_DIGITAL_INPUT == 1
else if (sensor_type == SENSOR_DIGITAL_INPUT) return registerSensor(new SensorDigitalInput(child_id, pin)); else if (sensor_type == SENSOR_DIGITAL_INPUT) return registerSensor(new SensorDigitalInput(child_id, pin));
...@@ -1607,7 +1688,7 @@ void NodeManager::_process(const char * message) { ...@@ -1607,7 +1688,7 @@ void NodeManager::_process(const char * message) {
else if (strcmp(message, "BATTERY") == 0) { else if (strcmp(message, "BATTERY") == 0) {
// measure the board vcc // measure the board vcc
float volt = 0; float volt = 0;
if (_battery_internal_vcc || _battery_pin == -1) volt = _getVcc(); if (_battery_internal_vcc || _battery_pin == -1) volt = getVcc();
else volt = analogRead(_battery_pin) * _battery_volts_per_bit; else volt = analogRead(_battery_pin) * _battery_volts_per_bit;
// calculate the percentage // calculate the percentage
int percentage = ((volt - _battery_min) / (_battery_max - _battery_min)) * 100; int percentage = ((volt - _battery_min) / (_battery_max - _battery_min)) * 100;
...@@ -1834,29 +1915,6 @@ int NodeManager::_getAvailableChildId() { ...@@ -1834,29 +1915,6 @@ int NodeManager::_getAvailableChildId() {
} }
} }
#if BATTERY_MANAGER == 1
// return vcc in V
float NodeManager::_getVcc() {
// Measure Vcc against 1.1V Vref
#if defined(__AVR_ATmega32U4__) || defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
ADMUX = (_BV(REFS0) | _BV(MUX4) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1));
#elif defined (__AVR_ATtiny24__) || defined(__AVR_ATtiny44__) || defined(__AVR_ATtiny84__)
ADMUX = (_BV(MUX5) | _BV(MUX0));
#elif defined (__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__)
ADMUX = (_BV(MUX3) | _BV(MUX2));
#else
ADMUX = (_BV(REFS0) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1));
#endif
// Vref settle
delay(70);
// Do conversion
ADCSRA |= _BV(ADSC);
while (bit_is_set(ADCSRA, ADSC)) {};
// return Vcc in mV
return (float)((1125300UL) / ADC) / 1000;
}
#endif
// guess the initial value of a digital output based on the configured interrupt mode // guess the initial value of a digital output based on the configured interrupt mode
int NodeManager::_getInterruptInitialValue(int mode) { int NodeManager::_getInterruptInitialValue(int mode) {
if (mode == RISING) return LOW; if (mode == RISING) return LOW;
...@@ -1864,3 +1922,4 @@ int NodeManager::_getInterruptInitialValue(int mode) { ...@@ -1864,3 +1922,4 @@ int NodeManager::_getInterruptInitialValue(int mode) {
return -1; return -1;
} }
...@@ -149,6 +149,8 @@ ...@@ -149,6 +149,8 @@
#define SENSOR_THERMISTOR 3 #define SENSOR_THERMISTOR 3
// MQ2 air quality sensor // MQ2 air quality sensor
#define SENSOR_MQ 19 #define SENSOR_MQ 19
// ML8511 UV sensor
#define SENSOR_ML8511 20
#endif #endif
#if MODULE_DIGITAL_INPUT == 1 #if MODULE_DIGITAL_INPUT == 1
// Generic digital sensor, return a pin's digital value // Generic digital sensor, return a pin's digital value
...@@ -196,7 +198,7 @@ ...@@ -196,7 +198,7 @@
// MLX90614 sensor, contactless temperature sensor // MLX90614 sensor, contactless temperature sensor
#define SENSOR_BME280 18 #define SENSOR_BME280 18
#endif #endif
// last Id: 19 // last Id: 20
/*********************************** /***********************************
Libraries Libraries
*/ */
...@@ -248,6 +250,7 @@ class PowerManager { ...@@ -248,6 +250,7 @@ class PowerManager {
void setPowerPins(int ground_pin, int vcc_pin, long wait = 50); void setPowerPins(int ground_pin, int vcc_pin, long wait = 50);
void powerOn(); void powerOn();
void powerOff(); void powerOff();
float getVcc();
private: private:
int _vcc_pin = -1; int _vcc_pin = -1;
int _ground_pin = -1; int _ground_pin = -1;
...@@ -407,6 +410,78 @@ class SensorThermistor: public Sensor { ...@@ -407,6 +410,78 @@ class SensorThermistor: public Sensor {
float _offset = 0; float _offset = 0;
}; };
/*
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;
};
/*
SensorML8511
*/
class SensorML8511: public Sensor {
public:
SensorML8511(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);
protected:
float _mapfloat(float x, float in_min, float in_max, float out_min, float out_max);
};
/* /*
SensorDigitalInput: read the digital input of the configured pin SensorDigitalInput: read the digital input of the configured pin
*/ */
...@@ -622,60 +697,7 @@ class SensorBME280: public Sensor { ...@@ -622,60 +697,7 @@ class SensorBME280: public Sensor {
}; };
#endif #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 *