This project turns a bare ESP32 into a device that reports real environmental data. You add a DHT22 sensor, read it on every cycle, and publish temperature and humidity in the canonical format that DOBI expects, so the readings show up live on the device detail page. It builds directly on the ESP32 skeleton you already flashed in the How to connect an ESP32 to DOBI guide, so the network and provisioning part stays untouched.
The point of the exercise is not just reading a sensor. It is publishing metrics in a shape DOBI can aggregate: once the device is grouped in a DAM, those same temperature and humidity values feed the cluster's aggregation and alarm rules. The guide takes about 30 minutes and assumes a registered ESP32 with its id and provisioning key, plus the Adafruit DHT library installed from the Library Manager.
Supplies
- ESP32 development board
- DHT22 sensor (a DHT11 also works)
- 10 kΩ resistor for the pull-up on the data pin
- Breadboard and jumper wires
Prerequisites
- You have completed the How to connect an ESP32 to DOBI guide.
- The Adafruit DHT library is installed through the Library Manager.
- The ESP32 is already registered with its id and provisioning key.
Step 1: Wire the DHT22 to the ESP32
Before you write a single line of firmware, the sensor has to be wired correctly, because a floating or noisy data line is the most common reason a DHT read fails intermittently and you end up chasing bugs in code that is fine. Connect VCC to 3.3 V, GND to GND, and the data pin to a digital GPIO; here the guide uses pin 4.
The 10 kΩ resistor goes between VCC and the data pin. That pull-up stabilizes the reading by holding the line at a defined level between the sensor's open-drain pulses. Without it, the sensor may report plausible-looking but wrong values or drop reads.
Use the 3.3 V rail, not 5 V, and double-check the pin order on your specific DHT22 breakout: loose modules vary, and reversing VCC and GND will destroy the sensor.
Step 2: Read temperature and humidity
Now you teach the firmware to read the sensor. Initialize the DHT object in
setup()and read the values on each cycle, so every publish carries a fresh measurement instead of a stale one. The DHT protocol is slow and occasionally fails, which is why the guide tells you explicitly: if the read fails, do not send the heartbeat. Publishing corrupted or default values is worse than publishing nothing, because DOBI will aggregate those values and your alarms will be evaluated against garbage.The usual failure mode here is sending the heartbeat anyway, with whatever the buffer contained from the previous cycle. Guard the read, and let the next cycle try again.
Reading the sensor · cpp
#include <DHT.h> #define DHTPIN 4 #define DHTTYPE DHT22 DHT dht(DHTPIN, DHTTYPE); void setup() { dht.begin(); } float readTemperature() { return dht.readTemperature(); } float readHumidity() { return dht.readHumidity(); }Step 3: Publish metrics in the heartbeat
With the sensor reading cleanly, you replace the example values in the heartbeat payload with the real readings. This is where the project becomes a DOBI device rather than a serial monitor demo: DOBI aggregates these metrics per cluster and evaluates them against the alarm rules of your DAM, so the field names and the format have to match the canonical shape exactly.
On the device detail page you will see a chart with historical temperature and humidity. If you group the device in a DAM, those metrics feed the aggregation and alarms of the cluster. The failure to watch for is sending the metrics under different field names or with the sensor values swapped while you test; the device looks online but the chart stays empty.
Heartbeat with real metrics · cpp
void dobiHeartbeat() { float t = readTemperature(); float h = readHumidity(); if (isnan(t) || isnan(h)) return; HTTPClient http; http.begin(String(DOBI_PLATFORM) + "/api/devices/" + DOBI_DEVICE_ID + "/heartbeat"); http.addHeader("Content-Type", "application/json"); StaticJsonDocument<256> doc; JsonArray metrics = doc.createNestedArray("metrics"); JsonObject m1 = metrics.createNestedObject(); m1["name"] = "temperature"; m1["value"] = t; m1["unit"] = "C"; JsonObject m2 = metrics.createNestedObject(); m2["name"] = "humidity"; m2["value"] = h; m2["unit"] = "%"; String body; serializeJson(doc, body); http.POST(body); http.end(); }Step 4: Complete sketch: ESP32 + DHT22
The three previous steps are fragments. This last step gives you the complete sketch, wiring, sensor read, and heartbeat in one file, so you can copy and paste the whole block into your environment and flash in one go. That removes the risk of a mismatch between the read routine and the publish routine, which is exactly where partial edits go wrong.
Replace the credentials with your own before flashing: the WiFi network and password, and the device id and provisioning key from your registration. Keep them out of anything you publish.
If the board connects but no metrics appear, the most likely cause is that you flashed the sketch with the placeholder credentials still in place.
Complete sketch: ESP32 + DHT22 · cpp
#include <WiFi.h> #include <HTTPClient.h> #include <ArduinoJson.h> #include <DHT.h> #define DOBI_PLATFORM "https://dobi.guru" #define WIFI_SSID "YOUR_WIFI" #define WIFI_PASSWORD "YOUR_PASSWORD" #define DOBI_DEVICE_ID "living-room-sensor-01" #define DOBI_DEVICE_NAME "Living room temperature sensor" #define DOBI_PROVISION_KEY "your-provision-key" #define DOBI_DEVICE_TYPE "iot_sensor" #define DOBI_HEARTBEAT_MS 30000 #define DHTPIN 4 #define DHTTYPE DHT22 DHT dht(DHTPIN, DHTTYPE); static unsigned long lastTick = 0; void dobiRegister() { HTTPClient http; http.begin(String(DOBI_PLATFORM) + "/api/devices/register"); http.addHeader("Content-Type", "application/json"); StaticJsonDocument<256> doc; doc["provision_key"] = DOBI_PROVISION_KEY; doc["id_asset"] = DOBI_DEVICE_ID; doc["device_name"] = DOBI_DEVICE_NAME; doc["device_type"] = DOBI_DEVICE_TYPE; String body; serializeJson(doc, body); http.POST(body); http.end(); } void dobiHeartbeat() { float t = dht.readTemperature(); float h = dht.readHumidity(); if (isnan(t) || isnan(h)) return; HTTPClient http; http.begin(String(DOBI_PLATFORM) + "/api/devices/" + DOBI_DEVICE_ID + "/heartbeat"); http.addHeader("Content-Type", "application/json"); StaticJsonDocument<256> doc; JsonArray metrics = doc.createNestedArray("metrics"); JsonObject m1 = metrics.createNestedObject(); m1["name"] = "temperature"; m1["value"] = t; m1["unit"] = "C"; JsonObject m2 = metrics.createNestedObject(); m2["name"] = "humidity"; m2["value"] = h; m2["unit"] = "%"; String body; serializeJson(doc, body); http.POST(body); http.end(); } void setup() { Serial.begin(115200); dht.begin(); WiFi.begin(WIFI_SSID, WIFI_PASSWORD); while (WiFi.status() != WL_CONNECTED) delay(500); dobiRegister(); lastTick = millis() - DOBI_HEARTBEAT_MS; } void loop() { if (millis() - lastTick >= DOBI_HEARTBEAT_MS) { dobiHeartbeat(); lastTick = millis(); } delay(200); }The full source guide, with the updated version of this integration, is at dobi.guru/guides/temperature-sensor.