Skip to content

Instantly share code, notes, and snippets.

@ic
Last active July 12, 2017 04:23
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ic/6412a4354304665e05d5951912c2cbfb to your computer and use it in GitHub Desktop.
Save ic/6412a4354304665e05d5951912c2cbfb to your computer and use it in GitHub Desktop.
MG-811 Calibration for the auto-ctrl board on Arduino (UNO)
/*
* About the calibration model:
* This calibration is very simple, and so lacks accuracy for serious use. See this script as a getting-started solution.
* The calibration relies on 2 points, at 400ppm and 1000ppm. The 400ppm is the assumed CO2 concentration in "fresh air".
* You see that this number is really inaccurate (references ranges from 350 to 390ppm). The 1000ppm comes from TODO.
* 150s -> ~40000ppm
* 0s -> ~400ppm, approximate linear growth based on Vernier's
* slope = 264 ppm/s; V0 = 400
* Avg human 1 breath every 3-5s
* => 1 breath ~ +792 to +1320ppm => avg. 1056ppm, let's say 1000ppm
*
* See also:
* + [Calibration notes from CO2 Meter](https://www.co2meter.com/blogs/news/7512282-co2-sensor-calibration-what-you-need-to-know)
* + [Sandbox Electronics' MG-811 CO2 Sensor Module](http://sandboxelectronics.com/?p=147)
* + [Vernier's post on human respiration](http://www.vernier.com/innovate/human-respiration/)
*/
#define MG811_OUT_PIN (0)
#define MG811_DIGITAL_PIN (2)
//#define DC_GAIN (8.5) //define the DC gain of amplifier
#define VENT_PIN (8) // Fan to force an airflow on the sensor.
#define READ_SAMPLE_INTERVAL (50)
#define READ_SAMPLE_TIMES (5)
// Constants
#define LOG_400 (2.602)
#define LOG_1000 (3)
// Measure model.
#define LOG_400_VOLTAGE (0.30) // Voltage when the concentration of CO2 is 400ppm.
#define LOG_1000_VOLTAGE (0.25) // Voltage when the concentration of CO2 is 1000ppm.
float co2_log_slope = (LOG_1000_VOLTAGE - LOG_400_VOLTAGE) / (LOG_1000 - LOG_400);
float v400 = LOG_400_VOLTAGE - co2_log_slope * LOG_400;
void setup() {
Serial.begin(9600);
pinMode(MG811_DIGITAL_PIN, INPUT);
digitalWrite(MG811_DIGITAL_PIN, HIGH);
pinMode(VENT_PIN, OUTPUT);
digitalWrite(VENT_PIN, HIGH);
Serial.print("Settings:\n");
Serial.print("CO2 measurement slope: ");
Serial.print(co2_log_slope);
Serial.print(" V / log(ppm)\n");
Serial.print("Voltage at 400ppm: ");
Serial.print(v400);
Serial.print("V\n\n");
}
void loop() {
float volts = MG811ReportRaw();
MG822ReportPPM(volts);
MG822ReportDigital();
Serial.print("\n");
delay(3000);
}
float MG811ReportRaw() {
float volts;
volts = MG811Read(MG811_OUT_PIN);
Serial.print("Reading: ");
Serial.print(volts);
Serial.print("V\n");
return volts;
}
int MG822ReportPPM(float volts) {
int ppm;
ppm = MG811ToPPM(volts);
Serial.print("CO2: ");
if (ppm == -1) {
Serial.print( "< 400" );
} else {
Serial.print(ppm);
}
Serial.print("ppm\n");
return ppm;
}
void MG822ReportDigital() {
if (digitalRead(MG811_DIGITAL_PIN) ){
Serial.print("HIGH");
} else {
Serial.print("LOW");
}
Serial.print("\n");
}
float MG811Read(int pin) {
int i;
float v = 0;
for (i = 0; i < READ_SAMPLE_TIMES; i++) {
v += analogRead(pin);
delay(READ_SAMPLE_INTERVAL);
}
// Convert digital to analog value.
v = (v / READ_SAMPLE_TIMES) * 5 / 1024;
return v;
}
int MG811ToPPM(float volts) {
if (volts >= LOG_400_VOLTAGE) {
return -1;
} else {
return pow(10, (volts - v400) / co2_log_slope);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment