# The MIT License (MIT)
#
# Copyright (c) 2017 Tony DiCola for Adafruit Industries
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
"""
`adafruit_tcs34725`
====================================================
CircuitPython module for the TCS34725 color sensor. Ported from the
micropython-adafruit-tcs34725 module by Radomir Dopieralski:
https://github.com/adafruit/micropython-adafruit-tcs34725
See examples/simpletest.py for an example of the usage.
* Author(s): Tony DiCola
"""
import time
import adafruit_bus_device.i2c_device as i2c_device
from micropython import const
__version__ = "0.0.0-auto.0"
__repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_TCS34725.git"
# Register and command constants:
# pylint: disable=bad-whitespace
_COMMAND_BIT = const(0x80)
_REGISTER_ENABLE = const(0x00)
_REGISTER_ATIME = const(0x01)
_REGISTER_AILT = const(0x04)
_REGISTER_AIHT = const(0x06)
_REGISTER_ID = const(0x12)
_REGISTER_APERS = const(0x0c)
_REGISTER_CONTROL = const(0x0f)
_REGISTER_SENSORID = const(0x12)
_REGISTER_STATUS = const(0x13)
_REGISTER_CDATA = const(0x14)
_REGISTER_RDATA = const(0x16)
_REGISTER_GDATA = const(0x18)
_REGISTER_BDATA = const(0x1a)
_ENABLE_AIEN = const(0x10)
_ENABLE_WEN = const(0x08)
_ENABLE_AEN = const(0x02)
_ENABLE_PON = const(0x01)
_GAINS = (1, 4, 16, 60)
_CYCLES = (0, 1, 2, 3, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60)
# pylint: enable=bad-whitespace
def _temperature_and_lux(data):
"""Convert the 4-tuple of raw RGBC data to color temperature and lux values. Will return
2-tuple of color temperature and lux."""
r, g, b, _ = data
x = -0.14282 * r + 1.54924 * g + -0.95641 * b
y = -0.32466 * r + 1.57837 * g + -0.73191 * b
z = -0.68202 * r + 0.77073 * g + 0.56332 * b
divisor = x + y + z
n = (x / divisor - 0.3320) / (0.1858 - y / divisor)
cct = 449.0 * n**3 + 3525.0 * n**2 + 6823.3 * n + 5520.33
return cct, y
[docs]class TCS34725:
"""Driver for the TCS34725 color sensor."""
# Class-level buffer for reading and writing data with the sensor.
# This reduces memory allocations but means the code is not re-entrant or
# thread safe!
_BUFFER = bytearray(3)
def __init__(self, i2c, address=0x29):
self._device = i2c_device.I2CDevice(i2c, address)
sensor_id = self._read_u8(_REGISTER_SENSORID)
self._active = False
self.integration_time = 2.4
# Check sensor ID is expectd value.
sensor_id = self._read_u8(_REGISTER_SENSORID)
if sensor_id not in (0x44, 0x10):
raise RuntimeError('Could not find sensor, check wiring!')
def _read_u8(self, address):
# Read an 8-bit unsigned value from the specified 8-bit address.
with self._device as i2c:
self._BUFFER[0] = (address | _COMMAND_BIT) & 0xFF
i2c.write(self._BUFFER, end=1, stop=False)
i2c.readinto(self._BUFFER, end=1)
return self._BUFFER[0]
def _read_u16(self, address):
# Read a 16-bit BE unsigned value from the specified 8-bit address.
with self._device as i2c:
self._BUFFER[0] = (address | _COMMAND_BIT) & 0xFF
i2c.write(self._BUFFER, end=1, stop=False)
i2c.readinto(self._BUFFER, end=2)
return (self._BUFFER[0] << 8) | self._BUFFER[1]
def _write_u8(self, address, val):
# Write an 8-bit unsigned value to the specified 8-bit address.
with self._device as i2c:
self._BUFFER[0] = (address | _COMMAND_BIT) & 0xFF
self._BUFFER[1] = val & 0xFF
i2c.write(self._BUFFER, end=2)
def _write_u16(self, address, val):
# Write a 16-bit BE unsigned value to the specified 8-bit address.
with self._device as i2c:
self._BUFFER[0] = (address | _COMMAND_BIT) & 0xFF
self._BUFFER[1] = (val >> 8) & 0xFF
self._BUFFER[2] = val & 0xFF
i2c.write(self._BUFFER)
@property
def active(self):
"""Get and set the active state of the sensor. Boolean value that will
enable/activate the sensor with a value of True and disable with a
value of False.
"""
return self._active
@active.setter
def active(self, val):
val = bool(val)
if self._active == val:
return
self._active = val
enable = self._read_u8(_REGISTER_ENABLE)
if val:
self._write_u8(_REGISTER_ENABLE, enable | _ENABLE_PON)
time.sleep(0.003)
self._write_u8(_REGISTER_ENABLE, enable | _ENABLE_PON | _ENABLE_AEN)
else:
self._write_u8(_REGISTER_ENABLE,
enable & ~(_ENABLE_PON | _ENABLE_AEN))
@property
def integration_time(self):
"""Get and set the integration time of the sensor in milliseconds."""
return self._integration_time
@integration_time.setter
def integration_time(self, val):
assert 2.4 <= val <= 614.4
cycles = int(val / 2.4)
self._integration_time = cycles * 2.4 # pylint: disable=attribute-defined-outside-init
self._write_u8(_REGISTER_ATIME, 256-cycles)
@property
def gain(self):
"""Get and set the gain of the sensor. Should be a value of 1, 4, 16,
or 60.
"""
return _GAINS[self._read_u8(_REGISTER_CONTROL)]
@gain.setter
def gain(self, val):
assert val in _GAINS
self._write_u8(_REGISTER_CONTROL, _GAINS.index(val))
@property
def interrupt(self):
"""Get and clear the interrupt of the sensor. Returns a bool that's
True if the interrupt is set. Can be set to False (and only False)
to clear the interrupt.
"""
return bool(self._read_u8(_REGISTER_STATUS) & _ENABLE_AIEN)
@interrupt.setter
def interrupt(self, val):
assert not val
with self._device:
self._device.write(b'\xe6')
def _valid(self):
# Check if the status bit is set and the chip is ready.
return bool(self._read_u8(_REGISTER_STATUS) & 0x01)
@property
def color_raw(self):
"""Read the raw RGBC color detected by the sensor. Returns a 4-tuple of
16-bit red, green, blue, clear component byte values (0-65535).
"""
was_active = self.active
self.active = True
while not self._valid():
time.sleep((self._integration_time + 0.9)/1000.0)
data = tuple(self._read_u16(reg) for reg in (
_REGISTER_RDATA,
_REGISTER_GDATA,
_REGISTER_BDATA,
_REGISTER_CDATA,
))
self.active = was_active
return data
@property
def color_rgb_bytes(self):
"""Read the RGB color detected by the sensor. Returns a 3-tuple of
red, green, blue component values as bytes (0-255).
"""
r, g, b, clear = self.color_raw
# pylint: disable=bad-whitespace
red = int(pow((int((r/clear) * 256) / 255), 2.5) * 255)
green = int(pow((int((g/clear) * 256) / 255), 2.5) * 255)
blue = int(pow((int((b/clear) * 256) / 255), 2.5) * 255)
return (red, green, blue)
@property
def temperature(self):
"""Return the detected color temperature in degrees."""
temp, _ = _temperature_and_lux(self.color_raw)
return temp
@property
def lux(self):
"""Return the detected light level in lux."""
_, lux = _temperature_and_lux(self.color_raw)
return lux
@property
def cycles(self):
"""Get and set the persistence cycles of the sensor."""
if self._read_u8(_REGISTER_ENABLE) & _ENABLE_AIEN:
return _CYCLES[self._read_u8(_REGISTER_APERS) & 0x0f]
return -1
@cycles.setter
def cycles(self, val):
enable = self._read_u8(_REGISTER_ENABLE)
if val == -1:
self._write_u8(_REGISTER_ENABLE, enable & ~(_ENABLE_AIEN))
else:
assert val in _CYCLES
self._write_u8(_REGISTER_ENABLE, enable | _ENABLE_AIEN)
self._write_u8(_REGISTER_APERS, _CYCLES.index(val))
@property
def min_value(self):
"""Get and set the minimum threshold value (AILT register) of the
sensor as a 16-bit unsigned value.
"""
return self._read_u16(_REGISTER_AILT)
@min_value.setter
def min_value(self, val):
self._write_u16(_REGISTER_AILT, val)
@property
def max_value(self):
"""Get and set the minimum threshold value (AIHT register) of the
sensor as a 16-bit unsigned value.
"""
return self._read_u16(_REGISTER_AIHT)
@max_value.setter
def max_value(self, val):
self._write_u16(_REGISTER_AIHT, val)