Hello Community,
Hope that someone can help me with my problem moving all static text into Flash memory.
The board I use is a Mega 2560 with Ethernet shield, a real time clock DS3231 and a self made shield for all needed I/O ports, relays and LEDs.
My project is an alarm system based on a state machine. External triggers and events can change the state, and with it the output pins. Every state change is written into a log file. The text for each line is compiled from real time, date, IP address of the requester – if the trigger was an Ethernet request, and the actual message.
In my first attempt, I created the strings “hard coded” in the code – which quickly brought the SRAM down to its knees.
Example:
sprintf (tmpString, "%s %s %s", Zustand WechselToDisarmed: RFID ", sensorText, " hat System Armed");
The second attempt was to create the text from separate modules, where mostly every word was a separate array of chars. Now the text is easily compiled via sprintf, using the char * .
Example:
sprintf (tmpString, "%s %s: %s %s %s %s %s",s_Zustand, s_WechselToDisarmed, s_RFID, sensorText, s_hat,s_System, s_Armed);
Now I have most of the text only once in the memory, but with the number of different messages, the SRAM is still used too much. To remedy this, I tried to put the text into the Flash memory. The first attempt was to use F(). This works great on Serial.print and client.print, but apparently not with sprintf and strcat (See Option 1 in my code).
Option 2 and option 3 use PROGMEM. Option 2 would be my preferred solution. The problem is that it only seems to work with a separate buffer per parameter. My problem is that the number of parameters and their length vary with the text. That means creating many buffers would put a high load on the SRAM again – which I try to avoid.
Option 3 works, but it unnecessarily inflates the code and makes the code virtually unreadable.
Has anybody an idea how to solve this problem by using Option 2? For weeks now I’ve done extensive research on the Forum, but I wasn’t able to find a solution or an idea for how to tackle this problem.
Thanks in advance for your help.
Axel
#include <avr/pgmspace.h>
const byte d_Armed = 0; const char s_Armed[] PROGMEM = "Armed";
const byte d_WechselToDisarmed = 1; const char s_WechselToDisarmed[] PROGMEM = "WechselToDisarmed";
const byte d_RFID = 2; const char s_RFID[] PROGMEM = "RFID";
const byte d_Zustand = 3; const char s_Zustand[] PROGMEM = "Zustand";
const byte d_System = 4; const char s_System[] PROGMEM = "System";
const byte d_hat = 5; const char s_hat[] PROGMEM = "hat";
// Then set up a table to refer to your strings.
const char* const string_table[] PROGMEM = { s_Armed,
s_WechselToDisarmed,
s_Zustand,
s_RFID,
s_System,
s_hat,
};
char buffer[50]; // make sure this is large enough for the largest string it must hold
#define _Stringlaenge 255
char sensorText[] = "Sensor 1"; // In the real program this text is assigned by the trigger detector
char tmpString[_Stringlaenge] = "";
// ######################## Function getString ################################################################################
char *get_string (int i_element)
{
strcpy_P(buffer, (char*)pgm_read_word(&(string_table[i_element]))); // Necessary casts and dereferencing, just copy.
return (buffer);
} // End Function get_string
// ######################## Function Setup ####################################################################################
void setup()
{
Serial.begin(9600);
while(!Serial);
Serial.println("Setup done");
} // End Function Setup
// ######################## Loop Forever ######################################################################################
void loop()
{
// ########################################## Option 1 with F() ###########################################################
// The best solution, unfortunately F() doesn't work in this context. Sprintf deliveres garbage and for strcat, the compiler reports errors
// Expected Output: "Zustand WechselToDisarmed: RFID Sensor 1 hat System Armed"
sprintf (tmpString, "%s", F("Zustand WechselToDisarmed: RFID ")); // This line delivers garbage
strcat (tmpString,sensorText); // This line works
// strcat (tmpString, F(" hat System Armed")); // Compiler error
Serial.println (tmpString);
// Output: Sensor 1
// ########################################## Option 2 with PROGMEM and sprintf ###########################################
// My hope was that sprintf processes the parameters one by one, running the function get_string and then copying the result. Apparently
// all the function calls are called before the copies take place. Therefore sprintf copies 6 times the last string "Armed".
// Expected Output: "Zustand WechselToDisarmed: RFID Sensor 1 hat System Armed"
sprintf (tmpString, "%s %s: %s %s %s %s %s", get_string(d_Zustand),get_string(d_WechselToDisarmed),get_string(d_RFID),
sensorText,
get_string(d_hat),get_string(d_System),get_string(d_Armed));
Serial.println (tmpString);
// Output: Armed Armed: Armed Sensor 1 Armed Armed Armed
// ########################################## Option 3 with PROGMEM and seperate strcat ####################################
// This version works, however is way to much code for the result
// Expected Output: "Zustand WechselToDisarmed: RFID Sensor 1 hat System Armed"
tmpString[0] = '�'; // Delete old content of string
strcat (tmpString,get_string(d_Zustand));
strcat (tmpString," ");
strcat (tmpString,get_string(d_WechselToDisarmed));
strcat (tmpString,": ");
strcat (tmpString,get_string(d_RFID));
strcat (tmpString," ");
strcat (tmpString,sensorText);
strcat (tmpString," ");
strcat (tmpString,get_string(d_hat));
strcat (tmpString," ");
strcat (tmpString,get_string(d_System));
strcat (tmpString," ");
strcat (tmpString,get_string(d_Armed));
Serial.println (tmpString); // Output as expected
while (1); // Stop program here
} // End Loop