Created
May 21, 2019 07:26
-
-
Save Electronza/d6083fb5c6741891874245c96e5c8896 to your computer and use it in GitHub Desktop.
H2S sensor code
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// **************************************************************************** | |
// * H2S GAS SENSOR WITH DATA UPLOADS TO Ubidots AND openSenseMap * | |
// * LOW POWER MODE, CAN RUN ON BATTERY * | |
// * Uses ESP8266 thing and DGS-H2S sensor from SPEC sensors * | |
// * https://www.sparkfun.com/products/13231 * | |
// * https://www.spec-sensors.com/product/digital-gas-sensor-h2s/ * | |
// * Wiring instructions * | |
// * https://electronza.com/ * | |
// * CHANGELOG * | |
// * v7 - first stable version * | |
// * v8 - delayed start of radio modem * | |
// * - logging of slightly negative values (influence of other gases) * | |
// **************************************************************************** | |
#include "UbidotsMicroESP8266.h" | |
#include <SoftwareSerial.h> | |
// #include "time.h" | |
// **************************************************************************** | |
// * SETTINGS FOR ESP8266 * | |
// **************************************************************************** | |
// WiFi credentials | |
// (hardwired in this code version) | |
#define SSID "ssid" | |
#define PASS "pass" | |
// Low voltage detection | |
// Note that we read Vin, and not the battery voltage, | |
// as the battery voltage is not accessible to be measured | |
// ESP8266 thing uses AP2112K-3.3V | |
// https://www.diodes.com/assets/Datasheets/AP2112.pdf | |
// Low DropoutVoltage (3.3V): 250mV (Typ.) @IOUT=600mA | |
// DGS-H2S supply voltage is 2.6V - 3.6V | |
// ESP8266 supply voltage is 2.5V - 3.6V | |
ADC_MODE(ADC_VCC); | |
int Batt; | |
// Deep sleep | |
// time to sleep (in seconds): | |
// 300 sec = 5 minutes | |
const int sleepTimeS = 300; | |
// Pin to trigger zeroing of | |
#define pin_zero 4 | |
// Debugging | |
// Debug via LED | |
#define debugled true | |
// Debug via serial (on ESP8266 thing DEV) | |
#define debugserial false | |
void preinit() { | |
// Global WiFi constructors are not called yet | |
// (global class instances like WiFi, Serial... are not yet initialized).. | |
// No global object methods or C++ exceptions can be called in here! | |
//The below is a static class method, which is similar to a function, so it's ok. | |
ESP8266WiFiClass::preinitWiFiOff(); | |
} | |
// **************************************************************************** | |
// * SETTINGS FOR SPEC DGS-H2S GAS SENSOR * | |
// **************************************************************************** | |
// The following variables store the output of the gas sensor | |
// Sensor values | |
// The format of the output is: SN[XXXXXXXXXXXX], PPB [0 : 999999], TEMP [-99:99], | |
// RH[0:99], RawSensor[ADCCount], TempDigital, RHDigital, Day[0:99], Hour [0:23] | |
// Note that on ESP8266 integer variable (int) stores a 32-bit (4-byte) value. | |
// This yields a range of -2,147,483,648 to 2,147,483,647 | |
// (minimum value of -2^31 and a maximum value of (2^31) - 1). | |
// On 8 bit boards some readings have to be recorded as floats | |
String SensorSerialNo; | |
float data_H2S; | |
int H2S; | |
int Temperature; | |
int RH; | |
int RawSensor; | |
int TempDigital; | |
int RHDigital; | |
int Days; | |
int Hours; | |
int Minutes; | |
int Seconds; | |
String dataString = ""; | |
String responseString = ""; | |
boolean dataStringComplete = 0; | |
char inChar; | |
bool success; | |
// We use software serial, RX = digital pin 12, TX = digital pin 13 | |
SoftwareSerial portOne(12, 13); | |
// **************************************************************************** | |
// * SETTINGS FOR UBIDOTS * | |
// **************************************************************************** | |
#define upload_to_ubidots true | |
// Replace it with your Ubidots' credentials | |
#define TOKEN "A1E-N0zMKdFgOCqjzhmQ07YORGvGcxVkqT" | |
#define ID1 "5c175c49c03f973d9d808a33" | |
#define ID2 "5c175c51c03f973d9d808a34" | |
#define ID3 "5c175c59c03f973d9d808a35" | |
#define ID4 "5c175c5bc03f973d7cf82eb3" | |
#define ID5 "5cab0003c03f971324c454b3" // Battery | |
Ubidots client(TOKEN); | |
// **************************************************************************** | |
// * SETTINGS FOR OPENSENSEMAP * | |
// **************************************************************************** | |
#define upload_to_osm true | |
// Replace it with your openSenseMap credentials | |
char osmserver[] = "ingress.opensensemap.org"; | |
// senseBox ID | |
#define SENSEBOX_ID "5cacd92e3680f2001b815aef" | |
// Sensor IDs | |
// H2S | |
#define SENSOR1_ID "5cacd92e3680f2001b815af3" | |
// H2S RAW | |
#define SENSOR2_ID "5cacd92e3680f2001b815af2" | |
// Temperature | |
#define SENSOR3_ID "5cacd92e3680f2001b815af1" | |
// Humidity | |
#define SENSOR4_ID "5cacd92e3680f2001b815af0" | |
WiFiClient osmclient; | |
#define command_delay 500 | |
#define start_delay 2500 | |
void setup() { | |
// EVERYTHING HAPPENS HERE | |
// 1. Check battery status | |
// 2. Initialize serial ports and LED | |
// 3. Wake up sensor | |
// 4. Set Zero (option) | |
// 5. We perform one measurement | |
// 6. We put the sensor back to sleep | |
// 7. Wake on WiFi | |
// 8. We send data to online services | |
// 9. The whole system goes to sleep | |
// ******************************************************************************** | |
// 1. Check battery status | |
// ******************************************************************************** | |
// Reads Vin (not battery voltage!!!) | |
// But Vin = battery voltage if battery_voltage < 3.3V | |
Batt = ESP.getVcc(); | |
// If the battery is discharged don't go any further!!! | |
if(Batt < 3100){ | |
// Deep sleep for as long as you can | |
ESP.deepSleep(ESP.deepSleepMax()); | |
} | |
// ******************************************************************************** | |
// 2. Initializations | |
// ******************************************************************************** | |
// Initialize LED | |
if(debugled == 1){ | |
// initialize digital pin LED_BUILTIN as an output. | |
pinMode(LED_BUILTIN, OUTPUT); | |
digitalWrite(LED_BUILTIN, HIGH); | |
delay(500); | |
digitalWrite(LED_BUILTIN, LOW); | |
} | |
// Initialize serial port for debugging | |
if(debugserial){ | |
// initialize serial port | |
Serial.begin(9600); | |
Serial.println("Program started"); | |
} | |
// Initialize SoftwareSerial port for sensor | |
portOne.begin(9600); | |
// Normally, data is returned within one second | |
// portOne.setTimeout(1500); | |
// reserve 80 bytes for the dataString | |
dataString.reserve(80); | |
responseString.reserve(150); | |
// ******************************************************************************** | |
// 3. Sensor wake up | |
// ******************************************************************************** | |
if(debugserial){ | |
Serial.println("Sensor wake up"); | |
} | |
portOne.print('\r'); | |
// Just wait for 15 seconds | |
// (the sensor normally wakes up in less than 10) | |
delay(15000); | |
// ******************************************************************************** | |
// 4. Set Zero | |
// ******************************************************************************** | |
// Pin to trigger zeroing of sensor | |
pinMode(4, INPUT_PULLUP); | |
if (digitalRead(4)==0){ | |
if(debugserial){ | |
Serial.println("Setting zero"); | |
} | |
success = SPEC_set_zero(); | |
if (success ==1){ | |
digitalWrite(LED_BUILTIN, HIGH); | |
// keep LED on | |
while(1){ | |
yield(); | |
} | |
} | |
else | |
{ | |
while(1){ | |
digitalWrite(LED_BUILTIN, HIGH); | |
delay(100); | |
digitalWrite(LED_BUILTIN, LOW); | |
delay(100); | |
yield(); | |
} | |
} | |
} | |
// ******************************************************************************** | |
// 5. Measurement | |
// ******************************************************************************** | |
if(debugled){ | |
// initialize digital pin LED_BUILTIN as an output. | |
digitalWrite(LED_BUILTIN, HIGH); | |
} | |
//SPEC_Data_Read(); | |
SPEC_Data_Read(); | |
SPEC_Parse_Data(); | |
// convert ppb to ppm | |
data_H2S = (float)H2S / 1000; | |
// ******************************************************************************** | |
// 6. Sensor sleep | |
// ******************************************************************************** | |
if(debugled){ | |
// Turn off LED | |
delay(250); | |
digitalWrite(LED_BUILTIN, LOW); | |
} | |
portOne.print('s'); | |
if(debugserial){ | |
Serial.println("Sensor goes to sleep"); | |
} | |
// ******************************************************************************** | |
// 7. Wake WiFi | |
// ******************************************************************************** | |
Serial.println("waking WiFi up, sleeping 2s"); | |
WiFi.forceSleepWake(); | |
delay(2000); | |
// ******************************************************************************** | |
// 8. Sending data to online services | |
// ******************************************************************************** | |
if(debugserial){ | |
Serial.println("Sending data..."); | |
} | |
// Connecting to WiFi + UBIDOTS | |
// Doesn't work so good | |
// client.setDebug(false); | |
client.wifiConnection(SSID,PASS); | |
if (WiFi.status() == WL_CONNECTED) { | |
if (debugled) { | |
// Flash LED 5 times | |
debugflash (5, LED_BUILTIN); | |
} | |
if (debugserial) { | |
Serial.println("Connected to WiFi"); | |
} | |
} | |
// We are now connected | |
// OK to send data | |
if (debugserial) { | |
Serial.println("OK to send."); | |
} | |
Update_Ubidots(); | |
// Sometimes the sensors returns negative values | |
// This is wrong, and it should not be visible to the general public | |
// As such, we don't send the wrong data to openSenseMap | |
if (H2S > -30){ | |
Update_openSenseMap(); | |
} | |
// ******************************************************************************** | |
// 9. Node sleep | |
// ******************************************************************************** | |
if(debugserial){ | |
Serial.println("Node goes to sleep"); | |
} | |
// Deep sleep 5 min | |
WiFi.disconnect(); | |
ESP.deepSleep(sleepTimeS * 1000000); | |
} | |
void loop() { | |
// HERE GOES NOTHING | |
} | |
// ******************************************************************************** | |
// * UPLOADS DATA TO OPENSENSEMAP * | |
// ******************************************************************************** | |
void Update_openSenseMap (void){ | |
if (upload_to_osm){ | |
if (debugserial){ | |
Serial.println("Uploading data to openSenseMap"); | |
} | |
postFloatValue(data_H2S, 3, SENSOR1_ID); | |
postFloatValue(RawSensor, 0, SENSOR2_ID); | |
postFloatValue(Temperature, 0, SENSOR3_ID); | |
postFloatValue(RH, 0, SENSOR4_ID); | |
if (debugled){ | |
//Turns ON LED for a second | |
digitalWrite(LED_BUILTIN, HIGH); // turn the LED on (HIGH is the voltage level) | |
delay(1000); | |
digitalWrite(LED_BUILTIN, LOW); // turn the LED on (HIGH is the voltage level) | |
delay(1000); | |
} | |
} | |
} | |
// ******************************************************************************** | |
// * OPENSENSEMAP UPLOAD FUNCTION * | |
// ******************************************************************************** | |
void postFloatValue (float measurement, int digits, String sensorId) { | |
// Convert Float to String | |
char obs[10]; | |
dtostrf(measurement, 5, digits, obs); | |
// Create Json | |
String jsonValue = "{\"value\":"; | |
jsonValue += obs; | |
jsonValue += "}"; | |
// Connect to OSeM Server and perform POST operation | |
if (debugserial){ | |
Serial.println("-------------------------------------"); | |
Serial.print("Connecting to OSeM Server..."); | |
} | |
if (osmclient.connect(osmserver, 80)) { | |
if (debugserial){ | |
Serial.println("connected!"); | |
Serial.println("-------------------------------------"); | |
} | |
// Build HTTP headers | |
osmclient.print("POST /boxes/"); osmclient.print(SENSEBOX_ID); osmclient.print("/"); osmclient.print(sensorId); osmclient.println(" HTTP/1.1"); | |
osmclient.print("Host:"); | |
osmclient.println(osmserver); | |
osmclient.println("Content-Type: application/json"); | |
osmclient.println("Connection: close"); | |
osmclient.print("Content-Length: "); osmclient.println(jsonValue.length()); | |
osmclient.println(); | |
//Daten senden | |
osmclient.println(jsonValue); | |
} else { | |
if (debugserial){ | |
Serial.println("failed!"); | |
Serial.println("-------------------------------------"); | |
} | |
} | |
// Show answer from server in the serial monitor | |
waitForServerResponse(); | |
} | |
void waitForServerResponse () { | |
// Output incoming bytes | |
boolean repeat = true; | |
do { | |
if (osmclient.available()) { | |
char c = osmclient.read(); | |
if (debugserial){ | |
Serial.print(c); | |
} | |
} | |
// End connection | |
if (!osmclient.connected()) { | |
if (debugserial){ | |
Serial.println(); | |
Serial.println("--------------"); | |
Serial.println("Disconnecting."); | |
Serial.println("--------------"); | |
} | |
osmclient.stop(); | |
repeat = false; | |
} | |
} while (repeat); | |
} | |
// ******************************************************************************** | |
// * UPLOADS DATA TO UBIDOTS * | |
// ******************************************************************************** | |
void Update_Ubidots (void){ | |
if (upload_to_ubidots){ | |
if (debugserial){ | |
Serial.println("Uploading data to Ubidots"); | |
} | |
//H2S ppm | |
client.add(ID1,data_H2S); | |
// RAW H2S | |
client.add(ID2,RawSensor); | |
// Temperature | |
client.add(ID3,Temperature); | |
// Humidity | |
client.add(ID4,RH); | |
client.add(ID5,Batt); | |
client.sendAll(); | |
if (debugled){ | |
//Turns ON LED for a second | |
digitalWrite(LED_BUILTIN, HIGH); // turn the LED on (HIGH is the voltage level) | |
delay(3000); | |
digitalWrite(LED_BUILTIN, LOW); // turn the LED on (HIGH is the voltage level) | |
delay(1000); | |
} | |
} | |
} | |
// ******************************************************************************** | |
// * FUNCTIONS FOR DGS-H2S SENSOR * | |
// ******************************************************************************** | |
/* ******************************************************************************** | |
* This function triggers one measurement and receives the data from the sensor | |
**********************************************************************************/ | |
void SPEC_Data_Read(){ | |
// First, we do some initialization | |
// dataStringComplete is set as "false", as we don't have any valid data received | |
dataStringComplete = 0; | |
// Clear the data string | |
dataString = ""; | |
// Now we trigger a measurement | |
portOne.print("\r"); | |
// We wait for the sensor to respond | |
dataString = portOne.readStringUntil('\n'); | |
if (debugserial) { | |
Serial.print("Data string length "); | |
Serial.println(dataString.length()); | |
Serial.println(dataString); | |
} | |
} | |
/* ******************************************************************************** | |
* This function takes the received string and upodates sensor data | |
**********************************************************************************/ | |
void SPEC_Parse_Data(){ | |
// Parses the received dataString | |
// Data string is comma separated | |
// The format of the output is: SN[XXXXXXXXXXXX], PPB [0 : 999999], TEMP [-99:99], | |
// RH[0:99], RawSensor[ADCCount], TempDigital, RHDigital, Day[0:99], Hour [0:23], | |
// Minute[0:59], Second[0 : 59]\r\n | |
// Take a look also at | |
// https://stackoverflow.com/questions/11068450/arduino-c-language-parsing-string-with-delimiter-input-through-serial-interfa | |
// We look first for the SN | |
int idx1 = dataString.indexOf(','); | |
SensorSerialNo = dataString.substring(0, idx1); | |
int idx2 = dataString.indexOf(',', idx1 + 1); | |
// Hint: after comma there's a space - it should be ignored | |
String S_gas = dataString.substring(idx1 + 2, idx2); | |
H2S = S_gas.toInt(); | |
int idx3 = dataString.indexOf(',', idx2 + 1); | |
String S_temp = dataString.substring(idx2 + 2, idx3); | |
Temperature = S_temp.toInt(); | |
int idx4 = dataString.indexOf(',', idx3 + 1); | |
String S_humi = dataString.substring(idx3 + 2, idx4); | |
RH = S_humi.toInt(); | |
int idx5 = dataString.indexOf(',', idx4 + 1); | |
String S_raw_gas = dataString.substring(idx4 + 2, idx5); | |
RawSensor = S_raw_gas.toInt(); | |
int idx6 = dataString.indexOf(',', idx5 + 1); | |
String S_Tdigital = dataString.substring(idx5 + 2, idx6); | |
TempDigital = S_Tdigital.toInt(); | |
int idx7 = dataString.indexOf(',', idx6 + 1); | |
String S_RHdigital = dataString.substring(idx6 + 2, idx7); | |
RHDigital = S_RHdigital.toInt(); | |
int idx8 = dataString.indexOf(',', idx7 + 1); | |
String S_Days = dataString.substring(idx7 + 2, idx8); | |
Days = S_Days.toInt(); | |
int idx9 = dataString.indexOf(',', idx8 + 1); | |
String S_Hours = dataString.substring(idx8 + 2, idx9); | |
Hours = S_Hours.toInt(); | |
int idx10 = dataString.indexOf(',', idx9 + 1); | |
String S_Minutes = dataString.substring(idx9 + 2, idx10); | |
Minutes = S_Minutes.toInt(); | |
int idx11 = dataString.indexOf('\r'); | |
String S_Seconds = dataString.substring(idx10 + 2, idx11); | |
Seconds = S_Seconds.toInt(); | |
} | |
/* ******************************************************************************** | |
* This function triggers one measurement and receives the data from the sensor | |
**********************************************************************************/ | |
bool SPEC_set_zero(void) | |
{ | |
String response1 = ""; | |
String response2 = ""; | |
portOne.write('Z'); | |
response1 = portOne.readStringUntil('\n'); | |
response2 = portOne.readStringUntil('\n'); | |
if (response2 == "Setting zero...done\r"){ | |
if (debugserial){ | |
Serial.println("Success"); | |
} | |
return 1; | |
} | |
else { | |
if (debugserial){ | |
Serial.println("failed"); | |
} | |
} | |
return 0; | |
} | |
/* ******************************************************************************** | |
* This function clears the serial buffer | |
**********************************************************************************/ | |
void flush_serial(void){ | |
// Do we have any data in the serial buffer? If so, flush it | |
if (portOne.available() > 0){ | |
//Serial.println ("Flushing serial buffer..."); | |
while(1){ | |
inChar = (char)portOne.read(); | |
delay(10); | |
if (debugserial){ | |
Serial.print(inChar); | |
} | |
if (inChar == '\n') break; | |
} | |
if (debugserial){ | |
Serial.println (" "); | |
Serial.println ("Buffer flushed!"); | |
} | |
} | |
} | |
// ******************************************************************************** | |
// * Debug via LED - flashes the led a number of times, with 1 sec delay * | |
// ******************************************************************************** | |
void debugflash (int flashes, int mypin){ | |
for (int count = 0; count < flashes; count ++){ | |
digitalWrite(mypin, HIGH); // turn the LED on (HIGH is the voltage level) | |
delay(200); | |
digitalWrite(mypin, LOW); // turn the LED on (HIGH is the voltage level) | |
delay(200); | |
} | |
delay(1000); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment