From thermistors to OLED dashboards.

Wire it. Register it. Read it. Three commands, any sensor - all over NATS.

Every sensor, same pattern

Doesn't matter if it's a $0.50 thermistor or a $6 BME280. The workflow is always the same.

1. Wire it

4 wires for I2C (3.3V, GND, SDA, SCL). 3 wires for analog (3.3V, GND, signal). Same pins every time.

2. Register it

ionode device add ionode-01 temp i2c_bme280 --channel 0 --unit C --i2c-addr 118

3. Read it

ionode read ionode-01 temp → 23.5 C

what you get for free
✓ Readable via CLI, NATS, web UI, and dashboard
✓ EMA-smoothed for stable readings
✓ History recorded every 5 minutes (sparkline)
✓ Threshold events with edge detection
✓ Persisted across reboots
✓ Discoverable by other tools (OpenClaw, scripts, etc.)

All automatic. Zero config.

I2C Sensors

Raw Wire.h drivers. No external libraries. Under 18KB flash total. Five sensor families built in.

i2c_bme280

BME280

The king of environmental sensors. Temperature, humidity, and barometric pressure from one chip.

ch0: temp ch1: humidity ch2: pressure
i2c_bh1750

BH1750

Calibrated ambient light in lux. One channel, one address, dead simple.

lux
i2c_sht31

SHT31

High-accuracy temperature and humidity. Slightly more precise than BME280 for humidity.

ch0: temp ch1: humidity
i2c_ads1115

ADS1115

16-bit ADC with 4 channels. When the ESP32's 12-bit ADC isn't enough. Soil moisture, gas sensors, anything analog.

ch0–3: mV
i2c_generic

Any I2C Sensor

The escape hatch. Read any register, set address, length, and scale. Hundreds of sensors - no firmware update needed.

configurable
ntc_10k · ldr

Analog Sensors

NTC thermistors ($0.50, Steinhart-Hart, EMA-smoothed) and LDRs (light level as 0–100%). One resistor, two wires.

°C %
ionode i2c scan
$ ionode i2c ionode-01 scan

━━ I2C Scan  ·  ionode-01

  ADDR    HEX      COMMON DEVICE
────────────────────────────────────────
  35      0x23     BH1750
  60      0x3C     SSD1306 OLED
  118     0x76     BME280

  3 device(s) found

Plug in a sensor. Scan the bus. IOnode tells you what's there.

SSD1306 / SH1106 OLED Display

A live dashboard on a $3 screen. Two controllers, same template engine with auto-refresh. No code.

ionode-01 192.168.1.42

Temp: 23.5 C
Humi: 45.2 %
Pres: 1013.2 hPa
Light: 342 lux

Up:4h 37m 189KB

128×64 pixels · 8 lines × 21 chars · refreshes every 5 seconds

template engine
# Register display with a live template
$ ionode device add ionode-01 screen ssd1306 0 --i2c-addr 60 \
    --template "{name} {ip}\nT:{bme_temp}C H:{bme_humi}%\nUp:{uptime} {heap}KB"

Tokens auto-resolve to live sensor values:
  {bme_temp}  → current reading of the bme_temp sensor
  {ip}        → node IP address
  {uptime}    → time since boot
  {heap}      → free memory in KB
  {name}      → device name

# Or send raw text anytime
$ ionode write ionode-01 screen "!Hello World"
  ✓ screen ← Hello World

Same four wires

Every I2C device connects the same way. Multiple sensors share one bus.

Single sensor
ESP32           BME280
─────           ──────
3.3V ────────── VCC
GND  ────────── GND
SDA  ────────── SDA
SCL  ────────── SCL
Shared bus (sensor + display)
ESP32       BME280    SSD1306/SH1106
─────       ──────    ──────────────
3.3V ────── VCC ───── VCC
GND  ────── GND ───── GND
SDA  ────── SDA ───── SDA
SCL  ────── SCL ───── SCL
ESP32-C6SDA: GPIO 6 · SCL: GPIO 7
ESP32-S3SDA: GPIO 8 · SCL: GPIO 9
ESP32-C3SDA: GPIO 4 · SCL: GPIO 6
Classic ESP32SDA: GPIO 21 · SCL: GPIO 22

Fixed per chip. Auto-detected. No configuration.

Weather station in 4 commands

BME280 + OLED display on the same I2C bus. Complete setup.

from zero to weather station
# Register the BME280 channels
$ ionode device add ionode-01 bme_temp i2c_bme280 --channel 0 --unit C --i2c-addr 118
  +  bme_temp  i2c_bme280  I2C 0x76 ch0

$ ionode device add ionode-01 bme_humi i2c_bme280 --channel 1 --unit % --i2c-addr 118
  +  bme_humi  i2c_bme280  I2C 0x76 ch1

$ ionode device add ionode-01 bme_pres i2c_bme280 --channel 2 --unit hPa --i2c-addr 118
  +  bme_pres  i2c_bme280  I2C 0x76 ch2

# Register the OLED with a live template
$ ionode device add ionode-01 display ssd1306 0 --i2c-addr 60 \
    --template "T:{bme_temp}C H:{bme_humi}%\nP:{bme_pres}hPa\n\n{name} {ip}\nUp:{uptime}"
  +  display  ssd1306  I2C 0x3C

Done. Live readings on the OLED. Auto-refreshes. Survives reboots.

Raw I2C access

Debug the bus remotely. No serial cable needed.

ionode i2c scanFind every device on the bus
ionode i2c detect <addr>Check if a specific address responds
ionode i2c read <addr>Read register bytes
ionode i2c write <addr>Write register bytes
ionode i2c recoverBus stuck? 9× SCL toggle to unstick it

Every I2C operation is also a NATS subject: {name}.hal.i2c.scan, {name}.hal.i2c.{addr}.read, etc.

All 21 device types

Sensors

digital_in

Digital input → 0 or 1

analog_in

ADC → 0–4095 (12-bit)

ntc_10k

Thermistor → °C (Steinhart-Hart)

ldr

Photoresistor → 0–100%

internal_temp

Chip temperature (no hardware)

clock_hour

Current hour 0–23 (NTP)

clock_minute

Current minute 0–59

clock_hhmm

Combined HHMM (1430 = 2:30 PM)

nats_value

Subscribes to a NATS subject

serial_text

UART line → numeric value

i2c_bme280

Temp / humidity / pressure

i2c_bh1750

Ambient light (lux)

i2c_sht31

Temp / humidity (high accuracy)

i2c_ads1115

16-bit ADC (4 channels)

i2c_generic

Any I2C register + scale

Actuators

digital_out

High or low

relay

With inversion

pwm

0–255 duty

rgb_led

0xRRGGBB

ssd1306

OLED text display

sh1106

OLED text display

Ready to scale up?

One node is fun. A fleet is powerful.