Taking stock of Particles

A project log for MultiBot CNC v2

A low cost 3D printed CNC that can be built with minimal tools yet is capable of great things.

David TuckerDavid Tucker 05/28/2022 at 23:500 Comments

So a while ago I picked up this low cost dust monitor from somewhere.  It is based around a Plantower PMS5003 Air Quality Sensor.  It never seems to work correctly, that is the PM 2.5 and PM 5 values almost never move above 3, even when I'm cutting with the laser.  A while back I ordered a new dust sensor to see if that was the problem, however the new sensor acts the same as the original.

Since I now have two sensors, and it appears they both work equally well (or bad) I decided to wire one up to a M5StickC Plus module and see what I could get out of it. This actually works out really well.  The M5StickC is a 3.5v module, but it has a 5v battery so we can supply the needed 5v to run the fan, while still using 3.5v for the logic pins.  

The code is very simple, and minimal.  Here it is in its full.  This relies on the Adafruit PM25AQI library for the communication.  90% of the code is just moving data from the sensor to the serial port and display, easy peasy.

#include <M5StickCPlus.h>
#include <SoftwareSerial.h>
#include "Adafruit_PM25AQI.h"

SoftwareSerial swSerial(25, 26); // in/out
Adafruit_PM25AQI aqi = Adafruit_PM25AQI();
PM25_AQI_Data data;

void setup()


  while (!Serial) delay(10);

  // Wait one second for sensor to boot up



void loop()
  if (swSerial.available() &&
#if 0
    Serial.print("PM1:"); Serial.print(data.pm10_standard);
    Serial.print(",PM2_5:"); Serial.print(data.pm25_standard);
    Serial.print(",PM10:"); Serial.print(data.pm100_standard);
    Serial.print(",ePM1:"); Serial.print(data.pm10_env);
    Serial.print(",ePM2_5:"); Serial.print(data.pm25_env);
    Serial.print(",ePM10:"); Serial.print(data.pm100_env);

    M5.Lcd.setCursor(0, 0);

    M5.Lcd.print("010:"); M5.Lcd.println(data.pm10_standard);
    M5.Lcd.print("025:"); M5.Lcd.println(data.pm25_standard);
    M5.Lcd.print("100:"); M5.Lcd.println(data.pm100_standard);
    M5.Lcd.print("010:"); M5.Lcd.println(data.pm10_env);
    M5.Lcd.print("025:"); M5.Lcd.println(data.pm25_env);
    M5.Lcd.print("100:"); M5.Lcd.println(data.pm100_env);
    Serial.print("P3:"); Serial.print(data.particles_03um);
    Serial.print(",P5:"); Serial.print(data.particles_05um);
    Serial.print(",P10:"); Serial.print(data.particles_10um);
    Serial.print(",P25:"); Serial.print(data.particles_25um);
    Serial.print(",P50:"); Serial.print(data.particles_50um);
    Serial.print(",P100:"); Serial.print(data.particles_100um);

    M5.Lcd.setCursor(0, 0);

    M5.Lcd.print("003:"); M5.Lcd.println(data.particles_03um);
    M5.Lcd.print("005:"); M5.Lcd.println(data.particles_05um);
    M5.Lcd.print("010:"); M5.Lcd.println(data.particles_10um);
    M5.Lcd.print("025:"); M5.Lcd.println(data.particles_25um);
    M5.Lcd.print("050:"); M5.Lcd.println(data.particles_50um);
    M5.Lcd.print("100:"); M5.Lcd.println(data.particles_100um);

 After letting this run while doing some engraving and then cutting I ended up with the following plot.  You can't tell from this plot but most of the data on the left was collected when engraving.  It produced a signal about 3x stronger than the baseline, but about 20x less strong than when cutting. I never saw the 5 nanometer and 10 nanometer readings go above 1, I suspect this detector is incapable of classifying anything larger than 2.5 nanometers.  Also you can see in this graph that the sensor peaks out at 65535, and that was with the sensor being upwind from the laser and not really in the heavy smoke.

Looking at the raw sensor data, it turns out the PM 2.5 and PM 5 values are in fact fairly useless.  They don't have a fractional component so they are in fact only set to 1,2,3... and even at high smoke count the PM levels just don't get very high, it is not very good at letting you know how much smoke is in the air.

The particle counters on the other hand have more potential.  They seem very responsive and they break down the particle size into bins ranging from 0.01 to 2.5 um.  However I struggled to find much information tying particle size to PM (particle mass).  It turns out that PM 2.5 is the measure of the mass of all particles at a size of 2.5 um and smaller.  Basically you take each count from each bin and multiply that by the estimation of the average mass of the particles you expect to see in the air to derive the PM 2.5 value. This is basically a wild guess, because you don't know what the weight of the particle is, only its size, and the size and weight are not strongly correlated.

Anyway PM2.5 is almost completely dominated by particles in the 2.5 um diameter, with the smaller and much more common particle sizes not contributing much of anything to the mass of the material.  This contributes to the PM2.5 output being so binary since we rarely get any counts of particles in the PM2.5 bins.

Here are some more papers that may help sort this out, if you are curious.