Jetson AGX Orin基于BlueZl蓝牙协议栈AOJ红外蓝牙体温计开发(低功耗蓝牙ble)

本文主要是介绍Jetson AGX Orin基于BlueZl蓝牙协议栈AOJ红外蓝牙体温计开发(低功耗蓝牙ble),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

一、准备工作

安装blueZ以及相关的蓝牙测试工具:

sudo apt update
sudo apt install bluez
sudo apt install bluez-hcidump

然后看下蓝牙设备是否识别到,已经是否处于开启状态:

root@test-desktop:~# hciconfig -a
hci0:   Type: Primary  Bus: USBBD Address: A4:42:3B:2A:47:DD  ACL MTU: 1021:4  SCO MTU: 96:6UP RUNNING PSCAN ISCANRX bytes:437421 acl:0 sco:0 events:8518 errors:0TX bytes:18880 acl:0 sco:0 commands:1564 errors:0Features: 0xbf 0xfe 0x0f 0xfe 0xdb 0xff 0x7b 0x87Packet type: DM1 DM3 DM5 DH1 DH3 DH5 HV1 HV2 HV3Link policy: RSWITCH SNIFFLink mode: SLAVE ACCEPTName: 'test-desktop'Class: 0x1c0000Service Classes: Rendering, Capturing, Object TransferDevice Class: Miscellaneous,HCI Version: 5.1 (0xa)  Revision: 0x100LMP Version: 5.1 (0xa)  Subversion: 0x100Manufacturer: Intel Corp. (2)

UP RUNNING PSCAN ISCAN说明已经打开。
此时可以通过指令bluetoothctl scan on来扫描周围的蓝牙设备;
命令bluetoothctl pair 需要连接的蓝牙MAC可以进行配对,然后再通过bluetoothctl connect 需要连接的蓝牙MAC连接对应蓝牙。

二、经典蓝牙的编程方式

无论低功耗蓝牙BLE还是经典蓝牙,都可以通过以下方式进行编程处理。

2.1 扫描设备

例程:

#include <iostream>
#include <unistd.h>
#include <sys/socket.h>
#include <bluetooth/bluetooth.h>
#include <bluetooth/hci.h>
#include <bluetooth/hci_lib.h>int main(int argc, char **argv) {int dev_id, sock, len, max_rsp, flags;int i;char addr[19] = {0};char name[248] = {0};inquiry_info *ii = NULL;dev_id = hci_get_route(NULL);sock = hci_open_dev(dev_id);if (dev_id < 0 || sock < 0) {perror("打开设备失败");exit(1);}len = 8;max_rsp = 255;flags = IREQ_CACHE_FLUSH;ii = (inquiry_info *)malloc(max_rsp * sizeof(inquiry_info));memset(ii, 0, max_rsp * sizeof(inquiry_info));int num_rsp = hci_inquiry(dev_id, len, max_rsp, NULL, &ii, flags);if (num_rsp < 0) perror("hci_inquiry");for (i = 0; i < num_rsp; i++) {ba2str(&(ii + i)->bdaddr, addr);memset(name, 0, sizeof(name));if (hci_read_remote_name(sock, &(ii + i)->bdaddr, sizeof(name), name, 0) < 0)strcpy(name, "[unknown]");printf("%s  %s", addr, name);}free(ii);close(sock);return 0;
}

2.2发送数据

//以下是一个简单的Linux蓝牙C++收发例程,用于在两个设备之间发送和接收数据:
//发送端(sender.cpp):
#include <iostream>
#include <unistd.h>
#include <sys/socket.h>
#include <bluetooth/bluetooth.h>
#include <bluetooth/rfcomm.h>int main(int argc, char **argv) {struct sockaddr_rc addr = {0};int s, status;char dest[18] = "01:23:45:67:89:AB"; // 目标设备的蓝牙地址// 分配一个 sockets = socket(AF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM);if (s == -1) {perror("socket");exit(1);}// 连接到目标设备addr.rc_family = AF_BLUETOOTH;addr.rc_channel = (uint8_t)1;str2ba(dest, &addr.rc_bdaddr);status = connect(s, (struct sockaddr *)&addr, sizeof(addr));if (status == -1) {perror("connect");exit(1);}// 发送数据const char *msg = "Hello, receiver!";status = write(s, msg, strlen(msg));if (status == -1) {perror("write");exit(1);}// 关闭 socketclose(s);return 0;
}

2.3 接收数据

//接收端(receiver.cpp):
#include <iostream>
#include <unistd.h>
#include <sys/socket.h>
#include <bluetooth/bluetooth.h>
#include <bluetooth/rfcomm.h>int main(int argc, char **argv) {struct sockaddr_rc loc_addr = {0}, rem_addr = {0};socklen_t opt = sizeof(rem_addr);int s, client, bytes_read;char buf[1024] = {0};// 分配一个 sockets = socket(AF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM);if (s == -1) {perror("socket");exit(1);}// 绑定本地地址和通道loc_addr.rc_family = AF_BLUETOOTH;loc_addr.rc_channel = (uint8_t)1;bind(s, (struct sockaddr *)&loc_addr, sizeof(loc_addr));// 监听连接请求listen(s, 1);// 接受连接请求client = accept(s, (struct sockaddr *)&rem_addr, &opt);if (client == -1) {perror("accept");exit(1);}// 读取数据bytes_read = read(client, buf, sizeof(buf));if (bytes_read > 0) {printf("Received: %s", buf);}// 关闭 socketclose(client);close(s);return 0;
}

因为我开发的是低功耗蓝牙,因此上面的例程不做过多解释。

三、低功耗蓝牙广播获取

AOJ的体温计属于低功耗蓝牙设备,并且不能通过上述的连接方式进行配对连接。只能监听蓝牙广播,获取体温数据。
例程:

bool monitor()
{int device_id = hci_get_route(nullptr);int sock = hci_open_dev(device_id);if (device_id < 0 || sock < 0){std::cerr << "Error opening socket." << std::endl;return 1;}// 设置HCI套接字选项struct hci_filter new_filter;hci_filter_clear(&new_filter);hci_filter_set_ptype(HCI_EVENT_PKT, &new_filter);hci_filter_set_event(EVT_LE_META_EVENT, &new_filter);if (setsockopt(sock, SOL_HCI, HCI_FILTER, &new_filter, sizeof(new_filter)) < 0){std::cerr << "Error setting socket options." << std::endl;close(sock);return 1;}uint8_t buf[HCI_MAX_EVENT_SIZE];while (true){ssize_t len = read(sock, buf, sizeof(buf));if (len < 0){std::cerr << "Error reading from socket." << std::endl;break;}evt_le_meta_event *meta = (evt_le_meta_event *)(buf + (1 + HCI_EVENT_HDR_SIZE));if (meta->subevent != EVT_LE_ADVERTISING_REPORT){// printf("Not BLE Advertising!\n");continue;}le_advertising_info *info = (le_advertising_info *)(meta->data + 1);char addr[19] = {0};ba2str(&info->bdaddr, addr);std::cout << "mac:" << addr << std::endl;if (compare_mac(addr, target_mac)){std::cout << "Found target device: " << addr << std::endl;std::cout << "Advertising data: ";print_advertising_data(info->data, info->length);// 处理设备广播消息}else{std::cout << "Found device: " << addr << std::endl;}sleep(0.5);}close(sock);return 0;
}

代码分析:
注意,在nano的测试中,我发现上述的程序会一直卡在if (meta->subevent != EVT_LE_ADVERTISING_REPORT) 也就是说,识别不到具体的广播包类型。
这个稍后详细说明。
首先,int device_id = hci_get_route(nullptr); int sock = hci_open_dev(device_id);都是固定代码。

struct hci_filter new_filter;hci_filter_clear(&new_filter);hci_filter_set_ptype(HCI_EVENT_PKT, &new_filter);hci_filter_set_event(EVT_LE_META_EVENT, &new_filter);if (setsockopt(sock, SOL_HCI, HCI_FILTER, &new_filter, sizeof(new_filter)) < 0){std::cerr << "Error setting socket options." << std::endl;close(sock);return 1;}

这部分用来设置套接字选项的,需要根据具体需要设置。

  1. 定义和清除过滤器
struct hci_filter new_filter;
hci_filter_clear(&new_filter);

new_filter 是一个结构体 hci_filter,用于存储和管理HCI套接字的过滤规则。
hci_filter_clear(&new_filter) 函数用于清除 new_filter 结构体的所有过滤规则,确保其初始状态为空。
2. 设置数据包类型和事件类型过滤

hci_filter_set_ptype(HCI_EVENT_PKT, &new_filter);

hci_filter_set_ptype 函数用于设置过滤器的数据包类型。在这里,使用 HCI_EVENT_PKT 表示设置过滤器捕获所有HCI事件类型的数据包。在 C 语言中,特别是在蓝牙编程中,该函数通常用于设置套接字以过滤和捕获特定类型的蓝牙数据包。

功能和用法:

功能:

hci_filter_set_ptype 用于设置 HCI 过滤器以指定接收的数据包类型。在蓝牙编程中,常见的数据包类型包括命令包(Command Packet)、事件包(Event Packet)、ACL 数据包(ACL Data Packet)和 SCO 数据包(SCO Data Packet)等。

参数:

数据包类型(Packet Type):通常作为参数传递给 hci_filter_set_ptype,用于指定要接收的数据包类型。
例如:
HCI_COMMAND_PKT:命令包类型。
HCI_EVENT_PKT:事件包类型。
HCI_ACLDATA_PKT:ACL 数据包类型。
HCI_SCODATA_PKT:SCO 数据包类型。

具体含义:

命令包(Command Packet)

类型码:HCI_COMMAND_PKT

  • 含义:用于从主机(Host)向控制器(Controller)发送命令。这些命令可以请求控制器执行各种操作,例如启动扫描、建立连接、设置参数等。命令包通常包含一个命令标识符和相关的参数。
    事件包(Event Packet):

类型码:HCI_EVENT_PKT

  • 含义:控制器向主机发送的事件报告。这些事件可以包括设备状态改变、连接状态改变、错误报告等。事件包通常包含一个事件标识符和相关的事件参数。

ACL 数据包(ACL Data Packet)

类型码:HCI_ACLDATA_PKT

  • 含义:用于传输应用数据的数据包类型。ACL(Asynchronous Connectionless Link)数据包用于在蓝牙连接上传输大部分数据,例如传输文件、音频流等。ACL 数据包通常包含数据通道标识符、数据长度和实际的应用数据。
    SCO 数据包(SCO Data Packet):

类型码:HCI_SCODATA_PKT

  • 含义:用于传输音频数据的数据包类型。SCO(Synchronous Connection-Oriented)数据包专门用于在蓝牙连接上传输实时音频数据,例如电话通话中的语音数据。SCO 数据包通常包含音频通道标识符、数据长度和音频数据。
设置过程:

在设置 HCI 套接字的过程中,首先需要创建一个 hci_filter 结构体,并使用 hci_filter_clear 函数初始化。
然后,使用 hci_filter_set_ptype 函数将特定的数据包类型设置到过滤器中。
最后,使用 setsockopt 函数将设置好的过滤器应用到 HCI 套接字上,以确保套接字只接收所需的数据包类型。

hci_filter_set_event(EVT_LE_META_EVENT, &new_filter);

hci_filter_set_event 函数用于设置过滤器捕获特定的HCI事件类型。在这里,使用 EVT_LE_META_EVENT 表示设置过滤器捕获蓝牙低功耗(BLE)元事件。
3. 应用和检查设置选项

if (setsockopt(sock, SOL_HCI, HCI_FILTER, &new_filter, sizeof(new_filter)) < 0)
{std::cerr << "Error setting socket options." << std::endl;close(sock);return 1;
}

setsockopt 函数用于设置套接字选项。在这里,通过 SOL_HCI HCI_FILTER参数设置HCI套接字的过滤器选项。
如果 setsockopt 返回小于0的值,表示设置选项时出错,可能是因为权限不足或其他问题。
如果设置失败,输出错误信息,关闭套接字并返回1表示程序执行失败。
在理解 SOL_HCI HCI_FILTER 参数的作用之前,我们需要明确以下几点:

3.1 SOL_HCI

SOL_HCI 是用于指定 socket 选项的协议层级,它用于与 Linux 下的 HCI(Host Controller Interface)套接字通信。HCI 是用于与蓝牙设备通信的标准接口,它允许在应用程序和蓝牙适配器之间进行数据交换和控制。

在 Linux 系统中,与蓝牙通信的套接字(socket)可以通过AF_BLUETOOTHBTPROTO_HCI 协议族来创建。SOL_HCI 就是指定在这一协议族中使用的选项层级。

3.2HCI_FILTER

HCI_FILTERsetsockopt 函数中用于设置 HCI 套接字过滤器选项的参数。通过设置 HCI_FILTER,可以定义套接字如何处理接收到的数据包,以及哪些数据包应该被传递给应用程序。

具体来说,通过 HCI_FILTER 可以设置的内容包括:

数据包类型(Packet Type):例如指定只接收 HCI 命令包、事件包或者数据包。
事件类型(Event Type):指定只接收特定类型的事件,如 LE 元事件(Low Energy Meta Event)。
广播频道(Advertising Channel):指定只接收特定广播频道的数据。
连接句柄(Connection Handle):指定只接收特定连接的数据。
在实际应用中,设置正确的 HCI_FILTER 可以帮助应用程序过滤掉不需要的数据包,集中处理特定类型或特定条件下的数据,从而提高数据处理效率和精度。

3.3 AOJ的广播类型

其使用的是ADV_IND广播数据包

3.3.1 ADV_IND 数据包格式

·ADV_IND(Advertising Indication·)数据包通常用于蓝牙设备定期广播自身的信息,以便周围设备发现和连接。它的格式可以分为两部分:广告数据(Advertising Data) 和 广告地址(Advertising Address)。
广告数据(Advertising Data):
包含设备广播的信息,例如设备名称、服务UUID、厂商自定义数据等。广告数据最多可以达到 31 字节。
广告地址(Advertising Address)
指示广播这些数据的设备的地址。广播地址通常是 6 字节的蓝牙设备地址。
具体字段解释:
广告数据(Advertising Data)

可能包括但不限于以下信息:
设备名称:设备的用户可见名称。
服务UUID:设备支持的服务的唯一标识符。
厂商自定义数据:设备制造商自定义的信息。
广告地址(Advertising Address):
是广播这些数据的蓝牙设备的物理地址,通常是设备的 MAC 地址。

在蓝牙(Bluetooth)中,ADV_IND 是一种广告数据包(Advertising Data Packet)的类型。它是一种广告数据类型,用于在蓝牙低功耗(Bluetooth Low Energy, BLE)中用于设备的广告和扫描响应过程中。

具体来说,ADV_IND 表示广告数据包中的广告指示器(Advertising Indicator),它包含了设备的标识信息和其他必要的数据。广告数据包(Advertising Data Packet)是蓝牙设备周期性地广播的一种数据包,用于向周围设备传达自身的存在和基本信息,例如设备类型、服务支持、厂商信息等。

在 BLE 中,广告数据包通常用于以下几种情况:

广播(Advertising):设备定期广播自身的信息,例如在处于非连接状态下向周围设备宣传自身。
扫描响应(Scan Response):在接收到扫描请求后,设备可以选择性地发送扫描响应数据包,包含更详细的信息。
ADV_IND 数据包是广告和扫描响应的核心组成部分之一,通过解析ADV_IND数据包,周围的设备可以识别和了解广播设备的特性和服务,进而采取适当的响应行动,例如连接设备或者采集设备信息。
开发过程中,我想要只接收这种广播,那么我可以设置过滤器只接收特定类型的广播数据包(例如 ADV_IND),可以结合使用 HCI_FILTER hci_filter_set_event 函数来实现。

四、遇到的问题以及解决方案

4.1 接收不到任何广播数据

ssize_t len = read(sock, buf, sizeof(buf));

程序当中,buf里面没有读到任何数据。程序运行之前,首先需要将蓝牙设备设置成扫描状态bluetoothctl scan no

4.2 接收不到对应的广播类型

if (meta->subevent != EVT_LE_ADVERTISING_REPORT)

程序中这个判断有可能会一直成立。

具体原因未知,目前的解决方案是直接解析buf中的数据:

void findAndPrint(uint8_t *array, size_t length)
{bool found = false;for (size_t i = 0; i < length - 2; ++i){if (array[i] == 0xAA && array[i + 1] == 0x01 && array[i + 2] == 0xC1){found = true;if (i + 3 + 5 <= length){ // Check if there are at least 5 bytes after the patternprintf("Temperature: %d ", (array[i + 4] << 8) + array[i + 5]);printf("Test Mode : ");switch (array[i + 6]){case 0x01:printf("Adult Mode!\n");break;case 0x02:printf("Children Mode!\n");break;case 0x03:printf("Ear Mode!\n");break;case 0x04:printf("Surface Mode!\n");break;default:break;}}else{std::cout << "Pattern found at index " << i << ", but not enough bytes after the pattern." << std::endl;}}}
}

通过 if (array[i] == 0xAA && array[i + 1] == 0x01 && array[i + 2] == 0xC1)查找特定广播协议的头,从而实现数据解析。

这篇关于Jetson AGX Orin基于BlueZl蓝牙协议栈AOJ红外蓝牙体温计开发(低功耗蓝牙ble)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1061418

相关文章

这15个Vue指令,让你的项目开发爽到爆

1. V-Hotkey 仓库地址: github.com/Dafrok/v-ho… Demo: 戳这里 https://dafrok.github.io/v-hotkey 安装: npm install --save v-hotkey 这个指令可以给组件绑定一个或多个快捷键。你想要通过按下 Escape 键后隐藏某个组件,按住 Control 和回车键再显示它吗?小菜一碟: <template

Hadoop企业开发案例调优场景

需求 (1)需求:从1G数据中,统计每个单词出现次数。服务器3台,每台配置4G内存,4核CPU,4线程。 (2)需求分析: 1G / 128m = 8个MapTask;1个ReduceTask;1个mrAppMaster 平均每个节点运行10个 / 3台 ≈ 3个任务(4    3    3) HDFS参数调优 (1)修改:hadoop-env.sh export HDFS_NAMENOD

嵌入式QT开发:构建高效智能的嵌入式系统

摘要: 本文深入探讨了嵌入式 QT 相关的各个方面。从 QT 框架的基础架构和核心概念出发,详细阐述了其在嵌入式环境中的优势与特点。文中分析了嵌入式 QT 的开发环境搭建过程,包括交叉编译工具链的配置等关键步骤。进一步探讨了嵌入式 QT 的界面设计与开发,涵盖了从基本控件的使用到复杂界面布局的构建。同时也深入研究了信号与槽机制在嵌入式系统中的应用,以及嵌入式 QT 与硬件设备的交互,包括输入输出设

OpenHarmony鸿蒙开发( Beta5.0)无感配网详解

1、简介 无感配网是指在设备联网过程中无需输入热点相关账号信息,即可快速实现设备配网,是一种兼顾高效性、可靠性和安全性的配网方式。 2、配网原理 2.1 通信原理 手机和智能设备之间的信息传递,利用特有的NAN协议实现。利用手机和智能设备之间的WiFi 感知订阅、发布能力,实现了数字管家应用和设备之间的发现。在完成设备间的认证和响应后,即可发送相关配网数据。同时还支持与常规Sof

活用c4d官方开发文档查询代码

当你问AI助手比如豆包,如何用python禁止掉xpresso标签时候,它会提示到 这时候要用到两个东西。https://developers.maxon.net/论坛搜索和开发文档 比如这里我就在官方找到正确的id描述 然后我就把参数标签换过来

Linux_kernel驱动开发11

一、改回nfs方式挂载根文件系统         在产品将要上线之前,需要制作不同类型格式的根文件系统         在产品研发阶段,我们还是需要使用nfs的方式挂载根文件系统         优点:可以直接在上位机中修改文件系统内容,延长EMMC的寿命         【1】重启上位机nfs服务         sudo service nfs-kernel-server resta

【区块链 + 人才服务】区块链集成开发平台 | FISCO BCOS应用案例

随着区块链技术的快速发展,越来越多的企业开始将其应用于实际业务中。然而,区块链技术的专业性使得其集成开发成为一项挑战。针对此,广东中创智慧科技有限公司基于国产开源联盟链 FISCO BCOS 推出了区块链集成开发平台。该平台基于区块链技术,提供一套全面的区块链开发工具和开发环境,支持开发者快速开发和部署区块链应用。此外,该平台还可以提供一套全面的区块链开发教程和文档,帮助开发者快速上手区块链开发。

Vue3项目开发——新闻发布管理系统(六)

文章目录 八、首页设计开发1、页面设计2、登录访问拦截实现3、用户基本信息显示①封装用户基本信息获取接口②用户基本信息存储③用户基本信息调用④用户基本信息动态渲染 4、退出功能实现①注册点击事件②添加退出功能③数据清理 5、代码下载 八、首页设计开发 登录成功后,系统就进入了首页。接下来,也就进行首页的开发了。 1、页面设计 系统页面主要分为三部分,左侧为系统的菜单栏,右侧

v0.dev快速开发

探索v0.dev:次世代开发者之利器 今之技艺日新月异,开发者之工具亦随之进步不辍。v0.dev者,新兴之开发者利器也,迅速引起众多开发者之瞩目。本文将引汝探究v0.dev之基本功能与优势,助汝速速上手,提升开发之效率。 何谓v0.dev? v0.dev者,现代化之开发者工具也,旨在简化并加速软件开发之过程。其集多种功能于一体,助开发者高效编写、测试及部署代码。无论汝为前端开发者、后端开发者

pico2 开发环境搭建-基于ubuntu

pico2 开发环境搭建-基于ubuntu 安装编译工具链下载sdk 和example编译example 安装编译工具链 sudo apt install cmake gcc-arm-none-eabi libnewlib-arm-none-eabi libstdc++-arm-none-eabi-newlib 注意cmake的版本,需要在3.17 以上 下载sdk 和ex