Built-in project · Arduino Uno R3

Swipe lamp on an Arduino Uno

An APDS-9960 over I2C with SparkFun’s library: swipe up and down to dim an LED, left and right to switch it, and read the room’s light and color. The whole build, an Arduino Uno and 3 more parts, runs here in your browser on the firmware below; open it in the editor to change the wiring or the code and run it again.

Teaching? Assign it to your class in one click.

Intermediate About 20 minutes to build and run. Runs in your browser. Free, and no account needed.

The parts retail for about $33. On Mokxi they're free.

The circuit runs here once the page has loaded.

What you'll learn

  • Reading gestures from an I2C sensor
  • Setting LED brightness with PWM using analogWrite
  • Keeping a value in range with min and max
  • Printing sensor data on a timer
  • Difficulty: Intermediate
  • Time: About 20 minutes
  • Board: Arduino Uno R3
  • Parts: 5

How it works

An APDS-9960 sensor talks to the Uno over I2C on A4 and A5, powered from 3.3 V. It has four photodiodes and an infrared LED. A hand swiping over it lights the diodes in an order that tells the direction.

The sketch uses SparkFun's library to read that gesture. Up raises the LED brightness by 64, down lowers it by 64, left turns the lamp off and right turns it on. The LED is on pin 9 through 220 ohms, with brightness set by analogWrite. Every 2 seconds it also prints the light level.

Parts list

5 parts, plus the jumper wires. They retail for about $33; on Mokxi they're free.

Wiring table

Every connection, pin by pin, read from the circuit itself. Each row joins the pin on the left to the pin on the right, by a jumper wire or a breadboard strip.

Part and pinConnects to
Resistor, 220 Ω pin 1Arduino Uno R3 pin 9
APDS-9960 gesture sensor pin VCCArduino Uno R3 pin 3V3
APDS-9960 gesture sensor pin GNDArduino Uno R3 pin GND
LED, yellow pin CArduino Uno R3 pin GND
APDS-9960 gesture sensor pin SDAArduino Uno R3 pin A4
APDS-9960 gesture sensor pin SCLArduino Uno R3 pin A5
LED, yellow pin AResistor, 220 Ω pin 2

The code

The firmware exactly as the editor opens it. Change a line there and press Run: it compiles in the browser.

sketch.ino · Arduino Uno R3
// Swipe lamp: an APDS-9960 watches for a hand swiping over it, read with
// SparkFun's library, and dims an LED to match.
//
//   APDS-9960   VCC -> 3.3 V   GND -> GND   SDA -> A4   SCL -> A5
//   LED         pin 9 -> 220 ohm -> LED -> GND
//
// Four photodiodes sit behind the sensor's window, one on each side, and an
// infrared LED lights the hand. A hand swiping up reaches the up diode first
// and the down one last, so the order the four light up in says which way it
// went. Swipe up for brighter, down for dimmer, left for off and right for on.
// Every two seconds it also prints the light it sees.
//
// The arrows on the sensor swipe a hand across it; the sliders and the C
// button set the room's light.

#include <Wire.h>
#include <SparkFun_APDS9960.h>

const int LAMP_PIN = 9;

SparkFun_APDS9960 apds;
int level = 128;
bool on = true;
unsigned long lastLight = 0;

void show() {
  analogWrite(LAMP_PIN, on ? level : 0);
  Serial.print("lamp ");
  Serial.println(on ? level : 0);
}

void setup() {
  Serial.begin(9600);
  pinMode(LAMP_PIN, OUTPUT);
  if (!apds.init()) {
    Serial.println("Something went wrong during APDS-9960 init!");
    while (1) {
    }
  }
  apds.enableLightSensor(false);
  apds.enableGestureSensor(true);
  Serial.println("Swipe lamp ready");
  show();
}

void loop() {
  if (apds.isGestureAvailable()) {
    switch (apds.readGesture()) {
    case DIR_UP:
      Serial.println("UP");
      level = min(level + 64, 255);
      break;
    case DIR_DOWN:
      Serial.println("DOWN");
      level = max(level - 64, 0);
      break;
    case DIR_LEFT:
      Serial.println("LEFT");
      on = false;
      break;
    case DIR_RIGHT:
      Serial.println("RIGHT");
      on = true;
      break;
    default:
      Serial.println("NONE");
      return;
    }
    show();
  }
  if (millis() - lastLight > 2000) {
    lastLight = millis();
    uint16_t c, r, g, b;
    apds.readAmbientLight(c);
    apds.readRedLight(r);
    apds.readGreenLight(g);
    apds.readBlueLight(b);
    Serial.print("light ");
    Serial.print(c);
    Serial.print("  red ");
    Serial.print(r);
    Serial.print("  green ");
    Serial.print(g);
    Serial.print("  blue ");
    Serial.println(b);
  }
}

Code walkthrough

The key functions of the sketch, and what each one does.

show sketch.ino

Writes the lamp's brightness to pin 9 with analogWrite. It writes level when the lamp is on and 0 when it is off, then prints the lamp value to serial.

void show() {
  analogWrite(LAMP_PIN, on ? level : 0);
  Serial.print("lamp ");
  Serial.println(on ? level : 0);
}

setup sketch.ino

Sets pin 9 as an output and starts the APDS-9960. If it does not start, it prints an error and stops. Otherwise it turns on the light and gesture sensors, prints a ready line and calls show.

void setup() {
  Serial.begin(9600);
  pinMode(LAMP_PIN, OUTPUT);
  if (!apds.init()) {
    Serial.println("Something went wrong during APDS-9960 init!");
    while (1) {
    }
  }
  apds.enableLightSensor(false);
  apds.enableGestureSensor(true);
  Serial.println("Swipe lamp ready");
  show();
}

loop sketch.ino

If a gesture is waiting, it reads it and changes level or on: up and down move by 64 within 0 to 255, left switches off, right switches on. Then it calls show. Every 2 seconds it prints the clear, red, green and blue light readings.

void loop() {
  if (apds.isGestureAvailable()) {
    switch (apds.readGesture()) {
    case DIR_UP:
      Serial.println("UP");
      level = min(level + 64, 255);
      break;
    case DIR_DOWN:
      Serial.println("DOWN");
      level = max(level - 64, 0);
      break;
    case DIR_LEFT:
      Serial.println("LEFT");
      on = false;
      break;
    case DIR_RIGHT:
      Serial.println("RIGHT");
      on = true;
      break;
    default:
      Serial.println("NONE");
      return;
    }
    show();
  }
  if (millis() - lastLight > 2000) {
    lastLight = millis();
    uint16_t c, r, g, b;
  // ... 14 more lines in the editor

Troubleshooting

The usual mistakes with this circuit, and the fix for each.

  • Serial says Something went wrong during APDS-9960 init This project
    Check SDA on A4, SCL on A5 and that VCC is on the 3.3 V pin.
  • Swipes are missed This project
    Swipe with your whole hand at a steady speed, across the sensor and not just near it.
  • Swiping up and down changes the lamp in big jumps This project
    Each swipe moves it by 64. Change the 64 in loop for finer steps.
  • The sensor is not found, or the chip misbehaves on 5 V. APDS-9960 gesture sensor
    It is a 3.3 V part with I2C address 0x39. Do not feed it 5 V on VCC or on SDA and SCL. Use a level shifter if your board is 5 V, and use pull-ups to 3.3 V.
  • Gestures, color or proximity read nothing. APDS-9960 gesture sensor
    Each feature must be enabled in code, such as enableProximity, enableColor and enableGesture. Gesture needs a hand about 10 to 20 cm away, moving steadily. The INT pin can be used for gesture interrupts.
  • The LED is dim, burns out, or the pin reads wrong because the resistor value is off. Resistor
    Read the color bands again. A 220 ohm to 330 ohm resistor suits a typical LED on 5 V. 10k is a common pull-up or pull-down value. Check with a multimeter if unsure.
  • A pull-up or pull-down resistor is missing, so the input floats and reads random values. Resistor
    Tie the input to 5 V or ground through about 10k. Or use INPUT_PULLUP and wire the switch to ground.
  • The LED does not light, or it lights once and then dies. LED
    LEDs have polarity. The longer leg (anode) goes toward the positive side. Always add a series resistor, about 220 ohms to 1 k ohm on a 5 V pin.

Change it and keep it

Open it in the editor, change the circuit or the code, and keep your version in a free account.