Using PROGMEM to store my array gives different results than RAM [SOLVED]

I am working with a U-Blox GPS unit, that I need to configure via the serial port by sending specific sentences to it.

Every sentence I send has a checksum in the last two bytes of the sentence. Therefore I made a small sketch, where I paste a UBLOX command string into my sketch, and my sketch calculates the checksum. I then add the two calculated checksum bytes, to the command string and use that as the UBLOX sentence to send to my GPS.

Here is my sketch that I have tested and it works fine. The output it generates is CK_A = 91 and CK_B = 84

/*UBlox GPS message checksum calculator
  V1.0 by Hans Meijdam
  This sketch will calculate the UBLOX checksum. The checksum algorithm used is the 8-Bit Fletcher Algorithm, which is used in the TCP standard (RFC 1145).
  leave out the first two sync-char of the UBLOX sentence and the last two bytes are the checksum bytes.
 
 expected outcome of the below example should be: CK_A = 91 and CK_B = 84
*/
const static uint8_t myStr[] = {0xB5, 0x62, 0x06, 0x00, 0x14, 0x00, 0x01, 0x00, 0x00, 0x00, 0xD0, 0x08, 0x00, 0x00, 0x00, 0x96, 0x00, 0x00, 0x07, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,  0x91, 0x84};
//                              ====sync=== ============================================== checksum calculated over these bytes ===========================================================  =checksum=
void setup() {
  Serial.begin(9600);
  uint8_t CK_A = 0;
  uint8_t CK_B = 0;
  uint8_t i;

  for (i = 0; i < sizeof(myStr) - 4; i++) { // "-4" because leave the first two and the last two bytes out of the checksum calculation. 
    CK_A = CK_A + myStr[i + 2]; // "+2" because we skip the first 2 sync bytes
    CK_B = CK_B + CK_A;
  }
  Serial.print("CK_A = ");
  Serial.print(CK_A, HEX);
  Serial.print(" and CK_B = ");
  Serial.println(CK_B, HEX);
}

void loop() {
}

So then I thought. "why waste dynamic ram if I can also store the array in PROGMEM?", so I changed this line

const static uint8_t myStr[] = {0xB5, 0x62, 0x06, 0x00, 0x14, 0x00, 0x01, 0x00, 0x00, 0x00, 0xD0, 0x08, 0x00, 0x00, 0x00, 0x96, 0x00, 0x00, 0x07, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,  0x91, 0x84};

into this line

const static uint8_t myStr[] PROGMEM = {0xB5, 0x62, 0x06, 0x00, 0x14, 0x00, 0x01, 0x00, 0x00, 0x00, 0xD0, 0x08, 0x00, 0x00, 0x00, 0x96, 0x00, 0x00, 0x07, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,  0x91, 0x84};

but now the output is: CK_A = 24 and CK_B = 91

I would like to understand what is causing the difference. I always assumed I can use static constants from PROGMEM and it will be like they were stored in RAM. But I fear my assumption is wrong.

note: I shuffled the position of PROGMEM around, as that seems to matter for the compiler, but in this case did not make a difference.

Do you not need to use special commands to load values from PROGMEM ?

UKHeliBob:
Do you not need to use special commands to load values from PROGMEM ?

PROGMEM - Arduino Reference

I have used it in the past without the special commands I think, so let me try the examples you referred to and work from there.

Now I am even getting more confused. I added the macro(?) "pgm_read_byte_near" from the PROGMEM example and also tried "pgm_read_byte", but now I get yet another calculation result "CK_A = 1C and CK_B = 74" with below sketch.

/*UBlox GPS message checksum calculator
  V1.0 by Hans Meijdam
  This sketch will calculate the UBLOX checksum. The checksum algorithm used is the 8-Bit Fletcher Algorithm, which is used in the TCP standard (RFC 1145).
  leave out the first two sync-char of the UBLOX sentence and the last two bytes are the checksum bytes.
 
 expected outcome of the below example should be: CK_A = 91 and CK_B = 84
*/
const static uint8_t PROGMEM myStr[] = {0xB5, 0x62, 0x06, 0x00, 0x14, 0x00, 0x01, 0x00, 0x00, 0x00, 0xD0, 0x08, 0x00, 0x00, 0x00, 0x96, 0x00, 0x00, 0x07, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,  0x91, 0x84};
//                                      ====sync=== ============================================== checksum calculated over these bytes ===========================================================  =checksum=

uint8_t myChar;

void setup() {
  Serial.begin(9600);
  uint8_t CK_A = 0;
  uint8_t CK_B = 0;
  uint8_t i;

  for (i = 0; i < sizeof(myStr) - 4; i++) { // "-4" because leave the first two and the last two bytes out of the checksum calculation. 
    myChar = pgm_read_byte_near(myStr[i + 2]);// "+2" because we skip the first 2 sync bytes
    CK_A = CK_A + myChar; 
    CK_B = CK_B + CK_A;
  }
  Serial.print("CK_A = ");
  Serial.print(CK_A, HEX);
  Serial.print(" and CK_B = ");
  Serial.println(CK_B, HEX);
}

void loop() {
}

Instead

myChar = pgm_read_byte_near(myStr[i + 2]);

try

myChar = pgm_read_byte_near(&myStr[i + 2]);

or

myChar = pgm_read_byte_near(myStr + i + 2);

Whandall, you just earned your 1101th karma point as both of your suggestions fixed the issue. Would you mind elaborate a bit more on both of your suggestions rationale, as I suspect you are helping the compiler?

I did not help the compiler, but the user that did not read the documentation and the examples. :wink:

pgm_read_byte() and its companions take an address as a parameter.

You gave it an array value, and that was even fetched from the wrong address space.

& is used to get the address of an object (variant 1),
arrays are constant pointers, adding to them is supported (variant 2).

This was very helpful. I played with below sketch to familiarize myself with being more aware of the difference between the address of something and the actual something.

The base address of myStr was apparently the decimal number 104 and when printing (&myStr[i + 2]) along the way I could see that nicely counting up, while printing myStr[i + 2]) gives all sorts of random values making me wonder where they came from (RAM?).

Anyway.. thanks again, I learned something and that is always nice.

/*UBlox GPS message checksum calculator
  V1.0 by Hans Meijdam
  This sketch will calculate the UBLOX checksum. The checksum algorithm used is the 8-Bit Fletcher Algorithm, which is used in the TCP standard (RFC 1145).
  leave out the first two sync-char of the UBLOX sentence and the last two bytes are the checksum bytes.

  expected outcome of the below example should be: CK_A = 91 and CK_B = 84
*/
const static uint8_t myStr[] PROGMEM = {0xB5, 0x62, 0x06, 0x00, 0x14, 0x00, 0x01, 0x00, 0x00, 0x00, 0xD0, 0x08, 0x00, 0x00, 0x00, 0x96, 0x00, 0x00, 0x07, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,  0x91, 0x84};
//                                      ====sync=== ============================================== checksum calculated over these bytes ===========================================================  =checksum=
uint16_t prnt; // helper for printing

void setup() {
  Serial.begin(9600);
  uint8_t CK_A = 0;
  uint8_t CK_B = 0;
  uint8_t i;

  for (i = 0; i < sizeof(myStr) - 4; i++) { // "-4" because leave the first two and the last two bytes out of the checksum calculation.
    CK_A = CK_A + pgm_read_byte(&myStr[i + 2]); // "+2" because we skip the first 2 sync bytes
 //   CK_A = CK_A + pgm_read_byte(myStr + i + 2); // "+2" because we skip the first 2 sync bytes
    CK_B = CK_B + CK_A;
Serial.print(myStr[i + 2], HEX);
Serial.print(" ");
prnt = (&myStr[i + 2]);
Serial.print(prnt);
Serial.print(" ");
Serial.println(pgm_read_byte(prnt), HEX);
    
  }
  Serial.print("Base address of myStr = ");
  prnt = (&myStr[0]);
  Serial.print(prnt);  
  Serial.print(" CK_A = ");
  Serial.print(CK_A, HEX);
  Serial.print(" and CK_B = ");
  Serial.println(CK_B, HEX);
}

void loop() {
}

This topic was automatically closed 120 days after the last reply. New replies are no longer allowed.