Continuously Add to Array in Loop

Hi,

I need to send commands to an LCD. I need to send multiple commands as the program loops.

In order for the screen to work properly - it must be cleared each time through the loop.

So I need to have the LCD commands put in an array then sent to the LCD. Next time through the loop, add new commands to the array and send the entire array to LCD (previous and current commands).

In short - I need to continuously add to an array while the program loops.

I'm using an arduino Due and the NHD-5.0-800480FT-CTXL-T from New Haven Display.

I've attached the portion of my code which shows what I have done so far. It will put the commands in an array and I can call out a command using the pointers.

I don't know how to add to the array as the program loops or send the entire array to the LCD.

Any help you can provide is greatly appreciated. Thanks

screen_array.ino (5.33 KB)

Seems you will keep adding to your list of commands. the arduino Due is a bit richer in memory than its UNO or MEGA counterparts but still "only" has 96 KB (two banks: 64KB and 32KB) of SRAM.

--> how many "commands" will you need to add to your array ? is there a finite number ? are commands about the same length each ?

if you know you won't have more than 128 commands and each command is like 64 characters max then you could decide to pre-allocate 128 x 64 = 8KB for your list of commands.

const uint8_t maxNbOfCommand = 128;
const uint8_t maxLengthOfCommand = 63; // number of effective chars, you'll need a trailing NULL
char commands[maxNbOfCommand][maxLengthOfCommand+1];
uint8_t commandIndex = 0;

then it's a matter of getting the commands from wherever they come and storing them (strcpy()) into the buffer

char aCommand[maxLengthOfCommand+1];
getCommand(aCommand); // whatever you do to acquire a new command
strcpy(commands[commandIndex], aCommand); // memorize the command into the array at commandIndex position
commandIndex++; // go to the next position
if (commandIndex >= maxNbOfCommand) <<< WE HAVE NO ROOM LEFT >>>

otherwise you can play with malloc, free, realloc and pointers and build a dynamic array at run time. But watch your memory usage.

Thanks for the reply,

I'm not sure what the max command number is - would vary each time program is run. Each command is about 40-60 characters. See below.

"Ft_App_WrCoCmd_Buffer(phost, BEGIN(RECTS))",
"Ft_App_WrCoCmd_Buffer(phost, COLOR_RGB(255, 255, 255))",
"Ft_App_WrCoCmd_Buffer(phost, VERTEX2F(screenX, screenY))",
"Ft_App_WrCoCmd_Buffer(phost, VERTEX2F(screenX2, screenY2))",
"Ft_App_WrCoCmd_Buffer(phost, END())"};

Do I need to use malloc, free, realloc etc... because the commands are not the same length?

Can I just allocate the majority of the Due's SRAM to these commands?

if you can go with the option of waisting some bytes in the char array, then that's the easiest way to code.

malloc will help you allocate the right amount of memory for each command and if you never free stuff then you don't risk fragmentation.

in both cases I would keep an array with a max size defined to hold the entries to the commands as you don't have unlimited memory anyway.

if you go for an array of pointersconst char* commands[maxNbOfCommand];then you would acquire a command and use strdup() to add it into the array. (will hide alloc() for you).

Okay - I will try to take this approach using malloc. I should never have to free stuff. Thank you for your help

Okay sorry but I am struggling with this. I tried to implement your advice in the first post - just because it was the most familiar to me. I decided to just run some test codes to see if I could get it working. I got it to run and it would return commands (printing through serial) but it would also output a lot of garbage.

const uint8_t maxNbofCommand = 20;
const uint8_t maxLengthofCommand = 63;
char commands[maxNbofCommand][maxLengthofCommand + 1];
uint8_t commandIndex = 0;


void setup() {
  // put your setup code here, to run once:
    Serial.begin(9600);
}

void loop() {
  // put your main code here, to run repeatedly:

if (millis() <= 5000){
         
        strcpy(commands[commandIndex], "Ft_App_WrCoCmd_Buffer(phost, BEGIN(RECTS))");
        commandIndex++;
        strcpy(commands[commandIndex], "Ft_App_WrCoCmd_Buffer(phost, COLOR_RGB(0, 0, 0))");
        commandIndex++;
        strcpy(commands[commandIndex], "Ft_App_WrCoCmd_Buffer(phost, VERTEX2F(screenX, screenY))");
        commandIndex++;
        strcpy(commands[commandIndex], "Ft_App_WrCoCmd_Buffer(phost, VERTEX2F(screenX2, screenY2))");
        commandIndex++;
        strcpy(commands[commandIndex], "Ft_App_WrCoCmd_Buffer(phost, END())");
        commandIndex++;

        for(int i = 0; i < 100; i++)
        {
          Serial.println(commands[i]);
        }

}
if (millis() >= 5000 && millis() <= 10000) {
  
        strcpy(commands[commandIndex], "Ft_App_WrCoCmd_Buffer(phost, BEGIN(RECTS))");
        commandIndex++;
        strcpy(commands[commandIndex], "Ft_App_WrCoCmd_Buffer(phost, COLOR_RGB(255, 255, 255))");
        commandIndex++;
        strcpy(commands[commandIndex], "Ft_App_WrCoCmd_Buffer(phost, VERTEX2F(screenX, screenY))");
        commandIndex++;
        strcpy(commands[commandIndex], "Ft_App_WrCoCmd_Buffer(phost, VERTEX2F(screenX2, screenY2))");
        commandIndex++;
        strcpy(commands[commandIndex], "Ft_App_WrCoCmd_Buffer(phost, END())");
        commandIndex++;
}
  for(int i = 0; i < 100; i++)
        {
          Serial.println(commands[i]);
        }
}

So I moved on to trying to use strdup() and malloc but I really am not familiar with these functions.

From my little understanding - strdup() duplicates a string and uses malloc to allocate memory for the string copy.

I'm not sure how I use strdup() to add the command to an array. This is the little test code I wrote so far.

const uint8_t maxNbofCommand = 256;
const char*commands[maxNbofCommand];
String rect = "Ft_App_WrCoCmd_Buffer(phost, BEGIN(RECTS))";

void setup() {
  // put your setup code here, to run once:
  Serial.begin(9600);

}
//void loop() {

char*strdup(const char *rect)
{
  // Compute the required space.
  size_t len_rect = strlen(rect) + 1;

  // Allocate that amount of space, and check for failure.
  char *mem_rect = malloc(len_rect);
  if (!mem_rect) return NULL;

  // Copy the data and return.
  return memcpy(mem_rect, rect, len_rect);
  Serial.println(rect);
}
//}
void loop() {}

Any further explanation on how to use strdup() to add the commands to the array would be greatly appreciated.

My end result is hopefully something like the code below where the commands for certain conditions are added to the array each time through the loop and then the array of commands is sent to the LCD.

char*screenArray = {};
int elementCount = sizeof(screenArray) / sizeof(char);

int previousX = 0;
int previousY = 0;

int screenX = 0;
int screenY = 0;
int screenX2 = 0;
int screenY2 = 0;


void setup() {
  // put your setup code here, to run once:
  Serial.begin(115200);
}

void loop() {
  DisplayData();
  showScreen();
}

void DisplayData() {

  screenX = Xaverage * Xratio;
  screenY = 7680 - (Yaverage * Yratio);
  Serial.println(screenX);
  Serial.println(screenY);

  if (y >= 0 && screenY <= 7680 && x >= 0 && screenX <= 12800) {

    if (previousX != screenX && previousY != screenY) {

      previousX = screenX;
      previousY = screenY;

      screenX2 = screenX + 480;
      screenY2 = screenY + 480;

      Ft_Gpu_CoCmd_Dlstart(phost);
      Ft_App_WrCoCmd_Buffer(phost, CLEAR(1, 1, 1));

      if (average > 2000) {

        char*screenArray[] = {
          "Ft_App_WrCoCmd_Buffer(phost, BEGIN(RECTS))",
          "Ft_App_WrCoCmd_Buffer(phost, COLOR_RGB(255, 255, 255))",
          "Ft_App_WrCoCmd_Buffer(phost, VERTEX2F(screenX, screenY))",
          "Ft_App_WrCoCmd_Buffer(phost, VERTEX2F(screenX2, screenY2))",
          "Ft_App_WrCoCmd_Buffer(phost, END())"
        };
      }

      if (1900 < average && average <= 2000) {

        char*screenArray[] = {
          "Ft_App_WrCoCmd_Buffer(phost, BEGIN(RECTS))",
          "Ft_App_WrCoCmd_Buffer(phost, COLOR_RGB(255, 255, 25))",
          "Ft_App_WrCoCmd_Buffer(phost, VERTEX2F(screenX, screenY))",
          "Ft_App_WrCoCmd_Buffer(phost, VERTEX2F(screenX2, screenY2))",
          "Ft_App_WrCoCmd_Buffer(phost, END())"
        };
      }

      if (1600 < average && average <= 1900 ) {

        char*screenArray[] = {
          "Ft_App_WrCoCmd_Buffer(phost, BEGIN(RECTS))",
          "Ft_App_WrCoCmd_Buffer(phost, COLOR_RGB(255, 125, 0))",
          "Ft_App_WrCoCmd_Buffer(phost, VERTEX2F(screenX, screenY))",
          "Ft_App_WrCoCmd_Buffer(phost, VERTEX2F(screenX2, screenY2))",
          "Ft_App_WrCoCmd_Buffer(phost, END())"
        };
      }

      if (1200 < average && average <= 1600) {

        char*screenArray[] = {
          "Ft_App_WrCoCmd_Buffer(phost, BEGIN(RECTS))",
          "Ft_App_WrCoCmd_Buffer(phost, COLOR_RGB(205, 0, 0))",
          "Ft_App_WrCoCmd_Buffer(phost, VERTEX2F(screenX, screenY))",
          "Ft_App_WrCoCmd_Buffer(phost, VERTEX2F(screenX2, screenY2))",
          "Ft_App_WrCoCmd_Buffer(phost, END())"
        };
      }

      if (900 < average && average <= 1200) {

        char*screenArray[] = {
          "Ft_App_WrCoCmd_Buffer(phost, BEGIN(RECTS))",
          "Ft_App_WrCoCmd_Buffer(phost, COLOR_RGB(50, 0, 100))",
          "Ft_App_WrCoCmd_Buffer(phost, VERTEX2F(screenX, screenY))",
          "Ft_App_WrCoCmd_Buffer(phost, VERTEX2F(screenX2, screenY2))",
          "Ft_App_WrCoCmd_Buffer(phost, END())"
        };
      }

      if (200 < average && average <= 900) {

        char*screenArray[] = {
          "Ft_App_WrCoCmd_Buffer(phost, BEGIN(RECTS))",
          "Ft_App_WrCoCmd_Buffer(phost, COLOR_RGB(0, 0, 50))",
          "Ft_App_WrCoCmd_Buffer(phost, VERTEX2F(screenX, screenY))",
          "Ft_App_WrCoCmd_Buffer(phost, VERTEX2F(screenX2, screenY2))",
          "Ft_App_WrCoCmd_Buffer(phost, END())"
        };
      }

      if (average <= 200) {

        char*screenArray[] = {
          "Ft_App_WrCoCmd_Buffer(phost, BEGIN(RECTS))",
          "Ft_App_WrCoCmd_Buffer(phost, COLOR_RGB(0, 0, 0))",
          "Ft_App_WrCoCmd_Buffer(phost, VERTEX2F(screenX, screenY))",
          "Ft_App_WrCoCmd_Buffer(phost, VERTEX2F(screenX2, screenY2))",
          "Ft_App_WrCoCmd_Buffer(phost, END())"
        };
      }
    }
  }

}

void showScreen() {

  for (int index = 0; index < elementCount; index++)
  {
    Ft_App_WrCoCmd_Buffer(phost, screenArray[0]);
    Ft_App_WrCoCmd_Buffer(phost, screenArray[1]);
    Ft_App_WrCoCmd_Buffer(phost, screenArray[2]);
    Ft_App_WrCoCmd_Buffer(phost, screenArray[3]);
    Ft_App_WrCoCmd_Buffer(phost, screenArray[4]);
    Ft_App_WrCoCmd_Buffer(phost, DISPLAY());
    Ft_Gpu_CoCmd_Swap(phost);
    Ft_App_Flush_Co_Buffer(phost);
    Ft_Gpu_Hal_WaitCmdfifo_empty(phost);
  }

Thanks again

When you do

 if (millis() <= 5000){

this will be executed zillion times in the first 5 seconds and you will over run your array space. Same for the other after

You need to add you instructions only once wether you use strdup() or strcpy() and endure you don’t add after the last possible entry

Have a Boolean variable like this

void loop()
{
  static bool batch1Done = false; 
  static bool batch2Done = false; 

  if (!batch1Done && (millis() <= 5000)) {
    // add to array
    ...
    // mark job done
    batch1Done = true;
  } else
  if (!batch2Done && (millis() <= 10000)) {
    // add to array
    ...
    // mark job done
    batch2Done = true;
  } else
   ...
}

An alternative if you know already all the commands is to preload the array and based on time only activate to position N in the array and ignore the rest. Where are the commands really coming from?

The commands come from condtions met by sensors connected to the Due. So it gets screenX and screenY from a sensor connected to serial (Rx,Tx). The average variable is obtained using an analog sensor connected to A0.

I was just using millis() to test putting commands in an array - instead of trying to run the whole program.

The data comes in pretty quick with the serial operating at 115200 baud rate.

Ok your way of adding data was ok - just that you were overflowing the buffer

Okay - I'll give it a shot in the actual program. Thank you for all your help - appreciate it

kduggs33:
Okay - I'll give it a shot in the actual program. Thank you for all your help - appreciate it

here is an example with both versions

#define PRE_ALLOCATE_MEMORY true // set it to false if you want to use dynamic memory allocation through strdup()

const uint8_t maxNbofCommand = 10;
const uint8_t maxLengthofCommand = 63;

char serialBuffer[maxLengthofCommand + 1];  // +1 as we want to add a trailing '\0' to terminate a cSrting
uint8_t commandIndex = 0;

#if PRE_ALLOCATE_MEMORY
char listOfCommands[maxNbofCommand][maxLengthofCommand + 1];// +1 as we want to add a trailing '\0' to terminate a cSrting
#else
char* listOfCommands[maxNbofCommand];
#endif


boolean getCommand()
{
  static byte currentIndex = 0;
  boolean commandReady = false;

  while (!commandReady) {
    int c = Serial.read();
    if (c != -1) { // -1 means nothing to read
      switch (c) {
        case '\n':         // end marker --> command complete?
          serialBuffer[currentIndex] = '\0'; // terminate the c-string
          currentIndex = 0; // get ready for next time
          commandReady = true;
          break;

        case '\r':         // ignore CR
          break;

        default:         // otherwise if we have room left, store the incoming char
          if (currentIndex < maxLengthofCommand) serialBuffer[currentIndex++] = (char) c;
          break;
      }
    } else break;
  }
  return commandReady;
}

bool addCommandToList(const char* aCommand)
{
  bool commandAdded = false;
  if (commandIndex < maxNbofCommand) {
#if PRE_ALLOCATE_MEMORY
    strncpy(listOfCommands[commandIndex], aCommand, maxLengthofCommand); // http://www.cplusplus.com/reference/cstring/strncpy/
    listOfCommands[commandIndex][maxLengthofCommand] = '\0'; // to be sure it's properly terminated
    commandIndex++;
    commandAdded = true;
#else
    listOfCommands[commandIndex] = strdup(aCommand); // https://en.cppreference.com/w/c/experimental/dynamic/strdup
    if (listOfCommands[commandIndex] == NULL) {
      Serial.println(F("ERROR: SRAM IS FULL"));
    } else {
      commandIndex++;
      commandAdded = true;
    }
#endif
  } else {
    Serial.println(F("ERROR: COMMAND BUFFER FULL"));
  }
  return commandAdded;
}

void printListOfCommands()
{
  for (byte i = 0; i < commandIndex; i++) {
    Serial.print(i);
    Serial.write('\t');
    Serial.println(listOfCommands[i]);
  }
  Serial.println(F("--------------------------"));
}

void setup()
{
  Serial.begin(115200);
#if PRE_ALLOCATE_MEMORY
  Serial.println(F("STATIC ALLOCATION"));
#else
  Serial.println(F("DYNAMIC ALLOCATION"));
#endif
  Serial.println(F("Ready"));
}

void loop()
{
  if (getCommand())
    if (addCommandToList(serialBuffer)) printListOfCommands();
}

this code should read from the Serial monitor (ensure it is set at 115200 bauds and sending CR+LF) and add whatever line you type to a buffer. there are two versions that you can control by defining on the first line PRE_ALLOCATE_MEMORY to true or false

if you pre-allocate the memory, then no dynamic behavior happens and all the bytes are reserved at compile time for your storage, and you likely loose some SRAM space for each command as they won't fill up the buffer. The second version is dynamic, so we keep a list of pointers and dynamically allocate only the right amount of bytes to keep a copy of the full command.

Once we add a new command, the list is printed out to the Serial Console with an index. (I limited the number of commands to 10 to see what happens if you try to store a 11th one.)

give it a try.