Sensing Color With The Arduino and the TCS34725

tcs34725_1

Sensing color with the Arduino and the TCS34725 is a pretty straight forward exercise.  The TCS34725 is an IC manufactured by AMS (previously Taos).  It packages color sensing analog-to-digital converters for clear, red, green, and blue color sensing into a tiny 6 pin package.  The TCS34725 implements an I2C interface to configure settings and reading the color values.

In the photo above you can see a prototype of our BM017 color sensor module.  We’ve included a white LED on the module that allows you to illuminate the object whose color you are sensing.

tcs34725_2

Here’s an image of the senor measuring a blue sheet of paper.

Our first application with this sensor simply configures the TCS34725 and reads each color value one time per second.  Following that there’s a simple comparison of the red, green, and blue values to see which value is larger.  The largest value is assumed to be the color present.  Here are some points of interest from the work I did that might help you during implementation.

1.  Red is the color most easily measured.  Green and blue are closer in value and can be indistinguishable if the color sample is too far from the sensor.
2.  Since the sensor may be close to the object it is sampling we added a white LED to our module to allow illumination of the sample.
3.  The TCS34725 operates at 2.7V-3.6V.  The Arduino at 5V.  So our module also has a voltage level converter on the I2C pins allowing both devices to see the voltage they operate at.
4.  The Arduino code uses the wire library with the SDA and SCL pins associated with A4 and A5 respectively.
5.  The ATime setting in the TCS34725 determines the resolution of the color measurement.  I have mine set for 16-bit values, and I think the integration (measurement) time is about 600mS.

This module will be available for resale in about 6 weeks.  I’ll place the schematic on our web site at some point in the next few weeks.  In the mean time here’s a screen capture.
bm017_schematic

The Arduino code will set the TCS34725 up for operation and check that it is connected to the I2C lines on power up.  Then it will read the color registers and try to determine which color is dominant.  If you enable the Serial Monitor it will display the color it sees as red, green, or blue.  If you want to see the raw measurements you can comment in that portion of the code and it will send the data to the Serial Monitor.

 

BM017 arduino screen capture

Here is the code I used for simple color comparisons.

/*
BM017_Arduino_color_sensing:  This program interfaces to the AMS TCS34725 color light
to digital converter IC.  It uses the Arduino I2C interface.  

Schematics associated with the BM017 module may be used for hardware wiring information.
See the user datasheet at www.solutions-cubed.com for additional information.

*/

#include <Wire.h>
#include <Math.h>

byte i2cWriteBuffer[10];
byte i2cReadBuffer[10];

#define SensorAddressWrite 0x29 //
#define SensorAddressRead 0x29 // 
#define EnableAddress 0xa0 // register address + command bits
#define ATimeAddress 0xa1 // register address + command bits
#define WTimeAddress 0xa3 // register address + command bits
#define ConfigAddress 0xad // register address + command bits
#define ControlAddress 0xaf // register address + command bits
#define IDAddress 0xb2 // register address + command bits
#define ColorAddress 0xb4 // register address + command bits

/*  
Send register address and the byte value you want to write the magnetometer and 
loads the destination register with the value you send
*/
void Writei2cRegisters(byte numberbytes, byte command)
{
    byte i = 0;

    Wire.beginTransmission(SensorAddressWrite);   // Send address with Write bit set
    Wire.write(command);                          // Send command, normally the register address 
    for (i=0;i<numberbytes;i++)                       // Send data 
      Wire.write(i2cWriteBuffer[i]);
    Wire.endTransmission();

    delayMicroseconds(100);      // allow some time for bus to settle      
}

/*  
Send register address to this function and it returns byte value
for the magnetometer register's contents 
*/
byte Readi2cRegisters(int numberbytes, byte command)
{
   byte i = 0;

    Wire.beginTransmission(SensorAddressWrite);   // Write address of read to sensor
    Wire.write(command);
    Wire.endTransmission();

    delayMicroseconds(100);      // allow some time for bus to settle      

    Wire.requestFrom(SensorAddressRead,numberbytes);   // read data
    for(i=0;i<numberbytes;i++)
      i2cReadBuffer[i] = Wire.read();
    Wire.endTransmission();   

    delayMicroseconds(100);      // allow some time for bus to settle      
}  

void init_TCS34725(void)
{
  i2cWriteBuffer[0] = 0x10;
  Writei2cRegisters(1,ATimeAddress);    // RGBC timing is 256 - contents x 2.4mS =  
  i2cWriteBuffer[0] = 0x00;
  Writei2cRegisters(1,ConfigAddress);   // Can be used to change the wait time
  i2cWriteBuffer[0] = 0x00;
  Writei2cRegisters(1,ControlAddress);  // RGBC gain control
  i2cWriteBuffer[0] = 0x03;
  Writei2cRegisters(1,EnableAddress);    // enable ADs and oscillator for sensor  
}

void get_TCS34725ID(void)
{
  Readi2cRegisters(1,IDAddress);
  if (i2cReadBuffer[0] = 0x44)
    Serial.println("TCS34725 is present");    
  else
    Serial.println("TCS34725 not responding");    
}

/*
Reads the register values for clear, red, green, and blue.
*/
void get_Colors(void)
{
  unsigned int clear_color = 0;
  unsigned int red_color = 0;
  unsigned int green_color = 0;
  unsigned int blue_color = 0;

  Readi2cRegisters(8,ColorAddress);
  clear_color = (unsigned int)(i2cReadBuffer[1]<<8) + (unsigned int)i2cReadBuffer[0];
  red_color = (unsigned int)(i2cReadBuffer[3]<<8) + (unsigned int)i2cReadBuffer[2];
  green_color = (unsigned int)(i2cReadBuffer[5]<<8) + (unsigned int)i2cReadBuffer[4];
  blue_color = (unsigned int)(i2cReadBuffer[7]<<8) + (unsigned int)i2cReadBuffer[6];

  // send register values to the serial monitor 
/*
  Serial.print("clear color=");
  Serial.print(clear_color, DEC);    
  Serial.print(" red color=");
  Serial.print(red_color, DEC);    
  Serial.print(" green color=");
  Serial.print(green_color, DEC);    
  Serial.print(" blue color=");
  Serial.println(blue_color, DEC);
*/

 // Basic RGB color differentiation can be accomplished by comparing the values and the largest reading will be 
 // the prominent color

  if((red_color>blue_color) && (red_color>green_color))
    Serial.println("detecting red");
  else if((green_color>blue_color) && (green_color>red_color))
    Serial.println("detecting green");
  else if((blue_color>red_color) && (blue_color>green_color))
    Serial.println("detecting blue");
  else
    Serial.println("color not detectable");

}  

void setup() {
  Wire.begin();
  Serial.begin(9600);  // start serial for output
  init_TCS34725();
  get_TCS34725ID();     // get the device ID, this is just a test to see if we're connected
}

void loop() {
    get_Colors();
    delay(1000);
}

Comments

  1. Hi LON GLAZNER,

    Great thanks for your code sharing about driving TCS34725, it works very well.
    But what puzzle me that the register address definition.
    How can you get the following definition:
    #define EnableAddress 0xa0 // register address + command bits
    #define ATimeAddress 0xa1 // register address + command bits
    #define WTimeAddress 0xa3 // register address + command bits
    #define ConfigAddress 0xad // register address + command bits
    #define ControlAddress 0xaf // register address + command bits
    #define IDAddress 0xb2 // register address + command bits
    #define ColorAddress 0xb4 // register address + command bits

    But according to the datasheet, i only got following:
    0x00 ENABLE R/W Enables states and interrupts 0x00
    0x01 ATIME R/W RGBC time 0xFF
    0x03 WTIME R/W Wait time 0xFF
    0x04 AILTL R/W Clear interrupt low threshold low byte 0x00
    0x05 AILTH R/W Clear interrupt low threshold high byte 0x00
    0x06 AIHTL R/W Clear interrupt high threshold low byte 0x00

    Thanks
    –Sun, Yi

    • Does it due to the ‘Command Register’? I meant I have to add B(10100000) to any command which I want to write to chip?

      • Hi Sun Yi,

        It is due to the “command code”. If you take a look at page 12, figure 10, of the TCS34725 datasheet it shows the I2C protocol. Under the write portion you can see that you need to send the slave address, command code, and then the data.

        On page 14 you can see the command register description. The higher nibble defines the command bit and the nature of the communication. I used 0xA for the high nibble so that you can write to sequential registers. I don’t recall if I actually implemented multiple byte writes in the code you’re looking at. The lower nibble is the index of the register your write begins at.

        For example, if I wanted to write 0x22aa to the AILTL and AILTH registers in a single communication I would send…
        Slave address, 0xa4 (command register + address of AILTL), 0xaa, 0x22.

        At least that’s my understanding. Let me know if you find something different.

        Lon

        • Hi Lon,
          Nice of you to explain above, and that’s clear enough. Thank you so much! :p
          BTW, if you have some code which uses interrupt to read / write data, that will great useful for me.

          And another problem is the chip response speed. I want a very fast response about 10ms(100hz) color detection. According to the spec page 16, It can be fit for my requirement only when RGBC Timing register is set to 0xff (2.4ms). But if I do that the value converted from the amplified photodiode is only 0,1,2 which too small.
          So I am wondering whether TCS3414 has better performance on the response time. Did you occasionally have any experience on TCS3414?

          • Hi Sun Yi,

            I have not written interrupt based code. I also haven’t used the TCS3414, so I can’t help you there.

            My recollection is that the TCS34725 operates as an integrator. There might be a couple of ways to get to a 100Hz sample rate.

            1. You could set the integration time to some duration longer than 10mS and read the accumulated signal data before the integration is complete. You’ll have to read the datasheet to see if this is possible. If the data in the registers only updates after the sample period completes then this probably won’t work.

            2. You could read the data at the 2.4mS sample rate and add the results that occur over 10mS (basically integrate it in the microcontroller).

            3. Attempt to get more resolution at high sample rates by lighting the object whose color you are measuring with an adjacent white LED. Also, move the sensor closer to the object and see if that helps.

            Lon

  2. Hi Lon,
    Great thanks for your guide. Your suggestion is very useful.
    I would like update my progress here, if you’re interested in. 🙂

    Thanks,
    Yi

Trackbacks

  1. […] clear color values from object in front of the sensor. I covered basic use of the sensor in my post Sensing Color with the Arduino and the TCS34725.   The code I use here builds on that blog […]

Speak Your Mind

*