-2

Trying to build a menu system but running into some issues with pointers - which I don't have much experience with.

I don't understand why removing the while(1) makes the comparison fail between mainmenu_table[1][i] == &option6 but for some reason it does.

What am I doing wrong? Using visual studio and an atmega328p. Thanks

Serial output with original code:

Serial begins
MeNu6
MeNu6
Starting compare loop
it worked

Serial output with while(1) removed.

Serial begins
MeNu6
MeNu6
Starting compare loop
the end

Original code (with while(1) included)

const char option1[] PROGMEM = "Menu1";
const char option2[] PROGMEM = "MEnu2";
const char option3[] PROGMEM = "MeNu3";

const char option4[] PROGMEM = "Menu4";
const char option5[] PROGMEM = "MEnu5";
const char option6[] PROGMEM = "MeNu6";
const char option7[] PROGMEM = "menu7";

const char* const submenu1_table[] PROGMEM = { option1, option2, option3 }; // array of pointers to chars stored in flash
const char* const submenu2_table[] PROGMEM = { option4, option5, option6, option7 };
const char** const mainmenu_table[] PROGMEM = { submenu1_table, submenu2_table }; //array of pointers to pointers to chars in flash


// The setup() function runs once each time the micro-controller starts
void setup()
{

    Serial.begin(9600);
    delay(100);
    Serial.println("Serial begins");  

    Serial.println((const __FlashStringHelper*)(mainmenu_table[1][2]));  // prints "Menu6" as expected
    Serial.println((const __FlashStringHelper*)option6);  // also prints "Menu6"

    Serial.println("Starting compare loop");

    for (int i = 0; i < 4; i++) {
        if ( mainmenu_table[1][i] == &option6 ) { //
            Serial.println("it worked");
            while (1);  // COMMENTING THIS OUT MEANS DOESN'T COMPARE SUCCESSFULLY.
        }
    }

    Serial.println("the end");

}

// Add the main program code into the continuous loop() function
void loop()
{

}
walnut
  • 21,629
  • 4
  • 23
  • 59
Calum Nicoll
  • 109
  • 5
  • Is `Serial.println` a standard function in C++? You have no includes so where is it defined? – Jongware Mar 02 '20 at 21:10
  • 1
    @usr2564301 It is the Arduino standard library. – walnut Mar 02 '20 at 21:15
  • Thanks @walnut that is correct – Calum Nicoll Mar 02 '20 at 21:15
  • 2
    First of, `while (1);` is not allowed. Infinite loops that do not eventually access either IO, volatiles, or atomics have undefined behavior in C++. – walnut Mar 02 '20 at 21:17
  • 1
    Can you try compiling this will all optimization turned off? I wonder if Arduino optimizes out naked Serial.println commands? – JohnFilleau Mar 02 '20 at 21:19
  • 2
    Second, the type of `mainmenu_table` is wrong. It should not compile in standard-C++. If I remember correctly Arduino uses `-fpermissive` by-default, which is a terrible idea. The type should be `const char* const* const`. – walnut Mar 02 '20 at 21:20
  • I've turned off optimization in visual studio - I am using the visual micro plugin - I don't think this has additional optimization. After turning off visual studio optimisation ("No optimisation"), the issue persists. – Calum Nicoll Mar 02 '20 at 21:27
  • Thanks @walnut I will change the type now – Calum Nicoll Mar 02 '20 at 21:27
  • Similarly `&option6` does not have the correct type either and would fail to compile without `-fpermissive`. The type of `&option6` is `const char (*)[6]`, not `const char*` as you expect. I have no idea how GCC with `-fpermissive` behaves in such cases. But to write standard-C++ you need to remove the `&`. The array decays to a pointer to the first element by itself. This may or may not be the cause of your issue. – walnut Mar 02 '20 at 21:30
  • So try `if ( mainmenu_table[1][i] == option6 ) {` – KamilCuk Mar 02 '20 at 21:31
  • Unfortunately after changing the type of mainmenu_table and also trying ( mainmenu_table[1][i] == option6 ) it still only works if I include the while(1). – Calum Nicoll Mar 02 '20 at 21:37

1 Answers1

2

According to Arduino description of PROGMEM, you cannot access the data through pointers to it directly as with plain pointers. You need to use the proper macros/functions to access the data.

In your code, the pointer tables themselves are located in the PROGMEM, so, to extract the individual pointers, you are supposed to do something like:

const char** submenu = (const char**)pgm_read_word(&(mainmenu_table[1]));
const char* option = (const char*)pgm_read_word(&(submenu[i]));
if (option == option6) {
    //...

This code is based on the string table example from the first link.

Kit.
  • 2,386
  • 1
  • 12
  • 14