Comparing array against PROGMEM arrays

Hi Programmers,
Here I have a working sample sketch. I have a few arrays in PROGMEM, and I have an array in RAM that I want to compare against each of the PROGMEM arrays and see which one matches.

Can you help me understand is it possible to pass the name of a PROGMEM array to my compare function areSame, so I can move my while loop into the areSame function? I want to move the while loop that brings the PROGMEM array into the tempArray, so I am not repeating that code over and over for each compare. If I have a way to pass which PROGMEM array I want to compare to inside the function, I could even eliminate using the tempArray.

/*
   Compare arrays
   https://forum.arduino.cc/t/solved-comparing-whole-arrays/247786/17
   Added PROGMEM storage of arrays
*/

const PROGMEM uint16_t pat11[] = {1, 2, 2, 25, 3, 1, 4, 14, 1, 4, 2, 20, 3, 2, 4, 14, 1, 4, 2, 20, 3, 2, 4, 100, 13, 0, 0, 0, 0, 0};
const PROGMEM uint16_t pat12[] = {1, 2, 2, 85, 3, 2, 4, 15, 1, 3, 2, 17, 3, 1, 4, 100, 13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
const PROGMEM uint16_t pat13[] = {1, 3, 2, 69, 3, 1, 4, 23, 1, 1, 2, 24, 3, 1, 4, 16, 1, 1, 2, 28, 3, 1, 4, 100, 13, 0, 0, 0, 0, 0};
const PROGMEM uint16_t pat14[] = {1, 2, 2, 23, 3, 1, 4, 20, 1, 1, 2, 72, 3, 1, 4, 20, 1, 1, 2, 20, 3, 1, 4, 100, 13, 0, 0, 0, 0, 0};
const PROGMEM uint16_t pat15[] = {1, 29, 2, 100, 13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
const PROGMEM uint16_t pat16[] = {1, 1, 2, 100, 13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
const PROGMEM uint16_t pat17[] = {1, 4, 2, 15, 3, 2, 4, 21, 1, 1, 2, 100, 13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
const PROGMEM uint16_t pat18[] = {1, 3, 2, 20, 3, 1, 4, 17, 1, 1, 2, 17, 3, 1, 4, 26, 1, 2, 2, 100, 13, 0, 0, 0, 0, 0, 0, 0, 0, 0};
const PROGMEM uint16_t pat19[] = {1, 2, 2, 25, 3, 1, 4, 100, 13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};

unsigned int mycode[30] = {1, 29, 2, 100, 13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
unsigned int tempArray[30];

void setup() {
  Serial.begin(115200);
}

boolean areSame(int arrayA[], int arrayB[]) {
  // compare arrays
  boolean same = true;
  int i = 0;
  while (i < 30 && same && ! (arrayA[i] == 0 && arrayB[i] == 0)) {
    same = arrayA[i] == arrayB[i];
    i++;
  }
  return same;
}

void printFormattedArray(unsigned int arrayA[]) {
  // print array elements up to the end or the first 0
  Serial.print("{");
  int i = 0;
  while (i < 30) {
    Serial.print(arrayA[i]);
    i++;
    if (i == 30)
      Serial.println("}");
    else
      Serial.print(",");
  }
}



void loop() {
  Serial.println("begin");
  Serial.println("model array");
  printFormattedArray(mycode);
  Serial.println("");
  int i = 0;
  while (i < 30) {
    tempArray[i] = pgm_read_word_near(pat11 + i);
    i++;
  }
  printFormattedArray(tempArray);
  if (areSame(tempArray,mycode)) {
    Serial.println("match=yes");
  }
  else {
    Serial.println("match=no");
  }
  i = 0;
  while (i < 30) {
    tempArray[i] = pgm_read_word_near(pat12 + i);
    i++;
  }
  printFormattedArray(tempArray);
  if (areSame(tempArray,mycode)) {
    Serial.println("match=yes");
  }
  else {
    Serial.println("match=no");
  }
  i = 0;
  while (i < 30) {
    tempArray[i] = pgm_read_word_near(pat13 + i);
    i++;
  }
  printFormattedArray(tempArray);
  if (areSame(tempArray,mycode)) {
    Serial.println("match=yes");
  }
  else {
    Serial.println("match=no");
  }
  i = 0;
  while (i < 30) {
    tempArray[i] = pgm_read_word_near(pat14 + i);
    i++;
  }
  printFormattedArray(tempArray);
  if (areSame(tempArray,mycode)) {
    Serial.println("match=yes");
  }
  else {
    Serial.println("match=no");
  }
  i = 0;
  while (i < 30) {
    tempArray[i] = pgm_read_word_near(pat15 + i);
    i++;
  }
  printFormattedArray(tempArray);
  if (areSame(tempArray,mycode)) {
    Serial.println("match=yes");
  }
  else {
    Serial.println("match=no");
  }
  i = 0;
  while (i < 30) {
    tempArray[i] = pgm_read_word_near(pat16 + i);
    i++;
  }
  printFormattedArray(tempArray);
  if (areSame(tempArray,mycode)) {
    Serial.println("match=yes");
  }
  else {
    Serial.println("match=no");
  }
  i = 0;
  while (i < 30) {
    tempArray[i] = pgm_read_word_near(pat17 + i);
    i++;
  }
  printFormattedArray(tempArray);
  if (areSame(tempArray,mycode)) {
    Serial.println("match=yes");
  }
  else {
    Serial.println("match=no");
  }
  i = 0;
  while (i < 30) {
    tempArray[i] = pgm_read_word_near(pat18 + i);
    i++;
  }
  printFormattedArray(tempArray);
  if (areSame(tempArray,mycode)) {
    Serial.println("match=yes");
  }
  else {
    Serial.println("match=no");
  }
  i = 0;
  while (i < 30) {
    tempArray[i] = pgm_read_word_near(pat19 + i);
    i++;
  }
  printFormattedArray(tempArray);
  if (areSame(tempArray,mycode)) {
    Serial.println("match=yes");
  }
  else {
    Serial.println("match=no");
  }
  while (true) {}
}

You should find this thread useful: passing PROGMEM arrays to a function [SOLVED]

There really is no difference in how you pass the PROGMEM array to the function, although you do need to specify that the function parameter is const. The function itself needs to handle the PROGMEM access. A slight complication is if you want a function that can handle either an array in PROGMEM or an array in RAM, because the compiler has no way to differentiate between the two. A technique sometimes used is to append _P to the function name to specify it is the PROGMEM version, and have a separate function for use with RAM.


boolean areSame_P(const unsigned int arrayA[], const unsigned int arrayB[]) {
  // compare arrays
  boolean same = true;
  int i = 0;
  while (i < 30 && same && ! (pgm_read_word(&arrayA[i]) == 0 && arrayB[i] == 0)) {
    same = pgm_read_word(&arrayA[i]) == arrayB[i];
    i++;
  }
  return same;
}

If you are passing a char array, there is a pre-defined __FlashStringHelper* so that the compiler can differentiate between a char* to PROGMEM and a char* to RAM.

Would memcmp_P() work ? I think the first parameter is to ram and the second parameter is to PROGMEM, but I can not find any documentation on that.

The memcmp_P() function seems the right function for the job.
I updated the sketch, but this is just quick and dirty to prove the concept. I hope that someone else can make it better/modern with "auto" and overloading :wink:

// Testing the memcmp_P() function.

#define PATTERN_SIZE 30
#define LIST_SIZE 9

const PROGMEM uint16_t pat11[] = {1, 2, 2, 25, 3, 1, 4, 14, 1, 4, 2, 20, 3, 2, 4, 14, 1, 4, 2, 20, 3, 2, 4, 100, 13, 0, 0, 0, 0, 0};
const PROGMEM uint16_t pat12[] = {1, 2, 2, 85, 3, 2, 4, 15, 1, 3, 2, 17, 3, 1, 4, 100, 13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
const PROGMEM uint16_t pat13[] = {1, 3, 2, 69, 3, 1, 4, 23, 1, 1, 2, 24, 3, 1, 4, 16, 1, 1, 2, 28, 3, 1, 4, 100, 13, 0, 0, 0, 0, 0};
const PROGMEM uint16_t pat14[] = {1, 2, 2, 23, 3, 1, 4, 20, 1, 1, 2, 72, 3, 1, 4, 20, 1, 1, 2, 20, 3, 1, 4, 100, 13, 0, 0, 0, 0, 0};
const PROGMEM uint16_t pat15[] = {1, 29, 2, 100, 13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
const PROGMEM uint16_t pat16[] = {1, 1, 2, 100, 13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
const PROGMEM uint16_t pat17[] = {1, 4, 2, 15, 3, 2, 4, 21, 1, 1, 2, 100, 13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
const PROGMEM uint16_t pat18[] = {1, 3, 2, 20, 3, 1, 4, 17, 1, 1, 2, 17, 3, 1, 4, 26, 1, 2, 2, 100, 13, 0, 0, 0, 0, 0, 0, 0, 0, 0};
const PROGMEM uint16_t pat19[] = {1, 2, 2, 25, 3, 1, 4, 100, 13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};

const uint16_t *const pattern_table[LIST_SIZE] PROGMEM = { 
  pat11,
  pat12,
  pat13,
  pat14,
  pat15,
  pat16,
  pat17,
  pat18,
  pat19,
};

unsigned int mycode[30] = {1, 29, 2, 100, 13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};

void setup() {
  Serial.begin(115200);

  Serial.println("begin");
  Serial.println("model array");
  printFormattedArray(mycode);          // the array in ram
  Serial.println();                     // println without parameter will print \r\n

  for (int i = 0; i < LIST_SIZE; i++) {
    const unsigned int *pPattern = pgm_read_word(&(pattern_table[i]));
    printFormattedArray_P(pPattern);
    if (memcmp_P(mycode, pPattern, sizeof(mycode)) == 0) {   // returns zero if equal
      Serial.println("match=yes");
    }
    else {
      Serial.println("match=no");
    }
  }
}

void loop() {
}

void printFormattedArray(const unsigned int arrayA[]) {
  // print array elements up to the end or the first 0
  Serial.print("{");
  int i = 0;
  while (i < 30) {
    Serial.print(arrayA[i]);
    i++;
    if (i == 30)
      Serial.println("}");
    else
      Serial.print(",");
  }
}

void printFormattedArray_P(const unsigned int *p) {
  // print array elements up to the end or the first 0
  Serial.print("{");
  int i = 0;
  while (i < 30) {
    Serial.print(pgm_read_word_near(p + i));
    i++;
    if (i == 30)
      Serial.println("}");
    else
      Serial.print(",");
  }
}

This sketch in Wokwi:

See https://www.nongnu.org/avr-libc/user-manual/group__avr__pgmspace.html

If all the patterns are the same length you can use a two-dimensional array instead of an array of pointers to separate arrays.

1 Like

Thank you all. The responses are helpful and interesting, and I will have no trouble cleaning up my sketch.