Skip to content

Instantly share code, notes, and snippets.

@Electronza
Created December 14, 2018 13:02
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/7a4a733445c748cc291bcea76718c006 to your computer and use it in GitHub Desktop.
Save Electronza/7a4a733445c748cc291bcea76718c006 to your computer and use it in GitHub Desktop.
Air quality monitoring station
#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);
}
// 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