inital push
This commit is contained in:
commit
fa6470723e
331
MQ7-ESP.ino
Normal file
331
MQ7-ESP.ino
Normal file
@ -0,0 +1,331 @@
|
||||
/* sensor MQ7. alternate heater 5V/1.4V 60sec/90sec
|
||||
// read sensor @ 88sec(of 90-period)
|
||||
// Kn.Ny '16
|
||||
// This program to be used as CO-alarm in tents wintertime during night. (gasoline or firewood burners)
|
||||
// http://www.savvysolutions.info/savvymicrocontrollersolutions/arduino.php?topic=arduino-mq7-CO-gas-sensor
|
||||
// http://www.savvysolutions.info/savvymicrocontrollersolutions/index.php?sensor=mq-7-gas-sensors
|
||||
// ****************************************************************************************************/
|
||||
|
||||
#define serial
|
||||
#define warnlevel 50 //CO ppm warnlevel
|
||||
#define continousMeasurement
|
||||
#define HeatOnPin D0
|
||||
#define Buzzer D5
|
||||
#define ADCOffset 3
|
||||
#define lotime 90000UL // 90 sek low heating
|
||||
#define hitime 60000UL // 60 sek high heating
|
||||
#define read_time 148000UL // read 2 sek ahead of end low period
|
||||
#define thresholdMeasurement 10 //ToDo
|
||||
|
||||
|
||||
/*
|
||||
struct for rtc memory
|
||||
*/
|
||||
struct {
|
||||
bool burnIn = false; //set to false in productive
|
||||
unsigned int minADC = 65535; //save minimal adc value
|
||||
byte ringPointer = 0;
|
||||
unsigned int adcRingBuf[thresholdMeasurement] = { 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 }; //ring buffer for ten values
|
||||
} rtcData;
|
||||
|
||||
|
||||
// Adjust vRef to be the true supply voltage in mV.
|
||||
const float vRef = 4470.0; //mVolt
|
||||
const float RL = 10.0; //kOhm
|
||||
const float CleanAirRatio = 26.09;
|
||||
float Rs = 0.0;
|
||||
float R0 = 0.0;
|
||||
float ambTemp = 20.0;
|
||||
float ambHum = 65.0;
|
||||
unsigned int measurement = 0;
|
||||
|
||||
|
||||
uint32_t calculateCRC32( const uint8_t *data, size_t length ) {
|
||||
uint32_t crc = 0xffffffff;
|
||||
while ( length-- ) {
|
||||
uint8_t c = *data++;
|
||||
for ( uint32_t i = 0x80; i > 0; i >>= 1 ) {
|
||||
bool bit = crc & 0x80000000;
|
||||
if ( c & i ) {
|
||||
bit = !bit;
|
||||
}
|
||||
|
||||
crc <<= 1;
|
||||
if ( bit ) {
|
||||
crc ^= 0x04c11db7;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return crc;
|
||||
}
|
||||
|
||||
void updateRTC() {
|
||||
//ToDo save rtcData
|
||||
//crc32 stuff
|
||||
}
|
||||
|
||||
void getRTC() {
|
||||
//crc32 stuff
|
||||
//ToDo get rtcData
|
||||
}
|
||||
|
||||
void saveMeasurement (unsigned int measurement) {
|
||||
rtcData.adcRingBuf[rtcData.ringPointer] = measurement;
|
||||
rtcData.ringPointer = (rtcData.ringPointer + 1) % thresholdMeasurement;
|
||||
}
|
||||
|
||||
float CalcRsFromVRL(float VRL) {
|
||||
// VRL = RL voltage in mV.
|
||||
// vRef = supply voltage, 5000 mV
|
||||
// RL = load resistor in k ohms
|
||||
// The equation Rs = (Vc - Vo)*(RL/Vo)
|
||||
// is derived from the voltage divider
|
||||
// principle: Vo = RL * Vc (Rs + RL)
|
||||
//
|
||||
// Note. Alternatively you could calc
|
||||
// Rs from ADC value using
|
||||
// Rs = RL * (1024 - ADC) / ADC
|
||||
if ( VRL == 0.0 )
|
||||
{
|
||||
return vRef * VRL;
|
||||
}
|
||||
return ((vRef - VRL) * (RL / VRL));
|
||||
}
|
||||
|
||||
float CalcRsFromADC(float ADC) {
|
||||
if ( ADC == 0 )
|
||||
{
|
||||
return 10240;
|
||||
}
|
||||
return (RL * (1024 - ADC) / ADC);
|
||||
}
|
||||
|
||||
unsigned int GetCOPpmForRatioRsRo(float RsRo_ratio) {
|
||||
/*
|
||||
x=Rs/R0; y=ppm
|
||||
1,6122158133673148; 50,488950942143966
|
||||
0,999928961393748; 100
|
||||
0,39070475612941435; 396,4363929567069
|
||||
0,22807477512659288; 1000
|
||||
0,09483831585572762; 3964,363929567065
|
||||
y = 100.607 * (Rs/R0)^-1.54901
|
||||
this regression needs to be shifted by the CleanAirRatio.
|
||||
R0 = Rs/CleanAirRatio => Rs/R0 => Rs/(Rs_old/CleanAirRatio)
|
||||
*/
|
||||
float ppm = 0;
|
||||
ppm = 100.607 * pow( (RsRo_ratio * CleanAirRatio), -1.54901);
|
||||
return (unsigned int) ppm;
|
||||
}
|
||||
|
||||
int measurementAnalog() {
|
||||
//measure x values and get the mean value
|
||||
static unsigned int x = 10;
|
||||
unsigned int valueADC = 0;
|
||||
for (unsigned int i = 0; i < x; i++) {
|
||||
valueADC += analogRead(A0);
|
||||
delay(10);
|
||||
}
|
||||
Serial.println("DEBUG: measurement ADC " + String(valueADC / x));
|
||||
return (valueADC / x);
|
||||
}
|
||||
|
||||
|
||||
void burnIn(bool trigger) {
|
||||
/*
|
||||
if not flag in eeprom set
|
||||
then burn in sensor
|
||||
*/
|
||||
unsigned int measureRow[thresholdMeasurement] = { 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 };
|
||||
int counter = 0;
|
||||
//set timer with the current milli counter
|
||||
unsigned long starttime = millis();
|
||||
unsigned long currenttime = millis();
|
||||
unsigned long static burnInTime = 172800000; //48 hours * 60 minutes * 60 seconds * 1000 milli
|
||||
Serial.println("DEBUG: burnIn");
|
||||
if ( !rtcData.burnIn ) {
|
||||
digitalWrite(HeatOnPin, HIGH); // => 5V
|
||||
while ( currenttime < burnInTime ) {
|
||||
delay(1000);
|
||||
currenttime = abs(millis() - starttime);
|
||||
Serial.print("\r" + String((int)((currenttime / 1000) / 360)) + ":" + String((int)((currenttime / 1000) / 60)) + ":" + String((int)((currenttime / 1000) % 60)) + "..");
|
||||
if (trigger) {
|
||||
unsigned int tmp = CalcRsFromADC(measurementAnalog());
|
||||
measureRow[counter] = tmp;
|
||||
counter = ( counter + 1 ) % thresholdMeasurement;
|
||||
Serial.println("BurnIn: Current RS from ADC " + String(tmp));
|
||||
//ToDo a value which means that the sensor is clean enough.
|
||||
//However the resistance drop in first sigth and stabilize then at a specific value, mine approx. 37k Ohm.
|
||||
/*if (tmp >= 36.0) {
|
||||
Serial.println("BurnIn: Trigger");
|
||||
break;
|
||||
}*/
|
||||
int sum = 0;
|
||||
for (int i = 0; i < thresholdMeasurement - 1; i++) {
|
||||
sum = sum + abs(measureRow[i] - measureRow[i + 1]);
|
||||
}
|
||||
Serial.println("DEBUG: Currentsum value " + String(sum));
|
||||
if (sum <= 0) {
|
||||
Serial.println("BurnIn: Stabilization");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
//set initial lowest value
|
||||
rtcData.minADC = measurementAnalog();
|
||||
rtcData.burnIn = true;
|
||||
Serial.println("DEBUG BurnIn:\t" + String(currenttime / 1000) + " seconds| rtc.minADC" + String(rtcData.minADC) + " || RS " + String(CalcRsFromADC(rtcData.minADC)));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
a method to react to changing environmental parameters.
|
||||
ToDo: The values are NOT usuable!
|
||||
|
||||
void compensateDriftOld() {
|
||||
if (Rs <= 20.0 && Rs > R0) {
|
||||
R0 = Rs;
|
||||
Serial.println("DEBUG: Drift compensate done. New R0:\t" + String(R0));
|
||||
return;
|
||||
}
|
||||
if ( Rs > 20.0) {
|
||||
Serial.println("DEBUG: Sensor needs cleaning!");
|
||||
burnIn(false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
/*
|
||||
a method to react to changing environmental parameters.
|
||||
take the ADC values and calculate the summed up difference.
|
||||
renew the R0 if stabilizied ADc values in a row and the old R0 value needs to be smaller
|
||||
*/
|
||||
void compensateDrift() {
|
||||
int sum = 0;
|
||||
|
||||
for (int i = 0; i < thresholdMeasurement - 1 ; i++) {
|
||||
sum = sum + (abs(rtcData.adcRingBuf[i] - rtcData.adcRingBuf[i + 1]));
|
||||
}
|
||||
|
||||
if (R0 > Rs && sum <= 1) {
|
||||
R0 = Rs;
|
||||
Serial.println("DEBUG: Drift compensate done. New R0:\t" + String(R0));
|
||||
return;
|
||||
}
|
||||
//check for lowest resistence after heating
|
||||
if ( rtcData.adcRingBuf[rtcData.ringPointer] > rtcData.minADC) {
|
||||
Serial.println("DEBUG: Sensor needs cleaning!");
|
||||
burnIn(false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
int measurementCycle() {
|
||||
//set timer with the current milli counter
|
||||
unsigned long starttime = millis();
|
||||
unsigned long currenttime = 0;
|
||||
static unsigned long next_time = hitime + lotime; //set complete runtime for one measurement
|
||||
unsigned int currentMeasurement, lastMeasurement = 0;
|
||||
boolean done_reading = true; //only a simple toggle switch
|
||||
|
||||
//start cleaning sensitive part of sensor
|
||||
Serial.println("-------------------------------------------------------------");
|
||||
digitalWrite(HeatOnPin, HIGH); // => 5V
|
||||
Serial.print("5V:\t on");
|
||||
|
||||
while (currenttime < next_time) {
|
||||
//refresh current time
|
||||
currenttime = abs(millis() - starttime);
|
||||
#ifndef continousMeasurement
|
||||
lastMeasurement = currentMeasurement;
|
||||
currentMeasurement = measurementAnalog();
|
||||
if (thresholdMeasurement > abs(lastMeasurement - currentMeasurement) ) {
|
||||
delay(10);
|
||||
}
|
||||
|
||||
if (done_reading && currenttime >= hitime) //sensor reach heating time limit
|
||||
{
|
||||
digitalWrite(HeatOnPin, LOW); // => 1.4V
|
||||
Serial.print("\n\r1.4V:\t on\n");
|
||||
done_reading = false;
|
||||
}
|
||||
if (currenttime >= next_time ) {
|
||||
break;
|
||||
}
|
||||
#else
|
||||
if (done_reading && currenttime >= hitime) //sensor reach heating time limit
|
||||
{
|
||||
digitalWrite(HeatOnPin, LOW); // => 1.4V
|
||||
Serial.print("\n\r1.4V:\t on\n");
|
||||
done_reading = false;
|
||||
}
|
||||
if (currenttime > read_time) // then do a reading now
|
||||
{
|
||||
currentMeasurement = measurementAnalog();
|
||||
done_reading = true;
|
||||
break;
|
||||
//add nynquist thereom and sums up multiple measurements
|
||||
}
|
||||
delay(50);
|
||||
#endif
|
||||
}
|
||||
return currentMeasurement;
|
||||
}
|
||||
|
||||
void calibrateCleanAir() {
|
||||
Serial.println("DEBUG: calibrate Clean AIR");
|
||||
//wait until sensor reach low end resistance
|
||||
unsigned int i = 0;
|
||||
while ( true ) {
|
||||
i++;
|
||||
Rs = CalcRsFromADC(measurementCycle());
|
||||
Serial.println("Calibrate Clean Air:\t#" + String(i) + "\tRs: \t" + String(Rs));
|
||||
/*
|
||||
At least three measurements to settle down the sensor
|
||||
ToDo : the datasheet mentions that the Rs is between 2k and 20k for 100ppm in air. Therefore the values multiplies with the clean_air_ratio of 26.09
|
||||
That leads to a range: 5,2MOhm up to 52,2MOhm which is not in any reach of the real world (17.06.18: current value is ADC 2 up to 3, which is 3403 or 5110 Ohm.
|
||||
the resistance get lower with higher CO measurements.
|
||||
I guess the maximum value of 20k for 100ppm in air (a cold sensor has Rs = 330k)
|
||||
*/
|
||||
//if (i >= 3 && Rs <= 20.0) {
|
||||
if (i >= 3 && Rs >= 3000.0) {
|
||||
//Serial.println ("Rs <= 20.0");
|
||||
Serial.println ("Rs >= 3000.0");
|
||||
break;
|
||||
}
|
||||
}
|
||||
R0 = Rs;
|
||||
Serial.println("R0: \t" + String(R0));
|
||||
}
|
||||
|
||||
void ambientMeasurement() {
|
||||
|
||||
}
|
||||
|
||||
float correlateAmbient() {
|
||||
return 1.0;
|
||||
}
|
||||
|
||||
|
||||
// the setup routine runs once when you press reset:
|
||||
void setup() {
|
||||
// initialize serial communication at 115200 bits per second:
|
||||
Serial.begin(115200);
|
||||
pinMode(HeatOnPin, OUTPUT);
|
||||
burnIn(true);
|
||||
calibrateCleanAir();
|
||||
}
|
||||
|
||||
|
||||
void loop() {
|
||||
float ppm = 0;
|
||||
//save adc data
|
||||
saveMeasurement(measurementCycle());
|
||||
Rs = CalcRsFromADC(rtcData.adcRingBuf[rtcData.ringPointer]);
|
||||
Serial.println("Rs: \t" + String(Rs) + " | R0:\t" + String(R0) + " || Ratio:\t" + String(Rs / R0) + "| Ratio:\t" + String(Rs / R0 * CleanAirRatio) );
|
||||
ppm = GetCOPpmForRatioRsRo(Rs / R0);
|
||||
Serial.println("CO ppm: \t" + String(ppm));
|
||||
compensateDrift();
|
||||
}
|
63
README:md
Normal file
63
README:md
Normal file
@ -0,0 +1,63 @@
|
||||
CO Sensor
|
||||
-10.12.17: MQ-7 Sensor, Stecksockel + Platine bestellt
|
||||
|
||||
Aufbau mit wemos D1 mini und Dual Board
|
||||
-ggf Temperatur/Luftfeuchte Korrektur
|
||||
Brauche Datasheet sowie Berechnung der Kurven
|
||||
Low Heat bei bestimmten Umgebungswerten wegen Zerstörungsgefahr durch Wasser
|
||||
SHT30 Shield (Pin D1 und D2), genauer und einfach anzusteuern
|
||||
-Entscheidung über OLED oder RGB LED
|
||||
OLED Display wird ebenfalls über I2C angesteuert (Pin D1 und D2)
|
||||
-Lokaler Alarm
|
||||
Buzzer Board (Pin D5)
|
||||
-globaler Alarm
|
||||
mqtt/ESP Now
|
||||
|
||||
-Spannungsversorgung über NPN --geht da 0,7V Drop --> N-MOSFET https://arduinodiy.wordpress.com/2012/05/02/using-mosfets-with-ttl-levels/
|
||||
IRLML6344 bestellt
|
||||
-Kalibrierung?
|
||||
Zwei Möglichkeiten, entweder Clean Air Kalibrierung oder 100ppm Wert + Messgerät
|
||||
ESP8266 Speichern der Kalibrierung im RTC
|
||||
-Austausch des Sensors?
|
||||
Platine enthält Pinlöcher für den Sensor sowie den Sockel
|
||||
-Spannungsteiler für Ausgangsspannung MQ7
|
||||
Zusätzlicher Ausgang plus Spannungsteiler für 1V (ESP8266)
|
||||
560k/150k -> 1.056V --> Kondensator benötigt? http://esp8266-projects.org/2016/08/esp8266-internal-adc-2-the-easy-way-example/
|
||||
3k/820 -> 1.073V --> ggf. zu gering
|
||||
Spannungsteiler für Wemos Board nicht notwendig -> 150kOhm in Reihe zum eingebauten (110k/220k) Spannungsteiler
|
||||
ggf. Kondensator für den Spannungsteiler
|
||||
-Regressionsgerade bestimmen
|
||||
http://davidegironi.blogspot.de/2017/07/mq-gas-sensor-correlation-function.html
|
||||
http://davidegironi.blogspot.de/2017/05/mq-gas-sensor-correlation-function.html
|
||||
Achsen sind vertauscht sinnvoller
|
||||
beide Skalen logarithmisch
|
||||
mittels: https://apps.automeris.io/wpd/ die punkte bestimmen
|
||||
power-regresseion berechnen: http://keisan.casio.com/exec/system/14059931777261
|
||||
Min/Max bestimmen
|
||||
|
||||
Platine muss angepasst werden
|
||||
https://forum.arduino.cc/index.php?topic=294085.0
|
||||
http://www.instructables.com/id/Arduino-CO-Monitor-Using-MQ-7-Sensor/
|
||||
http://www.savvysolutions.info/savvymicrocontrollersolutions/index.php?sensor=mq-7-gas-sensors
|
||||
http://www.savvysolutions.info/savvymicrocontrollersolutions/arduino.php?topic=arduino-mq7-CO-gas-sensor
|
||||
|
||||
|
||||
Ablauf:
|
||||
Init System
|
||||
BurnIn Werte prüfen
|
||||
Falls nicht vorhanden, BurnIn beginnen
|
||||
Umgebungswerte messen
|
||||
Prüfen ob Betrieb von Sensor sicher ist ( insbesondere Wasser)
|
||||
Preheat nutzen
|
||||
Clean Air Kalibrierung durchführen
|
||||
BurnIn Werte korrelieren
|
||||
Nyquist-Theorem einhalten
|
||||
Vergleich mit vorherigen Werten im EEPROM auf Abweichung
|
||||
Warnung bei zu großen Abweichungen, Hinweis auf Alterung
|
||||
|
||||
Übergang zum Regelbetrieb
|
||||
Messwertreihe vorhalten
|
||||
Zu schnelle Wertänderungen lösen Warnung aus
|
||||
Zu hoher Wert löst Warnung aus
|
||||
Meldung an übergeordnete Instanz bei Warnung
|
||||
|
Loading…
Reference in New Issue
Block a user