当前位置:   article > 正文

linux下BLE(低功耗蓝牙协议)C语言开发笔记(2)---ble蓝牙扫描-连接-读写_linux c语言 打开蓝牙、传输数据

linux c语言 打开蓝牙、传输数据

前言

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

这件事已经有老外实现了,但是我在交叉编译过程中遇到很多问题未解决,所以并未再继续,有兴趣的可以看看:

GitHub - labapart/gattlib: Library to access GATT information from BLE (Bluetooth Low Energy) devices

扫描

扫描参考hcitool工具源码:/tool/hcitool.c

扫描的api再hci_lib.h中已经定义了,也包含在libbluetooth.so中,可以直接使用。

  1. static void cmd_lescan(int dev_id, int argc, char **argv)
  2. {
  3. int err, opt, dd;
  4. uint8_t own_type = LE_PUBLIC_ADDRESS;
  5. uint8_t scan_type = 0x01;
  6. uint8_t filter_type = 0;
  7. uint8_t filter_policy = 0x00;
  8. uint16_t interval = htobs(0x0010);
  9. uint16_t window = htobs(0x0010);
  10. uint8_t filter_dup = 0x01;
  11. for_each_opt(opt, lescan_options, NULL) {
  12. switch (opt) {
  13. case 's':
  14. own_type = LE_RANDOM_ADDRESS;
  15. break;
  16. case 'p':
  17. own_type = LE_RANDOM_ADDRESS;
  18. break;
  19. case 'P':
  20. scan_type = 0x00; /* Passive */
  21. break;
  22. case 'w':
  23. filter_policy = 0x01; /* Whitelist */
  24. break;
  25. case 'd':
  26. filter_type = optarg[0];
  27. if (filter_type != 'g' && filter_type != 'l') {
  28. fprintf(stderr, "Unknown discovery procedure\n");
  29. exit(1);
  30. }
  31. interval = htobs(0x0012);
  32. window = htobs(0x0012);
  33. break;
  34. case 'D':
  35. filter_dup = 0x00;
  36. break;
  37. default:
  38. printf("%s", lescan_help);
  39. return;
  40. }
  41. }
  42. helper_arg(0, 1, &argc, &argv, lescan_help);
  43. if (dev_id < 0)
  44. dev_id = hci_get_route(NULL);
  45. dd = hci_open_dev(dev_id);
  46. if (dd < 0) {
  47. perror("Could not open device");
  48. exit(1);
  49. }
  50. err = hci_le_set_scan_parameters(dd, scan_type, interval, window,
  51. own_type, filter_policy, 10000);
  52. if (err < 0) {
  53. perror("Set scan parameters failed");
  54. exit(1);
  55. }
  56. err = hci_le_set_scan_enable(dd, 0x01, filter_dup, 10000);
  57. if (err < 0) {
  58. perror("Enable scan failed");
  59. exit(1);
  60. }
  61. printf("LE Scan ...\n");
  62. err = print_advertising_devices(dd, filter_type);
  63. if (err < 0) {
  64. perror("Could not receive advertising events");
  65. exit(1);
  66. }
  67. err = hci_le_set_scan_enable(dd, 0x00, filter_dup, 10000);
  68. if (err < 0) {
  69. perror("Disable scan failed");
  70. exit(1);
  71. }
  72. hci_close_dev(dd);
  73. }

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从设备端会拒绝下次连接。

  1. static int l2cap_le_att_connect(bdaddr_t *src, bdaddr_t *dst, uint8_t dst_type, int sec)
  2. {
  3. int sock;
  4. struct sockaddr_l2 srcaddr, dstaddr;
  5. struct bt_security btsec;
  6. #if 1
  7. char srcaddr_str[18];
  8. char dstaddr_str[18];
  9. ba2str(src, srcaddr_str);
  10. ba2str(dst, dstaddr_str);
  11. printf("btgatt-client: Opening L2CAP LE connection on ATT "
  12. "channel:\n\t src: %s\n\tdest: %s\n",
  13. srcaddr_str, dstaddr_str);
  14. #endif
  15. sock = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP);
  16. if (sock < 0) {
  17. perror("Failed to create L2CAP socket");
  18. return -1;
  19. }
  20. /* Set up source address */
  21. memset(&srcaddr, 0, sizeof(srcaddr));
  22. srcaddr.l2_family = AF_BLUETOOTH;
  23. srcaddr.l2_cid = htobs(ATT_CID);
  24. srcaddr.l2_bdaddr_type = 0;
  25. bacpy(&srcaddr.l2_bdaddr, src);
  26. if (bind(sock, (struct sockaddr *)&srcaddr, sizeof(srcaddr)) < 0) {
  27. perror("Failed to bind L2CAP socket");
  28. close(sock);
  29. return -1;
  30. }
  31. /* Set the security level */
  32. memset(&btsec, 0, sizeof(btsec));
  33. btsec.level = sec;
  34. if (setsockopt(sock, SOL_BLUETOOTH, BT_SECURITY, &btsec,
  35. sizeof(btsec)) != 0) {
  36. printf(stderr, "Failed to set L2CAP security level\n");
  37. close(sock);
  38. return -1;
  39. }
  40. /* Set up destination address */
  41. memset(&dstaddr, 0, sizeof(dstaddr));
  42. dstaddr.l2_family = AF_BLUETOOTH;
  43. dstaddr.l2_cid = htobs(ATT_CID);
  44. dstaddr.l2_bdaddr_type = dst_type;
  45. bacpy(&dstaddr.l2_bdaddr, dst);
  46. printf("Connecting to device...");
  47. fflush(stdout);
  48. //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]);
  49. if (connect(sock, (struct sockaddr *) &dstaddr, sizeof(dstaddr)) < 0) {
  50. perror(" Failed to connect");
  51. close(sock);
  52. return -1;
  53. }
  54. printf(" Done\n");
  55. return sock;
  56. }

服务、特征值读写

参考btgatt-client工具源码:/tool/btgatt-client.c

源码先注册一个loop循环,然后注册一系列回调,通过回调接收发现的服务和特征值,写在源码中也有,这里就不赘述了。

  1. static struct client *client_create(int fd, uint16_t mtu)
  2. {
  3. struct client *cli;
  4. cli = new0(struct client, 1);
  5. if (!cli) {
  6. printf("Failed to allocate memory for client\n");
  7. return NULL;
  8. }
  9. cli->att = bt_att_new(fd, false);
  10. if (!cli->att) {
  11. printf(stderr, "Failed to initialze ATT transport layer\n");
  12. bt_att_unref(cli->att);
  13. free(cli);
  14. return NULL;
  15. }
  16. if (!bt_att_set_close_on_unref(cli->att, true)) {
  17. printf("Failed to set up ATT transport layer\n");
  18. bt_att_unref(cli->att);
  19. free(cli);
  20. return NULL;
  21. }
  22. if (!bt_att_register_disconnect(cli->att, att_disconnect_cb, NULL,
  23. NULL)) {
  24. printf(stderr, "Failed to set ATT disconnect handler\n");
  25. bt_att_unref(cli->att);
  26. free(cli);
  27. return NULL;
  28. }
  29. cli->fd = fd;
  30. cli->db = gatt_db_new();
  31. if (!cli->db) {
  32. printf("Failed to create GATT database\n");
  33. bt_att_unref(cli->att);
  34. free(cli);
  35. return NULL;
  36. }
  37. cli->gatt = bt_gatt_client_new(cli->db, cli->att, mtu);
  38. if (!cli->gatt) {
  39. printf("Failed to create GATT client\n");
  40. gatt_db_unref(cli->db);
  41. bt_att_unref(cli->att);
  42. free(cli);
  43. return NULL;
  44. }
  45. gatt_db_register(cli->db, service_added_cb, service_removed_cb,NULL, NULL);
  46. bt_att_set_debug(cli->att, att_debug_cb, "att: ", NULL);
  47. bt_gatt_client_set_debug(cli->gatt, gatt_debug_cb, "gatt: ",
  48. NULL);
  49. bt_gatt_client_ready_register(cli->gatt, ready_cb, cli, NULL);
  50. bt_gatt_client_set_service_changed(cli->gatt, service_changed_cb, cli,NULL);
  51. /* bt_gatt_client already holds a reference */
  52. gatt_db_unref(cli->db);
  53. return cli;
  54. }

实现的方法有了,接下来就是如何把这些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

我已经编译上传基于bluez5.50修改的gattapi库_bluezgatt-嵌入式代码类资源-CSDN下载

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Gausst松鼠会/article/detail/483742
推荐阅读
相关标签
  

闽ICP备14008679号