You are currently not logged in and your progress will not be saved. Register or Log in

Exercise 2

Connecting to the BH1749 Ambient Light Sensor on the Thingy:91 and Thingy:53

Instead of the usual development kits, we have used to run samples and exercises throughout this course, this exercise uses a special type of Nordic Semiconductors’ prototyping boards called the Thingy. This exercise can be run on either a Thingy:53 or a Thingy:91.


The Thingy is an easy-to-use battery-operated prototyping board that allows the user to build a proof-of-concept in minimal time thanks to its variety of sensors, actuators, and interfaces onboard. For this exercise, to illustrate how to use the I2C interface, we will be using either the Thingy:53 or the Thingy:91 and make use of the BH1749: Ambient Light Sensor they have onboard.

Thingy:53 PCB, Front view, Light sensor location
Thingy:91 PCB, Back view, Light sensor location

The BH1749NUC is a color sensor which uses I2C for its interface. The Integrated Circuit (IC) onboard the sensor senses Red, Green, Blue (RGB), and infrared colors, then represents their intensities as digital values stored in 2 bytes for each value.

In this exercise, we will read the RGB value from the sensor and print it on a serial terminal.

Registers’ table as found in BH1749NUC color sensor datasheet

The above table shows the registers of the color sensor and their corresponding addresses. For our exercise, the relevant registers are the 4 registers in the red box. The 4 registers are, the basic SYSTEM_CONTROL register, the 2 MODE_CONTROL registers, used to set specific modes of the sensor, and lastly the RED_DATA register and more specifically the lower byte [7-0] in it, located at address 0x50.

To read color data (RGB without the IR value) from the sensor, you need to first set up the sensor mode, by writing to the MODE_CONTROL1 register. After that, you will need to enable measurements by writing to the MODE_CONTROL2 register. Lastly, you will need to do the actual reading from the data registers of RED_DATA, GREEN_DATA, and _BLUE_DATA. We will do a burst read starting from the first byte of the RED_DATA register.

Note that you can use the sensor’s driver (found here <nRF Connect SDK Installation Path>\nrf\drivers\sensor\bh1749>) to enable other features as well, such as reading infrared (IR) light intensity and utilizing interrupts. However, this exercise focuses only on the I2C aspect of interfacing the sensor, hence extra features are not within the scope of this exercise.

Exercise steps:

1. Download the base exercise project and extract it in your exercise folder for this course.

2. Enable the I2C driver by adding the following line into the prj.conf file.


3. In main.c , include the header file of the I2C API.

#include <drivers/i2c.h>

4. To display the sensor readings on the console, we will use the simple printk().

4.1 Include the header file <sys/printk.h> to use printk()

#include <sys/printk.h>

5. (No action needed) In the previous exercise, we were using an external expansion board. Since the sensor was on that expansion board, and not built-in on a Nordic Semiconductor board, we needed Step number 5 to specify that the sensor is connected to an I2C controller and provide its address in an overlay file. Meanwhile, in this exercise, the sensor is built-in on the Thingy board and is already specified in the devietree file. You can verify this by checking the devicetree file and searching for the BH1749 sensor as shown below.

6. Get the node identifier of the sensor. This was explained in detail in step 4 of the I2C Driver section.

#define I2C_NODE DT_NODELABEL(bh1749)

7. Retrieve the API-specific device structure, make sure that the device is ready to use, and print an error message if the I2C device is not ready to use:

	static const struct i2c_dt_spec dev_i2c = I2C_DT_SPEC_GET(I2C_NODE);
	if (!device_is_ready(dev_i2c.bus)) {
		printk("I2C bus %s is not ready!\n\r",dev_i2c.bus->name);

8. Define the addresses of the relevant registers, which can be found in the sensor datasheet. This information typically goes into a separate header file (.h). However, for the sake of keeping this demonstration simple, we will add them in main.c.

#define BH1749_SYSTEM_CONTROL                           0x40
#define BH1749_MODE_CONTROL1                            0x41
#define BH1749_MODE_CONTROL2                            0x42
#define BH1749_RED_DATA_LSB                             0x50
#define BH1749_MODE_CONTROL2_RGB_EN_ENABLE              BIT(4)

#define BH1749_MODE_CONTROL1_DEFAULTS                   0x2A

9. Setup the sensor by writing the value 0x2A to the MODE_CONTROL1 register.

As evident by the MODE_CONTROL1 register map below, 0x2A (0 01 01 010 in binary) would mean:

  • IR Gain: x1,
  • RGB Gain: x1
  • Measurement mode: 120ms
char buff1[] = {BH1749_MODE_CONTROL1,BH1749_MODE_CONTROL1_DEFAULTS};
	ret = i2c_write_dt(&dev_i2c,buff1,sizeof(buff1));
	if(ret != 0){
			printk("Failed to write to I2C device address 0x%c at Reg. 0x%c\n",dev_i2c.addr,BH1749_MODE_CONTROL1);

10. To read the RGB values from the sensor we need to enable measurement by writing 1 to bit 4 of the MODE_CONTROL2 register. We will use the i2c_write_read_dt() API as shown below

	char buff2[] = {BH1749_MODE_CONTROL2,BH1749_MODE_CONTROL2_RGB_EN_ENABLE};
	ret = i2c_write_dt(&dev_i2c,buff2,sizeof(buff2));
	if(ret != 0){
		printk("Failed to write to I2C device address 0x%c at Reg. 0x%c\n",dev_i2c.addr,BH1749_MODE_CONTROL2);

11. Read the RGB values from the RED_DATA, GREEN_DATA, and _BLUE_DATA registers. Since these registers are sequential, we will do a burst read starting from the first byte of the RED_DATA which is at address 0x50 (BH1749_RED_DATA_LSB). Notice that we are reading six bytes (as the size of rgb_value the buffer is 6 bytes) so the register would store the values of RGB as: (most significant bit)BBGGRR(least significant bit)

uint8_t rgb_value[6]= {0};
//Do a burst read of 6 bytes as each color channel is 2 bytes
ret = i2c_burst_read_dt(&dev_i2c, BH1749_RED_DATA_LSB,rgb_value,sizeof(rgb_value));
if(ret != 0){
   printk("Failed to read to I2C device address 0x%c at Reg. 0x%c\n",dev_i2c.addr,BH1749_RED_DATA_LSB);
//Print reading to console  
printk("Red Value:\t %d\n", (rgb_value[0] | rgb_value[1] << 8));
printk("Green Value:\t %d\n", (rgb_value[2] | rgb_value[3] << 8));
printk("Blue Value:\t %d\n", (rgb_value[4] | rgb_value[5] << 8));


12. Build the application

12.1 To build for a Thingy:91, use the same building procedure as used in the previous exercises, with the exception of choosing thingy91_nrf9160_ns as the target board in the Add Build Configuration window as shown below.

12.2 To build for a Thingy:53, use the same building procedure as used in the previous exercises, with the exception of choosing thingy53_nrf5340_cpuapp_ns as the target board in the Add Build Configuration window as shown below.

13. Flash the application to your Thingy device. To read more about flashing applications for the Thingy, click here for instructions regarding the Thingy:53 or click here for instructions regarding the Thingy:91. You should see an output similar to the one below.

To verify that the color values respond to ambient light changes, you can try for example covering the light sensor on the board (remember the location of the light sensor varies according to the Thingy version you are using, please refer to the board pictures at the top of this page) and observe how the values drastically decrease. You can also subject the board to a certain light color and observe how this value in the output increases.