This week we have built our environmental sensor and monitor that will allow us to measure the quality of our natural ventilation habits. We have built the device, we have created a first interface to monitor the data in real time. We have connected our device to the arduino cloud. And we have created a dashboard to remotely view the data collected by our sensor. We have used a sensor combo that gives us data about humidity, temperature, TVOCs and equivalent CO2 (or eCO2), barometric pressure, CO2 (or eCO2) in the parts per million (PPM) and total volatile organic compounds in the parts per billion (PPB). We have also included the temperature reading provided by the LSM6DDS3 IMU hosted on the Arduino NANO 33 IoT to have environmental data in the window.
Tracking System for Classroom Ventilation Routines
A STEM project for classrooms
the VenTTracker project - VenTTracker #11 - Wireless Environmental Monitor on Arduino Nano 33 IoT
We have already built our second device based on the Arduino Nano 33 IoT boardArduino Nano 33 IoT board. The sensor and environmental monitor that will allow us to take measurements of volumes of CO2 in air, temperature, humidity and pressure to be able to measure the efficiency of natural ventilation habits in classrooms.
This is one of the versions of the user interface that we are testing:
The device was held upright with a slight incline to facilitate reading.
The graphic display shows:
- the actual time retrieved by NTP using the WIFI connection.
- environmental sensor combo data:
- CO2 concentration in ppm
- a color scale with four levels of CO2 from normal (green) to adverse (red)
- the temperature in degrees Celsius or Fahrenheit
- and the percentage of relative humidity
Here our family of devices: Wireless Window Sensor & Wireless Environmental Sensor & Monitor:
The assembly
Visualizing Data
While we continue working with the final version of the user interface, we have designed a simple screen to verify that everything works by showing data in real time on the screen and connecting to the Arduino IoT cloud.
Dashboard
Device overview
We have built the device inside a plastic box. On the front we have placed a 1.8 "color LCD display and 128x160 pixel resolution.
At the top we have placed a couple of sensors in a combo, the SparkFun Environmental Combo Breakout - CCS811 / BME280.
The CCS811 provides readings for equivalent CO2 (or eCO2) in the parts per million (PPM) and total volatile organic compounds in the parts per billion (PPB).
The BME280 provides humidity, temperature and barometric pressure. The CCS811 must be adjusted with humidity and temperature measurements.
The assembly is stacked and is screwed to the front cover of the box.
The Arduino Nano 33 IoT fits into two female headers on the back of our board stack.
The SparkFun Environmental Combo Breakout - CCS811 / BME280 connects to the main board via a 90 degree header pin.
The final assembly of the combo is done with the box closed.
The device can be powered by the USB port that is accessible from the outside of the box or by a 9V battery that fits inside the box.
The box has three slots, one to allow the connection of the combo of environmental sensors another to allow the external connection to the usb port and a third to allow the exchange of heat with the outside.
The front and the environmental sensor combo are assembled at 90 degrees to allow the sensors to be in contact with the outside.
The 1.8 "SPI TFT LCD Screen Module, 9 pins interface.
The LCD drive ic is ST7735S. It's a 128 X 160 (resolution).
The display is screwed to the cover and connected to the main board by header pins and a screw to secure the board.
The environmental sensor combo sits on the top outside of the box.
The transparent box allows us to visualize the placement of the 9-volt battery.
We can also power our device through the usb port. In the image powered by a power bank. In the image our labyrinth puzzle created to study the use of the accelerometer to estimate the speed and position of the windows that we developed in the past blog VenTTracker #04 - Playing with the IMU
Materials
Schematic
Headers back view.
Punchboard
Display1.8 TFT SPI 128x160
It is a 1.8 "SPI TFT LCD Screen Module, 9pins interface. Not just a LCD break but include SD card (2GB),
The LCD drive ic is ST7735S. Resolution: 128 X 160
The display interface is SPI, it just needs 5 wires (CS, RS, SCL, SDA, RST) for controlling.
SDcard use hardware SPI interface (CS / MOSI / MISO / SCK)
Environmental Combo Breakout
SparkFun Environmental Combo Breakout - CCS811/BME280 (Qwiic) - SEN-14348 - SparkFun Electronics
The CCS811 provides readings for equivalent CO2 (or eCO2) in the parts per million (PPM) and total volatile organic compounds in the parts per billion (PPB).
The CCS811 also has a feature that allows it to fine-tune its readings if it has access to the current humidity and temperature.
The BME280 provides humidity, temperature and barometric pressure.
This allows the sensors to work together to give us more accurate readings.
- humidity,
- temperature,
- TVOCs and equivalent CO2 (or eCO2)
- barometric pressure,
- CO2 (or eCO2) in the parts per million (PPM)
- total volatile organic compounds in the parts per billion (PPB)
Features
- Qwiic-Connector Enabled
- Operation Voltage: 3.3V
- Total Volatile Organic Compound (TVOC) sensing from 0 to 1,187 parts per billion
- eCO2 sensing from 400 to 8,192 parts per million
- Temp Range: -40C to 85C
- Humidity Range: 0--100% RH, =-3% from 20--80%
- Pressure Range: 30,000Pa to 110,000Pa, relative accuracy of 12Pa, absolute accuracy of 100Pa
- Altitude Range: 0 to 30,000 feet (9.2 km), relative accuracy of 3.3 feet (1m) at sea level, 6.6 (2m) at 30,000 feet
Arduino Libraries
sparkfun/Qwiic_BME280_CCS811_Combo (github.com)
CCS811
BME280
The I2C address of the Environmental Combo Breakout is 0x77 / 0x5B and is jumper selectable to 0x76 / 0x5A.
Checking the environmental combo
References:
https://github.com/sparkfun/CCS811_Air_Quality_Breakout
https://github.com/sparkfun/SparkFun_CCS811_Arduino_Library
Testing the environmental combo
Arduino IoT Cloud
Creating a new device and a new thing
The code
/* This is an example for testing the VenTTracker Environmental Sensor connected to Arduino IoT Cloud. Displays data in a ST7735 LCD Display Uses BME280 /CCS811 Combo Cloud Variables: int cO2_Concentration_ppm; float pressure_bar; CloudRelativeHumidity relative_humidity; CloudTemperatureSensor temperature_combo_celsius; CloudTemperatureSensor temperature_imu_celsius; Author: Enrique Albertos Date: 2021-05-09 */ #include "thingProperties.h" #include <SPI.h> #include <Wire.h> #include <SparkFunBME280.h> //Click here to get the library: http://librarymanager/All#SparkFun_BME280 #include <SparkFunCCS811.h> //Click here to get the library: http://librarymanager/All#SparkFun_CCS811 #include <SparkFunLSM6DS3.h> #include <Adafruit_GFX.h> #include <Adafruit_ST7735.h> // Hardware-specific library for ST7735 #include "DSEG14_Classic_Regular_20.h" #include "DSEG14_Classic_Regular_32.h" #define CCS811_ADDR 0x5B //Default I2C Address //#define CCS811_ADDR 0x5A //Alternate I2C Address #define PIN_NOT_WAKE 5 #define SCREEN_WIDTH 128 // OLED display width, in pixels #define SCREEN_HEIGHT 160 // OLED display height, in pixels #define TFT_CS 10 #define TFT_RST -1 // Or set to -1 and connect to Arduino RESET pin #define TFT_DC 8 Adafruit_ST7735 display(TFT_CS, TFT_DC, TFT_RST); LSM6DS3 IMU(I2C_MODE, 0x6A); //Global sensor objects CCS811 myCCS811(CCS811_ADDR); BME280 myBME280; bool envEnabled = false; #define READINGS_INTERVAL 5000 unsigned long lastReadingAt; void setup() { Serial.begin(9600); // This delay gives the chance to wait for a Serial Monitor without blocking if none is found delay(5000); initProperties(); // Connect to Arduino IoT Cloud ArduinoCloud.begin(ArduinoIoTPreferredConnection); ArduinoCloud.addCallback(ArduinoIoTCloudEvent::CONNECT, onIoTConnect); ArduinoCloud.addCallback(ArduinoIoTCloudEvent::DISCONNECT, onIoTDisconnect); ArduinoCloud.addCallback(ArduinoIoTCloudEvent::SYNC, onIoTSync); setDebugMessageLevel(2); delay(3000); ArduinoCloud.printDebugInfo(); lastReadingAt = millis(); } //--------------------------------------------------------------- void loop() { unsigned long msNow = millis(); ArduinoCloud.update(); //Check to see if data is available if (envEnabled) { if ( (msNow - lastReadingAt > READINGS_INTERVAL) && myCCS811.dataAvailable()) { //Calling this function updates the global tVOC and eCO2 variables myCCS811.readAlgorithmResults(); //printInfoSerial fetches the values of tVOC and eCO2 //printInfoSerial(); displayInfo(); infoToCloud(); float BMEtempC = myBME280.readTempC(); float BMEhumid = myBME280.readFloatHumidity(); Serial.print("Applying new values (deg C, %): "); Serial.print(BMEtempC); Serial.print(","); Serial.println(BMEhumid); Serial.println(); //This sends the temperature data to the CCS811 myCCS811.setEnvironmentalData(BMEhumid, BMEtempC); lastReadingAt = msNow; } else if (myCCS811.checkForStatusError()) { //If the CCS811 found an internal error, print it. printSensorError(); CCS811Core::CCS811_Status_e returnCode = myCCS811.beginWithStatus(); Serial.print("CCS811 begin exited with: "); Serial.println(myCCS811.statusString(returnCode)); } } } void setupIMU() { IMU.begin(); } void setupEnvironmentalCombo() { Serial.println("Apply BME280 data to CCS811 for compensation."); Wire.begin(); display.initR(INITR_BLACKTAB); // Init ST7735S chip, black tab //This begins the CCS811 sensor and prints error status of .beginWithStatus() CCS811Core::CCS811_Status_e returnCode = myCCS811.beginWithStatus(); Serial.print("CCS811 begin exited with: "); Serial.println(myCCS811.statusString(returnCode)); //For I2C, enable the following and disable the SPI section myBME280.settings.commInterface = I2C_MODE; myBME280.settings.I2CAddress = 0x77; //Initialize BME280 //For I2C, enable the following and disable the SPI section myBME280.settings.commInterface = I2C_MODE; myBME280.settings.I2CAddress = 0x77; myBME280.settings.runMode = 3; //Normal mode myBME280.settings.tStandby = 0; myBME280.settings.filter = 4; myBME280.settings.tempOverSample = 5; myBME280.settings.pressOverSample = 5; myBME280.settings.humidOverSample = 5; //Calling .begin() causes the settings to be loaded delay(10); //Make sure sensor had enough time to turn on. BME280 requires 2ms to start up. myBME280.begin(); } //--------------------------------------------------------------- void printInfoSerial() { //getCO2() gets the previously read data from the library Serial.println("CCS811 data:"); Serial.print(" CO2 concentration : "); Serial.print(myCCS811.getCO2()); Serial.println(" ppm"); //getTVOC() gets the previously read data from the library Serial.print(" TVOC concentration : "); Serial.print(myCCS811.getTVOC()); Serial.println(" ppb"); Serial.println("BME280 data:"); Serial.print(" Temperature: "); Serial.print(myBME280.readTempC(), 2); Serial.println(" degrees C"); Serial.print(" Temperature: "); Serial.print(myBME280.readTempF(), 2); Serial.println(" degrees F"); Serial.print(" Pressure: "); Serial.print(myBME280.readFloatPressure()*100, 2); Serial.println(" hPa"); Serial.print(" Pressure: "); Serial.print((myBME280.readFloatPressure() * 0.0002953), 2); Serial.println(" InHg"); Serial.print(" Altitude: "); Serial.print(myBME280.readFloatAltitudeMeters(), 2); Serial.println("m"); Serial.print(" Altitude: "); Serial.print(myBME280.readFloatAltitudeFeet(), 2); Serial.println("ft"); Serial.print(" %RH: "); Serial.print(myBME280.readFloatHumidity(), 2); Serial.println(" %"); Serial.println(); } void infoToCloud() { cO2_Concentration_ppm = myCCS811.getCO2(); temperature_combo_celsius =((int)(myBME280.readTempC() *10) )/ 10.0; pressure_bar =(int) (myBME280.readFloatPressure()/100); //hpa relative_humidity = myBME280.readFloatHumidity(); temperature_imu_celsius = ((int)(IMU.readTempC()*10)) / 10.0; } void displayInfo() { clearDisplay(); display.setTextColor(ST7735_BLACK); display.setCursor(0, 50 ); display.setFont(&DSEG7_Classic_Regular_32); display.setTextSize(0); display.print(myCCS811.getCO2()); display.setTextSize(0); display.setFont(NULL); display.print(" ppm"); display.setCursor(0, 85 ); display.setFont(&DSEG14_Classic_Regular_20); display.print(myBME280.readTempC(), 1); display.setFont(NULL); display.print(" C"); display.setCursor(0, 115 ); display.setFont(&DSEG14_Classic_Regular_20); display.print(myBME280.readFloatPressure()/100.0, 0); display.setFont(NULL); display.print(" HPa"); display.setCursor(0, 145 ); display.setFont(&DSEG14_Classic_Regular_20); display.print(myBME280.readFloatHumidity(), 1); display.setFont(NULL); display.print(" %"); } //printSensorError gets, clears, then prints the errors //saved within the error register. void printSensorError() { uint8_t error = myCCS811.getErrorRegister(); if (error == 0xFF) //comm error { Serial.println("Failed to get ERROR_ID register."); } else { Serial.print("Error: "); if (error & 1 << 5) Serial.print("HeaterSupply"); if (error & 1 << 4) Serial.print("HeaterFault"); if (error & 1 << 3) Serial.print("MaxResistance"); if (error & 1 << 2) Serial.print("MeasModeInvalid"); if (error & 1 << 1) Serial.print("ReadRegInvalid"); if (error & 1 << 0) Serial.print("MsgInvalid"); Serial.println(); } } uint16_t getBackgroundColor(){ return ST7735_WHITE; } void clearDisplay() { display.fillScreen(getBackgroundColor()); } void onIoTConnect() { // enable your other i2c devices Serial.println(F(">>> connected to Arduino IoT Cloud")); Serial.println(F("enabling other i2c devices")); // run the setup code (begin or else) for your other i2c devices display.initR(INITR_BLACKTAB); // Init ST7735S chip, black tab clearDisplay(); Serial.println(F("ST77xx TFT Begin")); setupIMU(); Serial.println(F("IMU Begin")); setupEnvironmentalCombo(); Serial.println(F("I2C Environmental COMBO Begin")); } void onIoTDisconnect() { // disable your other i2c devices Serial.println(F(">>> disconnected to Arduino IoT Cloud")); Serial.println(F("disabling other i2c devices")); // if necessary call the end() method on your i2c device library or even get rid of them. // really it's on a peripheral basis envEnabled = false; } void onIoTSync() { Serial.println(">>> Board and Cloud SYNC OK"); }
Next Steps
- Develop a new UI for the Environmental Monitor.
- Train a Machine learning model that allows us to detect anomalies in the movement of the window to warn of the need for window maintenance.
- Train a Machine learning model that allows us to classify natural ventilation quality based in the environmental data from the environmental combo and the IMU (Temp and vibration)
<< Previous VenTTracker Blog | Next VenTTracker Blog >> |
---|---|
VenTTracker #10 - Ventilation Monitor on Arduino IoT Cloud | VenTTracker #12 - Window Anomaly Detection. Edge Impulse & Arduino Nano 33 IoT |
Top Comments