GAP

The Generic Access Profile (GAP) defines device discovery, link management, and related security procedures. In BLE, several modes and procedures are defined for different scenarios:

  • Broadcast mode and observation procedure. This mode and procedure are suitable for connectionless communication scenarios, such as beacons.

    • A device in broadcast mode is called a broadcaster. It will send non-connectable advertisements with specified data.

    • A device implementing the observation procedure is called an observer. It will scan non-connectable advertisements to obtain data.

  • Discovery mode and procedure, connection mode and procedure. These two sets of modes and procedures are for link establishment in different scenarios, such as directed connection and normal connection.

    • A device that accepts link establishment is called a peripheral. It will send connectable or directed connectable advertisements.

    • A device that initiates link establishment is called a central. It will scan advertisements and connect to a specified peer address through advertisements.

  • Pairing mode and procedure. The mode and procedure are to set the link to different security levels.

GAP provides peripheral APIs to manage advertising, connection, and security.

For GAP API details, please refer to @ref GAP.

Bluetooth Low Energy Address Types

Bluetooth LE has 4 address types: 1. Public device address, assigned by SIG, is a unique address. 1. Static random device address is randomly generated and should not change during power cycles. The highest 2 bits should be 0x11. 1. Private device address is used to protect device addresses and can be changed within minutes. Devices using private device addresses must have an identity address, which can be either a public address or a static random address. 1. Resolvable private device address, generated by the local Identity Resolving Key (IRK). It can be resolved by peer devices that have the IRK. The IRK will be exchanged during bonding. The highest 2 bits should be 0x01. 1. Non-resolvable private device address is randomly generated. It cannot be resolved. The highest 2 bits should be 0x00.

Users can use these 4 address types in different scenarios:

  • In general cases, public addresses or static random addresses should be used.

  • In security-sensitive connectable situations, resolvable private device addresses should be used.

  • In security-sensitive non-connectable situations, non-resolvable private addresses should be used.

Advertising

Bluetooth LE has 40 physical channels (indexed from 0~39) from 2.4GHz - 2.4835Hz, where channels 37, 38, and 39 are allocated for advertising. Advertisements sent sequentially on channels 37, 38, or 39 are called advertising events. The distance between two starts of adjacent advertising events is called the advertising interval. Channels can be configured through the advertising parameter channel_map to any of channels 37, 38, 39.

If the interval is smaller, the advertising is denser, and the central device can more easily scan the advertisement or connect faster, while power consumption is higher. In contrast, the larger the interval, the lower the power consumption, but scanning or connecting will be more difficult.

A special case is high duty cycle directed connectable advertising. Directed advertising is sent to a specified peer address device, making it carry the peer address instead of advertising data or scan response data. High duty cycle expects the peer device to quickly initiate link establishment and advertise at a very high frequency. In this case, no advertising interval is used. Due to the high frequency, the advertising duration cannot exceed 1.28s.

Connection Parameters

In the controller, the device that initiates link establishment is called the master, and the device that accepts link establishment is called the slave. After link establishment, connection events are used to exchange data packets. During a connection event, master and slave alternately send and receive data. The master sends data first and receives data, while the slave receives data and responds with data. In a normal connection event, both master and slave should send at least one data packet.

There are many parameters that affect connection behavior:

  • The length of a connection event is called CE length. The parameters max_ce_len and min_ce_len determine the length.

  • The distance between two consecutive connection events is called the connection interval.

  • Slave latency allows the slave to not respond to the master in some connection events. For example, if slave latency is 2, the slave can respond to the master every two connection events.

  • The maximum interval between two data receptions is called supervision timeout. If no data is received within this interval, it indicates an abnormal connection. There are many reasons for timeout, such as one device being out of range, a device suddenly shutting down, or stack corruption.

Connection Parameters

Slave Latency

Throughput and Low Power Consumption

Throughput can be simply calculated by Dsize (data size transmitted in one connection event) * Dcount (connection event count per second, 1/connection_interval). The larger Dsize and Dcount, the higher the throughput. Dcount is only determined by the connection interval; the smaller the interval, the larger the count. The minimum connection interval is 7.5 ms, and typically 15 - 48 ms for high throughput. Dsize is more complex, and users can improve it using the following methods: - Ensure both master and slave support the Data Length Extension (DLE) feature, which is an optional feature after Bluetooth Core Specification 4.2. This feature allows the maximum packet size to increase from 27 bytes to 251 bytes. Each packet has certain headers, and when Dsize increases, the number of packets decreases, and header overhead is reduced.

- Ensure both master and slave support the 2M PHY feature supported by Bluetooth Core Specification 5.0. This feature allows the physical rate to increase from 1M to 2M.

- Prepare sufficient data for transmission. From the connection parameter description, master and slave alternately send and receive data. If there is no more data to send, the connection event ends. Data requires some time to prepare;
if the controller has no data to transmit in sequence, data can only be transmitted in the next connection event. For example, if the host sends one packet and needs the slave to respond before sending the next packet, two connection events can only send one packet.
But if the host sends two packets and then needs the slave to respond, two connection events can send two packets.
![](../../../assets/slave_respond.png)

If no data needs to be transmitted, devices can change parameters to enter low-power transmission mode. There are 2 methods: 1. Use a larger connection interval, for example, changing the connection interval from 30 to 500 ms. This method can reduce both master and slave transmission power. However, there will be some delay when switching back to high throughput intervals. Typically, connection interval switching requires at least 6 connection events. If switching from 500 ms to 30 ms, the first 3 seconds (500 * 6) still use the 500 ms interval. 2. Enable slave latency, allowing the slave to skip several connection events to respond. It can solve the delay issue, but the host cannot save transmission power. For example, set interval to 30 ms and slave latency to 20. Then the master transmits every 30 ms, while the slave transmits every 600 ms. When the slave wants to send data, it can only respond every 30 ms.

SMP

The Security Manager Profile (SMP) defines protocols and behaviors for generating and distributing security keys and encrypting links with these keys. Related APIs are integrated with GAP. There are four functions in SMP: - Pairing: Creating shared keys. - Bonding: Storing keys created by pairing and used for subsequent connections. After bonding, two devices become a trusted device pair. - Authentication: Verifying that two devices have the same keys. - Encryption: Encrypting the link to ensure secure communication.

As shown in the SMP flow diagram, the master can initiate a pairing request to trigger the pairing procedure, while the slave can initiate a security request to request the master to initiate a pairing request.

SMP Flow

In the SIFLI BLE SDK, for the slave role, most procedures are encapsulated in the stack, and users only need to handle #BLE_GAP_BOND_IND and #BLE_GAP_ENCRYPT_IND.

The default authentication method is just works. To modify the pairing authentication method to passkey entry or numeric comparison, you need to call the following function to set the SDK’s IO capabilities:

uint8_t connection_manager_set_bond_cnf_iocap(uint8_t iocap)

Also call the function to set pairing confirmation to BOND_PENDING:

uint8_t connection_manager_set_bond_ack(uint8_t state)

When BOND_PENDING is set, users need to handle the #CONNECTION_MANAGER_BOND_AUTH_INFOR message to reply to the corresponding pairing event:

case CONNECTION_MANAGER_BOND_AUTH_INFOR:
{
	connection_manager_bond_ack_infor_t *info = (connection_manager_bond_ack_infor_t *)data;
	if (info->request == GAPC_PAIRING_REQ)
	{
		// Peer-initiated pairing event, can show popup or other handling, finally need to call ack_reply below
		connection_manager_bond_ack_reply(info->conn_idx, GAPC_PAIRING_REQ, true);
	}
	else if (info->request == GAPC_TK_EXCH)
	{
		uint32_t pin_code = info->confirm_data;
		// TODO: Display passkey
		// Reply to peer
		connection_manager_bond_ack_reply(info->conn_idx, GAPC_TK_EXCH, true);
	}
	else if (info->request == GAPC_NC_EXCH)
	{
		uint32_t nc_number = confirm_data;
		// TODO: Display nc number
		// Reply to peer
		connection_manager_bond_ack_reply(info->conn_idx, GAPC_NC_EXCH, true);
	}
	break;
}

Message Flow

  • Advertising procedure:

  • Connection procedure:

  • Connection parameter update procedure:

  • Disconnection procedure:

Examples

  • Advertising

To enable advertising, users need to: 1. Create advertising with specified parameters. 1. Set advertising data and scan response data. 1. Start advertising with duration.


static uint8_t adv_idx;

/** advertising data format. Flag refers to @ble_gap_adv_type.
	   
 * ------------------------------------------------------------------
 *  | Length(1B) | Flag(1B) |  Value  |  Value  |   ...   |   Value  | 
 *	------------------------------------------------------------------
*/

uint8_t adv_data[] = {0x07, 0x09, 0x53, 0x69, 0x66, 0x6c, 0x69, 0x00} // full name: sifli
uint8_t scan_rsp[] = {0x07, 0xff, 0x01, 0x00, 0x01, 0x02, 0x03, 0x04} // Manufacturer specified data, company id : 0x01, additional data: 0x01, 0x02, 0x03, 0x04

int app_advertising_evt_handler(uint16_t event_id, uint8_t *data, uint16_t len, uint32_t context)
{
	uint8_t ret;
    switch (event_id)
    {
    case BLE_GAP_ADV_CREATED_IND:
    {
		// If create advertising successfully, user will receive BLE_GAP_ADV_CREATED_IND.
        ble_gap_adv_created_ind_t *ind = (ble_gap_adv_created_ind_t *)data;

		// Store adv index for following API usage.
        adv_idx = ind->actv_idx;
        ble_gap_adv_start_t adv_start;

		// Set advertising data.
        ret = ble_gap_set_adv_data(adv_data);
        LOG("Set adv data result %d", ret);
		
		// Set scan response data.
		ble_gap_set_scan_rsp_data(scan_rsp);
        LOG("Set scan rsp result %d", ret);
        
		adv_start.actv_idx = ind->actv_idx;

        adv_start.duration = 0;
        adv_start.max_adv_evt = 0;
		
        ret = ble_gap_start_advertising(&adv_start);
        LOG("start adv result %d", ret);
        break;
    }
	case BLE_GAP_SET_ADV_DATA_CNF:
	{
		ble_gap_set_adv_data_cnf_t *cnf = (ble_gap_set_adv_data_cnf_t *)data;
		LOG("Set adv cnf result %d", cnf->status);
		break;
	}
	case BLE_GAP_SET_SCAN_RSP_DATA_CNF:
	{
		ble_gap_set_scan_rsp_data_cnf_t *cnf = (ble_gap_set_scan_rsp_data_cnf_t *)data;
		LOG("Set scan rsp cnf result %d", cnf->status);
		break;
	}
    case BLE_GAP_START_ADV_CNF:
    {
        ble_gap_start_adv_cnf_t *cnf = (ble_gap_start_adv_cnf_t*)data;
		LOG("Start adv cnf result %d", cnf->result);
        break;

    }
    case BLE_GAP_ADV_STOPPED_IND:
    {
		// Duration timeout will notify with reason #GAP_ERR_TIMEOUT
        ble_gap_adv_stopped_ind_t *ind = (ble_gap_adv_stopped_ind_t *)data;
		LOG("ADV stopped reason %d", ind->reason);
        break;
    }
    case BLE_GAP_STOP_ADV_CNF:
    {
        ble_gap_stop_adv_cnf_t *cnf = (ble_gap_stop_adv_cnf_t *)data;
		LOG("ADV stop adv cnf result %d", cnf->status);
        break;
    }
    default:
        break;
    }
    return 0;

}

// Create advertising
void app_start_advertising(void *para)
{
    // Prepare advertising parameter
    ble_gap_adv_parameter_t adv_para;
	uint8_t ret;

	// Set parameter via user's scenario.
	adv_para.own_addr_type = GAPM_STATIC_ADDR;
    adv_para.type = GAPM_ADV_TYPE_LEGACY;
    adv_para.disc_mode = GAPM_ADV_MODE_NON_DISC;
    adv_para.max_tx_pwr = 0;
    adv_para.filter_pol = ADV_ALLOW_SCAN_ANY_CON_ANY;
	adv_para.prop = GAPM_ADV_PROP_UNDIR_CONN_MASK;
    adv_para.prim_cfg.adv_intv_max = 0x40;
    adv_para.prim_cfg.adv_intv_min = 0x20;
	adv_para.prim_cfg.chnl_map = 0x07;
	adv_para.prim_cfg.phy = GAP_PHY_TYPE_LE_1M;
	
	// Create advertising.
	ret = ble_gap_create_advertising(&para);
	LOG("Create ADV result %d", ret);
}

// Stop advertising
void app_stop_advertising(void *para)
{
	ble_gap_adv_stop_t stop_req;
	uint8_t ret;
	
	stop_req.actv_idx = adv_idx;
	ble_gap_stop_advertising(&stop_req)
	LOG("Stop ADV result %d", ret);
}

BLE_EVENT_REGISTER(sibles_advertising_evt_handler, NULL);

  • Connection

Acting as a peripheral role, users only need to enable advertising and wait for the central device to connect.

Besides data transmission, the following sub-procedures are also important in GAP: 1. Connection parameter update procedure. The peripheral may need the central to modify some parameters, such as connection interval and supervision timeout, to balance high throughput and low power consumption. 2. Disconnection procedure. Disconnect from the central device.


static uint8_t conn_idx;

int app_connection_evt_handler(uint16_t event_id, uint8_t *data, uint16_t len, uint32_t context)
{
    case BLE_GAP_CONNECTED_IND:
    {
        ble_gap_connect_ind_t *ind = (ble_gap_connect_ind_t *)data;
		// Store the conn idx for following usage.
        conn_idx = ind->conn_idx;
		LOG("conn interval:%d, conn timeout:%d", ind->con_interval, ind->sup_to);
        break;
    }
	case BLE_GAP_UPDATE_CONN_PARAM_IND:
	{
		// Get the adjusted parameters.
		ble_gap_update_conn_param_ind_t *ind = (ble_gap_update_conn_param_ind_t *)data;
		if (ind->conn_idx == conn_idx) // Make sure the conn_idx is the same device.
			LOG("new conn interval :%d, conn timeout :%d", ind->con_interval, ind->sup_to);
		break;
	}
	case BLE_GAP_UPDATE_CONN_PARAM_CNF:
	{
		// Check whether update was successful.
		BLE_GAP_UPDATE_CONN_PARAM_CNF *cnf = (ble_gap_update_conn_param_cnf_t *)data;
		if (ind->conn_idx == conn_idx) // Make sure the conn_idx is the same device.
			LOG("conn update reason %d", cnf->reason);
		break;
	}
	case BLE_GAP_DISCONNECTED_IND:
	{
		// Receiving this event indicates connection is disconnected and can check the disconnection reason.
		ble_gap_disconnected_ind_t *ind = (ble_gap_disconnected_ind_t *)data;
		if (ind->conn_idx == conn_idx) // Make sure the conn_idx is the same device.
			LOG("disconnect reason %d", ind->reason);
		break;
	}
    default:
        break;
    }
}

void app_update_connection_parameter(void *para)
{
	ble_gap_update_conn_param_t conn_para;
	uint8_t ret;
	
	// Request new parameters.
	conn_para.conn_idx = conn_idx;
	conn_para.intv_min = 0x20;
	conn_para.intv_max = 0x30;
	conn_para.latency = 0x0;
	conn_para.time_out = 500;
	conn_para.ce_len_min = 20;
	conn_para.ce_max_len = 100;
	
	ret = ble_gap_update_conn_param(&conn_para);
	LOG("Update conn parameter ret %d", ret);
}

void app_disconnect(void *para)
{
    ble_gap_disconnect_t disc_cmd;
	
	disc_cmd.conn_idx = conn_idx;
	disc_cmd.reason = 0x13;
	
	ret = ble_gap_disconnect(&disc_cmd);
	LOG("Disconnect ret %d", ret);
	
}

BLE_EVENT_REGISTER(app_connection_evt_handler, NULL);