Skip to Content

APDS-9930 Proximity and Light Sensor with Arduino

APDS-9930 Proximity and Light Sensor with Arduino

In this tutorial you will learn how to use the APDS-9930 Proximity and Light Sensor with an Arduino or any other common microcontroller (ESP32/ESP8266).

The APDS-9930 is a very small sensor that is often used in smartphones and tablets for automatic screen brightness adjustment and proximity sensing during calls. You can also use it as a simple gesture detector that activates lights or other devices when waving your hand in front of it.

Required Parts

For this project you will need an APDS-9930 sensor and a microcontroller. I used an Arduino Uno but any other Arduino or any ESP32/ESP8266 will work as well, as long as it provides a 3.3V power output.

APDS-9930 Sensor

Arduino

Arduino Uno

USB Data Sync cable Arduino

USB Cable for Arduino UNO

Dupont wire set

Dupont Wire Set

Half_breadboard56a

Breadboard

Makerguides.com is a participant in the Amazon Services LLC Associates Program, an affiliate advertising program designed to provide a means for sites to earn advertising fees by advertising and linking to products on Amazon.com. As an Amazon Associate we earn from qualifying purchases.

Features of the APDS-9930 Proximity and Light Sensor

The APDS-9930 is a tiny (3.94 x 2.36 x 1.35mm) sensor for proximity detection and digital ambient light sensing. The picture below shows the back and front of the APDS-9930 chip.

APDS-9930 Chip
APDS-9930 Chip

The sensor measures the proximity of an object by sending out an IR pulse vie the integrated IR Led and measuring the intensity of the reflected light. Ambient light is detected by two photodiodes. One photodiode detects visible plus infrared (Ch0 ) and the other one detects infrared only (Ch1). An integrated microprocessor combines the two signals to compute an ambient light level (ALS) that approximate the human eye response. See the picture below with the Functional Block Diagram of the APDS-9930:

Functional Block Diagram of APDS-9930 (source)

The APDS-9930 also allows you to set upper and lower thresholds for light and proximity values, and sends an interrupt signal (INT), if those thresholds are exceeded. For communication and programming, the APDS-9930 provides an I2C interface (SCL, SDA)

The following list summarizes the main features of the APDS-9930:

  • Ambient Light Sensing (ALS)
  • Approximate human visual response
  • Programmable interrupt capability with upper and lower thresholds
  • Up to 16-bit resolution
  • High sensitivity operation behind dark glass
  • 01lux low lumen performance
  • Proximity detection
  • Fully calibrated to 100 mm detection
  • Integrated infrared LED and synchronous LED driver
  • Eliminates factory calibration of the proximity sensor
  • Programmable wait timer
  • Wait state power consumption – 90μA typical
  • Programmable range from 2.7 ms to more than 8 seconds
  • I2C interface compatible
  • Up to 400kHz (I2C fast mode)
  • Dedicated interrupt pin
  • Sleep Mode Power – 2.2μA typical

For more detailed information have a look at the datasheet linked below:

Breakout board for APDS-9930

The APDS-9930 Chip is too small to connect it to an Arduino directly. Typically, you will need a breakout board as shown below:

Front and Back of Breakout board for APDS-9930
Front and Back of Breakout board for APDS-9930

Breakout boards for the APDS-9930 usually have six pins:

  • VCC: Power supply (2.4 – 3.6V)
  • GND: Ground
  • VL: IR LED power
  • SDA: I2C data signal
  • SCL: I2C clock signal
  • INT: Interrupt pin

In most cases you will need only the power supply pins (VCC, GND) and the pins for I2C communication (SCL, SDA). The INT pin becomes active if ambient light or proximity exceeds programmable thresholds.

VL allows you to provide external power to the IR LED. On the back of the breakout board is a solder jumper that you need to close if you want to use VL for IR LED power.

Note that the APDS-9930 runs on 3.3V and that most breakout boards usually don’t have a voltage regulator. This means you must connect VCC to 3.3V and cannot use the 5V output of a Arduino!

Connecting the APDS-9930 to Arduino

Thanks to the I2C interface of the APDS-9930, connecting it to an Arduino is easy. First, connect the SCL pin of the APDS-9930 breakout board to A5 of the Arduino. Similarly, connect SDA to A4 of the Arduino. Next, connect ground to GND and 3.3V to VCC of the APDS-9930.

Connecting APDS-9930 to Arduino
Connecting APDS-9930 to Arduino

Make sure that you use 3.3V as power supply. The APDS-9930 sensor is not rated for 5V and the typical breakout boards for the APDS-9930 do not have a voltage regulator.

Install APDS-9930 Library

Before we can use the APDS-9930 we need to install a library. The only library that I could find is the APDS-9930 Library by Depau, which is not maintained anymore but still works fine. To install it go to the repo and click on the green “Code” button. Then click on “Download Zip” as show below:

Download APDS-9930 library
Download APDS-9930 library

Then go “Sketch” -> “Include Library” -> “Add .Zip Library..” and select the “APDS9930-master.zip” file you just downloaded before:

Adding APDS-9930 library to sketch
Adding APDS-9930 library to sketch

Alternatively, you can just download the entire code of the repo, zip it and then include the library in the same way as described above. Now we are ready to write some code.

Measuring Ambient Light with APDS-9930

The following code reads ambient light measurements from the APDS-9930 sensor.

#include "Wire.h"
#include "APDS9930.h"

APDS9930 sensor = APDS9930();

void setup() {
  Serial.begin(9600);
  sensor.init();
  sensor.enableLightSensor(false);
  delay(500); 
}

void loop() {
  static float light = 0;
  
  if (sensor.readAmbientLightLux(light)) {
    Serial.print("light:");
    Serial.println(light);
  }

  delay(500);
}

We start by including the required libraries and creating the sensor object. In the setup function the sensor gets initialized and the light sensor enabled.

The false in enableLightSensor(false), means that we are not using interrupts, when measuring ambient light. You can connect the INT output of the APDS-9930 to a digital input of the Arduino and activate an Interrupt Service Routines (ISR) when a certain level of light is detected. The APDS-9930 has example code for this.

In the loop function we read the light level via readAmbientLightLux(light) and if successful print it to the Serial Monitor. See the following example readings:

Ambient light levels printed to Serial Monitor
Ambient light levels printed to Serial Monitor

And if you open the Serial Plotter and point the APDS-9930 to a light source, e.g. a desk lamp in this case, you should see a peak in the graph:

Ambient light levels on Serial Plotter
Ambient light levels on Serial Plotter

Measuring Proximity with APDS-9930

The APDS-9930 detects the proximity of an object by sending an IR pulse from the internal IR LED and measuring the amount of IR energy reflected. The closer the object the more energy is reflected.

The code for measuring proximity is very similar to the code for measuring ambient light. The only two differences are that we call enableProximitySensor(false), in the setup function and readProximity(proximity) in the loop function. See the example below:

#include "Wire.h"
#include "APDS9930.h"

APDS9930 sensor = APDS9930();

void setup() {
  Serial.begin(9600);
  sensor.init();
  sensor.enableProximitySensor(false);
  delay(500); 
}

void loop() {
  static uint16_t proximity = 0;
  
  if (sensor.readProximity(proximity)) {
    Serial.print("proximity:");
    Serial.println(proximity);
  }

  delay(500);
}

As before the false in enableProximitySensor(false), means that we are not using interrupts but continuously reading proximity values. Typical proximity values range between around 300 (no object) and 1023 when the object is very close.

If you open the Serial Plotter and repeatedly place your hand closer or further away from the sensor you should see a wave like pattern similar to the one shown below:

Proximity values on Serial Plotter
Proximity values on Serial Plotter

Measuring Proximity and Light with APDS-9930

Finally, we obviously can measure proximity and ambient light levels at the same time. The code example below enables the proximity and the light sensor in the setup function and the read the proximity values and the light levels in the loop function:

#include "Wire.h"
#include "APDS9930.h"

APDS9930 sensor = APDS9930();

void setup() {
  Serial.begin(9600);
  sensor.init();
  sensor.enableProximitySensor(false);  
  sensor.enableLightSensor(false);  
}

void loop() {
  static uint16_t proximity = 0;
  static float light = 0;
  
  if (sensor.readProximity(proximity)) {
    Serial.print("proximity:");
    Serial.println(proximity);
  }
    
  if (sensor.readAmbientLightLux(light)) {
    Serial.print("light:");
    Serial.println(light);
  }  

  delay(500);
}

If you run this code and look at the Serial Plotter, you will notice that the light sensor and the proximity sensor show a negatively correlated behaviour. See the picture below.

Proximity and Light values on Serial Plotter
Proximity and Light values on Serial Plotter

This is, because, if you bring hand close to the sensor the proximity value is high but since the hand obstructs the ambient light, the measured light level is low. Inversely, if there is no object close to the sensor, the proximity value is low, but the light level is higher (assuming there is any light).

Automatic brightness adjustment with APDS-9930

For our final example let’s implement the typical application of the APDS-9930. We are activating an LED if an object (hand) is close to the sensor and adjust the brightness of the LED according to the ambient light levels.

For that we just need to add an LED to our circuit. In the wiring shown below I added an LED with a 220Ω resistor to pin 11. You can use another output pin but it has to be a PWM pin.

Connecting APDS-9930 and LED to Arduino
Connecting APDS-9930 and LED to Arduino

In the code below, we read the proximity and light values. If the proximity threshold exceeds 500 (hand is close to sensor), we set the brightness of the LED relative to the level of the ambient light. The more ambient light the brighter the LED.

#include "Wire.h"
#include "APDS9930.h"

APDS9930 sensor = APDS9930();
const int ledPin = 11;  // PWM

void setup() {
  Serial.begin(9600);
  sensor.init();
  sensor.enableProximitySensor(false);
  sensor.enableLightSensor(false);
  pinMode(ledPin, OUTPUT);
  delay(500);
}

void loop() {
  static uint16_t proximity = 0;
  static float light = 0;
  static float maxLight = 0;

  sensor.readProximity(proximity);
  sensor.readAmbientLightLux(light);

  if (proximity > 500) {
    maxLight = max(light, maxLight);
    int brightness = map(light, 0, maxLight, 0, 1023);
    analogWrite(ledPin, brightness);
  } else {
    analogWrite(ledPin, 0);
  }

  delay(100);
}

Since we don’t know upfront how bright the ambient light can be, the code also updates the maximum ambient light level in each iteration. But you could also run some tests and pick a fixed maximum light level.

And that’s it. I hope you had fun playing with the APDS-9930 Proximity and Light Sensor – I certainly did ; )

Conclusions

In this tutorial you learned how to use the APDS-9930 Proximity and Light Sensor with an Arduino to detect objects and to measure ambient light levels.

Note that the APDS-9930 Library by Depau has more functions than what we demonstrated here. Specifically, you can read the values for the two photodiodes (Ch0, Ch1), set thresholds for proximity and light levels, and react to interrupts. Have a look at the example code that comes with the library.

The APDS-9930 is a good sensor to detect simple gestures and to react to light. For instance, if you want to activate a display by waving your hand in front of it and also adjust the brightness of the display depending on the ambient light at the same time, the APDS-9930 is great for that.

However, if you want to accurately measure distances to objects, e.g. for robotics applications, you are better off with infrared distance sensors such as the GP2Y0A710K0F that use triangulation to determine the distance to an object. Or even better, pick a Time-of-Flight (ToF) laser range distance sensor such as the TOF10120 or the VL53L1X library, which can measure even long distances very accurately.

If you have any questions feel free to leave them in the comment section.

Happy Tinkering ; )