The project works as proposed.
You place a colored gel over the light sensor which takes a transmissive reading and outputs the color.
This whole project is about calibration.
Input:
•The light must be balanced. In testing I used a bright "white" LED. left uncorrected the result was a green output (after the output had been calibrated...more on this in a bit). The LED was actually quite Green and Warm. I used glass Color Correction Filters over the lens. An 82B and a 05 Magenta. This brought the light to around 5450 Kelvin.
Once the light was balanced. I set to mapping each sensor to it's high and low for each Primary R,G, and B response. Using these primaries I could find the maximum saturation level of the sensor. The only caveat here though is that the Lee Filters are calibrated for maximum saturation at 6774K which is "cool" daylight. So it was the maximum minus the mired shift to 5500K.
r255= (int)rv; //Casting float to an INT
g255= (int)gv; //Casting float to an INT
b255= (int)bv; //Casting float to an INT
rmap= map(r255,142,172,0,255)*.8;
gmap= map(g255,85,185,0,255);
bmap= map(b255,74,255,0,255);
Output:
Once these values were identified. I mapped them to the visible range of PWM.
Red was multiplied by a value of .8 This code being a appropriated from Adam Meyer of RISD, I was forced to handle variables that I don't know how to properly manipulate yet. The Casting of the float to INT was such an instance of my inexperience forcing me to simplify. I was unable to figure out how to print the float value. Even though the Arduino reference said that the truncated value would be print inside of a serial.print() it was a no go.
This simplification must be addressed at the input level.
Carrying on. Once the variables were mapped to the stable and color corrected brightness, the device worked with minimum flaw. Green being the only color which was produced somewhat inconsistently. I am unsure as to why this might be.
Color Test
The original code was from Adam Meyer Many thanks to him for getting me started.
CODE:
#include
#define I2C_ADDRESS 0x74 // 7bit
#define REG_CAP_RED 0x06
#define REG_CAP_GREEN 0x07
#define REG_CAP_BLUE 0x08
#define REG_CAP_CLEAR 0x09
#define REG_INT_RED_LO 0x0A
#define REG_INT_RED_HI 0x0B
#define REG_INT_GREEN_LO 0x0C
#define REG_INT_GREEN_HI 0x0D
#define REG_INT_BLUE_LO 0x0E
#define REG_INT_BLUE_HI 0x0F
#define REG_INT_CLEAR_LO 0x10
#define REG_INT_CLEAR_HI 0x11
#define REG_DATA_RED_LO 0x40
#define REG_DATA_RED_HI 0x41
#define REG_DATA_GREEN_LO 0x42
#define REG_DATA_GREEN_HI 0x43
#define REG_DATA_BLUE_LO 0x44
#define REG_DATA_BLUE_HI 0x45
#define REG_DATA_CLEAR_LO 0x46
#define REG_DATA_CLEAR_HI 0x47
//define the output pins for the external RGB LED
#define groundPin 8
#define redPin 9
#define greenPin 10
#define bluePin 11
float redFactor=1;
float blueFactor=1;
float greenFactor=1;
//initial darkLevel;
int calibrationDarkness = 0;
byte calibrationRed = 5;
byte calibrationGreen = 5;
byte calibrationBlue = 5;
void setup(void)
{
Serial.begin(9600);
Wire.begin();
pinMode(groundPin, OUTPUT);
pinMode(redPin, OUTPUT);
pinMode(greenPin, OUTPUT);
pinMode(bluePin, OUTPUT);
//pinMode(ledPin, OUTPUT);
//pinMode(clearCalPin, INPUT);
// sensor gain setting (Avago app note 5330)
// CAPs are 4bit (higher value will result in lower output)
set_register(REG_CAP_RED, 0x05);
set_register(REG_CAP_GREEN, 0x05);
set_register(REG_CAP_BLUE, 0x01);
set_register(REG_CAP_CLEAR, 0x01);
//calibrate led
digitalWrite(groundPin,LOW);// this was a rig that is not advisable.
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);// show no color on these pins
digitalWrite(bluePin,0);
digitalWrite(greenPin,0);
int m=2000; //bigger anyway
m=min(m,red);// min means whichever one is less
m=min(m,green);
m=min(m,blue);
Serial.print("m - ");//print out the smallest value of Red Green or Blue
Serial.println(m);
/* This next section mimics white balance */
redFactor=((float)m*255.0)/(1000*(float)red);
greenFactor=((float)m*255.0)/(1000*(float)green);
blueFactor=((float)m*255.0)/(1000*(float)blue);
}
void loop() {
//digitalWrite(redPin,0);
//digitalWrite(bluePin,0);
//digitalWrite(greenPin,0);
int clearGain = getClearGain();
set_gain(REG_INT_CLEAR_LO,clearGain);
int colorGain = getColorGain();
set_gain(REG_INT_RED_LO,colorGain);
set_gain(REG_INT_GREEN_LO,colorGain);
set_gain(REG_INT_BLUE_LO,colorGain);
//reset the RGB (and clear) values
int cc = 0;
int red=0;
int green=0;
int blue=0;
// Take 4 samples, and add them together.
for (int i=0; i<4>
performMeasurement();
cc +=get_readout(REG_DATA_CLEAR_LO);
red +=get_readout(REG_DATA_RED_LO);
green +=get_readout(REG_DATA_GREEN_LO);
blue +=get_readout(REG_DATA_BLUE_LO);
}
//now, divide the totals for each by 4 to get their average.
cc/=4;
red /=4;
green /=4;
blue /=4;
//print out the findings
Serial.print("Readout @");
Serial.print(clearGain);
Serial.print("/");
Serial.print(colorGain);
Serial.print(" c=");
Serial.print(cc);
Serial.print(", r=");
Serial.print(red);
Serial.print(", g=");
Serial.print(green);
Serial.print(", b=");
Serial.println(blue);
//take the values mesured from above, and multiply them with the factors to
//find out what value should be sent to the external RGB LED to reproduce this color
float rv = (float)red*redFactor;
float gv = (float)green*greenFactor;
float bv = (float)blue*blueFactor;
//As we can only output an integer, convert each value.
//These variables Modified by Brian Jones for transmissive readings.
int r = red;
int g = green;
int b = blue;
int r255;
int g255;
int b255;
int rmap;
int gmap;
int bmap;
r255= (int)rv; //Casting float to an INT
g255= (int)gv; //Casting float to an INT
b255= (int)bv; //Casting float to an INT
rmap= map(r255,142,172,0,255)*.8;
gmap= map(g255,85,185,0,255);
bmap= map(b255,74,255,0,255);
rmap= max(rmap,0);
gmap= max(gmap,0);
bmap= max(bmap,0);
//output values to the external RGB LED
analogWrite(redPin, rmap);
analogWrite(greenPin,gmap);
analogWrite(bluePin,bmap);
//output what values where sent
Serial.print(" r255=");
Serial.print(r255);
Serial.print(", g255=");
Serial.print(g255);
Serial.print(", b255=");
Serial.println(b255);
Serial.print("Replay : rmap=");
Serial.print(rmap);
Serial.print(", gmap=");
Serial.print(gmap);
Serial.print(", bmap=");
Serial.println(bmap);
Serial.println("---------------");
//hold it for one second
//delay(1000);
}
/* the next comment is sort function Most likely ---confirm--- so this will set the "exposure" for next loop */
int getClearGain() {
int gainFound = 0;
int upperBox=4096;
int lowerBox = 0;
int half;
while (!gainFound) {
half = ((upperBox-lowerBox)/2)+lowerBox;
if (half == lowerBox) { //no further halfing possbile
break; //no further halfing possbile
} 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 {
break; //no further halfing possbile
}
}
}
return half;
}
int getColorGain() {
int gainFound = 0;
int upperBox=4096;
int lowerBox = 0;
int half;
while (!gainFound) {
half = ((upperBox-lowerBox)/2)+lowerBox;
if (half==lowerBox) { //no further halfing possbile
break; // gain found
} 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 {
break; // gain found
}
}
}
return half;
}
void performMeasurement() {
set_register(0x00,0x01); // start sensing
while(read_register(0x00) != 0) {
// waiting for a result
}
}
int get_readout(int readRegister) {
return read_register(readRegister) + (read_register(readRegister+1)<<8);
}
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);
}
}
void set_register(unsigned char r, unsigned char v){
Wire.beginTransmission(I2C_ADDRESS);
Wire.send(r);
Wire.send(v);
Wire.endTransmission();
}
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 Future:
Continuing on, I need to figure out how to calibrate the sensor to a wider range of color temperature. I need to establish a standard Kelvin value from which all color calculation outputs are made. As of now the sensor reads all temperatures as "normal" and the sensor values are all calculated the same. Optimally they should be weighted to reflect the cast of the light to give the sensors a proper read and that should be adjusted for output.
This would be going from a non-linear space to a linear space and then back to non-linear for output. The output non-linear. Food for thought no doubt.