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

Migrated from github.com/Trevato/SO-ARM100

Dosing: pH/EC probes via ADS1115 with guided calibration and monthly reminder #609

Merged opened this

Check · Review · Land done · Live — Landed; nothing deploys from this branch, so this is the end of its path.

Merged

The pH and EC probes now work end to end: calibrate them from `/care` and log calibrated readings into the log book. I haven't tried it on a real ADS1115 board. The full `bun test` output was lost when the command ran in the background. The 7 new probe tests and the 123 care, relay and server tests I ran earlier pass.

shipped merged

opened this

The pH and EC probes now work end to end: calibrate them from /care and log calibrated readings into the log book. I haven't tried it on a real ADS1115 board. The full bun test output was lost when the command ran in the background. The 7 new probe tests and the 123 care, relay and server tests I ran earlier pass.

  • Controller missing: the #586 controller, page card, terminal and MCP doors aren't on main, so nothing plugs into them. I built on the care app's CareLog instead: the probes write ordinary sensor readings into the log book.
  • Pi side: the arm server reads the board through i2cset and i2cget, with no native module. It serves GET /probes and POST /probes/calibrate. Calibration is kept in probes.json under the data dir.
  • Calibration steps:

- Order: pH 7.0, then pH 4.0, then the 1413 µS/cm solution. - Settling check: a step is refused while the probe is still settling. - Uncalibrated probes: an uncalibrated probe returns no value rather than a guess.

  • Compensation: pH is scaled for temperature (Nernst) and EC is brought to 25 °C at 2% per °C. The temperature comes from the latest waterTemp reading in the log book from the last two hours, and falls back to 25 °C when there isn't one. The DS18B20 driver (#605) isn't here yet, so for now that reading is whatever was logged by hand.
  • Care page: the /care page has a probe panel with the readings, the calibration dates and the guided steps. Only the owner gets the Capture buttons (POST /care/probes/calibrate). Finishing both probes logs the "calibrate" reminder as done.
  • Sampling: a calibrated reading goes into the log book every 5 minutes (probeEveryMs, set in the fake-serve.ts launcher the forge runs). A pH or EC value outside physical bounds is dropped instead of logged.
  • Reminder: the existing weekly "Calibrate the pH probe" reminder in spinach.json is now monthly and covers both probes. I added it to the harvest stage too. I changed the cadence assertion in care.test.ts from 168 to 720 hours because the test was out of date, not because the code was wrong.
  • Not done: the terminal and MCP doors don't expose the probes, and the /probes routes are owner-only through the relay.
  • Dependencies and docs: no new dependencies. The README has a short section on wiring and calibrating the probes.

Reviewing: Software/so101/src/probes.ts and probe-math.ts for the calibration flow, and care-probes.ts for the sampler and panel. bun x tsc --noEmit reports no errors in the files I touched; the errors it does report are in files I didn't touch.

Closes #606

commented

The pH and EC probes now work end to end: calibrate them from /care and log calibrated readings into the log book. I haven't tried it on a real ADS1115 board. The full bun test output was lost when the command ran in the background. The 7 new probe tests and the 123 care, relay and server tests I ran earlier pass.

  • Controller missing: the #586 controller, page card, terminal and MCP doors aren't on main, so nothing plugs into them. I built on the care app's CareLog instead: the probes write ordinary sensor readings into the log book.
  • Pi side: the arm server reads the board through i2cset and i2cget, with no native module. It serves GET /probes and POST /probes/calibrate. Calibration is kept in probes.json under the data dir.
  • Calibration steps:

- Order: pH 7.0, then pH 4.0, then the 1413 µS/cm solution. - Settling check: a step is refused while the probe is still settling. - Uncalibrated probes: an uncalibrated probe returns no value rather than a guess.

  • Compensation: pH is scaled for temperature (Nernst) and EC is brought to 25 °C at 2% per °C. The temperature comes from the latest waterTemp reading in the log book from the last two hours, and falls back to 25 °C when there isn't one. The DS18B20 driver (#605) isn't here yet, so for now that reading is whatever was logged by hand.
  • Care page: the /care page has a probe panel with the readings, the calibration dates and the guided steps. Only the owner gets the Capture buttons (POST /care/probes/calibrate). Finishing both probes logs the "calibrate" reminder as done.
  • Sampling: a calibrated reading goes into the log book every 5 minutes (probeEveryMs, set in the fake-serve.ts launcher the forge runs). A pH or EC value outside physical bounds is dropped instead of logged.
  • Reminder: the existing weekly "Calibrate the pH probe" reminder in spinach.json is now monthly and covers both probes. I added it to the harvest stage too. I changed the cadence assertion in care.test.ts from 168 to 720 hours because the test was out of date, not because the code was wrong.
  • Not done: the terminal and MCP doors don't expose the probes, and the /probes routes are owner-only through the relay.
  • Dependencies and docs: no new dependencies. The README has a short section on wiring and calibrating the probes.

Reviewing: Software/so101/src/probes.ts and probe-math.ts for the calibration flow, and care-probes.ts for the sampler and panel. bun x tsc --noEmit reports no errors in the files I touched; the errors it does report are in files I didn't touch.

Changes requested — before the latest push

The probe work is sound in the diff: no value until calibrated, a settling check, an owner-only calibrate that is tested, no new dependency. Two things block it.

First, fix this: the terminal door is missing. Everything the page does for probes (read the values, walk the calibration, capture a step) has no counterpart in src/tty.ts, and the README does not say so. The list requires both doors. Add /probes (readings, calibration dates, next step) for anyone, and /probes calibrate <ph7|ph4|ec1413> [tempC] for the owner, going through the same ProbeHooks and the same "calibrate" done entry as the page. Test it. A calibration made in the terminal must reach web viewers live (the care event stream), and the README must show the command. Putting the "done" logic in one shared function would keep the two doors from drifting.

Second, the page waits on the Pi. GET /care now awaits a fetch to the Pi with a timeout of up to 1.5 s before it renders. When the Pi is down or slow, the first paint of the care page is 1.5 s late, and every page view makes the Pi take two I2C reads. Server-render the page from the last cached view, with a short TTL of a few seconds. Refresh the cache in the background and from the sampler. Show the cached view with its age.

Smaller points are in the line comments.

commented

The review points are addressed and pushed. The care, terminal and probe tests pass (care-terminal.test.ts 4/4). My first push had a failing test, which the second commit fixed.

  • Terminal door:

- /probes prints the readings, calibration dates, the age of the reading and the next step, for anyone. - /probes calibrate <ph7|ph4|ec1413> [tempC] is owner-only. It makes the same request as the page's button, and the server refuses non-owners. - The README shows both commands, and /help lists them.

  • Shared done logic: calibrateProbe() in care-probes.ts is now the only calibration path. Both the page and the terminal call it. It adds the "calibrate" done entry once both probes are calibrated, then notifies the care event stream, so even an unfinished step reaches web viewers live.
  • Recalibration: each finished recalibration adds another done entry, and an unfinished step adds none. A test now covers this.
  • Cached view:

- GET /care no longer waits on the Pi. It renders from a ProbeCache and starts a background refresh if the view is more than 5 seconds old. - The panel shows "Read N s ago". Before the first answer arrives, the page paints without the panel. - Concurrent refreshes share one Pi request, and the sampler refreshes the cache too. - Viewers are only woken when something other than the numbers changes, such as the next step or a calibration date. - The terminal's GET /care/probes is the one place that waits, and only when nothing is cached yet. A test runs the relay against a dead Pi and checks the page answers fast.

  • Inline style: the panel's inline margin-top:0 is now an h2.first rule in the care page's stylesheet. The repository has no scripts/design-lint.ts or docs/design/areas/, so I couldn't run design-lint or add a docs entry. I left the existing inline styles in care-page.ts alone.
commented

I merged main into the branch, resolved the four conflicts and pushed (c917160). bun test ran 1250 tests with 1 failure, a STOP-through-the-relay test that has nothing to do with this pull. It passes when run on its own (8/8), so it looks like a timing flake under full-suite load.

  • care-log.ts: I kept main's addReading(Reading) from #605, which keeps one sensor reading an hour per kind and makes room by dropping the oldest sensor reading when the book is full. My earlier version is gone, and so is the push helper that only it used. The probe sampler (sampleProbes) now calls the main version, so a probe reading is logged at most once an hour per kind.
  • probes.test.ts: I updated the sampler test for that hourly limit. It now checks that a second sample a minute later writes nothing, and that one two hours later does.
  • relay.ts and server.ts: both sides stay. The sensors.json sensors and my pH/EC probes run side by side, each with its own timer, option and route, and the relay clears both timers on close.
  • bench/fake-serve.ts: it now sets both probeEveryMs (5 minutes) and sensorsMs (10 minutes).
commented

I found nothing to fix, so I didn't push anything. The preview for the current head (c917160) came up, and it was not failed when I checked.

  • Preview: https://arm--609--forge-trevato-apps.open-platform.sh/care answers 200 and shows the probe panel. It reads "No probe board answering", which is correct since the preview has no ADS1115 attached. The forge's check for that push was still shown as running when I checked, so I can't say whether the notification you got was for an earlier push.
  • Tests: the full bun test passes locally (1250 pass, 0 fail) on the merged branch. The one failure in my last full run was the STOP-through-the-relay test, which is unrelated to this pull and passed when run alone.
  • Startup: I started the app the way the deploy does, bun run bench/fake-serve.ts. It came up and served / and /care.
  • Branch: it already contains the latest main, including #608.

I couldn't see the failed deploy's log; the API routes I tried for it don't exist. If it fails again, send me the log and I'll fix what it names.

Approved

Works in the preview: /care paints the probe panel from the cache without waiting on the Pi, with no probe board it says so in words ("No probe board answering: no ADS1115 at 0x48…") and still offers hand logging, /care/probes answers 200, and an unauthenticated POST /care/probes/calibrate is 401. The terminal door (/probes, /probes calibrate) goes through the same calibrateProbe path as the page and the care event stream, and the tests cover both. Only the first 60 KB of the diff was visible to me, so server.ts and terminal.ts were not read line by line. Next, in order: (1) restore await relay?.close() in the care-terminal.test.ts afterEach, because the relay and its probe timer now leak across tests; (2) give the Capture form a larger tap target and a visible focus state on a 360px phone, and add aria-labelledby to the absent-board section as the present one has.

Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs