pulseIn() timeout possible? Will it affect Dust Sensor readings?

I’m using pulseIn() for reading Low Pulse Occupancy Time from Dust Sensor PPD42NS and the process is taking too long to complete and blocks other functions of the project! In this project, I want a loop to be executed every second, but with pulseIn() its not able to…

Without pulseIn():-
Timer: 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40
Executes every second consistently

With pulseIn():-
Timer: 31, 32, 33, 36, 37, 40, 42, 43, 45, 45, 46, 50
Gets blocked at 34 & 35, then 38 & 39, then 41, then 44, then 47,48,49. And at 45 gets executed twice rapidly…

Option #1:-
Well, I’m still not sure of this is a solution, but can anyone let me know if this will work?
pulseIn() on Arduino has a timeout feature for this function…
Arduino Syntax: pulseIn(pin, value, timeout)
particle Photon Syntax: pulseIn(pin, value)
Can you add this feature or is there a way to add this feature myself manually? Code level changes? Dust Sensor connectivity changes? Anything?
If this feature is included, how will it affect the Dust Sensor’s readings?

Option #2:-
Can I add, a dedicated simple cheap device, which can read Low Pulse Occupancy Time and report the results to particle photon? What’s the best way to do this? Hardware options?

This reminded me of an idea I had for making an asynchronous version of pulseIn. It’s probably not exactly right for all use cases, but it seems to work. Even if it doesn’t work directly for your use case, it might have some handy tips and tricks that can used.

The annoying thing about pulseIn(), a function inherited from Arduino, is that it’s blocking. Also, the current Particle implementation doesn’t allow you to set the timeout, currently 3 seconds. But even with a configurable timeout, I’m not fond of it being blocking.

What this code does is use an interrupt to monitor the pin. It can catch exceptionally narrow pulses that way, pretty accurately.

The big question is: what do you do with those pulse width measurements? The problem with an interrupt handler is that there are huge number of restrictions of what you can do from your interrupt handler, so you want to minimize what you do there. What I decided on was a circular buffer (FIFO) that remembers the recent pulse widths. This can handle the simple case where you trigger the sensor and measure one pulse, but also more complicated cases where you have multiple pulses coming in.

You typically create a global object like this:

// Initialize the AsyncPulseIn object to listen on D2 and measure the width of the HIGH part of the pulse
AsyncPulseIn asyncPulse(D2, HIGH);

Note that since it uses interrupts, there are some restrictions on what pins you can use. See:
https://docs.particle.io/reference/firmware/photon/#attachinterrupt-

This is the code example to just print out all of the pulse info. This code was in loop();:

AsyncPulseInPulseInfo pulseInfo;
while(asyncPulse.getNextPulse(pulseInfo)) {
    // We have a saved pulse
    Serial.printlnf("millis=%lu width=%lu", pulseInfo.startMillis, pulseInfo.widthMicros);
}

The main thing is that you call getNextPulse() out of loop and it returns the previously measured pulses. It does not block waiting for new pulses. This will require some restructuring over code ported directly from Arduino, but it will make your code much more efficient and predicable, not blocking loop to measure pulses.

The code is quite simple so feel free to just copy and paste the stuff directly into your code as it’s a reasonable template for a pulse-detecting interrupt service routine.

The library is up in the community libraries in the Particle Build web IDE (AsyncPulseIn) and also here on github:

4 Likes

Coolest solution @rickkas7. It works! And its flawless for my purpose…
Thanks :smiley:

With AsyncPulseIn:-
Timer: 1555, 1556, 1557, 1558, 1559, 1560, 1561, 1562, 1563, 1564. Ratio: 2.739890. Concentration: 1419.461392pcs/0.01cf
Timer: 1565, 1566, 1567, 1568, 1569, 1570, 1571, 1572, 1573, 1574. Ratio: 2.505770. Concentration: 1297.067425pcs/0.01cf

For these results, I just have to change this:
AsyncPulseIn asyncPulse(D2, HIGH);
to this:
AsyncPulseIn asyncPulse(D2, LOW);
as we’re calculating the Low Pulse Occupancy Time

Thank you very much @rickkas7

2 Likes