Welcome to the Pycom forum Forum
    • \[\[global:header.categories\]\]
    • \[\[global:header.recent\]\]
    • \[\[global:header.tags\]\]
    • \[\[global:header.popular\]\]
    • Login

    Explore Pybytes | Official Documentation | Report a Firmware Bug/Issue | GitHub

    Watchdog failed? too much timer.alarm and timer.chrono?

    Scheduled Pinned Locked Moved LoPy
    7 Posts 2 Posters 57 Views 2 Watching
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    Reply
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • T Offline
      thibault
      last edited by

      Hello all, i'm front a big problem with the watchdog.

      A lopy run since 14 hour, when he loop restart by watchdog timer. The watchdog was set for 10 s, wich give me the time to try some code for understand what become.

      My watchdog:

      class Watchdog:
      
          def __init__(self):
              self.wdt_feeder_period = 3 # secondes
              self.wdt_timeout = 10 # secondes
              self.wdt = WDT(timeout=(self.wdt_timeout*1000))  # timeout en milliseconde
              self.__feeder = Timer.Alarm(self.feed_the_dog, self.wdt_feeder_period, periodic=True)
      
          def feed_the_dog(self, alarm):
              self.wdt.feed()
              collect()
      
      watchdog = Watchdog()
      

      when the device reboot, i have few second to send him code (with CTRL+B shortcut) like this

      for _ in range(100):
          watchdog.wdt.feed()
          sleep(0.2)
      

      this one leave the device available. But this one not:

      class Clock:
      
          def __init__(self):
              self.seconds = 0
              self.__alarm = Timer.Alarm(self._seconds_handler, 1, periodic=True)
      
          def _seconds_handler(self, alarm):
              self.seconds += 1
              watchdog.wdt.feed()
      
      clock = Clock()
      
      

      It's like the timer.alarm don't want run(?). I have try use a thread to feed the (watch)dog and this rule fine. But i don't know why the wathdog (and timer.alarm) don't want run. After many restart, the device seems stable, and don't restart. But it's clear that my code was not stable. And i don't know if a such behaviour is because i use timer.alarm in two module permanently, and two timer.chrono sometimes.

      In waiting any idea for explain this behaviour, i will try to replace timer.alarm with thread. But i will fine if anybody find the reason (and maybe have a solution) for this silly watchdog. Thank's

      1 Reply Last reply Reply Quote 0
      • G Offline
        g0hww
        last edited by

        After concluding that my codebase needed a fair bit of refactoring to make it stable, and it has been stable for a long time now (though I haven't tried any FW updates for about 1 year), I ended up with the following approach.

        My class Node provides the following 2 methods of relevance

            def cbit_cb(self, void):
                try:
                    if self.cbit_timer != None:
                        self.cbit_timer.cancel()
                        self.cbit_timer = None
                    self.cbit_active = False
                    self.wdt.feed()
                    #print('Node.cbit_cb() complete')
                except Exception as e:
                    print('Node.cbit_cb() - failed!')
                    sys.print_exception(e)
        
            def cbit(self):
                with self.cbit_lock :
                    try:
                        if self.running and not self.cbit_active:
                            self.cbit_timer = Timer.Alarm(self.cbit_cb, s=1.0, periodic=False)
                            self.cbit_active = True
                            #print('Node.cbit_() starting cbit check')
                    except Exception as e:
                        print('Node.cbit() - failed to set cbit timer!')
                        sys.print_exception(e)
        

        and my main() has this ...

                        with Navigator(pyt, node, 10) as nav:
                            while node.is_running() :
                                #print('Doing work')
                                if nav.work():
                                    node.cbit()
                                    gc.collect()
                                    node.try_uptime_status()
                                    node.try_memory_status()
                                    node.try_battery_status(pyt)
        

        The intent with this code is to not rely on a repeating timer callback to kick the dog. Instead, when the main thread succeeds in performing its primary task, it calls node.cbit() which sets up a one shot timer callback which actually kicks the dog. This ensures that the primary work is being done successfully and avoids the problematic scenario in which a repeating timer callback keeps kicking the dog when everything else has turned to poo.

        That code has been running on a LoPy4 GPS tracker (which sends its position via LoRa) for 23 days. I'm not sure why its uptime isn't longer as it is stable enough that I don't pay attention to it any more. I have another variant of that code running on another pair of LoPy4s that act as LoRa to MQTT gateways. They tend to get watchdog resets when their MQTT publishing fails for more than 5 minutes, using the same mechanism shown above. This is normally because the wifi goes down when the router is rebooted and those gateway devices have nothing better to do than reboot under such circumstances.

        Of course there is more code to handle deletion of the node object and the cancellation of any active timers, etc.

        Hope this helps.

        1 Reply Last reply Reply Quote 0
        • T Offline
          thibault
          last edited by

          @g0hww thanks for your code example and experience feedback. I see that you use

                          self.cbit_timer.cancel()
                          self.cbit_timer = None
          

          to disable the timer. At your opinion, it's neccessary whereas the periodic input of the alarm creation was set to False? For be sure that the alarm was delete?

          in our case, our device can stay unused for long time (some hours), so there is no main function will run when he does nothing. So i can't use the same idea of you, and for now i have use a thread to solve (i hope) this problem. But i'm curious to understand if i can use or not the alarm object. But for later coding alarm, i will use your solution for create/delete alarm!

          G 1 Reply Last reply Reply Quote 0
          • G Offline
            g0hww @thibault
            last edited by

            @thibault

            I set self.cbit_timer = None so I can check to see if it is None in the destructor for the Node object and cancel it in the destructor if it isn't None. I prefer to call cancel() on it first, though that may be excessive.

            T 1 Reply Last reply Reply Quote 0
            • T Offline
              thibault @g0hww
              last edited by

              @g0hww ok, it's clear to me now. Thank's!

              T 1 Reply Last reply Reply Quote 0
              • T Offline
                thibault @thibault
                last edited by

                I will have the result in few days (if our device don't restart) but it seem that the reason of our reboot problem was due to the misuse of timer.alarm creation from interruption. Since we have done some modification about this, the device seem get a better stability.

                1 Reply Last reply Reply Quote 0
                • G Offline
                  g0hww
                  last edited by g0hww

                  I thought that I would mention another trick that I use, when using the REPL and code is running. The content of main runs in a try block and I have catch blocks like so:

                      except KeyboardInterrupt as e:
                          # the wdt will give us 30 mins to play in the repl ...
                          wdt = WDT(timeout=30*60*1000)
                          print('CAUTION: Watchdog will reboot in 30 minutes!')
                          print('Node stopped')
                      except Exception as e:
                          sys.print_exception(e)
                          # game over man, expect reboot soon!
                          wdt = WDT(timeout=5000)
                          pycom.heartbeat(False)
                          utime.sleep_ms(100)
                          pycom.rgbled(0x7f0000) # red
                          print('FATAL: Watchdog will reboot soon!')
                  

                  Catching the keyboard interrupt means that I get 30 mins to play in the REPL console before the thing will reboot, in case I wander off and forget to restart it.

                  1 Reply Last reply Reply Quote 0

                  Hello! It looks like you're interested in this conversation, but you don't have an account yet.

                  Getting fed up of having to scroll through the same posts each visit? When you register for an account, you'll always come back to exactly where you were before, and choose to be notified of new replies (either via email, or push notification). You'll also be able to save bookmarks and upvote posts to show your appreciation to other community members.

                  With your input, this post could be even better 💗

                  Register Login
                  • 1 / 1
                  • First post
                    Last post

                  Pycom on Twitter

                  Pycom