Servo::attach() before setup()

Please refer to this thread.

I found the thread while researching why my custom servo library behaves erratically. It boils down to Servo::attach() being placed before setup, which I solved by calling it within setup() instead.

However what’s the reason attach() can’t be placed before setup? The explanation didnt quite go there. Is the code not executed due to the board powering up?

Servo::attach() is getting called in the static initialization phase, before setup() is called. Looking at the source for attach(), it seems like that could be bad.

I'm not a C++ expert, but I'm fairly certain that no actual "dynamic" code can be executed outside of the main() function's scope. Although it isn't easily apparent, the Arduino IDE abstracts the fact that anything processed in setup() and loop() (and ONLY things that are processed in setup() and loop()) are actually stuffed together into a main() function.

Anything outside of setup() and loop() can only be function definitions, global variable definitions, macro definitions, preprocessor directives, and includes - NOT dynamic code.

Calling Servo.attach() is actually dynamic code. The call executes a function and, therefore, needs to be in either setup() or loop() so that it eventually gets put into the abstracted main() when compiling.

Does this all make sense?

If I were you, I'd follow the advice already given in your linked thread. Perhaps you can describe why you want to create your own servo library? Can you send a link to your new library?

Servo::attach() is dependent on a pin assignment. If it is called before int main(void), which contains both a call to setup() and a call to loop(), how can one be certain it is called after the pin assignment or if before, how can one be certain that the attach function executes as expected?

Power_Broker:
I’m not a C++ expert, but I’m fairly certain that no actual “dynamic” code can be executed outside of the main() function’s scope. Although it isn’t easily apparent, the Arduino IDE abstracts the fact that anything processed in setup() and loop() (and ONLY things that are processed in setup() and loop()) are actually stuffed together into a main() function.

Anything outside of setup() and loop() can only be function definitions, global variable definitions, macro definitions, preprocessor directives, and includes - NOT dynamic code.

Calling Servo.attach() is actually dynamic code. The call executes a function and, therefore, needs to be in either setup() or loop() so that it eventually gets put into the abstracted main() when compiling.

Does this all make sense?

That makes things a bit clearer, but looking at the Servo::attach() code:

uint8_t Servo::attach(int pin, int min, int max)
{
  if(this->servoIndex < MAX_SERVOS ) {
    pinMode( pin, OUTPUT) ;                                   // set servo pin to output
    servos[this->servoIndex].Pin.nbr = pin;
    // todo min/max check: abs(min - MIN_PULSE_WIDTH) /4 < 128
    this->min  = (MIN_PULSE_WIDTH - min)/4; //resolution of min/max is 4 uS
    this->max  = (MAX_PULSE_WIDTH - max)/4;
    // initialize the timer if it has not already been initialized
    timer16_Sequence_t timer = SERVO_INDEX_TO_TIMER(servoIndex);
    if(isTimerActive(timer) == false)
      initISR(timer);
    servos[this->servoIndex].Pin.isActive = true;  // this must be set after the check for isTimerActive
  }
  return this->servoIndex ;
}

it seems to be, with the exception of pinMode(), mostly value assignments, which I believe are legal outside of main() or setup(). Is pin assignment the problem here then?

Also, is the class constructor considered “dynamic”? I would assume not since objects can be safely declared outside of main() or setup().

Power_Broker:
If I were you, I’d follow the advice already given in your linked thread. Perhaps you can describe why you want to create your own servo library? Can you send a link to your new library?

I want to have a bunch of specific servo movements which I can share easily between projects. It basically #includes the original Servo.h and uses it to implement different movement patterns that I need for my work. Unfortunately its still a WIP, so I can’t share it yet.

If you place the attach() in the global section, it's executed before main() is executed. Next main() is executed which calls init(). init() can very well interfere with things that you did earlier; you can find what init() does in (on a windows system) C:\Program Files (x86)\Arduino\hardware\arduino\avr\cores\arduino\wiring.c.

Wangmaster:
it seems to be, with the exception of pinMode(), mostly value assignments, which I believe are legal outside of main() or setup().

False, calling pinMode() outside of setup() or loop() will cause a compile-time error.

Wangmaster:
Also, is the class constructor considered “dynamic”? I would assume not since objects can be safely declared outside of main() or setup().

Yes, the constructor is dynamic code. For instance, the following C++ code will NOT compile:

#include <iostream>

using namespace std;

class construct {
public:
 int a;

 void begin()
 {
 a = 10;
 }
};

construct c;

c.begin();

int main()
{
 return 1;
}

Yet this WILL compile:

#include <iostream>

using namespace std;

class construct {
public:
 int a;

 void begin()
 {
 a = 10;
 }
};

construct c;

int main()
{
 c.begin();
 return 1;
}

Wangmaster:
I want to have a bunch of specific servo movements which I can share easily between projects. It basically #includes the original Servo.h and uses it to implement different movement patterns that I need for my work.

Ok, sounds cool - you should design the library's class so that it's constructor is called in setup() with a parameter that specifies what pin the servo will be attached to. Then you can implement all your coolio servo commands after.

Wangmaster:
Unfortunately its still a WIP, so I can't share it yet.

Sigh Allowing others to critique your code/share ideas will only help improve your library and skills as a programmer....

Power_Broker:
False, calling pinMode() outside of setup() or loop() will cause a compile-time error.

What I meant was that value assignments are legal outside main() or setup(), but ok, i think you’ve answered my question anyway, seems pinMode() is the problem here.

Power_Broker:
Yes, the constructor is dynamic code. For instance, the following C++ code will NOT compile:

A little confused, you can create an object outside setup() or main() like you would Servo myServo, can’t you? So if that is dynamic, shouldn’t it throw an error on compile time as well?

Power_Broker:
Ok, sounds cool - you should design the library’s class so that it’s constructor is called in setup() with a parameter that specifies what pin the servo will be attached to. Then you can implement all your coolio servo commands after.

If I call my object within setup(), wouldn’t I be unable to access it in main() (out of scope)? Or did you mean something else?

I’m following the attached thread’s suggestion of calling a class method with Servo::attach() in setup(), that seems to work fine.

Power_Broker:
Sigh Allowing others to critique your code/share ideas will only help improve your library and skills as a programmer…

When I said I can’t share I meant that my library is so barebones at the moment I don’t think it would add much to the discussion, but sure, I’ve attached it as is for reference. I’m open to suggestions on how to improve.

servoControl.h (1.97 KB)

Wangmaster:
seems pinMode() is the problem here.

No, the problem is that init() resets the timer(s) that Servo uses.

Wangmaster:
A little confused, you can create an object outside setup() or main() like you would Servo myServo, can't you? So if that is dynamic, shouldn't it throw an error on compile time as well?

If I call my object within setup(), wouldn't I be unable to access it in main() (out of scope)? Or did you mean something else?

As stated in the original thread you referenced, the suggestion was to not have the Servo::attach() in the constructor, but certainly the constructor can exist outside setup(). Then in addition to the class constructor, have a class method begin() that can be called during setup() when all the memory is in place and in that method, have Servo::attach().

Wangmaster:
A little confused, you can create an object outside setup() or main() like you would Servo myServo, can’t you? So if that is dynamic, shouldn’t it throw an error on compile time as well?

“Servo myServo;” instantiates an instance of the Servo class. That is, the compiler makes sure memory is set aside for the class as well as creating all the necessary class pointers. It is very similar to “int x”. Nothing dynamic happens when you call “int x” and neither does “Servo myServo”.

The problem arises when you construct and initialize the instantiated class. For instance, calling “.begin()”. Although you can (and should) instantiate a class outside of main(), you can’t construct a class outside of main().

Wangmaster:
If I call my object within setup(), wouldn’t I be unable to access it in main() (out of scope)? Or did you mean something else?

You’ve already instantiated the class globally, so any changes made to the class in setup() persist through to loop(). Here’s a simple example:

int x;

void setup()
{
  Serial.begin(9600);
  
  x = 5;
}

void loop()
{
  Serial.println(x);
  delay(100);
}

Even though the value of x is only changed in setup(), x keeps it’s update through to loop().

Wangmaster:
When I said I can’t share I meant that my library is so barebones at the moment I don’t think it would add much to the discussion, but sure, I’ve attached it as is for reference. I’m open to suggestions on how to improve.

I apologize if I sounded mean - didn’t intend to.

Here’s the header:

#ifndef SERVOCONTROL_H_INCLUDED
#define SERVOCONTROL_H_INCLUDED

#define SERVO_UPPER_MICRO 1710
#define SERVO_LOWER_MICRO 610
#define SERVO_TRAVEL_MILLIS 800
#define CHAMBER_COUNT 10

#include <Arduino.h>
#include <Servo.h>

class servoControl{
public:
    Servo myServo;

    servoControl(int pin)                               //Constructor, set up positional values for writeMicrosecond()
    {
        _pinNo = pin;
        int increment = (SERVO_UPPER_MICRO - SERVO_LOWER_MICRO) / (CHAMBER_COUNT - 1);
        for (int i = 0; i < 10; i++)
        {
            _posPresetData[i] = SERVO_LOWER_MICRO + (increment * i);
        }
    }

    void initServo(int pin)                             //Initialize Servo: Place in setup()
    {

        myServo.attach(pin);
    }

    void testServo()                                    //Test that servo is operational
    {
        myServo.writeMicroseconds(SERVO_LOWER_MICRO);
        delay(2000);
        myServo.writeMicroseconds(SERVO_UPPER_MICRO);
        delay(2000);
    }

    void goToPos(int chamber)                           //Servo go to specific chamber
    {
        myServo.writeMicroseconds(_posPresetData[chamber-1]);
        if (_currentPos < chamber)
        {
            delay(SERVO_TRAVEL_MILLIS*(chamber - _currentPos));
        }
        if (_currentPos >= chamber)
        {
            delay(SERVO_TRAVEL_MILLIS*(_currentPos - chamber));
        }
        _currentPos = chamber;
    }

    void resetServo()                                   //Servo reverts to Chamber 1
    {
        myServo.writeMicroseconds(SERVO_LOWER_MICRO);
        _currentPos = 1;
        delay(SERVO_TRAVEL_MILLIS * (CHAMBER_COUNT -1));
    }

    int getCurrentPos()                                 //Getter for current position
    {
        return _currentPos;
    }

private:

    int _pinNo;
    int _posPresetData[10];
    int _currentPos = 0;

};

#endif // SERVOCONTROL_H_INCLUDED

Suggestions:

1.) Host and version control your library using GitHub. GitHub allows others to easily view, download, submit bug tickets, etc. It also allows you to remotely backup your code while automatically version-controlling it.

2.) You should also be using a .cpp file in your library. The .h is for function and class declarations and the .cpp is for function and class definitions. Take a look at this simple Arduino library.

3.) Instead of using “#ifndef SERVOCONTROL_H_INCLUDED”, you can do a “#pragma once” (I’m guilty of this myself)

4.) Shy away from macros as much as possible (#define). Instead, use the const qualifier.

5.) If the body of an if statement, for loop, while loop, etc is only one line, exclude the curly braces - makes things more readable

Lastly, doing some googling on how C++ classes work might clear some things up.