Go Down

Topic: Indoor Air Quality Sensor (Read 6582 times) previous topic - next topic

UWBMES

Apr 16, 2010, 11:32 am Last Edit: Apr 16, 2010, 11:51 am by UWBMES Reason: 1
I need help with adapting an air quality sensor to the Arduino (specifically the Duemilanove). I'm working on an indoor air quality monitor project as part of my Biomedical Engineering (BME) design course, and my design team is using the following sensor to perform the said function:
"link 1" (see next post)

Furthermore, we inquired via email to the company about using the sensor, to which we received the following response (a document):
"link 2" (see next post)

Basically, the sensor produces a pulse in response to the particulates in the chamber, corresponding to some concentration of particulates. The Lo-Pulse Occupancy Time is a function of the particulate concentration, which is what we need (our ultimate design is to have a three-light display, in which good, fair, and poor air qualities are represented by green, yellow, and red LEDs, respectively, at given cutoffs of concentration). So the issue at hand now is to get the sensor output (i.e. Lo-Pulse Occupancy Time) to the Arduino so that it has a value corresponding to some particulate concentration. Essentially, the sensor gives a pulse output, and we need to implement some code in the Arduino to determine the Lo-Pulse Occupancy Time in a 30 second interval (i.e. what percentage of the time does the output of the sensor stay in the Lo Pulse state in a unit time of 30 seconds, in response to some concentration of particulates in the chamber?). The question is, how? Admittedly, there is a lack of programming experience between the four of us in our design team (in our defense, we are BMEs and not EEs or Computer Scientists!). I was looking at the PulseIn function in the References page of the main Arduino website, but I don't know if this is what we need for our purposes. I assume we will use one of the PWM digital channels of the Duemilanove as an input, but I still don't know how to implement code such that we can determine the percentage of time that the pulse is in the Lo state in a 30 s interval. Thanks very much in advance for any help or suggestions!!!

P.S. If it matters, we checked the raw sensor output on an oscilloscope, and the Lo-Pulse state is ~350 mV while the Hi-Pulse state is ~3.6 V, in case that is of any use

UWBMES

Links:
(1) http://www.shinyei.co.jp/kik/optical/main_ppd4ns_e.html
(2) https://mywebspace.wisc.edu/pala/web/BME%20301/PPD42NS%20how%20it%20works.pdf

...and the datasheet as an added bonus:
https://mywebspace.wisc.edu/pala/web/BME%20301/PPD42%20Datasheet.pdf

(Note: since the previous post was my first on the forum, I was not allowed to post hyperlinks, so I had to put them in a separate post)

RIDDICK

#2
Apr 16, 2010, 11:49 am Last Edit: Apr 16, 2010, 11:56 am by RIDDICK Reason: 1
Hi!

The pulse length is between 10msec and 90msec according to that datasheet...

For connections to arduino u should use a resistor... so that in case of a mistake u cant fry ur arduino or ur sensor... 10kR should suffice...  :P

U could try to use digitalRead() like this (due to interrupt activity the readings might be inaccurate):
Code: [Select]

// wait for end of pulse cycle
while (digitalRead(air_pin) == HIGH);
while (digitalRead(air_pin) == LOW);
// now do the timing
const uint32_t t0 = micros();
while (digitalRead(air_pin) == HIGH);
const uint32_t t1 = micros();
while (digitalRead(air_pin) == LOW);
const uint32_t t2 = micros();
// send out the duty cycle (HIGH percentage) via UART (maybe u need the LOW percentage)
Serial.print((t1-t0)*1e2/(t2-t0));
Serial.println('%');

There r more accurate methods by counting loop cycles (without micros() and with noInterrupts()):
http://arduino.cc/en/Reference/PortManipulation
and less accurate methods:
http://arduino.cc/en/Reference/PulseIn

or a RC-lowpass with analogRead()...

Cheers, Arne

UWBMES

Arne,
Thanks a lot, I really appreciate it!!! I think you definitely pointed us in the right direction! I'll keep you posted as to how it turns out (our team is scheduled to work on the project today... we are located in the United States, however, in case you couldn't tell :) ).

Grumpy_Mike

Quote
For connections to arduino u should use a resistor... so that in case of a mistake u cant fry ur arduino or ur sensor... 10kR should suffice..


I would disagree, 10K is to much, you run the risk of not being able to pull down to a logic zero level. I would not use any series resistor to connect internal parts. Even if it doesn't stop it working it will severely reduce your noise margin.

UWBMES

#5
Apr 20, 2010, 06:08 am Last Edit: Apr 20, 2010, 06:23 am by UWBMES Reason: 1
We attempted the following code in our design:

Code: [Select]

int x = 1; //integer value that will be incremented after every 30 seconds
int duration = micros(); //the amount of time the program has been running
int pulseLow = 0; //will store the amount of time the pulse from the sensor has been at LOW during the current interval
int digitalInput = 12; //digital pin 12 will serve as the input pin from the sensor
int ledPinRed = 5; //digital pin 5 will serve as output to red led
int ledPinYellow = 6; //digital pin 6 will serve as output to yellow led
int ledPinGreen = 7; //digital pin 7 will serve as output to green led
int percentage = 0; //the low pulse occupancy time

void setup()
{
 pinMode(digitalInput, INPUT); //pin 12 will be an input
 pinMode(ledPinRed, OUTPUT); //pin 5 will be an output
 pinMode(ledPinYellow, OUTPUT);//pin 6 will be an output
 pinMode(ledPinGreen, OUTPUT);// pin 7 will be an output
}

void loop() //loop program continuously
{
 if (duration != x*30e6) //if we are not at the end of a 30 second interval
 {
   do //do the following
   {
     pulseLow += pulseIn(digitalInput, LOW);//pulselow wills store the total amount of time are input is at low during the current interval
     duration = micros(); //duration should roughly store the current time the program has been running
   } while ((x*duration) / 30e6 != x); //program completes the above statements if, and only if, we are not at the end of a 30 s interval
 }
   else//otherwise we are at the end of a 30 second interval and need turn on a LED
   {
     x++;//increment x by 1, this will allow the above loop to work for the next 30 s interval
     percentage = (pulseLow/30e6)*100;//calculates the low pulse occupancy time
     if (percentage >= 0 && percentage < 4) //if low pulse occupancy time is between 0 (inclusive) and 4 (exclusive) %, green LED is on
     {
       digitalWrite(ledPinGreen, HIGH); //turn the green led on      
       digitalWrite(ledPinYellow, LOW); //turn the yellow led off
       digitalWrite(ledPinRed, LOW); //turn the red led off
       delay(200); //stop program for .2 s, initiate  one blink
       digitalWrite(ledPinGreen, LOW);
       delay(200);
       digitalWrite(ledPinGreen, HIGH); //green led is on
     }
     else if (percentage >= 4 && percentage < 8) //if low pulse occupancy time is between 4 (inclusive) and 8 (exclusive) %, yellow LED is on
     {
       digitalWrite(ledPinGreen, LOW);        
       digitalWrite(ledPinYellow, HIGH);
       digitalWrite(ledPinRed, LOW);
       delay(200);
       digitalWrite(ledPinYellow, LOW);
       delay(200);
       digitalWrite(ledPinYellow, HIGH);
     }
     else {} //if low pulse occupancy time is greater than 8 (inclusive) %, red LED is on
       digitalWrite(ledPinGreen, LOW);        
       digitalWrite(ledPinYellow, LOW);
       digitalWrite(ledPinRed, HIGH);
       delay(200);
       digitalWrite(ledPinRed, LOW);
       delay(200);
       digitalWrite(ledPinRed, HIGH);
   }
   pulseLow = 0;//resets the low pulse time to 0 for the next 30 second interval
 }



It doesn't seem to work, however; thus, I would like to request your input/insight...

The setup is as follows:
- The output of the sensor (which is powered by the 5V supply from the Arduino and is also connected to common ground) is connected to digital pin 12
- three LEDs are connected to pins 5, 6, and 7 (red, yellow, and green)

Here is what we would like the device to do:
- The sensor is to sample continously for 30 s, and the Low Pulse Occupancy Time (see the very first post of this thread) is supposed to be determined from this 30 s sample
- The resulting Low Pulse Occupancy Time is supposed to be sent to one of the three LEDs, depending on one of the cutoffs, 4% and 8% (the resulting LED is then to blink to indicate that the sampling was updated, and remain lit)
- the continuous sampling is to occur indefinitely, with the LED output being updated every 30 s (hence the overall while loop)

...This code does not appear to work correctly, even after we fixed some wiring errors. Does anyone have any suggestions???


RIDDICK

#6
Apr 23, 2010, 12:46 am Last Edit: Apr 23, 2010, 04:45 pm by RIDDICK Reason: 1
oh - i forgot to check this thread again...
i hope i m still in time...

it is very unlikely that u check at the right microsecond, because all those things can take several microseconds...
so u should write "if (duration < x*1e6)"...

the while-condition looks suspicious, too...
it should be "while (duration < x*1e6);"...

btw: why do u want to use floating point arithmetics?
u could use "if (duration < x*1000000UL)", too...
"UL" means "unsigned long"... an "unsigned long" is a "uint32_t"...

and u should reset "pulseLow" to "0" in the "else" branch...

pulseIn delivers a value in "milli seconds"... so u should devide it by 30e3 (not 30e6)...

the rest looks good...
but just for 1.19hrs... :-) then or about then micros() starts at 0 again...

why dont u do it without the "x"?

Code: [Select]
void loop() {
 // 1. phase: pulseIn for 30 seconds
 const uint32_t howlong = 30000UL;
 const uint32_t sts = millis(); // start time stamp
 uint32_t ls = 0;
 while (millis() - sts < howlong)
   ls += pulseIn(...);
 percentage = ls/(howlong/100);
 // 2. phase: interpret the result
 ... like the else-branch
}


NB: in unsigned integer arithmetics a single overflow is well tolerated in the "millis() - sts" subtraction above...
e. g.: we use a 3 bit integer and howlong is 2:
Quote

sts=6 millis()=7 --> 7-6 = 1 < 2
sts=6 millis()=0 (overflow occurred) -> -6 mod 8 = (-6+8) mod 8 = 2 mod 8


-arne

Go Up