/ adafruit_ds18x20.py
adafruit_ds18x20.py
1 # The MIT License (MIT) 2 # 3 # Copyright (c) 2017 Carter Nelson 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_ds18x20` 24 ==================================================== 25 26 Driver for Dallas 1-Wire temperature sensor. 27 28 * Author(s): Carter Nelson 29 """ 30 31 __version__ = "0.0.0-auto.0" 32 __repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_DS18x20.git" 33 34 import time 35 from micropython import const 36 from adafruit_onewire.device import OneWireDevice 37 38 _CONVERT = b"\x44" 39 _RD_SCRATCH = b"\xBE" 40 _WR_SCRATCH = b"\x4E" 41 _CONVERSION_TIMEOUT = const(1) 42 RESOLUTION = (9, 10, 11, 12) 43 # Maximum conversion delay in seconds, from DS18B20 datasheet. 44 _CONVERSION_DELAY = {9: 0.09375, 10: 0.1875, 11: 0.375, 12: 0.750} 45 46 47 class DS18X20: 48 """Class which provides interface to DS18X20 temperature sensor.""" 49 50 def __init__(self, bus, address): 51 if address.family_code == 0x10 or address.family_code == 0x28: 52 self._address = address 53 self._device = OneWireDevice(bus, address) 54 self._buf = bytearray(9) 55 self._conv_delay = _CONVERSION_DELAY[12] # pessimistic default 56 else: 57 raise ValueError("Incorrect family code in device address.") 58 59 @property 60 def temperature(self): 61 """The temperature in degrees Celsius.""" 62 self._convert_temp() 63 return self._read_temp() 64 65 @property 66 def resolution(self): 67 """The programmable resolution. 9, 10, 11, or 12 bits.""" 68 return RESOLUTION[self._read_scratch()[4] >> 5 & 0x03] 69 70 @resolution.setter 71 def resolution(self, bits): 72 if bits not in RESOLUTION: 73 raise ValueError("Incorrect resolution. Must be 9, 10, 11, or 12.") 74 self._buf[0] = 0 # TH register 75 self._buf[1] = 0 # TL register 76 self._buf[2] = RESOLUTION.index(bits) << 5 | 0x1F # configuration register 77 self._write_scratch(self._buf) 78 79 def _convert_temp(self, timeout=_CONVERSION_TIMEOUT): 80 with self._device as dev: 81 dev.write(_CONVERT) 82 start_time = time.monotonic() 83 if timeout > 0: 84 dev.readinto(self._buf, end=1) 85 # 0 = conversion in progress, 1 = conversion done 86 while self._buf[0] == 0x00: 87 if time.monotonic() - start_time > timeout: 88 raise RuntimeError( 89 "Timeout waiting for conversion to complete." 90 ) 91 dev.readinto(self._buf, end=1) 92 return time.monotonic() - start_time 93 94 def _read_temp(self): 95 # pylint: disable=invalid-name 96 buf = self._read_scratch() 97 if self._address.family_code == 0x10: 98 if buf[1]: 99 t = buf[0] >> 1 | 0x80 100 t = -((~t + 1) & 0xFF) 101 else: 102 t = buf[0] >> 1 103 return t - 0.25 + (buf[7] - buf[6]) / buf[7] 104 t = buf[1] << 8 | buf[0] 105 if t & 0x8000: # sign bit set 106 t = -((t ^ 0xFFFF) + 1) 107 return t / 16 108 109 def _read_scratch(self): 110 with self._device as dev: 111 dev.write(_RD_SCRATCH) 112 dev.readinto(self._buf) 113 return self._buf 114 115 def _write_scratch(self, buf): 116 with self._device as dev: 117 dev.write(_WR_SCRATCH) 118 dev.write(buf, end=3) 119 120 def start_temperature_read(self): 121 """Start asynchronous conversion, returns immediately. 122 Returns maximum conversion delay [seconds] based on resolution.""" 123 with self._device as dev: 124 dev.write(_CONVERT) 125 return _CONVERSION_DELAY[self.resolution] 126 127 def read_temperature(self): 128 """Read the temperature. No polling of the conversion busy bit 129 (assumes that the conversion has completed).""" 130 return self._read_temp()