This document describes how to port the Bluetooth-assisted Wi-Fi provisioning feature of the IoT Platform software development kit (SDK) to a new combo device. A combo device supports both Wi-Fi and Bluetooth Low Energy (BLE). This document uses a sample hardware development board to guide you through the porting, application development, and debugging processes.
The main process for porting the Bluetooth-assisted provisioning feature to a combo device is as follows.
Select a hardware model
Device manufacturers, module vendors, and chip vendors can select a suitable hardware platform with a combo chip or module based on their requirements.
Create a product in the console
After you select the device hardware, you must create a project and a product in the IoT Platform console. Then, you can add a test device and configure the application parameters.
Obtain the SDK
The IoT Platform SDK that you download contains the required provisioning module.
Port the Bluetooth-assisted provisioning HAL
Port the device-side IoT Platform SDK to your hardware platform. The SDK includes the Wi-Fi provisioning module and the Bluetooth Breeze module. Then, you can compile and debug the code.
NoteFor more information about developing the Bluetooth-assisted provisioning feature, see Bluetooth-assisted provisioning development. If you select a hardware platform that is certified by IoT Platform, you do not need to port the code again. For more information about how to select a hardware platform, see Select a certified module or chip.
Generate the device firmware
Based on the full-featured sample application, you can compile the device firmware for Bluetooth-assisted provisioning that can run on your hardware platform.
Verify the Bluetooth-assisted provisioning feature
You can use the Cloud Intelligence App provided by IoT Platform to verify the Bluetooth-assisted provisioning feature.
1. Prepare the hardware device
Select a suitable hardware device for your product. For more information about available resources, see Bluetooth-assisted provisioning development. This document uses the BK7231U combo chip, which supports both Wi-Fi and BLE, as an example.
2. Develop the product in the console
- Log on to the IoT Platform console.
Select an existing project or create a new one. For more information, see Create a project.
Create a product and define its features. For more information, see Create a product and define features.
NoteWhen you create the product, set the Network Connection Method parameter to WiFi. A Wi-Fi + BLE combo device that supports Bluetooth-assisted provisioning is a Wi-Fi device that can directly connect to the network. The BLE feature is used to assist the Wi-Fi module with provisioning.
Add a test device. For more information, see Add a device.
Select an application version and configure the application feature parameters. For more information, see Human-computer interaction overview.
When you configure the Provisioning Guide, set the Default Provisioning Method or Alternative Provisioning Method parameter to Bluetooth-assisted provisioning. You must also configure the corresponding provisioning text. For more information, see Configure the provisioning guide.
3. Get the SDK
When you obtain the IoT Platform SDK, we recommend that you use the latest version of the SDK with AliOS Things for device-side development. To download the SDK, see Obtain the SDK.
4. Port the Bluetooth-assisted provisioning HAL
Bluetooth-assisted provisioning uses both Wi-Fi and BLE communication capabilities. Therefore, porting this feature module involves porting both the Wi-Fi provisioning module and the Bluetooth Breeze module.
Port the BLE protocol stack.
The BLE communication for Bluetooth-assisted provisioning uses the IoT Platform Bluetooth Breeze protocol. This protocol is defined and implemented through upper-layer communication profile rules. After you port the Bluetooth protocol stack Hardware Abstraction Layer (HAL), the protocol can run on Bluetooth protocol stacks from different vendors. For information about the HAL interfaces to port, see Bluetooth-assisted provisioning development.
This section uses the BK7231U chip as an example. In the SDK code package with AliOS Things, the Bluetooth Breeze module and its HAL are located in the /Living_SDK/framework/bluetooth/breeze/ directory. The contents of this directory are described in the following table.
Content
Description
breeze.mk
The makefile for the Bluetooth Breeze module.
core/
The protocol implementation of the Bluetooth Breeze module. You do not need to understand its implementation details when porting.
include/
The header files for the protocol implementation of the Bluetooth Breeze module. You do not need to understand its implementation details when porting.
hal/
The HAL implementation of the Bluetooth Breeze module. This is the key part to implement when porting. The default implementation here uses the open source BLE protocol stack provided by AliOS Things.
api/
The user programming interfaces for upper-layer application development. You do not need to understand its details when porting.
Porting this part mainly involves implementing breeze.mk for compilation control and the HAL for the chip's Bluetooth protocol stack.
Set up breeze.mk.
NoteThe BK7231U example is based on the GCC cross-compilation toolchain and uses the
.mkmakefile compilation method. If you use other compilation tools, you must port the implementation of files such as.mkto your compilation tool environment.NAME := breeze $(NAME)_MBINS_TYPE := kernel $(NAME)_VERSION := 1.0.0 $(NAME)_SUMMARY := breeze provides secure BLE connection to Alibaba IoT cloud and services. $(NAME)_SOURCES += core/core.c $(NAME)_SOURCES += core/transport.c $(NAME)_SOURCES += core/ble_service.c $(NAME)_SOURCES += core/sha256.c $(NAME)_SOURCES += core/utils.c GLOBAL_INCLUDES += api include hal/include $(NAME)_COMPONENTS := chip_code # Breeze secure advertising feature, used to enhance the security of broadcast data. Not used in Bluetooth-assisted provisioning. secure_adv ?= 0 ifeq ($(secure_adv), 1) GLOBAL_DEFINES += CONFIG_AIS_SECURE_ADV endif # Whether the open source BLE protocol stack provided by AliOS Things has been ported and is in use. # If the vendor's driver already includes its own BLE protocol stack, this feature does not need to be selected. # BK7231U uses the vendor's own BLE protocol stack, so this is not enabled here. btstack ?= zephyr ifeq (zephyr, $(btstack)) $(NAME)_COMPONENTS += framework.bluetooth.breeze.hal.ble endif $(NAME)_SOURCES += api/breeze_export.c # Breeze secure authentication feature. Must be enabled for Bluetooth-assisted provisioning. bz_en_auth ?= 1 ifeq ($(bz_en_auth), 1) GLOBAL_DEFINES += EN_AUTH $(NAME)_SOURCES += core/auth.c endif # Breeze-assisted provisioning feature. Must be enabled for Bluetooth-assisted provisioning. bz_en_awss ?= 1 ifeq ($(bz_en_awss), 1) ifeq ($(bz_en_auth), 0) $(error awss need authentication, please set "bz_en_auth = 1") endif GLOBAL_DEFINES += EN_COMBO_NET GLOBAL_DEFINES += AWSS_REGION_ENABLE $(NAME)_SOURCES += core/extcmd.c $(NAME)_SOURCES += api/breeze_awss_export.c endifImplement the files in the hal directory.
The files to be ported in the hal directory are breeze_hal_ble.h, breeze_hal_os.h, and breeze_hal_sec.h. For the implementation of these three files, see the content in the /Living_SDK/platform/mcu/bk7231u/hal/breeze_hal/ directory of the SDK code package.
breeze_hal_ble.h
This is the Bluetooth protocol stack porting interface. It involves BLE advertising, connection, and GATT-related content. You must implement breeze_hal_ble.c.
/** * API to initialize ble stack. * @parma[in] ais_init Bluetooth stack init parmaters. * @return 0 on success, error code if failure. */ // Initialize the BLE protocol stack for your hardware platform. The BLE feature can be used only after the protocol stack is successfully initialized. // During the Bluetooth-assisted provisioning phase, first initialize the BLE protocol stack. Then, register the GATT Service based on the protocol stack. Communication can start only after this is complete. // The Breeze BLE communication channel is designed based on the GATT Profile. The Breeze GATT Service is implemented on the device side. // IoT Platform calls this AIS (Alibaba IoT Service). // This GATT Service is a set of Characteristics that support Read, Write, Notify, and Indicate operations. // The BLE protocol stack initialization function is passed a description of this GATT Primary Service. The BLE protocol stack initialization function needs to register this Service // (also known as the Attribute Table) with the BLE stack and associate the BLE link's connect and disconnect events with the elements in the passed Service description. // This method establishes the connection between the BLE stack and the AIS Service. // When a BLE connection is established or disconnected, the Breeze module is notified through the connect and disconnect callbacks registered during initialization. // At the same time, operations on the AIS Service by the peer device in the connected state are also notified to the Breeze module through Read, Write, and other callbacks. // The definition of the AIS Service is as follows for your reference during porting and code debugging. // Attribute Type |UUID |Properties |Permission // AIS Primary Service |0xFEB3 // Read Characteristic |0xFED4 |Read |Read // Write Characteristic |0xFED5 |Read/Write |Read/Write // Indicate Characteristic |0xFED6 |Read/Indicate |Indication // WriteWithNoRsp Characteristic |0xFED7 |Read/WriteCommand |Read/Write // Notify Characteristic |0xFED8 |Read/Notify |Notification ais_err_t ble_stack_init(ais_bt_init_t *ais_init); /** * API to de-initialize ble stack. * @return 0 on success, error code if failure. */ // De-initialize the BLE protocol stack of your hardware platform. If the device will no longer use BLE communication capabilities after Bluetooth-assisted provisioning is complete, // for scenarios where BLE is used infrequently, you can de-initialize the BLE protocol stack after the Bluetooth communication is complete. // This avoids unnecessary power consumption and Wi-Fi/BLE coexistence issues caused by keeping the BLE protocol stack running when not in use. ais_err_t ble_stack_deinit(); /** * API to send data via AIS's Notify Characteristics. * @parma[in] p_data data buffer. * @parma[in] length data length. * @return 0 on success, error code if failure. */ // Send data to the peer device on the connection link through the Notify Characteristic of the previously registered AIS Service. ais_err_t ble_send_notification(uint8_t *p_data, uint16_t length); /** * API to send data via AIS's Indicate Characteristics. * @parma[in] p_data data buffer. * @parma[in] length data length. * @parma[in] txdone txdone callback. * @return 0 on success, erro code if failure. */ // Send data to the peer device on the connection link through the Indicate Characteristic of the registered AIS Service. ais_err_t ble_send_indication(uint8_t *p_data, uint16_t length, void (*txdone)(uint8_t res)); /** * API to disconnect BLE connection. * @param[in] reason the reason to disconnect the connection. */ // Actively disconnect the established BLE connection from the device side. void ble_disconnect(uint8_t reason); /** * API to start bluetooth advertising. * @return 0 on success, erro code if failure. */ // Start broadcasting specific content. This content is Manufacturer Specific Data, filled by Breeze, // so that the peer device can identify the services provided by the Breeze Bluetooth device and perform related identity verification. ais_err_t ble_advertising_start(ais_adv_init_t *adv); /** * API to stop bluetooth advertising. * @return 0 on success, erro code if failure. */ // Stop broadcasting the specific advertising packet filled by Breeze, so that peer devices that support the Breeze protocol no longer discover this device. ais_err_t ble_advertising_stop(); /** * API to start bluetooth advertising. * @parma[out] mac the uint8_t[BD_ADDR_LEN] space the save the mac address. * @return 0 on success, erro code if failure. */ // Get the device's Bluetooth MAC address. The address is used for identity verification and other purposes. ais_err_t ble_get_mac(uint8_t *mac);breeze_hal_os.h
This is the OS porting interface. You must implement breeze_hal_os.c.
/** * This function will create a timer. * * @param[in] timer pointer to the timer. * @param[in] fn callbak of the timer. * @param[in] arg the argument of the callback. * @param[in] ms ms of the normal timer triger. * @param[in] repeat repeat or not when the timer is created. * @param[in] auto_run run auto or not when the timer is created. * * @return 0: success. */ // Create a system software timer. Parameters can configure the timer's duration, the callback to be triggered, // whether it should be a repeating timer, and whether the timer should start immediately upon creation. // The following interfaces are related to software timer operations. The Breeze module uses them for timed triggers and timeout calculations. // Multiple software timers may be created during operation. int os_timer_new(os_timer_t *timer, os_timer_cb_t cb, void *arg, int ms); /** * This function will start a timer. * * @param[in] timer pointer to the timer. * * @return 0: success. */ // Start a previously created software timer. int os_timer_start(os_timer_t *timer); /** * This function will stop a timer. * * @param[in] timer pointer to the timer. * * @return 0: success. */ // Stop a running software timer. This corresponds to the os_timer_start function. int os_timer_stop(os_timer_t *timer); /** * This function will delete a timer. * * @param[in] timer pointer to a timer. */ // Delete a previously created system software timer. This corresponds to the os_timer_new function. void os_timer_free(os_timer_t *timer); /** * Reboot system. */ // Reboot the device system. This is generally needed in services like OTA to perform related firmware migration and system initialization. // Services like these in the Breeze module will need to use this interface. void os_reboot(); /** * Msleep. * * @param[in] ms sleep time in milliseconds. */ // System sleep and delay. Some operations need to wait for a certain action to occur before proceeding to the next step, and this interface is used. // In a multi-threaded implementation, this interface generally puts the current thread to sleep for a specified time without affecting the execution of other threads. void os_msleep(int ms); /** * Get current time in mini seconds. * * @return elapsed time in mini seconds from system starting. */ // Get the current system time. This time is a relative time from the system start point, in milliseconds. long long os_now_ms(); /** * Add a new KV pair. * * @param[in] key the key of the KV pair. * @param[in] value the value of the KV pair. * @param[in] len the length of the value. * @param[in] sync save the KV pair to flash right now (should always be 1). * * @return 0 on success, negative error on failure. */ // The following interfaces are related to Key-Value storage. The Breeze module uses them for persistent storage of some data. Currently, Bluetooth-assisted provisioning // does not persistently store data, but considering future feature extensions, the Key-Value storage related interfaces also need to be ported. // os_kv_set stores the specified data into the Key-Value store. int os_kv_set(const char *key, const void *value, int len, int sync); /** * Get the KV pair's value stored in buffer by its key. * * @note: the buffer_len should be larger than the real length of the value, * otherwise buffer would be NULL. * * @param[in] key the key of the KV pair to get. * @param[out] buffer the memory to store the value. * @param[in-out] buffer_len in: the length of the input buffer. * out: the real length of the value. * * @return 0 on success, negative error on failure. */ // Get the corresponding data from the Key-Value store. int os_kv_get(const char *key, void *buffer, int *buffer_len); /** * Delete the KV pair by its key. * * @param[in] key the key of the KV pair to delete. * * @return 0 on success, negative error on failure. */ // Delete the data corresponding to a certain key from the Key-Value store. int os_kv_del(const char *key); /** * Generate random number. * * @return random value implemented by platform. */ // Return a random value. int os_rand(void);breeze_hal_sec.h
This is the security algorithm porting interface. You must implement breeze_hal_sec.c.
/** * @brief Initialize the aes context, which includes key/iv info. * The aes context is implementation specific. * * @param[in] key: * @param[in] iv: * @param[in] dir: AIS_AES_ENCRYPTION or AIS_AES_DECRYPTION * @return p_ais_aes128_t @verbatim None @endverbatim * @see None. * @note None. */ // This section implements the AES-128 algorithm. Breeze module communication is encrypted with AES-128 CBC. // Therefore, it is crucial to ensure that this implementation is correct, otherwise it will cause communication abnormalities between the peer and the device. void *ais_aes128_init(const uint8_t *key, const uint8_t *iv); /** * @brief Destroy the aes context. * * @param[in] aes: the aex context. * @return @verbatim = 0: succeeded = -1: failed @endverbatim * @see None. * @note None. */ int ais_aes128_destroy(void *aes); /** * @brief Do aes-128 cbc encryption. * No padding is required inside the implementation. * * @param[in] aes: AES handler * @param[in] src: plain data * @param[in] block_num: plain data number of 16 bytes size * @param[out] dst: cipher data * @return @verbatim = 0: succeeded = -1: failed @endverbatim * @see None. * @note None. */ int ais_aes128_cbc_encrypt(void *aes, const void *src, size_t block_num, void *dst); /** * @brief Do aes-128 cbc decryption. * No padding is required inside the implementation. * * @param[in] aes: AES handler * @param[in] src: cipher data * @param[in] block_num: plain data number of 16 bytes size * @param[out] dst: plain data * @return @verbatim = 0: succeeded = -1: failed @endverbatim * @see None. * @note None. */ int ais_aes128_cbc_decrypt(void *aes, const void *src, size_t block_num, void *dst);
Port the Wi-Fi protocol stack.
The BLE communication for Bluetooth-assisted provisioning uses the IoT Platform Bluetooth Breeze protocol. Breeze defines and implements upper-layer communication profile rules. With the correct porting of the Bluetooth protocol stack HAL, it can run on Bluetooth protocol stacks from different vendors. For information about the HAL interfaces to port, see Bluetooth-assisted provisioning development.
This section uses the BK7231U porting implementation as an example. In the IoT Platform SDK code package with AliOS Things, the Wi-Fi module and its HAL are located in the following directories.
/Living_SDK/framework/protocol/linkkit/sdk/iotx-sdk-c_clone/include/imports/
/Living_SDK/framework/protocol/linkkit/sdk/iotx-sdk-c_clone/include/iot_import.h
For information about porting dependent common HAL interfaces, such as OS, LwIP, and Security, see Wi-Fi device provisioning adaptation development. The following section describes the porting implementation of the Wi-Fi HAL interface on BK7231U based on AliOS Things.

Some porting interfaces in HAL_AWSS must be implemented only for a specific provisioning method. The HAL interfaces of HAL_AWSS are categorized as follows.
Category
Description
Common provisioning interfaces
Interfaces that must be implemented for all provisioning methods, including Bluetooth-assisted provisioning, device hot spot provisioning, one-click provisioning, and zero-configuration provisioning.
Device hot spot provisioning specific
Interfaces that need to be implemented when supporting the device hot spot provisioning method, such as HAL_Awss_Open_Ap and HAL_Awss_Close_Ap, which open and close the device hot spot.
Bluetooth-assisted provisioning, device hot spot provisioning, zero-configuration provisioning, mobile hot spot provisioning
The HAL_Awss_Get_Conn_Encrypt_Type interface needs to be implemented when supporting one or more of these provisioning methods.
One-click provisioning specific
Interfaces that need to be implemented when supporting the one-click provisioning method, such as HAL_Awss_Get_Encrypt_Type.
Control compilation-related settings.
For the compilation configuration file, see /Living_SDK/framework/protocol/linkkit/sdk/iotx-sdk-c_clone/make.settings.
# # Automatically generated file; DO NOT EDIT. # Main Menu # # # Configure Link Kit SDK for IoT Embedded Devices # FEATURE_SRCPATH="." FEATURE_MQTT_COMM_ENABLED=y FEATURE_ALCS_ENABLED=y # # MQTT Configurations # # FEATURE_MQTT_SHADOW is not set # FEATURE_MQTT_LOGPOST is not set FEATURE_MQTT_PREAUTH_SUPPORT_HTTPS_CDN=y FEATURE_DEVICE_MODEL_ENABLED=y FEATURE_MQTT_AUTO_SUBSCRIBE=y # # Device Model Configurations # # FEATURE_DEVICE_MODEL_GATEWAY is not set # The device binding feature needs to be enabled. Binding with the user account occurs after device provisioning is complete. FEATURE_DEV_BIND_ENABLED=y # FEATURE_DEVICE_MODEL_RAWDATA_SOLO is not set # FEATURE_COAP_COMM_ENABLED is not set FEATURE_OTA_ENABLED=y # FEATURE_HTTP2_COMM_ENABLED is not set # FEATURE_HTTP_COMM_ENABLED is not set FEATURE_SUPPORT_TLS=y # FEATURE_SAL_ENABLED is not set # The device Wi-Fi provisioning feature must be enabled. FEATURE_WIFI_PROVISION_ENABLED=y # # AWSS Configurations # # Supported device Wi-Fi provisioning methods: one-click provisioning, zero-configuration provisioning, device hot spot provisioning (Bluetooth-assisted provisioning does not need to be set here currently). FEATURE_AWSS_SUPPORT_SMARTCONFIG=y FEATURE_AWSS_SUPPORT_ZEROCONFIG=y FEATURE_AWSS_SUPPORT_DEV_AP=yImplement common provisioning interfaces.
/** * @brief Get the MAC address of the Wi-Fi device. The format must be "XX:XX:XX:XX:XX:XX". * * @param mac_str : Buffer array to store the MAC address string. * @return A character pointer to the start of the buffer array. */ char *HAL_Wifi_Get_Mac(_OU_ char mac_str[HAL_MAC_LEN]) { uint8_t mac[6] = { 0 }; // Call the driver layer interface to get the device's MAC address and convert it to string format. hal_wifi_get_mac_addr(NULL, mac); snprintf(mac_str, HAL_MAC_LEN, "%02x:%02x:%02x:%02x:%02x:%02x", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); return mac_str; } extern void wifi_get_ip(char ips[16]); /** * @brief Get the IP address of the Wi-Fi network interface. The dotted decimal format is saved in the output string array parameter. * The binary format is returned as the return value, expressed in network byte order (big-endian). * * @param ifname : The name of the specified Wi-Fi network interface. * @param ip_str : Array to store the IP address string in dotted decimal format. * @return The IP address in binary format, organized in network byte order (big-endian). */ uint32_t HAL_Wifi_Get_IP(_OU_ char ip_str[NETWORK_ADDR_LEN], _IN_ const char *ifname) { //(void *)ifname; // Call the driver layer interface to get the device's IP address. This IP address can be in two situations: // 1. In general, the device acts as a Station connected to an AP and is assigned an IP address. // 2. The device supports device hot spot and acts as a gateway with an IP address when SoftAP mode is enabled. wifi_get_ip(ip_str); return 0; } /** * @brief Get the duration for scanning on each channel, in milliseconds. * This interface is mainly used for one-click provisioning and zero-configuration provisioning, as they * require polling and scanning on the list of Wi-Fi channels during provisioning. * * @return The duration in milliseconds. * @note The recommended duration is 200 to 400 milliseconds. */ int HAL_Awss_Get_Channelscan_Interval_Ms(void) { // This default value is generally used. return 250; } /** * @brief Get the timeout duration for the provisioning service (AWSS), in milliseconds. * * @return The timeout duration in milliseconds. * @note The recommended duration is 3 minutes. */ int HAL_Awss_Get_Timeout_Interval_Ms(void) { // This default value is generally used. return 3 * 60 * 1000; } /** * @brief 802.11 frame processing function. You can pass the 802.11 Frame to this function. * * @param[in] buf @n 80211 frame buffer, or pointer to struct ht40_ctrl * @param[in] len @n 80211 frame buffer length * @param[in] link_type @n AWSS_LINK_TYPE_NONE for most rtos HAL, * and for linux HAL, do the following step to check * which header type the driver supported. * @verbatim * a) iwconfig wlan0 mode monitor #open monitor mode * b) iwconfig wlan0 channel 6 #switch channel 6 * c) tcpdump -i wlan0 -s0 -w file.pacp #capture 80211 frame * & save d) open file.pacp with wireshark or omnipeek check the link header * type and fcs included or not * @endverbatim * @param[in] with_fcs @n 80211 frame buffer include fcs(4 byte) or not * @param[in] rssi @n rssi of packet */ awss_recv_80211_frame_cb_t g_ieee80211_handler; static void monitor_data_handler(uint8_t *buf, int len, hal_wifi_link_info_t *info) { int with_fcs = 0; int link_type = AWSS_LINK_TYPE_NONE; (*g_ieee80211_handler)((char *)buf, len, link_type, with_fcs, info->rssi); } /** * @brief Set the Wi-Fi network interface card to work in monitor (or sniffer) mode. * When an 802.11 frame is received, call the passed-in callback function. The format of the callback function is as above. * You must provide the 802.11 frame buffer, length, HAL type, whether it has FCS, RSSI, and other information to the upper layer. * * @param[in] cb @n A function pointer, called back when wifi receive a * frame. */ void HAL_Awss_Open_Monitor(_IN_ awss_recv_80211_frame_cb_t cb) { // Here, BK7231U has ported the driver porting layer of AliOS Things, so the implementation is in the hal_wifi_module // style. This mainly involves registering the callback with the driver layer first and enabling Monitor mode. // When an 802.11 frame is monitored, it is reported up through the callback function. hal_wifi_module_t *module = hal_wifi_get_default_module(); if (module == NULL) { return; } g_ieee80211_handler = cb; hal_wifi_register_monitor_cb(module, monitor_data_handler); hal_wifi_start_wifi_monitor(module); HAL_Awss_Switch_Channel(6, 0, NULL); } /** * @brief Set the Wi-Fi network interface card to exit monitor (or sniffer) mode * and start working in station mode. */ void HAL_Awss_Close_Monitor(void) { // Unregister the previously registered callback function (set to NULL) and disable the device's Monitor mode. hal_wifi_module_t *module; module = hal_wifi_get_default_module(); if (module == NULL) { return; } hal_wifi_register_monitor_cb(module, NULL); hal_wifi_stop_wifi_monitor(module); } /** * @brief handle one piece of AP information from Wi-Fi scan result * * @param[in] ssid @n name of AP * @param[in] bssid @n mac address of AP * @param[in] channel @n AP channel * @param[in] rssi @n rssi range[-127, -1]. * the higher the RSSI number, the stronger the signal. * @param[in] is_last_ap @n this AP information is the last one if * is_last_ap > 0. this AP information is not the last one if is_last_ap == * 0. * @return 0 for Wi-Fi scan is done, otherwise return -1 * @see None. * @note None. */ typedef int (*awss_wifi_scan_result_cb_t)(const char ssid[HAL_MAX_SSID_LEN], const uint8_t bssid[ETH_ALEN], enum AWSS_AUTH_TYPE auth, enum AWSS_ENC_TYPE encry, uint8_t channel, signed char rssi, int is_last_ap); /** * @brief Start a Wi-Fi air scan. * This mode needs to be distinguished from the previous Monitor (or Sniffer) mode. * Monitor (Sniffer): Continuously monitors for 802.11 frames and reports them in real-time until the mode is disabled. * Scan: Scans for APs (via Beacon and Probe Request frames) and records the results of multiple APs found in one scan. * * @param[in] cb @n pass ssid info(scan result) to this callback one by one * @return 0 for Wi-Fi scan is done, otherwise return -1 * @see None. * @note * This API should NOT exit before the invoking for cb is finished. * This rule is something like the following : * HAL_Wifi_Scan() is invoked... * ... * for (ap = first_ap; ap <= last_ap; ap = next_ap){ * cb(ap) * } * ... * HAL_Wifi_Scan() exit... */ int HAL_Wifi_Scan(awss_wifi_scan_result_cb_t cb) { // Register the callback function for the list of scanned APs. // And start the scan (for surrounding APs, it is recommended to implement as an active scan for higher efficiency). // This function is executed synchronously, meaning the calling thread will be blocked until the current scan is complete. netmgr_register_wifi_scan_result_callback( (netmgr_wifi_scan_result_cb_t)cb); hal_wifi_start_scan_adv(NULL); while (netmgr_get_scan_cb_finished() != true) { // block aos_msleep(50); } return 0; } /** * @brief Set the Wi-Fi network interface card to switch to the specified channel. * * @param[in] primary_channel @n Primary channel. * @param[in] secondary_channel @n Auxiliary channel if 40Mhz channel is * supported, currently this param is always 0. * @param[in] bssid @n A pointer to Wi-Fi BSSID on which awss lock the * channel, most HAL may ignore it. */ void HAL_Awss_Switch_Channel(_IN_ char primary_channel, _IN_OPT_ char secondary_channel, _IN_OPT_ uint8_t bssid[ETH_ALEN]) { hal_wifi_module_t *module; module = hal_wifi_get_default_module(); if (module == NULL) { return; } // Call the driver layer interface to set the device to work on the specified channel. For some applications, specifying the channel // will make some operations more efficient. hal_wifi_set_channel(module, (int)primary_channel); } /** * @brief Function to request the Wi-Fi network interface card to connect to a specified Access Point. * * @param[in] connection_timeout_ms @n AP connection timeout in ms or HAL_WAIT_INFINITE * @param[in] ssid @n AP ssid * @param[in] passwd @n AP passwd * @param[in] auth @n optional(AWSS_AUTH_TYPE_INVALID), AP auth info * @param[in] encry @n optional(AWSS_ENC_TYPE_INVALID), AP encry info * @param[in] bssid @n optional(NULL or zero mac address), AP bssid info * @param[in] channel @n optional, AP channel info * @return @verbatim = 0: connect AP & DHCP success = -1: connect AP or DHCP fail/timeout @endverbatim * @see None. * @note * If the STA connects the old AP, HAL should disconnect from the old AP firstly. */ int HAL_Awss_Connect_Ap(_IN_ uint32_t connection_timeout_ms, _IN_ char ssid[HAL_MAX_SSID_LEN], _IN_ char passwd[HAL_MAX_PASSWD_LEN], _IN_OPT_ enum AWSS_AUTH_TYPE auth, _IN_OPT_ enum AWSS_ENC_TYPE encry, _IN_OPT_ uint8_t bssid[ETH_ALEN], _IN_OPT_ uint8_t channel) { int ms_cnt = 0; netmgr_ap_config_t config = { 0 }; if (ssid != NULL) { strncpy(config.ssid, ssid, sizeof(config.ssid) - 1); } if (passwd != NULL) { strncpy(config.pwd, passwd, sizeof(config.pwd) - 1); } if (bssid != NULL) { memcpy(config.bssid, bssid, ETH_ALEN); } // Temporarily store the information of the AP to be connected. netmgr_set_ap_config(&config); // Before formally connecting to the AP, suspend the station to prevent interference from unfinished previous operations. hal_wifi_suspend_station(NULL); // LOGI("aos_awss", "Will reconnect wifi: %s %s", ssid, passwd); // This will actually call the driver layer interface to initiate a connection to the specified AP. netmgr_reconnect_wifi(); // After calling the driver layer interface to connect to the AP, block the thread for a period of time. During this time, continuously check if the device // has successfully connected to the AP and obtained an IP address. If an IP address is obtained, the function ends and returns a successful connection result. while (ms_cnt < connection_timeout_ms) { if (netmgr_get_ip_state() == false) { LOGD("[waitConnAP]", "waiting for connecting AP"); aos_msleep(500); ms_cnt += 500; } else { LOGI("[waitConnAP]", "AP connected"); return 0; } } // if AP connect fail, should inform the module to suspend station // to avoid module always reconnect and block Upper Layer running // If the connection to the AP fails to be established within the timeout period, or if the AP is connected but an IP address cannot be obtained, it means that the device's // connection attempt has failed (possibly due to incorrect AP SSID, password, or other information, or the AP itself is not working properly, or // strong interference or weak signal prevents a successful connection). In this case, suspend the station (the driver layer may still // be in the state of initiating a connection to the AP; suspending the station terminates this state, so the device no longer initiates a connection to the AP), // and finally return a connection failure result to the upper layer for processing. hal_wifi_suspend_station(NULL); return -1; } #define FRAME_ACTION_MASK (1 << FRAME_ACTION) #define FRAME_BEACON_MASK (1 << FRAME_BEACON) #define FRAME_PROBE_REQ_MASK (1 << FRAME_PROBE_REQ) #define FRAME_PROBE_RESP_MASK (1 << FRAME_PROBE_RESPONSE) #define FRAME_DATA_MASK (1 << FRAME_DATA) /** * @brief Send a raw 802.11 frame on the current channel at the basic data rate (1 Mbps). * * @param[in] type @n see enum HAL_Awss_frame_type, currently only * FRAME_BEACON,FRAME_PROBE_REQ is used * @param[in] buffer @n 80211 raw frame, include complete mac header & FCS field * @param[in] len @n 80211 raw frame length * @return @verbatim = 0, send success. = -1, send failure. = -2, unsupported. @endverbatim * @see None. * @note awss use this API send raw frame in Wi-Fi monitor mode & station mode */ int HAL_Wifi_Send_80211_Raw_Frame(_IN_ enum HAL_Awss_Frame_Type type, _IN_ uint8_t *buffer, _IN_ int len) { // Call the driver layer interface to send an 802.11 frame. The type must support the 5 types defined above. return hal_wlan_send_80211_raw_frame(NULL, buffer, len); } /** * @brief Management frame processing callback function. * * @param[in] buffer @n 80211 raw frame or ie(information element) buffer * @param[in] len @n buffer length * @param[in] rssi_dbm @n rssi in dbm * @param[in] buffer_type @n 0 when buffer is a 80211 frame, * 1 when buffer only contain IE info * @return None. * @see None. * @note None. */ typedef void (*awss_wifi_mgmt_frame_cb_t)(_IN_ uint8_t *buffer, _IN_ int len, _IN_ signed char rssi_dbm, _IN_ int buffer_type); static awss_wifi_mgmt_frame_cb_t monitor_cb = NULL; static void mgnt_rx_cb(uint8_t *data, int len, hal_wifi_link_info_t *info) { if (monitor_cb) { monitor_cb(data, len, info->rssi, 0); } } /** * @brief Enable or disable filtering of management frames. * * @param[in] filter_mask @n see mask macro in enum HAL_Awss_frame_type, * currently only FRAME_PROBE_REQ_MASK & FRAME_BEACON_MASK is used * @param[in] vendor_oui @n oui can be used for precise frame match, optional * @param[in] callback @n see awss_wifi_mgmt_frame_cb_t, passing 80211 * frame or ie to callback. when callback is NULL * disable sniffer feature, otherwise enable it. * @return @verbatim = 0, success = -1, fail = -2, unsupported. @endverbatim * @see None. * @note awss use this API to filter specific mgnt frame in Wi-Fi station mode */ int HAL_Wifi_Enable_Mgmt_Frame_Filter( _IN_ uint32_t filter_mask, _IN_OPT_ uint8_t vendor_oui[3], _IN_ awss_wifi_mgmt_frame_cb_t callback) { monitor_cb = callback; // Enabling and disabling management frame filtering are both implemented in this interface. When enabling, a valid callback function must be passed. Passing NULL // means disabling the management frame filtering feature. // Management frame filtering may be enabled when the device is in Station mode or in device hot spot mode. // Therefore, as long as the device's Wi-Fi stack can obtain surrounding management frames, it needs to support enabling management frame filtering. if (callback != NULL) { hal_wlan_register_mgnt_monitor_cb(NULL, mgnt_rx_cb); } else { hal_wlan_register_mgnt_monitor_cb(NULL, NULL); } return 0; } /** * @brief check system network is ready(get ip address) or not. * * @param None. * @return 0, net is not ready; 1, net is ready. * @see None. * @note None. */ int HAL_Sys_Net_Is_Ready() { // Call the interface to determine if the device's current IP address is valid. return netmgr_get_ip_state() == true ? 1 : 0; } /** * @brief Get information about the connected Access Point. * * @param[out] ssid: array to store ap ssid. It will be null if ssid is not required. * @param[out] passwd: array to store ap password. It will be null if ap password is not required. * @param[out] bssid: array to store ap bssid. It will be null if bssid is not required. * @return @verbatim = 0: succeeded = -1: failed @endverbatim * @see None. * @note None. */ int HAL_Wifi_Get_Ap_Info(_OU_ char ssid[HAL_MAX_SSID_LEN], _OU_ char passwd[HAL_MAX_PASSWD_LEN], _OU_ uint8_t bssid[ETH_ALEN]) { netmgr_ap_config_t config = { 0 }; netmgr_get_ap_config(&config); if (ssid) { strncpy(ssid, config.ssid, HAL_MAX_SSID_LEN - 1); } if (passwd) { #ifdef DISABLE_SECURE_STORAGE strncpy(passwd, config.pwd, HAL_MAX_PASSWD_LEN - 1); #else extern int iotx_ss_decrypt(const char* in_data, int in_len, char* out_data, int out_len); iotx_ss_decrypt(config.pwd, MAX_PWD_SIZE, passwd, MAX_PWD_SIZE); #endif } if (bssid) { memcpy(bssid, config.bssid, ETH_ALEN); } return 0; } /** * @brief Get the connection status information of the current Station mode with the AP. * * @param[out] p_rssi: rssi value of current link * @param[out] p_channel: channel of current link * * @return @verbatim = 0: succeeded = -1: failed @endverbatim * @see None. * @note None. * @note awss use this API to get rssi and channel of current link */ int HAL_Wifi_Get_Link_Stat(_OU_ int *p_rssi, _OU_ int *p_channel) { int ret; hal_wifi_link_stat_t link_stat; if (netmgr_get_ip_state() == true) { ret = hal_wifi_get_link_stat(NULL, &link_stat); if ((ret == 0) && link_stat.is_connected) { *p_rssi = link_stat.wifi_strength; *p_channel = link_stat.channel; } else { return -1; } } else { return -1; } return 0; }Implement the dedicated interface for Bluetooth-assisted provisioning.
/** * @brief Get Security level for Wi-Fi configuration with connection. * Used for AP solution of router and App. * * @param None. * @return The security level: @verbatim 3: aes128cfb with aes-key per product and aes-iv = random 4: aes128cfb with aes-key per device and aes-iv = random 5: aes128cfb with aes-key per manufacture and aes-iv = random others: invalid @endverbatim * @see None. */ int HAL_Awss_Get_Conn_Encrypt_Type() { char invalid_ds[DEVICE_SECRET_LEN + 1] = {0}; char ds[DEVICE_SECRET_LEN + 1] = {0}; // Used to distinguish whether the encryption method for this provisioning method is "per-device secret" or "per-product secret". // If the DeviceSecret can be obtained, the "per-device secret" high-security level encryption method is used. // If the DeviceSecret is not found locally on the device, it downgrades to the "per-product secret" next-highest security level encryption method. HAL_GetDeviceSecret(ds); if (memcmp(invalid_ds, ds, sizeof(ds)) == 0) return 3; memset(invalid_ds, 0xff, sizeof(invalid_ds)); if (memcmp(invalid_ds, ds, sizeof(ds)) == 0) return 3; return 4; }
5. Generate the device firmware
The IoT Platform SDK provides a sample application for Bluetooth-assisted provisioning. After you port the code, you can compile the device firmware for Bluetooth-assisted provisioning based on the sample application and quickly verify the overall functionality.
SDK version |
Compilation command |
1.3.0 and later |
./build.sh example smart_outlet bk7231udevkitc MAINLAND ONLINE 1 |
1.3.0 and earlier |
cd Living_SDK aos make clean aos make comboapp@bk7231udevkitc btstack=vendor |
Initialize and start the application.
int application_start(int argc, char **argv)The
application_startfunction in the app_entry.c file of the sample application directory implements the following features.System initialization and startup
Device debug log level, device information, and device diagnosis module settings
Wi-Fi module initialization and related event subscriptions
Initialization and registration of the serial port interactive command
cliWi-Fi module startup and creation of related tasks
Dynamically enable or disable the Bluetooth-assisted provisioning mode of the device.
After you initialize the device system and each module, you can dynamically enable or disable the Bluetooth-assisted provisioning mode of the device using the following code.
// Disable the Bluetooth-assisted provisioning feature breeze_awss_stop(); // Enable the Bluetooth-assisted provisioning feature breeze_awss_start();Implement the Bluetooth-assisted provisioning workflow.
The core process of the sample application is implemented in the
combo_net_initfunction of the combo_net.c file in the sample application directory. This function implements the following features.Register the application layer callback, which triggers the connection to the router.
Set the device information in the underlying Breeze SDK.
Initialize and enable the BLE communication channel for Bluetooth-assisted provisioning.
The device sends BLE broadcasts that carry the identifier for the Bluetooth-assisted provisioning feature. A device in the BLE broadcasting state can be discovered by the mobile application. The operation process on the application and the device's status changes are described as follows.
When the device enters the Bluetooth-assisted provisioning state, it continuously sends BLE broadcasts. The broadcasts carry the identifier for the Bluetooth-assisted provisioning feature.
The mobile application scans for and discovers the combo device to be provisioned and initiates a connection request to the device.
When the connection is being established, the mobile application and the device perform secure authentication to ensure that the established BLE connection is secure and reliable.
The application sends provisioning information to the device through the secure BLE connection channel.
The device receives and parses the provisioning information through the Breeze SDK.
After the device obtains the provisioning information, it triggers the registered
combo_service_eventevent handler. At this point, the underlying SDK has already obtained the router's SSID, password, and other information required for Wi-Fi networking. Thecombo_service_eventevent handler checks the router's SSID, signal strength, and other conditions, and initiates a connection request to the router.After the device successfully connects to the router, it obtains an IP address and starts to connect to the cloud.
NoteIf an exception occurs during the process, the device generates a critical error code through self-diagnosis and sends it to the mobile application through the BLE connection. The error code is then displayed on the mobile application interface.
int combo_net_init() { breeze_dev_info_t dinfo = { 0 }; // Register the callback for when provisioning information from the app is received, which will trigger the action to connect to the router. aos_register_event_filter(EV_BZ_COMBO, combo_service_event, NULL); if ((strlen(g_combo_pk) > 0) && (strlen(g_combo_ps) > 0) \ && (strlen(g_combo_dn) > 0) && (strlen(g_combo_ds) > 0) && g_combo_pid > 0) { // Set the device information in the underlying SDK. Compared to a regular Wi-Fi device, Bluetooth-assisted provisioning // has an additional PID device information, which is specifically used for Bluetooth communication handshakes and other functions. dinfo.product_id = g_combo_pid; dinfo.product_key = g_combo_pk; dinfo.product_secret = g_combo_ps; dinfo.device_name = g_combo_dn; dinfo.device_secret = g_combo_ds; // Initialize the BLE protocol stack, Breeze SDK, etc., required for Bluetooth-assisted provisioning, and register the callback for receiving provisioning information. // At the same time, formally start Bluetooth-assisted provisioning. breeze_awss_init(apinfo_ready_handler, &dinfo); breeze_awss_start(); } else { // If the device information is set incorrectly, Bluetooth-assisted provisioning cannot be performed. printf("combo device info not set!\n"); } return 0; }Debug the Bluetooth-assisted provisioning device.
After you compile and generate the device firmware and flash it to the development board, you can use the following serial commands to trigger the corresponding actions on the device. Dynamic serial command interaction is supported in IoT Platform SDK 1.3.0 and later.
Flash the device certificate information.
The Bluetooth-related device certificate includes ProductKey, DeviceName, DeviceSecret, ProductSecret, and ProductID. These are automatically issued by the platform after you create the product and device in the IoT Platform console. You can run the following command to flash the device certificate information when the device is powered on.
linkkey ProductKey DeviceName DeviceSecret ProductSecret ProductIDEnable the one-click provisioning feature.
If Bluetooth-assisted provisioning is in progress, it is automatically disabled first.
awss active_awssEnable the Bluetooth-assisted provisioning feature.
After the device starts, it enters the channel scanning state by default. If one-click provisioning is in progress, it is automatically disabled first.
ble_awssAfter you start Bluetooth-assisted provisioning, the device broadcasts its Bluetooth-assisted provisioning-related device information over BLE. The mobile application can discover the device in the provisioning state on its device discovery page. You can initiate Bluetooth-assisted provisioning for the device from the mobile application interface. The provisioning process, result, and any exceptions that occur are displayed in real time on the mobile application interface.
Clear the device provisioning information.
reset
6. Verify the Bluetooth-assisted provisioning feature
You can use the public version of the Cloud Intelligence App provided by IoT Platform to verify the Bluetooth-assisted provisioning feature.
Download the Cloud Intelligence App version 2.7.5 or later. For information about download methods, see Cloud Intelligence App introduction.
Log on to the Cloud Intelligence App.
Turn on the Bluetooth switch on your mobile phone.
Go to the device discovery interface of the application and start scanning for devices that are in the Bluetooth-assisted provisioning state.
If the device cannot be discovered, check whether the following items are configured incorrectly.
Is the device in the Bluetooth broadcasting state? You can use tools such as the nRF Connect application to search for the device.
An example of Bluetooth-assisted provisioning broadcast content is shown in the following figure.
The broadcast data is in the Manufacturer data field of the broadcast packet. This field contains the Company ID, service support flags, and the MAC address. The MAC address is the Wi-Fi MAC address because a combo device is a Wi-Fi device that uses Bluetooth for auxiliary provisioning. The device communicates with the cloud over Wi-Fi. In this example, the Wi-Fi MAC address is
C8:47:8C:00:00:18.Is the device certificate information configured correctly? This is the content configured using the linkkey command.
Does the application's account environment correspond to the site where the device product was created?
Is the Bluetooth-assisted provisioning method configured in the Human-Computer Interaction section of the console?
After the application interface discovers the Bluetooth-assisted provisioning device, tap the device to start the Bluetooth-assisted provisioning process. During this process, you must enter the SSID and password of the router to which your phone is connected and confirm on the application UI that the device is in the provisioning state. After the confirmation is complete, the application establishes a Bluetooth connection with the device.
If the Bluetooth connection disconnects after a short period, check whether the following items are normal.
Is the device certificate information configured correctly? This is the content configured using the linkkey command.
Is the porting of the device's BLE protocol stack and the implementation of the sample application correct?
Is the network of the router to which your phone is connected working properly?
The mobile phone transmits the router information to the device. After the device receives the information, it connects to the target router.
At this time, the Bluetooth connection between the device and the mobile phone is not disconnected. If a failure occurs while the device is connecting to the router or the cloud, the device starts a self-diagnosis. This feature requires an upgrade to IoT Platform SDK 1.3.0 or later. The self-diagnosis result is returned to the mobile phone over the Bluetooth connection and displayed on the application interface.
The application interface jumps to the device's control interface. For example, the control interface for a light product can be used to turn the light on and off and adjust its brightness.
At this point, the debugging of the Bluetooth-assisted provisioning feature is successful. Next, you must test and control the functionality, stability, and success rate of the device product to complete the mass production and release of the solution.