Sharp Dust Sensor – Arduino Nano and 2×16 LCD

Parts List:

  • Sharp Dust Sensor GP2Y1010AU0F
  • Arduino Nano (or pretty much any other 5v Arduino)
  • 2×16 LCD
  • Connector for sharp sensor (JST S6B-ZR-SM4A-TF)
  • Stripboard (25 x 9, lengthways copper strips)
  • 150 ohm resistor
  • 220uF capacitor
  • 10k trimmer pot
  • ribbon cable
  • wire
  • (Optional) Small 5v cooling fan
  • (Optional) 3 pin male header, for fan connection
  • (Optional) 9 pin female header, makes testing easier



Wiring Details

Stripboard Connections:

Row 1 (+5v)

  • LCD Pin 2 (VDD)
  • LCD Pin 15 (A)
  • Sensor Pin 6
  • Arduino +5v
  • Wire jumper to row 8
  • Resistor lead 1
  • Trimmer Pin 1

Row 2 (LCD Contrast)

  • LCD Pin 3 (VO)
  • Trimmer Pin 2

Row 3 (GND)

  • Trimmer Pin 3
  • Wire jumper to row 9

Row 4 (Sensor Output)

  • Sensor Pin 5
  • Arduino A0

Row 5 (Sensor LED Supply)

  • Sensor Pin 1
  • Resistor lead 2
  • Capacitor +ve

Row 6 (Sensor LED control)

  • Sensor Pin 3
  • Arduino D2

Row 7 (Not Connected)

  • Male header Pin 1 (NC)

Row 8 (+5v, for Fan)

  • Wire jumper to row 1
  • Male header Pin 2 (+5v)

Row 9 (GND)

  • LCD Pin 1 (VSS)
  • LCD Pin 5 (R_W)
  • LCD Pin 16 (K)
  • Sensor Pin 2
  • Sensor Pin 4
  • Arduino GND
  • Male header Pin 3 (GND)
  • Capacitor -ve
  • Wire jumper to row 3

Arduino to LCD Connections:

  • Arduino D7 to LCD Pin 4 (RS)
  • Arduino D8 to LCD Pin 6 (E)
  • Arduino D9 to LCD Pin 11 (D4)
  • Arduino D10 to LCD Pin 12 (D5)
  • Arduino D11 to LCD Pin 13 (D6)
  • Arduino D12 to LCD pin 14 (D7)


Sharp Dust Sensor with Arduino – ISR Code to correct timing

This code works on an Arduino Nano connected to a Sharp Dust Sensor and a 2 line LCD. It shows how to get the correct timing for the pulse that controls the LED in the sensor. The display shows the minimum and maximum voltages measured from the sensor output, in 5 second sampling periods.

This version doesn’t do any conversion of the output voltage to a dust density.


 Interface to Sharp GP2Y1010AU0F Particle Sensor
 Modified from Program by Christopher Nafis 
 Written April 2012

 Changes (Sept 2016):
 LCD Display
 ISR code for ADC
#include <LiquidCrystal.h>
#include <stdlib.h>

const byte adcPin = 0;
// Variables that can be changed by an ISR must be declared as volatile
volatile int adcReading;
volatile boolean adcDone;
boolean adcStarted;

int dustPin=0;
int ledPower=2;
int delayTime=280;
int delayTime2=40;
float offTime=9580;
int dustVal=0;
int i=0;
int c=0;
int adcMin=1023;
int adcMax=0;
float ppm=0.0;
char s[32];
float voltage = 0.0;
float vMin = 0.0;
float vMax = 0.0;
float dustdensity = 0.0;
float ppmpercf = 0.0;

// Define the pins used by the LCD
LiquidCrystal lcd(7,8,9,10,11,12);

void setup(){ 
 // set the analog reference (high two bits of ADMUX) and select the
 // channel (low 4 bits). this also sets ADLAR (left-adjust result)
 // to 0 (the default).
 ADMUX = bit (REFS0) | (adcPin & 0x07);

// ADC complete Interupt Service Routine
ISR (ADC_vect)
 byte low, high;
 // read the result registers and store value in adcReading
 // Must read ADCL first
 low = ADCL;
 high = ADCH;
 adcReading = (high << 8) | low;
 adcDone = true; 
// end of ADC_vect

void loop(){
 do {
 digitalWrite(ledPower,LOW); // power on the LED
 delayMicroseconds(delayTime); // wait 280uS
 // Check the conversion hasn't been started already
 if (!adcStarted)
 adcStarted = true;
 // start the conversion
 ADCSRA |= bit (ADSC) | bit (ADIE);
 delayMicroseconds(delayTime2); // wait another 40uS or so to give 320uS pulse width
 digitalWrite(ledPower,HIGH); // turn the LED off
 // give the ADC time to complete then process the reading
 if (adcDone) 
// adcDone is set to True by the ISR, called when the conversion is complete.
 adcStarted = false;
 if (adcMin > adcReading) adcMin = adcReading; // 5 second Minimum
 if (adcMax < adcReading) adcMax = adcReading; // 5 second Maximum
 c = c+1; // count of readings
 adcDone = false;
 delayMicroseconds(offTime); // wait for the remainder of the 10mS cycle time
 } while (i <500); //repeat for 5 seconds
 // Convert ADC max/min readings in last 5 seconds (0-1023) to voltages
 vMin = adcMin * 5.0 / 1024;
 vMax = adcMax * 5.0 / 1024;
 String dataString = "vMin:";
 dataString += dtostrf(vMin, 9, 4, s);
 dataString = "vMax:";
 dataString += dtostrf(vMax, 9, 4, s);

Sharp Dust Sensor with Arduino – Getting the timing right.

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, 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
  • Repeat

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.