2

I am currently trying to find the simplest way to convert a struct which contains a char * to a char array for transmitting a serial data frame.

The struct stores the frame headers, checksum, msg size etc along with the data to be transmit. eg

struct {
    unsigned char header;
    unsigned char msgSizeL;
    unsigned char msgSizeH;
    unsigned char *data;
    unsigned char checksum;
    ....
    ....
    ....
    ....
    unsinged char endFrame;
} Frame;

The data is a pointer as the amount of data can change with each frame transmit in the range from 1 to 16 bytes.

I am trying to find the simplest way to convert the struct to an array of unsigned chars without copying out each entry in the struct to the array but don't seem to be able to find a way.

Alternately I don't have to use a struct, as i am generating the frame contents excluding the data contents from scratch, I just want to avoid a case of -

unsigned char frame[1000];
frame[0] = 0x01; // header
frame[1] = msgSizeL; // msg size low byte
frame[2] = msgSizeH; // msg size high byte
frame[3] = data[0]; // data contents 0    Over simplification
frame[4] = data[1]; // data contents 1    for data copying - 
frame[5] = data[2]; // data contents 2    data length is variable.
frame[6] = data[3]; // data contents 3
frame[7] = data[4]; // data contents 4
frame[8] = data[5]; // data contents 5
frame[9] = data[6]; // data contents 6
frame[10] = data[7]; // data contents 7
frame[3 + msgSize]; = getChecksum(data);
....
....
....
....
frame [3 + msgSize + 20] = 0xFF; // end frame

where i write data to what reads as a random array index rather than something meaningful like frame.checksum = getChecksum(data);.

Open to suggestions and a little bit of flaming for asking a general question rather than a specific one :-)

Shaun Davy
  • 21
  • 2
  • will your data frame length be dynamic? for example if your data contains 12 bytes, will your frame length n + 12? or will you send all 16 bytes and your frame length will be n + 16 for every case? – Taha Paksu Nov 15 '17 at 06:26
  • is the message format enforced on you? can you amend it? If you switch the checksum and message payload order your task should be easier. – luantkow Nov 15 '17 at 06:35
  • The problem is portability. All you are guaranteed with a `struct` is that the address of the `struct` will be the same as that of its first member. After that compiler handling of padding can cause problems. So copying address past the first to your array becomes problematic. `offsetof` in `stddef.h` can help with the member addresses, but a fixed array sure sounds easier. – David C. Rankin Nov 15 '17 at 06:37
  • You may also want to look at a serialization library like [tpl](https://github.com/troydhanson/tpl), [binn](https://github.com/liteserver/binn), [protobuf-c](https://github.com/protobuf-c/protobuf-c) – David C. Rankin Nov 15 '17 at 07:16
  • Taha Paksu, yes frame length will be dynamic with the length of the data portion. So the frame size is a constant overhead with variable data length. – Shaun Davy Nov 15 '17 at 08:23
  • user3188346, yes the message format is enforced. How I build the message is essentially completely up to me. Yes data portion at the end of the struct would be much easier. – Shaun Davy Nov 15 '17 at 08:26
  • David C. Rankin unfortunately my data portion of the frame is not fixed, so a fixed array would then have to be special cased when the array isnt full. I will checkout the serialization libraries you suggested. I have two structs like this so felt using a serializer might be a bit overkill. Appreciate the suggestions. – Shaun Davy Nov 15 '17 at 08:30

2 Answers2

0

I am trying to find the simplest way to convert the struct to an array of unsigned chars without copying out each entry in the struct to the array but don't seem to be able to find a way.

If data would contain up to 16 bytes, I would just declare it as 16-char array, like so:

#define MAX_DATA_LEN 16
struct {
    unsigned char header;
    unsigned char msgSizeL;
    unsigned char msgSizeH;
    unsigned char data[MAX_DATA_LEN]; // 16-char array instead of pointer
    unsigned char checksum;
    ....
    ....
    ....
    ....
    unsinged char endFrame;
} Frame;

If this is done, then all your data inside this struct has a pre-defined size (i.e. number of unsigned char * sizeof(unsigned char), and so on with other data types...) and you can use simple memcpy to copy the struct to an array:

size_t struct_len = /* computing the length of the data */ 100;
memcpy(frame, struct Frame, struct_len);
CIsForCookies
  • 12,097
  • 11
  • 59
  • 124
  • Unfortunately only the actual number of bytes of data must be sent. So the data length will be 16 most of the time sometimes it could be 1 byte, and 1 byte only must be sent, in addition to the constant frame overhead. – Shaun Davy Nov 15 '17 at 09:27
  • Ok, but somewhere you'll know how much bytes are sent - so if the number < 16 pad the rest with 0 – CIsForCookies Nov 15 '17 at 09:54
  • I can't pad with 0s. I must send only the number of bytes, without padding. – Shaun Davy Nov 15 '17 at 10:25
0

You have two options:

1. dynamic frame length

struct {
    unsigned char header;
    unsigned char msgSizeL; // this will be dynamic from 0x06 (empty) to 0x16 (full)
    unsigned char msgSizeH; // this will be always 0x00
    unsigned char dataSize; // specify the data size here
    unsigned char data[16]; // use a static memory area for the package
    unsigned char checksum;
    unsigned char endFrame;
} Frame;

then you can do this:

Frame xFrame;
unsigned char ucaData[17] = {0}; // your data 
unsigned int uiDataLength = 6; // for example
memset(&xFrame, 0, sizeof(Frame)); // just to be sure
xFrame.header = 0x01;
xFrame.msgSizeL = uiDataLength;
xFrame.msgSizeH = 0;
xFrame.dataSize = uiDataLength;
memcpy(xFrame.data, ucaData, uiDataLength);
xFrame.checksum = CRC8(&xFrame, uiDataLength + 4); // crc for filled area
xFrame.endFrame = 0xFF;

then when building the package:

unsigned char xDataFrame[1000];
memcpy(xDataFrame, xFrame, 4 + uiDataLength);
memcpy(xDataFrame + 4 + uiDataLength, &xFrame + 20, 2);
transmit(xDataFrame, 6 + uiDataLength);

This way, you will discard empty bytes from sending and from calculating checksum for.

2. static frame length

Then you can use a struct like this:

struct {
    unsigned char header;
    unsigned char msgSizeL; // this will be always 0x16 (22)
    unsigned char msgSizeH; // this will be always 0x00
    unsigned char dataSize; // specify the data size here
    unsigned char data[16]; // use a static memory area for the package
    unsigned char checksum;
    unsigned char endFrame;
} Frame;

This way you can calculate the checksum for the first 20 bytes of this structure, but be aware of that this structure may contain padding areas which may move your data around the defined and padded memory area. sizeof(Frame) may be calculated by hand as 22 bytes but the compiler may decide that it should use 32 bytes or 24 bytes of memory related with the pack setting. You may use

#pragma pack(1) // disable padding
[structure code]
#pragma pack()  // back to the original setting

More info about packing:

#pragma pack effect

Taha Paksu
  • 15,371
  • 2
  • 44
  • 78
  • The dynamic frame length suggestion would work. This keeps the struct and doesn't make the struct to array conversion too messy. The static frame length is out as i must transmit only the number of bytes available and not pad the size out with trailing 0s. Appreciate your response! – Shaun Davy Nov 15 '17 at 09:32