Request: Add custom I2C registers as sensors

Hey there!

I’m using Klipper/Mainsail for my AnyCubic Vyper along with a custom built wooden enclosure. My raspberry pi 4B is connected via I2C to DockerPi Sensor Hub, shown here:

The Sensor Hub contains multiple sensors for temperature, humidity, pressure (and some others, not necessary in this case), “hidden” behind a I2C “aggregator”.

So there’s one I2C device with one I2C address containing multiple registers with corresponding values and I’d like to add them separately as sensors.

Any ideas how I can achieve this goal? Tried to customize a own sensor, but right now I couldn’t fully emerse into the kind of code sorcery used.

One first attempt looked this (/home/pi/klipper/klippy/extras/hub.py), I used existing I2C code and tried to make it work:

# HUB(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

HUB_ADDR= 0x17

HUB_COMMANDS = {
    'TEMP_REG'       :0x01,
    'ON_BOARD_TEMP_REG'      :0x05,
    'ON_BOARD_HUMIDITY_REG'    :0x06,
    'BMP280_TEMP_REG'   :0x08

}


class HUB:
    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=HUB_ADDR, default_speed=100000)
        self.report_time = config.getint('HUB_report_time',30,minval=5)
        if self.resolution not in HUB_RESOLUTIONS:
            raise config.error("Invalid HUB Resolution. Valid are %s"
                % '|'.join(HUB_RESOLUTIONS.keys()))
        self.deviceId = config.get('sensor_type')
        self.temp = self.min_temp = self.max_temp = self.humidity = 0.
        self.sample_timer = self.reactor.register_timer(self._sample_hub)
        self.printer.add_object("hub " + self.name, self)
        self.printer.register_event_handler("klippy:connect",
                                            self.handle_connect)

    def handle_connect(self):
        self._init_hub()
        self.reactor.update_timer(self.sample_timer, self.reactor.NOW)

    def setup_minmax(self, min_temp, max_temp):
        self.min_temp = min_temp
        self.max_temp = max_temp

    def setup_callback(self, cb):
        self._callback = cb

    def get_report_time_delta(self):
        return self.report_time

    def _init_hub(self):

    def _sample_hub(self, eventtime):
        try:

            params = self.i2c.i2c_read([],3)

            response = bytearray(params['response'])
            rtemp  = response
            if self._chekCRC8(rtemp) != response[2]:
                logging.warn("HUB: Checksum error on Temperature reading!")
            else:
                self.temp = (0.002681 * float(rtemp) - 46.85)
                logging.debug("HUB: Temperature %.2f " % self.temp)

            # Read Humidity
            if self.hold_master_mode:
                self.i2c.i2c_write([HUB_COMMANDS['HUB_HUMID']])
            else:
                self.i2c.i2c_write([HUB_COMMANDS['HUB_HUMID_NH']])

            # Wait
            self.reactor.pause(self.reactor.monotonic()
            + HUB_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("HUB: 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.humidity = 0
                elif (self.humidity > 100):
                    self.humidity = 100
                # Only for HUB & SHT21.
                # Calculates temperature compensated Humidity, %RH
                if( self.deviceId in ['SHT21','HUB']
                    and self.temp > 0 and self.temp < 80):
                    logging.debug("HUB: Do temp compensation..")
                    self.humidity = self.humidity
                    + (25.0 - self.temp) * HUB_TEMP_COEFFICIENT;
                logging.debug("HUB: Humidity %.2f " % self.humidity)
        except Exception:
            logging.exception("HUB: Error reading data")
            self.temp = self.humidity = .0
            return self.reactor.NEVER

        if self.temp < self.min_temp or self.temp > self.max_temp:
            self.printer.invoke_shutdown(
                "HUB temperature %0.1f outside range of %0.1f:%.01f"
                % (self.temp, self.min_temp, self.max_temp))

        measured_time = self.reactor.monotonic()
        print_time = self.i2c.get_mcu().estimated_print_time(measured_time)
        self._callback(print_time, self.temp)
        return measured_time + self.report_time


    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")
	pheater.add_sensor_factory("HUB", HUB)

But couldn’t get it running

Did you ever get anywhere on this?

I have a slave Arduino with temperature/humidity sensors for print chamber and filament storage, door sensors, fan and leds.

Previously I was using the octoprint enclosure plug in for reading the temperatures.

Hoping for something outside of octoprint so that I could also potentially control the enclosure fan.

Hey qtemp!

Nah, never got any answer, so I discarded the SensorHUB and instead used independent sensors connected to different I2C-ports to avoid address collisions. It’s like 4x HUT21, 1x BME680 and some other sensors placed at intake, chamber, outlet behind fan.

Failed using I2C multiplexer, so I used raspi4 possibility of multiple I2C channels/ports…