Go Down

Topic: Help With TCS3200 Color Sensor (Understanding Output) (Read 6433 times) previous topic - next topic

aerouta

Hello, I am using a version of the code posted below to read colors then ultimately match them. However, I am not sure what the output (white, red, green, blue) are. I would expect that the RGB values be between 0 and 255, or [0,1] but that is not the case. I have reviewed alot of information on the Parallax and TAOS site.  I am sure I'm missing something quite elementary but I can't seem to put it all together. What is the correct way to interpet the output from this sensor? How should they be normalized? Any help would be great

Code: [Select]
/*

Eecher's TCS3200 program
adapted from code found at reibot.org

*/

int S0 = 8;//pinB
int S1 = 9;//pinA
int S2 = 12;//pinE
int S3 = 11;//pinF
int taosOutPin = 10;//pinC
int LED = 13;//pinD

void setup() {
TCS3200setup();
Serial.begin(115200);
Serial.print("\n\n\nready\n\n\n");
delay(100);
}

// primary loop takes color readings from all four channels and displays the raw values once per second.  What you might wish to do with those values is up to you...
void loop() {

detectColor(taosOutPin);
Serial.print("\n\n\n");
delay(1000);

}

int detectColor(int taosOutPin){

float white = colorRead(taosOutPin,0,1);
float red = colorRead(taosOutPin,1,1);
float blue = colorRead(taosOutPin,2,1);
float green = colorRead(taosOutPin,3,1);

Serial.print("white ");
Serial.println(white);

Serial.print("red ");
Serial.println(red);

Serial.print("blue ");
Serial.println(blue);

Serial.print("green ");
Serial.println(green);

}

/*
This section will return the pulseIn reading of the selected color. 
It will turn on the sensor at the start taosMode(1), and it will power off the sensor at the end taosMode(0)
color codes: 0=white, 1=red, 2=blue, 3=green
if LEDstate is 0, LED will be off. 1 and the LED will be on.
taosOutPin is the ouput pin of the TCS3200.
*/

float colorRead(int taosOutPin, int color, boolean LEDstate){
 
//turn on sensor and use highest frequency/sensitivity setting
taosMode(1);

//setting for a delay to let the sensor sit for a moment before taking a reading.
int sensorDelay = 100;

//set the S2 and S3 pins to select the color to be sensed
if(color == 0){//white
digitalWrite(S3, LOW); //S3
digitalWrite(S2, HIGH); //S2
// Serial.print(" w");
}

else if(color == 1){//red
digitalWrite(S3, LOW); //S3
digitalWrite(S2, LOW); //S2
// Serial.print(" r");
}

else if(color == 2){//blue
digitalWrite(S3, HIGH); //S3
digitalWrite(S2, LOW); //S2
// Serial.print(" b");
}

else if(color == 3){//green
digitalWrite(S3, HIGH); //S3
digitalWrite(S2, HIGH); //S2
// Serial.print(" g");
}

// create a var where the pulse reading from sensor will go
float readPulse;

//  turn LEDs on or off, as directed by the LEDstate var
if(LEDstate == 0){
    digitalWrite(LED, LOW);
}
if(LEDstate == 1){
    digitalWrite(LED, HIGH);
}

// wait a bit for LEDs to actually turn on, as directed by sensorDelay var
delay(sensorDelay);

// now take a measurement from the sensor, timing a low pulse on the sensor's "out" pin
readPulse = pulseIn(taosOutPin, LOW, 80000);

//if the pulseIn times out, it returns 0 and that throws off numbers. just cap it at 80k if it happens
if(readPulse < .1){
readPulse = 80000;
}

//turn off color sensor and LEDs to save power
taosMode(0);

// return the pulse value back to whatever called for it...
return readPulse;

}

// Operation modes area, controlled by hi/lo settings on S0 and S1 pins.
//setting mode to zero will put taos into low power mode. taosMode(0);

void taosMode(int mode){
   
    if(mode == 0){
    //power OFF mode-  LED off and both channels "low"
    digitalWrite(LED, LOW);
    digitalWrite(S0, LOW); //S0
    digitalWrite(S1, LOW); //S1
    //  Serial.println("mOFFm");
   
    }else if(mode == 1){
    //this will put in 1:1, highest sensitivity
    digitalWrite(S0, HIGH); //S0
    digitalWrite(S1, HIGH); //S1
    // Serial.println("m1:1m");
   
    }else if(mode == 2){
    //this will put in 1:5
    digitalWrite(S0, HIGH); //S0
    digitalWrite(S1, LOW); //S1
    //Serial.println("m1:5m");
   
    }else if(mode == 3){
    //this will put in 1:50
    digitalWrite(S0, LOW); //S0
    digitalWrite(S1, HIGH); //S1
    //Serial.println("m1:50m");
    }
   
    return;

}

void TCS3200setup(){

    //initialize pins
    pinMode(LED,OUTPUT); //LED pinD
   
    //color mode selection
    pinMode(S2,OUTPUT); //S2 pinE
    pinMode(S3,OUTPUT); //s3 pinF
   
    //color response pin (only actual input from taos)
    pinMode(taosOutPin, INPUT); //taosOutPin pinC
   
    //communication freq (sensitivity) selection
    pinMode(S0,OUTPUT); //S0 pinB
    pinMode(S1,OUTPUT); //S1 pinA
   
    return;

}



PeterH

What you get back from pulseIn() is an unsigned long indicating the length of the pulse in microseconds. You'd need to read the spec of the device which was providing the pulse to understand what that means. Your code returns it as a float, which is pointless (it is not a float, contains no fractional part, and by converting to a float you reduce the resolution of the value). Anyway, it's unlikely to be a value in the range 0 .. 255 as you seem to have expected.
I only provide help via the forum - please do not contact me for private consultancy.

aerouta

thanks for the reply, I will change the variable type. Here is the documentation that I cannot understand.

varela5280

#3
Apr 27, 2013, 05:47 am Last Edit: May 04, 2013, 02:56 am by varela5280 Reason: 1
I've been trying to get a TCS3200 sensor working with an arduino due too.  I think your code might be missing a timer - I found this sensor
http://www.robotshop.com/dfrobot-color-sensor-tcs3200.html
The way I understand it is that the output of the light sensor is a digital frequency that varies with the intensity of the light so you need the timer to measure the frequency.
This post might help also:
http://arduino.cc/forum/index.php?topic=130423.0
If I make any progress I will post it here just be sure to post your code once you have it working.

varela5280

Although timers/interrupts are cool they are in no way needed to read the frequency of the light sensor all that is needed is the digitalRead [make sure to set the pinMode to output].  I'm now finding that its great to know that there are 5768 highs in a 10 ms time frame but I still need to figure out some sort of meaningful output... the quest continues.  Here is the code to at least read the input:

Code: [Select]
#define OUT  10
#define S2   8
#define S3   9
#define S0   6
#define S1   7
#define OE   23
#define LED  22

#define  Filters_R  0
#define  Filters_G  1
#define  Filters_B  2

int Count[3] = {0};
long count = 0;
int counter = 0;
long start_time = 0;
int G_flag = 1;
int RGB = 0;
long freq = 0;

void setup()
{
  pinMode(OUT, INPUT);
  pinMode(S2, OUTPUT);
  pinMode(S3, OUTPUT);
  pinMode(S0, OUTPUT);
  pinMode(S1, OUTPUT);
  pinMode(OE, OUTPUT);

  digitalWrite(S2, HIGH);
  digitalWrite(S3, LOW);
  digitalWrite(S0, HIGH);    // 100% of Output Frequence
  digitalWrite(S1, HIGH);
  digitalWrite(OE, LOW);

  Serial.begin(9600);
  Serial.println("Starting...");
}

void loop()
{
  Serial.println("loop");
  for(int i=0; i<3; i++)
  {
    RGB=i;
    Select_Filters(i);
    start_time = millis();
    count=0;
    for(int i=start_time; i<start_time+10000; i++)
    {
      if(digitalRead(10) == HIGH)
      {
        count++;
      }
      counter++;
    }
    freq = count / 7200;
    Serial.print("value: ");
    Serial.println(count % 255);
    Serial.print("count: ");
    Serial.println(count);
    Serial.print("counter ");
    Serial.println(counter);
    counter=0;
    count=0;
    freq=0;
  }
  delay(3000);
}

void Select_Filters(int RGB)
{
  switch(RGB)
  {
    case Filters_R:          //Red
    digitalWrite(S2, LOW);   
    digitalWrite(S3, LOW);
    Serial.println("-----select Red color");
    break;
   
    case Filters_G:          //Green
    digitalWrite(S2, HIGH);   
    digitalWrite(S3, HIGH);
    Serial.println("-----select Green color");
    break;
   
    case Filters_B:          //Blue
    digitalWrite(S2, LOW);   
    digitalWrite(S3, HIGH);
    Serial.println("-----select Blue color");
    break;
   
    default:                  //Clear(no filter)
    digitalWrite(S2, HIGH);   
    digitalWrite(S3, LOW);
    Serial.println("-----no filter");
    break;
  }
}

void callback()
{
  count++;
}

marco_c

#5
May 06, 2013, 10:13 am Last Edit: May 06, 2013, 02:56 pm by marco_c Reason: 1
The output of the sensor is a frequency that is linearly proportional to the colour intensity. What you need to do is measure that frequency for R, G and B and then interpret what colour you have.

I wrote a library for this sensor and have a pdf as part of the distribution that explains how the sensor can be calibrated. It also has a few examples sketches, one of which is for calibration. If you are interested follow the link in my signature below.
Arduino libraries http://arduinocode.codeplex.com
Parola for Arduino http://parola.codeplex.com

aerouta

Great writeup!! This helped shed light on several items. Though I am no longer using this sensor(I seemed to get better results with HDJD color sensor) it seems that the approach described is slightly more complex. My approach with this sensor was to average several of the values from the pulsein function. Which if I understand the write up it is similar using the time period in that several measured values are obtained for a single reading. 

Another different is the forumla described in the paper uses f_d. I am curious what type of values are you getting for f_d. I did not considered mearsuing f_d so in my case f_d is (0,0,0). This would reduce the equations to x = 255fo/fw which is the approach I used.

marco_c

#7
Jun 05, 2013, 11:59 pm Last Edit: Aug 17, 2014, 04:02 am by marco_c Reason: 1
I didn't take averages because I found that the readings I got back were identical, surprisingly, but that meant that one reading was enough. I guess that is because we are counting pulses over a relatively (in computer terms) long time.

Whether you need Fd depends on how you apply the sensor. With my sensor Fd was 2-5% of the full range, depending on the filter applied (R, G or B). If you are only trying to measure relativity between samples (ie, something is more 'red' or 'green' than something else) then ignoring Fd is fine, as the sensor's linearity will give you what you need.

However, if you are trying to find the actual RGB colour of a sample, then Fd becomes important. The mappings I was getting were close to the colour's RGB number, but in some cases the 'look' of that colour was very different. Adding the Fd correction fixed that up.

Fd is also useful for correction of your background is not 'black' as the background will contribute to the colour you are trying to measure.
Arduino libraries http://arduinocode.codeplex.com
Parola for Arduino http://parola.codeplex.com

rave00fever


Although timers/interrupts are cool they are in no way needed to read the frequency of the light sensor all that is needed is the digitalRead [make sure to set the pinMode to output].  I'm now finding that its great to know that there are 5768 highs in a 10 ms time frame but I still need to figure out some sort of meaningful output... the quest continues.  Here is the code to at least read the input:


I modified the code you posted and made it working.  See https://github.com/jonfu/LightSensingWithoutInterrupt

Go Up