Public: anyone can read this; the crew can change it.

Migrated from github.com/Trevato/SO-ARM100

workbenchthis machine

Close the loop: sensors (pH, EC, temperatures, level, flow) and dosing pumps (nutrients, pH, top-off) with hard safety limits; suggest first, then auto; the crew can drive it via MCP #604

Parked opened this in answer to
Closed without merging

smith/arm:smith/586-close-the-loop-sensors-ph-ec-temperature → main 8 commits fork

  • Closed without merging. Nothing was applied to main.

Review

Sign in to review this change.
1 file changed +6 −0
Software/so101/src/relay.ts +6
@@ -169,6 +169,8 @@ export interface RelayOptions {
169169 stations?: StationHub;
170170 /** The plant care app's log book and plant profile (src/care-routes.ts); by default in memory, with the built-in spinach profile. */
171171 care?: { log: CareLog; profile: Profile; doser?: Doser; dosingFile?: string };
172 /** How often automation looks at the dosing loops; a minute by default. */
173 doseEveryMs?: number;
172174 /** How often the Pi's pH/EC probes are read into the care log, ms; unset, they are not sampled (the page still shows them and walks the calibration). */
173175 probeEveryMs?: number;
174176 /** The grow box's stills, log and how often a still is taken (src/grow-stills.ts); without it nothing is taken and the card shows no timelapse. */
@@ -341,6 +343,9 @@ export async function startRelay(options: RelayOptions): Promise<RunningRelay> {
341343 /** The Pi's dosing pumps (src/dose-pumps.ts), reached through the arm server like the grow light; set up below with armGrow. */
342344 const pumpHooks: PumpHooks = { read: () => pumpRead.read(), write: (body, timeoutMs) => pumpWrite(body, timeoutMs) };
343345 const doser = care.doser ?? makeDoser(care.log, armPumps(pumpHooks), care.dosingFile);
346 /** Automation: each loop in auto gets its dose when there is one to give, inside the controller's limits. */
347 const doseTimer = setInterval(() => void doser.tick().catch((err) => console.error(`dosing: ${(err as Error).message}`)), options.doseEveryMs ?? 60_000);
348 doseTimer.unref?.();
344349 const cameras = options.cameras ?? new CameraHub({ feed: { ...options.camera, metrics }, seed: remote ? [{ name: "Camera 1", source: remote.cameras[0]! }] : [] });
345350 const leaders = options.leaders ?? new LeaderCalibrations();
346351 const videos = new Map<number, VideoFeed>(Object.entries(options.video ?? {}).map(([id, sources]) => [Number(id), new VideoFeed(sources, {}, options.env ?? process.env)]));
@@ -1641,6 +1646,7 @@ export async function startRelay(options: RelayOptions): Promise<RunningRelay> {
16411646 clearInterval(sampleTimer);
16421647 clearInterval(stillTimer);
16431648 clearInterval(probeTimer);
1649 clearInterval(doseTimer);
16441650 clearInterval(sensorTimer);
16451651 readBacks?.();
16461652 arrived?.();

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs