setup() and order of execution of code outside it

This is a simple question that goes like this:

Is the code for variables declared (and instantiated) in libraries executed after or before setup() is called?

And here’s the longer version:

So, I have this code with tact switches. Simple stuff, pinMode with INPUT_PULLUP. They way it works, the buttons are initialized (ie call pinMode) directly from code like this:

Buttons.cpp:

// The buttons class goes like this (heavely simplified to just show how it works):

#include "Buttons.h"

TButtons ButtonsHandler(PIN_B1, PIN_B2, PIN_B3, 3); //where PIN_B# is a pin number of course

TButtons::TButtons(byte ButtonPins[], byte ButtonCount){
  for (byte i= 0; i < ButtonCount; i++){
    pinMode(ButtonPins[i], INPUT_PULLUP);
  }
}

byte TButtons::GetButton(){
  for(byte i= 0; i < ButtonCount; i++)
    if (digitalRead(ButtonPins[i]) == LOW)
     return i;
  return 0;
}

Now here’s the problem. In my contraption, I’d like to be able to enter a special mode that should happen before anything else. For that I decided to check if a button was pressed or not from startup. So I did this:

In main.ino:

#include "Buttons.h"

void setup(){
  if (ButtonsHandler.GetButton() == PIN_B1)
    GoSpecialMode();
  else
    DoTheRestOfTheStuff();
}

void loop(){
 and the rest of things.
}

But it doesn’t work, it ignores the button being pressed. My guess is that the pin is not yet “functional” when I try to digitalRead() it.

If I do this:

void setup(){
  pinMode(Pin_B1, INPUT_PULLUP); //This should have been done already by TButtons' constructor at this time
  delay(50);
  if (ButtonsHandler.GetButton() == PIN_B1)
    GoSpecialMode()
}

it works perfectly. I did check that the object is being instantiated before setup() and it does and sets up everything as it should.

So it got my thinking. What is going on? :slight_smile:

I’d imagine that setup() is called AFTER all the code from libraries is executed, so ButtonHandler should have been initialized by then. Which it does (I logged the order of execution in EEPROM and then checked).

It is called after all other initializations, since it is called from main().

I would guess that your library uses the initial state of the button to determine the "off" state of the button.

In main.ino:

Is your file actually named main.ino or are you referring to your main ino file that has a different name ?

KeithRB:
It is called after all other initializations, since it is called from main().

I would guess that your library uses the initial state of the button to determine the “off” state of the button.

hmm… Kinda but GetButton() ignores all that and just literally digitalReads the pins ones by one until one is LOW.

UKHeliBob:
Is your file actually named main.ino or are you referring to your main ino file that has a different name ?

Yeah, I wanted to make it clear I was talking about the main .ino file, but it’s called CajaComida.ino.

It is hard to say what is happening as you are not showing us all the code you are actually using/running.

  • we don't know the contents of the header file.
  • we can't build your code
    The constructor seems "funny" in that it wants a pointer to byte array and a count, but is passed 4 parameters when the object is declared.

Pin_B1 vs PIN_B1 as you show in your "working" code is an issue as those are not necessarily the same values.
Also you may want to avoid using a define like PIN_B# it is used by some cores (like Teensy) and would have issues on some AVR processors.

--- bill


C/C++ low level startup code should

  • zero out "zero" AVR register
  • set up heap & stack pointers
  • zero out any areas in bss RAM
  • copy initialized data values from flash to RAM
  • call all constructors (order is not guaranteed) - note: I've looked for this in the crt code and still can't find it - but it must happen before main() is called
  • call main()

You can look at the Arduino main() function but it basically

  • calls init() - to set up timers
  • calls initVariant() - which is an empty function by default
  • potentially calls USBDevice.attach() - to initialize USB virtual port
  • calls setup()

I could post the whole code... well, I can't, because the company net blocks Dropbox. :smiley: but I should be able to upload it when I get home.

The code posted is an oversimplification of the whole thing with only the parts relevant to the problem. The constructor is not like the real one of course (what I posted is not even C++ valid).

My intent was to show a) where the object was instantiated, b) what TButtons' constructor was doing (taking the pin numbers and calling pinMode() on them) and c) where I was calling the object to read the buttons.

I agree on the names, but they're also fake (the originals are in Spanish).

We don't really need to see the whole original code, but in order to to really diagnose the issue to see whether the issue is in the user sketch code or library, we need to see code that is compilable that actually fails to operate as expected.
So if you have a nice compact example that compiles that demonstrates the issue, that would be great.

--- bill

Its hard to tell from what you've posted but.. I've run into & have been bitten by code in class creator methods. What happens is people tend to create global objects. We all do, very handy. This is all fine and good, but when your object is created outside the code path [ startup() & loop() ] It doesn't know the order of object creation, so it can't trust any calls to any other information while constructing. Globals or anything. The compiler will let you call them but what the constructor gets back can be garbage. I'm thinking this is why you see begin() a lot in Arduino code. Your constructor can do things in "its yard" set initial values what have you. Then, in the begin() call, all the world's globals are ready to go and can be used.

I'm not seeing your object being created. So I assume its a global. I'd not try any outside calls from my constructor.

Hope this helps.

-jim lee

Code attached (sorry but it’s in Spanish, so…)

The thing to look for is this:

File “Buttons.h/cpp” has the buttons class. The object “Botones” of this class is used to read buttons, and it’s instantiated in the third line of file “Interfaz.cpp”

The setup() in CajaComida2.ino reads:

void setup(){
[...]
 pinMode(BTN_CANCELAR, INPUT_PULLUP);
  delay(50);
  if (digitalRead(BTN_CANCELAR) == LOW)
    Config();
  else
    CargarReglas();
[...]
}

That’s what I had to modify. Originally it was:

if (Botones.LeerBoton() == BTN_CANCELAR)
  Config();
else
  CargarReglas();

BTW, the contraption, a “food vault” that only opens at certain times based on rules you create, is working very nicely.

Translation: Want a snack? Well, wait until dinner time, buddy! And good luck if you want something to eat after 9pm, because next opening happens at 12 the next day. :smiley:

CajaComida2.zip (19.7 KB)

BTW, note that TBotones::LeerBoton() has a delay built in, but in this case it's only 250 milliseconds (specified in constructor). I tried without it before and didn't make any difference.