今天收到了极术社区寄来的礼物,非常感谢。为此今天突击贡献一份蓝牙配网的小Demo,由于我对BLE通信不是很熟,此Demo仅供演示,存在几个问题尚未解决:
- 无法实现广播名
- 没有修改GATT表,直接套用例程的了
没有实现二次进入配网的功能
废话不多说,上干货:
首先还是先编译原生库:cd device/xradio/xr806/xr_skylark cp project/demo/wlan_ble_demo/gcc/defconfig .config make menuconfig make build_clean make lib -j cd ../../../.. hb set hb build -f
然后ohosdemo目录下面复制hello_demo,改名为ble_demo,目录结构如下:
ble_demo ├── BUILD.gn └── src └── main.c
然后修改ohosdemo目录下的BUILD.gn,内容如下:
group("ohosdemo") { deps = [ "ble_demo:app_ble", ] }
接下来修改ble_demo下的BUILD.gn,内容如下:
import("//device/xradio/xr806/liteos_m/config.gni") static_library("app_ble") { configs = [] sources = [ "src/main.c", ] cflags = board_cflags include_dirs = board_include_dirs include_dirs += [ "//kernel/liteos_m/kernel/arch/include", "//base/iot_hardware/peripheral/interfaces/kits", ".", "//utils/native/lite/include", "//foundation/communication/wifi_lite/interfaces/wifiservice", "//device/xradio/xr806/xr_skylark/project", "//device/xradio/xr806/xr_skylark/include/ble", ] }
然后是主程序:
#include <stdio.h> #include <string.h> #include <stddef.h> #include <errno.h> #include "ohos_init.h" #include "kernel/os/os.h" #include <ble/sys/byteorder.h> #include <bluetooth/bluetooth.h> #include <bluetooth/conn.h> #include <bluetooth/uuid.h> #include <bluetooth/gatt.h> #include "wifi_device.h" #include "cjson/cJSON.h" static OS_Thread_t g_main_thread; static struct bt_conn *default_conn = NULL; static OS_Semaphore_t sem; static uint8_t g_ssid[33] = ""; static uint8_t g_pwd[65] = ""; static uint8_t g_ble_config = 0; static void conn_addr_str(struct bt_conn *conn, char *addr, size_t len) { struct bt_conn_info info; if (bt_conn_get_info(conn, &info) < 0) { addr[0] = '\0'; return; } if(info.type == BT_CONN_TYPE_LE) { bt_addr_le_to_str(info.le.dst, addr, len); } } static void connected(struct bt_conn *conn, uint8_t err) { char addr[BT_ADDR_LE_STR_LEN]; conn_addr_str(conn, addr, sizeof(addr)); if (err) { printf("Failed to connect to %s (0x%02x)\n", addr, err); return; } bt_le_adv_stop(); printf("Connected: %s\n", addr); if (!default_conn) { default_conn = bt_conn_ref(conn); } } static void disconnected(struct bt_conn *conn, uint8_t reason) { char addr[BT_ADDR_LE_STR_LEN]; conn_addr_str(conn, addr, sizeof(addr)); printf("Disconnected: %s (reason 0x%02x)\n", addr, reason); if (default_conn == conn) { bt_conn_unref(default_conn); default_conn = NULL; } } static bool le_param_req(struct bt_conn *conn, struct bt_le_conn_param *param) { printf("LE conn param req: int (0x%04x, 0x%04x) lat %d to %d\n", param->interval_min, param->interval_max, param->latency, param->timeout); return true; } static void le_param_updated(struct bt_conn *conn, uint16_t interval, uint16_t latency, uint16_t timeout) { printf("LE conn param updated: int 0x%04x lat %d to %d\n", interval, latency, timeout); } static const char *ver_str(uint8_t ver) { const char * const str[] = { "1.0b", "1.1", "1.2", "2.0", "2.1", "3.0", "4.0", "4.1", "4.2", "5.0", "5.1", }; if (ver < ARRAY_SIZE(str)) { return str[ver]; } return "unknown"; } static void remote_info_available(struct bt_conn *conn, struct bt_conn_remote_info *remote_info) { struct bt_conn_info info; bt_conn_get_info(conn, &info); if (IS_ENABLED(CONFIG_BT_REMOTE_VERSION)) { printf("Remote LMP version %s (0x%02x) subversion 0x%04x manufacturer 0x%04x\n", ver_str(remote_info->version), remote_info->version, remote_info->subversion, remote_info->manufacturer); } if (info.type == BT_CONN_TYPE_LE) { uint8_t features[8]; char features_str[2 * sizeof(features) + 1]; sys_memcpy_swap(features, remote_info->le.features, sizeof(features)); bin2hex(features, sizeof(features), features_str, sizeof(features_str)); printf("LE Features: 0x%s\n", features_str); } } static void le_data_len_updated(struct bt_conn *conn, struct bt_conn_le_data_len_info *info) { printf("LE data len updated: TX (len: %d time: %d) RX (len: %d time: %d)\n", info->tx_max_len, info->tx_max_time, info->rx_max_len, info->rx_max_time); }; static struct bt_conn_cb conn_callbacks = { .connected = connected, .disconnected = disconnected, .le_param_req = le_param_req, .le_param_updated = le_param_updated, .remote_info_available = remote_info_available, .le_data_len_updated = le_data_len_updated, }; static void bt_ready(int err) { if (err) { printf("Bluetooth init failed (err %d)\n", err); return ; } if (IS_ENABLED(CONFIG_SETTINGS)) { settings_load(); printf("Settings Loaded\n"); } bt_conn_cb_register(&conn_callbacks); } ///////////////////////////////////////////////////////////////////// static const struct bt_data ad_discov[] = { BT_DATA_BYTES(BT_DATA_FLAGS, (BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR)), //BT_DATA_BYTES(BT_DATA_NAME_COMPLETE, 't', 'e', 's', 't'), }; static int advertise_on(void) { struct bt_le_adv_param param = {}; int err; param.id = BT_ID_DEFAULT; param.interval_min = BT_GAP_ADV_FAST_INT_MIN_2; param.interval_max = BT_GAP_ADV_FAST_INT_MAX_2; param.options = (BT_LE_ADV_OPT_CONNECTABLE | BT_LE_ADV_OPT_USE_NAME); err = bt_le_adv_start(¶m, ad_discov, ARRAY_SIZE(ad_discov), NULL, 0); if (err < 0) { printf("Failed to start advertising (err %d)\n", err); return err; } else { printf("Advertising started\n"); } return 0; } ///////////////////////////////////////////////////////////////////// #define CHAR_SIZE_MAX 512 static struct bt_uuid_128 met_svc_uuid = BT_UUID_INIT_128( 0x01, 0xde, 0xbc, 0x9a, 0x78, 0x56, 0x34, 0x12, 0x78, 0x56, 0x34, 0x12, 0x78, 0x56, 0x34, 0x12); static const struct bt_uuid_128 met_char_uuid = BT_UUID_INIT_128( 0x02, 0xde, 0xbc, 0x9a, 0x78, 0x56, 0x34, 0x12, 0x78, 0x56, 0x34, 0x12, 0x78, 0x56, 0x34, 0x12); static uint8_t met_char_value[CHAR_SIZE_MAX] = "hello"; static ssize_t read_met(struct bt_conn *conn, const struct bt_gatt_attr *attr, void *buf, uint16_t len, uint16_t offset) { const char *value = attr->user_data; uint16_t value_len; value_len = MIN(strlen(value), CHAR_SIZE_MAX); return bt_gatt_attr_read(conn, attr, buf, len, offset, value, value_len); } static ssize_t write_met(struct bt_conn *conn, const struct bt_gatt_attr *attr, const void *buf, uint16_t len, uint16_t offset, uint8_t flags) { uint8_t *value = attr->user_data; if (offset + len > sizeof(met_char_value)) { return BT_GATT_ERR(BT_ATT_ERR_INVALID_OFFSET); } memcpy(value + offset, buf, len); value[offset + len] = '\0'; cJSON *root = NULL; root = cJSON_Parse(value); cJSON *ssid = cJSON_GetObjectItem(root, "ssid"); cJSON *pwd = cJSON_GetObjectItem(root, "pwd"); if(ssid && ssid->type == cJSON_String && pwd && pwd->type == cJSON_String) { strncpy(g_ssid, ssid->valuestring, sizeof(g_ssid) - 1); strncpy(g_pwd, pwd->valuestring, sizeof(g_pwd) - 1); g_ble_config = 1; } cJSON_Delete(root); if(OS_SemaphoreRelease(&sem) != OS_OK) { printf("OS_SemaphoreRelease fail\n"); } return len; } static struct bt_gatt_attr met_attrs[] = { BT_GATT_PRIMARY_SERVICE(&met_svc_uuid), BT_GATT_CHARACTERISTIC(&met_char_uuid.uuid, BT_GATT_CHRC_READ | BT_GATT_CHRC_WRITE, BT_GATT_PERM_READ | BT_GATT_PERM_WRITE, read_met, write_met, met_char_value), }; static struct bt_gatt_service met_svc = BT_GATT_SERVICE(met_attrs); /***************************************************************** *****************************************************************/ static void MainThread(void *arg) { int err; if(OS_SemaphoreCreate(&sem, 0, 1) != OS_OK) { printf("OS_SemaphoreCreate fail!\n"); return; } bt_ctrl_enable(); printf("Start BLE Example!\n"); err = bt_enable(NULL); bt_ready(err); advertise_on(); bt_gatt_service_register(&met_svc); printf("--------------\n" ); if(OS_SemaphoreWait(&sem, OS_WAIT_FOREVER) != OS_OK) { printf("OS_SemaphoreWait fail!\n"); return; } OS_Sleep(1); bt_ctrl_disable(); if (WIFI_SUCCESS != EnableWifi()) { printf("Error: EnableWifi fail\n"); return; } OS_Sleep(1); if (WIFI_SUCCESS != Scan()) { printf("Error: Scan fail.\n"); return; } OS_Sleep(4); WifiScanInfo scan_results[30]; unsigned int scan_num = 30; if (WIFI_SUCCESS != GetScanInfoList(scan_results, &scan_num)) { printf("Error: GetScanInfoList fail.\n"); return; } WifiDeviceConfig config = { 0 }; int netId = 0; int i; for (i = 0; i < scan_num; i++) { printf("ssid: %s ", scan_results[i].ssid); printf("securityType: %d\n", scan_results[i].securityType); if (0 == strcmp(scan_results[i].ssid, g_ssid)) { memcpy(config.ssid, scan_results[i].ssid, WIFI_MAX_SSID_LEN); memcpy(config.bssid, scan_results[i].bssid, WIFI_MAC_LEN); strcpy(config.preSharedKey, g_pwd); config.securityType = scan_results[i].securityType; config.wapiPskType = WIFI_PSK_TYPE_ASCII; config.freq = scan_results[i].frequency; break; } } if (i >= scan_num) { printf("Error: No found ssid in scan_results\n"); return; } if (WIFI_SUCCESS != AddDeviceConfig(&config, &netId)) { printf("Error: AddDeviceConfig Fail\n"); return; } printf("Config Success\n"); if (WIFI_SUCCESS != ConnectTo(netId)) { printf("Error: ConnectTo Fail\n"); return; } while(1) { } } void BLEMain(void) { if (OS_ThreadCreate(&g_main_thread, "MainThread", MainThread, NULL, OS_THREAD_PRIO_APP, 4 * 1024) != OS_OK) { printf("[ERR] Create MainThread Failed\n"); } } SYS_RUN(BLEMain);
然后解决编译过程中容量超限问题参照编译手册,烧录成功后,我们用nrfConnect APP将路由器的SSID和PWD传输过去,注意安卓手机APP在发送前设置MTU,防止只能发20个字节。
以下是开发板发送内容:
选择text,发送{"ssid":"abc","pwd":"def"}
以下是开发板打印:==================================================================== Hello! OpenHarmony! System tag : OpenHarmony 1.1.2_LTS ==================================================================== use default flash chip mJedec 0x0 [FD I]: mode: 0x10, freq: 96000000Hz, drv: 0 [FD I]: jedec: 0x0, suspend_support: 1 mode select:e wlan information =================================================== firmware: version : R0-XR_C07.08.52.65_02.84 May 27 2021 11:41:33-Y02.84 buffer : 8 driver: version : XR_V02.05 mac address: in use : 1c:98:c9:bc:50:01 in use : 1c:98:c9:bc:50:02 ==================================================================== wlan mode:a [VFS INF] SPIFFS mount success. platform information =============================================== XR806 SDK v1.2.0 Dec 19 2021 13:12:37 heap space [0x2294e8, 0x247c00), size 124696 cpu clock 160000000 Hz HF clock 40000000 Hz sdk option: XIP : enable INT LF OSC : enable SIP flash : enable mac address: efuse : 80:74:84:21:38:8e in use : 1c:98:c9:bc:50:01 ==================================================================== hiview init success. ble controller open version : 9.1.19 build sha1 : v9.1.19-20210601 build date : Jun 1 2021 build time : 19:32:17 console init success platform : xr806 ble rf_init done! BLE INIT ALL DONE! BT Coex. Init. OK. Start BLE Example! == XRadio BLE HOST V2.5.0 == [bt] [WRN] set_flow_control: Controller to host flow control not supported [bt] [INF] bt_init: No ID address. App must call settings_load() [bt] [INF] bt_dev_show_info: Identity: DB:54:FC:5A:09:10 (random) [bt] [INF] bt_dev_show_info: HCI: version 5.0 (0x09) revision 0x0113, manufacturer 0x063d [bt] [INF] bt_dev_show_info: LMP: version 5.0 (0x09) subver 0x0113 Settings Loaded ************************************************* [RandomAddress 63:26:7B:59:9F:70 ] ************************************************* Advertising started -------------- Connected: 69:06:6D:65:26:0F (random) Remote LMP version 4.2 (0x08) subversion 0x0710 manufacturer 0x0046 LE Features: 0x0000000000000000 LE conn param updated: int 0x0006 lat 0 to 500 LE conn param updated: int 0x0024 lat 0 to 500 [net INF] no need to switch wlan mode 0 [net INF] msg <wlan scan success> ssid: abc securityType: 2 Config Success [net INF] no need to switch wlan mode 0 en1: Trying to associate with a4:c7:4b:71:f9:84 (SSID='abc' freq=2462 MHz) en1: Associated with a4:c7:4b:71:f9:84 en1: WPA: Key negotiation completed with a4:c7:4b:71:f9:84 [PTK=CCMP GTK=CCMP] en1: CTRL-EVENT-CONNECTED - Connection to a4:c7:4b:71:f9:84 completed [id=0 id_str=] [net INF] msg <wlan connected> [net INF] netif is link up [net INF] start DHCP... [net INF] netif (IPv4) is up [net INF] address: 192.168.3.65 [net INF] gateway: 192.168.3.1 [net INF] netmask: 255.255.255.0 [net INF] msg <network IPv6 state> [net INF] IPv6 addr state change: 0x0 --> 0x1 [net INF] msg <> WAR drop=1117, fctl=0x00d0.
然后就可以干物联网该干的事情了,好了在这里先提前预祝各位元旦快乐