-
Notifications
You must be signed in to change notification settings - Fork 3
/
adafruit_bh1750.py
243 lines (171 loc) · 7.11 KB
/
adafruit_bh1750.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
# SPDX-FileCopyrightText: 2017 Scott Shawcroft, written for Adafruit Industries
# SPDX-FileCopyrightText: Copyright (c) 2020 Bryan Siepert for Adafruit Industries
#
# SPDX-License-Identifier: MIT
"""
`adafruit_bh1750`
================================================================================
CircuitPython library for use with the Adafruit BH1750 breakout
* Author(s): Bryan Siepert
Implementation Notes
--------------------
**Hardware:**
* Adafruit `BH1750 Light Sensor
<https://www.adafruit.com/product/4681>`_ (Product ID: 4681)
**Software and Dependencies:**
* Adafruit CircuitPython firmware for the supported boards:
https://circuitpython.org/downloads
* Adafruit's Bus Device library: https://github.com/adafruit/Adafruit_CircuitPython_BusDevice
"""
# imports
__version__ = "0.0.0+auto.0"
__repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_BH1750.git"
from time import sleep
from struct import unpack_from
from micropython import const
from adafruit_bus_device import i2c_device
try:
from typing import Optional, List, Tuple, Type
from busio import I2C
except ImportError:
pass
_BH1750_DEVICE_ID = 0xE1 # Correct content of WHO_AM_I register
# I2C addresses (without R/W bit)
_BH1750_DEFAULT_ADDRESS = const(0x23) # I2C address with ADDR pin low
_BH1750_ALT_ADDRESS = const(0x5C) # I2C address with ADDR pin high
# Instructions
_BH1750_POWER_DOWN = const(0x00) # Power down instruction
_BH1750_POWER_ON = const(0x01) # Power on instruction
_BH1750_RESET = const(0x07) # Reset instruction
# Bitfields
_BH1750_MODE_MASK = const(0x30) # Mode mask bits
_BH1750_RES_MASK = const(0x03) # Mode resolution mask bits
# Worst case conversion timing in ms
_BH1750_CONV_TIME_L = const(24) # Worst case conversion timing low res
_BH1750_CONV_TIME_H = const(180) # Worst case conversion timing high res
# Change measurement time.
# ※ Please refer "adjust measurement result for influence of optical window."",
# 0b01000MT[7-5] # set measurement time high nibble
# 0b011MT[4-0] # set measurement time low nibble
class CV:
"""struct helper"""
@classmethod
def add_values(
cls, value_tuples: List[Tuple[str, int, str, Optional[int]]]
) -> None:
"""Add CV values to the class"""
cls.string = {}
cls.lsb = {}
for value_tuple in value_tuples:
name, value, string, lsb = value_tuple
setattr(cls, name, value)
cls.string[value] = string
cls.lsb[value] = lsb
@classmethod
def is_valid(cls, value: int) -> bool:
"""Validate that a given value is a member"""
return value in cls.string
class RWBitfields:
"""
A class to do bitwise operations to get and set a range of bits within a byte but
gets and sets the full byte value from the ``_settings`` attribute of the calling
object.
Values are `int` between 0 and :math:`2^num_bits - 1`
:param int num_bits: The number of bits in the field.
:param type lowest_bit: The lowest bits index within the byte at ``register_address``
"""
def __init__(self, num_bits: int, lowest_bit: int) -> None:
self._bit_mask = ((1 << num_bits) - 1) << lowest_bit
self._lowest_bit = lowest_bit
def __get__(self, obj: Optional["BH1750"], objtype: Type["BH1750"]) -> int:
return (obj._settings & self._bit_mask) >> self._lowest_bit
def __set__(self, obj: "BH1750", value: int) -> None:
# shift the value over to the right spot
value <<= self._lowest_bit
settings = obj._settings
# mask off the bits we're about to change
settings &= ~self._bit_mask
settings |= value # then or in our new value
obj._settings = settings
class Mode(CV):
"""Options for `mode`. Valid values are ``SHUTDOWN``, ``CONTINUOUS``, and ``ONE_SHOT``"""
pass # pylint: disable=unnecessary-pass
Mode.add_values(
(
("SHUTDOWN", 0, "Shutdown", None),
("CONTINUOUS", 1, "Continuous", None),
("ONE_SHOT", 2, "One Shot", None),
)
)
class Resolution(CV):
"""Options for `resolution` Valid values are ``LOW``, ``MID``, and ``HIGH``"""
pass # pylint: disable=unnecessary-pass
Resolution.add_values(
(
("LOW", 3, "Low", None), # 4 lx resolution "L-Resolution Mode" in DS
("MID", 0, "Mid", None), # 1 lx resolution "H-Resolution Mode" in DS
("HIGH", 1, "High", None), # 0.5 lx resolution, "H-Resolution Mode2" in DS
)
)
class BH1750: # pylint:disable=too-many-instance-attributes
"""Library for the BH1750 Sensor
:param ~busio.I2C i2c: The I2C bus the BH1750 is connected to.
:param int address: The I2C device address. Defaults to :const:`0x23`.Can be
set to :const:`0x5C` by pulling the address pin high.
**Quickstart: Importing and using the BH1750**
Here is an example of using the :class:`BH1750` class.
First you will need to import the libraries to use the sensor
.. code-block:: python
import board
import adafruit_bh1750
Once this is done you can define your `board.I2C` object and define your sensor object
.. code-block:: python
i2c = board.I2C() # uses board.SCL and board.SDA
sensor = adafruit_bh1750.BH1750(i2c)
Now you have access to the :attr:`lux` value in lux
.. code-block:: python
lux = sensor.lux
"""
mode = RWBitfields(2, 4)
"""The capture mode for the sensor. See `Mode` for valid values."""
resolution = RWBitfields(2, 0)
"""The resolution of the sensor. See `Resolution` for valid values."""
""""""
def __init__(self, i2c: I2C, address: int = _BH1750_DEFAULT_ADDRESS) -> None:
self.i2c_device = i2c_device.I2CDevice(i2c, address)
self._buffer = bytearray(2)
self._settings_byte = 0
self.initialize()
def initialize(self) -> None:
"""Configure the sensors with the default settings."""
self.mode = Mode.CONTINUOUS # pylint:disable=no-member
self.resolution = Resolution.HIGH # pylint:disable=no-member
@property
def _settings(self) -> int:
return self._settings_byte
@_settings.setter
def _settings(self, value: int) -> None:
self._settings_byte = value
self._write(self._settings_byte)
sleep(0.180) # worse case time to take a new measurement
@property
def _raw_reading(self) -> int:
self._buffer[0] = 0
self._buffer[1] = 0
with self.i2c_device as i2c:
i2c.readinto(self._buffer)
return unpack_from(">H", self._buffer)[0]
@property
def lux(self) -> float:
"""Light value in lux."""
raw_lux = self._raw_reading
return self._convert_to_lux(raw_lux)
def _convert_to_lux(self, raw_lux: int) -> float:
measured_lux = raw_lux / 1.2
if self.resolution == Resolution.HIGH: # pylint:disable=no-member
measured_lux = measured_lux / 2
return measured_lux
def _write(self, cmd_byte: int) -> None:
self._buffer[0] = cmd_byte
with self.i2c_device as i2c:
i2c.write(self._buffer, end=1)