Use of millis in sensor read

Hi,
I am working on a project in which I want to make a battery thermostat. Before each reading of the AHT21 sensor, with 300ms power the module from pin D4 and execute the radio.powerUp command, then the reading is done and a 200ms delay follows to make sure that the nrf24 sends the data.
I wrote a code, but it often happens that it reads in 10 minutes instead of 5... I think something is wrong. If someone can help me structure it better...


short int IntervCitire  = 3;   //exprimat in Minute
short int delayStart  = 300;
short int delayStop  = 200;
unsigned long currentMillis = 0;
unsigned long previousMillis_readandsendTemperatura = 0;
unsigned long previousMillis_delayStart = 0;
unsigned long previousMillis_delayStop = 0;

void loop()
{
  currentMillis = millis();
  if(( currentMillis - previousMillis_readandsendTemperatura ) > ( IntervCitire * 60000 )) {
    digitalWrite(SDA, HIGH);
    digitalWrite(SCL, HIGH);
    digitalWrite(4, HIGH);
    radio.powerUp();
            if(( currentMillis - previousMillis_delayStart ) >  delayStart)
             { readandsendTemperatura();
                      if(( currentMillis - previousMillis_delayStop ) >  delayStop)
                            {
                                  DIDR0 = 0x3F;
                                  TWCR &= ~(_BV(TWEN)); 
                                  TWCR = 0;
                                   digitalWrite(SDA, LOW);
                                   digitalWrite(SCL, LOW);
                                   digitalWrite(4, LOW);
                                   radio.powerDown();
                            previousMillis_delayStop = currentMillis; }

              previousMillis_delayStart = currentMillis;}
  
  previousMillis_readandsendTemperatura = currentMillis;}
}

The millis can't work with enclosed conditions like this way:

because after the first condition met, you resetting the
previousMillis_readandsendTemperatura
value and the cycle rewinds, leaving little chance for internal conditions to be fulfilled.

You should rewrite your code and ether use delays instead of second and third millis conditions, or rework your logic with finite state machines

Which board are you using? On AVR based boards, calculations are done using integers, not long integers. 60000 overflows what an integer can hold and gets truncated. I would start by changing 60000 to 60000UL to force the compiler to calculated with unsigned longs.

Not saying that it's the cause of your problem but it is a problem if you use an AVR based board.

ATmega328PB it's the microcontroller

I try to write in my own words what I have understood

once every 10 minutes = 600.000 milliseconds switch on power of radio-module
wait 300 milliseconds to let do the radio-module its boot-procedure
then read and send temperature-data
wait 200 milliseconds to make sure the nrf24-module has finished sending the data

wait 59,5 seconds until the next minute arrived and then repeat the above described procedure.

Is this a correct description of what you want?

This is a repeat always the same steps sequence.
Such things can be coded with a StateMachine

Your application is a nice example to explain how state-machines work

You can imagine a statemachine as a machine that has different modes of operation
in your case these operations are coded as constants used by the state-machine
You should use self-explaining names that describe SPOT-ON what the state is doing

const byte sm_wait_1minute   = 0;
const byte sm_switchPowerOn  = 1;
const byte sm_wait_300ms     = 2;
const byte sm_ReadSendTemp   = 3;
const byte sm_wait_200ms     = 4;
const byte sm_switchPowerOff = 5;

The code below has 3 useful macros for serial debug-output
this makes visible what the code is doing. At first jump over the macros and start reading at function

void myReadSendTempStateMachine() {

This code uses a IMHO more intuitive non-blocking timing function than the delay without understanding standard-example of the IDE

// MACRO-START * MACRO-START * MACRO-START * MACRO-START * MACRO-START * MACRO-START *

https://forum.arduino.cc/t/comfortable-serial-debug-output-short-to-write-fixed-text-name-and-content-of-any-variable-code-example/888298
#define dbg(myFixedText, variableName) \
  Serial.print( F(#myFixedText " "  #variableName"=") ); \
  Serial.println(variableName);
// usage: dbg("1:my fixed text",myVariable);
// myVariable can be any variable or expression that is defined in scope

#define dbgi(myFixedText, variableName,timeInterval) \
  do { \
    static unsigned long intervalStartTime; \
    if ( millis() - intervalStartTime >= timeInterval ){ \
      intervalStartTime = millis(); \
      Serial.print( F(#myFixedText " "  #variableName"=") ); \
      Serial.println(variableName); \
    } \
  } while (false);
// usage: dbgi("2:my fixed text",myVariable,1000);
// myVariable can be any variable or expression that is defined in scope
// third parameter is the time in milliseconds that must pass by until the next time a
// Serial.print is executed
// end of macros dbg and dbgi
//
// print only once when value has CHANGED
#define dbgc(myFixedText, variableName) \
  do { \
    static long lastState; \
    if ( lastState != variableName ){ \
      Serial.print( F(#myFixedText " "  #variableName" changed from ") ); \
      Serial.print(lastState); \
      Serial.print( F(" to ") ); \
      Serial.println(variableName); \
      lastState = variableName; \
    } \
  } while (false);
// MACRO-END * MACRO-END * MACRO-END * MACRO-END * MACRO-END * MACRO-END * MACRO-END *

// the states for the state-machine
const byte sm_wait_1minute   = 0;
const byte sm_switchPowerOn  = 1;
const byte sm_wait_300ms     = 2;
const byte sm_ReadSendTemp   = 3;
const byte sm_wait_200ms     = 4;
const byte sm_switchPowerOff = 5;

byte myState = sm_wait_1minute;

const unsigned long powerOnWaitTime = 300;
const unsigned long FinishSendingWaitTime = 200;
const unsigned long PausingTime = 10000; // increase to 600000 for 10 minutes

unsigned long Waiting60SecsTimer;
unsigned long Waiting_ms_Timer;

void myReadSendTemp() {
  Serial.println("Read and send temperature");
  Serial.println();
}

void SwitchPowerOn() {
  Serial.println("Switching Power on");
  Serial.println();
}

void SwitchPowerOff() {
  Serial.println("Switching Power OFF");
  Serial.println();
}

void myReadSendTempStateMachine() {
  dbgc("statechange",myState);
  
  switch (myState) {
  
    case sm_wait_1minute:
      // only once every 2000 milliseconds print 
      dbgi("waiting ",millis() - Waiting60SecsTimer,2000);
      
      if ( TimePeriodIsOver(Waiting60SecsTimer, PausingTime) ) {
        Serial.println();
        Serial.println();
        Serial.println("next Time to read and send temp has come");
        myState = sm_switchPowerOn;
      }
      break; // immidiately jump down to End-of-Switch


    case sm_switchPowerOn:
      // lines of code to switch power on
      SwitchPowerOn();
      Serial.println("start waiting"); 
      Waiting_ms_Timer = millis();
      myState = sm_wait_300ms;
      break; // immidiately jump down to End-of-Switch

    case sm_wait_300ms:
      // only once every 100 milliseconds print 
      dbgi("waiting",millis() - Waiting_ms_Timer,100);
      
      if ( TimePeriodIsOver(Waiting_ms_Timer, powerOnWaitTime) ) {
        Serial.println("waiting over"); 
        myState = sm_ReadSendTemp;
      }
      break; // immidiately jump down to End-of-Switch


    case sm_ReadSendTemp:
      // code to read and sedn temperatur
      myReadSendTemp();
      Serial.println("start waiting");
      Waiting_ms_Timer = millis();
      myState = sm_wait_200ms;
      break; // immidiately jump down to End-of-Switch


    case sm_wait_200ms:
      // only once every 100 milliseconds print 
      dbgi("waiting",millis() - Waiting_ms_Timer,100);

      if ( TimePeriodIsOver(Waiting_ms_Timer, FinishSendingWaitTime) ) {
        Serial.println("waiting over");
        myState = sm_switchPowerOff;
      }
      break; // immidiately jump down to End-of-Switch


    case sm_switchPowerOff:
      // code to switch power off
      SwitchPowerOff();
      Serial.println("initialise waiting for next sending");
      Serial.println();
      Serial.println();
      Waiting60SecsTimer = millis();
      myState = sm_wait_1minute;
      break; // immidiately jump down to End-of-Switch
  } // End-of-Switch

}


void PrintFileNameDateTime() {
  Serial.println( F("Code running comes from file ") );
  Serial.println( F(__FILE__) );
  Serial.print( F("  compiled ") );
  Serial.print( F(__DATE__) );
  Serial.print( F(" ") );
  Serial.println( F(__TIME__) );
}


// easy to use helper-function for non-blocking timing
boolean TimePeriodIsOver (unsigned long &startOfPeriod, unsigned long TimePeriod) {
  unsigned long currentMillis  = millis();
  if ( currentMillis - startOfPeriod >= TimePeriod ) {
    // more time than TimePeriod has elapsed since last time if-condition was true
    startOfPeriod = currentMillis; // a new period starts right here so set new starttime
    return true;
  }
  else return false;            // actual TimePeriod is NOT yet over
}

unsigned long MyTestTimer = 0;                   // Timer-variables MUST be of type unsigned long
const byte    OnBoard_LED = 13;


void BlinkHeartBeatLED(int IO_Pin, int BlinkPeriod) {
  static unsigned long MyBlinkTimer;
  pinMode(IO_Pin, OUTPUT);

  if ( TimePeriodIsOver(MyBlinkTimer, BlinkPeriod) ) {
    digitalWrite(IO_Pin, !digitalRead(IO_Pin) );
  }
}


void setup() {
  Serial.begin(115200);
  Serial.println("Setup-Start");
  PrintFileNameDateTime();
}


void loop() {
  BlinkHeartBeatLED(OnBoard_LED, 100);
  myReadSendTempStateMachine();
}

The serial output looks like this

09:58:43.680 -> Setup-Start
09:58:43.680 -> Code running comes from file 
09:58:43.680 -> F:\myData\Arduino\pilutz-StateMachine-001\pilutz-StateMachine-001.ino
09:58:43.680 ->   compiled Dec 28 2022 09:58:37
09:58:45.679 -> "waiting " millis() - Waiting60SecsTimer=2000
09:58:47.668 -> "waiting " millis() - Waiting60SecsTimer=4000
09:58:49.650 -> "waiting " millis() - Waiting60SecsTimer=6000
09:58:51.671 -> "waiting " millis() - Waiting60SecsTimer=8000
09:58:53.679 -> "waiting " millis() - Waiting60SecsTimer=10000
09:58:53.679 -> 
09:58:53.679 -> 
09:58:53.679 -> next Time to read and send temp has come
09:58:53.679 -> "statechange" myState changed from 0 to 1
09:58:53.679 -> Switching Power on
09:58:53.679 -> 
09:58:53.679 -> start waiting
09:58:53.679 -> "statechange" myState changed from 1 to 2
09:58:53.679 -> "waiting" millis() - Waiting_ms_Timer=6
09:58:53.784 -> "waiting" millis() - Waiting_ms_Timer=103
09:58:53.885 -> "waiting" millis() - Waiting_ms_Timer=203
09:58:53.988 -> waiting over
09:58:53.988 -> "statechange" myState changed from 2 to 3
09:58:53.988 -> Read and send temperature
09:58:53.988 -> 
09:58:53.988 -> start waiting
09:58:53.988 -> "statechange" myState changed from 3 to 4
09:58:53.988 -> "waiting" millis() - Waiting_ms_Timer=6
09:58:54.091 -> "waiting" millis() - Waiting_ms_Timer=103
09:58:54.193 -> waiting over
09:58:54.193 -> "statechange" myState changed from 4 to 5
09:58:54.193 -> Switching Power OFF
09:58:54.193 -> 
09:58:54.193 -> initialise waiting for next sending"statechange" myState changed from 5 to 0
09:58:55.676 -> "waiting " millis() - Waiting60SecsTimer=1484
09:58:57.648 -> "waiting " millis() - Waiting60SecsTimer=3484
09:58:59.654 -> "waiting " millis() - Waiting60SecsTimer=5484
09:59:01.662 -> "waiting " millis() - Waiting60SecsTimer=7484
09:59:03.635 -> "waiting " millis() - Waiting60SecsTimer=9484
09:59:04.188 -> 
09:59:04.188 -> 
09:59:04.188 -> next Time to read and send temp has come
09:59:04.188 -> "statechange" myState changed from 0 to 1
09:59:04.188 -> Switching Power on

best regards Stefan

Hello pilutz

Do you have experience in coding C++.

My recomendation is to design a time controlled function sequencer.

Like an egg timer that reminds you to start an action and thus controls a sequence of activities.

Have a nice day and enjoy coding in C++.

In addition to other issues you are only reading millis() one time at the beginning of the loop, then comparing all your conditions to that one value. If the first condition is met obviously the two inner conditions will always be met, so your "delays" are not working at all. Similarly you are also setting all the previous values you use to compare to that same value at the end of each condition.

You need to entirely rework your code...

short int IntervCitire  = 3;   //exprimat in Minute
short int delayStart  = 300;
short int delayStop  = 200;
unsigned long currentMillis = 0;
unsigned long previousMillis_readandsendTemperatura = 0;
unsigned long previousMillis_delayStart = 0;
unsigned long previousMillis_delayStop = 0;
void loop()
{

  if(!radioUp && ( millis() - previousMillis_readandsendTemperatura ) > ( IntervCitire * 60000UL )) {
    powerUp();
    radioUp = true;

  previousMillis_delayStart = millis();//ca sa se reseteze timpul
  }


if(radioUp && !dataSended  && (( millis() - previousMillis_delayStart ) >  delayStart)){
    readandsendTemperatura();
    dataSended = true;
    previousMillis_delayStop = millis();//ca sa se reseteze timpul
 }
if(radioUp && dataSended  && (( millis() - previousMillis_delayStop ) >  delayStop)){
    powerDown();
    radioUp = false;
    dataSended = false;
    previousMillis_readandsendTemperatura = millis();//ca sa se reseteze timpul
 }
}
void powerUp()
{
    digitalWrite(SDA, HIGH);
    digitalWrite(SCL, HIGH);
    digitalWrite(4, HIGH);
    radio.powerUp();
}

void powerDown()
{
digitalWrite(SDA, LOW);
digitalWrite(SCL, LOW);
digitalWrite(4, LOW);
radio.powerDown();
}

This it`s a working code... thanks all

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.