I set up a device with various sensors that measure different air polluants and sends values through bluetooth to an android app. This is the code:
#include <math.h>
#include <Arduino.h>
#include "PMS.h"
#include "SparkFun_SGP30_Arduino_Library.h" // Click here to get the library: http://librarymanager/All#SparkFun_SGP30
#include <Wire.h>
SGP30 mySensor; //create an object of the SGP30 class
// Define the serial port for MH-Z19B sensor (Serial2 on Arduino Mega)
#define MHZ19B_SERIAL Serial2
// Define the serial port for Bluetooth (Serial3 on Arduino Mega)
#define BLUETOOTH_SERIAL Serial3
// Define the baud rate for MH-Z19B sensor
#define MHZ19B_BAUDRATE 9600
// Define the command to read CO2 concentration
uint8_t MHZ19B_CMD_READ_CO2[] = {0xFF, 0x01, 0x86, 0x00, 0x00, 0x00, 0x00, 0x00, 0x79};
PMS pms(Serial1);
PMS::DATA data;
enum channel {
CH_NH3, CH_RED, CH_OX
};
typedef enum channel channel_t;
// Enum for proper gas declaration
enum gas {
CO, NO2, NH3, C3H8, C4H10, CH4, H2, C2H5OH
};
typedef enum gas gas_t;
#define NH3PIN A1
#define COPIN A0
#define OXPIN A2
uint16_t NH3baseR;
uint16_t REDbaseR;
uint16_t OXbaseR;
/**
Requests the current resistance for a given channel
from the sensor. The value is an ADC value between
0 and 1024.
@param channel
The channel to read the base resistance from.
@return The unsigned 16-bit base resistance
of the selected channel.
*/
uint16_t getResistance(channel_t channel) {
unsigned long rs = 0;
int counter = 0;
switch (channel) {
case CH_NH3:
for(int i = 0; i < 100; i++) {
rs += analogRead(NH3PIN);
counter++;
delay(2);
}
return rs/counter;
case CH_RED:
for(int i = 0; i < 100; i++) {
rs += analogRead(COPIN);
counter++;
delay(2);
}
return rs/counter;
case CH_OX:
for(int i = 0; i < 100; i++) {
rs += analogRead(OXPIN);
counter++;
delay(2);
}
return rs/counter;
}
return 0;
}
void calibrateMICS() {
// Continuously measure the resistance,
// storing the last N measurements in a circular buffer.
// Calculate the floating average of the last seconds.
// If the current measurement is close to the average stop.
// Seconds to keep stable for successful calibration
// (Keeps smaller than 64 to prevent overflows)
uint8_t seconds = 10;
// Allowed delta for the average from the current value
uint8_t delta = 2;
// Circular buffer for the measurements
uint16_t bufferNH3[seconds];
uint16_t bufferRED[seconds];
uint16_t bufferOX[seconds];
// Pointers for the next element in the buffer
uint8_t pntrNH3 = 0;
uint8_t pntrRED = 0;
uint8_t pntrOX = 0;
// Current floating sum in the buffer
uint16_t fltSumNH3 = 0;
uint16_t fltSumRED = 0;
uint16_t fltSumOX = 0;
// Current measurements;
uint16_t curNH3;
uint16_t curRED;
uint16_t curOX;
// Flag to see if the channels are stable
bool NH3stable = false;
bool REDstable = false;
bool OXstable = false;
// Initialize buffer
for (int i = 0; i < seconds; ++i) {
bufferNH3[i] = 0;
bufferRED[i] = 0;
bufferOX[i] = 0;
}
do {
// Wait a second
delay(1000);
Serial.print(".");
// Read new resistances
unsigned long rs = 0;
delay(50);
for (int i = 0; i < 3; i++) {
delay(1);
rs += analogRead(NH3PIN);
}
curNH3 = rs/3;
rs = 0;
delay(50);
for (int i = 0; i < 3; i++) {
delay(1);
rs += analogRead(COPIN);
}
curRED = rs/3;
rs = 0;
delay(50);
for (int i = 0; i < 3; i++) {
delay(1);
rs += analogRead(OXPIN);
}
curOX = rs/3;
// Update floating sum by subtracting value
// about to be overwritten and adding the new value.
fltSumNH3 = fltSumNH3 + curNH3 - bufferNH3[pntrNH3];
fltSumRED = fltSumRED + curRED - bufferRED[pntrRED];
fltSumOX = fltSumOX + curOX - bufferOX[pntrOX];
// Store new measurement in buffer
bufferNH3[pntrNH3] = curNH3;
bufferRED[pntrRED] = curRED;
bufferOX[pntrOX] = curOX;
// Determine new state of flags
NH3stable = abs(fltSumNH3 / seconds - curNH3) < delta;
REDstable = abs(fltSumRED / seconds - curRED) < delta;
OXstable = abs(fltSumOX / seconds - curOX) < delta;
// Advance buffer pointer
pntrNH3 = (pntrNH3 + 1) % seconds ;
pntrRED = (pntrRED + 1) % seconds;
pntrOX = (pntrOX + 1) % seconds;
//Mikä kestää?
if(!NH3stable) {
Serial.print("(NH3:");
Serial.print(abs(fltSumNH3 / seconds - curNH3));
Serial.println(")");
}
if(!REDstable) {
Serial.print("(RED:");
Serial.print(abs(fltSumRED / seconds - curRED));
Serial.println(")");
}
if(!OXstable) {
Serial.print("(OX:");
Serial.print(abs(fltSumOX / seconds - curOX));
Serial.println(")");
}
} while (!NH3stable || !REDstable || !OXstable);
NH3baseR = fltSumNH3 / seconds;
REDbaseR = fltSumRED / seconds;
OXbaseR = fltSumOX / seconds;
// Store new base resistance values in EEPROM
}
uint16_t getBaseResistance(channel_t channel) {
/* if (1 == __version) {
// Version 1 can query every channel independently
// Reply is 4 bytes long with relevant data in second and third byte
switch (channel) {
case CH_NH3:
return getRuntimeData(CMD_V1_GET_R0_NH3, 4, 1);
case CH_RED:
return getRuntimeData(CMD_V1_GET_R0_RED, 4, 1);
case CH_OX:
return getRuntimeData(CMD_V1_GET_R0_OX, 4, 1);
}
}
if (2 == __version) {
// Version 2 uses the same command every time, but different offsets*/
switch (channel) {
case CH_NH3:
return NH3baseR;
case CH_RED:
return REDbaseR;
case CH_OX:
return OXbaseR;
}
// }
return 0;
}
/**
Calculates the current resistance ratio for the given channel.
@param channel
The channel to request resistance values from.
@return The floating-point resistance ratio for the given channel.
*/
float getCurrentRatio(channel_t channel) {
float baseResistance = (float) getBaseResistance(channel);
float resistance = (float) getResistance(channel);
return resistance / baseResistance * (1023.0 - baseResistance) / (1023.0 - resistance);
return -1.0;
}
/**
Measures the gas concentration in ppm for the specified gas.
@param gas
The gas to calculate the concentration for.
@return The current concentration of the gas
in parts per million (ppm).
*/
float measureMICS(gas_t gas) {
float ratio;
float c = 0;
switch (gas) {
case CO:
ratio = getCurrentRatio(CH_RED);
c = pow(ratio, -1.179) * 4.385;
break;
case NO2:
ratio = getCurrentRatio(CH_OX);
c = pow(ratio, 1.007) / 6.855;
break;
case NH3:
ratio = getCurrentRatio(CH_NH3);
c = pow(ratio, -1.67) / 1.47;
break;
case C3H8:
ratio = getCurrentRatio(CH_NH3);
c = pow(ratio, -2.518) * 570.164;
break;
case C4H10:
ratio = getCurrentRatio(CH_NH3);
c = pow(ratio, -2.138) * 398.107;
break;
case CH4:
ratio = getCurrentRatio(CH_RED);
c = pow(ratio, -4.363) * 630.957;
break;
case H2:
ratio = getCurrentRatio(CH_RED);
c = pow(ratio, -1.8) * 0.73;
break;
case C2H5OH:
ratio = getCurrentRatio(CH_RED);
c = pow(ratio, -1.552) * 1.622;
break;
}
return isnan(c) ? -1 : c;
}
void setup() {
// Initialize Serial Monitor for debugging
BLUETOOTH_SERIAL.begin(9600);
Serial.begin(9600);
Serial1.begin(9600); // For PMS5003
MHZ19B_SERIAL.begin(MHZ19B_BAUDRATE);
// Wait for the sensor to warm up
delay(2000);
Serial.println("MICS-6814 Sensor Test v0.1");
Serial.print("Calibrating Sensor");
calibrateMICS();
Serial.println("OK!");
Wire.begin();
//Initialize sensor
if (mySensor.begin() == false)
{
Serial.println("No SGP30 Detected. Check connections.");
while (1);
}
//Initializes sensor for air quality readings
//measureAirQuality should be called in one second increments after a call to initAirQuality
mySensor.initAirQuality();
}
void readPM() {
const int timeOut = 2000;
if (pms.readUntil(data, timeOut)) {
Serial.print("PM 1.0 (ug/m3): ");
Serial.println(data.PM_AE_UG_1_0);
BLUETOOTH_SERIAL.print(data.PM_AE_UG_1_0);
BLUETOOTH_SERIAL.print("ug/m3");
BLUETOOTH_SERIAL.print(",");
Serial.print("PM 2.5 (ug/m3): ");
Serial.println(data.PM_AE_UG_2_5);
BLUETOOTH_SERIAL.print(data.PM_AE_UG_2_5);
BLUETOOTH_SERIAL.print("ug/m3");
BLUETOOTH_SERIAL.print(",");
Serial.print("PM 10.0 (ug/m3): ");
Serial.println(data.PM_AE_UG_10_0);
BLUETOOTH_SERIAL.print(data.PM_AE_UG_10_0);
BLUETOOTH_SERIAL.print("ug/m3");
BLUETOOTH_SERIAL.print(",");
} else {
Serial.println("Failed to read PM data");
}
}
void readCO2() {
// Send command to MH-Z19B sensor to read CO2 concentration
MHZ19B_SERIAL.write(MHZ19B_CMD_READ_CO2, sizeof(MHZ19B_CMD_READ_CO2));
// Read response from the sensor
uint8_t response[9];
MHZ19B_SERIAL.readBytes(response, 9);
// Check if response is valid
if (response[0] == 0xFF && response[1] == 0x86) {
// Calculate CO2 concentration (high byte * 256 + low byte)
int co2 = response[2] * 256 + response[3];
// Print CO2 concentration to Serial Monitor
Serial.print("CO2 Level: ");
Serial.print(co2);
Serial.println(" ppm");
BLUETOOTH_SERIAL.print(co2);
BLUETOOTH_SERIAL.print("ppm");
BLUETOOTH_SERIAL.print(",");
} else {
// Print error if response is invalid
Serial.println("Error: Invalid response from sensor");
}
}
void readtvoc(){
mySensor.measureAirQuality();
Serial.print(mySensor.TVOC);
Serial.println(" ppb");
BLUETOOTH_SERIAL.print(mySensor.TVOC);
BLUETOOTH_SERIAL.print(" ppb");
BLUETOOTH_SERIAL.print(",");
}
void readmics(){
Serial.print("NH3: ");
Serial.print(getResistance(CH_NH3));
Serial.print("/");
Serial.print(getBaseResistance(CH_NH3));
Serial.print(" = ");
Serial.print(getCurrentRatio(CH_NH3));
Serial.print(" => ");
Serial.print(measureMICS(NH3));
Serial.println("ppm");
BLUETOOTH_SERIAL.print(measureMICS(NH3));
BLUETOOTH_SERIAL.print("ppm");
BLUETOOTH_SERIAL.print(",");
delay(50);
Serial.print("CO: ");
Serial.print(getResistance(CH_RED));
Serial.print("/");
Serial.print(getBaseResistance(CH_RED));
Serial.print(" = ");
Serial.print(getCurrentRatio(CH_RED));
Serial.print(" => ");
Serial.print(measureMICS(CO));
Serial.println("ppm");
BLUETOOTH_SERIAL.print(measureMICS(CO));
BLUETOOTH_SERIAL.print("ppm");
BLUETOOTH_SERIAL.print(",");
delay(50);
Serial.print("NO2: ");
Serial.print(getResistance(CH_OX));
Serial.print("/");
Serial.print(getBaseResistance(CH_OX));
Serial.print(" = ");
Serial.print(getCurrentRatio(CH_OX));
Serial.print(" => ");
Serial.print(measureMICS(NO2));
Serial.println("ppm");
BLUETOOTH_SERIAL.print(measureMICS(NO2));
BLUETOOTH_SERIAL.print("ppm");
BLUETOOTH_SERIAL.print(";");
delay(50);
delay(1000);
}
void loop() {
readPM();
readCO2();
readtvoc();
readmics();
delay(2000);
}
Everything works, with one exception. Someimes the mhz19b co2 sensor won t show any data. Basically if i unplug the rx or tx of the sensor from arduino mega and put it back, it works, but it seems like it has a delay whenever i exhale on it or something. This problem ocurred when i tried to add another sensor. After that , the co2 didn t show any data and i had to undo everything until today. What could have happened? I power up externally my sensors + arduino using 9v@1A power supply which goes through a dc-dc buck convertert to get 5V.




