small array gets corrupted when put in flash PROGMEM

I have implemented a sine wave oscillator by short look-up table (257 values). I want to store the table in program memory so I can save RAM. The code works perfectly until I declare the array as PROGMEM, in which case it gets strangely corrupted. Can someone please help me figure out what I’m doing wrong?

before:
static const uint8_t LUT[TABLE_SIZE+1];

program output:

0, 0, 0, 0, 1, 1, 1, 2, 2, 3, 4, 5, 6, 6, 8, 9, 10, 11, 12, 14, 15, 17, 18, 20, 22, 23, 25, 27, 29, 31, 33, 35, 38, 40, 42, 45, 47, 49, 52, 54, 57, 60, 62, 65, 68, 71, 73, 76, 79, 82, 85, 88, 91, 94, 97, 100, 103, 106, 109, 113, 116, 119, 122, 125, 128, 131, 135, 138, 141, 144, 147, 150, 153, 156, 159, 162, 165, 168, 171, 174, 177, 180, 183, 186, 189, 191, 194, 197, 199, 202, 204, 207, 209, 212, 214, 216, 218, 221, 223, 225, 227, 229, 231, 232, 234, 236, 238, 239, 241, 242, 243, 245, 246, 247, 248, 249, 250, 251, 252, 252, 253, 253, 254, 254, 255, 255, 255, 255, 255, 255, 255, 255, 254, 254, 253, 253, 252, 252, 251, 250, 249, 248, 247, 246, 245, 243, 242, 241, 239, 238, 236, 234, 232, 231, 229, 227, 225, 223, 221, 218, 216, 214, 212, 209, 207, 204, 202, 199, 197, 194, 191, 189, 186, 183, 180, 177, 174, 171, 168, 165, 162, 159, 156, 153, 150, 147, 144, 141, 138, 135, 131, 128, 125, 122, 119, 116, 113, 109, 106, 103, 100, 97, 94, 91, 88, 85, 82, 79, 76, 73, 71, 68, 65, 62, 60, 57, 54, 52, 49, 47, 45, 42, 40, 38, 35, 33, 31, 29, 27, 25, 23, 22, 20, 18, 17, 15, 14, 12, 11, 10, 9, 8, 6, 6, 5, 4, 3, 2, 2, 1, 1, 1, 0, 0, 0, 0,

after:
static const PROGMEM uint8_t LUT[TABLE_SIZE+1];

program output with corrupted values ;(

0, 0, 184, 0, 0, 0, 1, 2, 0, 184, 184, 184, 184, 184, 184, 184, 0, 0, 135, 0, 0, 184, 0, 0, 0, 10, 0, 184, 94, 1, 0, 0, 231, 3, 0, 0, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 1, 4, 239, 0, 0, 184, 0, 184, 0, 248, 254, 255, 0, 0, 184, 184, 2, 184, 6, 184, 207, 0, 0, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 33, 184, 184, 184, 184, 184, 184, 184, 0, 0, 0, 0, 128, 1, 224, 0, 13, 1, 205, 1, 62, 1, 28, 1, 48, 1, 13, 10, 0, 115, 116, 97, 114, 116, 105, 110, 103, 46, 46, 46, 0, 84, 105, 109, 101, 114, 49, 32, 64, 32, 0, 32, 72, 122, 59, 32, 79, 67, 82, 49, 65, 58, 32, 0, 60, 84, 65, 66, 76, 69, 62, 0, 44, 32, 0, 60, 47, 84, 65, 66, 76, 69, 62, 0, 115, 105, 110, 101, 58, 32, 0, 0, 2, 4, 0, 0, 117, 45, 4, 0, 0, 4, 1, 0, 0, 232, 3, 0, 0, 0, 0, 0,
#include <avr/pgmspace.h>

class SineOsc {
protected:
  static const size_t TABLE_SIZE = 256;
  static const uint8_t LUT[TABLE_SIZE+1];

public:
  SineOsc() {;}
  
  void readTable() {
    Serial.print("<TABLE>\n");
    for(int i=0; i< TABLE_SIZE; i++) {
        Serial.print(LUT[i]);
        Serial.print(", ");
    }
    Serial.print("\n</TABLE>\n");
  }

};

const uint8_t SineOsc::LUT[TABLE_SIZE+1] = {
  0, 0, 0, 0, 1, 1, 1, 2, 2, 3, 4, 5, 6, 6, 8, 9, 10, 11, 12, 14, 15, 17, 18, 20, 22, 23, 25, 27, 29, 31, 33, 35, 38, 40, 42, 45, 47, 49, 52, 54, 57, 60, 62, 65, 68, 71, 73, 76, 79, 82, 85, 88, 91, 94, 97, 100, 103, 106, 109, 113, 116, 119, 122, 125, 128, 131, 135, 138, 141, 144, 147, 150, 153, 156, 159, 162, 165, 168, 171, 174, 177, 180, 183, 186, 189, 191, 194, 197, 199, 202, 204, 207, 209, 212, 214, 216, 218, 221, 223, 225, 227, 229, 231, 232, 234, 236, 238, 239, 241, 242, 243, 245, 246, 247, 248, 249, 250, 251, 252, 252, 253, 253, 254, 254, 255, 255, 255, 255, 255, 255, 255, 255, 254, 254, 253, 253, 252, 252, 251, 250, 249, 248, 247, 246, 245, 243, 242, 241, 239, 238, 236, 234, 232, 231, 229, 227, 225, 223, 221, 218, 216, 214, 212, 209, 207, 204, 202, 199, 197, 194, 191, 189, 186, 183, 180, 177, 174, 171, 168, 165, 162, 159, 156, 153, 150, 147, 144, 141, 138, 135, 131, 128, 125, 122, 119, 116, 113, 109, 106, 103, 100, 97, 94, 91, 88, 85, 82, 79, 76, 73, 71, 68, 65, 62, 60, 57, 54, 52, 49, 47, 45, 42, 40, 38, 35, 33, 31, 29, 27, 25, 23, 22, 20, 18, 17, 15, 14, 12, 11, 10, 9, 8, 6, 6, 5, 4, 3, 2, 2, 1, 1, 1, 0, 0, 0, 0, 0
};

SineOsc lightOsc;

void setup() {
  Serial.begin(9600);
  lightOsc.readTable();
}

void loop() {
}

Where’s your pgm_read_xxxx_near...?

You can put stuff in PROGMEM, but the compiler cannot read variables from there automatically on lots of chips. You need to add the correct pgm_read function. See the PROGMEM reference.

Don't save it in the Program Memory. You can declare the array. There is enough RAM for programs to run. For what do you want to save the RAM? There is one way, if you don't need to change 257 values, you can save the values in the EEPROM. (Upload one sketch to save the values once and then use your other sketch which ONLY needs to read the values). Hope it helps you.

.. Arnav

When you read values from PROGMEM, you need to use the pgm_read_(datatype)_near() macro provided in pgmspace.h

(note also that when using the megaAVR 0-series and tinyAVR 0-series and 1-series, because the flash is memory-mapped, anything declared const is automatically stored only in the flash and not copied to RAM - though if you declare it PROGMEM, you do still need to use the pgm_read... macros)

Thanks, now I see I omitted pgm_read_byte (which apparently is a macro from pgm_read_byte_near )
However even this doesn’t seem to make it work.

Now I get this output:

12, 12, 177, 12, 12, 12, 148, 182, 12, 177, 177, 177, 177, 177, 177, 177, 12, 12, 35, 12, 12, 177, 12, 12, 12, 222, 12, 177, 0, 0, 12, 12, 255, 0, 12, 12, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 148, 12, 9, 12, 12, 177, 12, 177, 12, 245, 236, 234, 12, 12, 177, 177, 182, 177, 222, 177, 232, 12, 12, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 12, 12, 12, 12, 22, 148, 253, 12, 148, 148, 229, 148, 222, 148, 12, 148, 12, 148, 148, 222, 12, 5, 6, 148, 4, 6, 0, 1, 0, 39, 39, 39, 12, 12, 0, 1, 148, 4, 148, 12, 12, 12, 12, 12, 12, 12, 0, 12, 2, 2, 222, 148, 148, 222, 12, 12, 12, 12, 148, 133, 12, 148, 222, 12, 12, 12, 12, 12, 3, 12, 148, 133, 12, 148, 222, 12, 5, 0, 1, 148, 222, 12, 12, 12, 0, 12, 12, 12, 12, 12, 12, 12, 12, 12, 148, 12, 12, 255, 0, 12, 12, 12, 12, 12,

I have simplified the example code so you can easily run it. I want to store this array in PROGMEM because I am running a lot of other complex code and need all the room I can get… RAM was filling up with global variables.

#include <avr/pgmspace.h>

class SineOsc {
protected:
  static const size_t TABLE_SIZE = 256;
//  static const uint8_t LUT[TABLE_SIZE+1];
  static const uint8_t PROGMEM LUT[TABLE_SIZE+1];

public:
  SineOsc() {;}
  
  void readTable() {
    Serial.print("<TABLE>\n");
    for(int i=0; i< TABLE_SIZE; i++) {
        Serial.print(pgm_read_byte(LUT[i]));
//        Serial.print(LUT[i]);
        Serial.print(", ");
    }
    Serial.print("\n</TABLE>\n");
  }

};

const uint8_t SineOsc::LUT[TABLE_SIZE+1] = {
  0, 0, 0, 0, 1, 1, 1, 2, 2, 3, 4, 5, 6, 6, 8, 9, 10, 11, 12, 14, 15, 17, 18, 20, 22, 23, 25, 27, 29, 31, 33, 35, 38, 40, 42, 45, 47, 49, 52, 54, 57, 60, 62, 65, 68, 71, 73, 76, 79, 82, 85, 88, 91, 94, 97, 100, 103, 106, 109, 113, 116, 119, 122, 125, 128, 131, 135, 138, 141, 144, 147, 150, 153, 156, 159, 162, 165, 168, 171, 174, 177, 180, 183, 186, 189, 191, 194, 197, 199, 202, 204, 207, 209, 212, 214, 216, 218, 221, 223, 225, 227, 229, 231, 232, 234, 236, 238, 239, 241, 242, 243, 245, 246, 247, 248, 249, 250, 251, 252, 252, 253, 253, 254, 254, 255, 255, 255, 255, 255, 255, 255, 255, 254, 254, 253, 253, 252, 252, 251, 250, 249, 248, 247, 246, 245, 243, 242, 241, 239, 238, 236, 234, 232, 231, 229, 227, 225, 223, 221, 218, 216, 214, 212, 209, 207, 204, 202, 199, 197, 194, 191, 189, 186, 183, 180, 177, 174, 171, 168, 165, 162, 159, 156, 153, 150, 147, 144, 141, 138, 135, 131, 128, 125, 122, 119, 116, 113, 109, 106, 103, 100, 97, 94, 91, 88, 85, 82, 79, 76, 73, 71, 68, 65, 62, 60, 57, 54, 52, 49, 47, 45, 42, 40, 38, 35, 33, 31, 29, 27, 25, 23, 22, 20, 18, 17, 15, 14, 12, 11, 10, 9, 8, 6, 6, 5, 4, 3, 2, 2, 1, 1, 1, 0, 0, 0, 0, 0
};

SineOsc lightOsc;

void setup() {
  Serial.begin(9600);
  lightOsc.readTable();
}

void loop() {
}

Please read the reference guide more carefully. The function expects an address, not a value. Try

        Serial.print(pgm_read_byte(LUT+i));

oops, thank you! It's been awhile since I've worked in low-level languages, having to track pointers to memory and such :stuck_out_tongue:

C/C++ are high level languages and pointers form an essential element of both.

You will have to refresh those skills, if you plan to continue with Arduino.

Serial.print(pgm_read_byte(LUT+i));

You might find it more natural to write

Serial.print(pgm_read_byte(&LUT[i]));

Shifu Westfw, it occurs to me that this approach might be useful for the array that controls the behavior of a finite state machine.