Call millis() before setup

Creating a library that initializes SD card on the constructor I have understood that is not possible to call millis() in the code before the setup().

constructorCallingMillis(0x0013a200, 0x406163FD, 4, nTentativi, ritardoProve);

void setup() {    
  Serial.begin(9600);
  Serial.println("I am in the setup"); 
}

void loop() {
  Serial.println("loop begin");
  }

If I call it on that part of the sketch the compliers ends well but after the cede execution crashes. Could someone tell me why please? thanks

You are free to look at the code for millis() to work out why - its not a secret!

Creating a library that initializes SD card on the constructor I have understood that is not possible to call millis() in the code before the setup().

That's not all it is not possible/advisable to do.

Where is the library code that calls millis()? Where is the call to create an instance of that class?

MarkT:
You are free to look at the code for millis() to work out why - its not a secret!

Please do not post "answers" ONLY for increase your posts nubers! I know arduino code is not not a secret and I have seen wiring.c code but I have not understood why I can complie without getting error but after the code execution crashes.

Thanks PaulS, the library that call millis() is in SD.ccp.
SDClass::begin calls card.init that calls millis().

I have put Sd initialization on a constructor call I make before the setup(). I can solve my problem not initializing the SD in the constructor but in the setup:

myClassConstructorWithoutSdInit(0x0013a200, 0x406163FD, 4, nTentativi, ritardoProve);

void setup() { 
  myClass.initializeSd.   
  Serial.begin(9600);
  Serial.println("I am in the setup"); 
}

void loop() {
  Serial.println("loop begin");
  }

but I can't understand why on the old code the complier ends well but after arduino crashes.
Thanks

You're really going to need to post all of your code.

ok thanks
PDE

#include <nodoGenerico.h>

//IMPOSTAZIONI 
#define intervalloMinimoTraCampioni 2000 //180000//ritardo fra 2 campioni in millesecondi
#define sdPin 4                          //per lo stalker 10, per ethernet shield 4
#define coordnatorAddressMsb 0x0013a200
#define coordnatorAddressLsb 0x406163FD

//include locali

#include "DS1307.h" //i2c clock
#include <SD.h>
#include <XBee.h>
#include <Wire.h>
#include "WProgram.h"


const int nTentativi=3;
const int ritardoProve=2000;
uint8_t timestamp[7];


nodoGenerico provaNodo(0x0013a200, 0x406163FD, nTentativi, ritardoProve);

void setup() {    
  Serial.begin(9600);
  Serial.println("sono nel setup");
}

void loop() {
  Serial.println("inizio un loop");
  
  delay(intervalloMinimoTraCampioni);
}

the .cpp

/******************************************************************************
 * Includes
 ******************************************************************************/
#include "nodoGenerico.h"
#include "WProgram.h" 	//Arduino includes
#include <SD.h>

/******************************************************************************
 * Definitions
 ******************************************************************************/

//#define sdPin 4 //per lo stalker 10, per ethernet shield 4

/******************************************************************************
 * Constructors
 ******************************************************************************/

nodoGenerico::nodoGenerico(uint32_t coordAddMsb, uint32_t coordAddLsb, int nTentativiParam, int ritardoProveParam)
{	
	Serial.begin(9600);
	
	
	xbee = XBee();
	
	addr64 = XBeeAddress64(coordAddMsb, coordAddLsb);
	txStatus = ZBTxStatusResponse();
	
	sdScritta=false;
	sdPresente=false;
	nTentativi=nTentativi;
	ritardoProve=ritardoProve;	
	
	xbee.begin(9600);


	//Serial.print("Initializing SD card...");
	// On the Ethernet Shield, CS is pin 4. It's set as an output by default.
	// Note that even if it's not used as the CS pin, the hardware SS pin 
	// (10 on most Arduino boards, 53 on the Mega) must be left as an output 
	// or the SD library functions will not work. 
	pinMode(10, OUTPUT);
	
	
	 if (!SD.begin(sdPin)) {			
		 Serial.println("initialization failed!");
		 sdPresente=false;
		 Serial.println("sono nel costruttore");
	}
		 
	else{
		 Serial.println("initialization done.");
		 sdPresente=true;
		 //se la sd è presente guardo se è scritta
		 mioFile = SD.open("valori.txt");
		 if (mioFile) {
		  if(mioFile.available()>0 )
			sdScritta=true;
		  mioFile.close();
		 }
	}
	
		 
}




nodoGenerico::~nodoGenerico( )
{

}

everything compiles well but at running time arduino crashes in SD.begin when reach the function millis() called by the sd library

everything compiles well but at running time arduino crashes in SD.begin

I think that this should be a clue, right there. The HardwareSerial class has a begin() method. The SD class has a begin method. The XBee class has a begin method.

Your class needs a begin() method. Constructors should not call begin() methods or execute code that belongs in a begin() method. Anything that involves pin modes, millis(), etc. is not ready for you to use until the init() method completes. The init() method is not called until after all constructors are called.

thank you for your answer. May be the program crashes also if compiling succes because of compilation order?
I found this article interesting:
Constructors, C++ FAQ it is about "static" but may be the logic is similar

May be the program crashes also if compiling succes because of compilation order?

Successful compilation != successful execution.

cantore:
thank you for your answer. May be the program crashes also if compiling succes because of compilation order?
I found this article interesting:
Constructors, C++ FAQ it is about "static" but may be the logic is similar

Yes, it's a well known problem. Don't initialize stuff in static constructors is the answer. Or more accurately, don't rely upon other stuff being initialized before your stuff. It is safe to, say, move zero to a variable. It isn't safe to assume another library was initialized before yours. So, for example, you can't assume that millis() will work. You can use a begin method as PaulS suggested. Or dynamically allocate the object rather than statically allocate it.

I have not understood why I can complie without getting error but after the code execution crashes.

That's no secret. For example I can compile this:

void setup ()
{
while (true) ;  // loop forever

pinMode (13, OUTPUT);
digitalWrite (13, HIGH);
}

void loop () {}

That will compile, but not turn on pin 13. They are two separate issues.

PaulS:
Anything that involves pin modes, millis(), etc. is not ready for you to use until the init() method completes.

I don't see the code in init() that "initializes" pin modes, except pins 0 and 1. Is that the only exception you were talking about? What if we put UCSRB = 0; in our constructor before using pinMode?

It doesn't seem like using attachInterrupt in a constructor would hurt anything either, as long as the interrupt doesn't use millis(). (Is micros() okay? I don't really understand its code)

WizenedEE:
What if we put UCSRB = 0; in our constructor before using pinMode?

That would probably be OK as it disconnects the UART.

WizenedEE:
It doesn't seem like using attachInterrupt in a constructor would hurt anything either, as long as the interrupt doesn't use millis(). (Is micros() okay? I don't really understand its code)

micros() and millis() both use the internal timer. millis() gets the last "tick" from the last interrupt, micros() takes that and adds in the "extra" from the currently-running timer to get greater resolution. Thus you need init to complete for either of those to return anything meaningful.

Of course, in your constructor, you could just assume millis() and micros() return zero. That would be reasonably close.

You are probably still better off have the constructor do nothing much, and call nodoGenerico::begin() as an initializer (in setup). Any reason not to do that? Otherwise you rely upon, in future releases, that init() doesn't do something that might clash with what you are doing.

cantore:
thank you for your answer. May be the program crashes also if compiling succes because of compilation order?
I found this article interesting:
Constructors, C++ FAQ it is about "static" but may be the logic is similar

Compiling translates your language instructions into machine code. As long as there exists a proper translation from language instructions to machine code, then there will be no compilation errors.
That doesn't mean that the language instructions are doing exactly what you may think you are telling it to do, though. Most software development (like most enginering) consists of figuring out where your plans/designs/rules/code/thoughts are wrong, and fixing them, until the system behaves the way you want it to.

No static checking tool (such as a compiler) can detect that you're actually telling the system to do something other than what you intended to tell it.

If you really need to run code from a constructor, that needs to run inside init(), then you can use placement new to construct this object:

char buffer[sizeof(MyClass)];
MyClass *instance;

inline void *operator new(void *ptr) { return ptr; }

void init() {
    instance = new(buffer) MyClass(constructor arguments);
}

jwatte:
If you really need to run code from a constructor, that needs to run inside init(), then you can use placement new to construct this object:
...

Thank you very much for explaining how placement new is used. I was wondering how it would be helpful.

Ah, except that it doesn't compile:

sketch_oct04b:-1: error: 'operator new' takes type 'size_t' ('unsigned int') as first parameter

See:

http://www.parashift.com/c++-faq-lite/dtors.html#faq-11.14

Now when the compiler sees new(pool) Foo(), it calls the above operator new and passes sizeof(Foo) and pool as parameters ...

So combining your example with this code for placement new, it will work:

char buffer[sizeof(MyClass)];
MyClass *instance;

// placement new
inline void * operator new (size_t size, void * ptr) { return ptr; }

void init() {
    instance = new(buffer) MyClass(constructor arguments);
}

Thanks to everyone!

Bah, typo! Glad you got it working, though :slight_smile: