You are on page 1of 6

/*

Calculate IAQ indices scaled 0-100% (100% is excellent) and 0-500 where 0 is
excellent and 500 Hazardous.
*/
/****************************************************************************
This is a library for the BME680 gas, humidity, temperature & pressure sensor
Designed specifically to work with the Adafruit BME680 Breakout
----> http://www.adafruit.com/products/XXXX
These sensors use I2C or SPI to communicate, 2 or 4 pins are required
to interface.
***************************************************************************/

/* Version 6 of the Volante Puro software.


This version takes the readings from the BME680, the resulting ambience score, and
the SCD30, and serial prints them, writes to the OLED, and writes them to the SD
card, including a date and time from the RTC.
Version 6 adds wifi connection to WEP network
*/

#include <SD.h>
#include <SPI.h>
#include <Wire.h>
#include <Adafruit_Sensor.h>
#include "Adafruit_BME680.h" //Standard PIN for BME680 is 0x76, so this redirects
to there.
#include "RTClib.h"
#include "SparkFun_SCD30_Arduino_Library.h" //Click here to get the library:
http://librarymanager/All#SparkFun_SCD30
#include "rgb_lcd.h"
#include <SeeedOLED.h>
#include <WiFiNINA.h>
#include "secrets.h"
#include <Seeed_HM330X.h>

#define SEALEVELPRESSURE_HPA (1013.25)

Adafruit_BME680 bme; // I2C

float hum_weighting = 0.25; // so hum effect is 25% of the total air quality score
float gas_weighting = 0.75; // so gas effect is 75% of the total air quality score

int humidity_score, gas_score;


float gas_reference = 2500;
float hum_reference = 40;
int getgasreference_count = 0;
int gas_lower_limit = 10000; // Bad air quality limit
int gas_upper_limit = 300000; // Good air quality limit

// int chipSelect=15; //Normally this is 4, but using 15 as 4 is used for I2C.


Doesn't seem to work so pin selected further down
File AmbienceData; //Variable for working with our file object

HM330X sensor;
uint8_t buf[30];
const char *str[] = {"sensor num: ", "PM1.0 concentration(CF=1,Standard particulate
matter,unit:ug/m3): ",
"PM2.5 concentration(CF=1,Standard particulate
matter,unit:ug/m3): ",
"PM10 concentration(CF=1,Standard particulate
matter,unit:ug/m3): ",
"PM1.0 concentration(Atmospheric environment,unit:ug/m3): ",
"PM2.5 concentration(Atmospheric environment,unit:ug/m3): ",
"PM10 concentration(Atmospheric environment,unit:ug/m3): ",
};

RTC_DS1307 rtc;

char daysOfTheWeek[7][12] = {"Sunday", "Monday", "Tuesday", "Wednesday",


"Thursday", "Friday", "Saturday"};

SCD30 airSensor; //start SCD30

rgb_lcd lcd; //start LCD

const int colorR = 255;


const int colorG = 0;
const int colorB = 0;

char ssid[] = SECRET_SSID; // your network SSID (name)


char pass[] = SECRET_PASS; // your network password (use for WPA, or use as
key for WEP)
int keyIndex = 0; // your network key Index number
int status = WL_IDLE_STATUS; // the Wifi radio's status

void setup() {
Serial.begin(9600); // start the serial communication at 9600 baud
lcd.begin(16, 2); // set up the LCD's number of columns and rows
lcd.setRGB(colorR, colorG, colorB);
lcd.print("Volante Puro v.4");
Serial.println(F("BME680 test"));
Wire.begin();
if (!bme.begin()) {
Serial.println("Could not find a valid BME680 sensor, check wiring!");
while (1);
} else Serial.println("Found a sensor");

// Set up oversampling and filter initialization


bme.setTemperatureOversampling(BME680_OS_8X);
bme.setHumidityOversampling(BME680_OS_2X);
bme.setPressureOversampling(BME680_OS_4X);
bme.setIIRFilterSize(BME680_FILTER_SIZE_3);

Serial.print(now.month(), DEC);
Serial.print('/');
Serial.print(now.day(), DEC);
Serial.print(' ');
Serial.print(now.hour(), DEC);
Serial.print(':');
Serial.print(now.minute(), DEC);
Serial.print(':');
Serial.print(now.second(), DEC);
Serial.println();

Serial.println("Sensor Readings:");
Serial.println(" Temperature = " + String(bme.readTemperature(), 2) +
"°C");
Serial.println(" Pressure = " + String(bme.readPressure() / 100.0F) + "
hPa");
Serial.println(" Humidity = " + String(bme.readHumidity(), 1) +
"%");
Serial.println(" Gas = " + String(gas_reference) + "
ohms\n");
Serial.print("Qualitative Air Quality Index");

humidity_score = GetHumidityScore();
gas_score = GetGasScore();

//Combine results for the final IAQ index value (0-100% where 100% is good
quality air)
float air_quality_score = humidity_score + gas_score;
Serial.println(" comprised of " + String(humidity_score) + "% Humidity and " +
String(gas_score) + "% Gas");
if ((getgasreference_count++) % 5 == 0) GetGasReference();
Serial.println(CalculateIAQ(air_quality_score));

if (sensor.read_sensor_value(buf, 29)) {
Serial.println("HM330X read result failed!!!");
}
parse_result_value(buf);
parse_result(buf);
print_result(*str, buf);
Serial.println("");
delay(2000);

if (airSensor.dataAvailable())
{
Serial.print("SCD30 Data - CO2(ppm):");
Serial.print(airSensor.getCO2());

Serial.print(" Temp(C):");
Serial.print(airSensor.getTemperature(), 1);

Serial.print(" Humidity(%):");
Serial.print(airSensor.getHumidity(), 1);

Serial.println();
}
else
Serial.println("Waiting for new data");

AmbienceData= SD.open("AmbLog.csv", FILE_WRITE); //Open the SD card data file


or append to it
if (AmbienceData) //Only do these next things if the file opens correctly {

//Write the date and time to the card


AmbienceData.print(now.year(), DEC);
AmbienceData.print('/');
AmbienceData.print(now.month(), DEC);
AmbienceData.print('/');
AmbienceData.print(now.day(), DEC);
AmbienceData.print(' ');
AmbienceData.print(now.hour(), DEC);
AmbienceData.print(':');
AmbienceData.print(now.minute(), DEC);
AmbienceData.print(':');
AmbienceData.print(now.second(), DEC);
AmbienceData.print(","); //Write comma to SD card
AmbienceData.print(bme.readTemperature(), 2); //Write temperature data to SD
card
AmbienceData.print(","); //Write comma to SD card
AmbienceData.print(bme.readPressure() / 100.0F); //Write pressure data to SD
card
AmbienceData.print(","); //Write comma to SD card
AmbienceData.print(bme.readHumidity(), 1); //Write humidity data to SD card
AmbienceData.print(","); //Write comma to SD card
AmbienceData.print(gas_reference); //Write gas data to SD card
AmbienceData.print(","); //Write comma to SD card
AmbienceData.print(CalculateIAQ(air_quality_score)); //Write air quality score
to SD card
AmbienceData.print(","); //Write comma to SD card
AmbienceData.print(airSensor.getCO2()); //Write SCD30 CO2 PPM data to SD card
AmbienceData.print(","); //Write comma to SD card
AmbienceData.print(airSensor.getTemperature(), 1); //Write SCD30 temperature
data to SD card
AmbienceData.print(","); //Write comma to SD card
AmbienceData.println(airSensor.getHumidity(), 1); //Write SCD30 humidity data
to SD card
AmbienceData.close(); //Close the file
Serial.println("SD Card written correctly");

Serial.println("--------------------------------------------------------------");

delay(10000); //run this code every 10

// set the cursor to column 0, line 1


// (note: line 1 is the second row, since counting begins with 0):
lcd.setCursor(0, 1);
lcd.print(bme.readTemperature(), 1); // print the BME temperature to the LCD to
1 decimal point
lcd.setCursor(5, 1);
lcd.print(bme.readHumidity(), 0); // print the BME humidity to the LCD, with no
decimal point
lcd.setCursor(8, 1);
lcd.print(airSensor.getCO2()); // print the SCD30 PPM to the LCD
lcd.setCursor(12, 1);
lcd.print(airSensor.getTemperature(), 1); // print the SCD30 temp to the LCD
//lcd.setCursor(8, 1);
//lcd.print(airSensor.getHumidity(), 0); // print the SCD30 humidity to the LCD

SeeedOled.init(); //initialize SEEED OLED display


SeeedOled.clearDisplay(); // clear the screen and set start
position to top left corner
SeeedOled.deactivateScroll(); // deactivate scroll (might be
activated by previous test case)
SeeedOled.setNormalDisplay(); // non-inverted display
SeeedOled.setPageMode(); // Page mode to start with
SeeedOled.setTextXY(1,0); // 0 Page, 0th Column
SeeedOled.putString("Volante Puro v.5");
SeeedOled.setTextXY(2,0);
//SeeedOled.putString("BME680");
SeeedOled.setTextXY(3,0);
SeeedOled.putFloat(bme.readTemperature(), 1);
SeeedOled.putString("C ");
SeeedOled.putNumber(bme.readHumidity());
SeeedOled.putString("% ");
SeeedOled.setTextXY(4,0);
//SeeedOled.putString("SCD30");
SeeedOled.setTextXY(5,0);
SeeedOled.putFloat(airSensor.getTemperature(), 1);
SeeedOled.putString("C ");
SeeedOled.putNumber(airSensor.getHumidity());
SeeedOled.putString("% ");
SeeedOled.setTextXY(6,0);
SeeedOled.putNumber(airSensor.getCO2());
SeeedOled.putString("ppm ");

delay(2000); //run this code every 10


}

void GetGasReference() {
// Now run the sensor for a burn-in period, then use combination of relative
humidity and gas resistance to estimate indoor air quality as a percentage.
Serial.println("Getting a new gas reference value");
int readings = 10;
for (int i = 1; i <= readings; i++) { // read gas for 10 x 0.150mS = 1.5secs
gas_reference += bme.readGas();
}
gas_reference = gas_reference / readings;
Serial.println("Gas Reference = "+String(gas_reference,3));
}

String CalculateIAQ(int score) {


String IAQ_text = "air quality is ";
score = (100 - score) * 5;
if (score >= 301) IAQ_text += "Hazardous";
else if (score >= 201 && score <= 300 ) IAQ_text += "Very Unhealthy";
else if (score >= 176 && score <= 200 ) IAQ_text += "Unhealthy";
else if (score >= 151 && score <= 175 ) IAQ_text += "Unhealthy for Sensitive
Groups";
else if (score >= 51 && score <= 150 ) IAQ_text += "Moderate";
else if (score >= 00 && score <= 50 ) IAQ_text += "Good";
Serial.print("IAQ Score = " + String(score) + ", ");
return IAQ_text;
}

int GetHumidityScore() { //Calculate humidity contribution to IAQ index


float current_humidity = bme.readHumidity();
if (current_humidity >= 38 && current_humidity <= 42) // Humidity +/-5% around
optimum
humidity_score = 0.25 * 100;
else
{ // Humidity is sub-optimal
if (current_humidity < 38)
humidity_score = 0.25 / hum_reference * current_humidity * 100;
else
{
humidity_score = ((-0.25 / (100 - hum_reference) * current_humidity) +
0.416666) * 100;
}
}
return humidity_score;
}
int GetGasScore() {
//Calculate gas contribution to IAQ index
gas_score = (0.75 / (gas_upper_limit - gas_lower_limit) * gas_reference -
(gas_lower_limit * (0.75 / (gas_upper_limit - gas_lower_limit)))) * 100.00;
if (gas_score > 75) gas_score = 75; // Sometimes gas readings can go outside of
expected scale maximum
if (gas_score < 0) gas_score = 0; // Sometimes gas readings can go outside of
expected scale minimum
return gas_score;
}

HM330XErrorCode print_result(const char* str, uint16_t value) {


if (NULL == str) {
return ERROR_PARAM;
}
Serial.print(str);
Serial.println(value);
return NO_ERROR;
}

/*parse buf with 29 uint8_t-data*/


HM330XErrorCode parse_result(uint8_t* data) {
uint16_t value = 0;
if (NULL == data) {
return ERROR_PARAM;
}
for (int i = 1; i < 8; i++) {
value = (uint16_t) data[i * 2] << 8 | data[i * 2 + 1];
print_result(str[i - 1], value);

return NO_ERROR;
}

HM330XErrorCode parse_result_value(uint8_t* data) {


if (NULL == data) {
return ERROR_PARAM;
}
for (int i = 0; i < 28; i++) {
Serial.print(data[i], HEX);
Serial.print(" ");
if ((0 == (i) % 5) || (0 == i)) {
Serial.println("");
}
}
uint8_t sum = 0;
for (int i = 0; i < 28; i++) {
sum += data[i];
}
if (sum != data[28]) {
Serial.println("wrong checkSum!!!!");
}
Serial.println("");
return NO_ERROR;
}

You might also like