/ adafruit_am2320.py
adafruit_am2320.py
  1  # The MIT License (MIT)
  2  #
  3  # Copyright (c) 2018 Limor Fried
  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_am2320`
 24  ====================================================
 25  
 26  This is a CircuitPython driver for the AM2320 temperature and humidity sensor.
 27  
 28  * Author(s): Limor Fried
 29  
 30  Implementation Notes
 31  --------------------
 32  
 33  **Hardware:**
 34  
 35  * Adafruit `AM2320 Temperature & Humidity Sensor
 36    <https://www.adafruit.com/product/3721>`_ (Product ID: 3721)
 37  
 38  **Software and Dependencies:**
 39  
 40  * Adafruit CircuitPython firmware for the ESP8622 and M0-based boards:
 41      https://github.com/adafruit/circuitpython/releases
 42  * Adafruit's Bus Device library: https://github.com/adafruit/Adafruit_CircuitPython_BusDevice
 43  
 44  """
 45  
 46  # imports
 47  try:
 48      import struct
 49  except ImportError:
 50      import ustruct as struct
 51  
 52  import time
 53  
 54  from adafruit_bus_device.i2c_device import I2CDevice
 55  from micropython import const
 56  
 57  __version__ = "0.0.0-auto.0"
 58  __repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_am2320.git"
 59  
 60  
 61  AM2320_DEFAULT_ADDR = const(0x5C)
 62  AM2320_CMD_READREG = const(0x03)
 63  AM2320_REG_TEMP_H = const(0x02)
 64  AM2320_REG_HUM_H = const(0x00)
 65  
 66  
 67  def _crc16(data):
 68      crc = 0xFFFF
 69      for byte in data:
 70          crc ^= byte
 71          for _ in range(8):
 72              if crc & 0x0001:
 73                  crc >>= 1
 74                  crc ^= 0xA001
 75              else:
 76                  crc >>= 1
 77      return crc
 78  
 79  
 80  class AM2320:
 81      """A driver for the AM2320 temperature and humidity sensor.
 82  
 83      :param i2c_bus: The `busio.I2C` object to use. This is the only required parameter.
 84      :param int address: (optional) The I2C address of the device.
 85  
 86      """
 87  
 88      def __init__(self, i2c_bus, address=AM2320_DEFAULT_ADDR):
 89          for _ in range(3):
 90              # retry since we have to wake up the devices
 91              try:
 92                  self._i2c = I2CDevice(i2c_bus, address)
 93                  return
 94              except ValueError:
 95                  pass
 96              time.sleep(0.25)
 97          raise ValueError("AM2320 not found")
 98  
 99      def _read_register(self, register, length):
100          with self._i2c as i2c:
101              # wake up sensor
102              try:
103                  i2c.write(bytes([0x00]))
104              except OSError:
105                  pass
106              time.sleep(0.01)  # wait 10 ms
107  
108              # Send command to read register
109              cmd = [AM2320_CMD_READREG, register & 0xFF, length]
110              # print("cmd: %s" % [hex(i) for i in cmd])
111              i2c.write(bytes(cmd))
112              time.sleep(0.002)  # wait 2 ms for reply
113              result = bytearray(length + 4)  # 2 bytes pre, 2 bytes crc
114              i2c.readinto(result)
115              # print("$%02X => %s" % (register, [hex(i) for i in result]))
116              # Check preamble indicates correct readings
117              if result[0] != 0x3 or result[1] != length:
118                  raise RuntimeError("I2C read failure")
119              # Check CRC on all but last 2 bytes
120              crc1 = struct.unpack("<H", bytes(result[-2:]))[0]
121              crc2 = _crc16(result[0:-2])
122              if crc1 != crc2:
123                  raise RuntimeError("CRC failure 0x%04X vs 0x%04X" % (crc1, crc2))
124              return result[2:-2]
125  
126      @property
127      def temperature(self):
128          """The measured temperature in celsius."""
129          temperature = struct.unpack(">H", self._read_register(AM2320_REG_TEMP_H, 2))[0]
130          if temperature >= 32768:
131              temperature = 32768 - temperature
132          return temperature / 10.0
133  
134      @property
135      def relative_humidity(self):
136          """The measured relative humidity in percent."""
137          humidity = struct.unpack(">H", self._read_register(AM2320_REG_HUM_H, 2))[0]
138          return humidity / 10.0