Send sensor data from Python, MicroPython, Node.js and Go

Send signed sensor data from a Raspberry Pi, a Pico W with MicroPython, Node.js, Go or a shell script. One file, no dependencies, commands and offline buffer.

HydroNode 7 min read
The HydroNode SDK guide with the first reading in Python, JavaScript, Go and shellThe HydroNode SDK guide with the first reading in Python, JavaScript, Go and shell

Not every sensor hangs off an ESP32. A Raspberry Pi in the greenhouse, a Pico W with MicroPython, a gateway that forwards values from another source, a cron job on a server. So they can send sensor data too, there is now the HydroNode SDK for Python, MicroPython, Node.js and Go: one file per language, no dependencies, with the same signature as the Arduino library.

One file per language

You haveYou copyNeeds
Raspberry Pi, Linux, macOS, Windowspython/hydronode.pyPython 3.8+
ESP32, ESP8266, Pico W with MicroPythonpython/hydronode.py and hydronode_roots.pyMicroPython 1.20+
Node.js, Deno, Bunnode/hydronode.mjsNode 18+
Gogithub.com/TexhFexLabs/hydronode-sdk/goGo 1.21+
A shellshell/hydronode.shbash, curl, openssl

Everything is on GitHub. You get the Sensor ID and secret as usual under SettingsConnection of the sensor.

The first reading

send.pyPython
from hydronode import HydroNode

hydro = HydroNode("YOUR_SENSOR_ID", "YOUR_SECRET")
hydro.send_values({"TEMPERATURE": 21.5, "HUMIDITY": 48})

202 means accepted. Then open the sensor page and press `: the console shows the request, the signature check and how far your computer's clock is off.

  1. Your codePython, Node, Go, shell
  2. SignedHMAC-SHA256 over the values
  3. 202Stored and charted
  4. CommandsArrive with the answer
The same signature as on the ESP32. HydroNode cannot tell the difference.

MicroPython on ESP32 and Pico W

The same file runs under MicroPython. It computes the HMAC itself, because MicroPython has no hmac module, and formats values exactly, even though MicroPython only has 32-bit numbers on these boards. With ca=ROOTS it checks the server certificate too.

main.pyMicroPython
import network, ntptime, time
from hydronode import HydroNode
from hydronode_roots import ROOTS

wlan = network.WLAN(network.STA_IF)
wlan.active(True)
wlan.connect("YOUR_WIFI", "YOUR_PASSWORD")
while not wlan.isconnected():
    time.sleep(0.5)
ntptime.settime()                     # the signature carries the time

hydro = HydroNode("YOUR_SENSOR_ID", "YOUR_SECRET", ca=ROOTS)  # checks the certificate
while True:
    hydro.send_values([("TEMPERATURE", 21.5), ("HUMIDITY", 48)])
    time.sleep(60)
Check the certificate Without ca=ROOTS MicroPython does not check the server. The signature still keeps your values from being forged, but someone in between could read them or send you false commands. Keep the check on wherever the board has the memory for it, that is on ESP32 and Pico W.

Collect offline, send later

record() keeps a reading with its time, flush() sends everything collected in one request, up to 100 rounds and seven days back. The example raspberry_pi_offline_safe.py keeps the buffer in a file, so not even a restart costs a reading. A reading that arrives twice is stored once.

raspberry_pi_offline_safe.pyPython
hydro.load_buffer("buffer.json")       # what waited before a restart

hydro.record({"TEMPERATURE": read_cpu_temperature()})
hydro.save_buffer("buffer.json")

try:
    hydro.flush()                      # every stored reading, each with its own time
except OSError:
    pass                               # offline: they stay for the next try

Commands from the app

Commands work as on the ESP32: name, type, value. The SDK confirms every command before your handler runs and declines what has no handler or the wrong type. The app then shows Confirmed or the reason.

commands.pyPython
def run_pump(ms):
    ms = min(ms, 10000)                # never trust a network value
    pump_on(ms)

hydro.on("pump", run_pump, "UINT32")
hydro.on("lamp", lambda on: relay(on), "BOOL")
hydro.send_values({"WATER_LEVEL": 72})  # commands arrive with this answer

Any other language

If your platform can do HTTPS and HMAC-SHA256, it can talk to HydroNode. The whole protocol is in PROTOCOL.md: every endpoint, every answer, every limit. In short:

protocolHTTP
X-Sensor-Id:  <sensor id>
X-Timestamp:  <Unix seconds>
X-Signature:  Base64(HMAC-SHA256(secret, canonical + X-Timestamp))

canonical = {"sensorId":"<id>","timestamp":1730638800,"values":[{"type":"TEMPERATURE","value":21.50}]}
The decimals trap Values go into the signature with exactly two decimals, rounded up on an exact five, computed on the 32-bit number. 21.125 becomes 21.13, not the 21.12 that printf gives. The file vectors/signature-vectors.json holds exactly such cases with the expected signatures. The HydroNode server, the Arduino library and every SDK test against this one file. If your own client passes it, it works.

Where to go next