Interfacing the Arduino to the Taos TCS3414 via I2C, continued

By Max

Taos TCS3414

November, so soon!  I kind of had to put this one down for awhile and work on things that were not laptop-centric, I just was spending too much time in front of a computer screen.  Luckily, Tiuri De Jong was on the case, and he has developed the proof-of-concept code I posted earlier into a fully-working demonstration.

I uploaded it to my Arduino, and I’m getting stable results with an apparently linear response, although the specific numerical values themselves aren’t particularly meaningful.    For example, under my workbench lamp that I metered at about 40fc and has a CCT of 5000K, it measured 290fc at 3500K.  I get that the part would require calibration before it would be useful, but it’s strange to me that it would be off by 1500K color temperature and a factor of 5 for illuminance, out of the box?  I’d be interested to know if anyone else has a similar experience.

What I’m working on now is measuring the values given by the TCS3414 vs. a known accurate illuminance and color temperature meter, to tell if the deviation from true value is a constant, or a linear function, or what.  I’ve verified that the calculations are done correctly in the code per the datasheet.

But in the meantime, it seems like a shame to deprive everyone else of Tiuri De Jong’s complete and, might I add, lucidly written code, so it may be downloaded here:

2012-11-18 Light Meter Code Rev5

I’ve also posted the code in full after the jump.

Some helpful hints on hardware:  It turns out that as long as you have only one 3.3V device on the i2c bus, you don’t have to have the level shifter circuit I was using, you can connect it directly to the i2c pins (but power it via 3.3V supply, not 5V).  So that’s simpler.  Also, Taos recommends a .1μF decoupling capacitor between the 3.3V supply and ground, located adjacent to the unit, to reduce fluctuations due to logic level shifting:

From page 32 of the datasheet.

The interrupt functionality is not implemented in the current code, so you can leave that pin unconnected, and omit RPI.

And, the code.  As always, I recommend downloading the zip file rather than copying-and-pasting, sometimes the WordPress monster eats semicolons.

 


// TCS3414 receiver code rev 5
// Developed by Tiuri de Jong, with contributions by Max Pierson
// This software is released under WTFPL, although credit and share-alike are appreciated.
// 18 November 2012

//include the library for i2c
#include

unsigned int TCS3414values[4]; // [Clear,Red,Green,Blue]
float TCS3414medium[4]; // [Clear,Red,Green,Blue]
float TCS3414mediate[4]; // [Clear,Red,Green,Blue]
float ColorTemperature = 0;

// SET the integration time here. Higher times allow for higher values with better precicion.
int integrationtime = 400; //12 == 12ms, 100 = 100ms, 400 = 400ms. Other values are note accepted
int loopdelay = integrationtime; //loop delay depends on the integration time

boolean debug = false; //change to true if you want to see the various debug serial output bits
boolean percentageEnabled = false; //enable/disable the percentage mode
boolean compensateEnabled = false; //enable/disable color compensation of the sensor sensitivity per color

void setup(){
Wire.begin();// join i2c bus (address optional for master)
Serial.begin(9600);//115200
while (!Serial) {
; // wait for serial port to connect. This while loop is needed for the Arduino Leonardo only
}
CMD(0);
TCS3414Start(14,2000);
}

//The main function.. This repeats itself forever!
void loop() {
getSerialCommands(); //to be able to receive commands

//gets the raw values from the sensors and writes it to TCS3414values[]
TSC3414All(TCS3414values);

//compensate based on the filter characteristics of the TCS3414
if(compensateEnabled)
colorCompensator(TCS3414values);

//keeps a running average from the last 4 values per color.
calculateMedium(TCS3414mediate,TCS3414values,4.0);

//calculates the color temperature, using the algorithm in the TCS3414 datasheet
ColorTemperature = CCTCalc(TCS3414values);

//displays percentage values, if enabled.
if(percentageEnabled){
makePercentage(TCS3414values, TCS3414medium);
}

Serial.print(“Clear: “);
Serial.print(TCS3414values[0]);
if(percentageEnabled)
Serial.print(“%”);
Serial.print(“\tRed: “);
Serial.print(TCS3414values[1]);
if(percentageEnabled)
Serial.print(“%”);
Serial.print(” \tGreen: “);
Serial.print(TCS3414values[2]);
if(percentageEnabled)
Serial.print(“%”);
Serial.print(“\tBlue: “);
if(percentageEnabled){
Serial.print(TCS3414values[3]);
Serial.println(“%”);
}else{
Serial.println(TCS3414values[3]);
}

delay(loopdelay); //delays by the integration time between measurements

}//end loop()

/*
* ======================================================
* Calculation functions
* ======================================================
*/

/*** takes the raw values from the sensors and converts them to
Correlated Color Temperature. Returns a float with CCT ***/
float CCTCalc(unsigned int allcolors[]){
float TCS3414tristimulus[3]; // [tri X, tri Y, tri Z]
float TCS3414chromaticityCoordinates[2]; //chromaticity coordinates // [x, y]

//calculate tristimulus values (chromaticity coordinates)
//The tristimulus Y value represents the illuminance of our source
TCS3414tristimulus[0] = (-0.14282 * allcolors[1]) + (1.54924 * allcolors[2]) + (-0.95641 * allcolors[3]); //X
TCS3414tristimulus[1] = (-0.32466 * allcolors[1]) + (1.57837 * allcolors[2]) + (-0.73191 * allcolors[3]); //Y // = Illuminance
TCS3414tristimulus[2] = (-0.68202 * allcolors[1]) + (0.77073 * allcolors[2]) + (0.56332 * allcolors[3]); //Z

float XYZ = TCS3414tristimulus[0] + TCS3414tristimulus[1] + TCS3414tristimulus[2];

//calculate the chromaticiy coordinates
TCS3414chromaticityCoordinates[0] = TCS3414tristimulus[0] / XYZ; //x
TCS3414chromaticityCoordinates[1] = TCS3414tristimulus[1] / XYZ; //y

float n = (TCS3414chromaticityCoordinates[0] – 0.3320) / (0.1858 – TCS3414chromaticityCoordinates[1]);

float CCT = ( (449*pow(n,3)) + (3525*pow(n,2)) + (6823.3 * n) + 5520.33 );

Serial.print(“Illuminance: “);
Serial.print(TCS3414tristimulus[1]);
Serial.print(“\tx: “);
Serial.print(TCS3414chromaticityCoordinates[0]);
Serial.print(” \ty: “);
Serial.print(TCS3414chromaticityCoordinates[1]);
Serial.print(” \tCCT: “);
Serial.print(CCT);
Serial.print(“K\t — \t”);

return CCT;
}

/*** Keeps a running average of 4 values per color. ***/
void calculateMedium(float med[], unsigned int value[], float divider){
for(int i = 0; i < 4; i++){
med[i] = ( (med[i]*(divider-1.0)) + value[i] ) / divider;
}
}

/*** calculates percentages for R,G,B channels, if enabled. ***/
void makePercentage(unsigned int allcolors[], float allmedium[]){ //makes every color a percentage, 100% is the average of the previous 4 values before this is entered.
for(int i=0; i 0){
int receive_command = Serial.read();
if(receive_command == 49){//49 == 1
Serial.println(“Percentage enabled, max value (100%) set to current medium”);
percentageEnabled = true;//enables/disables percentage mode.
for(int o = 0; o < 4; o++){
TCS3414medium[o] = TCS3414mediate[o];
}
}else if(receive_command == 48){//48 == 0
Serial.println(“Percentage disabled”);
percentageEnabled = false;//enables/disables percentage mode.
}else if(receive_command == 63){//63 == ?
CMD(2000);
}else if(receive_command == 112){//112 == p
Serial.println(“pausing for 5 seconds (stackable)…”);
delay(5000);
getSerialCommands();
}else if(receive_command == 99){//99 == c
Serial.println(“Color compensation enabled”);
compensateEnabled = true;
}else if(receive_command == 110){//110 == n
Serial.println(“Color compensation disabled”);
compensateEnabled = false;
}else{
Serial.print(“The command entered ( “);
Serial.print(receive_command);
Serial.println(” ) was NOT found in the command list”);
}
}
}

void CMD(int delayTime){
Serial.println(“=========== Command list ===========”);
Serial.println(“\’ \’”);
Serial.println(” ? == Show this command list”);
Serial.println(“”);
Serial.println(” p == pause for 5 seconds (stackable)”);
Serial.println(“”);
Serial.println(” 1 == Enable Percentage mode”);
Serial.println(” 0 == Disable Percentage mode”);
Serial.print(” Percentage mode is currently: “);
if(percentageEnabled){
Serial.println(“ON”);
}else{
Serial.println(“OFF”);
}
Serial.println(“”);
Serial.println(” c == Enable Color compensation mode”);
Serial.println(” n == Disable Color compensation mode”);
Serial.print(” Color compensation mode is currently: “);
if(compensateEnabled){
Serial.println(“ON”);
}else{
Serial.println(“OFF”);
}
Serial.println(“\’ \’”);
Serial.println(“====================================”);
delay(delayTime);
}


6 Responses to “Interfacing the Arduino to the Taos TCS3414 via I2C, continued”

  • D Hill Says:

    Thanks Max! Your links to De Jong’s code was a great help. Now I can fiddle with using the sensor instead of spending time getting it to work.

    Dave

  • RifRaf Says:

    Thanks so much for this advise, have tryed the code, was getting weird values for cct as well, the formulas do seem good but a few comments;
    Have got much better results using the formulas from http://www.cs.rit.edu/~ncs/color/t_convert.html to get a more accurate reading, in particular using,
    [ X ] [ 0.412453 0.357580 0.180423 ] [ R ]
    [ Y ] = [ 0.212671 0.715160 0.072169 ] * [ G ]
    [ Z ] [ 0.019334 0.119193 0.950227 ] [ B ]

    in place of the values from the datasheet, weird but is working.

    Also the colour compensation values appear to be for the CS tsc3414 package rather than the FN package we may be using, after testing the other values i believe that the compensation is already internally done and not required.

    The percentage option is neat and seems to work well, i may be wrong on some or all these things but they are worth a try.

  • SLS Says:

    Hi
    thanks for this sample code and tutorial!

    I have the Seeedstudio Grove I2C Color sensor which uses the TCS3414 CS. They have sample code on their wiki website. their default is Free Run, Gain 1, Prescaler 4. That is different compared to your code.

    But I can’t get the Kelvin nor the Lux values to work correctly with any sample code, with or without color compensation. I have a luxmeter and various light sources, it just doesn’t show the right values. It also doesn’t measure reflected light (object color) correctly.

    At this moment I think this sensor is completely useless, a big waste of time.

  • SLS Says:

    An update to the previous post.
    I’ve added Gain and Prescale settings (btw, Seeedstudio’s code has an error, colors are declared INT, not UNSIGNED INT as they should) to the code on this page.

    As RifRaf said, the matrix from the official pdf isn’t working, for reasons I don’t know. It works with the standard RGB-XYZ sRGB matrix (another source here http://www.brucelindbloom.com/index.html?Eqn_RGB_XYZ_Matrix.html).

    So I’m reading r,g,b from the sensor, converting to XYZ using sRGB matrix and then calculating CCT which seems right because my monitor is calibrated with a colorimeter at 6500K and the sensor shows 6541K (measured on black screen). Other light sources seem to be correct too, like sun or halogen.

    To get usable colors I convert again using the XYZ-RGB sRGB D65 matrix. The color reading is close or very close to the color displayed and measured with the sensor placed directly on the lcd.

    I wrote a Processing app that lets me set gain, prescale and integration time on-the-fly. The sensor is connected to Arduino, and I’m comparing its reading to a luxmeter. The sensor gets close or very close to the luxmeter reading only for some gain/prescale pair of values. Don’t know yet what that means :) but in the TCS3414CS pdf their calculation example they’re probably using low gain and high prescaler to get these raw values: R=231,G=260, B=95.

    Probably the Clear channel could be directly interpolated to Lux values if you plot a chart comparing it to luxmeter readings for various lights.

  • Fred Says:

    I have found a program for calibrate the TCS3414 :
    http://rienquepourlesyeux.free.fr/Photographer%20Tools/Programmes%20du%20Photographer%20Tools/Photographer%20Tools%20programs.htm

    That calculate the correlation matrix.

  • Michael Says:

    We are using these sensors as well, and have noticed a gradual. Not sure in what version as has been mentioned here. Only to say, we setup with nominal settings, 400 milliseconds integration time, free running. We observe gradual incline in values from T-0, which we understand to be a response to temperature. This is obvious if for example the sensors are close to a piece of paper while you are illuminating the paper, which consequently gets warm. One concern of ours is that the ARM CPU we are using, for instance, is providing the power source, which sometimes we see a current draw when the CPU goes into scheduling mode over Linux crond (?), plausible, we don’t exactly know. Could be another I2C bus request going on as well, also plausible. Personally I am not completely convinced it’s not a total hardware design issue, but I could be wrong about that.

Leave a Reply