設(shè)備端開發(fā)
為提升您基于新開發(fā)的Combo設(shè)備(同時(shí)支持Wi-Fi和BLE)硬件平臺移植生活物聯(lián)網(wǎng)平臺SDK提供的藍(lán)牙輔助Wi-Fi配網(wǎng)功能的效率,本文檔將選擇一款硬件開發(fā)板,進(jìn)行實(shí)際的移植示例,將整個(gè)功能移植、應(yīng)用開發(fā)、功能調(diào)試等過程串聯(lián)起來供您參考。
Combo設(shè)備移植藍(lán)牙輔助配網(wǎng)功能的主要流程如下。
選擇硬件設(shè)備
設(shè)備研發(fā)生產(chǎn)廠商、模組廠商、芯片廠商等,根據(jù)您自己的產(chǎn)品與場景需要,選擇合適的硬件平臺(需有Combo芯片或模組)。
控制臺創(chuàng)建產(chǎn)品
設(shè)備硬件選擇好后,需在生活物聯(lián)網(wǎng)平臺控制臺,創(chuàng)建項(xiàng)目和產(chǎn)品,新增測試設(shè)備,并配置好App的各項(xiàng)參數(shù)。
獲取SDK
下載的生活物聯(lián)網(wǎng)平臺SDK中包含了所需的配網(wǎng)模塊。
移植藍(lán)牙輔助配網(wǎng)HAL
移植設(shè)備端生活物聯(lián)網(wǎng)平臺SDK(包括其中的Wi-Fi配網(wǎng)模塊和藍(lán)牙Breeze模塊)到您的硬件平臺上,并進(jìn)行編譯和調(diào)試。
說明藍(lán)牙輔助配網(wǎng)開發(fā)相關(guān)的介紹,請參見藍(lán)牙輔助配網(wǎng)開發(fā)。如果您選擇生活物聯(lián)網(wǎng)平臺認(rèn)證的硬件平臺,則不需要重新移植。硬件平臺的選擇和介紹請參見選擇認(rèn)證模組/芯片。
生成設(shè)備固件
基于完整功能的示例應(yīng)用,編譯能運(yùn)行于您硬件平臺的藍(lán)牙輔助配網(wǎng)設(shè)備固件。
驗(yàn)證藍(lán)牙輔助配網(wǎng)功能
使用生活物聯(lián)網(wǎng)平臺提供的云智能App,驗(yàn)證藍(lán)牙輔助配網(wǎng)功能。
一、準(zhǔn)備硬件設(shè)備
請您根據(jù)自身產(chǎn)品選擇合適的硬件設(shè)備,具體資源請參見藍(lán)牙輔助配網(wǎng)開發(fā)(本文檔以同時(shí)支持Wi-Fi和BLE的Combo芯片,BK7231U為示例)。
二、在控制臺開發(fā)產(chǎn)品
- 登錄生活物聯(lián)網(wǎng)控制臺。
選擇已有項(xiàng)目或創(chuàng)建一個(gè)新項(xiàng)目。請參見創(chuàng)建項(xiàng)目。
創(chuàng)建產(chǎn)品,并定義產(chǎn)品功能。請參見創(chuàng)建產(chǎn)品并定義功能。
說明創(chuàng)建產(chǎn)品時(shí),連網(wǎng)方式需配置為WiFi。支持藍(lán)牙輔助配網(wǎng)的Wi-Fi + BLE的Combo設(shè)備,實(shí)質(zhì)上還是一個(gè)可以直連網(wǎng)絡(luò)的Wi-Fi設(shè)備,BLE的功能是Wi-Fi模塊輔助配網(wǎng)。
添加測試設(shè)備。請參見添加設(shè)備。
選擇App版本并配置App功能參數(shù),請參見人機(jī)交互概述。
配置配網(wǎng)引導(dǎo)時(shí),默認(rèn)配網(wǎng)方式或備選配網(wǎng)方式需配置為藍(lán)牙輔助配網(wǎng),并配置相應(yīng)的配網(wǎng)文案,請參見配置配網(wǎng)引導(dǎo)。
三、獲取SDK
獲取生活物聯(lián)網(wǎng)平臺SDK時(shí),建議您使用最新版本的含AliOS Things的SDK開發(fā)設(shè)備端。SDK下載地址請參見獲取SDK。
四、移植藍(lán)牙輔助配網(wǎng)HAL
藍(lán)牙輔助配網(wǎng)同時(shí)使用了Wi-Fi和BLE的通信能力,因此該功能模塊的移植,包括Wi-Fi配網(wǎng)模塊和藍(lán)牙Breeze模塊的移植。
移植BLE協(xié)議棧。
藍(lán)牙輔助配網(wǎng)中的BLE通信部分,使用了生活物聯(lián)網(wǎng)平臺的藍(lán)牙Breeze(通過上層通信Profile的規(guī)則定義和實(shí)現(xiàn))協(xié)議,基于藍(lán)牙協(xié)議棧HAL的移植(需移植的HAL接口請參見藍(lán)牙輔助配網(wǎng)開發(fā))后,可以運(yùn)行在不同廠商的藍(lán)牙協(xié)議棧上。
以移植BK7231U型號的芯片為示例,在含AliOS Things的SDK代碼包中,藍(lán)牙Breeze模塊及其需要移植的HAL位于/Living_SDK/framework/bluetooth/breeze/目錄下,該目錄中的內(nèi)容說明如下。
內(nèi)容
說明
breeze.mk
藍(lán)牙Breeze模塊的makefile
core/
藍(lán)牙Breeze模塊的協(xié)議實(shí)現(xiàn),移植時(shí)不用了解其實(shí)現(xiàn)細(xì)節(jié)
include/
藍(lán)牙Breeze模塊的協(xié)議實(shí)現(xiàn)的頭文件,移植時(shí)不用了解其實(shí)現(xiàn)細(xì)節(jié)
hal/
藍(lán)牙Breeze模塊的HAL實(shí)現(xiàn),移植時(shí)需要重點(diǎn)實(shí)現(xiàn),此處默認(rèn)的實(shí)現(xiàn)是使用AliOS Things提供的開源BLE協(xié)議棧的移植實(shí)現(xiàn)
api/
供上層應(yīng)用開發(fā)調(diào)用的用戶編程接口,移植時(shí)可以不用了解其細(xì)節(jié)
該部分的移植,主要是實(shí)現(xiàn)breeze.mk(與編譯控制相關(guān))和HAL(與芯片藍(lán)牙協(xié)議棧相關(guān))的部分。
實(shí)現(xiàn)breeze.mk。
說明示例BK7231U基于GCC交叉編譯工具鏈,采用
.mk
的makefile的編譯方式。如果您使用其他類型的編譯工具,類似.mk
的實(shí)現(xiàn)需完整移植到您所使用的編譯工具環(huán)境下。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安全廣播功能,用于增強(qiáng)廣播數(shù)據(jù)的安全性,藍(lán)牙輔助配網(wǎng)中未使用 secure_adv ?= 0 ifeq ($(secure_adv), 1) GLOBAL_DEFINES += CONFIG_AIS_SECURE_ADV endif # 是否已移植并使用AliOS Things提供的開源BLE協(xié)議棧 # 如果廠商驅(qū)動中已包含自己的BLE協(xié)議棧,此項(xiàng)功能不用選擇 # BK7231U使用的是廠商自己的BLE協(xié)議棧,因此這里不會使能 btstack ?= zephyr ifeq (zephyr, $(btstack)) $(NAME)_COMPONENTS += framework.bluetooth.breeze.hal.ble endif $(NAME)_SOURCES += api/breeze_export.c # Breeze安全認(rèn)證功能,藍(lán)牙輔助配網(wǎng)中必須打開 bz_en_auth ?= 1 ifeq ($(bz_en_auth), 1) GLOBAL_DEFINES += EN_AUTH $(NAME)_SOURCES += core/auth.c endif # Breeze輔助配網(wǎng)功能,藍(lán)牙輔助配網(wǎng)中必須打開 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 endif
實(shí)現(xiàn)hal目錄下的文件。
hal目錄下面需要移植的文件分別是:breeze_hal_ble.h、breeze_hal_os.h、breeze_hal_sec.h。三個(gè)文件的實(shí)現(xiàn)請參見SDK代碼包中/Living_SDK/platform/mcu/bk7231u/hal/breeze_hal/下的內(nèi)容。
breeze_hal_ble.h
藍(lán)牙協(xié)議棧移植接口,涉及BLE的廣播、連接、GATT相關(guān)的內(nèi)容,需實(shí)現(xiàn)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. */ // 對您使用的硬件平臺的BLE協(xié)議棧初始化,協(xié)議棧初始化成功后,BLE功能才能正常使用 // 藍(lán)牙輔助配網(wǎng)階段,先初始化BLE協(xié)議棧,接著基于協(xié)議棧注冊GATT Service,完成后才能通信 // Breeze的BLE通信通道是基于GATT Profile設(shè)計(jì)的,在設(shè)備端實(shí)現(xiàn)了Breeze的GATT Service // 生活物聯(lián)網(wǎng)平臺稱之為AIS(Alibaba IoT Service) // 該GATT Service中是一組分別支持Read、Write、Notify、Indicate操作的Characteristic, // BLE協(xié)議棧初始化函數(shù)傳入的是該GATT Primary Service的描述,BLE協(xié)議棧初始化函數(shù)需要將該Service // (也可以稱為Attribute Table)注冊到BLE stack,并將BLE鏈路的connect、disconnect與傳入Service描述中的元素對應(yīng) // 通過該方式實(shí)現(xiàn)了BLE stack和AIS Service之間的聯(lián)系, // 當(dāng)BLE連接建立或斷開,會通過初始化注冊的connect、disconnect回調(diào)通知到Breeze模塊中, // 同時(shí)在連接狀態(tài)下對端對于AIS Service的操作,也會通過Read,Write等回調(diào)通知到Breeze模塊中。 // 關(guān)于AIS Service的定義如下,供您移植和代碼調(diào)試時(shí)參考 // 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. */ // 對您使用的硬件平臺的BLE協(xié)議棧反初始化,如果在藍(lán)牙輔助配網(wǎng)結(jié)束,設(shè)備將不會再使用BLE通信能力, // 對于此種BLE使用頻率低的場景,可以在藍(lán)牙通信功能完成后, // 將BLE協(xié)議棧反初始化,避免BLE協(xié)議棧持續(xù)開啟但不使用,導(dǎo)致的不必要功耗和Wi-Fi、BLE共存問題 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. */ // 通過前面注冊的AIS Service的Notify Characteristic向連接鏈路的對端設(shè)備發(fā)送數(shù)據(jù) 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. */ // 通過注冊的AIS Service的Indicate Characteristic向連接鏈路的對端設(shè)備發(fā)送數(shù)據(jù) 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. */ // 從設(shè)備端主動斷開已經(jīng)建立的BLE連接 void ble_disconnect(uint8_t reason); /** * API to start bluetooth advertising. * @return 0 on success, erro code if failure. */ // 開始廣播特定的內(nèi)容,該內(nèi)容為Manufacturer Specific Data,是Breeze填充, // 以便對端設(shè)備能夠識別Breeze藍(lán)牙設(shè)備所提供的服務(wù),以及進(jìn)行相關(guān)的身份校驗(yàn) ais_err_t ble_advertising_start(ais_adv_init_t *adv); /** * API to stop bluetooth advertising. * @return 0 on success, erro code if failure. */ // 停止廣播Breeze填充的特定的廣播包,使支持Breeze協(xié)議的對端設(shè)備不要再發(fā)現(xiàn)此設(shè)備 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. */ // 獲取設(shè)備的Bluetooth MAC地址,會用于身份識別等用途 ais_err_t ble_get_mac(uint8_t *mac);
breeze_hal_os.h
OS系統(tǒng)移植接口,需實(shí)現(xiàn)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. */ // 創(chuàng)建一個(gè)系統(tǒng)的software timer,參數(shù)可配置該timer的定時(shí)時(shí)長,定時(shí)觸發(fā)的回調(diào), // 是否反復(fù)定時(shí),是否創(chuàng)建時(shí)立即運(yùn)行timer等 // 下面幾個(gè)接口是software timer操作相關(guān),Breeze模塊中會用來做定時(shí)觸發(fā)和超時(shí)計(jì)算的用途, // 運(yùn)行過程中可能會創(chuàng)建多個(gè)software timer 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. */ // 運(yùn)行前面創(chuàng)建好的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. */ // 停止正在運(yùn)行中的software timer,與前面的os_timer_start相對應(yīng) int os_timer_stop(os_timer_t *timer); /** * This function will delete a timer. * * @param[in] timer pointer to a timer. */ // 刪除前面創(chuàng)建的一個(gè)系統(tǒng)的software timer,與os_timer_new相對應(yīng) void os_timer_free(os_timer_t *timer); /** * Reboot system. */ // 設(shè)備系統(tǒng)重啟,一般在OTA一類的服務(wù)中,需要重啟系統(tǒng)以便執(zhí)行相關(guān)的固件搬移和系統(tǒng)初始化, // 類似這樣的Breeze模塊中的服務(wù)會需要用到該接口 void os_reboot(); /** * Msleep. * * @param[in] ms sleep time in milliseconds. */ // 系統(tǒng)睡眠和延時(shí),有的操作需要等待某個(gè)動作發(fā)生才能執(zhí)行下一步,就會用到該接口。 // 該接口在多線程實(shí)現(xiàn)中,一般會讓所在線程休眠指定的時(shí)間,而不影響其他線程的執(zhí)行 void os_msleep(int ms); /** * Get current time in mini seconds. * * @return elapsed time in mini seconds from system starting. */ // 獲取系統(tǒng)的當(dāng)前時(shí)間,該時(shí)間是一個(gè)相對系統(tǒng)啟動點(diǎn)的相對時(shí)間,單位為ms 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. */ // 下面幾個(gè)接口是Key-Value存儲相關(guān)的接口,Breeze模塊會用于一些數(shù)據(jù)的固化存儲,目前藍(lán)牙輔助配網(wǎng) // 中暫時(shí)未固化存儲數(shù)據(jù),但考慮后續(xù)的功能擴(kuò)展,Key-Value存儲相關(guān)的接口也需要進(jìn)行移植 // os_kv_set為將指定的數(shù)據(jù)存入Key-Value存儲中 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. */ // 從Key-Value存儲中獲取相應(yīng)的數(shù)據(jù) 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. */ // 將Key-Value存儲中某個(gè)Key對應(yīng)的數(shù)據(jù)刪除 int os_kv_del(const char *key); /** * Generate random number. * * @return random value implemented by platform. */ // 返回一個(gè)隨機(jī)值 int os_rand(void);
breeze_hal_sec.h
安全算法移植接口,需實(shí)現(xiàn)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. */ // 此部分為AES128算法的實(shí)現(xiàn),Breeze模塊通信是使用AES128 CBC加密的, // 因此務(wù)必保證該實(shí)現(xiàn)正常,否則會導(dǎo)致對端和設(shè)備間的通信異常 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);
移植Wi-Fi協(xié)議棧。
藍(lán)牙輔助配網(wǎng)中的BLE通信部分,使用了生活物聯(lián)網(wǎng)平臺的藍(lán)牙Breeze協(xié)議,藍(lán)牙Breeze屬于上層通信Profile的規(guī)則定義和實(shí)現(xiàn),基于藍(lán)牙協(xié)議棧HAL的正確移植,可以運(yùn)行于不同廠商的藍(lán)牙協(xié)議棧之上。需移植的HAL接口(參見藍(lán)牙輔助配網(wǎng)開發(fā))。
以BK7231U的移植實(shí)現(xiàn)為示例,在含AliOS Things的生活物聯(lián)網(wǎng)平臺SDK的代碼包中,Wi-Fi及其需要移植的HAL位于以下目錄下。
/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
依賴的通用HAL的接口移植(如OS,LwIP,Security等)請參見Wi-Fi設(shè)備配網(wǎng)適配開發(fā),對Wi-Fi HAL接口在BK7231U(基于AliOS Things)的移植實(shí)現(xiàn)示例說明如下。
HAL_AWSS中的移植接口,有些是針對某種配網(wǎng)方式才需要實(shí)現(xiàn)的接口,對HAL_AWSS的所有HAL接口分類如下。
分類
說明
配網(wǎng)通用接口
所有配網(wǎng)方式都必須要實(shí)現(xiàn)的接口,包括藍(lán)牙輔助配網(wǎng)、設(shè)備熱點(diǎn)配網(wǎng)、一鍵配網(wǎng)、零配配網(wǎng)等
設(shè)備熱點(diǎn)配網(wǎng)專用
需要支持設(shè)備熱點(diǎn)配網(wǎng)方式時(shí)需實(shí)現(xiàn)的接口,如HAL_Awss_Open_Ap、HAL_Awss_Close_Ap等打開和關(guān)閉設(shè)備熱點(diǎn)的接口
藍(lán)牙輔助配網(wǎng)設(shè)備熱點(diǎn)配網(wǎng)零配配網(wǎng)手機(jī)熱點(diǎn)配網(wǎng)
需要支持這幾種配網(wǎng)方式中的一種或幾種時(shí),需實(shí)現(xiàn)的接口HAL_Awss_Get_Conn_Encrypt_Type
一鍵配網(wǎng)專用
需要支持一鍵配網(wǎng)方式時(shí)需實(shí)現(xiàn)的接口,如HAL_Awss_Get_Encrypt_Type
編譯相關(guān)控制。
編譯配置文件請參見/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 # 設(shè)備綁定的Feature需要打開,在設(shè)備配網(wǎng)完成后會和用戶賬戶之間進(jìn)行綁定 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 # 設(shè)備Wi-Fi配網(wǎng)的Feature必須打開 FEATURE_WIFI_PROVISION_ENABLED=y # # AWSS Configurations # # 設(shè)備Wi-Fi配網(wǎng)可以支持的配網(wǎng)方式:一鍵配網(wǎng)、零配配網(wǎng)、設(shè)備熱點(diǎn)配網(wǎng)(藍(lán)牙輔助配網(wǎng)目前無需在此設(shè)置) FEATURE_AWSS_SUPPORT_SMARTCONFIG=y FEATURE_AWSS_SUPPORT_ZEROCONFIG=y FEATURE_AWSS_SUPPORT_DEV_AP=y
實(shí)現(xiàn)配網(wǎng)通用接口。
/** * @brief 獲取Wi-Fi設(shè)備的MAC地址, 格式應(yīng)當(dāng)是"XX:XX:XX:XX:XX:XX" * * @param mac_str : 用于存放MAC地址字符串的緩沖區(qū)數(shù)組 * @return 指向緩沖區(qū)數(shù)組起始位置的字符指針 */ char *HAL_Wifi_Get_Mac(_OU_ char mac_str[HAL_MAC_LEN]) { uint8_t mac[6] = { 0 }; // 調(diào)用驅(qū)動層的接口獲取設(shè)備的MAC地址,并轉(zhuǎn)為字符串的格式 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 獲取Wi-Fi網(wǎng)口的IP地址,點(diǎn)分十進(jìn)制格式保存在字符串?dāng)?shù)組出參, * 二進(jìn)制格式則作為返回值,并以網(wǎng)絡(luò)字節(jié)序(大端)表達(dá) * * @param ifname : 指定Wi-Fi網(wǎng)絡(luò)接口的名字 * @param ip_str : 存放點(diǎn)分十進(jìn)制格式的IP地址字符串的數(shù)組 * @return 二進(jìn)制形式的IP地址,以網(wǎng)絡(luò)字節(jié)序(大端)組織 */ uint32_t HAL_Wifi_Get_IP(_OU_ char ip_str[NETWORK_ADDR_LEN], _IN_ const char *ifname) { //(void *)ifname; // 調(diào)用驅(qū)動層的接口,獲取設(shè)備的IP地址。該IP地址分為兩種情況: // 1.一般情況,設(shè)備作為Station連接到AP,被分配的IP地址 // 2.設(shè)備支持設(shè)備熱點(diǎn),開啟SoftAP模式時(shí)作為gateway的IP地址 wifi_get_ip(ip_str); return 0; } /** * @brief 獲取在每個(gè)信道(`channel`)上掃描的時(shí)間長度,單位是毫秒 * 該接口主要是一鍵配網(wǎng)和零配配網(wǎng)會使用到,因?yàn)檫@兩者在配網(wǎng)時(shí) * 需要在Wi-Fi信道列表上進(jìn)行輪詢掃描 * * @return 時(shí)間長度, 單位是毫秒 * @note 推薦時(shí)長是200毫秒到400毫秒 */ int HAL_Awss_Get_Channelscan_Interval_Ms(void) { // 一般都設(shè)置為該默認(rèn)的值 return 250; } /** * @brief 獲取配網(wǎng)服務(wù)(`AWSS`)的超時(shí)時(shí)間長度,單位是毫秒 * * @return 超時(shí)時(shí)長,單位是毫秒 * @note 推薦時(shí)長是3分鐘 */ int HAL_Awss_Get_Timeout_Interval_Ms(void) { // 一般都設(shè)置為該默認(rèn)的值 return 3 * 60 * 1000; } /** * @brief 802.11幀的處理函數(shù),可以將802.11 Frame傳遞給這個(gè)函數(shù) * * @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 設(shè)置Wi-Fi網(wǎng)卡工作在監(jiān)聽(Monitor或Sniffer)模式, * 并在收到802.11幀的時(shí)候調(diào)用被傳入的回調(diào)函數(shù),回調(diào)函數(shù)的格式如上 * 必須要將802.11幀Buffer、長度、HAL類別,是否帶有FCS、RSSI等信息提供給上層 * * @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) { // 這里BK7231U是移植了AliOS Things的驅(qū)動移植層的,因此是hal_wifi_module // 風(fēng)格的實(shí)現(xiàn)。這里主要是將回調(diào)先注冊到驅(qū)動層,并開啟Monitor模式, // 在監(jiān)聽到802.11的幀時(shí),都通過回調(diào)函數(shù)上報(bào)上去 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 設(shè)置Wi-Fi網(wǎng)卡離開監(jiān)聽(Monitor或Sniffer)模式, * 并開始以站點(diǎn)(Station)模式工作 */ void HAL_Awss_Close_Monitor(void) { // 將原來注冊的回調(diào)函數(shù)取消(設(shè)置為NULL),并關(guān)閉設(shè)備的Monitor模式, 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 啟動一次Wi-Fi的空中掃描(Scan) * 該模式需要與前面的Monitor(或Sniffer)模式區(qū)別, * Monitor(Sniffer):持續(xù)開啟802.11幀監(jiān)聽,并實(shí)時(shí)上報(bào)監(jiān)聽到的幀,直到該模式被關(guān)閉 * Scan:掃描AP(通過Beacon和Probe Request幀),并記錄一次掃描到的多個(gè)AP的結(jié)果 * * @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) { // 注冊Scan到的AP列表的回調(diào)函數(shù) // 并啟動Scan(對周邊AP的掃描,建議實(shí)現(xiàn)為active scan,掃到的效率更高) // 該函數(shù)是同步執(zhí)行的方式,即調(diào)用后線程會被其阻塞,直到本次掃描結(jié)束 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 設(shè)置Wi-Fi網(wǎng)卡切換到指定的信道(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; } // 調(diào)用驅(qū)動層接口,設(shè)置設(shè)備此時(shí)工作在指定的信道,對于某些應(yīng)用需要,指定信道 // 會使一些操作更加高效 hal_wifi_set_channel(module, (int)primary_channel); } /** * @brief 要求Wi-Fi網(wǎng)卡連接指定熱點(diǎn)(Access Point)的函數(shù) * * @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); } // 將要連接的AP的信息暫存下來 netmgr_set_ap_config(&config); // 在正式連接AP前,Suspend Station,防止設(shè)備受上一次操作未結(jié)束的干擾 hal_wifi_suspend_station(NULL); // LOGI("aos_awss", "Will reconnect wifi: %s %s", ssid, passwd); // 實(shí)際會調(diào)用驅(qū)動層接口,向指定的AP發(fā)起連接 netmgr_reconnect_wifi(); // 在調(diào)用驅(qū)動層接口去連接AP后,在此阻塞線程一斷時(shí)間,該段時(shí)間內(nèi)持續(xù)檢查設(shè)備當(dāng)前是否 // 已經(jīng)連接AP成功且獲取到了IP地址,如果獲取到IP地址,該函數(shù)結(jié)束,并返回連接成功的結(jié)果 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 // 如果在連接AP超時(shí)時(shí)間到達(dá),都沒能成功連上AP,或者連上了AP沒能獲取到IP地址,說明設(shè)備 // 本次發(fā)起連接是失敗的(可能是AP的ssid,密碼等信息有誤,或者是AP本身工作異常,或者是 // 干擾太強(qiáng)或信號太弱導(dǎo)致沒法順利連接上),那么此時(shí)Suspend Station(驅(qū)動層可能還在 // 處于對AP發(fā)起連接的狀態(tài),Suspend Station將該狀態(tài)終止,使設(shè)備不再向AP發(fā)起連接), // 最后返回連接失敗的結(jié)果給上層處理 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 在當(dāng)前信道(channel)上以基本數(shù)據(jù)速率(1Mbps)發(fā)送裸的802.11幀(raw * 802.11 frame) * * @param[in] type @n see enum HAL_Awss_frame_type, currently only * FRAME_BEACON,F(xiàn)RAME_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) { // 調(diào)用驅(qū)動層的接口發(fā)送802.11的幀,類型必須要支持上面定義的5種 return hal_wlan_send_80211_raw_frame(NULL, buffer, len); } /** * @brief 管理幀的處理回調(diào)函數(shù) * * @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 使能或禁用對管理幀的過濾 * * @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; // 管理幀的過濾開啟與關(guān)閉,都在此接口中實(shí)現(xiàn),開啟時(shí)需傳入有效的回調(diào)函數(shù),而傳入NULL時(shí) // 表示將管理幀的過濾功能關(guān)閉。 // 管理幀過濾的開啟時(shí)機(jī),可能在設(shè)備處于Station模式,也可能在設(shè)備處于設(shè)備熱點(diǎn)模式, // 因此只要設(shè)備Wi-Fi stack能獲取到周邊的管理幀,都需要能支持管理幀的過濾開啟 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() { // 調(diào)用接口判斷設(shè)備當(dāng)前的IP地址是否有效 return netmgr_get_ip_state() == true ? 1 : 0; } /** * @brief 獲取所連接的熱點(diǎn)(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 獲取當(dāng)前Station模式與AP連接狀態(tài)的信息 * * @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; }
實(shí)現(xiàn)藍(lán)牙輔助配網(wǎng)關(guān)聯(lián)的專用接口。
/** * @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}; // 用于區(qū)分該種配網(wǎng)方式的加密方式是使用“一機(jī)一密”還是“一型一密” // 如果DeviceSecret可以獲取到,則使用“一機(jī)一密”的高安全級別加密方式 // 如果設(shè)備本地未找到DeviceSecret,則降級使用“一型一密”的次高安全級別加密方式 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; }
五、生成設(shè)備固件
生活物聯(lián)網(wǎng)平臺SDK提供了藍(lán)牙輔助配網(wǎng)的示例應(yīng)用,完成移植后,可以基于示例應(yīng)用編譯藍(lán)牙輔助配網(wǎng)設(shè)備固件,并對藍(lán)牙輔助配網(wǎng)的整體功能進(jìn)行快速驗(yàn)證。
SDK版本 | 編譯指令 |
1.3.0以上版本 | ./build.sh example smart_outlet bk7231udevkitc MAINLAND ONLINE 1 |
1.3.0及以下的版本 | cd Living_SDK aos make clean aos make comboapp@bk7231udevkitc btstack=vendor |
初始化與啟動應(yīng)用。
int application_start(int argc, char **argv)
示例應(yīng)用目錄中的app_entry.c文件的
application_start
函數(shù),該函數(shù)主要實(shí)現(xiàn)如下功能。系統(tǒng)初始化與啟動
設(shè)備調(diào)試日志等級、設(shè)備信息、設(shè)備診斷模塊設(shè)置
Wi-Fi模塊初始化,以及相關(guān)的事件訂閱
串口交互命令
cli
的初始化和注冊Wi-Fi模塊的啟動,與相關(guān)任務(wù)的創(chuàng)建
動態(tài)開啟或關(guān)閉設(shè)備的藍(lán)牙輔助配網(wǎng)模式。
實(shí)現(xiàn)設(shè)備系統(tǒng)與各模塊的初始化后,通過如下代碼實(shí)現(xiàn),可以動態(tài)開啟或關(guān)閉設(shè)備的藍(lán)牙輔助配網(wǎng)模式。
// 關(guān)閉藍(lán)牙輔助配網(wǎng)功能 breeze_awss_stop(); // 開啟藍(lán)牙輔助配網(wǎng)功能 breeze_awss_start();
實(shí)現(xiàn)藍(lán)牙輔助配網(wǎng)工作流程。
示例應(yīng)用的其核心流程實(shí)現(xiàn)在示例應(yīng)用目錄中的combo_net.c文件的
combo_net_init
函數(shù)。該函數(shù)主要實(shí)現(xiàn)以下功能。注冊應(yīng)用層的回調(diào),會觸發(fā)去連接路由器。
將設(shè)備信息設(shè)置到下層的Breeze SDK中。
初始化并開啟藍(lán)牙輔助配網(wǎng)的BLE通信通道。
設(shè)備發(fā)出BLE廣播(廣播里面會攜帶藍(lán)牙輔助配網(wǎng)功能的標(biāo)識),處于BLE廣播狀態(tài)的設(shè)備可以被移動端App掃描發(fā)現(xiàn)。此時(shí)App上的操作過程以及設(shè)備的狀態(tài)變化說明如下。
設(shè)備進(jìn)入藍(lán)牙輔助配網(wǎng)狀態(tài)時(shí),開始持續(xù)發(fā)出BLE廣播,廣播里攜帶藍(lán)牙輔助配網(wǎng)功能的標(biāo)識。
移動端App掃描發(fā)現(xiàn)該待配網(wǎng)的Combo設(shè)備,并從移動端App發(fā)起與該設(shè)備建立連接的請求。
建立連接時(shí),移動端App與設(shè)備之間通過安全認(rèn)證,確保建立的BLE連接是安全可靠的。
App通過BLE安全連接通道下發(fā)配網(wǎng)信息給設(shè)備端。
設(shè)備端通過Breeze SDK,接收和解析配網(wǎng)信息。
設(shè)備端在獲取到配網(wǎng)信息后,觸發(fā)注冊的
combo_service_event
事件處理函數(shù)(此時(shí)底層SDK已經(jīng)獲取到了Wi-Fi聯(lián)網(wǎng)需要的路由器的SSID和密碼等信息)。combo_service_event
事件處理函數(shù)時(shí),會檢測路由器的SSID和信號強(qiáng)度等情況,并向路由器發(fā)起連接請求。連接路由器成功后,獲取IP地址,啟動設(shè)備連云。
說明整個(gè)過程中如果出現(xiàn)異常,設(shè)備端會通過異常自檢生成關(guān)鍵錯誤碼信息,通過和移動端App之間的BLE連接傳回,并在移動端App界面顯示。
int combo_net_init() { breeze_dev_info_t dinfo = { 0 }; // 注冊獲取到App端傳過來的配網(wǎng)信息時(shí)的回調(diào),會觸發(fā)去連接路由器的動作 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) { // 設(shè)備信息設(shè)置到下層的SDK中,藍(lán)牙輔助配網(wǎng)與一般的Wi-Fi設(shè)備相比, // 會多一個(gè)PID的設(shè)備信息,專用于藍(lán)牙的通信握手等功能之用 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; // 初始化藍(lán)牙輔助配網(wǎng)所需的BLE協(xié)議棧、Breeze SDK等,并注冊獲取到配網(wǎng)信息的回調(diào) // 同時(shí)正式啟動藍(lán)牙輔助配網(wǎng) breeze_awss_init(apinfo_ready_handler, &dinfo); breeze_awss_start(); } else { // 如果設(shè)備信息設(shè)置有誤,則無法進(jìn)行藍(lán)牙輔助配網(wǎng) printf("combo device info not set!\n"); } return 0; }
調(diào)試藍(lán)牙輔助配網(wǎng)設(shè)備。
編譯生成設(shè)備固件,并燒錄到相應(yīng)的開發(fā)板之后,可以通過如下的串口命令,觸發(fā)設(shè)備的相應(yīng)動作(生活物聯(lián)網(wǎng)平臺SDK 1.3.0及之后的版本才支持動態(tài)串口命令交互)。
燒錄設(shè)備證書信息。
藍(lán)牙相關(guān)的設(shè)備證書包括ProductKey、DeviceName、DeviceSecret、ProductSecret、ProductID(您在生活物聯(lián)網(wǎng)控制臺創(chuàng)建的產(chǎn)品與設(shè)備后平臺自動頒發(fā)的),在設(shè)備上電初始執(zhí)行以下命令燒錄設(shè)備證書信息。
linkkey ProductKey DeviceName DeviceSecret ProductSecret ProductID
開啟一鍵配網(wǎng)功能。
此時(shí)如果藍(lán)牙輔助配網(wǎng)進(jìn)行中,會先自動關(guān)閉藍(lán)牙輔助配網(wǎng)功能。
awss active_awss
開啟藍(lán)牙輔助配網(wǎng)功能。
在設(shè)備正常啟動后,默認(rèn)會進(jìn)入信道掃描的狀態(tài)。此時(shí)如果一鍵配網(wǎng)進(jìn)行中,會先自動關(guān)閉一鍵配網(wǎng)功能。
ble_awss
啟動藍(lán)牙輔助配網(wǎng)后,設(shè)備會通過BLE廣播自己的藍(lán)牙輔助配網(wǎng)相關(guān)的設(shè)備信息,移動端的App可在其設(shè)備發(fā)現(xiàn)頁面發(fā)現(xiàn)處于待配網(wǎng)狀態(tài)的設(shè)備。通過在移動端App界面可發(fā)起對設(shè)備的藍(lán)牙輔助配網(wǎng),配網(wǎng)過程、配網(wǎng)結(jié)果、配網(wǎng)過程中發(fā)生的異常等信息會通過移動端App界面實(shí)時(shí)展示。
清除設(shè)備配網(wǎng)信息。
reset
六、驗(yàn)證藍(lán)牙輔助配網(wǎng)功能
您可以使用生活物聯(lián)網(wǎng)平臺提供的云智能App(公版App)來驗(yàn)證藍(lán)牙輔助配網(wǎng)功能。
下載云智能App(2.7.5或以上的版本)。下載方式請參見云智能App介紹。
登錄云智能App。
打開手機(jī)系統(tǒng)的藍(lán)牙開關(guān)。
進(jìn)入App的設(shè)備發(fā)現(xiàn)界面,開始掃描發(fā)現(xiàn)藍(lán)牙輔助配網(wǎng)狀態(tài)的設(shè)備。
如果設(shè)備無法發(fā)現(xiàn),請檢查確保以下各項(xiàng)是否設(shè)置有誤。
設(shè)備是否已處于藍(lán)牙廣播狀態(tài)(可使用相關(guān)工具搜索,如nRF Connect App)。
藍(lán)牙輔助配網(wǎng)廣播內(nèi)容示例說明如下。
廣播數(shù)據(jù)內(nèi)容主要位于廣播的Manufacturer data字段中,其中包含Company ID和一些服務(wù)支持標(biāo)識,以及MAC地址。其中MAC地址是表示W(wǎng)i-Fi MAC(Combo實(shí)際上是Wi-Fi設(shè)備,此處藍(lán)牙只是作為輔助配網(wǎng),設(shè)備和云端通信鏈路通過Wi-Fi),此處Wi-Fi MAC地址為
C8:47:8C:00:00:18
。設(shè)備證書信息是否設(shè)置正確(使用linkkey命令設(shè)置的內(nèi)容)。
App的賬號環(huán)境與創(chuàng)建設(shè)備產(chǎn)品的站點(diǎn)是否對應(yīng)。
控制臺人機(jī)交互中是否配置了藍(lán)牙輔助配網(wǎng)的配網(wǎng)方式。
App界面發(fā)現(xiàn)藍(lán)牙輔助配網(wǎng)設(shè)備后,點(diǎn)擊即可開始藍(lán)牙輔助配網(wǎng)流程,其間需要輸入手機(jī)連著的路由器的SSID和密碼,并且在App UI界面確認(rèn)設(shè)備已處于配網(wǎng)狀態(tài),用戶界面確認(rèn)完畢后,App端會去和設(shè)備建立藍(lán)牙連接。
如果藍(lán)牙連接在短時(shí)間內(nèi)斷開,請檢查確保以下項(xiàng)是否正常。
設(shè)備證書信息是否設(shè)置正確(使用linkkey命令設(shè)置的內(nèi)容)。
設(shè)備BLE協(xié)議棧移植以及示例應(yīng)用的實(shí)現(xiàn)是否檢查確認(rèn)無誤
手機(jī)連著的路由器的網(wǎng)絡(luò)是否能正常使用
手機(jī)將路由器信息傳輸給設(shè)備端,設(shè)備收到信息后去連接目標(biāo)路由器。
此時(shí)設(shè)備與手機(jī)之間的藍(lán)牙連接不會斷開,設(shè)備連接路由器、連接云端過程中如果有失敗情況發(fā)生,設(shè)備會啟動自檢(需升級至生活物聯(lián)網(wǎng)平臺SDK 1.3.0之后的版本才支持失敗詳情自檢的能力),并將自檢結(jié)果通過藍(lán)牙連接返回給手機(jī),并在App界面上顯示。
App界面跳轉(zhuǎn)出設(shè)備的控制界面(如“燈”產(chǎn)品的控制界面可以開關(guān)燈,調(diào)整燈的亮度等)。
此時(shí),藍(lán)牙輔助配網(wǎng)的功能調(diào)試成功。接下來您需要對設(shè)備產(chǎn)品的完整功能、穩(wěn)定性能、成功率等進(jìn)行嚴(yán)格測試把控,最終完成整個(gè)方案的量產(chǎn)和發(fā)布。