
Hardware | |||
---|---|---|---|
1 | Wemos D1 mini (ESP8266) | $ 3.5 | |
1 | OLED Shield for WeMos D1 | $ 4.1 | |
1 | GY-MCU680V1 BME680 Sensor | $ 9.7 |
In this video we will introduce how to make a very simple air quality meter in a room. For this purpose, we use the GY-MCU680V1 sensor containing the BME680 chip from Bosch Sensortec. BME680 is a metal oxide-based sensor that detects VOCs by adsorption (and subsequent oxidation/reduction) on its sensitive layer. Thus, BME680 reacts to most volatile compounds polluting indoor air (one exception is for instance CO2). In addition to measuring the VOC content, the sensor can also measure temperature, humidity and air pressure. We use ThingSpeak interface (optional) to record the measured values and a simple OLED shield to display them. Today, the sensor can be purchased at Chinese stores at a relatively friendly price (see links below for current price).
Libraries:https://github.com/mcauser/Adafruit_SSD1306/tree/esp8266-64x48
https://github.com/plerup/espsoftwareserial/
ThingSpeak data:
https://thingspeak.com/channels/661295
//GY_MCU680 air quality sensor | |
#include <SPI.h> | |
#include <Wire.h> | |
#include <Adafruit_GFX.h> | |
#include <Adafruit_SSD1306.h> | |
#define OLED_RESET 0 // GPIO0 | |
Adafruit_SSD1306 display(OLED_RESET); | |
#include <SoftwareSerial.h> | |
#include <ESP8266WiFi.h> | |
const char* ssid = "YOUR_SSID"; | |
const char* password = "YOUR_PASSWORD"; | |
const char* server = "api.thingspeak.com"; | |
const char* api_key = "YOUR_API_KEY"; | |
// Optional - you can define air pressure correction for your altitude | |
#define ALT_CORRECTION 3144 | |
SoftwareSerial mySerial(D6, D5); | |
uint16_t temp1=0; | |
int16_t temp2=0; | |
unsigned char Re_buf[30],counter=0; | |
unsigned char sign=0; | |
int led = 13; | |
WiFiClient client; | |
unsigned long prev = millis(); | |
// You need to use modified Adafruit library with support of small displays | |
// see here: https://github.com/mcauser/Adafruit_SSD1306/tree/esp8266-64x48 | |
#if (SSD1306_LCDHEIGHT != 48) | |
#error("Height incorrect, please fix Adafruit_SSD1306.h!"); | |
#endif | |
void setup() | |
{ | |
Serial.begin(9600); | |
display.begin(SSD1306_SWITCHCAPVCC, 0x3C); | |
display.clearDisplay(); | |
delay(1000); | |
//--------------------------------- | |
Serial.println("Connecting to wifi..."); | |
WiFi.begin(ssid, password); | |
while (WiFi.status() != WL_CONNECTED){ | |
// Blink LED when connecting to wifi | |
pinMode(LED_BUILTIN, OUTPUT); | |
digitalWrite(LED_BUILTIN, LOW); | |
delay(150); | |
digitalWrite(LED_BUILTIN, HIGH); | |
delay(150); | |
} | |
Serial.println("WiFi connected"); | |
//--------------------------------- | |
pinMode(D2, INPUT); | |
pinMode(D1, OUTPUT); | |
mySerial.begin(9600); | |
mySerial.listen(); | |
delay(5000); | |
mySerial.write(0XA5); | |
mySerial.write(0X55); | |
mySerial.write(0X3F); | |
mySerial.write(0X39); | |
delay(100); | |
mySerial.write(0XA5); | |
mySerial.write(0X56); | |
mySerial.write(0X02); | |
mySerial.write(0XFD); | |
Serial.println("Setup complete"); | |
delay(100); | |
} | |
void loop(){ | |
float Temperature ,Humidity; | |
unsigned char i=0,sum=0; | |
uint32_t Gas; | |
uint32_t Pressure; | |
uint16_t IAQ; | |
int16_t Altitude; | |
uint8_t IAQ_accuracy; | |
//Serial.println("Checking serial..."); | |
while (mySerial.available()) { | |
//Serial.println("mySerial available"); | |
Re_buf[counter]=(unsigned char)mySerial.read(); | |
if(counter==0&&Re_buf[0]!=0x5A) { | |
Serial.println("Nothing received"); | |
} | |
if(counter==1&&Re_buf[1]!=0x5A) | |
{ | |
counter=0; | |
return; | |
}; | |
counter++; | |
if(counter==20) | |
{ | |
counter=0; | |
sign=1; | |
} | |
} | |
if(sign) | |
{ | |
sign=0; | |
if(Re_buf[0]==0x5A&&Re_buf[1]==0x5A ) | |
{ | |
for(i=0;i<19;i++) | |
sum+=Re_buf[i]; | |
if(sum==Re_buf[i] ) | |
{ | |
temp2=(Re_buf[4]<<8|Re_buf[5]); | |
Temperature=(float)temp2/100; | |
temp1=(Re_buf[6]<<8|Re_buf[7]); | |
Humidity=(float)temp1/100; | |
Pressure=((uint32_t)Re_buf[8]<<16)|((uint16_t)Re_buf[9]<<8)|Re_buf[10]; | |
float altPressure=(float(Pressure)+ALT_CORRECTION)/100; | |
IAQ_accuracy= (Re_buf[11]&0xf0)>>4; | |
IAQ=((Re_buf[11]&0x0F)<<8)|Re_buf[12]; | |
Gas=((uint32_t)Re_buf[13]<<24)|((uint32_t)Re_buf[14]<<16)|((uint16_t)Re_buf[15]<<8)|Re_buf[16]; | |
Altitude=(Re_buf[17]<<8)|Re_buf[18]; | |
Serial.print("Temperature:"); | |
Serial.print(Temperature); | |
Serial.print(" ,Humidity:"); | |
Serial.print(Humidity); | |
Serial.print(" ,Pressure:"); | |
Serial.print(Pressure); | |
Serial.print("|"); | |
Serial.print(altPressure); | |
Serial.print(" ,IAQ:"); | |
Serial.print(IAQ); | |
Serial.print(" ,Gas:"); | |
Serial.print(Gas); | |
Serial.print(" ,Altitude:"); | |
Serial.print(Altitude); | |
Serial.print(" ,IAQ_accuracy:"); | |
Serial.println(IAQ_accuracy); | |
// Display result on OLED display | |
dislayResult(IAQ); | |
if(millis() - prev > 60000){ | |
postData(Temperature,Humidity,altPressure,IAQ,Gas,IAQ_accuracy); | |
prev = millis(); | |
} | |
} | |
delay(1000); | |
} | |
} | |
} | |
void postData(float temperature, float humidity, float altPressure, int iaq, int gas, int iaq_accuracy){ | |
digitalWrite(LED_BUILTIN, LOW); | |
// Send data to ThingSpeak | |
if (client.connect(server,80)) { | |
Serial.println("Connect to ThingSpeak - OK"); | |
String dataToThingSpeak = ""; | |
dataToThingSpeak+="GET /update?api_key="; | |
dataToThingSpeak+=api_key; | |
dataToThingSpeak+="&field1="; | |
dataToThingSpeak+=String(temperature); | |
dataToThingSpeak+="&field2="; | |
dataToThingSpeak+=String(humidity); | |
dataToThingSpeak+="&field3="; | |
dataToThingSpeak+=String(altPressure); | |
dataToThingSpeak+="&field4="; | |
dataToThingSpeak+=String(iaq); | |
dataToThingSpeak+="&field5="; | |
dataToThingSpeak+=String(gas); | |
dataToThingSpeak+="&field6="; | |
dataToThingSpeak+=String(iaq_accuracy); | |
dataToThingSpeak+=" HTTP/1.1\r\nHost: a.c.d\r\nConnection: close\r\n\r\n"; | |
dataToThingSpeak+=""; | |
client.print(dataToThingSpeak); | |
int timeout = millis() + 5000; | |
while (client.available() == 0) { | |
if (timeout - millis() < 0) { | |
Serial.println("Error: Client Timeout!"); | |
client.stop(); | |
return; | |
} | |
} | |
} | |
while(client.available()){ | |
String line = client.readStringUntil('\r'); | |
//Serial.print(line); | |
} | |
digitalWrite(LED_BUILTIN, HIGH); | |
} | |
void dislayResult(int iaq){ | |
String disp; | |
if(iaq <= 50) disp="GOOD"; | |
else if(iaq > 50 && iaq <= 100) disp = "AVER"; | |
else if(iaq > 100 && iaq <= 150) disp = "L.BAD"; | |
else if(iaq > 150 && iaq <= 200) disp = "BAD"; | |
else if(iaq > 200 && iaq <= 300) disp = "WORSE"; | |
else disp = "V.BAD"; | |
display.setTextColor(WHITE); | |
display.drawLine(0, 24, 64, 24, WHITE); | |
display.setCursor(3+(5-disp.length())*8,5); | |
display.setTextSize(2); | |
display.fillRect(0, 0, 64, 23, BLACK); | |
display.println(disp); | |
display.setCursor(8,30); | |
display.setTextSize(1); | |
display.fillRect(0, 25, 64, 20, BLACK); | |
display.print("IAQ: "); | |
display.print(iaq); | |
display.display(); | |
} |