There are various articles and posts about using the Sharp Gp2Y1010AU0F sensor for air quality monitoring. The original seems to be Chris Nafis in 2012 http://www.howmuchsnow.com/arduino/airquality/, but there are several other posts covering this interesting and cheap device.
The Sharp data sheet for the sensor explains how it works and has diagrams showing the required driving circuit, details of the timing required and a graph of the typical output.
There’s also a slightly more informative application note available from Sharp.
Both of these show the timing required for the sensor LED as a 10mS pulse cycle time with a .32mS pulse width. They also state that the output voltage should be measured .28mS after the start of the LED pulse.
The original code, on which all the other versions I’ve seen are based, used the analogRead() function to measure the output voltage from the sensor using the Arduino’s analouge to digital converter (ADC). In outline the sampling loop of the code goes like this…
- Turn LED ON
- Wait 280uS
- Sample sensor output voltage
- Wait 40uS
- Turn LED OFF
- Wait 9680uS
At first glance this would seem to meet the timings specified by Sharp. However, analogRead() takes 13 clock cycles to complete an ADC conversion and these are ADC clock cycles which are prescaled from the 16MHz system clock by a factor of 128. This means that analogRead() takes at least 104uS to return a value, so the pulse width ends up being far in excess of the 320uS required. The following screen shot from an oscilloscope shows this.
The yellow trace shows the LED pulse (0v=LED On, +5v =LED Off), and the cyan trace the output from the sensor.
Having the longer pulse does seem to distort the output, though starting analogRead() at 280uS would still be measuring the maximum value so perhaps it doesn’t change the result. To check if this is the case, I used a signal generator to get the exact timing specified for the LED which, as can be seen below, makes the peak value a little higher.
So, having established that the timing does have an impact on the sensor output values I looked at what could be done to get the timing right. The ADC prescaling factor defaults to 128 but this can be reduced. This makes the conversion faster but at the expense of accuracy. When used with a prescaling factor of 64 analogRead() takes around 52uS, so starting the conversion at 280uS would allow the LED to be turned off at about 350uS. Although the main problem is the time that analogRead() takes, all the other instructions in the code take time too. Fiddling around with the settings and checking the effect on the ‘scope about the closest to the 280/320 ‘ideal’ settings are acheived with a delay of 250uS between turning the LED on and the analogRead(), then turning the LED off as soon as it returns(i.e. post read delay of zero).
Although you can’t get the timing right using the analogRead() function, it can be done with a 16MHz Arduino and using the default ADC prescaling of 128 for full accuracy. The solution is to attach an interrupt service routine to the ADC completed interrupt to read the value. This means that control returns while the ADC conversion is in progress so the LED can be turned off at the right time. The conversion can be started at the right time, but the ADC has to be controlled directly rather than by calling analogRead(). Arduino code showing how to do this will be in my next post.