ArduBoy resets unexpectedly [solved]

I got my ArduBoy today and, naturally, decided to play around with it. So I started putting together a small project like I might have with Hypercard but something unexpected has happened. I have a function, print_string, which is used to take a string from program memory and display it on screen. It seems to work fine. However, while adding a shop I used the print_string function and now … well, if I add it then the game resets whenever you start the game on the title screen. In fact if I use it ANYWHERE else in the project it resets the program. Like I’ve hit some limit on how many times this function can be called in the project.

Naturally I’d like to think I’ve done something that is causing it, but given how it doesn’t matter where in the project I add it, I’ll admit I’m at a loss for what the cause is. Granted C++ is definitely not my top skill so I’m hoping it’s something incredibly obvious to someone else. I’m using the ArduBoy2 library and this is the function:

void print_string( byte x, byte y, byte index, byte buffer_length, byte align = TEXT_LEFT ) {
	char t_buffer[ buffer_length ];
	
	switch( align ) {
		case TEXT_CENTER: x -= ( buffer_length * FONT_WIDTH ) / 2; break;
		case TEXT_RIGHT	: x -= buffer_length * FONT_WIDTH; break;
	}
	arduboy.setCursor( x, y );
	arduboy.print( strcpy_P( t_buffer, (char*)pgm_read_word( &( stringTable[ index ] ) ) ) );
	
}

Any help would be duly appreciated.

Could you post a cut down program that shows it failing?

How is stringTable defined?

Also you don’t need to be doing a strcpy_P here.
If stringTable is what I think it is then it’ll probably end up looking like:
arduboy.print(reinterpret_cast<const __FlashStringHelper *>(pgm_read_word(&stringTable[index])));

At first glance I’m expecting this to be a progmem issue or a case of running out of RAM.
It could also be a case of the buffer not being big enough, in which case strcpy_P would overwrite other parts of the stack.

2 Likes

I gave your suggestion a try, and voila, it worked fine. However, that leads me into my next question which would be, what exactly is your particular code doing? New to C++ and DEFINITELY new to Arduino so please excuse some of the nonsense that pours out of my keyboard here.

As far as I understand it, what happens is rather than addressing space in RAM, I’m addressing a space in the ROM, so I need to point my address to that location instead. As far as I understand it, the &stringTable is pointing to that address, and pgm_read_word is using that to retrieve the proper value. If I were to print that return, however, I would simply get the address back of the original string since the array is only holding the memory positions of the string constants. Therefore I need to take THAT address to find the final value. So, if I understand the document on reinterpret_cast correctly, it’s telling the compiler to treat this as a new const? Or it’s saying treat these bits AS a const? Since that const is local to the function, I’m going to assume it is properly freed when the function ends?

2 Likes

When you write (char*), you’re using a ‘C-style cast’, which has some very specific rules as to what it does (which I won’t go in to, because that’ll confuse things).

Normally in C++ code it’s preferred to use one of the C+±style casts, which consist of static_cast, dynamic_cast, reinterpret_cast and const_cast.

static_cast does basic value conversions like char to int and unsigned int to signed int etc.
reinterpret_cast is used for converting pointers. The code reinterpret_cast<ptrtype>(ptr) is explicitly saying "treat this pointer value ptr as the pointer type ptrtype".

(Don’t worry about the other, they’re rarely needed.)

So you can imagine reinterpret_cast<const __FlashStringHelper *> as (const __FlashStringHelper *) if it helps.

If you haven’t seen const before, don’t worry about it too much (but make a note to look into it some day). It basically means ‘readonly’ and it’s used to signal that a value is intended to be read from and not written to.

The real magic here is the __FlashStringHelper. The print function has an overload that accepts a const __FlashStringHelper *, which it knows to treat as a pointer to a null-terminated char array (or ‘string’ if you prefer to call it that) in progmem.
Normally you only get a const __FlashStringHelper * by using the F macro, but you can just cast a const char * to a section of progmem to that type and it’ll then treat that pointer as a string stored in progmem.

(I won’t explain what exactly __FlashStringHelper is or why it works the way it does, because it means explaining quite a bit of other stuff first, but just imagine it’s a sort of way of telling the print function “this is in progmem, not RAM”.)


To break my code down specifically:

stringTable[index] this represents the value at the index index in stringTable, hence &stringTable[index] is the address of that particular value.

pgm_read_word then takes that address, treats it as a ROM address and reads out a word-sized value (i.e. a 16-bit value).

reinterpret_cast<const __FlashStringHelper *> then takes that 16-bit value and tells the compiler to treat it as a const __FlashStringHelper * (a pointer to a string in progmem).

arduboy.print is then the correct print function for dealing with strings in progmem, and it will read that string from progmem character by character and print it onto the screen.

Importantly, it’s not copying the contents of ROM into RAM (well, not the whole string, technically print keeps a copy of each char as it’s printing) so there’s nothing to be ‘freed’ per se. There is no buffer allocation because printing reads straight from ROM instead of from a buffer.


By the way, I to take it that stringTable is something like this?

const char string0[] PROGMEM = "First bit of text";
const char string1[] PROGMEM = "Second block of text";

const char * stringTable[] PROGMEM = { string0, string1 };

(Just wanted to check to make sure you’re doing what I think you’re doing and there’s no misunderstanding.)

1 Like

Hmm, so reading up on __FlashStringHelper * brings up an alternate question. Would avoiding all of this and just using ardbuboy.print(F(“Hello world!”)); have a similar impact? You’d lose the reusability of the string, but if I understand it correctly, “Hello world!” in this case would be stored in PROGMEM.

EDIT: Further reading says … maybe? It wouldn’t handle the duplicate cases, as expected. Unfortunately I’m not at home and can’t give this a try right now, but I’d be interested in any other insights you have.

Obviously it wouldn’t have the cursor changing part baked in, but otherwise it’s generally accepted to just do arduboy.print(F("Hello world!"));. Your understanding is right, the point of F is to put stuff in progmem and it doesn’t attempt to avoid duplicates.

There are reasons to prefer the other approach though, particularly in the case of localisation (i.e. you plan to ‘translate’ the game) or in the case that you need the index for some reason. (There might be other uses, those are the two I’ve used it for in the past.)

In the game I’m currently helping out with, Dark & Under, the enemy names are stored in a table a bit like that to help keep them together, to make the reusable and to save space.

1 Like

Yeah, in the other discussions I was reading it seems that a lot of people lean heavily towards the const array solution for the reasons you just gave. Thank you very much for your assistance, I believe insofar as this topic is concerned, it can be considered solved.

1 Like

Technically speaking we didn’t actually pinpoint why it was resetting, but I’m willing to bet it was either a case of overwriting RAM that shouldn’t have been overwritten or running out of RAM.

I’ve done tests in the past which indicate that tipping the stack (by allocating a larger local array/buffer than the stack can hold) can cause the Arduboy to reset.

Without looking at more of the code’s context I couldn’t say which it was, but you’ve got a workaround anyway so it’s probably not very important now :P.

1 Like

That is true, but while the specific underlying reason is not known, adding another call to that function ANYWHERE in the program caused the reset. Either strcpy_P or the declaration of tbuffer were at fault, and both suggest your analysis is probably accurate. I already knew it was one of those two lines, commenting them out also “fixed” the problem, so that your fix also works rules out everything else.

More importantly based on your descriptions, this is a better and simpler way to do it. I still definitely wonder what happened. Right now I don’t know enough to figure it out myself, but hopefully as I learn more the answer will become apparent.

1 Like