본문으로 바로가기

[nRF52 ] ble_app_hrs_rscs_relay 프로젝트 분석

category Nordic_nRF52 2022. 6. 17. 12:28

 : 이 예제는 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

 

nRF5 SDK v17.1.0: Experimental: BLE Relay Example

This example requires one of the following SoftDevices: S132, S140 Important: Before you run this example, make sure to program the SoftDevice. This example application demonstrates a simple relay that receives values and passes them on. The application co

infocenter.nordicsemi.com

 

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

 

 

그럼 수고하세요.

반응형