Release v2.0.0 with dual-nail control and hardened safety

This commit is contained in:
2026-03-12 02:06:42 +00:00
parent cd07703f67
commit c4c86747e5
10 changed files with 2046 additions and 451 deletions
+48 -7
View File
@@ -10,6 +10,9 @@ Wraps the MAX6675 thermocouple reader with:
import logging
import collections
import math
import importlib
import types
log = logging.getLogger(__name__)
@@ -48,8 +51,29 @@ class Thermocouple:
self._total_reads = 0
try:
import MAX6675.MAX6675 as MAX6675
self._sensor = MAX6675.MAX6675(clk, cs, do)
sensor_ctor = None
module_candidates = [
("MAX6675.MAX6675", "MAX6675"),
("MAX6675.MAX6675.MAX6675", "MAX6675"),
("MAX6675.MAX6675", None),
("MAX6675.MAX6675.MAX6675", None),
]
for module_name, attr_name in module_candidates:
try:
module = importlib.import_module(module_name)
except Exception:
continue
obj = getattr(module, attr_name, None) if attr_name else module
if isinstance(obj, types.ModuleType):
obj = getattr(obj, "MAX6675", None)
if callable(obj):
sensor_ctor = obj
break
if not callable(sensor_ctor):
raise TypeError("MAX6675 constructor unavailable")
self._sensor = sensor_ctor(clk, cs, do)
self._is_connected = True
log.info("MAX6675 thermocouple initialized (CLK=%d, CS=%d, DO=%d)", clk, cs, do)
except Exception as e:
@@ -76,8 +100,20 @@ class Thermocouple:
self._is_connected = False
return self._last_good_temp
# Check for open thermocouple (MAX6675 returns very high values or specific error codes)
if raw_c is None or raw_c > 1023:
# Check for open thermocouple / invalid frame.
# MAX6675 open probe often reports NaN; guard all non-finite values.
if raw_c is None:
log.warning("Thermocouple appears disconnected (raw_c=%s)", raw_c)
self._is_connected = False
return self._last_good_temp
try:
raw_c = float(raw_c)
except (TypeError, ValueError):
log.warning("Thermocouple appears disconnected (raw_c=%s)", raw_c)
self._is_connected = False
return self._last_good_temp
if (not math.isfinite(raw_c)) or raw_c > 1023:
log.warning("Thermocouple appears disconnected (raw_c=%s)", raw_c)
self._is_connected = False
return self._last_good_temp
@@ -149,10 +185,15 @@ class Thermocouple:
@property
def stats(self):
"""Return diagnostic stats."""
def safe(value):
if isinstance(value, (int, float)) and math.isfinite(float(value)):
return round(float(value), 2)
return None
return {
"raw_temp": round(self._raw_temp, 2),
"filtered_temp": round(self._filtered_temp, 2),
"raw_temp": safe(self._raw_temp),
"filtered_temp": safe(self._filtered_temp),
"is_connected": self._is_connected,
"total_reads": self._total_reads,
"recent_readings": [round(r, 2) for r in self._readings],
"recent_readings": [safe(r) for r in self._readings],
}