How to Build a BLE Sensor Node with Arduino UNO Q

Building a Bluetooth Low Energy sensor node with the Arduino UNO Q board

Every previous Arduino UNO needed an external Bluetooth module. A $12 HC-05. An Adafruit Bluefruit shield. An HM-10 dangling off a breadboard with sketchy wiring. The UNO Q ships with BLE baked in, so you can go from parts on a desk to live temperature data on your phone in about 20 minutes.

That’s what we’re building: a BLE peripheral that reads a DHT22 temperature/humidity sensor and broadcasts the data to any smartphone. No prior BLE experience needed. You’ll have a working project by the end of this article, and you’ll understand enough about how BLE works to extend it into something bigger.

What You’ll Need

ComponentNotes
Arduino UNO QBuilt-in BLE, no shield required
DHT22 sensorDHT11 works too, just less accurate
10kΩ resistorPull-up between VCC and DATA
Breadboard + jumper wiresStandard stuff
USB-C cableFor programming and power

Software:

  • Arduino IDE 2.x
  • Libraries: ArduinoBLE, DHT sensor library (by Adafruit)
  • Smartphone app: nRF Connect (Nordic) or LightBlue (Punch Through)

BLE Basics: Only 3 Concepts Matter Right Now

Peripheral vs. Central. Your Arduino is a peripheral, like a weather station sign posted on a building. It just sits there, broadcasting data. Your phone is the central: it walks up and reads the sign. The peripheral advertises; the central connects and reads.

Service. A service is a container that groups related data. We’ll create one called Environmental Sensing (a standard Bluetooth SIG service, UUID 0x181A). It tells any connecting device “hey, I’ve got environment data in here.”

Characteristic. A characteristic is a single data point inside a service. We’ll have two: temperature and humidity. Each gets its own UUID.

┌─────────────┐         BLE          ┌─────────────┐
│  Arduino Q  │  ◄──────────────►    │ Smartphone  │
│ (Peripheral)│   advertises data    │  (Central)  │
│             │                      │             │
│ Service:    │                      │ nRF Connect │
│  └ Temp     │                      │ reads values│
│  └ Humidity │                      │             │
└─────────────┘                      └─────────────┘

The ArduinoBLE library handles everything else. If you want a deeper understanding of how BLE advertising and services work under the hood, the Hubble terrestrial SDK’s advertising packet docs explain the structure well, even though they’re aimed at a different platform.

Wiring the DHT22

Four pins on the DHT22, but one’s not connected.

DHT22 PinConnects To
Pin 1 (VCC)Arduino 5V
Pin 2 (DATA)Arduino Digital Pin 2
Pin 3 (NC)Not connected
Pin 4 (GND)Arduino GND

Put the 10kΩ resistor between VCC and DATA. This pull-up resistor keeps the data line stable. Without it, you’ll get NaN readings and wonder what you did wrong.

Arduino UNO Q          DHT22
─────────────          ─────
5V  ──────────────►  Pin 1 (VCC)
Digital Pin 2 ────►  Pin 2 (DATA)
                      Pin 3 (NC)
GND ──────────────►  Pin 4 (GND)

10kΩ resistor between Pin 1 (VCC) and Pin 2 (DATA)

Installing the Libraries

Open Arduino IDE. Go to Tools > Manage Libraries (or click the library icon in the sidebar).

Search for ArduinoBLE. Install it.

Search for DHT sensor library by Adafruit. Install it. When the IDE asks about dependencies (Adafruit Unified Sensor), click “Install All.”

One thing to check: go to Tools > Board and make sure the UNO Q is selected. If you don’t see it, update your Arduino AVR Boards package under Tools > Board > Boards Manager. Upload failures almost always trace back to having the wrong board selected.

The Complete Sketch

Here’s the full code. I’ll walk through it piece by piece below.

#include <ArduinoBLE.h>
#include <DHT.h>

// DHT22 setup
#define DHTPIN 2
#define DHTTYPE DHT22
DHT dht(DHTPIN, DHTTYPE);

// BLE Environmental Sensing Service (standard UUID)
BLEService envService("181A");

// Temperature characteristic (custom UUID, read + notify)
BLEFloatCharacteristic tempChar("2A6E", BLERead | BLENotify);

// Humidity characteristic (custom UUID, read + notify)
BLEFloatCharacteristic humChar("2A6F", BLERead | BLENotify);

void setup() {
  Serial.begin(9600);
  while (!Serial);

  // Start DHT sensor
  dht.begin();

  // Start BLE
  if (!BLE.begin()) {
    Serial.println("BLE failed to start!");
    while (1);
  }

  // Set the name that shows up when scanning
  BLE.setLocalName("UNO-Q-Sensor");
  BLE.setAdvertisedService(envService);

  // Bolt characteristics onto the service
  envService.addCharacteristic(tempChar);
  envService.addCharacteristic(humChar);

  // Add service to BLE stack
  BLE.addService(envService);

  // Set initial values
  tempChar.writeValue(0.0);
  humChar.writeValue(0.0);

  // Start advertising
  BLE.advertise();
  Serial.println("BLE: Advertising...");
}

void loop() {
  // Check if a central device has connected
  BLEDevice central = BLE.central();

  if (central) {
    Serial.print("BLE: Central connected: ");
    Serial.println(central.address());

    while (central.connected()) {
      float temp = dht.readTemperature();
      float hum = dht.readHumidity();

      // Only update if readings are valid
      if (!isnan(temp) && !isnan(hum)) {
        tempChar.writeValue(temp);
        humChar.writeValue(hum);

        Serial.print("Temperature: ");
        Serial.print(temp);
        Serial.println(" C");
        Serial.print("Humidity: ");
        Serial.print(hum);
        Serial.println(" %");
      }

      delay(2000); // DHT22 needs ~2s between reads
    }

    Serial.println("BLE: Central disconnected");
  }
}

What each section does

Includes and sensor setup. We pull in ArduinoBLE.h for all Bluetooth functionality and DHT.h for the sensor. The DHT22 data line is on pin 2.

BLE service and characteristics. BLEService envService("181A") creates an Environmental Sensing service using the standard Bluetooth SIG UUID. The two characteristics use UUIDs 2A6E (temperature) and 2A6F (humidity), which are also Bluetooth SIG standard assigned numbers. Using standard UUIDs means apps like nRF Connect can sometimes auto-label them for you. The BLERead | BLENotify flags mean a connected phone can both poll the value and subscribe to automatic updates.

setup() function. Initialize serial for debugging. Start the DHT sensor. Start BLE (with an error check). Set the device name to “UNO-Q-Sensor” so you can find it when scanning. Add the characteristics to the service, add the service to the BLE stack, and start advertising. That’s seven steps, each one line of code.

loop() function. Wait for a phone (central) to connect. Once connected, read the sensor every 2 seconds and write the values to the BLE characteristics. Print to serial so you can watch it in the IDE. When the phone disconnects, log it and go back to waiting.

That’s roughly 50 lines of real code.

Upload and Test

  1. Connect the UNO Q via USB-C. Select the right board and port in the IDE. Hit upload.
  2. Open Serial Monitor at 9600 baud. You should see:
Serial Monitor:
───────────────
BLE: Advertising...
  1. Open nRF Connect on your phone. Tap “Scan.” Look for “UNO-Q-Sensor.”
  2. Tap “Connect.” You’ll see the Environmental Sensing service listed, so tap into it.
  3. Read the temperature and humidity characteristics. The float values should match what the Serial Monitor reports.
Temperature: 23.40 C
Humidity: 51.20 %
BLE: Central connected: XX:XX:XX:XX:XX:XX

Quick troubleshooting:

  • Device not showing up? Check that Serial Monitor confirms “Advertising…” is printed. Move your phone within 1–2 meters. Some phones need location permissions enabled for BLE scanning, which catches a lot of people off guard.
  • Values read as 0 or NaN? Double-check the DHT22 wiring, especially the pull-up resistor. A missing pull-up is the #1 cause of garbage readings.
  • Upload fails? Verify the correct board is selected under Tools > Board. Try a different USB-C cable; some are charge-only and don’t carry data.

Extending Your Sensor Node

You’ve got a working BLE sensor node. Here’s where it gets interesting.

Add more sensors. Wire up a light sensor or soil moisture probe. Each new measurement becomes a new BLEFloatCharacteristic bolted onto the same service (or a new service, your call). The pattern is identical to what you just built.

Enable notifications. The code already sets BLENotify on both characteristics. In nRF Connect, tap the subscribe button (the down arrow icon) on a characteristic, and your phone will get pushed updates every 2 seconds without manually refreshing.

Build a multi-node network. Use 2 or 3 UNO Q boards, each running this sketch with a different setLocalName. A Raspberry Pi running Python with the bleak library makes a good central for this. It can connect to all of them and log data to a database. If you’re thinking about scaling BLE devices into something production-grade, the Hubble Device SDK is worth a look for handling provisioning and connectivity at larger scale.

Ship data to the cloud. Pair your BLE peripheral with a gateway that has WiFi or cellular, and you’ve got a full IoT pipeline. You can wire up a webhook endpoint on the cloud side to push sensor data into your own backend whenever new readings arrive.

The pattern you just learned (peripheral advertising a service with characteristics) is the same pattern used in commercial BLE products: heart rate monitors, tire pressure sensors, industrial equipment. You’ve got the foundation. Build something weird with it.


Hubble Network enables direct satellite connectivity for BLE devices—no gateways, no extra infrastructure. See how it works →