How to Create a Color Sensor for Digital Art
2018-04-09 | By Maker.io Staff
License: GNU Lesser General Public License Arduino
How to Create a Color Sensor for Digital Art
In this Maker.io project, we will use what we have learned about the Arduino (How to Get Started With Arduino), Eclipse, and external circuits (How to Add a Simple Circuit to Your Arduino) to create a system that converts a detected color to a hashtag code for use with website design and graphics.
Things you will need
- Arduino Uno
- TCS3200 Color Sensing Module
- Jumper Wires
- USB B lead (for connecting the Arduino to the PC)
- Materials for handheld sensor tool
Scheme-It Schematic
You can see the full schematic here.
Overview
The aim of this project is to use an Arduino with a TCS3200 color sensor to convert detected colors to hashtag color codes that are commonly used in computer graphics. This project is composed of three main steps:
- The hardware (connecting the sensor to the Arduino)
- The Arduino code (this reads the color sensor)
- The PC program (this connects to the Arduino and outputs hashtag codes)
Connecting the Arduino
One of the neat features about the Arduino Uno is that the IO pins are easily accessible, thanks to the pre-soldered pin headers that are also labeled for convenience. So, all we need to do is connect our TSC3200 to the Arduino Uno, making sure that the pins from the TSC3200 are connected to the corresponding Arduino pins:
With the TSC3200 connected, the only other connection needed to make this project work is the line between the Arduino and the PC via a USB cable. Power for this project is all provided by the USB port, so there is no need for an external power supply or even a regulator circuit.
The TCS3200 Sensor
The TCS3200 sensor consists of an array of photodiodes. Some have a red filter, some have a green filter, some have a blue filter, and some have no filter, and these can be used to detect red light, green light, blue light, and light, respectively. The sensor has four function pins, which provide frequency scaling and color selecting. S0 and S1 are responsible for the frequency scaling, while pins S2 and S3 are responsible for selecting the color. The tables below show the different functions that are extracted from the TCS3200 datasheet.
When a specific color is selected (by setting the S2 and S3 pins), the OUT pin outputs a square wave, whose frequency is proportional to the amount of light falling on that color sensor. If no frequency scaling is used, then the maximum output frequency of the square wave is approximately 500kHz. If scaling is used, then the maximum frequency can be either 100kHz or 10kHz. In our project, we will use a frequency scaling of 20%, so our maximum frequency will be 100kHz.
How It Works: the Arduino Code
Our Arduino code consists of three main sections
- Initial variable declaration
- Setup
- Main loop
The first section declares our variables, which include the color readings and the pin numbers that our TCS3200 connects to.
int S0=2, S1=3, S2=4, S3=5, OUT=6, OE=7;
int redReading, blueReading, greenReading, whiteReading;
The second section of code configures the Arduino hardware, including the pins and the serial port. All of the TCS3200 connections are configured as outputs, except the OUT pin from the TCS3200, which is an input pin (this is read by the Arduino). With the IO pins configured, we then configure the TCS3200 to use a frequency setting of 20% (this setting worked best with the Arduino), as well as enable the TCS3200 by pulling the OE pin low. The last piece of configuration code enables the hardware serial port and sets the baud rate to 9600.
void setup () {
// put your setup code here, to run once:
pinMode (S0, OUTPUT);
pinMode (S1, OUTPUT);
pinMode (S2, OUTPUT);
pinMode (S3, OUTPUT);
pinMode (OE, OUTPUT);
pinMode (OUT, INPUT);
//Configure frequency setting to 20%
digitalWrite (S0, HIGH);
digitalWrite (S1, LOW);
//Enable the module
digitalWrite (OE, LOW);
//Create a serial object
Serial.begin (9600);
}
The last section takes color readings from the TCS3200, applies some scaling and shifting, then outputs this data over the serial port. The first step in making a color reading is to select the color by setting the S2 and S3 pins. The code below shows the S2 and S3 settings for detecting the color red.
//***********************************************
//Configure TCS3200 to detect red setting
digitalWrite (S2, LOW);
digitalWrite (S3, LOW);
With the color selected, we now need to measure the color intensity. To do this, we need to measure the frequency of the square wave that is sent by the TCS3200 sensor on the OUT pin. Luckily for us, the Arduino library has a very useful function for doing just this: “pulseIn(pin, signal)”. This function, when called, will wait for the chosen pin to go to the value of signal, start timing, then return the value of the timer when the signal goes to the opposite value. For example, the instruction “pulseIn(2, LOW)” will wait for pin 2 to go low, start timing, and then return the value of the timer when pin 2 goes high. If the instruction “pulseIn(2, HIGH)” is called, then the Arduino will wait for pin 2 to go HIGH, start a timer, and then return the value of the timer when pin 2 goes low.
//Make frequency reading from OUT
redReading = pulseIn (OUT, LOW);
Once all four color readings have been taken (red, green, blue, white), some simple adjustments are applied. These adjustments are used to make the output as accurate as possible, but understand, due to the reflectivity of materials, it is very difficult to get perfect results. So, the best way to find your adjustment values is to place a piece of white paper in front of your sensor, record the raw values, and then subtract these values from future sensor readings. Since computer color codes are between 0 and 255, I subtract our adjusted color values from 255 to produce a color reading. The reason why the color values are subtracted from 255 and not the other way around is because the timer values from pulseIn will be inversely proportional to the intensity of the color!
//***********************************************
//Apply scaling and brightness
whiteReading = 255 – whiteReading;
redReading = 255 – (redReading – 99);
blueReading = 255 - (blueReading – 167);
greenReading = 255 – (greenReading – 154);
We also need to sanitize the readings and ensure that they fall within the 0 to 255 range. This is easily done using several if statements, where we check if the value falls outside the range and then make adjustments if they do. You can also use the white reading to adjust the red, green, and blue to account for overexposure, but this is a very complex technique and will not be covered here.
if (redReading > 255) if(redReading < 0)
redReading = 255; redReading = 0;
The last step in our code sends the red, green, and blue values to the PC using the serial port. To make it easy for our Python program to separate the value, we use an @ symbol between the numbers, which is used to split the string in the Python program. The last data to be sent down the serial port is a carriage return and linefeed, so we can use the readline function in the PySerial library.
Check out the full Arduino code here.
How It Works: the Python Code
Before we can look at our Python code, we need to install a Python library called PyGame. This will allow us to create a window, create a rectangular square, and then color the square with the detected color by the TCS3200 sensor. To do this, open a command prompt and type the following command
pip install PyGame
When our Python program loads, it starts by initiating PyGame, creates a window for PyGame, enables fonts, and sets the title text of the window.
# Initialize PyGame
pygame.init()
pygame.font.init()
pygame.display.set_caption(“MAKER IO – TCS3200 HEX”)
screen = pygame.display.set_mode((400, 300))
myfont = pygame.font.SysFont(“monospace”, 15)
done = False
The next section of code enables the serial port connection and declares variables for use in the Python program.
# Open the serial port to the Arduino
serialPort = serial.Serial (port = “COM7”, baudrate=9600, bytesize=8, timeout=1, stopbits=serial.STOPBITS_ONE)
#Buffer that holds our readline string
serialBuffer = “”
#Color values from the serial port
redValue = 0
greenValue = 0
blueValue = 0
redHex = “”
greenHex = “”
blueHex = “”
#Array used to hold split values
splitArray = [4]
With all the declarations and initializations done, the program then executes the main program loop. This loop does several things:
- It clears the window
- It takes readings from the Arduino
- It checks to see if the user has pressed the ESC key
- It draws a rectangle, whose color is the recorded color values
- It displays the hex color code
Filling the screen black is easily done using the screen.fill function.
# Clear the screen
screen.fill((0,0,0))
Getting the data from the Arduino is also trivial. thanks to the use of the readline function. This will read a string from the serial port that is terminated with the carriage return linefeed characters (0x0D 0x0A). This function, however, returns a byte array. so we use decode(“ascii”) to turn the bytes into an ASCII string.
# Get data from the Arduino
serialBuffer = serialPort.readline().decode(“ascii”)
Our data from the Arduino is a string that has variables separated by the @ character. This is done so we can easily parse the data into individual numbers, whose length could vary. Splitting a string is done using the split function, and it returns an array of elements.
# Split the string into its values
splitArray = serialBuffer.split(‘@’)
# Get the values
redValue = int(splitArray[0])
greenValue = int(splitArray[1])
blueValue = int(splitArray[2])
Checking to see if the escape key has been pressed is important so we can properly close the PyGame window. This is done by going through all events and looking for those that involve key presses, then seeing if the keypress is the escape key.
# PyGame events for determining if we should quit
for event in pygame.event.get():
if event.type == pygame.KEYDOWN and event.key == pygame.K_ESCAPE:
done=True
With our decimal values for each color obtained, we can now draw a square whose color is equal to that recorded by the Arduino. This is done using the draw.rect function, as shown below.
#Draw the rectangle and paint it the values that our TCS3200 sensor detects
pygame.draw.rect(screen, (redValue, greenValue, blueValue), pygame.Rect(30, 30, 60, 60))
The last piece of code prints the hexadecimal value of the color so we can use it for computer graphics and images. The decimal values are converted to a hexadecimal string with zero padding, which are then combined together into a label that is then displayed in the PyGame window.
# Draw the rectangle and paint it the values that our TCS3200 sensor detects
pygame.draw.rect(screen, (redValue, greenValue, blueValue), pygame.Rect(30, 30, 60, 60))
redHex = ‘%0*X’ % (2,redValue)
greenHex = ‘%0*X’ % (2,greenValue)
blueHex = ‘%0*X’ % (2,blueValue)
hexLabel = myfont.render (“#” + redHex + greenHex + blueHex, 1, (255,255,0))
screen.blit(hexLabel, (100,30))
Check out the full Python code here.
Construction and Testing
This project does not require breadboards or other external circuitry aside from the TCS3200. The TCS3200 has solid core wires connected to each of its pins, which are then connected to the Arduino Uno. The sensor itself is placed in a small cardboard housing that helps to prevent stray light entering the detection zone. It also provides a constant distance between the sensor and color under detection.
The image below shows the output when placing a piece of white paper in the housing.
While the hashtag codes are not entirely accurate, they are rather close to the true values, which makes this project good for estimation. The codes may not be entirely accurate because brightness and exposure are hard to compensate for. In addition, each color sensor type (R, G, and B) has different sensitivities, which means each sensor may need scaling.
Have questions or comments? Continue the conversation on TechForum, DigiKey's online community and technical resource.
Visit TechForum