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>
This commit is contained in:
commit
ad9c457ea7
24 files changed
+2178
No files matched your search
+135
@@ -0,0 +1,135 @@
|
||||
# Wiring & Breakout
|
||||
|
||||
Implements PRD §2 (cabling) and §3.1–3.2 (signal integrity, ground bounce).
|
||||
|
||||

|
||||
|
||||
## 1. Cat5 pinout
|
||||
|
||||
Standard T568B colours. Power and ground share one twisted pair so the
|
||||
supply loop area stays small (PRD §2.2).
|
||||
|
||||
| RJ45 pin | Colour | Function | MCU side |
|
||||
|----------|--------------|---------------------|----------------|
|
||||
| 1 | Orange/White | **GND** | GND |
|
||||
| 2 | Orange | **+5V** | Switched 5V rail |
|
||||
| 3 | Green/White | Analog signal 1 | GPIO4 (ADC1_CH3) |
|
||||
| 6 | Green | Analog signal 2 | GPIO5 (ADC1_CH4) |
|
||||
| 4 | Blue | Analog signal 3 | GPIO6 (ADC1_CH5) |
|
||||
| 5 | Blue/White | Analog signal 4 | GPIO7 (ADC1_CH6) |
|
||||
| 7 | Brown/White | Analog signal 5 | GPIO8 (ADC1_CH7) |
|
||||
| 8 | Brown | Analog signal 6 | GPIO9 (ADC1_CH8) |
|
||||
|
||||
**Known tradeoff:** six signals over three pairs means each pair carries two
|
||||
*signals* rather than a signal and its return, which is the worst case for
|
||||
pair-to-pair crosstalk. It is acceptable here only because these are slow DC
|
||||
levels and the RC filters below roll off everything above a few hertz. If you
|
||||
ever repurpose this harness for anything with edges in it, re-plan the pairing.
|
||||
|
||||
## 2. RC low-pass filter — mandatory (PRD §3.1)
|
||||
|
||||

|
||||
|
||||
One per analog line, on the RJ45 breakout board, **between the jack and the
|
||||
ESP32 pin**:
|
||||
|
||||
```
|
||||
RJ45 signal ──[ 10 kΩ ]──┬── GPIO (ADC1)
|
||||
│
|
||||
[ 0.1 µF ] ceramic, X7R
|
||||
│
|
||||
GND
|
||||
```
|
||||
|
||||
Placement is the whole point: the capacitor must sit at the *ESP32 end* of the
|
||||
resistor, physically close to the pin. Put it at the jack end and you have
|
||||
filtered the cable but left the ADC pin exposed to everything picked up on the
|
||||
last few centimetres of board trace.
|
||||
|
||||
- 10 kΩ + 0.1 µF → τ = 1 ms. Fine for soil, which moves over minutes.
|
||||
- 10 kΩ + 1 µF → τ = 10 ms. Quieter; still far faster than the sample burst.
|
||||
|
||||
The series resistor also raises source impedance above what the SAR ADC would
|
||||
like. The capacitor is what fixes that — it supplies the sampling charge — so
|
||||
do not fit the resistor without it.
|
||||
|
||||
## 3. Ground integrity (PRD §3.2)
|
||||
|
||||
- Every splice at the sensor end: soldered + heat-shrink, or a high-retention
|
||||
Wago. Twist-and-tape connections corrode and drift the ground reference.
|
||||
- Budget ~5 mA per HW-390, so ~30 mA total. One 24 AWG conductor carries this
|
||||
with negligible drop over a normal garden run.
|
||||
- Run **one** GND back to a single point at the MCU board. Do not daisy-chain
|
||||
sensor grounds through each other — that is exactly the shared-return path
|
||||
that makes one probe's current show up as another probe's reading.
|
||||
|
||||
## 4. Sensor power switch (battery profile)
|
||||
|
||||

|
||||
|
||||
`SENSOR_POWER_GATE_PIN` (GPIO14) is `HIGH` for *sensors on*. A 3.3 V GPIO
|
||||
cannot drive a P-channel MOSFET's gate on a 5 V rail hard enough to fully turn
|
||||
it off, so an inverting stage is required:
|
||||
|
||||
```
|
||||
+5V ──────────┬──────────────┬─── source
|
||||
│ │
|
||||
[100kΩ] [ P-ch MOSFET ] e.g. AO3401
|
||||
│ │
|
||||
GPIO14 ──[10kΩ]──┤ gate └─── drain ──→ Cat5 pin 2 (+5V to sensors)
|
||||
│
|
||||
[ 2N7002 N-ch ] drain ──┘
|
||||
│
|
||||
GND
|
||||
```
|
||||
|
||||
- GPIO14 HIGH → 2N7002 conducts → P-FET gate pulled low → rail **on**.
|
||||
- GPIO14 LOW **or floating** → 100 kΩ pulls the gate to 5 V → rail **off**.
|
||||
|
||||
The pull-up is what makes deep sleep safe: pins float during sleep, and this
|
||||
guarantees a floating pin means *off* rather than *undefined*.
|
||||
|
||||
## 5. Battery & solar (PRD §4.2)
|
||||
|
||||

|
||||
|
||||
**The 18650 cannot feed the sensors directly.** A cell runs 3.0–4.2 V, and the
|
||||
HW-390's XC6206 LDO needs roughly 3.4 V of headroom to hold a clean 3.3 V. Wire
|
||||
the cell straight to the Cat5 5 V pair and the probes brown out and drift as
|
||||
the cell discharges — readings that look like the soil drying out. Put a
|
||||
**boost converter to 5 V on the switched side** of the MOSFET.
|
||||
|
||||
```
|
||||
Solar panel (6V) ──→ TP4056 (with DW01 protection) ──→ 18650
|
||||
│
|
||||
├──→ ESP32-S3 5V/VIN
|
||||
│
|
||||
└──→ [P-FET] ──→ boost 5V ──→ Cat5 pin 2
|
||||
```
|
||||
|
||||
Battery sense divider into GPIO10:
|
||||
|
||||
```
|
||||
VBAT ──[ R1 1MΩ ]──┬── GPIO10 (ADC1_CH9)
|
||||
│
|
||||
[ R2 1MΩ ] ┬── [1µF]
|
||||
│ │
|
||||
GND GND
|
||||
```
|
||||
|
||||
1 MΩ/1 MΩ draws ~2 µA, which is the right order next to the ESP32's ~10 µA
|
||||
deep-sleep current. The obvious 100 kΩ/100 kΩ pair draws ~21 µA — more than the
|
||||
sleeping MCU, and enough to dominate your winter power budget. The 1 µF cap is
|
||||
not optional at this impedance: it supplies the ADC's sampling charge.
|
||||
|
||||
`BATTERY_DIVIDER_RATIO = 2.0` in `include/config.h` assumes R1 == R2. Measure
|
||||
the actual cell voltage with a meter and adjust the ratio to trim out resistor
|
||||
tolerance.
|
||||
|
||||
## 6. Sensor sealing (PRD §2.3)
|
||||
|
||||
Every HW-390's upper PCB — components, exposed board edges, and the splice —
|
||||
sealed with marine epoxy or conformal coating. **Only the capacitive prongs
|
||||
stay bare.** This is the single most common reason these probes die in a
|
||||
season; an unsealed board wicks moisture up under the silkscreen and the
|
||||
readings drift long before anything looks visibly corroded.
|
||||
Reference in new issue
Block a user