Using pulseIn() function twice in 1 loop

Hi I have bought 2 Grove - Dust Sensors, 1 is measuring pm2.5 and the other pm10.

So I want to get both values in the same loop but I seem to be having issues using the pulseIn() function twice.

The values I get when using it twice are much lower then they should be and I am not sure why. I have read loads of forums which say you have to create your own function to do this. I created my own function but its basically the same as the original.

long customPulseIn(int pin) {
    // wait for any previous pulse to end
    while ((digitalRead(pin)) == LOW) {}

    // wait for the pulse to start
    while ((digitalRead(pin)) != LOW) {}

    unsigned long start = micros();
    
    // wait for the pulse to stop
    while ((digitalRead(pin)) == LOW) {}

    return micros() - start;
}

When I use this code on one sensor its fine but when I use it on both the values are really low again. Why does it make a difference if I use it twice I dont understand?

I call the function it goes through the while loops and then gives me the value, I then call the function it does the exact same thing yet the values are smaller even though the 1st call shouldn't affect the second.

Thats what the main pulseIn() function does as well so I dont get why calling it twice affects anything.

This is the code inside my loop:

void loop() 
{
    long duration_small = pulseIn(pin_small, LOW);
    //duration_small = customPulseIn(pin_small);
    lowpulseoccupancy_small = lowpulseoccupancy_small + duration_small;

    long duration_large = pulseIn(pin_small, LOW);
    //duration_large = customPulseIn(pin_large);
    lowpulseoccupancy_large = lowpulseoccupancy_large + duration_large;

    if ((millis()-starttime) > sampletime_ms)//if the sampel time == 30s
    {
        ratio_small = lowpulseoccupancy_small/(sampletime_ms*10.0);  // Integer percentage 0=>100
        concentration_small = 1.1*pow(ratio_small,3)-3.8*pow(ratio_small,2)+520*ratio_small+0.62; // using spec sheet curve
        Serial.print("Small - ");
        Serial.print(lowpulseoccupancy_small);
        Serial.print(",");
        Serial.print(ratio_small);
        Serial.print(",");
        Serial.println(concentration_small);
        lowpulseoccupancy_small = 0;

        ratio_large = lowpulseoccupancy_large/(sampletime_ms*10.0);  // Integer percentage 0=>100
        concentration_large = 1.1*pow(ratio_large,3)-3.8*pow(ratio_large,2)+520*ratio_large+0.62; // using spec sheet curve
        Serial.print("Large - ");
        Serial.print(lowpulseoccupancy_large);
        Serial.print(",");
        Serial.print(ratio_large);
        Serial.print(",");
        Serial.println(concentration_large);
        lowpulseoccupancy_large = 0;
        
        starttime = millis();
    }
}

I am guessing each needs its own sample time. Difficult not seeing all your code, but I assume you initialize starttime in setup. When millis() exceeds sampletime_ms the totals are printed using how many sample events occured during that timeframe. With two samples they have to share sampletime_ms. So Sample 1 is not over time sampletime_ms, but over Sampletime_ms - time for sample 2. Sample 2 is not over sampletime_ms, but over sampletime_ms-time for sample 1. Good Luck

Try separating each sample into its own sampletime_ms loop and see if that makes a difference.

void loop()
{
  //duration small loop
  starttime = millis();
  while (millis() - starttime < sampletime_ms) {
    long duration_small = pulseIn(pin_small, LOW);
    //duration_small = customPulseIn(pin_small);
    lowpulseoccupancy_small = lowpulseoccupancy_small + duration_small;
  }
  //duration large loop
  starttime = millis();
  while (millis() - starttime < sampletime_ms) {
    long duration_large = pulseIn(pin_small, LOW);
    //duration_large = customPulseIn(pin_large);
    lowpulseoccupancy_large = lowpulseoccupancy_large + duration_large;
  }

  ratio_small = lowpulseoccupancy_small / (sampletime_ms * 10.0); // Integer percentage 0=>100
  concentration_small = 1.1 * pow(ratio_small, 3) - 3.8 * pow(ratio_small, 2) + 520 * ratio_small + 0.62; // using spec sheet curve
  Serial.print("Small - ");
  Serial.print(lowpulseoccupancy_small);
  Serial.print(",");
  Serial.print(ratio_small);
  Serial.print(",");
  Serial.println(concentration_small);
  lowpulseoccupancy_small = 0;

  ratio_large = lowpulseoccupancy_large / (sampletime_ms * 10.0); // Integer percentage 0=>100
  concentration_large = 1.1 * pow(ratio_large, 3) - 3.8 * pow(ratio_large, 2) + 520 * ratio_large + 0.62; // using spec sheet curve
  Serial.print("Large - ");
  Serial.print(lowpulseoccupancy_large);
  Serial.print(",");
  Serial.print(ratio_large);
  Serial.print(",");
  Serial.println(concentration_large);
  lowpulseoccupancy_large = 0;
}
    long duration_small = pulseIn(pin_small, LOW);

    long duration_large = pulseIn(pin_small, LOW);

You cannot use the same pin for the two sensors.

Hi Guys, Sorry for such a delayed response I was on holiday. I just got back tried out the code that you wrote and it doesnt seem to make a difference. @pylon thanks for spotting that for me I have been debugging the code so much I missed it.

@wzaggle I tried your code but it didnt seem to make a difference either. I am getting results they are just much lower then they should be.

Can someone explain to me why I am not able to use the pulseIn() function twice in the same sketch, it doesnt make sense to me. Surely the first one runs once that has finished it and then run the next one on the other pin and that should be fine?

Perhaps if you post a complete code that actually compiles ......

Can someone explain to me why I am not able to use the pulseIn() function twice in the same sketch, it doesnt make sense to me.

Does that mean all the changes didn't change the result? At least my change should have changed the result as you were reading the same pin twice.

I agree with gfvalvo, post complete code, the error might be in another section of the code.

Post the results you get and post what values you'd expect.

I did change the pin it was trying to use, that was just a typo from my end I was using the correct pin before making this post.

Here is all the code:

int pin_small = 3;
int pin_large = 4;

unsigned long duration_small;
unsigned long lowpulseoccupancy_small = 0;
float ratio_small = 0;
float concentration_small = 0;

unsigned long duration_large;
unsigned long lowpulseoccupancy_large = 0;
float ratio_large = 0;
float concentration_large = 0;

unsigned long starttime;
unsigned long sampletime_ms = 30000;//sampe 30s ;

long customPulseIn(int pin) {
    // wait for any previous pulse to end
    while ((digitalRead(pin)) == LOW) {}

    // wait for the pulse to start
    while ((digitalRead(pin)) != LOW) {}

    unsigned long start = micros();
    
    // wait for the pulse to stop
    while ((digitalRead(pin)) == LOW) {}

    return micros() - start;
}

void setup()
{
    Serial.begin(9600);
    pinMode(pin_small,INPUT);
    pinMode(pin_large,INPUT);
    starttime = millis();//get the current time;
}

void loop() 
{
    long duration_small = pulseIn(pin_small, LOW);
    //duration_small = customPulseIn(pin_small);
    lowpulseoccupancy_small = lowpulseoccupancy_small + duration_small;

    long duration_large = pulseIn(pin_large, LOW);
    //duration_large = customPulseIn(pin_large);
    lowpulseoccupancy_large = lowpulseoccupancy_large + duration_large;

    if ((millis()-starttime) > sampletime_ms)//if the sampel time == 30s
    {
        ratio_small = lowpulseoccupancy_small/(sampletime_ms*10.0);  // Integer percentage 0=>100
        concentration_small = 1.1*pow(ratio_small,3)-3.8*pow(ratio_small,2)+520*ratio_small+0.62; // using spec sheet curve
        Serial.print("Small - ");
        Serial.print(lowpulseoccupancy_small);
        Serial.print(",");
        Serial.print(ratio_small);
        Serial.print(",");
        Serial.println(concentration_small);
        lowpulseoccupancy_small = 0;

        ratio_large = lowpulseoccupancy_large/(sampletime_ms*10.0);  // Integer percentage 0=>100
        concentration_large = 1.1*pow(ratio_large,3)-3.8*pow(ratio_large,2)+520*ratio_large+0.62; // using spec sheet curve
        Serial.print("Large - ");
        Serial.print(lowpulseoccupancy_large);
        Serial.print(",");
        Serial.print(ratio_large);
        Serial.print(",");
        Serial.println(concentration_large);
        lowpulseoccupancy_large = 0;
        
        starttime = millis();
    }
}

I would try using your if statement to set variable when your past sample time, then break out each call so that your code pulls from just first sensor on that loop. The following loop check the other variable and if true it reads second sensor and then resets your 30s clock and it’s own variable to null.

You will getting seemingly concurrent results, but your allowing everything to cycle.

Slumpert: I would try using your if statement to set variable when your past sample time, then break out each call so that your code pulls from just first sensor on that loop. The following loop check the other variable and if true it reads second sensor and then resets your 30s clock and it’s own variable to null.

You will getting seemingly concurrent results, but your allowing everything to cycle.

Hi would you be able to write some code to explain what you mean. I don't think I quite understand so I will probably do it wrong

If the last code you posted does not work (what numbers does it produce?), then I would start looking at mixed data types and how the math is performed to prevent numbers from rolling over (exceed the limits of the data types):

//Following line mixes math between floats and unsigned longs which
//may cause fractions to be truncated
ratio_small = lowpulseoccupancy_small/(sampletime_ms*10.0);

//The following line again mixes data types "520" is in int, if "520*ratio_small"
//exceeds 32767, it will roll over and become negative.
concentration_small = 1.1*pow(ratio_small,3)-3.8*pow(ratio_small,2)+520*ratio_small+0.62;

Instead of the above, try to foce data types and group the math with paranthesis:

ratio_small = float(lowpulseoccupancy_small) / float(sampletime_ms*10);
concentration_small = (1.1f * pow(ratio_small, 3) - 3.8f) * (pow(ratio_small, 2) + (520.0f * ratio_small + 0.62f);

Also mind, that "(520.0f * ratio_small) + 0.62f" will not evaluate to the same as "520.0f * (ratio_small + 0.62f)".

You should also really consider to use arrays and create a method which does each sensor. Much easier to maintain! ;)

As soon as you invoke pulseIn it starts looking for the prescribed level and initiates its timer as soon as that level is detected until the level changes. To avoid the possibility of catching a signal mid-pulse, it is sometimes useful to wait until the opposite level is detected and then invoke the command. For example, if you want to test a high pulse on a pin, wait until the pin is low and then call pulseIn - while(digitalRead(pin) == HIGH){};