ESP8266 sensing CO2 + Temp + RH + Pressure
I've progressed somewhat in home-integrated sensors, and I've established a baseline for a connected home-environment sensor, which I document here.

Update: This design appears to cause some self-heating of the bme280 sensor, leading to a temperature of 2-3 degrees too high. Perhaps mounting it outside the 'case' solves this.
The ESP8266 has one hardware serial (UART) port, marked RX and TX on the board, which is also used for logging and uploading firmware over USB.
Alternatively, ESPHome supports software serial where the CPU simulates a UART port on any GPIO pin. This can cause glitches, but at the low baud rates (<100k) our sensors need this is likely OK. The advantage is that you keep logging and uploading firmware over the serial (USB) port.
When connecting the serial MH-Z19B sensor to the ESP8266 board, you have two choices:






Update: This design appears to cause some self-heating of the bme280 sensor, leading to a temperature of 2-3 degrees too high. Perhaps mounting it outside the 'case' solves this.
Goal
For this sensor, my goals are:- Measure indoor air quality: achieved with range CO2, temperature, relative humidity, and pressure sensors
- Small formfactor: achieved with small Arduino clone
- Extensible: achieved with ESPHome / flexible hardware
- Versatile data collection: achieved with ESPHome
- Non-cloud: achieved with ESPHome combined with RPi running Influxdb/Mosquitto broker
Options and considerations
I use the following hardware and software, alternatives are also included for comparison- Board: ESP8266 Lolin D1 Mini Pro: because it's cheap, compact, enough CPU power, sufficient IO. See also my previous post
- CO2: Winsen MH-Z19b, reasonable price/quality ratio, measures direct CO2 (vs equivalent CO2 like AMS CSS811), see also in my previous post
- T/RH/P: Bosch BME280: very good price/quality ratio, see also this definitive guide
- Screen: 1.3" I2C 128x64 pixel OLED display, reasonable size and price, I2C is easy to work with.
- OS: ESPHome, similar to ESPEasy but I like it a bit better because it builds on existing platformio backbone. See also this comparison
- Mounting: perspex with m2/m3/m4 bolts
Integration
Optional: get familiar with ESP8266
If you've never done anything with ESP8266 yet, I recommend running the fade.ino routine on your board. See my previous post until 'test program' for instructions.Install ESPHome
First install ESPHome. This allows us to update the board later.- Get ESPHome on your computer
- Start ESPHome dashboard
- Optional: install serial port driver, e.g. for the Winchiphead CH340 (CH340C/CH340G): check instructions for your OS (outside scope of this article)
- Create new configuration in ESPHome dashboard (see here for instructions for Adafruit version)
- Upload basic config, example below
- You can now access (and change) your board over WiFi!
YAML:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
| esphome: name: esp_test_board platform: ESP8266 board: d1_mini_pro wifi: ssid: "your wifi SSID" password: "your wifi password" #fast_connect: True # Required to connect to hidden SSIDs and only with one network # Enable fallback hotspot (captive portal) in case wifi connection fails ap: ssid: "Esp Living Fallback Hotspot" password: "your fallback password" captive_portal: # Enable logging logger: # Disable UART Logging to fix MHZ-19B preamble issue when using hardware UART, see https://github.com/esphome/issues/issues/488 # Not strictly necessary anymore with 1.14.0 https://github.com/esphome/esphome/releases/tag/v1.14.0 # baud_rate: 0 # Disable Home Assistant API # api: web_server: port: 80 ota: |
Assemble hardware
Once you have all parts, connect as below. Either using headers and jump wires like these (easier), or direct wire soldering (more compact).The ESP8266 has one hardware serial (UART) port, marked RX and TX on the board, which is also used for logging and uploading firmware over USB.
Alternatively, ESPHome supports software serial where the CPU simulates a UART port on any GPIO pin. This can cause glitches, but at the low baud rates (<100k) our sensors need this is likely OK. The advantage is that you keep logging and uploading firmware over the serial (USB) port.
When connecting the serial MH-Z19B sensor to the ESP8266 board, you have two choices:
- Recommended: use software serial port (any pins, e.g. 12 and 13), lower baud rate, and keep logging
- Use hardware UART (pins RX and TX) and disable logging, see also this issue
- Unfortunately it took me a while to figure out SW serial works, so several of my first boards (like the one below) use the hardware serial.
Wiring using software UART

Wiring using hardware UART
This is not recommended as it breaks uploading firmware via serial port until you disconnect the MH-Z19B sensor.
Wire example




Configure software
Once hardware is connected, configure the sensors as you see fit. See the extensive documentation at ESPHome.io. My example is shown below.code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
| esphome: name: esp_test_board platform: ESP8266 board: d1_mini_pro wifi: ssid: "your wifi SSID" password: "your wifi password" #fast_connect: True # Required to connect to hidden SSIDs and only with one network # Enable fallback hotspot (captive portal) in case wifi connection fails ap: ssid: "Esp Living Fallback Hotspot" password: "your fallback password" captive_portal: # Enable logging logger: # Disable UART Logging to fix MHZ-19B preamble issue when using hardware UART, see https://github.com/esphome/issues/issues/488 # Not strictly necessary anymore with 1.14.0 https://github.com/esphome/esphome/releases/tag/v1.14.0 # baud_rate: 0 # Disable Home Assistant API # api: web_server: port: 80 ota: uart: # For hardware serial: rx_pin: GPIO3 # GPIO3 = RX hardware tx_pin: GPIO1 # GPIO1 = TX hardware # For software serial: rx_pin: GPIO12 tx_pin: GPIO13 baud_rate: 9600 id: myuart1 i2c: sda: 4 scl: 5 scan: True # Ensure you get *ttf files from somewhere font: - file: "slkscr.ttf" id: my_font1 size: 8 - file: "slkscr.ttf" id: my_font2 size: 16 - file: "Arial.ttf" id: my_font3 size: 16 mqtt: # For mobile = WAN : use FQDN, for local (IoT network - no WAN), use home IP. broker: "192.168.0.1" #broker: home.yourhostname.org port: 1883 username: "esp_board_client" password: "AQFCg72z5MqFihspHGbkqOj9" sensor: - platform: wifi_signal name: "WiFi Signal" update_interval: 10s id: mywifi1 - platform: wifi_signal name: "WiFi Signal2" update_interval: 10s filters: - exponential_moving_average: alpha: 0.001 # We want ~6 hour averaging, at 10s update, that's 6*3600/10 = 2160 points, thus alpha should be 2/2160 ~ 0.001 send_every: 1 id: mywifi2 - platform: mhz19 co2: name: "MH-Z19 CO2" state_topic: influx/environv3/quantity/CO2/source/mhz19b/board/esp_living/location/mobile/room/car2/value/state id: mhz_19_co2 temperature: name: "MH-Z19 Temperature" #state_topic: influx/environv3/quantity/T/source/mhz19b/board/esp_testing/location/home/room/bathroom/value/state state_topic: influx/environv3/quantity/T/source/mhz19b/board/esp_living/location/mobile/room/car2/value/state id: mhz_19_T update_interval: 20s uart_id: myuart1 - platform: mhz19 co2: name: "MH-Z19 CO2" #state_topic: influx/environv3/quantity/CO2/source/mhz19b/location/testing/value/state id: mhz_19_co2_ema filters: - exponential_moving_average: alpha: 0.001 # We want ~12 hour averaging, at 20s update, that's 12*3600/20 = 2160 points, thus alpha should be 2/2160 ~ 0.001 send_every: 1 temperature: name: "MH-Z19 Temperature" #state_topic: influx/environv3/quantity/T/source/mhz19b/location/testing/value/state id: mhz_19_T_ema filters: - exponential_moving_average: alpha: 0.001 # We want ~12 hour averaging, at 20s update, that's 12*3600/20 = 2160 points, thus alpha should be 2/2160 ~ 0.001 send_every: 1 update_interval: 20s # if changed also update alpha uart_id: myuart1 - platform: bme280 temperature: name: "BME280 Temperature" oversampling: 16x id: bme_280_temp #state_topic: influx/environv3/quantity/T/source/bme280/board/esp_mobile/location/mobile/room/car/value/state #state_topic: influx/environv3/quantity/T/source/bme280/board/esp_mobile/location/home/room/living/value/state state_topic: influx/environv3/quantity/T/source/bme280/board/esp_living/location/mobile/room/car2/value/state pressure: name: "BME280 Pressure" id: bme_280_press #state_topic: influx/environv3/quantity/P/source/bme280/board/esp_mobile/location/mobile/room/car/value/state #state_topic: influx/environv3/quantity/P/source/bme280/board/esp_mobile/location/home/room/living/value/state state_topic: influx/environv3/quantity/P/source/bme280/board/esp_living/location/mobile/room/car2/value/state humidity: name: "BME280 Humidity" id: bme_280_rh # state_topic: influx/environv3/quantity/RH/source/bme280/board/esp_mobile/location/mobile/room/car/value/state #state_topic: influx/environv3/quantity/RH/source/bme280/board/esp_mobile/location/home/room/living/value/state state_topic: influx/environv3/quantity/RH/source/bme280/board/esp_living/location/mobile/room/car2/value/state address: 0x76 update_interval: 30s - platform: bme280 temperature: name: "BME280 Temperature" oversampling: 16x id: bme_280_temp_ema filters: - exponential_moving_average: alpha: 0.001 # We want ~12 hour averaging, at 20s update, that's 12*3600/20 = 2160 points, thus alpha should be 2/2160 ~ 0.001 send_every: 1 # state_topic: influx/environv3/quantity/T/source/bme280/location/mobile/value/state pressure: name: "BME280 Pressure" id: bme_280_press_ema filters: - exponential_moving_average: alpha: 0.0002 # We want ~48 hour averaging, at 20s update, that's 48*3600/20 = 8640 points, thus alpha should be 2/8640 ~ 0.0002 send_every: 1 # state_topic: influx/environv3/quantity/P/source/bme280/location/mobile/value/state humidity: name: "BME280 Humidity" id: bme_280_rh_ema filters: - exponential_moving_average: alpha: 0.001 # We want ~12 hour averaging, at 20s update, that's 12*3600/20 = 2160 points, thus alpha should be 2/2160 ~ 0.001 send_every: 1 # state_topic: influx/environv3/quantity/RH/source/bme280/location/mobile/value/state address: 0x76 update_interval: 20s # if you change this don't forget to update alpha display: - platform: ssd1306_i2c model: "SH1106 128x64" address: 0x3C lambda: |- it.printf(0, 2, id(my_font1), "CO2"); it.printf(0, 10, id(my_font1), "PPM"); it.printf(24, 6, id(my_font3), "%d (%d)", int(id(mhz_19_co2).state), int(id(mhz_19_co2_ema).state)); it.printf(0, 18, id(my_font1), "T"); it.printf(0, 26, id(my_font1), "C"); it.printf(24, 22, id(my_font3), "%.1f (%.1f)", id(bme_280_temp).state, id(bme_280_temp_ema).state); it.printf(0, 34, id(my_font1), "RH"); it.printf(0, 42, id(my_font1), "%%"); it.printf(24, 38, id(my_font3), "%.1f (%.1f)", (id(bme_280_rh).state), (id(bme_280_rh_ema).state)); it.printf(0, 50, id(my_font1), "P"); it.printf(0, 58, id(my_font1), "HPa"); it.printf(24, 54, id(my_font3), "%d (%d)", int(id(bme_280_press).state), int(id(bme_280_press_ema).state)); |
Future work
- Document fine dust measurements with SDS-011
- Get AMS CCS811 sensor working
- Improve housing options --> now available using PCB design!
References
- Atomstar's blog: Getting started with LOLIN D1 mini/DHT22 on Mac
- Atomstar's blog: Measuring CO2 using MH-Z19B and D1 mini pro
- https://www.circuits.dk/t...9-ndir-co2-sensor-module/
- http://www.kandrsmith.org...grometers/calib_many.html
- https://www.jaredwolff.co...811-vs-bme680-vs-sgp30/#!
- https://blog.jokielowie.c...680-oraz-ccs811-iaq-tvoc/
12-'19 Securely wiping SSDs
11-'19 Flash problems to ESP8266 boards
Comments
Thanks. Ik wil al een tijdje soortgelijks maken. Aan de hand van dit boodschappenlijstje en beschrijving ga ik er eens voor zitten.
Very nice and elaborate post! Over the last 6 months I have spent a lot of time programming ESP32's and lately ESP8266's in combination with a range of temperature, humidity, luminosity and UV sensors, mostly in Micropython, but I never heard of the ESPhome OS, so I will give that a try sooner rather than later.
Unfortunately I am afraid you will very likely run into problems with your BME280 sensor. Despite the good results in the comparative test, I have identified several issues with this sensor during 3 months non-stop measuring, at a rate of 1 reading every 12 seconds.
- in temperatures close to freezing, the sensor shows erratic temperature readings, with jumps of about 2 degrees up and down.
-I had 1 BME280 with a soldering fault in factory, leading to shorting the sensor and burning the ESP32 on the way.
-the sensor needs proper calibration. On average it measure the humidity 7% too low (verified against 2 old-fashioned thermometers (dry and wet bulb) calibrated to WMO standards.
-Place two BME280 next to each other and get 2 different temperature readings, up to 1.5 degrees apart.
So far I seem to be getting most accurate temperature readings on the MCP9808.
Unfortunately I am afraid you will very likely run into problems with your BME280 sensor. Despite the good results in the comparative test, I have identified several issues with this sensor during 3 months non-stop measuring, at a rate of 1 reading every 12 seconds.
- in temperatures close to freezing, the sensor shows erratic temperature readings, with jumps of about 2 degrees up and down.
-I had 1 BME280 with a soldering fault in factory, leading to shorting the sensor and burning the ESP32 on the way.
-the sensor needs proper calibration. On average it measure the humidity 7% too low (verified against 2 old-fashioned thermometers (dry and wet bulb) calibrated to WMO standards.
-Place two BME280 next to each other and get 2 different temperature readings, up to 1.5 degrees apart.
So far I seem to be getting most accurate temperature readings on the MCP9808.
Je weet dat de MHZ19B afgeschermd moet worden van extern infrarood licht?
Zelf was/ ben ikook bezig zulke sensor units te bouwen. Uiteindelijk Xiaomi sensoren door het huis geplaatst, goedkoper en makkelijker.
Ik heb een aantal van die BME 280 sensoren naast elkaar aan 1 ESP32 Gehad om hun gedrag te bekijken. Ik kwam er achter dat ze temperatuur veranderingen goed aangeven, maar bij elke keer opnieuw opstarten hadden ze een nieuwe offset. Die offset kreeg ik niet weggewerkt.
Voor CO2 was ik nog aan het rondkijken. De MHZ19B ziet er interessant uit.
Ik heb een aantal van die BME 280 sensoren naast elkaar aan 1 ESP32 Gehad om hun gedrag te bekijken. Ik kwam er achter dat ze temperatuur veranderingen goed aangeven, maar bij elke keer opnieuw opstarten hadden ze een nieuwe offset. Die offset kreeg ik niet weggewerkt.
Voor CO2 was ik nog aan het rondkijken. De MHZ19B ziet er interessant uit.
Interesting project! Don't forget to calibrate the humidity and temperature settings. Especially the humidity can be way off (up to 5%-points), which may be relevant to indoor measurements. I used this guide to calibrate:
https://www.allaboutcircu...ibrate-a-humidity-sensor/
It is not easy to acquire cheap good salts. I think I used normal salt and "badzout", which were easy to obtain. You can find the saturation values for different salts online.
For temperature, I have more faith in the DS18B20 for accurate measurements. Calibration there is also straightforward: melting ice, boiling water (just put the sensor in your kettle), and use a simple fever thermometer to get an accurate reading of around 37 degrees.
CO2 is silently on my list as well...
https://www.allaboutcircu...ibrate-a-humidity-sensor/
It is not easy to acquire cheap good salts. I think I used normal salt and "badzout", which were easy to obtain. You can find the saturation values for different salts online.
For temperature, I have more faith in the DS18B20 for accurate measurements. Calibration there is also straightforward: melting ice, boiling water (just put the sensor in your kettle), and use a simple fever thermometer to get an accurate reading of around 37 degrees.
CO2 is silently on my list as well...
I vaguely remembered, but now I doCurlyMo wrote on Sunday 22 December 2019 @ 21:24:
Je weet dat de MHZ19B afgeschermd moet worden van extern infrarood licht?

COTS hardware is probably much easier, but I like the learning experience and the versatility of these sensors as wellIruk1981 wrote on Monday 23 December 2019 @ 09:54:
Zelf was/ ben ikook bezig zulke sensor units te bouwen. Uiteindelijk Xiaomi sensoren door het huis geplaatst, goedkoper en makkelijker.

[Comment edited on Wednesday 15 January 2020 18:26]
Derecho wrote on Sunday 22 December 2019 @ 18:26:
[...]
So far I seem to be getting most accurate temperature readings on the MCP9808.
Thanks for the sensor and calibration recommendations! I was thinking of somehow getting the sensors close to a calibrated station (e.g. KNMI), but that might require building a stand-alone box and put it in a field somewhere. Consider this on my to-do listZwartoog wrote on Monday 23 December 2019 @ 23:34:
Interesting project! Don't forget to calibrate the humidity and temperature settings. Especially the humidity can be way off (up to 5%-points), which may be relevant to indoor measurements.

For calibration, you will need to be very close to their sensors. Pressure is easier to tune by checking a nearby calibrated station, since this is more regional. Accurate devices are very expensive, unfortunately.Atomstar wrote on Sunday 29 December 2019 @ 10:49:
Thanks for the sensor and calibration recommendations! I was thinking of somehow getting the sensors close to a calibrated station (e.g. KNMI), but that might require building a stand-alone box and put it in a field somewhere. Consider this on my to-do list

Thanks Atomstar, your project greatly influenced my recent build of an air quality logger. See https://forum.arduino.cc/index.php?topic=663389.0 to se it in real life.
I am very curious about your dust sensor research in the future. Please keep me informed if you don't mind.
I am very curious about your dust sensor research in the future. Please keep me informed if you don't mind.
Comments are closed