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

OAT Analog & 4–20 mA Reader

Bring analog and current-loop sensors — the huge install base with no digital bus — into data you own.

Status Source release — build & flash it yourself
Interface ADS1115 16-bit ADC (I²C) — reads 0–2.5 V / 0–5 V and 4–20 mA
Delivers Webhook (HTTP POST) · MQTT — oat-ods/0.3
Site page openagriculturetechnology.com/build/sketches/analog-reader/
License Apache-2.0

A world of good sensors speak only analog: a voltage, or a 4–20 mA current loop. This node reads them cleanly through an ADS1115 (a 16-bit I²C ADC), applies a per-channel linear calibration, and emits each as oat-ods.

value = m × volts + b

That one linear map covers amplified 0–2.5 V / 0–5 V outputs and 4–20 mA (with a sense resistor turning current into a voltage) — you just pick m and b to match the sensor's range. The bridge for the huge install base of analog and current-loop sensors that have no digital bus.

The channel map

One line per ADS1115 channel on the setup page:

<ch> <stream_id> <measure>:<unit>:<m>:<b>

Examples:

0 canopy-par par:umol/m2/s:800:0      # Apogee SQ-512: 0-2.5V = 0-2000 umol -> m=800, b=0
1 tank       level:%:41.667:-25       # 4-20mA via 150 ohm: 0.6-3.0V -> 0-100%

Each channel → one oat-ods message: stream = stream_id, physical_id = ads1115:<ch>.

Finding m and b

Two points (volts, value) define the line:

m = (value2 − value1) / (volts2 − volts1)
b = value1 − m × volts1

The setup page's Live volts table shows the raw voltage on each channel — read it at two known sensor states and compute m/b.

Wiring

ADS1115 is I²C (3.3 V): SDA, SCL, 3V3, GND (default addr 0x48). Sensor output to A0–A3.

Set the gain on the setup page to the smallest full-scale that still covers your voltage — smaller full-scale = finer resolution.

Setup

Flash (see Build), join OAT-Analog-xxxx, open the setup page (admin / oatsetup), set Wi-Fi + delivery + the I²C pins/gain + the channel map. Point delivery at the test endpoint to confirm.

Build & flash

pipx install platformio          # or: pip install --user platformio
cd oat-analog-reader
./build.sh

This is a source release: it compiles against the pinned deps but hasn't earned the bench-verified badge the live sketches carry — build it, flash it, and prove it on your bench before you trust it in the field. Deps (pinned in platformio.ini): Adafruit ADS1X15, PubSubClient, ArduinoJson, and the local oat_ods module.

Notes (v1)

FAQ

How do I read a 4–20 mA sensor with an ESP32? Put a precise sense resistor (commonly 150 Ω) across the current loop so the 4–20 mA becomes roughly 0.6–3.0 V, then read that voltage with a 16-bit ADS1115 ADC over I²C. A linear calibration (value = m × volts + b) maps the voltage back to the sensor's engineering units.

Can an ESP32 read 0–5 V analog sensors accurately? The ESP32's built-in ADC is noisy and tops out around 3.3 V. An external ADS1115 gives clean 16-bit readings, and for 0–5 V you scale into range with a resistor divider or use amplified 0–2.5 V outputs. A two-point calibration then converts volts to the measurement.

Shared core: this sketch carries its own copy of the config / Wi-Fi / delivery scaffolding; as the shared oat-node-core library is adopted, only the acquisition layer stays sketch-specific.


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