Author Topic: inaccurate cyclic timer using millis() anf RFM69HW  (Read 1395 times)

DRCO

  • NewMember
  • *
  • Posts: 19
inaccurate cyclic timer using millis() anf RFM69HW
« on: May 30, 2021, 11:26:42 PM »
Hi everyone. Is there any reason that millis() might become inaccurate on a Moteino or 328p-based configuration due to the way the radio uses interrupts? I have a really simple timer that I just can't seem to get accurate (varying from minutes to hours) and it is just incredibly unreliable. Is it better to look at an external timer like a TPL5110?

Jason

  • Jr. Member
  • **
  • Posts: 57
Re: inaccurate cyclic timer using millis() anf RFM69HW
« Reply #1 on: May 31, 2021, 10:49:36 AM »
1. millis() returns an unsigned long. By chance are you using an int or long to store those values.
Code: [Select]
int myTime1 = millis(); // Can cause problems
long myTime2 = millis(); // Can cause problems
unsigned long myTime3 = millis(); // Correct usage
 

2. millis() uses interrupts. Are you disabling interrupts anywhere?

3. *Disclaimer Not an Expert* - As far as I can tell no Low Power Lab libraries disable interrupts. I searched the GitHub repository for "cli" and didn't find it used. I then searched for "interrupt" and didn't find anywhere where interrupts are disabled.

Jason

  • Jr. Member
  • **
  • Posts: 57
Re: inaccurate cyclic timer using millis() anf RFM69HW
« Reply #2 on: May 31, 2021, 10:58:35 AM »
For most use cases millis() should be plenty accurate. Definitely not minutes or hours off.

I originally planed on having an external timer for my setup but then realized it wasn't worth it for me. The data received by the gateway is timestamped and that is good enough for me for now.

Possibly in the future, was thinking of every night say midnight, having the motes check in for the current time from the gateway and adjust for any possible offset. That would allow for the motes to accurately timestamp and save the data in the case there are dropped packets. I digress from your question.

Basically, unless needed for a very specific reason, adding a timer adds more complexity, power requirements, size and another point of failure. Is it worth it for you?

Felix

  • Administrator
  • Hero Member
  • *****
  • Posts: 6866
  • Country: us
    • LowPowerLab
Re: inaccurate cyclic timer using millis() anf RFM69HW
« Reply #3 on: June 01, 2021, 12:02:59 PM »
DRCO,

RFM69 interrupts have nothing to do with millis(). With or without RFM69 onboard the result of sleeping (or "accuracy") will be the same.
If you put your board to sleep, then millis() will stop working because the atmega328p timer used for millis() is stopped. You have to account for the slept time and compensate.
You can also use the RFM69 crystal for sleeping and save ~2-3uA over watchdog (periodic) sleep of the MCU, see ListenModeSleep example in the library.
This is a much more accurate crystal for timing purposes, it is used for frequency synthesis in the radio module itself and can be leveraged to generate interrupts at precise timings.

DRCO

  • NewMember
  • *
  • Posts: 19
Re: inaccurate cyclic timer using millis() anf RFM69HW
« Reply #4 on: June 02, 2021, 06:13:36 PM »
Thanks everyone, I am in some, cases getting 50 minute discrepancies. I am using unsigned longs on all variables. I am not using interrupts in this code - and I'm not  using listen mode. I have a single timer instance for 2 hours that is cycled by incrementing a variable to start a pump, run it for 2 hours - then stop it for 4 hours while a bore is replenished.
I am using the moteino code with a DIL 328p (28 pin) 3.3v at 8mHz with a RFM69HW radio

The relevant code goes:

//Declared

unsigned long TimerZero = 0;
const unsigned long TimerminuTes = 7200000; // 2 hours
volatile int TimerCounter = 0;
int opMode = 0;
int FourHourPrint =0;
unsigned long TimerZero = 0;


//Loop

/*
this code controls the 2 hour pump / 4 hours off
*/
  if(opMode ==0){// opMode ==0 depicts received radio message to stop the pump (tank full)
      TimerCounter=0;// timer counter is a cyclic condition to stop and start the pump after a radio
                    // message has indicated the tank needs filling
                    // TimerCounter=0; also included after actual radio message received
  }
   
  if((opMode ==1)&&(TimerCounter ==0)){//received incoming pump now tx after transition from full
          Serial.println("pumping now - start timer");
           delay(10);     
          PumpStart();// function that starts pump
           TimerCounter++;//=1
           FourHourPrint =0;// condition that prints end of four hour replenish cycle
      }
          unsigned long currentMillis2 = millis();
           if ((currentMillis2 - TimerZero) >= TimerminuTes ) { // waits for this interval
           TimerZero = currentMillis2;
           TimerCounter++;//=2 from a zero value start cycle
      }
         if((TimerCounter == 2)&& (FourHourPrint ==0)){
             PumpStop();// function that stops pump
             Serial.println("initial pump time elapsed - stop for replenish");
              delay(10);
              FourHourPrint =1;
      // the cyclic TimerCounter will increment twice (4 hours)  TimerCounter = 4 
     }
     if(TimerCounter == 4){ // start all over again with the 2/4 on/off cycle
         TimerCounter=0;   
   }