Bluez: advertise service / gatt server example?

前端 未结 2 680
无人及你
无人及你 2020-12-13 04:42

Goal

I am developping a simple device running Linux. It is BLE capable, and I am currently using bluez 5.8.

I want to trigger an action

相关标签:
2条回答
  • 2020-12-13 05:08

    Eventually, I discovered the answers to all the questions I had.

    I will start by answering the last question:

    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?

    This one was answered on the bluez mailing-list, in response to me.

    Summary: the BLE connection is accepted at the HCI level by the kernel. If you want to use that connection from user space you need to use an l2cap socket with the ATT channel ID (which is 4).

    Bleno has a good example of using an L2CAP socket.

    How an L2CAP socket works is basically like this:

    /* 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?

    I realized I needed an answer to the previous question to answer that one.

    Once you can read the data over L2CAP socket, everything makes more sense, for example, if your Android phone does gatt.discoverServices(), then the little program above will read (i.e. receive):

    10 0100 ffff 0028
    

    Which basically means:

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

    This request is the way any BLE peripheral will request the list of services.

    Then, you can answer this request with the list of services your device provides, formatted according to the GATT protocol.

    Again, see the implementation of this in Bleno.

    0 讨论(0)
  • 2020-12-13 05:18

    You're really close.

    To see the services on a device using your iOS code, try adding

    peripheral.delegate = self;
    

    to your didConnectPeripheral, before the discoverServices call. I got that from the Apple documentation and it fixed things for me (just don't forget to add CBPeripheralDelegate to the interface declaration in the header file). Without it, didDiscoverServices will never be called.

    I was able to get the gatt-example service plugin to run by compiling BlueZ from source with the ./configure --enable-maintainer-mode. Then if you launch bluetoothd -nd you'll see something like

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

    near the top of the output, and then

    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
    

    At that point, my iOS app was able to see the BlueZ peripheral, connect, and discover its services (after a hciconfig hci0 leadv).

    0 讨论(0)
提交回复
热议问题