Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions docs/Config_Reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -5953,6 +5953,31 @@ sensor_type:
# decreasing force value when placed under load.
```

#### CS1237

The CS1237 sensor backend supports both `[load_cell]` and
`[load_cell_probe]`.

```
[load_cell my_cs1237]
sensor_type: cs1237
sclk_pin:
# The pin connected to the CS1237 clock line. This parameter must be
# provided.
dout_pin:
# The pin connected to the bidirectional CS1237 DOUT line. This parameter
# must be provided.
#sample_rate: 640
# Valid values for sample_rate are 10, 40, 640, or 1280. The default is 640.
#gain: 128
# Valid values for gain are 1, 2, 64, or 128. The default is 128.
#reference_output: True
# Enable the CS1237 REFO reference output. The default is True.
```

Both pins must be on the same MCU. DOUT is bidirectional during
configuration.

#### HX711
This is a 24 bit low sample rate chip using "bit-bang" communications. It is
suitable for filament scales.
Expand Down
1 change: 1 addition & 0 deletions docs/Load_Cell.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ reference_tare_counts: 12345
* [`ads1220`](Config_Reference.md#ads1220)
* [`ads131m02`](Config_Reference.md#ads131m02)
* [`ads131m04`](Config_Reference.md#ads131m04)
* [`cs1237`](Config_Reference.md#cs1237)

- `counts_per_gram: 245`\
_Default Value: None_\
Expand Down
3 changes: 2 additions & 1 deletion klippy/extras/load_cell/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from klippy.configfile import ConfigWrapper
from klippy.printer import Printer, SubsystemComponentCollection

from . import ads131m0x, ads1220, hx71x
from . import ads131m0x, ads1220, cs1237, hx71x
from .load_cell import LoadCell


Expand All @@ -16,6 +16,7 @@ def register_components(subsystem: SubsystemComponentCollection):
hx71x.HX71X_SENSOR_TYPES
| ads1220.ADS1220_SENSOR_TYPE
| ads131m0x.ADS131M0X_SENSOR_TYPES
| cs1237.CS1237_SENSOR_TYPE
)
for name, sensor in sensors.items():
subsystem.register_component("load_cell_sensors", name, sensor)
Expand Down
152 changes: 152 additions & 0 deletions klippy/extras/load_cell/cs1237.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
# Chipsea CS1237 support
#
# Copyright (C) 2026 Xiaoyue Cui <2508041672@qq.com>
#
# This file may be distributed under the terms of the GNU GPLv3 license.
import logging

from .. import bulk_sensor

UPDATE_INTERVAL = 0.10


class CS1237:
def __init__(self, config):
self.printer = printer = config.get_printer()
self.name = config.get_name().split()[-1]
self.last_error_count = 0
self._sensor_errors = {}

rate_options = {"10": 0, "40": 1, "640": 2, "1280": 3}
gain_options = {"1": 0, "2": 1, "64": 2, "128": 3}
self.sps = config.getchoice(
"sample_rate",
{key: int(key) for key in rate_options},
default="640",
)
rate = config.getchoice("sample_rate", rate_options, default="640")
gain = config.getchoice("gain", gain_options, default="128")
self.reference_output = config.getboolean("reference_output", True)
ref_off = 0 if self.reference_output else 1
self.chip_config = (ref_off << 6) | (rate << 4) | (gain << 2)

dout_pin_name = config.get("dout_pin")
sclk_pin_name = config.get("sclk_pin")
ppins = printer.lookup_object("pins")
dout_ppin = ppins.lookup_pin(dout_pin_name)
sclk_ppin = ppins.lookup_pin(sclk_pin_name)
self.mcu = mcu = dout_ppin["chip"]
if sclk_ppin["chip"] is not mcu:
raise config.error(
"CS1237 config error: All pins must be "
"connected to the same MCU"
)
self.dout_pin = dout_ppin["pin"]
self.sclk_pin = sclk_ppin["pin"]
self.oid = mcu.create_oid()

chip_smooth = self.sps * UPDATE_INTERVAL * 2
self.ffreader = bulk_sensor.FixedFreqReader(mcu, chip_smooth, "<i")
self.batch_bulk = bulk_sensor.BatchBulkHelper(
printer,
self._process_batch,
self._start_measurements,
self._finish_measurements,
UPDATE_INTERVAL,
)
self.query_cmd = None
self.attach_probe_cmd = None
mcu.add_config_cmd(
"config_cs1237 oid=%d config=%d dout_pin=%s sclk_pin=%s"
% (self.oid, self.chip_config, self.dout_pin, self.sclk_pin)
)
mcu.add_config_cmd(
"query_cs1237 oid=%d rest_ticks=0" % self.oid, on_restart=True
)
mcu.register_config_callback(self._build_config)

def _build_config(self):
cmd_queue = self.mcu.alloc_command_queue()
self.query_cmd = self.mcu.lookup_command(
"query_cs1237 oid=%c rest_ticks=%u", cq=cmd_queue
)
self.attach_probe_cmd = self.mcu.lookup_command(
"cs1237_attach_load_cell_probe oid=%c load_cell_probe_oid=%c",
cq=cmd_queue,
)
self.ffreader.setup_query_command(
"query_cs1237_status oid=%c", oid=self.oid, cq=cmd_queue
)
errors = self.mcu.get_enumerations().get("cs1237_error:", {})
self._sensor_errors = {v: k for k, v in errors.items()}

def attach_load_cell_probe(self, load_cell_probe_oid):
self.attach_probe_cmd.send([self.oid, load_cell_probe_oid])

def get_mcu(self):
return self.mcu

def get_samples_per_second(self):
return self.sps

def get_range(self):
return -0x800000, 0x7FFFFF

def get_channel_count(self):
return 1

def get_status(self, eventtime):
return {
"errors": self.last_error_count,
"overflows": self.ffreader.get_last_overflows(),
"sample_rate": self.sps,
}

def lookup_sensor_error(self, error_code):
return self._sensor_errors.get(error_code, "Unknown CS1237 error")

def add_client(self, callback):
self.batch_bulk.add_client(callback)

def _convert_samples(self, samples):
adc_factor = 1.0 / (1 << 23)
count = 0
for ptime, value in samples:
top_byte = (value >> 24) & 0xFF
if top_byte != 0x00 and top_byte != 0xFF:
self.last_error_count += 1
continue
samples[count] = (
round(ptime, 6),
value,
round(value * adc_factor, 9),
)
count += 1
del samples[count:]

def _start_measurements(self):
rest_ticks = self.mcu.seconds_to_clock(1.0 / (10.0 * self.sps))
self.query_cmd.send([self.oid, rest_ticks])
logging.info(
"CS1237 starting '%s' measurements at %d SPS", self.name, self.sps
)
self.ffreader.note_start()

def _finish_measurements(self):
if self.printer.is_shutdown():
return
self.query_cmd.send_wait_ack([self.oid, 0])
self.ffreader.note_end()
logging.info("CS1237 finished '%s' measurements", self.name)

def _process_batch(self, eventtime):
samples = self.ffreader.pull_samples()
self._convert_samples(samples)
return {
"data": samples,
"errors": self.last_error_count,
"overflows": self.ffreader.get_last_overflows(),
}


CS1237_SENSOR_TYPE = {"cs1237": CS1237}
3 changes: 2 additions & 1 deletion klippy/extras/load_cell_probe.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
# Copyright (C) 2025 Gareth Farrington <gareth@waves.ky>
#
# This file may be distributed under the terms of the GNU GPLv3 license.
from klippy.extras.load_cell import ads131m0x, ads1220, hx71x
from klippy.extras.load_cell import ads131m0x, ads1220, cs1237, hx71x
from klippy.extras.load_cell.load_cell_probe import LoadCellPrinterProbe
from klippy.extras.load_cell.tap_analysis import TapClassifierModule
from klippy.extras.load_cell.tap_quality_classifier import TapQualityClassifier
Expand All @@ -16,6 +16,7 @@ def register_components(subsystem: SubsystemComponentCollection):
hx71x.HX71X_SENSOR_TYPES
| ads1220.ADS1220_SENSOR_TYPE
| ads131m0x.ADS131M0X_SENSOR_TYPES
| cs1237.CS1237_SENSOR_TYPE
)
for name, sensor in sensors.items():
subsystem.register_component("load_cell_probe_sensors", name, sensor)
Expand Down
11 changes: 9 additions & 2 deletions src/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,10 @@ config WANT_ADS131M0X
bool
depends on WANT_SPI
default y
config WANT_CS1237
bool
depends on HAVE_GPIO
default y
config WANT_LDC1612
bool
depends on WANT_I2C
Expand All @@ -227,12 +231,12 @@ config WANT_GPIO_I2C
config NEED_SENSOR_BULK
bool
depends on WANT_ADXL345 || WANT_LIS2DW || WANT_MPU9250 || WANT_ICM20948 \
|| WANT_HX71X || WANT_ADS1220 || WANT_ADS131M0X \
|| WANT_HX71X || WANT_ADS1220 || WANT_ADS131M0X || WANT_CS1237 \
|| WANT_LDC1612 || WANT_SENSOR_ANGLE
default y
config NEED_LOAD_CELL_PROBE
bool
depends on WANT_HX71X || WANT_ADS1220 || WANT_ADS131M0X
depends on WANT_HX71X || WANT_ADS1220 || WANT_ADS131M0X || WANT_CS1237
default y
config NEED_SOS_FILTER
bool
Expand Down Expand Up @@ -317,6 +321,9 @@ config WANT_ADS1220
config WANT_ADS131M0X
bool "Support ADS131M02 and ADS131M04 ADC chips"
depends on WANT_SPI
config WANT_CS1237
bool "Support CS1237 ADC chips"
depends on HAVE_GPIO
config WANT_LDC1612
bool "Support ldc1612 eddy current sensor chip"
depends on WANT_I2C
Expand Down
1 change: 1 addition & 0 deletions src/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ src-$(CONFIG_WANT_ICM20948) += sensor_icm20948.c
src-$(CONFIG_WANT_HX71X) += sensor_hx71x.c
src-$(CONFIG_WANT_ADS1220) += sensor_ads1220.c
src-$(CONFIG_WANT_ADS131M0X) += sensor_ads131m0x.c
src-$(CONFIG_WANT_CS1237) += sensor_cs1237.c
src-$(CONFIG_WANT_SENSOR_ANGLE) += sensor_angle.c
src-$(CONFIG_NEED_SENSOR_BULK) += sensor_bulk.c
src-$(CONFIG_NEED_SOS_FILTER) += sos_filter.c
Expand Down
Loading
Loading