String array variable not storing values

Hi. I'm trying to store values from a char to a String, sometimes stores the values, sometimes doesn't store them.
In the char array always store the values.
In the code I'm increment ascii code and store it in a char array buffer by pressing up or down button and left or right for increment or decrement length of the char.
For each char array I want to store it in the String array.

#include <Wire.h>

//Grove-LCD RGB Backlight V4.0
#include "rgb_lcd.h"

rgb_lcd lcd;

uint32_t previousPushMillis = 0;
uint32_t previousMillis = 0;
uint32_t currentMillis = 0;

uint8_t menuCat = 0;

int16_t analogPin0 = A0;
int16_t buttonValue = 0; //Store value from the analog pin
bool selectButton = false; 
bool upButton = false;
bool downButton = false;
bool leftButton = false;
bool rightButton = false;
bool pushedButton = false; //flag if a button was pressed or not
bool flagLCD = false;

uint16_t referenceIdCount = 0; //ID count for String array
uint8_t const referenceId = 200; //Max val for String array

String referenceMenu[referenceId]; //String array
char refMem[16]; // Temp char array to store values to a String 
int8_t refMemCount = 0; //counter for char array
void setup() {
  lcd.begin(16, 2);
  lcd.clear();
  Serial.begin(9600);
}

void loop() {
  currentMillis = millis();
  keypad();
  menu();
}

void menu() {
  switch (menuCat) {
    case 0:
       //increment String array
      if (upButton == true && (currentMillis - previousMillis >= 50)) {
        if (referenceIdCount < (referenceId - 1)) {
          referenceIdCount++;
        } else {
          referenceIdCount = referenceId - 1;
        }
        flagLCD = false;
        previousMillis = millis();
      }
      //decrement String array
      if (downButton == true && (currentMillis - previousMillis >= 50)) {
        if (referenceIdCount > 0) {
          referenceIdCount--;
        } else {
          referenceIdCount = 0;
        }
        flagLCD = false;
        previousMillis = millis();
      }
      //select the current String array number and go to the char array input
      if (selectButton == true && (currentMillis - previousMillis >= 500)) {
        menuCat = 10;
        // Serial.println("Lenght: ");
        // Serial.println(referenceMenu[referenceIdCount].length());
        // verify if the current String array number is not empty and if not empty store the actual value to char array
        if (referenceMenu[referenceIdCount].length() != 0) {
          referenceMenu[referenceIdCount].toCharArray(refMem, 16);
          refMemCount = 0;
        }
        flagLCD = false;
        previousMillis = millis();
      }
      if (flagLCD == false) {
        lcd.clear();
        lcd.setCursor(12, 0);
        lcd.print(referenceIdCount);
        lcd.setCursor(0, 1);
        lcd.print(referenceMenu[referenceIdCount]);
        flagLCD = true;
      }

      break;
    case 10:
      //go  to increment the char array
      if (leftButton == true && (currentMillis - previousMillis >= 500)) {
        menuCat = 11;
        previousMillis = millis();
      }
     // go to decrement the char array
      if (rightButton == true && (currentMillis - previousMillis >= 500)) {
        menuCat = 12;
        previousMillis = millis();
      }
      //go to  incremet the ascii code for the selected char from array
      if (upButton == true && (currentMillis - previousMillis >= 200)) {
        menuCat = 13;
        previousMillis = millis();
      }
       //go to  decrement the ascii code for the selected char from array
      if (downButton == true && (currentMillis - previousMillis >= 200)) {
        menuCat = 14;
        previousMillis = millis();
      }
      // go to store the values from char array to selected String
      if (selectButton == true && (currentMillis - previousMillis >= 500)) {
        menuCat = 15;
        previousMillis = millis();
      }
      lcd.setCursor(0, 1);
      lcd.print(refMem);


      break;
    //increment the char array
    case 11:
      if (refMemCount <= 0) {
        refMemCount = 0;
      } else {
        refMemCount--;
      }
      menuCat = 10;
      break;
    //decrement the char array
    case 12:
      if (refMemCount >= 15) {
        refMemCount = 15;
      } else {
        refMemCount++;
      }
      menuCat = 10;
      break;
    //incremet the ascii code for the selected char from array
    case 13:
      refMem[refMemCount]++;

      if (refMem[refMemCount] >= 90) {
        refMem[refMemCount] = 90;
      } else if (refMem[refMemCount] > 57 && refMem[refMemCount] < 65) {
        refMem[refMemCount] = 65;
      } else if (refMem[refMemCount] > 32 && refMem[refMemCount] < 48) {
        refMem[refMemCount] = 48;
      } else if (refMem[refMemCount] < 32){
        refMem[refMemCount] = 32;
      }
      menuCat = 10;
      break;
    //decrement the ascii code for the selected char from array
    case 14:
      refMem[refMemCount]--;
      if (refMem[refMemCount] <= 32) {
        refMem[refMemCount] = 32;
      } else if (refMem[refMemCount] > 32 && refMem[refMemCount] < 48) {
        refMem[refMemCount] = 32;
      } else if (refMem[refMemCount] > 57 && refMem[refMemCount] < 65) {
        refMem[refMemCount] = 57;
      }
      menuCat = 10;
      break;
    //store the values from char array to selected String
    case 15:
      lcd.noCursor();
      String strtingRefMenu(refMem);
      referenceMenu[referenceIdCount] = strtingRefMenu;
      lcd.setCursor(0, 1);
      // lcd.print(referenceMenu[referenceIdCount]);
      // Serial.print("Ref Mem: ");
      // Serial.println(refMem);
      // Serial.print("Ref Menu String: ");
      // Serial.println(strtingRefMenu);
      // Serial.print("Ref ID: ");
      // Serial.println(referenceIdCount);
      Serial.print("Ref Menu: ");
      Serial.println(referenceMenu[referenceIdCount]);
      menuCat = 0;
      refMemCount = 0;
      break;
  }
}

void keypad() {
  buttonValue = analogRead(analogPin0);
  if (buttonValue < 1000) {
    delay(100);
    buttonValue = analogRead(analogPin0);
  }

  switch (buttonValue / 100) {
    case 7:
      if (pushedButton == false && (currentMillis - previousPushMillis) >= 100) {
        selectButton = true;
        upButton = false;
        downButton = false;
        leftButton = false;
        rightButton = false;
        previousPushMillis = millis();
      }
      break;
    case 1:
      if (pushedButton == false && (currentMillis - previousPushMillis) >= 100) {
        selectButton = false;
        upButton = true;
        downButton = false;
        leftButton = false;
        rightButton = false;
        previousPushMillis = millis();
      }
      break;
    case 3:
      if (pushedButton == false && (currentMillis - previousPushMillis) >= 100) {
        selectButton = false;
        upButton = false;
        downButton = true;
        leftButton = false;
        rightButton = false;
        previousPushMillis = millis();
      }
      break;
    case 0:
      if (pushedButton == false && (currentMillis - previousPushMillis) >= 100) {
        selectButton = false;
        upButton = false;
        downButton = false;
        leftButton = true;
        rightButton = false;
        previousPushMillis = millis();
      }
      break;
    case 5:
      if (pushedButton == false && (currentMillis - previousPushMillis) >= 100) {
        selectButton = false;
        upButton = false;
        downButton = false;
        leftButton = false;
        rightButton = true;
        previousPushMillis = millis();
      }
      break;
    case 10:
      selectButton = false;
      upButton = false;
      downButton = false;
      leftButton = false;
      rightButton = false;
      break;
  }
}

I really need help for this. I was looking and looking over the code and I can't understand what is wrong.
Thanks.

What kind of arduino ?

Leonardo

you don't have much SRAM (2.5KB) and you are declaring a very large String array

you might be running out of memory.


pushedButton is always false, why do you have that?


if you want to use refMem as a c-string you need to terminate it with a null character

Can you please provide a link to the rgb_lcd library that you use.

You have created an array of 200 String objects; that is already taking up 200x6 = 1200 bytes of RAM (nearly half of the memory of the Leonardo). When you store data in one of those 200 String objects, it will be added to the 1200; e.g. placing 2 characters in each String object will increase that usage to 200x8 = 1600 bytes. There might be some other side effects.

I strongly suggest that you stay away from String objects.

Sketch uses 9936 bytes (34%) of program storage space. Maximum is 28672 bytes.
Global variables use 1654 bytes (64%) of dynamic memory, leaving 906 bytes for local variables. Maximum is 2560 bytes.

It is only a fragment of the code (test code written in separate ino file). I always use redundancy to check some inputs and I forgot to clear it from the code.

Is not by default adding null character?

When I store the values in the String, it works from two to two String increments. I don't know if I can put some pictures here to show you. I am new on the forum.

Down the page is the link to the library.

You say that, every time I store a value in the String, it will increase the memory usage?

Can you give me a suggestion what can I use instead?

Thanks.

do you need to store 200 of them ? what's the max number you really need and what do you want to store in them? (how long is the text)?

Around 200, I don't know the exact number. What I know is that the number is bellow 200 and more than 100.

if you store an array of String pointers, a pointer will be 2 bytes and a String instance takes up 6 bytes, so your array

is using 200 x 2 bytes for the pointers and 200 x 6 bytes for the Strings = 1600 bytes

Now there is more memory needed when you store stuff in the Strings.

how long is the maximum string you want. to store in there ? is that the 16 byte buffer?

200 x 16 = 3200 bytes

so you are trying to use up to 4800 bytes of memory in a board that has ~2600 bytes of ram...

what do you think can go wrong ?? :cold_face:

if you want to store say t 150 c-stings (less memory than with the String class) with 15 chars + trailing null char

char stringStorage[150][16];

that's a minimum of 2400 bytes. that's already probably too much as you need SRAM for the rest of the program.

➜ you need an arduino with more SRAM like an ESP or a MKR

So I need more memory to store the stuff inside the Strings.

Yes it is the 16 byte buffer.

I already tried with c-strings and I was shocked how much memory it uses.

I will buy another mcu but I still don't understand why it it store values random.

Like this:

I uncommented from case 15
// Serial.print("Ref Mem: ");
// Serial.println(refMem);
// Serial.print("Ref Menu String: ");
// Serial.println(strtingRefMenu);
// Serial.print("Ref ID: ");
// Serial.println(referenceIdCount);

and the result is like this:

First try to store:

Ref Mem:
Ref Menu String:
Ref ID: 0
Ref Menu:

Second try to store:

Ref Mem: 57
Ref Menu String:
Ref ID: 1
Ref Menu:

Third try to store:

Ref Mem: 67
Ref Menu String: 67
Ref ID: 2
Ref Menu:

Fourth try to store:

Ref Mem: 6A
Ref Menu String: 6A
Ref ID: 3
Ref Menu: 6A

In the first try I didn't selected any character to store so I don't know if it was stored the value because ascii is 32 (space). In the second I've selected 5 and 7 characters and not even on the char buffer didn't store the values. In the third try I've selected characters 6 and 7, this time it stored in the char buffer but not converted to String. Only in the fourth try stored the characters selected. 6 and A. And always is random.

Every time in different String (see Ref ID) in String array.

When the Arduino runs out of ram (dynamic) memory, there are no error messages, and random things happen.

I've stored only in 4 locations in the String array with max of 2 bytes. It is possible to run out of memory?

if your sketch is large, use lots of library requiring memory then you don't really know how much is left.

what do you expect ? 1 character requires one byte... if you want to store 200x16 characters, you need at least 3200 bytes available...

When you define a global variable like this
char stringStorage[150][16];
the memory is allocated statically and the compiler knows how much memory is needed, so it can be reported at the end of the compilation. But when you do

uint8_t const referenceId = 200; //Max val for String array
String referenceMenu[referenceId]; //String array

you only statically allocate the 200 pointers to the String instances, so as I said before 200x8 bytes that the compiler can report but this includes NO storage whatsoever for the data (the content of the strings).

Unless you move to a much more capable hardware, start by ditching the String class and use only char arrays. This way you know upfront how many bytes you have to play with.

side note: the delays and use of millis feels weird in your code

so change the declaration of 200 String array to array of 10 Strings and test the code

Basically yes.

if your String objects are fixed text, store them in PROGMEM. If they are created at run time store them in (external) EEPROM/FRAM or on SD card.

That is because the String class hides the actual underlaying character array (c-strings).

Works flawlessly!

Ref Mem: 24
Ref Menu String: 24
Ref ID: 0
Ref Menu: 24

Ref Mem: A6
Ref Menu String: A6
Ref ID: 1
Ref Menu: A6

Ref Mem: 56
Ref Menu String: 56
Ref ID: 2
Ref Menu: 56

Ref Mem: D6
Ref Menu String: D6
Ref ID: 3
Ref Menu: D6

Ref Mem: F6
Ref Menu String: F6
Ref ID: 4
Ref Menu: F6

Thanks all for support. The problem is the ram, to less.

The delay it was only to test the function of the buttons, I forgot to comment it :grin:. But the millis, can you be more specific? Maybe you open my eyes to use them correctly.

Always is something new to learn. :slightly_smiling_face:

I'm curious about the various duration you use throughout the code

Where is 500, it was in all locations, but I saw that some increments takes to long.
Where is 50, is for incrementing faster (it takes time to increment to 200).
Where is 200, is for incrementing relatively faster (for ascii characters increments).

There is another approach, to use it smarter?

OK I was just wondering if there was a rationale for this.