bme280.py 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818
  1. #
  2. # BME280 device driver
  3. # Copyright (c) 2020-2023 Michael Büsch <m@bues.ch>
  4. #
  5. # The reference and base for this software is the public document
  6. # BME280 - Data sheet
  7. # Document revision 1.6
  8. # Document release date September 2018
  9. # Technical reference code 0 273 141 185
  10. # Document number BST-BME280-DS002-15
  11. #
  12. # This program is free software; you can redistribute it and/or modify
  13. # it under the terms of the GNU General Public License as published by
  14. # the Free Software Foundation; either version 2 of the License, or
  15. # (at your option) any later version.
  16. #
  17. # This program is distributed in the hope that it will be useful,
  18. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  19. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  20. # GNU General Public License for more details.
  21. #
  22. # You should have received a copy of the GNU General Public License along
  23. # with this program; if not, write to the Free Software Foundation, Inc.,
  24. # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  25. #
  26. __all__ = [
  27. "BME280Error",
  28. "BME280",
  29. ]
  30. import sys
  31. assert sys.version_info[0] == 3
  32. isMicropython = sys.implementation.name == "micropython"
  33. if isMicropython:
  34. import uasyncio as asyncio
  35. import micropython
  36. from micropython import const
  37. else:
  38. import asyncio
  39. class micropython:
  40. const = native = viper = lambda x: x
  41. const = micropython.const
  42. class BME280Error(Exception):
  43. """BME280 exception.
  44. """
  45. __slots__ = (
  46. )
  47. def makePin(pin, constructor):
  48. from machine import Pin
  49. if isinstance(pin, Pin):
  50. return pin
  51. return constructor(pin)
  52. class BME280I2C:
  53. """BME280 low level I2C wrapper.
  54. """
  55. __slots__ = (
  56. "__micropython",
  57. "__addr",
  58. "__i2c",
  59. )
  60. def __init__(self, i2cBus, i2cAddr, i2cFreq):
  61. self.__addr = i2cAddr
  62. self.__micropython = isMicropython
  63. try:
  64. if self.__micropython:
  65. from machine import I2C, SoftI2C, Pin
  66. if isinstance(i2cBus, (I2C, SoftI2C)):
  67. self.__i2c = i2cBus
  68. else:
  69. opts = {
  70. "freq" : i2cFreq * 1000,
  71. }
  72. if isinstance(i2cBus, dict):
  73. opts["scl"] = makePin(i2cBus["scl"], lambda p: Pin(p, mode=Pin.OPEN_DRAIN, value=1))
  74. opts["sda"] = makePin(i2cBus["sda"], lambda p: Pin(p, mode=Pin.OPEN_DRAIN, value=1))
  75. i2cBus = i2cBus.get("index", -1)
  76. else:
  77. assert i2cBus >= 0
  78. if i2cBus < 0:
  79. self.__i2c = SoftI2C(**opts)
  80. else:
  81. self.__i2c = I2C(i2cBus, **opts)
  82. else:
  83. from smbus import SMBus
  84. if isinstance(i2cBus, SMBus):
  85. self.__i2c = i2cBus
  86. else:
  87. self.__i2c = SMBus(i2cBus)
  88. except Exception as e:
  89. raise BME280Error("BME280: I2C error: %s" % str(e))
  90. def close(self):
  91. if self.__micropython:
  92. try:
  93. if hasattr(self.__i2c, "deinit"):
  94. self.__i2c.deinit()
  95. except Exception:
  96. pass
  97. else:
  98. try:
  99. self.__i2c.close()
  100. except Exception:
  101. pass
  102. self.__i2c = None
  103. def write(self, reg, data):
  104. try:
  105. if self.__micropython:
  106. self.__i2c.writeto_mem(self.__addr, reg, data)
  107. else:
  108. self.__i2c.write_i2c_block_data(self.__addr, reg, list(data))
  109. except Exception as e:
  110. raise BME280Error("BME280: I2C error: %s" % str(e))
  111. def read(self, reg, length):
  112. try:
  113. if self.__micropython:
  114. return self.__i2c.readfrom_mem(self.__addr, reg, length)
  115. else:
  116. return bytes(self.__i2c.read_i2c_block_data(self.__addr, reg, length))
  117. except Exception as e:
  118. raise BME280Error("BME280: I2C error: %s" % str(e))
  119. class BME280SPI:
  120. """BME280 low level SPI wrapper.
  121. """
  122. __slots__ = (
  123. "__micropython",
  124. "__spi",
  125. "__cs",
  126. )
  127. def __init__(self, spiBus, spiCS, spiFreq):
  128. self.__micropython = isMicropython
  129. try:
  130. if self.__micropython:
  131. from machine import SPI, SoftSPI, Pin
  132. if spiCS is None:
  133. raise Exception("No spiCS parameter specified.")
  134. if isinstance(spiBus, (SPI, SoftSPI)):
  135. self.__spi = spiBus
  136. else:
  137. opts = {
  138. "baudrate" : spiFreq * 1000,
  139. "polarity" : 0,
  140. "phase" : 0,
  141. "bits" : 8,
  142. "firstbit" : SPI.MSB,
  143. }
  144. if isinstance(spiBus, dict): # Software SPI
  145. opts["sck"] = makePin(spiBus["sck"], lambda p: Pin(p, mode=Pin.OUT, value=0))
  146. opts["mosi"] = makePin(spiBus["mosi"], lambda p: Pin(p, mode=Pin.OUT, value=0))
  147. opts["miso"] = makePin(spiBus["miso"], lambda p: Pin(p, mode=Pin.IN))
  148. spiBus = spiBus.get("index", -1)
  149. else:
  150. assert spiBus >= 0
  151. if spiBus < 0:
  152. self.__spi = SoftSPI(**opts)
  153. else:
  154. self.__spi = SPI(spiBus, **opts)
  155. self.__cs = makePin(spiCS, lambda p: Pin(p, mode=Pin.OUT, value=1))
  156. else:
  157. from spidev import SpiDev
  158. if isinstance(spiBus, SpiDev):
  159. self.__spi = spiBus
  160. else:
  161. if spiCS is None:
  162. raise Exception("No spiCS parameter specified.")
  163. self.__spi = SpiDev()
  164. self.__spi.max_speed_hz = spiFreq * 1000
  165. self.__spi.mode = 0b00
  166. self.__spi.bits_per_word = 8
  167. self.__spi.threewire = False
  168. self.__spi.lsbfirst = False
  169. self.__spi.cshigh = False
  170. self.__spi.open(spiBus, spiCS)
  171. except Exception as e:
  172. raise BME280Error("BME280: SPI error: %s" % str(e))
  173. def close(self):
  174. if self.__micropython:
  175. try:
  176. self.__cs(1)
  177. except Exception:
  178. pass
  179. try:
  180. if hasattr(self.__spi, "deinit"):
  181. self.__spi.deinit()
  182. except Exception:
  183. pass
  184. else:
  185. try:
  186. self.__spi.close()
  187. except Exception:
  188. pass
  189. self.__spi = None
  190. def write(self, reg, data):
  191. try:
  192. reg &= 0x7F # clear RW bit
  193. writeData = reg.to_bytes(1, "little") + data
  194. if self.__micropython:
  195. try:
  196. self.__cs(0)
  197. self.__spi.write(writeData)
  198. finally:
  199. self.__cs(1)
  200. else:
  201. self.__spi.xfer2(writeData)
  202. except Exception as e:
  203. raise BME280Error("BME280: SPI error: %s" % str(e))
  204. def read(self, reg, length):
  205. try:
  206. reg |= 0x80 # set RW bit
  207. if self.__micropython:
  208. try:
  209. self.__cs(0)
  210. return memoryview(self.__spi.read(length + 1, reg))[1:]
  211. finally:
  212. self.__cs(1)
  213. else:
  214. writeData = reg.to_bytes(1, "little") * (length + 1)
  215. return memoryview(self.__spi.xfer2(writeData))[1:]
  216. except Exception as e:
  217. raise BME280Error("BME280: SPI error: %s" % str(e))
  218. # Registers
  219. _REG_dig_T1 = const(0x88) # 16 bit LE
  220. _REG_dig_T2 = const(0x8A) # 16 bit LE
  221. _REG_dig_T3 = const(0x8C) # 16 bit LE
  222. _REG_dig_P1 = const(0x8E) # 16 bit LE
  223. _REG_dig_P2 = const(0x90) # 16 bit LE
  224. _REG_dig_P3 = const(0x92) # 16 bit LE
  225. _REG_dig_P4 = const(0x94) # 16 bit LE
  226. _REG_dig_P5 = const(0x96) # 16 bit LE
  227. _REG_dig_P6 = const(0x98) # 16 bit LE
  228. _REG_dig_P7 = const(0x9A) # 16 bit LE
  229. _REG_dig_P8 = const(0x9C) # 16 bit LE
  230. _REG_dig_P9 = const(0x9E) # 16 bit LE
  231. _REG_dig_H1 = const(0xA1) # 8 bit
  232. _REG_id = const(0xD0) # 8 bit
  233. _REG_reset = const(0xE0) # 8 bit
  234. _REG_dig_H2 = const(0xE1) # 16 bit LE
  235. _REG_dig_H3 = const(0xE3) # 8 bit
  236. _REG_dig_H4 = const(0xE4) # 12 bit BE (overlaps with next)
  237. _REG_dig_H5 = const(0xE5) # 12 bit LE (overlaps with previous)
  238. _REG_dig_H6 = const(0xE7) # 8 bit
  239. _REG_ctrl_hum = const(0xF2) # 8 bit
  240. _REG_status = const(0xF3) # 8 bit
  241. _REG_ctrl_meas = const(0xF4) # 8 bit
  242. _REG_config = const(0xF5) # 8 bit
  243. _REG_press_msb = const(0xF7) # 8 bit
  244. _REG_press_lsb = const(0xF8) # 8 bit
  245. _REG_press_xlsb = const(0xF9) # 8 bit
  246. _REG_temp_msb = const(0xFA) # 8 bit
  247. _REG_temp_lsb = const(0xFB) # 8 bit
  248. _REG_temp_xlsb = const(0xFC) # 8 bit
  249. _REG_hum_msb = const(0xFD) # 8 bit
  250. _REG_hum_lsb = const(0xFE) # 8 bit
  251. # Operation mode
  252. MODE_SLEEP = const(0b00)
  253. MODE_FORCED = const(0b01)
  254. MODE_NORMAL = const(0b11)
  255. # Oversampling (osrs_h, osrs_p, osrs_t)
  256. OVSMPL_SKIP = const(0b000) # measurement turned off
  257. OVSMPL_1 = const(0b001)
  258. OVSMPL_2 = const(0b010)
  259. OVSMPL_4 = const(0b011)
  260. OVSMPL_8 = const(0b100)
  261. OVSMPL_16 = const(0b101)
  262. # Standby time (t_sb)
  263. T_SB_p5ms = const(0b000) # 0.5 ms
  264. T_SB_10ms = const(0b110) # 10 ms
  265. T_SB_20ms = const(0b111) # 20 ms
  266. T_SB_62p5ms = const(0b001) # 62.5 ms
  267. T_SB_125ms = const(0b010) # 125 ms
  268. T_SB_250ms = const(0b011) # 250 ms
  269. T_SB_500ms = const(0b100) # 500 ms
  270. T_SB_1000ms = const(0b101) # 1 s
  271. # Filter settings
  272. FILTER_OFF = const(0b000)
  273. FILTER_2 = const(0b001)
  274. FILTER_4 = const(0b010)
  275. FILTER_8 = const(0b011)
  276. FILTER_16 = const(0b100)
  277. # Calculation mode for compensation functions.
  278. CALC_FLOAT = const(0)
  279. CALC_INT32 = const(1)
  280. CALC_INT64 = const(2)
  281. class BME280:
  282. """BME280 device driver.
  283. """
  284. __slots__ = (
  285. "__calc",
  286. "__bus",
  287. "__resetPending",
  288. "__cal_dig_T1",
  289. "__cal_dig_T2",
  290. "__cal_dig_T3",
  291. "__cal_dig_P1",
  292. "__cal_dig_P2",
  293. "__cal_dig_P3",
  294. "__cal_dig_P4",
  295. "__cal_dig_P5",
  296. "__cal_dig_P6",
  297. "__cal_dig_P7",
  298. "__cal_dig_P8",
  299. "__cal_dig_P9",
  300. "__cal_dig_H1",
  301. "__cal_dig_H2",
  302. "__cal_dig_H3",
  303. "__cal_dig_H4",
  304. "__cal_dig_H5",
  305. "__cal_dig_H6",
  306. "__cache_config",
  307. "__cache_ctrl_hum",
  308. )
  309. def __init__(self,
  310. i2cBus=None,
  311. i2cAddr=0x76,
  312. spiBus=None,
  313. spiCS=None,
  314. busFreq=100,
  315. calc=(CALC_INT32 if isMicropython else CALC_FLOAT)):
  316. """Create BME280 driver instance.
  317. 'i2cBus': I2C hardware bus index to use for communication with the device.
  318. Or dict { "scl": 1, "sda": 2 } of pin numbers for software I2C.
  319. Or dict { "index": 0, "scl": 1, "sda": 2 } for hardware I2C-0 with different pinning.
  320. Or a fully initialized Micropython I2C/SoftI2C object.
  321. Pin numbers may either be integers or Micropython Pin objects.
  322. 'i2cAddr': I2C address of the device. May be 0x76 or 0x77.
  323. 'spiBus': SPI hardware bus index to use for communication with the device.
  324. Or dict { "sck": 1, "mosi": 2, "miso": 3 } of pin numbers for software SPI.
  325. Or dict { "index": 0, "sck": 1, "mosi": 2, "miso": 3 } for hardware SPI-0 with different pinning.
  326. Or a fully initialized Micropython SPI/SoftSPI object.
  327. Pin numbers may either be integers or Micropython Pin objects.
  328. 'spiCS': SPI chip select identifier number or pin number or Micropython Pin object.
  329. 'busFreq': I2C/SPI bus clock frequency, in kHz.
  330. 'calc': Calculation mode for compensation functions. One of CALC_...
  331. """
  332. self.__calc = calc
  333. self.__resetPending = True
  334. if i2cBus is not None:
  335. self.__bus = BME280I2C(i2cBus, i2cAddr, busFreq)
  336. elif spiBus is not None:
  337. self.__bus = BME280SPI(spiBus, spiCS, busFreq)
  338. else:
  339. raise BME280Error("BME280: No bus configured.")
  340. async def closeAsync(self):
  341. """Shutdown communication to the device.
  342. """
  343. if self.__bus:
  344. try:
  345. await self.startAsync(mode=MODE_SLEEP)
  346. except BME280Error:
  347. pass
  348. self.__resetPending = True
  349. self.__bus.close()
  350. self.__bus = None
  351. self.__resetPending = True
  352. def close(self):
  353. """Shutdown communication to the device.
  354. """
  355. asyncio.run(self.closeAsync())
  356. def __enter__(self):
  357. return self
  358. async def __aenter__(self):
  359. return self
  360. def __exit__(self, exc_type, exc_value, traceback):
  361. self.close()
  362. async def __aexit__(self, exc_type, exc_value, traceback):
  363. await self.closeAsync()
  364. def __readCal(self):
  365. """Read the calibration data from the device.
  366. """
  367. data = bytes(self.__readBurst(_REG_dig_T1, _REG_dig_H1))
  368. data += bytes(self.__readBurst(_REG_dig_H2, _REG_dig_H6))
  369. def twos(value, bits):
  370. """Convert a raw value represented in two's complement to signed Python int.
  371. 'bits': The length of the raw value.
  372. """
  373. mask = (1 << bits) - 1
  374. value &= mask
  375. if value & (1 << (bits - 1)):
  376. return -((~value + 1) & mask)
  377. return value
  378. def getU8(reg):
  379. if reg >= _REG_dig_H2:
  380. return data[(reg - _REG_dig_H2) + (_REG_dig_H1 - _REG_dig_T1 + 1)]
  381. return data[reg - _REG_dig_T1]
  382. def getS8(reg):
  383. return twos(getU8(reg), 8)
  384. def getU16LE(reg):
  385. return (getU8(reg + 1) << 8) | getU8(reg)
  386. def getS16LE(reg):
  387. return twos(getU16LE(reg), 16)
  388. def getS12BE(reg):
  389. return twos((getU8(reg) << 4) | (getU8(reg + 1) & 0xF), 12)
  390. def getS12LE(reg):
  391. return twos((getU8(reg + 1) << 4) | (getU8(reg) >> 4), 12)
  392. def cal(x):
  393. return float(x) if self.__calc == CALC_FLOAT else x
  394. self.__cal_dig_T1 = cal(getU16LE(_REG_dig_T1))
  395. self.__cal_dig_T2 = cal(getS16LE(_REG_dig_T2))
  396. self.__cal_dig_T3 = cal(getS16LE(_REG_dig_T3))
  397. self.__cal_dig_P1 = cal(getU16LE(_REG_dig_P1))
  398. self.__cal_dig_P2 = cal(getS16LE(_REG_dig_P2))
  399. self.__cal_dig_P3 = cal(getS16LE(_REG_dig_P3))
  400. self.__cal_dig_P4 = cal(getS16LE(_REG_dig_P4))
  401. self.__cal_dig_P5 = cal(getS16LE(_REG_dig_P5))
  402. self.__cal_dig_P6 = cal(getS16LE(_REG_dig_P6))
  403. self.__cal_dig_P7 = cal(getS16LE(_REG_dig_P7))
  404. self.__cal_dig_P8 = cal(getS16LE(_REG_dig_P8))
  405. self.__cal_dig_P9 = cal(getS16LE(_REG_dig_P9))
  406. self.__cal_dig_H1 = cal(getU8(_REG_dig_H1))
  407. self.__cal_dig_H2 = cal(getS16LE(_REG_dig_H2))
  408. self.__cal_dig_H3 = cal(getU8(_REG_dig_H3))
  409. self.__cal_dig_H4 = cal(getS12BE(_REG_dig_H4))
  410. self.__cal_dig_H5 = cal(getS12LE(_REG_dig_H5))
  411. self.__cal_dig_H6 = cal(getS8(_REG_dig_H6))
  412. async def resetAsync(self):
  413. """Reset the device.
  414. This is a coroutine.
  415. """
  416. if not self.__bus:
  417. raise BME280Error("BME280: Device not opened.")
  418. self.__cache_config = None
  419. self.__cache_ctrl_hum = None
  420. # Reset the chip.
  421. self.__write8(_REG_reset, 0xB6)
  422. # Wait for the chip to come alive again.
  423. await asyncio.sleep(0.05)
  424. for _ in range(5):
  425. if self.__readU8(_REG_id) == 0x60:
  426. break
  427. await asyncio.sleep(0.01)
  428. else:
  429. raise BME280Error("BME280: ID register response incorrect (1).")
  430. for _ in range(5):
  431. im_update, measuring = self.__read_status()
  432. if not im_update and not measuring:
  433. break
  434. await asyncio.sleep(0.01)
  435. else:
  436. raise BME280Error("BME280: status register response incorrect.")
  437. # Read the ID register a couple of times to test if
  438. # the bus communication is stable.
  439. # If this fails, then there probably is a bus problem.
  440. for _ in range(10):
  441. if self.__readU8(_REG_id) != 0x60:
  442. raise BME280Error("BME280: ID register response incorrect (2).")
  443. # Read calibration data.
  444. self.__readCal()
  445. self.__resetPending = False
  446. def reset(self):
  447. """Synchronously call the coroutine resetAsync().
  448. See resetAsync() for documentation about behaviour, arguments and return value.
  449. """
  450. asyncio.run(self.resetAsync())
  451. async def startAsync(self,
  452. mode,
  453. standbyTime=T_SB_125ms,
  454. filter=FILTER_OFF,
  455. tempOversampling=OVSMPL_1,
  456. humidityOversampling=OVSMPL_1,
  457. pressureOversampling=OVSMPL_1):
  458. """Reconfigure the device.
  459. 'mode': Operation mode. MODE_SLEEP, MODE_FORCED or MODE_NORMAL.
  460. 'standbyTime': Standby time. One of T_SB_...
  461. 'filter': Filter configuration. One of FILTER_...
  462. 'tempOversampling': Temperature oversampling. One of OVSMPL_...
  463. 'humidityOversampling': Humidity oversampling. One of OVSMPL_...
  464. 'pressureOversampling': Pressure oversampling. One of OVSMPL_...
  465. By default this starts in MODE_SLEEP.
  466. """
  467. if not self.__bus:
  468. raise BME280Error("BME280: Device not opened.")
  469. if self.__resetPending:
  470. await self.resetAsync()
  471. self.__write_config(t_sb=standbyTime,
  472. filter=filter,
  473. spi3w_en=False)
  474. self.__write_ctrl_hum(osrs_h=humidityOversampling)
  475. self.__write_ctrl_meas(osrs_t=tempOversampling,
  476. osrs_p=pressureOversampling,
  477. mode=mode)
  478. def start(self, *args, **kwargs):
  479. """Synchronously call the coroutine startAsync().
  480. See startAsync() for documentation about behaviour, arguments and return value.
  481. """
  482. asyncio.run(self.startAsync(*args, **kwargs))
  483. async def readForcedAsync(self, *, pollSleep=0.05, **kwargs):
  484. """Trigger a MODE_FORCED conversion,
  485. wait for it to complete and return the same as read().
  486. This is a coroutine.
  487. """
  488. if not self.__bus:
  489. raise BME280Error("BME280: Device not opened.")
  490. await self.startAsync(**kwargs, mode=MODE_FORCED)
  491. while await self.isMeasuringAsync():
  492. await asyncio.sleep(pollSleep)
  493. return await self.readAsync()
  494. def readForced(self, *args, **kwargs):
  495. """Synchronously call the coroutine readForcedAsync().
  496. See readForcedAsync() for documentation about behaviour, arguments and return value.
  497. """
  498. return asyncio.run(self.readForcedAsync(*args, **kwargs))
  499. async def isMeasuringAsync(self):
  500. """Returns True, if the device is currently running the measurement cycle.
  501. In MODE_FORCED: If this returns False, it's Ok to read() the new data.
  502. """
  503. if not self.__bus:
  504. raise BME280Error("BME280: Device not opened.")
  505. im_update, measuring = self.__read_status()
  506. return measuring
  507. def isMeasuring(self):
  508. """Synchronously call the coroutine isMeasuringAsync().
  509. See isMeasuringAsync() for documentation about behaviour, arguments and return value.
  510. """
  511. return asyncio.run(self.isMeasuringAsync())
  512. async def readAsync(self):
  513. """Read the temperature, humidity and pressure from the device.
  514. Returns a tuple (temperature, humidity, pressure).
  515. temparature in degree Celsius.
  516. humitidy as value between 0 and 1. 0.0 = 0% -> 1.0 = 100%.
  517. pressure in Pascal.
  518. """
  519. if not self.__bus:
  520. raise BME280Error("BME280: Device not opened.")
  521. # Read the raw registers.
  522. data = self.__readBurst(_REG_press_msb, _REG_hum_lsb)
  523. def get(reg):
  524. return data[reg - _REG_press_msb]
  525. # Extract raw pressure.
  526. up = ((get(_REG_press_msb) << 12) |
  527. (get(_REG_press_lsb) << 4) |
  528. (get(_REG_press_xlsb) >> 4))
  529. # Extract raw temperature.
  530. ut = ((get(_REG_temp_msb) << 12) |
  531. (get(_REG_temp_lsb) << 4) |
  532. (get(_REG_temp_xlsb) >> 4))
  533. # Extract raw humidity.
  534. uh = ((get(_REG_hum_msb) << 8) |
  535. get(_REG_hum_lsb))
  536. # Run compensations.
  537. t_fine, t = self.__compT(ut)
  538. h = self.__compH(t_fine, uh)
  539. p = self.__compP(t_fine, up)
  540. return t, h, p
  541. def read(self):
  542. """Synchronously call the coroutine readAsync().
  543. See readAsync() for documentation about behaviour, arguments and return value.
  544. """
  545. return asyncio.run(self.readAsync())
  546. def __compT(self, ut):
  547. """Convert the uncompensated temperature 'ut'
  548. to compensated degree Celsius.
  549. """
  550. if self.__calc == CALC_FLOAT:
  551. t_fine, t = self.__compT_float(ut)
  552. else:
  553. t_fine, t = self.__compT_int32(ut)
  554. t = float(t) * 1e-2
  555. return t_fine, min(max(t, -40.0), 85.0)
  556. def __compT_float(self, ut):
  557. ut = float(ut)
  558. T1 = self.__cal_dig_T1
  559. T2 = self.__cal_dig_T2
  560. T3 = self.__cal_dig_T3
  561. a = (ut / 16384.0 - T1 / 1024.0) * T2
  562. b = ut / 131072.0 - T1 / 8192.0
  563. b *= b * T3
  564. t_fine = a + b
  565. t = t_fine / 5120.0
  566. return int(t_fine), t
  567. @micropython.viper
  568. def __compT_int32(self, ut: int):
  569. T1 = int(self.__cal_dig_T1)
  570. T2 = int(self.__cal_dig_T2)
  571. T3 = int(self.__cal_dig_T3)
  572. a = (((ut >> 3) - (T1 << 1)) * T2) >> 11
  573. b = (ut >> 4) - T1
  574. b = (((b * b) >> 12) * T3) >> 14
  575. t_fine = a + b
  576. t = (t_fine * 5 + 128) >> 8
  577. return t_fine, t
  578. def __compP(self, t_fine, up):
  579. """Convert the uncompensated pressure 'up' to compensated Pascal.
  580. 't_fine' is the high resolution temperature.
  581. """
  582. if self.__calc == CALC_FLOAT:
  583. p = self.__compP_float(t_fine, up)
  584. elif self.__calc == CALC_INT32:
  585. p = float(self.__compP_int32(t_fine, up))
  586. else:
  587. p = self.__compP_int64(t_fine, up) / 256.0
  588. return min(max(p, 30000.0), 110000.0)
  589. def __compP_float(self, t_fine, up):
  590. up = float(up)
  591. P1 = self.__cal_dig_P1
  592. P2 = self.__cal_dig_P2
  593. P3 = self.__cal_dig_P3
  594. P4 = self.__cal_dig_P4
  595. P5 = self.__cal_dig_P5
  596. P6 = self.__cal_dig_P6
  597. P7 = self.__cal_dig_P7
  598. P8 = self.__cal_dig_P8
  599. P9 = self.__cal_dig_P9
  600. a = (t_fine / 2.0) - 64000.0
  601. b = a * a * P6 / 32768.0
  602. b += a * P5 * 2.0
  603. b = (b / 4.0) + (P4 * 65536.0)
  604. a = (P3 * a * a / 524288.0 + P2 * a) / 524288.0
  605. a = (1.0 + a / 32768.0) * P1
  606. if a:
  607. p = ((1048576.0 - up) - (b / 4096.0)) * 6250.0 / a
  608. a = P9 * p * p / 2147483648.0
  609. b = p * P8 / 32768.0
  610. p = p + (a + b + P7) / 16.0
  611. return p
  612. return 0.0
  613. @micropython.viper
  614. def __compP_int32(self, t_fine: int, up: int) -> int:
  615. P1 = int(self.__cal_dig_P1)
  616. P2 = int(self.__cal_dig_P2)
  617. P3 = int(self.__cal_dig_P3)
  618. P4 = int(self.__cal_dig_P4)
  619. P5 = int(self.__cal_dig_P5)
  620. P6 = int(self.__cal_dig_P6)
  621. P7 = int(self.__cal_dig_P7)
  622. P8 = int(self.__cal_dig_P8)
  623. P9 = int(self.__cal_dig_P9)
  624. a = (t_fine >> 1) - 64000
  625. c = a >> 2
  626. b = ((c * c) >> 11) * P6
  627. b += (a * P5) << 1
  628. b = (b >> 2) + (P4 << 16)
  629. a = (((P3 * ((c * c) >> 13)) >> 3) + ((P2 * a) >> 1)) >> 18
  630. a = ((32768 + a) * P1) >> 15
  631. if a:
  632. p = ((((1048576 - up) - (b >> 12)) * 3125) // a) << 1
  633. c = p >> 3
  634. a = (P9 * ((c * c) >> 13)) >> 12
  635. b = (P8 * (p >> 2)) >> 13
  636. p += (a + b + P7) >> 4
  637. return p
  638. return 0
  639. @micropython.native
  640. def __compP_int64(self, t_fine, up):
  641. P1 = self.__cal_dig_P1
  642. P2 = self.__cal_dig_P2
  643. P3 = self.__cal_dig_P3
  644. P4 = self.__cal_dig_P4
  645. P5 = self.__cal_dig_P5
  646. P6 = self.__cal_dig_P6
  647. P7 = self.__cal_dig_P7
  648. P8 = self.__cal_dig_P8
  649. P9 = self.__cal_dig_P9
  650. a = t_fine - 128000
  651. b = a * a * P6
  652. b = b + ((a * P5) << 17)
  653. b = b + (P4 << 35)
  654. a = ((a * a * P3) >> 8) + ((a * P2) << 12)
  655. a = (((1 << 47) + a) * P1) >> 33
  656. if a:
  657. p = ((((1048576 - up) << 31) - b) * 3125) // a
  658. c = p >> 13
  659. a = (P9 * c * c) >> 25
  660. b = (P8 * p) >> 19
  661. p = ((p + a + b) >> 8) + (P7 << 4)
  662. return p
  663. return 0
  664. def __compH(self, t_fine, uh):
  665. """Convert the uncompensated relative humidity 'uh'
  666. to compensated relative humidity 0.0 = 0% -> 1.0 = 100%.
  667. 't_fine' is the high resolution temperature.
  668. """
  669. if self.__calc == CALC_FLOAT:
  670. h = self.__compH_float(t_fine, uh) * 1e-2
  671. else:
  672. h = self.__compH_int32(t_fine, uh) / 102400.0
  673. return min(max(h, 0.0), 1.0)
  674. def __compH_float(self, t_fine, uh):
  675. uh = float(uh)
  676. H1 = self.__cal_dig_H1
  677. H2 = self.__cal_dig_H2
  678. H3 = self.__cal_dig_H3
  679. H4 = self.__cal_dig_H4
  680. H5 = self.__cal_dig_H5
  681. H6 = self.__cal_dig_H6
  682. a = uh - (H4 * 64.0 + H5 / 16384.0 * (t_fine - 76800.0))
  683. a *= H2 / 65536.0 * (1.0 + H6 / 67108864.0 * a * (1.0 + H3 / 67108864.0 * a))
  684. a *= 1.0 - H1 * a / 524288.0
  685. return a
  686. @micropython.viper
  687. def __compH_int32(self, t_fine: int, uh: int) -> int:
  688. H1 = int(self.__cal_dig_H1)
  689. H2 = int(self.__cal_dig_H2)
  690. H3 = int(self.__cal_dig_H3)
  691. H4 = int(self.__cal_dig_H4)
  692. H5 = int(self.__cal_dig_H5)
  693. H6 = int(self.__cal_dig_H6)
  694. a = (((uh << 14) - (H4 << 20) - (H5 * (t_fine - 76800))) + 0x4000) >> 15
  695. a *= ((((((a * H6) >> 10) * (((a * H3) >> 11) + 0x8000)) >> 10) + 0x200000) * H2 + 0x2000) >> 14
  696. a -= ((((a >> 15) * (a >> 15)) >> 7) * H1) >> 4
  697. return a >> 12
  698. def __read_status(self):
  699. """Read 'status' register.
  700. """
  701. status = self.__readU8(_REG_status)
  702. im_update = bool(status & (1 << 0))
  703. measuring = bool(status & (1 << 3))
  704. return im_update, measuring
  705. def __write_config(self, t_sb, filter, spi3w_en):
  706. """Write the 'config' register.
  707. """
  708. data = ((t_sb & 7) << 5) | ((filter & 7) << 2) | (spi3w_en & 1)
  709. if data != self.__cache_config:
  710. self.__write8(_REG_config, data)
  711. self.__cache_config = data
  712. def __write_ctrl_meas(self, osrs_t, osrs_p, mode):
  713. """Write the 'ctrl_meas' register.
  714. """
  715. data = ((osrs_t & 7) << 5) | ((osrs_p & 7) << 2) | (mode & 3)
  716. self.__write8(_REG_ctrl_meas, data)
  717. def __write_ctrl_hum(self, osrs_h):
  718. """Write the 'ctrl_hum' register.
  719. """
  720. data = osrs_h & 7
  721. if data != self.__cache_ctrl_hum:
  722. self.__write8(_REG_ctrl_hum, data)
  723. self.__cache_ctrl_hum = data
  724. def __readU8(self, reg):
  725. """Read a register and interpret the value as unsigned 8-bit.
  726. """
  727. return self.__bus.read(reg, 1)[0]
  728. def __readBurst(self, startReg, endReg):
  729. """Read multiple registers with a burst transfer.
  730. """
  731. assert endReg >= startReg
  732. length = (endReg - startReg) + 1
  733. return self.__bus.read(startReg, length)
  734. def __write8(self, reg, value):
  735. """Write an 8-bit register.
  736. """
  737. self.__bus.write(reg, (value & 0xFF).to_bytes(1, "little"))
  738. # vim: ts=4 sw=4 expandtab