aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLucio Tarantino <lucio.tarantino@gmail.com>2020-05-04 00:47:24 +0200
committerGitHub <noreply@github.com>2020-05-03 18:47:24 -0400
commitdac42efbd9236cfdeea6baefe65bbd3ab46abcdb (patch)
tree06823885e06d40b914af9f9165f453e6824c7fda
parentf8649b4ba9347bde03191bb0820ab23142a0b429 (diff)
downloadkutter-dac42efbd9236cfdeea6baefe65bbd3ab46abcdb.tar.gz
kutter-dac42efbd9236cfdeea6baefe65bbd3ab46abcdb.tar.xz
kutter-dac42efbd9236cfdeea6baefe65bbd3ab46abcdb.zip
htu21d: Support for HTI21D family sensor on I2C bus (#2803)
Signed-off-by: Lucio Tarantino <lucio.tarantino@gmail.com>
-rw-r--r--config/example-extras.cfg34
-rw-r--r--config/sample-macros.cfg29
-rw-r--r--klippy/extras/heaters.py1
-rw-r--r--klippy/extras/htu21d.py238
-rw-r--r--src/linux/i2c.c3
5 files changed, 304 insertions, 1 deletions
diff --git a/config/example-extras.cfg b/config/example-extras.cfg
index 830381ce..7463051c 100644
--- a/config/example-extras.cfg
+++ b/config/example-extras.cfg
@@ -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
diff --git a/config/sample-macros.cfg b/config/sample-macros.cfg
index 3b92e34b..60ec51ec 100644
--- a/config/sample-macros.cfg
+++ b/config/sample-macros.cfg
@@ -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))}
diff --git a/klippy/extras/heaters.py b/klippy/extras/heaters.py
index 29fb5d6e..cdb5c186 100644
--- a/klippy/extras/heaters.py
+++ b/klippy/extras/heaters.py
@@ -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(
diff --git a/klippy/extras/htu21d.py b/klippy/extras/htu21d.py
new file mode 100644
index 00000000..3c9e0590
--- /dev/null
+++ b/klippy/extras/htu21d.py
@@ -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)
diff --git a/src/linux/i2c.c b/src/linux/i2c.c
index b8e8f253..ac3feb32 100644
--- a/src/linux/i2c.c
+++ b/src/linux/i2c.c
@@ -91,7 +91,8 @@ void
i2c_read(struct i2c_config config, uint8_t reg_len, uint8_t *reg
, uint8_t read_len, uint8_t *data)
{
- i2c_write(config, reg_len, reg);
+ if(reg_len != 0)
+ i2c_write(config, reg_len, reg);
int ret = read(config.fd, data, read_len);
if (ret != read_len) {
if (ret < 0)