지식/BLE

BLE ESP32 Bluedroid_Beacon 코드 분석하기 1편-전역변수

원원 2025. 11. 22. 20:26

안녕하세요. 오늘은 ESP32 Bluedroid_Beacon 예제코드를 분석해보겠습니다.
사용하는 소스코드는 아래의 링크입니다.
https://github.com/espressif/esp-idf/tree/master/examples/bluetooth/ble_get_started/bluedroid/Bluedroid_Beacon


1편에서는 프로젝트에서 사용한 전역변수 3개를 알아보겠습니다.

* adv_params 구조체
 - 광고를 어떻게 보낼지 설정하는 구조체입니다.

static esp_ble_adv_params_t adv_params = {
    .adv_int_min = 0x20,
    .adv_int_max = 0x20,
    .adv_type = ADV_TYPE_SCAN_IND,
    .own_addr_type = BLE_ADDR_TYPE_PUBLIC,
    .channel_map = ADV_CHNL_ALL,
    .adv_filter_policy = ADV_FILTER_ALLOW_SCAN_ANY_CON_ANY,
};

해당 구조체의 원형은 esp_gap_ble_api.h에 있습니다.

typedef struct {
    uint16_t                adv_int_min;        /*!< Minimum advertising interval for
                                                  undirected and low duty cycle directed advertising.
                                                  Range: 0x0020 to 0x4000 Default: N = 0x0800 (1.28 second)
                                                  Time = N * 0.625 msec Time Range: 20 ms to 10.24 sec */
    uint16_t                adv_int_max;        /*!< Maximum advertising interval for
                                                  undirected and low duty cycle directed advertising.
                                                  Range: 0x0020 to 0x4000 Default: N = 0x0800 (1.28 second)
                                                  Time = N * 0.625 msec Time Range: 20 ms to 10.24 sec Advertising max interval */
    esp_ble_adv_type_t      adv_type;           /*!< Advertising type */
    esp_ble_addr_type_t     own_addr_type;      /*!< Owner bluetooth device address type */
    esp_bd_addr_t           peer_addr;          /*!< Peer device bluetooth device address */
    esp_ble_addr_type_t     peer_addr_type;     /*!< Peer device bluetooth device address type, only support public address type and random address type */
    esp_ble_adv_channel_t   channel_map;        /*!< Advertising channel map */
    esp_ble_adv_filter_t    adv_filter_policy;  /*!< Advertising filter policy */
} esp_ble_adv_params_t;

해당 구조체의 선언은 모두 bluetooth 문서에 나와있습니다. (1252 page)

첫번째 변수부터 알아보겠습니다. 
(1) adv_int_min, adv_int_max : 최소/최대 광고시간 간격 설정입니다.  범위는 20ms~10.24s 입니다.
코드에서는 min/max가 0x20이므로 32*0.625 = 20ms마다 광고하게 되어있습니다.

실제로 광고 간격을 측정해 보면 정확히 20ms로 반복되지 않고, 약 20~30ms 범위에서 랜덤하게 변화하는 것을 확인할 수 있습니다. 이 현상은 BLE 스펙에 의해 정의된 동작으로, 광고 간격(advertising interval)에 추가로 0~10ms의 랜덤 지연이 더해지기 때문입니다. 랜덤 딜레이가 필요한 이유는, BLE 광고가 단일 채널이 아닌 37, 38, 39 총 3개의 채널에서 반복적으로 브로드캐스트되기 때문입니다. 만약 여러 BLE 장치가 동일한 고정 간격(예: 20ms)으로 광고를 전송한다면, 각 장치가 동일한 타이밍에 3개의 채널에서 동시에 송신하게 되어 지속적인 패킷 충돌이 발생할 수 있습니다. 이를 방지하기 위해 BLE는 각 광고 이벤트마다 0~10ms의 랜덤 지연을 적용하여 충돌 패턴이 반복되지 않도록 하고, 결과적으로 광고 수신 성공률을 높입니다.

테스트로 min/max를 1초로 바꾸고 측정을 해보면 약 1초간격으로 광고가 찍힙니다.
min을 0.5초, max를 1초로 바꾸고 측정해봐도 약 1초간격으로 광고가 찍혔습니다.

(2) adv_typ : BLE 장치가 어떤 방식으로 광고할지 정의하는 속성입니다.
광고 타입은 총 5개가 있습니다. 데이터시트에나와있는게 소스코드에도 정의되어있습니다.

typedef enum {
    ADV_TYPE_IND                = 0x00,
    ADV_TYPE_DIRECT_IND_HIGH    = 0x01,
    ADV_TYPE_SCAN_IND           = 0x02,
    ADV_TYPE_NONCONN_IND        = 0x03,
    ADV_TYPE_DIRECT_IND_LOW     = 0x04,
} esp_ble_adv_type_t;

타입을 나누는 기준은 두가지입니다. 실제로 어떤기준인지 알아보겠습니다.
(1) 연결 가능?
아래 그림을보면 왼쪽은 CONNECT버튼이 없고 오른쪽은 CONNECT 버튼이 있습니다.  이게 연결가능? 속성입니다.
연결이 필요없는경우는 일반적으로 데이터 교환이 필요없는 Beacon같은경우입니다. 연결이 필요한경우는 데이터교환이 필요한 온도계같은 경우입니다.

(2) 스캔요청에 응답가능?
스캔요청은 scanner가 device에게 scan을 요청하는 기능입니다.
사용하는 이유는 크게 3가지입니다.
[1] Advertising data의 크기가 제한되어있기 때문입니다. Advertising data의 크기는 31바이트이고 Scan Response Data의 크기는 31바이트입니다.
[2] Scanner가 원할때만 추가 데이터를 제공해서 전력과 채널 사용 효율을 높이기 위함입니다.
[3] 연결전에 device 기능/정체성 확인이 필요하기때문입니다. Scan response에 Profile UUID나 Device Type같은걸 넣으면 탐색이 쉽습니다.

정리하면 아래의 표와 같습니다.

No TYPE conntable scannable
0 ADV_TYPE_IND O O
1 ADV_TYPE_DIRECT_IND_HIGH O X
2 ADV_TYPE_SCAN_IND X O
3 ADV_TYPE_NONCONN_IND X X
4 ADV_TYPE_DIRECT_IND_LOW O X

1,4번은 옵션이 같은데 HIGH/LOW라고 적혀있습니다.
Direct Advertising이란 특정 장치만 연결을 허용하는 기능입니다.
예를 들어 무선 이어폰이 마지막으로 연결된 스마트폰을 다시 찾을 때나, 스마트워치가 사용자의 스마트폰 근처에 있는지 확인할 때 사용됩니다. 이때 광고 패킷 안에는 Target Device Address(연결 대상 장치의 MAC 주소)가 포함되며, 따라서 이 MAC 주소가 일치하는 장치만 연결을 시도할 수 있습니다. Direct Advertising은 다시 두 가지 유형으로 나뉩니다.
HIGH Duty Direct Advertising은 매우 짧은 간격(약 3.75ms~10ms)으로 빠르게 광고하여,
가능한 한 빠른 재연결을 목표로 합니다. Fast Pairing이나 자동 재연결 기능을 구현할 때 사용되며,
사용자가 이어폰 케이스를 열자마자 폰과 즉시 연결되는 경험을 제공하기 위한 방식입니다.
다만 소비 전력이 크기 때문에 스펙상 최대 1.28초까지만 사용이 허용됩니다.
반면, LOW Duty Direct Advertising은 이러한 빠른 연결보다는 배터리 활용 효율을 우선합니다.
간격이 더 길고 전력 소모가 적으며 HIGH Duty 모드 실패 후 이어서 동작하는 경우가 일반적입니다.
즉 HIGH Duty로 빠르게 재연결을 시도하고, 실패하면 LOW Duty 모드로 전환하여
장시간 동안 연결 가능 상태를 유지하는 구조입니다.
정리하면 HIGH Duty는 '빠른 연결 우선', LOW Duty는 '배터리 효율과 지속성 우선'이라는 차이가 있습니다.


3. own_addr_type :BLE장치가 광고하거나 연결할때 어떤종류의 Bluetooth 주소 형식을 사용할지 정의하는 속성입니다.

typedef enum {
    BLE_ADDR_TYPE_PUBLIC        = 0x00,     /*!< Public Device Address */
    BLE_ADDR_TYPE_RANDOM        = 0x01,     /*!< Random Device Address. To set this address, use the function esp_ble_gap_set_rand_addr(esp_bd_addr_t rand_addr) */
    BLE_ADDR_TYPE_RPA_PUBLIC    = 0x02,     /*!< Resolvable Private Address (RPA) with public identity address */
    BLE_ADDR_TYPE_RPA_RANDOM    = 0x03,     /*!< Resolvable Private Address (RPA) with random identity address. To set this address, use the function esp_ble_gap_set_rand_addr(esp_bd_addr_t rand_addr) */
} esp_ble_addr_type_t;


4. channel_map : BLE 광고가 어떤 채널에서 송출될지 결정하는 속성입니다.
BLE 광고는 37,38,39광고에서 송출됩니다. 3개의 채널을 사용하는 이유는 스캔되고 발견될 확률증가, WI-FI 간섭 회피, 패킷손실감소 목적입니다. BLE장비 대부분 37,38,39모두를 사용합니다.

typedef enum {
    ADV_CHNL_37     = 0x01,
    ADV_CHNL_38     = 0x02,
    ADV_CHNL_39     = 0x04,
    ADV_CHNL_ALL    = 0x07,
} esp_ble_adv_channel_t;



5. esp_ble_adv_filter_t : BLE 광고가 어떤 디바이스의 스캔/연결 요청을 허용할지 정하는 보안/동작 정책입니다.

typedef enum {
    ///Allow both scan and connection requests from anyone
    ADV_FILTER_ALLOW_SCAN_ANY_CON_ANY  = 0x00,
    ///Allow both scan req from White List devices only and connection req from anyone
    ADV_FILTER_ALLOW_SCAN_WLST_CON_ANY,
    ///Allow both scan req from anyone and connection req from White List devices only
    ADV_FILTER_ALLOW_SCAN_ANY_CON_WLST,
    ///Allow scan and connection requests from White List devices only
    ADV_FILTER_ALLOW_SCAN_WLST_CON_WLST,
    ///Enumeration end value for advertising filter policy value check
} esp_ble_adv_filter_t;

결론적으로 이 프로젝트에서 광고방식은 광고를 20ms마다 하고, 누구나 스캔할 수 있지만 연결은 불가능한(Scannable Undirected) 방식으로 송출되며, 모든 광고 채널을 사용해 자신의 존재를 알리도록 설정되어 있습니다.



*adv_raw_data, scan_rsp_raw_data 변수
AD types의 기본 포맷은 length, AD Type, AD Data입니다.
AD types에 대해서는 이전 글에 설명이 있습니다.
https://wowon.tistory.com/373

//configure raw data for advertising packet
static uint8_t adv_raw_data[] = {
    0x02, ESP_BLE_AD_TYPE_FLAG, 0x06,
    0x11, ESP_BLE_AD_TYPE_NAME_CMPL, 'B', 'l', 'u', 'e', 'd', 'r', 'o', 'i', 'd', '_', 'B', 'e', 'a', 'c', 'o', 'n',
    0x02, ESP_BLE_AD_TYPE_TX_PWR, 0x09,
    0x03, ESP_BLE_AD_TYPE_APPEARANCE, 0x00,0x02,
    0x02, ESP_BLE_AD_TYPE_LE_ROLE, 0x00,
};

static uint8_t scan_rsp_raw_data[] = {
    0x08, ESP_BLE_AD_TYPE_LE_DEV_ADDR, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x11, ESP_BLE_AD_TYPE_URI, URI_PREFIX_HTTPS, '/', '/', 'e', 's', 'p', 'r', 'e', 's', 's', 'i', 'f', '.', 'c', 'o', 'm',
};

ADV_TYPE_SCAN_IND 타입이므로 스캔요청에 응답이 가능한 타입입니다.
그래서 앱을보면 BT 주소, URI이 나와있습니다. 스캔요청이 불가능한 ADV_TYPE_NONCONN_IND타입으로 바꿔서 확인해보겠습니다.

ADV_TYPE_NONCONN_IND 타입은 스캔 데이터가 없는 것을 확인할 수 있습니다.

다음글에서는 BLE가 광고되는 흐름에대해 알아보겠습니다.