Release v2.0.0 with dual-nail control and hardened safety
This commit is contained in:
+48
-7
@@ -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],
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user