Overview
In this simple tutorial you will:
- Use the Arduino IDE to program an ESP32
- Interface temperature, humidity and eCO2 modules to the ESP32
- Output data on both a physical display, and an IOT cloud service with integrated dashboard functionality
Components
Microcontroller
The ESP32 runs at 240MHz stock, is dirt cheap, and has both WiFi and Bluetooth.
TVOC/eCO2 Sensor
The CCS811 is a hot plate based gas sensor which detects Volatile Organic Compounds (VOCs) levels and provides an equivalent carbon dioxide estimate (eCO2) over I2C.
eCO2 you say! Why, doesn’t that mean we could use this thing to detect CO2 levels outdoors as well?
Alas, the eCO2 reading is an estimate derived from the proven correlation between CO2 levels and VOC levels indoors. The CCS811 does not directly measure CO2 and as a result, wouldn’t be usable as part of say – a green house gas sensor array.
The datasheet for this sensor recommends that you apply a “burn in” period, by running it for at least 48 hours before use, which should add some semblance of stability to variations in sensitivity levels.
You’ll also want to let the sensor run for at least 20 minutes on boot before trusting the readings. It takes a while for the MOX to heat up.
Temperature and Humidity Sensor
The SHT30-D is one of the faster temperature and humidity sensors out there, outpacing dinosaurs like the hobbyist favorite – the DHT22, in terms of both accuracy and response time, for roughly the same cost.
Display
The SSD1306 is a popular driver IC which is typically used to control the wide array of low cost OLED modules that are now available. OLEDs typically have lower power consumption relative to TFT LCD displays.
MQTT
Message Queuing Telemetry Transport or MQTT is a popular, lightweight messaging protocol. It’s based on a publish-subscribe model, called so because clients can either publish or subscribe to the server, or “broker” using the protocol’s terminology.
Data is organized according to topics. Clients can then publish data to a topic or multiple topics, and subscribe to several topics at the same time. The simplicity of MQTT has made it incredibly popular for IOT applications.
Wiring
Power
The CCS811, SHT30-D and SSD1306 modules are all 3.3V or 5V compatible, so directly connecting the 3.3V output pin on the ESP32 dev board to the VCC input of each module should pose no issue.
Signals
The SDA and SCL pins on the ESP32, which are pins 21 and 22 respectively, are connected to the SDA and SCL pins of the CCS811, SHT30-D and SSD1306 modules. They all use I2C to communicate, so they will share a common bus for their data and clock signals. The WAKE signal on the CCS811 module must be pulled down to ground in order to establish communications.
Libraries
Here are the libraries you would want to have installed to get these devices up and running quickly.
- https://github.com/adafruit/Adafruit_MQTT_Library
- https://github.com/adafruit/Adafruit_CCS811
- https://github.com/adafruit/Adafruit_SHT31
- https://github.com/ThingPulse/esp8266-oled-ssd1306
Go to Sketch->Include Library->Manage Libraries to find these libraries.
Software Setup
To use the Arduino IDE to program an ESP32, you’re going to need to add the ESP32 to the board manager of the Arduino IDE. You can accomplish this by:
- Opening the preferences window in the Arduino IDE.
- Entering https://dl.espressif.com/dl/package_esp32_index.json into the Additional Board Manager URLs field.
- Open Boards Manager from Tools > Board menu and install esp32 platform
Additionally you’ll need to change the WiFi and Adafruit IO credentials in the code to make it work.
- WLAN_SSID
- WLAN_PASS
- AIO_USERNAME
- AIO_KEY
If you want to use a different cloud service, you’ll also have to change these – consult the service provider website to determine the values you’ll use:
- AIO_SERVER
- AIO_SERVERPORT
Code
#include "WiFi.h"
#include "Adafruit_MQTT.h"
#include "Adafruit_MQTT_Client.h"
#include "Adafruit_CCS811.h"
#include "Adafruit_SHT31.h"
#include "SSD1306Wire.h"
/************************* WiFi Access Point *********************************/
#define WLAN_SSID "YOUR_WIFI_SSID"
#define WLAN_PASS "YOUR_WIFI_PASSWORD"
/************************* Adafruit.io Setup *********************************/
#define AIO_SERVER "io.adafruit.com"
#define AIO_SERVERPORT 1883 // use 8883 for SSL
#define AIO_USERNAME "INSERT_YOUR_USERNAME_HERE"
#define AIO_KEY "INSERT_YOUR_KEY_HERE"
/************************* Setup *********************************/
WiFiClient client;
Adafruit_MQTT_Client mqtt(&client, AIO_SERVER, AIO_SERVERPORT, AIO_USERNAME, AIO_KEY);
/****************************** Feeds ***************************************/
Adafruit_MQTT_Publish eCO2_feed = Adafruit_MQTT_Publish(&mqtt, AIO_USERNAME "/feeds/eco2");
Adafruit_MQTT_Publish TVOC_feed = Adafruit_MQTT_Publish(&mqtt, AIO_USERNAME "/feeds/tvoc");
Adafruit_MQTT_Publish temp_feed = Adafruit_MQTT_Publish(&mqtt, AIO_USERNAME "/feeds/temp");
Adafruit_CCS811 ccs;
Adafruit_SHT31 sht31 = Adafruit_SHT31();
SSD1306Wire display(0x3c, 21, 22);
void MQTT_connect();
void setup() {
Serial.begin(9600);
WiFi.begin(WLAN_SSID, WLAN_PASS);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("CCS811 test");
if(!ccs.begin()){
Serial.println("Couldn't find SHT31");
while(1);
}
Serial.println("SHT31 test");
if (! sht31.begin(0x44)) { // Set to 0x45 for alternate i2c addr
Serial.println("Couldn't find SHT31");
while (1) delay(1);
}
display.init();
//calibrate temperature sensor
while(!ccs.available());
float temp = sht31.readTemperature();
ccs.setTempOffset(temp - 25.0);
}
int counter = 1;
void loop() {
MQTT_connect();
if(ccs.available()){
display.clear();
float temp = sht31.readTemperature();
if(!ccs.readData()){
Serial.print("eCO2: ");
float eCO2 = ccs.geteCO2();
Serial.print(eCO2);
display.drawString(0,0,"eCO2: " + String(eCO2) + "ppm");
display.display();
if(counter%5 == 0) //limit data usage
eCO2_feed.publish(eCO2);
Serial.print("ppm, TVOC: ");
float TVOC = ccs.getTVOC();
Serial.print(TVOC);
display.drawString(0,12,"TVOC: " + String(TVOC) + "ppb");
display.display();
if(counter%6 == 0) //limit data usage
TVOC_feed.publish(TVOC);
Serial.print("ppb Temp:");
Serial.println(temp);
display.drawString(0,24,"Temp: " + String(temp));
display.display();
if(counter%7 == 0) //limit data usage
temp_feed.publish(temp);
}
else{
Serial.println("ERROR!");
while(1);
}
}
counter++;
if(counter > 11)
counter = 1;
delay(1000);
}
void MQTT_connect() {
int8_t ret;
// Stop if already connected.
if (mqtt.connected()) {
return;
}
Serial.print("Connecting to MQTT... ");
uint8_t retries = 3;
while ((ret = mqtt.connect()) != 0) { // connect will return 0 for connected
Serial.println(mqtt.connectErrorString(ret));
Serial.println("Retrying MQTT connection in 5 seconds...");
mqtt.disconnect();
delay(5000); // wait 5 seconds
retries--;
if (retries == 0) {
// basically die and wait for WDT to reset me
while (1);
}
}
Serial.println("MQTT Connected!");
}
Cloud Service
You’ll want to sign up for an Adafruit IO account to follow this tutorial. A free account provides up to 30 data points per minute for 10 feeds and 5 dashboards. The code included in this tutorial limits the data point output rate to allow even free accounts to work well.
Feeds/Topics
After signing up for a free account, you should create at least 3 feeds (what Adafruit chose to call their topics), one for each sensor module. In our code, we specified three feeds:
- eco2
- tvoc
- temp
You can create a feed by hitting feeds > actions > create a new feed > create
Dashboards
Now that you have setup some feeds, you’ll want a dashboard to visualize that data.
You can do this by going to dashboards > actions > create a new dashboard > create
This will create an empty dashboard – now add some blocks by entering your dashboard, and clicking on create a new block
Choose any appropriate block type to display the data you will be receiving – I’d personally recommend a line chart in this situation.
Pick the feed you want this chart to visualize. Set labels as well as maximum and minimum values for your newly created chart, or leave them as default (you can modify this to your satisfaction later).
You’ll end up with a dashboard that looks like this.
That’s all folks!
The logical next step, for the more adventurous, might be to step off the perfboard and run the ICs these modules use on a PCB you would design!
Good luck, and stay creative!