如何从蓝牙 LE 设备获取数据

2024-04-05

我有一台支持蓝牙 LE 的蓝牙条形码扫描仪,我试图在扫描时从中获取条形码信息。

我可以正常连接onServicesDiscovered被叫到我的BluetoothGattCallback但我不知道从那里该做什么。

通过经典的蓝牙连接,您将获得InputStream from a BluetoothSocket你只需等待read()为您提供数据,但我不确定它如何与蓝牙 LE 配合使用。我尝试循环遍历BluetoothGattCharacteristic正在检查该属性,如果它是我调用的读取属性gatt.readCharacteristic(characteristic);但这只是给了我无用的信息,甚至在我尝试扫描某些东西之前。

那么如何从扫描枪获取条码数据呢?

这是我的扫描仪https://www.zebra.com/us/en/support-downloads/scanners/ultra-rugged-scanners/li3608-li3678.html https://www.zebra.com/us/en/support-downloads/scanners/ultra-rugged-scanners/li3608-li3678.html


BLE设备提供的数据称为特征。这些数据包是特殊形成的、紧密封装的字节数组,它们为特定的特定值编码。Services。您可以查看指定号码 https://www.bluetooth.com/specifications/assigned-numbers/在蓝牙官方网站上。在这里您将找到定义的(权威的)GATT 服务及其所属特征。

例如,您有一个可报告速度和踏频的 BLE 自行车电脑。您可以在指定的数字规格中查找骑行速度和踏频项目。这关贸总协定服务(第 3.4 章)包含服务的 UUID (0x1816)。然后您转到蓝牙规格 https://www.bluetooth.com/specifications/specs/页面并执行以下操作:

  • 搜索骑行速度和踏频服务。结果将有链接到规格 https://www.bluetooth.com/specifications/specs/cycling-speed-and-cadence-service-1-0/CSC 服务。其中包含有关服务、其特征等的有用信息。您检查特征并记下您感兴趣的内容。在我们的例子中,它将是CSC测量特征。下一点解释如何获取它的字段。
  • 搜索关贸总协定规范补充。结果列表将包含该结果的许多修订关贸总协定规范补充 https://www.bluetooth.com/specifications/specs/gatt-specification-supplement/。该文档包含所有 GATT 特征字段、类型信息等。在 PDF 中搜索CSC测量这应该会带你到第 3.61 章。它描述了 CSC 字段、以八位位组(字节)为单位的数据大小以及有关值的附加信息。

这是一般的蓝牙 LE 部分,现在回到 Android。请注意,您必须查找这些字段才能从特征中获取值。我只是假设您已经具有想要从中获取数据的特征。这是一个检索车轮和曲柄转数(如果可用)的快速示例。

BluetoothGattCharacteristic characteristic = ... ;

int offset = 0; // we define the offset that is to be used when reading the next field

// FORMAT_* values are constants in BluetoothGattCharacteristic
// these represent the values you can find in the "Value Fields" table in the "Format" column
int flags = characteristic.getIntValue(FORMAT_UINT8, offset);

offset += 1; // UINT8 = 8 bits = 1 byte

// we have to check the flags' 0th bit to see if C1 field exists 
if ((flags & 1) != 0) {
    int cumulativeWheelRevolutions = characteristic.getIntValue(FORMAT_UINT32, offset);
    offset += 4; // UINT32 = 32 bits = 4 bytes
    
    int lastWheelEventTime = characteristic.getIntValue(FORMAT_UINT16, offset);
    offset += 2; // UINT16 = 16 bits = 2 bytes
}

// we have to check the flags' 1st bit to see if C2 field exists 
if ((flags & 2) != 0) {
    int cumulativeCrankRevolutions = characteristic.getIntValue(FORMAT_UINT16, offset);
    offset += 2;
    
    int lastCrankEventTime = characteristic.getIntValue(FORMAT_UINT16, offset);
    offset += 2;
}

The flags需要检查字段中的特定位,因为设备可能不会报告每种类型的数据,例如它不计算车轮转数。所选特征的工作表始终包含有关此字段的相关信息(如果存在)。

还值得注意的是,文档说

CSC 测量特性(CSC 指骑行速度和踏频)是一种包含标志字段的可变长度结构,并且根据标志字段的内容,可能包含一个或多个附加字段 [...]

这就是为什么您不能假设在 7 个字节(8+32+16位;分别为1+4+2字节) 偏移量,并且当您沿着字段前进时,应该计算偏移量。


这是从 BLE 设备读取骑行速度和踏频值的示例。您必须为您想要在应用程序中支持的每个设备(或更确切地说是服务)查找这些可用字段和值。如果设备是特殊设备并且无法在此 GATT 目录中找到,您需要查阅设备的手册、SDK 或供应商以获取更多信息。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何从蓝牙 LE 设备获取数据 的相关文章

随机推荐

  • 如何让Android KitKat用户不下载应用程序?

    我在Android 中开发了一个应用程序 它适用于除 KitKat 用户之外的所有用户 当我在 Play 商店上传此应用程序时 我希望 KitKat 用户无法下载此应用程序 即使当我在系统中执行此应用程序时 它也会检测 KitKat 手机
  • 在java中创建自定义对象数组

    我有 100 条数据记录 这些数据是从服务传入我的系统的 我想为每条记录创建 100 个类对象 以便将其序列化为我的自定义类 我在 for 循环内进行内存创建 如下所示 for int i 0 i lt 100 i SomeClass s1
  • 将 HTML 文件解析为 PHP

    这是将 html 文件解析为 php 的正确方法吗 RemoveHandler html htm AddType application x httpd php php htm html 保存在根文件夹中的 htaccess 文件中吗 我添
  • NSApplication windows 属性 - windows 未删除?

    我有一个显示模式的 NSWindow Controller 它有一个 关闭 按钮 连接到如下操作 IBAction close id sender self window orderOut sender self window close
  • 在多个线程中重用 Tensorflow 会话会导致崩溃

    背景 我有一些复杂的强化学习算法 我想在多个线程中运行 Problem 当尝试打电话时sess run在一个线程中我收到以下错误消息 RuntimeError The Session graph is empty Add operation
  • 为什么渲染属性会多次调用 getter?

    与前面的示例相关 我尝试监视服务器上的 get set 方法 调用它们的时间以及调用频率 所以 我的实际情况是这样的 ManagedBean name selector RequestScoped public class Selector
  • 为什么 HSQLDialect 初始化在 Spring Boot 项目中需要这么长时间?

    我编写了一个 Spring Boot REST api 它连接到具有大量表的旧版 MySQL 数据库 10759 当我启动 Spring Boot 项目时 日志停在一行 表明 HSQLDialect 正在初始化 日志需要几分钟才能恢复工作
  • Terraform:将整个资源组移动到新的 Azure 订阅

    历史上增长的项目及其在 Azure 上的相关基础设施必须进行拆分 幸运的是 它的结构良好 因此很明显我们需要将两个资源组及其附属资源移动到新的 Azure 订阅 我查过了 大部分资源可以移动 https learn microsoft co
  • 在打字稿中导入模块时,绝对路径的根是什么?

    我正在使用打字稿 在 Visual Studio 2015 中 开发一个应用程序 并具有以下基本文件结构 Solution AppProject Scripts framework Utils ts app SomeApp ts tscon
  • 通过socks的Python ssh客户端(代理)

    所以 我需要通过代理socks连接到SSH服务器 我阅读了 paramiko 和twisted conch 文档 但没有在那里找到代理袜子支持 这个套接字包装器允许您使用静态 ssh 隧道 我找到了解决我的问题的通用方法 使用帕里科SSHC
  • OCaml 在运行时编译和加载

    我正在尝试实现类似的目标eval 在 OCaml 中 我有一个string我想从中得到一个 OCaml 函数 目前我正在做以下事情 我将字符串转储到new ml并编译文件 Compile implementation Format std
  • JavaScript AJAX 远程记录器 [关闭]

    Closed 这个问题正在寻求书籍 工具 软件库等的推荐 不满足堆栈溢出指南 help closed questions 目前不接受答案 我正在平台上开发 JavaScript 应用程序 该平台不支持日志输出 不允许为记录器输出打开新窗口
  • Swift 使用 UnsafePointer 从 UnsafeMutablePointer 获取值

    我正在努力通过contextInfo of typeUnsafeMutablePointer
  • 如何将异常保存在txt文件中?

    public DataTable InsertItemDetails FeedRetailPL objFeedRetPL DataTable GetListID new DataTable try SqlParameter arParams
  • 如何知道 Mongoose 的 upsert 是否创建了新文档?

    我在 node js express js 中有这段代码 var User mongoose model User var usersRouter express Router usersRouter put id function req
  • 升级AGP版本失败

    我无法更新我的项目 当我尝试这样做时 我收到了这条消息 升级助手无法升级此项目 找不到执行将 AGP 版本从 4 1 3 升级到 4 2 0 命令的方法 可能是因为该项目的构建文件使用了升级助手当前不支持的功能 例如 使用定义的常量 在 b
  • 如何在 Treeview 控件中使子节点可见 = false

    我有一个带有树视图控件的窗口窗体 该树视图有一个根节点和 2 个子节点 我的要求是我需要隐藏第一个子节点 是否有可能使特定孩子点头可见为假 是的 您可以从树节点继承并创建您自己的行为 就像这样 public class RootNode T
  • 使用 Angular 6 和 Spring Rest API 下载文件

    我在使用 Angular 6 从 Rest api 下载文件时遇到问题 后端方法 RequestMapping value print id public ResponseEntity
  • 使用python发送消息时出现错误400

    我正在尝试使用 Gmail API 发送电子邮件 我已成功通过身份验证 并且我的计算机上有一个 client secret json 文件 我已经能够使用 Gmail API 网站上的快速入门示例获取标签列表 我已成功将范围重置为 SCOP
  • 如何从蓝牙 LE 设备获取数据

    我有一台支持蓝牙 LE 的蓝牙条形码扫描仪 我试图在扫描时从中获取条形码信息 我可以正常连接onServicesDiscovered被叫到我的BluetoothGattCallback但我不知道从那里该做什么 通过经典的蓝牙连接 您将获得I