Need to put a large 2D matrix in flash

I am using an Adafruit RP2040 as an Arduino. It has 264K of ram and 16mb of qspi flash. The matrix I want to store is too big to fit in ram, but I don't know how to put into flash. (I am not very experienced with Arduino programming). The matrix can be read only, and is populated from a file on an SD card. The file contains a large number of RGB values.

I know PROGMEM is probably the answer, but I think the matrix has to be declared as a const, in which case I can't write values to it in a loop? Any help would be greatly appreciated. Code is below;

#include "Adafruit_NeoPixel.h"
#include "SdFat.h"

#define PIXEL_PIN    6  // Digital IO pin connected to the NeoPixels.
#define PIXEL_COUNT 20  // Number of NeoPixels
#define SD_CS_PIN 23    // CS pin for SD card

Adafruit_NeoPixel strip(PIXEL_COUNT, PIXEL_PIN, NEO_GRB + NEO_KHZ800);
SdFat sd;
File32 myFile;
int Wide = 0;
const int MAX_WIDTH = 100; // Maximum width of matrix
byte matrix[360][650]; // Define matrix as a global variable
int dT = -3;
int F = 0; //starting y position to read neopixel values from matrix
int sensorPin = A0;   // select the input pin for the potentiometer

void setup() {
  Serial.begin(115200);
  while (!Serial) {
    ; // wait for serial port to connect. Needed for native USB port only
  }

  Serial.print("Initializing SD card...");
  if (!sd.begin(SD_CS_PIN)) {
    Serial.println("Initialization failed!");
    return;
  }
  Serial.println("Initialization done.");

  myFile.open("colors.txt");
  if (myFile) {
    Serial.println("colors.txt:");
    const int Width = myFile.parseInt() * 3;
    Wide = Width;
    const int Height = myFile.parseInt();
   
    Serial.print("width = :");
    Serial.println(Width);
     Serial.print("Height = :");
    Serial.println(Height);

    // Read from the file and populate matrix
    for (int i = 0; i < Height; i++) {
      for (int j = 0; j < Width; j++) {
        if (myFile.available()) {
          matrix[j][i] = myFile.parseInt(); // Read a number from the file
          Serial.print(matrix[j][i]);
          Serial.print(" ");
        }
      }
      Serial.println();
    }
    strip.begin();
    strip.show();
    myFile.close();
  } else {
    Serial.println("Error opening colors.txt");
  }
}

void loop() {
F = map(analogRead(sensorPin),0,1023,0,80);
  dT = dT + 3;
  if (dT > (Wide - 3)) dT = 0;
// Serial.print(Wide);
// Serial.print(", ");
// Serial.println(dT);
  // Loop and assign neopixel values
  for (int i = F; i < (F + PIXEL_COUNT); i++) {
    int R = matrix[dT][i];
    int G = matrix[dT+1][i];
    int B = matrix[dT+2][i];
    strip.setPixelColor(i - F,strip.Color(R ,G ,B ));
    // Serial.print(R);
    // Serial.print(", ");
    // Serial.print(G);
    // Serial.print(", ");
    // Serial.println(B);
   
  }
  strip.show();
  delay (1000/30);
}

This may be of interest

How would I assign 10,000 values from a text file to a "const byte matrix [] [n] PROGMEM" array so it is stored in flash? Some kind of char variable that has the proper format in it? Like "char content = "{{1,2,3....},{4,5,6,....}, etc }""?

const byte matrix [] [n] PROGMEM content;

Pretty confused...

Exactly like that. An editor with some sort of keyboard macro capability is useful, if you data is currently text. If they're not numbers, it'll look something like

const char font[][10] = {"abcde", "xyzzy", "012345678" };

on an RP2040, "PROGMEM" isn't even necessary (const is sufficient), but I'd keep it there anyway to make your intent clear...

Except:

Reading from an SD card is a run-time operation. You can't fill in a compile-time const array at run-time.

OK, so what can I do??

use flash through a filesystem ( LittleFS)
https://arduino-pico.readthedocs.io/en/latest/fs.html

(if the data does not change may be you don't need the SD card at all, just write the data as a file in flash and use it from there)

So hard code the numbers into the Arduino code file? I can do this by modifying the program that preps the data from a .jpg image so it produces a text file like;

{
{1,2,3,...},{2,3,4...},
{5,5,6...},{7,8,2}
}
Then copy and paste.
Is there a limit on the line length in the Arduino code file?
if PROGMEM is not needed, then can I access the data in the matrix in the normal way?

You could write it directly into a stand-alone, well formed matrix.h file and then #include "matrix.h" it and avoid the cut & paste:

const byte matrix [] [n] PROGMEM = {
{1,2,3,...},{2,3,4...},
{5,5,6...},{7,8,2},
...
};

I tried the hard code method, and that actually worked! Thanks for all your help on this one. The "const" function was enough for the RP2040 to put the matrix in flash (although I included PROGMEM), and I could read the values back in the normal way x = matrix [4] [23];

If you already have it on an SD card why bother putting it in flash? I mean, you already have it stored.

-jim lee

Yeah those MCU (like ESP32) don’t need PROGMEM, it just does nothing.

To be used, the data must be read byte-by-byte from the SD card and put somewhere. That somewhere is likely RAM (I don't know if it's possible to read from SD and write to Flash at run-time). If the application and contents of the array is such that the whole thing must be accessible during program execution, then the whole thing must be read into RAM. But:

on an ESP32 or RP2040 the flash memory is mapped into the program space so you can read it just like it's in RAM, it will be a bit slower but faster than reading from an SD card

Understood. But my point was (responding to @jimLee's quetion), if the data is stored on SD Card, it must be copied somewhere at run-time to be used as an array by the code. Can that that "somewhere" be Flash?

My suggestion was to use littleFS if this needs to happen at run time and the SD card is really necessary

OK, but littleFS (like SD) is just a file system. It's not an array. How does OP's code access the data as an array (or "Matrix", per the title of this thread)?

My solution (with your suggestions) got rid of the need for the SD card. I can't read directly from the SD during execution, because I need random access and speed. I wrote an external program in XOJO that processes the .jpg file, extracts the pixel colors, and formats the output so it is a simple cut and paste into the arduino code.

Interestingly, the first time the code runs, it takes about 5 seconds to load the numbers and get to "loop {;". The second time it is almost instantaneous, so it seems like it doesn't need to reload the matrix into flash again.

Without the "const" in front of "matrix", it goes into ram, and if I make the matrix as big as I need it, the prog fills up ram and hangs...

indeed - it's just faster than SD because in flash

if you store binary structures into a file system then accessing a structure at a given index is seeking to an easy to calculate position and read in enough bytes to fill in the structure if you want to bring that in RAM