2

I'm currently in the process of re-writing a program to process data received over a serial connection using the RDM protocol, each packet is received by a UART and has a specific structure but may vary in length, a packet structure example is below, assuming the number of bytes in the packet to be n (this may change depending on the contents of the packet)

What I want to do is define a struct in my C code that has the various parameters defined, but to be able to read and write bytes to/from the struct from the UART as though the struct is just an array of uint8_t. My issue with this is that I have read that structs may not always be stored in continuous sections of memory, so taking &RDMPacket1 and increment through the struct may end up with the data not being in the right place.

My other problem is that if I have an array to store a packet data of the maximum possible length (220 bytes) inside the struct, then the checksum at the end of the packet would be written into the wrong place. What methods could be used to receive the data and place it into the struct?

Example packet definition (shortened from standard)

Byte     | Description
0        | START Code - Constant, can be ignored
1        | Sub-Start Code - Contains command for device to process
2        | Message Length - Points to byte number of Checksum High (up to 255)
3-8      | Destination UID - Unique ID of packet Destination
9-14     | Source UID - Unique ID of packet Source
15       | Transaction Number - ID of transaction between controller and responder
16-(n-2) | Data (up to 220 bytes long)
n-1      | Checksum High
n        | Checksum Low

This is an example of a struct to hold the a packet of maximum possible length:

struct RDMPacket
{
    uint8_t     subStartCode;
    uint8_t     messageLength;
    uint32_t    destinationUID;
    uint32_t    sourceUID;
    uint8_t     transactionNumber;
    uint8_t     portID;
    uint8_t     messageCount;
    uint8_t     subDevice;
    uint8_t     commandClass
    uint8_t     parameterID;
    uint8_t     parameterDataLength;
    uint8_t     parameterData[220];
    uint16_t    checksum
} RDMPacket1;
3
  • 2
    I don't think uint32_t has 6 bytes... and honestly, maybe it'll be a better idea not to use struct for this, you'll have to deal with things like padding, alignment etc.. not worth the trouble imo Commented Jan 21, 2015 at 15:01
  • The checksum appears at a variable position; that at least requires special attention. There's likely to be padding in the structure after messageLength and before destinationUID, too. That means the structure cannot accurately represent the byte stream on the wire. You're better off not trying to serialize/deserialize the structure directly (it won't work accurately). Use code to pack/unpack the relevant data into/out of an array of bytes. Commented Jan 21, 2015 at 16:12
  • @JonathanLeffler That is true, I think I'll take this approach to it. Shouldn't be too hard to have it packed into the struct as it arrives. Thanks! Commented Jan 22, 2015 at 10:37

1 Answer 1

4

The problem that you are describing can arise when you are dealing with a non-byte aligned memory structure. In this case each struct field will have the specific alignment. I.e., if the alignment is 4 bytes, each field will start on an address that is divisible by 4. To avoid this, you can use GCC's attribute packed for the structure, that instructs the compiler to pack the structure to a minimal memory. In other compilers there is #pragma pack or some other corresponding compiler directives for this purpose. To make sure that your struct is packed, you can check it's size with sizeof and compare it to the expected size.

Sign up to request clarification or add additional context in comments.

2 Comments

Agreed. The answer might be to pre-pad his structure with 2 bytes to tip the structure into alignment.
In the sample structure, the only place where padding is likely to be needed is after messageLength. If uint32_t members must be aligned on a 4-byte boundary (a common requirement), then there will be 2 bytes of padding between messageLength and destinationUID. Each member of the structure has to be placed such that it will be aligned correctly for its type, and the structure as a whole has to be allocated such that its member with the most stringent alignment requirement is always properly aligned. But there'll be no padding between consecutive uint8_t members, for example.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.