Sensor Commands
Sensor commands read physical measurements from connected sensors. All implementations in this chapter use bit-bang protocols — no external libraries are required beyond the Arduino core.
DHT_READ
Reads temperature and humidity from a DHT11 or DHT22 sensor.
Compatibility
| Board | Supported | Notes |
|---|---|---|
| Arduino UNO R4 WiFi | ✅ | Bit-bang timing tested |
| ESP32 WROOM-32 | ✅ | Higher clock speed — timing may need adjustment |
| ESP8266 NodeMCU | ✅ | Works on most GPIO pins |
| Arduino Nano | ✅ | Any digital pin |
[!WARNING] DHT sensors use a timing-sensitive bit-bang protocol. On very fast MCUs (ESP32 at 240 MHz), the microsecond timing may be too tight and produce checksum errors. If you experience frequent
ERR_CHECKSUMerrors on ESP32, consider using the DHT sensor library instead of the raw bit-bang implementation shown here.
Request:
{"cmd": "DHT_READ", "id": 1, "args": ["D4"]}
Or pipe: DHT_READ|D4
Response:
{"ok": 1, "temp": 24.5, "humidity": 61.0, "id": 1}
[!IMPORTANT] DHT_READ returns
tempandhumidity— notvalue. This is the only sensor command that does not use the universalvaluekey. The CeresFlows backend handles this via role-basedvaluePathin the device template. See Firmware Response Protocol.
Required hardware: Single data pin + 4.7–10 kΩ pull-up resistor to VCC.
Implementation:
String handleDHTRead(const char* params) {
if (!params || strlen(params) < 1)
return F("{\"ok\":0,\"error\":\"ERR_MISSING_PARAMETER\"}");
int dataPin = parsePin(String(params));
if (dataPin == -1) return F("{\"ok\":0,\"error\":\"ERR_INVALID_PIN\"}");
byte data[5] = {0};
// Start signal
pinMode(dataPin, OUTPUT);
digitalWrite(dataPin, LOW); delayMicroseconds(18000);
digitalWrite(dataPin, HIGH); delayMicroseconds(40);
pinMode(dataPin, INPUT);
unsigned long t0 = millis();
while (digitalRead(dataPin) == HIGH) { if (millis()-t0>5000) return F("{\"ok\":0,\"error\":\"ERR_SENSOR_TIMEOUT\"}"); }
while (digitalRead(dataPin) == LOW) { if (millis()-t0>5000) return F("{\"ok\":0,\"error\":\"ERR_SENSOR_TIMEOUT\"}"); }
while (digitalRead(dataPin) == HIGH) { if (millis()-t0>5000) return F("{\"ok\":0,\"error\":\"ERR_SENSOR_TIMEOUT\"}"); }
for (int i = 0; i < 40; i++) {
while (digitalRead(dataPin) == LOW) { if (millis()-t0>5000) return F("{\"ok\":0,\"error\":\"ERR_READ_TIMEOUT\"}"); }
unsigned long ps = micros();
while (digitalRead(dataPin) == HIGH) { if (millis()-t0>5000) return F("{\"ok\":0,\"error\":\"ERR_READ_TIMEOUT\"}"); }
if (micros() - ps > 40) data[i/8] |= (1 << (7-(i%8)));
}
byte cs = data[0] + data[1] + data[2] + data[3];
if (cs != data[4]) return F("{\"ok\":0,\"error\":\"ERR_CHECKSUM\"}");
float humidity = ((data[0] << 8) | data[1]) / 10.0;
float temperature = (((data[2] & 0x7F) << 8) | data[3]) / 10.0;
if (data[2] & 0x80) temperature = -temperature;
String r = "{\"ok\":1,\"temp\":";
r += String(temperature, 1); r += ",\"humidity\":"; r += String(humidity, 1); r += "}";
return r;
}
ONEWIRE_READ_TEMP
Reads temperature from a DS18B20 sensor on the OneWire bus. Supports a specific sensor address for multi-sensor setups.
Compatibility
| Board | Supported | Notes |
|---|---|---|
| Arduino UNO R4 WiFi | ✅ | Any digital pin |
| ESP32 WROOM-32 | ✅ | Any GPIO |
| ESP8266 NodeMCU | ✅ | Any GPIO |
| Arduino Nano | ✅ | Any digital pin |
Required hardware: Single data pin + 4.7 kΩ pull-up resistor to VCC (3.3V or 5V depending on board).
Request (single sensor — Skip ROM):
{"cmd": "ONEWIRE_READ_TEMP", "id": 2, "args": ["D7"]}
Request (specific sensor — Match ROM):
{"cmd": "ONEWIRE_READ_TEMP", "id": 3, "args": ["{\"pin\":\"D7\",\"address\":\"28ad7daa10000071\"}"]}
Response:
{"ok": 1, "value": 21.50, "id": 2}
[!NOTE] When multiple DS18B20 sensors are on the same bus, use
ONEWIRE_SCANfirst to discover their addresses. Then use theaddressparameter in subsequent reads to target a specific sensor.
ONEWIRE_SCAN
Scans one or all digital pins for DS18B20 sensors and returns their 64-bit ROM addresses.
Compatibility
| Board | Supported | Scanned pins |
|---|---|---|
| Arduino UNO R4 WiFi | ✅ | D2–D12 (when “ALL”) or specific pin |
| ESP32 WROOM-32 | ⚠️ | Adjust hardcoded pin list for ESP32 GPIO |
| ESP8266 NodeMCU | ⚠️ | Adjust hardcoded pin list for ESP8266 GPIO |
| Arduino Nano | ✅ | D2–D12 or specific pin |
[!WARNING] The “ALL” scan mode uses a hardcoded list of pins to check. This list is defined in firmware and is board-specific. The default list (
D2–D12) is correct for Arduino UNO R4 and Nano. For ESP32 or ESP8266, you must modify this list to reflect the valid GPIO pins on your board. Scanning invalid pins (e.g., internal flash pins on ESP8266) can cause instability.
Request (specific pin):
{"cmd": "ONEWIRE_SCAN", "id": 4, "args": ["D7"]}
Request (scan all known pins):
{"cmd": "ONEWIRE_SCAN", "id": 5, "args": ["ALL"]}
Response:
{"ok": 1, "results": {"7": "28ad7daa10000071", "4": "28ff1a9a01000036"}, "id": 5}
The results object maps pin number (as string) → 16-character hex ROM address.
ULTRASONIC_TRIG_ECHO
Measures distance using an HC-SR04 ultrasonic sensor. Requires two separate pins: trigger and echo.
Compatibility
| Board | Supported | Notes |
|---|---|---|
| Arduino UNO R4 WiFi | ✅ | |
| ESP32 WROOM-32 | ✅ | |
| ESP8266 NodeMCU | ✅ | |
| Arduino Nano | ✅ |
Required hardware: HC-SR04 sensor — VCC (5V), GND, TRIG pin, ECHO pin. The ECHO pin outputs 5V on HC-SR04 — use a voltage divider (2:1) if connecting to a 3.3V board (ESP32, ESP8266).
[!WARNING] HC-SR04 ECHO pin outputs 5V. On 3.3V boards (ESP32, ESP8266), connecting ECHO directly will damage the MCU. Use a voltage divider (e.g., 1 kΩ + 2 kΩ) to reduce the ECHO signal to ~3.3V, or use an HC-SR04P model which operates at 3.3V.
Request:
{"cmd": "ULTRASONIC_TRIG_ECHO", "id": 6, "args": ["D9", "D10"]}
Or pipe: ULTRASONIC_TRIG_ECHO|D9|D10
- Arg 0: Trigger pin
- Arg 1: Echo pin
Response:
{"ok": 1, "value": 15.3, "id": 6}
value: distance in centimeters (float, 1 decimal place)- Valid range: 2–400 cm. Out-of-range returns
ERR_OUT_OF_RANGE.
HX711_READ
Reads the raw 24-bit ADC value from an HX711 load cell amplifier.
Compatibility
| Board | Supported | Notes |
|---|---|---|
| Arduino UNO R4 WiFi | ✅ | |
| ESP32 WROOM-32 | ✅ | |
| ESP8266 NodeMCU | ✅ | |
| Arduino Nano | ✅ |
Required hardware: HX711 breakout board connected to a load cell. Two digital pins required: DT (Data) and SCK (Clock).
Request:
{"cmd": "HX711_READ", "id": 7, "args": ["D4", "D5"]}
Or pipe: HX711_READ|D4|D5
- Arg 0: DT (Data) pin
- Arg 1: SCK (Clock) pin
Response:
{"ok": 1, "value": 824500, "id": 7}
value: raw 24-bit signed ADC value. The backend applies calibration (offset + scale) to convert to actual weight in grams.
Implementation notes:
- Uses bit-bang communication — no library required
- Gain is set to 128 (Channel A) via the 25th clock pulse
- Waits up to 500ms for HX711 DOUT to go LOW (ready signal)
PULSE_COUNT
Counts pulses on a pin over time using a hardware interrupt. Used for flow meters (water volume), tachometers (RPM), and similar pulse-based sensors.
Compatibility
| Board | Supported | Interrupt pins |
|---|---|---|
| Arduino UNO R4 WiFi | ✅ | Most digital pins |
| ESP32 WROOM-32 | ✅ | Any GPIO (except input-only in some modes) |
| ESP8266 NodeMCU | ✅ | GPIO0–GPIO15 |
| Arduino Nano | ⚠️ | D2 and D3 only |
[!CAUTION] On Arduino Nano, only pins D2 and D3 support hardware interrupts. If you connect a flow meter to any other pin and use
PULSE_COUNT, no pulses will be counted and the command will always return0. This is the most common mistake when usingPULSE_COUNTon Arduino Nano.
Request:
{"cmd": "PULSE_COUNT", "id": 8, "args": ["D2"]}
Or pipe: PULSE_COUNT|D2
Response:
{"ok": 1, "value": 56, "id": 8}
value: number of pulses counted since the last call (counter resets on each read)- The backend calculates flow rate (L/min) or frequency (Hz) from the pulse count and the elapsed time between calls
[!NOTE]
PULSE_COUNTuses an interrupt-driven counter. The first call on a new pin sets up the interrupt. Subsequent calls read and reset the counter atomically. The backend is responsible for timing the calls (usually every 1 second) and converting counts to engineering units via calibration.