Extract 4 Bits of Bluetooth Hex Data

Extract 4 bits of Bluetooth HEX Data

How Can I convert:

From - [HEX] 00 00 40 02
To - [DEC] 16386
To - [BIN] 0100 0000
0000 0010

You can simply use ContiguousBytes withUnsafeBytes method to load your bytes as UInt32. Note that it will use only the same amount of bytes needed to create the resulting type (4 bytes)

let byteArray: [UInt8] = [2, 64, 0, 0, 8, 32, 0, 0]
let decimal = byteArray.withUnsafeBytes { $0.load(as: UInt32.self) }
decimal // 16386

To convert from bytes to binary you just need to pad to left your resulting binary string. Note that your expected binary string has only 2 bytes when a 32-bit unsigned integer should have 4:

extension FixedWidthInteger {
var binary: String {
(0 ..< Self.bitWidth / 8).map {
let byte = UInt8(truncatingIfNeeded: self >> ($0 * 8))
let string = String(byte, radix: 2)
return String(repeating: "0",
count: 8 - string.count) + string
}.reversed().joined(separator: " ")
}
}

let binary = decimal.binary // "00000000 00000000 01000000 00000010"

To know if a specific bit is on or off you can do as follow:

extension UnsignedInteger {
func bit<B: BinaryInteger>(at pos: B) -> Bool {
precondition(0..<B(bitWidth) ~= pos, "invalid bit position")
return (self & 1 << pos) > 0
}
}


decimal.bit(at: 0)  // false
decimal.bit(at: 1) // true
decimal.bit(at: 2) // false
decimal.bit(at: 3) // false
decimal.bit(at: 14) // true

If you need to get a value at a specific bytes position you can check this post

Extract 4 bits vs 2Bit of Bluetooth HEX Data, why same method results in error

There is a problem with this code that you say works... but it seems to work "accidentally":

let flags = byteArray[1]<<8 + byteArray[0]

This results in a UInt8, but the flags field in the first table is 16 bits. Note that byteArray[1] << 8 always evaluates to 0, because you are shifting all of the bits of the byte out of the byte. It appeared to work because the only bit you were interested in was in byteArray[0].

So you need it convert it to 16-bit (or larger) first and then shift it:

let flags = (UInt16(byteArray[1]) << 8) + UInt16(byteArray[0])

Now flags is UInt16

Similarly when you do 4 bytes, you need them to be 32-bit values, before you shift. So

let flags = UInt32(byteArray[3]) << 24 
+ UInt32(byteArray[2]) << 16
+ UInt32(byteArray[1]) << 8
+ UInt32(byteArray[0])

but since that's just reading a 32-bit value from a sequence of bytes that are in little endian byte order, and all current Apple devices (and the vast majority of all other modern computers) are little endian machines, here is an easier way:

let flags = byteArray.withUnsafeBytes {
$0.bindMemory(to: UInt32.self)[0]
}

In summary, in both cases, you had been only preserving byte 0 in your shift-add, because the other shifts all evaluated to 0 due to shifting the bits completely out of the byte. It just so happened that in the first case byte[0] contained the information you needed. In general, it's necessary to first promote the value to the size you need for the result, and then shift it.

Extract a 10-bit integer from an ArrayBuffer in JavaScript

If I read that chart right the data is byte[1]'s lower 4 bits combined with byte[0]'s higher 6 bits;

// (buffer[1] - 0) & 0x0f) // get the 4 bits from byte 1
// (((buffer[0] - 0) & 0xfc) << 2) // get the 6 bits from byte 0 and shift over twice
result = ((buffer[1] - 0) & 0x0f) + ( ((buffer[0] - 0) & 0xfc) << 2 )

Do you have a sample of what the byte buffer look likes?

Well, to better understand the 0x stuff, we need to look at the diagram in a bit more detail.

The first 4 bits are on byte 1 and specifically bits 0-3. Bits 0-3 are 1 + 2 + 4 + 8 = 15 or 0x0f. I could have said (buffer[1] - 0) & 15), but using the hex notation 0x0f looks cleaner and kinda mirrors the data.

And for the data in byte 0, its from bits 2-7, which added together equal 252 or 0xfc. Now the issue with this data is it starts on bit 2 and the data from byte 1 ends on bit 3. We need to shift the bits, so they start on bit 4 and don't have any overlap. With the bits shifted, we can add the two numbers together and get the combined number.

And if you need to know, I subtract 0 all over the place, because that forces JavaScript to treat the operations as numbers and not try to perform string math.

How to extract temperature decimal value from a Bluetooth LE (SIG) hex value

"My problem is that it's in hexadecimal format".
I'm sure that you receive a byte-array, you just read or print it in hexadecimal format.

To get something useful, you have to get the relevant data from the original byte array.

06-68-01-00-FF-E2-07-03-0A-15-34-00-02 is [6,104,1,0,255,226,7,3,10,21,52,0,2]

The first byte (byte[0] = 6 is a bit-flag: 00000110.

bit 0 is the most right = 0 , meaning Temperature Measurement Value in units of Celsius. (if 1 Fahrenheit).
bit 1 = 1, meaning Time Stamp field present, if 0 Time Stamp field not present.
bit 2 = 1, meaning Temperature Type field present, if 0 Temperature Type field not present.

All other bits are not relevant, they are reserved for future use.

Bites[1] (104) and [2] (1) is the temperature as FLOAT in Celsius * 10. To get the temp multiply byte[2] by 256 and add byte[1], divide total by 10.
result: 1 * 256 + 104 = 360. Temperature is 36.0

Bites3 and 4 have no meaning here. normally they are part of the 4 byte temperature float.

Bites [5] (226) and [6] (7) is the Year as INT16: 7 * 256 + 226 = 2018.

Byte[7] (3) is the month. 0 meaning unknown, 1 = January , here 3 = March.

Byte[8] (10) is the day of the month, here 10.

Byte[10] (21) is the Hour(Number of hours past midnight), here 21.

Byte[11] (52) is the Minute, here 52.

Byte[12] (0) is the Second, here 0.

Byte[13] (2) is the Temperature Type, here Body (general).

1 Armpit

2 Body (general)

3 Ear (usually ear lobe)

4 Finger

5 Gastro-intestinal Tract

6 Mouth

7 Rectum

8 Toe

9 Tympanum (ear drum)

10 - 255 Reserved for future use

0 Reserved for future use

How to break down hex data into usable data from BLE Device? (Speed and Cadence)

First, don't think about this as "hex data." This is just a sequence of bytes. It happens to be displayed in hex, just because that's often useful. But the data coming from the device is not "in hex." It's just a bunch of bytes, and you need to decode those bytes as the spec indicates. The best way to decode bytes, IMO, is by consuming them as you go along. Subscripting Data is dangerous because the first index is not promised to be 0. I use the following to do that:

extension Data {
// Based on Martin R's work: https://stackoverflow.com/a/38024025/97337
mutating func consume<T>(type: T.Type) -> T? where T: ExpressibleByIntegerLiteral {
let valueSize = MemoryLayout<T>.size
guard count >= valueSize else { return nil }
var value: T = 0
_ = Swift.withUnsafeMutableBytes(of: &value, { copyBytes(to: $0)} )
removeFirst(valueSize)
return value
}
}

Here's the main decoder, which creates a CSCData struct (this could be a bit nicer with throws, but it add complexity to the example):

struct CSCData {
var wheelRevolutions: RevolutionData?
var crankRevolutions: RevolutionData?

init?(data: Data) {

var data = data // Make mutable so we can consume it

// First pull off the flags
guard let flags = Flags(consuming: &data) else { return nil }

// If wheel revolution is present, decode it
if flags.contains(.wheelRevolutionPresent) {
guard let value = RevolutionData(consuming: &data, countType: UInt32.self) else {
return nil
}
self.wheelRevolutions = value
}

// If crank revolution is present, decode it
if flags.contains(.wheelRevolutionPresent) {
guard let value = RevolutionData(consuming: &data, countType: UInt16.self) else {
return nil
}
self.crankRevolutions = value
}

// You may or may not want this. Left-over data suggests that there was an error
if !data.isEmpty {
return nil
}
}
}

Flags are an OptionSet and are decoded this way:

struct Flags : OptionSet {
let rawValue: UInt8

static let wheelRevolutionPresent = Flags(rawValue: 1 << 0)
static let crankRevolutionPresent = Flags(rawValue: 1 << 1)
}

extension Flags {
init?(consuming data: inout Data) {
guard let byte = data.consume(type: UInt8.self) else { return nil }
self.init(rawValue: byte)
}
}

And RevolutionData is decoded this way. Note the use of .littleEndian; it's good to be precise when decoding even if you think you'll never run on a big endian platform:

struct RevolutionData {
var revolutions: Int
var eventTime: TimeInterval

init?<RevolutionCount>(consuming data: inout Data, countType: RevolutionCount.Type)
where RevolutionCount: FixedWidthInteger
{
guard let count = data.consume(type: RevolutionCount.self)?.littleEndian,
let time = data.consume(type: UInt16.self)?.littleEndian
else {
return nil
}

self.revolutions = Int(clamping: count)
self.eventTime = TimeInterval(time) / 1024.0 // Unit is 1/1024 second
}
}

Note the use of Int(clamping:). This isn't stricly needed for your specific use, but it's legal to call this code with a UInt32 (or larger) on a 32-bit platform. That could overflow and crash. Deciding what to do in that case is an important choice, but a init(clamping:) is a good default if bad data would not be catastrophic and you don't want to crash. This isn't needed for TimeInterval, since it's certain to be larger than UInt16.

The deeper point about this is that when decoding data you get from Bluetooth, you should always be very defensive. You may have misunderstood the spec, or the device may have bugs. They can send you unexpected data, and you should be able to recover from that.

And testing this:

let data = Data([0x03,0x07,0x00,0x00,0x00,0x44,0x2c,0x05,0x00,0xaf,0x25])
let result = CSCData(data: data)!
// CSCData(wheelRevolutions: Optional(RevolutionData(revolutions: 7, eventTime: 11.06640625)),
// crankRevolutions: Optional(RevolutionData(revolutions: 5, eventTime: 9.4208984375)))

How to extract the LSB from a byte in C and Python?

Use a bit operation:

C code

char b = 0x01;

if( b & 0x01 ) {
// LSB is set
}
else {
// LSB is not set
}

Python code

b = 0x01
if (b&0x01)==0x01 :
# LSB is set
else:
# LSB is not set

LSB = Least Significant Bit (in your case)



Related Topics



Leave a reply



Submit