Pmsx003 Air quality sensor longevity and data stability

this was a pms5003. now I’m thinking that the first few hours was a “break-in” period of false high numbers, since it seems to be reacting fine to “events” like cooking and candles now, but the numbers are a lot lower than I expected and are mostly at all 0s for all particulate sizes most of the time.

Is that normal? I don’t know. I haven’t been able to find any data on what typical PM1, PM2.5, and PM10 numbers are supposed to look like indoors. Can someone share some typical graphs of theirs?

Try finding some nearby outdoor air quality sensors working from pm2.5 online. Stick yours outside(but protected from the elements) and see if it generally matches the trends and values.

I tried and it seems okay and pretty close to nearby sensors. I guess my house is really clean, and the first few hours was a break-in period or something (dust inside after manufacturing?). Seems to be working well now. Thanks a lot

I recently set a Pms7003 sensor up also. I went with a rpi Pico W and Mqtt, but was more curious what people are using for enclosures. Any examples? Mine is currently setting on a breadboard in the living room, need something with a higher WAF. Unfortunately, I don’t have access to a 3d printer.

I’ve unfortunately just been using the cardboard box it came in, with one flap cut off. There’s space in there for an ESP. I haven’t tested it for accuracy after being in there yet, though.

1 Like

Search for ‘ABS enclosure’ on Amazon. Wide variety of sizes.

Best solution I found is to use the soldering iron near the sensor. You’ll see a sharp increase in particulate. I turn the soldering iron on, then just melt some solder close to the sensor.

1 Like

Or cook some bacon, bad for your health in more than one way…

2 Likes

Or light a candle and blow it out near the sensor.

1 Like

Is there a consensus on how often to poll the 7003 and how long the warm up period should be? I’m currently sampling every five minutes with a thirty second warmup. This is pulling both SET and RST to ground between reads. I’m then taking ten samples and applying an interquartile range filter for the final result. Results seem reasonable, just curious about what others are seeing.

Current setup sans enclosure.

Cludgy micropython repo.

My current dashboard setup. Gonna try to get an AQI number like @PrairieSnpr

My esphome is configured to sample every 30 seconds from a PSMA003. The latest version of esphome will start and stop the fan without having to connect to the RST pin, using update_interval. I’m just using the TX/RX signals and esphome takes care of stopping/starting. My PSMA initially always read 0, but over time it has gotten more accurate.

In home assistant I’m running a mean over the last 7 readings. During the day it’s not uncommon for the readings to sit at 0. Cooking eggs or using the oven sends the sensors very high.

After considering everything in this thread and the requirements for this sensor (saving the laser lifetime and allowing a warm-up period), and several hours of trial and error, I’ve come up with this sleek ESPHome configuration.

It turns on the laser once every 5 minutes (adjustable; just change the - interval: 300s line) for 40 seconds, throwing away the first 30 seconds (warm-up) and using the last 10 seconds, and averaging those 10 readings together into a single number for higher resolution (to one decimal place instead of none).

This should let the laser live for years to come, and the data will be very stable because the fan has had time to spin up and warm up. You can start a measurement period manually through Home Assistant thanks to the “Measuring” switch as well.

esphome:
  name: air-quality-sensor

esp8266:
  board: esp8285

# Enable logging
logger:

## Enable Home Assistant API                                                                       
#api:
#  password: "xxxxxxx"

ota:
  password: "xxxxxxx"

wifi:
  ssid: "xxxxxxx"
  password: "xxxxxxx"

  # Enable fallback hotspot (captive portal) in case wifi connection fails
  ap:
    ssid: "Air-Quality-Sensor Fallback Hotspot"
    password: "xxxxxxxx"

captive_portal:

mqtt:
  broker: xxxxxxxx
  username: xxxxxxx
  password: xxxxxxx
  discovery: false
  topic_prefix: sensor/air-quality

uart:
  tx_pin: GPIO12
  rx_pin: GPIO13
  baud_rate: 9600


# The 3-second debounce only publishes the average (otherwise published
# every second) once the per-second readings stop (i.e. the measurement 
# period ends). This makes sure the output average doesn't slip back a
# reading every measurement period.
sensor:

  - platform: pmsx003
    type: PMSX003
    pm_1_0:
      name: "Particulate Matter <1.0µm Concentration"
      id: pms_10
      accuracy_decimals: 1
      filters:
      - lambda: |-
          if (id(warmed_up).state) {
            return x;
          } else {
            return {};
          }
      - sliding_window_moving_average:
          window_size: 10
          send_every: 1
          send_first_at: 1
      - debounce: 3s
    pm_2_5:
      name: "Particulate Matter <2.5µm Concentration"
      id: pms_25
      accuracy_decimals: 1
      filters:
      - lambda: |-
          if (id(warmed_up).state) {
            return x;
          } else {
            return {};
          }
      - sliding_window_moving_average:
          window_size: 10
          send_every: 1
          send_first_at: 1
      - debounce: 3s
    pm_10_0:
      name: "Particulate Matter <10.0µm Concentration"
      id: pms_100
      accuracy_decimals: 1
      filters:
      - lambda: |-
          if (id(warmed_up).state) {
            return x;
          } else {
            return {};
          }
      - sliding_window_moving_average:
          window_size: 10
          send_first_at: 1
          send_every: 1
      - debounce: 3s


binary_sensor:

  - platform: template
    name: "Warmed Up"
    id: warmed_up
    filters:
      - delayed_on: 30s
    on_press:
      - delay: 10s
      - switch.turn_off: measuring


switch:

  - platform: gpio
    name: "Measuring"
    id: measuring
    pin:
      number: GPIO14
    restore_mode: ALWAYS_OFF
    on_turn_on:
      - binary_sensor.template.publish:
          id: warmed_up
          state: ON
    on_turn_off:
      - binary_sensor.template.publish:
          id: warmed_up
          state: OFF

  - platform: restart
    name: "Restart"


interval:

  - interval: 300s
    then:
      - wait_until:
          condition:
            mqtt.connected: # Can use api.connected if using HA API instead
      - switch.turn_on: measuring # Will turn itself off later
4 Likes

3d printer enclosure for me.

My device also has CO2, temp, humidity.

Plus audio/visual alerts and displays etc. Beeps/flashes when above thresholds.

You can see the PMS in/outlet on the side.

2 Likes

Pretty sure the “30sec warm-up” is from the datasheet.

So that’s kind of a “must”. Or at least “should”.

Then the rest of the polling interval is really a tradeoff which is up to you and your use case - long life vs latency…

Median is master for me. As far as an aggregation. It’s a “robust measure” of center.

Sick! What are the doodads on the front bottom?

Rttl buzzer plays different tunes at different thresholds.

Led flashes fast/slow at different thresholds.

Button press does a bunch of things:
Dismiss alerts if alerting.
Long press toggles silent mode.
Short press switches pages

Most of the programming/hardware is the same as this.

Thanks for this, I’ve just deployed it to interface a PMS7003 to an ESP-01S and its solved the longevity issue quite nicely.

1 Like

apologies, this is probably a dumb question. In your code you have gpio14 connected to something for the “measuring” section. Is this pin actually connected to anything in real life? At first I thought it was connected to the SET cable of the PMS but now im unsure and think it doesnt actually connect to anything?

Thanks!

GPIO 14 goes to the SET pin on the PMS. It’s what controls whether the laser is on or not

1 Like