Project

Using Scilab GUI for RGB and Lux Measurements

January 28, 2016 by Robert Keim

Gather data via USB from a BH1745NUC optical sensor and then display color characteristics and illuminance values.

Gather data via USB from a BH1745NUC optical sensor and then display color characteristics and illuminance values.

Supporting Information

A Handy Optical-Analysis System

The goal of this project is to turn our EFM8-plus-BH1745NUC circuit into a USB-based system for monitoring and analyzing the color characteristics and illuminance of whatever light is illuminating the sensor. You can use this system to determine the RGB content generated by any light source, but the intended purpose is the general evaluation of ambient illumination. This system provides information on the two most important aspects of ambient lighting: illuminance, which could be described as the “effective brightness” (more details here), and spectral composition, which influences a person’s physiological and psychological response to lighting conditions (more details on spectral composition here, under “The Colors of White”).

The GUI

This is the graphical user interface we will be using for this project:

The GUI runs in Scilab and was designed with the help of the GUI Builder toolbox, which you can download through Scilab’s ATOMS module manager:

You can use the following link to download the GUI script (it’s just a single text file).

RGB_Sensor_and_Lux_Meter_Scilab_GUI.zip

First you use the “Open VCP Port” button to establish a virtual COM port connection to the EFM8 microcontroller. Next, choose the desired interval between data requests; 5 seconds is the minimum. After you click “Begin RGBC Data Collection,” the GUI repeatedly requests data until you click “Stop RGBC Data Collection.” The vertical sliders and the small colored rectangle convey RGB data for the most recent measurement, and the plot shows all the lux values since the beginning of the current data collection period; to clear the plot and begin a new lux record, click “Stop RGBC Data Collection” then “Begin RGBC Data Collection.”

The plotting functionality is useful for tracking changes in illuminance—for example, you could record how much illuminance is provided by windows throughout the course of a day, and this information could help you to determine an appropriate schedule for artificial lighting. (The variations in the plot shown above reflect changes in indoor illuminance caused by “partially cloudy” weather conditions.) The sliders and the rectangle are two different ways of displaying color information: the sliders allow you to accurately assess the proportion of red, green, and blue light in the incident radiation, whereas the rectangle combines the red, green, and blue proportions into a single color that represents which wavelengths are dominant.

Conveying Color

Let’s take a look at some examples of how the GUI reports RGB information for different light sources. The following spectral composition plots (available here) from the National Institute of Standards and Technology remind us of what we should expect from sunlight, incandescent bulbs, and white LEDs; the peak sensitivity wavelength of our R, G, and B detectors is about 625 nm, 535 nm, and 455 nm, respectively.

Here are results for indirect sunlight shining through a window. Notice that blue and green are dominant with significantly less red, resulting in a greenish-bluish rectangle. This is consistent with the general spectral trend shown in the above plot.

Next we have results for incandescent lighting. Notice how the rectangle immediately conveys the dominance of longer (i.e., yellow/orange/red) wavelengths. The sliders also aptly represent how the intensity of the incandescent spectrum increases with wavelength. 

Lastly, we have data for light from a white-LED flashlight. White LEDs are known for their higher portion of blue wavelengths, and that is exactly what we see in the colored rectangle and the sliders.

Note: The sliders operate on an absolute scale. The default maximum value for each slider is 5000. This works well for indoor measurements, but the intensities will easily exceed this in outdoor or other high-brightness situations. The following code excerpt shows where to adjust the maximum values:

In Search of Lux

It is not particularly easy to perform accurate illuminance measurements, a fact of which you are well aware if you have read my three previous articles on this topic (Understanding Illuminance: What’s in a Lux? and Measuring and Calculating Lux Values, Part 1 and Part 2). The GUI developed for this project follows the calculation procedure discussed in Measuring and Calculating Lux Values, Part 2. Here is my Scilab-script implementation of this procedure:

Below is the code I use to create the colored rectangle. The scaling technique is discussed in Design a Color Sensor with Measurements Displayed via an RGB LED Module, Part 2; I’ll repeat the salient sentences for your convenience:

Our goal here is to “measure” color, regardless of the overall intensity of the light illuminating the photodetectors. Thus, we need to scale the RGB values in a way that standardizes the absolute value of the measurements while preserving the relative value—in other words, we maximize the overall intensity while maintaining the proportion of red, green, and blue in the incident light. To accomplish this, we multiply the highest of the three measurements by whatever factor increases this highest measurement to the maximum value, then we multiply the other two measurements by the same factor.
 

Important Note: The datasheet for the BH1745NUC indicates that the sensitivity specs given in the “Electrical Characteristics” table are based on measurements performed with the sensor’s internal gain set to 16x. I believe that this is an error; I calculated the sensitivity assuming that the gain was 1x. An error of this kind would not be surprising in view of the overall quality of the datasheet; actually, one of the other specifications in this table appears to include a similar mistake. Furthermore, most of my lux measurements indicate that the 1x-based sensitivity is correct. I performed two experiments, one with incandescent bulbs and one with a candle, and in both cases my lux values were consistent with theoretical predictions—certainly not off by a factor of 16. However, my lux values for full-sun outdoor conditions seemed too low, though increasing these values by a factor of 16 would make them too high. The low outdoor lux values could be related to some sort of saturation or desensitization that occurs at very high light intensities—the datasheet indicates that the dynamic range of the sensor extends only to 40,000 lux, whereas direct sunlight can exceed 100,000 lux. Bottom line, more experimentation is needed to determine how to optimize a BH1745NUC-based lux meter.

Firmware

Here is a link to download the firmware.

RGB_Sensor_with_LED_Feedback_and_USB.zip

Please refer to Design a Color Sensor with Measurements Displayed via an RGB LED Module, Part 1 and Part 2, for information on the firmware used in this project. Most of the USB functionality required for communicating with Scilab is in the “USBInterface.c” source file:

uint16_t xdata USBBytesRcvd = 0;          //holds size of received packet
uint16_t xdata USBBytesTransmitted = 0;    //holds number of bytes actually transmitted
uint8_t xdata USBRxPacket[USB_PACKET_SIZE];  //packet received from host

bit REQUEST_RGBC_DATA = FALSE;
bit REQUEST_TEMP_DATA = FALSE;


/******************************************************************************
 * VCPXpress callback
 * This function is called by VCPXpress.
 *****************************************************************************/
VCPXpress_API_CALLBACK(myAPICallback)
{
   uint32_t API_InterruptCode;

   //get the code that indicates the reason for the interrupt
   API_InterruptCode = Get_Callback_Source();

   //if the USB connection was just opened
   if (API_InterruptCode & DEVICE_OPEN)
   {
	   //start the first USB read procedure
	   Block_Read(USBRxPacket, USB_PACKET_SIZE, &USBBytesRcvd);
	   /*we will process the received bytes when we get
	   a callback with an RX_COMPLETE interrupt code*/
   }

   if (API_InterruptCode & RX_COMPLETE)   //USB read complete
   {
	   //confirm that the received packet is the proper length
	   if(USBBytesRcvd == USB_CMD_LEN)
	   {
		   //'R' indicates that the host is Requesting data
		   if(USBRxPacket[0] == 'R')
		   {
			   //is the host requesting data from the RGBC sensor?
			   if(USBRxPacket[2] == 'R' && USBRxPacket[3] == 'G' && USBRxPacket[4] == 'B' && USBRxPacket[5] == 'C')
			   {
				   REQUEST_RGBC_DATA = TRUE;
			   }

			   //is the host requesting data from the thermocouple IC?
			   else if(USBRxPacket[2] == 'T' && USBRxPacket[3] == 'E' && USBRxPacket[4] == 'M' && USBRxPacket[5] == 'P')
			   {
				   REQUEST_TEMP_DATA = TRUE;
			   }
		   }
	   }

	   //continue with the next USB read procedure
	   Block_Read(USBRxPacket, USB_PACKET_SIZE, &USBBytesRcvd);
   }
}

void TransmitUSB_TempData()
{
    Block_Write(TempDataTx, TEMP_DATA_LEN, &USBBytesTransmitted);
}

void TransmitUSB_RGBCData()
{
    Block_Write(I2C_RcvData, RGBC_DATA_LEN, &USBBytesTransmitted);
}


You will notice that the USB code includes a request for temperature data in addition to a request for RGBC data. This gives you an example of how to incorporate additional commands into the USB interface (the temperature-measurement functionality will be used in a future project).