Search code examples
objective-cstructhexpacketbytestream

Unpack hex-encoded NSData


on peripheral:didReceiveWriteRequest a CBATTRequest returns hex-encoded NSData via request.value.

This is what I have tried

// Define struct
typedef struct __attribute__((packed)) {
    UInt8  pktNo;
    UInt8  ctrlCmd;
    UInt8  txPowerRequest;
    UInt16 uuid;
    UInt16 userPayload; // how to store 15 octets?
} Packet;

   // Unpack 
   Packet *packet = (Packet *)request.value.bytes;
    if (packet) {
        UInt8  pktNo   = packet->pktNo;
        UInt8  cmd     = packet->ctrlCmd;
        UInt8  tx      = packet->txPowerRequest;
        UInt16 uuid    = packet->uuid;
        UInt16 payload = packet->userPayload;
        NSLog(@"pktNo: %hhu, cmd: %hhu, tx: %hhu, uuid: %hu, payload: %hu", pktNo, cmd, tx, uuid, payload);
    }

Console

pktNo: 121, cmd: 202, tx: 130, uuid: 48321, payload: 21421

First, these numbers look inaccurate, and I'm uncertain about what format this is even in, since the following analogous values I got from debugging tool don't seem to match.

Default: raw strings?

packet          Packet *    0x281af0cc0 0x0000000281af0cc0
pktNo           UInt8       'y'
ctrlCmd         UInt8       '\xca'
txPowerRequest  UInt8       '\x82'
uuid            UInt16      48321
userPayload     UInt16      21421

Solution

  • The hex string representation of your NSData is apparently:

    <79ca82c1 bcad530e 016a1435 127993ee 01ef7579>
    

    That translates to:

    • The 0x79 is the PKT, 121 in decimal
    • The 0xca is the CMD, 202 in decimal
    • The 0x82 is the TXP, 130 in decimal
    • The 0xbcc1 is the UUID, 48321 in decimal
    • The PAYLOAD is ad 53 0e 01 6a 14 35 12 79 93 ee 01 ef 75 79

    So, you can use:

    typedef struct __attribute__((packed)) {
        UInt8  pktNo;
        UInt8  ctrlCmd;
        UInt8  txPowerRequest;
        UInt16 uuid;
        UInt8  userPayload[15];
    } Packet;
    

    Which you can populate with:

    Packet packet = {};
    assert(data.length == sizeof(Packet));
    memcpy(&packet, data.bytes, sizeof(Packet));
    

    Note, I didn’t just set the packet to a pointer to the bytes of the NSData, because if the NSData is deallocated, you don’t want to use a pointer to that deallocated memory. Instead, copy the bytes to your packet struct (perhaps checking to make sure the two match in size).


    In the interest of full disclosure, the above makes a somewhat cavalier assumption that endianness of the payload matches that of device running your app. You theoretically might want to make the UUID a UInt8 uuid[2], instead, and then if you need the UUID value, recalculate it from those two octets.