【问题标题】:Extract and convert NSData value from Bluetooth reading in Swift从 Swift 中的蓝牙读取中提取和转换 NSData 值
【发布时间】:2014-09-29 11:39:41
【问题描述】:

我正在尝试从 iOS 上的蓝牙 4.0 LE 秤读取值。如何将作为 NSData 接收的蓝牙特性测量值转换为专用的 Swift 对象?

作为一个规范,我知道......

Bit 0 到 Bit 12 → 重量(0 到 5000 g)

Bit 15 → 正/负重量值

func peripheral(peripheral: CBPeripheral!, didUpdateValueForCharacteristic characteristic: CBCharacteristic!, error: NSError!) {

    let value:NSData = characteristic.value

    let weight:Double = … ?
    let negative:Bool = … ?

还有一条信息——查看

value.length

看起来我总是从设备获取 1 或 2 个字节 (?) 的数据。但是我仍然不确定如何提取我正在寻找的数据/值。我会很感激任何建议。

到目前为止,有点...

    var bin16:UInt16 = 0
    characteristic.value.getBytes(&bin16, range: NSRange(location: 0,length: 1))

    println("Bytes \(characteristic.value.bytes) Length \(characteristic.value.length)")
    println("Value \(bin16)")

– 这样做我设法得到一些体重读数。除非该值大于 255 或负数,否则它似乎有效。下面是一些例子:

75 克

字节 0x1655e458 长度 1 值 75

367 克

字节 0x1765cbc8 长度 2 价值 161

-6 克

字节 0x17670f18 长度 2 值 32

此外,这在两者之间传输的频率更高——在这种情况下,它不代表 160 克。也许是某种错误代码?!

字节 0x146528b8 长度 2 价值 160

【问题讨论】:

  • 我只是添加了更多示例。看起来我只得到超过 1 个字节当需要时,这意味着当重量超过 255 克或负数时。然而,在那些 2 字节的情况下,我的基本值提取停止工作。
  • 哦,我以为characteristic.value.length给了我使用的字节数,不是吗?在我的示例 1 或 2 中输出。
  • 感谢您的澄清。要获得十六进制值,我只需使用代码示例中所写的“characteristic.value.bytes”
  • 任何示例项目都可用,所以我可以连接我的体重秤??

标签: ios bluetooth swift nsdata core-bluetooth


【解决方案1】:

需要有关接收数据的更多信息。假设这两个字节是 b0 和 b1,b0 是前导字节(接收到的第一个字节)。 1. 哪个字节有符号位。 2.符号位是最右边位(0x01)的最左边位(0x80)吗?

这是一个基于假定字节、位编号顺序和字节序的潜在解决方案:

// let value:NSData = characteristic.value
// println("value: \(value)")

let value16 = UnsafePointer<UInt16>(value.bytes)[0]

//value16 = value16.bigEndian // if value is bigendian uncomment, change let to var

println("value16: 0x\(String(value16, radix:16))")

let negative:Bool = (value16 & 0x0001) == 0 // Possibly the sign is a different bit
let weightInt = (value16 >> 4) & 0x0fff     // Possibly a different shift is needed
let weight:Double = Double(weightInt)

【讨论】:

  • 非常感谢。我试过了,但我似乎没有得到重量值。示例:83g→(value16:0x53负:假重量int:5重量:5.0),510g→(value16:0xfe负:真重量int:15重量:15.0)
【解决方案2】:

好像有两个问题。

  1. 如何将 NSData 中的二进制数据提取为 swift 数据类型
  2. 如何从二进制字符串中提取有用的数据

从 NSData 中提取 Swift 数据类型

查看上面的问题,您的数据是 16 位二进制字符串。因此,我们的第一个任务是将 16 位二进制字符串提取为数据类型。我认为UInt16 最适合这个。

func peripheral(peripheral: CBPeripheral!, didUpdateValueForCharacteristic characteristic: CBCharacteristic!, error: NSError!) {

    var bin16:UInt16 = 0
    var wrapin: NSNumber = NSNumber(unsignedShort: 0)
    characteristic.value.getBytes(&wrapin, range: NSRange(location: 0,length: 2))
    bin16 = wrapin.unsignedShortValue

从二进制字符串中提取数据

此时bin16 有一个 16 位二进制字符串。根据您的描述,重量数据存储在第 0-12 位,符号位是第 16 位。下面是如何使用按位运算符 & 和 >> 提取此数据的示例。在Swift 中查看 swift 语言指南以了解有关二元运算符的更多信息。

// The actual data
let value  : UInt16 = 0b0010_1010_0000_0000             // 2560g & positive sign
let weight : uint16 = value & 0b0001_1111_1111_1111     // Extract weight
let sign : UInt16 = value >> 15                         // Posative or negative

请注意,我做了以下假设:

  1. 你的二进制字符串是 LSB
  2. 您的二进制字符串只有 16 位长。如果不是这种情况,那么您应该使用 & 运算符而不是 >> 来提取符号。

更新 - 包括游乐场内容

// Important Note: Reading & Writing to NSData using the native UInt16 does
// not work correctly. Something happens and it mangles the data. To get 
// around this in xcode beta 5 you must wrap your data in an NSNumber.

import UIKit

// Build an NSData type
// Bit 0 through 12 --> Weight in g
// Bit 13 & 14      --> not read or used in example (discarded)
// Bit 15           --> Sign-bit
// The value below:
//      Weight: (bit 0-12) : 2560G
//        Sign: Positive
let rawvalue : UInt16 = 0b0010_1010_0000_0000


// Build NSData payload
var wrapout : NSNumber = NSNumber(unsignedShort: rawvalue)
var payload : NSData = NSData(bytes: &wrapout, length: 2)


// Extracting data
var finalWeight = 0

if payload.length >= 2 {
    var wrapin   : NSNumber = NSNumber(unsignedShort: 0)
    var bstring  : UInt16 = 0
    payload.getBytes(&wrapin, range: NSRange(location: 0, length: 2))
    bstring = wrapin.unsignedShortValue

    let weight : UInt16 = bstring & 0b0001_1111_1111_1111
    let valsign: UInt16 = (bstring & 0b1000_0000_0000_0000) >> 15

    if valsign == 0 {
        finalWeight = Int(weight)
    } else {
        finalWeight = -1 * Int(weight)
    }
}
println("\(finalWeight)")

【讨论】:

  • 这对我来说看起来不错。非常感谢。但是,当我运行此程序时,在“characteristic.value.getBytes(&bin16, range: NSRange(location: 0,length: 16))” → “... 由于未捕获的异常 'NSRangeException' 导致应用程序终止,原因:'* ** -[_NSInlineData getBytes:range:]: 范围 {0, 16} 超出数据长度 1'"
  • 我将其更改为“characteristic.value.getBytes(&bin16, range: NSRange(location: 0,length: characteristic.value.length))”……但似乎还是有问题。对于体重和符号,我每次读数都会得到“2560”和“0”。
  • 您是否在重量和符号计算中将value 更改为bin16?即let weight : uint16 = bin16 &amp; 0b0001_1111_1111_1111let sign : UInt16 = bin16 &gt;&gt; 15?
  • @MikeS 很抱歉让您感到困惑。我试图将我打开的两个测试游乐场的示例联系在一起。我在两者中都使用了不同的名称。我已经包含了我使用的完整游乐场。
  • @BerndPlontsch 我很抱歉。我的第一反应是混淆了我如何将这两个例子联系在一起。为了帮助澄清,我已经用编译我的答案时使用的实际操场文件更新了我的回复。你能发布你最近的代码吗?
猜你喜欢
  • 2014-12-07
  • 1970-01-01
  • 2021-08-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-12-14
  • 1970-01-01
相关资源
最近更新 更多