Files
soilMoistureSystem/docs/CALIBRATION.md
T
mattandClaude ad9c457ea7 Initial firmware for ESP32-S3 six-channel soil moisture node
Implements ESP32_Soil_Moisture_Node_PRD.md on the VCC-GND Studio
YD-ESP32-S3 (ESP32-S3-WROOM-1) using Arduino via PlatformIO, publishing
to Home Assistant over MQTT with auto-discovery.

Two power profiles behind a compile-time switch (PRD 4):
  -e mains    always on, publishes every 60s
  -e battery  RTC wake, sample, publish, deep sleep 15min

PRD mitigations:
  - All six probes on ADC1 (GPIO4-9); ADC2 is unusable while Wi-Fi is up.
  - analogReadMilliVolts() for eFuse ADC calibration rather than raw counts.
  - 16 samples per probe, sorted, 10% trimmed from each end, averaged.
  - Per-probe two-point calibration; HW-390 output is inverted (dry reads
    high), and an inverted config is rejected rather than silently producing
    a backwards curve.
  - MAC-derived node ID so identical binaries never collide on topics.

Design decisions that diverge from a literal reading of the PRD:
  - Out-of-range probes publish null (HA "unknown") instead of clamping to
    0%/100%. A cut Cat5 pair reads ~0mV, which clamps to a believable "very
    dry" and would drive irrigation on wrong data.
  - The battery profile uses expire_after rather than an availability topic;
    a sleeping node is offline most of the time and would sit greyed out.
  - AWAKE_BUDGET_MS forces deep sleep if a wake cycle overruns, covering
    failure modes the per-step network timeouts miss (DHCP never issued,
    CONNACK never sent) that would otherwise flatten the cell.
  - docs/WIRING.md notes the 18650 cannot feed the probes directly: the
    XC6206 LDO needs ~3.4V headroom, so a boost converter is required on the
    switched side of the MOSFET.

Both profiles compile clean under -Wall. Not yet run on hardware; pin map,
MOSFET circuit and calibration defaults are unverified against a board.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-14 13:38:41 -07:00

80 lines
3.3 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Two-point calibration
Implements PRD §3.3. Do this **once per probe**, after sealing them (§2.3) and
with the probe connected through its final Cat5 run — the cable and the RC
filter both shift the numbers, so calibrating on the bench with short leads
produces values that are wrong in the field.
## Why per-probe
`CAL_AIR_MV` / `CAL_WATER_MV` in `include/config.h` are arrays, not scalars.
HW-390s vary: each has its own XC6206 LDO, its own coating thickness, and its
own cable run. Using one shared pair of endpoints for six probes typically
costs 5–15% of absolute accuracy, which is enough to trigger irrigation on a
bed that is already wet.
The defaults shipped in `config.h` are placeholders. They compile and produce
plausible-looking output, which makes them dangerous — plausible output is
exactly what stops you noticing you skipped this step.
## Procedure
1. Flash the mains build and let the node come up:
```
pio run -e mains -t upload -t monitor
```
2. In Home Assistant, find the **Raw mV 1–6** diagnostic entities on the node's
device page. (These exist for this procedure; set `PUBLISH_RAW_MV 0` in
`config.h` to hide them once you are done.) You can also read them straight
off the serial monitor in the `state:` line.
3. **Air point (0%).** Probe clean, dry, in open air. Wait ~30 s for the value
to settle. Record `raw_mv_N` → this is `CAL_AIR_MV[N-1]`.
4. **Water point (100%).** Submerge the probe in a glass of water **up to but
not past the marked line** on the PCB. Do not submerge the electronics.
Wait ~30 s. Record `raw_mv_N` → this is `CAL_WATER_MV[N-1]`.
5. Paste both arrays into `include/config.h` and reflash.
## Expected values
A healthy 3.3 V HW-390 v2.0 reads roughly:
| Condition | Typical |
|-----------|---------|
| Air (dry) | 2400–2800 mV |
| Water | 1000–1400 mV |
**Air must read higher than water** — the HW-390's output is inverted. The
firmware rejects `CAL_AIR_MV <= CAL_WATER_MV` at runtime and logs an error
rather than silently producing an inverted percentage, because a
moisture curve running backwards is very easy to miss on a dashboard and would
tell your irrigation to water dry soil less.
## Troubleshooting
**Air-to-water spread under ~800 mV.** Usually a probe that is degrading, or
one whose coating crept onto the prongs. A narrow span makes the percentage
noisy no matter how much you oversample.
**Reads ~0 mV.** No power reaching the probe: check the Cat5 5 V pair, the
MOSFET gate, and the splice. The firmware reports this as *unknown* rather than
0%, which is the giveaway.
**Reads ~3300 mV (rail).** Signal line shorted to the 5 V pair, or the probe's
output is disconnected and the ADC pin is floating up.
**Reading drifts down over weeks with no rain.** Moisture ingress into an
imperfectly sealed board. Re-seal per `WIRING.md` §6 and recalibrate.
## Fault margin
`SENSOR_FAULT_MARGIN_MV` (default 400 mV) sets how far outside the calibrated
window a reading may sit before it is reported as unavailable instead of being
clamped. Soil genuinely can read slightly outside the air/water endpoints —
saturated clay can go below the water point — so the margin exists to avoid
false faults. Widen it if healthy probes flap to *unknown*; narrow it if real
failures are being clamped into believable numbers.