A wearable CO monitor that uses an ESP8266
To make the experience fit your profile, pick a username and tell us what interests you.
We found and based on your interests.
I have quit because
a) I found out that the sensor doesn't return PPM values, and that it isn't really reliable to get it to do so
b) I didn't understand the table on the datasheet
c) Wemos wifi problems (the code worked once, then I changed it and lost the code)
The wifi coding was also extremely frustrating through the wemos. I am learning python now, and maybe will learn how to use it with wifi through a wifi dongle on my computer, then see if I can port that code to the wemos, with a different sensor.
Andreas Speiss has some videos about that (Python on wemos), which I will refer to.
I quit early last year, am only updating now though. Sorry if you have been following this project, though I don't think anyone has been.
I used the code and tutorial in this video to set up and use a telnet server with the ESP. Then I used puTTY to monitor its serial output wirelessly.
I used the same circuit as in the last log, but did not use the temperature sensor because I was in too much of a hurry to add that to the code.
Circuit:
Code:
Replace the IP address, gateway, and subnet with your own. You can find the subnet and gateway of your router/hotspot with ipconfig in command prompt of a computer connected to it. The ip address can be whatever you want, as long as nothing else is using it.
#include <ESP8266WiFi.h>
WiFiServer TelnetServer(23);
WiFiClient Telnet;
IPAddress ip (********; //assigned ip address
IPAddress gateway (********); //gateway of the network you're connected to
IPAddress subnet (*********); //subnet of the network your connected to
//IMPORTANT: use commas (,) and not full stops (.) to in the three addresses
//above, and replace the SSID and password with your own when using the
//code
void handleTelnet() {
if (TelnetServer.hasClient()) {
if (!Telnet || !Telnet.connected()) {
if (Telnet) Telnet.stop();
Telnet = TelnetServer.available();
} else {
TelnetServer.available().stop();
}
}
}
void setup() {
WiFi.config(ip, gateway, subnet);
WiFi.begin("YOUR SSID", "YOUR PASSWORD");
Serial.println();
Serial.print("\nConnecting to: YOUR SSID ");
uint8_t i = 0;
while (WiFi.status() != WL_CONNECTED && i++ < 20) delay(500);
if (i == 21) {
Serial.print("Could not connect to YOUR SSID");
ESP.reset();
}
//start UART and the server
Serial.begin(115200);
TelnetServer.begin();
TelnetServer.setNoDelay(true);
Serial.print("Ready! Use 'telnet ");
Serial.print(WiFi.localIP());
Serial.println(" 23' to connect");
}
void loop() {
handleTelnet();
Telnet.print("ADC: ");
Telnet.println(analogRead(A0));
delay(2000);
}
I used a simple test code to test the Wemos with the DHT and the MQ9.
An Arduino Uno is used to power the MQ9 at 1.5V with a voltage divider on the 5V pin. Another voltage divider cuts the MQ9's output voltage into 2/3 its original to avoid damaging the ADC of the Wemos.
Circuit:
Code:
#include "DHTesp.h"
DHTesp dht;
void setup()
{
Serial.begin(115200);
Serial.println();
dht.setup(12, DHTesp::DHT11); // Connect DHT sensor to GPIO 17
}
void loop()
{
delay(500);
float humidity = dht.getHumidity();
float temperature = dht.getTemperature();
Serial.print(humidity, 1);
Serial.print("\t\t");
Serial.print(temperature, 1);
Serial.print("\t\t");
Serial.println(analogRead(A0));
}
When soldering wires to the chip, some solder got stuck to the shielding and shorted everything together.
I'll use a Wemos D1 Mini from now on, to prevent such errors and so that I waste less time with upload errors and switching the ESP's mode from boot to normal running and back again.
I prepared the new ESP:
And connected it to the Arduino and the sensor:
The connections were:
Arduino ESP DHT11
3.3V Vcc, CH_PD, RST Vcc (pin 1)
GND GND, GPIO0, GPIO15 GND (pin 4)
TX (pin 1) TXD0
RX (pin 0) RXD0
GPIO12 Data(pin 2)
Reset(connect to arduino's ground)
Then I uploaded the code. I had modified the web server code from ESP8266Arduino core website. I added my WiFi name and password in the sketch, included the DHTesp library and set it up, then replaced the analogRead in the HTML page section with dht.getTemperature(). After uploading the code and copying the ip address from the serial monitor into the browser, I was the temperature in the web browser , which updated every 5 seconds, like it was supposed to.
#include <DHTesp.h>
DHTesp dht;
#include <ESP8266WiFi.h>
const char* ssid = "******"; //Put your wifi name here
const char* password = "*******"; //Put your wifi password here
WiFiServer server(80);
void setup()
{
dht.setup(12, DHTesp::DHT11); // Connect DHT sensor to GPIO 17
Serial.begin(115200);
Serial.println();
Serial.printf("Connecting to %s ", ssid);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED)
{
delay(500);
Serial.print(".");
}
Serial.println(" connected");
server.begin();
Serial.printf("Web server started, open %s in a web browser\n", WiFi.localIP().toString().c_str());
}
// prepare a web page to be send to a client (web browser)
String prepareHtmlPage()
{
String htmlPage =
String("HTTP/1.1 200 OK\r\n") +
"Content-Type: text/html\r\n" +
"Connection: close\r\n" + // the connection will be closed after completion of the response
"Refresh: 5\r\n" + // refresh the page automatically every 5 sec
"\r\n" +
"<!DOCTYPE HTML>" +
"<html>" +
"The temperature: " + String(dht.getTemperature()) + //read the temperature
"</html>" +
"\r\n";
return htmlPage;
}
void loop()
{
WiFiClient client = server.available();
// wait for a client (web browser) to connect
if (client)
{
Serial.println("\n[Client connected]");
while (client.connected())
{
// read line by line what the client (web browser) is requesting
if (client.available())
{
String line = client.readStringUntil('\r');
Serial.print(line);
// wait for end of client's request, that is marked with an empty line
if (line.length() == 1 && line[0] == '\n')
{
client.println(prepareHtmlPage());
break;
}
}
}
delay(1); // give the web browser time to receive the data
// close the connection:
client.stop();
Serial.println("[Client disonnected]");
}
}
I connected the MQ sensor to jumper wires, and soldered one half of its pins together for Vcc(yellow wire), and on the other side I soldered the opposite two together for the output(orange wire), while the last pin was its GND(purple wire).
Here's the code I uploaded to the ESP:
void setup() {
Serial.begin(115200);
}
void loop() {
delay(500);
Serial.println(analogRead(A0));
}
But it only returned 1024, which meant the sensor's output voltage was equal to its input voltage, which means something is wrong. When removing the pin from the breadboard, the analog reading fluctuated, but returned to 1024. After checking the ESP8266 Arduino Core website(https://arduino-esp8266.readthedocs.io/en/latest/reference.html#analog-input), I found that the ADC pin can only handle 1V. I think the ADC on the chip may be broken, because I connected it to 5V initially, before changing it to 3.3V. Luckily, I have a spare. I'll have to use a voltage divider.
I connected the ESP to my Arduino UNO and uploaded a cut-down version of the example code from the dhtESP library:
#include "DHTesp.h"
#ifdef ESP32
#pragma message(THIS EXAMPLE IS FOR ESP8266 ONLY!)
#error Select ESP8266 board.
#endif
DHTesp dht;
void setup()
{
Serial.begin(115200);
Serial.println();
Serial.println("Status\tHumidity (%)\tTemperature (C)\t(F)\tHeatIndex (C)\t(F)");
String thisBoard= ARDUINO_BOARD;
Serial.println(thisBoard);
// Autodetect is not working reliable, don't use the following line
// dht.setup(17);
// use this instead:
dht.setup(12, DHTesp::DHT11); // Connect DHT sensor to GPIO 15
}
void loop()
{
delay(dht.getMinimumSamplingPeriod());
float humidity = dht.getHumidity();
float temperature = dht.getTemperature();
Serial.println(dht.getStatusString());
Serial.println(humidity, 1);
Serial.println(temperature, 1);
}
The connections I made are as follows:
Arduino ESP
3.3V ---------------Vcc, Rst, CH_PD/EN
GND ----------------GND, GPIO0, GPIO15,
Reset (connect to the Arduino's ground)
TX ------------------TXD0
RX ------------------RXD0
Before starting I had uploaded the bareMinimum example sketch to the Arduino.
Then the DHT11 was connected to GPIO12 of the ESP, and the code was uploaded.
It worked!
Next, I'll try and connect it to the MQ gas sensor.
Here are the problems I faced when fixing the math and how I solved them(if I did):
PROBLEM | SOLUTION |
The 'volts' variable returned 0.00 | Splitting it into the variables 'preVolts' and Volts |
The 'ratio' variable returned 'ovf' which I guess means overflow | Splitting it into the variables 'preRatio', 'RatioDiv', and 'Ratio' Reducing the 1K2 Rl value to 0.9K2 |
The 'ratiox' variable returned 0.00 | I found a math library called BigNumber which can handle big numbers and operations, and I thought that it would solve the problem |
The 'ratiox' variable returned 0 | I split it into 'preRatiox', and 'Ratiox' |
While 'preRatiox' worked fine, 'Ratiox' still returned 0 | When it did the negative exponent, it rounded the result, which was a decimal less than 0 , to 0. So I removed the minus sign, and decided to make it divide later in the 'ppm' variable, then I would get the same result as a negative exponent. |
The 'ppm' variable returned 0 | I think that division was too much for it... I checked it in a calculator, and it gave me 0.000004326450746152052817198327389249, which is too much. I added 20 zeroes behind the numerator in 'ppm' in the hopes that it would have the same effect as multiplying by 10^20, and it would then round it off to something more precise. |
The 'ppm' variable returned -15226 after about a minute of calculating | I read that the Arduino does make mistakes in division, and that it could take a longer time than multiplication. So I added 'L' after the dividend to make sure the program new it was a 'long' and reduced the number to 0s to 10, and got -26107. Then I reduced the number of 0s to five and got 4. That's good! It's getting closer! With 7 0s, it returns 432. That's even closer! 9 0s returns -22272, which is back to the nonsense values we had earlier. 8 0s returns 4326. It looks like this is the closest I'm going to get. However, it took 1.5 minutes to calculate that. |
Time-consuming calculation | I uploaded the code again, now it works fine. |
I can't find an LPG source to test the code | I'm using a AAA battery to power the sensor at 1.5V, and changed the numbers in the code accordingly (swap the 5s in 'volts' for 1.5). Now it will measure carbon monoxide, which I can test with a match or candle.'' |
'ppm' returns -1 |
I'm currently testing everything on my Arduino UNO before I start using the ESP
I'm trying to get PPM values from the sensor through the analogRead function. First I plotted a Rs/Ro to PPM graph in excel from the datasheet of the MQ9 gas sensor. Then I used the trendline function to get the formula and I've put it in the Arduino IDE so it can calculate the PPM.
void setup() {
Serial.begin(9600);
}
void loop() {
int input = analogRead(A0);
float Volts = input*5/1024;
float ratio = (100000000*Volts)/(5-Volts);
float ratiox = pow(ratio,-2.625);
float ppm = (3572.6*ratiox);
Serial.print("ppm=");
Serial.println(ppm);
delay(500);
}
Unfortunately, the Arduino is having trouble with the maths and is returning 'inf' in the serial monitor, which I think stands for infinity. So I'm reading each line of maths in the serial monitor to find the problem and (hopefully) fix it.
What I'm doing here is loosely based of this video(https://www.youtube(dot)com/watch?v=fBo3Yq9LK1U), but I can't understand what's going on in the code displayed there(at 8:39), so I'm trying this.
Create an account to leave a comment. Already have an account? Log In.
Well, its very polluted where I live, and I'm going to do some experiments where two people use it on the same day to commute. We may see which method of transport exposes you to less pollution, like a bike will probably expose you to far more than a car, but what about cars verses buses? Or the Metro? It'll also help raise awareness abut the just HOW bad the pollution is, because most people know it's there, but aren't aware of the implications on their health.
Become a member to follow this project and never miss any updates
Neat! What kind of applications do you see for this as a wearable?