Usar strings no Arduino pode ser muito simples – existe uma classe chamada String – mas, por outro lado, traz uma grande utilização de memória em um dispositivo muito limitado nesse quesito. Este post traz algumas formas para mitigar a utilização de memória utilizando strings.
Quando se inicia com Arduino, os primeiros exemplos que se encontra para utilização de strings é a classe String. Ela é muito simples de utilizar e contém métodos bastante úteis, para concatenação, comparação e, na verdade, se assemelha muito à class String da linguagem Java fornecendo o mesmo tipo de conveniência
String stringOne = "Hello String"; // usando uma string constante
String stringOne = String('a'); // convertendo um caractere constante para uma String
Code language: C++ (cpp)
Porém, essa facilidade e flexibilidade vem com um preço relativamente alto:


Nos exemplos acima, os textos declarados como char[] e String são exatamente os mesmos, porém, a diferença em memória alocada é de 28 bytes.
Pode não parecer muito, mas considerando que essa diferença tende a aumentar quando se utiliza os métodos de manipulação das Strings, e que a memória de um Arduino Nano não ultrapassa 2Kb, essa pequena diferença pode ser crucial para um programa executar ou não devido à restrição.
Alternativas
Para evitar a alta utilização de memória com strings, existem duas alternativas possíveis:
- char[]: Para strings dinâmicas, criadas e manipuladas em tempo de execução, deve-se utilizar as char arrays.
- PROGMEM char[]: Para strings estáticas, que não mudam, como labels, informações a serem enviadas via Serial, as PROGMEM char[] são as indicadas.
Char Arrays (char[])
As 0-terminated char[] conforme desmonstrado nas imagens acima, ocupam menos memória RAM. Porém, notadamente, apesar de sua inicialização ser, em geral, simples, a sua manipulação é mais complicada:
char minha_string[] = "string inicializada";Code language: JavaScript (javascript)
Porém, a concatenação não pode ser realizada via sinal “+”. Ao invés disso, é necessário utlizar a função strcat(), como no exemplo abaixo.
char minha_string[25];
strcpy(minha_string,"string inicializada");
strcat(minha_string," - OK");Code language: JavaScript (javascript)
Para definir uma função ou método que receba a char[], deve-se utilizar char *:
void addicionar_sufixo(char* str2suffix) {
strcat(str2suffix, "...");
}
char minha_string[25];
strcpy(minha_string, "Carregando");
adicionar_sufixo(minha_string);
Code language: JavaScript (javascript)
Finalmente, é importante ter em mente que os valores a serem concatenados não podem ultrapassar o tamanho alocado para o char[].
Do contrário, o conteúdo concatenado avança além dos bytes alocados, pois o complilador não impede que isso acontença. E quando acontecer, provavelmente , vai gerar algum resultado indesejado que pode, inclusive, ser o travamento e/ou reinicialização do dispositivo.
PROGMEM char[]
As PROGMEM char[] são strings que são armazenadas não em memória RAM, mas na memória de código, que é consideravelmente maior (aproximadament 30Kbytes no Arduino Nano) e, portanto, para strings que não mudam, são ideais.
Pode-se definí-las / utilizá-las de mais de uma forma.
Utilizando a macro F():
Serial.println(F("Iniciando a aplicação"));
lcd.print(F("> Menu Principal"));Code language: PHP (php)
Esta é a forma mais simples quando se quer utilizar as strings para enviar para a serial ou para um LCD.

Neste exemplo, a memória utilizada, com o mesmo texto nas 3 strings cai para 188, ou seja, uma redução de 54 bytes se comparado com char[].
Utilizando a (__FlashStringHelper*)
const char stringOne[] PROGMEM = "Hello String";
...
Serial.println((__FlashStringHelper*)stringOne);Code language: JavaScript (javascript)
Esta é uma forma alternativa à macro F(), mas tem o mesmo resultado.
A diferença é não ser necessário escrever a string F(“Alguma Coisa”) mais de uma vez caso ela seja utilizada em diferentes lugares do código.
Nesse caso, uma constante é criada e apenas referenciada da forma acima onde for necessário.

Como o Output mostra, a quantidade de memória utilizada, se comparada com a macro F() não muda.
Utilizando a função memcpy_P()
Se for necessário carregar uma char[] de forma a manipulá-la, é possível declarar de outra forma:
const char STR_TO_INITIALIZE[] PROGMEM = "Teste de PROGMEM";
...
char temporaria[17];
memcpy_P(temporaria, &STR_TO_INITIALIZE, sizeOf(STR_TO_INITIALIZE));Code language: JavaScript (javascript)
Este código declara a string STR_TO_INITIALIZE em espaço de programa e então usa a função memcpy_P() para copiá-la para uma variavel temporária em memória RAM.
É importante utilizar a função sizeOf() para buscar o tamanho correto da string, pois além de ser mais simples, o compilador aloca um carctere adicional para o “\0”, que é adicionado como terminador da string.

Novamente, a memória utilizada permanece em 188, pois a variável tmp é alocada dentro de uma função, ou seja, no momento em que a função retornar, ela será automaticamente desalocada.
Conclusão
Deve-se ou não utilizar a classe String?
Não existe um certo ou errado para este assundo. A resposta adequada é “depende”:
- Caso se esteja utilizando um dispositivo com bastante memória RAM (como o Arduino Mega, por exemplo) e memória não é um problema, a flexibilidade da classe String pode ser bastante útil.
- Por outro lado, caso a memória seja bastante restrita e a manipulação das strings não seja complexa, deve-se utilizar as char[].
Qualquer que seja a forma mais adequada para um programa específico, os exemplos acima podem fornecem alternativas para otimizar o código.