033 - MicroPython TechNotes: TCS34725 RGB Color Sensor

Introduction

In this article, we will learn how to use the TCS34725 RGB Color Sensor with ESP32 using MicroPython programming language.

Bill Of Materials

  1. ESP32 development board.
  2. Gorillacell ESP32 shield.
  3. 4-pin female-female dupont wires.
  4. TCS34725 RGB color sensor module.

Pinout

  1. GND – for the ground pin.
  2. VCC – for the supply voltage.
  3. SDA – for the I2C serial data pin.
  4. SCL – for the I2C serial clock pin.

Hardware Instruction

  1. First, attach the ESP32 board on top of the ESP32 shield and make sure that both USB ports are on the same side.
  2. Next, attach the dupont wires to the RGB Color Sensor by following the color coding which is black for the ground, red for the VCC, yellow for the SDA pin, and white for the SCL pin.
  3. Next, attach the other end of the dupont wires to the ESP32 shield by matching the colors of the wires to the colors of the pin headers such as black to black, red to red, and yellow and the following colors to yellow pin headers. For this experiment, I choose GPIO 21 for the SDA pin and GPIO 22 for the SCL pin.
  4. Next, power the ESP32 shield with an external power supply with a type-C USB connector and make sure that the power switch is set to ON state.
  5. Lastly, connect the ESP32 to the computer.

Software Instruction

  1. Copy the tcs34725.py below and paste it to Thonny IDE and save it to ESP32 MicroPython root directory by clicking the File menu and select Save As. Click the MicroPython Device and save it as tcs34725.py.
  2. Copy the example source codes and play with it.

Video Demonstration

Call To Action

For any concern, write your message in the comment section.

You might also like to support my journey on Youtube by Subscribing. Click this to Subscribe to TechToTinker.

Thank you and have a good days ahead.

See you,

– George Bantique | tech.to.tinker@gmail.com

Source Code

Example # 1, exploring the basics of TCS34725 RGB Color Sensor:

 1# More details can be found in TechToTinker.blogspot.com 
 2# George Bantique | tech.to.tinker@gmail.com
 3
 4from machine import Pin
 5from machine import I2C
 6from tcs34725 import TCS34725
 7from time import sleep_ms
 8
 9i2c_bus = I2C(0, sda=Pin(21), scl=Pin(22))
10tcs = TCS34725(i2c_bus)
11
12# # The following lines of code should be tested in the REPL:
13# #
14# # 1. To print the raw data:
15# print('raw: {}'.format(tcs.read('raw')))
16# #
17# # 2. To print the RGB data:
18# print('rgb: {}'.format(tcs.read('rgb')))
19# #
20# # 3. To print the RGB data in decimal form:
21# print('dec: {}'.format(tcs.read('dec')))
22# #
23# # 4. To print the RGB data in hex form:
24# print('hex: {}'.format(tcs.read('hex')))
25# #
26# # 5. To print the color temperature in ^Kelvin and
27# #    the luminosity in lux
28# print('lux: {}'.format(tcs.read('lux')))

Example # 2, simple application of TCS34725:

 1# More details can be found in TechToTinker.blogspot.com 
 2# George Bantique | tech.to.tinker@gmail.com
 3
 4from machine import Pin
 5from machine import I2C
 6from machine import PWM
 7from time import sleep_ms
 8from tcs34725 import TCS34725
 9from gorillacell_rgb import GORILLACELL_RGB
10
11i2c_bus = I2C(0, sda=Pin(21), scl=Pin(22))
12tcs = TCS34725(i2c_bus)
13rgb = GORILLACELL_RGB(25, 26, 27)
14
15while True:
16    red, grn, blu = tcs.read('dec')
17    rgb.set_rgb(red, grn, blu)

3. tcs34725.py modified driver library for TCS34725:

  1import time  
  2 import ustruct  
  3 #const = lambda x:x  
  4 _COMMAND_BIT = const(0x80)  
  5 _REGISTER_ENABLE = const(0x00)  
  6 _REGISTER_ATIME = const(0x01)  
  7 _REGISTER_AILT = const(0x04)  
  8 _REGISTER_AIHT = const(0x06)  
  9 _REGISTER_ID = const(0x12)  
 10 _REGISTER_APERS = const(0x0c)  
 11 _REGISTER_CONTROL = const(0x0f)  
 12 _REGISTER_SENSORID = const(0x12)  
 13 _REGISTER_STATUS = const(0x13)  
 14 _REGISTER_CDATA = const(0x14)  
 15 _REGISTER_RDATA = const(0x16)  
 16 _REGISTER_GDATA = const(0x18)  
 17 _REGISTER_BDATA = const(0x1a)  
 18 _ENABLE_AIEN = const(0x10)  
 19 _ENABLE_WEN = const(0x08)  
 20 _ENABLE_AEN = const(0x02)  
 21 _ENABLE_PON = const(0x01)  
 22 _GAINS = (1, 4, 16, 60)  
 23 _CYCLES = (0, 1, 2, 3, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60)  
 24 class TCS34725:  
 25   def __init__(self, i2c, address=0x29):  
 26     self.i2c = i2c  
 27     self.address = address  
 28     self._active = False  
 29     self.integration_time(2.4)  
 30     sensor_id = self.sensor_id()  
 31     if sensor_id not in (0x44, 0x10):  
 32       raise RuntimeError("wrong sensor id 0x{:x}".format(sensor_id))  
 33   def _register8(self, register, value=None):  
 34     register |= _COMMAND_BIT  
 35     if value is None:  
 36       return self.i2c.readfrom_mem(self.address, register, 1)[0]  
 37     data = ustruct.pack('<B', value)  
 38     self.i2c.writeto_mem(self.address, register, data)  
 39   def _register16(self, register, value=None):  
 40     register |= _COMMAND_BIT  
 41     if value is None:  
 42       data = self.i2c.readfrom_mem(self.address, register, 2)  
 43       return ustruct.unpack('<H', data)[0]  
 44     data = ustruct.pack('<H', value)  
 45     self.i2c.writeto_mem(self.address, register, data)  
 46   def active(self, value=None):  
 47     if value is None:  
 48       return self._active  
 49     value = bool(value)  
 50     if self._active == value:  
 51       return  
 52     self._active = value  
 53     enable = self._register8(_REGISTER_ENABLE)  
 54     if value:  
 55       self._register8(_REGISTER_ENABLE, enable | _ENABLE_PON)  
 56       time.sleep_ms(3)  
 57       self._register8(_REGISTER_ENABLE,  
 58         enable | _ENABLE_PON | _ENABLE_AEN)  
 59     else:  
 60       self._register8(_REGISTER_ENABLE,  
 61         enable & ~(_ENABLE_PON | _ENABLE_AEN))  
 62   def sensor_id(self):  
 63     return self._register8(_REGISTER_SENSORID)  
 64   def integration_time(self, value=None):  
 65     if value is None:  
 66       return self._integration_time  
 67     value = min(614.4, max(2.4, value))  
 68     cycles = int(value / 2.4)  
 69     self._integration_time = cycles * 2.4  
 70     return self._register8(_REGISTER_ATIME, 256 - cycles)  
 71   def gain(self, value):  
 72     if value is None:  
 73       return _GAINS[self._register8(_REGISTER_CONTROL)]  
 74     if value not in _GAINS:  
 75       raise ValueError("gain must be 1, 4, 16 or 60")  
 76     return self._register8(_REGISTER_CONTROL, _GAINS.index(value))  
 77   def _valid(self):  
 78     return bool(self._register8(_REGISTER_STATUS) & 0x01)  
 79   def read(self, raw=False):  
 80     was_active = self.active()  
 81     self.active(True)  
 82     while not self._valid():  
 83       time.sleep_ms(int(self._integration_time + 0.9))  
 84     data = tuple(self._register16(register) for register in (  
 85       _REGISTER_RDATA,  
 86       _REGISTER_GDATA,  
 87       _REGISTER_BDATA,  
 88       _REGISTER_CDATA,  
 89     ))  
 90     self.active(was_active)  
 91     if raw:  
 92       return data  
 93     return self._temperature_and_lux(data)  
 94   def _temperature_and_lux(self, data):  
 95     r, g, b, c = data  
 96     x = -0.14282 * r + 1.54924 * g + -0.95641 * b  
 97     y = -0.32466 * r + 1.57837 * g + -0.73191 * b  
 98     z = -0.68202 * r + 0.77073 * g + 0.56332 * b  
 99     d = x + y + z  
100     n = (x / d - 0.3320) / (0.1858 - y / d)  
101     cct = 449.0 * n**3 + 3525.0 * n**2 + 6823.3 * n + 5520.33  
102     return cct, y  
103   def threshold(self, cycles=None, min_value=None, max_value=None):  
104     if cycles is None and min_value is None and max_value is None:  
105       min_value = self._register16(_REGISTER_AILT)  
106       max_value = self._register16(_REGISTER_AILT)  
107       if self._register8(_REGISTER_ENABLE) & _ENABLE_AIEN:  
108         cycles = _CYCLES[self._register8(_REGISTER_APERS) & 0x0f]  
109       else:  
110         cycles = -1  
111       return cycles, min_value, max_value  
112     if min_value is not None:  
113       self._register16(_REGISTER_AILT, min_value)  
114     if max_value is not None:  
115       self._register16(_REGISTER_AIHT, max_value)  
116     if cycles is not None:  
117       enable = self._register8(_REGISTER_ENABLE)  
118       if cycles == -1:  
119         self._register8(_REGISTER_ENABLE, enable & ~(_ENABLE_AIEN))  
120       else:  
121         self._register8(_REGISTER_ENABLE, enable | _ENABLE_AIEN)  
122         if cycles not in _CYCLES:  
123           raise ValueError("invalid persistence cycles")  
124         self._register8(_REGISTER_APERS, _CYCLES.index(cycles))  
125   def interrupt(self, value=None):  
126     if value is None:  
127       return bool(self._register8(_REGISTER_STATUS) & _ENABLE_AIEN)  
128     if value:  
129       raise ValueError("interrupt can only be cleared")  
130     self.i2c.writeto(self.address, b'xe6')  
131 def html_rgb(data):  
132   r, g, b, c = data  
133   red = pow((int((r/c) * 256) / 255), 2.5) * 255  
134   green = pow((int((g/c) * 256) / 255), 2.5) * 255  
135   blue = pow((int((b/c) * 256) / 255), 2.5) * 255  
136   return red, green, blue  
137 def html_hex(data):  
138   r, g, b = html_rgb(data)  
139   return "{0:02x}{1:02x}{2:02x}".format(int(r),  
140                int(g),  
141                int(b))  

4. gorillacell_rgb driver library for the RGB LED:

 1# This is a class helper for the RGB module
 2# George Bantique | tech.to.tinker@gmail.com
 3
 4from machine import Pin
 5from machine import PWM
 6
 7class GORILLACELL_RGB:
 8    def __init__(self, r_pin, g_pin, b_pin):
 9        self.r = PWM(Pin(r_pin))
10        self.g = PWM(Pin(g_pin))
11        self.b = PWM(Pin(b_pin))
12 
13        # Initialize the PWM frequency to 60Hz 
14        self.r.freq(60) 
15        self.g.freq(60) 
16        self.b.freq(60) 
17        # and Initialize with pulse turned OFF 
18        self.r.duty(0) 
19        self.g.duty(0) 
20        self.b.duty(0)
21        
22    def map(self, x, in_min, in_max, out_min, out_max): 
23        # This will not handle x value greater than in_max or 
24        #                      x value less than in_min 
25        return int((x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min) 
26    def set_rgb(self, x, y, z): 
27        # Maximum duty cycle is 1023 
28        # RGB values is usually 0 to 255 
29        # By using ratio and proportion 
30        self.r.duty(self.map(x, 0, 255, 0, 1023)) 
31        self.g.duty(self.map(y, 0, 255, 0, 1023)) 
32        self.b.duty(self.map(z, 0, 255, 0, 1023)) 
33             
34    def rst_rgb(self): 
35        # Turn off all pwm pulse 
36        self.r.duty(0) 
37        self.g.duty(0) 
38        self.b.duty(0)

References And Credits

  1. Purchase your Gorillacell ESP32 development kit at: https://gorillacell.kr

  2. TCS34725 Driver Library: https://github.com/adafruit/micropython-adafruit-tcs34725



Posts in this series



No comments yet!

GitHub-flavored Markdown & a sane subset of HTML is supported.