Dust Sensor - PMS 5003/6003/7003


#21

The issue was that you are not allowed to block shared resources (like Serial1) in a software timer.
Moving that stuff into serialEvent1() isn’t really anything other than having the same code inside loop() since these “events” are no real events, but just a synchronous function that’s called between iterations of loop().

But this will still have impact on the execution speed of loop() whereas this (untested) code would be non-blocking

char buf[31];
int  bufIndex = -1;  // current index in message buffer (-1 indicates not started)

void SerialEvent1()
{
    int b = 0;
    
    // when not already started a message, flush everything up to first 'B'
    while (bufIndex < 0 && (b = Serial1.read()) >= 0 && b != 'B') Particle.process();  

    // if we got a start byte start a new message
    if ('B' == b) bufIndex = 0;  // had en error here, hence the following discussion (if (`B` = b) caused the problem)  

    // fill all available bytes into open message buffer
    while (0 <= bufIndex && bufIndex < sizeof(buf) && Serial1.available())
    {
        buf[bufIndex++] = Serial1.read();
        Particle.process();
    }
    
    if (bufIndex == sizeof(buf))
    {
        if(buf[0] == 0x4d && 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 
        }
        bufIndex = -1;  // mark as treated and ready for next message
    }
}

The reason why software timers are not impacted by a blocking app code is that these are handled by RTOS, which executes them “asynchronously” to the main application.

If you add a D7 blink code like digitalWrite(D7, (millis() >> 2) & 0x88); to loop() you should see frequent glitches in the pattern when your code blocks, but with the above, these glitches should be less noticeable.


#22

Non-blocking code, gave these values initially:
PM1.0: 15 ug/m3
PM2.5: 20 ug/m3
PM1 0: 22 ug/m3
and these values remain forever. Would be nice if this works…

Will attempt to debug when I’m back from work…

This code:

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 
            }
        }
    }
}

it updates nicely. But as you said, I think it has an impact on the loop()… It didn’t completely hang or crash, but there was occasional WiFi disconnects with blinking cyan… Have to study more into this…


#23

With suggested non-blocking code:

if (bufIndex == sizeof(buf))
    {
        if(buf[0] == 0x4d && 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 
        }
        bufIndex = -1;  // mark as treated and ready for next message
    }

Only once, the first time:
if (bufIndex == sizeof(buf)) came true
if(buf[0] == 0x4d) came true
Never again!


#24

Could you determine the reason?

  • Was it that the buffer never got filled again, or
  • was next byte after B never an M (=0x4d)
  • what bytes did you get comming in after the first successful message?

Adding some extra Serial.print() statements to see the behaviour of variables (e.g. bufIndex, buf[] and the data from Serial1) would be the first step to debug the cause.
I have no such sensor, so I can’t really test anything.

If you can expect full messages to come back to back - without garbage in between - you could try setting bufIndex = 0; instead of -1 after a successful read.


#25

will try this…


#26

When bufIndex = -1;
if(bufIndex == sizeof(buf)); came true once

Have set bufIndex = 0;
if(bufIndex == sizeof(buf)) came true twice


#27

With this code:

void serialEvent1()
{
    int b = 0;
    
    // when not already started a message, flush everything up to first 'B'
    // Serial.print("Serial1 Read: ");
    // Serial.println(Serial1.read());
    while (bufIndex < 0 && (b = Serial1.read()) >= 0 && b != 'B'){
        Particle.process();
        // Serial.println("BufIndex is less than zero, AND b is eqial to Serial1 Read AND b is not equal to B");
    }

    // if we got a start byte start a new message
    if (b = 'B') {
        // Serial.println("b is equal to B");
        bufIndex = 0;
    }

    // fill all available bytes into open message buffer
    // Serial.print("Serial1 Available: ");
    // Serial.println(Serial1.available());
    while (0 <= bufIndex && bufIndex < sizeof(buf) && Serial1.available())
    {
        // Serial.println("Serial1 Read while 0<=bufIndex & bufIndex < sizeof(buf): ");
        // Serial.println(Serial1.read());
        Serial.print("bufIndex: ");
        Serial.println(bufIndex);
        buf[bufIndex++] = Serial1.read();
        Particle.process();
    }
    
    // Serial.print("Buffer Index: ");
    // Serial.println(bufIndex);
    // Serial.print("Size of buf: ");
    // Serial.println(sizeof(buf));
    
    if (bufIndex == sizeof(buf))
    {
        Serial.println("bufIndex == sizeof(buf)");
        // Serial.print("Buffer at Zero: ");
        // Serial.println(buf[0]);
        if(buf[0] == 0x4d) {
            Serial.println("buf[0] == 0x4d");
            if(checkValue(buf,LENG)) {
                // Serial.println("Checked Value buf,length");
                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
            }
        }
        bufIndex = 0;  // mark as treated and ready for next message
    }
}
Getting values: 
Sensors, ACTIVE!
bufIndex: 0
bufIndex: 1
bufIndex: 2
bufIndex: 3
bufIndex: 4
bufIndex: 5
bufIndex: 6
bufIndex: 7
bufIndex: 8
bufIndex: 9
bufIndex: 10
bufIndex: 11
bufIndex: 12
bufIndex: 13
bufIndex: 14
bufIndex: 15
bufIndex: 16
bufIndex: 17
bufIndex: 18
bufIndex: 19
bufIndex: 20
bufIndex: 21
bufIndex: 22
bufIndex: 23
bufIndex: 24
bufIndex: 25
bufIndex: 26
bufIndex: 27
bufIndex: 28
bufIndex: 29
bufIndex: 30
bufIndex == sizeof(buf)
buf[0] == 0x4d
bufIndex: 0
bufIndex: 1
bufIndex: 2
bufIndex: 3
bufIndex: 4
bufIndex: 5
bufIndex: 6
bufIndex: 7
bufIndex: 8
bufIndex: 9
bufIndex: 10
bufIndex: 11
bufIndex: 12
bufIndex: 13
bufIndex: 14
bufIndex: 15
bufIndex: 16
bufIndex: 17
bufIndex: 18
bufIndex: 19
bufIndex: 20
bufIndex: 21
bufIndex: 22
bufIndex: 23
bufIndex: 24
bufIndex: 25
bufIndex: 26
bufIndex: 27
bufIndex: 28
bufIndex: 29
bufIndex: 30
bufIndex == sizeof(buf)
bufIndex: 0
bufIndex: 0
bufIndex: 0
bufIndex: 0
bufIndex: 0
bufIndex: 0
bufIndex: 0
bufIndex: 0
bufIndex: 0
bufIndex: 0
bufIndex: 0
bufIndex: 0
bufIndex: 0
bufIndex: 0
bufIndex: 0
bufIndex: 0
bufIndex: 0
bufIndex: 0
bufIndex: 0
bufIndex: 0
bufIndex: 0
bufIndex: 0
bufIndex: 0
bufIndex: 0
bufIndex: 0
bufIndex: 0
bufIndex: 0
bufIndex: 0
bufIndex: 0
bufIndex: 0
bufIndex: 0
bufIndex: 0
PM1.0: 23  ug/m3
PM2.5: 26  ug/m3
PM1 0: 29  ug/m3

bufIndex: 0
bufIndex: 0
bufIndex: 0
bufIndex: 0
bufIndex: 0
bufIndex: 0
bufIndex: 0
bufIndex: 0
bufIndex: 0
bufIndex: 0
bufIndex: 0
bufIndex: 0
bufIndex: 0
bufIndex: 0
bufIndex: 0
bufIndex: 0
bufIndex: 0
bufIndex: 0
bufIndex: 0
bufIndex: 0
bufIndex: 0
bufIndex: 0
bufIndex: 0
bufIndex: 0
bufIndex: 0
bufIndex: 0
bufIndex: 0
bufIndex: 0
bufIndex: 0
bufIndex: 0
bufIndex: 0
bufIndex: 0
bufIndex: 0
bufIndex: 0
bufIndex: 0
bufIndex: 0
bufIndex: 0
bufIndex: 0
bufIndex: 0
bufIndex: 0
bufIndex: 0
bufIndex: 0
bufIndex: 0
bufIndex: 0
bufIndex: 0
bufIndex: 0
bufIndex: 0
bufIndex: 0
bufIndex: 0
bufIndex: 0
bufIndex: 0
bufIndex: 0
bufIndex: 0
bufIndex: 0
bufIndex: 0
bufIndex: 0
bufIndex: 0
bufIndex: 0
bufIndex: 0
bufIndex: 0
bufIndex: 0
bufIndex: 0
bufIndex: 0
bufIndex: 0
PM1.0: 23  ug/m3
PM2.5: 26  ug/m3
PM1 0: 29  ug/m3

bufIndex: 0
bufIndex: 0
bufIndex: 0
bufIndex: 0
bufIndex: 0
bufIndex: 0
bufIndex: 0
bufIndex: 0
bufIndex: 0
bufIndex: 0
bufIndex: 0
bufIndex: 0
bufIndex: 0
bufIndex: 0
bufIndex: 0
bufIndex: 0
bufIndex: 0
bufIndex: 0
bufIndex: 0
bufIndex: 0
bufIndex: 0
bufIndex: 0
bufIndex: 0
bufIndex: 0
bufIndex: 0
bufIndex: 0
bufIndex: 0
bufIndex: 0
bufIndex: 0
bufIndex: 0
bufIndex: 0
bufIndex: 0
PM1.0: 23  ug/m3
PM2.5: 26  ug/m3
PM1 0: 29  ug/m3

bufIndex: 0
bufIndex: 0
bufIndex: 0
bufIndex: 0
bufIndex: 0
bufIndex: 0
bufIndex: 0
bufIndex: 0
bufIndex: 0
bufIndex: 0
bufIndex: 0
bufIndex: 0
bufIndex: 0
bufIndex: 0
bufIndex: 0
bufIndex: 0
bufIndex: 0
bufIndex: 0
bufIndex: 0
bufIndex: 0
bufIndex: 0
bufIndex: 0
bufIndex: 0
bufIndex: 0
bufIndex: 0
bufIndex: 0
bufIndex: 0
bufIndex: 0
bufIndex: 0
bufIndex: 0
bufIndex: 0
bufIndex: 0
PM1.0: 23  ug/m3
PM2.5: 26  ug/m3
PM1 0: 29  ug/m3

#28

This code is suspect:

    if (b = 'B') {
        // Serial.println("b is equal to B");
        bufIndex = 0;
    }

That’s not testing if (b == 'B'), it’s assigning 'B' to the variable b and always setting bufIndex to 0.


#29

Works!

**Getting Serial Output:**
Sensors, ACTIVE!
bufIndex: 0
bufIndex: 1
bufIndex: 2
bufIndex: 3
bufIndex: 4
bufIndex: 5
bufIndex: 6
bufIndex: 7
bufIndex: 8
bufIndex: 9
bufIndex: 10
bufIndex: 11
bufIndex: 12
bufIndex: 13
bufIndex: 14
bufIndex: 15
bufIndex: 16
bufIndex: 17
bufIndex: 18
bufIndex: 19
bufIndex: 20
bufIndex: 21
bufIndex: 22
bufIndex: 23
bufIndex: 24
bufIndex: 25
bufIndex: 26
bufIndex: 27
bufIndex: 28
bufIndex: 29
bufIndex: 30
bufIndex == sizeof(buf)
buf[0] == 0x4d
bufIndex: 0
bufIndex: 1
bufIndex: 2
bufIndex: 3
bufIndex: 4
bufIndex: 5
bufIndex: 6
bufIndex: 7
bufIndex: 8
bufIndex: 9
bufIndex: 10
bufIndex: 11
bufIndex: 12
bufIndex: 13
bufIndex: 14
bufIndex: 15
bufIndex: 16
bufIndex: 17
bufIndex: 18
bufIndex: 19
bufIndex: 20
bufIndex: 21
bufIndex: 22
bufIndex: 23
bufIndex: 24
bufIndex: 25
bufIndex: 26
bufIndex: 27
bufIndex: 28
bufIndex: 29
bufIndex: 30
bufIndex == sizeof(buf)
buf[0] == 0x4d
PM1.0: 11  ug/m3
PM2.5: 15  ug/m3
PM1 0: 21  ug/m3

bufIndex: 0
bufIndex: 1
bufIndex: 2
bufIndex: 3
bufIndex: 4
bufIndex: 5
bufIndex: 6
bufIndex: 7
bufIndex: 8
bufIndex: 9
bufIndex: 10
bufIndex: 11
bufIndex: 12
bufIndex: 13
bufIndex: 14
bufIndex: 15
bufIndex: 16
bufIndex: 17
bufIndex: 18
bufIndex: 19
bufIndex: 20
bufIndex: 21
bufIndex: 22
bufIndex: 23
bufIndex: 24
bufIndex: 25
bufIndex: 26
bufIndex: 27
bufIndex: 28
bufIndex: 29
bufIndex: 30
bufIndex == sizeof(buf)
buf[0] == 0x4d
PM1.0: 13  ug/m3
PM2.5: 15  ug/m3
PM1 0: 20  ug/m3

bufIndex: 0
bufIndex: 1
bufIndex: 2
bufIndex: 3
bufIndex: 4
bufIndex: 5
bufIndex: 6
bufIndex: 7
bufIndex: 8
bufIndex: 9
bufIndex: 10
bufIndex: 11
bufIndex: 12
bufIndex: 13
bufIndex: 14
bufIndex: 15
bufIndex: 16
bufIndex: 17
bufIndex: 18
bufIndex: 19
bufIndex: 20
bufIndex: 21
bufIndex: 22
bufIndex: 23
bufIndex: 24
bufIndex: 25
bufIndex: 26
bufIndex: 27
bufIndex: 28
bufIndex: 29
bufIndex: 30
bufIndex == sizeof(buf)
buf[0] == 0x4d
PM1.0: 13  ug/m3
PM2.5: 15  ug/m3
PM1 0: 20  ug/m3

Thanks @rickkas7 @ScruffR


#30

Thanks @rickkas7, that’s so embarrasing :blush:
In another thread myself suggested to another member to flip if statements round to if ('B' == b) to get build errors when failing to use the correct == :flushed:

@ilak2k, I think you can now put the -1 back in place, where it’s supposed to be.


I’ll correct the code above too


#31

Reverted! I see you’ve corrected the code and my posts too… Noted and will do it here after… Thanks @ScruffR


#32

So my PMS7003 arrived today… but it might be a bit before I get it working.

You were right - it’s small… REALLY small. So small that the 2x5 connector for it is on - get this - 0.05" centers! Not the usual 0.1" centers… 0.05". Fortunately they paired it with an equally tiny socket connector so I have something to wire it to… but it’s going to be a challenge either way.

Will update when I’ve gotten it wired up and connected.


#33

So, after one tricky soldering job and a few translations of similar datasheets, I got mine connected and got some data out. Currently it’s serial, but I’ll probably change that to cloud soon, once I better-understand what all the data means.


#34

I hope you got the PMS7003 with cable like in this picture here…

Even though the connectors on the sensor side for 5003 and 7003 are different, it looks like the same connector that goes to the micro controller side…

To connect the sensor, I did this:

  1. Removed/popped the leads off the small white connector
  2. Connected the leads to a header strip, not soldered yet!
  3. Fixed the header strip to a breadboard…

Impressive translation @MartyMacGyver. I gotta learn to do this stuff…

Tried your code on PMS5003… Getting this:

rr=00 csum=02e6 == xsum=02e6
-- Frame: [42 4d] (001c) CF1=[0006 0008 0009] amb=[0006 0008 0009] raw=[05ee 0182 0022 0003 0001 0000] ver=71 err=00 csum=0242 != xsum=02e6
-- Frame: [42 4d] (001c) CF1=[0008 0009 000a] amb=[0008 0009 000a] raw=[0648 01a4 0025 0002 0001 0000] ver=71 err=00 csum=026d == xsum=026d
-- Frame: [42 4d] (001c) CF1=[0008 0009 000a] amb=[0008 0009 000a] raw=[0648 01a4 0025 0002 0001 0000] ver=71 err=00 csum=026d == xsum=026d
-- Frame: [42 4d] (001c) CF1=[0008 0009 000a] amb=[0008 0009 000a] raw=[0648 01a4 0025 0002 0001 0000] ver=71 err=00 csum=0242 != xsum=026d
-- Frame: [42 4d] (001c) CF1=[0006 0009 000a] amb=[0006 0009 000a] raw=[062a 0199 0029 0002 0001 0000] ver=71 err=00 csum=0244 == xsum=0244
-- Frame: [42 4d] (001c) CF1=[0006 0009 0009] amb=[0006 0009 0009] raw=[05f1 018d 0028 0002 0001 0000] ver=71 err=00 csum=02fb == xsum=02fb
-- Frame: [42 4d] (001c) CF1=[0006 0009 0009] amb=[0006 0009 0009] raw=[05f1 018d 0028 0002 0001 0000] ver=71 err=00 csum=0242 != xsum=02fb
-- Frame: [42 4d] (001c) CF1=[0006 0009 0009] amb=[0006 0009 0009] raw=[05f7 018d 0028 0002 0001 0000] ver=71 err=00 csum=0301 == xsum=0301
-- Frame: [42 4d] (001c) CF1=[0006 0009 0009] amb=[0006 0009 0009] raw=[05f7 018d 0028 0002 0001 0000] ver=71 err=00 csum=0301 == xsum=0301
-- Frame: [42 4d] (001c) CF1=[0006 0009 000a] amb=[0006 0009 000a] raw=[062d 0197 002c 0002 0001 0000] ver=71 err=00 csum=0242 != xsum=0248
-- Frame: [42 4d] (001c) CF1=[0008 0009 000a] amb=[0008 0009 000a] raw=[065a 01a7 0032 0002 0001 0000] ver=71 err=00 csum=028f == xsum=028f
-- Frame: [42 4d] (001c) CF1=[0008 0009 000a] amb=[0008 0009 000a] raw=[065a 01a7 0032 0002 0001 0000] ver=71 err=00 csum=028f == xsum=028f
-- Frame: [42 4d] (001c) CF1=[0008 0009 000a] amb=[0008 0009 000a] raw=[065a 01a7 0032 0002 0001 0000] ver=71 err=00 csum=0242 != xsum=028f
-- Frame: [42 4d] (001c) CF1=[0008 0009 000a] amb=[0008 0009 000a] raw=[066c 01a7 0032 0001 0001 0000] ver=71 err=00 csum=02a0 == xsum=02a0
-- Frame: [42 4d] (001c) CF1=[0008 0009 0009] amb=[0008 0009 0009] raw=[06d5 01c2 002f 0000 0000 0000] ver=71 err=00 csum=031d == xsum=031d
-- Frame: [42 4d] (001c) CF1=[0008 0009 0009] amb=[0008 0009 0009] raw=[06d5 01c2 002f 0000 0000 0000] ver=71 err=00 csum=0342 != xsum=031d
-- Frame: [42 4d] (001c) CF1=[0008 0009 0009] amb=[0008 0009 0009] raw=[06d5 01c4 0027 0000 0000 0000] ver=71 err=00 csum=0317 == xsum=0317
-- Frame: [42 4d] (001c) CF1=[0008 0009 0009] amb=[0008 0009 0009] raw=[06d5 01c4 0027 0000 0000 0000] ver=71 err=00 csum=0317 == xsum=0317
-- Frame: [42 4d] (001c) CF1=[0006 0008 0008] amb=[0006 0008 0008] raw=[0660 019d 0027 0000 0000 0000] ver=71 err=00 csum=0242 != xsum=0273
-- Frame: [42 4d] (001c) CF1=[0006 0008 0008] amb=[0006 0008 0008] raw=[065a 0198 0021 0000 0000 0000] ver=71 err=00 csum=0262 == xsum=0262

#35

NNNNNNNNNNNOPE!

I got a tiny 1.27mm pitch 2x5 female header and that was all. No carrier board, no wire, no nothing else (it looks like that board is made to adapt that 1003/3003/5003 cable to the 7003’s new, microscopic header… would be nice to be able to get one of those eventually). Had my AliExpress order gone through I would’ve gotten the one pictured. That’d’ve saved a lot of time.

Still, at least it seems to work OK… I’m busy integrating this into my multi-sensor hobby project now. Hopefully it’s instructive (this sensor’s code is NOT fully integrated yet - just dabbling with it as I have free personal time).

EDIT: And yes, my old code had a subtle bug in it (which is why the checksums would mismatch periodically)… I’ve uploaded the updated demo code for the PMS7003 at:

EDIT2: A photo of the project as it is today. Inset shows my dodgy soldering job on that tiny connector… if half-pitch perf boards weren’t so rare it’d have been a lot easier.


#36

Hi… do you have the final arduino code for the pms5003?

many thanks


#37

I won’t say it’s final, but the repos above are current (and the sensor manager project is going to be the most current for now). It ought to work with the 5003, 3003, etc. but I haven’t tested those… I think (but am not sure) that the generations of Plantower’s devices vary primarily in size, not API/interface.

In other news, DigiKey appears to carry the vexing little 1.27mm pitch connector for the PMS7003 - part #609-3754-ND (part of Amphenol’s Minitek 127 series). I’ll probably be buying a couple of these to improve upon my current soldering job (I’ve already sourced some 1.27mm pitch double-sided plated perfboard for my bench supplies).


#38

The code mentioned in this post: Dust Sensor - PMS 5003/6003/7003
It may be possible to optimize this, but works as is and final for me… gives PM1, PM2.5 & PM10 values


#39

True!

Your codes look very organised and properly formatted… Nice work!

You actually soldered on to those tiny pins… You got curious right?
I would’ve just ordered the connectors and waited :blush:


#40

The half-pitch perfboard is neither cheap nor readily available (it’s still on its way from China). I wasn’t about to wait. :smiley: (You’re rather lucky with the 5003 - it’s not hard to get a connector for that, versus the 7003 - nobody sells a breakout board or converter for that.)

I’m glad you like the code! It’s still very much a work in progress… still need to update the backend code to be more useful, and interpret the PM data more usefully as well.

In other news, Plantower has released an even smaller sensor, the A003… haven’t seen it for sale yet but it uses that same tiny connector as the 7003

http://www.plantower.com/content/?125.html