A PlatformIO library for securely provisioning WiFi credentials to Raspberry Pi Pico W boards with over BLE (Bluetooth Low Energy).
TODO: publish to PlatformIO registry
This library provides a simple and secure way to provision WiFi credentials to a Raspberry Pi Pico W device using Bluetooth Low Energy (BLE) with secure pairing. It's designed specifically for use with the arduino-pico core and requires the Pico W variant with onboard WiFi and Bluetooth capabilities.
- Secure BLE Provisioning: Receive WiFi SSID and password securely over a paired BLE connection.
- Secure Pairing: Utilizes BLESecure for secure bonding and encryption during provisioning.
- Credential Storage: Saves WiFi network configurations to LittleFS flash memory.
- Automatic Connection: Attempts to connect to stored networks on startup.
- Status Callbacks: Provides callbacks to monitor the provisioning process, WiFi status, and BLE connection state.
- Hardware: Raspberry Pi Pico W
- Framework: Arduino
- Core: earlephilhower/arduino-pico
- PlatformIO Platform: maxgerhardt/platform-raspberrypi
- arduino-pico core with BLE and WiFi support
- pico-ble-secure library for secure BLE connections
- pico-ble-notify library for BLE notifications
- ArduinoJson library for configuration storage
- Add the library to your
platformio.ini
:
lib_deps =
; If published to PlatformIO registry:
; pico-wifi-provisioning
; Otherwise, use repo URL:
https://github.com/IoT-gamer/pico-wifi-provisioning
Check out the BasicProvisioning example for a complete implementation including:
- LED status indication
- Slow blink: connected to BLE client/central
- Fast blink: connecting to WiFi
- Solid on: connected to WiFi
- BOOTSEL button handling for network reset
- BLE pairing with encryption and feedback
- WiFi connection management
- Onboard LED may stop blinking for a fews seconds after sending wifi credentials due to some internal limitations (use LCD/OLED for better feedback)
- Pairing on android may show duplicate requests (click both)
- Pairing request will timout quickly
Here's a basic example of how to use the library:
#include <Arduino.h>
#include <WiFi.h>
#include <BTstackLib.h>
#include <BLESecure.h>
#include <BLENotify.h>
#include "PicoWiFiProvisioning.h"
// LED for status indication
const int LED_PIN = LED_BUILTIN;
// Callback for WiFi status changes
void onWiFiStatus(wl_status_t status) {
switch (status) {
case WL_CONNECTED:
Serial.println("WiFi connected!");
Serial.print("IP address: "); Serial.println(WiFi.localIP());
digitalWrite(LED_PIN, HIGH); // LED on when connected
break;
case WL_DISCONNECTED:
case WL_CONNECTION_LOST:
Serial.println("WiFi disconnected");
digitalWrite(LED_PIN, LOW); // LED off when disconnected
break;
default:
break;
}
}
void setup() {
Serial.begin(115200);
delay(1000);
// Initialize LED
pinMode(LED_PIN, OUTPUT);
digitalWrite(LED_PIN, LOW);
// Set callback for WiFi status changes
PicoWiFiProvisioning.setWiFiStatusCallback(onWiFiStatus);
// Initialize WiFi provisioning (device name, security level, IO capability)
if (PicoWiFiProvisioning.begin("PicoW", SECURITY_MEDIUM, IO_CAPABILITY_NO_INPUT_NO_OUTPUT)) {
// Try to connect to any stored WiFi networks
Serial.println("Attempting to connect to stored networks...");
if (PicoWiFiProvisioning.connectToStoredNetworks()) {
Serial.println("Connected to a stored network!");
} else {
Serial.println("No stored networks or failed to connect");
Serial.println("Waiting for BLE connection to provision WiFi...");
}
}
}
void loop() {
// Process WiFi and BLE events
PicoWiFiProvisioning.loop();
// Your application code here
delay(10);
}
- Include the
PicoWiFiProvisioning.h
header. - Define and implement callback functions for
onWiFiStatus
,onProvisionStatus
, andhandleBleConnectionChange
. - Set the callback functions using
PicoWiFiProvisioning.set...Callback()
. - Initialize the provisioning service using
PicoWiFiProvisioning.begin()
, providing a device name, security level, and IO capability. - Optionally, call
PicoWiFiProvisioning.connectToStoredNetworks()
insetup()
to automatically connect if credentials exist. - Call
PicoWiFiProvisioning.loop()
in your mainloop()
function to process BLE and WiFi events.
The library creates a custom BLE service with the following characteristics:
Characteristic | UUID | Properties | Description |
---|---|---|---|
SSID | 5a67d678-6361-4f32-8396-54c6926c8fa2 | Read, Write | WiFi SSID |
Password | 5a67d678-6361-4f32-8396-54c6926c8fa3 | Write | WiFi Password |
Command | 5a67d678-6361-4f32-8396-54c6926c8fa4 | Write | Control commands |
Pairing Status | 5a67d678-6361-4f32-8396-54c6926c8fa5 | Read, Notify | BLE pairing status |
You can customize the following parameters in PicoWiFiProvisioning.h
:
MAX_WIFI_NETWORKS
: Maximum number of WiFi networks that can be stored (default: 5)MAX_SSID_LENGTH
: Maximum length for SSID (default: 32)MAX_PASSWORD_LENGTH
: Maximum length for password (default: 64)WIFI_CONFIG_FILE
: File path for storing WiFi credentials (default: "/wifi_config.json")
When calling PicoWiFiProvisioning.begin()
, you can configure:
deviceName:
The name broadcasted via BLE.securityLevel:
The BLE security levelioCapability:
The input/output capabilities of your device for pairing (see IO Capabilities)
- The library uses LittleFS to store up to
MAX_WIFI_NETWORKS
(default 5) WiFi network configurations in a file named/wifi_config.json
. Each entry includes the SSID, password, and an enabled flag. saveNetwork(ssid, password)
: Saves or updates a network in flash.loadNetworksFromFlash()
: Loads networks from the configuration file on startup.clearNetworks()
: Erases all stored networks and the configuration file.
You can set various callbacks to react to different events:
setStatusCallback(void (*callback)(PicoWiFiProvisioningStatus status))
: Called when the internal provisioning status changes.setWiFiStatusCallback(void (*callback)(wl_status_t status))
: Called when the underlying WiFi connection status changes.setBLEConnectionStateCallback(void (*callback)(bool isConnected))
: Called when a BLE device connects or disconnects.setPasskeyDisplayCallback(void (*callback)(uint32_t passkey))
: (Optional) Called when a passkey needs to be displayed during pairing.setNumericComparisonCallback(void (*callback)(uint32_t passkey, BLEDevice *device))
: (Optional) Called during numeric comparison pairing. You'll need to call acceptNumericComparison(bool accept) in response.
The following commands can be sent to the Command characteristic:
Command | Value | Description |
---|---|---|
CMD_SAVE_NETWORK | 0x01 | Save the current SSID and password as a network |
CMD_CONNECT | 0x02 | Connect to the specified network or stored networks |
CMD_CLEAR_NETWORKS | 0x03 | Clear all stored networks |
CMD_GET_STATUS | 0x04 | Request the current status (Partially implemented) |
CMD_DISCONNECT | 0x05 | Disconnect from the current WiFi network |
CMD_START_SCAN | 0x06 | Start a WiFi network scan (not fully implemented) |
CMD_GET_SCAN_RESULTS | 0x07 | Get WiFi scan results (not fully implemented) |
The library supports different security levels through the BLESecure library:
SECURITY_MEDIUM
: Authentication, encryptionSECURITY_HIGH
: Authentication, MITM protection, encryptionSECURITY_HIGH_SC
: Authentication, MITM protection, encryption, secure connections
The library supports different IO capabilities through the BLESecure library:
IO_CAPABILITY_DISPLAY_ONLY
: Device can display a 6-digit passkey- e.g., OLED screen, no buttons
IO_CAPABILITY_DISPLAY_YES_NO
: Device can display and confirm a passkey- e.g., OLED screen + confirmation button
- offers the best security through numeric comparison (MITM protection)
IO_CAPABILITY_KEYBOARD_ONLY
: Device has a keyboard for passkey entryIO_CAPABILITY_NO_INPUT_NO_OUTPUT
: Device has no input or output capabilities- This falls back to "Just Works" pairing
IO_CAPABILITY_KEYBOARD_DISPLAY
: Device has both keyboard and display
A simple Flutter app is available to demonstrate the provisioning process. It allows you to connect to the Pico W device and send WiFi credentials securely.
- BLE not advertising: Ensure the arduino-pico core is configured with BLE support in your platformio.ini.
- WiFi connection failures: Check the SSID and password being sent. Ensure they're valid and within length limits.
- Flash storage issues: Make sure LittleFS is properly initialized and has enough space.
- BLE pairing issues: Any time bonding information is lost on either the peripheral or central device, re-encryption will fail, and the bond must be reestablished.
- For example, if the peripheral device (Pico W) flash is erased, the bond information will also be lost. And the central device (e.g., a mobile phone) will need to unpair (in settings/Bluetooth) and re-pair with the Pico W.
- example error message:
Re-encryption failed, status: 61 BLE pairing failed Pairing failed, status: 61, reason: 0
This library is released under the MIT License. See the LICENSE file for details.
Contributions are welcome! Please feel free to submit a Pull Request.