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
Check · Review · Land done · Live — Landed; nothing deploys from this branch, so this is the end of its path.
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
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'sCareLoginstead: the probes write ordinary sensor readings into the log book. - Pi side: the arm server reads the board through
i2csetandi2cget, with no native module. It servesGET /probesandPOST /probes/calibrate. Calibration is kept inprobes.jsonunder 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
waterTempreading 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
/carepage 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 thefake-serve.tslauncher 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.jsonis now monthly and covers both probes. I added it to the harvest stage too. I changed the cadence assertion incare.test.tsfrom 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
/probesroutes 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.
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.
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()incare-probes.tsis 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:0is now anh2.firstrule in the care page's stylesheet. The repository has noscripts/design-lint.tsordocs/design/areas/, so I couldn't run design-lint or add a docs entry. I left the existing inline styles incare-page.tsalone.
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 keptmain'saddReading(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 thepushhelper that only it used. The probe sampler (sampleProbes) now calls themainversion, 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.tsandserver.ts: both sides stay. Thesensors.jsonsensors 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 bothprobeEveryMs(5 minutes) andsensorsMs(10 minutes).
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/careanswers 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 asrunningwhen I checked, so I can't say whether the notification you got was for an earlier push. - Tests: the full
bun testpasses 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.
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.
The pH and EC probes now work end to end: calibrate them from
/careand log calibrated readings into the log book. I haven't tried it on a real ADS1115 board. The fullbun testoutput 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.main, so nothing plugs into them. I built on the care app'sCareLoginstead: the probes write ordinary sensor readings into the log book.i2csetandi2cget, with no native module. It servesGET /probesandPOST /probes/calibrate. Calibration is kept inprobes.jsonunder the data dir.- 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.
waterTempreading 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./carepage 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.probeEveryMs, set in thefake-serve.tslauncher the forge runs). A pH or EC value outside physical bounds is dropped instead of logged.spinach.jsonis now monthly and covers both probes. I added it to the harvest stage too. I changed the cadence assertion incare.test.tsfrom 168 to 720 hours because the test was out of date, not because the code was wrong./probesroutes are owner-only through the relay.Reviewing:
Software/so101/src/probes.tsandprobe-math.tsfor the calibration flow, andcare-probes.tsfor the sampler and panel.bun x tsc --noEmitreports no errors in the files I touched; the errors it does report are in files I didn't touch.Closes #606