Low Pulse Particle counter

Hello,

I'm working on an air quality sensor that uses a shinyei particle counter (http://www.sca-shinyei.com/pdf/PPD42NS.pdf). The sensor gives a low pulse when particles are detected. After 30 seconds the Arduino calculates the particle concentration using the ratio of time the pulse is low. I have functioning code using the pulsein function but I'm afraid I may be missing shorter pulses. I've been trying to write some interrupt code to detect the length of the pulse but I'm really struggling. Currently the code I have prints "st" and then stops. Any advice would be greatly appreciated!

const int PMPin = 2;
unsigned long PMstart;
unsigned long PMstop;
unsigned long sampletime;
float ratio = 0;
float concentration = 0;
volatile unsigned long downt;
volatile unsigned long pulset;
volatile int val =0;

void setup(){
  noInterrupts();
  pinMode(PMPin,INPUT);
  Serial.begin(9600);
  Serial.println("starting");
  attachInterrupt(0,pulses,CHANGE);
  pulset=0;
  PMstart=micros();
  Serial.println("PM_interrupt");
  interrupts();
}

void loop(){
  if (PMstart-micros()>30000000){
    noInterrupts();
    PMstop = micros();
    sampletime = (PMstop - PMstart)/1000;
    Serial.print("sample t(ms): ");
    Serial.print(sampletime);
    Serial.print(",pulse t(um): ");
    ratio = pulset/(sampletime*10.0);  // Integer percentage 0=>100
    concentration = 1.1*pow(ratio,3)-3.8*pow(ratio,2)+520*ratio+0.62; // using spec sheet curve
    Serial.print(pulset);
    Serial.print(",ratio: ");
    Serial.print(ratio);
    Serial.print(",conc: ");
    Serial.println(concentration);
    pulset = 0;
    PMstart = micros();
    interrupts();
  }
}
void pulses(){
  val= digitalRead(PMPin);
  if(val==LOW){
    downt=micros();
  }
  else{
    pulset=micros()-downt+pulset;
  }
}

The sensor gives a low pulse when particles are detected.

How long do these pulses last? Perhaps you could use two interrupts - one RISING and one FALLING to get the start and stop sides of the pulse.

According to the manufacturer the pulse widths are 10ms~90ms. From other sources I've read I thought there could only be one interrupt assigned per pin?

From other sources I've read I thought there could only be one interrupt assigned per pin?

So? You have a problem running the same wire to two pins?

According to the manufacturer the pulse widths are 10ms~90ms.

That's a pretty long time. I would think that polling would be sufficient. Detecting the edge (rising or falling) is easy. Recording the start time or the stop time is easy. Computing the difference is trivial.

Great should have thought of that. Thanks for the advice I'll try it!

Seems to me that you're trying to use a pin change interrupt, and the sketch is failing completely.

I'm not experienced at using Arduino interrupts and there may be some subtle issues like using a pin that supports the interrupt type you're trying to use, but the basic approach seems sensible to me.

The symptoms suggest that something is going wrong when you enable interrupts - presumably, at that point your interrupt handler would be called for the first time. So I'd be looking for anything going on in there that could affect the system stability. If you completely remove all the code from your interrupt handler and just leave an empty function, does that cure the symptoms?

pow(ratio,3)

Pretty expensive function call to do ratioratioratio.

pow(ratio,2)

Pretty expensive function call to do ratio*ratio.

Ok so I've changed some things as suggested. I double checked that I had the right interrupt pins for the UNO but I'm still having the same problem. The code below runs and prints "starting PM_interrupt" but when I uncomment the next line in the next Serial.print line it goes back to printing "st".

//trial interrupt code for PM sensor on UNO
const int PMPin = 2;
unsigned long PMstart;
unsigned long PMstop;
unsigned long sampletime;
float ratio = 0;
float concentration = 0;
volatile unsigned long downt;
volatile unsigned long pulset;

void setup(){
  noInterrupts();
  pinMode(PMPin,INPUT);
  Serial.begin(9600);
  Serial.println("starting");
  attachInterrupt(0,pulserise,RISING);//pin 2
  attachInterrupt(1,pulsefall,FALLING);// pin3
  pulset=0;
  PMstart=micros();
  Serial.println("PM_interrupt");
  downt=micros();
  interrupts();
}

void loop(){
  if (PMstart-micros()>30000000){
    noInterrupts();
    PMstop = micros();
    sampletime = (PMstop - PMstart)/1000;
    /*Serial.print("sample t(ms): ");
    Serial.print(sampletime);
    Serial.print(",pulse t(um): ");
    ratio = pulset/(sampletime*10.0);  // Integer percentage 0=>100
    concentration = 1.1*ratio*ratio*ratio-3.8*ratio*ratio+520*ratio+0.62; // using spec sheet curve
    Serial.print(pulset);
    Serial.print(",ratio: ");
    Serial.print(ratio);
    Serial.print(",conc: ");
    Serial.println(concentration);
    pulset = 0;
    PMstart = micros();*/
    interrupts();
  }
}
void pulsefall(){
  downt=micros();
}
void pulserise(){
  pulset=micros()-downt+pulset;
}
  if (PMstart-micros()>30000000){

In the absence of any directive to the contrary, literal constants, like 30000000 are treated as ints. Since 30000000 won't fit in an int, it is probably being interpreted as a negative number. So, on the first pass through loop, the block IS executed.

    sampletime = (PMstop - PMstart)/1000;

This is, therefore likely to be 0.

    ratio = pulset/(sampletime*10.0);  // Integer percentage 0=>100

And, of course, dividing by 0 is rarely a good idea.

I'd suggest changing 30000000 to 30000000UL, and testing that sampletime is not 0 before dividing by it.