adafruit-circuitpython-tcs3430 1.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,162 @@
1
+ Metadata-Version: 2.4
2
+ Name: adafruit-circuitpython-tcs3430
3
+ Version: 1.0.0
4
+ Summary: CircuitPython driver library for AMS TCS3430 / TCS34303 XYZ tri-stimulus color sensor.
5
+ Author-email: Adafruit Industries <circuitpython@adafruit.com>
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/adafruit/Adafruit_CircuitPython_TCS3430
8
+ Keywords: adafruit,blinka,circuitpython,micropython,tcs3430,rgb,light,sensor,tri-stimulus,xyz,tcs32303,light-sensor
9
+ Classifier: Intended Audience :: Developers
10
+ Classifier: Topic :: Software Development :: Libraries
11
+ Classifier: Topic :: Software Development :: Embedded Systems
12
+ Classifier: Topic :: System :: Hardware
13
+ Classifier: Programming Language :: Python :: 3
14
+ Description-Content-Type: text/x-rst
15
+ License-File: LICENSE
16
+ Requires-Dist: Adafruit-Blinka
17
+ Requires-Dist: adafruit-circuitpython-busdevice
18
+ Requires-Dist: adafruit-circuitpython-register
19
+ Provides-Extra: optional
20
+ Dynamic: license-file
21
+
22
+ Introduction
23
+ ============
24
+
25
+
26
+ .. image:: https://readthedocs.org/projects/adafruit-circuitpython-tcs3430/badge/?version=latest
27
+ :target: https://docs.circuitpython.org/projects/tcs3430/en/latest/
28
+ :alt: Documentation Status
29
+
30
+
31
+ .. image:: https://raw.githubusercontent.com/adafruit/Adafruit_CircuitPython_Bundle/main/badges/adafruit_discord.svg
32
+ :target: https://adafru.it/discord
33
+ :alt: Discord
34
+
35
+
36
+ .. image:: https://github.com/adafruit/Adafruit_CircuitPython_TCS3430/workflows/Build%20CI/badge.svg
37
+ :target: https://github.com/adafruit/Adafruit_CircuitPython_TCS3430/actions
38
+ :alt: Build Status
39
+
40
+
41
+ .. image:: https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json
42
+ :target: https://github.com/astral-sh/ruff
43
+ :alt: Code Style: Ruff
44
+
45
+ CircuitPython driver library for AMS TCS3430 / TCS34303 XYZ tri-stimulus color sensor.
46
+
47
+
48
+ Dependencies
49
+ =============
50
+ This driver depends on:
51
+
52
+ * `Adafruit CircuitPython <https://github.com/adafruit/circuitpython>`_
53
+ * `Bus Device <https://github.com/adafruit/Adafruit_CircuitPython_BusDevice>`_
54
+ * `Register <https://github.com/adafruit/Adafruit_CircuitPython_Register>`_
55
+
56
+ Please ensure all dependencies are available on the CircuitPython filesystem.
57
+ This is easily achieved by downloading
58
+ `the Adafruit library and driver bundle <https://circuitpython.org/libraries>`_
59
+ or individual libraries can be installed using
60
+ `circup <https://github.com/adafruit/circup>`_.
61
+
62
+
63
+ `Purchase one from the Adafruit shop <http://www.adafruit.com/products/6479>`_
64
+
65
+ Installing from PyPI
66
+ =====================
67
+
68
+ On supported GNU/Linux systems like the Raspberry Pi, you can install the driver locally `from
69
+ PyPI <https://pypi.org/project/adafruit-circuitpython-tcs3430/>`_.
70
+ To install for current user:
71
+
72
+ .. code-block:: shell
73
+
74
+ pip3 install adafruit-circuitpython-tcs3430
75
+
76
+ To install system-wide (this may be required in some cases):
77
+
78
+ .. code-block:: shell
79
+
80
+ sudo pip3 install adafruit-circuitpython-tcs3430
81
+
82
+ To install in a virtual environment in your current project:
83
+
84
+ .. code-block:: shell
85
+
86
+ mkdir project-name && cd project-name
87
+ python3 -m venv .venv
88
+ source .env/bin/activate
89
+ pip3 install adafruit-circuitpython-tcs3430
90
+
91
+ Installing to a Connected CircuitPython Device with Circup
92
+ ==========================================================
93
+
94
+ Make sure that you have ``circup`` installed in your Python environment.
95
+ Install it with the following command if necessary:
96
+
97
+ .. code-block:: shell
98
+
99
+ pip3 install circup
100
+
101
+ With ``circup`` installed and your CircuitPython device connected use the
102
+ following command to install:
103
+
104
+ .. code-block:: shell
105
+
106
+ circup install adafruit_tcs3430
107
+
108
+ Or the following command to update an existing version:
109
+
110
+ .. code-block:: shell
111
+
112
+ circup update
113
+
114
+ Usage Example
115
+ =============
116
+
117
+ .. code-block:: python
118
+
119
+ import time
120
+
121
+ import board
122
+
123
+ from adafruit_tcs3430 import TCS3430, ALSGain, InterruptPersistence
124
+
125
+ i2c = board.I2C()
126
+ tcs = TCS3430(i2c)
127
+
128
+ print("TCS3430 Basic Test")
129
+ print("TCS3430 found!")
130
+
131
+ # --- Tweak these settings for your environment ---
132
+ tcs.als_gain = ALSGain.GAIN_64X # 1X, 4X, 16X, 64X, or 128X
133
+ tcs.integration_time = 100.0 # 2.78ms to 711ms
134
+
135
+ # Enable ALS interrupt so we can poll AINT for data ready
136
+ tcs.als_interrupt_enabled = True
137
+ tcs.interrupt_persistence = InterruptPersistence.EVERY
138
+ tcs.clear_als_interrupt()
139
+
140
+ while True:
141
+ # Wait for new data
142
+ if tcs.als_interrupt:
143
+ x, y, z, ir1 = tcs.channels
144
+ print(f"X: {x} Y: {y} Z: {z} IR1: {ir1}")
145
+ tcs.clear_als_interrupt()
146
+
147
+ time.sleep(1.0)
148
+
149
+
150
+ Documentation
151
+ =============
152
+ API documentation for this library can be found on `Read the Docs <https://docs.circuitpython.org/projects/tcs3430/en/latest/>`_.
153
+
154
+ For information on building library documentation, please check out
155
+ `this guide <https://learn.adafruit.com/creating-and-sharing-a-circuitpython-library/sharing-our-docs-on-readthedocs#sphinx-5-1>`_.
156
+
157
+ Contributing
158
+ ============
159
+
160
+ Contributions are welcome! Please read our `Code of Conduct
161
+ <https://github.com/adafruit/Adafruit_CircuitPython_TCS3430/blob/HEAD/CODE_OF_CONDUCT.md>`_
162
+ before contributing to help this project stay welcoming.
@@ -0,0 +1,6 @@
1
+ adafruit_tcs3430.py,sha256=wSjjIIEMNyB54zt8sZ3dcFWo8O-8Tar3iQs4W8ka2R0,17563
2
+ adafruit_circuitpython_tcs3430-1.0.0.dist-info/licenses/LICENSE,sha256=IrDmGxfX4xdm-jZka6acotFMss63SMR3pHWyFp7jA3o,1100
3
+ adafruit_circuitpython_tcs3430-1.0.0.dist-info/METADATA,sha256=cZ9BAd4apvIpVcUEIrn19I6vIO9kbtAbIa0j4OWbasE,5187
4
+ adafruit_circuitpython_tcs3430-1.0.0.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
5
+ adafruit_circuitpython_tcs3430-1.0.0.dist-info/top_level.txt,sha256=dSUdVgXVIT64zVqg98R2PUSuZe4isQOAyd4LCJbLLlk,17
6
+ adafruit_circuitpython_tcs3430-1.0.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (82.0.1)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2026 Tim Cocks 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 all
13
+ 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 THE
21
+ SOFTWARE.
@@ -0,0 +1 @@
1
+ adafruit_tcs3430
adafruit_tcs3430.py ADDED
@@ -0,0 +1,441 @@
1
+ # SPDX-FileCopyrightText: Copyright (c) 2026 Tim Cocks for Adafruit Industries
2
+ #
3
+ # SPDX-License-Identifier: MIT
4
+ """
5
+ `adafruit_tcs3430`
6
+ ================================================================================
7
+
8
+ CircuitPython driver library for AMS TCS3430 / TCS34303 XYZ tri-stimulus color sensor.
9
+
10
+
11
+ * Author(s): Tim Cocks
12
+
13
+ Implementation Notes
14
+ --------------------
15
+
16
+ **Hardware:**
17
+
18
+ * `Adafruit TCS3430 / TCS34303 Ambient Tri-Stimulus Color Sensor <https://www.adafruit.com/product/6479>`_
19
+
20
+ **Software and Dependencies:**
21
+
22
+ * Adafruit CircuitPython firmware for the supported boards:
23
+ https://circuitpython.org/downloads
24
+
25
+ # * Adafruit's Bus Device library: https://github.com/adafruit/Adafruit_CircuitPython_BusDevice
26
+ # * Adafruit's Register library: https://github.com/adafruit/Adafruit_CircuitPython_Register
27
+ """
28
+
29
+ __version__ = "1.0.0"
30
+ __repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_TCS3430.git"
31
+
32
+ # imports
33
+ import time
34
+
35
+ from adafruit_bus_device import i2c_device
36
+ from adafruit_register.i2c_bit import RWBit
37
+ from adafruit_register.i2c_bits import ROBits, RWBits
38
+ from adafruit_register.i2c_struct import UnaryStruct
39
+ from micropython import const
40
+
41
+ try:
42
+ from typing import Tuple
43
+
44
+ import busio
45
+ except ImportError:
46
+ pass
47
+
48
+ # -----------------------------------------------------------------------
49
+ # I2C address
50
+ # -----------------------------------------------------------------------
51
+ _TCS3430_DEFAULT_ADDR = const(0x39)
52
+
53
+ # -----------------------------------------------------------------------
54
+ # Register addresses
55
+ # -----------------------------------------------------------------------
56
+ _TCS3430_REG_ENABLE = const(0x80) # Enable states and interrupts
57
+ _TCS3430_REG_ATIME = const(0x81) # ADC integration time
58
+ _TCS3430_REG_WTIME = const(0x83) # ALS wait time
59
+ _TCS3430_REG_AILTL = const(0x84) # ALS interrupt low threshold (16-bit LE)
60
+ _TCS3430_REG_AIHTL = const(0x86) # ALS interrupt high threshold (16-bit LE)
61
+ _TCS3430_REG_PERS = const(0x8C) # ALS interrupt persistence filters
62
+ _TCS3430_REG_CFG0 = const(0x8D) # Configuration register 0
63
+ _TCS3430_REG_CFG1 = const(0x90) # Configuration register 1
64
+ _TCS3430_REG_REVID = const(0x91) # Revision ID
65
+ _TCS3430_REG_ID = const(0x92) # Device ID
66
+ _TCS3430_REG_STATUS = const(0x93) # Device status
67
+ _TCS3430_REG_CH0DATAL = const(0x94) # Z channel data (16-bit LE)
68
+ _TCS3430_REG_CH1DATAL = const(0x96) # Y channel data (16-bit LE)
69
+ _TCS3430_REG_CH2DATAL = const(0x98) # IR1 channel data (16-bit LE)
70
+ _TCS3430_REG_CH3DATAL = const(0x9A) # X or IR2 channel data (16-bit LE)
71
+ _TCS3430_REG_CFG2 = const(0x9F) # Configuration register 2
72
+ _TCS3430_REG_CFG3 = const(0xAB) # Configuration register 3
73
+ _TCS3430_REG_AZ_CONFIG = const(0xD6) # Auto zero configuration
74
+ _TCS3430_REG_INTENAB = const(0xDD) # Interrupt enables
75
+
76
+ # Expected chip ID
77
+ _TCS3430_CHIP_ID = const(0xDC)
78
+
79
+
80
+ # -----------------------------------------------------------------------
81
+ # CV helper – same pattern as adafruit_apds9999
82
+ # -----------------------------------------------------------------------
83
+ class CV:
84
+ """Constant-value helper for enum-like classes."""
85
+
86
+ @classmethod
87
+ def is_valid(cls, value: int) -> bool:
88
+ """Validate that *value* is a member of this CV class."""
89
+ IGNORE = [cls.__module__, cls.__name__]
90
+ return value in cls.__dict__.values() and value not in IGNORE
91
+
92
+ @classmethod
93
+ def get_name(cls, value: int) -> str:
94
+ """Return the attribute name for *value*."""
95
+ for k, v in cls.__dict__.items():
96
+ if v == value:
97
+ return k
98
+ raise ValueError(f"Unknown value {value}")
99
+
100
+
101
+ # -----------------------------------------------------------------------
102
+ # Enum-like CV classes
103
+ # -----------------------------------------------------------------------
104
+ class ALSGain(CV):
105
+ """ALS gain settings for CFG1 register bits 1:0.
106
+
107
+ +-------------------------------+----------+
108
+ | Setting | Gain |
109
+ +===============================+==========+
110
+ | :py:const:`ALSGain.GAIN_1X` | 1x gain |
111
+ +-------------------------------+----------+
112
+ | :py:const:`ALSGain.GAIN_4X` | 4x gain |
113
+ +-------------------------------+----------+
114
+ | :py:const:`ALSGain.GAIN_16X` | 16x gain |
115
+ +-------------------------------+----------+
116
+ | :py:const:`ALSGain.GAIN_64X` | 64x gain |
117
+ +-------------------------------+----------+
118
+ | :py:const:`ALSGain.GAIN_128X` | 128x gain|
119
+ +-------------------------------+----------+
120
+ """
121
+
122
+ GAIN_1X = 0x00
123
+ GAIN_4X = 0x01
124
+ GAIN_16X = 0x02
125
+ GAIN_64X = 0x03
126
+ GAIN_128X = 0x04 # Requires HGAIN bit in CFG2
127
+
128
+
129
+ class InterruptPersistence(CV):
130
+ """ALS interrupt persistence filter values for PERS register bits 3:0.
131
+
132
+ +----------------------------------------------+------------------------------------+
133
+ | Setting | Description |
134
+ +==============================================+====================================+
135
+ | :py:const:`InterruptPersistence.EVERY` | Every ALS cycle |
136
+ +----------------------------------------------+------------------------------------+
137
+ | :py:const:`InterruptPersistence.CYCLES_1` | 1 consecutive value out of range |
138
+ +----------------------------------------------+------------------------------------+
139
+ | :py:const:`InterruptPersistence.CYCLES_2` | 2 consecutive values out of range |
140
+ +----------------------------------------------+------------------------------------+
141
+ | :py:const:`InterruptPersistence.CYCLES_3` | 3 consecutive values out of range |
142
+ +----------------------------------------------+------------------------------------+
143
+ | :py:const:`InterruptPersistence.CYCLES_5` | 5 consecutive values out of range |
144
+ +----------------------------------------------+------------------------------------+
145
+ | :py:const:`InterruptPersistence.CYCLES_10` | 10 consecutive values out of range |
146
+ +----------------------------------------------+------------------------------------+
147
+ | :py:const:`InterruptPersistence.CYCLES_15` | 15 consecutive values out of range |
148
+ +----------------------------------------------+------------------------------------+
149
+ | :py:const:`InterruptPersistence.CYCLES_20` | 20 consecutive values out of range |
150
+ +----------------------------------------------+------------------------------------+
151
+ | :py:const:`InterruptPersistence.CYCLES_25` | 25 consecutive values out of range |
152
+ +----------------------------------------------+------------------------------------+
153
+ | :py:const:`InterruptPersistence.CYCLES_30` | 30 consecutive values out of range |
154
+ +----------------------------------------------+------------------------------------+
155
+ | :py:const:`InterruptPersistence.CYCLES_35` | 35 consecutive values out of range |
156
+ +----------------------------------------------+------------------------------------+
157
+ | :py:const:`InterruptPersistence.CYCLES_40` | 40 consecutive values out of range |
158
+ +----------------------------------------------+------------------------------------+
159
+ | :py:const:`InterruptPersistence.CYCLES_45` | 45 consecutive values out of range |
160
+ +----------------------------------------------+------------------------------------+
161
+ | :py:const:`InterruptPersistence.CYCLES_50` | 50 consecutive values out of range |
162
+ +----------------------------------------------+------------------------------------+
163
+ | :py:const:`InterruptPersistence.CYCLES_55` | 55 consecutive values out of range |
164
+ +----------------------------------------------+------------------------------------+
165
+ | :py:const:`InterruptPersistence.CYCLES_60` | 60 consecutive values out of range |
166
+ +----------------------------------------------+------------------------------------+
167
+ """
168
+
169
+ EVERY = 0x00
170
+ CYCLES_1 = 0x01
171
+ CYCLES_2 = 0x02
172
+ CYCLES_3 = 0x03
173
+ CYCLES_5 = 0x04
174
+ CYCLES_10 = 0x05
175
+ CYCLES_15 = 0x06
176
+ CYCLES_20 = 0x07
177
+ CYCLES_25 = 0x08
178
+ CYCLES_30 = 0x09
179
+ CYCLES_35 = 0x0A
180
+ CYCLES_40 = 0x0B
181
+ CYCLES_45 = 0x0C
182
+ CYCLES_50 = 0x0D
183
+ CYCLES_55 = 0x0E
184
+ CYCLES_60 = 0x0F
185
+
186
+
187
+ # -----------------------------------------------------------------------
188
+ # Driver class
189
+ # -----------------------------------------------------------------------
190
+ class TCS3430:
191
+ """CircuitPython driver for the AMS TCS3430 XYZ Color and ALS Sensor.
192
+
193
+ :param ~busio.I2C i2c_bus: The I2C bus the device is connected to.
194
+ :param int address: The I2C device address. Defaults to :const:`0x39`.
195
+ """
196
+
197
+ # -- ENABLE register (0x80) bits --
198
+ power_on = RWBit(_TCS3430_REG_ENABLE, 0) # PON
199
+ """Power on/off the sensor. True for on, False for off."""
200
+
201
+ als_enabled = RWBit(_TCS3430_REG_ENABLE, 1) # AEN
202
+ """Enable/disable ALS functionality"""
203
+
204
+ wait_enabled = RWBit(_TCS3430_REG_ENABLE, 3) # WEN
205
+ """Enable/disable wait functionality"""
206
+
207
+ # -- ATIME register (0x81) – full byte --
208
+ integration_cycles = UnaryStruct(_TCS3430_REG_ATIME, "B")
209
+ """The number of integration cycles (1-256)"""
210
+
211
+ # -- WTIME register (0x83) – full byte --
212
+ wait_cycles = UnaryStruct(_TCS3430_REG_WTIME, "B")
213
+ """The number of wait cycles (1-256)"""
214
+
215
+ # -- ALS interrupt thresholds (16-bit little-endian) --
216
+ als_threshold_low = UnaryStruct(_TCS3430_REG_AILTL, "<H")
217
+ """The low threshold value for ALS"""
218
+
219
+ als_threshold_high = UnaryStruct(_TCS3430_REG_AIHTL, "<H")
220
+ """The high threshold value for ALS"""
221
+
222
+ # -- PERS register (0x8C), bits 3:0 --
223
+ interrupt_persistence = RWBits(4, _TCS3430_REG_PERS, 0)
224
+ """Enable/disable The interrupt persistence functionality"""
225
+
226
+ # -- CFG0 register (0x8D) --
227
+ wait_long = RWBit(_TCS3430_REG_CFG0, 2) # WLONG
228
+ """Enable/disable longer wait functionality. True to enable 12x time multiplier"""
229
+
230
+ # -- CFG1 register (0x90) --
231
+ _als_gain = RWBits(2, _TCS3430_REG_CFG1, 0) # AGAIN bits 1:0
232
+ als_mux_ir2 = RWBit(_TCS3430_REG_CFG1, 3) # AMUX
233
+ """ALS MUX setting for IR2 or X channel. True for IR2, False for X."""
234
+
235
+ # -- REVID register (0x91) – read-only --
236
+ rev_id = ROBits(8, _TCS3430_REG_REVID, 0)
237
+ """Revision ID"""
238
+
239
+ # -- ID register (0x92) – read-only --
240
+ chip_id = ROBits(8, _TCS3430_REG_ID, 0)
241
+ """Chip ID"""
242
+
243
+ # -- STATUS register (0x93) --
244
+ _status = UnaryStruct(_TCS3430_REG_STATUS, "B")
245
+ _als_interrupt_status = ROBits(1, _TCS3430_REG_STATUS, 4) # AINT
246
+ _als_saturated_status = ROBits(1, _TCS3430_REG_STATUS, 7) # ASAT
247
+
248
+ # -- Individual Channel data (16-bit little-endian) --
249
+ _channel_x_or_ir2 = UnaryStruct(_TCS3430_REG_CH3DATAL, "<H") # CH3 = X or IR2
250
+
251
+ # -- All 4 channels as a single 8-byte burst read (Z, Y, IR1, X/IR2) --
252
+ _channel_data_raw = ROBits(64, _TCS3430_REG_CH0DATAL, 0, register_width=8)
253
+
254
+ # -- CFG2 register (0x9F) --
255
+ _hgain = RWBit(_TCS3430_REG_CFG2, 4) # HGAIN
256
+
257
+ # -- CFG3 register (0xAB) --
258
+ interrupt_clear_on_read = RWBit(_TCS3430_REG_CFG3, 7) # INT_READ_CLEAR
259
+ """Enable/disable interrupt clear on reading"""
260
+
261
+ sleep_after_interrupt = RWBit(_TCS3430_REG_CFG3, 4) # SAI
262
+ """Enable/disable sleep after interrupt"""
263
+
264
+ # -- AZ_CONFIG register (0xD6) --
265
+ auto_zero_mode = RWBit(_TCS3430_REG_AZ_CONFIG, 7) # AZ_MODE
266
+ """Enable/disable auto-zero mode"""
267
+
268
+ auto_zero_nth = RWBits(7, _TCS3430_REG_AZ_CONFIG, 0) # AZ_NTH_ITERATION
269
+ """Auto-zero interval. Run auto-zero every N measurements"""
270
+
271
+ # -- INTENAB register (0xDD) --
272
+ saturation_interrupt_enabled = RWBit(_TCS3430_REG_INTENAB, 7) # ASIEN
273
+ """Enable/disable saturation interrupt"""
274
+
275
+ als_interrupt_enabled = RWBit(_TCS3430_REG_INTENAB, 4) # AIEN
276
+ """Enable/disable als interrupt"""
277
+
278
+ def __init__(self, i2c_bus: "busio.I2C", address: int = _TCS3430_DEFAULT_ADDR) -> None:
279
+ self.i2c_device = i2c_device.I2CDevice(i2c_bus, address)
280
+
281
+ # Verify chip ID
282
+ chip_id = self.chip_id
283
+ if chip_id != _TCS3430_CHIP_ID:
284
+ raise RuntimeError(
285
+ "Failed to find TCS3430 – check your wiring! "
286
+ + f"Expected ID 0x{_TCS3430_CHIP_ID:02X}, "
287
+ + f"got 0x{chip_id:02X}."
288
+ )
289
+
290
+ # Power on and enable ALS (matches Arduino begin())
291
+ self.power_on = True
292
+ self.als_enabled = True
293
+
294
+ # -----------------------------------------------------------------
295
+ # Integration time (convenience conversions around integration_cycles)
296
+ # -----------------------------------------------------------------
297
+ @property
298
+ def integration_time(self) -> float:
299
+ """Integration time in milliseconds.
300
+
301
+ Computed from :attr:`integration_cycles` as ``(cycles + 1) * 2.78``.
302
+ Setting this property writes the closest cycle count back to the
303
+ ATIME register.
304
+ """
305
+ return (self.integration_cycles + 1) * 2.78
306
+
307
+ @integration_time.setter
308
+ def integration_time(self, ms: float) -> None:
309
+ self.integration_cycles = int(ms / 2.78 - 1)
310
+
311
+ # -----------------------------------------------------------------
312
+ # Wait time (convenience conversions around wait_cycles)
313
+ # -----------------------------------------------------------------
314
+ @property
315
+ def wait_time(self) -> float:
316
+ """Wait time in milliseconds.
317
+
318
+ Computed from :attr:`wait_cycles` as ``(cycles + 1) * 2.78``.
319
+ When :attr:`wait_long` is enabled the actual wait is multiplied by 12.
320
+ Setting this property writes the closest cycle count back to the
321
+ WTIME register (it does **not** account for :attr:`wait_long`).
322
+ """
323
+ return (self.wait_cycles + 1) * 2.78
324
+
325
+ @wait_time.setter
326
+ def wait_time(self, ms: float) -> None:
327
+ self.wait_cycles = int(ms / 2.78 - 1)
328
+
329
+ # -----------------------------------------------------------------
330
+ # ALS gain (spans CFG1 AGAIN bits and CFG2 HGAIN bit)
331
+ # -----------------------------------------------------------------
332
+ @property
333
+ def als_gain(self) -> int:
334
+ """ALS analogue gain.
335
+
336
+ Must be an :class:`ALSGain` value. ``ALSGain.GAIN_128X`` is
337
+ achieved by setting the hardware gain to 64x **and** asserting the
338
+ HGAIN bit in CFG2.
339
+
340
+ * ``ALSGain.GAIN_1X``
341
+ * ``ALSGain.GAIN_4X``
342
+ * ``ALSGain.GAIN_16X``
343
+ * ``ALSGain.GAIN_64X``
344
+ * ``ALSGain.GAIN_128X``
345
+ """
346
+ if self._als_gain == ALSGain.GAIN_64X and self._hgain:
347
+ return ALSGain.GAIN_128X
348
+ return self._als_gain
349
+
350
+ @als_gain.setter
351
+ def als_gain(self, value: int) -> None:
352
+ if not ALSGain.is_valid(value):
353
+ raise ValueError("als_gain must be an ALSGain value")
354
+ if value == ALSGain.GAIN_128X:
355
+ self._als_gain = ALSGain.GAIN_64X
356
+ self._hgain = True
357
+ else:
358
+ self._als_gain = value
359
+ self._hgain = False
360
+
361
+ # -----------------------------------------------------------------
362
+ # Status helpers
363
+ # -----------------------------------------------------------------
364
+ @property
365
+ def als_saturated(self) -> bool:
366
+ """``True`` if the ALS data is saturated (read-only).
367
+
368
+ Use :meth:`clear_als_saturated` to clear the flag.
369
+ """
370
+ return bool(self._als_saturated_status)
371
+
372
+ def clear_als_saturated(self) -> None:
373
+ """Clear the ALS saturation flag by writing 0x80 to the STATUS register."""
374
+ self._status = 0x80
375
+
376
+ @property
377
+ def als_interrupt(self) -> bool:
378
+ """``True`` if the ALS interrupt flag is set (read-only).
379
+
380
+ Use :meth:`clear_als_interrupt` to clear the flag.
381
+ """
382
+ return bool(self._als_interrupt_status)
383
+
384
+ def clear_als_interrupt(self) -> None:
385
+ """Clear the ALS interrupt (and all other status flags) by writing
386
+ 0xFF to the STATUS register.
387
+
388
+ .. note::
389
+ If the threshold condition still exists and ALS is running,
390
+ the interrupt will re-fire on the next integration cycle.
391
+ """
392
+ self._status = 0xFF
393
+
394
+ # -----------------------------------------------------------------
395
+ # Channel reads
396
+ # -----------------------------------------------------------------
397
+ @property
398
+ def channels(self) -> Tuple[int, int, int, int]:
399
+ """Read the X, Y, Z, and IR1 channels in a single burst.
400
+
401
+ If the ALS MUX is currently set to IR2 mode, it is temporarily
402
+ switched back to X mode for the read and then restored.
403
+
404
+ Returns a tuple ``(x, y, z, ir1)`` of 16-bit unsigned values.
405
+ """
406
+ was_ir2 = self.als_mux_ir2
407
+ if was_ir2:
408
+ self.als_mux_ir2 = False
409
+
410
+ raw = self._channel_data_raw
411
+ z = raw & 0xFFFF
412
+ y = (raw >> 16) & 0xFFFF
413
+ ir1 = (raw >> 32) & 0xFFFF
414
+ x = (raw >> 48) & 0xFFFF
415
+
416
+ if was_ir2:
417
+ self.als_mux_ir2 = True
418
+
419
+ return (x, y, z, ir1)
420
+
421
+ @property
422
+ def ir2(self) -> int:
423
+ """Read the IR2 channel value.
424
+
425
+ This temporarily switches the ALS MUX to IR2, waits one integration
426
+ period for fresh data, reads CH3, and restores the previous MUX
427
+ setting.
428
+ """
429
+ was_ir2 = self.als_mux_ir2
430
+ if not was_ir2:
431
+ self.als_mux_ir2 = True
432
+
433
+ # Wait for one full integration cycle so data is fresh
434
+ time.sleep(self.integration_time / 1000.0)
435
+
436
+ value = self._channel_x_or_ir2
437
+
438
+ if not was_ir2:
439
+ self.als_mux_ir2 = False
440
+
441
+ return value