最近在搞藍牙主從通信這塊,公司裡面是有之前的代碼的,但是自己想在自己52832開發闆上弄一個主從通信。從機闆子是52832的,從機代碼采用官方的序列槽例程,主機是公司的51822闆子,主機代碼也是公司的,因為對這塊不熟悉,造成主從不能通信,最後請教老員工才知道,他們把藍牙服務的UUID更改成藍牙技術聯盟的基本UUID的問題,找到問題後,自己改了一下UUID類型,也就成功了,剛好趁這次機會,把UUID從128bit改成16bit的方式,也做個筆記,寫出來。本次運用的sdk依舊是SDK12.3版本。好了,先說簡單的更改UUID這塊。
1、UUID更改步驟以及注意事項
如果你是使用官方的标準128bitUUID,也就不需要經過這個步驟,但是,如果遇到需要更改的時候,可以參考一下。更改了UUID位數,要注意的是主從通信時候,主機會根據從機的主服務UUID名和類型,來和從機通信的,這點要注意。我們先說從機的更改,主機的更改放在主從通信這塊來講解。
步驟1:在從機序列槽服務初始化函數裡面,我們修改一下紅色地方

第一個地方,直接屏蔽掉,
第2的地方,我們要更改一下服務類型,為BLE_UUID_TYPE_BLE
第3,4,5,的地方,我們要更改一下主服務,RX,TX的UUID宏定義
第4,5函數裡面更改的地方是一樣的,貼出一個就行了
最後,我們更改的地方是廣播數組
更改完成以後,我們順便把廣播名更改了,因為此次例程是根據裝置名來尋找從機裝置的,主機尋找的裝置名要和從機的一樣。
更改好以後,下載下傳進開發闆裡面,我們可以看到廣播與以前 的不一樣的地方。
以前的是:
更改以後是;
到這裡,就已經完成了,下面我們開始講主從通信這塊。主從通信例程這塊,大家可以對照官網給的例程檢視,路徑為D:\nRF5_SDK_12.3.0_d7731ad\examples\ble_central_and_peripheral\experimental\ble_app_hrs_rscs_relay;我也是在這個例程上修改的。
1、修改宏定義,其實這個官網例程已經修改好了,也可以不必須修改。
可以看圖檔上的宏定義,CENTRAL_LINK_COUNT是作為主機時候,可以連接配接多少裝置,PERIPHERAL_LINK_COUNT是作為從機時候,可以連接配接多少裝置。此處均設為1。也就是說,作為從機時候,連接配接一個主裝置。作為主機時候,連接配接一個從機裝置。
2、向下可以到協定棧初始化,協定棧初始化沒什麼改變,主要是他的回調函數裡面,與之前不一樣。我們看一下
我們可以看出,作為從機時候,裡面的派發函數與之前官網的一樣,但是,我們是讓他作為主機,這裡我們不必過多檢視,主要看作為主機時候的三個派發函數:
1、static void on_ble_central_evt(const ble_evt_t * const p_ble_evt) //主機事假派發函數
2、void ble_db_discovery_on_ble_evt(ble_db_discovery_t * const p_db_discovery,
const ble_evt_t * const p_ble_evt) //資料發現事件派發函數
3、ble_nus_c_on_ble_evt(¢ral_to_perihterial, p_ble_evt) //主從通信事件派發函數
我們按照主機發現裝置—>連接配接裝置----->資料互動過程講解。
首先是發現裝置:在on_ble_central_evt()函數裡面會有報告BLE_GAP_EVT_ADV_REPORT,可以跳過去看一下這個代碼
case BLE_GAP_EVT_ADV_REPORT:
{
//¼ìÑéÐźÅ
uint8_t rssi_val = p_ble_evt->evt.gap_evt.params.adv_report.rssi;
{
if(rssi_val>0xC9)
{
if (strlen(m_target_periph_name) != 0)
{
if (find_adv_name(&p_gap_evt->params.adv_report, m_target_periph_name))
{
// Initiate connection.
err_code = sd_ble_gap_connect(&p_gap_evt->params.adv_report.peer_addr,
&m_scan_params,
&m_connection_param);
if (err_code != NRF_SUCCESS)
{
NRF_LOG_INFO("Connection Request Failed, reason %d\r\n", err_code);
}
}
}
}
}
// else
// {
// /** We do not want to connect to two peripherals offering the same service, so when
// * a UUID is matched, we check that we are not already connected to a peer which
// * offers the same service. */
// if ((find_adv_uuid(&p_gap_evt->params.adv_report, 0xA8A0)&&
// (m_conn_handle_nus_c == BLE_CONN_HANDLE_INVALID)))
// {
// // Initiate connection.
// err_code = sd_ble_gap_connect(&p_gap_evt->params.adv_report.peer_addr,
// &m_scan_params,
// &m_connection_param);
// if (err_code != NRF_SUCCESS)
// {
// NRF_LOG_INFO("Connection Request Failed, reason %d\r\n", err_code);
// }
// }
// }
} break; // BLE_GAP_ADV_REPORT
在這裡後面的通過UUID查找裝置我屏蔽掉了,用到按照裝置名尋找裝置,我們可以看一下函數find_adv_name();
我用RTT列印了一下發現裝置名的名稱。看到函數,我們可以發現就是擷取到裝置名以後,通過對比我們設定的需要連接配接的裝置名數組
static const char m_target_periph_name[] = “lvyapeng”;是否一樣,
如果一樣,就開始進行連接配接,如果不是,搜尋下一個。如果連接配接成功,就會觸發連接配接事件BLE_GAP_EVT_CONNECTED,還是在這個函數裡面,我們可以看一下。
首先判斷結構體m_conn_handle_nus_c是否等于BLE_CONN_HANDLE_INVALID,其中,我修改了地方是定義一個全局結構體
static uint16_t m_conn_handle_nus_c = BLE_CONN_HANDLE_INVALID;
官方之前定義了兩個,我給删除了,用了自己的,因為後面的主從通信要用到。建議最好用自己定義的,把官網定義的替換掉。
在這裡,我定義了一個bool類型的全局變量Send_cmd_flag,來判斷是否可以發送資料給從機。函數ble_db_discovery_start()是進行資料發現和連接配接的功能。因為這些地方都沒修改,是以簡單講解一下。後面的兩個函數ble_db_discovery_on_ble_evt()和ble_nus_c_on_ble_evt(),也是一樣沒有修改的地方,不做講解。
3、藍牙資料發現初始化
static void db_discovery_init(void)
{
ret_code_t err_code = ble_db_discovery_init(db_disc_handler);
APP_ERROR_CHECK(err_code);
}
這個沒什麼好說的,不過,我們要在他的回調裡面增加資料發現處理
static void db_disc_handler(ble_db_discovery_evt_t * p_evt)
{
// ble_rscs_on_db_disc_evt(&m_ble_rsc_c, p_evt);
// ble_hrs_on_db_disc_evt(&m_ble_hrs_c, p_evt);
ble_nus_c_on_db_disc_evt(&m_ble_nus_c, p_evt);
}
在void ble_nus_c_on_db_disc_evt(ble_nus_c_t * p_ble_nus_c, ble_db_discovery_evt_t * p_evt)函數裡面,我們要修改的地方在下面
因為這裡就是判斷哪個服務是自己要進行通信的,之前我們修改了從機的類型和服務UUID,是以這裡也要修改。
4、主機到從機資料流向。
nus_c_init();就是初始化主從資料通信這塊了,我們可以跳進去看一下
static void nus_c_init(void)
{
uint32_t err_code;
ble_nus_c_init_t nus_c_init_obj;
nus_c_init_obj.evt_handler = ble_nus_c_evt_handler;
err_code = ble_nus_c_init(&m_ble_nus_c, &nus_c_init_obj);
APP_ERROR_CHECK(err_code);
}
這些就是主從通信資料互動的初始化,、因為從機修改了服務UUID和類型,這裡也要修改,具體如下
步驟和從機一樣,把服務類型和uuID修改了
簡單修改就行,這裡主要注意他的回調函數。
static void ble_nus_c_evt_handler(ble_nus_c_t * p_ble_nus_c, const ble_nus_c_evt_t * p_ble_nus_evt)
兩者進行資料互動的時候,首先是觸發資料發現事件BLE_NUS_C_EVT_DISCOVERY_COMPLETE
如果需要從機發送資料給主機,就使能rx_notify。使能以後,就可接收資料,觸發BLE_NUS_C_EVT_NUS_RX_EVT事件,這裡我隻是列印一個資料就行了。
這兩個函數,ble_nus_c_handles_assign是獲得校驗從機服務參數的
ble_nus_c_rx_notif_enable是發現有RX通知特性,就打開RX使能,進行資料接收。
4、當主從連接配接好以後,就可以使用發送函數ble_nus_c_string_send(),我在測試過程中,發現雖然我的标志位Send_cmd_flag置1,主循環判斷标志位,然後發送,但是會出現發送失敗現象,經過測試,發現需要在連接配接成功以後,延時一段時間在發送才行,我是延時1s在發送資料的。
uint32_t ble_nus_c_string_send(ble_nus_c_t * p_ble_nus_c, uint8_t * p_string, uint16_t length)
發送資料。
5、主機開始掃描
static void adv_scan_start(void)
{
ret_code_t err_code;
uint32_t count;
//check if there are no flash operations in progress
printf"scan_start%02x\r\n",count);
if (count == 0)
{
scan_start();
err_code = ble_advertising_start(BLE_ADV_MODE_FAST);
APP_ERROR_CHECK(err_code);
}
}
在這裡面,主要是開始掃描和廣播開始,我們關注掃描函數
static void scan_start(void)
{
ret_code_t err_code;
(void) sd_ble_gap_scan_stop();
err_code = sd_ble_gap_scan_start(&m_scan_params);
// It is okay to ignore this error since we are stopping the scan anyway.
if (err_code != NRF_ERROR_INVALID_STATE)
{
APP_ERROR_CHECK(err_code);
}
}
這個函數裡面有一個結構體
static const ble_gap_scan_params_t m_scan_params =
{
.active = 1,
.interval = SCAN_INTERVAL,
.window = SCAN_WINDOW,
.timeout = SCAN_TIMEOUT,
#if (NRF_SD_BLE_API_VERSION == 2)
.selective = 0,
.p_whitelist = NULL,
#endif
#if (NRF_SD_BLE_API_VERSION == 3)
.use_whitelist = 0,
#endif
};
我在這裡就不細說了,直接截屏網上的一段資料給大家
6、發送函數,是定義一個軟體定時,10ms觸發一次回調函數
這樣就可以打開RTT,把主從程式下載下傳進去,檢視一下資料就行了,條件可以,也可以仿真一下,資料發送與接收的對不對,我的測試是沒有問題的。