PART 1 | How To Connect ESP-WROOM-32, MQ135 and ThingsBoard

0 13094 Medium

In this article, we will show how to use ESP-WROOM-32 to get real-time data from air quality sensor and display it on ThingsBoard dashboard

projectImage

Things used in this project

 

Hardware components

HARDWARE LIST
1 DFRobot FireBeetle ESP32 IOT Microcontroller (Supports Wi-Fi & Bluetooth)
1 Seeed Grove - Gas Sensor(MQ2) We were used MQ-135
1 Breadboard (generic)
1 Jumper wires (generic)

软件应用程序和在线服务

 

Arduino IDE

 

ThingsBoard

 

ESP32 Board Package and the Serial Port Driver

Story

 

Introduction

In this article, we will show how to use ESP-WROOM-32 to get real-time data from the air quality sensor and display it on the ThingsBoard dashboard. To send data to Thingsboard we will use ThingsBoard Arduino SDK.

 

Preparation

 

Arduino libraries:

 

-WiFi

 

-MQUnifiedsensor

 

-ThingsBoard Arduino SDK and dependencies:

 

-MQTT PubSub Client — for interacting with MQTT.

 

-ArduinoJSON — for dealing with JSON files.

 

-Arduino Http Client — for interacting with ThingsBoard using HTTP

 

Step 1

 

In the first step, we have to connect all required hardware.

projectImage

The connection scheme is:

projectImage

Step 2

 

In order to start programming, we have to download all required libraries. To do this click on Tools > Manage Libraries... and in the Search input alternately install the following libraries:

projectImage
projectImage
projectImage
projectImage
projectImage
projectImage

2.2 Connecting ESP-WROOM-32 to WiFi

 

Once the WiFi library is installed on the Arduino IDE, you can now add the header in code.

#include <WiFi.h>

Define SSID and password values for your WiFi point:

const char* ssid = "YOUR_NETWORK_SSID";
const char* password = "YOUR_PASSWORD";

Optionally, with setup() function we start a connection to WiFi and start up Serial port for logging. Serial Monitor will give us some debug information and sensor data.

void setup() 
{
    Serial.begin(9600);   
    if(Serial) Serial.println("Serial is open");  
    WiFi.begin(ssid, password);    
    Serial.print("Connecting");   
    while (WiFi.status() != WL_CONNECTED) {     
        delay(500);     
        Serial.print(".");   
    }   
    Serial.println();    
    Serial.print("Connected, IP address: ");   
    Serial.println(WiFi.localIP()); 
}

2.3 Collecting data from Air Quality sensor

 

To get data from the air quality sensor we use MQUnifiedsensor library. It is suitable for various MQ sensors.

So, we have to include the header on our sketch (below the WiFi library).

 

#include <MQUnifiedsensor.h>

According to the official documentation of the library, we should define setting variables in the global scope:

 

Please review the documentation if using another MQ sensors to get define keys.

#define placa "ESP-32"
#define Voltage_Resolution 3.3
#define pin 34
#define type "MQ-135"
#define ADC_Bit_Resolution 12
#define RatioMQ135CleanAir 3.6  
double CO2 = (0);  
MQUnifiedsensor MQ135(placa, Voltage_Resolution, ADC_Bit_Resolution, pin, type);

Now proceed with setup() function section:

void setup()  
{
    ...  
    //Set math model to calculate the PPM concentration and the value of constants   
    MQ135.setRegressionMethod(1); //_PPM =  a*ratio^b   
    MQ135.setA(110.47); 
    MQ135.setB(-2.862); 
    // Configurate the ecuation values to get NH4 concentration    
    MQ135.init();    
    Serial.print("Calibrating please wait.");   
    float calcR0 = 0;   
    for(int i = 1; i<=10; i ++)   {     
        MQ135.update(); // Update data, the arduino will be read the voltage on the analog pin     
        calcR0 += MQ135.calibrate(RatioMQ135CleanAir);    
        Serial.print(".");   
    }   
    MQ135.setR0(calcR0/10);   
    Serial.println("  done!.");      
    if(isinf(calcR0)) { Serial.println("Warning: Conection issue founded, R0 is infite (Open circuit detected) please check your wiring and supply"); while(1);}   
    if(calcR0 == 0){Serial.println("Warning: Conection issue founded, R0 is zero (Analog pin with short circuit to ground) please check your wiring and supply"); while(1);}   
    /*****************************  MQ CAlibration **************************/                   
    MQ135.serialDebug(false); 
}

CO2 value calculation requires setA and setB functions added. Set 110.47 coefficient for A value and -2.862 coefficient for B value.

 

NB: Coefficients are specific to the sensor model, meaning that MQ-135 sensor's index mismatches MQ-4 sensor's, for instance.

 

To accomplish setup of CO2 monitoring within loop() function we must map the key:

void loop() {
    MQ135.update(); // Update data, the arduino will be read the voltage on the analog pin   
    CO2 = MQ135.readSensor(); // Sensor will read CO2 concentration using the model and a and b values setted before or in the setup   
    Serial.print("CO2: ");   
    Serial.println(CO2);  
    delay(5000); 
}

Step 3

 

To send data to the ThingsBoard, we will use Arduino SDK that has all high-level functions for this.

 

3. Sending data into ThingsBoard Platform

 

To do this we have to define device token and ThingsBoard host:

#define TOKEN               "YOUR_TOKEN_TO_DEVICE" 
#define THINGSBOARD_SERVER  "thingsboard.cloud"  
// Initialize ThingsBoard client 
WiFiClient espClient; 
// Initialize ThingsBoard instance 
ThingsBoard tb(espClient); 
// the Wifi radio's status 
int status = WL_IDLE_STATUS;

3.1 Obtaining the token.

 

If you have ThingsBoard up and running and corresponding to use case device entity exists:

 

-Go to Device Groups of the device owner, select All.

 

-Click on the device row in the table to open device details

 

-Click "Copy access token". The Token will be copied to your clipboard.

 

1. If using a PaaS subscription, Log in or Sign Up to the thingsboard.cloud and perform previous steps.

 

2. If you do not have any device entity so far, Create a new device:

 

a. At the left navigation bar click on Device groups > All

 

b. At the right top menu click on the + button

 

c. Fill in all inputs in the form

 

projectImage

d. Click Add button

 

3. In the device list click on the created device

 

4. Click on Copy access token

projectImage

5. Paste token to the global variable TOKEN

 

To send our decimal value from the sensor to ThingsBoard we should use sendTelemetryFloat() function in the main loop:

void loop() {
    ...  
    if (!tb.connected()) {     
        // Connect to the ThingsBoard     
        Serial.print("Connecting to: ");     
        Serial.print(THINGSBOARD_SERVER);     
        Serial.print(" with token ");     
        Serial.println(TOKEN);     
        if (!tb.connect(THINGSBOARD_SERVER, TOKEN)) {       
            Serial.println("Failed to connect");      
            return;     
        }   
    }  
    ... 
}

The code above checks whether the client is connected to the ThingsBoard instance, and if not the client will try to reconnect to it.

 

Code Listing

 

You can find it in the attached files.

Result

projectImage
projectImage
projectImage

Was this article useful? Place a comment below!

Schematics

Schema

projectImage

Code

 

Arduino Code

C/C++

CODE
#include <WiFi.h>
#include <MQUnifiedsensor.h>
#include <ThingsBoard.h>

const char* ssid = "YOUR_SSID";           
const char* password = "YOUR_PASSWORD";

#define TOKEN               "YOUR_DEVICE_TOKEN"
#define THINGSBOARD_SERVER  "thingsboard.cloud"

// Initialize ThingsBoard client
WiFiClient espClient;
// Initialize ThingsBoard instance
ThingsBoard tb(espClient);
// the Wifi radio's status
int status = WL_IDLE_STATUS;

//Definitions
#define placa "ESP-32"
#define Voltage_Resolution 3.3
#define pin 34 //Analog input 0 of your arduino
#define type "MQ-135" //MQ135
#define ADC_Bit_Resolution 12 // For arduino UNO/MEGA/NANO
#define RatioMQ135CleanAir 3.6//RS / R0 = 3.6 ppm  

double          CO2          =   (0);

MQUnifiedsensor MQ135(placa, Voltage_Resolution, ADC_Bit_Resolution, pin, type);

void setup()
{
  Serial.begin(9600);
  if(Serial) Serial.println("Serial is open");

  WiFi.begin(ssid, password);

  Serial.print("Connecting");
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println();

  Serial.print("Connected, IP address: ");
  Serial.println(WiFi.localIP());

  //Set math model to calculate the PPM concentration and the value of constants
  MQ135.setRegressionMethod(1); //_PPM =  a*ratio^b
 
  MQ135.init(); 
  Serial.print("Calibrating please wait.");
  float calcR0 = 0;
  for(int i = 1; i<=10; i ++)
  {
    MQ135.update(); // Update data, the arduino will be read the voltage on the analog pin
    calcR0 += MQ135.calibrate(RatioMQ135CleanAir);
    Serial.print(".");
  }
  MQ135.setR0(calcR0/10);
  Serial.println("  done!.");
  
  if(isinf(calcR0)) {Serial.println("Warning: Conection issue founded, R0 is infite (Open circuit detected) please check your wiring and supply"); while(1);}
  if(calcR0 == 0){Serial.println("Warning: Conection issue founded, R0 is zero (Analog pin with short circuit to ground) please check your wiring and supply"); while(1);}
  /*****************************  MQ CAlibration ********************************************/ 
  MQ135.serialDebug(false);
}

void loop()
{
  if (!tb.connected()) {
    // Connect to the ThingsBoard
    Serial.print("Connecting to: ");
    Serial.print(THINGSBOARD_SERVER);
    Serial.print(" with token ");
    Serial.println(TOKEN);
    if (!tb.connect(THINGSBOARD_SERVER, TOKEN)) {
      Serial.println("Failed to connect");
      return;
    }
  }

  MQ135.update(); // Update data, the arduino will be read the voltage on the analog pin
 
  MQ135.setA(110.47); MQ135.setB(-2.862);
  CO2 = MQ135.readSensor();

  MQ135.setA(605.18); MQ135.setB(-3.937);
  float CO = MQ135.readSensor();

  MQ135.setA(77.255); MQ135.setB(-3.18);
  float Alcohol = MQ135.readSensor();

  MQ135.setA(44.947); MQ135.setB(-3.445);
  float Toluene = MQ135.readSensor();

  MQ135.setA(102.2 ); MQ135.setB(-2.473);
  float NH4 = MQ135.readSensor();

  MQ135.setA(34.668); MQ135.setB(-3.369);
  float Acetone = MQ135.readSensor();
  
  Serial.print("CO2: ");
  Serial.println(CO2);
  Serial.print("Light level: ");
  Serial.println(lux);
  Serial.println("Sending data...");

  tb.sendTelemetryFloat("CO2", CO2);  
  
  tb.sendTelemetryFloat("CO", CO);  
  
  tb.sendTelemetryFloat("Alcohol", Alcohol);  
  
  tb.sendTelemetryFloat("Toluene", Toluene);  
  
  tb.sendTelemetryFloat("NH4", NH4);  
  
  tb.sendTelemetryFloat("Acetone", Acetone);  

  tb.loop();
  
  delay(1000); //Sampling frequency
}

Dashboard JSON File

JSON

CODE
{
  "title": "Air Quality",
  "image": null,
  "mobileHide": false,
  "mobileOrder": null,
  "configuration": {
    "description": "",
    "widgets": {
      "11a80b8e-e962-de17-dc1c-cc1543aebf2c": {
        "isSystemType": true,
        "bundleAlias": "charts",
        "typeAlias": "basic_timeseries",
        "type": "timeseries",
        "title": "New widget",
        "image": null,
        "description": null,
        "sizeX": 8,
        "sizeY": 5,
        "config": {
          "datasources": [
            {
              "type": "entity",
              "name": null,
              "entityAliasId": "4bf9b60c-91d0-61e1-ec10-d1bdca48100d",
              "filterId": null,
              "dataKeys": [
                {
                  "name": "CO2",
                  "type": "timeseries",
                  "label": "CO2",
                  "color": "#2196f3",
                  "settings": {
                    "excludeFromStacking": false,
                    "hideDataByDefault": false,
                    "disableDataHiding": false,
                    "removeFromLegend": false,
                    "showLines": true,
                    "fillLines": false,
                    "showPoints": false,
                    "showPointShape": "circle",
                    "pointShapeFormatter": "var size = radius * Math.sqrt(Math.PI) / 2;\nctx.moveTo(x - size, y - size);\nctx.lineTo(x + size, y + size);\nctx.moveTo(x - size, y + size);\nctx.lineTo(x + size, y - size);",
                    "showPointsLineWidth": 5,
                    "showPointsRadius": 3,
                    "tooltipValueFormatter": "",
                    "showSeparateAxis": false,
                    "axisTitle": "",
                    "axisPosition": "left",
                    "axisTicksFormatter": "",
                    "thresholds": [
                      {
                        "thresholdValueSource": "predefinedValue"
                      }
                    ],
                    "comparisonSettings": {
                      "showValuesForComparison": true,
                      "comparisonValuesLabel": "",
                      "color": ""
                    }
                  },
                  "_hash": 0.019369294834221784
                },
                {
                  "name": "Alcohol",
                  "type": "timeseries",
                  "label": "Alcohol",
                  "color": "#f44336",
                  "settings": {
                    "excludeFromStacking": false,
                    "hideDataByDefault": false,
                    "disableDataHiding": false,
                    "removeFromLegend": false,
                    "showLines": true,
                    "fillLines": false,
                    "showPoints": false,
                    "showPointShape": "circle",
                    "pointShapeFormatter": "var size = radius * Math.sqrt(Math.PI) / 2;\nctx.moveTo(x - size, y - size);\nctx.lineTo(x + size, y + size);\nctx.moveTo(x - size, y + size);\nctx.lineTo(x + size, y - size);",
                    "showPointsLineWidth": 5,
                    "showPointsRadius": 3,
                    "tooltipValueFormatter": "",
                    "showSeparateAxis": false,
                    "axisTitle": "",
                    "axisPosition": "left",
                    "axisTicksFormatter": "",
                    "thresholds": [
                      {
                        "thresholdValueSource": "predefinedValue"
                      }
                    ],
                    "comparisonSettings": {
                      "showValuesForComparison": true,
                      "comparisonValuesLabel": "",
                      "color": ""
                    }
                  },
                  "_hash": 0.10773096272975002
                },
                {
                  "name": "CO",
                  "type": "timeseries",
                  "label": "CO",
                  "color": "#ffc107",
                  "settings": {
                    "excludeFromStacking": false,
                    "hideDataByDefault": false,
                    "disableDataHiding": false,
                    "removeFromLegend": false,
                    "showLines": true,
                    "fillLines": false,
                    "showPoints": false,
                    "showPointShape": "circle",
                    "pointShapeFormatter": "var size = radius * Math.sqrt(Math.PI) / 2;\nctx.moveTo(x - size, y - size);\nctx.lineTo(x + size, y + size);\nctx.moveTo(x - size, y + size);\nctx.lineTo(x + size, y - size);",
                    "showPointsLineWidth": 5,
                    "showPointsRadius": 3,
                    "tooltipValueFormatter": "",
                    "showSeparateAxis": false,
                    "axisTitle": "",
                    "axisPosition": "left",
                    "axisTicksFormatter": "",
                    "thresholds": [
                      {
                        "thresholdValueSource": "predefinedValue"
                      }
                    ],
                    "comparisonSettings": {
                      "showValuesForComparison": true,
                      "comparisonValuesLabel": "",
                      "color": ""
                    }
                  },
                  "_hash": 0.16983810931760046
                },
                {
                  "name": "NH4",
                  "type": "timeseries",
                  "label": "NH4",
                  "color": "#607d8b",
                  "settings": {
                    "excludeFromStacking": false,
                    "hideDataByDefault": false,
                    "disableDataHiding": false,
                    "removeFromLegend": false,
                    "showLines": true,
                    "fillLines": false,
                    "showPoints": false,
                    "showPointShape": "circle",
                    "pointShapeFormatter": "var size = radius * Math.sqrt(Math.PI) / 2;\nctx.moveTo(x - size, y - size);\nctx.lineTo(x + size, y + size);\nctx.moveTo(x - size, y + size);\nctx.lineTo(x + size, y - size);",
                    "showPointsLineWidth": 5,
                    "showPointsRadius": 3,
                    "tooltipValueFormatter": "",
                    "showSeparateAxis": false,
                    "axisTitle": "",
                    "axisPosition": "left",
                    "axisTicksFormatter": "",
                    "thresholds": [
                      {
                        "thresholdValueSource": "predefinedValue"
                      }
                    ],
                    "comparisonSettings": {
                      "showValuesForComparison": true,
                      "comparisonValuesLabel": "",
                      "color": ""
                    }
                  },
                  "_hash": 0.8795072817187648
                },
                {
                  "name": "Acetone",
                  "type": "timeseries",
                  "label": "Acetone",
                  "color": "#607d8b",
                  "settings": {
                    "excludeFromStacking": false,
                    "hideDataByDefault": false,
                    "disableDataHiding": false,
                    "removeFromLegend": false,
                    "showLines": true,
                    "fillLines": false,
                    "showPoints": false,
                    "showPointShape": "circle",
                    "pointShapeFormatter": "var size = radius * Math.sqrt(Math.PI) / 2;\nctx.moveTo(x - size, y - size);\nctx.lineTo(x + size, y + size);\nctx.moveTo(x - size, y + size);\nctx.lineTo(x + size, y - size);",
                    "showPointsLineWidth": 5,
                    "showPointsRadius": 3,
                    "tooltipValueFormatter": "",
                    "showSeparateAxis": false,
                    "axisTitle": "",
                    "axisPosition": "left",
                    "axisTicksFormatter": "",
                    "thresholds": [
                      {
                        "thresholdValueSource": "predefinedValue"
                      }
                    ],
                    "comparisonSettings": {
                      "showValuesForComparison": true,
                      "comparisonValuesLabel": "",
                      "color": ""
                    }
                  },
                  "_hash": 0.06822020169930765
                },
                {
                  "name": "Toluene",
                  "type": "timeseries",
                  "label": "Toluene",
                  "color": "#9c27b0",
                  "settings": {
                    "excludeFromStacking": false,
                    "hideDataByDefault": false,
                    "disableDataHiding": false,
                    "removeFromLegend": false,
                    "showLines": true,
                    "fillLines": false,
                    "showPoints": false,
                    "showPointShape": "circle",
                    "pointShapeFormatter": "var size = radius * Math.sqrt(Math.PI) / 2;\nctx.moveTo(x - size, y - size);\nctx.lineTo(x + size, y + size);\nctx.moveTo(x - size, y + size);\nctx.lineTo(x + size, y - size);",
                    "showPointsLineWidth": 5,
                    "showPointsRadius": 3,
                    "tooltipValueFormatter": "",
                    "showSeparateAxis": false,
                    "axisTitle": "",
                    "axisPosition": "left",
                    "axisTicksFormatter": "",
                    "thresholds": [
                      {
                        "thresholdValueSource": "predefinedValue"
                      }
                    ],
                    "comparisonSettings": {
                      "showValuesForComparison": true,
                      "comparisonValuesLabel": "",
                      "color": ""
                    }
                  },
                  "_hash": 0.8240666057673744
                }
              ]
            }
          ],
          "timewindow": {
            "realtime": {
              "timewindowMs": 60000
            }
          },
          "showTitle": true,
          "backgroundColor": "#fff",
          "color": "rgba(0, 0, 0, 0.87)",
          "padding": "8px",
          "settings": {
            "shadowSize": 4,
            "fontColor": "#545454",
            "fontSize": 10,
            "xaxis": {
              "showLabels": true,
              "color": "#545454"
            },
            "yaxis": {
              "showLabels": true,
              "color": "#545454",
              "tickDecimals": 2
            },
            "grid": {
              "color": "#545454",
              "tickColor": "#DDDDDD",
              "verticalLines": true,
              "horizontalLines": true,
              "outlineWidth": 1
            },
            "stack": false,
            "tooltipIndividual": false,
            "showTooltip": true,
            "timeForComparison": "previousInterval",
            "xaxisSecond": {
              "axisPosition": "top",
              "showLabels": true
            },
            "comparisonEnabled": false,
            "smoothLines": true,
            "tooltipCumulative": false
          },
          "title": "CO2",
          "dropShadow": true,
          "enableFullscreen": true,
          "titleStyle": {
            "fontSize": "16px",
            "fontWeight": 400
          },
          "useDashboardTimewindow": true,
          "displayTimewindow": true,
          "showTitleIcon": false,
          "iconColor": "rgba(0, 0, 0, 0.87)",
          "iconSize": "24px",
          "titleTooltip": "",
          "enableDataExport": true,
          "widgetStyle": {},
          "showLegend": true,
          "legendConfig": {
            "direction": "column",
            "position": "bottom",
            "sortDataKeys": false,
            "showMin": false,
            "showMax": false,
            "showAvg": true,
            "showTotal": false
          }
        },
        "row": 0,
        "col": 0,
        "id": "11a80b8e-e962-de17-dc1c-cc1543aebf2c"
      }
    },
    "states": {
      "default": {
        "name": "Air Quality",
        "root": true,
        "layouts": {
          "main": {
            "widgets": {
              "11a80b8e-e962-de17-dc1c-cc1543aebf2c": {
                "sizeX": 12,
                "sizeY": 6,
                "row": 0,
                "col": 0
              },
              "12d74d94-f73a-83d4-b708-6b4a4d7ad5f0": {
                "sizeX": 6,
                "sizeY": 6,
                "row": 0,
                "col": 12
              },
              "8b62ff45-cf96-5002-555a-a610115b545e": {
                "sizeX": 12,
                "sizeY": 5,
                "row": 6,
                "col": 0
              },
              "cd052af7-efa9-2e58-8197-0b406f3723e7": {
                "sizeX": 12,
                "sizeY": 3,
                "row": 6,
                "col": 12
              },
              "32311d7a-cfd7-23ea-a3f1-7687a79e7cb5": {
                "sizeX": 12,
                "sizeY": 2,
                "row": 9,
                "col": 12
              },
              "a03a3244-2944-55f8-35fe-df348de691ed": {
                "sizeX": 6,
                "sizeY": 6,
                "row": 0,
                "col": 18
              }
            },
            "gridSettings": {
              "backgroundColor": "#eeeeee",
              "columns": 24,
              "margin": 10,
              "backgroundSizeMode": "100%"
            }
          }
        }
      }
    },
    "entityAliases": {
      "4bf9b60c-91d0-61e1-ec10-d1bdca48100d": {
        "id": "4bf9b60c-91d0-61e1-ec10-d1bdca48100d",
        "alias": "Alias",
        "filter": {
          "type": "singleEntity",
          "resolveMultiple": false,
          "singleEntity": {
            "entityType": "DEVICE",
            "id": "d62db230-25b5-11ec-a9e6-556e8dbef35c"
          }
        }
      }
    },
    "filters": {},
    "timewindow": {
      "hideInterval": false,
      "hideAggregation": false,
      "hideAggInterval": false,
      "hideTimezone": false,
      "selectedTab": 0,
      "realtime": {
        "realtimeType": 0,
        "timewindowMs": 60000,
        "quickInterval": "CURRENT_DAY",
        "interval": 1000
      },
      "aggregation": {
        "type": "NONE",
        "limit": 25000
      }
    },
    "settings": {
      "stateControllerId": "entity",
      "showTitle": false,
      "showDashboardsSelect": true,
      "showEntitiesSelect": true,
      "showDashboardTimewindow": true,
      "showDashboardExport": true,
      "toolbarAlwaysOpen": true
    }
  },
  "name": "Air Quality"
}

The article was first published in hackster, October 22, 2021

cr: https://www.hackster.io/thingsboard/part-1-how-to-connect-esp-wroom-32-mq135-and-thingsboard-4a4bb4

author: Team ThingsBoard: Vitalik Bidochka, Andrew Shvayka, Ilya Barkov

License
All Rights
Reserved
licensBg
0