Code for using a TCS3200 with Arduino, and a question
eecher
Posts: 6
Hi Folks
After much struggle with non-functional code examples that I found on the web, I recently managed to get a TCS3200-DB to work with an Arduino, so I thought I share my working code with anybody else who's interested. I'll put the code in a quote block below, but first I have a question: the TCS3200-DB has a focusable lens above the sensor, but how would I know if it's focused properly?
After much struggle with non-functional code examples that I found on the web, I recently managed to get a TCS3200-DB to work with an Arduino, so I thought I share my working code with anybody else who's interested. I'll put the code in a quote block below, but first I have a question: the TCS3200-DB has a focusable lens above the sensor, but how would I know if it's focused properly?
/* 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; }
Comments
Welcome to the Parallax forum! This is very helpful. Thanks for posting!
The best focus on the latest model achieved when the top of the lens is flush with the top of the lens holder.
BTW, here's a little brief on posting code in the forum:
-Phil
Not to hijack my own thread, but I'm baffled by the experience I've had thusfar with the testing I've done with the TCS3200 sensor I got from Parallax and wonder if they or anyone else has any advice to offer. I'm trying to use it to measure the color of roasted coffee beans. After alot of tinkering with this and other sensors, I found that the physical conditions for this sort of task are extremely important-- to make useful, consistent measurements the sensor needs to be EXACTLY the same distance from its target every time with EXACTLY the same lighting conditions, etc. I've built a few housings for the sensor and the beans it is measuring and have that part of the setup taken care of. As far as I can tell, the electrical conditions (voltage supplied to my Arduino, and thus to the sensor) and ambient temperature are constant, too. What I'm finding is that as time goes by, the output from the sensor will vary widely, even when looking at the exact same target with completely unchanged conditions. For example, after using the sensor for awhile this morning, I took repeated measurements of the same color tile sample over the space of about 10 minutes. NOTHING about the target or lighting conditions or anything else changed. The measurements drifted from an initial reading of around 7000 all the way down to a little over 5000. As long as I continued to make measurements, the output from the sensor looking at the exact same target continued to drift downwards. Could this drift be related to temperature? Are the sensor and/or its attached LEDs "warming up", either figuratively or literally? In any case, it's not acceptable for my project. Here's a simple summary of the code that I'm using:
- set sensor to second-from-lowest sensitivity level (similar "drift" results are seen at all sensitivity levels)
- turn on LEDs
- wait a half second
- take 1000 measurements and average the results (coffee beans are a lumpy, variegated target, so the idea is that the user will shake around the container with the beans and sensor inside while these measurements are taken, averaging out the variability inherent in the beans. For these tests I'm using a static, uniform target. This usually takes between 2 to 8 seconds.)
- turn off LEDs
- return sensor to low-power state
Maybe this sensor isn't designed to be "on" for so long, taking so many consecutive readings? Apparently the usual usage for this sort of sensor is in a single-shot, fraction-of-a-second sort of way, but I figured it would be capable of more or less continuous usage, but maybe I'm wrong... Anybody have any ideas?
2. Take a reading.
3. Turn off LED.
4. Take another reading.
5. Subtract reading 4 from reading 2.
-Phil
Interesting... like some cameras do when taking a long exposure photo to get a "baseline black". I'll give that a try, thanks for the suggestion. The "black" reading in this case will produce a larger number than the reading I'm interested in (reading 2 in your scheme) so perhaps I should subtract 2 from 4... or would subtracting 4 from 2 and disregarding the negative sign be wiser for some reason?
Another thought occurred as well. Some roasted coffee beans are glossy. The angle of the LEDs prevents specular reflections from flat, glossy surfaces, but coffee beans are not flat. Could you be getting glare from their shiny surfaces?
-Phil
Glare from shiny beans is definitely an issue, part of the reason for the shotgun "take 1,000 readings of moving beans and average them" approach I've been taking. One thing I messed around with recently was purposely "defocusing" the lens on the sensor to provide a blurry view of the subject. It didn't seem to make much difference. Would you happen to know if different optics are available for that lens assembly? I'd like to try one with a wider angle of view and/or a "soft focus" effect if such things are available.
One problem with averaging is that the outlying values affect the result. One way around this is to use the median value of multiple samples, rather than the average (i.e. mean).
You can get shorter focal-length lenses for a wider-angle field of view, but you will still be constrained by the spot size of the LED illumination.
To encompass a larger field of view, you could dispense with the LEDs altogether and provide your own bright, white-light illumination. It would either need to be brightness regulated with feedback from a light sensor, or you could rig up a white target that could be rotated (with, say, an RC servo) into the color sensor's field of view from time to time for calibration (i.e. "white balance").
-Phil
I'll give the median value approach a try. Measuring beans does tend to give some fairly wild outlier values, so taking the median will almost certainly be the best way to get a realistic measurement.
I would really like to give a shorter focal-length lens a try-- know where/how I can get one? I thought I figured out at one point the company that makes the lens assembly on this sensor, but I can't find any traces of it now. I've been thinking that things would work better if I could have the sensor take a wider view of the beans while they are lit with a diffuse light source, and I have a bunch of bright white LEDs that I bought for the purpose. I was figuring I would need to regulate their output (or at least compensate in my calculations for any variance) and have been including a feedback light sensor in my plans. This project has turned out to be way more complicated than I initially thought, but it has certainly been an education in the complexities of real-world precision measurement!
The lens used in the TCS3200 module is purchased in volume directly from a Chinese manufacturer, and it will be difficult to get single pieces from them. Check out these domestic sources for replacement lenses:
Marshall Electronics
Sunex
Edmund Optics
The relevant spec is the thread diameter (12mm) and pitch (0.5mm). You will want a focal length significantly smaller than 5mm.
-Phil
I'm using the same chip in a Chinese (what isn't?) little board called a GY-31. The readings from the TCS3200 seem to jump around a lot on their own, need some serious filtering. And with the time required for the illuminating LEDs and readings to stabilize, it's hard to get a fast loop out of them. Oh, well.
I have a TCS 3200 color sensor as well connected (over a sensor shield) to an Arduino Mega 2560.
I want it to be able to recognize 3 colors: red, blue and green.
I held a red, blue and green card under it and took 10 consequent readings for each card.
As suggested here I subtracted the values of the reading with the LED on from those without. I called the result true[color]
First the red card.
then the green card
and then the blue card.
In all the readings of the red card and almost all other readings red is always the lowest of the RGB values.
In the green card reading the truegreen value is always the highest RGB value.
And in the blue card reading trueblue is almost always the highest RGB value.
During each of those readings the lighting conditions barely changed and I didn't move the card during its reading.
Why are the readings so unprecise? And why does the red value get smaller when you hold a red card in front of the sensor while for the other cards their value gets bigger?
-Phil
No I didn't. Could this be the cause of the inaccuracy?
The sensor is ~1.2cm above ground. I read that it needs to be white balanced but I am a total electrical engineering and programing newbie so I didn't understand how to do it. I thought that I could leave out the WB because I only need it to recognize 3 colors and not give me super precise measurements.
Possibly, but don't expect the color you're presenting to dominate in RGB space. What you should do is sample each color, then compare test colors to each sample to find which one it is closest to.
But even here, don't expect dark red to match bright red, say, better than it does green or blue. If intensity and saturation can vary across various samples, you may have to convert to a different color space, e.g. HSI (hue, saturation, intensity) and compare the hue coordinates only.
-Phil
Thank you for this tip. I will try that.
I am sorry but how does one change the color space?
-Phil
If I deiscover anything while I play, I will add to the thread.