PMS7003 Particulate matter sensor

A minimal driver for the Plantower PMS 7003 particulate matter sensor with Serial output. Might work with Plantower PMS5003 that uses the same protocol, but not tested. Use the PMS7003 (About Modules) module for it.

Here is an English translation of the Datasheet.

For minimal configuration, you only need to provide power (5V) to the PMS sensor, and it automatically sends measurements. Hook up it's TX pin to an Espruino's serial RX pin and you are good to go!


The connector on the sensor itself is tiny, 2x5 pin, 1.27mm pitch. Soldering does take some time. Pin1 is the one closest to the corner. The seller may offer an adapter cable, that's probably easier to use. The fan in the sensor uses 5 volt, but all of it's communication lines are 3.3v.

Connect via 10pin connector on the sensor

PMS7003 pins

Device Pin Espruino
1 (5V) 5V
2 (5V) 5V
5 (Reset) -
6 (N/C) -
7 (RX) -
8 (N/C) -
9 (TX) Serial RX pin
10 (Set) -

You can leave all other pins unconnected, they have internal pull-ups. The sensor automatically starts sending data, I measured a varying power consumption between 25-50mA.

Connect via seller supplied cable

Check the seller's description for pinout!

How to use the module

var PMS = require('PMS7003');

// define the callback that will be called by the module
function onPms(d) {
    if (d.checksumOk) {
        // Checksum OK, show it on a display, send thru MQTT...
        console.log('PMS data: ', d);
    } else {
        // Checksum error, discard it
        console.log('PMS checksum ERR!');

// Connecting with the Espruino Wifi's Serial1
var pms = PMS.connect(Serial1, B7, onPms);

And the sensor sends a new set of data every 800-900ms.

The response data

    checksumOk: boolean, // is the checksum OK? Discard if false, other properties will not be present!
    dCF1: PmData,        // CF=1, standard particle
    dAtm: PmData         // Under atmospheric environment

According to the information on AQICN, the "Atmospheric environment" data should be used.

PmData is an object with the following properties:

    pm1: number (integer),    // PM 1.0 concentration [μg/m3]
    pm2_5: number (integer),  // PM 2.5 concentration [μg/m3]
    pm10: number (integer)    // PM 10 concentration [μg/m3]

Sample output

  "dCF1": { "pm1": 11, "pm2_5": 20, "pm10": 20 },
  "dAtm": { "pm1": 11, "pm2_5": 20, "pm10": 20 },
  "checksumOk": true

Detailed data

The sensor sends more details, but probably not usefull for everybody, so to save some CPU cycles and memory, it's enabled by setting pms.details to true or if you pass true as the fourth paramenter to the connect() function. The extended response contains more fields with raw data:

    d7_0_3um:   Data 7 indicates the number of particles with diameter beyond 0.3 um in 0.1 L of air.
    d8_0_5um:   Data 8 indicates the number of particles with diameter beyond 0.5 um in 0.1 L of air.
    d9_1_0um:   Data 9 indicates the number of particles with diameter beyond 1.0 um in 0.1 L of air.
    d10_2_5um:  Data 10 indicates the number of particles with diameter beyond 2.5 um in 0.1 L of air.
    d11_5_0um:  Data 11 indicates the number of particles with diameter beyond 5.0 um in 0.1 L of air.
    d12_10_0um: Data 12 indicates the number of particles with diameter beyond 10 um in 0.1 L of air.

You can toggle this with setting pms.details to true or false.

Sample output with details

  "dCF1": { "pm1": 2, "pm2_5": 3, "pm10": 3 },
  "dAtm": { "pm1": 2, "pm2_5": 3, "pm10": 3 },
  "checksumOk": true,
  "d7_0_3um": 417,
  "d8_0_5um": 132,
  "d9_1_0um": 10,
  "d10_2_5um": 4,
  "d11_5_0um": 0,
  "d12_10_0um": 0

Power consumption & power down modes

You can use the pms.sleep() command to power down the sensor to save energy and the lifetime of the fan and laser diode in the sensor. For this to work you will need to connect the RX pin 7 to your serial TX pin. To wake the sensor up again, use pms.wakeup().

Device Pin Espruino
1 (5V) 5V
2 (5V) 5V
5 (Reset) -
6 (N/C) -
7 (RX) Optional, Serial TX pin
8 (N/C) -
9 (TX) Serial RX pin
10 (Set) -

Measured currents on my unit:

Mode Measured power
"On" 25 - 50 mA

The "On" current varies in each 800-900ms cycle, but don't have the equipment to measure the average current.

If sleep mode is not low-power enough, it's probably better to just cut power to the sensor if you want to save power. You can do this using a 5V boost converter with "true shutdown". The sensor just start sending data again after 2-3 seconds. Probably you should discard the first couple of measurements after power-on.


PmsDriver.prototype.sleep = function () { ... }

PmsDriver.prototype.wakeup = function () { ... }

PmsDriver.prototype.onSerial = function (sd) { ... }

/* Returns a new PmsDriver object that handles the data coming from the PMS7003.
  serialPort: Serial - a serial port, can be hw or sw serial. Required.
 rxPin: Pin - The rx pin for the serial port. Required.
 txPin: Pin - The tx pin for the serial port. Required for sleep/wakeup
 callback: function - callback to be called with the data.
 details: boolean - should the callback include the raw data from the sensor? Optional.
exports.connect = function (serialPort, rxPin, txPin, callback, details) { ... }


You can buy it from china, for example from Aliexpress: only the sensor 12-13$, or with adapter cable 17-18$.

This page is auto-generated from GitHub. If you see any mistakes or have suggestions, please let us know.