1000字范文,内容丰富有趣,学习的好帮手!
1000字范文 > 基于bluez的树莓派低功耗蓝牙开发:与多个低功耗蓝牙模块连接

基于bluez的树莓派低功耗蓝牙开发:与多个低功耗蓝牙模块连接

时间:2020-03-14 13:04:35

相关推荐

基于bluez的树莓派低功耗蓝牙开发:与多个低功耗蓝牙模块连接

Linux BLE编程——广播、扫描

一.广播与扫描

低功耗蓝牙通过广播信道来发现其他设备,从机设备通过广播信道发送广播数据包,主机设备通过扫描可以接收到范围内所有广播数据包,然后对感兴趣设备发送扫描请求报文,从机接收到该报文后可选择是否向主机发送扫描响应报文(包含自身ID、BAADDR等信息,可设置)。

广播能够发送两种数据:广播数据和扫描响应数据。主机在发送广播数据是需要设置广播参数,配置参数包括:

1.最小间隔时间,范围20ms—10.24s。

2.最大间隔时间,范围20ms—10.24s。

3.广播类型,广播类型一共四种:

可连接的非定向广播,表示设备可以接受任何设备的连接请求。

可连接的定向广播,表示仅仅能接受某一特定设备连接请求。

可扫描的非定向广播,发送广播数据和扫描响应数据,用来激活 扫描者。

不可连接的非定向广播,仅仅发送广播数据。

4 .地址类型,随机地址或固定地址

5. 对端设备地址类型,可选项,定向广播需要设置,

6. 对端设备地址,同上。

7. 信道映射。使用那三个广播信道。

8. 过滤政策,用来过滤不符合规则的广播数据包,过滤政策如下:

1. 接收任何设备的扫描请求和连接请求。

2. 仅仅接收白名单中设备的扫描请求,但接收任何设备的连接请求

3. 接收任何设备的扫描请求,但仅接受白名单中设备的连接请求。

4. 仅接受白名单中特定设备的扫描请求和连接请求。

扫描分为被动扫描和主动扫描,接收对端设备的广播数据包,可以使用被动扫描,主动扫描不仅可以捕获广播数据包,还能捕获扫描响应数据包。扫描需要配置的参数如下:

1.扫描类型,主动扫描或被动扫描。

2.扫描间隔,多长时间扫描一次。

3.扫描窗口,每次扫描持续多久。

4.个人地址类型,主动扫描才需配置,固定地址或随机地址

5.扫描策略,接收任何广播数据包或仅接收白名单设备广播数据包。

主机通过HCI层向控制器发送发送HCI命令数据包是蓝牙进行广播。

二、HCI协议概述

主机控制接口(HCI)是主机与控制器之间的接口,主要完成两个任务:一个是发送命令给控制器和接收来自控制器的事件,另一个是发送和接收来自对端设备的数据。HCI屏蔽了不同控制器之间的差异,规定一套统一的数据格式来与控制器进行交互,其有4种不同数据包格式:命令数据包(HCI Command)、事件数据包(Event)、数据包(ACL data)、流控。

1.命令数据包

主机通过向控制器发送命令数据包来执行命令。这些命令通常用于适配控制器状态,或者请求适配器完成某种操作。HCI命令数据包格式如下:

图1 HCI命令数据包格式

OpCode:操作码,用来确认发送的命令,由10bit的OCF(OpCode命令域)和6bit的OGF(OpCode组域)组成。OGF确定了所发送命令的组域,OCF确定了该组域中一个具体命令。低功耗蓝牙命令的组域为0x08。

Parameter Total Length:参数总长度。

Parameter:参数。每一个命令都会带有一些参数。

2.事件数据包

事件数据包是由控制器发往主机,主要用于反馈输入命令的结果和发送信息。其格式如下

Event Code:事件码。用来区分不同事件。

Parameter Length表示所带参数的长度,以字节数为单位,随后就是所带的参数列表。

三、HCI编程

1)int hci_open_dev(int dev_id) 函数

该函数用于创建一个HCI socket,并与指定的蓝牙设备ID绑定,这样就这个HCI socket来进行控制器与主机的数据传输。

2)int hci_close_dev(int dd)

关闭一个HCI socket

3)int hci_send_req(int dd, struct hci_request *r, int to)

该函数用于向控制器发送HCI命令数据包。参数:

dd:hci_open_dev()函数创建的HCI socket。

r :hci命令数据包。其数据结构如下:

to :超时时间,单位:milliseconds

4)int hci_get_route(bdaddr_t *bdaddr)

根据物理地址得出设备ID ,输入参数为NULL时默认返回第一个可用设备的设备ID.

5)int ba2str(const bdaddr_t *ba, char *str)

将物理地址转换为字符串.

6)int str2ba(const char *str, bdaddr_t *ba)

将字符串转换为bdaddr_t.

7)int hci_le_set_scan_parameters(int dd, uint8_t type,

uint16_t interval, uint16_t window,

uint8_t own_type, uint8_t filter, int to)

用于配置扫描参数.

8)int hci_le_set_scan_enable(int dd, uint8_t enable, uint8_t filter_dup, int to)

用于向控制器发送扫描指令.

代码实例

#include <stdio.h>#include <errno.h>#include <ctype.h>#include <fcntl.h>#include <unistd.h>#include <stdlib.h>#include <string.h>#include <sys/param.h>#include <sys/socket.h>#include <signal.h>#include "bluetooth/bluetooth.h"#include "bluetooth/hci.h"#include "bluetooth/hci_lib.h"#ifndef MIN#define MIN(a, b) ((a) < (b) ? (a) : (b))#endif/* Unofficial value, might still change */#define LE_LINK0x80#define FLAGS_AD_TYPE 0x01#define FLAGS_LIMITED_MODE_BIT 0x01#define FLAGS_GENERAL_MODE_BIT 0x02#define EIR_NAME_SHORT 0x08 /* shortened local name */#define EIR_NAME_COMPLETE 0x09 /* complete local name */static volatile int signal_received = 0;static void sigint_handler(int sig) {signal_received = sig; }static void eir_parse_name(uint8_t *eir, size_t eir_len, char *buf, size_t buf_len){size_t offset;offset = 0;while (offset < eir_len) {uint8_t field_len = eir[0];size_t name_len;/* Check for the end of EIR */if (field_len == 0)break;if (offset + field_len > eir_len)goto failed;switch (eir[1]) {case EIR_NAME_SHORT:case EIR_NAME_COMPLETE:name_len = field_len - 1; if (name_len > buf_len)goto failed;memcpy(buf, &eir[2], name_len);return;} offset += field_len + 1; eir += field_len + 1; } failed:snprintf(buf, buf_len, "(unknown)");}static int print_advertising_devices(int dd){unsigned char buf[HCI_MAX_EVENT_SIZE], *ptr;struct hci_filter nf, of;struct sigaction sa;socklen_t olen;int len;olen = sizeof(of);if (getsockopt(dd, SOL_HCI, HCI_FILTER, &of, &olen) < 0) {printf("Could not get socket options\n");return -1;}hci_filter_clear(&nf);hci_filter_set_ptype(HCI_EVENT_PKT, &nf); //hci数据包类型,此处设置为事件数据包hci_filter_set_event(EVT_LE_META_EVENT, &nf); //hci事件数据包中具体事件类型。EVT_LE_META_EVENT为事件码。if (setsockopt(dd, SOL_HCI, HCI_FILTER, &nf, sizeof(nf)) < 0) {printf("Could not set socket options\n");return -1;}memset(&sa, 0, sizeof(sa));sa.sa_flags = SA_NOCLDSTOP;sa.sa_handler = sigint_handler;sigaction(SIGINT, &sa, NULL);while (1) {evt_le_meta_event *meta;le_advertising_info *info;char addr[18];while ((len = read(dd, buf, sizeof(buf))) < 0) {if (errno == EINTR && signal_received == SIGINT) {len = 0;goto done;}if (errno == EAGAIN || errno == EINTR)continue;goto done;}ptr = buf + (1 + HCI_EVENT_HDR_SIZE);len -= (1 + HCI_EVENT_HDR_SIZE);meta = (void *) ptr;if (meta->subevent != 0x02)goto done;/* Ignoring multiple reports */info = (le_advertising_info *) (meta->data + 1);{char name[30];memset(name, 0, sizeof(name));ba2str(&info->bdaddr, addr);eir_parse_name(info->data, info->length,name, sizeof(name) - 1);printf("%s %s\n", addr, name);}}done:setsockopt(dd, SOL_HCI, HCI_FILTER, &of, sizeof(of));if (len < 0)return -1;return 0;}/* * 主机发送广播数据用于扫描设备是需要hci_le_set_scan_parameters函数来设置广播参数:* scan_type:扫描分为主动扫描(0x01)和被动扫描(0x00)* 被动扫描:接收对端设备的广播数据包。* 主动扫描:不仅可以捕获广播数据包,还能捕获扫描响应包* own_type:地址类型,固定设备地址或随机地址·* filter_policy:扫描策略,接收任何广播数据包(0x00)还是仅接收白名单设备的广播数据包(0x01)* intervl:扫描间隔,单位1.25ms,扫描频率* window:扫描窗口,扫描时间长短。* filter_dup: 是(0x00)否(0x01)显示重复扫描到的设备信息。* */int lescan(int dev_id, int argc, char **argv){interr, opt, dd;uint8_t own_type = LE_PUBLIC_ADDRESS;uint8_t scan_type = 0x00;uint8_t filter_policy = 0x00;uint16_t interval = htobs(0x0010);uint16_t window = htobs(0x0010);uint8_t filter_dup = 0x01;dd = hci_open_dev(dev_id);if (dd < 0) {perror("Could not open device");exit(1);}/*扫描参数配置*/err = hci_le_set_scan_parameters(dd, scan_type, interval, window,own_type, filter_policy, 10000);if (err < 0) {perror("Set scan parameters failed");exit(1);}/*发送指令包,使能扫描*/err = hci_le_set_scan_enable(dd, 0x01, filter_dup, 10000);if (err < 0) {perror("Enable scan failed");exit(1);}printf("LE Scan ...\n");err = print_advertising_devices(dd);if (err < 0) {perror("Could not receive advertising events");exit(1);}err = hci_le_set_scan_enable(dd, 0x00, filter_dup, 10000);if (err < 0) {perror("Disable scan failed");exit(1);}hci_close_dev(dd);}

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。