Hazardous Gas Monitor
Program It
First, set up a data stream on the data.sparkfun.com service. Next, write a program to read in the analog value of each gas sensor, convert it to PPM, and check it against known safe thresholds. Based on OSHA safety standards, the thresholds for the three gases are as follows:
- LPG: 1,000 PPM
- Methane: 1,000 PPM
- CO: 50 PPM
If you want to get up and running quickly, or are new to programming, feel free to grab the code from below, or you can get the most up to date files from the GitHub repository. Use it as-is or modify to suit your particular needs.
language:c // This #include statement was automatically added by the Particle IDE. //This library is used to push data to the data.sparkfun.com server. #include "SparkFunPhant/SparkFunPhant.h" #include "math.h" //This code was written by Jennifer Fox <jenfoxbot@gmail.com> /* * ---------------------------------------------------------------------------- * "THE Coffee-WARE LICENSE" (Revision 42): * <jenfoxbot@gmail.com> wrote this file. As long as you retain this notice you * can do whatever you want with this stuff. If we meet some day, and you think * this stuff is worth it, you can buy me a coffee in return. * ---------------------------------------------------------------------------- */ //Variables to push data to data.sparkfun.com host -- Change publicKey[] and privateKey[] const char server[] = "data.sparkfun.com"; // Phant destination server const char publicKey[] = "INSERT_PUBLIC_KEY_HERE"; // Phant public key const char privateKey[] = "INSERT_PRIVATE_KEY_HERE"; // Phant private key Phant phant(server, publicKey, privateKey); // Create a Phant object const unsigned long postingRate = 20000; //Post rate to data.sparkfun.com (time in milliseconds) unsigned long lastPost = millis(); //Keeps track of posting rate //Define analog pins on Photon to use for sensors const int LPG = A0; const int NG = A1; const int CO = A2; //Define digital pins on Photon to use for LEDs, buzzer, and MQ7 (CO sensor) heater const int LPGled = D0; const int NGled = D1; const int COled = D2; const int buzzer = D3; const int CORelayPin = D6; const int COVoltPin = D7; //Set up raw signal and PPM variables for each gas sensor int LPGRaw; int NGRaw; int CORaw; int LPGppm; int NGppm; int COppm; //Set safety threshold levels for each hazardous gas const int LPGthresh = 1000; const int NGthresh = 1000; const int COthresh = 50; //Set variables for CO sensor (MQ7) voltage cycle unsigned long startMillis; unsigned long switchTimeMillis; const int CO_5V_Interval = 60000; //60s for 5V interval const int CO_1_5V_Interval = 90000; //90s for 1.5V interval bool heaterInHighPhase; void setup() { Serial.begin(9600); //Initialize LED and buzzer output pins pinMode(LPGled, OUTPUT); pinMode(NGled, OUTPUT); pinMode(COled, OUTPUT); pinMode(buzzer, OUTPUT); //Initialize CO sensor heater pins pinMode(CORelayPin, OUTPUT); pinMode(COVoltPin, OUTPUT); //Set start time (for CO sensor heater voltage) startMillis = millis(); turnHeaterHigh(); } void loop() { //Cycle CO sensor (MQ7) heater voltage if(heaterInHighPhase){ // 5V phase - check to switch if(millis() > switchTimeMillis) { turnHeaterLow(); } } else { // 1.4V phase - check to switch if(millis() > switchTimeMillis) { turnHeaterHigh(); } } //Read in analog value from each gas sensor -- use function defined below to measure CO sensor at end of voltage cycle LPGRaw = analogRead(LPG); NGRaw = analogRead(NG); CORaw = measureCOSensor(); //Caclulate the PPM of each gas sensor using the funtions defined below LPGppm = LPG_ppm(LPGRaw); NGppm = NG_ppm(NGRaw); COppm = CO_ppm(CORaw); //Serial monitor print for debugging and checking data Serial.println(NGRaw); Serial.println(NGppm); delay(1000); //Check gas sensor measurements against safety thresholds checkThreshold(LPGppm, NGppm, COppm); //Wait to post until ~ 20s has lapsed if (lastPost + postingRate < millis()) { Serial.println("Reading!"); postToPhant(LPGppm, NGppm, COppm); //Post gas sensor readings and unit (PPM) to your data stream at data.sparkfun.com lastPost = millis(); } } //Functions to calculate PPM from Photon analog reading //Each equation is determined by visually picking points, plotting PPM v. V_RL, then fitting a trendline to the curve (exponential) //Calculate LPG PPM int LPG_ppm(double rawValue){ double ppm = 26.572*exp(1.2894*(rawValue*3.3/4095)); //Multiply raw analog value by 3.3/4095 to convert to a voltage return ppm; } //Calculate NG PPM int NG_ppm(double rawValue){ double ppm = 10.938*exp(1.7742*(rawValue*3.3/4095)); return ppm; } //Calculate CO PPM int CO_ppm(double rawValue){ double ppm = 3.027*exp(1.0698*(rawValue*3.3/4095)); return ppm; } //Function to check PPM reading with maximum safe PPM threshold //Include a margin of error (currently 10%) void checkThreshold(int lpgppm, int ngppm, int coppm){ int led1; int led2; int led3; if (lpgppm >= LPGthresh*0.9){ digitalWrite(LPGled, HIGH); led1 = TRUE; } else{ digitalWrite(LPGled, LOW); led1 = FALSE; } if (ngppm >= NGthresh*0.9){ digitalWrite(D1, HIGH); led2 = TRUE; } else{ digitalWrite(NGled, LOW); led2 = FALSE; } if (coppm >= COthresh*0.9){ digitalWrite(D2, HIGH); led3 = TRUE; } else{ digitalWrite(COled, LOW); led3 = FALSE; } if(led1 | led2 | led3){ digitalWrite(buzzer, HIGH); } else{digitalWrite(buzzer, LOW);} } //Functions to switch heater voltage on MQ7 (CO) sensor void turnHeaterHigh(){ // 5v phase digitalWrite(COVoltPin, LOW); digitalWrite(CORelayPin, HIGH); heaterInHighPhase = true; switchTimeMillis = millis() + CO_5V_Interval; } void turnHeaterLow(){ // 1.4v phase digitalWrite(COVoltPin, HIGH); digitalWrite(CORelayPin, LOW); heaterInHighPhase = false; switchTimeMillis = millis() + CO_1_5V_Interval; } //Function to read CO sensor voltage (just before switching to 1.5V) int measureCOSensor(){ unsigned int gasLevel = analogRead(CO); unsigned int time = (millis() - startMillis) / 1000; delay(time); return gasLevel; } //Function to post data to data.sparkfun.com host //Many thanks to Jim Lindblom <jim@sparkfun.com> for the sample code and Phant library. int postToPhant(int lpg, int ng, int co){ phant.add("lpg", lpg); //Data stream field name "sensorvalue1" phant.add("ng", ng); //Data stream field name "sensorvalue2" phant.add("co", co); //Data stream field name "sensorvalue3" TCPClient client; char response[512]; int i = 0; int retVal = 0; if (client.connect(server, 80)) // Connect to the server { // Post message to indicate connect success Serial.println("Posting!"); // phant.post() will return a string formatted as an HTTP POST. // It'll include all of the field/data values we added before. // Use client.print() to send that string to the server. client.print(phant.post()); delay(1000); // Now we'll do some simple checking to see what (if any) response // the server gives us. while (client.available()) { char c = client.read(); Serial.print(c); // Print the response for debugging help. if (i < 512) response[i++] = c; // Add character to response string } // Search the response string for "200 OK", if that's found the post // succeeded. if (strstr(response, "200 OK")) { Serial.println("Post success!"); retVal = 1; } else if (strstr(response, "400 Bad Request")) { // "400 Bad Request" means the Phant POST was formatted incorrectly. // This most commonly ocurrs because a field is either missing, // duplicated, or misspelled. Serial.println("Bad request"); retVal = -1; } else { // Otherwise we got a response we weren't looking for. retVal = -2; } } else { // If the connection failed, print a message: Serial.println("connection failed"); retVal = -3; } client.stop(); // Close the connection to server. return retVal; // Return error (or success) code. }
Change the following in the code:
Copy and paste your data stream public key to the array called
publicKey[]
.const char publicKey[] = "INSERT_PUBLIC_KEY_HERE";
2.Copy and paste your data stream private key to the array called privateKey[]
.
`const char privateKey[] = "INSERT_PRIVATE_KEY_HERE";`
To monitor the Photon output, use the Particle driver downloaded as described in the "Connecting Your Device" Photon tutorial. Once this is installed, in the command prompt, type particle serial monitor
. This is super helpful for debugging and checking that the Photon is posting data to the web.