From e03e0e9dac5694b35b81726bd2ed18f1a67b0a26 Mon Sep 17 00:00:00 2001 From: Kevin O'Connor Date: Wed, 11 Aug 2021 21:54:19 -0400 Subject: [PATCH] adxl345: Move g-code commands to new helper class Separate out the G-Code command handlers to a new ADXLCommandHelper() class. This helps separate the sensing code from the user interface code. Deprecate the RATE parameter of the ACCELEROMETER_MEASURE command. Signed-off-by: Kevin O'Connor --- docs/Config_Changes.md | 4 + docs/G-Codes.md | 26 +++--- klippy/extras/adxl345.py | 170 ++++++++++++++++++--------------------- 3 files changed, 95 insertions(+), 105 deletions(-) diff --git a/docs/Config_Changes.md b/docs/Config_Changes.md index e0521245..24d4b3e4 100644 --- a/docs/Config_Changes.md +++ b/docs/Config_Changes.md @@ -8,6 +8,10 @@ All dates in this document are approximate. ## Changes +20210830: The adxl345 ACCELEROMETER_MEASURE command no longer supports +a RATE parameter. To alter the query rate, update the printer.cfg +file and issue a RESTART command. + 20210821: Several config settings in `printer.configfile.settings` will now be reported as lists instead of raw strings. If the actual raw string is desired, use `printer.configfile.config` instead. diff --git a/docs/G-Codes.md b/docs/G-Codes.md index 745c63db..1c2644f3 100644 --- a/docs/G-Codes.md +++ b/docs/G-Codes.md @@ -725,21 +725,19 @@ is enabled: The following commands are available when an [adxl345 config section](Config_Reference.md#adxl345) is enabled: -- `ACCELEROMETER_MEASURE [CHIP=] [RATE=] - [NAME=]`: Starts accelerometer measurements at the requested - number of samples per second. If CHIP is not specified it defaults - to "default". Valid rates are 25, 50, 100, 200, 400, 800, 1600, - and 3200. The command works in a start-stop mode: when executed for - the first time, it starts the measurements, next execution stops - them. If RATE is not specified, then the default value is used - (either from `printer.cfg` or `3200` default value). The results of - measurements are written to a file named +- `ACCELEROMETER_MEASURE [CHIP=] [NAME=]`: Starts + accelerometer measurements at the requested number of samples per + second. If CHIP is not specified it defaults to "default". The + command works in a start-stop mode: when executed for the first + time, it starts the measurements, next execution stops them. The + results of measurements are written to a file named `/tmp/adxl345--.csv` where `` is the name of the - accelerometer chip (`my_chip_name` from `[adxl345 my_chip_name]`) and - `` is the optional NAME parameter. If NAME is not specified it - defaults to the current time in "YYYYMMDD_HHMMSS" format. If the - accelerometer does not have a name in its config section (simply - `[adxl345]`) part of the name is not generated. + accelerometer chip (`my_chip_name` from `[adxl345 my_chip_name]`) + and `` is the optional NAME parameter. If NAME is not + specified it defaults to the current time in "YYYYMMDD_HHMMSS" + format. If the accelerometer does not have a name in its config + section (simply `[adxl345]`) then `` part of the name is not + generated. - `ACCELEROMETER_QUERY [CHIP=] [RATE=]`: queries accelerometer for the current value. If CHIP is not specified it defaults to "default". If RATE is not specified, the default value diff --git a/klippy/extras/adxl345.py b/klippy/extras/adxl345.py index 23167ffc..1cddf0dd 100644 --- a/klippy/extras/adxl345.py +++ b/klippy/extras/adxl345.py @@ -96,10 +96,85 @@ class ADXL345Results: write_proc.daemon = True write_proc.start() +# Helper class for G-Code commands +class ADXLCommandHelper: + def __init__(self, config, chip): + self.printer = config.get_printer() + self.chip = chip + self.name = "default" + if len(config.get_name().split()) > 1: + self.name = config.get_name().split()[1] + self.register_commands(self.name) + if self.name == "default": + self.register_commands(None) + def register_commands(self, name): + # Register commands + gcode = self.printer.lookup_object('gcode') + gcode.register_mux_command("ACCELEROMETER_MEASURE", "CHIP", name, + self.cmd_ACCELEROMETER_MEASURE, + desc=self.cmd_ACCELEROMETER_MEASURE_help) + gcode.register_mux_command("ACCELEROMETER_QUERY", "CHIP", name, + self.cmd_ACCELEROMETER_QUERY, + desc=self.cmd_ACCELEROMETER_QUERY_help) + gcode.register_mux_command("ADXL345_DEBUG_READ", "CHIP", name, + self.cmd_ADXL345_DEBUG_READ, + desc=self.cmd_ADXL345_DEBUG_READ_help) + gcode.register_mux_command("ADXL345_DEBUG_WRITE", "CHIP", name, + self.cmd_ADXL345_DEBUG_WRITE, + desc=self.cmd_ADXL345_DEBUG_WRITE_help) + cmd_ACCELEROMETER_MEASURE_help = "Start/stop accelerometer" + def cmd_ACCELEROMETER_MEASURE(self, gcmd): + if not self.chip.is_measuring(): + # Start measurements + self.chip.start_measurements() + gcmd.respond_info("adxl345 measurements started") + return + # End measurements + name = gcmd.get("NAME", time.strftime("%Y%m%d_%H%M%S")) + if not name.replace('-', '').replace('_', '').isalnum(): + raise gcmd.error("Invalid adxl345 NAME parameter") + res = self.chip.finish_measurements() + # Write data to file + if self.name == "default": + filename = "/tmp/adxl345-%s.csv" % (name,) + else: + filename = "/tmp/adxl345-%s-%s.csv" % (self.name, name,) + res.write_to_file(filename) + gcmd.respond_info("Writing raw accelerometer data to %s file" + % (filename,)) + cmd_ACCELEROMETER_QUERY_help = "Query accelerometer for the current values" + def cmd_ACCELEROMETER_QUERY(self, gcmd): + if self.chip.is_measuring(): + raise gcmd.error("adxl345 measurements in progress") + self.chip.start_measurements() + self.printer.lookup_object('toolhead').dwell(1.) + result = self.chip.finish_measurements() + values = result.decode_samples() + if not values: + raise gcmd.error("No adxl345 measurements found") + _, accel_x, accel_y, accel_z = values[-1] + gcmd.respond_info("adxl345 values (x, y, z): %.6f, %.6f, %.6f" + % (accel_x, accel_y, accel_z)) + cmd_ADXL345_DEBUG_READ_help = "Query accelerometer register (for debugging)" + def cmd_ADXL345_DEBUG_READ(self, gcmd): + if self.chip.is_measuring(): + raise gcmd.error("adxl345 measurements in progress") + reg = gcmd.get("REG", minval=29, maxval=57, parser=lambda x: int(x, 0)) + val = self.chip.read_reg(reg) + gcmd.respond_info("ADXL345 REG[0x%x] = 0x%x" % (reg, val)) + cmd_ADXL345_DEBUG_WRITE_help = "Set accelerometer register (for debugging)" + def cmd_ADXL345_DEBUG_WRITE(self, gcmd): + if self.chip.is_measuring(): + raise gcmd.error("adxl345 measurements in progress") + reg = gcmd.get("REG", minval=29, maxval=57, parser=lambda x: int(x, 0)) + val = gcmd.get("VAL", minval=0, maxval=255, parser=lambda x: int(x, 0)) + self.chip.set_reg(reg, val) + # Printer class that controls ADXL345 chip class ADXL345: def __init__(self, config): self.printer = config.get_printer() + ADXLCommandHelper(config, self) self.query_rate = 0 self.last_tx_time = 0. am = {'x': (0, SCALE), 'y': (1, SCALE), 'z': (2, SCALE), @@ -127,34 +202,6 @@ class ADXL345: mcu.register_config_callback(self._build_config) mcu.register_response(self._handle_adxl345_start, "adxl345_start", oid) mcu.register_response(self._handle_adxl345_data, "adxl345_data", oid) - # Register commands - self.name = "default" - if len(config.get_name().split()) > 1: - self.name = config.get_name().split()[1] - gcode = self.printer.lookup_object('gcode') - gcode.register_mux_command("ACCELEROMETER_MEASURE", "CHIP", self.name, - self.cmd_ACCELEROMETER_MEASURE, - desc=self.cmd_ACCELEROMETER_MEASURE_help) - gcode.register_mux_command("ACCELEROMETER_QUERY", "CHIP", self.name, - self.cmd_ACCELEROMETER_QUERY, - desc=self.cmd_ACCELEROMETER_QUERY_help) - gcode.register_mux_command("ADXL345_DEBUG_READ", "CHIP", self.name, - self.cmd_ADXL345_DEBUG_READ, - desc=self.cmd_ADXL345_DEBUG_READ_help) - gcode.register_mux_command("ADXL345_DEBUG_WRITE", "CHIP", self.name, - self.cmd_ADXL345_DEBUG_WRITE, - desc=self.cmd_ADXL345_DEBUG_WRITE_help) - if self.name == "default": - gcode.register_mux_command("ACCELEROMETER_MEASURE", "CHIP", None, - self.cmd_ACCELEROMETER_MEASURE) - gcode.register_mux_command("ACCELEROMETER_QUERY", "CHIP", None, - self.cmd_ACCELEROMETER_QUERY) - gcode.register_mux_command("ADXL345_DEBUG_READ", "CHIP", None, - self.cmd_ADXL345_DEBUG_READ, - desc=self.cmd_ADXL345_DEBUG_READ_help) - gcode.register_mux_command("ADXL345_DEBUG_WRITE", "CHIP", None, - self.cmd_ADXL345_DEBUG_WRITE, - desc=self.cmd_ADXL345_DEBUG_WRITE_help) def is_initialized(self): # In case of miswiring, testing ADXL345 device ID prevents treating # noise or wrong signal as a correctly initialized device @@ -213,10 +260,9 @@ class ADXL345: reg, val, stored_val)) def is_measuring(self): return self.query_rate > 0 - def start_measurements(self, rate=None): + def start_measurements(self): if self.is_measuring(): return - rate = rate or self.data_rate if not self.is_initialized(): self.initialize() # Setup chip in requested query rate @@ -225,7 +271,7 @@ class ADXL345: clock = self.mcu.print_time_to_clock(self.last_tx_time) self.set_reg(REG_POWER_CTL, 0x00, minclock=clock) self.set_reg(REG_FIFO_CTL, 0x00) - self.set_reg(REG_BW_RATE, QUERY_RATES[rate]) + self.set_reg(REG_BW_RATE, QUERY_RATES[self.data_rate]) self.set_reg(REG_FIFO_CTL, 0x80) # Setup samples print_time = self.printer.lookup_object('toolhead').get_last_move_time() @@ -234,9 +280,9 @@ class ADXL345: self.samples_start1 = self.samples_start2 = print_time # Start bulk reading reqclock = self.mcu.print_time_to_clock(print_time) - rest_ticks = self.mcu.seconds_to_clock(4. / rate) + rest_ticks = self.mcu.seconds_to_clock(4. / self.data_rate) self.last_tx_time = print_time - self.query_rate = rate + self.query_rate = self.data_rate self.query_adxl345_cmd.send([self.oid, reqclock, rest_ticks], reqclock=reqclock) def finish_measurements(self): @@ -263,64 +309,6 @@ class ADXL345: logging.info("ADXL345 finished %d measurements: %s", res.total_count, res.get_stats()) return res - def end_query(self, name, gcmd): - if not self.is_measuring(): - return - res = self.finish_measurements() - # Write data to file - if self.name == "default": - filename = "/tmp/adxl345-%s.csv" % (name,) - else: - filename = "/tmp/adxl345-%s-%s.csv" % (self.name, name,) - res.write_to_file(filename) - gcmd.respond_info( - "Writing raw accelerometer data to %s file" % (filename,)) - cmd_ACCELEROMETER_MEASURE_help = "Start/stop accelerometer" - def cmd_ACCELEROMETER_MEASURE(self, gcmd): - if self.is_measuring(): - name = gcmd.get("NAME", time.strftime("%Y%m%d_%H%M%S")) - if not name.replace('-', '').replace('_', '').isalnum(): - raise gcmd.error("Invalid adxl345 NAME parameter") - self.end_query(name, gcmd) - gcmd.respond_info("adxl345 measurements stopped") - else: - rate = gcmd.get_int("RATE", self.data_rate) - if rate not in QUERY_RATES: - raise gcmd.error("Not a valid adxl345 query rate: %d" % (rate,)) - self.start_measurements(rate) - gcmd.respond_info("adxl345 measurements started") - cmd_ACCELEROMETER_QUERY_help = "Query accelerometer for the current values" - def cmd_ACCELEROMETER_QUERY(self, gcmd): - if self.is_measuring(): - raise gcmd.error("adxl345 measurements in progress") - self.start_measurements() - reactor = self.printer.get_reactor() - eventtime = starttime = reactor.monotonic() - while not self.raw_samples: - eventtime = reactor.pause(eventtime + .1) - if eventtime > starttime + 3.: - # Try to shutdown the measurements - self.finish_measurements() - raise gcmd.error("Timeout reading adxl345 data") - result = self.finish_measurements() - values = result.decode_samples() - _, accel_x, accel_y, accel_z = values[-1] - gcmd.respond_info("adxl345 values (x, y, z): %.6f, %.6f, %.6f" % ( - accel_x, accel_y, accel_z)) - cmd_ADXL345_DEBUG_READ_help = "Query accelerometer register (for debugging)" - def cmd_ADXL345_DEBUG_READ(self, gcmd): - if self.is_measuring(): - raise gcmd.error("adxl345 measurements in progress") - reg = gcmd.get("REG", minval=29, maxval=57, parser=lambda x: int(x, 0)) - val = self.read_reg(reg) - gcmd.respond_info("ADXL345 REG[0x%x] = 0x%x" % (reg, val)) - cmd_ADXL345_DEBUG_WRITE_help = "Set accelerometer register (for debugging)" - def cmd_ADXL345_DEBUG_WRITE(self, gcmd): - if self.is_measuring(): - raise gcmd.error("adxl345 measurements in progress") - reg = gcmd.get("REG", minval=29, maxval=57, parser=lambda x: int(x, 0)) - val = gcmd.get("VAL", minval=0, maxval=255, parser=lambda x: int(x, 0)) - self.set_reg(reg, val) def load_config(config): return ADXL345(config)