На плате Arduino UNO, NANO и им подобных установлен микроконтроллер ATmega328 / P. М-к ATmega328 содержит 32 Кбайт Flash-памяти программ, 1 Кбайт энергонезависимой памяти (EEPROM), 2 Кбайт оперативной памяти (SRAM).

Оперативная память ценный ресурс, а EEPROM не часто востребована. Строковые литералы засоряют оперативную память даже если они используются для объявления переменных и констант локально. Строковые литералы можно хранить в EEPROM.

Пример, сохраняем строку в EEPROM и восстанавливаем переменные из EEPROM:

#include <EEPROM.h>
String str = "Hello, World!";         // Объявляем строковую переменную

void setup() {
  char buf [21];                      // Создаём текстовый буфер для массива из 20 символов
  Serial.begin(115200);               // Инициализация последовательного порта
  while (!Serial) ;
  str.toCharArray(buf, 21);           // Помещаем содержимое строки str в buf (buf = str с преобразованием типов)
  for (int n = 0; n < 21; n++) {
    EEPROM[n] = byte(buf[n]);         // Размещаем содержимое массива buf в EEPROM побайтно
  }
  for (int n = 0; n < 21; n++) {
    buf[n] = char(EEPROM[n]);         // Копируем содержимое EEPROM в массива buf побайтно
  }
  Serial.println(buf);                // Печатаем содержимое buf в порт
  String str(buf);                    // конвертируем массив символов buf в строку str, если нужно
  Serial.println(str);                // Печатаем содержимое строки str в порт
}

void loop() {
}

Скетч использует 3226 байт (10%) памяти устройства. Всего доступно 30720 байт. Глобальные переменные используют 218 байт (10%) динамической памяти.

Пример, восстанавливаем ранее сохранённые переменные из EEPROM:

#include <EEPROM.h>
//String str = "Hello, World!";         // Объявляем строковую переменную

void setup() {
  char buf [21];                      // Создаём текстовый буфер для массива из 20 символов
  Serial.begin(115200);               // Инициализация последовательного порта
  while (!Serial) ;
//  str.toCharArray(buf, 21);           // Помещаем содержимое строки str в buf (buf = str с преобразованием типов)
//  for (int n = 0; n < 21; n++) {
//    EEPROM[n] = byte(buf[n]);         // Размещаем содержимое массива buf в EEPROM побайтно
//  }
  for (int n = 0; n < 21; n++) {
    buf[n] = char(EEPROM[n]);         // Копируем содержимое EEPROM в массива buf побайтно
  }
  Serial.println(buf);                // Печатаем содержимое buf в порт
  String str(buf);                    // конвертируем массив символов buf в строку str, если нужно
  Serial.println(str);                // Печатаем содержимое строки str в порт
}

void loop() {
}

Скетч использует 2752 байт (8%) памяти устройства. Всего доступно 30720 байт. Глобальные переменные используют 198 байт (9%).

В обоих случаях программа выводит в монитор порта 2 строки:

Hello, World!
Hello, World!

Экономия памяти 218 - 198 = 20 Байт.

Если вам не нужна строковая переменная, сэкономим ещё 10 Байт:

#include <EEPROM.h>
//String str = "Hello, World!";         // Объявляем строковую переменную

void setup() {
  char buf [21];                      // Создаём текстовый буфер для массива из 20 символов
  Serial.begin(115200);               // Инициализация последовательного порта
  while (!Serial) ;
//  str.toCharArray(buf, 21);           // Помещаем содержимое строки str в buf (buf = str с преобразованием типов)
//  for (int n = 0; n < 21; n++) {
//    EEPROM[n] = byte(buf[n]);         // Размещаем содержимое массива buf в EEPROM побайтно
//  }
  for (int n = 0; n < 21; n++) {
    buf[n] = char(EEPROM[n]);         // Копируем содержимое EEPROM в массива buf побайтно
  }
  Serial.println(buf);                // Печатаем содержимое buf в порт
//  String str(buf);                    // конвертируем массив символов buf в строку str, если нужно
//  Serial.println(str);                // Печатаем содержимое строки str в порт
}

void loop() {
}

Скетч использует 1556 байт (5%) памяти устройства. Всего доступно 30720 байт. Глобальные переменные используют 188 байт (9%) динамической памяти.