How-To-Tutorials · September 22, 2025

How to Set Up ESP32 BLE GATT Server for DHT22 Data Transmission Every 5 Seconds

Broadcasting Sensor Data Over BLE with ESP32 and DHT22

Bluetooth Low Energy is one of the best ways to wirelessly share sensor readings without the overhead (and power draw) of Wi-Fi. In this project, you'll turn an ESP32 into a BLE GATT server that broadcasts temperature and humidity from a DHT22 sensor every 5 seconds. Any BLE client—your phone, a tablet, another microcontroller—can connect and subscribe to those updates in real time.

The GATT (Generic Attribute Profile) model is straightforward: your ESP32 advertises a service, that service contains a characteristic, and connected clients subscribe to notifications on that characteristic. Every 5 seconds, you push fresh sensor data to them.

Prerequisites

  • Comfortable with Arduino-style C/C++ programming
  • ESP32 development board (any variant with BLE support)
  • DHT22 temperature and humidity sensor
  • Arduino IDE 2.x installed with the ESP32 board package (v3.x)
  • A BLE scanner app on your phone (nRF Connect is excellent for this)

Parts and Tools

  • ESP32 dev board
  • DHT22 sensor
  • Breadboard and jumper wires
  • USB cable for programming

Steps

  1. Wire Up the DHT22

    The DHT22 has three relevant pins: VCC, GND, and DATA. Connect VCC to the ESP32's 3.3V rail, GND to ground, and DATA to GPIO 23. Some DHT22 breakout boards already include a pull-up resistor on the data line—if yours doesn't, add a 10kΩ resistor between DATA and 3.3V. Without it, you'll get intermittent read failures that will drive you crazy.

  2. Set Up Arduino IDE for ESP32

    In Arduino IDE 2.x, open File > Preferences and add this URL to Additional Board Manager URLs:

    https://espressif.github.io/arduino-esp32/package_esp32_index.json

    Then go to Tools > Board > Board Manager, search for "esp32" by Espressif, and install the latest v3.x package. This gives you the BLE libraries built in—no extra BLE library install needed.

  3. Install the DHT Library

    Go to Tools > Manage Libraries, search for "DHT sensor library" by Adafruit and install it. It'll prompt you to also install "Adafruit Unified Sensor"—say yes to that.

  4. Write the Sketch
    
    #include <BLEDevice.h>
    #include <BLEServer.h>
    #include <BLE2902.h>
    #include <DHT.h>
    
    #define DHTPIN 23
    #define DHTTYPE DHT22
    #define SERVICE_UUID        "12345678-1234-5678-1234-56789abcdef0"
    #define CHARACTERISTIC_UUID "12345678-1234-5678-1234-56789abcdef1"
    
    DHT dht(DHTPIN, DHTTYPE);
    BLEServer* pServer = nullptr;
    BLECharacteristic* pCharacteristic = nullptr;
    bool deviceConnected = false;
    
    class MyServerCallbacks : public BLEServerCallbacks {
        void onConnect(BLEServer* pServer) { deviceConnected = true; }
        void onDisconnect(BLEServer* pServer) {
            deviceConnected = false;
            pServer->getAdvertising()->start(); // restart advertising
        }
    };
    
    void setup() {
        Serial.begin(115200);
        dht.begin();
    
        BLEDevice::init("ESP32_DHT22");
        pServer = BLEDevice::createServer();
        pServer->setCallbacks(new MyServerCallbacks());
    
        BLEService* pService = pServer->createService(SERVICE_UUID);
        pCharacteristic = pService->createCharacteristic(
            CHARACTERISTIC_UUID,
            BLECharacteristic::PROPERTY_NOTIFY
        );
        pCharacteristic->addDescriptor(new BLE2902());
        pService->start();
        pServer->getAdvertising()->start();
        Serial.println("BLE server started, waiting for connections...");
    }
    
    void loop() {
        if (deviceConnected) {
            float h = dht.readHumidity();
            float t = dht.readTemperature();
    
            if (isnan(h) || isnan(t)) {
                Serial.println("DHT read failed, skipping...");
            } else {
                String data = "T:" + String(t, 1) + "C H:" + String(h, 1) + "%";
                pCharacteristic->setValue(data.c_str());
                pCharacteristic->notify();
                Serial.println(data);
            }
        }
        delay(5000);
    }
    

    A few things worth calling out in this code. The BLE2902 descriptor is what enables the client to actually subscribe to notifications—without it, many BLE scanner apps won't show the subscribe button. The server callbacks handle re-advertising after a disconnect, so you don't have to power-cycle the ESP32 to reconnect. And the isnan() check catches those occasional DHT22 read failures instead of broadcasting garbage data.

  5. Upload and Run

    Select your ESP32 board under Tools > Board, pick the right port, and hit Upload. Open the Serial Monitor at 115200 baud to confirm it's running. Watch out: some ESP32 boards need you to hold the BOOT button during upload—if the upload times out, that's likely why.

  6. Test with a BLE Scanner

    Open nRF Connect (or any BLE scanner) on your phone. Scan for devices and look for "ESP32_DHT22". Connect, find the characteristic under the service UUID, and tap the subscribe/notify icon. You should see temperature and humidity strings arriving every 5 seconds.

    If you see the device but can't get notifications, double-check that you included the BLE2902 descriptor. That's the most common gotcha with BLE notify on ESP32.

Troubleshooting

  • Device not showing up in scans: Make sure the sketch uploaded successfully (check Serial Monitor for the startup message). Also verify your phone's Bluetooth is on and the app has location permissions—Android requires location access for BLE scanning.
  • Getting NaN readings: Check your wiring, especially the pull-up resistor on the data line. The DHT22 also needs about 2 seconds after power-on before it gives valid readings, which the setup() delay typically covers.
  • Notifications stop after disconnect/reconnect: The server callbacks handle re-advertising, but if you're still having trouble, make sure you're creating a new connection from the client side (not just resuming a cached one).
  • BLE range is poor: ESP32's built-in antenna is decent for a few meters indoors. If you need more range, consider a module with an external antenna connector (like the ESP32-WROOM-32U).

Where to Go From Here

This setup gives you a solid foundation for wireless sensor monitoring. You could extend it by adding more characteristics for separate temperature and humidity values (makes parsing easier on the client side), implementing BLE bonding for security, or building a companion app that logs the data over time. For production use, I'd recommend generating proper random UUIDs instead of the sequential ones used here—there are online UUID generators that make this trivial.