aboutsummaryrefslogtreecommitdiffstats
path: root/klippy/extras/adc_scaled.py
blob: 454e592e3f409aee5970bc30a0a1d84f4699a5fc (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
# Support for scaling ADC values based on measured VREF and VSSA
#
# Copyright (C) 2020  Kevin O'Connor <kevin@koconnor.net>
#
# This file may be distributed under the terms of the GNU GPLv3 license.

SAMPLE_TIME = 0.001
SAMPLE_COUNT = 8
REPORT_TIME = 0.300


class MCU_scaled_adc:
    def __init__(self, main, pin_params):
        self._main = main
        self._last_state = (0.0, 0.0)
        self._mcu_adc = main.mcu.setup_pin("adc", pin_params)
        query_adc = main.printer.lookup_object("query_adc")
        qname = main.name + ":" + pin_params["pin"]
        query_adc.register_adc(qname, self._mcu_adc)
        self._callback = None
        self.setup_adc_sample = self._mcu_adc.setup_adc_sample
        self.get_mcu = self._mcu_adc.get_mcu

    def _handle_callback(self, read_time, read_value):
        max_adc = self._main.last_vref[1]
        min_adc = self._main.last_vssa[1]
        scaled_val = (read_value - min_adc) / (max_adc - min_adc)
        self._last_state = (scaled_val, read_time)
        self._callback(read_time, scaled_val)

    def setup_adc_callback(self, report_time, callback):
        self._callback = callback
        self._mcu_adc.setup_adc_callback(report_time, self._handle_callback)

    def get_last_value(self):
        return self._last_state


class PrinterADCScaled:
    def __init__(self, config):
        self.printer = config.get_printer()
        self.name = config.get_name().split()[1]
        self.last_vref = (0.0, 0.0)
        self.last_vssa = (0.0, 0.0)
        # Configure vref and vssa pins
        self.mcu_vref = self._config_pin(config, "vref", self.vref_callback)
        self.mcu_vssa = self._config_pin(config, "vssa", self.vssa_callback)
        smooth_time = config.getfloat("smooth_time", 2.0, above=0.0)
        self.inv_smooth_time = 1.0 / smooth_time
        self.mcu = self.mcu_vref.get_mcu()
        if self.mcu is not self.mcu_vssa.get_mcu():
            raise config.error("vref and vssa must be on same mcu")
        # Register setup_pin
        ppins = self.printer.lookup_object("pins")
        ppins.register_chip(self.name, self)

    def _config_pin(self, config, name, callback):
        pin_name = config.get(name + "_pin")
        ppins = self.printer.lookup_object("pins")
        mcu_adc = ppins.setup_pin("adc", pin_name)
        mcu_adc.setup_adc_callback(REPORT_TIME, callback)
        mcu_adc.setup_adc_sample(SAMPLE_TIME, SAMPLE_COUNT)
        query_adc = config.get_printer().load_object(config, "query_adc")
        query_adc.register_adc(self.name + ":" + name, mcu_adc)
        return mcu_adc

    def setup_pin(self, pin_type, pin_params):
        if pin_type != "adc":
            raise self.printer.config_error("adc_scaled only supports adc pins")
        return MCU_scaled_adc(self, pin_params)

    def calc_smooth(self, read_time, read_value, last):
        last_time, last_value = last
        time_diff = read_time - last_time
        value_diff = read_value - last_value
        adj_time = min(time_diff * self.inv_smooth_time, 1.0)
        smoothed_value = last_value + value_diff * adj_time
        return (read_time, smoothed_value)

    def vref_callback(self, read_time, read_value):
        self.last_vref = self.calc_smooth(read_time, read_value, self.last_vref)

    def vssa_callback(self, read_time, read_value):
        self.last_vssa = self.calc_smooth(read_time, read_value, self.last_vssa)


def load_config_prefix(config):
    return PrinterADCScaled(config)