Time limitations on Teensy 3.5 wakeup alarms?

Status
Not open for further replies.

natef

Member
Hi everyone, thanks so much for all the information on this forum, it's been invaluable as I've been working on a few different Teensy projects. I'm currently working on a water sampling device that utilizes the Teensy 3.5 microcontroller to run code that lets the user set a predetermined date and time to power pumps for collecting water samples. The point of this project is to collect samples while not on-site. The user sets the alarms and the code operates in a loop that calculates time to the first set alarm, and then using the Teensy RTC it sleeps (snooze.hibernate, thanks to Duff for that one!) until time to wake up and run the pump. Once done running the Teensy recalculates time to sleep and does so until next alarm. The sampler has worked great for short term deployments (hours-days), but on the longer deployments (weeks, 2 weeks for sure, maybe less) it's getting stuck and not following through on the loop. My suspicion is that there is some limit to the Teensy's RTC in terms of how long it can sit and count down seconds to waking, or maybe the RTC is more limited in hibernate mode. I know the Teensy has a 32 bit processor, and I couldn't find any obvious issues in looking over the spec. sheet, so I wouldn't have thought this would be an issue, but given that the short-term alarms work fine, it seems like the length of time for alarms is the issue. Given the depth of knowledge on this forum I'm hoping some of you might be able to weigh in and give your two cents. Could the RTC be my problem? Does the snooze library reduce the RTC capabilities? Or is there any reason why neither of those could be the issue? Thanks for any help or input!
 
The sampler has worked great for short term deployments (hours-days), but on the longer deployments (weeks, 2 weeks for sure, maybe less) it's getting stuck and not following through on the loop.

deployment time is not the issue, but what is the max sleep time, or better what is the sleep time where it does not work anymore?
Also what means it is getting stuck? does it not wake-up od does the loop() freeze;
 
WMXZ thanks for responding! That’s a factor I haven’t pinpointed yet. One week is ok, two weeks is not. Somewhere in between is where it fails. The failure is the loop freezing. Once it’s programming mode is reactivated/woke up with a reed switch it goes back to normal. I’ve got a change to the code I’m testing right now that I’m hoping will solve this (but I won’t know for a few weeks still). The fix approaches the problem from it solely being a code issue and I’m hopeful it will work, but I just wanted to check in and see if someone could tell me I was playing an impossible game here.
 
Once it’s programming mode is reactivated/woke up with a reed switch it goes back to normal.

makes no sense.
the reed switch is doing what? switching the programming or do you have the reed on reset pin?

for further comments, we need to see a complete program that generates the effect.
 
The reed switch is programmed as an interrupt for the Teensy when it's in sleep mode (snooze.hibernate), bringing the programming back to the start menu where alarms are set by the user.

I realize I misrepresented part of my issue. The first wakeup alarm set always seems to go off ( I just had one go off after 3 weeks), it's the recalculation that seems to be time limited. If the second wakeup alarm is set to go off 24 or 48 hrs later, no problem, but when it's set for two weeks later it doesn't ever go off. I'm confused about why time is acting as a variable since the program can function over a few days without issue and wakeup right on time, but not for long-term deployments. I understand the need for code to receive analytical assistance, if it comes to that I'll parse it down and stick it on here.
 
This is very interesting, so the first RTC alarm wakeup always works even if it's say three weeks away but after going back to sleep (for like a two weeks or longer), does it fail every time? Can you share how you calculating the wakeup times and how you set it using Snooze? I never tested this long of a sleep before.

Is it battery powered? If so do you monitor the battery and share that data with us?
 
This is very interesting, so the first RTC alarm wakeup always works even if it's say three weeks away but after going back to sleep (for like a two weeks or longer), does it fail every time? Can you share how you calculating the wakeup times and how you set it using Snooze? I never tested this long of a sleep before.

Yes, I have yet to record a successful second alarm on the long deployments, so it seems to fail every time. Below is the section of the code for calculating the sleep times, but the general scheme is: Determine if Alarm A or Alarm B is set to go off first, then calculate the time until the earlier alarm is to go off. Snooze until then. RTC wakes up Teensy, pump goes off. A loop is now running that will go until the pump time is up, and then the pump turns off, the sleep time is recalculated, and the system is set to Snooze again until the newly set next alarm. If both alarms have already fired, and it's not set to fire every day (a daily mode that hasn't given any issues) it will wake up every month and go back to sleep. It seems to be getting stuck without a second alarm to wake up the system when it goes back to Snooze.

Is it battery powered? If so do you monitor the battery and share that data with us?

The sampler is battery powered and it does log battery voltage every time it samples. With the Teensy's low draw in Snooze (seriously, thanks for that!) we don't usually see a drop of more than 0.2-0.4 volts, if even that much, over the month long deployment time.
Here's an example of the log that is input on the microSD card each time it samples:

9:0:1,2/5/2019,19.75,10.89,1,0
10:0:1,4/5/2019,19.31,10.85,3,1


the data translates to:

Pump activated at 9:00:01 AM, May 2, 2019, Temperature=19.75, Voltage=10.89, Pump A started, Pump B waiting
Pump activated at 10:00:01 AM, May 4, 2019, Temperature=19.31, Voltage=10.85, Pump A done pumping and off, Pump B started


Here's the code:

Code:
void samplingMode(){
  //PUMP A
  if (aAlarmFlag == 0){//Execute the following if aAlarmFlag is 0
    digitalWrite(pumpAPin, LOW);//Make sure pumpA is off
    if ((aSecTime != 0)&&(nowSecTime > aSecTime)){aAlarmFlag = 1;}//If the time now is past the time pumpA should go off, set aAlarmFlag to 1
  }
  if (aAlarmFlag == 1){//Execute the following if aAlarmFlag is 1
    digitalWrite(pumpAPin, HIGH);//Turn on pumpA
    aEndTime = nowSecTime + (int(sampleVolume/(pumpACalibration/10.0))); //Calculate the amount of time that pumpA should run
  }
  if (aAlarmFlag == 2){//Execute the following if aAlarmFlag is 2
    digitalWrite(pumpAPin, HIGH);//Make sure pumpA is on
    if (nowSecTime > aEndTime){//Execute the following if duration of pumpA time is greater than the amount of time pumpA should run
      if (sampleMode == 1){aAlarmFlag = 3;}//If the sample mode is set to once, set aAlarmFlag to 3, i.e., never to run again.
      if (sampleMode == 0){//If the sample mode is set to daily, calculate the new aSecTime, the time for the aAlarm to wake
        aSecTime = (aSecTime + 86400);// Add 24 hrs to the time that A fires (aSecTime)
        tmElements_t updateAtm;//This is a time elements variable named updateAtm
        breakTime(aSecTime, updateAtm);// Break aSecTime into the time elements variable updateTm
        aHr = updateAtm.Hour;// Name the hour that pump A needs to fire aHr 
        aMin = updateAtm.Minute;// Name the minute that pump A needs to fire aMin 
        aSec = updateAtm.Second;// Name the second that pump A needs to fire aSec 
        aDay = updateAtm.Day;// Name the day that pump A needs to fire aDay
        aMon = updateAtm.Month;// Name the month that pump A needs to fire aMon 
        aYr = updateAtm.Year-30;// Name the year that pump A needs to fire aYr. Minus thirty because value is years since 1970, and we are dealing with years since 2000
        aAlarmFlag = 0; //Set aAlarmFlag to 0, so that it will restart and fire again when it wakes up at the appropriate time
        digitalWrite(pumpAPin, LOW);
      }
    }
  }
  if (aAlarmFlag == 3){digitalWrite(pumpAPin, LOW);}//If aAlarmFlag is 3, then make sure pumpA is off

  //PUMP B
  if (bAlarmFlag == 0){//Execute the following if bAlarmFlag is 0
    digitalWrite(pumpBPin, LOW);//Make sure pumpB is off
    if ((bSecTime != 0)&&(nowSecTime > bSecTime)){bAlarmFlag = 1;}//If the time now is past the time pumpB should go off, set bAlarmFlag to 1
  }
  if (bAlarmFlag == 1){//Execute the following if bAlarmFlag is 1
    digitalWrite(pumpBPin, HIGH);//Turn on pumpB
    bEndTime = nowSecTime + (int(sampleVolume/(pumpBCalibration/10.0))); //Calculate the amount of time that pumpB should run
  }
  if (bAlarmFlag == 2){//Execute the following if bAlarmFlag is 2
    digitalWrite(pumpBPin, HIGH);//Make sure pumpB is on
    if (nowSecTime > bEndTime){//Execute the following if duration of pumpB time is greater than the amount of time pumpB should run
      if (sampleMode == 1){bAlarmFlag = 3;}//If the sample mode is set to once, set bAlarmFlag to 3, i.e., never to run again.
      if (sampleMode == 0){//If the sample mode is set to daily, calculate the new bSecTime, the time for the aAlarm to wake
        bSecTime = (bSecTime + 86400);// Add 24 hrs to the time that B fires (bSecTime)
        tmElements_t updateBtm;//This is a time elements variable named updateBtm
        breakTime(bSecTime, updateBtm);// Break bSecTime into the time elements variable updateTm
        bHr = updateBtm.Hour;// Name the hour that pump B needs to fire bHr 
        bMin = updateBtm.Minute;// Name the minute that pump B needs to fire bMin 
        bSec = updateBtm.Second;// Name the second that pump B needs to fire bSec 
        bDay = updateBtm.Day;// Name the day that pump B needs to fire bDay
        bMon = updateBtm.Month;// Name the month that pump B needs to fire bMon 
        bYr = updateBtm.Year-30;// Name the year that pump B needs to fire bYr. Minus thirty because value is years since 1970, and we are dealing with years since 2000
        bAlarmFlag = 0; //Set bAlarmFlag to 0, so that it will restart and fire again when it wakes up at the appropriate time
        digitalWrite(pumpBPin, LOW);
      }
    }
  }
  if (bAlarmFlag == 3){digitalWrite(pumpBPin, LOW);}//If aAlarmFlag is 3, then make sure pumpB is off
}

void checkSleep(){//Determines if the sampler should go back to sleep
  if ((aAlarmFlag == 0)&&(bAlarmFlag == 3)&&(aSecTime>nowSecTime+60)){goToSleep();}
  if ((aAlarmFlag == 3)&&(bAlarmFlag == 0)&&(bSecTime>nowSecTime+60)){goToSleep();}
  if ((aAlarmFlag == 0)&&(bAlarmFlag == 0)&&(aSecTime>nowSecTime+60)&&(bSecTime>nowSecTime+60)){goToSleep();}
  if ((aAlarmFlag == 3)&&(bAlarmFlag == 3)){goToSleep();}
}

void goToSleep(){//Puts the sampler to sleep
  long sleepSecTime;
  if ((aAlarmFlag != 3)&&(bAlarmFlag != 3)&&(aSecTime < bSecTime)){sleepSecTime = aSecTime - nowSecTime;}
  if ((aAlarmFlag != 3)&&(bAlarmFlag != 3)&&(bSecTime < aSecTime)){sleepSecTime = bSecTime - nowSecTime;}
  if ((aAlarmFlag != 3)&&(bAlarmFlag != 3)&&(aSecTime == bSecTime)){sleepSecTime = aSecTime - nowSecTime;}
  
  if ((aAlarmFlag != 3)&&(bAlarmFlag == 3)) {sleepSecTime = aSecTime - nowSecTime;}
  if ((aAlarmFlag == 3)&&(bAlarmFlag != 3)) {sleepSecTime = bSecTime - nowSecTime;}
  if ((aAlarmFlag == 3)&&(bAlarmFlag == 3)) {sleepSecTime = 2628000;}//sleep one month, will wake and go to sleep again after that one month

  hrToSleep = int(sleepSecTime/3600);// calculates the hours to sleep
  minToSleep = int((sleepSecTime-(3600*hrToSleep))/60);// calculates the remaining minutes to sleep
  secToSleep = int(sleepSecTime-(3600*hrToSleep)-(60*minToSleep));// calculates the remaining seconds to sleep
  
  tempSensor.shutdown();// Shut down the temperature sensor
  delay (200);  
  digitalWrite(oledPowerPin, LOW);// Turn off the power to the OLED
  digitalWrite(tempPowerPin, LOW);// Turn off the power to the temperature sensor
  digitalWrite(irPowerPin, LOW);// Turn off the power to the IR sensor
  digitalWrite(voltageReadPin, LOW);// Turn off the power to the voltage read pin A15
  alarm.setRtcTimer(hrToSleep, minToSleep, secToSleep);// Sets the RTC TIMER with the appropriate amount of hour, min, sec to sleep
  delay (100);
  int who; 
  who = Snooze.hibernate( config_teensy35 );// wake up the Teensy and identify who woke it up
  if (who == REED_INTERRUPT_PIN){
    delay(500);
    if (digitalRead(REED_INTERRUPT_PIN)==HIGH){
      delay(500);
      if (digitalRead(REED_INTERRUPT_PIN)==HIGH){
        digitalWrite(oledPowerPin, HIGH);// Power on the oled    
        digitalWrite(tempPowerPin, HIGH);// Power on the temperature sensor
        digitalWrite(irPowerPin, HIGH);// Power on the IR sensor
        digitalWrite(voltageReadPin, HIGH);// Turn on the power to the voltage read pin A15

        tempSensor.wake();// Wake up the temperature sensor
        delay(200);
        display.begin(SSD1306_SWITCHCAPVCC, 0x3C);// Start the OLED back up again
        display.clearDisplay(); display.setTextSize(2); display.setTextColor(WHITE,BLACK); display.setCursor(0,16); display.println("Interrupt");// Put "Interrupt" on the first line of the OLED
        display.display();// Update the OLED display
        menu = 0; pos = 0;//Sets menu and position to 0
        readSampleParamArray();
        delay(1000);  
      }
      if (digitalRead(REED_INTERRUPT_PIN)==LOW){menu = 9;}
    }
    if (digitalRead(REED_INTERRUPT_PIN)==LOW){menu = 9;}
  }
  else{//alarm pin wake up
    digitalWrite(oledPowerPin, HIGH);// Power on the oled    
    digitalWrite(tempPowerPin, HIGH);// Power on the temperature sensor
    digitalWrite(voltageReadPin, HIGH);// Turn on the power to the voltage read pin A15

    tempSensor.wake();// Wake up the temperature sensor
    display.begin(SSD1306_SWITCHCAPVCC, 0x3C);// Start the OLED back up again
    delay(500);    
    getVoltage();
    menu = 9;//finishes out sleep and sends back to loop running sampling mode (case 9)
  }
}

void samplingDisplay(){//Updates the display during sampling
  if ((aAlarmFlag == 2)&&(bAlarmFlag !=2)){display.clearDisplay(); display.setTextSize(2); display.setTextColor(WHITE,BLACK); display.setCursor(0,16); display.println("Pump A");}// Display Pump A
  if ((aAlarmFlag != 2)&&(bAlarmFlag ==2)){display.clearDisplay(); display.setTextSize(2); display.setTextColor(WHITE,BLACK); display.setCursor(0,16); display.println("Pump B");}// Display Pump B
  if ((aAlarmFlag == 2)&&(bAlarmFlag ==2)){display.clearDisplay(); display.setTextSize(2); display.setTextColor(WHITE,BLACK); display.setCursor(0,16); display.println("Both Pumps");}// Display Both Pumps
  if ((aAlarmFlag != 2)&&(bAlarmFlag !=2)){display.clearDisplay(); display.setTextSize(2); display.setTextColor(WHITE,BLACK); display.setCursor(0,16); display.println("Wait...");}// Display Wait...
  display.display();// Update the OLED display
}
 
Ok looking at the setRtcTimer function in Snooze the hours, minutes and seconds are defined uint8_t so the maximum value can only be 255 for each, so 14 days would be 336 hours! Since I'm doing a complete update to Snooze for the upcoming T4 it's not in state that you can download and use from my GitHub currently so if you can change the your copy of Snooze setRtcTimer functions hours, mins, secs to uint32_t and see if that works, remember to change both the SnoozeAlarm.h and SnoozeAlarm.cpp files.
 
Duff I will try that right now and see what comes of it and follow up on here, thanks for the insight and guidance, you might've just saved my sanity!
 
Duff, follow up on the uint8 -> uint32 fix. I changed SnoozeAlarm.h and SnoozeAlarm.cpp files and it looks like it worked! Thank you for your help! I still don't understand why my initial alarm could work for a period longer than 255 hours since my second alarm clearly couldn't, but post-fix it's worked on three different machines with a second alarm two weeks after the first. Maybe the uint8 was still being used during the alarm reset after the first alarm finished
Code:
alarm.setRtcTimer(hrToSleep, minToSleep, secToSleep);
even though the Teensy had been woken up by the first alarm and should've been back to uint32? Just guessing now, I can live with the mystery but if anyone has other thoughts it is interesting. Thanks again for helping me move forward!
 
Status
Not open for further replies.
Back
Top