So I'm currently designing a simple transmission protocol for a ring network implemented in UART.
To transmit, I am converting the data from a struct to a char stream, started by < and terminated by > - I know of the possibility of any 0x3c or 0x3e value being mistaken for a < or > respectively, i am working on a solution to escape that. That's not part of my question.
So, the structure of it is something like <UINT32UINT32UINT8UINT8UINT16char[0...n]>
, those types representing: <destinationId senderId TimeToLive Cmdtype CmdId Payloadlength Payload>
. This always stays the same, so I can assume it without any delimiters between the values. This works, and I can theoretically also decode this. To easily access the bytes, I implemented the struct with unions:
typedef struct{
union{
uint32_t val;
char bytes[sizeof(uint32_t)];
} recipientId;
union{
uint32_t val;
char bytes[sizeof(uint32_t)];
} senderId;
union{
uint8_t val;
char bytes[sizeof(uint8_t)];
} timeToLive;
union{
uint8_t val;
char bytes[sizeof(uint8_t)];
} cmdType;
union{
uint8_t val;
char bytes[sizeof(uint8_t)];
} cmdId;
union{
uint16_t val;
char bytes[sizeof(uint16_t)];
} payloadLength;
char *payload;
char *commandRaw;
} aurPacket_t;
Once a packet exists, I decode it with something akin to this:
void decode_command(aurPacket_t packet){
if((packet.commandRaw[0] != '<' ) || (packet.commandRaw[strlen(packet.commandRaw) - 1] != '>') ){
printf("\n\nINVALID COMMAND\n\n");
}
else{
aurPacket_t newpacket;
// EITHER:
// for (int i = 0; i < strlen(newpacket.recipientId.bytes); i++){
// newpacket.recipientId.bytes[i] = (char)*(packet.commandRaw + 1 + i);
// }
// OR:
strncpy(newpacket.recipientId.bytes, (packet.commandRaw + 1), sizeof(newpacket.recipientId.bytes));
}
}
commandRaw contains the char stream that would be received in a message.
Using something like this I'd be able to do it, but I'd need to iterate it one by one since not all values are the same datatype - copying the string out to my payload in the end. Is there a way to make this more elegant than iterating through every single variable, to somehow iterate by using my struct as a guide for the iterator?
I was made aware of memcpy, but since I want to keep the protocol platform-independent, I'd prefer not to use it unless there is an elegant way to. Or is there a way to use it elegantly? Is my method of adding the variables even different from just using memcpy? Thinking about it, it doesn't seem like it would be, considering the ordering of the vars inside my struct. If I made a string containing <
, memcpy-appended everything up to the payload, then memcpy-appended the payload, then appended a >
, would there be any actual difference in the data? If not, I could just use that process in reverse to decode a message.
I'm currently encoding the message by using this function:
#define RAW_PACKET_LEN 1024
void parse_command(aurPacket_t packet){
snprintf(packet.commandRaw, RAW_PACKET_LEN, "<%.4s%.4s%.1s%.1s%.1s%.02s%.*s>",
packet.recipientId.bytes,
packet.senderId.bytes,
packet.timeToLive.bytes,
packet.cmdType.bytes,
packet.cmdId.bytes,
packet.payloadLength.bytes,
packet.payloadLength.val, packet.payload
);
} // memory for commandRaw allocated outside of function
which has the problem of not actually writing 0x00-bytes to the stream, but that's not part of the question - I'm looking for an answer to that at the moment (of course, if you know an easy fix, feel free to let me know :) )
The system is an ESP32 programmed using ESP-IDF.