Search code examples
cunions

Changing endianness or byte order within a union


I am working on a project involving communications. I would like to be able to take a struct and typecast it into bytes in preparation for transmission. Unfortunately, endianness is not working out for me.

The standard that I'm working with has the following definition:

uint8_t address;
uint8_t function;
uint8_t startAddressHi;
uint8_t startAddressLo;
uint8_t numberOfRegistersHi;
uint8_t numberOfRegistersLo;

It would be nice to be able to type cast to uint8_t and simply transmit the entire struct in one go, but the problem is that the Hi bytes of the startAddress and numberOfRegisters is at a lower index. I am hoping that there is some little-known method of changing the byte order of the definition:

typedef union{

    struct rturxfields{
        /* header */
        uint8_t address;
        uint8_t function;

        /* data */
        uint16_t startAddress;   // <= needs some special sauce
        uint16_t numberOfRegisters;
    }RtuRxFields;

    struct rturxfieldbytes{
        /* header */
        uint8_t address;
        uint8_t function;

        /* data */
        uint8_t startAddressHi;
        uint8_t startAddressLo;
        uint8_t numberOfRegistersHi;
        uint8_t numberOfRegistersLo;
    }RtuRxFieldBytes;

    uint8_t array[RX_FRAME_LENGTH];
}RtuRxFrame;

Again, as defined, my union, the endianness is off.

I won't have access to htons() and similar from the networking library. After doing more reading, I think I will simply get rid of the RtuRxFields struct and create utility functions getStartAddress(RtuRxFrame* frame) to retrieve the address cleanly.


Solution

  • One thing you can do is to use htons and ntohs to convert the 16 bit values between host byte order (machine dependent) and network byte order (big endian). When writing the 16 bit values, pass them through htons to put the bytes in the right order.

    myunion.RtuRxFields.numberOfRegisters = htons(5);
    

    If you don't have access to these functions, you can use bit shifting to get the high and low bytes and write them individually in the relevant fields:

    uint16_t registers = 5;
    ...
    myunion.RtuRxFieldBytes.numberOfRegistersHi = (registers >> 8) & 0xff;
    myunion.RtuRxFieldBytes.numberOfRegistersLo = registers & 0xff;
    

    Sending a struct over a network can be problematic due to structure padding. Two different compilers on two different machines could make the same struct have different sizes on each. See this guide for more details.