赞
踩
bluez编译完后会生成很多命令行工具,比如gatttool、hcitool、bluetoothctl等,bluetoothctl的生成需要在configure的时候把--disable-test去掉。这些工具可以用来在linux环境下与ble设备进行调试,但是本人需要的是可用的c语言api,如果你只是开发经典蓝牙,那么恭喜你,交叉编译完后的的api足够用了;但是低功耗蓝牙用的C接口是没有的,下面就是我的趟坑过程,希望对大家有所帮助。
虽然bluez并没有给c提供直接可用的ble接口,但是通过分析源码我们可以找到更下层实现方式。
1.先熟悉各个工具,用工具连接ble设备实现服务、关键字的读写,经测试可正常使用的工具是hcitool(ble扫描),gatttool、bluetoothctl、btgatt-client;
2.分析以上工具的源码,提取可用的api,将bluez源码重新编译成为我所用的api。
这件事已经有老外实现了,但是我在交叉编译过程中遇到很多问题未解决,所以并未再继续,有兴趣的可以看看:
扫描参考hcitool工具源码:/tool/hcitool.c
扫描的api再hci_lib.h中已经定义了,也包含在libbluetooth.so中,可以直接使用。
- static void cmd_lescan(int dev_id, int argc, char **argv)
- {
- int err, opt, dd;
- uint8_t own_type = LE_PUBLIC_ADDRESS;
- uint8_t scan_type = 0x01;
- uint8_t filter_type = 0;
- uint8_t filter_policy = 0x00;
- uint16_t interval = htobs(0x0010);
- uint16_t window = htobs(0x0010);
- uint8_t filter_dup = 0x01;
-
- for_each_opt(opt, lescan_options, NULL) {
- switch (opt) {
- case 's':
- own_type = LE_RANDOM_ADDRESS;
- break;
- case 'p':
- own_type = LE_RANDOM_ADDRESS;
- break;
- case 'P':
- scan_type = 0x00; /* Passive */
- break;
- case 'w':
- filter_policy = 0x01; /* Whitelist */
- break;
- case 'd':
- filter_type = optarg[0];
- if (filter_type != 'g' && filter_type != 'l') {
- fprintf(stderr, "Unknown discovery procedure\n");
- exit(1);
- }
-
- interval = htobs(0x0012);
- window = htobs(0x0012);
- break;
- case 'D':
- filter_dup = 0x00;
- break;
- default:
- printf("%s", lescan_help);
- return;
- }
- }
- helper_arg(0, 1, &argc, &argv, lescan_help);
-
- if (dev_id < 0)
- dev_id = hci_get_route(NULL);
-
- dd = hci_open_dev(dev_id);
- if (dd < 0) {
- perror("Could not open device");
- exit(1);
- }
-
- err = hci_le_set_scan_parameters(dd, scan_type, interval, window,
- own_type, filter_policy, 10000);
- if (err < 0) {
- perror("Set scan parameters failed");
- exit(1);
- }
-
- err = hci_le_set_scan_enable(dd, 0x01, filter_dup, 10000);
- if (err < 0) {
- perror("Enable scan failed");
- exit(1);
- }
-
- printf("LE Scan ...\n");
-
- err = print_advertising_devices(dd, filter_type);
- if (err < 0) {
- perror("Could not receive advertising events");
- exit(1);
- }
-
- err = hci_le_set_scan_enable(dd, 0x00, filter_dup, 10000);
- if (err < 0) {
- perror("Disable scan failed");
- exit(1);
- }
-
- hci_close_dev(dd);
- }
hci_get_route取得当前device
hci_open_dev打开本机的蓝牙设备
hci_le_set_scan_parameters设置扫描参数
hci_le_set_scan_enable开始扫描
print_advertising_devices打印扫描结果
参考btgatt-client工具源码:/tool/btgatt-client.c
连接使用的l2cap层,本质还是socket,网上给的例子我连接提示host is down,跟下面的源码比对后发现是安全参数设置的问题:
setsockopt(sock, SOL_BLUETOOTH, BT_SECURITY, &btsec,sizeof(btsec));
这个参数设置完毕后就可以正常连接了,注意每次连接完毕后接的关闭,不然ble从设备端会拒绝下次连接。
- static int l2cap_le_att_connect(bdaddr_t *src, bdaddr_t *dst, uint8_t dst_type, int sec)
- {
- int sock;
- struct sockaddr_l2 srcaddr, dstaddr;
- struct bt_security btsec;
- #if 1
- char srcaddr_str[18];
- char dstaddr_str[18];
-
-
- ba2str(src, srcaddr_str);
- ba2str(dst, dstaddr_str);
-
- printf("btgatt-client: Opening L2CAP LE connection on ATT "
- "channel:\n\t src: %s\n\tdest: %s\n",
- srcaddr_str, dstaddr_str);
-
- #endif
- sock = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP);
- if (sock < 0) {
- perror("Failed to create L2CAP socket");
- return -1;
- }
-
- /* Set up source address */
- memset(&srcaddr, 0, sizeof(srcaddr));
- srcaddr.l2_family = AF_BLUETOOTH;
- srcaddr.l2_cid = htobs(ATT_CID);
- srcaddr.l2_bdaddr_type = 0;
- bacpy(&srcaddr.l2_bdaddr, src);
-
- if (bind(sock, (struct sockaddr *)&srcaddr, sizeof(srcaddr)) < 0) {
- perror("Failed to bind L2CAP socket");
- close(sock);
- return -1;
- }
-
- /* Set the security level */
- memset(&btsec, 0, sizeof(btsec));
- btsec.level = sec;
- if (setsockopt(sock, SOL_BLUETOOTH, BT_SECURITY, &btsec,
- sizeof(btsec)) != 0) {
- printf(stderr, "Failed to set L2CAP security level\n");
- close(sock);
- return -1;
- }
-
- /* Set up destination address */
- memset(&dstaddr, 0, sizeof(dstaddr));
- dstaddr.l2_family = AF_BLUETOOTH;
- dstaddr.l2_cid = htobs(ATT_CID);
- dstaddr.l2_bdaddr_type = dst_type;
- bacpy(&dstaddr.l2_bdaddr, dst);
-
- printf("Connecting to device...");
- fflush(stdout);
- //printf("-%02x-%02x-%02x-%02x-%02x-%02x-\n",dstaddr.l2_bdaddr.b[0],dstaddr.l2_bdaddr.b[1],dstaddr.l2_bdaddr.b[2],dstaddr.l2_bdaddr.b[3],dstaddr.l2_bdaddr.b[4],dstaddr.l2_bdaddr.b[5]);
- if (connect(sock, (struct sockaddr *) &dstaddr, sizeof(dstaddr)) < 0) {
- perror(" Failed to connect");
- close(sock);
- return -1;
- }
-
- printf(" Done\n");
-
- return sock;
- }
参考btgatt-client工具源码:/tool/btgatt-client.c
源码先注册一个loop循环,然后注册一系列回调,通过回调接收发现的服务和特征值,写在源码中也有,这里就不赘述了。
- static struct client *client_create(int fd, uint16_t mtu)
- {
- struct client *cli;
-
- cli = new0(struct client, 1);
- if (!cli) {
- printf("Failed to allocate memory for client\n");
- return NULL;
- }
-
-
- cli->att = bt_att_new(fd, false);
- if (!cli->att) {
- printf(stderr, "Failed to initialze ATT transport layer\n");
- bt_att_unref(cli->att);
- free(cli);
- return NULL;
- }
-
- if (!bt_att_set_close_on_unref(cli->att, true)) {
- printf("Failed to set up ATT transport layer\n");
- bt_att_unref(cli->att);
- free(cli);
- return NULL;
- }
-
- if (!bt_att_register_disconnect(cli->att, att_disconnect_cb, NULL,
- NULL)) {
- printf(stderr, "Failed to set ATT disconnect handler\n");
- bt_att_unref(cli->att);
- free(cli);
- return NULL;
- }
-
- cli->fd = fd;
- cli->db = gatt_db_new();
- if (!cli->db) {
- printf("Failed to create GATT database\n");
- bt_att_unref(cli->att);
- free(cli);
- return NULL;
- }
-
- cli->gatt = bt_gatt_client_new(cli->db, cli->att, mtu);
- if (!cli->gatt) {
- printf("Failed to create GATT client\n");
- gatt_db_unref(cli->db);
- bt_att_unref(cli->att);
- free(cli);
- return NULL;
- }
-
- gatt_db_register(cli->db, service_added_cb, service_removed_cb,NULL, NULL);
-
- bt_att_set_debug(cli->att, att_debug_cb, "att: ", NULL);
- bt_gatt_client_set_debug(cli->gatt, gatt_debug_cb, "gatt: ",
- NULL);
- bt_gatt_client_ready_register(cli->gatt, ready_cb, cli, NULL);
- bt_gatt_client_set_service_changed(cli->gatt, service_changed_cb, cli,NULL);
-
- /* bt_gatt_client already holds a reference */
- gatt_db_unref(cli->db);
-
- return cli;
- }
实现的方法有了,接下来就是如何把这些api(btgatt-client.c中用到api)为我所用了,下面是我目录结构:
文件目录介绍
btio--------头文件
include-----bluez交叉编译时生成的头文件
lib---------静态依赖库,可自行修改Makefile编译
libb--------bluez交叉编译时生成的lib库
monitor-----头文件
profiles----头文件
src---------头文件
src/shared--静态依赖库,可自行修改Makefile编译
conn.c------测试demon
lib、src/shared中的代码编译成库文件,配合交叉编译bluez生成的so库一起使用即可直接调用上面提到的api了。
ps:以上基于bluez5.50源码修改,上面用到的库根据平台不同需要自行重新编译哦
github:https://github.com/guanggaungniao/bluez5.5_gattlib_luogf
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。