Skip to content

Instantly share code, notes, and snippets.

@Electronza
Last active December 10, 2019 10:58
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Electronza/246519b96242983ad3a011b4e05dadaf to your computer and use it in GitHub Desktop.
Save Electronza/246519b96242983ad3a011b4e05dadaf to your computer and use it in GitHub Desktop.
Flip & click code for the diy air quality monitoring station
/*******************************************************************
____ __ ____ ___ ____ ____ __ __ _ ____ __
( __)( ) ( __)/ __)(_ _)( _ \ / \ ( ( \(__ ) / _\
) _) / (_/\ ) _)( (__ )( ) /( O )/ / / _/ / \
(____)\____/(____)\___) (__) (__\_) \__/ \_)__)(____)\_/\_/
Project name: DIY Air Quality Monitoring Station
Project page: https://electronza.com/diy-air-quality-monitoring-station/
Description : Air quality station with Arduino Due & ESP8266
--- Flip & Click code ---
********************************************************************/
// BME280 / Weather click
#include <SPI.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_BME280.h>
// *****************************************************************
// This section is for the H2S sensor
// *****************************************************************
// Serial number of my sensor
// #define mysensor_serial_no 012017030207
// #define SERIAL1_RX_BUFFER_SIZE 256
// #define SERIAL1_TX_BUFFER_SIZE 256
// Sensor values
// The format of the output is: SN[XXXXXXXXXXXX], PPB [0 : 999999], TEMP [-99:99],
// RH[0:99], RawSensor[ADCCount], TempDigital, RHDigital, Day[0:99], Hour [0:23]
// Note that on Arduino Due integer variable (int) stores a 32-bit (4-byte) value.
// This yields a range of -2,147,483,648 to 2,147,483,647
// (minimum value of -2^31 and a maximum value of (2^31) - 1).
// On 8 bit boards some readings have to be recorded as floats
String SensorSerialNo;
// Take care: On the Arduino Due and SAMD based boards (like MKR1000 and Zero),
// an int stores a 32-bit (4-byte) value. This yields a range of -2,147,483,648 to 2,147,483,647
// (minimum value of -2^31 and a maximum value of (2^31) - 1).
int H2S;
int Temperature;
int RH;
int RawSensor;
int TempDigital;
int RHDigital;
int Days;
int Hours;
int Minutes;
int Seconds;
#define command_delay 500
#define start_delay 2500
String dataString = "";
String responseString = "";
boolean dataStringComplete = 0;
char inChar;
// *****************************************************************
// This section is for the Ozone sensor
// *****************************************************************
#define Ozone_CS 87
unsigned int RAW_ozone;
// *****************************************************************
// This section initializes the BME280 / Weather click
// *****************************************************************
#define BME_CS 77
#define SEALEVELPRESSURE_HPA (1013.25)
Adafruit_BME280 bme(BME_CS); // hardware SPI
float bme280_temperature;
float bme280_humidity;
float bme280_pressure;
// *****************************************************************
// Debugging on serial output
// *****************************************************************
#define is_debugging 0
// *****************************************************************
// SETUP
// *****************************************************************
void setup() {
// *****************************************************************
// This section initializes LEDs
// *****************************************************************
pinMode(37, OUTPUT);
pinMode(38, OUTPUT);
pinMode(39, OUTPUT);
pinMode(40, OUTPUT);
// ***************************************************************
// This section initializes SPI
// ***************************************************************
SPI.begin();
// ***************************************************************
// This section is for the H2S sensor
// ***************************************************************
Serial1.begin(9600);
// Normally, data is returned within one second
Serial1.setTimeout(1500);
// reserve 80 bytes for the dataString
dataString.reserve(80);
responseString.reserve(150);
// Wait for sensor
digitalWrite(37, HIGH);
delay(1000);
flush_serial1();
digitalWrite(37, LOW);
// ***************************************************************
// This section initializes the BME280 / Weather click
// ***************************************************************
bool status;
// default settings
// (you can also pass in a Wire library object like &Wire2)
status = bme.begin();
// *****************************************************************
// This section initializes the MCP3201 / Ozone click
// *****************************************************************
/* Resetting MCP3201
* From MCP3201 datasheet: If the device was powered up
* with the CS pin low, it must be brought high and back low
* to initiate communication.
* The device will begin to sample the analog
* input on the first rising edge after CS goes low. */
pinMode (Ozone_CS, OUTPUT);
digitalWrite(Ozone_CS, 1);
delay(100);
digitalWrite(Ozone_CS, 0);
// *****************************************************************
// Settings for ESP8266
// *****************************************************************
Serial3.begin(9600);
// Normally, data is returned within one second
Serial3.setTimeout(1500);
// Reset the ESP8266 module
// everytime the Flip & Click is reset
pinMode(35, OUTPUT);
// bring pin down
digitalWrite(35, LOW);
delay(500);
// bring pin up
digitalWrite(35, HIGH);
// wait 30 seconds for ESP8266 to connect
delay(30000);
// ESP8266 sends some garbage when starting out
flush_serial3();
if (is_debugging){
Serial.begin(9600);
Serial.println("Air quality monitoring station");
// EEPROM dump
SPEC_dump_EEPROM();
// Do a H2S readout
SPEC_Data_read();
SPEC_parse_data();
SPEC_print_data();
if (!status) {
Serial.println("Could not find a valid BME280 sensor, check wiring!");
while (1);
}
Serial.println("-- BME280 ready --");
Serial.println("-- Ozone click ready --");
Serial.println(" ");
Serial.println ("");
Serial.println ("Initialization complete");
Serial.println ("");
}
}
void loop() {
// Do a H2S readout
SPEC_Data_read();
SPEC_parse_data();
// Read RAW ozone value
RAW_ozone = get_ADC();
// Read BME280 data
bme280_temperature = bme.readTemperature();
bme280_humidity = bme.readHumidity();
bme280_pressure = bme.readPressure();
// Upload data to ESP8266
upload_data();
if (is_debugging){
SPEC_print_data();
Serial.println ("");
Serial.print ("RAW Ozone: ");
Serial.println (RAW_ozone);
Serial.print ("Temperature: ");
Serial.println (bme280_temperature);
Serial.print ("Humidity: ");
Serial.println (bme280_humidity);
Serial.print ("Pressure: ");
Serial.println (bme280_pressure / 100);
Serial.println ("");
}
tenminutedelay();
}
// ********************************************************************************
// FUNCTIONS FOR THE H2S SENSOR
// ********************************************************************************
/* ********************************************************************************
* This function triggers one measurement and receives the data from the sensor
**********************************************************************************/
void SPEC_Data_read(){
// First, we do some initialization
// dataStringComplete is set as "false", as we don't have any valid data received
dataStringComplete = 0;
// Clear the data string
dataString = "";
// Now we trigger a measurement
Serial1.print(" ");
// We wait for the sensor to respond
dataString = Serial1.readStringUntil('\n');
if (is_debugging){
Serial.println(dataString);
}
}
/* ********************************************************************************
* This function takes the received string and upodates sensor data
**********************************************************************************/
void SPEC_parse_data(){
// Parses the received dataString
// Data string is comma separated
// The format of the output is: SN[XXXXXXXXXXXX], PPB [0 : 999999], TEMP [-99:99],
// RH[0:99], RawSensor[ADCCount], TempDigital, RHDigital, Day[0:99], Hour [0:23],
// Minute[0:59], Second[0 : 59]\r\n
// Take a look also at
// https://stackoverflow.com/questions/11068450/arduino-c-language-parsing-string-with-delimiter-input-through-serial-interfa
// We look first for the SN
int idx1 = dataString.indexOf(',');
SensorSerialNo = dataString.substring(0, idx1);
int idx2 = dataString.indexOf(',', idx1 + 1);
// Hint: after comma there's a space - it should be ignored
String S_gas = dataString.substring(idx1 + 2, idx2);
H2S = S_gas.toInt();
int idx3 = dataString.indexOf(',', idx2 + 1);
String S_temp = dataString.substring(idx2 + 2, idx3);
Temperature = S_temp.toInt();
int idx4 = dataString.indexOf(',', idx3 + 1);
String S_humi = dataString.substring(idx3 + 2, idx4);
RH = S_humi.toInt();
int idx5 = dataString.indexOf(',', idx4 + 1);
String S_raw_gas = dataString.substring(idx4 + 2, idx5);
RawSensor = S_raw_gas.toInt();
int idx6 = dataString.indexOf(',', idx5 + 1);
String S_Tdigital = dataString.substring(idx5 + 2, idx6);
TempDigital = S_Tdigital.toInt();
int idx7 = dataString.indexOf(',', idx6 + 1);
String S_RHdigital = dataString.substring(idx6 + 2, idx7);
RHDigital = S_RHdigital.toInt();
int idx8 = dataString.indexOf(',', idx7 + 1);
String S_Days = dataString.substring(idx7 + 2, idx8);
Days = S_Days.toInt();
int idx9 = dataString.indexOf(',', idx8 + 1);
String S_Hours = dataString.substring(idx8 + 2, idx9);
Hours = S_Hours.toInt();
int idx10 = dataString.indexOf(',', idx9 + 1);
String S_Minutes = dataString.substring(idx9 + 2, idx10);
Minutes = S_Minutes.toInt();
int idx11 = dataString.indexOf('\r');
String S_Seconds = dataString.substring(idx10 + 2, idx11);
Seconds = S_Seconds.toInt();
}
/* ********************************************************************************
* This function prints the sensor data
**********************************************************************************/
void SPEC_print_data(){
Serial.println("********************************************************************");
Serial.print ("Sensor Serial No. is ");
Serial.println (SensorSerialNo);
Serial.print ("H2S level is ");
Serial.print (H2S);
Serial.println (" ppb");
Serial.print ("Temperature is ");
Serial.print (Temperature, DEC);
Serial.println (" deg C");
Serial.print ("Humidity is ");
Serial.print (RH, DEC);
Serial.println ("% RH");
Serial.print ("Sensor is online since: ");
Serial.print (Days, DEC);
Serial.print (" days, ");
Serial.print (Hours, DEC);
Serial.print (" hours, ");
Serial.print (Minutes, DEC);
Serial.print (" minutes, ");
Serial.print (Seconds, DEC);
Serial.println (" seconds");
Serial.println ("Raw Sensor Data");
Serial.print ("Raw gas level: ");
Serial.println (RawSensor);
Serial.print ("Temperature digital: ");
Serial.println (TempDigital);
Serial.print ("Humidity digital: ");
Serial.println (RHDigital);
Serial.println ("");
}
/* ********************************************************************************
* EEPROM dump
**********************************************************************************/
void SPEC_dump_EEPROM(){
// First we trigger a measurement
Serial1.print(" ");
// Within one second time we send the command "e"
delay(400);
Serial1.print("e");
dataString = Serial1.readStringUntil('\n');
// You can uncomment this line if you wish
//Serial.println(dataString);
for (int i=0; i<20; i++){
responseString = Serial1.readStringUntil('\n');
Serial.println(responseString);
}
}
void flush_serial1(){
// Do we have data in the serial buffer?
// If so, flush it
if (Serial1.available() > 0){
Serial.println ("Flushing serial buffer...");
while(1){
inChar = (char)Serial1.read();
delay(10);
Serial.print(inChar);
if (inChar == '\n') break;
}
Serial.println (" ");
Serial.println ("Buffer flushed!");
}
}
void flush_serial3(){
// Do we have data in the serial buffer?
// If so, flush it
if (Serial3.available() > 0){
//Serial.println ("Flushing serial buffer...");
while(1){
inChar = (char)Serial3.read();
delay(10);
//Serial.print(inChar);
if (inChar == '\n') break;
}
//Serial.println (" ");
//Serial.println ("Buffer flushed!");
}
}
// ********************************************************************************
// FUNCTIONS FOR THE OZONE SENSOR
// ********************************************************************************
unsigned int get_ADC(void){
/*
DAC works on SPI
We receive 16 bits
Of which we extract only 12 bits
MCP3201 has a strange way of formatting data
with 5 bits in the first byte and
the rest of 7 bits in the second byte
*/
unsigned int result;
unsigned int first_byte;
unsigned int second_byte;
SPI.beginTransaction(SPISettings(1000000, MSBFIRST, SPI_MODE1));
digitalWrite(Ozone_CS, 0);
first_byte = SPI.transfer(0);
second_byte = SPI.transfer(0);
digitalWrite(Ozone_CS, 1);
SPI.endTransaction();
/* After the second eight clocks have been
sent to the device, the MCU receive register
will contain the lowest order seven bits and
the B1 bit repeated as the A/D Converter has begun
to shift out LSB first data with the extra clock.
Typical procedure would then call for the lower order
byte of data to be shifted right by one bit
to remove the extra B1 bit.
See MCP3201 datasheet, page 15
*/
result = ((first_byte & 0x1F) << 8) | second_byte;
result = result >> 1;
return result;
}
// ********************************************************************************
// Upload data to ESP8266
// ********************************************************************************
void upload_data(void ){
Serial3.print((int)(bme280_temperature*100));
Serial3.print(",");
Serial3.print((int)(bme280_humidity*100));
Serial3.print(",");
Serial3.print((int)(bme280_pressure));
Serial3.print(",");
Serial3.print(H2S);
Serial3.print(",");
Serial3.print(RawSensor);
Serial3.print(",");
Serial3.println(RAW_ozone);
}
void tenminutedelay(void){
for(int count = 0; count < 600 ; count++)
{
delay(900);
digitalWrite(40, HIGH);
delay(100);
digitalWrite(40, LOW);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment