該教程講解如何實現用手機來動態修改設備名,達到手機連接設備後修改設備名稱,然後斷開連接再掃描能夠看到新的設備名。
教程基於sdk9.0 下的uartdemo,如下目錄中xxx\Keil_v5\ARM\Pack\NordicSemiconductor\nRF_Examples\9.0.0\ble_peripheral\ble_app_uart
Nordic的協議棧實現中默認都會有一個 Generic Access服務和Generic Attribute服務(有的BLE app可能顯示不了名字,只顯示這兩個服務的UUID,分別是0x1800和0x1801)。
即一個工程中即使沒有定義任何服務,燒寫到板子上後,手機連接上也能看到這兩個服務。
我們動態修改設備名就是利用了第一個服務Generic Access。
該服務爲通用屬性規範服務,改服務爲設備提供了一種確定信息的方式,包括設備自身的名稱,外觀特性,首先連接參數等。
使用其中的 設備名屬性,就能實現我們需要的動態修改設備名。
實現方式就是 手機連接上設備後訪問這個服務下的設備名屬性,然後通過這個屬性寫新的名字,設備這邊判斷手機發送過來的寫是不是對Generic Access服務下的 設備名 屬性的寫操作。如果是就保存名字到flash中。並且更新設備名,這樣當設備重啓或者斷開連接後 手機這邊再掃描就能看到新的設備名字了。
PS:當然你也可以專門建立一個服務然後添加一個特徵值用來當做修改名字用。接收手機發送過來的新名字然後存儲修改。 不過既然默認存在的Generic Access服務中已經有了設備名屬性,這裏就直接用,不再去專門創建服務
因爲涉及到flash的存儲首先 添加flash相關的處理(關於flash的操作看前面的教程 如何在協議棧中使用flash)
//添加 sys_evt事件處理
static void sys_evt_dispatch(uint32_t sys_evt)
{
pstorage_sys_event_handler(sys_evt);
}
//協議棧初始化中註冊上面的派發函數
static void ble_stack_init(void)
{
uint32_t err_code;
// Initialize SoftDevice.
SOFTDEVICE_HANDLER_INIT(NRF_CLOCK_LFCLKSRC_XTAL_20_PPM, NULL);
………………
………………
………………
// Subscribe for BLE events.
err_code = softdevice_ble_evt_handler_set(ble_evt_dispatch);
APP_ERROR_CHECK(err_code);
//添加 sys_evt事件處理函數 註冊。 flash需要用到
err_code = softdevice_sys_evt_handler_set(sys_evt_dispatch);
APP_ERROR_CHECK(err_code);
}
首先初始化flash,並且定義自己的flash操作完成後的回調函數,在main.c文件的最上面添加如下代碼
//第一個字節存放的是標識符表示 flash中的數據是否是有效的 name
//device_name[0]=0xAA 表示是有效name,device_name[1]表示name的長度
#define NAME_SIZE 32
uint8_t device_name[NAME_SIZE];
pstorage_handle_t my_name_addr; //記錄name存放的flash地址
//flash操作完成後的回調函數。 並沒有做什麼有用的事,但是註冊flash塊//的時候需要有回調函數所以這裏需要定義
static void my_cb(pstorage_handle_t * handle,
uint8_t op_code,
uint32_t result,
uint8_t * p_data,
uint32_t data_len)
{
switch(op_code)
{
case PSTORAGE_UPDATE_OP_CODE:
if (result == NRF_SUCCESS)
{
printf("update end");
}
break;
}
}
//定義flash初始化函數
void my_flash_init(void){
uint32_t err_code;
pstorage_module_param_t param;
//申請一個塊 用來存放name
param.block_count = 1;
param.block_size = NAME_SIZE;
param.cb = my_cb;
err_code = pstorage_init();
printf("init err_code:%d\r\n",err_code);
err_code = pstorage_register(¶m, &my_name_addr);
printf("register err_code:%d\r\n",err_code);
//加載flash內容。後面會判斷name是否有效,如果有效就會用改name
//否則使用默認name
err_code = pstorage_load(device_name, &my_name_addr, NAME_SIZE, 0);
printf("load err_code:%d\r\n",err_code);
}
然後將該flash初始化函數放到main函數中
int main(void)
{
uint32_t err_code;
bool erase_bonds;
uint8_t start_string[] = START_STRING;
// Initialize.
APP_TIMER_INIT(APP_TIMER_PRESCALER, APP_TIMER_MAX_TIMERS, APP_TIMER_OP_QUEUE_SIZE, false);
uart_init();
//添加flash初始化,因爲用到了打印需要放到uart_init函數之後
//因爲gap_params_init函數中會判斷使用flash中的名字還是默認名字
//所以需要放到該函數之前
my_flash_init();
buttons_leds_init(&erase_bonds);
ble_stack_init();
gap_params_init();
services_init();
advertising_init();
conn_params_init();
printf("%s\r\n",start_string);
err_code = ble_advertising_start(BLE_ADV_MODE_FAST);
APP_ERROR_CHECK(err_code);
// Enter main loop.
for (;;)
{
power_manage();
}
}
然後修改 main函數中調用的gap_params_init函數。該函數中判斷my_flash_init 函數中加載的flash內容是否是有效name,是就使用,不是就用默認name.
static void gap_params_init(void)
{
uint32_t err_code;
ble_gap_conn_params_t gap_conn_params;
ble_gap_conn_sec_mode_t sec_mode;
BLE_GAP_CONN_SEC_MODE_SET_OPEN(&sec_mode);
//flash數據有效則使用flash中的名字
if ( device_name[0] == 0xaa ){
//有效 使用新名字
//device_name[1]爲名字長度
printf("update name\r\n");
err_code = sd_ble_gap_device_name_set(&sec_mode,
(const uint8_t *) device_name+2,
device_name[1]);
}else{
printf("default name\r\n");
err_code = sd_ble_gap_device_name_set(&sec_mode,
(const uint8_t *) DEVICE_NAME,
strlen(DEVICE_NAME));
}
APP_ERROR_CHECK(err_code);
memset(&gap_conn_params, 0, sizeof(gap_conn_params));
gap_conn_params.min_conn_interval = MIN_CONN_INTERVAL;
gap_conn_params.max_conn_interval = MAX_CONN_INTERVAL;
……………
…………
}
關於flash中的內容添加完了,然後是處理手機發送過來的新名字。
定義事件處理函數
static void name_change(ble_evt_t * p_ble_evt)
{
ble_gatts_evt_write_t * p_evt_write = &p_ble_evt->evt.gatts_evt.params.write;
//通過UUID來判斷事件是不是寫Generic Access服務中的名字屬性
if((p_evt_write->context.char_uuid.uuid == BLE_UUID_GAP_CHARACTERISTIC_DEVICE_NAME) &&
(p_ble_evt->header.evt_id == BLE_GATTS_EVT_WRITE))
{
printf("name change \r\n");
device_name[0] = 0xaa;
device_name[1] = p_evt_write->len;
memcpy(device_name+2, p_evt_write->data, p_evt_write->len);
pstorage_update(&my_name_addr, device_name, NAME_SIZE, 0 );
}
}
然後再將這個事件處理函數加到 事件派發函數ble_evt_dispatch中
static void ble_evt_dispatch(ble_evt_t * p_ble_evt)
{
name_change(p_ble_evt);
ble_conn_params_on_ble_evt(p_ble_evt);
ble_nus_on_ble_evt(&m_nus, p_ble_evt);
on_ble_evt(p_ble_evt);
ble_advertising_on_ble_evt(p_ble_evt);
bsp_btn_ble_on_ble_evt(p_ble_evt);
}
到這裏基本實現了 手機修改設備名字。不過連接上修改名字後斷開然後點擊掃描發現名字並未更新,需要復位一下硬件設備才能看到設備名字變了。
想在不復位的情況下,斷開連接然後點掃描就能看到新的設備名還需要在斷開連接後重新初始化 廣播數據。
繼續修改事件派發函數ble_evt_dispatch
//應爲該函數是在下面定義的,這裏使用到,所以要聲明一下
void advertising_init(void);
static void ble_evt_dispatch(ble_evt_t * p_ble_evt)
{
name_change(p_ble_evt);
//添加代碼,在斷開連接事件後初始化廣播數據
if ( p_ble_evt->header.evt_id == BLE_GAP_EVT_DISCONNECTED ){
advertising_init();
}
ble_conn_params_on_ble_evt(p_ble_evt);
ble_nus_on_ble_evt(&m_nus, p_ble_evt);
on_ble_evt(p_ble_evt);
ble_advertising_on_ble_evt(p_ble_evt);
bsp_btn_ble_on_ble_evt(p_ble_evt);
}
現在編譯後燒寫到板子,手機連接後進入 Generic Access服務(有的可能只看到UUID 0x1800),然後點擊device name(UUID 0x2A00)屬性,寫新名字。
然後斷開連接。 點擊掃描按鍵就可以看到新的設備名字了