mirror of
				https://github.com/Klipper3d/klipper.git
				synced 2025-10-31 02:15:52 +01:00 
			
		
		
		
	Use alternate import syntax to improve Python3 compatibility. Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
		
			
				
	
	
		
			238 lines
		
	
	
		
			8.5 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			238 lines
		
	
	
		
			8.5 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| # HTU21D(F)/Si7013/Si7020/Si7021/SHT21 i2c based temperature sensors support
 | |
| #
 | |
| # Copyright (C) 2020  Lucio Tarantino <lucio.tarantino@gmail.com>
 | |
| #
 | |
| # This file may be distributed under the terms of the GNU GPLv3 license.
 | |
| import logging
 | |
| from . import bus
 | |
| 
 | |
| ######################################################################
 | |
| # NOTE: The implementation requires write support of length 0
 | |
| #       before reading on the i2c bus of the mcu.
 | |
| #
 | |
| # Compatible Sensors:
 | |
| #       HTU21D - Tested on Linux MCU.
 | |
| #       Si7013 - Untested
 | |
| #       Si7020 - Untested
 | |
| #       Si7021 - Untested
 | |
| #       SHT21  - Untested
 | |
| #
 | |
| ######################################################################
 | |
| 
 | |
| HTU21D_I2C_ADDR= 0x40
 | |
| 
 | |
| HTU21D_COMMANDS = {
 | |
|     'HTU21D_TEMP'       :0xE3,
 | |
|     'HTU21D_HUMID'      :0xE5,
 | |
|     'HTU21D_TEMP_NH'    :0xF3,
 | |
|     'HTU21D_HUMID_NH'   :0xF5,
 | |
|     'WRITE'             :0xE6,
 | |
|     'READ'              :0xE7,
 | |
|     'RESET'             :0xFE,
 | |
|     'SERIAL'            :[0xFA,0x0F,0xFC,0xC9],
 | |
|     'FIRMWARE_READ'     :[0x84,0xB8]
 | |
| 
 | |
| }
 | |
| 
 | |
| HTU21D_RESOLUTION_MASK = 0x7E;
 | |
| HTU21D_RESOLUTIONS = {
 | |
|     'TEMP14_HUM12':int('00000000',2),
 | |
|     'TEMP13_HUM10':int('10000000',2),
 | |
|     'TEMP12_HUM08':int('00000001',2),
 | |
|     'TEMP11_HUM11':int('10000001',2)
 | |
| }
 | |
| 
 | |
| # Device with conversion time for tmp/resolution bit
 | |
| # The format is:
 | |
| #  <CHIPNAME>:{id:<ID>, ..<RESOlUTION>:[<temp time>,<humidity time>].. }
 | |
| HTU21D_DEVICES = {
 | |
|     'SI7013':{'id':0x0D,
 | |
|         'TEMP14_HUM12':[.11,.12],
 | |
|         'TEMP13_HUM10':[ .7, .5],
 | |
|         'TEMP12_HUM08':[ .4, .4],
 | |
|         'TEMP11_HUM11':[ .3, .7]},
 | |
|     'SI7020':{'id':0x14,
 | |
|         'TEMP14_HUM12':[.11,.12],
 | |
|         'TEMP13_HUM10':[ .7, .5],
 | |
|         'TEMP12_HUM08':[ .4, .4],
 | |
|         'TEMP11_HUM11':[ .3, .7]},
 | |
|     'SI7021':{'id':0x14,
 | |
|         'TEMP14_HUM12':[.11,.12],
 | |
|         'TEMP13_HUM10':[ .7, .5],
 | |
|         'TEMP12_HUM08':[ .4, .4],
 | |
|         'TEMP11_HUM11':[ .3, .7]},
 | |
|     'SHT21': {'id':0x31,
 | |
|         'TEMP14_HUM12':[.85,.29],
 | |
|         'TEMP13_HUM10':[.43, .9],
 | |
|         'TEMP12_HUM08':[.22, .4],
 | |
|         'TEMP11_HUM11':[.11,.15]},
 | |
|     'HTU21D':{'id':0x32,
 | |
|         'TEMP14_HUM12':[.50,.16],
 | |
|         'TEMP13_HUM10':[.25, .5],
 | |
|         'TEMP12_HUM08':[.13, .3],
 | |
|         'TEMP11_HUM11':[.12, .8]}
 | |
| }
 | |
| #temperature coefficient for RH compensation at range 0C..80C,
 | |
| #  for HTU21D & SHT21 only
 | |
| HTU21D_TEMP_COEFFICIENT= -0.15
 | |
| #crc8 polynomial for 16bit value, CRC8 -> x^8 + x^5 + x^4 + 1
 | |
| HTU21D_CRC8_POLYNOMINAL= 0x13100
 | |
| 
 | |
| class HTU21D:
 | |
|     def __init__(self, config):
 | |
|         self.printer = config.get_printer()
 | |
|         self.name = config.get_name().split()[-1]
 | |
|         self.reactor = self.printer.get_reactor()
 | |
|         self.i2c = bus.MCU_I2C_from_config(
 | |
|             config, default_addr=HTU21D_I2C_ADDR, default_speed=100000)
 | |
|         self.hold_master_mode = config.getboolean('htu21d_hold_master',False)
 | |
|         self.resolution = config.get('htu21d_resolution','TEMP12_HUM08')
 | |
|         self.report_time = config.getint('htu21d_report_time',30,minval=5)
 | |
|         if self.resolution not in HTU21D_RESOLUTIONS:
 | |
|             raise config.error("Invalid HTU21D Resolution. Valid are %s"
 | |
|                 % '|'.join(HTU21D_RESOLUTIONS.keys()))
 | |
|         self.deviceId = config.get('sensor_type')
 | |
|         self.temp = self.humidity = 0.
 | |
|         self.sample_timer = self.reactor.register_timer(self._sample_htu21d)
 | |
|         self.printer.add_object("htu21d " + self.name, self)
 | |
|         self.printer.register_event_handler("klippy:ready", self.handle_ready)
 | |
| 
 | |
|     def handle_ready(self):
 | |
|         self._init_htu21d()
 | |
|         self.reactor.update_timer(self.sample_timer, self.reactor.NOW)
 | |
| 
 | |
|     def setup_minmax(self, min_temp, max_temp):
 | |
|         pass
 | |
| 
 | |
|     def setup_callback(self, cb):
 | |
|         self._callback = cb
 | |
| 
 | |
|     def _init_htu21d(self):
 | |
|         # Device Soft Reset
 | |
|         self.i2c.i2c_write([HTU21D_COMMANDS['RESET']])
 | |
|         # Wait 15ms after reset
 | |
|         self.reactor.pause(self.reactor.monotonic() + .15)
 | |
| 
 | |
|         # Read ChipId
 | |
|         params = self.i2c.i2c_read([HTU21D_COMMANDS['SERIAL'][2],
 | |
|                                     HTU21D_COMMANDS['SERIAL'][3]], 3)
 | |
|         response = bytearray(params['response'])
 | |
|         rdevId = response[0] << 8
 | |
|         rdevId |= response[1]
 | |
|         checksum = response[2]
 | |
|         if self._chekCRC8(rdevId) != checksum:
 | |
|             logging.warn("htu21d: Reading deviceId !Checksum error!")
 | |
|         rdevId = rdevId >> 8
 | |
|         deviceId_list = list(
 | |
|             filter(
 | |
|               lambda elem: HTU21D_DEVICES[elem]['id'] == rdevId,HTU21D_DEVICES)
 | |
|             )
 | |
|         if len(deviceId_list) != 0:
 | |
|             logging.info("htu21d: Found Device Type %s" % deviceId_list[0])
 | |
|         else:
 | |
|             logging.warn("htu21d: Unknown Device ID %#x " % rdevId)
 | |
| 
 | |
|         if(self.deviceId != deviceId_list[0]):
 | |
|             logging.warn(
 | |
|                 "htu21d: Found device %s. Forcing to type %s as config.",
 | |
|                  deviceId_list[0],self.deviceId)
 | |
| 
 | |
|         # Set Resolution
 | |
|         params = self.i2c.i2c_read([HTU21D_COMMANDS['READ']], 1)
 | |
|         response = bytearray(params['response'])
 | |
|         registerData = response[0] & HTU21D_RESOLUTION_MASK
 | |
|         registerData |= HTU21D_RESOLUTIONS[self.resolution]
 | |
|         self.i2c.i2c_write([HTU21D_COMMANDS['WRITE']],registerData)
 | |
|         logging.info("htu21d: Setting resolution to %s " % self.resolution)
 | |
| 
 | |
|     def _sample_htu21d(self, eventtime):
 | |
|         try:
 | |
|             # Read Temeprature
 | |
|             if self.hold_master_mode:
 | |
|                 params = self.i2c.i2c_write([HTU21D_COMMANDS['HTU21D_TEMP']])
 | |
|             else:
 | |
|                 params = self.i2c.i2c_write([HTU21D_COMMANDS['HTU21D_TEMP_NH']])
 | |
| 
 | |
|             # Wait
 | |
|             self.reactor.pause(self.reactor.monotonic()
 | |
|             + HTU21D_DEVICES[self.deviceId][self.resolution][0])
 | |
| 
 | |
| 
 | |
|             params = self.i2c.i2c_read([],3)
 | |
| 
 | |
|             response = bytearray(params['response'])
 | |
|             rtemp  = response[0] << 8
 | |
|             rtemp |= response[1]
 | |
|             if self._chekCRC8(rtemp) != response[2]:
 | |
|                 logging.warn("htu21d: Checksum error on Temperature reading!")
 | |
|             else:
 | |
|                 self.temp = (0.002681 * float(rtemp) - 46.85)
 | |
|                 logging.debug("htu21d: Temperature %.2f " % self.temp)
 | |
| 
 | |
|             # Read Humidity
 | |
|             if self.hold_master_mode:
 | |
|                 self.i2c.i2c_write([HTU21D_COMMANDS['HTU21D_HUMID']])
 | |
|             else:
 | |
|                 self.i2c.i2c_write([HTU21D_COMMANDS['HTU21D_HUMID_NH']])
 | |
| 
 | |
|             # Wait
 | |
|             self.reactor.pause(self.reactor.monotonic()
 | |
|             + HTU21D_DEVICES[self.deviceId][self.resolution][1])
 | |
| 
 | |
|             params = self.i2c.i2c_read([],3)
 | |
| 
 | |
|             response = bytearray(params['response'])
 | |
|             rhumid = response[0] << 8
 | |
|             rhumid|= response[1]
 | |
|             if self._chekCRC8(rhumid) != response[2]:
 | |
|                 logging.warn("htu21d: Checksum error on Humidity reading!")
 | |
|             else:
 | |
|                 #clear status bits,
 | |
|                 # humidity always returns xxxxxx10 in the LSB field
 | |
|                 rhumid   ^= 0x02;
 | |
|                 self.humidity = (0.001907 * float(rhumid) - 6)
 | |
|                 if (self.humidity < 0):
 | |
|                     #due to RH accuracy, measured value might be
 | |
|                     # slightly less than 0 or more 100
 | |
|                     self.temp = 0
 | |
|                 elif (self.humidity > 100):
 | |
|                     self.humidity = 100
 | |
|                 # Only for HTU21D & SHT21.
 | |
|                 # Calculates temperature compensated Humidity, %RH
 | |
|                 if( self.deviceId in ['SHT21','HTU21D']
 | |
|                     and self.temp > 0 and self.temp < 80):
 | |
|                     logging.debug("htu21d: Do temp compensation..")
 | |
|                     self.humidity = self.humidity
 | |
|                     + (25.0 - self.temp) * HTU21D_TEMP_COEFFICIENT;
 | |
|                 logging.debug("htu21d: Humidity %.2f " % self.humidity)
 | |
|         except Exception:
 | |
|             logging.exception("htu21d: Error reading data")
 | |
|             self.temp = self.humidity = .0
 | |
|             return self.reactor.NEVER
 | |
| 
 | |
|         measured_time = self.reactor.monotonic()
 | |
|         self._callback(measured_time, self.temp)
 | |
|         return measured_time + self.report_time
 | |
| 
 | |
|     def _chekCRC8(self,data):
 | |
|         for bit in range(0,16):
 | |
|             if (data & 0x8000):
 | |
|                 data = (data << 1) ^ HTU21D_CRC8_POLYNOMINAL;
 | |
|             else:
 | |
|                 data <<= 1
 | |
|         data = data >> 8
 | |
|         return data
 | |
| 
 | |
|     def get_status(self, eventtime):
 | |
|         return {
 | |
|             'temperature': self.temp,
 | |
|             'humidity': self.humidity,
 | |
|         }
 | |
| 
 | |
| 
 | |
| def load_config(config):
 | |
|     # Register sensor
 | |
|     pheater = config.get_printer().lookup_object("heaters")
 | |
|     for stype in HTU21D_DEVICES:
 | |
|         pheater.add_sensor_factory(stype, HTU21D)
 |