5

I'm currently facing an issue using delay() and millis() functions in an external cpp file. The issue is that when I used delay() in my main program (meaning .ino file) it works well but when I call the same function in an external cpp file, I'm stuck in the delay function. Concerning the setup, the .hpp linked to the used cpp is well included in the .ino file and the librairy Arduino.h is included in my cpp. I also tried to replace delay by a loop using millis but the value returned by millis is always 0. I'm using an Arduino mega2560 and a water level sensor in the follwing example. The Arduino IDE version is 1.8.16.

Here is an extract from my ino file:

#include <Arduino.h>
#include "DS3231.h"
#include "MyGardenClock.hpp"
#include "sensors.hpp"
#include <LiquidCrystal.h>
#include "Keypad.h"
#include <Wire.h>

///Time object creation
DS3231 clock; //Clock object -> contains basic Real Time Clock functions
RTCDateTime dt; //Struct from DS3231 lib - contains date and time 
mG_RTC myClock; //Object that contains specific clock functions to my project like printing date and time on the lcd...

///LCD object creation
//--Pin allocation
const int rs = 2, en = 1, d4 = 4, d5 = 7, d6 = 8, d7 = 10;
//--LCD dynamic object creation
LiquidCrystal* pLCD = new LiquidCrystal(rs, en, d4, d5, d6, d7);


///Keypad parametrization and object creation
//Define col nb and row nb
const byte ROWS = 4; //four rows
const byte COLS = 4; //four columns
//define the cymbols on the buttons of the keypads
char hexaKeys[ROWS][COLS] = {
  {'1','2','3','A'},
  {'4','5','6','B'},
  {'7','8','9','C'},
  {'*','0','#','D'}
};
byte rowPins[ROWS] = {52, 50, 48, 46}; //connect to the row pinouts of the keypad
byte colPins[COLS] = {44, 42, 40, 38}; //connect to the column pinouts of the keypad
//Keypad dynamic object creation
Keypad* pKpd = new Keypad(makeKeymap(hexaKeys),rowPins,colPins,ROWS,COLS);
    
///Sensors object creation 
//WaterLvl Sensors
//--Pin allocation
const int WtrLvlAnalogPin = 8; //pin used to read value from water lvl sensor
const int WtrLvlPowerPin = 35; //pin used to power the sensor before reading a value
//--WaterLvl dynamic object creation
Water_Lvl_Sensor* pWtrLvlSens = new Water_Lvl_Sensor(WtrLvlAnalogPin,WtrLvlPowerPin); //contains the delay and millis functions that don't work

/////////// TEST FUNCTIONS ///////////
void testLCD_Kpd(Keypad*,LiquidCrystal*); //test lcd and keypad
void testWtrLvlSens(Water_Lvl_Sensor*,LiquidCrystal*); //test water lvl sensor

void setup()
{
    Serial.begin(9600);
    pLCD->begin(16,2);
    testLCD_Kpd(pKpd,pLCD);
    testWtrLvlSens(pWtrLvlSens,pLCD);  
   
}

void loop()
{
    
}
////////////////////// TEST FUNCTIONS ///////////////////////////
void testLCD_Kpd(Keypad* pKpd, LiquidCrystal* pLCD)
{
   char test;
   test ='i';
   
   pLCD->print("Hello");
   do
   {
    test = pKpd->waitForKey();
   }while(test=='i');
   pLCD->clear();
   delay(3000); //This one work well
   pLCD->print("test = ");
   pLCD->print(test);
   
}

void testWtrLvlSens(Water_Lvl_Sensor* pWLS,LiquidCrystal* plcd)
{
  int wtrLvl;
  wtrLvl = pWLS->getCurrentWaterLvlValue();
  plcd->setCursor(0,1);
  plcd->print("wtrLvl = ");
  plcd->print(wtrLvl);
  
}

Here is an extract from my cpp file:

#include <Arduino.h>
#include "sensors.hpp"
    
/********************************************
*   CLASS WATER LvL SENSOR DESCRIPTION      *
********************************************/
///Constructor
//-- Assign pin number (analog & digital)
//-- Define pinMode for VCC + set to low
//-- read and store actual waterLevelValue as initial val
Water_Lvl_Sensor::Water_Lvl_Sensor(int anPinNb, int DigPinNb)
{
  //Variable used for millis 
  unsigned long prevTime;
  unsigned long currentTime;
  const unsigned long delayTime = 100;

  Serial.begin(9600); //to debug

  //Assign analog and digital pin numbers
  analogPinNumber = anPinNb;
  VCC_digitalPinNumber = DigPinNb;
  pinMode(VCC_digitalPinNumber, OUTPUT); //Digital pin to power the sensor
  digitalWrite(VCC_digitalPinNumber,HIGH);//Turn sensor ON before reading initial value
  
  //delay(100); //not working ???
  prevTime = millis(); //get and store actual time
  do
  {
    currentTime = millis(); //get actual time
    Serial.println(currentTime); //print actual time (debug only)
  }while(currentTime<(prevTime+delayTime)); //while time spent < 100
  
  currentValue = analogRead(analogPinNumber); //read analog value
  
  digitalWrite(VCC_digitalPinNumber,LOW); //turn sensor OFF
  historyValue = currentValue; //init history val
  minValue = 0; //default value - sensor dry
  maxValue = 520; //default value - sensor under water
}

Does anyone has a solution?

I hope I've given enough details. If not, please feel free to ask.

Thanks!

1 Answer 1

10

The Arduino framework does some initializing for you before going into setup() and loop(), for example configuring Timer0 correctly for millis(), delay() and siblings. This is done in the main() function, which then after the first initializing calls setup() and loop().

But the creation of globally defined objects (and thus the execution of their constructors) is done even before the main() function. At that time neither millis(), nor delay() will work, since the hardware Timer0 is needed to be configured correctly for them to work. The hardware timer generates interrupts with a specific frequency and in the corresponding ISR (Interrupt Service Routine) the variable for millis() is incremented. If the Timer0 isn't configured, it's interrupt cannot trigger and thus millis() will stay zero. So you cannot do all that in the constructor of the object, or you always need to create the object in setup() or loop().

For this very reason most hardware related Arduino libraries use a .begin() method to initialize the hardware related stuff. That function can be called on the global object in the setup() function, when everything else is already configured correctly.

What to do now? Create a .begin() method for your class. Move all the code from the constructor (except for the pin number assignment) into this .begin() function. And then call that function in setup().

2
  • Thanks a lot for your quick answer. It really makes sense, indeed. I'll make the change and tell you if it works, but I'm confident.
    – Cyril_Ram
    Oct 1, 2021 at 10:00
  • Hi chrisl, just to let you know your solution works well. Thanks a lot.
    – Cyril_Ram
    Oct 2, 2021 at 8:01

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

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