1

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.

hat
  • 781
  • 2
  • 14
  • 25
slightlynybbled
  • 2,408
  • 2
  • 20
  • 38
  • This is a surprisingly hard problem. See [the related question of mine](https://stackoverflow.com/questions/30945121/dealing-with-data-serialization-without-violating-the-strict-aliasing-rule) on the topic – Eugene Sh. Jun 28 '17 at 19:58

1 Answers1

2

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.

dbush
  • 205,898
  • 23
  • 218
  • 273
  • I should have added this to my post. I'm writing this for a microcontroller and it does not have access to the network library. I will add this information to my initial post and read through your reference. Thank you! – slightlynybbled Jun 28 '17 at 20:13
  • @slightlynybbled See my edit. You can use bit operations to extract the relevant bytes to write to the correct place. – dbush Jun 28 '17 at 20:18
  • is this something that can be done at compile time? Your example appears to occur at run time. – slightlynybbled Jun 28 '17 at 20:20
  • @slightlynybbled Unless you plan on setting constant data, it has to be done at run time. Bitwise operations are pretty cheap, so I would think using them wouldn't be a concern. – dbush Jun 28 '17 at 20:24
  • You are right, not a concern. I was just hoping for something more elegant, thank you for your time! – slightlynybbled Jun 28 '17 at 20:27