【问题标题】:Bluez: advertise service / gatt server example?Bluez:广告服务/gatt 服务器示例?
【发布时间】:2014-01-08 01:21:47
【问题描述】:

目标

我正在开发一个运行 Linux 的简单设备。它支持 BLE,我目前使用的是 bluez 5.8。

我想使用 iPhone 触发此设备上的操作。

已经有效的方法:

  • 我可以让 iPhone “看到”设备。
  • iPhone 也连接到设备。

我在linux上这样设置蓝牙设备(感谢this question):

# activate bluetooth
hciconfig hci0 up                                             
# set advertise data: "hello world"
hcitool -i hci0 cmd 0x08 0x0008 48 45 4c 4c 4f 57 4f 52 4c 44
# start advertising as connectable
hciconfig hci0 leadv 0

iOS 代码很简单:

- (int) scanForPeripherals
{
    if (self->centralManager.state != CBCentralManagerStatePoweredOn) {
        return -1;
    }
    NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithBool:NO], CBCentralManagerScanOptionAllowDuplicatesKey, nil];
    [self.centralManager scanForPeripheralsWithServices:nil options:options];
    return 0;
}

- (void)centralManagerDidUpdateState:(CBCentralManager *)central
{
    if (central.state == CBCentralManagerStatePoweredOn) {
        NSLog(@"Starting scan");
        [self scanForPeripherals];
    }
}

- (void) centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI
{
    NSLog(@"didDiscoverPeripheral");
    /* 
     * Retain the peripheral to avoid the error:
     *  CoreBluetooth[WARNING]: state = connecting> is being dealloc'ed while connecting
     */
    self.activePeripheral = peripheral;
    [centralManager connectPeripheral:peripheral options:nil];
}

- (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral
{
    NSLog(@"Connected to peripheral");

    /* discover all services */
    [peripheral discoverServices:nil];
}

- (void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error
{
    NSLog(@"Discovered services");
    for (CBService *service in peripheral.services) {
        NSLog(@"Discovered service %@", service);
    }
}

在 iPhone 上运行此代码时,我得到以下日志:

2013-12-19 12:53:22.609 Test2[18518:60b] Starting scan
2013-12-19 12:53:29.945 Test2[18518:60b] didDiscoverPeripheral
2013-12-19 12:53:31.230 Test2[18518:60b] Connected to peripheral

所以看来 iPhone 连接正常,但没有看到任何服务。

我错过了什么

  • 我需要宣传一个简单的 BLE 服务,但我在 bluez 中找不到任何关于如何做到这一点的文档。
  • 我认为我需要类似 gatt-server 的东西来接收我要宣传的服务的读/写特征。我在 bluez 中看到了 plugins/gatt-example.c 文件,但我完全不知道如何使用它:没有文档。

我应该提到我看到了这个问题:Creating a gatt server,但答案提出了太多问题(例如,bluez的GATT api在哪里?如何设置GATT数据库?如何注册读/写事件?)

编辑: 我使用的命令仅设置 BLE 设备以通告一些数据,但 iOS 报告连接已接受。 bluez 的哪一部分接受传入连接?

【问题讨论】:

  • 你是对的,你需要一个 GATT 服务器。您运行的命令只是将硬件设置为广播广告数据包,但不会启动任何要连接的东西。不幸的是,我还没有弄清楚如何自己设置 GATT 服务器,所以我无法为您提供帮助......
  • @TimTisdall 我知道这些命令不会设置服务器。这就提出了一个问题:谁在接受蓝牙连接?我编辑了我的帖子以明确这个问题。
  • 我的猜测是内核中有什么东西在建立连接,但这只是一个猜测。

标签: linux bluetooth bluetooth-lowenergy core-bluetooth bluez


【解决方案1】:

最终,我找到了所有问题的答案。

我先回答最后一个问题:

我使用的命令仅设置 BLE 设备以发布一些数据,但 iOS 报告连接已被接受。 bluez 的哪个部分接受传入连接?

回复我的是answered on the bluez mailing-list

总结: BLE 连接在 HCI 级别被内核接受。如果您想从用户空间使用该连接,您需要使用具有 ATT 通道 ID(即 4)的 l2cap 套接字。

Bleno has a good example of using an L2CAP socket.

L2CAP 套接字的工作原理基本上是这样的:

/* create L2CAP socket, and bind it to the local adapter */
l2cap_socket = socket(AF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP);

hci_device_id = hci_get_route(NULL);
hci_socket = hci_open_dev(hci_device_id);
memset(&l2cap_address, sizeof(l2cap_address));
l2cap_address.l2_family = AF_BLUETOOTH;
l2cap_address.l2_bdaddr = hci_device_address;
l2cap_address.l2_cid = htobs(ATT_CID);

bind(l2cap_socket, (struct sockaddr*)&l2cap_address, sizeof(l2cap_address));
listen(l2cap_socket, 1);

while (1) {
  /* now select and accept() client connections. */
  select(l2cap_socket + 1, &afds, NULL, NULL, &tv);
  client_socket = accept(l2cap_socket, (struct sockaddr *)&l2cap_address, &len);

  /* you can now read() what the client sends you */
  int ret = read(client_socket, buffer, sizeof(buffer));
  printf("data len: %d\n", ret);
  for (i = 0; i < ret; i++) {
    printf("%02x", ((int)buffer[i]) & 0xff);
  }
  printf("\n");
  close(client_socket);
}

如何宣传服务?

我意识到我需要上一个问题的答案才能回答那个问题。

一旦你可以通过 L2CAP 套接字读取数据,一切都会变得更有意义,例如,如果你的 Android 手机执行gatt.discoverServices(),那么上面的小程序就会读取(即接收):

10 0100 ffff 0028

这基本上意味着:

10: READ_BY_GROUP
0100: from handle 0001
ffff: to handle ffff
0028: with UUID 2800

此请求是任何 BLE 外围设备请求服务列表的方式。

然后,您可以使用设备提供的服务列表(根据 GATT 协议格式化)来回答此请求。

再说一遍,see the implementation of this in Bleno

【讨论】:

  • 您是否曾经设法让您的 GATT 服务与 Bluez 一起使用?我正在努力实现同样的目标。
【解决方案2】:

你真的很亲密。

要使用您的 iOS 代码查看设备上的服务,请尝试添加

peripheral.delegate = self;

discoverServices 电话之前,您的didConnectPeripheral。我从Apple documentation 得到它,它为我解决了问题(只是不要忘记将CBPeripheralDelegate 添加到头文件中的接口声明中)。没有它,didDiscoverServices 将永远不会被调用。

通过使用./configure --enable-maintainer-mode 从源代码编译BlueZ,我能够运行gatt-example 服务插件。然后,如果你启动 bluetoothd -nd,你会看到类似

src/plugin.c:add_plugin() Loading gatt_example plugin

靠近输出的顶部,然后

attrib/gatt-service.c:gatt_service_add() New service: handle 0x0009, UUID a002, 4 attributes
src/attrib-server.c:attrib_db_add_new() handle=0x0009
attrib/gatt-service.c:gatt_service_add() New characteristic: handle 0x000a
src/attrib-server.c:attrib_db_add_new() handle=0x000a

那时,我的 iOS 应用能够看到 BlueZ peripheral、连接并发现它的服务(在 hciconfig hci0 leadv 之后)。

【讨论】:

  • 感谢分享。看来我确实没有使用--enable-maintainer-mode编译bluez,但是连接被接受。在(太多)阅读bluez代码之后,我认为连接被bluez的服务发现部分接受了......(参见src/sdpd-server.c)。
  • 是的,我在启用 gatt-example 插件之前看到了相同的行为。这是因为首先接受连接,然后发生服务发现。如果没有bluetoothd 中包含的gatt-example 插件,您的iPhone 仍会看到您的广告包,并且可以连接,但找不到任何服务。 --enable-maintainer-mode 是在构建中包含 gatt-example 插件而不修改 Makefile.plugins 的快捷方式。
  • 我有两个相关的快速问题:a) 我在哪里可以在 bluez 中配置服务和属性? b) 如果一个属性是可写的,那么 bluez 在哪里“存储”这个信息(或者是某种回调),以便对 ios 应用程序写入的这个值做一些事情。
  • 修复了重启 (a)。关于(b)。我刚刚看了attrib-server。这是 bluez 存储更新的地方吗?第三方应用应该使用这种数据结构吗?
  • @geri-m 如果我没看错你的问题,相信你看看gatt-example.的源码就能找到答案。
猜你喜欢
  • 2019-08-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-04-12
  • 2016-08-22
  • 2014-08-29
相关资源
最近更新 更多