GAP
通用访问配置文件是处理连接任务的堆栈层。 这包括链接建立和终止,广告和扫描。
具有要发布的数据的设备可以使用 GAP 进行广告。它们可以在广告本身中包含数据,在扫描响应内部,或者在建立连接后让对等设备对其进行查询。
该过程的另一方面是扫描行为,其侦听广告,允许您通过扫描请求查询广告商以获取更多数据,或者连接以便向对等设备查询所需数据。
广告,扫描和连接都有参数,可让您在所需的功耗水平,延迟和这些过程的效率之间找到折衷方案。
广告
广告包括定期广播包含有关设备的有价值信息的少量数据。在 BLE 广告信道上监听的对等设备可以扫描这些分组。
扫描仪还可以通过发送扫描请求从设备广告中请求附加信息。如果广播公司接受扫描请求,则它可以使用包含附加信息的扫描响应包进行回复。
扫描
扫描包括侦听对等广告包。通过扫描,设备可以识别其环境中可用的设备。
如果设备主动扫描,它会向可扫描广告商发送扫描请求并收集其扫描响应。
隐私
隐私是一种允许设备避免被其他(不受信任的)设备跟踪的功能。该设备通过周期性地生成新的随机地址来实现它。随机地址可以是可解析的随机地址,使得可信设备能够将其识别为属于同一设备。这些可信设备在配对期间接收身份解析密钥(IRK)。 SecurityManager 处理此问题并依赖于接受和存储 IRK 的其他设备。
您需要在初始化 SecurityManager 之后调用 enablePrivacy() 来启用隐私,因为隐私需要 SecurityManager 来处理 IRK。使用 setCentralPrivacyConfiguration() 设置启用隐私的设备的行为,该设置指定设备使用随机地址的设备应该是什么,以及 setPeripheralPrivacyConfiguration。启用隐私的设备生成的随机地址可以是两种类型:可解析(由具有 IRK 的设备)和不可解析的。您不能使用无法解析的地址进行连接和可连接的广告;因此,无论隐私配置如何,都可以使用可解析的。
调制方案
当主机和控制器支持时,您可以选择不同的调制方案:
- LE 1M PHY。
- LE 2M PHY。
- LE coded PHY。
这些在带宽,功率使用和错误恢复能力之间提供了不同的折衷(参见 BLUETOOTH SPECIFICATION Version 5.0 Vol 1,Part A-1.2)。
您可以使用 setPreferredPhys() 设置首选 PHY(分别用于 RX 和 TX)。您还可以使用 setPhy() 在所选连接上设置当前使用的 PHY。这两个设置都只是建议性的,并且允许控制器根据您的请求,对等方支持的功能和连接的物理条件,自行决定使用最佳PHY。
您可以使用 readPhy() 查询当前使用的 PHY,它通过调用注册的事件处理程序返回结果。您可以使用 setEventHandler() 注册该处理程序。事件通知当前使用的 PHY 以及 PHY 的任何变化,控制器或对等体可以自动触发。
GAP 类参考
数据结构 | |
struct | AdvertisementCallbackParams_t |
struct | CentralPrivacyConfiguration_t |
struct | ConnectionCallbackParams_t |
struct | ConnectionParams_t |
struct | DisconnectionCallbackParams_t |
struct | GapState_t |
struct | PeripheralPrivacyConfiguration_t |
struct | Whitelist_t |
静态公共成员函数 | |
static uint16_t | MSEC_TO_GAP_DURATION_UNITS (uint32_t durationInMillis) |
static ble_error_t | getRandomAddressType (const BLEProtocol::AddressBytes_t address, RandomAddressType_t *addressType) |
静态公共属性 | |
static const unsigned | ADDR_LEN = BLEProtocol::ADDR_LEN |
static const uint16_t | UNIT_1_25_MS = 1250 |
static const PeripheralPrivacyConfiguration_t | default_peripheral_privacy_configuration |
static const CentralPrivacyConfiguration_t | default_central_privacy_configuration |
受保护的成员函数 | |
virtual ble_error_t | startRadioScan (const GapScanningParams &scanningParams) |
Gap () |
GAP 示例
这是一个示例,演示如何使用 GAP API 来通告,扫描,连接和断开连接以及参数如何影响这些操作的效率。
/* mbed Microcontroller Library
* Copyright (c) 2006-2013 ARM Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <events/mbed_events.h>
#include <mbed.h>
#include "ble/BLE.h"
/** This example demonstrates all the basic setup required
* to advertise, scan and connect to other devices.
*
* It contains a single class that performs both scans and advertisements.
*
* The demonstrations happens in sequence, after each "mode" ends
* the demo jumps to the next mode to continue. There are several modes
* that show scanning and several showing advertising. These are configured
* according to the two arrays containing parameters. During scanning
* a connection will be made to a connectable device upon its discovery.
*/
static const uint8_t DEVICE_NAME[] = "GAP_device";
/* Duration of each mode in milliseconds */
static const size_t MODE_DURATION_MS = 6000;
/* Time between each mode in milliseconds */
static const size_t TIME_BETWEEN_MODES_MS = 2000;
/* how long to wait before disconnecting in milliseconds */
static const size_t CONNECTION_DURATION = 3000;
typedef struct {
GapAdvertisingParams::AdvertisingType_t adv_type;
uint16_t interval;
uint16_t timeout;
} AdvModeParam_t;
typedef struct {
uint16_t interval;
uint16_t window;
uint16_t timeout;
bool active;
} ScanModeParam_t;
/** the entries in this array are used to configure our advertising
* parameters for each of the modes we use in our demo */
static const AdvModeParam_t advertising_params[] = {
/* advertising type interval timeout */
{ GapAdvertisingParams::ADV_CONNECTABLE_UNDIRECTED, 40,/*ms*/ 3/*s*/},
{ GapAdvertisingParams::ADV_SCANNABLE_UNDIRECTED, 100, 4 },
{ GapAdvertisingParams::ADV_NON_CONNECTABLE_UNDIRECTED, 100, 0 }
};
/* when we cycle through all our advertising modes we will move to scanning modes */
/** the entries in this array are used to configure our scanning
* parameters for each of the modes we use in our demo */
static const ScanModeParam_t scanning_params[] = {
/* interval window timeout active */
{ 4,/*ms*/ 4,/*ms*/ 0,/*s*/ false },
{ 160, 100, 3, false },
{ 160, 40, 0, true },
{ 500, 10, 0, false }
};
/* parameters to use when attempting to connect to maximise speed of connection */
static const GapScanningParams connection_scan_params(
GapScanningParams::SCAN_INTERVAL_MAX,
GapScanningParams::SCAN_WINDOW_MAX,
3,
false
);
/* get number of items in our arrays */
static const size_t SCAN_PARAM_SET_MAX =
sizeof(scanning_params) / sizeof(GapScanningParams);
static const size_t ADV_PARAM_SET_MAX =
sizeof(advertising_params) / sizeof(GapAdvertisingParams);
/** Demonstrate advertising, scanning and connecting
*/
class GAPDevice : private mbed::NonCopyable<GAPDevice>
{
public:
GAPDevice() :
_ble(BLE::Instance()),
_led1(LED1, 0),
_set_index(0),
_is_in_scanning_mode(false),
_is_connecting(false),
_on_duration_end_id(0),
_scan_count(0) { };
~GAPDevice()
{
if (_ble.hasInitialized()) {
_ble.shutdown();
}
};
/** Start BLE interface initialisation */
void run()
{
ble_error_t error;
if (_ble.hasInitialized()) {
printf("Ble instance already initialised.\r\n");
return;
}
/* this will inform us off all events so we can schedule their handling
* using our event queue */
_ble.onEventsToProcess(
makeFunctionPointer(this, &GAPDevice::schedule_ble_events)
);
/* handle timeouts, for example when connection attempts fail */
_ble.gap().onTimeout(
makeFunctionPointer(this, &GAPDevice::on_timeout)
);
error = _ble.init(this, &GAPDevice::on_init_complete);
if (error) {
printf("Error returned by BLE::init.\r\n");
return;
}
/* to show we're running we'll blink every 500ms */
_event_queue.call_every(500, this, &GAPDevice::blink);
/* this will not return until shutdown */
_event_queue.dispatch_forever();
};
private:
/** This is called when BLE interface is initialised and starts the first mode */
void on_init_complete(BLE::InitializationCompleteCallbackContext *event)
{
if (event->error) {
printf("Error during the initialisation\r\n");
return;
}
/* print device address */
Gap::AddressType_t addr_type;
Gap::Address_t addr;
_ble.gap().getAddress(&addr_type, addr);
printf("Device address: %02x:%02x:%02x:%02x:%02x:%02x\r\n",
addr[5], addr[4], addr[3], addr[2], addr[1], addr[0]);
/* all calls are serialised on the user thread through the event queue */
_event_queue.call(this, &GAPDevice::demo_mode_start);
};
/** queue up start of the current demo mode */
void demo_mode_start()
{
if (_is_in_scanning_mode) {
/* when scanning we want to connect to a peer device so we need to
* attach callbacks that are used by Gap to notify us of events */
_ble.gap().onConnection(this, &GAPDevice::on_connect);
_ble.gap().onDisconnection(this, &GAPDevice::on_disconnect);
_event_queue.call(this, &GAPDevice::scan);
} else {
_event_queue.call(this, &GAPDevice::advertise);
}
/* for performance measurement keep track of duration of the demo mode */
_demo_duration.start();
/* keep track of our state */
_is_connecting = false;
/* queue up next demo mode */
_on_duration_end_id = _event_queue.call_in(
MODE_DURATION_MS, this, &GAPDevice::on_duration_end
);
printf("\r\n");
}
/** Set up and start advertising */
void advertise()
{
ble_error_t error;
GapAdvertisingData advertising_data;
/* add advertising flags */
advertising_data.addFlags(GapAdvertisingData::LE_GENERAL_DISCOVERABLE
| GapAdvertisingData::BREDR_NOT_SUPPORTED);
/* add device name */
advertising_data.addData(
GapAdvertisingData::COMPLETE_LOCAL_NAME,
DEVICE_NAME,
sizeof(DEVICE_NAME)
);
error = _ble.gap().setAdvertisingPayload(advertising_data);
if (error) {
printf("Error during Gap::setAdvertisingPayload\r\n");
return;
}
/* set the advertising parameters according to currently selected set,
* see @AdvertisingType_t for explanation of modes */
GapAdvertisingParams::AdvertisingType_t adv_type =
advertising_params[_set_index].adv_type;
/* how many milliseconds between advertisements, lower interval
* increases the chances of being seen at the cost of more power */
uint16_t interval = advertising_params[_set_index].interval;
/* advertising will continue for this many seconds or until connected */
uint16_t timeout = advertising_params[_set_index].timeout;
_ble.gap().setAdvertisingType(adv_type);
_ble.gap().setAdvertisingInterval(interval);
_ble.gap().setAdvertisingTimeout(timeout);
error = _ble.gap().startAdvertising();
if (error) {
printf("Error during Gap::startAdvertising.\r\n");
return;
}
printf("Advertising started (type: 0x%x, interval: %dms, timeout: %ds)\r\n",
adv_type, interval, timeout);
};
/** Set up and start scanning */
void scan()
{
ble_error_t error;
/* scanning happens repeatedly, interval is the number of milliseconds
* between each cycle of scanning */
uint16_t interval = scanning_params[_set_index].interval;
/* number of milliseconds we scan for each time we enter
* the scanning cycle after the interval set above */
uint16_t window = scanning_params[_set_index].window;
/* how long to repeat the cycles of scanning in seconds */
uint16_t timeout = scanning_params[_set_index].timeout;
/* active scanning will send a scan request to any scanable devices that
* we see advertising */
bool active = scanning_params[_set_index].active;
/* set the scanning parameters according to currently selected set */
error = _ble.gap().setScanParams(interval, window, timeout, active);
if (error) {
printf("Error during Gap::setScanParams\r\n");
return;
}
/* start scanning and attach a callback that will handle advertisements
* and scan requests responses */
error = _ble.gap().startScan(this, &GAPDevice::on_scan);
if (error) {
printf("Error during Gap::startScan\r\n");
return;
}
printf("Scanning started (interval: %dms, window: %dms, timeout: %ds).\r\n",
interval, window, timeout);
};
/** After a set duration this cycles to the next demo mode
* unless a connection happened first */
void on_duration_end()
{
print_performance();
/* alloted time has elapsed, move to next demo mode */
_event_queue.call(this, &GAPDevice::demo_mode_end);
};
/** Look at scan payload to find a peer device and connect to it */
void on_scan(const Gap::AdvertisementCallbackParams_t *params)
{
/* keep track of scan events for performance reporting */
_scan_count++;
/* don't bother with analysing scan result if we're already connecting */
if (_is_connecting) {
return;
}
/* parse the advertising payload, looking for a discoverable device */
for (uint8_t i = 0; i < params->advertisingDataLen; ++i) {
/* The advertising payload is a collection of key/value records where
* byte 0: length of the record excluding this byte
* byte 1: The key, it is the type of the data
* byte [2..N] The value. N is equal to byte0 - 1 */
const uint8_t record_length = params->advertisingData[i];
if (record_length == 0) {
continue;
}
const uint8_t type = params->advertisingData[i + 1];
const uint8_t *value = params->advertisingData + i + 2;
/* connect to a discoverable device */
if ((type == GapAdvertisingData::FLAGS)
&& (*value & GapAdvertisingData::LE_GENERAL_DISCOVERABLE)) {
/* abort timeout as the mode will end on disconnection */
_event_queue.cancel(_on_duration_end_id);
printf("We found a connectable device\r\n");
ble_error_t error = _ble.gap().connect(
params->peerAddr, Gap::ADDR_TYPE_RANDOM_STATIC,
NULL, &connection_scan_params
);
if (error) {
printf("Error during Gap::connect\r\n");
/* since no connection will be attempted end the mode */
_event_queue.call(this, &GAPDevice::demo_mode_end);
return;
}
/* we may have already scan events waiting
* to be processed so we need to remember
* that we are already connecting and ignore them */
_is_connecting = true;
return;
}
i += record_length;
}
};
/** This is called by Gap to notify the application we connected,
* in our case it immediately disconnects */
void on_connect(const Gap::ConnectionCallbackParams_t *connection_event)
{
print_performance();
printf("Connected in %dms\r\n", _demo_duration.read_ms());
/* cancel the connect timeout since we connected */
_event_queue.cancel(_on_duration_end_id);
_event_queue.call_in(
CONNECTION_DURATION, &_ble.gap(), &Gap::disconnect, Gap::REMOTE_USER_TERMINATED_CONNECTION
);
};
/** This is called by Gap to notify the application we disconnected,
* in our case it calls demo_mode_end() to progress the demo */
void on_disconnect(const Gap::DisconnectionCallbackParams_t *event)
{
printf("Disconnected\r\n");
/* we have successfully disconnected ending the demo, move to next mode */
_event_queue.call(this, &GAPDevice::demo_mode_end);
};
/** called if timeout is reached during advertising, scanning
* or connection initiation */
void on_timeout(const Gap::TimeoutSource_t source)
{
_demo_duration.stop();
switch (source) {
case Gap::TIMEOUT_SRC_ADVERTISING:
printf("Stopped advertising early due to timeout parameter\r\n");
break;
case Gap::TIMEOUT_SRC_SCAN:
printf("Stopped scanning early due to timeout parameter\r\n");
break;
case Gap::TIMEOUT_SRC_CONN:
printf("Failed to connect after scanning %d advertisements\r\n", _scan_count);
_event_queue.call(this, &GAPDevice::print_performance);
_event_queue.call(this, &GAPDevice::demo_mode_end);
break;
default:
printf("Unexpected timeout\r\n");
break;
}
};
/** clean up after last run, cycle to the next mode and launch it */
void demo_mode_end()
{
/* reset the demo ready for the next mode */
_scan_count = 0;
_demo_duration.stop();
_demo_duration.reset();
/* cycle through all demo modes */
_set_index++;
/* switch between advertising and scanning when we go
* through all the params in the array */
if (_set_index >= (_is_in_scanning_mode? SCAN_PARAM_SET_MAX : ADV_PARAM_SET_MAX)) {
_set_index = 0;
_is_in_scanning_mode = !_is_in_scanning_mode;
}
_ble.shutdown();
_event_queue.break_dispatch();
};
/** print some information about our radio activity */
void print_performance()
{
/* measure time from mode start, may have been stopped by timeout */
uint16_t duration = _demo_duration.read_ms();
if (_is_in_scanning_mode) {
/* convert ms into timeslots for accurate calculation as internally
* all durations are in timeslots (0.625ms) */
uint16_t interval_ts = GapScanningParams::MSEC_TO_SCAN_DURATION_UNITS(
scanning_params[_set_index].interval
);
uint16_t window_ts = GapScanningParams::MSEC_TO_SCAN_DURATION_UNITS(
scanning_params[_set_index].window
);
uint16_t duration_ts = GapScanningParams::MSEC_TO_SCAN_DURATION_UNITS(
duration
);
/* this is how long we scanned for in timeslots */
uint16_t rx_ts = (duration_ts / interval_ts) * window_ts;
/* convert to milliseconds */
uint16_t rx_ms = (rx_ts * GapScanningParams::UNIT_0_625_MS) / 1000;
printf("We have scanned for %dms with an interval of %d"
" timeslot and a window of %d timeslots\r\n",
duration, interval_ts, window_ts);
printf("We have been listening on the radio for at least %dms\r\n", rx_ms);
} else /* advertising */ {
/* convert ms into timeslots for accurate calculation as internally
* all durations are in timeslots (0.625ms) */
uint16_t interval_ts = GapAdvertisingParams::MSEC_TO_ADVERTISEMENT_DURATION_UNITS(
advertising_params[_set_index].interval
);
uint16_t duration_ts = GapAdvertisingParams::MSEC_TO_ADVERTISEMENT_DURATION_UNITS(
duration
);
/* this is how many times we advertised */
uint16_t events = duration_ts / interval_ts;
printf("We have advertised for %dms"
" with an interval of %d timeslots\r\n",
duration, interval_ts);
/* non-scannable and non-connectable advertising
* skips rx events saving on power consumption */
if (advertising_params[_set_index].adv_type
== GapAdvertisingParams::ADV_NON_CONNECTABLE_UNDIRECTED) {
printf("We created at least %d tx events\r\n", events);
} else {
printf("We created at least %d tx and rx events\r\n", events);
}
}
};
/** Schedule processing of events from the BLE middleware in the event queue. */
void schedule_ble_events(BLE::OnEventsToProcessCallbackContext *context)
{
_event_queue.call(mbed::callback(&context->ble, &BLE::processEvents));
};
/** Blink LED to show we're running */
void blink(void)
{
_led1 = !_led1;
};
private:
BLE &_ble;
events::EventQueue _event_queue;
DigitalOut _led1;
/* Keep track of our progress through demo modes */
size_t _set_index;
bool _is_in_scanning_mode;
bool _is_connecting;
/* Remember the call id of the function on _event_queue
* so we can cancel it if we need to end the mode early */
int _on_duration_end_id;
/* Measure performance of our advertising/scanning */
Timer _demo_duration;
size_t _scan_count;
};
int main()
{
GAPDevice gap_device;
while (1) {
gap_device.run();
wait_ms(TIME_BETWEEN_MODES_MS);
printf("\r\nStarting next GAP demo mode\r\n");
};
return 0;
}