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 temp23.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.