/ adafruit_veml6075.py
adafruit_veml6075.py
  1  # The MIT License (MIT)
  2  #
  3  # Copyright (c) 2018 ladyada 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_veml6075`
 24  ====================================================
 25  
 26  CircuitPython library to support VEML6075 UVA & UVB sensor.
 27  
 28  * Author(s): ladyada
 29  
 30  Implementation Notes
 31  --------------------
 32  
 33  **Hardware:**
 34  
 35  **Software and Dependencies:**
 36  
 37  * Adafruit CircuitPython firmware for the supported boards:
 38    https://github.com/adafruit/circuitpython/releases
 39  
 40  * Adafruit's Bus Device library: https://github.com/adafruit/Adafruit_CircuitPython_BusDevice
 41  
 42  """
 43  
 44  __version__ = "0.0.0-auto.0"
 45  __repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_VEML6075.git"
 46  
 47  # imports
 48  
 49  import time
 50  from adafruit_bus_device.i2c_device import I2CDevice
 51  from micropython import const
 52  
 53  # pylint: disable=bad-whitespace
 54  _VEML6075_ADDR = const(0x10)
 55  
 56  _REG_CONF = const(0x00)
 57  _REG_UVA = const(0x07)
 58  _REG_DARK = const(0x08)  # check is true?
 59  _REG_UVB = const(0x09)
 60  _REG_UVCOMP1 = const(0x0A)
 61  _REG_UVCOMP2 = const(0x0B)
 62  _REV_ID = const(0x0C)
 63  
 64  # Valid constants for UV Integration Time
 65  _VEML6075_UV_IT = {50: 0x00, 100: 0x01, 200: 0x02, 400: 0x03, 800: 0x04}
 66  # pylint: enable=bad-whitespace
 67  
 68  
 69  class VEML6075:
 70      """
 71      Driver base for the VEML6075 UV Light Sensor
 72      :param i2c_bus: The `busio.I2C` object to use. This is the only required parameter.
 73      :param int integration_time: The integration time you'd like to set initially. Availble
 74      options - each in milliseconds: 50, 100, 200, 400, 800.
 75      The higher the '_x_' value, the more accurate
 76      the reading is (at the cost of less samples per reading).
 77      Defaults to 100ms if parameter not passed. To change
 78      setting after intialization, use
 79      ``[veml6075].integration_time = new_it_value``.
 80      :param bool high_dynamic: whether to put sensor in 'high dynamic setting' mode
 81      :param float uva_a_coef: the UVA visible coefficient
 82      :param float uva_b_coef: the UVA IR coefficient
 83      :param float uvb_c_coef: the UVB visible coefficient
 84      :param float uvb_d_coef: the UVB IR coefficient
 85      :param float uva_response: the UVA responsivity
 86      :param float uvb_response: the UVA responsivity
 87      """
 88  
 89      def __init__(
 90          self,
 91          i2c_bus,
 92          *,
 93          integration_time=50,
 94          high_dynamic=True,
 95          uva_a_coef=2.22,
 96          uva_b_coef=1.33,
 97          uvb_c_coef=2.95,
 98          uvb_d_coef=1.74,
 99          uva_response=0.001461,
100          uvb_response=0.002591
101      ):
102          # Set coefficients
103          self._a = uva_a_coef
104          self._b = uva_b_coef
105          self._c = uvb_c_coef
106          self._d = uvb_d_coef
107          self._uvaresp = uva_response
108          self._uvbresp = uvb_response
109          self._uvacalc = self._uvbcalc = None
110  
111          # Init I2C
112          self._i2c = I2CDevice(i2c_bus, _VEML6075_ADDR)
113          self._buffer = bytearray(3)
114  
115          # read ID!
116          veml_id = self._read_register(_REV_ID)
117          if veml_id != 0x26:
118              raise RuntimeError("Incorrect VEML6075 ID 0x%02X" % veml_id)
119  
120          # shut down
121          self._write_register(_REG_CONF, 0x01)
122  
123          # Set integration time
124          self.integration_time = integration_time
125  
126          # enable
127          conf = self._read_register(_REG_CONF)
128          if high_dynamic:
129              conf |= 0x08
130          conf &= ~0x01  # Power on
131          self._write_register(_REG_CONF, conf)
132  
133      def _take_reading(self):
134          """Perform a full reading and calculation of all UV calibrated values"""
135          time.sleep(0.1)
136          uva = self._read_register(_REG_UVA)
137          uvb = self._read_register(_REG_UVB)
138          # dark = self._read_register(_REG_DARK)
139          uvcomp1 = self._read_register(_REG_UVCOMP1)
140          uvcomp2 = self._read_register(_REG_UVCOMP2)
141          # Equasion 1 & 2 in App note, without 'golden sample' calibration
142          self._uvacalc = uva - (self._a * uvcomp1) - (self._b * uvcomp2)
143          self._uvbcalc = uvb - (self._c * uvcomp1) - (self._d * uvcomp2)
144          # print("UVA = %d, UVB = %d, UVcomp1 = %d, UVcomp2 = %d, Dark = %d" %
145          #      (uva, uvb, uvcomp1, uvcomp2, dark))
146  
147      @property
148      def uva(self):
149          """The calibrated UVA reading, in 'counts' over the sample period"""
150          self._take_reading()
151          return self._uvacalc
152  
153      @property
154      def uvb(self):
155          """The calibrated UVB reading, in 'counts' over the sample period"""
156          self._take_reading()
157          return self._uvbcalc
158  
159      @property
160      def uv_index(self):
161          """The calculated UV Index"""
162          self._take_reading()
163          return ((self._uvacalc * self._uvaresp) + (self._uvbcalc * self._uvbresp)) / 2
164  
165      @property
166      def integration_time(self):
167          """The amount of time the VEML is sampling data for, in millis.
168          Valid times are 50, 100, 200, 400 or 800ms"""
169          key = (self._read_register(_REG_CONF) >> 4) & 0x7
170          for k, val in enumerate(_VEML6075_UV_IT):
171              if key == k:
172                  return val
173          raise RuntimeError("Invalid integration time")
174  
175      @integration_time.setter
176      def integration_time(self, val):
177          if not val in _VEML6075_UV_IT.keys():
178              raise RuntimeError("Invalid integration time")
179          conf = self._read_register(_REG_CONF)
180          conf &= ~0b01110000  # mask off bits 4:6
181          conf |= _VEML6075_UV_IT[val] << 4
182          self._write_register(_REG_CONF, conf)
183  
184      def _read_register(self, register):
185          """Read a 16-bit value from the `register` location"""
186          self._buffer[0] = register
187          with self._i2c as i2c:
188              i2c.write_then_readinto(self._buffer, self._buffer, out_end=1, in_end=2)
189          return (self._buffer[1] << 8) | self._buffer[0]
190  
191      def _write_register(self, register, value):
192          """Write a 16-bit value to the `register` location"""
193          self._buffer[0] = register
194          self._buffer[1] = value
195          self._buffer[2] = value >> 8
196          with self._i2c as i2c:
197              i2c.write(self._buffer)