The initial idea of “How was your day, darling” was to record the colors for a day. You know a morning is golden, evening is reddish, thunderstorm is greenish and so on. Measuring this with a simple RGB LED is a brave approach – but hard and cumbersome. So I ordered an ADJD-S371-Q999 color sensor breakout board from Sparkfun and played around with it.
Finding resource on this chip is very hard and getting it really working is much harder. Dealing with this complex topic is much more difficult than I thought. So I prepared some arduino code to get you started (after the break).
First of all: How to feed it? It takes voltages up to 3.6 volt. An USB Buarduino loves 5 volt. First I tried to take the 3V from the FTDI232R on the Boardino. Don’t try this at home. The 3.3V is go for 50mA but neither enough to power the whole Boardino, nor to power the color sensor or a RGB LED. The FTD232R got so hot that I thought I fried it – the Boardino and USB port acted quite weird. But it was just overheated.
The ADJD with Buardino on the breadboard
So I grabbed my 3.3V breadboard (see above, just a breadboard with some batteries, regulated down to 3.3V), disabled the Boardnio power supply, hooked up the I2C connection, enabled TWI in my sketch – and here we go:
The TWI/I2C uses the following ports on the Arduino:
- SCL – the serial clock – is connected to a5 (analog input 5)
- SDA – the serial data line – is connected to a5 (analog input 4)
- SLEEP was tied to GND
- LED was connected to pin 2
- XCLK – the external clock was left floating.
TWI can be enabled quite simple, just by including the header “wire.h”:
#includeGrab the data sheet, find some application note, read it and pretend to understand what you are doing. Fine. But how do I actually talk to that thing? Google! At the end I came up with a sketch in a thread on the Sparkfun Forums. It got everything to write. But reading was more or less random. Until I found out that the reading from the ADJD was like writing the adress of the register and waiting for the answer. Somewhere in the Internet I found a piece of code to use (if you know where, please leave a note in the comments – I would really like to give the correct credit. But I did not find it again.):
#include <Wire.h> const int serialSpeed=9600; int ledPin = 2; // the light //ADJD Settings #define ADJD 0x74 #define CAP_RED 0x06 #define CAP_GREEN 0x07 #define CAP_BLUE 0x08 #define CAP_CLEAR 0x09 #define INT_RED_LO 0x0A #define INT_RED_HI 0x0B #define INT_GREEN_LO 0x0C #define INT_GREEN_HI 0x0D #define INT_BLUE_LO 0x0E #define INT_BLUE_HI 0x0F #define INT_CLEAR_LO 0x10 #define INT_CLEAR_HI 0x11 #define DATA_RED_LO 0x40 #define DATA_RED_HI 0x41 #define DATA_GREEN_LO 0x42 #define DATA_GREEN_HI 0x43 #define DATA_BLUE_LO 0x44 #define DATA_BLUE_HI 0x45 #define DATA_CLEAR_LO 0x46 #define DATA_CLEAR_HI 0x47 #define OFFSET_RED 0x48 #define OFFSET_GREEN 0x49 #define OFFSET_BLUE 0x4A #define OFFSET_CLEAR 0x4B void setup() { pinMode(ledPin, OUTPUT); // declare the ledPin as an OUTPUT Serial.begin(serialSpeed); // set up Serial library at 9600 bps Wire.begin(); Serial.println("Sending Calibration data ... "); adjd_init(); Serial.println("Calibration data sent. "); } void loop() { digitalWrite(ledPin,HIGH); read_register(DATA_RED_LO); digitalWrite(ledPin,LOW); delay(4000); } static void adjd_init() { write_register(CAP_RED, 0x05); write_register(CAP_GREEN, 0x05); write_register(CAP_BLUE, 0x05); write_register(CAP_CLEAR, 0x05); write_register(INT_RED_LO, 0xC4); write_register(INT_RED_HI, 0x09); write_register(INT_GREEN_LO, 0xC4); write_register(INT_GREEN_HI, 0x09); write_register(INT_BLUE_LO, 0xC4); write_register(INT_BLUE_HI, 0x09); write_register(INT_CLEAR_LO, 0xC4); write_register(INT_CLEAR_HI, 0x09); } void read_adjd_offset() { } static void write_register(uint8_t register_name, uint8_t register_value) { Wire.beginTransmission(ADJD); Wire.send(register_name); Wire.send(register_value); Wire.endTransmission(); } static uint8_t read_register(uint8_t register_name) { Wire.beginTransmission(ADJD); Wire.send(register_name); Wire.endTransmission(); Wire.requestFrom(ADJD,2); Serial.print("read:"); while (Wire.available()<1) { Serial.print("."); } int result = Wire.receive(); Serial.println(result); return 0; }
The routine for reading the register did the trick:
static uint8_t read_register(uint8_t register_name) { Wire.beginTransmission(ADJD); Wire.send(register_name); Wire.endTransmission(); Wire.requestFrom(ADJD,2); Serial.print("read:"); while (Wire.available()>1) { Serial.print("."); } int result = Wire.receive(); Serial.println(result); return 0; }
That was a promising beginning! But the sketch did miss one big feature of the ADJD: It did not adapt to different light levels. I had to come up with a very clever idea how to calibrate it. The calibration was done adapting the integration time of the sensors to the light level. First for the clear channel (it is allays a bit more sensitive than the color levels):
int getClearGain() { int gainFound = 0; int upperBox=4096; int lowerBox = 0; int half; while (!gainFound) { half = ((upperBox-lowerBox)/2)+lowerBox; //no further halfing possbile if (half==lowerBox) { gainFound=1; } else { set_gain(REG_INT_CLEAR_LO,half); performMeasurement(); int halfValue = get_readout(REG_DATA_CLEAR_LO); if (halfValue>1000) { upperBox=half; } else if (halfValue<1000) { lowerBox=half; } else { gainFound=1; } } } return half; }
After this worked flawlessly the same trick was used to get the perfect color detection gain. According to the data sheet in both cases 1000 was used as target value (for the clear readout or the one of the colors).
int getColorGain() { int gainFound = 0; int upperBox=4096; int lowerBox = 0; int half; while (!gainFound) { half = ((upperBox-lowerBox)/2)+lowerBox; //no further halfing possbile if (half==lowerBox) { gainFound=1; } else { set_gain(REG_INT_RED_LO,half); set_gain(REG_INT_GREEN_LO,half); set_gain(REG_INT_BLUE_LO,half); performMeasurement(); int halfValue = 0; halfValue=max(halfValue,get_readout(REG_DATA_RED_LO)); halfValue=max(halfValue,get_readout(REG_DATA_GREEN_LO)); halfValue=max(halfValue,get_readout(REG_DATA_BLUE_LO)); if (halfValue>1000) { upperBox=half; } else if (halfValue<1000) { lowerBox=half; } else { gainFound=1; } } } return half; }
Two small helper method were used to read or write the 16bit registers (and yes they could have been named better. The gain setting routine is this:
void set_gain(int gainRegister, int gain) { if (gain < 4096) { uint8_t hi = gain >> 8; uint8_t lo = gain; set_register(gainRegister, lo); set_register(gainRegister+1, hi); }
and the read routine is this:
int get_readout(int readRegister) { return read_register(readRegister) + (read_register(readRegister+1) << 8); }
Now I was able to get perfect readouts from the chip. The next, quite complicated task was to calibrate the ADJD. The whole setup was again covered by a half table tennis ball. But somehow the internal LED of the ADJD was far to weak to illuminate table tennis ball. So I added a second (white) LED to get a bit more effect. The calibration was done adding capacitors from the measurement (anyone who read the datasheet knows what I mean): You can set the number of capacitors used during the measurement. Unfortunately the data sheet only tells that the more capacitor you use the lower readout you get. How the capacitors work or what they are remains unclear. Now matter. The sketch was just implemented that way:
- The white LED is lighted
- Each color is read and the capacitors are trimmed to get more or less the same readout from each color channel, this is repeated until the gap between the readouts do not get smaller (I know – I will come up with a better routine for this).
- The capacitor values are stored as defaults.
The sketch tries to keep the values as low as possible – since the drop between the clear and the color channels is high enough – this gives at least a comparable readout (for example if you use the same integration time for the clear and color values:
void calibrateChip() { calibrationRed = 0; calibrationBlue = 0; calibrationGreen = 0; byte calibrated = 0; //neede to store detect better calibration int oldDiff = 5000; while (!calibrated) { //enabled the white light digitalWrite(ledPin, HIGH); // calibrate the sensor // sensor gain setting (Avago app note 5330) // CAPs are 4bit (higher value will result in lower output) set_register(REG_CAP_RED, calibrationRed); set_register(REG_CAP_GREEN, calibrationGreen); set_register(REG_CAP_BLUE, calibrationBlue); int colorGain = getColorGain(); set_gain(REG_INT_RED_LO,colorGain); set_gain(REG_INT_GREEN_LO,colorGain); set_gain(REG_INT_BLUE_LO,colorGain); int maxRead = 0; int minRead = 4096; int red=0; int green=0; int blue=0; for (int i=0; i<4 ;i ++) { performMeasurement(); red +=get_readout(REG_DATA_RED_LO); green +=get_readout(REG_DATA_GREEN_LO); blue +=get_readout(REG_DATA_BLUE_LO); } red /=4; green /=4; blue /=4; maxRead = max (maxRead, red); maxRead = max (maxRead, green); maxRead = max (maxRead, blue); minRead = min(minRead, red); minRead = min(minRead, green); minRead = min(minRead, blue); int diff = maxRead - minRead; if (oldDiff != diff) { if ((maxRead==red) &&; (calibrationRed<15)) { calibrationRed++; } else if ((maxRead == green) && (calibrationGreen<15)) { calibrationGreen++; } else if ((maxRead == blue) && (calibrationBlue<15)) { calibrationBlue++; } } else { calibrated = 1; } oldDiff=diff; int rCal = calibrationRed; int gCal = calibrationGreen; int bCal = calibrationBlue; Serial.print("calibration :"); Serial.print(diff); Serial.print(" r="); Serial.print(rCal); Serial.print("/"); Serial.print(red); Serial.print(", g="); Serial.print(gCal); Serial.print("/"); Serial.print(green); Serial.print(" b="); Serial.print(bCal); Serial.print("/"); Serial.println(blue); } digitalWrite(ledPin, LOW); }
The next step was to calibrate the RGB LED against the ADJD. If you send the same courrent to each color of the RGB LED you get some red result. Since the red LED normally is the most efficient. The least efficient LED is normally the blue one. So I developed a way to calibrate the different LED colors:
These routines use floating point variables, tis might not be the most efficient way – but the math was complex enough. In the end the calibration is calculated the following way:
float redFactor=1; float blueFactor=1; float greenFactor=1; void calibrateRGB() { digitalWrite(redPin,HIGH); digitalWrite(bluePin,HIGH); digitalWrite(greenPin,HIGH); int ledGain = getColorGain(); set_gain(REG_INT_RED_LO,ledGain); set_gain(REG_INT_GREEN_LO,ledGain); set_gain(REG_INT_BLUE_LO,ledGain); performMeasurement(); int red=get_readout(REG_DATA_RED_LO); int green=get_readout(REG_DATA_GREEN_LO); int blue=get_readout(REG_DATA_BLUE_LO); digitalWrite(redPin,0); digitalWrite(bluePin,0); digitalWrite(greenPin,0); int m=2000; //bigger anyway m=min(m,red); m=min(m,green); m=min(m,blue); redFactor=((float)m*255.0)/(1000*(float)red); greenFactor=((float)m*255.0)/(1000*(float)green); blueFactor=((float)m*255.0)/(1000*(float)blue); }
And the replay of the 10bit readouts goes like this:
float rv = (float)red*redFactor; float gv = (float)green*greenFactor; float bv = (float)blue*blueFactor; int r = rv; int g = gv; int b = bv; analogWrite(redPin,r); analogWrite(bluePin,g); analogWrite(greenPin, b);
In the end it gives quite usefull results.
I tinkered around with it a bit further and got a complete working program: ADJD-S371. It contains a lot of rubbish and the organization of the code is less than perfect. But at least it gives a good overview of how it can be done.


{ 72 comments… read them below or add one }
Hi, Marcus
Your work is really great.
At the moment, I am trying to connect my Skinny from SFE with the Avago color eval. board.
Would the power from the Skinny 3.3v be insufficient to drive the Avago eval. board? I have series of “read 0″s all the way through, with the program list you cited first in this section.
Thanks so much.
sjyng
Hi thanks for all the information Marcus, but i is it possible you have a complete sensor code with build in calibration? I tested the code you had ” ADJD-S371″. But Im not good enough to make it work. Do you have any complete code? If you have I would be happy and gladly to have it!
I have coloring measuring codes that works ok, but the problem is that it adapts poorly to light and once it been exposed to light it ever resets and requires constant calibration. Making a project with Arduino, interactive lights and clothing.
Hi Ivan,
yes and no. Ye, I would really like to get out a real Arduino Library for this sensor. Unfortunately I do not have enough time at the moment to program some.
Perhaps next summer (unfortunately my to do list is that packed).
If you need some support for a project just contact me.
Hi sjyng,
3.3V seems perfect to me. It is the same as I used. Are you sure you used the right register_read() routine? The example from the Sparkfun forum cannot read values.
Before I fixed the read routine I also got columns of columns of 0 readouts. This got fixed by the above read routine:
static uint8_t read_register(uint8_t register_name)
{
Wire.beginTransmission(ADJD);
Wire.send(register_name);
Wire.endTransmission();
Wire.requestFrom(ADJD,2);
Serial.print(“read:”);
while (Wire.available()<1) {
Serial.print(“.”);
}
int result = Wire.receive();
Serial.println(result);
return 0;
}
Thanks, Marcus.
I’ll give it a try soon. That will be very helpful to me.
sjyng
Hi,
Thank you for posting this code. I am in the process of trying to build a project using the avago color sensor. I have run into some problems and have determined that I either killed my sensor or have misunderstood some aspect of the code development process you have outlined in this blog entry. It seems like in the code you posted here that you have tried several different ways of reading from the sensor. It is clear to me that this is the correct read routine:
static uint8_t read_register(uint8_t register_name)
{
Wire.beginTransmission(ADJD);
Wire.send(register_name);
Wire.endTransmission();
Wire.requestFrom(ADJD,2);
Serial.print(â€Âread:â€Â);
while (Wire.available()<1) {
Serial.print(â€Â.â€Â);
}
int result = Wire.receive();
Serial.println(result);
return 0;
}
but there seems to be a lot of leftover code in your file from earlier attempts, this has been somewhat confusing in trying to determine whether the problem is with my sensor or with my implementation of your code. What would be most helpful to me would be to see a copy of the exact final code you used to get the values from sensor. I just want to confirm that I didn’t break mine.
Yes, you are right.
There was (I fixed it) a old rubbish read routine in the code. The rest of the code is more or less a sketch (not as in arduino sketch).
But you grabbed the right read routine. This is exactly the method how I read data from the ADJD. The only addition I made in the final code was to adjust gain and white level.
I have added the final code to the post, so that you can verify your attempt. But be warned the code is not very good, but works flawlessly.
Thank you so much for sharing your findings. I was able to confirm that my sensor is not broken and your program will definitely be helpful towards figuring out my project.
hi
marcus can you make me some favor i will pay you for your time.
plz help i just got freaky how to code my mobile robot. actualy my robot supposed to be like this if my mobile robot see light color red then my mobile robot will stop from running and if color light green my robot will continue running
plz marcus build me some code in arduino in how to do it. ill pay you $10. plz or hiegher from that.
Hi Jimson,
I am sorry- I already got a day job. ;)
To be honest – I simply got no time to do this. But I can help you to get you code debugged.
Have you tried to output the color seen by the roboter via serial/usb (Serial.print()) ?
Does it decode the right color?
Does it need light or color calibration?
Hi marcus
I did not try. the robot must supposed to see light color
tnkz for the little help marcus. i appreciate it.
by the LED being connected to ‘pin 2′ do you mean the TX port on the arduino?
thanks
Yes,
but that is not critical – you can connect it to any port – as long as you adjust the source code.
BTW: Some people mentioned that the led is quite dim. Did you notice it too?
thanks Marcus,
I tried your code with my arduino decimila but couldn’t see anything in the arduino terminal after transferring the code and pressing the reset button. the LED on the color sensor was lit up, though, bright enough for my use. I connected the LED port to pin 1 (tx) on the board. I also tried pin 2, to no avail. what would you suggest?
thanks
do you have an email address, so i don’t have to keep checking this website? or perhaps the sparkfun forum would be better?
Hi Mr. Marcus i just want you to give an appreciation of the sample codes you’ve been posted, to tell you frankly its a big help to me and to my project but there is only one problem that i’ve encounterin’ right now. I just want to ask you if how can i connect this ADJD-S371-Q999 color light sensor to an arduino dueminalove microcontroller?
Sorry, I do not own an arduino duemilanove, but reading the specs you should be able to connect it to the same pins as with other arduinos. The atmega*8 controllers are pin compatible.
Thank you very much Mr. Marcus.
Mr. Marcus just one last question i wanna ask to you, are these ATmega*8 controllers the same or similar with the ATmega168 controllers?
Thats what I meant by ATmega*8. As far as I know (and odd little details may prove me wrong) the Atmega8 ATmega168 and ATmega328 (and perhaps some other controllers) are pin compatible.
You ever get this working really well?
Im using 90% of your code on this, and I can only ever get red and blue. Other colors seem to be random.
http://vimeo.com/3902448
Yes, I got it right. If you can get red and blue it is allreday 2/3 ;)
Calibration can be a bit tricky. And the built in LED seems to be much too weak.
Do you get REAL random values? OR do the values do not correspond to the real colors?
Choosing the correct gain can be tricky too.
Actually, I ended up getting it pretty good. It was the gain.
I ended up putting 3 pots on it to control them so I could tweek it as it ran.
Red never comes out pure red. I ended up making some if statements that limited the colors, and was able to do it, but with the normal way of working, when it sees pure red, it always adds green. (no matter the gain setting)
But yeah, I really wish the led was brighter. My projected color duplication is so much better than the reflective.
So I have this hooked up to a Diecimila, The LED is lit, but in the Arduino serial monitor it reads “sending configuration data…” and never changes. I have the first code you have listed here (the long one.) What am I missing, sorry I am very new at this.
Thanks so much.
This work is great! I myself have got SFE color light sensor eavluation board from spurkfun. I have been suffering of where to get some detailed resource concernig the sensor but all in vain.
Thanks to this site that i have finally landed at.
My problem is that am not very conversant with c/c++ programming and therefore when i tried putting up the code given by Marcus I missed set_register( ) function which is used in set_gain( ) function.
Someone please, define this function(set_register) for me please so that i can test my evalution board.
Again when i use the first sketch give at the top by Marcus the Serialprint out put is always Read:0. I supposed it should read value from sensor once different colors are introduced it?
Hi, this is the set_register function (quite straight forward from the data sheet):
void set_register(unsigned char r, unsigned char v)
{
Wire.beginTransmission(I2C_ADDRESS);
Wire.send(r);
Wire.send(v);
Wire.endTransmission();
}
Registers are read by:
unsigned char read_register(unsigned char r)
{
unsigned char v;
Wire.beginTransmission(I2C_ADDRESS);
Wire.send(r); // register to read
Wire.endTransmission();
Wire.requestFrom(I2C_ADDRESS, 1); // read a byte
while(!Wire.available()) {
// waiting
}
v = Wire.receive();
return v;
}
The whole read thing could be a bit more error robust – but there is allways something for improvement.
Hi Marcus,
Do you think the color sensor could be used in daylight outside to recognize colors of for example the street, grass, sand, dirt? I get the idea that this colorsensor is designed for putting something with color directly on it.
I want to make a vehicle that has the color sensor underneath it and uses the light of the enviroment.
This color sensor also seems extremely complex, why on earth does it need to be this complex..
Thanks for posting this btw.
Ok,
Answering the easiest questions first. The color sensor needs to be that complex since it covers a big range of dynamic range (dark and light light – or colors). It is up to the application to make it easier. especially setting capacitors & integration time is complex. It would be nice if the engineers gave a bit more description in the datasheet.
I think you can use it in daylight. But the calibration can get a bit challenging – since daylight is very colored (from blueish at noon to reddish at the afternoon) – just play around with it. You always have the led to switch on. It will most probably be not powerful enough to light the surface – but the analysis between the color with and without the led can give you interesting insights about the real color and light conditions. Recognizing dirt, sand and grass is absolutely possible. But which color is which can be a bit challenging.
I understand, I agree, it would be nice if the engineers had made an explanation for microcontroller users. Well I think this all is a bit more than I can chew on. Perhaps a webcam with processing recognizing colors is a better way for me.
Tnx again, cheers.
Thank you for the code, but I am not getting anything on the serial monitor, any suggestions?
Thanks in advance!
Hmm, OK if you don’t get any output at all the case is quite simple:
Add more debug code, especially in the setup and boot routine.
If you still get no output at all. You have communication problem (does uploading sketches work?) or power supply problem.
If the output stops at one point – then the problemis at this point. Most often someting with the I2C is wrong and the CPU hangs issuing the first I2C command
Hope that helps
Thank you for the response. I actually started doing that and the upload worked. I set up several Serial.print() markers and I found it is hanging at the Wire.endTransmission() in the set_register code. I have read some stuff online about people having issues with the Wire Library. I erased all the .o files that were created by the compliler and recompiled, but to no avail. Any thoughts on this particular issue would be great. Thank you so much by the way for this code, it will be very helpful if I can get it to work properly!
Ok, as far as I remember Wire.endTransmission() does the actual I2C communication. So I think that your device is not responding.
There can be many reasons for that: Either you do not have pullups, you have some problems in the hookup of the sensor, you somehow address the wrong I2C address, the sensor is dead.
I am sorry, since from that point it gets nasty.
If you have an oscilloscope you could watch the communication in detail.
If you have another I2C sensor you can try to communicate to this to ensure that you Arduino talks I2C correctly.
Not exactly the best news to hear, but thank you for the response. I am using the same setup that you are using except with a Arduino Duemalinove board. Since the Color sensor board has the pull-up resistor already on it, I don’t think that is the issue. I’ll review the datasheet for the color sensor again and hope that there is something that I missed. Thank you for your time.
Just another quick guess: Did you leave XCLK, XRST, SLEEP alone? Did you connect a wire there?
Perhaps it is in permanent reset or waiting for a clock (it does not need no external) or is in sleep mode. Recheck that against the datasheet too.
That’s the best guess I can offer.
Thank you very much, I will check that as well. Any recommendations on the XCLK input, you left it floating, should I ground it or something?
Check the schematics at sparkfun – I think it is grounded via a resistor. It should work – but if in doubt recheck the datasheet against the sparkfun schematics.
But I used it ‘floating’ all the time – so that should do the trick ;)
Markus,
Thanks for your excellent article, nevertheless, I am a bit stuck :(. I tried it but I don’t get readings, just zeros all the time, the ADJD white LED is on all the time. My questions: how do you know that the address is 0×74? I found different values on the web. Can you query the device to tell you its address? Are SCL, SDA connected directly from the Arduino to the ADJD or are there pull-up resisters being implemented (how?)?
Thanks a bunch!
Andreas
Hi Andreas,
the situation you are describing is not good!
The LED should not be on all the time. Recheck the wiring (which you have done anyway ;) ). There should always be pull up resistors on SCL and SDA. Sometimes it works without. The current revision of the Sparkfun board has them integrated. So nothing to worry about.
The I2C address is a complex thing. Often 8 and 7 bit addresses are mixed up. In datasheets you can often find separate addresses for reading and writing – those are normally 8 bit adresses for use with arduino shift them one position to the left. the 0×74 is a 7 bit address. The real I2C address consist of the 7 bit address with a write bit – giving you an 8 bit address. But that does the I2C library for you.
Normally polling is not an I2C feature. Many devices have an identifier register, which can be read out.
BTW: Are you driving the part from 3.3V? It is a good ide to do so. I do not think that you will do damages, since there are voltage regulators on the board. But I am not sure if you will not break something either ;)
A good way to verify I2C communication is to read out a register which value you already know (e.g. a config register). By that you can verfy the communication.
Marcus this is Owen Bergen I am working on a electrical engineering project at WSU, I am using the code you provided on your blog.
However I don’t understand the int getColorGain() section of the code. I was specifically wondering what’s going on with the upperbox and lowerbox values and how to change them. I would really appriciate your help. Thanks
Basically it is a very efficient algorithm to find the perfect gain. Basically the whole range of gain is halfed. If the gain in the upper half of the range is better than this half is used, else the lower half of the range. Then this half-range is the new range and the algorithm is repeated until the upper and lower level meets.
It is easier to draw a picture than to understand this from code only. The basic principle is easy, the code not ;)
Greetings.
I’m in the same group as Owen. Is there a reason why you chose communicating with the sensor through the analog channels rather than the digital channels?
I do not understand your question. The whole communication is done using I2C – so digitally. The I2C port of the Arduino is located on the analogue inputs – so I actually lost two perfectly good analogue channels for I2C ;) – check the Atmega168 datasheet.
Is that what you meant?
Marcus, Could you help explain how integration time works, and how your program uses it? Is there any advantage to being able to change it?
Thanks
Hi,
As far as I have understood the datasheet the integration time works somehow like an exposure setting in a camera. Both, integration time and gain control how much light is needed for a certain output (e.g. 128).
I have not done any deeper analysis about this topic. If you want to try it you can easily adapt the source code so that the integration time is used to adapt to different light levels.
Hey Marcus, I just got the sensor and i’ve never interfaced something this complex before with my arduino. Could you tell me how to implement the code files that you provided in colormeter.zip? What else do i need to do to get a working sensor.
-Thanks
I think first of all you need time.
This color sensor is quite complex in regard of data readout. I had hevn’t done this myself yet. But a good thing is to understand the different gain settings and get an optimal setup for the color sensor, spitting out a single RGB value no matter what your gain settings are. For that you need to understand how all those numbers sum up.
Secondly you need a problem ;)
Third physical mount of the sensor is no no brainer too – since it must sit where you want to have the color readout.
The rest is a good mixture of creativity, patience, innovation and googling ;)
I have following questions about the color sensor ADJD-S371-Q999:
1. Could you please tell me what is the sampling rate of the color sensor ADJD-S371-Q999? Specially when we use I2C to read color data from sensor, how many samples can we read per second?
2. Do we need to attach the object to the color sensor in order to detect the color? or the color sensor detects color even if object is at some distance (e.g. 50 mm, 100 mm) apart?
3. Whether is it possible to detect color for a fast moving object? e.g. a ping-pong ball which freefalls at some distance (e.g. 50 mm) apart from the sensor?
Hi,
unfortunately I cannot answer your questions in full detail, but I will try:
1. It is completely unclear what the sampling rate is. I have not found it in the data sheet. There is an integration time setting for the various channels – something like exposuer. But I have not tested how fast that actually is. Since it is a CMOS sensor expect similar ranges as with camera CMOS sensors.
2. The color sensor has quite a wide viewing angle. So to measure a color you have to put the object directly in front of the sensor. This is what the datasheet recommends. You can of course measure the color of light by not putting anything on front of the sensor. If a red ball is flying by the sensor – it will be quite hard to determine that the image gets more red.
3. I think it is possible. Depends a bit on the lightning. But why do you want to use a COLOR sensor for it? do the ping pong balls have different colors? Perhaps it is usefull to come up with some sort of lens, that narrows the viewing angle.
That are the best answer I can offer.
Dear Markus,
Thanks for your explanation.
I would like to detect ping-pong balls of at most 3 or 4 colors (blue, red, white, geen). The choice of color is totally free and any color can be chosen. The ping pong balls have a diameter of 40 mm and while passing in front of the sensor (most probably at a distance of 5-10 mm) the speed of the ping-pong ball will be approx. 2 m/s (i.e. 2mm/ms) maximum. So ball will remain in front of the sensor for approx. 20 ms (considering the ball has diameter of 40 mm). If we take 10 ms just for safe side, now there are two possiblities that can affect the detection of color of ball.
1. Either the sampling rate of sensor is not fast enough.
2. I2C communication is slow enough to skip some of the samples which might result in loss.
Do you think with 10 ms as the period during which ball will be in front of the sensor (at a distance of 5-10 mm), the sensor might work?
Hi Markus,
Thanks for you replies. I just have got the color sensor and playing with it. However currently I am having problems. First of all when I configured the CTRL register to 0×1. After reading back, it returns 0×0. Also the RGB values that I am getting is always 1023 for all RGBC. Any idea what could be wrong. One thing to mention that I am using a power supply of approx. 2.7 volt. Is this enough?
Marcus, I have ported your code to C# for use with the Netduino. You can find it here: http://harfordhackerspace.org/2010/07/netduino-debut/
Happy Coding!
Marcus,
Thanks for the tutorial. I’m new to electronics…so still learning.
I’m using your code but I’m stuck.
In the serial monitor, I’m only receiving “Sending Calibration data …”
Nothing else after that.
Any ideas?
Thanks Randy
If you see this it is a goo hint that the I2C communication does not work. HEre is a short check list for these situations:
– Have you really connected the sensor correctly?
– Do you have pull upst for SCL & SDA?
– Are you sure you have the right I2C address in your sketch?
I reviewed the connections and it matches what you have at the beginning of your tutorial.
I’m using an Arduino Duemilanove.
I added a 10K pull up to SDA & SCL
The sketch includes the code at the beginning but I replaced “static uint8_t read_register(uint8_t register_name”) with the second example.
I could not get first section of code to compile. It would error on “(Wire.available()<1)”.
Not sure what you mean in your third item of your check list.
Hi Marcus!
I’m connecting to other replies – the work is great!
Is it possible to modify your code to use about 6 sensors with Arduino?
I’m new in electronics, so I’m sorry for so questions.
Thanks
Dmitry,
I am happy that my code is helpfull for you and that you are able to connect with others here! That’s how it is supposed to work.
Of course you can connect 6 or more sensors on one Arduino. Each ADJD has a sleep pin. So by theory you can connect them all in parallel, with only one sleep pin low and all other sleep pins high.
I hope that helps you.
Thanks! I will try.
Do I need to run ADJD confing functions when set them in run mode?
P.S. Sorry for my English
Marcus,
Thank you for sharing the code and providing explanations about this great colour sensor.
There are 3 methods for calibration:
• int getClearGain()
• int getColorGain()
• void calibrateChip() which calls getColorGain()
I’m presently facing two questions:
• What is the correct order to run the methods for calibration?
• During calibration, is an object, white or coloured, to be put in front of the sensor?
Do you plan developing the OFFSET procedure?
• offsets acquisition : write 02H to CTRL register—address 00H
• offset values at data registers—address 72H, 73H, 74H, 75H
• offset trimming: write 01H to CONFIG register—address 01H
Thank you and best regards,
A flowchart to explain the working code would have been very useful. I am writing a code for PIC in Basic.
“C” is not my cup of tea.
Thanks for the info anyways.
regards
Charudatt
You are right. That is a good idea. Unfortunately I am currently lacking a bit of time doing this flow chart. So I have to ask for some patience.
When I try to compile the code I get the error
‘I2C_ADDRESS’ was not declared in this scope.
The error first occurs in the line
” Wire.beginTransmission(I2C_ADDRESS);”
Has anyone ells had this problem before??
I will look into this.
I saw your projects and seems nice. I also have a project on adaptive color light,which senses an object color and lights an RGB bulb with the same color. I have a plan to use Atmega32 AVR. So what do you advise me especially on the color sensor part and its power circuit in order to get a high power for the LEDs.
Hi,
if you are using high power RGB LEDs I would take special care of the power circuitry. If it is ok that the ATMega runs at 8Mhz use 3.3V for the ATMega and the sensor.
For the LEDs you need more than 3.3V – even though it is technically working.
So I would design two power circuits. E.g. 5V or 4V for the LEDs and 3.3V for the digital part.
Be sure to have enough capacitance on your board.
Marcus
Thanks Marcus.
when I read about RGB, they tell me about using RGB driver,why I need it and how can design a poer circuit?
Thanks in advance
For SDA and SCL, is there a way to change with analog pins you use? I’m using a mini arduino instead, and well it would be easier to use different analog pins.
Ellen
It depends …
If you want to use the hardware I2C implementation there is no chance to change the I2C pins. But there ist always the opportunity to use use software based I2C.
With software I2C you can use any pin. But software I2C uses more resources (processing cycle wise).
Dear Marcus
Actually I have done my best to calibrate the colour sensor in other languages (MikroC and MikroBasic ) but I can not receive something like (255 0,0) on Character Lcd for pure red. I have traced your code to understand your equation and conversion after reading out but not so simple. my attempt is to recoding whole thing for PIC controllers but there are two major problems : 1) my sensor is Adjd-S311-CR999 instead of S371 and there is no enclosure between sensor and LED mounted by sparkfun. can you please help me?
Thanks Mate,
the main issue is I am a bit confused about the factors you have generated for outputs I am not sure these calculation works for LCD values correct me if i am wrong. the other problem is that Sparkfun will no longer sell ADJD-S371-QR999 and I have bought the ADJD-S311-CR999 which there is no barrier between on board LED and the sensor I don’t know why they have decided to remove the light enclosure. based on the application node I saw it is just one step of gain adjustment necessary but I found the adjustment for integration timers in your main loop can you explain about it.
Regards
Ali
damn keyboard, sorry I am sending you these comments by mobile “I wrote the application node released by Avago. it is application note”
Hi,
unfortunately I did not find anything regarding sampling speed in the data sheet. And currently I do not have enough time to play around with it. But let’s do the math:
I2C runs on 100kbit/s – transmitting 32 – 64 bit per readout (8 bit address, 8 bit register, than some bytes for the data). To be on the safe side (and to make it easy) we calculate with 100 bits. This gives us a sampling frequency of about 1000 samples per second, or one sample per second maximum. I do not think you can reach that maximum but it is a safe margin to get your 10ms frames.
I think you can tune the sensor to work with something like 5-10ms sampling frequency. It will be som fiddling, but with the help of a led blinking at a defined frequency you can achieve it.
I personaly think it is a 50/50 chance. The breakout board is quite cheap at sparkfun, so get one and play around with it. ;)
My source code plugin likes to mess up the source code. > must be >
Sorry for he inconvenience
{ 3 trackbacks }