Bluez: service de publicité / exemple de serveur du gatt?

Objectif

je développe un simple appareil fonctionnant sous Linux. J'utilise actuellement bluez 5.8.

je veux déclencher une action sur cet appareil à l'aide d'un iPhone.

ce qui fonctionne déjà:

  • je peux faire le iPhone "voir" l'appareil.
  • l'iPhone se connecte aussi à l'appareil.

j'ai configuré le périphérique bluetooth comme ceci sur linux (grâce à cette 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

le code iOS est simple:

- (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);
    }
}

lors de l'exécution de ce code sur l'iPhone, je reçois ce log:

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

il semble donc que l'iPhone se connecte bien, mais ne voit pas de service.

ce que je suis manquant

  • j'ai besoin de faire de la publicité pour un simple BLE1519100920 "service , mais je ne trouve aucune documentation sur la façon de le faire dans bluez .
  • je pense que j'ai besoin de quelque chose comme une gatt-serveur pour recevoir de lecture/écriture des caractéristiques du service que je voudrais faire de la publicité. J'ai vu l'exemple plugins/gatt.c fichier dans bluez, mais je n'ai absolument aucune idée de comment l'utiliser: il n'y a pas de documentation.

je devrais probablement mentionner que j'ai vu cette question: création d'un serveur du gatt , mais les réponses soulèvent trop de questions (par exemple, où est l'api du GATT pour bluez? comment configurer la base de données du GATT? Comment s'inscrire à des événements de lecture/écriture?)

EDIT: Les commandes que j'utilise seulement la mise en place de L'appareil BLE pour annoncer certaines données, MAIS IOS rapports que la connexion est accepté . Quelle partie de bluez est d'accepter connexions?

24
demandé sur Community 2013-12-19 16:52:51

2 réponses

Finalement, j'ai découvert les réponses à toutes les questions que j'avais.

je vais commencer par répondre à la dernière question:

les commandes que j'utilise seulement configurer L'appareil blable pour annoncer certaines données, MAIS IOS rapports que la connexion est acceptée. Quelle partie de bluez accepte les connexions entrantes?

celui-ci était répondu sur la liste de diffusion bluez , en réponse à je.

résumé: la connexion blable est acceptée au niveau HCI par le noyau. Si vous voulez utiliser cette connexion depuis l'espace utilisateur, vous devez utiliser une socket l2cap avec L'ID du canal ATT (qui est 4).

Bleno a un bon exemple d'utilisation D'un socket L2CAP .

comment fonctionne une socket L2CAP est fondamentalement comme ceci:

/* 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);
}

Comment faire faire de la publicité pour un service?

j'ai réalisé que j'avais besoin d'une réponse à la question précédente pour répondre à celle-ci.

une fois que vous pouvez lire les données sur L2CAP socket, tout a plus de sens, par exemple, si votre téléphone Android fait gatt.discoverServices() , alors le petit programme ci-dessus Lira (i.e. receive):

10 0100 ffff 0028

qui signifie essentiellement:

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

cette demande est la façon dont tout périphériques demande la liste des services.

alors, vous pouvez répondre à cette demande avec la liste des services que votre appareil fournit, formatée selon le protocole du GATT.

de nouveau, voir la mise en œuvre de ceci dans Bleno .

16
répondu Gilles Gregoire 2014-11-26 20:34:54

vous êtes vraiment proche.

pour voir les services sur un appareil en utilisant votre code iOS, essayez d'ajouter

peripheral.delegate = self;

à votre didConnectPeripheral , avant l'appel discoverServices . J'ai obtenu cela de la documentation Apple et il a corrigé des choses pour moi (juste n'oubliez pas d'ajouter CBPeripheralDelegate à la déclaration d'interface dans le fichier d'en-tête). Sans lui, didDiscoverServices ne sera jamais appelé.

j'étais capable d'obtenir le plugin de service gatt-example à exécuter en compilant BlueZ à partir des sources avec le ./configure --enable-maintainer-mode . Ensuite, si vous lancez bluetoothd -nd vous verrez quelque chose comme

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

près du haut de la sortie, puis

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

à ce moment, mon application iOS a pu voir le BlueZ peripheral , se connecter, et découvrir ses services (après un hciconfig hci0 leadv ).

4
répondu Gabriel 2014-01-21 04:51:24