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 | ~£20. 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.
Try It Yourself · Conceptual exercises 15 min
Go to uuidgen.com (or any UUID-generator site, or run uuidgen in a terminal). Generate a 128-bit UUID. Paste it into your notebook as your "personal project base UUID". By convention you'd then derive characteristic UUIDs by changing the last 4 hex digits.
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")
Reveal
- Nano = peripheral.
- iPhone + app = central.
- LED brightness = characteristic.
- Lighting = service.
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?
Reveal
- (a) Read-only sensor →
BLERead. Optionally alsoBLENotifyif you want push updates without the central polling. - (b) Setpoint →
BLEWrite(central writes to set it). Often alsoBLEReadso the central can re-read its current value. - (c) Streaming →
BLERead | BLENotify. Central subscribes once; peripheral pushes on every change.
Apple blocks third-party Bluetooth Classic SPP devices. Why does this push the entire iOS ecosystem to BLE?
Reveal
Apple's MFI (Made for iPhone) certification programme is expensive and slow. Cheap HC-05 modules never go through it, so iOS won't let third-party apps open an SPP connection. BLE doesn't need MFI — Apple opened the CoreBluetooth API to any app from day 1. So all consumer wearables, sensors, fitness gear, plant monitors, etc., default to BLE — it's the only path that works on both iOS and Android.
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?
Reveal
Two paths: (1) negotiate a larger MTU (BLE 4.2+ supports up to 251 bytes; both sides have to agree); (2) chunk the 100 bytes into five 20-byte writes and reassemble on the receiver. Both are common; the chunking approach works on any BLE stack. This is one of the reasons BLE is slower than Classic — small packets, lots of overhead.
Mini-Challenge · Design a service 10 min
Design (on paper) a BLE service for a smart plant monitor with: current soil moisture, current temperature, water-now button.
- Name the service. Generate or invent a base UUID.
- List the characteristics: name, properties (read/write/notify), data type, derived UUID.
- Note which characteristics push updates (BLENotify) vs are polled.
Reveal one good design
Service: PlantMonitor Base UUID: 6E400000-B5A3-F393-E0A9-E50E24DCCA9E - moisturePct uint8 read | notify ...0001... - tempC float read | notify ...0002... - waterNow bool write ...0003... - batteryPct uint8 read | notify ...0004...
The Nano 33 BLE Sense already has temperature on-board; the soil probe is analog → A0; water-now triggers a pump via a relay. The whole spec fits on a notebook page and tells future-you exactly what every BLE characteristic does.
Tomorrow we implement one such characteristic — phone-controlled LED brightness — with read, write, and notify all wired up.
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).
Homework 5 min
- If you have a Nano 33 BLE, upload the §3 sketch and connect from your phone using nRF Connect / LightBlue. Write
01/00to toggle the LED. - If you don't have BLE hardware, install nRF Connect on your phone anyway. Scan the room and screenshot what BLE devices are nearby — earphones, smartwatches, IoT sensors. Note which ones use standard service UUIDs (Heart Rate, Battery) and which use custom ones.
- Generate your personal base UUID (uuidgen, online, or via terminal).
- Read ahead to ARD-L03-27 (A BLE Characteristic). Tomorrow we build a phone-controlled LED brightness slider — read, write, notify, all in one.
Bring back next class:
- Your base UUID written in your notebook.
- Working LED-Demo (if you have BLE hardware) OR your BLE scan screenshot.