Updating Bounce Objects After Wakeup from Snooze

Status
Not open for further replies.

RFModulator

Member
I am finishing up the development of a prototype using the Teensy 3.6. I have some pushbuttons and toggle switches that I debounce using the Bounce library. Occasionally, a button's action that is called by Bounce's risingEdge or falllingEdge is not triggered when the device comes out of hibernate. FWIW, I also use an AlarmTimer in my SnoozeBlock.

I saw this example code on Duff's Github page (Low_Power_Push_Button_Hold_SD_Logger.ino, lines 109-117) and I wonder if my problem might be because I don't perform the bolded operations:

Code:
    [b]// you need to update bounce before sleeping.[/b]
    button.update();
    // put Teensy into low power sleep mode
    Snooze.hibernate( config );
    digitalWriteFast(LED_BUILTIN, HIGH);
    // debounce timer
    elapsedMillis timeout = 0;
    [b]// after waking must update Bounce for longer than debounce time[/b]
    while (timeout < 6) button.update();

Are these two statements true because of the details of this particular program, or are these things always true when using Bounce in conjunction with Snooze? If the latter is true, I'm curious as to why that is.

By the way - I am very happy with the Snooze library! Switching my prototype from an Arduino Mega + a bunch of shields over to a Teensy with all the built-in functionality, as well as all the other improvements, has been a great decision :)

Thanks!
 
It's because Bounce uses millis for debouncing that gets paused (millis) when hibernating. Maybe you can setup Bounce in a different way but this is what worked for me so I would probably do the same in your program. This would seem to me why you're missing state changes because Bounce needs to resync in sense when waking up.
 
Thanks Duff, I will give that a try shortly and I'll let you know how it goes.

The only other thing that tripped me up when using the Snooze library is that the TimeLib clock is not automatically updated when the Teensy 3.6 wakes up from hibernate. I correct this by doing setTime(Teensy3Clock.get()) immediately after waking up. It took me a while to figure out why my timed events weren't happening at the right times. I didn't come across this when Googling so I figured I should mention it here in case somebody else is looking for the answer to this problem.
 
I looked at this a bit closer, and it appears that maybe 50% of the time, the Teensy is not waking up from hibernate when an INPUT_PULLUP pin is pulled to ground via a toggle switch. It never seems to fail to wake up from a transition from LOW back to HIGH through the internal pullup. I'm seeing this on two different pins where I have toggle switches, and on two different 3.6s I have available for testing. My SnoozeDigital is set to CHANGE for these pins.

Does anyone have any suggestions as to anything I could do to improve the performance? Is there reason to think a lower-value external pullup resistor might help?
 
Last edited:
I looked at this a bit closer, and it appears that maybe 50% of the time, the Teensy is not waking up from hibernate when an INPUT_PULLUP pin is pulled to ground via a toggle switch. It never seems to fail to wake up from a transition from LOW back to HIGH through the internal pullup. I'm seeing this on two different pins where I have toggle switches, and on two different 3.6s I have available for testing. My SnoozeDigital is set to CHANGE for these pins.

Does anyone have any suggestions as to anything I could do to improve the performance? Is there reason to think a lower-value external pullup resistor might help?
Can you post an example that shows this? Have you measured the voltage on that pin?
 
analogRead shows the pin reading 962 when the toggle switch is open, it reads 0 when the toggle switch is closed. I see the same with my meter.

I've attached an example sketch. I'm running it with a Tera Term Serial console open.

There's one thing I don't understand. When the Teensy does wake up from a transition to HIGH (which happens ~50% of the time), it rarely prints any output to the Serial console. I'm stumped, but I'm also overworked and I've been overlooking some obvious things lately....
 

Attachments

  • SnoozeTest.ino
    655 bytes · Views: 82
analogRead shows the pin reading 962 when the toggle switch is open, it reads 0 when the toggle switch is closed. I see the same with my meter.
This looks normal if your measuring with 10bit adc?

There's one thing I don't understand. When the Teensy does wake up from a transition to HIGH (which happens ~50% of the time), it rarely prints any output to the Serial console. I'm stumped, but I'm also overworked and I've been overlooking some obvious things lately....
So is the problem you are seeing is the usb serial only print every other time? If so try this sketch below, I setup momentary button switch like *this and just pressed the button 30 times and sucessfully woke up and printed to the serial monitor. Usb and sleeping can be problematic, SnoozeUSBSerial driver still needs more work but it works ok now.
Code:
#include <Snooze.h>


SnoozeDigital digital;
SnoozeUSBSerial usb;
SnoozeBlock config(usb, digital);
int who;


void setup() {
  pinMode(16, INPUT_PULLUP);
  pinMode(LED_BUILTIN, OUTPUT);
  digitalWrite(LED_BUILTIN, HIGH);
  digital.pinMode(16, INPUT_PULLUP, CHANGE);
  Serial.begin(9600);
  while (!Serial);
  delay(20);
  Serial.println("Starting...");
  delay(200);


}


void loop() {
  digitalWrite(LED_BUILTIN, LOW);
  who = Snooze.hibernate(config);
  // wait for serial monitor
  elapsedMillis time = 0;
  while (!Serial && time < 1000) {
    Serial.write(0x00);// print out a bunch of NULLS to serial monitor
    digitalWriteFast(LED_BUILTIN, HIGH);
    delay(30);
    digitalWriteFast(LED_BUILTIN, LOW);
    delay(30);
  }
  // normal delay for Arduino Serial Monitor
  delay(200);
  Serial.print(digitalRead(16));
  Serial.print(" Wakeup pin: ");
  Serial.println(who);
  delay(100); //to give time for serial to come back up
}
 
Thanks for letting me know about how to deal with the Serial problem! I am very glad I can get rid of those ridiculous delays.

However, the larger issue (which I didn't clearly articulate) is that the Teensy isn't waking up about 25-50% of the time (as evidenced by the LED not lighting and serial output not occuring) when I flip the toggle switch and cause the pin to go from LOW to HIGH. It seems 100% reliable toggling from HIGH to LOW. When I use a pushbutton, I don't see this problem at all (although my pushbuttons currently on hand are junk and they're quite bouncy). Since it just seems to be an issue with the toggle switch, and I wonder if it's because there's less contact bouncing. Maybe the transition occurs too fast for the Teensy to catch it?

I've uploaded the serial console showing the output generated by me flipping the switch about once every 5 seconds. You'll see it tends to miss several transitions to 1 in a row, but never misses a transition to 0.
 

Attachments

  • toggle.txt
    1.3 KB · Views: 90
I changed the code to try using sleep instead of hibernate. Now, the LED turns on with 100% reliability while I flicked the switch for ~4 minutes. Occasionally the Serial console is not updated, but that isn't a big deal to me since my device doesn't require Serial for anything critical. Maybe that could be improved, but I didn't play around with writing more nulls or extending the delays.

As another test, I switched back to hibernate and changed my SnoozeDigital to RISING. I also see similar results as I described in Post #8.

So, it seems to me like maybe this problem doesn't affect all Snooze modes. I have to run my devices on solar-charged batteries since most of them will be installed miles from commercial power, so using hibernate is extremely appealing because I'm designing for ultra-low current draw.

FWIW, these are the toggle switches I'm using:

https://www.amazon.com/10pcs-MTS-102-125VAC-Toggle-Switches/dp/B00YHHDO6Y
 
I don't have a toggle switch handy to try out but I can get one tomorrow and let you know what I find. I know the momentary switches work fine here for me in all modes using INPUT_PULLUP.
 
That works for me on T_3.6 - Using TyCommander as Serial Monitor on Win 10 ( MHz 72 & 180 & 240 ). With that I minimized wake and wait time - it wakes and prints and sleeps in 360ms. With the post #7 the run time looks like 620ms with minor edit to print that - including the Serial.flush() - adding more output made it less repeatable. I shortened various times and added a flush before sleep. I used the same AdaFruit button switch - soldered a pair of wires to plug into soldered female headers.

On Windows neither p#7 code nor that below wakes IDE SerMon and responds to display during button press. So TyCommander on Windows was essential. Also the NULL prints to Serial were required - without it only printed on Alternate presses per p#4. I reduced the number of NULL's printed and time - but the wait after that when too short took the USB port offline for some time so I had to move the Teensy as it never left setup() while !Serial.

Added a WakeCnt to track detected button presses - it seems to catch each one as long as prior exit is complete. Made it easier to run up longer strings to see they all got displayed

I put in a while ( button pressed ) hoping I could use the button to keep it awake to reprogram - and that failed [even with a yield() in the while] - at least using TyCommander as the loader. Even repeat button presses Snooze seems to not allow USB entry to bootloader mode. just added a delay( 20000 ); and that also failed to let bootloader get triggered - so that must be one of the Snooze USB issues to resolve?

Not sure what this adds - other than seeing it work with reduced timing { while awake and between presses } and I needed TyCommander - but I soldered the button so I had to spend time testing it.
Code:
#include <Snooze.h>

SnoozeDigital digital;
SnoozeUSBSerial usb;
SnoozeBlock config(usb, digital);
int who;

void setup() {
  pinMode(16, INPUT_PULLUP);
  pinMode(LED_BUILTIN, OUTPUT);
  digitalWrite(LED_BUILTIN, HIGH);
  digital.pinMode(16, INPUT_PULLUP, CHANGE);
  Serial.begin(9600);
  while (!Serial);
  Serial.println("Starting...");
  delay(100);
}

int WakeCnt = 0;
void loop() {
  delay(10); // to give time for serial to come back up
  digitalWriteFast(LED_BUILTIN, LOW);
  who = Snooze.hibernate(config);
  elapsedMillis time = 0;
  WakeCnt++;
  // wait for serial monitor
  while (!Serial && time < 260) {
    Serial.write(0x00); // print out a bunch of NULLS to serial monitor
    digitalWriteFast(LED_BUILTIN, HIGH);
    delay(20);
    digitalWriteFast(LED_BUILTIN, LOW);
    delay(30);
  }
  // normal delay for Arduino Serial Monitor
  delay(60);  // Critical wait
  Serial.print(digitalReadFast(16));
  Serial.print(" Wakeup pin: ");
  Serial.print(who);
  Serial.print(" Awake= ");
  while (!digitalReadFast(16)) {
    digitalWriteFast(LED_BUILTIN, HIGH);
    delay(5);
    digitalWriteFast(LED_BUILTIN, LOW);
    delay(10);
  }
  Serial.print(time);
  Serial.print(" Cnt= ");
  Serial.println(WakeCnt);
  // Serial.flush(); // may not help anything
}
 
@Duff - try my code {above}. If you long press the button ~360ms it will exit with button pressed.

With who = Snooze.hibernate(config); - it only ever re-enters on the next press as the CHANGE is not seen.

When changed to who = Snooze.sleep(config); - it wakes on PRESS - and long press returns to SLEEP - then RELEASE of the button causes it to see that as a CHANGE and it will enter.

This is because Code above is hardcoded bias for trigger on PRESS? The code below works with either hibernate or sleep as follows { where short state change is before flash stops, and long is after }::

Quick HACK Code below tracks state and exits normally on long press ( over ~360ms ) and then responds to release unless a LONG RELEASE. I think this shows SNOOZE to work in both cases of CHANGE and simulates a toggle's behavior?

Code:
#include <Snooze.h>

SnoozeDigital digital;
SnoozeUSBSerial usb;
SnoozeBlock config(usb, digital);
int who;

void setup() {
  pinMode(16, INPUT_PULLUP);
  pinMode(LED_BUILTIN, OUTPUT);
  digitalWrite(LED_BUILTIN, HIGH);
  digital.pinMode(16, INPUT_PULLUP, CHANGE);
  Serial.begin(9600);
  while (!Serial);
  Serial.println("Starting...");
  delay(100);
}

int WakeCnt = 0;
void loop() {
  bool ButtonState;
  delay(10); // to give time for serial to come back up
  digitalWriteFast(LED_BUILTIN, LOW);
//  who = Snooze.hibernate(config);
  who = Snooze.sleep(config);
  elapsedMillis time = 0;
  WakeCnt++;
  // wait for serial monitor
  while (!Serial && time < 260) {
    Serial.write(0x00); // print out a bunch of NULLS to serial monitor
    digitalWriteFast(LED_BUILTIN, HIGH);
    delay(20);
    digitalWriteFast(LED_BUILTIN, LOW);
    delay(30);
  }
  // normal delay for Arduino Serial Monitor
  digitalWriteFast(LED_BUILTIN, HIGH);
  delay(60);  // Critical wait
  ButtonState = digitalReadFast(16);
  Serial.print(ButtonState);
  Serial.print(" Wakeup pin: ");
  Serial.print(who);
  Serial.print(" Awake= ");
  while ( ButtonState != digitalReadFast(16) ) {
    digitalWriteFast(LED_BUILTIN, HIGH);
    delay(5);
    digitalWriteFast(LED_BUILTIN, LOW);
    delay(10);
  }
  Serial.print(time);
  Serial.print(" Cnt= ");
  Serial.println(WakeCnt);
  // Serial.flush();
}
 
Last edited:
Defragster, I'm having the same issue with the Teensy not reliably waking up when going from LOW to HIGH when using your sketch in post #12 in hibernate.
 
@RFModulator - how is this toggle switch wired up to the teensy? Can you give a pic or diagram? Does it connect to 2 pins on the teensy with the center to ground?
 
The center pin of the toggle switch is connected to ground. One of the other pins on the switch is connected to Pin 16. The remaining pin on the switch has no connection.

So, the switch in one orientation connects GND to Pin 16, and flipping the switch in the other orientation opens the path, and the internal pullup brings the pin HIGH (as verified by my meter and analogRead).
 
Defragster, I'm having the same issue with the Teensy not reliably waking up when going from LOW to HIGH when using your sketch in post #12 in hibernate.

Are you going by Serial Out or I suppose the LED FLICKER? P#12 code sat over night here and still working as I attempted to describe - i.e. as expected with single button to GND.

What OS? Are you using TyComm by any chance? Just for Ref I'm IDE 1.85 with TD 1.41b1 and : using library Snooze at version 6.3.1 in folder: T:\arduino_1.8.5\hardware\teensy\avr\libraries\Snooze
 
I'm going by the LED flicker.

I'm using Tera Term on Windows 10. I foolishly never mentioned which version of Snooze I'm using - it's the one that comes with Teensyduino 1.40 (6.3.1)
 
Odd, there is an issue of detecting 'change' between my two sketches in p# 11 & 12. But for me #12 code works as I expect on either sleep or hibernate. Once awake the state at SNOOZE time is the one from which CHANGE is determined. But p#11 code is biased to like pressed or low of button.

I wonder if commenting out the 'while (!Serial);' in setup() and not connecting serial monitor might change your behavior? Or changing to TyComm for a test in case the USB coming and going are having some effect. When I added Serial.flush() and or changed that one time { delay(60); // Critical wait } too low I had to abandon that USB port as Windows got tired of, or mad at it, it did work on return later without a reboot.

I just recompiled with Serial: Type RAW_HID and it runs at the speed of light as Serial is not seen as active and there are no waits as coded when !Serial. Normally TyComm picks up (slower to connect?) RAW_HID - but the appearance of it is TOO SHORT to exist - or that interface is not Snooze supported ? - and it wakes and blinks and seems to work button HIGH or LOW - but hard to work the button backwards and be sure the change is not detected on the alternate state as it is SO FAST.

If you don't need USB I'd take it out - USB isn't implemented to work across such short transitions. The LED_BUILTIN (or others) can be left on to observed pin #16 state before sleep? Maybe if you had a scope or multiple LEDS you could use that for debug pin states - or Serial#?

Snooze seems to be working well - though I'd have to reset my DVM to detect current as best it can. Perhaps a second Teensy monitoring pins to debug operation and know it is working.
 
RAW_HID works better than USB_SERIAL I think.

Interesting - good news. But on these short duration wake and sleep events TyComm never saw it. That seems to agree with what I saw on RAW_HID before - it seems to be a lazy connection.

Indeed RAW_HID works with .hibernate : I put this extra print of (time) in my code to stay awake 2 seconds and the final HID prints always appear - though the preceding string parts are skipped off and on.

<EDIT - meant to try this as well in this delayed HID>: FYI - I tried TyComm Reset and Bootloader buttons during the 2 waking print seconds and both just take Teensy to a faulted 'running' state - sketch freezes and TyComm sees it as running. Somehow the USB trigger to bootloader/reset fails on Snooze.

<edit2>: indeed a similar 2 second print loop in setup before first snooze still allows GUI Reset and Bootloader from TyComm. Not sure if that will help when it comes to improving USB under Snooze.

Code:
// ...
  Serial.print(time);
for ( int ii=0; ii<10; ii++ ) {
    Serial.print("__");
    Serial.print(time);
    delay( 200 );
}
  Serial.print(" Cnt= ");
// ...

1 Wakeup pin: 16 Awake= 360__360__560__760__960__1160__1360__1560__1760__1960__2160 Cnt= 19
1 Wakeup pin: 16 Awake= 360__360__560__760__960__1160__1360__1560__1760__1960__2160 Cnt= 20
__560__760__960__1160__1360__1560__1760__1960__2160 Cnt= 21
1 Wakeup pin: 16 Awake= 360__360__560__760__960__1160__1360__1560__1760__1960__2160 Cnt= 22
0 Wakeup pin: 16 Awake= 360__360__560__760__960__1160__1360__1560__1760__1960__2160 Cnt= 23
0 Wakeup pin: 16 Awake= 360__360__560__760__960__1160__1360__1560__1760__1960__2160 Cnt= 24
0 Wakeup pin: 16 Awake= 360__360__560__760__960__1160__1360__1560__1760__1960__2160 Cnt= 25
__560__760__960__1160__1360__1560__1760__1960__2160 Cnt= 26
 
Last edited:
So I can confirm that using this type of switch in this type of configuration produces the LOW -> HIGH pin wakeup problem described. I tried enabling the glitch filter thinking some transients might be at play but it still had the same problem. Are you willing to change how you connect that switch? With the teensy pin configured as an INPUT connected to the switch's middle pin and the switch's outer pins connected to gnd and 3.3V receptively it works for me. Configure the pin wakeup type to CHANGE also.
 
I'm glad you could reproduce the problem, but that's unfortunate that it looks like there really is a problem here! Mixed emotions indeed.

Tonight, I will modify my toggle switch and pinMode and I'll let you know how it goes. First, I'll also experiment with some different value external pull-up resistors to see if that helps.

I'm using the toggle switches to emulate water level switches (it's a PITA to hold the real thing in the operated position for extended periods while on the bench). The one I'm using contains a reed switch, so the contact closing is fairly clean and fast (more similar to a toggle switch than a pushbutton) so I wonder if I'll have issues when I switch from the toggle switches to the water level switch once I'm farther along in the prototyping process. Those devices were purchased as SPST, so I hope I can figure out a way to reliably detect LOW to HIGH transitions without having to switch to SPDT float switches.
 
Indeed - I don't think my sketch is showing what I thought it was. In .hibernate() it only wakes on a HI>LOW even with the simple pushbutton. The wake triggers on trying to release and press before the loop() completion in ~250ms. Switching to .sleep() shows it working immediately with LED flash on release - not the re-press.

<edit>: Plugged my DVM across the current path to my T_3.6 @ 180MHz
~3.1 mA :: who = Snooze.sleep(config); // CHANGE works both
0.4 mA :: who = Snooze.hibernate(config); // CHANGE works HI>LOW
0.1 mA :: who = Snooze.deepSleep( config ); // CHANGE works HI>LOW
77.x mA :: Awake after Pin 16 Change with delay(2000); - LED on
74.x mA :: Awake after Pin 16 Change with delay(2000); - LED off
28.2 mA :: Bootloader mode after Button Press
~77 mA :: During programming upload
~40 mA :: With WFI in Yield() and attachInterrupt()
 
Last edited:
Sorry for the delay, the holidays and some other deadlines got in the way.

For my first test, I used the code from Post #7 and changed my pinMode and SnoozeDigital to INPUT. I modified the toggle switch to switch between GND and 3.3V on Pin 16. As expected, the LED flashed on 100% of the switches over a course of one flip every 3 seconds for 3 minutes.

Next, I removed the 3.3V wire from the toggle switch and returned the toggle switch to its original configuration. I put a 47K resistor (the closest thing I have to the internal 50K pullup) between Pin 16 and 3.3V. I saw the same results as above; the LED didn't miss a change over the course of 3 minutes.

So, this leads me to believe that the issue is specific to using the internal pullup resistor since I see the expected results with a similar-value external resistance. For now, this appears to be my solution; just use an external pullup resistor and keep my SPST switches. Naturally, I would prefer to use the internal pullup, but I'm happy that an external resistor is all that is necessary to improve the performance.

Thanks for all the time you've spent on this, duff and defragster! If either of you try this with an external pull up as well, I'm curious to see what you find. Even though I have my solution, I'm happy to stay with this if either of you have additional things you'd like me to test.
 
Status
Not open for further replies.
Back
Top