: 이 예제는 peripheral 에서 들어온 센서 데이타를 collector 로 전달하는 Central + Peripheral 예제입니다.
쉽게 얘기해 heart rate 데이타를 받아서 collector 에 해당하는 장치(nRF Connect )에
notification을 보냅니다.
1> Nordic Document
https://infocenter.nordicsemi.com/topic/sdk_nrf5_v17.1.0/ble_sdk_app_rscs_relay.html
2> 프로젝트위치
▶ <InstallFolder>\examples\ble_central_and_peripheral\experimental\ble_app_hrs_rscs_relay
3> 상세분석
3-0> Block diagram
3-1 > 미연결시의 부팅 메시지
<info> app_timer: RTC: initialized.
<info> app: Relay example started.
<info> app: Fast advertising.
3-2> LED 정의
LED | 의미 |
LED1 | Central side is scanning |
LED2 | Central side is connected to a peripheral |
LED3 | Peripheral side is advertising |
LED4 | Peripheral side is connected to a central. |
4> Testing
: 2 ~3개의 보드가 필요하나 테스트 시 3개 사용했습니다.
보드 | 프로젝트 |
1st Board | <InstallFolder>\examples\ble_peripheral\ble_app_hrs |
2nd Board | <InstallFolder>\examples\ble_peripheral\ble_app_rscs |
3rd Board | <InstallFolder>\examples\ble_central_and_peripheral\experimental\ble_app_hrs_rscs_relay |
4-1> 1,2번 보드는 위 표의 프로젝트 이미지를 프로그램 합니다.
4-2> 3번 보드에 ble_app_hrs_rscs_relay 프로젝트 이미지 프로그램 (+ SoftDevice 포함.)
4-3> LED 상태 확인하기
▶ Central side : connected , Peripheral side is advertising
자동으로 연결이 되니 특별히 설정할 부분은 없습니다.
만약 ble_app_hrs 또는 ble_app_rscs 장비를 한개만 끄면 LED1 에도 불이 들어옵니다.
2개의 보드를 다 끌경우 LED2 가 꺼집니다.
위 LED를 자세히 설명하면 LED2, LED3 만 켜져 있으니
Central 기능으로의 ble_app_hrs 와 ble_app_rscs 장비 모두 연결된 상태이고
Peripheral 기능은 Advertising 상태입니다.
4-4> nRF Connect 앱을 통한 peripheral 장치 연결하기
▶ nRF Relay 장치를 스캔해 CONNECT 합니다.
4-5> Service 검색 및 notification enable 하기
▶ 4개의 서비스
▶ Heart Rate 서비스 안의 characteristics 보기 && notification enable 하기
▶ Running Speed and Cadence 서비스 보기 && notification enable 하기
5> 소스분석
5-0> Central && Peripheral 초기화 관련 코드
// Central 관련
scan_init();
// Peripheral 관련
advertising_init();
5-1> 자동연결코드
: Central 기능에서 BLE_GAP_EVT_ADV_REPORT 는 주변장치가 검색시 호출되는 이벤트로 이곳부터 시작합니다.
5-1-1> hrs (heart rate Service) && rscs ( Running Speed and Cadence Service)
static ble_uuid_t m_adv_uuids[] =
{
{BLE_UUID_HEART_RATE_SERVICE, BLE_UUID_TYPE_BLE},
{BLE_UUID_RUNNING_SPEED_AND_CADENCE, BLE_UUID_TYPE_BLE}
};
void scan_init(void)
{
nrf_ble_scan_init(&m_scan, &init_scan, scan_evt_handler);
~~~~ 중략 ~~~~
nrf_ble_scan_filter_set(&m_scan,
SCAN_UUID_FILTER,
&m_adv_uuids[HART_RATE_SERVICE_UUID_IDX]);
nrf_ble_scan_filter_set(&m_scan,
SCAN_UUID_FILTER,
&m_adv_uuids[RSCS_SERVICE_UUID_IDX]);
nrf_ble_scan_filters_enable(&m_scan,
NRF_BLE_SCAN_ALL_FILTER,
false);
}
#define NRF_BLE_SCAN_DEF(_name) \
static nrf_ble_scan_t _name; \
NRF_SDH_BLE_OBSERVER(_name ## _ble_obs, \
NRF_BLE_SCAN_OBSERVER_PRIO, \
nrf_ble_scan_on_ble_evt, &_name); \
NRF_BLE_SCAN_DEF(m_scan);
void nrf_ble_scan_on_ble_evt(ble_evt_t const * p_ble_evt, void * p_contex)
{
~~~~ 중략 ~~~~
switch (p_ble_evt->header.evt_id)
{
case BLE_GAP_EVT_ADV_REPORT:
nrf_ble_scan_on_adv_report(p_scan_data, p_adv_report);
break;
~~~~ 중략 ~~~~
}
}
void nrf_ble_scan_on_adv_report(nrf_ble_scan_t const * const p_scan_ctx,
ble_gap_evt_adv_report_t const * const p_adv_report)
{
~~~~ 중략 ~~~~
scan_evt.scan_evt_id = NRF_BLE_SCAN_EVT_FILTER_MATCH;
~~~~ 중략 ~~~~
p_scan_ctx->evt_handler(&scan_evt);
}
void scan_evt_handler(scan_evt_t const * p_scan_evt)
{
~~~~ 중략 ~~~~
switch(p_scan_evt->scan_evt_id)
{
case NRF_BLE_SCAN_EVT_FILTER_MATCH:
sd_ble_gap_connect(&p_adv->peer_addr, p_scan_param,
&m_scan.conn_params, APP_BLE_CONN_CFG_TAG);
break;
~~~~ 중략 ~~~~
}
}
5-2> notification 관련 코드
: notification data는 BLE_GATTC_EVT_HVX 이벤트를 통해서 들어온다는 부분만 알면 그다음부터는
아래처럼 검색하시면 됩니다.
5-2-0> ble_event_handler() 부터 찾기 시작하는 이유
NRF_SDH_BLE_OBSERVER(m_ble_observer, APP_BLE_OBSERVER_PRIO, ble_evt_handler, NULL);
5-2-1> hrs notification data 관련 코드
void ble_evt_handler(ble_evt_t const * p_ble_evt, void * p_context)
{
~~~~ 중략 ~~~~
ble_hrs_c_on_ble_evt(p_ble_evt, &m_hrs_c);
}
void ble_hrs_c_on_ble_evt(ble_evt_t const * p_ble_evt, void * p_context)
{
~~~~ 중략 ~~~~
switch (p_ble_evt->header.evt_id)
{
case BLE_GATTC_EVT_HVX:
on_hvx(p_ble_hrs_c, p_ble_evt);
break;
~~~~ 중략 ~~~~
}
}
static void on_hvx(ble_hrs_c_t * p_ble_hrs_c, const ble_evt_t * p_ble_evt)
{
~~~~ 중략 ~~~~
if (p_ble_evt->evt.gattc_evt.params.hvx.handle == p_ble_hrs_c->peer_hrs_db.hrm_handle){
ble_hrs_c_evt_t ble_hrs_c_evt;
ble_hrs_c_evt.evt_type = BLE_HRS_C_EVT_HRM_NOTIFICATION;
~~~~ 중략 ~~~~
}
p_ble_hrs_c->evt_handler(p_ble_hrs_c, &ble_hrs_c_evt);
}
void hrs_c_evt_handler(ble_hrs_c_t * p_hrs_c, ble_hrs_c_evt_t * p_hrs_c_evt)
{
switch (p_hrs_c_evt->evt_type){
~~~~ 중략 ~~~~
case BLE_HRS_C_EVT_HRM_NOTIFICATION:
NRF_LOG_INFO("Heart Rate = %d", p_hrs_c_evt->params.hrm.hr_value);
~~~~ 중략 ~~~~
}
5-2-2> rscs notification data 관련 코드
void ble_evt_handler(ble_evt_t const * p_ble_evt, void * p_context)
{
~~~~ 중략 ~~~~
ble_rscs_c_on_ble_evt(p_ble_evt, &m_rscs_c);
}
void ble_rscs_c_on_ble_evt(ble_evt_t const * p_ble_evt, void * p_context)
{
~~~~ 중략 ~~~~
switch (p_ble_evt->header.evt_id)
{
case BLE_GATTC_EVT_HVX:
on_hvx(p_ble_rscs_c, p_ble_evt);
break;
~~~~ 중략 ~~~~
}
}
static void on_hvx(ble_rscs_c_t * p_ble_rscs_c, const ble_evt_t * p_ble_evt)
{
~~~~ 중략 ~~~~
ble_rscs_c_evt_t ble_rscs_c_evt;
ble_rscs_c_evt.evt_type = BLE_RSCS_C_EVT_RSC_NOTIFICATION;
~~~~ 중략 ~~~~
p_ble_rscs_c->evt_handler(p_ble_rscs_c, &ble_rscs_c_evt);
}
void rscs_c_evt_handler(ble_rscs_c_t * p_rscs_c, ble_rscs_c_evt_t * p_rscs_c_evt)
{
~~~~ 중략 ~~~~
switch (p_rscs_c_evt->evt_type)
{
~~~~ 중략 ~~~~
case BLE_RSCS_C_EVT_RSC_NOTIFICATION:
{
NRF_LOG_INFO("Speed = %d", p_rscs_c_evt->params.rsc.inst_speed);
~~~~ 중략 ~~~~
}
~~~~ 중략 ~~~~
}
}
5-3> nRF Connect 앱의 notification data 관련 코드
5-3-1> hrs
: 5-2-1번 항목의 hrs_c_evt_handler() 부터 시작합니다.
void hrs_c_evt_handler(ble_hrs_c_t * p_hrs_c, ble_hrs_c_evt_t * p_hrs_c_evt)
{
switch (p_hrs_c_evt->evt_type){
~~~~ 중략 ~~~~
case BLE_HRS_C_EVT_HRM_NOTIFICATION:
NRF_LOG_INFO("Heart Rate = %d", p_hrs_c_evt->params.hrm.hr_value);
ble_hrs_heart_rate_measurement_send(&m_hrs, p_hrs_c_evt->params.hrm.hr_value);
break;
~~~~ 중략 ~~~~
}
uint32_t ble_hrs_heart_rate_measurement_send(ble_hrs_t * p_hrs, uint16_t heart_rate)
{
hrm_encode(p_hrs, heart_rate, encoded_hrm);
~~~~ 중략 ~~~~
hvx_params.handle = p_hrs->hrm_handles.value_handle;
hvx_params.type = BLE_GATT_HVX_NOTIFICATION;
hvx_params.offset = 0;
hvx_params.p_len = &hvx_len;
hvx_params.p_data = encoded_hrm;
sd_ble_gatts_hvx(p_hrs->conn_handle, &hvx_params);
}
5-3-2> rscs
: 위 내용과 거의 흡사합니다.
5-2-2번 항목의 rscs_c_evt_handler() 부터 시작합니다.
sd_ble_gatts_hvx()함수 호출하면 이후 nRF Connect 앱에도 자동으로 값이 변경됩니다.
static ble_rscs_t m_rscs;
void rscs_c_evt_handler(ble_rscs_c_t * p_rscs_c, ble_rscs_c_evt_t * p_rscs_c_evt)
{
~~~~ 중략 ~~~~
switch (p_rscs_c_evt->evt_type)
{
~~~~ 중략 ~~~~
case BLE_RSCS_C_EVT_RSC_NOTIFICATION:
{
NRF_LOG_INFO("Speed = %d", p_rscs_c_evt->params.rsc.inst_speed);
rscs_measurment.is_running = p_rscs_c_evt->params.rsc.is_running;
rscs_measurment.is_inst_stride_len_present = p_rscs_c_evt->params.rsc.is_inst_stride_len_present;
rscs_measurment.is_total_distance_present = p_rscs_c_evt->params.rsc.is_total_distance_present;
rscs_measurment.inst_stride_length = p_rscs_c_evt->params.rsc.inst_stride_length;
rscs_measurment.inst_cadence = p_rscs_c_evt->params.rsc.inst_cadence;
rscs_measurment.inst_speed = p_rscs_c_evt->params.rsc.inst_speed;
rscs_measurment.total_distance = p_rscs_c_evt->params.rsc.total_distance;
ble_rscs_measurement_send(&m_rscs, &rscs_measurment);
}
~~~~ 중략 ~~~~
}
}
uint32_t ble_rscs_measurement_send(ble_rscs_t * p_rscs, ble_rscs_meas_t * p_measurement)
{
~~~~ 중략 ~~~~
rsc_measurement_encode(p_rscs, p_measurement, encoded_rsc_meas);
hvx_params.handle = p_rscs->meas_handles.value_handle;
hvx_params.type = BLE_GATT_HVX_NOTIFICATION;
hvx_params.offset = 0;
hvx_params.p_len = &hvx_len;
hvx_params.p_data = encoded_rsc_meas;
sd_ble_gatts_hvx(p_rscs->conn_handle, &hvx_params);
~~~~ 중략 ~~~~
}
<기타>
A> ble_app_hrs 장치 및 ble_app_rscs 장치 사용시 로그
<info> app_timer: RTC: initialized.
<info> app: Relay example started.
<info> app: Fast advertising.
<info> app: Fast advertising.
<info> app: Fast advertising.
<info> app: Fast advertising.
<info> app: Fast advertising.
<info> app: Fast advertising.
<info> app: Fast advertising.
<info> app: Fast advertising.
<info> app: Fast advertising.
<info> app: Fast advertising.
<info> app: Running Speed and Cadence service discovered on conn_handle 0x0
<info> app: DB Discovery instance 0x20002E00 available on conn handle: 0
<info> app: Found 1 services on conn_handle: 0
<info> app: Speed = 1280
<info> app: Central connected
<info> app: Attempt to find HRS or RSC on conn_handle 0x1
<info> peer_manager_handler: Connection secured: role: Central, conn_handle: 0, procedure: Bonding
<info> peer_manager_handler: Peer data updated in flash: peer_id: 0, data_id: Bonding data, action: Update
<info> peer_manager_handler: Peer data updated in flash: peer_id: 0, data_id: Peer rank, action: Update
<info> peer_manager_handler: Peer data updated in flash: peer_id: 0, data_id: Local database, action: Update
<info> app: HRS discovered on conn_handle 0x1
<info> app: DB Discovery instance 0x20002E00 available on conn handle: 1
<info> app: Found 1 services on conn_handle: 1
<info> peer_manager_handler: Peer data updated in flash: peer_id: 0, data_id: Central address resolution, action: Update
<info> app: Speed = 1664
<info> app: Heart Rate = 160
<info> peer_manager_handler: Connection secured: role: Central, conn_handle: 1, procedure: Bonding
<info> peer_manager_handler: Peer data updated in flash: peer_id: 1, data_id: Bonding data, action: Update
<info> peer_manager_handler: Peer data updated in flash: peer_id: 1, data_id: Peer rank, action: Update
<info> peer_manager_handler: Peer data updated in flash: peer_id: 1, data_id: Local database, action: Update
<info> peer_manager_handler: Peer data updated in flash: peer_id: 1, data_id: Central address resolution, action: Update
<info> app: Heart Rate = 170
<info> app: Speed = 1280
<info> app: Heart Rate = 180
<info> app: Speed = 896
<info> app: Heart Rate = 190
<info> app: Speed = 512
>> nRF Connect 앱 연결시 로그
<info> app: Peripheral connected
: 자동연결이 되고 Heart Rate , Speed 값을 주기적으로 받네요.
B> ble_app_hrs 로그
<info> app_timer: RTC: initialized.
<info> app: Heart Rate Sensor example started.
<info> app: Fast advertising.
<info> peer_manager_handler: Peer data updated in flash: peer_id: 1, data_id: Peer rank, action: Update, no change
<info> app: Connected.
<info> peer_manager_handler: Connection secured: role: Peripheral, conn_handle: 0, procedure: Encryption
<info> peer_manager_handler: Peer data updated in flash: peer_id: 1, data_id: Peer rank, action: Update, no change
<info> app: GATT ATT MTU on connection 0x0 changed to 23.
<info> peer_manager_handler: Peer data updated in flash: peer_id: 1, data_id: Local database, action: Update
<info> peer_manager_handler: Connection secured: role: Peripheral, conn_handle: 0, procedure: Encryption
<info> peer_manager_handler: Peer data updated in flash: peer_id: 1, data_id: Peer rank, action: Update, no change
C> ble_app_rscs 로그
<info> app_timer: RTC: initialized.
<info> app: Running Speed and Cadence example started.
<info> app: Fast advertising.
<info> peer_manager_handler: Peer data updated in flash: peer_id: 1, data_id: Peer rank, action: Update, no change
<info> app: Connected.
<info> peer_manager_handler: Connection secured: role: Peripheral, conn_handle: 0, procedure: Encryption
<info> peer_manager_handler: Peer data updated in flash: peer_id: 1, data_id: Peer rank, action: Update, no change
<info> peer_manager_handler: Peer data updated in flash: peer_id: 1, data_id: Local database, action: Update
<info> peer_manager_handler: Connection secured: role: Peripheral, conn_handle: 0, procedure: Encryption
<info> peer_manager_handler: Peer data updated in flash: peer_id: 1, data_id: Peer rank, action: Update, no change
그럼 수고하세요.
'Nordic_nRF52' 카테고리의 다른 글
[nRF52 ] DFU serial 에러 메시지 (0) | 2022.07.21 |
---|---|
[nRF52 ] ble_app_cscs 프로젝트 분석 (0) | 2022.06.30 |
[nRF52 ] ble_app_bps 프로젝트 분석 (0) | 2022.06.14 |
[nRF52 ] ble_app_alert_notification 프로젝트 분석 (0) | 2022.06.13 |
[nRF52] NRF_ERROR_NO_MEM 에러 디버깅 (0) | 2022.05.04 |