Reputation: 693
I want to assemble a char array that contains one byte with a compile time constant value and a string, which is also compile time constant. Solution would be:
char packet[] = "\x42" __DATE__;
That works but is not very readable and maintainable, as that 0x42 is a message opcode that is used elsewhere, making this a magic number. Now, I could put a dummy x into the string and follow this definition with an assignment like this:
#define OPCODE 0x42
char packet[] = "x" __DATE__;
packet[0] = OPCODE;
But I have the feeling that could be done in a purely constant string literal, I just can't find how to do it. Any idea?
Upvotes: 3
Views: 577
Reputation: 37847
As the C preprocessor cannot help here, this might be a case for an additional preprocessor to be added to the build system.
Whether you choose a second stage of C preprocessor, m4, a shell or python script or whatever else is up to you.
Or you go another route and change the packet type from char array to a struct
with a flexible array member, something like the following:
/* foo.c
* Compile with something like
* avr-gcc -mmcu=atmega328 -Os -Wall -Wextra -Werror \
* -save-temps=obj -Wa,-adhlns=foo.lst,-gstabs -c foo.c
*/
#include <stddef.h>
#include <stdint.h>
#include <avr/pgmspace.h>
#define OPCODE_DATE 0x42
struct packet_descr {
uint8_t opcode;
uint16_t size;
};
struct string_packet {
struct packet_descr descr;
char string[];
};
#define STRING_PACKET_P(IDENTIFIER, OPCODE, STRING) \
const struct string_packet IDENTIFIER PROGMEM = { \
{ (OPCODE), \
sizeof(STRING)-1 \
}, \
(STRING) \
}
STRING_PACKET_P(packet_date_P, OPCODE_DATE, __DATE__);
void uart_send_char(const char ch);
void uart_send_char(const char ch)
{
UDR0 = ch;
}
/* Send string packet in the following format:
* uint8_t opcode;
* uint16_t len; // in AVR endianness
* char string[len]; // string of "len" characters, unterminated
*/
extern
void string_packet_send_P(const struct string_packet *string_packet_P);
void string_packet_send_P(const struct string_packet *string_packet_P)
{
const uint8_t opcode = pgm_read_byte(&string_packet_P->descr.opcode);
uart_send_char(opcode);
size_t len = pgm_read_word(&string_packet_P->descr.size);
for (PGM_P byte_P = (PGM_P)&string_packet_P->string; len > 0; len--, byte_P++) {
uart_send_char(pgm_read_byte(byte_P));
}
}
int main(void)
{
string_packet_send_P(&packet_date_P);
return 0;
}
One obvious disadvantage of a struct
with a flexible array member is that sizeof(packet)
will only yield the size of the non-array part. However, depending on your actual packet format (the receiver of the packet also needs to know when a packet starts and finishes, right?), recording the size separately might be feasible.
Upvotes: 1
Reputation: 141613
__DATE__
should mostly have exactly Mmm dd yyyy
format, so 11 characters. You can do this:
char packet[] = {
OPCODE,
__DATE__[0],
__DATE__[1],
__DATE__[2],
__DATE__[3],
__DATE__[4],
__DATE__[5],
__DATE__[6],
__DATE__[7],
__DATE__[8],
__DATE__[9],
__DATE__[10],
__DATE__[11],
0,
};
Upvotes: 4