Is setup() even necessary

I’m sure this is a silly question but I couldn’t find it in the doc.

I was just looking at my source code when it occurred to me that I put some functions before setup(), such as setting up timers, and other functions, like setting the publishing of variables inside setup().

So then it occurred to me, what is the different between what executes before setup() and what executives inside setup()? They both executive once before loop() starts…

I so SEMI_AUTOMATIC.

Everything works; I’m just curious.

Thanks, Tahl

1 Like

@Tahl, gobal declarations can go outside of setup() but not function calls (per se). Typically, this includes variable declarations and object constructors and function declarations/definitions. Can you post your code?

1 Like

@peekay123, here’s much of it before loop():

PRODUCT_ID(xxx);
PRODUCT_VERSION(7);

#include "InternetButton/InternetButton.h"

STARTUP(System.enableFeature(FEATURE_RESET_INFO));
STARTUP(System.enableFeature(FEATURE_RETAINED_MEMORY));
SYSTEM_THREAD(ENABLED);                                             //have application and system threads as independent
SYSTEM_MODE(SEMI_AUTOMATIC);                                        //call setup() immediately; we'll connect to cloud once in loop

int redcolor []    = {255,0,0};
int yellowcolor [] = {128,128,0};
int greencolor []  = {0,255,0};
int cyancolor []   = {0,128,128};
int bluecolor []   = {0,0,255};
int magentacolor[] = {128,0,128};
int whitecolor []  = {64,64,64};                                    //a bit dim
int rainbowcolor [] = {128,128,128};                                //not an actual color; signal to rotate thru colors

// many more var declarations

ApplicationWatchdog wd(60000, System.reset);                        //if loop() blocked for more than 60 seconds, reset Photon
InternetButton b = InternetButton();

Timer offtimer(800,neoOffTimerFunc,true);                           //true means it's a one-shot
Timer locktimer(7000,unlockKeysTimerFunc,true);                     //user locked out from making selection until this many millis; true is one-shot
Timer statustimer(30000,statusrefresh);                             //status var updated every 30 seconds
Timer diagtimer(45000,diagTimeoutTimerFunc,true);                   //diagnostic mode timesout in 45 seconds; true is one-shot
Timer snoozetimer(1800000,snoozeTimerFunc,true);                     //true is one-shot


void setup() {
    b.begin();                                                      //start up Internet button
    
    Particle.function("xxx",notifyproc);                         //SS cloud will call this function to call notification LED (different ones)
    Particle.function("xxx",  boothelper);                         //SS cloud sends this after getting a publish event during boot; provides info
    Particle.function("xxx", resetdevice);                        //does a software reboot
    Particle.variable("xxx",statusstr);                          //variable published which SS cloud polls every few hours
    Particle.variable("xxx",connectCounts);                    //"A;B" where A is times WiFi not connected and B is times Cloud not connected since boot

    statustimer.start();
}

You can be assured that the hardware is ready for any configuration once setup() is executed. The main() function looks something like this:

int main(void)
{
 init();

 initVariant();

#if defined(USBCON)
 USBDevice.attach();
#endif
 
 setup();
    
 for (;;) {
 loop();
 if (serialEventRun) serialEventRun();
 }
        
 return 0;
}

For example, if you make a call to pinMode() in a constructor, you may not be guaranteed that the hardware was ready, depending on how the compiler did its thing.

So, typically you would see the class do the software assignments in the constructor, and there would be another member function that would be called (e.g. begin() or init()) to set any hardware-specific setters/getters.

That may be because 1) you get lucky or 2) you are not affecting the hardware.

@Tahl, all those are global variable definitions and constructors, all of which need to be outside of setup(). So, yes, you do need setup() and what you have in there is correct!

Reading your question I do sense some misconception which wasn’t addressed yet.

The actual order of your function implementations in your code hasn’t got anything to do with the actual time of execution.
Whether you implement a function before or after setup() does not have any impact on setup() being executed first - setup() will always run first (apart from STARTUP() code and object constructors that is).

2 Likes

@ScruffR,

I’ve kinda been assuming that it is a one, maybe two, pass compiler and that the code that goes into the binary is basically the order that it appears in the source code. With a couple of exceptions. The STARTUP() macro is executed first then the bit before setup(), then setup() then loop(). I try to avoid race conditions, etc., being an old hand at embedded systems. :wink:

What drove the question is why something appear before setup() and some things appear in setup() with the basic assumption that things execute in order of appearance whether it is inside or outside setup(). The documentation doesn’t make it clear really at all.

Tahl

@Tahl, this is C++, which does not guarantee the order of execution of constructors. You need to read up a bit on C++ and program scope in terms of global definitions (stuff that does not appear in either setup() or loop() or any other function definition) versus local (in function) definitions. In C, the first thing to be called is main() but Arduino takes main() over and calls setup() ONCE and then loop() in a forever loop. This is why they both exist, regardless of whether they contain code or not.

1 Like