Developing a Mobile App for IoT Device Provisioning
Provisioning is the first thing users face unpacking a new IoT device. If it takes more than 2 minutes or requires instructions, conversion to active users drops. Development task: turn technical process (WiFi credentials, platform registration, initial config) into linear 3–4 step UX.
What IoT Provisioning Is Technically
Fresh device doesn't know WiFi password and isn't tied to user account. Must pass three things:
- Network credentials (SSID + password)
- Owner identifier (user_id or platform token)
- Initial config (timezone, device name, server endpoint)
Technically via:
- BLE (Bluetooth Low Energy)—device in provisioning mode raises GATT-server, mobile app writes to characteristics
- Wi-Fi Soft AP—device raises its own access point, phone connects and transmits via HTTP
- SoftAP + BLE combo—BLE for discovery and initial exchange, WiFi for certificate transmission
- QR-code—basic credentials burned in QR at factory, phone scans and supplements with account data
Choice depends on device hardware. ESP32 supports all variants. nRF52 only BLE. RTL8710 only WiFi.
ESP-IDF Provisioning: Practice
Espressif provides ready esp_prov component on firmware side and official mobile SDKs:
- Android:
com.espressif:provisioning-android - iOS:
ESPProvision(Swift Package Manager)
BLE flow with Android SDK:
ESPProvisionManager.getInstance(context).searchBleEspDevices("PROV_") { devices, error ->
// devices—found devices with PROV_ prefix
val device = devices?.firstOrNull() ?: return@searchBleEspDevices
device.connectBLEDevice(bleScanResult) { session ->
device.provision(ssid, passphrase) { status ->
when (status) {
ProvisioningStatus.SUCCESS -> onProvisioned()
ProvisioningStatus.FAILURE -> onFailed(status.error)
ProvisioningStatus.CONFIG_SENT -> updateProgress(50)
}
}
}
}
Under the hood SDK establishes encrypted session via Session Security (protocol sec1—Curve25519 + AES-CTR), passes WiFi credentials via protocomm layer. Protobuf protocol—binary, compact.
Typical issue: searchBleEspDevices doesn't find device despite being on. Usually—device already provisioned and doesn't advertise BLE services. Need "factory reset" button in instructions.
WiFi Provisioning via Soft AP
Device raises PROV_XXXXXX point. Phone must connect—non-trivial on Android because system may decide this network has "no internet" and switch back to cellular.
On Android 10+ explicit connection via WifiNetworkSpecifier:
val specifier = WifiNetworkSpecifier.Builder()
.setSsid("PROV_${deviceSuffix}")
.setWpa2Passphrase(apPassword)
.build()
val request = NetworkRequest.Builder()
.addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
.setNetworkSpecifier(specifier)
.build()
connectivityManager.requestNetwork(request, object : ConnectivityManager.NetworkCallback() {
override fun onAvailable(network: Network) {
// All HTTP requests to device must use this network object
val client = OkHttpClient.Builder()
.socketFactory(network.socketFactory)
.build()
sendProvisioningData(client)
}
})
Without explicit network.socketFactory in OkHttp, requests go via default interface (cellular), not device AP—connection fails.
UX Scenario and Error Handling
Bad UX: endless spinner. Good: progress with specific steps—"Finding device," "Connecting," "Sending network," "Device joining WiFi," "Registering." Each step has timeout. If device doesn't join WiFi in 30 seconds—suggest checking password.
Most common user error: enters 5 GHz network password, device only supports 2.4 GHz. Detect beforehand via WifiManager.scanResults checking frequency. Android 30+ needs ACCESS_FINE_LOCATION or NEARBY_WIFI_DEVICES permissions.
Timeline
Provisioning via one channel (BLE or Soft AP) using Espressif SDK: 2–4 weeks. Custom protocol, multi-vendor support, full flow with platform registration: 5–8 weeks. Cost calculated individually.







