一,前述

1,上一篇讲述了如何通过Windows实现蓝牙应用,该方式可适用于Windows/Linux/Macos等场景。[https://blog.csdn.net/u014028690/article/details/113943287?spm=1001.2014.3001.5502],该方式比较特殊的一点,用了指定的蓝牙dongle设备,也规避了不同平台产生的差异而导致的问题。
2,但是市面上更多的是WIFI+BT/BLE combo的芯片。针对该类设备,各个厂商都遵循统一的HCI接口以及适配协议,不同的是针对各自设备都有各自的驱动程序。所以contorller部分协议以及硬件都是由原厂提供,host部分的协议栈有我们熟知的linux官方蓝牙协议栈bluez。(http://www.bluez.org/)

二、设计思路

1,模型框图

当前我们利用通用设备(如市面上的USB蓝牙dongle/系统蓝牙等)实现ble master/ble slave。

2,具体设计

2.1 我们采用MGMT方式实现ble master/ble slave。为什么应用MGMT方式实现主从机。

1,很重要的一点,占用资源少。(实测Dbus环境动态库接近2M,bin接近1M,还是strip之后的。对于flash很小的设备而言内存占用压力很大,而用MGMT加起来不到1M)
2,便于纠错,直接和kernel通信的接口,避免过多的进程间交互,导致问题出来,不需要花大量时间纠错。(也是避免了内核与用户空间没有冲突的风险)
3,剩下的优点不提了,对于我当前简单实现ble slave,单点连接设备而言,用的比较少。
// 感兴趣可以看一下,来自官方blog
Command queues and synchronization
Since the kernel is now responsible for all HCI traffic there’s no risk of conflicts between kernel and userspace.Blocking operations
With the management interface there are simple asynchronous messages that are used to power on and off adapters: blocking problem solved.Unnecessary HCI event processing
No raw HCI sockets means no promisc flag on the kernel side. So extra processing of these packets isn’t needed anymore.Distributed security policy and logic
With the management interface only user interaction (PIN code/pass key requests, etc) and link key storage is handled on the user space side. User space will feed the kernel with all stored link keys, including the key types, upon adapter initialization. After that the kernel is responsible for handling link key requests.An additional benefit with having an abstracted interface for security is that it can be used for the Security Manager Protocol (SMP) that’s part of the Bluetooth Low Energy (LE) specification. SMP has a similar user interaction model as SSP so the same messages between user space and the kernel can be reused.As long as SMP is implemented on the kernel side there’d be a big problem with dealing with it from user space using the existing kernel interface since unlike SSP, SMP uses L2CAP and not HCI for messaging.Lack of early-tracing capability
The management interface will offer a special type of tracing socket which can be used to get the HCI traffic of all connected adapters. This will allow a userspace process to catch all traffic to and from an adapter from the first moment that it is plugged in.

这里可以参考另一篇文章。([https://blog.csdn.net/u014028690/article/details/107246633])
2.2 基础篇,我们注重从源码出发,便于理解以及应用。详细的多设备控制,我们后续补充。

三、代码理解

1,Ble Master之扫描设备

源码信息:btmgmt.c

由于我们采用MGMT方式实现ble master,故我们应用btmgmt工具集实现扫描设备。

// 扫描设备分为两个步骤// 第一步设定扫描参数:
// 对应命令行:btmgmt scan-params 0x30 0x30
// 对应代码如下:
static void cmd_scan_params(int argc, char **argv)
{struct mgmt_cp_set_scan_params cp;uint16_t index;index = mgmt_index;if (index == MGMT_INDEX_NONE)index = 0;cp.interval = strtol(argv[1], NULL, 0);cp.window = strtol(argv[2], NULL, 0);if (mgmt_send(mgmt, MGMT_OP_SET_SCAN_PARAMS, index, sizeof(cp), &cp,scan_params_rsp, NULL, NULL) == 0) {error("Unable to send set_scan_params cmd");return bt_shell_noninteractive_quit(EXIT_FAILURE);}
}// 第二步扫描设备:
// 对应命令行:btmgmt find-service -u 0xa201 -l
// 注:如需多个设备扫描可在后面追加service uuid,例如:find-service -u 0xa201 -u 0x1827 -u 0x1828 -l (这里包含了mesh 设备的unprov service uuid和 proxy service uuid)
// 对应代码如下:
static void cmd_find_service(int argc, char **argv)
{struct mgmt_cp_start_service_discovery *cp;uint8_t buf[sizeof(*cp) + 16 * MAX_UUIDS];uuid_t uuid;uint128_t uint128;uuid_t uuid128;uint8_t type = SCAN_TYPE_DUAL;int8_t rssi;uint16_t count;int opt;uint16_t index;index = mgmt_index;if (index == MGMT_INDEX_NONE)index = 0;...//省略很多行argc -= optind;argv += optind;optind = 0;cp = (void *) buf;cp->type = type;cp->rssi = rssi;cp->uuid_count = cpu_to_le16(count);if (mgmt_send(mgmt, MGMT_OP_START_SERVICE_DISCOVERY, index,sizeof(*cp) + count * 16, cp,find_service_rsp, NULL, NULL) == 0) {error("Unable to send start_service_discovery cmd");return bt_shell_noninteractive_quit(EXIT_FAILURE);}
}
对应到kernel的源码如下:
static int start_service_discovery(struct sock *sk, struct hci_dev *hdev,void *data, u16 len)
{struct mgmt_cp_start_service_discovery *cp = data;struct mgmt_pending_cmd *cmd;struct hci_request req;const u16 max_uuid_count = ((U16_MAX - sizeof(*cp)) / 16);u16 uuid_count, expected_len;u8 status;int err;BT_DBG("%s", hdev->name);hci_dev_lock(hdev);...//省略很多行if (!trigger_discovery(&req, &status)) {err = mgmt_cmd_complete(sk, hdev->id,MGMT_OP_START_SERVICE_DISCOVERY,status, &cp->type, sizeof(cp->type));mgmt_pending_remove(cmd);goto failed;}err = hci_req_run(&req, start_discovery_complete);if (err < 0) {mgmt_pending_remove(cmd);goto failed;}hci_discovery_set_state(hdev, DISCOVERY_STARTING);failed:hci_dev_unlock(hdev);return err;
}
// 扫描开启成功后,会设定对应设定扫描Timeout
// 这里很重要,决定了你的设备是否需要短期多次被扫到或者扫描开启周期等。
static void start_discovery_complete(struct hci_dev *hdev, u8 status,u16 opcode);

以下贴图,便于理解,如果不好理解,可省略。。。

扫描设备实际结果:

注:这里我们选取一个设备(BC:23:00:00:FF:02)。

2,Ble Master之设备连接

源码信息:btgatt-client.c

// 蓝牙子设备连接包含两个主要步骤:
// 第一步:L2CAP链路连接:
// 这里的connect是否成功很大程度取决于蓝牙设备的性能,否则会一直出现conn_fail()的情况,或者conn_fail()之后connected的情况,设备占用,但是无法操作等等状况。static int l2cap_le_att_connect(bdaddr_t *src, bdaddr_t *dst, uint8_t dst_type,int sec)
{int sock;struct sockaddr_l2 srcaddr, dstaddr;struct bt_security btsec;...//省略很多行/* Set up destination address */memset(&dstaddr, 0, sizeof(dstaddr));dstaddr.l2_family = AF_BLUETOOTH;dstaddr.l2_cid = htobs(ATT_CID);dstaddr.l2_bdaddr_type = dst_type;bacpy(&dstaddr.l2_bdaddr, dst);printf("Connecting to device...");fflush(stdout);if (connect(sock, (struct sockaddr *) &dstaddr, sizeof(dstaddr)) < 0) {  // 这里如果连接失败或者errno出现busy/not implement等情况,fd会失效perror(" Failed to connect");close(sock);return -1;}printf(" Done\n");return sock;
}

这里仅仅是ATT连接成功流程。

// 第二步便是注册GATT Client。
// 上一步connect后的fd在这一步中进行数据交互。
// 包含有:断开事件注册,服务获取
static struct client *client_create(int fd, uint16_t mtu)
{struct client *cli;cli = new0(struct client, 1);if (!cli) {fprintf(stderr, "Failed to allocate memory for client\n");return NULL;}cli->att = bt_att_new(fd, false);if (!cli->att) {fprintf(stderr, "Failed to initialze ATT transport layer\n");bt_att_unref(cli->att);free(cli);return NULL;}if (!bt_att_set_close_on_unref(cli->att, true)) {fprintf(stderr, "Failed to set up ATT transport layer\n");bt_att_unref(cli->att);free(cli);return NULL;}if (!bt_att_register_disconnect(cli->att, att_disconnect_cb, NULL,NULL)) {fprintf(stderr, "Failed to set ATT disconnect handler\n");bt_att_unref(cli->att);free(cli);return NULL;}cli->fd = fd;cli->db = gatt_db_new();if (!cli->db) {fprintf(stderr, "Failed to create GATT database\n");bt_att_unref(cli->att);free(cli);return NULL;}cli->gatt = bt_gatt_client_new(cli->db, cli->att, mtu, 0);if (!cli->gatt) {fprintf(stderr, "Failed to create GATT client\n");gatt_db_unref(cli->db);bt_att_unref(cli->att);free(cli);return NULL;}gatt_db_register(cli->db, service_added_cb, service_removed_cb,NULL, NULL);if (verbose) {bt_att_set_debug(cli->att, BT_ATT_DEBUG_VERBOSE, att_debug_cb,"att: ", NULL);bt_gatt_client_set_debug(cli->gatt, gatt_debug_cb, "gatt: ",NULL);}bt_gatt_client_ready_register(cli->gatt, ready_cb, cli, NULL);bt_gatt_client_set_service_changed(cli->gatt, service_changed_cb, cli,NULL);/* bt_gatt_client already holds a reference */gatt_db_unref(cli->db);return cli;
}

连接后,我们便可以获取到设备的服务及特征值信息了。

注册一下notify cb,便于接收对端数据

3,Ble Master之设备透传

// 连上之后,数据透传就比较简单了,可以按需选择。
// 当前我连的这个设备,发什么数据就回什么数据。
static void cmd_write_value(struct client *cli, char *cmd_str)
{int opt, i, val;char *argvbuf[516];char **argv = argvbuf;int argc = 1;uint16_t handle;char *endptr = NULL;int length;uint8_t *value = NULL;bool without_response = false;bool signed_write = false;if (!bt_gatt_client_is_ready(cli->gatt)) {printf("GATT client not initialized\n");return;}if (!parse_args(cmd_str, 514, argv + 1, &argc)) {printf("Too many arguments\n");write_value_usage();return;}optind = 0;argv[0] = "write-value";while ((opt = getopt_long(argc, argv, "+ws", write_value_options,NULL)) != -1) {switch (opt) {case 'w':without_response = true;break;case 's':signed_write = true;break;default:write_value_usage();return;}}... //省略很多行  if (without_response) {if (!bt_gatt_client_write_without_response(cli->gatt, handle,signed_write, value, length)) {printf("Failed to initiate write without response ""procedure\n");goto done;}printf("Write command sent\n");goto done;}if (!bt_gatt_client_write_value(cli->gatt, handle, value, length,write_cb,NULL, NULL))printf("Failed to initiate write procedure\n");done:free(value);
}

对应write的notify的handle value

发送给设备端数据(对应handle=0x10):0x00 0x01 0x02 0x03 0x04 0x05

4,Ble Master补充说明

1,真正投入产品,这些远远是不够的,所以这里要求我们对基本原理了解的同时,衍生出对多个设备的思考,以及如何维护多个设备fd和连接/断开/透传等状态机。

2,关于如何剥离bluez源码,需要实现跨平台裸代码无第三方编译及使用的可以私聊我。

【BlueZ】【蓝牙】跨平台实现Ble MasterSlaveMesh 之Linux篇-1相关推荐

  1. iOS蓝牙开发---CoreBluetooth[BLE 4.0] 初级篇[内附Demo地址]

    一.蓝牙基础知识 (一)常见简称 1.MFI  make for ipad ,iphone, itouch 专们为苹果设备制作的设备,开发使用ExternalAccessory 框架(认证流程貌似挺复 ...

  2. 针对蓝牙4.0 BLE通讯过程的逆向和攻击

    本文讲的是针对蓝牙4.0 BLE通讯过程的逆向和攻击,从6个月前,我就开始针对BLE设备进行学习和研究,其中接触到了一些关于BLE逆向的博客和文章,但是相关内容都没有给出很好的方案.因此通过我的这篇文 ...

  3. java 蓝牙4.0_《蓝牙4.0 BLE开发完全手册---物联网开发技术实战

    图书目录: 第1章 蓝牙4.0 BLE简介 1.1 无线网络数据传输协议对比 1.2 短距离无线网络的分类 1.2.1 什么是蓝牙4.0 BLE 1.2.2 蓝牙4.0 BLE的特点 1.3 蓝牙4. ...

  4. iOS开发 之 可穿戴设备 蓝牙4.0 BLE 开发

    1 前言 当前有越来越多的可穿戴设备使用了蓝牙4.0 BLE(Bluetooth Low Energy).对于iOS开发而言,Apple之前专门推出CoreBluetooth的Framework来支持 ...

  5. android ble蓝牙接收不到数据_Android蓝牙4.0 Ble读写数据详解 -2

    Android蓝牙4.0 Ble读写数据详解 -2 上一篇说了如何扫描与链接蓝牙 这篇文章讲讲与蓝牙的数据传输,与一些踩到的坑. 先介绍一款调试工具,专门调试Ble蓝牙的app.名字叫:nRF-Con ...

  6. 蓝牙解析(part5):BLE的广播通信

    转自Wowo大神的http://www.wowotech.net/bluetooth/ble_broadcast.html 1. 前言 大家都知道,相比传统蓝牙,蓝牙低功耗(BLE)最大的突破就是加大 ...

  7. 蓝牙4.0 BLE协议结构图详解

    随着智能硬件的发展,嵌入式和物联网这类专业越发受到大家的追捧,而不管是学习嵌入式还是学习物联网,蓝牙4.0 BLE协议结构都是必须重点掌握的知识点,今天和大家分享的就只这部分内容,一起来看看吧. 第一 ...

  8. 超级好用的小程序版蓝牙调试工具:Ble蓝牙开发助手

    随着物联网的快速发展,和硬件相关的项目越来越多,蓝牙WiFi几乎是必不可少的. 为了更方便的调试蓝牙模块的协议,最近写了一个蓝牙调试工具:Ble蓝牙开发助手 这样的工具,一搜一大堆,为什么还要再开发呢 ...

  9. android低耗能蓝牙开发,Android BLE低功耗蓝牙开发

    最近做了一个智能硬件开发(针灸仪)的项目,有一部分涉及到低功耗蓝牙的开发,就是通过蓝牙和设备进行数据的交互,比如控制改设备的LED的开关,设备的开关机,设置设备的时间和温度等,下面就项目中遇到的坑一一 ...

最新文章

  1. dataTable 从服务器获取数据源的两种表现形式
  2. 事务消息大揭秘!RocketMQ、Kafka、Pulsar全方位对比
  3. Swift溢出运算符
  4. 影响网站各个页面权重高低的因素有哪些?
  5. How to expand Azure VM OS Disk
  6. ros_openvino_toolkit环境搭建纪实
  7. java poi 导出excel 数字有问题
  8. 上大学后男生的两种变化
  9. Node.js下载安装及各种npm、cnpm、nvm、nrm配置(保姆式教程—提供全套安装包)—nrm的安装与配置(5)
  10. Test.ai完成1100万美元A轮融资,Google人工智能基金领投
  11. OpenLayers使用symbolizers样式特征
  12. Java机器学习开发库
  13. 一步一步学习Git(2)——Git基本操作
  14. 南阳理工acm 1007GCD
  15. 在VS中安装nuget离线包nupkg文件
  16. GJB9001-2017质量管理体系
  17. 计算机音乐吧粉刷匠,中班音乐歌曲《粉刷匠》
  18. 经济法基础——第五章第二节、个人所得税法律制度
  19. DAS、NAS和SAN,IPSAN的基本了解
  20. 《客户端脚本语言-JavaScript》

热门文章

  1. 【多线程】初识多线程
  2. 中国人寿保费项目数据集
  3. 汉字笔顺字帖在线生成器网站源码
  4. C语言停车场管理模拟系统
  5. SIM卡被猫吃了,1860电话录音
  6. list中抽出某一个字段的值_java8从list集合中取出某一属性的值的集合案例
  7. 电商商品3d展示---插件spritespin
  8. KingbaseES 中的xmin,xmax等系统字段说明
  9. js中数组的entries方法
  10. Py网络编程及应用(urllib、socket/selectors)