Color sensor with Arduino

Hi,
Am completly stuck with reading data from the sensor as the slave to my arduino :'(.Is there anyone who has worked with this kind of color sensor to assist me with a sketch that can input/read data from the sensor?
Any comment is welcomed.
Thanx for your time.

Someone help! Someone to help me change the following PIC code to work on my arduino environment. I have gotten the PIC code from http://www.robotshop.ca/content/ZIP/sfe-adjd-s371-v11-pic-code.zip.
The code is:

/*
    3-17-08
    Nathan Seidle
    nathan@sparkfun.com
    Copyright Spark Fun Electronics© 2008
    
    Written for a PIC 16F88 running SparkFun bootloader at internal 8MHz.
    Compiled with CC5x compiler.

    Basic software I2C interactions with the ADJD-S371-Q999 color
    sensor. Sensor works great but requires calibration. This firwmare
    is only meant to demonstrate simple command interface.
    
    The software I2C routines are heavily tested and seem to work well
    with many I2C devices.
*/
#define Clock_8MHz
#define Baud_9600

#include "c:\Global\Code\C\16F88.h"  // device dependent interrupt definitions

#pragma origin 4

#include "c:\Global\Code\Pics\Code\Delay.c"   //Standard delays
#include "c:\Global\Code\Pics\Code\stdio.c"   //Software based Basic Serial IO

#define STATUS_LED PORTB.3

void boot_up(void);

#define WRITE_sda() TRISB = TRISB & 0b.1011.1111 //SDA must be output when writing
#define READ_sda()  TRISB = TRISB | 0b.0100.0000 //SDA must be input when reading - don't forget the resistor on SDA!!

#define SCL PORTB.7
#define SDA PORtB.6

#define I2C_DELAY   1

#define ACK     1
#define NO_ACK  0

#define DEVICE_WRITE    0xE8 //Default ADJD-S371 I2C address - write
#define DEVICE_READ     0xE9 //Default ADJD-S371 I2C address - read

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

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

void adjd_s371_read(void);
void adjd_init(void);

void i2c_ack_polling(uns8 device_address);
void i2c_start(void);
void i2c_stop(void);
uns8 i2c_read_byte(void);
bit i2c_send_byte(uns8 out_byte);
uns8 read_register(uns8 register_name);
void write_register(uns8 register_name, uns8 register_value);

void main(void)
{
    uns8 choice;
    
    boot_up();
        
    printf("\n\r\n\ADJD Testing\n\r", 0);

    adjd_init();
    
    while(1)
    {
        adjd_s371_read();
        //delay_ms(10);
    }
    
    while(1);

}//End Main

void boot_up(void)
{
    //OSCCON = 0b.0111.0000; //Setup internal oscillator for 8MHz
    //while(OSCCON.2 == 0); //Wait for frequency to stabilize

    //Setup Ports
    ANSEL = 0b.0000.0000; //Turn off A/D

    PORTA = 0b.0000.0000;
    TRISA = 0b.0000.0000;

    PORTB = 0b.0001.0000;
    TRISB = 0b.0000.0100;   //0 = Output, 1 = Input RX on RB2

    //Setup the hardware UART module
    //=============================================================
    SPBRG = 51; //8MHz for 9600 inital communication baud rate
    //SPBRG = 59; //9.216MHz for 9600 inital communication baud rate
    //SPBRG = 4; //9.216MHz for 115200 inital communication baud rate
    //SPBRG = 129; //20MHz for 9600 inital communication baud rate

    TXSTA = 0b.0010.0100; //8-bit asych mode, high speed uart enabled
    RCSTA = 0b.1001.0000; //Serial port enable, 8-bit asych continous receive mode
    //=============================================================

}

void adjd_init(void)
{
    write_register(CAP_RED, 0x05);
    write_register(CAP_GREEN, 0x05);
    write_register(CAP_BLUE, 0x05);
    write_register(CAP_CLEAR, 0x05);
    
    write_register(INT_RED_LO, 0xC4);
    write_register(INT_RED_HI, 0x09);
    write_register(INT_GREEN_LO, 0xC4);
    write_register(INT_GREEN_HI, 0x09);
    write_register(INT_BLUE_LO, 0xC4);
    write_register(INT_BLUE_HI, 0x09);
    write_register(INT_CLEAR_LO, 0xC4);
    write_register(INT_CLEAR_HI, 0x09);
}

//Init the sensor and read out the humidity and temperature data
void adjd_s371_read(void)
{
    uns8 response;
    uns16 red, green, blue, clear;

    //Check ability to read CAP_RED (should be 15)
    //response = read_register(CAP_RED);
    //printf("CAP_RED:%d ", response);

    write_register(0x00, 0b.0000.0001); //Get sensor reading
    
    uns8 i = 0;
    while(1)
    {
        response = read_register(0x00);
        if (response == 0) break;
        i++;
    }
    printf("i=%d ", i);
        
    //Red
    red.low8 = read_register(DATA_RED_LO);
    red.high8 = read_register(DATA_RED_HI);

    //Green
    green.low8 = read_register(DATA_GREEN_LO);
    green.high8 = read_register(DATA_GREEN_HI);

    //Blue
    blue.low8 = read_register(DATA_BLUE_LO);
    blue.high8 = read_register(DATA_BLUE_HI);

    //Clear
    clear.low8 = read_register(DATA_CLEAR_LO);
    clear.high8 = read_register(DATA_CLEAR_HI);
    

    printf("R(%h) ", red);
    printf("G(%h) ", green);
    printf("B(%h) ", blue);
    printf("C(%h)", clear);
    
    printf("\r", 0);
}

//Reads a register from LIS
uns8 read_register(uns8 register_name)
{
    uns8 in_byte;
    
    i2c_ack_polling(DEVICE_WRITE);

    i2c_start();
    i2c_send_byte(DEVICE_WRITE); 
    i2c_send_byte(register_name); //Write register address   
    //i2c_stop();

    i2c_start(); //Repeat start (SR)
    i2c_send_byte(DEVICE_READ); //Now ask the IC to report on the last command
    in_byte = i2c_read_byte();
    i2c_stop();
        
    return(in_byte);
}

//Write to a register in LIS
void write_register(uns8 register_name, uns8 register_value)
{
    i2c_ack_polling(DEVICE_WRITE);

    i2c_start();
    i2c_send_byte(DEVICE_WRITE); 
    i2c_send_byte(register_name); //Write register address
    i2c_send_byte(register_value); //Write data
    i2c_stop();

    //Return nothing
}

//Software I2C Routines
//====================================
void i2c_ack_polling(uns8 device_address)
{
    while(1)
    {
        i2c_start();
        if (i2c_send_byte(device_address) == ACK) break;
    }
    i2c_stop();
}

void i2c_start(void)
{
    WRITE_sda();
    SDA = 1;
    delay_us(I2C_DELAY);

    SCL = 1;
    delay_us(I2C_DELAY);

    SDA = 0;
    delay_us(I2C_DELAY);
}

//The I2C Clock has a minimum of 2us high time and 2us low time
void i2c_stop(void)
{
    SCL = 0;
    delay_us(I2C_DELAY);

    WRITE_sda();

    SDA = 0;
    delay_us(I2C_DELAY);

    SCL = 1;
    delay_us(I2C_DELAY);

    SDA = 1;
    delay_us(I2C_DELAY);
}

//The I2C Clock has a minimum of 2us high time and 2us low time
//8MHz = 0.5us per instruction
uns8 i2c_read_byte(void)
{
    int j, in_byte;

    SCL = 0;

    READ_sda();

    for(j = 0 ; j < 8 ; j++)
    {
        SCL = 0;
        delay_us(I2C_DELAY);

        SCL = 1;
        delay_us(I2C_DELAY);

        in_byte = rl(in_byte);
        in_byte.0 = SDA;
    }

    //Send 9th bit acknowledge
/*
    For single byte reads, there is NMAK or no master acknowledge
    SCL = 0;
    WRITE_sda();
    SDA = 0;
    delay_us(I2C_DELAY);
    SCL = 1;

    delay_us(I2C_DELAY);
    SCL = 0;

    delay_us(I2C_DELAY);
    SDA = 1;
    while(1);
*/
    return(in_byte);
}

//The I2C Clock has a minimum of 2us high time and 2us low time
//8MHz = 500ns per instruction
bit i2c_send_byte(uns8 out_byte)
{
    uns8 i;

    WRITE_sda();

    for(i = 0 ; i < 8 ; i++)
    {
        SCL = 0;
        delay_us(I2C_DELAY);

        out_byte = rl(out_byte);
        SDA = Carry;
        delay_us(5);

        SCL = 1;
        delay_us(I2C_DELAY);
    }

    //Read ack
    SCL = 0;
    delay_us(I2C_DELAY);

    READ_sda();

    SCL = 1;
    delay_us(I2C_DELAY);

    //Wait for IC to acknowledge
    for(i = 0 ; i < 255 ; i++)
        if(SDA == 0) break;

    SCL = 0;
    delay_us(I2C_DELAY);

    if (i == 255) return(NO_ACK);
    
    return(ACK);
}
//====================================

I will be greatful for any help offered.

I don't know the sensor device, but the PIC code has a whole lot of device initialisation in adjd_init that I don't see in your code.
Also, on a quick scan of the datasheet, the PIC code and your software, I don't see where you're setting GSSR, which I'm guessing is required to initiate a reading.
If you look at adjd_s371_read, you'll see that you need to write the GSSR bit in the control register before you can read back anything.
Also, between yours and the PIC code, the device address looks different.
The datasheet address is 0x74 (0xE8/9), but yours is (0x4 << 3 == 0x20)
I think somewhere you may be confusing I2C device addresses and register addresses, but it's late and I'm too tired to read all of it.
Note that some I2C implementations do not allow you to read all registers; some may be write only.

HTH

Hey there, I am having the same problem. I found this code:

#include <Wire.h>
 
const int serialSpeed=9600;
int ledPin = 13;    // the light
 
//ADJD Settings
#define ADJD 0x74
 
#define CAP_RED 0x06
#define CAP_GREEN 0x07
#define CAP_BLUE 0x08
#define CAP_CLEAR 0x09
 
#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 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
 
 
void setup() {
  pinMode(ledPin, OUTPUT);  // declare the ledPin as an OUTPUT  
  Serial.begin(serialSpeed);           // set up Serial library at 9600 bps
  Wire.begin();
  Serial.println("Sending Calibration data ... ");
  adjd_init();
  Serial.println("Calibration data sent. "); 
}
 
void loop() {
  digitalWrite(ledPin,HIGH);
  read_register(DATA_RED_LO);
  digitalWrite(ledPin,LOW);
 
  delay(4000);
}
 
static void adjd_init()
{
  write_register(CAP_RED, 0x05);
  write_register(CAP_GREEN, 0x05);
  write_register(CAP_BLUE, 0x05);
  write_register(CAP_CLEAR, 0x05);
 
  write_register(INT_RED_LO, 0xC4);
  write_register(INT_RED_HI, 0x09);
  write_register(INT_GREEN_LO, 0xC4);
  write_register(INT_GREEN_HI, 0x09);
  write_register(INT_BLUE_LO, 0xC4);
  write_register(INT_BLUE_HI, 0x09);
  write_register(INT_CLEAR_LO, 0xC4);
  write_register(INT_CLEAR_HI, 0x09);
} 
 
void read_adjd_offset() {
}
 
static void write_register(uint8_t register_name, uint8_t register_value)
{
  Wire.beginTransmission(ADJD);
  Wire.send(register_name);
  Wire.send(register_value);
  Wire.endTransmission();
}
 
static uint8_t read_register(uint8_t register_name)
{
  Wire.beginTransmission(ADJD);
  Wire.send(register_name);
  Wire.endTransmission();
  Wire.requestFrom(ADJD,2);
  Serial.print("read:");
  while (Wire.available()<1) {
    Serial.print(".");
  }
  int result = Wire.receive();
  Serial.println(result);
  return 0;
}

but I can not make any sense of it. I don't totally understand I2C or the commands used in this code. when i run this program, I get just zeros in the serial monitor.

got the code here http://interactive-matter.org/2008/08/tinkering-with-adjd-s371-q999/

I have also tried using the code but get zero's too. Am worried wheather the code is setting the optimum digital values for the sensor.
Anyone who has worked with this kind of sensor, Please help us out! Maybe with a simple sketch that has worked for you.
Thanks for your time.

From the horse's mouth:

http://www.avagotech.com/docs/AV02-0359EN

"Sensor digital values can be acquired by writing 01H to CTRL register (address 00H). Read CTRL register. When the value in CTRL register is 00H, sensor digital values are acquired in sensor sample data registers."

From the PIC example above:

 write_register(0x00, 0b.0000.0001); //Get sensor reading

    uns8 i = 0;
    while(1)
    {
        response = read_register(0x00);
        if (response == 0) break;
        i++;
    }
    printf("i=%d ", i);

    //Red
    red.low8 = read_register(DATA_RED_LO);
    red.high8 = read_register(DATA_RED_HI);

Hi AWOL,
Thanks for your sketch and time. Am working on it to see whether it will work for me. I will keep you posted on my progress.

thanks, we'll see.. I think the problem here is I don't really get I2C

Kubia
This bit is wrong in your code:-
// I2C device address is 0 1 0 0 A2 A1 A0
#define SENSOR_ADDRESS (0x4 << 3 | 0x0)
It should be
// I2C device address is 0x74
#define SENSOR_ADDRESS 0x74

It's in the data sheet page 10.

Then you need to look at page 18 to define what commands you want to send to it. Here you are better off reading the application note. Page 3 of this note tells you what you have to do in order to read the values.
However, all you code does is request two bytes from the I2C device without setting up any registers.

You need to:-

Sensor digital values can be acquired by writing 01H to CTRL register (address 00H). Read CTRL register. When the value in CTRL register is 00H, sensor digital values are acquired in sensor sample data registers.

Then you need to read the sample registers.

All this assumes that you haven't set up the sensor for offset and span so I don't know how useful the results will be with out this. Again the application notes describe how this should be done.

I am sorry not to be able to give you the code but I do not have one of these sensors (yet).
If it is any help I do plan to use four of these sensors in an up coming project so I will have code available in a month or so.

Just done a web search and come up with this:-

OMG thank you, that code works

thanks thanks thanks

Hi Grumpy_Mike,
Thanx for the code and the link. Am working on it and atleast i can get some reading from the sensor.The problem is only that the reading seems to be abit unstable i.e. when the sensor reflects from a red material the reading keep on varying again clear channel seems to have more output even when the object is 0mm to the sensor.
But all the same this is a good progress. With time am sure we shall have a concete sketch.
Thanks for your time and info'.

Hi sardonicMath,
Nice to hear of your success, this thing had almost proven inpracticle.
How is your sensor behaving(i.e. the reading your are getting)? Are using the setup in the datasheet http://www.robotshop.ca/content/PDF/sfe-av02-0314-datasheet.pdf ? Again are you using this code Make: DIY Projects and Ideas for Makers |?

The code is perfect with simple customization and modification. This has been one of the most testing practical i have faced since started researching on robots.

Sir Grumphy_Mike! receive my lots of thanks for the time your spent and the info' your offered.
Am perfecting it out now to increase its efficiancy and later the interfacing.

Seen this?

http://interactive-matter.org/2008/08/tinkering-with-adjd-s371-q999/

yeah, it doesn't work

Just a note, if others have problems with this:
The wiring is super-simple, as all the infrastructure like pull-ups is already integrated on the Sparkfun breakout board.
You really just need to connect four wires (3.3v, ground, scl, sda). Both the codes from the Japanese Makezine and that from interactive-matter are then working.

Here's a Fritzing sketch of the minimum setup:

The weird problem I had was that I was using an external 3.3V power supply for the breakout board. For some reason this interfered with the I2C communication and I wouldn't get any response at all from the device. When I removed the power supply and used the Arduino's power output, it worked. I'd be happy if someone explained to me why.. :slight_smile:

The weird problem I had was that I was using an external 3.3V power supply for the breakout board.

Did you connect the ground of the external power supply and the Arduino?

Ahhh, right! I forgot to connect the ground of the Arduino, therefore the communication lines didn't have a ground reference (or whatever the correct expression is).

Thanks, Paul!

I am using the Arduino Duemilanove

I am still confuse after reading all the replies.

I believe that the Arduino SCL and SDA output level are 0 - 5V
If I connect the color sensor evaluation board from sparkfun directly, wouldn't it the voltage be too high?

Or is it the code from interactive-matter already disconnected the internal pull up on the Arduino board? and we are using the pull up from the sparkfun evaluation board?