Arduino library for PM sensors with serial interface
Plantower | Tested Works | Doesn't Work | Not Tested | Datasheet | Notes |
---|---|---|---|---|---|
PMS1003 (aka G1) | X | en, cn | |||
PMS3003 (aka G3) | X | en, cn | No passive mode read | ||
PMS5003 (aka G5) | X | en, cn | |||
PMS5003S (aka G5S) | X | cn | |||
PMS5003T (aka G5T) | X | ||||
PMS5003ST (aka G5ST) | X | ||||
PMS7003 (aka G7) | X | cn | |||
PMSA003 (aka G10) | X | cn |
MCU | Tested Works | Doesn't Work | Not Tested | Examples | Notes |
---|---|---|---|---|---|
ATmega168 @ 8MHz | X | SoftwareSerial | |||
Atmega168 @ 16MHz | X | 5V boards need 3.3V/5V level shifter | |||
Atmega328 @ 8MHz | X | SoftwareSerial | |||
Atmega328 @ 16MHz | X | 5V boards need 3.3V/5V level shifter | |||
Atmega32u4 @ 8MHz | X | ||||
Atmega32u4 @ 16MHz | X | HardwareSerial | 5V boards need 3.3V/5V level shifter | ||
Atmega2560 @ 16MHz | X | HardwareSerial | 5V boards need 3.3V/5V level shifter | ||
STM32f103c8 | X | HardwareSerial | |||
STM32f103cb | X | HardwareSerial | |||
ESP8266 | X | HardwareSerial SoftwareSerial OLED 64x48 | needs EspSoftwareSerial@>=6.7.1 | ||
ESP32 | X | HardwareSerial SoftwareSerial OLED 64x48 | Serial1 as SoftwareSerial |
The PMSx003
sensor type will infer the sensor type from the message header.
The sensor type inference does not cover the PMS5003S
and PMS5003T
variants, see #10.
PMS5003S
and PMS5003T
sensors need to be declared explicitly on the SerialPM
constructor.
#include <PMserial.h>
SerialPM pms(PMSx003, Serial); // PMSx003, UART
void setup() {
Serial.begin(9600);
pms.init(); // config serial port
}
void loop() {
pms.read(); // read the PM sensor
Serial.print(F("PM1.0 "));Serial.print(pms.pm01);Serial.print(F(", "));
Serial.print(F("PM2.5 "));Serial.print(pms.pm25);Serial.print(F(", "));
Serial.print(F("PM10 ")) ;Serial.print(pms.pm10);Serial.println(F(" [ug/m3]"));
delay(10000); // wait for 10 seconds
}
Setup for different MCU is covered on the HardwareSerial example.
#include <PMserial.h>
SerialPM pms(PMSx003, 10, 11); // PMSx003, RX, TX
void setup() {
Serial.begin(9600);
pms.init(); // config serial port
}
void loop() {
pms.read(); // read the PM sensor
Serial.print(F("PM1.0 "));Serial.print(pms.pm01);Serial.print(F(", "));
Serial.print(F("PM2.5 "));Serial.print(pms.pm25);Serial.print(F(", "));
Serial.print(F("PM10 ")) ;Serial.print(pms.pm10);Serial.println(F(" [ug/m3]"));
delay(10000); // wait for 10 seconds
}
Setup for different MCU is covered on the SoftwareSerial example.
On some ESP32 boards Serial1 default pins are connected to the flash. Using the standard constructor will cause a crash, see espressif/arduino-esp32#148.
// will crash the ESP32
SerialPM pms(PMSx003, Serial1);
Fortunately, it is possible to define alternative for pins by calling:
// define Serial1 pins
Serial1.begin(9600, SERIAL_8N1, <RX>, <TX>);
The PMSerial library uses this feature to implement the flexibility of SoftwareSerial
// define Serial1 pins
SerialPM pms(PMSx003, <RX>, <TX>);
The SoftwareSerial example uses Serial1 on pins 23 (RX) and 19 (TX). The HardwareSerial example uses Serial2.
With the exemption of the PMS3003
and PMS5003ST
, all supported sensors
transmit particulate matter (PM) and number concentrations (NC)
measurements in a 32 byte long message.
The PMS3003
only transmit PM measurements in a 24 byte message.
The PMS5003ST
transmit PM, NC, temperature (T), relative humidity (RH) and formaldehyde concentration (HCHO) measurements
on a 40 byte message.
On the examples, the PMSx003
sensor type will infer the sensor type from the message header
and select the message length and decoding protocol accordingly.
The supported protocols are:
message/protocol | length | PM | NC | T & RH | HCHO | sensors (aliases) |
---|---|---|---|---|---|---|
PLANTOWER_AUTO |
self discovery | 3 | auto | PMSx003 |
||
PLANTOWER_24B |
24 bytes | 3 | PMS3003 |
|||
PLANTOWER_32B |
32 bytes | 3 | 6 | PMS1003 , PMS5003 , PMS7003 , PMSA003 |
||
PLANTOWER_32B_S |
32 bytes | 3 | 6 | X | PMS5003S , can not be self discovered |
|
PLANTOWER_32B_T |
32 bytes | 3 | 4 | X | PMS5003T , can not be self discovered |
|
PLANTOWER_40B |
40 bytes | 3 | 6 | X | X | PMS5003ST |
The pms.read()
method will request a new measurement set from the sensor and decode the sensor message.
The has_particulate_matter()
/has_number_concentration()
methods indicate if the message was valid
and PM/NC measurements were successfully decoded.
Similarly the has_temperature_humidity
/has_formaldehyde
methods indicate if the message was valid
and T & RH/HCHO measurements were successfully decoded, however
this kind of additional measurements are only available on PMS5003S
, PMS5003T
and PMS5003ST
sensors.
All measurements found in the sensor message will be decoded and stored in following member variables:
variable | type[len] | measurement | particle diameter | unit | Notes |
---|---|---|---|---|---|
pm01 |
uint16_t |
PM | <= 1.0 µm | µg/m³ | PM1.0, ultra fine particles |
pm25 |
uint16_t |
PM | <= 2.5 µm | µg/m³ | PM2.5, fine particles |
pm10 |
uint16_t |
PM | <= 10 µm | µg/m³ | PM10 |
n0p3 |
uint16_t |
NC | >= 0.3 µm | #/100 cm³ | |
n0p5 |
uint16_t |
NC | >= 0.5 µm | #/100 cm³ | |
n1p0 |
uint16_t |
NC | >= 1.0 µm | #/100 cm³ | |
n2p5 |
uint16_t |
NC | >= 2.5 µm | #/100 cm³ | |
n5p0 |
uint16_t |
NC | >= 5.0 µm | #/100 cm³ | |
n10p0 |
uint16_t |
NC | >= 10 µm | #/100 cm³ | |
pm |
uint16_t[3] |
PM | <= 1,2.5,10 µm | µg/m³ | array containing pm01,pm25,pm10 |
nc |
uint16_t[6] |
NC | >= 0.3,0.5,1,5,10 µm | #/100cm³ | array containing n0p3,..,n10p0 |
data |
uint16_t[9] |
PM/NC | all PM/NC data pm01,..,n10p0 |
||
temp |
float |
T | °C | temperature | |
rhum |
float |
RH | % | relative humidity | |
hcho |
float |
HCHO | mg/m³ | formaldehyde concentration | |
extra |
float[3] |
T/RH/HCHO | array containing temp,rhum,hcho |
For an efficient use of memory, the data
, pm
and nc
arrays and the
individual measurement variables are implemented with a union/struct combination.
See the examples for an full PM/NC output.
The pms.status
member variable contains the status and eventual error code
resulting from the last sensor read.
The available status/error codes and pre-defined error messages are:
status/error code | error message | pre-defined text |
---|---|---|
pms.OK |
||
pms.ERROR_TIMEOUT |
PMS_ERROR_TIMEOUT |
"Sensor read timeout" |
pms.ERROR_PMS_TYPE |
PMS_ERROR_PMS_TYPE |
"Wrong PMSx003 sensor type" |
pms.ERROR_MSG_UNKNOWN |
PMS_ERROR_MSG_UNKNOWN |
"Unknown message protocol" |
pms.ERROR_MSG_HEADER |
PMS_ERROR_MSG_HEADER |
"Incomplete message header" |
pms.ERROR_MSG_BODY |
PMS_ERROR_MSG_BODY |
"Incomplete message body" |
pms.ERROR_MSG_START |
PMS_ERROR_MSG_START |
"Wrong message start" |
pms.ERROR_MSG_LENGTH |
PMS_ERROR_MSG_LENGTH |
"Message too long" |
pms.ERROR_MSG_CKSUM |
PMS_ERROR_MSG_CKSUM |
"Wrong message checksum" |
For easy of use, the error message are pre-defined with #define
.
See the examples for error handling implementation.
Some sensors have a small deviation with temperature and humidity readings. The compensate is possible with a correction offset defined by set_temp_offset() and set_rhum_offset(). The result will be reading + offset. Default offset values are O.
Example:
set_temp_offset(-0.6);
set_rhum_offset(2);
Some libraries (e.g Arduino-SDI-12) conflict with SoftwareSerial library, which is loaded by default for
some boards. In order to disable SoftwareSerial compatibility, define NO_SW_SERIAL_REQUIRED
before
including the library. For example:
#define NO_SW_SERIAL_REQUIRED
#include <PMSerial.h>
If you have read this far, this is the library for your project or you can not figure out how to use it. In any case, I can use your help.
If you find any typos or something is not clear, please go to issue #3 and leave a message. Reopen the issue if needed.
If you want something a bit more challenging. I would appreciate more example projects. See issue #4 for inspiration. PRs are welcomed.