README preview — how these render on GitHub (temp page):front pageble-listenersht30-nodeds18b20-nodesdi12-readermodbus-readeranalog-readercwsi-nodeloadcell-water

OAT SHT-30 Temperature & Humidity Node

Four wires and an accurate air sensor, reporting to a place you own.

Status Liveflash from the browser, no IDE needed
Interface I²C · SHT-30 (0x44 / 0x45 — one or two sensors per board)
Chips ESP32 (classic; bench-verified)
Delivers Webhook (HTTP POST) · MQTT — oat-ods/0.3
License Apache-2.0

A self-configuring ESP32 node that reads an SHT-30 air temperature + humidity sensor over I²C and pushes the readings as oat-ods to an endpoint the grower owns, by webhook or MQTT.

This is the first wired-sensor sketch built on the reference node's full core. Everything except the sensor read is lifted from oat-ble-listener v1.4.8: config + NVS, the shared field registry (one applyField/readField behind both the web form and the serial Console), WiFi/AP/captive portal with connect-then-confirm, mDNS, the push engine (https-preferred with signed-http fallback, HMAC signing, oat-ods batch, monotonic seq), the 60 s health heartbeat, and the two-way USB Console. Set up one OAT node and you have set up all of them.

A grower flashes it from the browser at openagriculturetechnology.com/build/sketches/sht30-node/ (ESP Web Tools / Web Serial), then configures it on the device's own page. No IDE, no command line, no code editing.

Wiring (classic ESP32)

SHT-30 ESP32
VIN / VCC 3V3 (not 5 V unless the breakout regulates)
GND GND
SDA GPIO 21
SCL GPIO 22
ADDR open = 0x44 · tied to 3V3 = 0x45 (a second sensor)

Why 21 and 22. GPIO 6–11 are the SPI flash the firmware runs from. GPIO 0/2/12/15 are strapping pins, and an I²C bus idles high through its pull-ups, which is exactly the pull that flips one into the wrong boot mode. GPIO 1/3 are UART0, the USB console. GPIO 34–39 are input-only with no pull-ups, so they can never carry a bidirectional bus. GPIO 16/17 are free on a plain devkit but are the PSRAM lines on WROVER modules and are already spent by the other sketches in this library (SDI-12 data, Modbus RX/TX, HX711). That leaves 21/22, which are also the ESP32 core's default Wire pins and what the two other I²C sketches here (Analog Reader, CWSI Node) default to. One wiring habit for the whole library. Both pins are configurable on the setup page for a board that differs.

Pull-ups. I²C needs a resistor pulling each line to 3.3 V. Nearly every SHT-30 breakout has them (typically 10 k); a bare chip does not — add 4.7 k–10 k.

What it reports

measurement unit fold over the push window
temperature Cel mean
humidity %RH mean

Dewpoint and VPD are not computed here. The node reports what it observed and the endpoint derives the rest, the same rule every OAT node follows.

source.physical_id carries the chip's own serial number (sht30:<serial>), read at boot — swappable provenance for "which gadget is filling this slot." A clone breakout that doesn't implement the serial command falls back to sht30:0x44.

The parts that matter

Files

File What it is
oat_sht30_node.ino The firmware source (Arduino / ESP32).
platformio.ini Build config. One env: classic ESP32. Pinned libs.
merge_bin.py Post-build hook → one merged factory image at out/firmware-esp32.bin, flashable at offset 0.
make_manifest.py Assembles manifest.json + manifest-esp32.json from whatever bins exist.
build.sh Builds, then copies bins + manifests + source downloads into the site assets.
docker-build.sh The same compile with no host toolchain (Docker + PlatformIO).

Build

pipx install platformio        # or: pip install --user platformio
./build.sh

No host toolchain?

./docker-build.sh              # compile in Docker
SKIP_BUILD=1 ./build.sh        # then the asset steps

build.sh produces the merged, flash-at-offset-0 image in out/, plus the manifest and download bundle the OAT site's flash-from-browser page serves.

One board on purpose

Every OAT sketch is split by sensor, never by chip, so adding the S3 / C3 / C6 later is an env in platformio.ini plus a default pin pair, not a second sketch. The classic ESP32 is what shipped first here because it is the board this was cut for and bench-run on. The pin guidance above is classic-ESP32 specific: the other chips number their pins differently and each needs its own verified default pair before it earns a button on the site.


Code: Apache-2.0 · Docs: CC BY 4.0 · An OAT sketch — an OpenCDC initiative.