May 18, 2009

Sketch to calibrate SEN-08663

Got my hands on a ADJD-S371 on a breakout board from Sparkfun. The code below can be used to calibrate it. The most up-to-date version of the code can be found at my git repository under colour_sensor_calibration.

#include <Wire.h>
/* Calibrates the sensor to get white balance. Pin 2 should be connected
* to LED on the breakout board's LED pin. Calibration is done by placing the
* breakout board inside a pin pong ball, and using the built-in LED for
* illumination.
* Amount of light is measured by charging N capacitors for time X then
* reading off the voltage. (Conjecture)
* N is controlled by CAP_XXX
* T is controlled by INT_XXX
* Calibration is done by adjusting the integration time. No real reason.
int _slave_id = 0x74;
int _LED_pin = 2;

uint8_t read_register(uint8_t addr)
i2c_send(_slave_id, &addr, 1);
return i2c_read(_slave_id);

void write_register_int(uint8_t addr, int data)
write_register_multibyte(addr, (uint8_t*)&data, 2);

/* write data[i] = register+i */
void write_register_multibyte(uint8_t addr, uint8_t* data, uint8_t bytes)
for (int i = 0; i < bytes; ++i)
write_register(addr+i, data[i]);

void write_register(uint8_t addr, uint8_t data)
uint8_t bytes[] = {addr, data};
i2c_send(_slave_id, bytes, 2);

uint8_t i2c_read(uint8_t id)
Wire.requestFrom(_slave_id, 1);
for(int i = 0; i<10 && !Wire.available(); ++i, delay(10));
if (!Wire.available())
return 11;
return Wire.receive();


void i2c_send(uint8_t id, uint8_t * data, uint8_t len)
for(int i = 0; i < len; ++i)

#define CTRL 0x00
#define CONFIG 0x01

#define CAP_RED 0x06
#define CAP_GREEN 0x07
#define CAP_BLUE 0x08

#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 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

int read_colour(uint8_t low_addr)
int lo = read_register(low_addr);
int hi = read_register(low_addr+1);

return lo|(hi<<8);

int red_integration_time = 2048;
int green_integration_time = 2048;
int blue_integration_time = 2048;

void set_integration_times(int red, int green, int blue)
write_register_int(INT_RED_LO, red);
write_register_int(INT_GREEN_LO, green);
write_register_int(INT_BLUE_LO, blue);

void setup()
pinMode(_LED_pin, OUTPUT);
digitalWrite(_LED_pin, HIGH);

Wire.begin(); // join i2c bus (address optional for master)
Serial.println("Setting up...");

// datasheet says, wait 10us for hardware reset, so lets wait 1000

// gain setup
write_register(CAP_RED, 0x08);
write_register(CAP_GREEN, 0x08);
write_register(CAP_BLUE, 0x08);


// ask for colour data and offset
write_register(CTRL, 0x01);

void loop()
if (read_register(CTRL))

int red, green, blue;
red = read_colour(DATA_RED_LO);
green = read_colour(DATA_GREEN_LO);
blue = read_colour(DATA_BLUE_LO);

Serial.print("red: ");Serial.println(red);
Serial.print("green: ");Serial.println(green);
Serial.print("blue: ");Serial.println(blue);

Serial.print("red_int: ");Serial.println(red_integration_time);
Serial.print("green_int: ");Serial.println(green_integration_time);
Serial.print("blue_int: ");Serial.println(blue_integration_time);

// have to calibrate against blue, because LED has a blue bias otherwise
// it would look like blue has high gain than it does
float P = 1;
int reference = blue;
red_integration_time += (reference - red)*P;
green_integration_time += (reference - green)*P;
blue_integration_time += (reference - blue)*P;

// set the new integration times

// ask for colour data again
write_register(CTRL, 0x01);