concatenating string in program memory

Go To Last Post
27 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi

I'm trying to combine a #defined value and a string into a program memory array

 

this what I have currently

 

#define        RESP_OK        1

const char PROGMEM SIM_MSG0[] = {RESP_OK "OK"};

the compiler says "expected '}' before string constant"

 

const char PROGMEM SIM_MSG0[] = {RESP_OK, "OK"};

fails with the message

"initializer element is not computable at load time"

 

const char PROGMEM SIM_MSG0[] = {RESP_OK, 'O','K','\0'};

This works but is a bit of a faff with the amount of strings I have to enter as arrays

 

using Stringify (x) compiles but the value is inserted as a string, not the binary value, when the defined value exceeds 9 I get a two digit value.

 

So what I'm trying to end up with is a string like 0x01, 0x4f,0x4b,0

 

does anyone of a way to do this?

Regards

Rob

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

robcarter wrote:

#define        RESP_OK        1
const char PROGMEM SIM_MSG0[] = {RESP_OK "OK"};

is equivalent to writiing

const char PROGMEM SIM_MSG0[] = {1 "OK"};

which is obviously wrong

 

What you need is:

const char PROGMEM SIM_MSG0[] = { "\x1" "OK" };

or similar.

 

Perhaps you can build that with the preprocessor token-pasting and stringify stuff?

 

But it might be simpler to simply set up a script or utility to generate the source file for you ... ?

 

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thanks for the reply, the reason I wanted to keep the

#define        RESP_OK        1

is that the value is used in a switch comparison in a part of the program which steps through the arrays, if I make it

#define        RESP_OK        "0x1"

The switch compile will fail because the comparison constant is now a string.

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

You cannot have mixed type arrays. "OK" is type char. What type is generated by #define RESP_OK 1 ?  Maybe some kind of int? Try casting it as a char in the array initialization?

 

Jim

Jim Wagner Oregon Research Electronics, Consulting Div. Tangent, OR, USA http://www.orelectronics.net

Last Edited: Fri. Aug 10, 2018 - 05:53 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

As Andy said it needs to be "\X01"

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

That looks like a hayes response code "1" so more likely an ascii 1 or 49 decimal or 0x31 hex.

 

Jim

 

 

Click Link: Get Free Stock: Retire early!

share.robinhood.com/jamesc3274

 

 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

With gcc, I believe you can initialize the flexible array member of a global or static struct.

"SCSI is NOT magic. There are *fundamental technical
reasons* why it is necessary to sacrifice a young
goat to your SCSI chain now and then." -- John Woods

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Yes - I thought as much.

 

That's why I said you'd need to build yourself a string in the "\x1" form - using nasty preprocessor tricks - and then put that into your array.

 

But such tricks get nasty - hence the suggestion of scripting it instead.

 

Or, as skeeve said, try flexible structures ...

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
Last Edited: Fri. Aug 10, 2018 - 08:15 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

skeeve wrote:
flexible array member of a global or static struct.

thinking further, does it even need flexible arrays?

 

The struct could be just the id code, and a pointer to the string ?

 

depends how tight Flash space is ...

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

PROGMEM is already an extension.

Initializing a flexible array member should not be a problem.

It will get OP the sequence of bytes he wants.

 

If one really wants, there are contortions one can go

through to get the size and not use a flexible array member,

but why bother?

 

It might be easiest in assembly.

 

"SCSI is NOT magic. There are *fundamental technical
reasons* why it is necessary to sacrifice a young
goat to your SCSI chain now and then." -- John Woods

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

It might also be helpful to see how this is actually going to be used.

 

There might be a better / easier way to achieve the end goal ...

 

http://www.catb.org/esr/faqs/sma...

 

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi

Thanks for the suggestions, the code part of a block which sends and decodes commands from a cellular module (sim800), the problem is I do not know necessarily which message is going to come back (some are unsolicited messages) and you can get several at a time all separated by <cr>

I created an array which stores a pointer to each string:

const char * const PROGMEM SIM800_RESP_MSG[] = {SIM_MSG0,SIM_MSG1,SIM_MSG2

so it's then easy to step through the array and check which command has been received, the first character of each string would be used to store the return code, which then goes through a switch statement to do what ever is necessary.

I did use a counter in the loop which just returned the position of the matched string in the array above, but being able to use a symbolic value makes maintenance easier, and some returned strings have the same actions attached to them.

The only solution I can think it to have two flash arrays, one as above with the pointers, the other with the result codes, i.e

const char * const PROGMEM SIM800_RESP_MSG[] = {SIM_MSG0,SIM_MSG1,SIM_MSG2
const char PROGMEM SIM800_RESP_CODES[] = {RESP_OK,RESP_READY,RESPCIFSR..

I mistakenly thought as arrays are just byte lists I could add in an char (byte) value

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Array of structs my friend.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

robcarter wrote:
I mistakenly thought as arrays are just byte lists I could add in an char (byte) value

No,  that's not your mistake - that is entirely true and possible.

 

It just that you were doing it wrong!

 

As the title says, you were trying to use string concatenation - but, as the name suggests, that only works with strings!

 

You can't use string concatenation to concatenate a string and a non-string!

 

As several have said, using a struct (or array of structs) would seem the obvious approach ...

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

#13 was typed on a mobile so it wasn't easy for me to provide an example so...

#include <avr/io.h>

typedef struct {
    uint8_t resp_code;
    char text[];
} resp_text_pairing_t;

const __flash resp_text_pairing_t data1 = { 1, "hello" };
const __flash resp_text_pairing_t data2 = { 2, "world" };
const __flash resp_text_pairing_t data3 = { 1, "or longer strings" };

const __flash resp_text_pairing_t * data[] = {
    &data1,
    &data2,
    &data3
};

int main (void) {
    PORTB = data[1]->resp_code;
    PORTB = data[1]->text[1];
}

The "text" member of the struct is using a feature Michaeal suggested in #7 which is:

 

https://gcc.gnu.org/onlinedocs/g...

 

This is an extension to normal C provided in avr-gcc only and only available when std= is set to one of the "gnu" options (so probably -std=gnu99 which is the AS7 default). However because you were already tied to GCC by the use of "PROGMEM" I guess this does not matter. Because this is 2018 I used "const __flash" not PROGMEM. The upshot of all this is:

#include <avr/io.h>

typedef struct {
    uint8_t resp_code;
    char text[];
} resp_text_pairing_t;

const __flash resp_text_pairing_t data1 = { 1, "hello" };
const __flash resp_text_pairing_t data2 = { 2, "world" };
const __flash resp_text_pairing_t data3 = { 1, "or longer strings" };

const __flash resp_text_pairing_t * const __flash data[] = {
    &data1,
    &data2,
    &data3
};

int main (void) {
    PORTB = data[1]->resp_code;
    PORTB = data[1]->text[1];
}

Rather curiously that compiled without me needing to specify -std= so perhaps it is a "core" part of C after all? Anyway this generates:

00000094 <main>:

int main (void) {
    PORTB = data[1]->resp_code;
  94:   82 e0           ldi     r24, 0x02       ; 2
  96:   88 bb           out     0x18, r24       ; 24
    PORTB = data[1]->text[1];
  98:   ef e6           ldi     r30, 0x6F       ; 111
  9a:   f0 e0           ldi     r31, 0x00       ; 0
  9c:   84 91           lpm     r24, Z
  9e:   88 bb           out     0x18, r24       ; 24
}

I just used PORTB as a useful  place to make test writes of some of the data to. As you can see it's hard coded the 2 for resp_code for the first write but the second write does involve it LPMing from the place where data[1]->text[1] is stored. You can see that I succeeded in getting all this located into flash (.text) here:

D:\atmel_avr\avr8-gnu-toolchain-win32_x86\bin>avr-objdump -s avr.elf

avr.elf:     file format elf32-avr

Contents of section .text:
 0000 0c943e00 0c944800 0c944800 0c944800  ..>...H...H...H.
 0010 0c944800 0c944800 0c944800 0c944800  ..H...H...H...H.
 0020 0c944800 0c944800 0c944800 0c944800  ..H...H...H...H.
 0030 0c944800 0c944800 0c944800 0c944800  ..H...H...H...H.
 0040 0c944800 0c944800 0c944800 0c944800  ..H...H...H...H.
 0050 0c944800 74006d00 5a00016f 72206c6f  ..H.t.m.Z..or lo
 0060 6e676572 20737472 696e6773 0002776f  nger strings..wo
 0070 726c6400 0168656c 6c6f0000 11241fbe  rld..hello...$..
 0080 cfe5d4e0 debfcdbf 0e944a00 0c945300  ..........J...S.
 0090 0c940000 82e088bb efe6f0e0 849188bb  ................
 00a0 80e090e0 0895f894 ffcf               ..........

The blue text is the data[] array with the three pointers to the three data items (0x0074, 0x006D, 0x005a) that then follow. (interesting that the linker places them "backwards"!). I then colored them differently so you can see their boundaries. Each starts with a response code (0x01 or 0x02).

Last Edited: Mon. Aug 13, 2018 - 11:32 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

clawson wrote:
This is an extension to normal C provided in avr-gcc only

Actually, it is introduced to Standard 'C' in C99: https://en.wikipedia.org/wiki/Flexible_array_member

 

But, if you wanted to do it without relying upon that feature, you could use a pointer - something like:

typedef struct {
    uint8_t resp_code;
    char * p_text;
} resp_text_pairing_t;

const char data1[] = "hello";
const char data2[] = "world";
const char data3[] = "or longer strings";

const resp_text_pairing_t * const data[] = {
    ( 1, data1 },
    { 2, data2 },
    { 3, data3 }
};

 

Any decent embedded compiler will have some means to put stuff into Flash - so add that as required. 

Shouldn't be hard to make that portable with suitable preprocessor stuff.

 

And, of course, you can still use your #defines for the resp_code values.

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

awneil wrote:
Actually, it is introduced to Standard 'C' in C99:
Thing is I did not pass ANY -std= value on the command line and still it accepted it. That suggests that these days the compiler must be defaulting to C99. Previously I think C89 was the default.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

clawson wrote:
these days the compiler must be defaulting to C99.

Indeed - as you said in #15.

 

This also means that you can have things like:

  • intermingled declarations and code
  • inline functions
  • // comments
  • designated initializers
  • declare the index variable within a 'for' clause

 

many/most of which had, of course, been available as extensions in "C89" compilers (including GCC) for years ...

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
Last Edited: Mon. Aug 13, 2018 - 11:46 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Flexible array members have been around for a while.

Initializing them is still an extension.

"SCSI is NOT magic. There are *fundamental technical
reasons* why it is necessary to sacrifice a young
goat to your SCSI chain now and then." -- John Woods

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

skeeve wrote:
Flexible array members have been around for a while.

Well, C99 has been around for a while - nearly 20 years now!

 

surprise

 

and it's usually the case that "new" Standards (and new revisions of old standards) just codify stuff that's already well-established

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

skeeve wrote:
Initializing them is still an extension.
Then again it's curious that avr-gcc built without error using a VERY plain command line invocation:

avr-gcc -mmcu=atmega16 -Os -g avr.c -o avr.elf

It really was as simple as that to generate:

  ...
  48:   0c 94 48 00     jmp     0x90    ; 0x90 <__bad_interrupt>
  4c:   0c 94 48 00     jmp     0x90    ; 0x90 <__bad_interrupt>
  50:   0c 94 48 00     jmp     0x90    ; 0x90 <__bad_interrupt>

00000054 <__trampolines_end>:
  54:   74 00           .word   0x0074  ; ????
  56:   6d 00           .word   0x006d  ; ????
  58:   5a 00           .word   0x005a  ; ????

0000005a <data3>:
  5a:   01 6f 72 20 6c 6f 6e 67 65 72 20 73 74 72 69 6e     .or longer strin
  6a:   67 73 00                                            gs.

0000006d <data2>:
  6d:   02 77 6f 72 6c 64 00                                .world.

00000074 <data1>:
  74:   01 68 65 6c 6c 6f 00 00                             .hello..

0000007c <__ctors_end>:
  7c:   11 24           eor     r1, r1
  7e:   1f be           out     0x3f, r1        ; 63
  ...

So I not only created but initialized flexible arrays without telling the compiler to expect something special (like -std=gnu99)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

clawson wrote:
So I not only created but initialized flexible arrays without telling the compiler to expect something special (like -std=gnu99)

GCC-Documentation wrote:
The default, if no C language dialect options are given, is -std=gnu11.

 

Stefan Ernst

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

clawson wrote:
Then again it's curious that avr-gcc built without error using a VERY plain command line invocation:

With no -std= argument, the default is a gnu dialect i.e. all gnu extensions are enabled.  The default for C files is -std=gnu11 at least since 5.4.0.

 

EDIT:  too slow

"Experience is what enables you to recognise a mistake the second time you make it."

"Good judgement comes from experience.  Experience comes from bad judgement."

"Wisdom is always wont to arrive late, and to be a little approximate on first possession."

"When you hear hoofbeats, think horses, not unicorns."

"Fast.  Cheap.  Good.  Pick two."

"Read a lot.  Write a lot."

"We see a lot of arses on handlebars around here." - [J Ekdahl]

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Stefan / Joey, ah, interesting. I never looked it up but I would have guessed it would always be a "C standard". Guess this shows why you should never ass-u-me anything!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I would tend to ass-u-me that a compiler is going to default to enabling all the latest & greatest of its own wonderful added extras - and that you'd need to specifically restrict it if you wanted "standards" only ...

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Not if I have legacy code to build. I'd want it default the way it always has then have any new changes turned on only if I specifically ask.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

When I have legacy code, I'd expect - not like, but expect - to be having to force it back to the "old ways".

 

I'd expect the default to be "latest & greatest".

 

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...