Hydrogen Sulfide sensing with Arduino Due

10

Performance and characteristics

This little hydrogen sulfide sensor has some pretty impressive specifications.

This article is more than two years old and might contain obsolete information; it is still kept here for informational purposes.
  • The measurement range is from 0 to 10ppm, with a resolution of 10 ppb (parts per billion).
  • Measurement accuracy is 15% of the reading
  • Measurement repeatability is below ±3% of reading
  • Very important, it has a low cross-sensitivity to other gases:
    • 10ppm chlorine will be registered as -2.2ppm H2S
    • 10ppm NO2 will be registered as -2.0ppm H2S
    • 20ppm SO2 will be registered as 1.7ppm H2S
    • 50ppm NO will be registered as 1.2ppm H2S
    • 400ppm CO will be registered as 1.1ppm H2S
    • Other gases such as ozone (5ppm), methane (500ppm), ammonia (100ppm) will alter the H2S readings by less than 1ppm.
  • The sensor is designed to be powered from 3.3V, with a range of 2.6 to 3.3V power supply voltage
  • Power consumption is 1mW for 1 minute triggered samples and 12mW for continuous operation.
  • Its environmental characteristics allow it to operate outside
    • Temperature Range is 20 to 40 C (-30 to 55 C intermittent)
    • Humidity Range is 15 to 95% (0 to 100% non – condensing intermittent)
    • Sensor lifespan is over 5 years.

Arduino code for the H2S sensor

Here comes the fun part: using the sensor with an Arduino board. I used one Arduino Due, as that board works with 3.3V logic levels. The on;y thing to keep in mind is that the Due is a 32-bit board, and as such the int variable is 32 bit (ranges from -2,147,483,648 to 2,147,483,647). On 8-bit boards some readings will have to be recorded as floats.

Considering the sensor connected to UART1, UART0 being used by the serial monitor, the code I propose performs an EEPROM dump, showing all the settings of the sensor, and then triggers a measurement every 10 seconds. The results are sent to the PC using the Serial Monitor in Arduino IDE.

Advertisements

NOTE: The code below is for sensor firmware 14FEB17.
For newer firmware versions see this blog post.

// SPEC H2S Sensor demo code
// This code works with sensor firmware 14FEB17

// Serial number of my sensor
// #define mysensor_serial_no 012017030207

// #define SERIAL1_RX_BUFFER_SIZE 256
// #define SERIAL1_TX_BUFFER_SIZE 256

// 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 Arduino Due 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; 
int H2S;
int Temperature;
int RH;
int RawSensor;
int TempDigital;
int RHDigital;
int Days;
int Hours;
int Minutes;
int Seconds;

#define command_delay 500
#define start_delay 2500
String dataString = "";
String responseString = "";
boolean dataStringComplete = 0;
char inChar;

void setup() {
  Serial.begin(9600);
  Serial.println("H2S sensor demo code!");
  Serial1.begin(9600);
  // Normally, data is returned within one second
  Serial1.setTimeout(1500);
  // reserve 80 bytes for the dataString
  dataString.reserve(80);
  responseString.reserve(150);
  
  // Wait for sensor 
  delay(500);
  flush_serial1();
  
  // EEPROM dump
  SPEC_dump_EEPROM();
  Serial.println(" ");
  Serial.println("STARTING MEASUREMENTS");
  Serial.println(" ");
}  



void loop() {
  // Do a readout every 10 seconds
  SPEC_Data_read();
  SPEC_parse_data();
  SPEC_print_data();
  delay(10000);
}

/* ********************************************************************************
 * 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
  Serial1.print(" ");
  // We wait for the sensor to respond
  dataString = Serial1.readStringUntil('\n');
  //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 prints the sensor data
 **********************************************************************************/
void SPEC_print_data(){
  Serial.println("********************************************************************");
  Serial.print ("Sensor Serial No. is ");
  Serial.println (SensorSerialNo);
  Serial.print ("H2S level is ");
  Serial.print (H2S);
  Serial.println (" ppb");
  Serial.print ("Temperature is ");
  Serial.print (Temperature, DEC);
  Serial.println (" deg C");
  Serial.print ("Humidity is ");
  Serial.print (RH, DEC);
  Serial.println ("% RH");
  Serial.print ("Sensor is online since: ");
  Serial.print (Days, DEC);
  Serial.print (" days, ");
  Serial.print (Hours, DEC);
  Serial.print (" hours, ");
  Serial.print (Minutes, DEC);
  Serial.print (" minutes, ");
  Serial.print (Seconds, DEC);
  Serial.println (" seconds");
  Serial.println ("Raw Sensor Data");    
  Serial.print ("Raw gas level: ");
  Serial.println (RawSensor);
  Serial.print ("Temperature digital: ");
  Serial.println (TempDigital);
  Serial.print ("Humidity digital: ");
  Serial.println (RHDigital);
  Serial.println ("");
}


/* ********************************************************************************
 * EEPROM dump
 **********************************************************************************/
void SPEC_dump_EEPROM(){
  // First we trigger a measurement
  Serial1.print(" ");
  // Within one second time we send the command "e"
  delay(400);
  Serial1.print("e");
  dataString = Serial1.readStringUntil('\n');
  // You can uncomment this line if you wish
  //Serial.println(dataString);
  for (int i=0; i<20; i++){ 
    responseString = Serial1.readStringUntil('\n');
    Serial.println(responseString);
  }   
}  

void flush_serial1(){
  // Do we have data in the serial buffer?
  // If so, flush it
  if (Serial1.available() > 0){
    Serial.println ("Flushing serial buffer...");
    while(1){
      inChar = (char)Serial1.read();
      delay(10);
      Serial.print(inChar);
      if (inChar == '\n') break; 
    }
    Serial.println (" ");
    Serial.println ("Buffer flushed!");
  }
}

Some comments regarding the code

The DGS-H2S 968-036 operating manual of the sensor (version March 2017) is a bit ambiguous regarding the UART communication protocol. On page 5 it states that a command is recognized if it’s received within 1-second from a TRIGGER event. On the next page, we find a 5-seconds interval for a command to be recognized.

I have found that the commands are recognized within an 800-milliseconds interval after the trigger event (before the sensor starts sending back the measured values). As a result, configuring the sensor with a terminal program such TeraTerm is next to impossible.

Also, the commands must NOT be terminated with \r\n or with \n, Just the command letter is enough.

Code examples for other commands will be published soon,

When starting for the first time, you will get some extreme readings. This is because the way this sensor works. In one of the technical documents, I have found a piece of information that compares this sensor with a capacitor, with bias placed across the working and reference electrodes being similar to the voltage across the plates of a capacitor. The high current observed at the sensor startup is like the charging current of a capacitor. As such, it is recommended to leave the sensor run for about one hour after power-up to get accurate readings.

[Update 23 October 2018] This is how to use the sensor with an Arduino Uno.

Advertisements
1 2
Share.

10 Comments

    • Teodor Costachioiu
      Teodor Costachioiu on

      The sensor is 3.3V only, so it will require some level translators to work with the Arduino Uno.
      The communication protocol is serial, but Arduino Uno has a single serial port. So, if you wish to do serial debugging or to send data to PC, then you will have to use software serial.
      I will try next week to post some images regarding the connections, and maybe a code example for the Arduino Uno, but it will take me a few days to do this.

    • Teodor Costachioiu
      Teodor Costachioiu on

      The analog version requires some extensive computations, especially when speaking on temperature and humidity compensation. The digital version does all this internally, and its easier to use.

  1. Avatar

    Ted,

    What PPB readings are you getting for “clean” air? I’m getting around 250 or so, even after running for several hours.

    • Teodor Costachioiu
      Teodor Costachioiu on

      It will detect H2S, even in very low amounts. However, this won’t be an accurate indicator of “smelliness”, as there are other gases that contribute to the overall odor.

        • Teodor Costachioiu
          Teodor Costachioiu on

          Hi Thomas,

          The cheaper alternative is MQ136 sensor. Also MQ137 might be interesting to detect ammonia. See https://playground.arduino.cc/Main/MQGasSensors for more info on MQ sensors.

          Please note that MQ series sensors are analog. Their output needs to be applied on a pin with A/D function, like A0-A5.

          Also, they are not calibrated. Their output is just proportional with the amount of gas in the air, but you won’t be able to tell exactly how much of the nasty gas is present.

Leave A Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.

error: