Подтвердить что ты не робот

Bluez: пример рекламного сервиса/gatt-сервера?

Цель

Я разрабатываю простое устройство под управлением Linux. Он поддерживает BLE, и в настоящее время я использую bluez 5.8.

Я хочу активировать действие на этом устройстве с помощью iPhone.

Что уже работает:

  • Я могу заставить iPhone "видеть" устройство.
  • iPhone также подключается к устройству.

Я устанавливаю устройство Bluetooth как это на linux (спасибо этот вопрос):

# 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-сервера для получения характеристик чтения/записи для сервиса, который я бы рекламировал. Я видел файл plugins/gatt-example.c в bluez, но я понятия не имею, как его использовать: документации нет.

Возможно, я должен упомянуть, что я видел этот вопрос: Создание gatt-сервера, но ответы задают слишком много вопросов (например, где GATT api для bluez? Как установить базу данных GATT? Как зарегистрироваться для событий чтения/записи?)

EDIT: Команды, которые я использую, только настраивают устройство BLE для рекламы некоторых данных, но iOS сообщает, что соединение принято. Какая часть bluez принимает входящие соединения?

4b9b3361

Ответ 1

В конце концов, я обнаружил ответы на все вопросы, которые у меня были.

Начну с ответа на последний вопрос:

Команды, которые я использую, только настраивают устройство BLE для рекламы некоторых данных, но iOS сообщает, что соединение принято. Какая часть bluez принимает входящие соединения?

На этот ответ ответили на список рассылки bluez, в ответ на меня.

Реферат: соединение BLE принимается на уровне HCI ядром. Если вы хотите использовать это соединение из пользовательского пространства, вам нужно использовать сокет l2cap с идентификатором канала ATT (который равен 4).

У Bleno есть хороший пример использования сокета L2CAP.

Как работает сокет 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.

Опять же, посмотрите на реализацию этого в Bleno.

Ответ 2

Вы действительно близки.

Чтобы просмотреть службы на устройстве с использованием кода iOS, попробуйте добавить

peripheral.delegate = self;

к вашему didConnectPeripheral, перед вызовом discoverServices. Я получил это из документации Apple, и он исправил все для меня (просто не забудьте добавить CBPeripheralDelegate в объявление интерфейса в заголовке файл). Без него didDiscoverServices никогда не будет вызываться.

Мне удалось получить плагин службы gatt-example для запуска, скомпилировав BlueZ из источника с помощью ./configure --enable-maintainer-mode. Затем, если вы запустите 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).