Exploring Color Sensing with Arduino: A Journey into the World of RGB Detection

                 


    

In today's tech-driven world, the Arduino platform continues to empower enthusiasts and professionals alike to explore the realms of electronics and programming. One fascinating avenue within this domain is color sensing using Arduino boards. With the integration of RGB sensors, Arduino opens up a world of possibilities for projects ranging from color sorting machines to ambient light displays. Let's delve into the exciting world of color sensing with Arduino and discover its applications, principles, and how you can embark on your own creative endeavors.


Understanding Color Sensing

     At the heart of color sensing lies the ability to distinguish between different wavelengths of light. RGB (Red, Green, Blue) sensors are commonly used for this purpose. These sensors typically consist of photodiodes or phototransistors, each sensitive to a specific range of wavelengths corresponding to red, green, and blue light. By measuring the intensity of light in each of these channels, an RGB sensor can effectively identify the color of an object.

Principles of Color Sensing with Arduino

Implementing color sensing with Arduino involves several key principles:

  1. RGB Sensor Integration: Choose an RGB sensor compatible with Arduino, such as the TCS3200 or TCS34725. These sensors typically communicate with Arduino via protocols like I2C or SPI.


  2. Calibration: Calibrating the RGB sensor ensures accurate color detection. This involves adjusting sensor parameters to account for ambient light conditions and optimize color accuracy.


  3. Data Processing: Arduino collects data from the RGB sensor and processes it to determine the color of the detected object. Algorithms for color recognition can range from simple thresholding to more advanced techniques like machine learning.


  4. Feedback or Output: Depending on the application, Arduino provides feedback or output based on the detected color. This could involve activating actuators, displaying information on an LCD screen, or transmitting data to a connected device.  



  5. Getting Started with Color Sensing Projects

    Embarking on a color sensing project with Arduino is an exciting journey accessible to beginners and experienced makers alike. Here's a basic roadmap to get started:

    Components Requirement :

    1.  Arduino UNO
    2.  RGB Sensor (TSC230 chip)
    3.  Jumper Wire

    Basic logic of RBG Sensor works on :

                The TCS230 color detector measures three primary colors Red, Green and Blue and also has a separate white light detector. Since any color can be created from different levels of these primary colors, you can find out the color composition of a light source.

    TCS230 Specification


      Parameter

    Value

      Voltage Supply (VDD)

    2V7 ~ 5V5

      Abs.Max VDD

    -0.3V ~ 6V3

      Measurement range

    Approx. 300nm to 980nm [3]

      Output interface type

    Frequency pin o/p single pin [2]

      Logic Levels

    CMOS or TTL

      Logic input voltage (L,H)

    0V0 ~ 0V8, 2V0 ~ VDD

      Logic output voltage (L,H)

    0V4, 4.5 (for VDD=5V)

      Output frequency Scale 100%

    600kHz (typ) [1][2]

      Output frequency Scale 20%

    120kHz (typ) [1][2]

      Output frequency Scale 2%

    12kHz (typ) [1][2]

      Active current

    2mA (typ), 3mA (max)

      Power down current

    7uA (typ), 15uA (max)

      Operating temperature

    -40°C ~ 70°C




    The output from TC230 is from a single square wave output that changes frequency with the amount of light sensed. The specific sensor R,G,B or W is selected using two digital control inputs (S0 and S1).

    The frequency of the square wave is proportional to the amount of light falling on a set of light detecting diodes. Since there are four different sources of information red, green, blue and clear photodiodes - each of these is selected in-turn using (S0 and S1). So only one set of photodiodes is connected at a time to generate the output.

    The chip inputs, S2 and S3, control the output frequency divider logic which you an use to get a different output frequency proportional to the colour level. You can select a longer output pulse to accommodate slow processor measurement capability - but the reading averages to the same output value regardless of frequency output.

    In the photo below you can see the individual red, blue, green and clear filters. It's a bit blurry - but that was a very difficult photo to get and remember each section is 120um square so each one is about the width of a hair 



    In fact several photodiodes for each color are connected in parallel and these are spread evenly over the sensor surface - you can see this in the photo above. This layout obtains an overall reading for each color not just from an individual photodiode. For the TCS230 there are a total of 64 photodiodes. 16 have a red filter above them, 16 have a green filter, 16 have a blue filter and 16 have a clear filter.


    Block Diagram showing how the Chip works





    Note: There is also an output enable that tristates the output when high (OEn). This would allow you to attach several different TCS230 chips to detect colors from different parts of a machine using only one input. When using only one device set it low permanently.

    Signal output

    The signal output by the TCS230 is obtained by a current to frequency converter that takes as its input the average current from the selected set of photodiodes. The idea is that you switch one set on, take a reading and then switch to the next set until all four measurements are done. The output signal is a square wave with even mark to space ratio: 50% high, 50% low.

    Frequency Dividers

    The square wave output by the device can also be divided down (internally in the TCS230) and this allows slower processors to to make measurements more easily. There is no measurement penalty as the divided down output is simply an average of the original. The only disadvantage is that because the signal is of a lower frequency you can not make measurements as quickly.

    If it was important to make a light reading very fast then you would want to use a high speed processor and not divide down the output signal. You can imagine that this might become important in an industrial process measurement e.g. for measuring the state of a product e.g. you would want to quickly measure the color of an item on a conveyor belt, and reject it if it as the wrong color.

    Frequency outputs

    The maximum full scale frequency output from the TCS230 are shown below for the scaling factors controlled by S0 and S1. Scaling is really just using an internal clock chip that counts the input signal (the primary clock output from the photodiode current to frequency converter) and generates divided down clocks.



    Control Scaling

    S0

    S1

    Max output frequency kHz

    %scale

    H

    H

    600kHz

    100%

    H

    L

    120kHz

    20%

    L

    H

    12kHz

    2%

    L

    S1 = L

    Power down

    Power down




    Control Photodiodes

    S2

    S3

    Selected Photodiodes

    L

    L

    Red

    L

    H

    Blue

    H

    L

    Clear

    H

    H

    Green




    Circuit and schematic :

    Schematic

    The following schematic shows connections from a TCS230 breakout board to an Arduino Uno, with output on pin 4 and the two photodiode selector muxes on S0 and S1 (attached to pins 10 & 11).

     

    The output frequency divider controls are  on S2 and S3 ( attached to pins 1 & 2). The output enable is on pin 7, and the digital output from the TCS230 is connected to the Uno as an input on pin 4.




    Example Sketch for Arduino UNO


    TCS230 Calibration :

    The sensor is very sensitive to any changes in light and in practice that means any slight distance changes from the sensor to the object will cause a different reading. Additionally any ambient light changes will also cause a different reading.

    So to make accurate (repeatable) readings you need to control two elements:

    • Distance to object.
    • Ambient light.

    To calibrate the sensor, push the sensor down onto colored paper as even a small adjustment fails since the sensor is extremely sensitive.

    Set the serial monitor to 115200 Baud and push the sensor down and hit the 'Enter' key in the serial monitor input field. This will then show you the current RGB values. Copy these values into the RBG array and set the equivalent colname text. Recompile (or add some different colored objects). Recompile and check that the 'color' is found.

    Now you can test the 'objects' with the code now reporting the color of the object.

    As for "real" calibration- Its difficult and the only way you will achieve it is to place the sensor in a closed environment where you can control the lighting levels - this will give repeatable reliable measurements.


     
        
    //
    // Detect colors using TCS230.
    //
    
    // Arduino uno pins for control of TCS230
    #define TCS320_OE 7
    #define TCS320_S0 10
    #define TCS320_S1 11
    #define TCS320_S2 2
    #define TCS320_S3 3
    #define TCS320_OUT 4
    
    #define variance 50  // Acceptable detection error 2%.
    
    #define SEL_RED  \
       digitalWrite(TCS320_S2,LOW);digitalWrite(TCS320_S3,LOW)
    #define SEL_GREEN \
       digitalWrite(TCS320_S2,HIGH);digitalWrite(TCS320_S3,HIGH)
    #define SEL_BLUE \
       digitalWrite(TCS320_S2,LOW);digitalWrite(TCS320_S3,HIGH)
    #define SEL_CLEAR \
       digitalWrite(TCS320_S2,HIGH);digitalWrite(TCS320_S3,LOW)
    
    #define TWO_PER \
       digitalWrite(TCS320_S0,LOW);digitalWrite(TCS320_S1,HIGH);
    
    #define debug(a) Serial.println((a));
    
    
    #define NUMCOL 5
    
    // int RGB[NUMCOL][3]; // Five colors with 3 elements.
    // Array of NUMCOL strings len 10. 11 for null.
    // char colname[NUMCOL][11];
    
    // Typical values for 2% dividers (set variance to 50).
    int RGB[NUMCOL][3]={
       {248,647,393},
       {188,261,265},
       {404,710,546},
       {506,493,304},
       {930,1199,837},
    };
    
    char colname[NUMCOL][11]={
    "red",
    "yellow",
    "brown",
    "blue",
    "black",
    };
    
    ////////////////////////////////////////////////////////////////
    void setup() {
    
       pinMode(TCS320_OE,OUTPUT);
       pinMode(TCS320_S0,OUTPUT);
       pinMode(TCS320_S1,OUTPUT);
       pinMode(TCS320_S2,OUTPUT);
       pinMode(TCS320_S3,OUTPUT);
       pinMode(TCS320_OUT,INPUT);
    
       TWO_PER;
    
       digitalWrite(TCS320_OE,LOW); // On always.
    
       Serial.begin(115200);
       Serial.println("TCS230 color detector");
    }
    
    ////////////////////////////////////////////////////////////////
    unsigned long get_TCS230_reading(void) {
      unsigned long val;
      noInterrupts();
      val = pulseIn(TCS320_OUT,HIGH,20000); // 2000us=2ms  2Hz min.
      interrupts();
      return val;
    }
    
    static int clr,red,green,blue;
    
    ////////////////////////////////////////////////////////////////
    uint16_t detect(void) {
       unsigned long val;
    
        SEL_RED;
        red = val = get_TCS230_reading();
        Serial.print("RED: "); Serial.print(val);
    
        SEL_GREEN;
        green = val = get_TCS230_reading();
        Serial.print(" GREEN: "); Serial.print(val);
    
        SEL_BLUE;
        blue = val = get_TCS230_reading();
        Serial.print(" BLUE: "); Serial.print(val);
    
        Serial.print(" \n");
    }
    
    ////////////////////////////////////////////////////////////////
    int withinEQ(int c, int xl, int xh) {
       if (c>=xl && c<=xh) return 1;
       return 0;
    }
    
    ////////////////////////////////////////////////////////////////
    // Compare a value to a value and variance.
    int compare(int c, int v, int err) {
    int xh=v+err, xl=v-err;
       if (withinEQ(c,xl,xh)) return 1;
       return 0;
    }
    
    ////////////////////////////////////////////////////////////////
    void loop() {
    uint8_t chr,i,fnd;
    
       if (Serial.available()>0) {
    
          chr = Serial.read(); // Consume.
    
          // Find color match.
          detect();
          fnd=0;
          for (i=0;i
         

    Conclusion :


            For the 20% dividers you are getting close to the measurement capability of pulseIn which is 4us i.e the minimum period measured was 18us and that is only -5 counts of pulseIn data. So that measurement is getting close to being too small and therefore less accurate i.e. the difference between colors will need to be larger to register a difference in the chip output = less sensitive.

    The resolution here is 22% of the minimum period which is fairly bad.

            The periods for 100% output will be even smaller (1.0/600e3 = 1.6us) and that would not be measurable using pulseIn - you may be able to do it using the asynchronous input in Timer 2.

    When the 2% dividers are used the program can make the most accurate period measurement since the periods output by the chip are long compared to the pulseIn accuracy of 4us. i.e the lowest period measured was 188us for these dividers ( ~48 periods of 4us ) compared to 18us ( ~5 periods of 4us ) for the 20% dividers.

    The resolution here is 2.13% of the minimum period which is quite good.

    Therefore the 2% divider output is the better option for measurement accuracy.

    Warning: The light detector is extremely sensitive so although you can get maximum accuracy the periods measured will change a lot depending on the lighting conditions and object distance. That is why the variable 'variance' allows you to specify an error value around each RBG center point for reliable color detection.