/ adafruit_mpl3115a2.py
adafruit_mpl3115a2.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_mpl3115a2` 24 ==================================================== 25 26 CircuitPython module for the MPL3115A2 barometric pressure & temperature sensor. 27 See examples/simpletest.py for a demo of the usage. 28 29 * Author(s): Tony DiCola 30 """ 31 import time 32 33 from micropython import const 34 35 try: 36 import ustruct as struct 37 except ImportError: 38 import struct 39 40 import adafruit_bus_device.i2c_device as i2c_device 41 42 43 __version__ = "0.0.0-auto.0" 44 __repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_MPL3115A2.git" 45 46 47 # pylint: disable=bad-whitespace 48 # Internal constants: 49 _MPL3115A2_ADDRESS = const(0x60) 50 _MPL3115A2_REGISTER_STATUS = const(0x00) 51 _MPL3115A2_REGISTER_PRESSURE_MSB = const(0x01) 52 _MPL3115A2_REGISTER_PRESSURE_CSB = const(0x02) 53 _MPL3115A2_REGISTER_PRESSURE_LSB = const(0x03) 54 _MPL3115A2_REGISTER_TEMP_MSB = const(0x04) 55 _MPL3115A2_REGISTER_TEMP_LSB = const(0x05) 56 _MPL3115A2_REGISTER_DR_STATUS = const(0x06) 57 _MPL3115A2_OUT_P_DELTA_MSB = const(0x07) 58 _MPL3115A2_OUT_P_DELTA_CSB = const(0x08) 59 _MPL3115A2_OUT_P_DELTA_LSB = const(0x09) 60 _MPL3115A2_OUT_T_DELTA_MSB = const(0x0A) 61 _MPL3115A2_OUT_T_DELTA_LSB = const(0x0B) 62 _MPL3115A2_WHOAMI = const(0x0C) 63 _MPL3115A2_BAR_IN_MSB = const(0x14) 64 _MPL3115A2_BAR_IN_LSB = const(0x15) 65 66 _MPL3115A2_REGISTER_STATUS_TDR = const(0x02) 67 _MPL3115A2_REGISTER_STATUS_PDR = const(0x04) 68 _MPL3115A2_REGISTER_STATUS_PTDR = const(0x08) 69 70 _MPL3115A2_PT_DATA_CFG = const(0x13) 71 _MPL3115A2_PT_DATA_CFG_TDEFE = const(0x01) 72 _MPL3115A2_PT_DATA_CFG_PDEFE = const(0x02) 73 _MPL3115A2_PT_DATA_CFG_DREM = const(0x04) 74 75 _MPL3115A2_CTRL_REG1 = const(0x26) 76 _MPL3115A2_CTRL_REG2 = const(0x27) 77 _MPL3115A2_CTRL_REG3 = const(0x28) 78 _MPL3115A2_CTRL_REG4 = const(0x29) 79 _MPL3115A2_CTRL_REG5 = const(0x2A) 80 81 _MPL3115A2_CTRL_REG1_SBYB = const(0x01) 82 _MPL3115A2_CTRL_REG1_OST = const(0x02) 83 _MPL3115A2_CTRL_REG1_RST = const(0x04) 84 _MPL3115A2_CTRL_REG1_RAW = const(0x40) 85 _MPL3115A2_CTRL_REG1_ALT = const(0x80) 86 _MPL3115A2_CTRL_REG1_BAR = const(0x00) 87 88 _MPL3115A2_CTRL_REG1_OS1 = const(0x00) 89 _MPL3115A2_CTRL_REG1_OS2 = const(0x08) 90 _MPL3115A2_CTRL_REG1_OS4 = const(0x10) 91 _MPL3115A2_CTRL_REG1_OS8 = const(0x18) 92 _MPL3115A2_CTRL_REG1_OS16 = const(0x20) 93 _MPL3115A2_CTRL_REG1_OS32 = const(0x28) 94 _MPL3115A2_CTRL_REG1_OS64 = const(0x30) 95 _MPL3115A2_CTRL_REG1_OS128 = const(0x38) 96 97 _MPL3115A2_REGISTER_STARTCONVERSION = const(0x12) 98 # pylint: enable=bad-whitespace 99 100 101 class MPL3115A2: 102 """Instance of the MPL3115A2 sensor. Must specify the following parameters 103 when creating an instance of this device: 104 - i2c: The I2C bus connected to the sensor. 105 106 In addition you can specify the following optional keyword arguments: 107 - address: The I2C address of the device if it's different from the default. 108 """ 109 110 # Class level buffer to reduce memory usage and allocations. 111 # Note this is not thread safe by design! 112 _BUFFER = bytearray(4) 113 114 def __init__(self, i2c, *, address=_MPL3115A2_ADDRESS): 115 self._device = i2c_device.I2CDevice(i2c, address) 116 # Validate the chip ID. 117 if self._read_u8(_MPL3115A2_WHOAMI) != 0xC4: 118 raise RuntimeError("Failed to find MPL3115A2, check your wiring!") 119 # Reset. Note the chip immediately resets and won't send an I2C back 120 # so we need to catch the OSError and swallow it (otherwise this fails 121 # expecting an ACK that never comes). 122 try: 123 self._write_u8(_MPL3115A2_CTRL_REG1, _MPL3115A2_CTRL_REG1_RST) 124 except OSError: 125 pass 126 time.sleep(0.01) 127 # Poll for the reset to finish. 128 self._poll_reg1(_MPL3115A2_CTRL_REG1_RST) 129 # Configure the chip registers with default values. 130 self._ctrl_reg1 = _MPL3115A2_CTRL_REG1_OS128 | _MPL3115A2_CTRL_REG1_ALT 131 self._write_u8(_MPL3115A2_CTRL_REG1, self._ctrl_reg1) 132 self._write_u8( 133 _MPL3115A2_PT_DATA_CFG, 134 _MPL3115A2_PT_DATA_CFG_TDEFE 135 | _MPL3115A2_PT_DATA_CFG_PDEFE 136 | _MPL3115A2_PT_DATA_CFG_DREM, 137 ) 138 139 def _read_into(self, address, buf, count=None): 140 # Read bytes from the specified 8-bit address into the provided buffer. 141 # If the count is not specified then the entire buffer is filled, 142 # otherwise count bytes are copied in. 143 if count is None: 144 count = len(buf) 145 with self._device as i2c: 146 i2c.write_then_readinto(bytes([address & 0xFF]), buf, in_end=count) 147 148 def _read_u8(self, address): 149 # Read an 8-bit unsigned value from the specified 8-bit address. 150 self._read_into(address, self._BUFFER, count=1) 151 return self._BUFFER[0] 152 153 def _write_u8(self, address, val): 154 # Write an 8-bit unsigned value to the specified 8-bit address. 155 with self._device as i2c: 156 self._BUFFER[0] = address & 0xFF 157 self._BUFFER[1] = val & 0xFF 158 i2c.write(self._BUFFER, end=2) 159 160 def _write_u16_be(self, address, val): 161 # Write a 16-bit big endian unsigned value to the specified 8-bit 162 # address. 163 with self._device as i2c: 164 self._BUFFER[0] = address & 0xFF 165 self._BUFFER[1] = (val >> 8) & 0xFF 166 self._BUFFER[2] = val & 0xFF 167 i2c.write(self._BUFFER, end=3) 168 169 def _poll_reg1(self, mask): 170 # Poll the CTRL REG1 value for the specified masked bits to NOT be 171 # present. 172 while self._read_u8(_MPL3115A2_CTRL_REG1) & mask > 0: 173 time.sleep(0.01) 174 175 @property 176 def pressure(self): 177 """Read the barometric pressure detected by the sensor in Pascals.""" 178 # First poll for a measurement to be finished. 179 self._poll_reg1(_MPL3115A2_CTRL_REG1_OST) 180 # Set control bits for pressure reading. 181 self._ctrl_reg1 &= ~0b10000000 # Turn off bit 7, ALT. 182 self._write_u8(_MPL3115A2_CTRL_REG1, self._ctrl_reg1) 183 self._ctrl_reg1 |= 0b00000010 # Set OST to 1 to start measurement. 184 self._write_u8(_MPL3115A2_CTRL_REG1, self._ctrl_reg1) 185 # Poll status for PDR to be set. 186 while ( 187 self._read_u8(_MPL3115A2_REGISTER_STATUS) & _MPL3115A2_REGISTER_STATUS_PDR 188 == 0 189 ): 190 time.sleep(0.01) 191 # Read 3 bytes of pressure data into buffer. 192 self._read_into(_MPL3115A2_REGISTER_PRESSURE_MSB, self._BUFFER, count=3) 193 # Reconstruct 20-bit pressure value. 194 pressure = ( 195 (self._BUFFER[0] << 16) | (self._BUFFER[1] << 8) | self._BUFFER[2] 196 ) & 0xFFFFFF 197 pressure >>= 4 198 # Scale down to pascals. 199 return pressure / 4.0 200 201 @property 202 def altitude(self): 203 """Read the altitude as calculated based on the sensor pressure and 204 previously configured pressure at sea-level. This will return a 205 value in meters. Set the sea-level pressure by updating the 206 sealevel_pressure property first to get a more accurate altitude value. 207 """ 208 # First poll for a measurement to be finished. 209 self._poll_reg1(_MPL3115A2_CTRL_REG1_OST) 210 # Set control bits for pressure reading. 211 self._ctrl_reg1 |= 0b10000000 # Turn on bit 0, ALT. 212 self._write_u8(_MPL3115A2_CTRL_REG1, self._ctrl_reg1) 213 self._ctrl_reg1 |= 0b00000010 # Set OST to 1 to start measurement. 214 self._write_u8(_MPL3115A2_CTRL_REG1, self._ctrl_reg1) 215 # Poll status for PDR to be set. 216 while ( 217 self._read_u8(_MPL3115A2_REGISTER_STATUS) & _MPL3115A2_REGISTER_STATUS_PDR 218 == 0 219 ): 220 time.sleep(0.01) 221 # Read 3 bytes of altitude data into buffer. 222 # Yes even though this is the address of the pressure register it 223 # returns altitude when the ALT bit is set above. 224 self._read_into(_MPL3115A2_REGISTER_PRESSURE_MSB, self._BUFFER, count=3) 225 # Reconstruct signed 32-bit altitude value (actually 24 bits shifted up 226 # and then scaled down). 227 self._BUFFER[3] = 0 # Top 3 bytes of buffer were read from the chip. 228 altitude = struct.unpack(">i", self._BUFFER[0:4])[0] 229 # Scale down to meters. 230 return altitude / 65535.0 231 232 @property 233 def temperature(self): 234 """Read the temperature as measured by the sensor in degrees Celsius. 235 """ 236 # Poll status for TDR to be set. 237 while ( 238 self._read_u8(_MPL3115A2_REGISTER_STATUS) & _MPL3115A2_REGISTER_STATUS_TDR 239 == 0 240 ): 241 time.sleep(0.01) 242 # Read 2 bytes of data from temp register. 243 self._read_into(_MPL3115A2_REGISTER_TEMP_MSB, self._BUFFER, count=2) 244 # Reconstruct signed 12-bit value. 245 temperature = struct.unpack(">h", self._BUFFER[0:2])[0] 246 temperature >>= 4 247 # Scale down to degrees Celsius. 248 return temperature / 16.0 249 250 @property 251 def sealevel_pressure(self): 252 """Read and write the pressure at sea-level used to calculate altitude. 253 You must look this up from a local weather or meteorlogical report for 254 the best accuracy. This is a value in Pascals. 255 """ 256 # Read the sea level pressure in bars. 257 self._read_into(_MPL3115A2_BAR_IN_MSB, self._BUFFER, count=2) 258 # Reconstruct 16-bit value and scale back to pascals. 259 pressure = (self._BUFFER[0] << 8) | self._BUFFER[1] 260 return pressure * 2.0 261 262 @sealevel_pressure.setter 263 def sealevel_pressure(self, val): 264 # Convert to bars of pressure and write to the sealevel register. 265 bars = val // 2 266 self._write_u16_be(_MPL3115A2_BAR_IN_MSB, bars)