3

Ich versuche, eine Verbindung zu einem Bluetooth LE-/Bluetooth Smart-/BLE-Gerät mit Gesundheitsthermometer (0x1809) herzustellen, wie hier offiziell beschrieben: https://developer.bluetooth.org/gatt/services/Pages/ServiceViewer.aspx?u=org.bluetooth.service.health_thermometer.xml. Insbesondere ersuche ich um Benachrichtigungen vom Gesundheitsthermometer-Merkmal (0x2A1C) mit der folgenden Beschreibung: https://developer.bluetooth.org/gatt/characteristics/Pages/CharacteristicViewer.aspx?u=org.bluetooth.characteristic.temperature_measurement.xml.So lesen Sie einen BLE-Merkmalsschwimmer in Swift

Ich habe einen anständigen Swift 2 Hintergrund, aber ich habe noch nie so eng mit NSData, Bytes oder bitweise Operatoren gearbeitet und ich bin komplett neu bei Little Endian vs. Big Endian, also ist das ziemlich neu für mich und Ich könnte etwas Hilfe gebrauchen. Das Merkmal enthält eine Logik, die festlegt, welche Daten Sie erhalten. Ich habe Daten in der Reihenfolge von Flags, Temperaturmesswert und Zeitstempel bisher in 100% der Zeit erhalten, aber leider bekomme ich immer die Steuerlogik von "010", was bedeutet, dass ich die Flags falsch lese. In Wahrheit denke ich, dass ich alles, was vom Timestamp schief ist, falsch einbringe. Ich schließe ein, welche Daten ich in den Code-Kommentaren sehe.

Ich habe mehrere Möglichkeiten versucht, diese Binärdaten zu erhalten. Die Flags sind ein einzelnes Byte mit Bitoperatoren. Die Temperaturmessung selbst ist ein Float, und es dauerte einige Zeit, bis ich erkannte, dass es kein Swift Float ist, sondern ein ISO/IEEE-Standard "IEEE-11073 32-Bit FLOAT" mit der Angabe "EXPONENT VALUE" "hier: https://www.bluetooth.com/specifications/assigned-numbers/format-types. Ich weiß nicht einmal, was das bedeutet. Hier ist mein Code aus dem didUpdateValueForCharacteristic() Funktion, wo man meine mehrere Versuche anzeigen können, die ich bemerkte, wie ich ein neues ausprobiert:

// Parse Characteristic Response 
let stream = NSInputStream(data: characteristic.value!) 
stream.open() // IMPORTANT 

// Retrieve Flags 
var readBuffer = Array<UInt8>(count: 1, repeatedValue: 0) 
stream.read(&readBuffer, maxLength: readBuffer.count) 
var flags = String(readBuffer[ 0 ], radix: 2) 
flags = String(count: 8 - flags.characters.count, repeatedValue: Character("0")) + flags 
flags = String(flags.characters.reverse()) 
print("FLAGS: \(flags)") 

// Example data: 
// ["01000000"] 
// 
// This appears to be wrong. I should be getting "10000000" according to spec 

// Bluetooth FLOAT-TYPE is defined in ISO/IEEE Std. 11073 
// FLOATs are 32 bit 
// Format [8bit exponent][24bit mantissa] 

/* Attempt 1 - Read in a Float - Doesn't work since it's an IEEE Float 
readBuffer = Array<UInt8>(count: 4, repeatedValue: 0) 
stream.read(&readBuffer, maxLength: readBuffer.count) 
var tempData = UnsafePointer<Float>(readBuffer).memory 

// Attempt 2 - Inverted bytes- Doesn't work since it's wrong and it's an IEEE Float 
let readBuffer2 = [ readBuffer[ 3 ], readBuffer[ 2 ], readBuffer[ 1 ], readBuffer[ 0 ] ] 
var tempValue = UnsafePointer<Float>(readBuffer2).memory 
print("TEMP: \(tempValue)") 

// Attempt 3 - Doesn't work for 1 or 2 since it's an IEEE Float 
var f:Float = 0.0 
memccpy(&f, readBuffer, 4, 4) 
print("TEMP: \(f)") 
var f2:Float = 0.0 
memccpy(&f2, readBuffer2, 4, 4) 
print("TEMP: \(f2)") 

// Attempt 4 - Trying to Read an Exponent and a Mantissa - Didn't work 
readBuffer = Array<UInt8>(count: 1, repeatedValue: 0) 
stream.read(&readBuffer, maxLength: readBuffer.count) 
let exponent = UnsafePointer<Int8>(readBuffer).memory 

readBuffer = Array<UInt8>(count: 3, repeatedValue: 0) 
stream.read(&readBuffer, maxLength: readBuffer.count) 
let mantissa = UnsafePointer<Int16>(readBuffer).memory 

let temp = NSDecimalNumber(mantissa: mantissa, exponent: exponent, isNegative: false) 
print("TEMP: \(temp)") 

// Attempt 5 - Invert bytes - Doesn't work 
readBuffer = Array<UInt8>(count: 4, repeatedValue: 0) 
stream.read(&readBuffer, maxLength: readBuffer.count) 
let exponentBuffer = [ readBuffer[ 3 ] ] 
let mantissaBuffer = [ readBuffer[ 2 ], readBuffer[ 1 ], readBuffer[ 0 ] ] 
let exponent = UnsafePointer<Int16>(exponentBuffer).memory 
let mantissa = UnsafePointer<UInt64>(mantissaBuffer).memory 
let temp = NSDecimalNumber(mantissa: mantissa, exponent: exponent, isNegative: false) 
print("TEMP: \(temp)") 

// Attempt 6 - Tried a bitstream frontwards and backwards - Doesn't work 
readBuffer = Array<UInt8>(count: 4, repeatedValue: 0) 
stream.read(&readBuffer, maxLength: readBuffer.count) 

var bitBuffer: [String] = Array<String>(count:4, repeatedValue: "") 
for var i = 0; i < bitBuffer.count; i++ { 
    bitBuffer[ i ] = String(readBuffer[ i ], radix: 2) 
    bitBuffer[ i ] = String(count: 8 - bitBuffer[ i ].characters.count, repeatedValue: Character("0")) + bitBuffer[ i ] 
    //bitBuffer[ i ] = String(bitBuffer[ i ].characters.reverse()) 
} 
print("TEMP: \(bitBuffer)") 

// Attempt 7 - More like the Obj. C code - Doesn't work 
readBuffer = Array<UInt8>(count: 4, repeatedValue: 0) 
stream.read(&readBuffer, maxLength: readBuffer.count) 
let value = UnsafePointer<UInt32>(readBuffer).memory 
let tempData = CFSwapInt32LittleToHost(value) 

let exponent = tempData >> 24 
let mantissa = tempData & 0x00FFFFFF 

if (tempData == 0x007FFFFF) { 
    print(" *** INVALID *** ") 
    return 
} 

let tempValue = Double(mantissa) * pow(10.0, Double(exponent)) 
print("TEMP: \(tempValue)") 

// Attempt 8 - Saw that BLE spec says "NO Exponent" - Doesnt' work 
readBuffer = Array<UInt8>(count: 1, repeatedValue: 0) 
stream.read(&readBuffer, maxLength: readBuffer.count) 

readBuffer = Array<UInt8>(count: 3, repeatedValue: 0) 
stream.read(&readBuffer, maxLength: readBuffer.count) 
let tempValue = UnsafePointer<Float>(readBuffer).memory 
print("TEMP: \(tempValue)") 

// Example data: 
// ["00110110", "00000001", "00000000", "11111111"] 
// 
// Only the first byte appears to ever change. 
*/ 


// Timestamp - Year - works 
readBuffer = Array<UInt8>(count: 2, repeatedValue: 0) 
stream.read(&readBuffer, maxLength: readBuffer.count) 
let year = UnsafePointer<UInt16>(readBuffer).memory 

// Timestamp Remainder - works 
readBuffer = Array<UInt8>(count: 5, repeatedValue: 0) 
stream.read(&readBuffer, maxLength: readBuffer.count) 
let month = readBuffer[ 0 ] 
let day = readBuffer[ 1 ] 
let hour = readBuffer[ 2 ] 
let minute = readBuffer[ 3 ] 
let second = readBuffer[ 4 ] 
print("TIMESTAMP: \(month)/\(day)/\(year) \(hour):\(minute):\(second)") 

ich dieses Beispiel in Objective C gefunden habe, die ich nicht weiß (https://github.com/AngelSensor/angel-sdk/blob/b7459d9c86c6a5c72d8e58b696345b642286b876/iOS/SDK/Services/HealthThermometer/ANHTTemperatureMeasurmentCharacteristic.m), und ich habe versucht, von ihm zu arbeiten, aber es ist mir nicht klar, was genau vor sich geht:

// flags 
    uint8_t flags = dataPointer[0]; 
    dataPointer++; 

    // temperature 
    uint32_t tempData = (uint32_t)CFSwapInt32LittleToHost(*(uint32_t *)dataPointer); 
    dataPointer += 4; 

    int8_t exponent = (int8_t)(tempData >> 24); 
    int32_t mantissa = (int32_t)(tempData & 0x00FFFFFF); 

    if (tempData == 0x007FFFFF) { 
     return; 
    } 

    float tempValue = (float)(mantissa*pow(10, exponent)); 

Wenn mir jemand helfen könnte aus mit, wie die Flaggen und Thermometer Messungen ziehen aus Diese BLE-Eigenschaft würde ich sehr dankbar sein. Vielen Dank.

Ich wurde gebeten, Beispieldaten unten zu geben. Hier ist mein Beispieldaten ist (12 Bytes gesamt):

["00000010", "00110011", "00000001", "00000000", "11111111", "11100000", "00000111", "00000100", "00001111", "00000001", "00000101", "00101100"] 

-OR- 

<025e0100 ffe00704 0f11150f> 
+0

Können Sie ein Beispiel für Ihre Eingangsdaten veröffentlichen? – pbush25

+0

Enthalten oben. Lass es mich wissen, wenn du etwas anderes meintest. Vielen Dank. – pennstump

+0

Sie erstellen also ein Array von Strings aus den Bits, die Sie von BLE erhalten? Ich bin verwirrt, was der Zweck der letzten Funktion ist, die Sie gerade gepostet haben. – pbush25

Antwort

0

ich ein paar Sachen gemacht habe ähnlich wie Sie ... Ich bin nicht sicher, ob dies immer noch für Sie relevant sind, aber seien wir graben ... Vielleicht ist meine Code könnte Ihnen einen Einblick:

zunächst erhalten die NSData auf ein Array von UInt8:

let arr = Array(UnsafeBufferPointer(start: UnsafePointer<UInt8>(data.bytes), count: data.length)) 

die Spezifikation, die wir verfolgen, sagt, dass die ersten drei Positionen in diesem Array wird die Mantisse repräsentieren und der letzte, wird der Exponent sein (im Bereich -128..127):

let exponentRaw = input[3] 
    var exponent = Int16(exponentRaw) 

    if exponentRaw > 0x7F { 
     exponent = Int16(exponentRaw) - 0x100 
    } 

    let mantissa = sumBits(Array(input[0...2])) 

    let magnitude = pow(10.0, Float32(exponent)) 
    let value = Float32(mantissa) * magnitude 

...Hilfsfunktion:

func sumBits(arr: [UInt8]) -> UInt64 { 
    var sum : UInt64 = 0 
    for (idx, val) in arr.enumerate() { 
     sum += UInt64(val) << (8 * UInt64(idx)) 
    } 
    return sum 
} 
2

Es ist ein bisschen schwierig zu um manchmal erhalten werden kann, aber hier ist meine einfache Implementierung, hoffen, dass es Ihnen aus

private func parseThermometerReading(withData someData : NSData?) { 
    var pointer = UnsafeMutablePointer<UInt8>(someData!.bytes) 
    let flagsValue = Int(pointer.memory) //First 8 bytes are the flag 

    let temperatureUnitisCelsius = (flagsValue & 0x01) == 0 
    let timeStampPresent   = (flagsValue & 0x02) > 0 
    let temperatureTypePresent = ((flagsValue & 0x04) >> 2) > 0 

    pointer = pointer.successor() //Jump over the flag byte (pointer is 1 bytes, so successor will automatically hot 8 bits), you can also user pointer = pointer.advanceBy(1), which is the same 

    let measurementValue : Float32 = self.parseFloat32(withPointer: pointer) //the parseFloat32 method is where the IEEE float conversion magic happens 
    pointer = pointer.advancedBy(4) //Skip 32 bits (Since pointer holds 1 byte (8 bits), to skip over 32 bits we need to jump 4 bytes (4 * 8 = 32 bits), we are now jumping over the measurement FLOAT 

    var timeStamp : NSDate? 

    if timeStampPresent { 
     //Parse timestamp 
     //ParseDate method is also a simple way to convert the 7 byte timestamp to an NSDate object, see it's implementation for more details 
     timeStamp = self.parseDate(withPointer: pointer) 
     pointer = pointer.advancedBy(7) //Skip over 7 bytes of timestamp 
    } 

    var temperatureType : Int = -1 //Some unknown value 

    if temperatureTypePresent { 
     //Parse measurement Type 
     temperatureType = Int(pointer.memory)) 
    } 
} 

nun zu dem kleinen Methode hilft, die Bytes ein konvertiert IEEE Float

internal func parseFloat32(withPointer aPointer : UnsafeMutablePointer<UInt8>) -> Float32 { 
    // aPointer is 8bits long, we need to convert it to an 32Bit value 
    var rawValue = UnsafeMutablePointer<UInt32>(aPointer).memory //rawValue is now aPointer, but with 32 bits instead of just 8 
    let tempData = Int(CFSwapInt32LittleToHost(rawValue)) //We need to convert from BLE Little endian to match the current host's endianness 

    // The 32 bit value consists of a 8 bit exponent and a 24 bit mantissa 
    var mantissa : Int32 = Int32(tempData & 0x00FFFFFF) //We get the mantissa using bit masking (basically we mask out first 8 bits) 

    //UnsafeBitCast is the trick in swift here, since this is the only way to convert an UInt8 to a signed Int8, this is not needed in the ObjC examples that you'll see online since ObjC supports SInt* types 
    let exponent = unsafeBitCast(UInt8(tempData >> 24), Int8.self) 
    //And we get the exponent by shifting 24 bits, 32-24 = 8 (the exponent) 
    var output : Float32 = 0 

    //Here we do some checks for specific cases of Negative infinity/infinity, Reserved MDER values, etc.. 
    if mantissa >= Int32(FIRST_RESERVED_VALUE.rawValue) && mantissa <= Int32(ReservedFloatValues.MDER_NEGATIVE_INFINITY.rawValue) { 
     output = Float32(RESERVED_FLOAT_VALUES[mantissa - Int32(FIRST_S_RESERVED_VALUE.rawValue)]) 
    }else{ 
     //This is not a special reserved value, do the normal mathematical calculation to get the float value using mantissa and exponent. 
     if mantissa >= 0x800000 { 
      mantissa = -((0xFFFFFF + 1) - mantissa) 
     } 
     let magnitude = pow(10.0, Double(exponent)) 
     output = Float32(mantissa) * Float32(magnitude) 
    } 
    return output 
} 

Und hier ist, wie das Datum in ein NSDate Objekt analysiert wird

internal func parseDate(withPointer aPointer : UnsafeMutablePointer<UInt8>) -> NSDate { 

    var bytePointer = aPointer //The given Unsigned Int8 pointer 
    var wordPointer = UnsafeMutablePointer<UInt16>(bytePointer) //We also hold a UInt16 pointer for the year, this is optional really, just easier to read 
    var year  = Int(CFSwapInt16LittleToHost(wordPointer.memory)) //This gives us the year 
    bytePointer  = bytePointer.advancedBy(2) //Skip 2 bytes (year) 
    //bytePointer  = wordPointer.successor() //Or you can do this using the word Pointer instead (successor will make it jump 2 bytes) 

    //The rest here is self explanatory 
    var month  = Int(bytePointer.memory) 
    bytePointer  = bytePointer.successor() 
    var day   = Int(bytePointer.memory) 
    bytePointer  = bytePointer.successor() 
    var hours  = Int(bytePointer.memory) 
    bytePointer  = bytePointer.successor() 
    var minutes  = Int(bytePointer.memory) 
    bytePointer  = bytePointer.successor() 
    var seconds  = Int(bytePointer.memory) 

    //Timestamp components parsed, create NSDate object 
    var calendar   = NSCalendar.currentCalendar() 
    var dateComponents  = calendar.components([.Year, .Month, .Day, .Hour, .Minute, .Second], fromDate: NSDate()) 
    dateComponents.year  = year 
    dateComponents.month = month 
    dateComponents.day  = day 
    dateComponents.hour  = hours 
    dateComponents.minute = minutes 
    dateComponents.second = seconds 

    return calendar.dateFromComponents(dateComponents)! 
} 

Das ist so ziemlich alle Tricks, die Sie auch für jede andere BLE Charakteristik benötigen, die ein FLOAT Typen verwendet