esp_provisioning_ble 1.1.0 copy "esp_provisioning_ble: ^1.1.0" to clipboard
esp_provisioning_ble: ^1.1.0 copied to clipboard

A Flutter plugin for Wi-Fi provisioning of ESP32 modules over Bluetooth Low Energy (BLE).

esp_provisioning_ble #

pub package pub points likes CI license: MIT Contributor Covenant

A Flutter plugin that simplifies provisioning ESP32 modules over Bluetooth Low Energy (BLE).

It is transport-agnostic: you implement a thin ProvTransport with the BLE package of your choice (the example uses flutter_blue_plus), and the plugin handles the secure handshake, Wi-Fi scanning, credential delivery and status reporting on top of Espressif's protocomm protocol.

Features #

  • Secure session establishment with Security 1 (Curve25519 key exchange + AES-CTR) using a Proof-of-Possession (PoP).
  • Scan the Wi-Fi networks visible to the device, with SSID, RSSI, BSSID and whether the network is secured.
  • Send Wi-Fi credentials and apply them, optionally targeting a specific access point by BSSID.
  • Query the provisioning status, including the assigned device IP and the failure reason.
  • Exchange arbitrary custom data over the encrypted session.
  • Transport-agnostic: bring your own BLE stack by implementing ProvTransport.

Supported platforms #

Platform Support
Android ✅
iOS ✅

The plugin logic is pure Dart on top of your ProvTransport, so the platform reach is ultimately bounded by the BLE package you plug in.

Table of contents #

Installation #

Add the package with:

flutter pub add esp_provisioning_ble

Or add it to your pubspec.yaml manually and run flutter pub get:

dependencies:
  esp_provisioning_ble: ^1.0.0

Getting started #

The package exposes an abstract ProvTransport class that you implement with your preferred Bluetooth package. The example provides a TransportBLE implementation built on flutter_blue_plus. A legacy example using flutter_ble_lib_ios_15 is available in example_legacy.

The snippets below are plain Dart, so they fit any state-management approach.

Create an EspProv instance #

EspProv takes a transport (any ProvTransport) and a security (any ProvSecurity; the package ships Security1, which carries the Proof-of-Possession).

final prov = EspProv(
  transport: TransportBLE(peripheral),
  security: Security1(pop: pop),
);

Establish a session #

Call establishSession to run the secure handshake. It returns an EstablishSessionStatus:

  • connected: the session was established successfully.
  • disconnected: the connection to the device dropped.
  • keymismatch: the Proof-of-Possession (PoP) is incorrect.
final status = await prov.establishSession();
switch (status) {
  case EstablishSessionStatus.connected:
    // Ready to scan and send Wi-Fi credentials.
    break;
  case EstablishSessionStatus.disconnected:
    // Handle the dropped connection.
    break;
  case EstablishSessionStatus.keymismatch:
    // Wrong Proof-of-Possession.
    break;
}

Once the session is established establishSession is idempotent, so you can call it defensively without repeating the handshake.

Scan for Wi-Fi networks #

startScanWiFi returns a list of WifiAP objects, each with:

  • String ssid
  • int rssi
  • bool active
  • bool private: whether the network is secured.
  • String? bssid: MAC-style address (aa:bb:cc:dd:ee:ff) when known.
final networks = await prov.startScanWiFi();
for (final ap in networks) {
  print('${ap.ssid} (${ap.rssi} dBm)');
}

Send and apply the Wi-Fi config #

sendWifiConfig delivers the credentials and returns whether the device accepted them; applyWifiConfig then tells the device to connect. Pass an optional bssid to target a specific access point.

await prov.sendWifiConfig(ssid: ssid, password: password);
// Or target a specific access point:
// await prov.sendWifiConfig(ssid: ssid, password: password, bssid: 'aa:bb:cc:dd:ee:ff');
await prov.applyWifiConfig();

Check the provisioning status #

getStatus returns a ConnectionStatus with:

  • WifiConnectionState state: Connecting, Connected, Disconnected or ConnectionFailed.
  • String? deviceIp: the device IP once connected.
  • WifiConnectFailedReason? failedReason: AuthError (wrong password) or NetworkNotFound (wrong SSID), set when the state is ConnectionFailed.
final status = await prov.getStatus();
switch (status.state) {
  case WifiConnectionState.Connecting:
    // Still connecting.
    break;
  case WifiConnectionState.Connected:
    print('Device IP: ${status.deviceIp}');
    break;
  case WifiConnectionState.Disconnected:
    // Not connected.
    break;
  case WifiConnectionState.ConnectionFailed:
    // Inspect status.failedReason.
    break;
}

Send and receive custom data #

sendReceiveCustomData sends a payload over the encrypted session and returns the device's response.

final answerBytes = await prov.sendReceiveCustomData(
  Uint8List.fromList(utf8.encode(message)),
);
final answer = utf8.decode(answerBytes);

See the example (flutter_blue_plus) or example_legacy (flutter_ble_lib_ios_15) for complete applications.

Security #

This package currently implements Security 1 through the Security1 class: a Curve25519 key exchange with AES-CTR encryption, authenticated with a Proof-of-Possession (PoP). This maps to Espressif's protocomm_security1.

  • Security 0 (no security) is in progress.
  • Security 2 (SRP6a key exchange + AES-GCM) is not yet available.

To use a different scheme, provide your own ProvSecurity implementation.

To report a security vulnerability, please follow the security policy.

Protocol communication overview #

The protocomm component from ESP-IDF manages secure sessions and provides the framework for multiple transports. Applications can also use the protocomm layer directly for application-specific extensions.

It defines three security schemes:

  • protocomm_security0: no security.
  • protocomm_security1: Curve25519 key exchange + AES-CTR (implemented here as Security1).
  • protocomm_security2: SRP6a key exchange + AES-GCM.

Proof-of-Possession is supported with security 1; salt and verifier with security 2. Protocomm uses protobuf for session establishment and provides the framework for transports such as Bluetooth LE, Wi-Fi (SoftAP + HTTPD) and console.

For security 1 and security 2 the client still needs to establish a session by performing the two-way handshake. See Unified Provisioning for more details on the handshake logic.

Comparison #

esp_provisioning_ble is the Bluetooth LE counterpart to esp_provisioning_softap: this package provisions the device over Bluetooth LE, while esp_provisioning_softap provisions it over Wi-Fi SoftAP. Both build on the protocomm security schemes and protobuf.

Changelog #

See the CHANGELOG for the release history.

Contributing #

Contributions are welcome. Please read the contributing guide to get set up and learn the workflow. This project follows a Code of Conduct, and security issues are handled through our security policy.

Credits #

License #

Released under the MIT License.