Home Articles

Bluez:做广告服务/ gatt服务器的例子?

Asked
Viewed 788 times
24

Goal

我正在开发一个运行Linux的简单设备 . 它具有BLE功能,我目前正在使用bluez 5.8 .

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

What already works:

  • 我可以让iPhone "see"成为该设备 .

  • 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连接正常,但没有看到任何服务 .

What I am missing

  • 我需要宣传一个简单的BLE服务,但我找不到任何关于如何在bluez中执行此操作的文档 .

  • 我想我需要像gatt-server这样的东西来接收我要宣传的服务的读/写特性 . 我在bluez中看到了plugins / gatt-example.c文件,但我完全不知道如何使用它:没有文档 .

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

编辑:我使用的命令只设置BLE设备来宣传一些数据,但iOS报告连接被接受 . bluez的哪一部分接受传入连接?

2 Answers

  • 16

    你真的很亲密

    要使用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 之后) .

  • 4

    最后,我发现了所有问题的答案 .

    我将首先回答最后一个问题:

    The commands I use only set-up the BLE device to advertise some data, but iOS reports that the connection is accepted. What part of bluez is accepting incoming connections?

    这个是answered on the bluez mailing-list,是对我的回应 .

    摘要:内核在HCI级别接受BLE连接 . 如果要从用户空间使用该连接,则需要使用带有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);
    }
    

    How to advertise a service?

    我意识到我需要回答上一个问题才能回答这个问题 .

    一旦你可以通过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 .

Related