29
29
30
30
See examples/tcs34725_simpletest.py for an example of the usage.
31
31
32
- * Author(s): Tony DiCola
32
+ * Author(s): Tony DiCola, Carter Nelson
33
33
34
34
Implementation Notes
35
35
--------------------
83
83
# pylint: enable=bad-whitespace
84
84
85
85
86
- def _temperature_and_lux (data ):
87
- """Convert the 4-tuple of raw RGBC data to color temperature and lux values. Will return
88
- 2-tuple of color temperature and lux."""
89
- r , g , b , _ = data
90
- x = - 0.14282 * r + 1.54924 * g + - 0.95641 * b
91
- y = - 0.32466 * r + 1.57837 * g + - 0.73191 * b
92
- z = - 0.68202 * r + 0.77073 * g + 0.56332 * b
93
- divisor = x + y + z
94
- n = (x / divisor - 0.3320 ) / (0.1858 - y / divisor )
95
- cct = 449.0 * n ** 3 + 3525.0 * n ** 2 + 6823.3 * n + 5520.33
96
- return cct , y
97
-
98
86
class TCS34725 :
99
87
"""Driver for the TCS34725 color sensor."""
100
88
@@ -105,44 +93,55 @@ class TCS34725:
105
93
106
94
def __init__ (self , i2c , address = 0x29 ):
107
95
self ._device = i2c_device .I2CDevice (i2c , address )
108
- sensor_id = self ._read_u8 (_REGISTER_SENSORID )
109
96
self ._active = False
110
97
self .integration_time = 2.4
98
+ self ._glass_attenuation = None
99
+ self .glass_attenuation = 1.0
111
100
# Check sensor ID is expectd value.
112
101
sensor_id = self ._read_u8 (_REGISTER_SENSORID )
113
102
if sensor_id not in (0x44 , 0x10 ):
114
103
raise RuntimeError ('Could not find sensor, check wiring!' )
115
104
116
- def _read_u8 (self , address ):
117
- # Read an 8-bit unsigned value from the specified 8-bit address.
118
- with self ._device as i2c :
119
- self ._BUFFER [0 ] = (address | _COMMAND_BIT ) & 0xFF
120
- i2c .write (self ._BUFFER , end = 1 , stop = False )
121
- i2c .readinto (self ._BUFFER , end = 1 )
122
- return self ._BUFFER [0 ]
105
+ @property
106
+ def lux (self ):
107
+ """The lux value computed from the color channels."""
108
+ return self ._temperature_and_lux_dn40 ()[0 ]
123
109
124
- def _read_u16 (self , address ):
125
- # Read a 16-bit BE unsigned value from the specified 8-bit address.
126
- with self ._device as i2c :
127
- self ._BUFFER [0 ] = (address | _COMMAND_BIT ) & 0xFF
128
- i2c .write (self ._BUFFER , end = 1 , stop = False )
129
- i2c .readinto (self ._BUFFER , end = 2 )
130
- return (self ._BUFFER [0 ] << 8 ) | self ._BUFFER [1 ]
110
+ @property
111
+ def color_temperature (self ):
112
+ """The color temperature in degrees Kelvin."""
113
+ return self ._temperature_and_lux_dn40 ()[1 ]
131
114
132
- def _write_u8 (self , address , val ):
133
- # Write an 8-bit unsigned value to the specified 8-bit address.
134
- with self ._device as i2c :
135
- self ._BUFFER [0 ] = (address | _COMMAND_BIT ) & 0xFF
136
- self ._BUFFER [1 ] = val & 0xFF
137
- i2c .write (self ._BUFFER , end = 2 )
115
+ @property
116
+ def color_rgb_bytes (self ):
117
+ """Read the RGB color detected by the sensor. Returns a 3-tuple of
118
+ red, green, blue component values as bytes (0-255).
119
+ """
120
+ r , g , b , clear = self .color_raw
121
+ # Avoid divide by zero errors ... if clear = 0 return black
122
+ if clear == 0 :
123
+ return (0 , 0 , 0 )
124
+ # pylint: disable=bad-whitespace
125
+ red = int (pow ((int ((r / clear ) * 256 ) / 255 ), 2.5 ) * 255 )
126
+ green = int (pow ((int ((g / clear ) * 256 ) / 255 ), 2.5 ) * 255 )
127
+ blue = int (pow ((int ((b / clear ) * 256 ) / 255 ), 2.5 ) * 255 )
128
+ # Handle possible 8-bit overflow
129
+ if red > 255 :
130
+ red = 255
131
+ if green > 255 :
132
+ green = 255
133
+ if blue > 255 :
134
+ blue = 255
135
+ return (red , green , blue )
138
136
139
- def _write_u16 (self , address , val ):
140
- # Write a 16-bit BE unsigned value to the specified 8-bit address.
141
- with self ._device as i2c :
142
- self ._BUFFER [0 ] = (address | _COMMAND_BIT ) & 0xFF
143
- self ._BUFFER [1 ] = (val >> 8 ) & 0xFF
144
- self ._BUFFER [2 ] = val & 0xFF
145
- i2c .write (self ._BUFFER )
137
+ @property
138
+ def color (self ):
139
+ """Read the RGB color detected by the sensor. Returns an int with 8 bits per channel.
140
+ Examples: Red = 16711680 (0xff0000), Green = 65280 (0x00ff00),
141
+ Blue = 255 (0x0000ff), SlateGray = 7372944 (0x708090)
142
+ """
143
+ r , g , b = self .color_rgb_bytes
144
+ return (r << 16 ) | (g << 8 ) | b
146
145
147
146
@property
148
147
def active (self ):
@@ -208,10 +207,6 @@ def interrupt(self, val):
208
207
with self ._device :
209
208
self ._device .write (b'\xe6 ' )
210
209
211
- def _valid (self ):
212
- # Check if the status bit is set and the chip is ready.
213
- return bool (self ._read_u8 (_REGISTER_STATUS ) & 0x01 )
214
-
215
210
@property
216
211
def color_raw (self ):
217
212
"""Read the raw RGBC color detected by the sensor. Returns a 4-tuple of
@@ -230,51 +225,6 @@ def color_raw(self):
230
225
self .active = was_active
231
226
return data
232
227
233
- @property
234
- def color_rgb_bytes (self ):
235
- """Read the RGB color detected by the sensor. Returns a 3-tuple of
236
- red, green, blue component values as bytes (0-255).
237
- """
238
- r , g , b , clear = self .color_raw
239
- # Avoid divide by zero errors ... if clear = 0 return black
240
- if clear == 0 :
241
- return (0 , 0 , 0 )
242
- # pylint: disable=bad-whitespace
243
- red = int (pow ((int ((r / clear ) * 256 ) / 255 ), 2.5 ) * 255 )
244
- green = int (pow ((int ((g / clear ) * 256 ) / 255 ), 2.5 ) * 255 )
245
- blue = int (pow ((int ((b / clear ) * 256 ) / 255 ), 2.5 ) * 255 )
246
- # Handle possible 8-bit overflow
247
- if red > 255 :
248
- red = 255
249
- if green > 255 :
250
- green = 255
251
- if blue > 255 :
252
- blue = 255
253
- return (red , green , blue )
254
-
255
- @property
256
- def color (self ):
257
- """Read the RGB color detected by the sensor. Returns an int with 8 bits per channel.
258
-
259
- Examples: Red = 16711680 (0xff0000), Green = 65280 (0x00ff00),
260
- Blue = 255 (0x0000ff), SlateGray = 7372944 (0x708090)
261
- """
262
- r , g , b = self .color_rgb_bytes
263
- return (r << 16 ) | (g << 8 ) | b
264
-
265
-
266
- @property
267
- def temperature (self ):
268
- """Return the detected color temperature in degrees."""
269
- temp , _ = _temperature_and_lux (self .color_raw )
270
- return temp
271
-
272
- @property
273
- def lux (self ):
274
- """Return the detected light level in lux."""
275
- _ , lux = _temperature_and_lux (self .color_raw )
276
- return lux
277
-
278
228
@property
279
229
def cycles (self ):
280
230
"""The persistence cycles of the sensor."""
@@ -314,3 +264,105 @@ def max_value(self):
314
264
@max_value .setter
315
265
def max_value (self , val ):
316
266
self ._write_u16 (_REGISTER_AIHT , val )
267
+
268
+ def _temperature_and_lux_dn40 (self ):
269
+ """Converts the raw R/G/B values to color temperature in degrees
270
+ Kelvin using the algorithm described in DN40 from Taos (now AMS).
271
+ Also computes lux. Returns tuple with both values or tuple of Nones
272
+ if computation can not be done.
273
+ """
274
+ # pylint: disable=bad-whitespace, invalid-name, too-many-locals
275
+
276
+ # Initial input values
277
+ ATIME = self ._read_u8 (_REGISTER_ATIME )
278
+ ATIME_ms = (256 - ATIME ) * 2.4
279
+ AGAINx = self .gain
280
+ R , G , B , C = self .color_raw
281
+
282
+ # Device specific values (DN40 Table 1 in Appendix I)
283
+ GA = self .glass_attenuation # Glass Attenuation Factor
284
+ DF = 310.0 # Device Factor
285
+ R_Coef = 0.136 # |
286
+ G_Coef = 1.0 # | used in lux computation
287
+ B_Coef = - 0.444 # |
288
+ CT_Coef = 3810 # Color Temperature Coefficient
289
+ CT_Offset = 1391 # Color Temperatuer Offset
290
+
291
+ # Analog/Digital saturation (DN40 3.5)
292
+ SATURATION = 65535 if 256 - ATIME > 63 else 1024 * (256 - ATIME )
293
+
294
+ # Ripple saturation (DN40 3.7)
295
+ if ATIME_ms < 150 :
296
+ SATURATION -= SATURATION / 4
297
+
298
+ # Check for saturation and mark the sample as invalid if true
299
+ if C >= SATURATION :
300
+ return None , None
301
+
302
+ # IR Rejection (DN40 3.1)
303
+ IR = (R + G + B - C ) / 2 if R + G + B > C else 0.
304
+ R2 = R - IR
305
+ G2 = G - IR
306
+ B2 = B - IR
307
+
308
+ # Lux Calculation (DN40 3.2)
309
+ G1 = R_Coef * R2 + G_Coef * G2 + B_Coef * B2
310
+ CPL = (ATIME_ms * AGAINx ) / (GA * DF )
311
+ lux = G1 / CPL
312
+
313
+ # CT Calculations (DN40 3.4)
314
+ CT = CT_Coef * B2 / R2 + CT_Offset
315
+
316
+ return lux , CT
317
+
318
+ @property
319
+ def glass_attenuation (self ):
320
+ """The Glass Attenuation (FA) factor used to compensate for lower light
321
+ levels at the device due to the possible presence of glass. The GA is
322
+ the inverse of the glass transmissivity (T), so GA = 1/T. A transmissivity
323
+ of 50% gives GA = 1 / 0.50 = 2. If no glass is present, use GA = 1.
324
+ See Application Note: DN40-Rev 1.0 – Lux and CCT Calculations using
325
+ ams Color Sensors for more details.
326
+ """
327
+ return self ._glass_attenuation
328
+
329
+ @glass_attenuation .setter
330
+ def glass_attenuation (self , value ):
331
+ if value < 1 :
332
+ raise ValueError ("Glass attenuation factor must be at least 1." )
333
+ self ._glass_attenuation = value
334
+
335
+ def _valid (self ):
336
+ # Check if the status bit is set and the chip is ready.
337
+ return bool (self ._read_u8 (_REGISTER_STATUS ) & 0x01 )
338
+
339
+ def _read_u8 (self , address ):
340
+ # Read an 8-bit unsigned value from the specified 8-bit address.
341
+ with self ._device as i2c :
342
+ self ._BUFFER [0 ] = (address | _COMMAND_BIT ) & 0xFF
343
+ i2c .write (self ._BUFFER , end = 1 , stop = False )
344
+ i2c .readinto (self ._BUFFER , end = 1 )
345
+ return self ._BUFFER [0 ]
346
+
347
+ def _read_u16 (self , address ):
348
+ # Read a 16-bit unsigned value from the specified 8-bit address.
349
+ with self ._device as i2c :
350
+ self ._BUFFER [0 ] = (address | _COMMAND_BIT ) & 0xFF
351
+ i2c .write (self ._BUFFER , end = 1 , stop = False )
352
+ i2c .readinto (self ._BUFFER , end = 2 )
353
+ return (self ._BUFFER [1 ] << 8 ) | self ._BUFFER [0 ]
354
+
355
+ def _write_u8 (self , address , val ):
356
+ # Write an 8-bit unsigned value to the specified 8-bit address.
357
+ with self ._device as i2c :
358
+ self ._BUFFER [0 ] = (address | _COMMAND_BIT ) & 0xFF
359
+ self ._BUFFER [1 ] = val & 0xFF
360
+ i2c .write (self ._BUFFER , end = 2 )
361
+
362
+ def _write_u16 (self , address , val ):
363
+ # Write a 16-bit unsigned value to the specified 8-bit address.
364
+ with self ._device as i2c :
365
+ self ._BUFFER [0 ] = (address | _COMMAND_BIT ) & 0xFF
366
+ self ._BUFFER [1 ] = val & 0xFF
367
+ self ._BUFFER [2 ] = (val >> 8 ) & 0xFF
368
+ i2c .write (self ._BUFFER )
0 commit comments