linux bluetooth bluetooth-lowenergy core-bluetooth bluez

linux - Bluez: ¿servicio de publicidad/ejemplo de servidor gatt?



bluetooth bluetooth-lowenergy (2)

Gol

Estoy desarrollando un dispositivo simple que ejecuta Linux. Es compatible con BLE, y actualmente estoy usando bluez 5.8.

Quiero activar una acción en este dispositivo usando un iPhone.

Lo que ya funciona:

  • Puedo hacer que el iPhone "vea" el dispositivo.
  • El iPhone también se conecta al dispositivo.

Configuré el dispositivo Bluetooth como este en Linux (gracias a esta pregunta ):

# 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

El código de iOS es sencillo:

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

Al ejecutar este código en el iPhone, obtengo este registro:

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

Así que parece que el iPhone se conecta bien, pero no ve ningún servicio.

Lo que me falta

  • Necesito anunciar un servicio BLE simple, pero no puedo encontrar ninguna documentación sobre cómo hacer esto en bluez.
  • Creo que necesito algo como un servidor de Gatt para recibir características de lectura / escritura para el servicio que me gustaría anunciar. Vi el archivo plugins / gatt-example.c en bluez, pero no tengo la menor idea de cómo usarlo: no hay documentación.

Probablemente debería mencionar que vi esta pregunta: crear un servidor gatt , pero las respuestas generan demasiadas preguntas (por ejemplo, ¿dónde está la API GATT para bluez? ¿Cómo configurar la base de datos GATT? ¿Cómo registrarse para eventos de lectura / escritura? )

EDITAR: Los comandos que uso solo configuran el dispositivo BLE para anunciar algunos datos, pero iOS informa que se acepta la conexión. ¿Qué parte de bluez está aceptando conexiones entrantes?


Estás muy cerca.

Para ver los servicios en un dispositivo usando su código iOS, intente agregar

peripheral.delegate = self;

a su didConnectPeripheral , antes de la llamada discoverServices . Lo obtuve de la documentación de Apple y me solucionó las cosas (no olvide agregar CBPeripheralDelegate a la declaración de interfaz en el archivo de encabezado). Sin él, didDiscoverServices nunca será llamado.

Pude hacer que el plugin de servicio gatt-example ejecute al compilar BlueZ desde la fuente con el ./configure --enable-maintainer-mode . Entonces si bluetoothd -nd y verás algo como

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

cerca de la parte superior de la salida, y luego

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

En ese momento, mi aplicación iOS pudo ver el peripheral BlueZ, conectarse y descubrir sus servicios (después de un hciconfig hci0 leadv ).


Finalmente, descubrí las respuestas a todas las preguntas que tenía.

Comenzaré contestando la última pregunta:

Los comandos que uso solo configuran el dispositivo BLE para anunciar algunos datos, pero iOS informa que se acepta la conexión. ¿Qué parte de bluez está aceptando conexiones entrantes?

Esta fue respondida en la lista de correo de bluez , en respuesta a mí.

Resumen: la conexión BLE es aceptada en el nivel HCI por el núcleo. Si desea usar esa conexión desde el espacio de usuario, necesita usar un socket l2cap con el ID de canal ATT (que es 4).

Bleno tiene un buen ejemplo del uso de un socket L2CAP .

Cómo funciona un socket L2CAP es básicamente así:

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

¿Cómo anunciar un servicio?

Me di cuenta de que necesitaba una respuesta a la pregunta anterior para responder a esa.

Una vez que puede leer los datos a través del socket L2CAP, todo tiene más sentido, por ejemplo, si su teléfono Android hace gatt.discoverServices() , entonces el pequeño programa anterior leerá (es decir, recibirá):

10 0100 ffff 0028

Lo que básicamente significa:

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

Esta solicitud es la forma en que cualquier periférico BLE solicitará la lista de servicios.

Luego, puede responder a esta solicitud con la lista de servicios que proporciona su dispositivo, formateada de acuerdo con el protocolo GATT.

Una vez más, ver la implementación de esto en Bleno .