Skip to content

Instantly share code, notes, and snippets.

@Electronza
Created May 21, 2019 07:26
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 Electronza/d6083fb5c6741891874245c96e5c8896 to your computer and use it in GitHub Desktop.
Save Electronza/d6083fb5c6741891874245c96e5c8896 to your computer and use it in GitHub Desktop.
H2S sensor code
// ****************************************************************************
// * 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