1

In the past I declared variables inside the main loop which worked just fine. In a new project I did the same:

void loop(void)
{
    uint8_t counter;
    ....
    if (buttonPress)
        counter = 0;
    ...
    if (someCondition == true)
    {
        Serial.println(counter);
        counter++
    }
}

But then the output of the variable counter shows that for some reason the counter variable is reset to 0 instead of counting up. This happens even after the button is released.

The scope of the variable counter is the main loop and I just assumed that it will keep its value and count up.

What might be the reason for this variable to reset to 0? Is it that other functions are called within the main loop? Is it that an interrupt handler is called every now and then?

The solution I've found is to declare the variable as static:

static uint8_t counter;

Then it increments as expected.

Even though I've found a solution I'd really like to understand what the problem with my first approach was.

  • 1
    This question fits better in stack overflow, since it is not related to Arduino. – Michel Keijzers Jun 30 at 10:20
  • So the main loop in the Arduino just behaves as any regular function that is called? For some reason I thought that variables declared in main() are automatically static and keep their value. – Sören Jun 30 at 10:27
  • 1
    No they are not, afaik the loop() is just a function that has a while(true) or while(1) inside. – Michel Keijzers Jun 30 at 10:29
  • Thank you very much for the explanation and possible solutions. – Sören Jun 30 at 10:35
  • 1
    @MichelKeijzers: Here is main(). – Edgar Bonet Jun 30 at 12:41
3

Michael's answer was good, as usual. Let me give you some more background though:

In C/C++ (And in most modern languages) variables have a "scope", or an area where they are defined.

Global scope:

Variables declared at the top level of your program have global scope, and exist for the life of your program. These variables are usually created on the .data or .bss sections.

Local scope

Variables declared inside a function have local scope. Unless they are static, they get created when the function is entered, and get discarded when the function exits. These variables are created on the stack as part of the function's stack frame.

Object scope

In C++ and other object-oriented languages, instances of objects have their own scope, "instance variables". Every instance of an object has its own set of instance variables. (Think of cars, and a car radio. You and I might own the exact same model of car, but if I set my car's radio station and you set your car's radio to a different station, each instance of the car has a different setting for the radio station.)

Static variables:

Static variables are variables that are declared "statically." That means that they are only created once and persist, even if they are declared inside a scope like a function or a class instance. These have the lifespan of a global variable but can be declared inside a function or in an object.


Loop is a function, so variables declared inside of loop() are local variables. They get created anew every time the function is called, and discarded every time the function exits.

  • 1
    +1 for the great explanation and background – Michel Keijzers Jun 30 at 12:31
2

Your code has a couple of bugs. First you don't initialize counter before using it. Second the variable 'counter' has limited scope, it has no definition when loop() exits. If you want this variable to stay around declare it as static.

Both bugs corrected e.g.:

void loop(void)
{
    static uint8_t counter = 0;
    ....
    if (buttonPress)
        counter = 0;
    ...
    if (someCondition == true)
    {
        Serial.println(counter);
        counter++
    }
}

Note: Initializing counter to 0 only happens once, on subsequent calls to loop() counter will retain it's previous value.

0

The problem is that you declare a new variable. So even though the name is the same, the local variable is destroyed at the end of the loop and recreated at the beginning.

Actually, there is no good solution:

  • Global variables are best to be avoided. They can be seen as 'singleton's', e.g. variables that are needed at many places in the code and have only one occurrence.
  • As you found out, static solves the problem. I would use this option, although personally I'm not a fan of static variables.
  • Another way is to put the loop in a while, so you get:

    void loop(void) { uint8_t counter = 0; // Always initialize variables while (true) { // Your code } }

    This will work too, as your counter is only initialized once, however the loop is already (internally) a while loop, so you have two.

  • A much better way but more extra work, is to create a C++ class, where counter can be a class variable, which is local for that class (which could still be a singleton). Of course this is only true when the counter variable is belonging logically to that class, if it is not, just keep it a global variable (updated after Edgar Bonet's remark).

  • Making the counter a property of an object which is itself a global variable would be no better than making the counter itself global. It would actually be worse: any complexity that serves no useful purpose is always a bad thing in a program. – Edgar Bonet Jun 30 at 11:18
  • @EdgarBonet You are right when that variable (the counter) is unrelated to the class that would be created (I was assuming so, but I will update my answer). If the counter is unrelated, than you are absolutely right. – Michel Keijzers Jun 30 at 12:33
  • Could you explain why simply using a global variable should be avoided? I see no disadvantage in terms of memory usage or performance. I'd say, using a global variable is just fine in many situations. In my opinion making it more complicate is also not very elegant. – Sim Son Jun 30 at 12:39
  • @SimSon There is no disadvantage in terms of memory usage or performance, bit if you make all variables global and you have thousands of them, you can imagine what mess it will be. This will not happen easily in an Arduino Uno with 2 KB memory, but it's best to keep functionality isolated. Actually, one of the worst things about the Arduino IDE is that it works best with just an Ino file, and most people do not get further creating classes or separate files. – Michel Keijzers Jun 30 at 12:41
  • @SimSon: See Are global variables evil in Arduino?. – Edgar Bonet Jun 30 at 12:42

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.