ESP32 UNO - TCS3200D/TCS230 Color Sensor

Want your ESP32 Uno to tell red from green from blue? Connect a TCS3200D/TCS230 color sensor to the ESP32 Uno form board, and the board turns light into RGB numbers you can print, compare or use to sort objects. A short calibration makes the readings fit your own room.

By the end, you can:

  1. Connect a TCS3200D/TCS230 color module to the ESP32 Uno
  2. Use the S0-S3 pins to pick the color filter and the output scaling
  3. Run a calibration sketch to find the pulse width range of your setup
  4. Read RGB values with pulseIn() in Arduino IDE
ESP32 Uno color sensor

Components You Need

1×ESP32 Uno-form Board
1×USB Cable Type-A to Type-C (for USB-A PC)
1×USB Cable Type-C to Type-C (for USB-C PC)
1×TCS3200D/TCS230 Color Recognition Sensor Module
1×Jumper Wires

Or you can buy the following kits:

1×DIYables Sensor Kit (18 sensors/displays)
Disclosure: Some of the links provided in this section are Amazon affiliate links. We may receive a commission for any purchases made through these links at no additional cost to you.
Additionally, some of these links are for products from our own brand, DIYables .

Overview of the TCS3200D/TCS230 Color Sensor

By the end of this part, you know how the sensor turns light into a signal.

Item Value
Light sensor 8x8 array of 64 photodiodes
Red filter 16 photodiodes
Green filter 16 photodiodes
Blue filter 16 photodiodes
No filter (clear) 16 photodiodes
Output Square wave, about 2 Hz to 500 kHz
Supply voltage 2.7V to 5.5V

The chip changes the light it sees into a square wave. Switch the filters one by one and time each wave. The timings show how much red, green and blue the object sends back. Most modules also have white LEDs around the sensor, so the target is lit the same way each time, even in a dark room.

Good to know: The ESP32 Uno uses 3.3V logic. Power the module from 3.3V, so the OUT signal is also 3.3V.

Pinout

By the end of this part, you know what each pin does.

Pin Job
VCC Power. Connect it to 3.3V on the ESP32 Uno
GND Ground (0V)
S0, S1 Choose the output frequency scaling
S2, S3 Choose the color filter
OUT Square-wave output with the color signal
OE Output enable, active LOW. Most modules tie it to GND already. If yours does not, connect it to GND
TCS3200 TCS230 color sensor module pinout diagram showing VCC GND S0 S1 S2 S3 OUT pins

How It Works

By the end of this part, you know which pin levels pick which setting.

Two pairs of control pins do all the work. The first pair sets the output speed. The second pair picks the color.

Output scaling (S0, S1)

S0 S1 Scaling
LOW LOW Power down
LOW HIGH 2%
HIGH LOW 20%
HIGH HIGH 100%

Color filter (S2, S3)

S2 S3 Filter
LOW LOW Red
LOW HIGH Blue
HIGH LOW Clear (no filter, all light)
HIGH HIGH Green

Reading the output

A brighter color gives a faster wave, so each pulse is shorter. The sketches use pulseIn() to time one pulse for each color. After calibration, the code changes the pulse width into a value between 0 and 255.

Tips for Stable Readings

  1. Keep the sensor about 2 cm from the object. Keep the same angle every time.
  2. Leave the white LEDs on, so the light does not change.
  3. Cover the sensor with a small dark tube or a piece of card to block room light.

ESP32 Uno Pinout

Each Uno header pin on the ESP32 Uno form board maps to one GPIO number. Find D3 to D7 in the diagram below before you plug in the color sensor.

ESP32 Uno pinout diagram

How to Connect

By the end of this part, the sensor is wired and ready. You need five signal wires: four outputs (S0-S3) and one input (OUT).

The wiring diagram between ESP32 Uno and TCS3200 color sensor  showing connection between pins

This image is created using Fritzing. Click to enlarge image

Component Pin ESP32 Uno Pin
VCC 3.3V
GND GND
S0 D4 (GPIO17)
S1 D3 (GPIO25)
S2 D6 (GPIO27)
S3 D5 (GPIO16)
OUT D7 (GPIO14)

WARNING

The pins of the ESP32 Uno cannot take 5V. Power the sensor from 3.3V, so OUT sends a 3.3V signal. Check the VCC wire and put it on 3.3V, not 5V. If you must run the module at 5V, add a level shifter or a voltage divider (for example 10k and 20k) on the OUT line to D7 (GPIO14).

Good to know: None of these five pins is a boot strapping pin or an input-only pin, so you can wire them as shown without special care.

Step A: Calibrate the Sensor (Pulse Width)

By the end of this part, you have the min and max pulse width for red, green and blue.

The raw numbers change with LED brightness, distance, surface and room light. This sketch finds the lowest and highest pulse width for each color, so the next sketch can scale them to 0-255.

/* * This ESP32 Uno code was developed by newbiely.com * * This ESP32 Uno code is made available for public use without any restriction * * For comprehensive instructions and wiring diagrams, please visit: * https://newbiely.com/tutorials/esp32-uno/esp32-uno-tcs3200d-tcs230-color-sensor */ // Sensor pins on the ESP32 Uno (Uno label and GPIO in each comment) #define S0 17 // D4 (GPIO17) sets the output scaling, bit 0 #define S1 25 // D3 (GPIO25) sets the output scaling, bit 1 #define S2 27 // D6 (GPIO27) picks the color filter, bit 0 #define S3 16 // D5 (GPIO16) picks the color filter, bit 1 #define sensorOut 14 // D7 (GPIO14) receives the pulse signal from OUT // Raw pulse widths from the sensor int redPW = 0; int greenPW = 0; int bluePW = 0; // Lowest and highest pulse width seen so far for each color int redMin = 10000, redMax = 0; int greenMin = 10000, greenMax = 0; int blueMin = 10000, blueMax = 0; void setup() { // S0 to S3 are control lines, so they are outputs pinMode(S0, OUTPUT); pinMode(S1, OUTPUT); pinMode(S2, OUTPUT); pinMode(S3, OUTPUT); // S0 HIGH and S1 LOW select 20% output scaling digitalWrite(S0, HIGH); digitalWrite(S1, LOW); // OUT is the only line that sends data back to the board pinMode(sensorOut, INPUT); // Start the Serial Monitor Serial.begin(9600); Serial.println("=== TCS3200 Calibration ==="); Serial.println("Point the sensor at different objects (white, black, colors)."); Serial.println("Min and Max values are tracked automatically."); Serial.println("When values look stable, note them down for the next code."); Serial.println("------------------------------------------"); } void loop() { // Measure red redPW = getRedPW(); // Short pause so the sensor can settle delay(200); // Measure green greenPW = getGreenPW(); // Short pause so the sensor can settle delay(200); // Measure blue bluePW = getBluePW(); // Short pause so the sensor can settle delay(200); // Keep the extreme values for every color if (redPW < redMin) redMin = redPW; if (redPW > redMax) redMax = redPW; if (greenPW < greenMin) greenMin = greenPW; if (greenPW > greenMax) greenMax = greenPW; if (bluePW < blueMin) blueMin = bluePW; if (bluePW > blueMax) blueMax = bluePW; // Show the latest readings Serial.print("Red PW = "); Serial.print(redPW); Serial.print(" - Green PW = "); Serial.print(greenPW); Serial.print(" - Blue PW = "); Serial.println(bluePW); // Show the lowest values so far Serial.print(" Min -> R:"); Serial.print(redMin); Serial.print(" G:"); Serial.print(greenMin); Serial.print(" B:"); Serial.println(blueMin); // Show the highest values so far Serial.print(" Max -> R:"); Serial.print(redMax); Serial.print(" G:"); Serial.print(greenMax); Serial.print(" B:"); Serial.println(blueMax); Serial.println("------------------------------------------"); } // Returns the pulse width measured with the red filter int getRedPW() { // S2 LOW + S3 LOW turns on the red filter digitalWrite(S2, LOW); digitalWrite(S3, LOW); // Holds the measured width int PW; // Time the LOW part of the pulse PW = pulseIn(sensorOut, LOW); // Send the result back return PW; } // Returns the pulse width measured with the green filter int getGreenPW() { // S2 HIGH + S3 HIGH turns on the green filter digitalWrite(S2, HIGH); digitalWrite(S3, HIGH); // Holds the measured width int PW; // Time the LOW part of the pulse PW = pulseIn(sensorOut, LOW); // Send the result back return PW; } // Returns the pulse width measured with the blue filter int getBluePW() { // S2 LOW + S3 HIGH turns on the blue filter digitalWrite(S2, LOW); digitalWrite(S3, HIGH); // Holds the measured width int PW; // Time the LOW part of the pulse PW = pulseIn(sensorOut, LOW); // Send the result back return PW; }
Code Part What It Does
#define S0 ... sensorOut Names the five GPIO pins used by the sensor
digitalWrite(S0, HIGH); digitalWrite(S1, LOW); Sets 20% output scaling
getRedPW(), getGreenPW(), getBluePW() Switch the filter and time one pulse with pulseIn()
if (redPW < redMin) ... Remembers the lowest and highest value of each color
Serial.print(...) Prints the current reading and the Min and Max rows

The sketch prints a new block of readings about every 0.6 seconds, and the Min and Max rows grow as you scan new objects.

Upload and Test

  1. First time with this board? Set it up with ESP32 Uno - Getting Started before you continue.
  2. Build the circuit by following the wiring table above.
  3. Plug in the USB Type-C cable between the ESP32 Uno and your computer.
  4. Select the board in Arduino IDE: pick ESP32 Dev Module and the COM port of the board (CH340).
  5. Paste the calibration code above into a new sketch.
  6. Press Upload and wait for Done uploading.
  7. Open the Serial Monitor at 9600 baud. Live readings appear with Min and Max rows under them.
  8. Scan a few objects: white paper, something black, and some colored items.
  9. Wait 10-20 seconds until the Min and Max rows stop changing. Write the numbers down.
  10. Pro tip: run the calibration at the same distance and in the same light as your final project. If either one changes, calibrate again.
∞
Newbiely | Arduino IDE 2.3.8
──
☐
✕
File
Edit
Sketch
Tools
Help
ESP32 Dev Module
Newbiely.ino
···
8 Serial.println("Hello World!");
Output
Serial Monitor
Message (Enter to send message to 'ESP32 Dev Module' on 'COM15')
New Line
9600 baud
=== TCS3200 Calibration === Point the sensor at different objects (white, black, colors). Min and Max values are tracked automatically. When values look stable, note them down for the next code. ------------------------------------------ Red PW = 38 - Green PW = 47 - Blue PW = 52 Min -> R:38 G:47 B:52 Max -> R:38 G:47 B:52 ------------------------------------------ Red PW = 236 - Green PW = 214 - Blue PW = 198 Min -> R:38 G:47 B:52 Max -> R:236 G:214 B:198 ------------------------------------------ Red PW = 40 - Green PW = 49 - Blue PW = 55 Min -> R:38 G:47 B:52 Max -> R:236 G:214 B:198 ------------------------------------------
Ln 11, Col 1
ESP32 Dev Module on COM15
2

With this output, your values are redMin = 38, redMax = 236, greenMin = 47, greenMax = 214, blueMin = 52 and blueMax = 198.

Step B: Read RGB Values

By the end of this part, the Serial Monitor shows red, green and blue as numbers from 0 to 255.

The sketch measures each color, then map() scales your min-max range to 255-0. A short pulse means a lot of light, so it becomes a high value.

/* * This ESP32 Uno code was developed by newbiely.com * * This ESP32 Uno code is made available for public use without any restriction * * For comprehensive instructions and wiring diagrams, please visit: * https://newbiely.com/tutorials/esp32-uno/esp32-uno-tcs3200d-tcs230-color-sensor */ // Sensor pins on the ESP32 Uno (Uno label and GPIO in each comment) #define S0 17 // D4 (GPIO17) sets the output scaling, bit 0 #define S1 25 // D3 (GPIO25) sets the output scaling, bit 1 #define S2 27 // D6 (GPIO27) picks the color filter, bit 0 #define S3 16 // D5 (GPIO16) picks the color filter, bit 1 #define sensorOut 14 // D7 (GPIO14) receives the pulse signal from OUT // Calibration numbers // *Copy them from the Calibration sketch int redMin = 0; // shortest red pulse width int redMax = 0; // longest red pulse width int greenMin = 0; // shortest green pulse width int greenMax = 0; // longest green pulse width int blueMin = 0; // shortest blue pulse width int blueMax = 0; // longest blue pulse width // Raw pulse widths from the sensor int redPW = 0; int greenPW = 0; int bluePW = 0; // Final color values after mapping int redValue = 0; int greenValue = 0; int blueValue = 0; void setup() { // S0 to S3 are control lines, so they are outputs pinMode(S0, OUTPUT); pinMode(S1, OUTPUT); pinMode(S2, OUTPUT); pinMode(S3, OUTPUT); // S0 HIGH and S1 LOW select 20% output scaling digitalWrite(S0, HIGH); digitalWrite(S1, LOW); // OUT is the only line that sends data back to the board pinMode(sensorOut, INPUT); // Start the Serial Monitor Serial.begin(9600); } void loop() { // Measure red redPW = getRedPW(); // Convert to 0-255 (short pulse = bright) redValue = map(redPW, redMin, redMax, 255, 0); // Short pause so the sensor can settle delay(200); // Measure green greenPW = getGreenPW(); // Convert to 0-255 (short pulse = bright) greenValue = map(greenPW, greenMin, greenMax, 255, 0); // Short pause so the sensor can settle delay(200); // Measure blue bluePW = getBluePW(); // Convert to 0-255 (short pulse = bright) blueValue = map(bluePW, blueMin, blueMax, 255, 0); // Short pause so the sensor can settle delay(200); // Show the three values in the Serial Monitor Serial.print("Red = "); Serial.print(redValue); Serial.print(" - Green = "); Serial.print(greenValue); Serial.print(" - Blue = "); Serial.println(blueValue); } // Returns the pulse width measured with the red filter int getRedPW() { // S2 LOW + S3 LOW turns on the red filter digitalWrite(S2, LOW); digitalWrite(S3, LOW); // Holds the measured width int PW; // Time the LOW part of the pulse PW = pulseIn(sensorOut, LOW); // Send the result back return PW; } // Returns the pulse width measured with the green filter int getGreenPW() { // S2 HIGH + S3 HIGH turns on the green filter digitalWrite(S2, HIGH); digitalWrite(S3, HIGH); // Holds the measured width int PW; // Time the LOW part of the pulse PW = pulseIn(sensorOut, LOW); // Send the result back return PW; } // Returns the pulse width measured with the blue filter int getBluePW() { // S2 LOW + S3 HIGH turns on the blue filter digitalWrite(S2, LOW); digitalWrite(S3, HIGH); // Holds the measured width int PW; // Time the LOW part of the pulse PW = pulseIn(sensorOut, LOW); // Send the result back return PW; }
Code Part What It Does
int redMin = 0; ... Holds your six calibration numbers
map(redPW, redMin, redMax, 255, 0) Converts a pulse width into a 0-255 value
delay(200) Gives the sensor time to settle after each filter change
Serial.print("Red = ") ... Prints the three values on one line

Step 1 — Add your calibration numbers

Look at the top of the code for these lines:

int redMin = 0; // Red minimum pulse width int redMax = 0; // Red maximum pulse width int greenMin = 0; // Green minimum pulse width int greenMax = 0; // Green maximum pulse width int blueMin = 0; // Blue minimum pulse width int blueMax = 0; // Blue maximum pulse width

Replace all six zeros with your own numbers. With the example above, the lines become:

int redMin = 38; int redMax = 236; int greenMin = 47; int greenMax = 214; int blueMin = 52; int blueMax = 198;

Step 2 — Upload and read

  1. Send the sketch to the ESP32 Uno with Upload.
  2. Hold a colored object 1-3 cm in front of the sensor.
  3. Watch the Serial Monitor for the three numbers.
  4. Pro tip: a value below 0 or above 255 means the object is darker or brighter than anything you scanned. Scan that object during calibration, then repeat.
∞
Newbiely | Arduino IDE 2.3.8
──
☐
✕
File
Edit
Sketch
Tools
Help
ESP32 Dev Module
Newbiely.ino
···
8 Serial.println("Hello World!");
Output
Serial Monitor
Message (Enter to send message to 'ESP32 Dev Module' on 'COM15')
New Line
9600 baud
Red = 244 - Green = 28 - Blue = 31 Red = 18 - Green = 231 - Blue = 52 Red = 40 - Green = 62 - Blue = 226
Ln 11, Col 1
ESP32 Dev Module on COM15
2

Now each color is a standard 0-255 RGB number. A longer pulse means less light, so it gives a lower value.

Video Tutorial

See the ESP32 Uno build in action in the video below.

ESP32 Uno vs UNO R4 for This Project

Moving this color sensor from the UNO R4 to the ESP32 Uno needs only new pin numbers and a 3.3V supply.

Item Arduino UNO R4 ESP32 Uno
Logic voltage 5V 3.3V
Sensor VCC 5V 3.3V
S0, S1 pins 4, 3 D4 (GPIO17), D3 (GPIO25)
S2, S3 pins 6, 5 D6 (GPIO27), D5 (GPIO16)
OUT pin 7 D7 (GPIO14)
Library None None
Arduino IDE board Arduino UNO R4 WiFi ESP32 Dev Module

The wires stay on the same Uno header positions. Only VCC moves from 5V to 3.3V, and the sketches use GPIO numbers instead of pin numbers.

Applications

Once the board reads RGB values, you can build projects like these:

  • Color sorter: send red, green and blue items to separate boxes
  • Color matching game: check if two objects have the same color
  • Line follower robot: follow a colored line on the floor
  • Quality control: spot products with the wrong color
  • Color alarm: turn on a buzzer or LED when one color shows up

Go Wireless: Color Values on Your Phone

By the end of this part, you know how to see the color readings without a USB cable.

The ESP32 Uno has WiFi and Bluetooth built in. Send the three RGB values to a web page or to a phone app, and you can watch the colors change live from across the room. The charts also show how stable your readings are.

Troubleshooting

Problem Possible Cause Solution
No COM port in Arduino IDE CH340 driver missing or charge-only cable Install the CH340 driver and use a USB cable that carries data
Upload fails with Failed to connect Board is not in download mode Hold the BOOT button when the upload starts, then release it
All readings are 0 or the sketch seems stuck OUT not connected, or S0 and S1 both LOW Check the wire from OUT to D7 (GPIO14) and the S0 and S1 wires
Readings jump around Room light changes or the object moves Keep a fixed distance of 1-3 cm and shade the sensor
Values go below 0 or above 255 Object is outside the calibration range Run the calibration once more with that object, or clamp the value with constrain(value, 0, 255)
Red and blue look exchanged S2 and S3 wires are crossed S2 must go to D6 (GPIO27) and S3 to D5 (GPIO16)
Board reboots or hangs The module runs at 5V and OUT feeds 5V into a 3.3V GPIO Power it from 3.3V, or fit a level shifter on OUT
Sensor LEDs are off or dim OE or power not connected Check VCC and GND, and connect OE to GND if your module needs it
Garbled text on screen Baud rate does not match Choose 9600 in the Serial Monitor drop-down

FAQ

How does the TCS3200 detect color?

It has 64 photodiodes, and each group of 16 sits behind a red, green, blue or clear filter. You pick one filter at a time with S2 and S3. The chip then outputs a wave whose speed depends on how much of that color it sees.

Can I power the TCS3200 from 3.3V on the ESP32 Uno?

Yes. The chip accepts 2.7V to 5.5V. Use 3.3V so the OUT signal stays safe for the board. The white LEDs may look a bit dimmer, and calibration handles that.

Why do I need to calibrate the sensor?

Raw pulse widths depend on light, distance and the surface. Calibration records the shortest and longest pulse for your setup. The second sketch then maps that range to 0-255.

Why do I get RGB numbers outside 0-255?

map() does not limit its result. If an object is darker or brighter than anything you scanned, the value leaves the 0-255 range. Calibrate with more objects, or use constrain().

Does this code need a library on the ESP32 Uno?

No. It uses only pinMode(), digitalWrite(), pulseIn() and map(), which are part of the core. Just select ESP32 Dev Module in Arduino IDE.

Is the ESP32 Uno code different from the UNO R4 code?

Only the pin numbers. In the new sketches, D4, D3, D6, D5 and D7 become GPIO17, 25, 27, 16 and 14. The logic is the same, and the sensor now runs from 3.3V.

Does the ESP32 Uno ADC matter for this sensor?

No. The sensor sends a pulse signal, not an analog voltage. The board times the pulse with pulseIn() on a digital pin, so the 12-bit ADC is not used here.

Can I use an input-only pin like A2-A5 for OUT?

Yes, OUT is an input, so GPIO35, 34, 36 and 39 can work. Do not use them for S0-S3, because those are outputs and input-only pins cannot drive a signal. This tutorial keeps OUT on D7 (GPIO14).

What if my project needs more pins than the Uno headers give?

The ESP32 Uno has a few extra holes near the power header: GPIO32 and GPIO33 (I/O, PWM, analog on ADC1, so they also work while WiFi is on) and GPIO15 (I/O, PWM, analog on ADC2). Solder pin headers to use them and write the GPIO number in code. For this project, GPIO32 and GPIO33 are good choices for a second sensor's OUT pin or an extra control line. Two warnings: GPIO15 and GPIO0 (the IO0 pin at the unused Uno NC position) are boot strapping pins, and a wrong connection can stop the board from booting or uploading code, so use GPIO32/33 first. Never use the six holes under the digital header (SD0-SD3, CMD, CLK = GPIO6-GPIO11), because they are wired to the flash chip and using them crashes the board.

Will the board print names like RED or BLUE?

Not on its own, because it only gives numbers. Write your own rule, such as: red high, green and blue low, so print RED. Scan your real objects first, then pick limits from the readings.

Can I show the color values on a web page with the ESP32 Uno?

Yes. The board has WiFi and Bluetooth, so you can send the RGB values to a web monitor or a phone app. See the Go Wireless section above for links.

※ OUR MESSAGES

  • As freelancers, We are AVAILABLE for HIRE. See how to outsource your project to us
  • Please feel free to share the link of this tutorial. However, Please do not use our content on any other websites. We invested a lot of effort and time to create the content, please respect our work!