htu21d: Support for HTI21D family sensor on I2C bus (#2803)

Signed-off-by: Lucio Tarantino <lucio.tarantino@gmail.com>
This commit is contained in:
Lucio Tarantino 2020-05-04 00:47:24 +02:00 committed by GitHub
parent f8649b4ba9
commit dac42efbd9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 304 additions and 1 deletions

View File

@ -966,6 +966,40 @@
# The I2C speed (in Hz) to use when communicating with the sensor. Default
# is 100000. On some MCUs changing this value has no effect.
# HTU21D family two wire interface (I2C) environmental sensor.
# Note that this sensor is not intended for use with extruders and heater beds,
# but rather for montitoring ambient temperature (C) and relative humidity.
# See sample-macros.cfg for a gcode_macro that may be used to report humidity
# in addition to temperature.
#[temperature_sensor my_sensor]
# See the "temperature_sensor" section below for a description of its
# parameters. The parameters below describe HTU21D family sensor parameters.
#sensor_type:
# Must be "HTU21D" , "SI7013", "SI7020", "SI7021" or "SHT21"
#i2c_address:
# Default is 64 (0x40).
#i2c_mcu:
# MCU the sensor is connected to. Default is the primary mcu.
#i2c_bus:
# The I2C bus the sensor is connected to. On some MCU platforms the default
# is bus 0. On platforms without bus 0 this parameter is required.
#i2c_speed:
# The I2C speed (in Hz) to use when communicating with the sensor. Default
# is 100000. On some MCUs changing this value has no effect.
#htu21d_hold_master:
# If the sensor can hold the I2C buf while reading. If True no other bus
# comunication can be performed while reading is in progress.Default is False
#htu21d_resolution:
# The resolution of temperature and humidity reading.
# Valid values are:
# 'TEMP14_HUM12' -> 14bit for Temp and 12bit for humidity
# 'TEMP13_HUM10' -> 13bit for Temp and 10bit for humidity
# 'TEMP12_HUM08' -> 12bit for Temp and 08bit for humidity
# 'TEMP11_HUM11' -> 11bit for Temp and 11bit for humidity
# Default is: "TEMP11_HUM11"
#htu21d_report_time:
# interval in seconds between readings. Default is 30
# Generic heaters (one may define any number of sections with a
# "heater_generic" prefix). These heaters behave similarly to standard
# heaters (extruders, heated beds). Use the SET_HEATER_TEMPERATURE

View File

@ -147,3 +147,32 @@ gcode:
printer[SENSOR].temperature,
printer[SENSOR].pressure,
printer[SENSOR].humidity))}
######################################################################
# HTU21D family Environmental Sensor
######################################################################
# The macro below assumes you have a HTU21D sensor_type defined in one
# of the applicable sections in printer.cfg, such as:
#
#[temperature_sensor my_sensor]
#sensor_type: HTU21D
#
# Note the format of the parameter SENSOR in the macro below. The HTU21D
# sensor status can be accessed using the format "htu21d <section_name>".
# The example section above is named "my_sensor", thus the htu21d can be
# queried as follows:
#
# QUERY_HTU21D SENSOR='htu21d my_sensor'
#
# Since a default parameter is defined one could simply enter QUERY_HTU21D
# as well.
[gcode_macro QUERY_HTU21D]
default_parameter_SENSOR: htu21d my_sensor
gcode:
{printer.gcode.action_respond_info(
"Temperature: %.2f C\n"
"Humidity: %.2f%%" % (
printer[SENSOR].temperature,
printer[SENSOR].humidity))}

View File

@ -265,6 +265,7 @@ class PrinterHeaters:
self.printer.try_load_module(config, "adc_temperature")
self.printer.try_load_module(config, "spi_temperature")
self.printer.try_load_module(config, "bme280")
self.printer.try_load_module(config, "htu21d")
sensor_type = config.get('sensor_type')
if sensor_type not in self.sensor_factories:
raise self.printer.config_error(

238
klippy/extras/htu21d.py Normal file
View File

@ -0,0 +1,238 @@
# 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.
######################################################################
# 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
#
######################################################################
import bus
import logging
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)

View File

@ -91,6 +91,7 @@ void
i2c_read(struct i2c_config config, uint8_t reg_len, uint8_t *reg
, uint8_t read_len, uint8_t *data)
{
if(reg_len != 0)
i2c_write(config, reg_len, reg);
int ret = read(config.fd, data, read_len);
if (ret != read_len) {