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

BoardSupportedNotes
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_CHECKSUM errors 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 temp and humidity — not value. This is the only sensor command that does not use the universal value key. The CeresFlows backend handles this via role-based valuePath in 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

BoardSupportedNotes
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_SCAN first to discover their addresses. Then use the address parameter 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

BoardSupportedScanned 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

BoardSupportedNotes
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

BoardSupportedNotes
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

BoardSupportedInterrupt 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 return 0. This is the most common mistake when using PULSE_COUNT on 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_COUNT uses 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.