Program Memory constant not working in include file - please help

I would appreciate your help on this as I'm going crazy :astonished: I have an ATMEGA328p in a standalone project and am having problems using PROGMEM for constants. To illustrate this, I have made a simple sketch and include file. What I am finding is if I set a program memory constant within a function, it is not being remembered (or I am not accessing it correctly). In the example below, the constant "KONSTANT" is heart of the issue
Focus on function1 in junk1.cpp which has the following lines:
prog_uint16_t KONSTANT PROGMEM=499;

if (pgm_read_word_near(&KONSTANT) != 499)
PORTD=0b00000000; // gets here!
else
PORTD=0b11000000;

Here is the sketch:

#include <junk1.h>

JunkClass junk_object;

void setup() {
  junk_object.function1();
}

void loop() {
}

Here is the class file junk1.cpp:

#include "junk1.h"

JunkClass::JunkClass()
{}

void JunkClass::function1() {
    MCUCR &= ~(1<<PUD);
    DDRD = 0xFF;
    prog_uint16_t KONSTANT PROGMEM=499;
    
    if (pgm_read_word_near(&KONSTANT) != 499)
        PORTD=0b00000000; // gets here!
    else
        PORTD=0b11000000;   
}

here is junk1.h:

#ifndef junk1_h
#define junk1_h

#include "Arduino.h"
#include <avr/pgmspace.h>

class JunkClass
{
  public:

    JunkClass();
    void function1();
};

#endif

Thanks for the help

In the sketch file, you have:

#include <junk1.h>

but then use

#include "junk1.h"

This works if you have the library correctly installed in the Arduino's IDE library directory. If not, it won't work. What's the directory name where you are compiling the code?

econjack:
In the sketch file, you have:

#include <junk1.h>

but then use

#include "junk1.h"

This works if you have the library correctly installed in the Arduino's IDE library directory. If not, it won't work. What's the directory name where you are compiling the code?

econjack,
I am running the sketch from /junk/junk.ino the junk1.h and cpp files are in /libraries/junk1/

I have the arduino ide setup to see this library directory (when I go to Sketch->Add library I see the junk1 directory listed)

The technique you're using works for global variables but not for auto variables.

This code prints "Value is wrong":

void setup() 
{
  prog_uint16_t KONSTANT PROGMEM=499;

  Serial.begin(9600);
  Serial.println(__FILE__ " " __DATE__ " " __TIME__);
  
  if (pgm_read_word_near(&KONSTANT) == 499)
  {
    Serial.println("Value is right");
  }
  else
  {
    Serial.println("Value is wrong");
  }
}

void loop()
{
}

This code prints "Value is right":

void setup() 
{
  static prog_uint16_t KONSTANT PROGMEM=499;

  Serial.begin(9600);
  Serial.println(__FILE__ " " __DATE__ " " __TIME__);
  
  if (pgm_read_word_near(&KONSTANT) == 499)
  {
    Serial.println("Value is right");
  }
  else
  {
    Serial.println("Value is wrong");
  }
}

void loop()
{
}

This code also prints "Value is right":

prog_uint16_t KONSTANT PROGMEM=499;

void setup() 
{
  Serial.begin(9600);
  Serial.println(__FILE__ " " __DATE__ " " __TIME__);
  
  if (pgm_read_word_near(&KONSTANT) == 499)
  {
    Serial.println("Value is right");
  }
  else
  {
    Serial.println("Value is wrong");
  }
}

void loop()
{
}

PeterH:
The technique you're using works for global variables but not for auto variables.

This code prints "Value is wrong":

void setup() 

{
  prog_uint16_t KONSTANT PROGMEM=499;

Serial.begin(9600);
  Serial.println(FILE " " DATE " " TIME);
 
  if (pgm_read_word_near(&KONSTANT) == 499)
  {
    Serial.println("Value is right");
  }
  else
  {
    Serial.println("Value is wrong");
  }
}

void loop()
{
}



This code prints "Value is right":


void setup()
{
  static prog_uint16_t KONSTANT PROGMEM=499;

Serial.begin(9600);
  Serial.println(FILE " " DATE " " TIME);
 
  if (pgm_read_word_near(&KONSTANT) == 499)
  {
    Serial.println("Value is right");
  }
  else
  {
    Serial.println("Value is wrong");
  }
}

void loop()
{
}




This code also prints "Value is right":


prog_uint16_t KONSTANT PROGMEM=499;

void setup()
{
  Serial.begin(9600);
  Serial.println(FILE " " DATE " " TIME);
 
  if (pgm_read_word_near(&KONSTANT) == 499)
  {
    Serial.println("Value is right");
  }
  else
  {
    Serial.println("Value is wrong");
  }
}

void loop()
{
}

PeterH,
Thanks for the testing and explanation. I'll read up on "static" keyword. I want to avoid using globals if at all possible

I want to avoid using globals if at all possible

Why? Sometimes, global ARE the right answer.

PaulS:

I want to avoid using globals if at all possible

Why? Sometimes, global ARE the right answer.

Sometimes they are but I try to use them only when I have to. As to why, I have run into issues using globals that multiple functions were modifying. It was a headache to debug. Also, according to "Atmel AVR4027: Tips and Tricks to Optimize
Your C Code for 8-bit AVR Microcontrollers" they have the following:

In most cases, the use of global variables is not recommended. Use local variables
whenever possible. If a variable is used only in a function, then it should be declared
inside the function as a local variable.
In theory, the choice of whether to declare a variable as a global or local variable
should be decided by how it is used.
If a global variable is declared, a unique address in the SRAM will be assigned to this
variable at program link time. Also accessing to a global variable will typically need
extra bytes (usually two bytes for a 16 bits long address) to get its address.
Local variables are preferably assigned to a register or allocated to stack if supported
when they are declared. As the function becomes active, the function’s local variables
become active as well. Once the function exits, the function’s local variables can be
removed.

Using a static local will defeat the main argument to avoiding globals. Static vars are also located in the heap, not registers or the stack.

The main difference is the compiler can 'sometimes' use one cycle to access a static local, over the 2 cycle operation on a 16-bit address. ( however even a real global can receive this benefit in rare circumstances ).

pYro_65:
Using a static local will defeat the main argument to avoiding globals.

The main advantage is that the variable is limited to the scope of the function that declares it, which eliminates a bunch of complexity and potential bugs relating to shared data.

Given that we're talking about a local constant defined in progmem, I can't imagine what other argument is being 'defeated' here.

PeterH:

pYro_65:
Using a static local will defeat the main argument to avoiding globals.

The main advantage is that the variable is limited to the scope of the function that declares it, which eliminates a bunch of complexity and potential bugs relating to shared data.

Given that we're talking about a local constant defined in progmem, I can't imagine what other argument is being 'defeated' here.

well, every variable declared inside a function is limited to its scope, however I'm referring to its lifetime and storage use. Global/static data provides persistent storage, and static locals can be quicker to access than variables in the global namespace.

My info is in reference to the post above it, try stay with the flow. PROGMEM is an AVR specific and not really part of C++ at all.

pYro_65:
well, every variable declared inside a function is limited to its scope, however I'm referring to its lifetime and storage use. Global/static data provides persistent storage

I'm not sure whether you're correcting me or agreeing with me.

Global data is generally disliked because it is visible to all code in the global scope which increases complexity and the potential for bugs. That's less of an issue in the sort of small application suitable to be implemented in an Arduino, but it's still an issue. Using static local variables gives you similar behaviour but takes the data out of the global scope, which avoids the main issues associated with global data.

I don't think that speed of access to memory should be a consideration unless you are up against a critical performance issue; an extra clock cycle here or there is insignificant compared to the other issues.

Given that we're discussing this in an Arduino forum, I don't understand your point about PROGMEM being AVR specific.