Guide no. 64
A BLE server with two characteristics: the phone reads the temperature, receives updates and turns on the LED. With nRF Connect.
By Ada Intelisano · informaticait.it technical team
In short
- Service = folder, characteristics = values, each with a UUID.
- Temperature with read and notifications; LED with a write of 1 or 0.
- You test it with nRF Connect for Mobile.
- After a disconnection: startAdvertising().
No router, no network: the phone talks to the board over Bluetooth LE, the same one used by fitness bands and home sensors. The phone reads the temperature and receives updates on its own; writing 1 or 0 turns the LED on and off.
1The words of Bluetooth LE
New word: service and characteristicsThe board is a small archive (the “GATT server”). Inside it there is a service, a folder; in the service there are the characteristics, the individual values: here the temperature and the LED command. Each characteristic states what you can do with it: read, write, receive notifications.
New word: UUIDThe long, unique name of services and characteristics, like
cb5a991b-bf61-…. The short 16-bit ones are reserved for standard uses; for your own project you generate 128-bit ones at random. The sketch already has them.2The circuit and the sketch
The sensor is connected as in guide no. 56; the LED is the one on the board. Bluetooth is already in the ESP32 package: no library to install.
// Bluetooth LE server: the phone reads the temperature (and receives updates) and turns on the LED by writing 1 or 0. #include <BLEDevice.h> #include <BLEServer.h> #include <BLE2902.h> #include <Adafruit_BME280.h> // 128-bit UUIDs made up for this project (generated at random, as the Bluetooth SIG expects for custom services) #define SERVICE_UUID "cb5a991b-bf61-4bee-af4b-976ee137c7e6" #define TEMPERATURE_UUID "a3cc1db8-b2b8-4886-af86-36deee2fdd20" #define LED_UUID "1171cd6c-7e71-4867-b3e1-2a8ac3b36cd4" const int LED = 2; Adafruit_BME280 bme; BLECharacteristic* temperature; bool connected = false; class Connection : public BLEServerCallbacks { void onConnect(BLEServer* s) { connected = true; } void onDisconnect(BLEServer* s) { connected = false; s->startAdvertising(); // becomes visible again for the next phone } }; class LedCommand : public BLECharacteristicCallbacks { void onWrite(BLECharacteristic* c) { auto value = c->getValue(); // std::string in package 2.x, String in 3.x: auto works for both if (value.length() > 0) digitalWrite(LED, value[0] == '1' ? HIGH : LOW); } }; void setup() { Serial.begin(115200); pinMode(LED, OUTPUT); if (!bme.begin(0x77)) Serial.println("BME280 not found"); BLEDevice::init("FireBeetle-Meteo"); // the name you see on the phone BLEServer* server = BLEDevice::createServer(); server->setCallbacks(new Connection()); BLEService* service = server->createService(SERVICE_UUID); temperature = service->createCharacteristic(TEMPERATURE_UUID, BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_NOTIFY); temperature->addDescriptor(new BLE2902()); // the phone needs it to turn on notifications BLECharacteristic* led = service->createCharacteristic(LED_UUID, BLECharacteristic::PROPERTY_WRITE); led->setCallbacks(new LedCommand()); service->start(); BLEDevice::getAdvertising()->addServiceUUID(SERVICE_UUID); BLEDevice::startAdvertising(); Serial.println("Ready: look for FireBeetle-Meteo with nRF Connect"); } void loop() { char text[8]; snprintf(text, sizeof(text), "%.1f", bme.readTemperature()); temperature->setValue((uint8_t*)text, strlen(text)); // the value the phone reads if (connected) temperature->notify(); // and sends it to whoever turned on notifications delay(2000); }
New word: notificationInstead of asking for the temperature every time, the phone “subscribes”: the board sends it on its own when it changes. That is why the characteristic has
PROPERTY_NOTIFY and the BLE2902 descriptor, the one the phone uses to turn on the subscription.3Try it with nRF Connect
- Install nRF Connect for Mobile by Nordic on your phone (Android and iOS).
- Tap Scan, find FireBeetle-Meteo and tap Connect.
- Open the service
cb5a991b…. On the characteristica3cc1db8…tap the read arrow: the temperature appears. Tap the notifications symbol: the value updates every 2 seconds. - On the characteristic
1171cd6c…tap the write arrow, choose the text format (UTF-8) and write 1: the LED turns on; 0 turns it off.
✓ What you should seeThe temperature updates by itself in the app and the LED responds to 1 and 0.
If it doesn't work
- The board does not appear: is Bluetooth on in the phone? The app may ask for scanning permissions: grant them.
- The value arrives as odd numbers (0x32 0x31…): the app shows it in hexadecimal; choose the text view.
- Once disconnected it cannot be found again:
startAdvertising()is missing fromonDisconnect.
4Two things to know
- Since ESP32 package 3.0 many BLE functions use
Stringinstead ofstd::string. In the sketch,auto value = c->getValue()works in both versions. - The range is a few meters and anyone nearby can connect: for real use you add pairing with a passkey.
- The classic ESP32 also has “traditional” (serial) Bluetooth, which BLE does not replace: they are two different things.
5Challenge yourself
Try it yourself
- Add a characteristic for humidity.
- Control the relay from guide no. 43 instead of the LED.
- Send the temperature only when it changes by at least 0.2 degrees.
✓ What you learned
- in BLE the board exposes services and characteristics, each with a UUID;
- the phone reads, writes and receives notifications;
- after a disconnection you must become visible again with
startAdvertising().
Sources: Espressif, ESP-IDF: Bluetooth LE guides; arduino-esp32: BLE examples and 2.x→3.0 migration guide · Bluetooth SIG, Assigned Numbers · Nordic Semiconductor, nRF Connect for Mobile · DFRobot, FireBeetle 2 ESP32-E wiki (Bluetooth example) · informaticait guides no. 43 and 56
Useful products
FireBeetle 2 ESP32-E board with headers, Wi-Fi and Bluetooth 4.2 BLE and Classic, 4 MB flash, USB-C – DFRobot DFR0654-F
Codice mepa: MIC15097IT
Quantità disponibili: 39
€17.30
BME280 environmental sensor with soldered headers, pressure, humidity and temperature, I2C and SPI, 3.3 V – SparkFun 13905
Codice mepa: MIC16348IT
Quantità disponibili: 86
€32.90
Premium female-to-female jumper wires 15.5 cm, 26 AWG, assorted colours (pack of 10) – SparkFun 08430
Codice mepa: MIC15987IT
Quantità disponibili: 110
€8.10
Other guides
- Resistor colour code and tolerances
- N-channel and P-channel MOSFETs
- Bipolar transistor (BJT) or MOSFET?
- Relays: how to drive them and how much current they draw
- THT and SMD/SMT components
- XIAO board pinouts
- LEDs: forward voltage and series resistor
- The multimeter: measuring voltage and current
- Wemos D1 mini: first steps
- MOSFET module with Arduino and ESP32
- Relay module with Arduino and ESP32
- 3D printing filaments: PLA, PETG and fibre-filled versions
- Analog and digital: the differences
- ADS1115: 16-bit converter with Arduino and ESP32
- From bits to real values: converting sensor readings
- DS3231 RTC clock with Arduino and ESP32
- Linear or switching regulator: which one to choose
- Step-down, step-up and buck-boost: choosing the right module
- Voltage divider: reading voltages higher than 5 V or 3.3 V
- Pull-up, pull-down and bounce: wiring a button properly
- 3.3 V and 5 V logic levels: getting an ESP32 and an Arduino to talk
- The I²C bus: addresses, pull-ups and the scanner
- Lithium batteries: voltages, charging and protection
- ESP32 power consumption and deep sleep: how long the battery lasts
- Temperature sensors compared
- ESP32: which pins to use and which to avoid
- Getting started with the ESP32 in Arduino IDE 2 and PlatformIO
- SSD1306 OLED display and 16×2 I²C LCD with Arduino and ESP32
- PIR motion sensor and HC-SR04 ultrasonic sensor
- Date and time from the Internet with the ESP32: NTP, Italian time zone and daylight saving time
- MicroSD data logger: saving measurements to CSV
- Solar-powered ESP32: panel, charger and the numbers for Italy
- Web server on the ESP32: control a relay from your phone
- Your first LED: wire it up and make it blink
- The button: let Arduino decide
- The potentiometer: turn a knob and read its value
- Soft light: adjusting the brightness of an LED
- The buzzer: playing notes and melodies
- The traffic light: three LEDs in sequence
- The thermometer: reading the temperature with the TMP36
- The servo motor: moving an arm with Arduino
- The night light: the photoresistor and your first decision
- The relay: switching on a light bulb with Arduino
- The LCD display: making Arduino write
- The electronic dice: your first complete project
- The RGB LED: every color from three lights
- The joystick: two-axis control with a shield
- The LED ring: twelve colors from a single pin
- The DC motor: driving it with a MOSFET
- Soil moisture: the plant that asks for water
- The stepper motor: turn exactly as far as you want
- The parking sensor: faster and faster beeps
- The 7-segment display: big, bright numbers
- Automatic watering: the plant that waters itself
- The keypad: a lock with a secret code
- Networked weather station: values and chart on your phone
- A Telegram bot on the ESP32: your home from your phone
- MQTT with the ESP32: the language of connected devices
- Updating the ESP32 over Wi-Fi (OTA)
- ESP-NOW: two ESP32 boards talking without a router
- WebSocket with the ESP32: the LED that follows the slider
- Readings on a Google sheet, with deep sleep
- WiFiManager: choose the Wi-Fi from your phone
- The camera in your browser with the ESP32-S3
- A portable Bluetooth speaker you build yourself
- Bluetooth desktop speakers, 2 × 20 W
- Stereo MP3 player with two buttons
- The shelf that plays music: a Visaton exciter
- How to solder well
- How much does it draw? Current and power with the INA219
- Load-cell scale with the HX711
- CO2 traffic light: when to open the windows
- RFID card door opener
- GPS: track logger on microSD
- LoRa: how far does it go? A range test with two boards
- Battery weather sign with an e-paper display
- Web radio with an ESP32 and an I2S amplifier
- Getting started with the Raspberry Pi Pico 2 in MicroPython
- Your first amplifier: the LM386 and a speaker
- Talking Arduino: the DFR0760 speech synthesis module
- The spectrum of your music on an LED matrix
- Drawing with 64 LEDs: the 8×8 matrix with the MAX7219
- A real voltage: the 12-bit MCP4725 DAC
- The heartbeat on Arduino: an ECG with the AD8232