interesting lcd.print memory bug

I have an interesting bug in my code.
First a little background I'm trying to make a "simple" menu system together with freeRTOS, an encoder and an I2C LCD (16x2).

I have a string array just sitting there in the code doing nothing. Somehow it's printed to the LCD.
Never does Lcd.print make a call to that string.
here below is the sting on line 44 - 46.

const int numOfSettingsBox = 7  ;
String settings[numOfSettingsBox] = {{"Choose Mode:"}, {"Brightness:"}, {"Back"}, {"Brightness:"}, {"Sensitivity"},  {"Reacts on:"},  {"Back"},
};

Then I thought well let's remove this string and when that bug happens t esp32 crashes with a LoadProhibited fault.
I know what this error means (it means you can't read this bit of memory) so think this is just the way I send the data out to the lcd.

I have uploaded The code to Github and here is a video what happens.

thank you for the help in advance, sorry for another esp32 topic on the Arduino forum :frowning:

Without examining it too closely, I'd begin to think that your method for initialising the Strings is, somehow, leaving them without a null terminator in their internal representation, allowing them to be concatenated with other unrelated display items.
Interesting problem though.

6v6gt:
Without examining it too closely, I'd begin to think that your method for initialising the Strings is, somehow, leaving them without a null terminator in their internal representation, allowing them to be concatenated with other unrelated display items.

that was also what i was thinking. here is where it gets more interesting.
when i do the same without freeRTOS (vTaskSuspend(), vTaskResume() and tasks pinned to core) this bug doesn't happen.
but the controlling code is more complex. and requires more space.

sebasdt1:
I have a string array [...]

I don't see a string array (or an array of strings for that matter) anywhere in your code. I do see hoverer an awful lot of arrays of String. That's certainly very different :wink:

But if things start to appear on screen out of the blue, you probably overshoot an array or mangle a pointer somewhere. I don't get when the bug happens from the video but it's probably around that bit of code.

Things I noticed:

 if ((long)(micros() - LastMicros) >= 25 * 1000) {

Dit you mean to make the multiplication a long? Because making the subtraction long makes NO sense. That would mangle any roll over protection you have from using unsigned and subtraction.

 if ((micros() - LastMicros) >= 25 * 1000UL) {

I don't want to distract you from finding out what exactly happened because we may learn something, but I'd tend to use this example of defining character arrays rather than Strings for your menu items. These are constants, so I guess you could event have them in flash memory ("PROGMEM") only, instead of also in RAM.

char *myStrings[] = {"This is string 1", "This is string 2", "This is string 3",
                     "This is string 4", "This is string 5", "This is string 6"
                    };

void setup() {
  Serial.begin(9600);
}

void loop() {
  for (int i = 0; i < 6; i++) {
    Serial.println(myStrings[i]);
    delay(500);
  }
}

from: string - Arduino Reference

septillion:
I don't see a string array (or an array of strings for that matter) anywhere in your code. I do see hoverer an awful lot of arrays of String. That's certainly very different :wink:

oh didn't see that typo woops.

septillion:
But if things start to appear on screen out of the blue, you probably overshoot an array or mangle a pointer somewhere. I don't get when the bug happens from the video but it's probably around that bit of code.

what happens is this: mode has changed, goes into the task does its job and returns.
In that task it does something like this:

  • Pause the main menu task.
  • Changes the boundary of the encoder
  • Prints the mode on the LCD.
  • resume the main menu task.
  • Pause the task its in. (so demo or music)
  • when the button is pressed in the main menu the boundary is changed.

Now i wrote this down, think that 6 needs to go between 4 and 5.
Septillion thanks for the thing you noticed. changed it and works even better.

When the menu system is more worked out I want to compare the string that's on-screen and the "back" string.
so the code knows when to return to the main menu.
i have done this:

 if (!settings[parameters.currentScherm].equals("Terug")) {

if I use the code here below do I lose this function?

char *myStrings[] = {"This is string 1", "This is string 2", "This is string 3",
                     "This is string 4", "This is string 5", "This is string 6"
                    };

That is an interesting way of doing it :

if (!settings[parameters.currentScherm].equals("Terug")) {

"Terug" is not currently in the array settings[] although settings[2], has "Back" as a near English equivalent.

Anyway, it is usually better to compare an index rather than the string (String) itself . . .

if ( parameters.currentScherm == 2 ) {

But, if you want to do it as you had, but with character arrays, you'd do something like:

if ( strcmp ( settings[parameters.currentScherm] , "Terug" ) {   // strcmp() returns 0 if match

yeah, I forgot to replace "terug" with "back".

I know that's an interesting way to compare a string. I went this way so it isn't hard to understand what I'm comparing against.
Also, I went this way so if the string ("back") is put somewhere else i didn't need to search in the code and change the 2.

thank you for the tip!

6v6gt:
But, if you want to do it as you had, but with character arrays, you'd do something like:

if ( strcmp ( settings[parameters.currentScherm] , "Terug" ) {   // strcmp() returns 0 if match

shouldn't it be:

if ( !strcmp ( settings[parameters.currentScherm] , "Terug" )){   // strcmp() returns 0 if match

No. Look carefully at the comment following the 'if' statement. I had already anticipated that it might be confusing and it looks like the original code should execute only if there is no match

strcmp()

sebasdt1:
what happens is this: mode has changed, goes into the task does its job and returns.
In that task it does something like this:

  • Pause the main menu task.
  • Changes the boundary of the encoder
  • Prints the mode on the LCD.
  • resume the main menu task.
  • Pause the task its in. (so demo or music)
  • when the button is pressed in the main menu the boundary is changed.

Now i wrote this down, think that 6 needs to go between 4 and 5.

But back to the main problem or bug.
when i moved 6. between 4. and 5. (changed where a new boundary is set) The bug happened less but is still there.
so that means somewhere in the code i need to check if the value of the encoder is between the set boundary.

6v6gt:
No. Look carefully at the comment following the 'if' statement. I had already anticipated that it might be confusing and it looks like the original code should execute only if there is no match

strcmp()

you're right about that. I had a minor brainfart. for now the bug is almost fixed.

Incidentally, if you do choose to do it like this:

char *myStrings[] = {"This is string 1", "This is string 2", "This is string 3",
                     "This is string 4", "This is string 5", "This is string 6"
                    };

You don't have to do something like this to say how many array items have been created :

const int numOfSettingsBox = 7  ;

you can calculate it like demonstrated here

numOfSettingsBox = sizeof( myStrings) / sizeof( char* )  ;

since myStrings is an array of pointers and sizeof( char* ) is the size of one pointer.

6v6gt:
You don't have to do something like this to say how many array items have been created :

const int numOfSettingsBox = 7  ;

you can calculate it like demonstrated here

numOfSettingsBox = sizeof( myStrings) / sizeof( char* )  ;

since myStrings is an array of pointers and sizeof( char* ) is the size of one pointer.

So you are saying i could do some thing like this:

char *settings[] = {"Choose Mode:", "Brightness:", "Back", "Brightness:", "Sensitivity audio:", "Reacts on:", "Back"
                   };
const int numOfSettings = sizeof( settings) / sizeof( char* );

That would make things even easier!

Yes. That will work because those sizes are known at compile time so can be assigned to a constant.

6v6gt and others thank you very much for the help! The code has been simplified and updated so the bug is gone

This topic was automatically closed 120 days after the last reply. New replies are no longer allowed.