Quickstart with ESP-IDF
The kit is an ESP-IDF component. It needs ESP-IDF 5.1 or later and an ESP32 with Bluetooth LE.
Using an AI coding agent?
Fill these in, copy the prompt, and paste it into your agent (Claude Code, Cursor, Copilot) at the root of your firmware project. It follows the steps below and shows you the diff.
Add Device Configs Wi-Fi setup to this ESP-IDF project.
Device Configs lets this device's owner put it on Wi-Fi from the Device Configs phone app: they scan the QR label on the unit, the app connects to that unit over Bluetooth LE with an encrypted session (Espressif protocomm security 2, a setup code per unit), and sends the Wi-Fi network, a token and a label. The device then says whether it joined, or why not. The firmware side is the "deviceconfigs" ESP-IDF component.
Product:
- Product id: <your product id>
- Bluetooth name prefix: <YOUR-PREFIX> (each unit advertises <YOUR-PREFIX>-<serial>)
- The kit (component folder): <path to the deviceconfigs kit folder>
Steps:
1. Check the project uses ESP-IDF 5.1 or later on an ESP32 chip with Bluetooth LE. If it doesn't, stop and tell me.
2. Make the component available: in the top-level CMakeLists.txt, before include($ENV{IDF_PATH}/tools/cmake/project.cmake), add
list(APPEND EXTRA_COMPONENT_DIRS "<path to the deviceconfigs kit folder>")
or copy the folder into the project's components/ folder. Add deviceconfigs to PRIV_REQUIRES of the component that holds app_main.
3. Add these to sdkconfig.defaults, keeping what is there (use NimBLE unless the project already uses Bluedroid):
CONFIG_BT_ENABLED=y
CONFIG_BT_NIMBLE_ENABLED=y
CONFIG_ESP_PROTOCOMM_SUPPORT_SECURITY_VERSION_0=n
CONFIG_ESP_PROTOCOMM_SUPPORT_SECURITY_VERSION_1=n
CONFIG_ESP_PROTOCOMM_SUPPORT_SECURITY_VERSION_2=y
Then delete sdkconfig (or set them in idf.py menuconfig) so they take effect.
4. Add a partition named dc_factory (type data, subtype nvs, size 0x3000) to the partition table. Keep every existing partition and keep the offsets valid; if the project uses a built-in table, switch to a custom partitions.csv (CONFIG_PARTITION_TABLE_CUSTOM=y).
5. In app_main: initialise NVS (erase and retry on ESP_ERR_NVS_NO_FREE_PAGES or ESP_ERR_NVS_NEW_VERSION_FOUND), call esp_netif_init() and esp_event_loop_create_default(), then
#include "deviceconfigs.h"
dc_config_t config = { .name_prefix = "<YOUR-PREFIX>", .product = "<your product id>" };
ESP_ERROR_CHECK(dc_start(&config));
dc_start() brings Wi-Fi up and keeps the device connected, so remove the project's own Wi-Fi setup: esp_wifi_init, hard-coded SSIDs and passwords, wifi_provisioning, SmartConfig. Wait for the network with dc_wait_online(portMAX_DELAY) before network work. Only if the project must keep owning Wi-Fi, set .app_owns_wifi = true and keep its reconnect logic (never call esp_wifi_init twice).
6. Where the device talks to its server, read the token with dc_get_token() and send it as the device's credential (for example an Authorization header). dc_get_label() is the name the owner gave the device.
7. If the device has an LED or a button: register a handler for DC_EVENT (DC_EVENT_SETUP_OPEN, DC_EVENT_WIFI_REJECTED, DC_EVENT_SETUP_DONE, DC_EVENT_ONLINE, DC_EVENT_OFFLINE) to show setup state, call dc_setup_open() on a press to re-run setup, and dc_factory_reset() on a long press.
8. Build with idf.py build and fix any errors.
Rules:
- Never log or print the Wi-Fi password, the token or a setup code.
- Don't add hard-coded Wi-Fi credentials or any shared default password.
- Don't change code unrelated to Wi-Fi setup.
- When you're done, show me the diff, where the dc_factory partition is (its offset), and the command that gives a unit its identity:
python <Device Configs firmware folder>/tools/dc_factory.py --product <your product id> --partitions partitions.csv --port <serial port>
Reference: https://deviceconfigs.com/docs/quickstart/esp-idf/ (every docs page as plain text: https://deviceconfigs.com/llms-full.txt)By hand
Section titled “By hand”-
Add the component. Copy
firmware/components/deviceconfigsinto your project’scomponents/folder, or point to it from your top-levelCMakeLists.txt:set(EXTRA_COMPONENT_DIRS path/to/firmware/components) -
Turn on Bluetooth LE and security 2. Add these to
sdkconfig.defaults:CONFIG_BT_ENABLED=yCONFIG_BT_NIMBLE_ENABLED=yCONFIG_ESP_PROTOCOMM_SUPPORT_SECURITY_VERSION_0=nCONFIG_ESP_PROTOCOMM_SUPPORT_SECURITY_VERSION_1=nCONFIG_ESP_PROTOCOMM_SUPPORT_SECURITY_VERSION_2=y -
Make room for the unit’s identity. Add a 12 KB
dc_factorypartition to your partition table:# Name, Type, SubType, Offset, Sizenvs, data, nvs, 0x9000, 0x6000,phy_init, data, phy, 0xf000, 0x1000,dc_factory, data, nvs, 0x10000, 0x3000,factory, app, factory, 0x20000, 0x1E0000, -
Start it. After the usual NVS, network and event loop setup:
#include "deviceconfigs.h"dc_config_t config = { .name_prefix = "ACME-AIR", .product = "airmon-2" };ESP_ERROR_CHECK(dc_start(&config));dc_wait_online(portMAX_DELAY);name_prefixis your product’s Bluetooth prefix; the unit advertisesACME-AIR-<serial>. The kit brings Wi-Fi up, opens setup when the device has no network yet, and keeps it online after. -
Label a unit and flash. See Label each unit, then
idf.py flash monitor.
After setup
Section titled “After setup”char token[1025];size_t len = sizeof(token);if (dc_get_token(token, &len) == ESP_OK) { // send it to your service: it says which product and unit this is}char label[33];dc_get_label(label, sizeof(label)); // what the owner named it, e.g. "Kitchen"The kit posts events on the default event loop (DC_EVENT): setup open, token stored, settings
refused (wrong password or network not found), setup done, online, offline.
Options
Section titled “Options”Field in dc_config_t |
Default | What it does |
|---|---|---|
setup_after_boot_s |
0 | Also open setup for this many seconds after each power-on on a device already set up. |
keep_bluetooth |
false | Keep Bluetooth’s memory after setup, for a product that uses Bluetooth itself. |
app_owns_wifi |
false | Your application brings Wi-Fi up and reconnects it. |
For a button: dc_setup_open() restarts into setup, and dc_factory_reset() forgets the network,
token and label. The setup code never changes.