New library for TCS230 RGB Color Sensor

The TCS230 is a color light-to-frequency converter on single CMOS integrated circuit. The output is a square wave(50% duty cycle) with frequency directly proportional to light intensity (irradiance). The full-scale output frequency can be scaled by one of three preset values via two control input pins. Output enable (OE) places the output in the high-impedance state for multiple-unit sharing of a icrocontroller input.

I recently acquired one of these sensors and was surprised at not being able to find a good library, and explanation for how to use the sensor, so I wrote one. The library is available at the link in my signature below). This library has a dependency on the FreqCount library for frequency counting (included in the download).

Library example code includes:

  • Simple blcking read from the sensor
  • Simple RGB non-blocking read
  • Example incorporating sensor calibration
  • Color learning and matching

As usual an questions, comments and suggestions for improvement are welcome.

Thanks for sharing! you're good in libs! Well done,

Some small remarks to think over

typedef struct
{
	uint32_t value[RGB_SIZE];	// Raw data from the sensor
} color32;

typedef struct
{
	uint8_t	value[RGB_SIZE]; // the evaluated colour data (RGB value 0-255 or other)
} color8;
void	setEnable(bool t);		// enable the device (using OE or frequency)
=>
void enable();
void disable();

Choose better names for some variables,
keeps the code more readable / maintainable
and you can remove comments as code becomes self documenting

e.g.

	uint8_t	_OutputEnable;
	uint8_t	_FreqScaler0, _FreqScaler1;	
	color32	_DarkCalibration;
	color32	_WhiteBalance;
	color32	_currentRaw;
	color8	_currectRGB;

also for the function prototypes

MD_TCS230(... uint8_t freqScaler0, uint8_t freqScaler1, uint8_t outputEnable);

Have a look at error handling and return values. It is only slightly more code and adds real value

bool MD_TCS230::setFilter(uint8_t f)
// set the sensor color filter
{
	if ((_S2 == NO_PIN) || (_S3 == NO_PIN))
		return false;

	DUMPS("\nsetFilter ");
	switch (f)
	{
	case TCS230_RGB_R:	DUMPS("R");	digitalWrite(_S2, LOW);		digitalWrite(_S3, LOW);		break;
	case TCS230_RGB_G:	DUMPS("G");	digitalWrite(_S2, HIGH);	digitalWrite(_S3, HIGH);	break;
	case TCS230_RGB_B:	DUMPS("B");	digitalWrite(_S2, LOW);		digitalWrite(_S3, HIGH);	break;
	case TCS230_RGB_X:	DUMPS("X");	digitalWrite(_S2, HIGH);	digitalWrite(_S3, LOW);		break;
	default:	DUMPS("setFilter error");	Serial.println(f, DEC); return false; break;   
	}
	return true;
}

or

bool MD_TCS230::setSampling(uint8_t t)
// set sampling rate divisor for frequency counter
{
	if (t > 0) 
	{
	   _readDiv =  t;
	   return true;
	   }
	return false;
}

or

bool MD_TCS230::setDarkCal(sensorData *d)
// set dark calibration data for rgb calculations
{
	if (d == NULL)
		return false;

	DUMPS("\nsetDarkCal");
	for (uint8_t i=0; i<RGB_SIZE; i++)
	{
		DUMP(" ", d->value[i]);
		_Fd.value[i] = d->value[i];
	}
	return true;
}

BTW I like your consequent debugging style!

Hey Rob! Nice to hear from you again :slight_smile:

Have a look at error handling and return values. It is only slightly more code and adds real value

Good point. I'll check what makes sense for the lib.

Of interest to me- did you notice that there was a pdf file that expained the theory of how to convert a reading from the sensor to a valid RGB value? Same folder as the libraries. Most of the nomenclature for the library comes from there for consistency with the algebra behind the code. Also a lot of the variable names are directly related to the pins of the sensor and what is printed on the breakout boards, which I thought would make it easier for users to relate the library to the hardware.

This next one is interesting and one I have thought about for some time and I would welcome yours and others thoughts:

void setEnable(bool t); // enable the device (using OE or frequency)
=>
void enable();
void disable();

I have used objects that split out methods for every little property (like the trivial enable/disable here) and I have found that I then have to handle calling different functions in my code somehow, like

if (condition)
  object.enable()
else
  object.disable()

when in fact I could have used

object.enablefunction(condition);

which I think is shorter, clearer and more 'data driven' rather than code intensive.

Similary for libraries where there are many options to set, I would tend to think in terms of object.setoption(option, value) rather than a slew of .option1(), .option2(), etc. Is there any specific advantage to splitting out the functions that I don't understand? Clearly my style can be easily converted to be more wordy by implementing wrapper functions.

OK,
you have good args for your choices made. Keeping in sync with the datasheet is - at least for private parts - a good idea. For the public part you are on another abstraction level so more readable makes sense there. A library can hide the internals of the datasheet


if (condition)
  object.enable();
else
  object.disable();
-->
object.enablefunction(condition);

This makes perfect sense if the condition evaluates to true, but less (imho) when you want to disable() when the condition is true

if (condition)
  object.disable();
else
  object.enable();
-->
object.enablefunction(condition == false);    // looks a bit strange
object.enablefunction(!condition);               // risk of missing the !

most conditions are easy to rewrite to their negate version, so I think your args make more sense.


I often have code like this

if (condition)
{
  object.enable();
  ... other code
}

....  lines of other code

if (condition2)
{
  object.disable();
  ... other code
}
-->
object.enablefunction(condition);

Hi, I've tried your library but it does not seem to work with the board that I have. This is myTCS230 board (bought it on Ebay) http://www.bajdi.com/?attachment_id=940
I've connected OE to pin 8, S2 to pin 12, S3 to pin 13 and connected 5V and GND. I then tried the example calibrate sketch but I only get [0,0,0] values. The other sketches also don't work for me. The blocking sketch only gives 0 all the time, the non blocking sketch give [255,255,255] not matter what I hold in front of the sensor.

Did you connect OUT to pin 5? The Freqcount library counts frequency on that pin.

You also need to decide how to set s0 and s1. I would suggest you set them for 100% output (both 5v from memory)

Edit: your sensor looks like mine. It needs to be modified as explained in the PDF file that is in the library folder with the .cpp file if you want to use the OE line for control.

Oops I hadn't seen the pdf file :blush: I've just read it and cut the trace on my board. It was connected to ground just like yours. I've just uploaded the calibrate sketch and it works. Thanks for making the library and writing the pdf file. Not many libraries are that well documented :slight_smile:

No problems. I hope you have fun with the sensor. It actually works quite well if the calibration is right.

I just want to say thank you for the library.
I'm new with Arduino and for my first project I'm making a color sorter machine with the TCS230.

hello

nice job, but i get a timer 2 error

In file included from C:\arduino-1.0.5\libraries\FreqCount\FreqCount.cpp:27:
C:\arduino-1.0.5\libraries\FreqCount/util/timers.h: In function 'void timer_shutdown()':
C:\arduino-1.0.5\libraries\FreqCount/util/timers.h:453: error: 'TCCR2B' was not declared in this scope

and can't solve it...using arduino uno

zhx in advance

I have not had this problem reported before and I don't get it when I compile my version of the same thing.

Can you compile the test programs that come with the library files?

From the paths it seems like you have installed the 'user' libraries in the Arduino libraries folder. It probably makes no difference, but the recommended location for user libraries is in a libraries folder in your sketchbook folder. This makes it easier to upgrade as this location does not change between upgrades of the Arduino IDE/compiler software. It is also worth a try to see if moving it fixes your problem.

You have not included the rest of your code (ie, the whole sketch). A lot of the time the problems are not where the errors are coming from. Without the entire context it is hard to offer advice about what could be going wrong.

hello,
I am so new at Arduino and I got one TCS230. exactly this one https://www.kartalotomasyon.com.tr/modules/catalog/products/pr_01_10006_max.jpg . I downloaded your library now trying to get some results but couldnt get anything on screen. Can you tell me the pic connections with arduino and TCS230 please?

You can connect the sensor to any pin as long as you tell the library which one is which when you create the object - please read the documentation PDF that comes with the library.

The one exception is the frequency reading pin. From the header file:

This library has a dependency on the FreqCount library for frequency counting. FreqCount library is available at FreqCount Library, for Measuring Frequencies in the 1 kHz to 5 MHz Range

** IMPORTANT NOTE**
FreqCount imposes a limitation that the frequency can only be counted on specific pins and limits the use of other pins as follows:
Board Input Pin Pins Unusable with analogWrite()


Arduino Uno 5 3, 9, 10, 11
Arduino 2009 5 3, 9, 10, 11
Arduino Mega 47 9, 10, 44, 45, 46
Sanguino 1 12, 13, 14, 15

Thanks very much for the library, it's working nicely for me. Is it possible to turn off the LEDs through a command or shall I just have VCC across a transistor so I can turn the chip on and off as required? For my application the readings will be taken in near dark conditions and the LEDs are dazzling.

Secondly, it is mentioned that you connect the output to 5 but I don't think it's clearly specified in the code. Anyone should be able to figure it out by reading the documentation a bit more carefully but I would have had your library going practically instantly if I had known this.

Thanks very much for your work though, I am very happy with it! :slight_smile:

You need to turn Vcc off. The problem will be that it turns the sensor off as well, so the chip will need to be reinitialised when power is applied. You may be able to modify the sensor so the LEDs are being powered separately from the chip.

I understand your comment on pin 5, but that is only true for the Uno and related Arduino. As per my previous post, the pin will change depending on the type of Arduino being used.

Ok, thanks for your reply. I'm sure I'll find something that works out.

I forgot about the differences between Arduino's, so you're quite right.

Good morning,

It looks amazing but i have one problem and i do not what to do.

My error.

avr-g++: error: missing filename after '-o'
exit status 1

Can you help me?
Thank you very much.

I know i'm a bit late to the party :smiley: but i just wanted to say thanks for the excellent, well documented library! It's better than even most of the official Adafruit libraries :). just don't forget to put the OUT pin to pin 5 like i did :grinning:

Thanks, always good to receive nice feedback :slight_smile:

I couldn't tell what it meant when it said to cut a track on the pcb, and the picture was relatively low quality, so could you may be explain the fix?
Thank You