Dust Sensor - PMS 5003/6003/7003

I found some libraries/code for these sensors on Arduino…

PMS3003 from here:
https://forum.arduino.cc/index.php?topic=355922.0

PMS5003 with MQTT from here:

PMS3003/5003 from here:
http://www.dfrobot.com/wiki/index.php?title=PM2.5_laser_dust_sensor_SKU:SEN0177

Found and translated PMS7003 datasheet to English and didn’t understand a thing. Interpreting datasheets is not my forte and thought the experts on the forum can guide me get readings from these sensors…

Product Pages from here:
http://www.one-xin.cn/a/LEDchanpin/LEDdengtiao/20151203/133.html
http://www.one-xin.cn/a/LEDchanpin/LEDdengtiao/20151203/136.html
http://www.one-xin.cn/a/LEDchanpin/LEDdengtiao/20151203/135.html

Available to purchase form here:
PMS7003:

PMS5003:

PMS3003:

If I get one of these, can we try to get readings with a Photon? Wanted to confirm before spending $ in this?

1 Like

@ilak2k, you won’t know till you try! Pick a sensor and we’ll go from there :wink:

If you don’t want to spend the money for the risk of not getting it to work, you might need to spend the time to dig up some datasheets and find out how these communicate with the outside world and at what electrical levels.
But that’s your duty.

1 Like

Sure, that's encouraging, will get one of these and we'll go from there...

As already mentioned, interpreting datasheets is not my forte... But expecting our friendly community here will surely come up with some way...

@ilak2k, all three sensors are powered by 5V but have 3.3v interfaces so it comes down to your choice and availability of libraries.

Will get the 7003, a thin version of the PMS Dust Sensor. Will also complement our little photon nicely for an overall compact package

I’ve been chewing on a project to detect dust levels in the air for my wood shop. If that is similar to your application you might also look at this sensor from Sparkfun:


Just has an analog voltage output.

This one's already on my list! The most famous, GP2Y1010AU0F from Sharp. Its super cheap. Already tested a somewhat similar Shinyei PPD42NS and the readings were noisy, no fan, detects dust particles >1um. But, its complicated to get useful ppm or ug/m3 values.

I'm curious how they compare with the PMS Sensors...

I've been pondering a similar purchase, and am very torn between the PMS7003 and the SDS021... the one big difference is the PMS7003 fractions out by particle sizes and gives readings in greater detail versus the SDS021, but I've no idea how they work in terms of relative accuracy and there's virtually nothing out there in terms of reviews.

Did you get your '7003 yet? Any first impressions?

PMS7003 was too small and a tad bit expensive. I found the PMS5003 small enough for my purpose. Device arrived just today. Only Arduino Code is readily available... Checked it... Getting values:
PM1.0: 9 ug/m3
PM2.5: 12 ug/m3
PM1 0: 14 ug/m3

@peekay123 @ScruffR
Here's the Arduino Code:

//******************************
 //*Abstract: Read value of PM1,PM2.5 and PM10 of air quality
 //
 //*Version:V3.1
 //*Author:Zuyang @ HUST
 //*Modified by Cain for Arduino Hardware Serial port compatibility
 //*Date:March.25.2016
 //******************************
#include <Arduino.h>
#define LENG 31   //0x42 + 31 bytes equal to 32 bytes
unsigned char buf[LENG];
 
int PM01Value=0;          //define PM1.0 value of the air detector module
int PM2_5Value=0;         //define PM2.5 value of the air detector module
int PM10Value=0;         //define PM10 value of the air detector module
 
 
void setup()
{
  Serial.begin(9600);   //use serial0
  Serial.setTimeout(1500);    //set the Timeout to 1500ms, longer than the data transmission periodic time of the sensor
 
}
 
void loop()
{
  if(Serial.find(0x42)){    //start to read when detect 0x42
    Serial.readBytes(buf,LENG);
 
    if(buf[0] == 0x4d){
      if(checkValue(buf,LENG)){
        PM01Value=transmitPM01(buf); //count PM1.0 value of the air detector module
        PM2_5Value=transmitPM2_5(buf);//count PM2.5 value of the air detector module
        PM10Value=transmitPM10(buf); //count PM10 value of the air detector module 
      }           
    } 
  }
 
  static unsigned long OledTimer=millis();  
    if (millis() - OledTimer >=1000) 
    {
      OledTimer=millis(); 
       
      Serial.print("PM1.0: ");  
      Serial.print(PM01Value);
      Serial.println("  ug/m3");            
     
      Serial.print("PM2.5: ");  
      Serial.print(PM2_5Value);
      Serial.println("  ug/m3");     
       
      Serial.print("PM1 0: ");  
      Serial.print(PM10Value);
      Serial.println("  ug/m3");   
      Serial.println();
    }
   
}
char checkValue(unsigned char *thebuf, char leng)
{  
  char receiveflag=0;
  int receiveSum=0;
 
  for(int i=0; i<(leng-2); i++){
  receiveSum=receiveSum+thebuf[i];
  }
  receiveSum=receiveSum + 0x42;
  
  if(receiveSum == ((thebuf[leng-2]<<8)+thebuf[leng-1]))  //check the serial data 
  {
    receiveSum = 0;
    receiveflag = 1;
  }
  return receiveflag;
}
 
int transmitPM01(unsigned char *thebuf)
{
  int PM01Val;
  PM01Val=((thebuf[3]<<8) + thebuf[4]); //count PM1.0 value of the air detector module
  return PM01Val;
}
 
//transmit PM Value to PC
int transmitPM2_5(unsigned char *thebuf)
{
  int PM2_5Val;
  PM2_5Val=((thebuf[5]<<8) + thebuf[6]);//count PM2.5 value of the air detector module
  return PM2_5Val;
  }
 
//transmit PM Value to PC
int transmitPM10(unsigned char *thebuf)
{
  int PM10Val;
  PM10Val=((thebuf[7]<<8) + thebuf[8]); //count PM10 value of the air detector module  
  return PM10Val;
}

.
.
.
From Arduino, tried to update these readings to Blynk and didn't succeed using the following code:

//******************************
 //*Abstract: Read value of PM1,PM2.5 and PM10 of air quality
 //
 //*Version:V3.1
 //*Author:Zuyang @ HUST
 //*Modified by Cain for Arduino Hardware Serial port compatibility
 //*Date:March.25.2016
 //******************************
#include <Arduino.h>
#include <SPI.h>
#include <Ethernet.h>
#include <BlynkSimpleEthernet.h>
#define LENG 31   //0x42 + 31 bytes equal to 32 bytes
char auth[] = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
unsigned char buf[LENG];
 
int PM01Value=0;          //define PM1.0 value of the air detector module
int PM2_5Value=0;         //define PM2.5 value of the air detector module
int PM10Value=0;         //define PM10 value of the air detector module
 
 
void setup()
{
  Serial.begin(9600);   //use serial0
  Blynk.begin(auth); // Connects to the Internet with DHCP
  Serial.setTimeout(1500);    //set the Timeout to 1500ms, longer than the data transmission periodic time of the sensor
}
 
void loop()
{
  Blynk.run();
  if(Serial.find(0x42)){    //start to read when detect 0x42
    Serial.readBytes(buf,LENG);
 
    if(buf[0] == 0x4d){
      if(checkValue(buf,LENG)){
        PM01Value=transmitPM01(buf); //count PM1.0 value of the air detector module
        PM2_5Value=transmitPM2_5(buf);//count PM2.5 value of the air detector module
        PM10Value=transmitPM10(buf); //count PM10 value of the air detector module 
      }           
    } 
  }
 
  static unsigned long OledTimer=millis();  
    if (millis() - OledTimer >=1000) 
    {
      OledTimer=millis(); 
       
      Serial.print("PM1.0: ");  
      Serial.print(PM01Value);
      Serial.println("  ug/m3");            
     
      Serial.print("PM2.5: ");  
      Serial.print(PM2_5Value);
      Serial.println("  ug/m3");     
       
      Serial.print("PM1 0: ");  
      Serial.print(PM10Value);
      Serial.println("  ug/m3");   
      Serial.println();
    }
   
}
char checkValue(unsigned char *thebuf, char leng)
{  
  char receiveflag=0;
  int receiveSum=0;
 
  for(int i=0; i<(leng-2); i++){
  receiveSum=receiveSum+thebuf[i];
  }
  receiveSum=receiveSum + 0x42;
  
  if(receiveSum == ((thebuf[leng-2]<<8)+thebuf[leng-1]))  //check the serial data 
  {
    receiveSum = 0;
    receiveflag = 1;
  }
  return receiveflag;
}
 
int transmitPM01(unsigned char *thebuf)
{
  int PM01Val;
  PM01Val=((thebuf[3]<<8) + thebuf[4]); //count PM1.0 value of the air detector module
  return PM01Val;
}
 
//transmit PM Value to PC
int transmitPM2_5(unsigned char *thebuf)
{
  int PM2_5Val;
  PM2_5Val=((thebuf[5]<<8) + thebuf[6]);//count PM2.5 value of the air detector module
  return PM2_5Val;
  }
 
//transmit PM Value to PC
int transmitPM10(unsigned char *thebuf)
{
  int PM10Val;
  PM10Val=((thebuf[7]<<8) + thebuf[8]); //count PM10 value of the air detector module  
  return PM10Val;
}

Previous code works great! But using the above code in an attempt to update values to Blynk, it doesn't update to Blynk and also Arduino's Serial Monitor doesn't update after 2 cycles... Not sure why! But, I'm ok if this work on just the Particle Photon and update values to Blynk, along with my other sensor values!

I ordered the PMS7003 the other day via eBay (I tried to order it via AliExpress but the amount of personal information they wanted to “verify” me made me pause). Will let you know when I get it and have had time to try it out.

Hopefully the smaller size doesn’t impact quality.

@peekay123 @ScruffR
Need help making this device work with Photon! Its the PMS5003 G5!
Spec sheet here: http://www.one-xin.cn/uploads/soft/160121/PMS5003(G5).pdf

The above (first) code should work as is on the Photon too - just replace #include <Arduino.h> with #include "Particle.h" (and some corrections in data types, but that should be easy by reading the error messages - change unsigned char for char and Serial.find(0x42) for Serial.find("B")).
And of course Serial needs to be replaced for Serial1 for the communication with the sensor.
But how does Serial work on the Arduino to talk to both - the sensor and the serial monitor?

Some bits of that code could be written “nicer”, but in general it should do as is.

In your second code I can’t see any attempt of yours to send any of your data to Blynk.
How would you expect should the Blynk lib know what variables you want to upload? :confused:

BTW: I can’t read Chinese, so the given link to the datasheet is no good for me - can you read it? Or have you not even had a look at it?

Ok...
Will use #include "Particle.h"
Will use Serial1.find("B") in place of Serial.find(0x42)
Then, should I be using something else in place of buf[0] == 0x4d

Arduino Serial works without issues. But I had to disconnect TX/RX of the sensor to be able to upload/modify code to Arduino. After upload is done, I connect the TX/RX back, and it works!

My bad! I removed the

Blynk.virtualWrite(V11, PM01Value);
Blynk.virtualWrite(V12, PM2_5Value);
Blynk.virtualWrite(V13, PM10Value);

while trying to isolate an issue where the concentration value not getting updated to Serial Monitor... When I added the Blynk part, Blynk didn't update values and also Serial Monitor stopped updating concentration value after just 2 cycles. So I removed the virtualWrite part to debug the issue. An posted here at that moment... Now I doubt if this is the Serial communication issue you mentioned earlier... "How Arduino talk to both - the sensor and the serial monitor?"

Of course I did and that's how I confirmed all the wiring... Other than that, I didn't understand a thing about the byte ordering and all that. I don't understand Chinese. I use Google Translate...

I could browse and add any document to translate. There's no option to save the translated doc, otherwise I would've provided that. So I had to attach the untranslated version, I could've taken a screenshot and attached it as a picture though... But the translated doc is just all text and no images. We need to correlate the docs before and after translation to get an idea.

I was under the assumption that you guys must have come across lots of Chinese spec sheets and have been translating them. Sorry!

I found a datasheet
https://github.com/avaldebe/AQmon/blob/master/Documents/PMS5003_LOGOELE.pdf

Getting values:
PM1.0: 12 ug/m3
PM2.5: 15 ug/m3
PM1 0: 18 ug/m3

With this code:

SYSTEM_THREAD(ENABLED);
//******************************
 //*Abstract: Read value of PM1,PM2.5 and PM10 of air quality
 //
 //*Version:V3.1
 //*Author:Zuyang @ HUST
 //*Modified by Cain for Arduino Hardware Serial port compatibility
 //*Date:March.25.2016
 //******************************
#include <Particle.h>
#define LENG 31   //0x42 + 31 bytes equal to 32 bytes
char buf[LENG];
 
int PM01Value=0;          //define PM1.0 value of the air detector module
int PM2_5Value=0;         //define PM2.5 value of the air detector module
int PM10Value=0;         //define PM10 value of the air detector module
 
 
void setup()
{
  Serial1.begin(9600);   //use serial0
//   Serial1.setTimeout(1500);    //set the Timeout to 1500ms, longer than the data transmission periodic time of the sensor
    Serial.begin(57600);
}
 
void loop()
{
  if(Serial1.find("B")){    //start to read when detect 0x42
    Serial1.readBytes(buf,LENG);
 
    if(buf[0] == 0x4d){
      if(checkValue(buf,LENG)){
        PM01Value=transmitPM01(buf); //count PM1.0 value of the air detector module
        PM2_5Value=transmitPM2_5(buf);//count PM2.5 value of the air detector module
        PM10Value=transmitPM10(buf); //count PM10 value of the air detector module 
      }
    }
  }
 
  static unsigned long OledTimer=millis();
    if (millis() - OledTimer >=1000)
    {
      OledTimer=millis();
      
      Serial.print("PM1.0: ");
      Serial.print(PM01Value);
      Serial.println("  ug/m3");
      
      Serial.print("PM2.5: ");
      Serial.print(PM2_5Value);
      Serial.println("  ug/m3");
      
      Serial.print("PM1 0: ");  
      Serial.print(PM10Value);
      Serial.println("  ug/m3");   
      Serial.println();
    }

}
char checkValue(char *thebuf, char leng)
{
  char receiveflag=0;
  int receiveSum=0;
 
  for(int i=0; i<(leng-2); i++){
  receiveSum=receiveSum+thebuf[i];
  }
  receiveSum=receiveSum + 0x42;
  
  if(receiveSum == ((thebuf[leng-2]<<8)+thebuf[leng-1]))  //check the serial data 
  {
    receiveSum = 0;
    receiveflag = 1;
  }
  return receiveflag;
}
 
int transmitPM01(char *thebuf)
{
  int PM01Val;
  PM01Val=((thebuf[3]<<8) + thebuf[4]); //count PM1.0 value of the air detector module
  return PM01Val;
}
 
//transmit PM Value to PC
int transmitPM2_5(char *thebuf)
{
  int PM2_5Val;
  PM2_5Val=((thebuf[5]<<8) + thebuf[6]);//count PM2.5 value of the air detector module
  return PM2_5Val;
  }
 
//transmit PM Value to PC
int transmitPM10(char *thebuf)
{
  int PM10Val;
  PM10Val=((thebuf[7]<<8) + thebuf[8]); //count PM10 value of the air detector module  
  return PM10Val;
}

I’m using 2 serials at 9600 and 57600. And getting values from Serial Monitor using both baud rates. But if I decide to use just one, I’m not getting any values… Is the Serial part of the code efficient? Or can I improve the code? Also I’ll be trying to make this work with SparkCorePolledTimer… And then on to power saving operation. And then BLYNK :smiley:

Serial and Serial1 are completely different interfaces - one hasn't got anything to do with the other!
While Serial (the only one that can be seen on Serial Monitor) communicates via the USB port and a Virtual COM protocol (hence you can actually use any baud rate - the speed will always be the same USB speed), Serial1 only communicates via the RX/TX pins and there the baud rate has to match the baud rate of your connected device.

I'd write this

      
      Serial.print("PM1.0: ");
      Serial.print(PM01Value);
      Serial.println("  ug/m3");
      
      Serial.print("PM2.5: ");
      Serial.print(PM2_5Value);
      Serial.println("  ug/m3");
      
      Serial.print("PM1 0: ");  
      Serial.print(PM10Value);
      Serial.println("  ug/m3");   
      Serial.println();

rather like this

  Serial.printlnf("PM  1.0: %5d µg/m³\r\n"
                  "PM  2.5: %5d µg/m³\r\n"
                  "PM 10.0: %5d µg/m³\r\n"
                  ,PM01Value
                  ,PM2_5Value
                  ,PM10Value);

Why not use the native Software Timers?

This part of the code in loop() function is blocking other functions, but gives proper dust values…

    if(Serial1.find("B")) {    //start to read when detect 0x42
        Serial1.readBytes(buf,LENG);
        if(buf[0] == 0x4d) {
            if(checkValue(buf,LENG)) {
                PM01Value=transmitPM01(buf); //count PM1.0 value of the air detector module
                PM2_5Value=transmitPM2_5(buf);//count PM2.5 value of the air detector module
                PM10Value=transmitPM10(buf); //count PM10 value of the air detector module 
            }
        }
    }

Dust values update every 2 seconds as intended!

Tried moving this code to a timer function that executes every 2 seconds and the dust values don’t update and giving me:
PM1.0: 12 ug/m3
PM2.5: 15 ug/m3
PM1 0: 18 ug/m3
like forever!

Can anyone suggest a solution?

I think Serial1 is not a good citizen for software timers (or ISRs), and particlularly not find() and readBytes() as they are blocking for up to timeout (default 1000) milliseconds.
So you best keep that in loop() (or wrap it in a function which is called from loop), or you replace the blocking calls (especially find) with a non-blocking solution.
If you want other stuff to happen in your code, this would be required and is better practice anyway.

You could try SerialEvent1() tho’

Moved the blocking part of the code into SerialEvent1:

void serialEvent1() {
        if(Serial1.find("B")) {    //start to read when detect 0x42
        Serial1.readBytes(buf,LENG);
        if(buf[0] == 0x4d) {
            if(checkValue(buf,LENG)) {
                PM01Value=transmitPM01(buf); //count PM1.0 value of the air detector module
                PM2_5Value=transmitPM2_5(buf);//count PM2.5 value of the air detector module
                PM10Value=transmitPM10(buf); //count PM10 value of the air detector module 
            }
        }
    }
}

I've set dust values to update to Blynk every 5 seconds. And, there's a 1 second timer that executes every second precisely. The find() and readbytes() doesn't seem to block now... Is this how its supposed to work?
What could have solved the issue?