Saturday, January 22, 2011

RGB Color Sensor on Arduino

The next board I want to show you is the ADJD-S371 Color Light Sensor Evaluation Board from sparkfun. It emits light and analyses the reflected color spectrum. The board can be controlled via I2C. The sleep and xclk pins were not used in this example. I found a really nice tutorial describing how to connect the sensor to the Arduino. The example also provided the code to communicate with the sensor. I only had to change minor parts to fit my needs.

My basic setup looks a little bit different because I used a full color rgb LED with six pins. Two for ground, one for red, one for green and two for blue. I also used another schematic this time. What you see here is a software developed by the University of Applied Sciences Potsdam, here in Germany. The software is called Fritzing. You can design your circuit on a protoboard, convert it into a circuit diagram and even into a PCB layout for later manufacturing. It's really nice and easy to use. Thanks for the tip Darsh!

Here is my setup:




As already mentioned I only changed the code from the tutorial a little bit to fit to my rgb LED.
//Configure gain here
//Higher numbers = less sencitive
// 0x00 through 0x0f
int redGain = 0x03;
int greenGain = 0x0;
int blueGain = 0x0f;
int clearGain = 0x01;

//Include the I2C Arduino library
#include <Wire.h>

//7 bit I2C address of this sensor
#define I2C_ADDRESS 0x74

#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

//PWM Digital Pin layout for RGB LED
#define greenLED 6
#define blue1LED 9
#define blue2LED 10
#define redLED 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();

  // sensor gain setting (Avago app note 5330)
  // CAPs are 4bit (higher value will result in lower output)
  set_register(REG_CAP_RED, redGain);
  set_register(REG_CAP_GREEN, greenGain);
  set_register(REG_CAP_BLUE, blueGain);
  set_register(REG_CAP_CLEAR, clearGain);

  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);

  int m=2000; //bigger anyway
  m=min(m,red);
  m=min(m,green);
  m=min(m,blue);

  //Serial.print("m - ");
  //Serial.println(m);

  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() {

  //set rgb LED off
  analogWrite(redLED, 0);
  analogWrite(greenLED, 0);
  analogWrite(blue1LED, 0);
  analogWrite(blue2LED, 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 ;i ++) {
    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;

  //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 redValue = (float)red*redFactor;
  float greenValue = (float)green*greenFactor;
  float blueValue = (float)blue*blueFactor;

  //Print the recognized color to the Serial Monitor and light up rgb LED accordingly.
  if(redValue > 100 && greenValue > 100 && blueValue > 100) {
    Serial.println("White or Clear");
    analogWrite(redLED, 255);
    analogWrite(greenLED, 255);
    analogWrite(blue1LED, 255);
    analogWrite(blue2LED, 255);
  }
  else if(redValue > 100 && greenValue > 100) {
    Serial.println("Yellow");
    analogWrite(redLED, 255);
    analogWrite(greenLED, 255);
  }
  else if(redValue > 100 && blueValue > 100) {
    Serial.println("Magenta");
    analogWrite(redLED, 255);
    analogWrite(blue1LED, 255);
    analogWrite(blue2LED, 255);
  }
  else if(greenValue > 100 && blueValue > 100) {
    Serial.println("Cyan");
    analogWrite(greenLED, 255);
    analogWrite(blue1LED, 255);
    analogWrite(blue2LED, 255);
  }
  else if(redValue > 100) {
    Serial.println("Red");
    analogWrite(redLED, 255);
  }
  else if(greenValue > 100) {
    Serial.println("Green");
    analogWrite(greenLED, 255);
  }
  else if(blueValue > 100) {
    Serial.println("Blue");
    analogWrite(blue1LED, 255);
    analogWrite(blue2LED, 255);
  }

  //Print reflected light spectrum to the Serial Monitor.
  Serial.print("red: ");
  Serial.println(redValue);

  Serial.print("green: ");
  Serial.println(greenValue);

  Serial.print("blue: ");
  Serial.println(blueValue);
  Serial.println("");

  //hold it for one second
  delay(1000);
}

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;
}
This is a demo of the sensor in action. It had a really hard time recognizing the green spectrum. It may have been my room lighting though.

1 comment:

  1. san nyo po nabili ung ADJD-S371-Q999 ? badly needed po for thesis. thanks.

    ReplyDelete