Introduction: DIY Color Sensor

About: Hello, my name is Michael. I am an electrical and computer engineer turned educator. I have a lot of experience in embedded software and hardware design. I am taking my technical experiences and leveraging the…

Arduino-based proof of concept for a simple, DIY color sensor using an RGB LED.


Microcontroller-based color detection can be difficult. Color sensors are expensive and most of the sensor kits do not come with color sensors. It is an issue I face time and time again in my classes when a student wants to do color-detection using an Arduino for sorting or some other application. Most suggestions you find online tell you to use a camera, which can be expensive and is overkill computationally, or to use an IR sensor, which cannot really distinguish colors and varies wildly.

After the dozenth time of running into this issue, I realized that we may be able to exploit a fundamental property of semiconductors to create a DIY, low-cost color sensor using components you likely already have. In this tutorial, I will give you a quick crash course in semiconductors and LEDs to learn how we can build a color sensor out of an RGB LED that is sufficient for many applications.

The code for the Arduino demo off the color sensor is here:


Basic components

  • Arduino Uno
  • USB-A to USB-B cable for programming
  • Common cathode RGB LED with clear lens
  • Active buzzer
  • Pushbutton
  • Jumper wires
  • Black heat shrink (to focus the reflected light and shield from ambient light)
  • Breadboard (optional, can just use jumper wires)

Components below are for driving a high brightness LED with the Arduino. Otherwise, you can use an external light source. Just ensure the color temperature is a close to true white as possible (around 6500 K).

  • A high-brightness LED (can salvage them from an old LED bulb)
  • NPN (or nMOSFET) like a 2N2222 or BC327 selected based on power required for LED
  • 1 kOhm Resistors for BJT base or MOSFET gate
  • A series resistor for LED based on the voltage/current requirement for the LED
  • Voltage supply for LED

Step 1: Intro to Semiconductors

A conductor is a material that is good at allowing charges to flow, while an insulator is a material that is good at impeding the flow of charge. A semiconductor is a material whose conductivity falls between that of a conductor and an insulator. But more importantly, it is a material whose conductivity and physical properties we can control with light, heat, and/or electric fields.

Semiconductors have several important properties that we exploit to use them in these contexts. They have a variable resistance, a sensitivity to light and/or heat, and they allow current to pass more easily in one direction. The most common elements used in semiconductors are silicon, germanium, and gallium nitride.

Semiconductors are one of the most important discoveries of the last century. They are the foundation of the digital era. Transistors are a semiconductor device used to electrically switch on and off the flow of electricity. They implement the binary logic at the heart of computing and many electronics. They are the most widely manufactured device in history and the reason that Silicon Valley got its name.

Step 2: P-N Junction

Silicon has 4 electrons and 4 “holes” meaning that it will easily share electrons with neighboring atoms. Holes can be thought of as the opposite of electrons, i.e. the space in the shell where an atom wants an electron. Holes flow in the opposite direction of electrons. Silicon on its own isn’t very useful; we "dope" it to make it useful. Through the process of doping, we introduce a new element to allow the semiconductor to have more holes "p-type" or to have more electrons "n-type".

A common way to do this is by mixing Boron or Phosphorous with Silicon. Boron has three valence electrons. When we dope Silicon with Boron, we are left with 1 hole, creating a positive or "p-type" semiconductor. Phosphorous has 5 valence electrons. When we dope Silicon with Phosphorous, we are left with 1 additional electron, creating a negative or "n-type" semiconductor.

Both p-type and n-type will conduct electricity, so they are not very useful individually. However, by placing a p-type and an n-type semiconductor alongside each other, we can leverage what is called a P-N junction. The P-N junction describes a boundary between these two types of semiconductors that either allow charges to flow across the boundary or prevent charges from flowing across the boundary. This is done by widening or shrinking the depletion region based on the voltage applied across the semiconductors. If the depletion region is narrow enough, charge carries can flow across this boundary.

When a P-N junction is in forward bias, it allows current to flow. Forward biasing this junction involves placing a positive voltage on the p-type semiconductor and a negative voltage on the n-type semiconductor. These like charges repel the charge carriers towards the junction, shrinking the depletion region so that current can flow across the junction.

When a P-N junction is in reverse bias, it prevents current from flowing. Reverse biasing this junction involves placing a negative voltage on the p-type semiconductor and a positive voltage on the n-type semiconductor. These unlike charges attract the charge carriers away from the junction, widening the depletion region and preventing current from flowing.

Step 3: Light-Emitting Diodes or LEDs

A diode is a P-N junction. It is a two-terminal electronic component that conducts current primarily in one direction. It has low (ideally zero) resistance in one direction, and high (ideally infinite) resistance in the other. An LED or light-emitting diode is a specific type of diode that emits light when current flows through it. When charge carriers move across the junction, energy is released in the form of photons.

The color of the light is determined by the energy required for electrons to cross the band gap of the semiconductor, based on the specific chemical properties. The colored lenses on most LEDs actually have no bearing on the color of the light. They are merely colored, so you know which ones are red LEDs and which are blue LEDs when you have a big bin of assorted LEDs.

Step 4: Generating a Voltage With LEDs

We can actually exploit the fundamental properties of semiconductors discussed earlier to use light to generate a voltage! Instead of passing a current through an LED to produce photons, we will use photons to excite the LED and produce a current. This phenomenon is very similar to the way we can turn a motor into a generator. By applying a voltage to a motor, we turn electrical energy into mechanical energy. By turning the motor with our hand, we can produce a voltage turning mechanical energy into electrical energy.

When we apply electricity to an LED, the energy is released in the form of photons. When we apply photons to a LED, we can produce electricity. There is actually a component called a photodiode whose sole function is to produce a current when it absorbs photons. LEDs will do this too, although not as well as photodiodes specifically manufactured for this purpose. However, LEDs are much lower cost, and you likely already have them around the house! Interestingly enough, each LED will only respond to the same color light it emits, i.e. a red LED will only produce a voltage when subjected to a red light. For more details and a neat demo, check out the video above.

Step 5: Theory of Operation

For our color sensor, we will use a common cathode RGB LED with a very clear lens. When our color sensor is subjected to a red light, the red anode will produce a positive voltage that will be detectable with a microcontroller pin. When subjected to a purple light, both the red anode and the blue anode will produce a positive voltage. We will use each anode of the RGB LED to detect the amount of red, green, and blue to produce a fairly close approximation of the RGB value of the object in question.

However, we will need strong light source if we want to detect the color of objects. For example, a green sheet of paper does not emit light; however, it will reflect light from the ambient light sources in the room. To make sure the intensity is strong enough that we can read subtle differences in color, we need a strong light source to reflect more light back. We also need to ensure that our light source is a white light. White light is produced by combining equal parts high-intensity red, green, and blue light.

You can use an external light source (like a phone or flashlight) or use a high brightness LED that you can drive from the Arduino. You must ensure that the light source is true white with a color temperature of ~6500 K. Another good solution for a light source is to harvest individual LEDs from a dead LED light bulb. Oftentimes, a single LED or a portion of the power supply burns out in an LED bulb, leaving many of the other components working and fully intact. You can either desolder an individual LED or cut out a piece of the printed circuit board to use in this new context.

Step 6: Circuit Setup

Construct the circuit as shown in the diagram above. As discussed previously, many portions of the circuit are optional. The pushbutton is used to start the sampling process. If you would like to consistently take samples or trigger the samplin g process in another way, you can forego the pushbutton. The active buzzer is used to indicate to the user when the samples are about to be taken, so they can ready the object for color sensing. Again, another form of indicator could be used in place, or you could just not incorporate an indicator.

Finally, the light source driving circuit. If you would like to drive a high-brightness light source from the Arduino, you will likely need to use an external supply and only use the Arduino digital output to trigger the light source. This can be done with a transistor, relay, or any number of different other ways. Your specific implementation will depend on the power characteristics of your light source. Make sure you do not exceed the current or power thresholds of your switching device and that you use the necessary resistors based on your voltage levels. The diagram shows a basic switching circuit using an NPN transistor.

Note: Use a tight-fitting piece of black heat shrink around the RGB LED to shield from the ambient light and focus the reflected light from the object to get a more accurate reading.

Step 7: Software Setup

The code for the Arduino demo off the color sensor is here:

Below shows the setup() function definition of the Arduino demo code. In this function, we configure the serial port and the digital pins.

// put your setup code here, to run once:
void setup() {
  // setup serial port
  // wait for serial port to be ready
  // configure pins
  pinMode(HB_LED, OUTPUT);
  pinMode(BUZZER, OUTPUT);

Below shows the loop() function definition of the Arduino demo code. The loop() function acts as the main while(1) loop for the program that runs repeatedly after setup(). In this function, we repeatedly check to see if the button is pressed to know if it is time to take a sample. Once we detect a valid button press, there is a countdown performed before collecting samples from the color sensor and sending the results to the PC via the serial port.

// put your main code here, to run repeatedly:
void loop() {
  // wait for debounced button press
  if (debounce_button_press(BUTTON)) {
    // turn on light source for reading
    digitalWrite(HB_LED, HIGH);
    // perform countdown
    // take readings
    rgbColor reading = rgb_sampling();
    // turn off light source
    digitalWrite(HB_LED, LOW);
    // print results

debounce_button_press() is a function that reads the switch to check to see if it is pressed. This function is written to specifically work with a pushbutton switch that is connected to a digital input using an internal pull-up resistor to prevent the pin from floating. This function also handles debouncing, aka ensuring it's a valid switch and ensuring it is only triggered once per button press.

countdown() is responsible for performing the countdown to indicate to the user when the Arduino will take the color sensor readings. It counts down, "3, 2, 1, Go!" via the serial port and by using an active buzzer.

rgb_sampling() takes the samples of the red, green, and blue intensities and returns them to the main function. It takes 10 readings of each component in the span of a second and averages them together. It turns on the buzzer during the sampling time to indicate it is actively reading the color sensor. This function also turns on/off the high-brightness LED circuit during sampling if that circuit is connected.

print_results() displays the RGB reading results to the PC via the serial port.

Step 8: More Projects

Colors of the Rainbow Contest

Participated in the
Colors of the Rainbow Contest