Credits to SparkFun for datasheet and sample Arduino codes
Works by typing a command into the Serial Monitor, which is much better than the simpler code because this code does not continuously collect data and this code is better for debugging the petty details in the data.
This code may seem very complex, but it is because the .h file is not present to pre-define certain definitions. If you read the code carefully, more than 90% of it is defining certain variables and characteristics of the color sensor. With the .h file, it will be significantly easier to type code, but I personally think that the .h file spoils the users. The .h file does not help programmers learn how to truly use I2C devices.
/*
The hookup:
ADJD-S311 Breakout ------------- Arduino
----------------------------------------
LED ---------------------------Digital pin 2
3.3V -------------------------3.3V
GND -------------------------- GND
SCL -------------------------- A5
SDA -------------------------- A4
GND -------------------------- GND
SLP --------------------- Not connected
CLK --------------------- Not connected
*/
#include <Wire.h> // We use Wire.h to talk I2C to the sensor
// ADJD-S311's I2C address, don't change
#define ADJD_S311_ADDRESS 0x74
#define RED 0
#define GREEN 1
#define BLUE 2
#define CLEAR 3
// ADJD-S311's register list
#define CTRL 0x00
#define CONFIG 0x01
#define CAP_RED 0x06
#define CAP_GREEN 0x07
#define CAP_BLUE 0x08
#define CAP_CLEAR 0x09
#define INT_RED_LO 0xA
#define INT_RED_HI 0xB
#define INT_GREEN_LO 0xC
#define INT_GREEN_HI 0xD
#define INT_BLUE_LO 0xE
#define INT_BLUE_HI 0xF
#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
// Pin definitions:
int sdaPin = A4; // serial data, hardwired, can't change
int sclPin = A5; // serial clock, hardwired, can't change
int ledPin = 2; // LED light source pin, any unused pin will work
// RGB LED pins, should all be PWM output pins:
int redledPin = 9;
int greenledPin = 10;
int blueledPin = 11;
int rgbPins[3] = {redledPin, greenledPin, blueledPin};
// initial values for integration time registers
unsigned char colorCap[4] = {9, 9, 2, 5}; // values must be between 0 and 15
unsigned int colorInt[4] = {2048, 2048, 2048, 2048}; // max value for these is 4095
unsigned int colorData[4]; // This is where we store the RGB and C data values
signed char colorOffset[4]; // Stores RGB and C offset values
void setup()
{
pinMode(ledPin, OUTPUT); // Set the sensor's LED as output
digitalWrite(ledPin, HIGH); // Initially turn LED light source on
for (int i=0; i<3; i++)
{ // Set up the RGB LED pins
pinMode(rgbPins[i], OUTPUT);
digitalWrite(rgbPins[i], LOW);
}
Serial.begin(9600);
Wire.begin();
delay(1); // Wait for ADJD reset sequence
initADJD_S311(); // Initialize the ADJD-S311, sets up cap and int registers
/* First we'll see the initial values
getRGBC(); // Call this to put new RGB and C values into the colorData array
printADJD_S311Values(); // Formats and prints all important registers of ADJD-S311
*/
Serial.println("\nHold up a white object in front of the sensor, then press any key to calibrate...\n");
while(!Serial.available())
; // Wait till a key is pressed
Serial.flush();
Serial.println("\nCalibrating...this may take a moment\n");
calibrateColor(); // This calibrates R, G, and B int registers
calibrateClear(); // This calibrates the C int registers
calibrateCapacitors(); // This calibrates the RGB, and C cap registers
getRGBC(); // After calibrating, we can get the first RGB and C data readings
printADJD_S311Values(); // Formats and prints all important ADJD-S311 registers
Serial.println("\nAll values should be under 1000. If they're not, try calibrating again, or decreasing the ambient brightness somehow. ");
Serial.println("\nPress SPACE to read, \"c\" to calibrate, \"o\" to get offset, \"l\" to go to LED mode");
}
void loop()
{
while(!Serial.available())
; // Wait till something's pressed
char inKey = Serial.read();
if (inKey == ' ')
{ // If SPACE is pressed, get one reading and print it
getRGBC();
printADJD_S311Values();
}
else if (inKey == 'c')
{ // If c is pressed, calibrate int and cap registers, then get a reading and print it
Serial.println("\nCalibrating...my retarded child\n");
calibrateColor();
calibrateClear();
calibrateCapacitors();
getRGBC();
printADJD_S311Values();
}
else if (inKey == 'o')
{ // if o is pressed, get the offset values
getOffset();
Serial.print("Offset: \t ");
for (int i=0; i<4; i++)
{
Serial.print(colorOffset[i], DEC);
Serial.print("\t ");
}
Serial.println();
}
else if (inKey == 'l')
{ // if l is pressed, output color readings to an RGB LED
// We'll assume the sensor is calibrated
Serial.println("\nReplicating color on RGB LED, press any key to stop...\n");
Serial.println("\t Red \t Green \t Blue");
int averageData[3] = {0, 0, 0}; // We'll averaged the data
while(!Serial.available())
{ // Run continuously, until a key is pressed
for (int i=0; i<4; i++)
{ // Average the data four times
getRGBC(); // Get data values
for (int j=0; j<3; j++)
averageData[i] += colorData[i];
}
for (int i=0; i<3; i++)
averageData[i] /= 4; // data averaging
for (int i=0; i<3; i++)
{ // print out the data, and send it to the RGB LED
Serial.print("\t");
Serial.print(averageData[i], DEC);
analogWrite(rgbPins[i], map(averageData[i], 0, 1024, 0, 255));
}
Serial.println();
}
// When exiting this mode, turn off the RGB LED
for (int i=0; i<3; i++)
digitalWrite(rgbPins[i], LOW);
}
else
Serial.println("\nPress SPACE to read, \"c\" to calibrate, \"o\" to get offset, \"l\" to go to LED mode");
Serial.flush();
}
/* printADJD_S311Values() reads, formats, and prints all important registers
of the ADJD-S311.
It doesn't perform any measurements, so you'll need to call getRGBC() to print
new values.
*/
void printADJD_S311Values()
{
Serial.println("\t\t Red \t Green \t Blue \t Clear");
Serial.print("Data: \t\t ");
for (int i=0; i<4; i++)
{
Serial.print(colorData[i]);
Serial.print("\t ");
}
Serial.println();
Serial.print("Caps: \t\t ");
for (int i=0; i<4; i++)
{
Serial.print(readRegister(CAP_RED+i), DEC);
Serial.print("\t ");
}
Serial.println();
Serial.print("Int: \t\t ");
for (int i=0; i<4; i++)
{
Serial.print(readRegisterInt(INT_RED_LO+(i*2)), DEC);
Serial.print("\t ");
}
Serial.println();
Serial.print("Offset: \t ");
for (int i=0; i<4; i++)
{
Serial.print((signed char) readRegister(OFFSET_RED+i), DEC);
Serial.print("\t ");
}
Serial.println();
}
/* initADJD_S311() - This function initializes the ADJD-S311 and its
capacitor and integration registers
The vaules for those registers are defined near the top of the code.
the colorCap[] array defines all capacitor values, colorInt[] defines
all integration values.
*/
void initADJD_S311()
{
/*sensor gain registers, CAP_...
to select number of capacitors.
value must be <= 15 */
writeRegister(colorCap[RED] & 0xF, CAP_RED);
writeRegister(colorCap[GREEN] & 0xF, CAP_GREEN);
writeRegister(colorCap[BLUE] & 0xF, CAP_BLUE);
writeRegister(colorCap[CLEAR] & 0xF, CAP_CLEAR);
/* Write sensor gain registers INT_...
to select integration time
value must be <= 4096 */
writeRegister((unsigned char)colorInt[RED], INT_RED_LO);
writeRegister((unsigned char)((colorInt[RED] & 0x1FFF) >> 8), INT_RED_HI);
writeRegister((unsigned char)colorInt[BLUE], INT_BLUE_LO);
writeRegister((unsigned char)((colorInt[BLUE] & 0x1FFF) >> 8), INT_BLUE_HI);
writeRegister((unsigned char)colorInt[GREEN], INT_GREEN_LO);
writeRegister((unsigned char)((colorInt[GREEN] & 0x1FFF) >> 8), INT_GREEN_HI);
writeRegister((unsigned char)colorInt[CLEAR], INT_CLEAR_LO);
writeRegister((unsigned char)((colorInt[CLEAR] & 0x1FFF) >> 8), INT_CLEAR_HI);
}
/* calibrateClear() - This function calibrates the clear integration registers
of the ADJD-S311.
*/
int calibrateClear()
{
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
{
writeInt(INT_CLEAR_LO, half);
performMeasurement();
int halfValue = readRegisterInt(DATA_CLEAR_LO);
if (halfValue>1000)
upperBox=half;
else if (halfValue<1000)
lowerBox=half;
else
gainFound=1;
}
}
return half;
}
/* calibrateColor() - This function clalibrates the RG and B
integration registers.
*/
int calibrateColor()
{
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 {
writeInt(INT_RED_LO, half);
writeInt(INT_GREEN_LO, half);
writeInt(INT_BLUE_LO, half);
performMeasurement();
int halfValue = 0;
halfValue=max(halfValue, readRegisterInt(DATA_RED_LO));
halfValue=max(halfValue, readRegisterInt(DATA_GREEN_LO));
halfValue=max(halfValue, readRegisterInt(DATA_BLUE_LO));
if (halfValue>1000) {
upperBox=half;
}
else if (halfValue<1000) {
lowerBox=half;
}
else {
gainFound=1;
}
}
}
return half;
}
/* calibrateCapacitors() - This function calibrates each of the RGB and C
capacitor registers.
*/
void calibrateCapacitors()
{
int calibrationRed = 0;
int calibrationBlue = 0;
int calibrationGreen = 0;
int calibrated = 0;
//need to store detect better calibration
int oldDiff = 5000;
while (!calibrated)
{
// sensor gain setting (Avago app note 5330)
// CAPs are 4bit (higher value will result in lower output)
writeRegister(calibrationRed, CAP_RED);
writeRegister(calibrationGreen, CAP_GREEN);
writeRegister(calibrationBlue, CAP_BLUE);
// int colorGain = _calibrateColorGain();
int colorGain = readRegisterInt(INT_RED_LO);
writeInt(INT_RED_LO, colorGain);
writeInt(INT_GREEN_LO, colorGain);
writeInt(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 += readRegisterInt(DATA_RED_LO);
green += readRegisterInt(DATA_GREEN_LO);
blue += readRegisterInt(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;
}
}
/* writeInt() - This function writes a 12-bit value
to the LO and HI integration registers */
void writeInt(int address, int gain)
{
if (gain < 4096)
{
byte msb = gain >> 8;
byte lsb = gain;
writeRegister(lsb, address);
writeRegister(msb, address+1);
}
}
/* performMeasurement() - This must be called before
reading any of the data registers. This commands the
ADJD-S311 to perform a measurement, and store the data
into the data registers.*/
void performMeasurement()
{
writeRegister(0x01, 0x00); // start sensing
while(readRegister(0x00) != 0)
; // waiting for a result
}
/* getRGBC() - This function reads all of the ADJD-S311's
data registers and stores them into colorData[]. To get the
most up-to-date data make sure you call performMeasurement()
before calling this function.*/
void getRGBC()
{
performMeasurement();
colorData[RED] = readRegisterInt(DATA_RED_LO);
colorData[GREEN] = readRegisterInt(DATA_GREEN_LO);
colorData[BLUE] = readRegisterInt(DATA_BLUE_LO);
colorData[CLEAR] = readRegisterInt(DATA_CLEAR_LO);
}
/* getOffset() - This function performs the offset reading
and stores the offset data into the colorOffset[] array.
You can turn on data trimming by uncommenting out the
writing 0x01 to 0x01 code.
*/
void getOffset()
{
digitalWrite(ledPin, LOW); // turn LED off
delay(10); // wait a tic
writeRegister(0x02, 0x00); // start sensing
while(readRegister(0x00) != 0)
; // waiting for a result
//writeRegister(0x01, 0x01); // set trim
//delay(100);
for (int i=0; i<4; i++)
colorOffset[i] = (signed char) readRegister(OFFSET_RED+i);
digitalWrite(ledPin, HIGH);
}
/* I2C functions...*/
// Write a byte of data to a specific ADJD-S311 address
void writeRegister(unsigned char data, unsigned char address)
{
Wire.beginTransmission(ADJD_S311_ADDRESS);
Wire.write(address);
Wire.write(data);
Wire.endTransmission();
}
// read a byte of data from ADJD-S311 address
unsigned char readRegister(unsigned char address)
{
unsigned char data;
Wire.beginTransmission(ADJD_S311_ADDRESS);
Wire.write(address);
Wire.endTransmission();
Wire.requestFrom(ADJD_S311_ADDRESS, 1);
while (!Wire.available())
; // wait till we can get data
return Wire.read();
}
// Write two bytes of data to ADJD-S311 address and addres+1
int readRegisterInt(unsigned char address)
{
return readRegister(address) + (readRegister(address+1)<<8);
}