ESP32 with multiple MQ Gas sensors getting unstable readings

Greetings!

I’m trying to assemble a ESP32 dev board with three MQ sensors for my bathroom: MQ-2 (smoke), MQ-4 (methane) and MQ-8 (hydrogen), but i’m having a hard time wiring it.

I’m currently powering the ESP32 with my PC USB port and the sensors with an external 5V 2.5A PSU. All the sensors turn on, and I’m using ADC1 ports 33, 32 and 35. The analog readings are very erratic, even if I leave only one sensor. The multimeter readings on positive and negative are very stable between 5.14 ~ 5.16 VDC.

The only way to get the sensor to give stable readings is to wire it directly to the Vin and GND from ESP32.

I tried downvoltaging the analog wire from 5 to 3.3 V (100 to wire and 200 ohms to GND), tried mounting 1mF and 100 mF capacitors to the 5 VDC feed and even tried using a LM7805 voltage regulator but still amounting to unstable readings when powering sensor with external PSU.

Any ideias?

Thanks in advance.

It is a bit hard to follow what you are saying. Post a schematic showing all power and ground connections, not a frizzy thing. It sounds like you are not powering them correctly, the schematic will clarify that. Include links to technical data of each hardware device you are using. The heaters on some of those sensors require almost 200mA at 5VDC. Once you get it operating let it “burn off” the contamination collected on the sensor. “downvoltaging” is not valid, you also have to supply current at the required voltage. When "downvoltaging " be sure you have enough head room for the device to work properly. your 7805 is doing exactly what I would expect, read the data sheets.

Did you connect the grounds together?

You will get way more accurate readings with the ESP32 and analog readings by using the ESP32 ADC API

1 Like

Here is the code for a project using the ESP32 ADC API in the Arduino IDE. I’ll break out the ADC API in the next post.

#include <WiFi.h>
#include <PubSubClient.h>
#include "certs.h" // include the connection infor for WiFi and MQTT
#include "sdkconfig.h" // used for log printing
#include "esp_system.h"
#include "freertos/FreeRTOS.h" //freeRTOS items to be used
#include "freertos/task.h"
#include <driver/adc.h>
#include <SimpleKalmanFilter.h>
////
WiFiClient      wifiClient; // do the WiFi instantiation thing
PubSubClient    MQTTclient( mqtt_server, mqtt_port, wifiClient ); //do the MQTT instantiation thing
ESP32Time       rtc;
////
#define evtDoParticleRead  ( 1 << 0 ) // declare an event
#define evtADCreading      ( 1 << 3 )
EventGroupHandle_t eg; // variable for the event group handle
////
SemaphoreHandle_t sema_MQTT_KeepAlive;
SemaphoreHandle_t sema_mqttOK;
////
QueueHandle_t xQ_RemainingMoistureMQTT;
QueueHandle_t xQ_RM;
QueueHandle_t xQ_Message;
////
struct stu_message
{
  char payload [150] = {'\0'};
  String topic;
} x_message;
////
int    mqttOK = 0;
bool   TimeSet = false;
bool   manualPumpOn = false;
////
// interrupt service routine for WiFi events put into IRAM
void IRAM_ATTR WiFiEvent(WiFiEvent_t event)
{
  switch (event) {
    case SYSTEM_EVENT_STA_CONNECTED:
      break;
    case SYSTEM_EVENT_STA_DISCONNECTED:
      log_i("Disconnected from WiFi access point");
      break;
    case SYSTEM_EVENT_AP_STADISCONNECTED:
      log_i("WiFi client disconnected");
      break;
    default: break;
  }
} // void IRAM_ATTR WiFiEvent(WiFiEvent_t event)
////
void IRAM_ATTR mqttCallback(char* topic, byte * payload, unsigned int length)
{
  // clear locations
  memset( x_message.payload, '\0', 150 );
  x_message.topic = ""; //clear string buffer
  x_message.topic = topic;
  int i = 0;
  for ( i; i < length; i++)
  {
    x_message.payload[i] = ((char)payload[i]);
  }
  x_message.payload[i] = '\0';
  xQueueOverwrite( xQ_Message, (void *) &x_message );// send data
} // void mqttCallback(char* topic, byte* payload, unsigned int length)
////
void setup()
{
  x_message.topic.reserve(150);
  //
  xQ_Message = xQueueCreate( 1, sizeof(stu_message) );
  xQ_RemainingMoistureMQTT = xQueueCreate( 1, sizeof(float) ); // sends a queue copy
  xQ_RM = xQueueCreate( 1, sizeof(float) );
  //
  eg = xEventGroupCreate(); // get an event group handle
  //
  sema_mqttOK =  xSemaphoreCreateBinary();
  xSemaphoreGive( sema_mqttOK );
  //
  gpio_config_t io_cfg = {}; // initialize the gpio configuration structure
  io_cfg.mode = GPIO_MODE_INPUT; // set gpio mode. GPIO_NUM_0 input from water level sensor
  io_cfg.pull_down_en = GPIO_PULLDOWN_ENABLE; // enable pull down
  io_cfg.pin_bit_mask = ( (1ULL << GPIO_NUM_0) ); //bit mask of the pins to set, assign gpio number to be configured
  gpio_config(&io_cfg); // configure the gpio based upon the parameters as set in the configuration structure
  //
  io_cfg = {}; //set configuration structure back to default values
  io_cfg.mode = GPIO_MODE_OUTPUT;
  io_cfg.pin_bit_mask = ( 1ULL << GPIO_NUM_4 | (1ULL << GPIO_NUM_5) ); //bit mask of the pins to set, assign gpio number to be configured
  gpio_config(&io_cfg);
  gpio_set_level( GPIO_NUM_4, LOW); // deenergize relay module
  gpio_set_level( GPIO_NUM_5, LOW); // deenergize valve
  // set up A:D channels  https://dl.espressif.com/doc/esp-idf/latest/api-reference/peripherals/adc.html
  adc1_config_width(ADC_WIDTH_12Bit);
  adc1_config_channel_atten(ADC1_CHANNEL_3, ADC_ATTEN_DB_11);// using GPIO 39
  //
  xTaskCreatePinnedToCore( MQTTkeepalive, "MQTTkeepalive", 10000, NULL, 6, NULL, 1 );
  xTaskCreatePinnedToCore( fparseMQTT, "fparseMQTT", 10000, NULL, 5, NULL, 1 ); // assign all to core 1, WiFi in use.
  xTaskCreatePinnedToCore( fPublish, "fPublish", 9000, NULL, 3, NULL, 1 );
  xTaskCreatePinnedToCore( fReadAD, "fReadAD", 9000, NULL, 3, NULL, 1 );
  xTaskCreatePinnedToCore( fDoMoistureDetector, "fDoMoistureDetector", 70000, NULL, 4, NULL, 1 );
  xTaskCreatePinnedToCore( fmqttWatchDog, "fmqttWatchDog", 3000, NULL, 2, NULL, 1 );
} //void setup()
////
void fReadAD( void * parameter )
{
  float    ADbits = 4096.0f;
  float    uPvolts = 3.3f;
  float    adcValue_b = 0.0f; //plant in yellow pot
  uint64_t TimePastKalman  = esp_timer_get_time(); // used by the Kalman filter UpdateProcessNoise, time since last kalman calculation
  float    WetValue = 1.07f; // value found by putting sensor in water
  float    DryValue = 2.732f; // value of probe when held in air
  float    Range = DryValue - WetValue;
  float    RemainingMoisture = 100.0f;
  SimpleKalmanFilter KF_ADC_b( 1.0f, 1.0f, .01f );
  for (;;)
  {
    xEventGroupWaitBits (eg, evtADCreading, pdTRUE, pdTRUE, portMAX_DELAY ); //
    adcValue_b = float( adc1_get_raw(ADC1_CHANNEL_3) ); //take a raw ADC reading
    adcValue_b = ( adcValue_b * uPvolts ) / ADbits; //calculate voltage
    KF_ADC_b.setProcessNoise( (esp_timer_get_time() - TimePastKalman) / 1000000.0f ); //get time, in microsecods, since last readings
    adcValue_b = KF_ADC_b.updateEstimate( adcValue_b ); // apply simple Kalman filter
    TimePastKalman = esp_timer_get_time(); // time of update complete
    RemainingMoisture = 100.0f * (1 - ((adcValue_b - WetValue) / (DryValue - WetValue))); //remaining moisture =  1-(xTarget - xMin) / (xMax - xMin) as a percentage of the sensor wet dry volatges
    xQueueOverwrite( xQ_RM, (void *) &RemainingMoisture );
    //log_i( "adcValue_b = %f remaining moisture %f%", adcValue_b, RemainingMoisture );
  }
  vTaskDelete( NULL );
}
////
void fPublish( void * parameter )
{
  float  RemainingMoisture = 100.0f;
  for (;;)
  {
    if ( xQueueReceive(xQ_RemainingMoistureMQTT, &RemainingMoisture, portMAX_DELAY) == pdTRUE )
    {
      xSemaphoreTake( sema_MQTT_KeepAlive, portMAX_DELAY ); // whiles MQTTlient.loop() is running no other mqtt operations should be in process
      MQTTclient.publish( topicRemainingMoisture_0, String(RemainingMoisture).c_str() );
      xSemaphoreGive( sema_MQTT_KeepAlive );
    }
  } // for (;;)
  vTaskDelete( NULL );
} //void fPublish( void * parameter )
////
void WaterPump0_off()
{
  gpio_set_level( GPIO_NUM_4, LOW); //denergize relay module
  vTaskDelay( 1 );
  gpio_set_level( GPIO_NUM_5, LOW); //denergize/close valve
}
////
void WaterPump0_on()
{
  gpio_set_level( GPIO_NUM_5, HIGH); //energize/open valve
  vTaskDelay( 1 );
  gpio_set_level( GPIO_NUM_4, HIGH); //energize relay module
}
////
void fmqttWatchDog( void * paramater )
{
  int UpdateImeTrigger = 86400; //seconds in a day
  int UpdateTimeInterval = 85000; // get another reading when = UpdateTimeTrigger
  int maxNonMQTTresponse = 12;
  TickType_t xLastWakeTime = xTaskGetTickCount();
  const TickType_t xFrequency = 5000; //delay for mS
  for (;;)
  {
    xLastWakeTime = xTaskGetTickCount();
    vTaskDelayUntil( &xLastWakeTime, xFrequency );
    xSemaphoreTake( sema_mqttOK, portMAX_DELAY ); // update mqttOK
    mqttOK++;
    xSemaphoreGive( sema_mqttOK );
    if ( mqttOK >= maxNonMQTTresponse )
    {
      ESP.restart();
    }
    UpdateTimeInterval++; // trigger new time get
    if ( UpdateTimeInterval >= UpdateImeTrigger )
    {
      TimeSet = false; // sets doneTime to false to get an updated time after a days count of seconds
      UpdateTimeInterval = 0;
    }
  }
  vTaskDelete( NULL );
} //void fmqttWatchDog( void * paramater )
////
void fDoMoistureDetector( void * parameter )
{
  //wait for a mqtt connection
  while ( !MQTTclient.connected() )
  {
    vTaskDelay( 250 );
  }
  int      TimeToPublish = 5000000; //5000000uS
  int      TimeForADreading = 100 * 1000; // 100mS
  uint64_t TimePastPublish = esp_timer_get_time(); // used by publish
  uint64_t TimeADreading   = esp_timer_get_time();
  TickType_t xLastWakeTime = xTaskGetTickCount();
  const TickType_t xFrequency = 10; //delay for 10mS
  float    RemainingMoisture = 100.0f; //prevents pump turn on during start up
  bool     pumpOn = false;
  uint64_t PumpOnTime = esp_timer_get_time();
  int      PumpRunTime = 11000000;
  uint64_t PumpOffWait = esp_timer_get_time();
  uint64_t PumpOffWaitFor = 60000000; //one minute
  float    lowMoisture = 23.0f;
  float    highMoisture = 40.0f;
  for (;;)
  {
    //read AD values every 100mS.
    if ( (esp_timer_get_time() - TimeADreading) >= TimeForADreading )
    {
      xEventGroupSetBits( eg, evtADCreading );
      TimeADreading = esp_timer_get_time();
    }
    xQueueReceive(xQ_RM, &RemainingMoisture, 0 ); //receive queue stuff no waiting
    //read gpio 0 is water level good. Yes: OK to run pump : no pump off.   remaining moisture good, denergize water pump otherwise energize water pump.
    if ( RemainingMoisture >= highMoisture ) 
    {
      WaterPump0_off();
    }
    if ( !pumpOn )
    {
      log_i( "not pump on ");
      if ( gpio_get_level( GPIO_NUM_0 ) )
      {
        if ( RemainingMoisture <= lowMoisture )
        {
          //has one minute passed since last pump energize, if so then allow motor to run
          if ( (esp_timer_get_time() - PumpOffWait) >= PumpOffWaitFor )
          {
            WaterPump0_on();
            log_i( "pump on " );
            pumpOn = !pumpOn;
            PumpOnTime = esp_timer_get_time();
          }
        }
        //xSemaphoreGive( sema_RemainingMoisture );
      } else {
        log_i( "water level bad " );
        WaterPump0_off();
        PumpOffWait = esp_timer_get_time();
      }
    } else {
      /*
         pump goes on runs for 5 seconds then turn off, then wait PumpOffWaitTime before being allowed to energize again
      */
      if ( (esp_timer_get_time() - PumpOnTime) >= PumpRunTime )
      {
        log_i( "pump off " );
        WaterPump0_off(); // after 5 seconds turn pump off
        pumpOn = !pumpOn;
        PumpOffWait = esp_timer_get_time();
      }
    }
    // publish to MQTT every 5000000uS
    if ( (esp_timer_get_time() - TimePastPublish) >= TimeToPublish )
    {
      xQueueOverwrite( xQ_RemainingMoistureMQTT, (void *) &RemainingMoisture );// data for mqtt publish
      TimePastPublish = esp_timer_get_time(); // get next publish time
    }
    xLastWakeTime = xTaskGetTickCount();
    vTaskDelayUntil( &xLastWakeTime, xFrequency );
  }
  vTaskDelete( NULL );
}// end fDoMoistureDetector()
////
void MQTTkeepalive( void *pvParameters )
{
  sema_MQTT_KeepAlive   = xSemaphoreCreateBinary();
  xSemaphoreGive( sema_MQTT_KeepAlive ); // found keep alive can mess with a publish, stop keep alive during publish
  MQTTclient.setKeepAlive( 90 ); // setting keep alive to 90 seconds makes for a very reliable connection, must be set before the 1st connection is made.
  TickType_t xLastWakeTime = xTaskGetTickCount();
  const TickType_t xFrequency = 250; // 250mS
  for (;;)
  {
    //check for a is-connected and if the WiFi 'thinks' its connected, found checking on both is more realible than just a single check
    if ( (wifiClient.connected()) && (WiFi.status() == WL_CONNECTED) )
    {
      xSemaphoreTake( sema_MQTT_KeepAlive, portMAX_DELAY ); // whiles MQTTlient.loop() is running no other mqtt operations should be in process
      MQTTclient.loop();
      xSemaphoreGive( sema_MQTT_KeepAlive );
    }
    else {
      log_i( "MQTT keep alive found MQTT status %s WiFi status %s", String(wifiClient.connected()), String(WiFi.status()) );
      if ( !(wifiClient.connected()) || !(WiFi.status() == WL_CONNECTED) )
      {
        connectToWiFi();
      }
      connectToMQTT();
    }
    xLastWakeTime = xTaskGetTickCount();
    vTaskDelayUntil( &xLastWakeTime, xFrequency );
  }
  vTaskDelete ( NULL );
}
////
void connectToMQTT()
{
  // create client ID from mac address
  byte mac[5];
  int count = 0;
  WiFi.macAddress(mac); // get mac address
  String clientID = String(mac[0]) + String(mac[4]);
  log_i( "connect to mqtt as client %s", clientID );
  while ( !MQTTclient.connected() )
  {
    MQTTclient.disconnect();
    MQTTclient.connect( clientID.c_str(), mqtt_username, mqtt_password );
    vTaskDelay( 250 );
    count++;
    if ( count == 5 )
    {
      ESP.restart();
    }
  }
  MQTTclient.setCallback( mqttCallback );
  MQTTclient.subscribe( topicOK );
}
////
void connectToWiFi()
{
  int TryCount = 0;
  while ( WiFi.status() != WL_CONNECTED )
  {
    TryCount++;
    WiFi.disconnect();
    WiFi.begin( SSID, PASSWORD );
    vTaskDelay( 4000 );
    if ( TryCount == 10 )
    {
      ESP.restart();
    }
  }
  WiFi.onEvent( WiFiEvent );
} // void connectToWiFi()
//////
void fparseMQTT( void *pvParameters )
{
  struct stu_message px_message;
  for (;;)
  {
    if ( xQueueReceive(xQ_Message, &px_message, portMAX_DELAY) == pdTRUE )
    {
      if ( px_message.topic == topicOK )
      {
        xSemaphoreTake( sema_mqttOK, portMAX_DELAY );
        mqttOK = 0; // clear mqtt ok count
        xSemaphoreGive( sema_mqttOK );
      }
      if ( !TimeSet )
      {
        String temp = "";
        temp = px_message.payload[0];
        temp += px_message.payload[1];
        temp += px_message.payload[2];
        temp += px_message.payload[3];
        int year =  temp.toInt();
        temp = "";
        temp = px_message.payload[5];
        temp += px_message.payload[6];
        int month =  temp.toInt();
        temp = "";
        temp = px_message.payload[8];
        temp += px_message.payload[9];
        int day =  temp.toInt();
        temp = "";
        temp = px_message.payload[11];
        temp += px_message.payload[12];
        int hour =  temp.toInt();
        temp = "";
        temp = px_message.payload[14];
        temp += px_message.payload[15];
        int min =  temp.toInt();
        rtc.setTime( 0, min, hour, day, month, year );
        log_i( "%s  ", rtc.getTime() );
        TimeSet = true;
      }
      //manual pump control
      //    if ( str_eTopic == topicPumpState )
      //    {
      //      if ( String(strPayload) == "off" )
      //      {
      //        WaterPump0_off();
      //        manualPumpOn = false;
      //      }
      //      if ( String(strPayload) == "on" )
      //      {
      //        WaterPump0_on();
      //        manualPumpOn = true;
      //      }
      //    }
      //xSemaphoreGive( sema_MQTT_Parser );
    }
  } //for(;;)
  vTaskDelete ( NULL );
} // void fparseMQTT( void *pvParameters )
////
void loop() {}

Here I add the adc library and a Kalman Filter library.

#include <driver/adc.h>
#include <SimpleKalmanFilter.h>

In setup the ADC is configured and a working channel is assigned.

void setup()
{
// set up A:D channels  https://dl.espressif.com/doc/esp-idf/latest/api-reference/peripherals/adc.html
  adc1_config_width(ADC_WIDTH_12Bit);
  adc1_config_channel_atten(ADC1_CHANNEL_3, ADC_ATTEN_DB_11);// using GPIO 39
}

The task that uses the ADC API.

void fReadAD( void * parameter )
{
  float    ADbits = 4096.0f;
  float    uPvolts = 3.3f;
  float    adcValue_b = 0.0f; //plant in yellow pot
  uint64_t TimePastKalman  = esp_timer_get_time(); // used by the Kalman filter UpdateProcessNoise, time since last kalman calculation
  float    WetValue = 1.07f; // value found by putting sensor in water
  float    DryValue = 2.732f; // value of probe when held in air
  float    Range = DryValue - WetValue;
  float    RemainingMoisture = 100.0f;
  SimpleKalmanFilter KF_ADC_b( 1.0f, 1.0f, .01f );
  for (;;)
  {
    xEventGroupWaitBits (eg, evtADCreading, pdTRUE, pdTRUE, portMAX_DELAY ); //
    adcValue_b = float( adc1_get_raw(ADC1_CHANNEL_3) ); //take a raw ADC reading
    adcValue_b = ( adcValue_b * uPvolts ) / ADbits; //calculate voltage
    KF_ADC_b.setProcessNoise( (esp_timer_get_time() - TimePastKalman) / 1000000.0f ); //get time, in microsecods, since last readings
    adcValue_b = KF_ADC_b.updateEstimate( adcValue_b ); // apply simple Kalman filter
    TimePastKalman = esp_timer_get_time(); // time of update complete
    RemainingMoisture = 100.0f * (1 - ((adcValue_b - WetValue) / (DryValue - WetValue))); //remaining moisture =  1-(xTarget - xMin) / (xMax - xMin) as a percentage of the sensor wet dry volatges
    xQueueOverwrite( xQ_RM, (void *) &RemainingMoisture );
    //log_i( "adcValue_b = %f remaining moisture %f%", adcValue_b, RemainingMoisture );
  }
  vTaskDelete( NULL );
}

This section is where some working variables are created for the task.
ADbits, uPvolts are used to convert the ADC readings to a voltage.
adcValue contains the raw and filtered ADC reading.
A Kalman filter is declared and instantiated. The filter will be used to smooth the ADC readings.
TimePastKalman is used to give the Kalman filter a time between processes for better intergration.

 float    ADbits = 4096.0f;
  float    uPvolts = 3.3f;
  float    adcValue_b = 0.0f; //plant in yellow pot
  uint64_t TimePastKalman  = esp_timer_get_time(); // used by the Kalman filter UpdateProcessNoise, time since last kalman calculation
  float    WetValue = 1.07f; // value found by putting sensor in water
  float    DryValue = 2.732f; // value of probe when held in air
  float    Range = DryValue - WetValue;
  float    RemainingMoisture = 100.0f;
  SimpleKalmanFilter KF_ADC_b( 1.0f, 1.0f, .01f );

The next section does the work
The task is run in an infinite loop.
evtADC reading is a request from another process requesting a ADC reading.
The next 2 lines are where the actual ADC API call does the ADC reading and converts the reading to a voltage.

for (;;)
  {
    xEventGroupWaitBits (eg, evtADCreading, pdTRUE, pdTRUE, portMAX_DELAY ); //
    adcValue_b = float( adc1_get_raw(ADC1_CHANNEL_3) ); //take a raw ADC reading
    adcValue_b = ( adcValue_b * uPvolts ) / ADbits; //calculate voltage

The next part is where the ADC data is filtered.
The Kalman Filter is updated with the time since last reading in microseconds. The Kalman filter is applied, The time of the last Kalman Filter reading is stored.

KF_ADC_b.setProcessNoise( (esp_timer_get_time() - TimePastKalman) / 1000000.0f ); //get time, in microsecods, since last readings
    adcValue_b = KF_ADC_b.updateEstimate( adcValue_b ); // apply simple Kalman filter
    TimePastKalman = esp_timer_get_time(); // time of update complete

The next part converts the ADC voltage reading to a percentage of moisture and then the reading is sent to the triggering task.

Of course at the end is the safety feature that stops a task from overrunning its stack space.

 RemainingMoisture = 100.0f * (1 - ((adcValue_b - WetValue) / (DryValue - WetValue))); //remaining moisture =  1-(xTarget - xMin) / (xMax - xMin) as a percentage of the sensor wet dry volatges
    xQueueOverwrite( xQ_RM, (void *) &RemainingMoisture );
    //log_i( "adcValue_b = %f remaining moisture %f%", adcValue_b, RemainingMoisture );
  }
  vTaskDelete( NULL );
}
1 Like

Sorry for the mess! Here is the simple way of assembly I was trying before all those frustrated atempts:

The datasheets for the MQ sensors are (had to separate links due to post resctriction):

MQ-2: https://www.pololu.com/file/0J309/MQ2.pdf-------MQ-4:https://www.pololu.com/file/0J311/MQ4.pdf-------MQ-8:https://dlnmh9ip6v2uc.cloudfront.net/datasheets/Sensors/Biometric/MQ-8.pdf

The serial window is reporting this:

rst:0x1 (POWERON_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)
configsip: 0, SPIWP:0xee
clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
mode:DIO, clock div:2
load:0x3fff0018,len:4
load:0x3fff001c,len:1216
ho 0 tail 12 room 4
load:0x40078000,len:10944
load:0x40080400,len:6360
entry 0x400806b4
Connecting to
mleiva5

WiFi connected
Trying to reconnect to MQTT Broker:7.7.7.7
Succesfully connected to MQTT Broker.
CH4: 4095.00 - H2: 4085.00 - Smoke: 4095.00
CH4: 0.00 - H2: 0.00 - Smoke: 0.00
CH4: 4095.00 - H2: 2415.00 - Smoke: 3583.00
CH4: 4095.00 - H2: 3946.00 - Smoke: 4095.00
CH4: 1416.00 - H2: 1043.00 - Smoke: 2216.00
CH4: 543.00 - H2: 0.00 - Smoke: 0.00
CH4: 3751.00 - H2: 2735.00 - Smoke: 3385.00
CH4: 4095.00 - H2: 4095.00 - Smoke: 4095.00
CH4: 1099.00 - H2: 1079.00 - Smoke: 1535.00
CH4: 1332.00 - H2: 0.00 - Smoke: 0.00
CH4: 4095.00 - H2: 4095.00 - Smoke: 4095.00
CH4: 1632.00 - H2: 512.00 - Smoke: 2223.00
CH4: 0.00 - H2: 0.00 - Smoke: 0.00
CH4: 3540.00 - H2: 2712.00 - Smoke: 3727.00
CH4: 4068.00 - H2: 3583.00 - Smoke: 4095.00
CH4: 0.00 - H2: 0.00 - Smoke: 0.00
CH4: 3695.00 - H2: 2625.00 - Smoke: 3927.00
CH4: 4095.00 - H2: 747.00 - Smoke: 1647.00
CH4: 0.00 - H2: 0.00 - Smoke: 0.00
CH4: 4095.00 - H2: 4095.00 - Smoke: 4095.00
CH4: 1495.00 - H2: 939.00 - Smoke: 1632.00
CH4: 1920.00 - H2: 3218.00 - Smoke: 3056.00
CH4: 4095.00 - H2: 3645.00 - Smoke: 4095.00
CH4: 0.00 - H2: 0.00 - Smoke: 0.00
CH4: 3440.00 - H2: 1722.00 - Smoke: 3264.00
CH4: 3295.00 - H2: 808.00 - Smoke: 2417.00
CH4: 0.00 - H2: 0.00 - Smoke: 0.00
CH4: 4095.00 - H2: 2862.00 - Smoke: 4095.00
CH4: 1571.00 - H2: 1096.00 - Smoke: 3156.00
CH4: 0.00 - H2: 0.00 - Smoke: 0.00
CH4: 4095.00 - H2: 2975.00 - Smoke: 4095.00
CH4: 3440.00 - H2: 2824.00 - Smoke: 4095.00
CH4: 1075.00 - H2: 384.00 - Smoke: 1387.00
CH4: 1983.00 - H2: 820.00 - Smoke: 676.00
CH4: 4095.00 - H2: 4095.00 - Smoke: 4095.00
CH4: 1662.00 - H2: 939.00 - Smoke: 2064.00
CH4: 0.00 - H2: 0.00 - Smoke: 0.00
CH4: 3695.00 - H2: 2767.00 - Smoke: 3552.00
CH4: 4095.00 - H2: 2991.00 - Smoke: 4060.00
CH4: 0.00 - H2: 0.00 - Smoke: 0.00
CH4: 4095.00 - H2: 2599.00 - Smoke: 2336.00
CH4: 4095.00 - H2: 4095.00 - Smoke: 4095.00
CH4: 1807.00 - H2: 1888.00 - Smoke: 1840.00
CH4: 0.00 - H2: 0.00 - Smoke: 0.00

Idahowalker thanks for the code and the tips, but I understand this your code is more like a refinement in the readings (aprox. ± 3-5%) than a major correction in the readings, right? I’ll definitely study this more when I’m done fixing those readings. Please let me know if I got it wrong!

Here is the code I’m using:

#include <WiFi.h>
#include <HTTPClient.h>
#include <PubSubClient.h>

#define H2AR 32
#define SMOKEAR 34
#define CH4AR 35

#define ID_MQTT                   "esp32_mq3_4_8_bathroom"     
#define TOPIC_PUBLISH_CH4_A      "House/Bathroom/CH4A"  
#define TOPIC_PUBLISH_H2_A       "House/Bathroom/H2A"  
#define TOPIC_PUBLISH_SMOKE_A    "House/Bathroom/FumacaA"  

const char *ssid =  "mleiva5";            
const char *pass =  "XXXXXX";
const char* BROKER_MQTT = "7.7.7.7";
int BROKER_PORT = 1883;
                      

WiFiClient client;
PubSubClient MQTT(client);       


void reconnectMQTT(void);


void setup() {
  
  Serial.begin(115200);
  delay(10);
  pinMode(CH4AR, INPUT);
  pinMode(H2AR, INPUT);
  pinMode(SMOKEAR, INPUT);
  Serial.println("Connecting to ");
  Serial.println(ssid);
  WiFi.begin(ssid, pass);
  while (WiFi.status() != WL_CONNECTED) 
    {
        delay(250);
        Serial.print(".");
    }
  Serial.println("");
  Serial.println("WiFi connected");
  MQTT.setServer(BROKER_MQTT, BROKER_PORT);  
  delay(2000); 
}


void reconnectMQTT(void)                   
{
    while (!MQTT.connected()) 
    {
        Serial.print("Trying to reconnect to MQTT Broker:");
        Serial.println(BROKER_MQTT);
        if (MQTT.connect(ID_MQTT)) 
        {
            Serial.println("Succesfully connected to MQTT Broker.");
        } 
        else
        {
            Serial.println("Failed to connect to MQTT Broker.");
            Serial.println("New connection attempt in 2s...");
            delay(2000);
        }
    }
}

void loop() {

    if(WiFi.status()== WL_CONNECTED){

    reconnectMQTT(); 

    float ch4 = analogRead(CH4AR);    delay(1000);
    float h2 = analogRead(H2AR);    delay(1000);
    float smoke = analogRead(SMOKEAR);

    Serial.print("CH4: ");
    Serial.print(ch4);
    Serial.print("    -   ");
    Serial.print("H2: ");
    Serial.print(h2);
    Serial.print("    -   ");
    Serial.print("Smoke: ");
    Serial.print(smoke);
    Serial.println(" ");

    char ch4_str[10]     = {0};
    char h2_str[10]     = {0};
    char smoke_str[10]     = {0};
    sprintf(ch4_str,"%.2f", ch4);
    sprintf(h2_str,"%.2f", h2);
    sprintf(smoke_str,"%.2f", smoke);

    MQTT.publish(TOPIC_PUBLISH_CH4_A, ch4_str);
    MQTT.publish(TOPIC_PUBLISH_H2_A, h2_str);
    MQTT.publish(TOPIC_PUBLISH_SMOKE_A, smoke_str);
    
    MQTT.loop();
}

  if (WiFi.status() != WL_CONNECTED) {Serial.println("WiFi Disconnected");}

}```

Hi,
Do you have the gnd of your sensor supply connected the gnd of the ESP?

I can see three wires that are the signal wires going to the ESP but no GND wire to reference the signals too.

Can you post links to where you got those sensor modules from please?

Tom… :grinning: :+1: :coffee: :australia:

1 Like

I hadn’t but I tried now and it’s working flawlessly! Thanks so much!

TomGeorge thanks for the tip too! Made me understand what Idahowalker suggested before!

As for the link, you should find these sensors in several online retailers, such as aliexpress or even local ones: https://pt.aliexpress.com/af/mq%252d2.html?d=y&origin=n&SearchText=mq-2&catId=0&initiative_id=SB_20210423063539

One last question: can I plug the GND pin by the side of Vin (5V) on ESP32 with the GND from the external PSU when powering ESP32 through PC USB? I fried one ESP32 yesterday fiddling with connectors and I just wanted to make sure since I’m not an electrical engineer :sweat_smile:

Hi,
Yes you can connect the gnds together, BUT do any wiring adjustments or changes with POWER OFF.

Tom… :grinning: :+1: :coffee: :australia:

1 Like

This topic was automatically closed 120 days after the last reply. New replies are no longer allowed.