I am trying to get a particle sensor to work with the Raspberry Pi (model B+). I am using the Shinyei PPD42N6 (datasheet here).

The sensor outputs a digital signal with pulses ranging from 10-90ms. According to the datasheet, the concentration of particles detected (pcs/283ml) is a function of the low pulse occupancy time (i.e., the total time that the input waveform is low over some specified sample interval). Sample code for the Arduino can be found at this Wiki page.

I am trying to re-write the sample code on the Raspberry Pi so that it has the same functional behavior. My first concern was that the Raspberry Pi would not be reliable due to the OS interrupts that would likely occur during the sampling period. @joan notified me that his PiGPIO library is able to count pulses to within 10-20us. However, I am having difficulty getting his library to work in the context of this problem. Here is the code that I have written:

import time
import pigpio

# Setup the Shinyei input
RX = 24 

duration = 0
starttime = 0
sampletime_ms = 30000 # sample time (ms)
lowpulseoccupancy = 0
ratio = 0
concentration = 0
prev_tick = None

def cb_high(g, l, t):
   global prev_tick, duration, lowpulseoccupancy 
   if prev_tick is not None:
      duration = pigpio.tickDiff(prev_tick, t)
      lowpulseoccupancy += duration

def cb_low(g, l, t):
   global prev_tick
   prev_tick = t

pi = pigpio.pi() # Connect to local Pi.
cb1 = pi.callback(RX, pigpio.FALLING_EDGE, cb_low)
cb2 = pi.callback(RX, pigpio.RISING_EDGE, cb_high)

   while True:
      if ((time.time() - starttime) > (sampletime_ms / 1000)):
         ratio = lowpulseoccupancy/((sampletime_ms)*10.0)
         concentration = 1.1*pow(ratio,3)-3.8*pow(ratio,2)+520*ratio+0.62
         print ("Concentration = {0} pcs/0.01cf".format(concentration))
         lowpulseoccupancy = 0
         starttime = time.time()
except KeyboardInterrupt:
   pi.stop() # Disconnect from local Pi.

Each time a transition occurs from a high state to a low state the prev_tick variable is set. When a transition from low to high occurs, the difference between the current tick t and the previous tick prev_tick is calculated (e.g., the total duration in which the signal was low). This duration is added to the lowpulseoccupancy time, which is used in the main loop of the main thread.

This code leads to extremely odd behavior. I get numbers that are out of this world, and very inconsistent.

Does anyone see any obvious issues with this code? If not, do you have any suggestions as to how this problem could best be solved?

1 Answer 1


I'm not going to delve too deeply into the code.

I had a look at the datasheet and it seems to encode the reading in the low time over some sort of interval. It seems a little light on details.

A first attempt would be something along the following lines.

#!/usr/bin/env python

# low_high.py
# 2015-11-05
# Public Domain

import time
import pigpio


start_tick = None
last_tick = None

low_ticks = 0
high_ticks = 0

def cbf(gpio, level, tick):
   global start_tick, last_tick, low_ticks, high_ticks
   if start_tick is not None:
      ticks = pigpio.tickDiff(last_tick, tick)
      last_tick = tick
      if level == 0: # Falling edge.
         high_ticks = high_ticks + ticks
      else: # Rising edge.
         low_ticks = low_ticks + ticks
      interval = pigpio.tickDiff(start_tick, tick)
      if interval >= INTERVAL:
         print("ratio = {:.3f}".format(float(low_ticks)/float(interval)))
         print("lt={} ht={} int={}".format(low_ticks, high_ticks, interval))
         start_tick = tick
         last_tick = tick
         low_ticks = 0
         high_ticks = 0
      start_tick = tick
      last_tick = tick

pi = pigpio.pi() # Connect to local Pi.

pi.set_mode(RX, pigpio.INPUT)

cb = pi.callback(RX, pigpio.EITHER_EDGE, cbf)


cb.cancel() # Cancel callback.

pi.stop() # Disconnect from local Pi.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.