Fan floors from the measurement; a hunt is measured, not judged

forgectrl pin 47e4256: the airflow floors set from the bench measurement
(exhaust 6400, intake 2290, air assist 6000 rpm, purge current 300, grace
15 s) and the operating-point rule: a fan is judged while the laser is
armed, when a job's profile may raise it but never lower it below the run
duty, or whenever it is commanded at the run duty; the service's hunts,
sent with the extraction fans off, are measured and published unjudged.

Catalog: cloud.mode-switch now waits out the connect-time hunt sampling
/cool/status and requires no AIRFLOW, the exhaust row unjudged and the
exhaust actually off; its covers gain forgectrl src/cool.* and
src/airflow.*. cooling.fan-gate-trips uses an 8 s test grace (the
intakes take 7 s to 90 percent and tripped under the old 2 s at the new
floor) and its off leg waits for the row state as well as gates_off.
fan_floor_measure.py names a reply that is not JSON and calls the purge
readings idle and run (the pump is always on).

Docs: COOLING 3a and the settings table, BRINGUP item 19 and a facts-bank
entry with the measured fan speeds, the bench README, and a CAMPAIGN-LOG
entry for the measurement, the two status-document finds, the hunt find
and the hot-deployed bench runs of both tests.
This commit is contained in:
ScottW514
2026-08-22 09:56:53 -04:00
parent 71a79b6675
commit adcd1adb9a
9 changed files with 165 additions and 32 deletions
+13 -8
View File
@@ -10,7 +10,9 @@ purge-air current (sysfs head/purge_air_current, raw) once a second:
reports the steady speed (the mean of the last --steady seconds),
the time to 90 % of it, and the spread (min, max, standard
deviation) over the steady window; for the purge fan the current
off and on. Needs the controller in GRBL mode, idle, fans quiet.
at idle and at run duty (the engine holds purge air on at all
times, so both are the "on" reading; a dead pump reads about 1).
Needs the controller in GRBL mode, idle, fans quiet.
cut No commands: samples for --seconds while the operator runs a
real cut, and reports the spread the same way. This is the
@@ -56,7 +58,10 @@ def fans_now():
def phase():
st, body = forgectrl_request("GET", "/cool/status")
return body.get("phase") if st == 200 and isinstance(body, dict) else None
if st != 200 or not isinstance(body, dict):
raise SystemExit("GET /cool/status: HTTP %s with a body that is not a JSON object: %r"
% (st, body if isinstance(body, str) else type(body).__name__))
return body.get("phase")
def sample(seconds, label):
@@ -95,7 +100,7 @@ def spinup_report(rows, steady_s):
t90 = next((r["t"] for r in rows if r[k] >= target), None) if target > 0 else None
st["t90_s"] = t90
out[k] = st
out["purge_on"] = stats([r["purge"] for r in steady])
out["purge_run"] = stats([r["purge"] for r in steady])
return out
@@ -151,8 +156,8 @@ def main(argv):
raise SystemExit("the engine is in phase %r; run this from idle" % ph)
idle = fans_now()
result["idle_fans"] = idle
result["purge_off"] = purge_current()
print("idle: fans %s purge %s" % (idle, result["purge_off"]))
result["purge_idle"] = purge_current()
print("idle: fans %s purge %s" % (idle, result["purge_idle"]))
g = Grbl()
try:
print("M8:", g.cmd("M8"))
@@ -173,9 +178,9 @@ def main(argv):
print("%-11s %8.0f %8d %8d %6.1f %7s" % (k, r.get("mean", 0), r.get("min", 0),
r.get("max", 0), r.get("sd", 0),
"%.0f s" % r["t90_s"] if r.get("t90_s") is not None else "-"))
po = rep["purge_on"]
print("purge current: off %s, on %.0f (min %s max %s)" % (result["purge_off"], po.get("mean", 0),
po.get("min"), po.get("max")))
po = rep["purge_run"]
print("purge current: idle %s, run %.0f (min %s max %s); the pump is always on, a dead one reads ~1"
% (result["purge_idle"], po.get("mean", 0), po.get("min"), po.get("max")))
print()
print("candidate floors at 55 %% of steady: " + " ".join(
"%s %.0f rpm" % (k, 0.55 * rep[k].get("mean", 0)) for k in FANS))