Once NimBLE host stack is synced with BLE controller, `on_stack_sync` in `gap.c` will be called by NimBLE host stack, which has been configured in `nimble_host_config_init`.
In this function, we will call `adv_init` function to ask NimBLE host stack to check if device MAC address is available by `ble_hs_util_ensure_addr` and `ble_hs_id_infer_auto` functions. If so, we will copy the address and try to start advertising by calling `start_advertising` in the same source file.
``` C
static void on_stack_sync(void) {
/* On stack sync, do advertising initialization */
adv_init();
}
void adv_init(void) {
/* Local variables */
int rc = 0;
char addr_str[12] = {0};
/* Make sure we have proper BT identity address set */
rc = ble_hs_util_ensure_addr(0);
if (rc != 0) {
ESP_LOGE(TAG, "device does not have any available bt address!");
return;
}
/* Figure out BT address to use while advertising */
rc = ble_hs_id_infer_auto(0, &own_addr_type);
if (rc != 0) {
ESP_LOGE(TAG, "failed to infer address type, error code: %d", rc);
ESP_LOGE(TAG, "failed to copy device address, error code: %d", rc);
return;
}
format_addr(addr_str, addr_val);
ESP_LOGI(TAG, "device address: %s", addr_str);
/* Start advertising. */
start_advertising();
}
```
### Start Advertising
As a beacon device, we're going to start advertising and send scan response if a scan request is received. To make it happen, we need to set advertisement and scan response data before advertising starts. So the following are what we do:
1. Initialize advertisement and scan response fields structs `adv_fields` and `rsp_fields`, as well as advertising parameters struct `adv_params`
``` C
static void start_advertising(void) {
/* Local variables */
int rc = 0;
const char *name;
struct ble_hs_adv_fields adv_fields = {0};
struct ble_hs_adv_fields rsp_fields = {0};
struct ble_gap_adv_params adv_params = {0};
...
}
```
2. Set advertising flags, device name, transmit power, appearance and LE role in `adv_fields`, and call `ble_gap_adv_set_fields`
1. For adveritisng flags, `BLE_HS_ADV_F_DISC_GEN` means advertising is general discoverable, and `BLE_HS_ADV_F_BREDR_UNSUP` means BLE support only (BR/EDR refers to Bluetooth Classic)
2. For appearance, it is used to tell scanner what does it look like; we use `BLE_GAP_APPEARANCE_GENERIC_TAG` here to make our device identified as a tag
ESP_LOGE(TAG, "failed to set advertising data, error code: %d", rc);
return;
}
...
}
```
3. Set device address and URI in `rsp_fields`, and call `ble_gap_adv_rsp_set_fields`
1. Since `AdvData` in advertisement packet **should not be longer than 31 bytes**, additional information must be placed in scan response packet
2. We put the official website link of espressif into URI field
``` C
static void start_advertising(void) {
...
/* Set device address */
rsp_fields.device_addr = addr_val;
rsp_fields.device_addr_type = own_addr_type;
rsp_fields.device_addr_is_present = 1;
/* Set URI */
rsp_fields.uri = esp_uri;
rsp_fields.uri_len = sizeof(esp_uri);
/* Set scan response fields */
rc = ble_gap_adv_rsp_set_fields(&rsp_fields);
if (rc != 0) {
ESP_LOGE(TAG, "failed to set scan response data, error code: %d", rc);
return;
}
...
}
```
4. Set advertising mode and discoverable mode to non-connectable and general-discoverable respectively in `adv_params`, and finally, start advertising by calling `ble_gap_adv_start`
``` C
static void start_advertising(void) {
...
/* Set non-connetable and general discoverable mode to be a beacon */
ESP_LOGE(TAG, "failed to start advertising, error code: %d", rc);
return;
}
ESP_LOGI(TAG, "advertising started!");
}
```
### Observation
If everything goes well, you should be able to see `NimBLE_Beacon` on a BLE scanner device, broadcasting a lot of information including an URI of "https://espressif.com" (The official website of espressif), which is exactly what we expect.
## Troubleshooting
For any technical queries, please file an [issue](https://github.com/espressif/esp-idf/issues) on GitHub. We will get back to you soon.