Learning Goals 5 min
The HC-05 you used in L03-23/24/25 uses Bluetooth Classic. iOS blocks Classic SPP for non-MFI devices, so iPhones can't pair with it. Bluetooth Low Energy (BLE) is the modern stack — works on every smartphone, lower power, but a completely different programming model. Today you meet the ArduinoBLE library and the four concepts that define BLE: peripheral, service, characteristic, and advertising. By the end of this lesson you will:
- Explain the difference between Bluetooth Classic (SPP, stream-based) and BLE (GATT, attribute-based), and why iOS supports the latter.
- Identify the BLE-capable Arduino boards (Nano 33 BLE family, MKR WiFi 1010, Nano ESP32) and install the
ArduinoBLElibrary. - Read a minimal BLE peripheral sketch — one service, one characteristic — and explain in plain language what each line does.
Warm-Up 10 min
Today is mostly conceptual; the hands-on starts tomorrow with a custom characteristic. If you have a Nano 33 BLE (or BLE Sense), plug it into USB. If you don't, follow along — you can still understand the model without the hardware.
Check the boards
BLE on Arduino requires a board with a Bluetooth-LE radio. The classroom-friendly options:
| Board | Radio | Notes |
|---|---|---|
| Nano 33 BLE | nRF52840 | Mid-priced. The standard. ArduinoBLE library works out of the box. |
| Nano 33 BLE Sense | nRF52840 + 9 sensors | BLE + IMU + microphone + temp + light + gesture — heaven for Edge AI (L04-31+). |
| MKR WiFi 1010 | NINA W102 (BLE + WiFi) | BLE + WiFi on a credit-card-sized board. |
| Nano ESP32 | ESP32-S3 (BLE + WiFi) | Newer; uses ArduinoBLE via a compatibility layer. |
The original UNO R3 does NOT have a BLE radio. UNO R4 WiFi does (ESP32-S3 co-processor). For this lesson, anything in the table works.
Install the library
Sketch → Include Library → Manage Libraries → search "ArduinoBLE". Install. Don't confuse with "ArduinoBlue", "BLE-mouse", or other lookalikes — "ArduinoBLE" is the official one, by Arduino.
New Concept · GATT — services and characteristics 25 min
Classic vs LE — two different conversations
| Aspect | Bluetooth Classic (SPP) | Bluetooth Low Energy (GATT) |
|---|---|---|
| Model | Stream of bytes — like a UART over the air | Database of attributes the central can read / write / subscribe to |
| Pairing | Required (PIN dialog), keys persist | Optional for many uses; many BLE apps connect without pairing at all |
| Power | Always on, ~30 mA active | Mostly asleep, < 1 mA average; great for battery |
| iOS support | Blocked for non-MFI | Full support since iOS 5 |
| Throughput | ~10 KB/s | ~1 KB/s typical (less with overhead) |
| Use case | Phone-to-Arduino bulk data | Sensors, wearables, button remotes, IoT |
BLE traded throughput for power efficiency, and stream-of-bytes for a structured database. The phone's app reads/writes named slots in that database; the Arduino reacts to those reads/writes.
The four words you need
- Peripheral: the device offering data (your Arduino). Advertises itself.
- Central: the device connecting to the peripheral (your phone, or a BLE-scanning laptop).
- Service: a named group of related characteristics. Identified by a UUID. Example: "LED Service".
- Characteristic: a named, typed value inside a service. Also has a UUID. Example: "LED brightness" (8-bit number, 0–255).
The full pattern: the peripheral advertises "hi, I'm offering Service X with Characteristic Y inside". The central scans, finds it, connects, reads / writes / subscribes to Y. When the central writes to Y, the peripheral's sketch gets a callback.
UUIDs — every service and characteristic needs one
A UUID is a 128-bit number used as a unique identifier. Two formats:
- 16-bit short UUIDs: reserved for standard services (Heart Rate, Battery Level, etc.). Example:
0x2A19= Battery Level characteristic. - 128-bit custom UUIDs: anything you make up for your own services / characteristics. Generate one online (uuidgen, or any random-UUID website).
For school projects, custom 128-bit UUIDs are standard. Once generated, they live in your sketch forever — the phone app finds your service by UUID.
The minimum-viable BLE peripheral sketch (preview)
#include <ArduinoBLE.h>
BLEService ledService("19B10000-E8F2-537E-4F6C-D104768A1214");
BLEByteCharacteristic ledChar("19B10001-E8F2-537E-4F6C-D104768A1214",
BLERead | BLEWrite);
void setup() {
Serial.begin(9600);
pinMode(LED_BUILTIN, OUTPUT);
if (!BLE.begin()) {
Serial.println("# BLE init failed");
while (true) ;
}
BLE.setLocalName("LED-Demo");
BLE.setAdvertisedService(ledService);
ledService.addCharacteristic(ledChar);
BLE.addService(ledService);
ledChar.writeValue((byte)0);
BLE.advertise();
Serial.println("# Advertising as LED-Demo");
}
void loop() {
BLEDevice central = BLE.central();
if (central) {
Serial.print("# Connected: ");
Serial.println(central.address());
while (central.connected()) {
if (ledChar.written()) {
digitalWrite(LED_BUILTIN, ledChar.value() ? HIGH : LOW);
}
}
Serial.println("# Disconnected");
}
}Don't upload yet — we'll walk through it line by line.
Reading the sketch
- Two UUIDs declared: one for the service, one for the characteristic.
BLEByteCharacteristic— a one-byte value. Other types:BLEIntCharacteristic,BLEFloatCharacteristic,BLEStringCharacteristic,BLECharacteristic(raw bytes).BLERead | BLEWrite— properties: the central can read it AND write it. AddBLENotifyif you want the peripheral to push updates to subscribers.BLE.setLocalName("LED-Demo")— what the central sees during scanning.BLE.setAdvertisedService+addService— tells the radio "advertise this service's UUID so centrals scanning for it find me".BLE.advertise()— start broadcasting.- In
loop(): wait for a central; while connected, react to writes.
What the phone sees
On iOS / Android, install a generic BLE scanner app (Adafruit Bluefruit Connect, LightBlue Explorer, nRF Connect). Scan; you'll see "LED-Demo" in the list. Tap → Connect. The app shows the service's UUID and the characteristic inside it. Tap the characteristic → Write a value (e.g. 01) → the on-board LED turns on. Write 00 → LED off.
No PIN, no pairing, no SPP. The phone reads the database, writes to a named slot, and the peripheral reacts. Welcome to BLE.
Worked Example · Walk through the minimal sketch 20 min
If you have a Nano 33 BLE
- Plug in via USB. In Boards Manager, install "Arduino Mbed OS Nano Boards" (auto-prompts if you select Nano 33 BLE).
- Install "ArduinoBLE" via Library Manager.
- Paste the §3 sketch and upload. Open Serial Monitor at 9600 baud; you should see "# Advertising as LED-Demo".
- On your phone: install nRF Connect (Android) or LightBlue (iOS). Scan; find "LED-Demo". Tap Connect.
- The app shows the service (long UUID). Tap to expand; you see the characteristic.
- Tap the up-arrow / write icon next to the characteristic. Write
01(hex). The on-board LED turns on. - Write
00. LED off.
If you don't have a BLE board
Read the sketch carefully and annotate every line in your notebook. The conceptual understanding is more important than the upload — the moment you get the hardware, you'll know what to do.
Step 2 — observe the disconnection behaviour
Close the phone app or move out of range. The Serial Monitor prints "# Disconnected". Reconnect → "# Connected: AA:BB:CC:DD:EE:FF" (the central's MAC). Multiple central connections are usually not supported on cheap BLE peripherals — one at a time.
Step 3 — try notifications (preview of tomorrow)
Modify the characteristic to also support BLENotify:
BLEByteCharacteristic ledChar("...", BLERead | BLEWrite | BLENotify);In your sketch, periodically call ledChar.writeValue(newValue). If the central has subscribed to notifications on this characteristic, it'll see the new value pushed without polling. Push notifications, but for sensor data. Tomorrow we use this for a brightness slider.
Step 4 — power efficiency check
BLE's main appeal is low power. A Nano 33 BLE advertising at the default 100 ms interval draws ~5 mA average. Connected, ~6–8 mA. Idle (with the radio off), < 1 mA. For comparison, the HC-05 always draws ~30 mA. For battery-powered projects (a wireless sensor logger, a wearable), BLE's 5–10× power saving is the headline reason to choose it.
Step 5 — a peek at the Battery Service (preview)
The standard BLE Battery Service uses 16-bit UUIDs 0x180F (service) and 0x2A19 (characteristic). Replace the custom UUIDs with the standard battery ones and any battery-monitor app will recognise your sketch as "a battery" — no custom app needed.
BLEService batteryService("180F");
BLEByteCharacteristic batteryLevel("2A19", BLERead | BLENotify);Standard services are how Apple's Heart-Rate sensors, fitness trackers and continuous glucose monitors all just work in the iPhone's Health app — they speak the standard UUIDs.
Basic 5 min
A custom UUID has five groups of hex digits: 8-4-4-4-12. Only the digits 0–9 and A–F are allowed. A phone will never find a broken UUID. Which of these four are broken, and why?
(a) 19B10000-E8F2-537E-4F6C-D104768A1214 (b) 19B10001-E8F2-537E-4F6C-D104768A1214 (c) 19B10002-E8F2-537E-4F6C-D104768A121 (d) 19B10003-E8F2-537E-4G6C-D104768A1214
Then write the corrected version of each broken one, using the next number in the pattern.
It works if each fixed UUID has groups of exactly 8, 4, 4, 4 and 12 hex digits.
Challenge 1 5 min
For each item, name whether it's a service, a characteristic, a peripheral, or a central:
- The Nano 33 BLE running our LED sketch
- My iPhone running the nRF Connect app
- "LED brightness" (a value 0–255)
- "Lighting" (a group containing "LED brightness" and "LED colour")
Challenge 2 5 min
Which combination of properties (BLERead, BLEWrite, BLENotify) would you use for: (a) reading current temperature, (b) setting a target setpoint, (c) live streaming a sensor value to the phone?
A remote sensor runs from a 1000 mAh battery pack. Use the current figures from the Worked Example, Step 4.
- (a) How many hours with an HC-05 drawing 30 mA all the time?
- (b) How many hours with a Nano 33 BLE advertising at 5 mA?
- (c) How many times longer does the BLE version last?
Ignore the Arduino's own current for now. Show the sum for each part.
It works if each answer has a unit and comes from battery mAh ÷ current mA.
BLE's default Maximum Transmission Unit (MTU) for one packet is 23 bytes (with 20 bytes of payload after headers). If you want to send a 100-byte image thumbnail, what are your options?
Challenge 3 · Fix the silent peripheral 10 min
A classmate changed LED-Demo. It compiles and uploads. The Serial Monitor shows nothing useful. The phone either cannot find the board, or finds it but cannot write. There are three mistakes.
#include <ArduinoBLE.h>
BLEService ledService("19B10000-E8F2-537E-4F6C-D104768A1214");
BLEByteCharacteristic ledChar("19B10001-E8F2-537E-4F6C-D104768A1214",
BLERead);
void setup() {
Serial.begin(9600);
pinMode(LED_BUILTIN, OUTPUT);
if (!BLE.begin()) {
Serial.println("# BLE init failed");
while (true) ;
}
BLE.setLocalName("LED-Demo");
BLE.setAdvertisedService(ledService);
BLE.addService(ledService);
ledService.addCharacteristic(ledChar);
ledChar.writeValue((byte)0);
}
void loop() {
BLEDevice central = BLE.central();
if (central) {
while (central.connected()) {
if (ledChar.written()) {
digitalWrite(LED_BUILTIN, ledChar.value() ? HIGH : LOW);
}
}
}
}For each mistake, give the line, what goes wrong on the phone, and the fixed line. Compare with the sketch in the New Concept section.
It works if the fixed sketch appears as "LED-Demo" in a scan, and writing 01 lights the LED.
Recap 5 min
BLE replaces Classic's "UART-over-air" with a structured database of services and characteristics. Your peripheral declares them; the central reads, writes, or subscribes. UUIDs name everything. iOS-compatible, low-power, slower throughput. The ArduinoBLE library gives you BLEService, BLEByteCharacteristic (and other types), BLE.advertise() and a callback-style loop. Tomorrow we wire a real characteristic — phone-controlled LED brightness, with notify — and start to feel why BLE is the modern standard.
- BLE (Bluetooth Low Energy)
- The post-2010 Bluetooth standard for low-power, attribute-based device communication. Completely separate radio protocol from Bluetooth Classic.
- GATT (Generic ATTribute Profile)
- The BLE data model: a database of attributes (services and characteristics) on the peripheral that the central reads/writes/subscribes to.
- Peripheral / Central
- BLE's replacement for the older "slave / master" terminology. The peripheral advertises and serves the GATT database; the central scans and connects.
- Service
- A named group of related characteristics. Identified by a UUID.
- Characteristic
- A named, typed value inside a service. Can be readable, writable, and/or notifiable. Identified by a UUID.
- UUID
- A 128-bit identifier (16-bit shortened form exists for standard services). Names services and characteristics uniquely across the world.
- Advertising
- The peripheral broadcasting its name and offered service UUIDs so centrals can find it during a scan. The BLE equivalent of "announcing yourself".
- Notify / Indicate
- Push mechanisms. Central subscribes; peripheral sends fresh values when they change. Notify is faster, no acknowledgement; Indicate is acknowledged.
- ArduinoBLE
- Arduino's official BLE library. Supports all BLE-capable boards (Nano 33 BLE, MKR WiFi 1010, Nano ESP32, UNO R4 WiFi).
Extra Mission 5 min
Part 1 — Design a BLE service for a home gadget
Pick a gadget at home that could talk to a phone over BLE. Ideas: a plant pot, a desk lamp, a pet water bowl, a bedroom thermometer. On paper, design its BLE service.
Your design must include:
- The service name and a base UUID you generated.
- At least three characteristics, each with a name, data type and derived UUID.
- The properties of each one:
BLERead,BLEWriteorBLENotify. - A note on which values the phone sets, and which ones the gadget pushes.
Part 2 — Make it
Build one writeable characteristic from your design on a BLE board (Nano 33 BLE, UNO R4 WiFi or Nano ESP32). Use your own UUIDs. Make a write of 01 / 00 switch an LED that stands in for your gadget's output. No BLE board yet? Write the full sketch and check it compiles for a Nano 33 BLE.
Bring back next class: your service design, your sketch, and a phone-app screenshot showing your service and its characteristic.