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!

Cyril_Ram
  • 53
  • 2

1 Answers1

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().

chrisl
  • 16,622
  • 2
  • 18
  • 27