본문 바로가기
Embedded/nRF52 BLE 개발 안내서

nRF52 BLE 개발하기 - ble_app_beacon

by 큐찡 2021. 1. 26.
반응형

시작하기

저번 글을 통해서 블루투스를 통해 LED 제어와 버튼 상태를 확인하는 예제에 대해서 알아보았다.

이번에는 비콘(Beacon)이라고 불리는 페어링 연결을 필요로 하지 않는 블루투스 통신 방법에 대해서 알아보도록 하자.

\examples\ble_peripheral\ble_app_beacon\pca10040\s132\ses 폴더에서 프로젝트를 실행시키고 예제 설명을 보자.

The Beacon Transmitter Sample Application is an example that implements a transmitter beacon using the hardware delivered in the nRF5 Development Kit.
The beacon broadcasts information to all compatible devices in its range as Manufacturer Specific Data in the advertisement packets.
This information includes:
    A 128-bit UUID to identify the beacon's provider.
    An arbitrary Major value for coarse differentiation between beacons.
    An arbitrary Minor value for fine differentiation between beacons.
    The RSSI value of the beacon measured at 1 meter distance, which can be used for estimating the distance from the beacon.

Configuring Major and Minor values
This example implements an optional feature which allows the change of Major and Minor values used in the advertisement packets without the need to recompile the application each time. 
To use this feature, the compiler define USE_UICR_FOR_MAJ_MIN_VALUES should be defined during compilation. 
If this is done, the application uses the value of the UICR (User Information Configuration Register) located at address 0x10001080 to populate the major and minor values. 
Whenever the values need to be updated, the user must set the UICR to a desired value using the nrfjprog tool and restart the application to bring the changes into effect. 
The byte ordering used when decoding the UICR value is shown in the image below.

Test the Beacon Transmitter Sample Application with nRF Connect for Desktop by performing the following steps:
    1. Compile and program the application. Observe that the BSP_INDICATE_ADVERTISING state is indicated.
    2. After starting discovery in nRF Connect, observe that the beacon is advertising with its Bluetooth device address without a Device Name.
    3. Click on Details under the beacon's address to view the full advertisement data.
    4. Observe that the Advertising Type is 'Non-connectable' and the Manufacturer Specific Data field of the Advertising Data is as follows:
       [59-00-02-15-01-12-23-34-45-56-67-78-89-9A-AB-BC-CD-DE-EF-F0-01-02-03-04-C3]

    1. Define the compiler define USE_UICR_FOR_MAJ_MIN_VALUES and recompile and flash.
    2. Use the nrfjprog tool to write the value 0xabcd0102 to the UICR register as follows:
       nrfjprog -f nrf52 --snr <Segger-chip-Serial-Number> --memwr 0x10001080 --val 0xabcd0102
    3. Reset the board and observe the bytes in bold below are seen in the Manufacturer Specific Data field of the Advertising Data. 
       This indicates that the Major and Minor values have been picked up from the UICR register written in the above step.
       [xx-xx-xx-xx-xx-xx-xx-xx-xx-xx-xx-xx-xx-xx-xx-xx-xx-xx-xx-xx-AB-CD-01-02-xx]

비콘 패킷은 다음과 같은 내용이 포함되어 있다.

비콘 공급자 식별용 128bit UUID, 비콘 간 구별을 위한 Major, Minor 값, 1미터 거리에서 측정된 비콘의 RSSI 값(비콘과의 거리 추정)

예제를 실행해보면 먼저 LED 1이 깜빡거리는 것을 볼 수 있다.

nRF Connect 애플리케이션을 실행하면 목록에 장치 이름이 없이 nRF Beacon이 Advertising 되고 있는 것을 볼 수 있다.

목록에서 nRF Beacon을 선택해 Manufacturer data를 보면 0x590002150112233445566778899AABBCCDDEEFF001020304C3으로 나온다.

이제 main 함수를 살펴보면서 비콘 동작에 대해서 알아보자.

#define APP_BLE_CONN_CFG_TAG            1                                  /**< A tag identifying the SoftDevice BLE configuration. */

#define NON_CONNECTABLE_ADV_INTERVAL    MSEC_TO_UNITS(100, UNIT_0_625_MS)  /**< The advertising interval for non-connectable advertisement (100 ms). This value can vary between 100ms to 10.24s). */

#define APP_BEACON_INFO_LENGTH          0x17                               /**< Total length of information advertised by the Beacon. */
#define APP_ADV_DATA_LENGTH             0x15                               /**< Length of manufacturer specific data in the advertisement. */
#define APP_DEVICE_TYPE                 0x02                               /**< 0x02 refers to Beacon. */
#define APP_MEASURED_RSSI               0xC3                               /**< The Beacon's measured RSSI at 1 meter distance in dBm. */
#define APP_COMPANY_IDENTIFIER          0x0059                             /**< Company identifier for Nordic Semiconductor ASA. as per www.bluetooth.org. */
#define APP_MAJOR_VALUE                 0x01, 0x02                         /**< Major value used to identify Beacons. */
#define APP_MINOR_VALUE                 0x03, 0x04                         /**< Minor value used to identify Beacons. */
#define APP_BEACON_UUID                 0x01, 0x12, 0x23, 0x34, \
                                        0x45, 0x56, 0x67, 0x78, \
                                        0x89, 0x9a, 0xab, 0xbc, \
                                        0xcd, 0xde, 0xef, 0xf0            /**< Proprietary UUID for Beacon. */

#define DEAD_BEEF                       0xDEADBEEF                         /**< Value used as error code on stack dump, can be used to identify stack location on stack unwind. */

#if defined(USE_UICR_FOR_MAJ_MIN_VALUES)
#define MAJ_VAL_OFFSET_IN_BEACON_INFO   18                                 /**< Position of the MSB of the Major Value in m_beacon_info array. */
#define UICR_ADDRESS                    0x10001080                         /**< Address of the UICR register used by this example. The major and minor versions to be encoded into the advertising data will be picked up from this location. */
#endif

int main(void)
{
    // Initialize.
    log_init();
    timers_init();
    leds_init();
    power_management_init();
    ble_stack_init();
    advertising_init();

    // Start execution.
    NRF_LOG_INFO("Beacon example started.");
    advertising_start();

    // Enter main loop.
    for (;; )
    {
        idle_state_handle();
    }
}

저번 글의 예제에 블루투스 설정을 해주는 부분이 매우 간소화되어 있는 것을 알 수 있다.

nRF52832의 주변기기를 설정하는 부분은 패스하고 블루투스 설정 부분만 살펴보겠다.

먼저 ble_stack_init 함수를 호출해 BLE 스택을 설정해 주는데 저번 글의 예제와 다른 점은 블루투스 이벤트 핸들러를 설정하지 않는다.

그 이유는 비콘 통신 방식이 양방향이 아니라 일방향 통신 방식이기 때문에 페어링 연결 등의 동작을 필요로 하지 않기 때문이다.

/**@brief Function for initializing the BLE stack.
 *
 * @details Initializes the SoftDevice and the BLE event interrupt.
 */
static void ble_stack_init(void)
{
    ret_code_t err_code;

    err_code = nrf_sdh_enable_request();
    APP_ERROR_CHECK(err_code);

    // Configure the BLE stack using the default settings.
    // Fetch the start address of the application RAM.
    uint32_t ram_start = 0;
    err_code = nrf_sdh_ble_default_cfg_set(APP_BLE_CONN_CFG_TAG, &ram_start);
    APP_ERROR_CHECK(err_code);

    // Enable BLE stack.
    err_code = nrf_sdh_ble_enable(&ram_start);
    APP_ERROR_CHECK(err_code);
}

다음은 advertising_init 함수를 호출해 Advertising 되는 비콘 패킷을 설정한다.

manuf_specific_data에 비콘 패킷이 설정되는데 company_identifier로 APP_COMPANY_IDENTIFIER(0x0059)으로 되어 있다.

이 부분은 회사 식별자로 블루투스 SIG에 등록해야만 할당받을 수 있는 값이다.

이에 대한 내용은 해당 링크에서 확인할 수 있다.

data에 m_beacon_info 값으로 설정되며 이 부분을 통해 비콘 패킷 내용을 알 수 있다.

APP_DEVICE_TYPE 값은 0x02로 비콘을 의미한다.

APP_ADV_DATA_LENGTH 값은 0x15로 Manufacturer specific data의 길이로 APP_BEACON_UUID, APP_MAJOR_VALUE, APP_MINOR_VALUE, APP_MEASURED_RSSI의 길이를 의미한다.

APP_BEACON_UUID 값은 [01 12 23 34 45 56 67 78 89 9A AB BC CD DE EF F0]으로 정의되어 있다.

APP_MAJOR_VALUE 값은 [01 02]로 정의되어 있다.

APP_MINOR_VALUE 값은 [03 04]로 정의되어 있다.

APP_MEASURED_RSSI 값은 0xC3으로 1미터 거리에서의 RSSI 값으로 -61 dBm으로 설정한 것을 의미한다.

Advertising 데이터 설정은 BLE_ADVDATA_NO_NAME, BLE_GAP_ADV_FLAG_BR_EDR_NOT_SUPPORTED, 비콘 패킷으로 정의한다.

Advertising 매개 변수 설정에 BLE_GAP_ADV_TYPE_NONCONNECTABLE_NONSCANNABLE_UNDIRECTED으로 연결하거나 스캔할 수 없도록 정의한다.

/**@brief Struct that contains pointers to the encoded advertising data. */
static ble_gap_adv_data_t m_adv_data =
{
    .adv_data =
    {
        .p_data = m_enc_advdata,
        .len    = BLE_GAP_ADV_SET_DATA_SIZE_MAX
    },
    .scan_rsp_data =
    {
        .p_data = NULL,
        .len    = 0

    }
};


static uint8_t m_beacon_info[APP_BEACON_INFO_LENGTH] =                    /**< Information advertised by the Beacon. */
{
    APP_DEVICE_TYPE,     // Manufacturer specific information. Specifies the device type in this
                         // implementation.
    APP_ADV_DATA_LENGTH, // Manufacturer specific information. Specifies the length of the
                         // manufacturer specific data in this implementation.
    APP_BEACON_UUID,     // 128 bit UUID value.
    APP_MAJOR_VALUE,     // Major arbitrary value that can be used to distinguish between Beacons.
    APP_MINOR_VALUE,     // Minor arbitrary value that can be used to distinguish between Beacons.
    APP_MEASURED_RSSI    // Manufacturer specific information. The Beacon's measured TX power in
                         // this implementation.
};

/**@brief Function for initializing the Advertising functionality.
 *
 * @details Encodes the required advertising data and passes it to the stack.
 *          Also builds a structure to be passed to the stack when starting advertising.
 */
static void advertising_init(void)
{
    uint32_t      err_code;
    ble_advdata_t advdata;
    uint8_t       flags = BLE_GAP_ADV_FLAG_BR_EDR_NOT_SUPPORTED;

    ble_advdata_manuf_data_t manuf_specific_data;

    manuf_specific_data.company_identifier = APP_COMPANY_IDENTIFIER;

#if defined(USE_UICR_FOR_MAJ_MIN_VALUES)
    // If USE_UICR_FOR_MAJ_MIN_VALUES is defined, the major and minor values will be read from the
    // UICR instead of using the default values. The major and minor values obtained from the UICR
    // are encoded into advertising data in big endian order (MSB First).
    // To set the UICR used by this example to a desired value, write to the address 0x10001080
    // using the nrfjprog tool. The command to be used is as follows.
    // nrfjprog --snr <Segger-chip-Serial-Number> --memwr 0x10001080 --val <your major/minor value>
    // For example, for a major value and minor value of 0xabcd and 0x0102 respectively, the
    // the following command should be used.
    // nrfjprog --snr <Segger-chip-Serial-Number> --memwr 0x10001080 --val 0xabcd0102
    uint16_t major_value = ((*(uint32_t *)UICR_ADDRESS) & 0xFFFF0000) >> 16;
    uint16_t minor_value = ((*(uint32_t *)UICR_ADDRESS) & 0x0000FFFF);

    uint8_t index = MAJ_VAL_OFFSET_IN_BEACON_INFO;

    m_beacon_info[index++] = MSB_16(major_value);
    m_beacon_info[index++] = LSB_16(major_value);

    m_beacon_info[index++] = MSB_16(minor_value);
    m_beacon_info[index++] = LSB_16(minor_value);
#endif

    manuf_specific_data.data.p_data = (uint8_t *) m_beacon_info;
    manuf_specific_data.data.size   = APP_BEACON_INFO_LENGTH;

    // Build and set advertising data.
    memset(&advdata, 0, sizeof(advdata));

    advdata.name_type             = BLE_ADVDATA_NO_NAME;
    advdata.flags                 = flags;
    advdata.p_manuf_specific_data = &manuf_specific_data;

    // Initialize advertising parameters (used when starting advertising).
    memset(&m_adv_params, 0, sizeof(m_adv_params));

    m_adv_params.properties.type = BLE_GAP_ADV_TYPE_NONCONNECTABLE_NONSCANNABLE_UNDIRECTED;
    m_adv_params.p_peer_addr     = NULL;    // Undirected advertisement.
    m_adv_params.filter_policy   = BLE_GAP_ADV_FP_ANY;
    m_adv_params.interval        = NON_CONNECTABLE_ADV_INTERVAL;
    m_adv_params.duration        = 0;       // Never time out.

    err_code = ble_advdata_encode(&advdata, m_adv_data.adv_data.p_data, &m_adv_data.adv_data.len);
    APP_ERROR_CHECK(err_code);

    err_code = sd_ble_gap_adv_set_configure(&m_adv_handle, &m_adv_data, &m_adv_params);
    APP_ERROR_CHECK(err_code);
}

마지막으로 advertising_start 함수를 호출하면 Advertising 비콘 패킷으로 동작한다.

/**@brief Function for starting advertising.
 */
static void advertising_start(void)
{
    ret_code_t err_code;

    err_code = sd_ble_gap_adv_start(m_adv_handle, APP_BLE_CONN_CFG_TAG);
    APP_ERROR_CHECK(err_code);

    err_code = bsp_indication_set(BSP_INDICATE_ADVERTISING);
    APP_ERROR_CHECK(err_code);
}

이제 구글 플레이스토어에서 nRF Beacon 애플리케이션을 설치하고 실행해보자.

첫 화면에서 (+)를 눌러 비콘을 찾으면 비콘 정보와 비콘 이벤트를 설정하는 화면이 나타난다.

여기서 이벤트 항목을 선택하면 범위를 벗어날 때, 범위에 들어올 때, 가까워졌을 때, 비콘 근처일 때 중 하나를 선택할 수 있다.

여기서는 가까워졌을 때를 선택하였다.

액션 항목을 선택하면 비콘 이벤트 발생 시 실행될 동작을 설정할 수 있는데 여기서는 모나리자 그림을 보여주는 것으로 선택하였다.

설정이 끝나면 첫 화면으로 돌아가 비콘 상태를 볼 수 있는데 현재 nRF52 DK와 스마트폰의 거리가 매우 가까워 At Beacon으로 되어 있어 모나리자가 나타나지 않는다.

거리를 떨어뜨려 Near 상태가 되면 아래 사진처럼 모나리자 그림이 팝업 되는 이벤트가 발생한다.

이제 nRF Connect 애플리케이션을 실행시켜보도록 하자.

장치 목록에 nRF Beacon을 선택하고 RAW 항목을 눌러보면 비콘 데이터 패킷을 확인할 수 있다.

02(Length)

01(Type)

04(BLE_GAP_ADV_FLAG_BR_EDR_NOT_SUPPORTED)

1A(Length)

FF(Type)

59 00(APP_COMPANY_IDENTIFIER)

02(APP_DEVICE_TYPE)

15(APP_ADV_DATA_LENGTH)

01 12 23 34 45 56 67 78 89 9A AB BC CD DE EF F0(APP_BEACON_UUID)

01 02(APP_MAJOR_VALUE)

03 04(APP_MINOR_VALUE)

C3(APP_MEASURED_RSSI)

예제는 비콘 간 구별을 위해 Major와 Minor 값을 소스코드 상에 정의되어 있어 이를 변경 시 컴파일을 해주어야 하는 번거로움이 있다.

하지만 이러한 번거로움 없이 Major와 Minor 값을 변경할 수 있는 방법이 있다.
소스코드 상에 #define USE_UICR_FOR_MAJ_MIN_VALUES을 추가하고 예제를 다시 실행시켜보자.

Major와 Minor 값이 설정되어 있지 않아 65535(0xFFFF)로 되어 있는 것을 확인할 수 있다.

명령 프롬프트를 실행하고 nrfjprog -f nrf52 --snr 682728166 --memwr 0x10001080 --val 0xabcd0102을 입력한다.

여기서 682728166은 nRF52 DK에 적혀 있는 일련번호이다.

해당 명령어는 UICR_ADDRESS(0x10001080)에 Major(0xABCD), Minor(0x0102) 값으로 변경한다.

다시 nRF Connect 애플리케이션에서 확인해보면 Major와 Minor 값이 변경된 것을 확인할 수 있다.

마지막으로 iBeacon에 대해서 알아보도록 하겠다.

iBeacon은 예제에서 약간의 수정만 해주면 적용 가능하며 수정되는 내용은 다음과 같다.

iBeacon에 대해 좀 더 자세히 알고 싶으면 해당 링크에 접속해 알아보도록 하자.

#define APP_COMPANY_IDENTIFIER          0x004C

static void advertising_init(void)
{
    uint32_t      err_code;
    ble_advdata_t advdata;
    uint8_t       flags = BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE;
    .
    .
    .
}

앞으로

지금까지 블루투스 통신 중 비콘에 대해서 알아보았다.

다음에는 블루투스를 이용한 UART 인터페이스에 대해서 알아보도록 하겠다.

반응형

댓글