/ adafruit_tsl2591.py
adafruit_tsl2591.py
1 # The MIT License (MIT) 2 # 3 # Copyright (c) 2017 Tony DiCola for Adafruit Industries 4 # 5 # Permission is hereby granted, free of charge, to any person obtaining a copy 6 # of this software and associated documentation files (the "Software"), to deal 7 # in the Software without restriction, including without limitation the rights 8 # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 # copies of the Software, and to permit persons to whom the Software is 10 # furnished to do so, subject to the following conditions: 11 # 12 # The above copyright notice and this permission notice shall be included in 13 # all copies or substantial portions of the Software. 14 # 15 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 # THE SOFTWARE. 22 """ 23 `adafruit_tsl2591` 24 ==================================================== 25 26 CircuitPython module for the TSL2591 precision light sensor. See 27 examples/simpletest.py for a demo of the usage. 28 29 * Author(s): Tony DiCola 30 31 Implementation Notes 32 -------------------- 33 34 **Hardware:** 35 36 * Adafruit `TSL2591 High Dynamic Range Digital Light Sensor 37 <https://www.adafruit.com/product/1980>`_ (Product ID: 1980) 38 39 **Software and Dependencies:** 40 41 * Adafruit CircuitPython firmware for the ESP8622 and M0-based boards: 42 https://github.com/adafruit/circuitpython/releases 43 * Adafruit's Bus Device library: https://github.com/adafruit/Adafruit_CircuitPython_BusDevice 44 """ 45 from micropython import const 46 47 import adafruit_bus_device.i2c_device as i2c_device 48 49 50 __version__ = "0.0.0-auto.0" 51 __repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_TSL2591.git" 52 53 54 # pylint: disable=bad-whitespace 55 # Internal constants: 56 _TSL2591_ADDR = const(0x29) 57 _TSL2591_COMMAND_BIT = const(0xA0) 58 _TSL2591_ENABLE_POWEROFF = const(0x00) 59 _TSL2591_ENABLE_POWERON = const(0x01) 60 _TSL2591_ENABLE_AEN = const(0x02) 61 _TSL2591_ENABLE_AIEN = const(0x10) 62 _TSL2591_ENABLE_NPIEN = const(0x80) 63 _TSL2591_REGISTER_ENABLE = const(0x00) 64 _TSL2591_REGISTER_CONTROL = const(0x01) 65 _TSL2591_REGISTER_DEVICE_ID = const(0x12) 66 _TSL2591_REGISTER_CHAN0_LOW = const(0x14) 67 _TSL2591_REGISTER_CHAN1_LOW = const(0x16) 68 _TSL2591_LUX_DF = 408.0 69 _TSL2591_LUX_COEFB = 1.64 70 _TSL2591_LUX_COEFC = 0.59 71 _TSL2591_LUX_COEFD = 0.86 72 _TSL2591_MAX_COUNT_100MS = const(36863) # 0x8FFF 73 _TSL2591_MAX_COUNT = const(65535) # 0xFFFF 74 75 # User-facing constants: 76 GAIN_LOW = 0x00 # low gain (1x) 77 """Low gain (1x)""" 78 GAIN_MED = 0x10 # medium gain (25x) 79 """Medium gain (25x)""" 80 GAIN_HIGH = 0x20 # medium gain (428x) 81 """High gain (428x)""" 82 GAIN_MAX = 0x30 # max gain (9876x) 83 """Max gain (9876x)""" 84 INTEGRATIONTIME_100MS = 0x00 # 100 millis 85 """100 millis""" 86 INTEGRATIONTIME_200MS = 0x01 # 200 millis 87 """200 millis""" 88 INTEGRATIONTIME_300MS = 0x02 # 300 millis 89 """300 millis""" 90 INTEGRATIONTIME_400MS = 0x03 # 400 millis 91 """400 millis""" 92 INTEGRATIONTIME_500MS = 0x04 # 500 millis 93 """500 millis""" 94 INTEGRATIONTIME_600MS = 0x05 # 600 millis 95 """600 millis""" 96 # pylint: enable=bad-whitespace 97 98 99 class TSL2591: 100 """TSL2591 high precision light sensor. 101 :param busio.I2C i2c: The I2C bus connected to the sensor 102 :param int address: The I2C address of the sensor. If not specified 103 the sensor default will be used. 104 """ 105 106 # Class-level buffer to reduce memory usage and allocations. 107 # Note this is NOT thread-safe or re-entrant by design. 108 _BUFFER = bytearray(2) 109 110 def __init__(self, i2c, address=_TSL2591_ADDR): 111 self._integration_time = 0 112 self._gain = 0 113 self._device = i2c_device.I2CDevice(i2c, address) 114 # Verify the chip ID. 115 if self._read_u8(_TSL2591_REGISTER_DEVICE_ID) != 0x50: 116 raise RuntimeError("Failed to find TSL2591, check wiring!") 117 # Set default gain and integration times. 118 self.gain = GAIN_MED 119 self.integration_time = INTEGRATIONTIME_100MS 120 # Put the device in a powered on state after initialization. 121 self.enable() 122 123 def _read_u8(self, address): 124 # Read an 8-bit unsigned value from the specified 8-bit address. 125 with self._device as i2c: 126 # Make sure to add command bit to read request. 127 self._BUFFER[0] = (_TSL2591_COMMAND_BIT | address) & 0xFF 128 i2c.write_then_readinto(self._BUFFER, self._BUFFER, out_end=1, in_end=1) 129 return self._BUFFER[0] 130 131 # Disable invalid name check since pylint isn't smart enough to know LE 132 # is an abbreviation for little-endian. 133 # pylint: disable=invalid-name 134 def _read_u16LE(self, address): 135 # Read a 16-bit little-endian unsigned value from the specified 8-bit 136 # address. 137 with self._device as i2c: 138 # Make sure to add command bit to read request. 139 self._BUFFER[0] = (_TSL2591_COMMAND_BIT | address) & 0xFF 140 i2c.write_then_readinto(self._BUFFER, self._BUFFER, out_end=1, in_end=2) 141 return (self._BUFFER[1] << 8) | self._BUFFER[0] 142 143 # pylint: enable=invalid-name 144 145 def _write_u8(self, address, val): 146 # Write an 8-bit unsigned value to the specified 8-bit address. 147 with self._device as i2c: 148 # Make sure to add command bit to write request. 149 self._BUFFER[0] = (_TSL2591_COMMAND_BIT | address) & 0xFF 150 self._BUFFER[1] = val & 0xFF 151 i2c.write(self._BUFFER, end=2) 152 153 def enable(self): 154 """Put the device in a fully powered enabled mode.""" 155 self._write_u8( 156 _TSL2591_REGISTER_ENABLE, 157 _TSL2591_ENABLE_POWERON 158 | _TSL2591_ENABLE_AEN 159 | _TSL2591_ENABLE_AIEN 160 | _TSL2591_ENABLE_NPIEN, 161 ) 162 163 def disable(self): 164 """Disable the device and go into low power mode.""" 165 self._write_u8(_TSL2591_REGISTER_ENABLE, _TSL2591_ENABLE_POWEROFF) 166 167 @property 168 def gain(self): 169 """Get and set the gain of the sensor. Can be a value of: 170 171 - ``GAIN_LOW`` (1x) 172 - ``GAIN_MED`` (25x) 173 - ``GAIN_HIGH`` (428x) 174 - ``GAIN_MAX`` (9876x) 175 """ 176 control = self._read_u8(_TSL2591_REGISTER_CONTROL) 177 return control & 0b00110000 178 179 @gain.setter 180 def gain(self, val): 181 assert val in (GAIN_LOW, GAIN_MED, GAIN_HIGH, GAIN_MAX) 182 # Set appropriate gain value. 183 control = self._read_u8(_TSL2591_REGISTER_CONTROL) 184 control &= 0b11001111 185 control |= val 186 self._write_u8(_TSL2591_REGISTER_CONTROL, control) 187 # Keep track of gain for future lux calculations. 188 self._gain = val 189 190 @property 191 def integration_time(self): 192 """Get and set the integration time of the sensor. Can be a value of: 193 194 - ``INTEGRATIONTIME_100MS`` (100 millis) 195 - ``INTEGRATIONTIME_200MS`` (200 millis) 196 - ``INTEGRATIONTIME_300MS`` (300 millis) 197 - ``INTEGRATIONTIME_400MS`` (400 millis) 198 - ``INTEGRATIONTIME_500MS`` (500 millis) 199 - ``INTEGRATIONTIME_600MS`` (600 millis) 200 """ 201 control = self._read_u8(_TSL2591_REGISTER_CONTROL) 202 return control & 0b00000111 203 204 @integration_time.setter 205 def integration_time(self, val): 206 assert 0 <= val <= 5 207 # Set control bits appropriately. 208 control = self._read_u8(_TSL2591_REGISTER_CONTROL) 209 control &= 0b11111000 210 control |= val 211 self._write_u8(_TSL2591_REGISTER_CONTROL, control) 212 # Keep track of integration time for future reading delay times. 213 self._integration_time = val 214 215 @property 216 def raw_luminosity(self): 217 """Read the raw luminosity from the sensor (both IR + visible and IR 218 only channels) and return a 2-tuple of those values. The first value 219 is IR + visible luminosity (channel 0) and the second is the IR only 220 (channel 1). Both values are 16-bit unsigned numbers (0-65535). 221 """ 222 # Read both the luminosity channels. 223 channel_0 = self._read_u16LE(_TSL2591_REGISTER_CHAN0_LOW) 224 channel_1 = self._read_u16LE(_TSL2591_REGISTER_CHAN1_LOW) 225 return (channel_0, channel_1) 226 227 @property 228 def full_spectrum(self): 229 """Read the full spectrum (IR + visible) light and return its value 230 as a 32-bit unsigned number. 231 """ 232 channel_0, channel_1 = self.raw_luminosity 233 return (channel_1 << 16) | channel_0 234 235 @property 236 def infrared(self): 237 """Read the infrared light and return its value as a 16-bit unsigned number. 238 """ 239 _, channel_1 = self.raw_luminosity 240 return channel_1 241 242 @property 243 def visible(self): 244 """Read the visible light and return its value as a 32-bit unsigned number. 245 """ 246 channel_0, channel_1 = self.raw_luminosity 247 full = (channel_1 << 16) | channel_0 248 return full - channel_1 249 250 @property 251 def lux(self): 252 """Read the sensor and calculate a lux value from both its infrared 253 and visible light channels. Note: ``lux`` is not calibrated! 254 """ 255 channel_0, channel_1 = self.raw_luminosity 256 257 # Compute the atime in milliseconds 258 atime = 100.0 * self._integration_time + 100.0 259 260 # Set the maximum sensor counts based on the integration time (atime) setting 261 if self._integration_time == INTEGRATIONTIME_100MS: 262 max_counts = _TSL2591_MAX_COUNT_100MS 263 else: 264 max_counts = _TSL2591_MAX_COUNT 265 266 # Handle overflow. 267 if channel_0 >= max_counts or channel_1 >= max_counts: 268 raise RuntimeError("Overflow reading light channels!") 269 # Calculate lux using same equation as Arduino library: 270 # https://github.com/adafruit/Adafruit_TSL2591_Library/blob/master/Adafruit_TSL2591.cpp 271 again = 1.0 272 if self._gain == GAIN_MED: 273 again = 25.0 274 elif self._gain == GAIN_HIGH: 275 again = 428.0 276 elif self._gain == GAIN_MAX: 277 again = 9876.0 278 cpl = (atime * again) / _TSL2591_LUX_DF 279 lux1 = (channel_0 - (_TSL2591_LUX_COEFB * channel_1)) / cpl 280 lux2 = ( 281 (_TSL2591_LUX_COEFC * channel_0) - (_TSL2591_LUX_COEFD * channel_1) 282 ) / cpl 283 return max(lux1, lux2)