Built-in project · Arduino Uno R3

PMS5003 dust monitor on an Arduino Uno

A PMS5003 laser particle sensor sends PM1.0, PM2.5 and PM10 as serial frames, and the Uno shows them on an LCD with an air quality grade. Drag the dust sliders. The whole build, an Arduino Uno and 2 more parts, runs here in your browser on the firmware below; open it in the editor to change the wiring or the code and run it again.

Teaching? Assign it to your class in one click.

Intermediate About 15 minutes to build and run. Runs in your browser. Free, and no account needed.

The parts retail for about $46. On Mokxi they're free.

The circuit runs here once the page has loaded.

What you'll learn

  • Sensors
  • Using the Air quality sensor
  • Using the Character LCD with I2C backpack
  • Difficulty: Intermediate
  • Time: About 15 minutes
  • Board: Arduino Uno R3
  • Parts: 4

How it works

A PMS5003 laser particle sensor sends PM1.0, PM2.5 and PM10 as serial frames, and the Uno shows them on an LCD with an air quality grade. Drag the dust sliders.

Arduino Uno R3 runs the sketch below. The circuit around it: Air quality sensor, PMS5003, Character LCD with I2C backpack. Serial.begin opens the serial port at 115200 baud.

Parts list

4 parts, plus the jumper wires. They retail for about $46; on Mokxi they're free.

Wiring table

Every connection, pin by pin, read from the circuit itself. Each row joins the pin on the left to the pin on the right, by a jumper wire or a breadboard strip.

Part and pinConnects to
Air quality sensor, PMS5003 pin GNDArduino Uno R3 pin GND
Air quality sensor, PMS5003 pin RXArduino Uno R3 pin 3
Air quality sensor, PMS5003 pin TXArduino Uno R3 pin 2
Air quality sensor, PMS5003 pin VCCArduino Uno R3 pin 5V
Character LCD with I2C backpack pin GNDArduino Uno R3 pin GND
Character LCD with I2C backpack pin SDAArduino Uno R3 pin A4
Character LCD with I2C backpack pin SCLArduino Uno R3 pin A5
Character LCD with I2C backpack pin VCC5 V

The code

The firmware exactly as the editor opens it. Change a line there and press Run: it compiles in the browser.

sketch.ino · Arduino Uno R3
// Dust monitor: a Plantower PMS5003 laser particle sensor and a 16x2 I2C LCD.
//
//   PMS5003 TX -> pin 2    PMS5003 RX <- pin 3    VCC -> 5 V    GND -> GND
//   LCD     SDA -> A4      SCL -> A5              VCC -> 5 V    GND -> GND
//
// The sensor sends a 32 byte frame at 9600 baud about every two seconds: PM1.0,
// PM2.5 and PM10 in micrograms per cubic meter. The PMS library takes the
// frame one byte per read() call, so loop() calls it as often as it can and
// does the printing only when a whole frame has arrived.
//
// The second line grades the PM2.5 with the US EPA's 24 hour bands: Good up to
// 12, Moderate up to 35, "Unhealthy SG" (sensitive groups) up to 55, Unhealthy
// over that. A real room needs the 30 seconds the sensor asks for after power
// on before its numbers settle; here the first frame is already right.
//
// Drag the PM2.5 slider on the sensor and watch the grade change.

#include <LiquidCrystal_I2C.h>
#include <PMS.h>
#include <SoftwareSerial.h>

SoftwareSerial pmsSerial(2, 3);  // RX (the sensor's TX), TX (the sensor's RX)
PMS pms(pmsSerial);
PMS::DATA data;
LiquidCrystal_I2C lcd(0x27, 16, 2);

const char *grade(uint16_t pm25) {
  if (pm25 <= 12) return "Good";
  if (pm25 <= 35) return "Moderate";
  if (pm25 <= 55) return "Unhealthy SG";
  return "Unhealthy";
}

void setup() {
  Serial.begin(115200);
  pmsSerial.begin(PMS::BAUD_RATE);
  lcd.init();
  lcd.backlight();
  lcd.print("Waiting for PMS");
  Serial.println("PMS5003 dust monitor");
}

void loop() {
  if (!pms.read(data)) return;

  Serial.print("PM1.0 ");  Serial.print(data.PM_AE_UG_1_0);
  Serial.print("  PM2.5 "); Serial.print(data.PM_AE_UG_2_5);
  Serial.print("  PM10 ");  Serial.println(data.PM_AE_UG_10_0);

  lcd.clear();
  lcd.print("PM2.5 ");
  lcd.print(data.PM_AE_UG_2_5);
  lcd.print(" PM10 ");
  lcd.print(data.PM_AE_UG_10_0);
  lcd.setCursor(0, 1);
  lcd.print(grade(data.PM_AE_UG_2_5));
}

Code walkthrough

The key functions of the sketch, and what each one does.

setup sketch.ino

Runs once when the board starts. It gets the pins and libraries ready.

void setup() {
  Serial.begin(115200);
  pmsSerial.begin(PMS::BAUD_RATE);
  lcd.init();
  lcd.backlight();
  lcd.print("Waiting for PMS");
  Serial.println("PMS5003 dust monitor");
}

loop sketch.ino

Runs over and over for as long as the board has power. This is where the project does its work.

void loop() {
  if (!pms.read(data)) return;

  Serial.print("PM1.0 ");  Serial.print(data.PM_AE_UG_1_0);
  Serial.print("  PM2.5 "); Serial.print(data.PM_AE_UG_2_5);
  Serial.print("  PM10 ");  Serial.println(data.PM_AE_UG_10_0);

  lcd.clear();
  lcd.print("PM2.5 ");
  lcd.print(data.PM_AE_UG_2_5);
  lcd.print(" PM10 ");
  lcd.print(data.PM_AE_UG_10_0);
  lcd.setCursor(0, 1);
  lcd.print(grade(data.PM_AE_UG_2_5));
}

Troubleshooting

The usual mistakes with this circuit, and the fix for each.

  • The screen stays blank or shows only a row of blocks. Character LCD with I2C backpack
    The I2C address is usually 0x27 or 0x3F. Run an I2C scanner to find yours. Then turn the contrast trimmer on the backpack slowly until text appears.
  • The backlight is on but there is no text, or text is garbled. Character LCD with I2C backpack
    Wire SDA and SCL to the right pins (A4 and A5 on an Uno), power it from 5 V, and call lcd.init() and the backlight call in setup.
  • Upload fails or nothing runs after wiring pins 0 and 1 to a module. Arduino Uno R3
    Pins 0 and 1 are the USB serial port. Move the module to other pins, or unplug it while uploading. Also check the right board is selected.
  • Pins misbehave or the board resets when you power motors or servos from it. Arduino Uno R3
    Each I/O pin gives about 20 mA, 40 mA max. Power motors and servos from their own supply and join its ground to the Uno's GND.

Change it and keep it

Open it in the editor, change the circuit or the code, and keep your version in a free account.