The first prototype of the rgb scanner

Table of Contents

Intro

I made this project to be able to set my Philips Hue lights to the color of objects in our flat. See the video below for an example:

Project files

The Parts

  • D1mini / ESP8266
  • APDS-9660 Module
  • 2x 5mm LED (white!)
  • 2x 470 Ohm Resistor (or simliar, for the LEDs)
  • Lipo Charger (TP4056)
  • 450 mAh Lipo (with protection circuit)
  • 5x7cm proto board
  • A switch

Assembly

Currently this is not a howto for beginners. You can contact me via social media if you need more help. I’ve written down this post, almost a year after building this prototype.

LEDs

Connect the resistors to the pins, to the LEDs, to GND. The LEDs will be driven directly with two pins (D5,D6) this way:

D5 --- R470 --- LED -|>|-- GND
D6 --- R470 --- LED -|>|-- GND

Hint: Make sure the resistors (470 Ohms in my case), match the specs of your LED and does not exceed 12mA ⚠️.

Power Source

I connected the LiPo to a switch, wich either connects it to the charger (TP4056), or to the main circuit. This way the switch toggles between OFF (TP4056/ or charging), and ON (3V3/no charging).

             ---- BAT+ (TP4056)
            /
LIPO+  -----
            \
             ---- 3V3 (D1mini)

Using and charging the device at the same time is not possible this way, but simplified things a bit.

APDS-9660

The APDS-9660 is an I2C device, you can read a more about I2C in general in my post here. If you want to practice with this module first take a look at this page: https://learn.sparkfun.com/tutorials/apds-9960-rgb-and-gesture-sensor-hookup-guide/all

D1 mini

Usually my projects are based on the D1 mini from Wemos. You find a lot of information on how to get this to run from their homepage: https://wiki.wemos.cc/products:d1:d1_mini

How it could look like

As you can see this is a rather experimental build. I recommend you start on a breadboard, then after getting everything to work translate the circuit to a something you can solder. This is the part where you can get creative ;).


The top view.

The bottom view.

The Code

The code is written using the Arduino platform, and built with PlatformIO. I prefer using PlatformIO with Microsoft Visual Studio Code, as it provides way better syntax highlighting/formatting/navigation as the Arduino IDE.

You can clone the repository from GitHub:

git clone https://github.com/pauls-3d-things/hue-rgb-scanner-remote.git

or download it https://github.com/pauls-3d-things/hue-rgb-scanner-remote

Make sure to

// IMPORTANT: create Config.h from Config.example.h FIRST

Architecture

The basic idea of the code is

  • connect to WiFi
  • detect when the APDS-9660 module is close to a surface
    • then measure the color a first time with the LEDs turned on
    • and then measure a second time, with the LEDs turned off
    • send data to Hue lights

Sending Data

The core of sending the values to the selected lights is done like this:

String body = "{\"on\": true, \"hue\": " + String(hue) + "}"; // this is the interesting part
String path = "PUT /api/" + apiKey + "/lights/" + String(light) + "/state"; // also good to know which ligth you need
client.print(path);
client.println(" HTTP/1.1");

// this is required otherwise we get a "400 Bad Request"
client.print("Host: ");
client.println(host); // e.g. "192.168.1.123"

client.println("Accept: application/json");
client.println("Content-Type: application/json");

client.print("Content-Length: ");
client.println(body.length());

client.println();
client.print(body);

You need to poke around the HueApi, to get an API key from your bridge and to establish the light IDs. You can find a ton of info about the API here: http://www.burgestrand.se/hue-api/

Main Loop

The main loop is rather simple. Only if we have a sufficient proximity, we actually execute it.

if (apds.readProximity() < 20) {
delay(50);
return;
}

Then, we read the color data, and do some normalization aroudn the highest value read, and mapping it to a [0-255] range. I found that using the values directly from the module did not resemble the perceived values form the lamps. You might need to experiment here, or fall back to original values, to get good results. This is more a hackjob than me knowing what I’m doing.

uint16_t r, g, b, c;
uint16_t r2, g2, b2, c2;
// read bright samples
getNormalizedColorData(&r, &g, &b, &c, 200, 16);
delay(5);
// read dark samples
getNormalizedColorData(&r2, &g2, &b2, &c2, 10, 16);

float maxval = 0;

if (r > maxval)
maxval = r;
if (g > maxval)
maxval = g;
if (b > maxval)
maxval = b;
if (c > maxval)
maxval = c;
r = (uint16_t)(((r + r2) / maxval) * 255.0);
g = (uint16_t)(((g + g2) / maxval) * 255.0);
b = (uint16_t)(((b + b2) / maxval) * 255.0);
c = (uint16_t)(((c + c2) / maxval) * 255.0);

Once we have the RGB values, we send them to all lights speicfied in the hueRGBLights array.

for (uint8_t i = 0; i < NUM_LIGHTS; i++) {
sendRGB(r > 255 ? 255 : r, g > 255 ? 255 : g, b > 255 ? 255 : b,
        hueRGBLights[i]);
delay(500);
}

The Case

I designed a case, that you can use if you build this project using the 5x7cm proto board. You can grab the .stl files from here: https://www.thingiverse.com/thing:2780005


Just place the board inside, this is the simplest step ;).