Particulates kill, build your sensor now!

By Atomstar on Wednesday 28 April 2021 19:48 - Comments (10)
Categories: ESP8266, Smarthome, Views: 8.992

One metric still missing from my home sensors is particulates, which are a known health hazard. Although I (secretly) already had a board running for a while, I wasn't satisfied enough to share it. Today, it's ready for sharing, including screaming headline for a change ;)

This board builds on my previous (smaller) design without particulate sensor, and also sports a custom-designed PCB (which you can order here!).

Starting with the end, it looks like:




Introduction

Building on my previous sensor projects, I'm now adding a Nova Fitness SDS011 PM2.5/PM10 fine dust sensor, widely used & recommended by e.g. RIVM, Luftdaten, and EU VAQUUMS.

For basic/context/intro, please read my previous guides here and here.

Bill of materials

  1. Special-built BigSensorThing PCB -- 9 euros with same-day shipping!
  2. ESP8266 WeMos D1 mini (do not get the pro! it's too big)
  3. Nova Fitness SDS011 fine dust sensor
  4. Winsen MH-Z19B CO2 sensor
  5. Optional: 1.3" 128x64 OLED or 1.5" 128x128 OLED (I use a 1.5" here)
  6. BME280 module with level converter - N.B. ensure you have a BME280 module that accepts 5V to use my print!
  7. DS18B20 temperature sensor
  8. Male pin headers straight (for modules)
  9. Male pin headers 90 degrees (for SDS011 & optionally DS18B20)
  10. Optional: Dupont female-female jumper wires (to increase distance of DS18B20)
  11. M3 screws >20mm thread or 2x 10mm M3 afstandsbusjes -- you need at least 15mm clearance between the PCB and the SDS011
  12. Optional: get a hose to sample air from a distance
Some caveats/considerations:
  • Get a flexible/small USB micro cable. The Wemos/Lolin D1 Mini Pro is a bit too long to comfortably fit a USB cable at the bottom without extruding. I actually built mine with the Pro before I figured this out, and it sort of stands because I had a very flexible USB cable. Even with the regular (smaller) Wemos D1 mini, a short/flexible USB micro plug helps.
  • Since I power everything on 5V and the BME280 only accepts 1.7-3.6V as input voltage, ensure you get a BME280 module with voltage regulator, e.g. the one linked above. It should explicitly note that input voltage can be either 3.3V or 5V.

Bill of process

Software integration
Install esphome as documented here.
Hardware integration
Solder male header pins onto PCB for (1) D1 mini, (2) MH-Z19B, (3) OLED, (4) BME280, (5) DS18B20 and (6) SDS011 connector (use 90 deg here!)

N.B. For the MH-Z19B you can save a few headers by only connecting the necessary VCC, GND, RX and TX pins. (You could also only solder used pins for the D1 mini but I chose to connect all so I can optionally connect additional stuff via jumper wires).
N.B. 2 you can connect the DS18B20 on the front or on the back



Solder on modules.



Sandwitch PCB & SDS011, and wrap serial cable around / through the sandwich. Optionally (this design), connect the DS18B20 using double female Dupont wires to reduce/prevent self-heating.




This design directly powers the ESP8266 board, so no need for (micro) USB port as in previous designs.
Software configuration
Again similar to previous projects, with modification to add SDS011. Some notes:
  • Ensure you reduce OLED refresh rate to e.g. 30s
  • I only got stable operation by disabling UART logger.
  • Optionally use deep sleep loop, losing EMA
  • I filter some sensors as exponential moving average (EMA) mode as well to easily get a average of the last ~12hrs. Somehow I can't get all sensors to run like this, perhaps I maxed out the ESP8266.
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
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
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
esphome:
  name: esp_bigsensorthing
  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
  domain: ".lan"

  # reboot_timeout: 0s # Do not reboot if no wifi (can be useful for offline use)

  # Enable fallback hotspot (captive portal) in case wifi connection fails
  ap:
    ssid: "Esp Living Fallback Hotspot"
    password: "your fallback password"

logger:
  # Disable because somehow this conflicts with double serial ports of SDS011 and MH-Z19B
  # Disable UART Logging to fix MHZ-19B preamble issue when using hardware UART, see https://github.com/esphome/issues/issues/488
  # N.B. disabling logger crashes my Mac because of buggy CH34x driver, so I disable it as last setting.
  baud_rate: 0
  # level: VERBOSE

# Enable Home Assistant API for logging over wifi.
# Disable reboot loop by setting reboot_timeout to 0s!
# See warning on https://esphome.io/components/mqtt.html
api:
  reboot_timeout: 0s

# Disable webserver, since we push data over mqtt. Can be useful for 
# diagnostics, but takes up quite some memory
# web_server:
#   port: 80

# Optionally use deep sleep loop to reduce power consumption/self-heating.
# deep_sleep:
#   run_duration: 30s
#   sleep_duration: 150s

# Allow OTA updates
ota:

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"


uart:
  - id: myuart0 # For SDS011 pm2.5/pm10
    rx_pin: GPIO14 # = D5
    tx_pin: GPIO12  # = D6
    baud_rate: 9600
  - id: myuart1 # For MH-Z19B CO2
    rx_pin: GPIO13 # = D7
    tx_pin: GPIO15 # = D8
    baud_rate: 9600


# For BME280 & OLED
i2c:
  sda: GPIO4 # = D2 = 4
  scl: GPIO5 # = D1 = 5
  scan: False
  frequency: 100kHz # Trying to get more stable OLED value display

# For Dallas temp sensors connected to pin GPIO0 = D3, enable internal pull-up
# resistor
dallas:
  id: ds18b20_temp_sensor
  pin:
    number: GPIO0 # D3 = 0
    # inverted: True
    mode: INPUT_PULLUP
  update_interval: 60s # if changed also update EMA alpha

sensor:
  - platform: wifi_signal
    name: "WiFi Signal"
    update_interval: 60s
    id: mywifi
  - platform: uptime
    name: Uptime
    update_interval: 60s
    id: myuptime
  - platform: sds011
    pm_2_5:
      state_topic: influx/environv3/quantity/pm25/source/sds011/board/esp_bigsensorthing/location/home/room/living/value/state
      name: "SDS011 PM2.5"
      id: sds011_pm25
    pm_10_0:
      state_topic: influx/environv3/quantity/pm10/source/sds011/board/esp_bigsensorthing/location/home/room/living/value/state
      name: "SDS011 PM10"
      id: sds011_pm10
    update_interval: 10min # factory default 0min, set to 10min to save limited 8000h laser lifetime. Also reduces fan noise
    id: mysds011
    uart_id: myuart0
  - platform: dallas
    index: 0
    name: "DS18B20 Temperature"
    state_topic: influx/environv3/quantity/T/source/dallas1/board/esp_bigsensorthing/location/home/room/living/value/state
    id: ds18b20_temp
  - platform: mhz19
    co2:
      name: "MH-Z19 CO2"
      state_topic: influx/environv3/quantity/CO2/source/mhz19b/board/esp_bigsensorthing/location/home/room/living/value/state
      id: mhz_19_co2
    temperature:
      name: "MH-Z19 Temperature"
      state_topic: influx/environv3/quantity/T/source/mhz19b/board/esp_bigsensorthing/location/home/room/living/value/state
      id: mhz_19_temp
    update_interval: 60s
    uart_id: myuart1
  - platform: mhz19
    co2:
      name: "MH-Z19 CO2"
      id: mhz_19_co2_ema
      filters:
      - exponential_moving_average:
          alpha: 0.0028 # We want ~12 hour averaging, at 60s update, that's 12*3600/60 = 720points, thus alpha should be 2/1440 ~ 0.0028 
          send_every: 1
    update_interval: 60s # if changed also update alpha
    uart_id: myuart1
  - platform: bme280
    temperature:
      name: "BME280 Temperature"
      oversampling: 1x
      id: bme_280_temp
      state_topic: influx/environv3/quantity/T/source/bme280/board/esp_bigsensorthing/location/home/room/living/value/state
    pressure:
      name: "BME280 Pressure"
      id: bme_280_press
      state_topic: influx/environv3/quantity/P/source/bme280/board/esp_bigsensorthing/location/home/room/living/value/state
    humidity:
      name: "BME280 Humidity"
      id: bme_280_rh
      state_topic: influx/environv3/quantity/RH/source/bme280/board/esp_bigsensorthing/location/home/room/living/value/state
    address: 0x76
    update_interval: 60s
  - platform: bme280
    humidity:
      name: "BME280 Humidity"
      id: bme_280_rh_ema
      filters:
      - exponential_moving_average:
          alpha: 0.0028 # We want ~12 hour averaging, at 60s update, that's 12*3600/60 = 720points, thus alpha should be 2/1440 ~ 0.0028 
          send_every: 1
    address: 0x76
    update_interval: 60s # if you change this don't forget to update alpha
    
font:
  - file: "slkscr.ttf"
    id: my_font1
    size: 8
  - file: "Arial.ttf"
    id: my_font3
    size: 16
    # # I want to use ø, drop unused chars in return
    glyphs: 
      ['ø', '~', '.', '%', '(', ')', '+', '-', '_', ':', '°', '0',
      '1', '2', '3', '4', '5', '6', '7', '8', '9', ' ', '/']

display:
  - platform: ssd1327_i2c
    model: "SSD1327 128x128"
    update_interval: 30s # Don't forget to set this to prevent esphome crash due to default 5s
    # reset_pin: D0
    address: 0x3C
    rotation: 180°

    # Small font, without EMA
    # lambda: |-
    #   it.printf(0, 0, id(my_font1), "CO2 (PPM): %d", int(id(mhz_19_co2).state));
    #   it.printf(0, 10, id(my_font1), "T1 (C):   %.1f", id(ds18b20_temp).state);
    #   it.printf(0, 20, id(my_font1), "T2 (C):   %.1f", id(bme_280_temp).state);
    #   it.printf(0, 30, id(my_font1), "RH (%%):  %.1f", id(bme_280_rh).state);
    #   it.printf(0, 40, id(my_font1), "P (HPa):  %d", int(id(bme_280_press).state));
    #   it.printf(0, 50, id(my_font1), "PM (2.5): %5d",  int(id(sds011_pm25).state));
    #   it.printf(0, 60, id(my_font1), "PM (10):  %5d", int(id(sds011_pm10).state));

    # Bigger font, mixed EMA      
    lambda: |-
      it.printf(0,  0, id(my_font1), "CO2");
      it.printf(0,  8, id(my_font1), "PPM");
      it.printf(24, 0, id(my_font3), "%d (~%d)", int(id(mhz_19_co2).state), int(id(mhz_19_co2_ema).state));
      
      it.printf(0, 16, id(my_font1), "T1");
      it.printf(0,  24, id(my_font1), "C");
      it.printf(24, 16, id(my_font3), "%.1f", id(ds18b20_temp).state);

      it.printf(0, 32, id(my_font1), "T2");
      it.printf(0,  40, id(my_font1), "C");
      it.printf(24, 32, id(my_font3), "%.1f", id(bme_280_temp).state);
      
      it.printf(0, 48, id(my_font1), "RH");
      it.printf(0,  56, id(my_font1), "%%");
      it.printf(24, 48, id(my_font3), "%.1f (~%.1f)", (id(bme_280_rh).state), (id(bme_280_rh_ema).state));
      
      it.printf(0, 64, id(my_font1), "P");
      it.printf(0, 72, id(my_font1), "HPa");
      it.printf(24, 64, id(my_font3), "%d", int(id(bme_280_press).state));

      it.printf(0, 80, id(my_font1), "PM");
      it.printf(0, 88, id(my_font1), "2.5");
      it.printf(24, 80, id(my_font3), "%.1f", id(sds011_pm25).state);

      it.printf(0, 96, id(my_font1), "PM");
      it.printf(0, 104, id(my_font1), "10");
      it.printf(24, 96, id(my_font3), "%.1f", id(sds011_pm10).state);

Future work/alternatives

Volgende: Measuring CO2/Temp/RH/Pressure with ESP8266 version 2 03-01 Measuring CO2/Temp/RH/Pressure with ESP8266 version 2

Comments


By Tweakers user vanaalten, Thursday 29 April 2021 07:46

Maar, ehm, ik neem aan dat je vooral let op de 2.5µm fijnstof-detectie? En wat doe je met de uitkomst, wat voor actie onderneem je als 'ie te hoog wordt? Alle ramen dicht of juist open?

By Tweakers user SeenD, Thursday 29 April 2021 09:28

Nice, great idea. I should make something like that as well.
I like the big amount of sensors. :)

Thinking about creating some kind of tricorder, device with loads of sensors, like star trek. And this is an awesome version 1.

[Comment edited on Thursday 29 April 2021 09:40]


By mark, Thursday 29 April 2021 16:01

How did you create th custom made PCB?

By Tweakers user i-chat, Thursday 29 April 2021 16:03

Maar, ehm, ik neem aan dat je vooral let op de 2.5µm fijnstof-detectie? En wat doe je met de uitkomst, wat voor actie onderneem je als 'ie te hoog wordt? Alle ramen dicht of juist open?

ramen, daar heb je toch gewoon koolstof en hepafilters voor? gewoon een paar goede presurefans in een doos filtertjes ervoor en HOPPA

By Tweakers user Atomstar, Saturday 1 May 2021 09:05

vanaalten wrote on Thursday 29 April 2021 @ 07:46:
Maar, ehm, ik neem aan dat je vooral let op de 2.5µm fijnstof-detectie? En wat doe je met de uitkomst, wat voor actie onderneem je als 'ie te hoog wordt? Alle ramen dicht of juist open?
Wat bedoel je met 2.5micron? Omdat die het dodelijkst zijn? In de praktijk merk ik dat ze redelijk gecorreleerd zijn. Als het te hoog wordt ren ik weg ;) of doe ik ramen open of de afzuigkap harder.

By Tweakers user Atomstar, Saturday 1 May 2021 09:06

SeenD wrote on Thursday 29 April 2021 @ 09:28:
Nice, great idea. I should make something like that as well.
I like the big amount of sensors. :)

Thinking about creating some kind of tricorder, device with loads of sensors, like star trek. And this is an awesome version 1.
Haha thanks, tricorder sounds cool 😎. Just need a battery pack. I did manage to run this in my car though, connecting to WiFi of my phone to connect to my home server.

By Tweakers user Atomstar, Saturday 1 May 2021 09:08

mark wrote on Thursday 29 April 2021 @ 16:01:
How did you create th custom made PCB?
I designed it using Fritzing (poor tool but easy to learn), and printed it al jlcpcb. I have extras, you can get one here 😎

By Tweakers user GekkePrutser, Sunday 2 May 2021 18:45

Tof project. Ik dacht er zelf ook aan om er eentje aan mijn home assistant te koppelen, en ook andere sensoren als VOOC en CO2.

Maar een particulate sensor heb ik op zich al, deze zit in mijn Xiaomi Air Purifier. Die kan je ook in home assistant uitlezen. Dus voorlopig heb ik het maar laten zitten.

Ik moet trouwens zeggen dat het me allemaal heel erg meevalt. Buiten is het ongeveer 50-60 PPM (heb zelf geen buitensensor maar iemand in de buurt zit op luftdaten en daar is ook een HA plugin voor). 2.5 micron particles, binnen zit ik rond de 1 a 2. Prima dus. Ik dacht eerst zelfs dat de sensor in de Xiaomi niet goed was omdat hij nooit boven de 10 uitkwam, maar toen er buiten een keer een lichte brandlucht was (smeulende afvalbak), sprong hij gelijk naar de 200. Ondanks dat er binnen alleen een hele lichte geur was, niet eens rook te zien.

Nou heb ik wel 's nachts de purifier altijd aan, en in dit seizoen overdag trouwens ook (hooikoorts) dus misschien helpt het wel veel. Ramen open zal gezien de getallen buiten vooral minder zijn. Ik woon dan ook in een drukke stad. Al heb ik de ramen wel vaak open natuurlijk want voor andere dingen zoals CO2 is het natuurlijk wel veel beter.

[Comment edited on Sunday 2 May 2021 18:53]


By Tweakers user ThomasAH, Wednesday 12 May 2021 14:34

Mooie blogpost (ook de voorgaande delen) over een interessant project! Leuk ook dat je zo gedetailleerd hard- en software beschrijft. Dank voor 't delen! :)

Vraagje: je hangt deze binnen ergens op neem ik aan? Zou denk ik interessant zijn als je deze buiten ergens aan een gevel kan hangen om daar alles te meten. Heb je dat wel eens overwogen, en zo ja, wat waren je afwegingen en/of opties daarvoor?

[Comment edited on Wednesday 12 May 2021 14:35]


By Tweakers user Atomstar, Sunday 16 May 2021 18:01

GekkePrutser wrote on Sunday 2 May 2021 @ 18:45:
Tof project. Ik dacht er zelf ook aan om er eentje aan mijn home assistant te koppelen, en ook andere sensoren als VOOC en CO2.

Maar een particulate sensor heb ik op zich al, deze zit in mijn Xiaomi Air Purifier. Die kan je ook in home assistant uitlezen. Dus voorlopig heb ik het maar laten zitten.
Bedankt! :) Maar zelf bouwen is natuurlijk veel leuker ;)
ThomasAH wrote on Wednesday 12 May 2021 @ 14:34:
Mooie blogpost (ook de voorgaande delen) over een interessant project! Leuk ook dat je zo gedetailleerd hard- en software beschrijft. Dank voor 't delen! :)

Vraagje: je hangt deze binnen ergens op neem ik aan? Zou denk ik interessant zijn als je deze buiten ergens aan een gevel kan hangen om daar alles te meten. Heb je dat wel eens overwogen, en zo ja, wat waren je afwegingen en/of opties daarvoor?
Bedankt! :) Ik heb 'm binnen staan inderdaad. Op Luftdaten (waar GekkePrutser naar verwijst) staat een guide om een buiten-versie te maken die tegen regen kan. Maar omdat ik meestal binnen zit en die lucht adem is dit denk ik relevanter. Verder weet ik niet hoe groot het verschil tussen binnen en buiten is, misschien eens checken/calibreren.

Comment form
(required)
(required, but will not be displayed)
(optional)