Created
December 14, 2018 13:02
-
-
Save Electronza/7a4a733445c748cc291bcea76718c006 to your computer and use it in GitHub Desktop.
Air quality monitoring station
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <ESP8266WiFi.h> | |
#include "ThingSpeak.h" | |
char ssid[] = "XXXX"; // your network SSID (name) | |
char pass[] = "XXXX"; // your network password | |
/* | |
***************************************************************************************** | |
**** Visit https://www.thingspeak.com to sign up for a free account and create | |
**** a channel. The video tutorial http://community.thingspeak.com/tutorials/thingspeak-channels/ | |
**** has more information. You need to change this to your channel, and your write API key | |
**** IF YOU SHARE YOUR CODE WITH OTHERS, MAKE SURE YOU REMOVE YOUR WRITE API KEY!! | |
*****************************************************************************************/ | |
unsigned long myChannelNumber = XXXXXX; | |
const char * myWriteAPIKey = "XXXXXX"; | |
String dataString = ""; | |
boolean dataStringComplete = 0; | |
char inChar; | |
float temperature; | |
float humidity; | |
float pressure; | |
int H2S; | |
int RAW_H2S; | |
int RAW_Ozone; | |
WiFiClient client; | |
void setup() { | |
// Configure serial port | |
Serial.begin(9600); | |
delay(10); | |
//Serial.println("Starting..."); | |
// reserve 80 bytes for the dataString | |
dataString.reserve(100); | |
flush_serial(); | |
//Serial.println(); | |
//Serial.print("Connecting to "); | |
//Serial.println(ssid); | |
/* Explicitly set the ESP8266 to be a WiFi-client, otherwise, it by default, | |
would try to act as both a client and an access-point and could cause | |
network-issues with your other WiFi-devices on your WiFi-network. */ | |
WiFi.mode(WIFI_STA); | |
WiFi.begin(ssid, pass); | |
while (WiFi.status() != WL_CONNECTED) { | |
delay(500); | |
//Serial.print("."); | |
} | |
//Serial.println(""); | |
//Serial.println("WiFi connected"); | |
//Serial.println("IP address: "); | |
//Serial.println(WiFi.localIP()); | |
ThingSpeak.begin(client); | |
} | |
void loop() { | |
// Read the input on each pin, convert the reading, and set each field to be sent to ThingSpeak. | |
// On Uno,Mega,Yun: 0 - 1023 maps to 0 - 5 volts | |
// On ESP8266: 0 - 1023 maps to 0 - 1 volts | |
// On MKR1000,Due: 0 - 4095 maps to 0 - 3.3 volts | |
//float pinVoltage = analogRead(A0) * (VOLTAGE_MAX / VOLTAGE_MAXCOUNTS); | |
// Read data from serial interface | |
// Wait for some data in the serial buffer | |
// send data only when you receive data: | |
while (Serial.available() < 1) { | |
delay(10); | |
yield(); | |
} | |
Serial_Data_Read(); | |
Parse_data(); | |
//Temperature | |
ThingSpeak.setField(1,temperature); | |
// Humidity | |
ThingSpeak.setField(2,humidity); | |
// Pressure | |
ThingSpeak.setField(3,pressure); | |
ThingSpeak.setField(4,H2S); | |
// RAW H2S | |
ThingSpeak.setField(5,RAW_H2S); | |
//RAW Ozone | |
ThingSpeak.setField(6,RAW_Ozone); | |
// Write the fields that you've set all at once. | |
ThingSpeak.writeFields(myChannelNumber, myWriteAPIKey); | |
//delay(20000); // ThingSpeak will only accept updates every 15 seconds. | |
} | |
void flush_serial(){ | |
// Do we have data in the serial buffer? | |
// If so, flush it | |
if (Serial.available() > 0){ | |
while(1){ | |
inChar = (char)Serial.read(); | |
delay(10); | |
if (inChar == '\n') break; | |
} | |
} | |
} | |
/* ******************************************************************************** | |
* This function triggers one measurement and receives the data from the sensor | |
**********************************************************************************/ | |
void Serial_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 = ""; | |
dataString = Serial.readStringUntil('\n'); | |
} | |
/* ******************************************************************************** | |
* This function takes the received string and upodates sensor data | |
**********************************************************************************/ | |
void Parse_data(){ | |
// Parses the received dataString | |
// Data string is comma separated | |
int idx1 = dataString.indexOf(','); | |
String S_temp = dataString.substring(0, idx1); | |
temperature = S_temp.toFloat() / 100; | |
int idx2 = dataString.indexOf(',', idx1 + 1); | |
String S_humi = dataString.substring(idx1 + 1, idx2); | |
humidity = S_humi.toFloat() / 100; | |
int idx3 = dataString.indexOf(',', idx2 + 1); | |
String S_pres = dataString.substring(idx2 + 1, idx3); | |
pressure = S_pres.toFloat() / 100; | |
int idx4 = dataString.indexOf(',', idx3 + 1); | |
String S_H2S = dataString.substring(idx3 + 1, idx4); | |
H2S = S_H2S.toInt(); | |
int idx5 = dataString.indexOf(',', idx4 + 1); | |
String S_H2Sraw = dataString.substring(idx4 + 1, idx5); | |
RAW_H2S = S_H2Sraw.toInt(); | |
int idx6 = dataString.indexOf('\r'); | |
String S_O3 = dataString.substring(idx5 + 1, idx6); | |
RAW_Ozone = S_O3.toInt(); | |
//Serial.println (temperature); | |
//Serial.println (humidity); | |
//Serial.println (pressure); | |
//Serial.println (H2S); | |
//Serial.println (RAW_H2S); | |
//Serial.println (RAW_Ozone); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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