Complex Color Sensor Code with Serial Command Capability

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);
}
 
start/classes/principlesofdesign/arduinogames/complexcolorcode_withouthfile.txt · Last modified: 2012/05/08 01:04 by ggo
 
Except where otherwise noted, content on this wiki is licensed under the following license:CC Attribution-Noncommercial-Share Alike 3.0 Unported
Recent changes RSS feed Donate Powered by PHP Valid XHTML 1.0 Valid CSS Driven by DokuWiki