ISL29125 RGB Light Sensor Hookup Guide

Pages
Contributors: JordanDee
Favorited Favorite 3

Advanced

Going beyond basic readings, the ISL29125 allows you to tailor the configuration of the sensor to custom-fit your application's specific needs. In the library there is a config() function that takes three arguments -- one for each of the sensor's configuration registers. That will be our workhorse for customizing the ISL29125's operation.

Configuring Active Channels and Interrupts

Let's take a look at the "ISL29125_interrupts" example, which demonstrates how to configure interrupts. This example is configured in a way that the sensor only reads red values and triggers a processor interrupt when the red sensor reading is above a specified threshold.

Before we can use the interrupts, we have to configure them -- set them up. Within the setup() function, after initializing the sensor as we did in the basic example, we configure the sensor with this function call:

language:cpp
RGB_sensor.config(CFG1_MODE_R | CFG1_10KLUX, CFG2_IR_ADJUST_HIGH, CFG3_R_INT | CFG3_INT_PRST8);

We use the first configuration register -- set with the first argument -- to define the sampling mode. In this case, we only want the sensor to collect data in the red spectrum, so it doesn't waste time sampling for blue and green. The mode we used in the basic example (set up behind the scenes) was CFG1_MODE_RGB, which collects data for all three colors. The mode can be set to any individual color, combination of two colors, or even powerdown and standby modes.

In addition to setting the channels sampled, the first parameter is also used to sets the light intensity. We set the light intensity scale to 10k lux, which is best for normal light levels. There's only one other option for light intensity -- 375 lux -- which is better for very dark environments. This register can also be used to change the sensor readings from 16-bit to 12-bit for less accurate but faster readings. It can even turn the INT pin into an input that triggers data sampling.

The constants you use to set up these registers, as well as additional information about using them can be found in the "SFE_ISL29125.h" file within the library directory. Feel free to take a look at that now or on an as-needed basis.

The second configuration register is solely concerned with IR filtering. Setting it properly, involves a calibration process with measurements taken with specified types of lights. The datasheet explains this process on pages 13 and 14, while more information on the register itself is on pages 10 and 11. If you're not sure what to set for this register, start with the value CFG_DEFAULT or CFG2_IR_ADJUST_HIGH. In this example we used the latter and it worked great for the office environment here at SparkFun. If neither of those seem to work for your application great, experiment with values, or follow the datasheet's calibration process.

The third configuration register is all about interrupts and can be left to default if you're not using them. In this example we set CFG3_R_INT, which tells the sensor to trigger interrupts based on red values being read by the sensor. You could also set this to green, blue or off, but there is no way to trigger on multiple colors at the same time.

Setting Interrupt Thresholds

Now that interrupts are on, when do they actually trigger? Well that has to do with thresholds, let's look at this next line of code within the setup() function of our example.

language:cpp
RGB_sensor.setUpperThreshold(0x0B00);
//RGB_sensor.setLowerThreshold(0x0300);

For an interrupt to be triggered, the red sensor value either has to be above the upper threshold or below the lower threshold. We set these two thresholds with the above functions but in this example we only use the upper one. By default the upper threshold is 0xFFFF -- the highest a sensor reading could be -- and the low threshold defaults to 0x0000 -- the lowest a reading could be.

So, will an interrupt trigger if the sensor reading for red exceeds 0x0B00 once? Well, if we configured the third register with the option CFG3_INT_PRST1, then the answer would be yes. But in this example we used CFG3_INT_PRST8, which means the sensor has to have eight consecutive readings that exceed the set threshold value before an interrupt triggers. This helps prevent false positives and allows us to see the larger picture without worrying about sudden fluctuations. Feel free to change the interrupt persist amount to what best suits your application. It can be set to 1, 2, 4, or 8.

Handling Interrupts

Now that we've learned more specifics for configuring the sensor, let's dive a bit further into the example. How does our Arduino actually use these interrupts coming from the sensor?

The INT pin of the sensor is active-low. This means it remains at 3.3V until an interrupt condition is met, at which point it goes LOW (ground). In our example, we connect this pin to digital pin 2 -- one of Arduino's external interrupt pins (using a logic level converter if needed as we did with the I2C lines).

In our setup(), we connect this interrupt to a function, also known as an interrupt service routine (ISR). The following code performs this:

language:cpp
attachInterrupt(0, increment, FALLING);

This makes interrupt 0, which is digital pin 2 on the Uno, call the increment() function when the interrupt pin transitions from HIGH to LOW (falling edge). So whenever the sensor reads the red value to be above 0x0B00 for eight consecutive samples, the interrupt line drops, and the increment() function is called in our code. This function simply increments a global variable i as seen below:

language:cpp
void increment()
{
    i++;
}

Each time through our loop() we check to see if i is different from our recorded lasti variable. If so, we print the interrupt number, the red sensor reading, and the amount of milliseconds since the last interrupt. Finally, we set lasti = i so we don't enter the if statement again until the next interrupt. We also call the sensor's object function readStatus() which clears the interrupt flag and allows another interrupt to be triggered in the future.

Here's the full loop() if you want to take a closer look at the details:

language:cpp
// Continuously check if an interrupt occured
// If so, print out interrupt #, sensor reading for red light, and time since last interrupt to serial monitor
void loop()
{
  static unsigned int lasti = 0; // Stores the number of the last interrupt
  static unsigned long ms = millis(); // Used to calculate the time between interrupts
  uint16_t red_value = 0; // Stores sensor reading for red light intensity
  uint8_t flags = 0; // Stores status flags read from the sensor

  // Check if an interrupt has occured, if so, enter the if block
  if (lasti != i)
  {
    // Read the detected light intensity of the red visible spectrum
    red_value = RGB_sensor.readRed();

    // Print out the interrupt # and sensor reading
    Serial.print("Interrupt #: ");
    Serial.println(i);
    Serial.print("Red Sensor Value (HEX): ");
    Serial.println(red_value, HEX);
    // Print out the # of milliseconds since the last interrupt
    Serial.print("Milliseconds since last interrupt: ");
    Serial.println(millis() - ms);
    Serial.println();
    ms = millis(); // Reset ms so we can start counting milliseconds up to the next interrupt

    // Set lasti to i, so that this if statement is not entered again until another interrupt is triggered
    lasti = i;

    // Read and clear the status flags including the interrupt triggered flag
    // This must be done otherwise another interrupt from the sensor can not be triggered
    flags = RGB_sensor.readStatus();

    // If you desire to see the reported status of the chip, uncomment the line below
    //checkSensorStatus(flags);
  }
}

Interrupts are quite useful when you want to monitor for a change in light level but don't know when it might happen. Remember to modify the configuration registers and the interrupt thresholds to what's suitable for your application.