BLE Provisioning для IoT-пристроїв через мобільні додатки
BLE Provisioning — передача Wi-Fi кредієнціалів та конфігурації IoT-пристрою через Bluetooth Low Energy. Це найкращий метод для зручного UX: не потребує перемикання Wi-Fi мереж на телефоні, надійніший за SmartConfig та не залежить від налаштувань маршрутизатора. Для ESP32, nRF52, чипів Nordic — бажаний вибір.
GATT-архітектура для Provisioning
Пристрій у режимі provisioning рекламує BLE-сервіс. Мобільний додаток підключається як GATT-клієнт та записує дані у характеристики сервісу. Стандартна схема для ESP-IDF:
-
Service UUID:
021a9004-0382-4aba-aa36-ec4d15d65e0e(Espressif Provisioning) - Характеристика конфігурації: write (SSID, password, auth mode)
- Характеристика статусу: notify (результат підключення пристрою до мережі)
Після запису кредієнціалів пристрій намагається підключитися до Wi-Fi та повідомляє телефон через notify-характеристику про успіх або помилку.
Android BLE API: Що йде не так
BLE на Android — джерело болю. Різні виробники реалізують стек по-різному. BluetoothGatt.writeCharacteristic() може повернути true при виклику, але onCharacteristicWrite приходить зі статусом GATT_ERROR (133) — найчастіша необясна помилка.
Правильний паттерн — черга команд. BLE не підтримує паралельні GATT-операції:
class BleCommandQueue {
private val queue: LinkedList<() -> Unit> = LinkedList()
private var isExecuting = false
fun enqueue(command: () -> Unit) {
queue.add(command)
if (!isExecuting) executeNext()
}
fun onCommandComplete() {
isExecuting = false
executeNext()
}
private fun executeNext() {
if (queue.isEmpty()) return
isExecuting = true
queue.poll()?.invoke()
}
}
Кожен writeCharacteristic, readCharacteristic, setNotification — через чергу. Callback onCharacteristicWrite → queue.onCommandComplete(). Без цього паралельні операції зависають GATT-стек та розривають з'єднання.
MTU negotiation. За замовчуванням MTU = 23 байти (20 байт payload). Довгі кредієнціали можуть не влізти. Запросіть розширення відразу після підключення:
override fun onConnectionStateChange(gatt: BluetoothGatt, status: Int, newState: Int) {
if (newState == BluetoothProfile.STATE_CONNECTED) {
gatt.requestMtu(512) // до 517 байт
}
}
override fun onMtuChanged(gatt: BluetoothGatt, mtu: Int, status: Int) {
// Тепер можна писати дані розміром до mtu - 3 байти
startProvisioning()
}
Espressif provisioning-android SDK
Espressif надає готовий SDK, що приховує низькорівневу роботу з GATT:
val device = ESPProvisionManager.getInstance(context)
.createESPDevice(
ESPConstants.TransportType.TRANSPORT_BLE,
ESPConstants.SecurityType.SECURITY_1
)
device.connectBLEDevice(scanResult) { connected ->
if (!connected) return@connectBLEDevice
device.scanNetworks { networks, error ->
// networks — список Wi-Fi мереж, які бачить пристрій
}
}
// Після вибору мережі користувачем
device.provision(selectedSsid, password) { status ->
when (status) {
ProvisioningStatus.SUCCESS -> navigateToSuccess()
ProvisioningStatus.FAILURE -> showError(status.toString())
}
}
SDK реалізує шифрування каналу через SRP6a (Security 2) або Curve25519+AES (Security 1). Кредієнціали ніколи не передаються у відкритому вигляді.
iOS: CoreBluetooth + ESPProvision
На iOS — пакет ESPProvision Swift від Espressif або нативний CoreBluetooth для користувацьких протоколів.
import ESPProvision
ESPProvisionManager.shared.searchESPDevices(devicePrefix: "PROV_", transport: .ble, security: .secure) { devices, error in
guard let device = devices?.first else { return }
device.connect(delegate: self) { status in
if case .connected = status {
device.provision(ssid: selectedSSID, passPhrase: password) { status in
// обробити результат
}
}
}
}
На iOS немає фрагментації GATT-стека — CoreBluetooth працює однаково на всіх пристроях. Але є обмеження: сканування BLE у фоновому режимі працює лише для пристроїв із відомими Service UUID, попередньо визначеними у Info.plist.
Типові помилки provisioning UX
Немає зворотного зв'язку про прогрес. Пристрій підключається до Wi-Fi 5–15 секунд. Без індикатора прогресу користувачі думають, що додаток зависає, та натискають назад.
Не обробляють помилку неправильного пароля. Пристрій повертає статус AUTH_ERROR через notify-характеристику. Показуйте «Неправильний пароль Wi-Fi» — не «Помилка підключення».
Не виходять з режиму provisioning після успіху. Пристрій припиняє рекламування BLE-сервісів після підключення до Wi-Fi — нормальна поведінка. Додаток має закрити BLE-з'єднання та перейти до наступного кроку.
Реалізація BLE Provisioning на базі Espressif SDK: 2–3 тижні. Користувацький GATT-протокол з шифруванням для іншого чипу: 4–6 тижнів.







