Take control of gas monitoring: DIY gas sensor with SD card logging using MQ-135 sensor and DS3231 RTC


The air we breathe is a vital resource, but we often take its quality for granted. Gas sensor air monitoring has become a global priority with increasing concerns about air pollution and environmental safety. The control and management of these gases, both in industrial and domestic environments, has become fundamental to ensuring human health and the integrity of the surrounding environment.

In response to this growing need, I have developed a project that allows anyone, even without advanced electronics skills, to build a custom, cost-effective gas monitoring system. We will use the versatile MQ-135 gas sensor, known for its reliability in detecting a wide range of toxic and polluting gases, and the DS3231 RTC module, which will allow us to precisely annotate monitoring data with timestamps. Furthermore, the collected data will be stored on a micro SD card, allowing easy access to the collected data and its efficient management.

In this article, I will walk you step by step through the process of creating this gas monitoring device, which could be used in various applications, such as detecting gas leaks in the home, monitoring air in workplaces, or even for educational purposes to raise awareness of air quality.

You will learn to connect the components, program an ESP8266 to acquire and record data, and finally interpret and analyze the results. This project will not only give you a solid foundation of practical knowledge in electronics and programming, but will also give you direct control over air quality surveillance in environments you consider important.

So begin this journey towards a safer and more aware environment, where you can take control of your gas monitoring in a simple and personalized way. Before diving into the technical details of the project, we will give an overview of the various components.

As always, to write the firmware, we will make use of the excellent IDE Platformio.

Read on to find out how!

What components do we need for the gas sensor?

The list of components is not particularly long:

  • a breadboard to connect the ESP8266 NodeMCU to the other components
  • some DuPont wires (male – male, male – female, female – female)
  • a MQ-135 gas sensor
  • an RTC DS3231 module
  • a micro SD card reading/writing module with SPI interface
  • a micro SD card of no more than 32GB formatted in FAT32
  • and, obviously, an ESP8266 NodeMCU !

Let’s now look at these new components in more detail.

The MQ-135 sensor

The MQ-135 sensor is an electronic component that belongs to the gas sensor family. It is widely used for the detection and quantification of various types of gases, especially toxic gases and air pollutants. This sensor is a popular choice for projects requiring air quality monitoring in domestic, industrial or outdoor environments.

It can detect gases such as: ammonia, sulfides, LPG, propane, methane, hydrogen, alcohol, smoke and carbon monoxide and other harmful gases. Although it can detect such gases, it cannot distinguish the difference between them.

The one used in this article is produced by the company AZDelivery. Here is a detailed description of the MQ-135 sensor:

Principle of operation

The MQ-135 sensor operates on the principle of varying electrical resistance in response to the presence of certain gases in the air. The heart of the sensor is a sensitive element made of a thin organic semiconductor film that reacts chemically to the gases it comes into contact with. For best detection results, the gas sensor must be preheated. The best warm-up time for the sensor is more than 48 hours.

WARNING: the sensor is pre-heated/heated by powering it normally. You must absolutely NOT bring open flames near it!!

Detection of specific gases

The MQ-135 sensor is known for its detection of various types of gases, including:

  1. Carbon Monoxide (CO): the MQ-135 sensor is particularly sensitive to carbon monoxide, an odorless and tasteless toxic gas produced by the incomplete combustion of carbon, often found in vehicle exhaust and fire situations.
  2. Ammonia (NH3): this gas is often present in agriculture and the chemical industry and is toxic when inhaled in high concentrations.
  3. Methane (CH4): methane is the main constituent of natural gas and is flammable and explosive in specific air/gas proportions.
  4. Benzene (C6H6): Benzene is a volatile chemical that can be present in various industrial environments and is known to be carcinogenic.
  5. Volatile Organic Vapors (VOCs): these gases include a wide range of volatile chemical compounds that can be released from chemicals, solvents and construction materials.


The sensor is enclosed within two layers of thin stainless steel mesh called anti-explosive mesh. As a result, it can detect flammable substances without incident. Likewise, it provides protection for the sensor and filters suspended particles. In this way only gases can pass inside the detection chamber. The module has an integrated LM393 comparator chip that converts the readings into digital and analog signals.

Adjustable threshold

The module offers two types of output: one digital and the other analog. The output value from the analog one ranges from 0 to 1023 and is proportional to the total gas concentration detected. The digital output instead switches to 0 when the total gas concentration exceeds a certain threshold. The MQ-135 sensor, in fact, offers the possibility of regulating, via an integrated trimmer, the threshold beyond which its digital output triggers (going from 1 to 0), signaling that the total gas concentration corresponding to the set threshold has been exceeded . The sensor has two LEDs: one red and one green. The red one indicates normal operation of the module and lights up when it is powered. The green LED lights up when the threshold adjusted via the trimmer has been exceeded.

Temperature and humidity

The accuracy of the MQ-135 sensor is affected by ambient temperature and humidity. Therefore, it is important to take these variables into account when using the sensor in critical applications and calibrate it accordingly.

Common applications

The MQ-135 sensor finds application in a wide range of industries and projects, including:

  1. Air quality monitoring: it is used to measure the concentration of toxic gases or pollutants in the air in indoor and outdoor environments.
  2. Gas alarm systems: can be integrated into alarm systems to detect leaks of dangerous gases such as carbon monoxide or methane gas.
  3. Agricultural ammonia detection: it is used to monitor ammonia levels in livestock environments for animal health.
  4. Home automation projects: can be incorporated into home automation systems to trigger ventilation devices or alerts in the event of dangerous gas concentrations.
  5. Educational projects: the MQ-135 sensor is often used in educational projects to teach young students about gas sensing and air quality concepts.

Limitations and precautions

It should be noted that the MQ-135 sensor has variable sensitivity and can be affected by gases other than the specific substance it is intended to detect. Therefore, it is important to calibrate the sensor carefully and take environmental conditions into account.

WARNING: this project has a purely educational intent and does not claim to create a precise and accurate gas monitoring device like a commercial one. Therefore, it should not be used for monitoring purposes in potentially hazardous environments.

In conclusion, the MQ-135 sensor is a versatile and reliable gas detection device, widely used in applications requiring monitoring of air quality and environmental safety. However, it is essential to understand its features and limitations before using it in a specific project.

The DS3231

The DS3231 RTC (Real-Time Clock DS3231) is an electronic component widely used to track time in embedded applications. Its I2C (Inter-Integrated Circuit) interface makes it easy to integrate with microcontrollers, such as the Arduino, and other digital devices (such as ESP8266 and ESP32). The one used in this article is produced by the company AZDelivery. Let’s see an accurate description of the DS3231:

Extreme precision

The DS3231 is known for its amazing accuracy in keeping track of time. It has a maximum error of just a few seconds per year, making it ideal for applications requiring accurate time stamps.

I2C interface

The DS3231 RTC communicates via the I2C (or I-squared-C) interface. I2C is a serial communications protocol that allows you to connect multiple devices on a single bus, making the DS3231 ideal for projects that require simple, efficient time management.

Complete calendar

In addition to keeping track of time, the DS3231 also manages a full calendar, including days of the week, months and years, even accounting for leap years. This feature makes it useful in applications such as alarm clocks, digital calendars, and real-time clocks.

Integrated EEPROM memory

The DS3231 is equipped with a small EEPROM (Electrically Erasable Programmable Read-Only Memory) that can be used to store additional data. This memory is non-volatile, which means that data remains preserved even in the absence of electrical power.

Configurable alarm

You can set up two separate alarms on the DS3231, allowing the device to generate a stop signal or an alarm signal when certain time conditions are met. This feature is useful in applications such as alarm clocks or time controls.

Integrated temperature

The DS3231 also features a built-in temperature sensor. This sensor can be used to monitor ambient temperature and is especially useful when temperature accuracy is important for an application.

Low power and battery backup

To maintain time accuracy even in the event of a main power failure, the DS3231 can be powered by a backup battery. This battery ensures that the device continues to function and keep track of time even when the main power is cut.

Common applications

The DS3231 is widely used in a wide range of applications, including:

  1. Real Time Clocks (RTC): it is commonly used to add precise time stamping capabilities to devices such as digital clocks.
  2. Digital alarm clocks: the DS3231 can be used to create precise alarm clocks that do not need to be reset frequently.
  3. Timed device control: this is useful in applications that require scheduled activations or deactivations.
  4. Data logger: can be used to annotate data with timestamps in data logging projects.
  5. Home Automation Systems: can be integrated into home automation systems to schedule time-based actions.

In summary, the DS3231 is a highly reliable and accurate component for time and date monitoring in electronic applications. Its I2C interface simplifies integration with a variety of devices, making it a popular choice for microcontroller-based projects.

The micro SD card module

The micro SD card module is an electronic component designed for use with Arduino boards and other compatible development platforms. This module allows you to read and write data to Micro SD and Micro SDHC (TransFlash) memory cards using SPI (Serial Peripheral Interface) communication. The one used in this article is produced by the company AZDelivery.

Here is a detailed description of this module:

SPI interface

The SD card reader module uses SPI interface, which is a 4-wire serial communication protocol commonly used in embedded projects. These four wires are:

  1. MISO (Master In Slave Out): this is the pin through which the module receives data from the master device, which is usually the Arduino or another microcontroller.
  2. MOSI (Master Out Slave In): this is the pin through which the module sends data to the master device.
  3. SCK (Serial Clock): this is the clock pin that synchronizes data transmission between the module and the master device.
  4. CS (Chip Select): this pin is used to select the SD card reading module and initialize read/write operations.

Support for Micro SD and Micro SDHC

This module is compatible with both Micro SD cards and Micro SDHC cards, allowing the use of cards with capacities up to 32GB. Micro SD cards are commonly available and offer ample storage capacity for data such as log files, images, audio, or any other information you want to record.

Easy to use

The SD Reader Module is easy to integrate into your Arduino projects. It comes with a pre-installed Arduino library, which makes reading and writing data to micro SD cards much easier. This library allows you to easily access files on the card and perform operations such as creating, reading, modifying and deleting files.

Status LED

The module is equipped with a status LED that indicates when the module is active and communicating with the master device. This LED can be useful for debugging and monitoring read/write operations.

Common applications

This micro SD card reader module is widely used in a number of projects, including:

  • Data logging: record data from sensors or other sources to a micro SD card for future analysis.
  • Reading media files: read audio or image files from micro SD cards for playback or viewing on devices.
  • IoT projects: in Internet of Things (IoT) based projects to record environmental or sensor data on micro SD cards.
  • Video recording: in video recording systems based on Arduino or similar microcontrollers.

The SD card module is a useful and practical component for projects that require reading and writing data on Micro SD memory cards. Its compatibility with Arduino and other development platforms makes it a valuable addition for projects that require data storage and management. Due to its ease of use and support for large memory cards, it is a popular choice among electronics enthusiasts and developers.

Project implementation

The wiring scheme

Before creating the actual circuit let’s take a look at the pinout of the board:

ESP8266 NodeMCU pinout
ESP8266 NodeMCU pinout

Let’s also see the pinouts of the other components:

The pinout of the RTC module
The pinout of the RTC module
The pinout of the micro SD card module
The pinout of the micro SD card module
The pinout of the gas sensor module
The pinout of the gas sensor module

Let’s now see the electrical diagram of the project, created as usual with Fritzing:

Complete gas sensor wiring diagram
Complete gas sensor wiring diagram

It is possible that some modules need some connectors and therefore it is necessary to do some soldering. If you are new to this topic I recommend you read the article Yet another tutorial on how to solder.

As you can see, the micro SD card and gas sensor modules are powered by 5V from the Vin terminal while the RTC module is powered by 3.3V from the 3V3 terminal of the ESP8266.

The micro SD card module is connected to the SPI port of the ESP8266 which uses the pins:

  • D8 for CS terminal (Chip Select)
  • D7 for MOSI (Master Out Slave In) terminal
  • D6 for MISO terminal (Master In Slave OUT)
  • D5 for SCK (Serial Clock) terminal

The RTC module is connected to the I2C port of the ESP8266 which uses the pins:

  • D1 for the SCL terminal
  • D2 for the SDA terminal

The gas sensor module is connected to one analogue and one digital input. In particular:

  • A0 for the analog value
  • D3 for digital value

The sketch

Let’s create the PlatformIO project

We have already seen the procedure for creating a PlatformIO project in the article How to create a project for NodeMCU ESP8266 with PlatformIO.

Do not install the libraries indicated in the article but simply install the RTClib by Adafruit library as indicated in the following photo:

Install the RTClib by Adafruit library
Install the RTClib by Adafruit library

Now edit the platformio.ini file to add these two lines:

monitor_speed = 115200
upload_speed = 921600

so that it looks like this:

platform = espressif8266
board = nodemcuv2
framework = arduino
monitor_speed = 115200
upload_speed = 921600
lib_deps = adafruit/RTClib@^2.1.1

Obviously you can download the project from the following link:

Replace the main.cpp file of the project you created with the one present in the zip file.

Now let’s see how the sketch works.

Initially the necessary libraries are included:

#include <Arduino.h>
#include <Wire.h>
#include <RTClib.h> // Library for the DS3231 RTC
#include <SPI.h> // Include the SPI.h library
#include <SD.h>

The rtc object that manages the clock is then instantiated:

RTC_DS3231 rtc;

The dataFile object is then instantiated which represents the file on the micro SD card where the data will be written and the chipSelect, gasDetectorDigital and ANALOGPIN0 pins are defined relating, respectively, to the SPI port of the micro SD card module, to the digital output of the sensor module of gas and to the analog output of the gas sensor.

File dataFile;
const int chipSelect = D8; // Set the CS pin of the SD Card Shield module 
const int gasDetectorDigital = D3;
#define ANALOGPIN0 A0  // ESP8266 Analog Pin ADC0 = A0

The variables that manage the timing of the measurements are then defined (measureDelay and lastTimeRan) and the prototype of the setDateTimeFromSerial function which we will talk about later.

unsigned long measureDelay = 2000;                //    NOT LESS THAN 2000!!!!!    BETTER 600000  (10 MINUTES)
unsigned long lastTimeRan;

void setDateTimeFromSerial();

The setup function then begins.

Here we have the initialization of the serial port, the I2C bus, the RTC module, the micro SD card. If you are unable to initialize the micro SD card the sketch freezes (due to the return command):


// Initialize SPI communication with the SD module
if (!SD.begin(chipSelect)) {
Serial.println("Error initializing the SD card.");

Serial.println("SD card initialized successfully.");

At this point the date and time are set in the RTC module. This block transfers the date and time taken from the computer when the sketch was compiled in the RTC module. We will see later that the sketch also includes a manual date and time entry system (based on the aforementioned setDateTimeFromSerial function) which can be accessed by typing the ‘S’ character or the ‘s’ character in the Serial Monitor:

// Set the date and time only once if the DS3231 has not been previously initialized
if (rtc.lostPower()) {
Serial.println("RTC not initialized. I set the time...");
rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
Serial.println("Type 'S' or 's' to set the time and date.");

At the end of the setup function we find the definition of the analog and digital input pins dedicated to reading the gas sensor module:

pinMode(ANALOGPIN0, INPUT); // Initializes ANALOGPIN0 as input
pinMode(gasDetectorDigital, INPUT); // Initializes gasDetectorDigital as input

The loop function now begins. We initially encounter an if block that establishes the timing of module reading operations.

At the beginning the analog value of the gas sensor is read. The returned value is an integer between 0 and 1023. To convert it
in percentage, the map() function is used. This value is then converted into a string.

int analogGasSensorValue = analogRead(ANALOGPIN0);
uint16_t  gasVal = map(analogGasSensorValue, 0, 1023, 0, 100);
String analogGasSensorValue_s = String(gasVal);

The RTC module is then read, the individual components (year, month, day, hours, minutes and seconds) isolated and transformed into strings:

DateTime now = rtc.now(); // Read the date and time from the DS3231    

String year_s = String(now.year());
String month_s = String(now.month());
String day_s = String(now.day());
String hour_s = String(now.hour());
String minute_s = String(now.minute());
String second_s = String(now.second());

The value of the digital pin of the gas sensor is then read:

bool gasDetectorDigital_b = digitalRead(gasDetectorDigital);

If the value is true it means that the threshold value has not been exceeded and the label “NOALERT” will be written on the file, if the value is false it means that the threshold value has been exceeded and the label “ALERT” will be written on the file:

    String gasDetectorDigital_s;
    if(gasDetectorDigital_b) {
        gasDetectorDigital_s = "NOALERT";
    } else {
        gasDetectorDigital_s = "ALERT";

The string to be written to the file on the micro SD card is then composed. It is given by the concatenation of the various data separated by commas, in order to create a CSV file:

String rowToLog = analogGasSensorValue_s + "%," + day_s + "," + month_s +  ","  +  year_s +  "," + hour_s +  "," + minute_s +  "," + second_s + "," + gasDetectorDigital_s;

An attempt is then made to open the file (called log.txt) for writing:

dataFile = SD.open("log.txt", FILE_WRITE);

If the file is opened successfully, the file descriptor points to the end of the file (dataFile.seek(dataFile.size());) so that writing continues from the previous point (and not always from the beginning of the file) and appends a new row (dataFile.println(rowToLog);). After which the file is closed waiting for a new cycle.

if (dataFile) {
    Serial.println("File 'data.txt' opened successfully.");

    // Write data to the file

Subsequently we find a part that remains “listening” on the Serial Monitor to understand whether the user has typed the “S” character or the “s” character (as mentioned above). If so, the setDateTimeFromSerial() function is called:

if (Serial.available() > 0) {
    char command = Serial.read();
    if (command == 'S' || command == 's') {

delay(1000); // Update the time every second

This function does nothing more than receive the date and time manually entered by the user via the Serial Monitor:

void setDateTimeFromSerial() {
  int year, month, day, hour, minute, second;
  Serial.println("Enter the year (es. 2023): ");
  while (!Serial.available());
  year = Serial.parseInt();

  Serial.println("Enter the month (1-12): ");
  while (!Serial.available());
  month = Serial.parseInt();

  Serial.println("Enter the day (1-31): ");
  while (!Serial.available());
  day = Serial.parseInt();

  Serial.println("Enter the hour(0-23): ");
  while (!Serial.available());
  hour = Serial.parseInt();

  Serial.println("Enter the minutes (0-59): ");
  while (!Serial.available());
  minute = Serial.parseInt();

  Serial.println("Enter the seconds(0-59): ");
  while (!Serial.available());
  second = Serial.parseInt();

  rtc.adjust(DateTime(year, month, day, hour, minute, second));
  Serial.println("Time and date updated successfully!");

Once the user has entered all the data, update the clock with the command:

rtc.adjust(DateTime(year, month, day, hour, minute, second));

Now compile and upload the sketch. If everything works well the device should start taking measurements and writing every measureDelay ms to the file. In the sketch it is set to 2000 (i.e. 2 seconds). Adjust the gas sensor trimmer to impose the desired threshold.

To check the produced file, first turn off the device, extract the micro SD card and insert it into your PC (perhaps using an adapter). Once you open the file you should see something like this:


As already mentioned, the file is in CSV format. This is because a Comma-Separated Values ​​(CSV) file is a type of text file used to represent tabular data, such as tables or spreadsheets, in which columns of data are separated by commas or other delimiters. Here is a description of a CSV file:

  1. Pure text: a CSV file is a pure text file that contains data in tabular format. It can be opened and read with any text editor.
  2. Rows and columns: data is organized into rows and columns. Each row represents a record or instance of data, while the columns represent fields or variables in the data.
  3. Delimiters: data in columns is separated by delimiters, usually commas (,), semicolons (;), or tabs (\t). The delimiter you choose determines the type of CSV file (CSV with comma, CSV with semicolon, CSV with tab, etc.).
  4. Headers: often, the first line of a CSV file contains the column headers, i.e. the field names. These headings are useful for understanding the meaning of the data in the columns.
  5. Data: the next rows contain the actual data. Each row represents a record and each field (column) contains a specific value.
  6. Quotes: in some cases, values ​​in a column may be enclosed in double quotes (“) to properly handle values ​​that contain the delimiter itself or special characters.

The CSV file can easily be opened as a spreadsheet with programs such as Excel or LibreOffice Calc in order to make calculations, statistics or graphs on the collected data.

Scroll to Top