Linux Bluez:广告服务/gatt 服务器示例?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/20682294/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Bluez: advertise service / gatt server example?
提问by Gilles Gregtheitroade
Goal
目标
I am developping a simple device running Linux. It is BLE capable, and I am currently using bluez 5.8.
我正在开发一个运行 Linux 的简单设备。它支持 BLE,我目前使用的是 bluez 5.8。
I want to trigger an action on this device using an iPhone.
我想使用 iPhone 在此设备上触发操作。
What already works:
什么已经有效:
- I can make the iPhone "see" the device.
- The iPhone also connects to the device.
- 我可以让 iPhone“看到”设备。
- iPhone 也连接到该设备。
I setup the bluetooth device like this on linux (thanks to this question):
我在 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
The iOS code is straightforward:
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);
}
}
When running this code on the iPhone, I get this log:
在 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
So it seems that the iPhone connects fine, but does not see any service.
所以看起来 iPhone 连接正常,但没有看到任何服务。
What I am missing
我缺少什么
- I need to advertise a simple BLE service, but I can't find any documentation on how to do this in bluez .
- I think I need something like a gatt-server to receive read/write characteristics for the service I would advertise. I saw the plugins/gatt-example.c file in bluez, but I have absolutely no idea how to use it: there is no documentation.
- 我需要宣传一个简单的 BLE服务,但在 bluez 中找不到任何有关如何执行此操作的文档。
- 我想我需要像 gatt-server 这样的东西来接收我要宣传的服务的读/写特征。我在 bluez 中看到了 plugins/gatt-example.c 文件,但我完全不知道如何使用它:没有文档。
I should probably mention that I saw this question: Creating a gatt server, but the answers raise too much questions (for example, where is the GATT api for bluez? how to set the GATT database? How to register for read/write events?)
我应该提到我看到了这个问题:Creating a gatt server,但答案提出了太多问题(例如,bluez 的 GATT api 在哪里?如何设置 GATT 数据库?如何注册读/写事件? )
EDIT: 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?
编辑:我使用的命令仅用于设置 BLE 设备来通告一些数据,但 iOS 报告已接受连接。bluez 的哪一部分正在接受传入连接?
采纳答案by Gilles Gregtheitroade
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?
我使用的命令只设置 BLE 设备来通告一些数据,但 iOS 报告连接已被接受。bluez 的哪一部分正在接受传入连接?
This one was answered on the bluez mailing-list, in response to me.
这个是在 bluez mailing-list 上回答的,作为对我的回应。
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).
总结:内核在 HCI 级别接受 BLE 连接。如果要从用户空间使用该连接,则需要使用带有 ATT 通道 ID(4)的 l2cap 套接字。
Bleno has a good example of using an L2CAP socket.
How an L2CAP socket works is basically like this:
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?
如何宣传服务?
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):
一旦你可以通过 L2CAP 套接字读取数据,一切就更有意义了,例如,如果你的 Android 手机这样做gatt.discoverServices()
,那么上面的小程序将读取(即接收):
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.
此请求是任何 BLE 外设请求服务列表的方式。
Then, you can answer this request with the list of services your device provides, formatted according to the GATT protocol.
然后,您可以使用根据 GATT 协议格式化的设备提供的服务列表来回答此请求。
回答by Gabriel
You're really close.
你真的很亲近。
To see the services on a device using your iOS code, try adding
要使用您的 iOS 代码查看设备上的服务,请尝试添加
peripheral.delegate = self;
to your didConnectPeripheral
, before the discoverServices
call. I got that from the Apple documentationand 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.
到您的didConnectPeripheral
, 在discoverServices
通话之前。我从Apple 文档中得到了它,它为我修复了一些问题(只是不要忘记添加CBPeripheralDelegate
到头文件中的接口声明中)。没有它,didDiscoverServices
永远不会被调用。
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
我能够gatt-example
通过从源代码编译 BlueZ 来运行服务插件./configure --enable-maintainer-mode
。然后如果你启动bluetoothd -nd
你会看到类似的东西
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
).
那时,我的 iOS 应用程序能够看到 BlueZ peripheral
、连接并发现它的服务(在 a 之后hciconfig hci0 leadv
)。