文章目录

  • 搭建环境
  • HCI编程
    • 编程范式
    • 查看HCI设备的详细信息
    • 扫描周围的LE设备

我是一名嵌入式蓝牙工程师,平时大部分时间都在RTOS系统上进行蓝牙开发,最近因为工作需求要在Unix环境下搭建蓝牙开发环境,而我最熟悉的Unix系统莫过于Linux/Ubuntu,于是开始下载bluez的源代码,搭建蓝牙开发环境,这篇博客就是介绍如何在Ubuntu系统下进行HCI编程

搭建环境

我使用的系统是ubuntu-16.04默认是已经安装了bluez,并且bluez是默认为开机启动的,大家可以用下面的命令测试下:

$ systemctl status bluetooth.service
● bluetooth.service - Bluetooth serviceLoaded: loaded (/lib/systemd/system/bluetooth.service; disabled; vendor preset: enabled)Active: active (running) since Fri 2021-08-13 15:42:22 CST; 2s agoDocs: man:bluetoothd(8)Main PID: 1213 (bluetoothd)Status: "Running"Tasks: 1 (limit: 4915)CGroup: /system.slice/bluetooth.service└─1213 /usr/lib/bluetooth/bluetoothd -C --noplugin=sap

如果输出这种结果,则说明bluez已经处于运行状态了

如果系统没有安装bluez,安装命令也非常简单:

$ sudo apt-get install bluez -y

安装好之后,bluez默认会安装一些实用工具,比如hcitoolhciconfig ,gatttool等,有兴趣的都可以试下这些命令

接着我们来安装bluez的开发库,运行下面命令即可:

$ sudo apt-get install libbluetooth-dev -y

安装好之后会发现系统中多了一些bluetooth的头文件,这些头文件默认安装在/usr/include/bluetooth/目录下,另外还安装了一个libbluetooth的库文件,可以查看下:

$ ls /usr/include/bluetooth/
bluetooth.h  cmtp.h  hci_lib.h  l2cap.h   sco.h  sdp_lib.h
bnep.h       hci.h   hidp.h     rfcomm.h  sdp.h
$ pkg-config --libs bluez
-lbluetooth

HCI编程

蓝牙开发环境安装好之后,下面实现一个非常简单的小程序,就是调用HCIAPI来获取蓝牙设备的ID:

#include <stdio.h>
#include <bluetooth/bluetooth.h>
#include <bluetooth/hci.h>
#include <bluetooth/hci_lib.h>
// filename: main.cint main(int argc, char* argv[])
{int dev_id;dev_id = hci_devid("hci0"); // 函数hci_devid的声明在头文件bluetooth/hci_lib.h中printf("dev_id: %d\n", dev_id);
}

然后运行下面的命令对其进行编译:

$ gcc main.c -o main -lbluetooth
$ ./main
dev_id: 0

因为我的系统上只有一个蓝牙设备,因此输出为:dev_id: 0,也就是hci0

编程范式

由上面的小例子可以知,HCI编程就是调用bluez提供的HCI接口来进行编程,只不过编译的时候要注意把蓝牙库链接进去,即-lbluetooth

其实HCI还有很多命令,可以查看下/usr/include/bluetooth/hci_lib.h文件,可以试着调用这些函数,用得多了就会越来越熟悉。

int hci_open_dev(int dev_id);
int hci_close_dev(int dd);
int hci_send_cmd(int dd, uint16_t ogf, uint16_t ocf, uint8_t plen, void *param);
int hci_send_req(int dd, struct hci_request *req, int timeout);int hci_create_connection(int dd, const bdaddr_t *bdaddr, uint16_t ptype, uint16_t clkoffset, uint8_t rswitch, uint16_t *handle, int to);
int hci_disconnect(int dd, uint16_t handle, uint8_t reason, int to);int hci_inquiry(int dev_id, int len, int num_rsp, const uint8_t *lap, inquiry_info **ii, long flags);
int hci_devinfo(int dev_id, struct hci_dev_info *di);
int hci_devba(int dev_id, bdaddr_t *bdaddr);
int hci_devid(const char *str);int hci_read_local_name(int dd, int len, char *name, int to);
int hci_write_local_name(int dd, const char *name, int to);
int hci_read_remote_name(int dd, const bdaddr_t *bdaddr, int len, char *name, int to);
int hci_read_remote_name_with_clock_offset(int dd, const bdaddr_t *bdaddr, uint8_t pscan_rep_mode, uint16_t clkoffset, int len, char *name, int to);
int hci_read_remote_name_cancel(int dd, const bdaddr_t *bdaddr, int to);
int hci_read_remote_version(int dd, uint16_t handle, struct hci_version *ver, int to);
int hci_read_remote_features(int dd, uint16_t handle, uint8_t *features, int to);
int hci_read_remote_ext_features(int dd, uint16_t handle, uint8_t page, uint8_t *max_page, uint8_t *features, int to);
int hci_read_clock_offset(int dd, uint16_t handle, uint16_t *clkoffset, int to);
int hci_read_local_version(int dd, struct hci_version *ver, int to);......

bluez源码中tools目录下的许多文件可以作为HCI编程的参考,例如tools/hcitool.ctools/hciconfig.c等,这些代码可以有些可以直接用在自己的项目中。

下载bluez的源码:

  • 最新版本可以在官网下载: http://www.bluez.org/
  • 更多的历史版本可以在这里下载:https://mirrors.edge.kernel.org/pub/linux/bluetooth/

下面简要介绍几个hcitool中的几个指令的实现

查看HCI设备的详细信息

当我们在命令行终端运行hcitool的时候,经常会得到这样的输出:

$ hciconfig
hci0:   Type: Primary  Bus: UARTBD Address: E4:5F:01:3D:DA:11  ACL MTU: 1021:8  SCO MTU: 64:1UP RUNNING PSCAN RX bytes:2609 acl:0 sco:0 events:182 errors:0TX bytes:10313 acl:0 sco:0 commands:182 errors:0

那么这个过程是如何实现的呢?我们可以查看下hcitool.c的源码:

int main(int argc, char *argv[])
{...int ctl;/* Open HCI socket  */if ((ctl = socket(AF_BLUETOOTH, SOCK_RAW, BTPROTO_HCI)) < 0) {perror("Can't open HCI socket.");exit(1);}if (argc < 1) {print_dev_list(ctl, 0);exit(0);}...
}

首先打开HCI socket,我们知道现在的Linux发行版基本上都把蓝牙驱动整合进Linux内核了,而HCI就是应用程序跟蓝牙驱动进行交互的接口,Linux内核向应用程序提供一个AF_BLUETOOTHsocket来实现蓝牙Host和Controller的交互。因此,我们代码中首先构建一个蓝牙socket

static void print_dev_list(int ctl, int flags)
{struct hci_dev_list_req *dl;struct hci_dev_req *dr;int i;if (!(dl = malloc(HCI_MAX_DEV * sizeof(struct hci_dev_req) +sizeof(uint16_t)))) {perror("Can't allocate memory");exit(1);}dl->dev_num = HCI_MAX_DEV;dr = dl->dev_req;if (ioctl(ctl, HCIGETDEVLIST, (void *) dl) < 0) {perror("Can't get device list");free(dl);exit(1);}for (i = 0; i< dl->dev_num; i++) {di.dev_id = (dr+i)->dev_id;if (ioctl(ctl, HCIGETDEVINFO, (void *) &di) < 0)continue;print_dev_info(ctl, &di);}free(dl);
}

HCI_MAX_DEV是内核支持的最大设备数,默认为16,struct hci_dev_list_req *dl是要从内核返回的结果,因而首先为其分配空间,然后通过蓝牙socket进行ioctl系统调用,其中HCIGETDEVLIST是蓝牙驱动提供的命令,它表示获取蓝牙设备的数量,命令HCIGETDEVINFO表示获取某个蓝牙设备的具体信息,最后调用print_dev_info将结果打印出来

扫描周围的LE设备

现在低功耗蓝牙的应用非常广泛,hcitool命令也可以扫描周围的低功耗蓝牙设备,命令行终端输入指令之后,输出是这样的:

$ sudo hcitool lescan
LE Scan ...
44:62:84:7A:DF:D7 (unknown)
44:62:84:7A:DF:D7 (unknown)
52:7A:77:FC:D4:79 (unknown)

括号中的是设备名字,如果没有名字则标记为unknown

那么我们可以看下这部分的实现是怎样的

static struct option lescan_options[] = {{ "help",    0, 0, 'h' },           // 帮助信息{ "static",   0, 0, 's' },         // 是否使用静态地址{ "privacy",    0, 0, 'p' },           // 是否使用随机地址{ "passive",  0, 0, 'P' },           // 是否进行被动扫描{ "whitelist",    0, 0, 'w' },       // 是否使用白名单{ "discovery", 1, 0, 'd' },       // Limited/General Discovery{ "duplicates",  0, 0, 'D' },     // 是否允许重复的scan report{ 0, 0, 0, 0 }
};static void cmd_lescan(int dev_id, int argc, char **argv)
{int err, opt, dd;uint8_t own_type = LE_PUBLIC_ADDRESS;uint8_t scan_type = 0x01;uint8_t filter_type = 0;uint8_t filter_policy = 0x00;uint16_t interval = htobs(0x0010);uint16_t window = htobs(0x0010);uint8_t filter_dup = 0x01;for_each_opt(opt, lescan_options, NULL) {switch (opt) {case 's':own_type = LE_RANDOM_ADDRESS;break;case 'p':own_type = LE_RANDOM_ADDRESS;break;case 'P':scan_type = 0x00; /* Passive */break;case 'w':filter_policy = 0x01; /* Whitelist */break;case 'd':filter_type = optarg[0];if (filter_type != 'g' && filter_type != 'l') {fprintf(stderr, "Unknown discovery procedure\n");exit(1);}interval = htobs(0x0012);window = htobs(0x0012);break;case 'D':filter_dup = 0x00;break;default:printf("%s", lescan_help);return;}}helper_arg(0, 1, &argc, &argv, lescan_help);if (dev_id < 0)dev_id = hci_get_route(NULL);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, filter_type);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);
}

首先lescan_options静态配置了lescan的参数,函数cmd_lescan解析该参数,然后调用HCI的API函数hci_open_dev获取可以和蓝牙设备通信的socket,紧接着调用hci_le_set_scan_parameters设置扫描参数,调用hci_le_set_scan_enable开始进行扫描,函数print_advertising_devices会不断的从socket中读数据,这个数据是扫描的结果,即LE SCAN REPORT,最后将扫描结果打印出来,如下所示:

static int print_advertising_devices(int dd, uint8_t filter_type)
{...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;}...printf("%s %s\n", addr, name);...}
}

蓝牙bluez进行HCI编程相关推荐

  1. 蓝牙核心技术概述(四):蓝牙协议规范(HCI、L2CAP、SDP、RFOCMM)

    一.主机控制接口协议  HCI 蓝牙主机-主机控模型 蓝牙软件协议栈堆的数据传输过程: 1.蓝牙控制器接口数据分组:指令分组.事件分组.数据分组 (1).指令分组 如:Accpet Connectio ...

  2. uniapp app蓝牙打印_给编程器加装蓝牙串口模块,用手机APP操作打印信息进控制台...

    公众号回复[编程器]可下载蓝牙串口APP 专用蓝牙串口模块购买: https://item.taobao.com/item.htm?id=619731291566 编程器加装蓝牙模块后,可以方便的使用 ...

  3. c#读蓝牙数据_[C#] 编程控制笔记本蓝牙与外部蓝牙设备通信

    2014-08-03 0 个评论 收藏 我要投稿 C# 编程使笔记本蓝牙和外部设备蓝牙通信: 其实配对以后,蓝牙就被模拟成了一个端口,我们可以用最简单的端口通讯来收发信息.首先,在每次启动 时,需要连 ...

  4. Linux 蓝牙读写,实战Linux Bluetooth编程(三) HCI层编程

    作者:Sam (甄峰) (HCI协议简介,HCI 在BlueZ中的实现以及HCI编程接口) 1. HCI层协议概述: HCI提供一套统一的方法来访问Bluetooth底层.如图所示: 从图上可以看出, ...

  5. linux 蓝牙编程,实战Linux Bluetooth编程(三) HCI层编程

    1. HCI层协议概述: HCI提供一套统一的方法来访问Bluetooth底层.如图所示: 从图上可以看出,Host Controller Interface(HCI)  就是用来沟通Host和Mod ...

  6. Linux下Bluez的编程实现

    2019独角兽企业重金招聘Python工程师标准>>> 蓝牙的各个协议栈的简介2 1.1.蓝牙技术2 1.1.蓝牙协议栈2 1.2.蓝牙技术的特点4 1.2.1.蓝牙协议栈体系结构4 ...

  7. android 蓝牙hf编程,基于Android蓝牙Inband ring功能实现.doc

    基于Android蓝牙Inband ring功能实现 基于Android蓝牙Inband ring功能实现 [摘要] 蓝牙作为一种短距无线数据与语音传输的开放性全球规范,目前在整个世界范围内都得到了很 ...

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

    Linux BLE编程--广播.扫描 一.广播与扫描 低功耗蓝牙通过广播信道来发现其他设备,从机设备通过广播信道发送广播数据包,主机设备通过扫描可以接收到范围内所有广播数据包,然后对感兴趣设备发送扫描 ...

  9. 蓝牙HCI Dongle说明

    Chipsets 本项目主要实现了蓝牙Host协议栈,并没有包含Controller部分,如果需要实现蓝牙交互,按照Core Spec,需要通过HCI接口连接其他蓝牙芯片实现蓝牙功能. 由于HCI接口 ...

  10. 蓝牙DA14580学习教程(附开源可编程手环/手表全套学习资料下载地址)

    DA14580学习 DA14580用来干什么? 1.超长待机的智能手环.手表和其他智能穿戴设备. 2.智能鼠标.键盘.遥控器.触控板.语音和手势识别控制板等. 3.计步(如小米手环).活动和睡眠监测器 ...

最新文章

  1. object-c中管理文件和目录:NSFileManager使用方法
  2. 【干货】APP产品处理加载机制和刷新机制的交互方法解析
  3. 方差 标准差_方差与标准差——杭州市初中数学核心组寒假微课学习八年级第38课...
  4. cstring判断是否包含子串_最长子串-滑动窗口
  5. android eclipse 导入idea项目
  6. 32 引脚_函数功能:定时器T0的中断服务子程序,使P3.7引脚输出音频方波
  7. 初识Matlab遗传算法工具箱 ga
  8. Jetson AGX Xavier配置cuDNN流程
  9. linux查看电脑硬件信息,Linux下查看电脑硬件配置【转】
  10. 汇编语言 王爽 第四版 实验4
  11. 一个屌丝程序猿的人生(四十九)
  12. 手机号码正则_正则表达式小白有这两个工具就够了 正则表达式生成工具
  13. 极简工具(AutoCAD、Tekla)——零件图自动标注
  14. 下载2019年7月实行的新标准JTT808-2019道路运输车辆卫星定位系统终端通讯协议及数据格式
  15. ftp上传文件到服务器上,ftp上传文件到服务器上
  16. Java项目架构的演变
  17. python什么字体好看_七个不一样的Python代码写法,让你写出一手漂亮的代码
  18. css设定元素左对齐,左对齐标签 - 右对齐选择元素(CSS)
  19. 【嵌入式模块】常用扩展芯片及数据手册总结
  20. 上海相会 | 冒志鸿与丁磊畅谈ArcBlock未来宏图

热门文章

  1. HDUOJ_2567_寻梦
  2. eclipse debug 多线程
  3. 51单片机学习笔记——串口通信
  4. 简单通用的Makefile
  5. 重磅丨教育部《高校人工智能创新行动计划》权威解读, AI人才缺口竟有500万!
  6. 数据库——概念模型(CDM)、逻辑模型(LDM)、物理模型(PDM)
  7. 程序员2022年薪资出炉:一线城市薪资中位数已破1.5万元,你工资涨了吗?
  8. 解决网页中一直点击出现蓝色背景问题
  9. 【OS学习笔记】二 汇编语言和汇编软件
  10. linux下安装redis报Mmmm... it seems like you don‘t have a redis executable. Did you run make install yet?