firmata / ConfigurableFirmata

A plugin-based version of Firmata
GNU Lesser General Public License v2.1
150 stars 71 forks source link

Watchdog for Firmata #47

Open jnsbyr opened 8 years ago

jnsbyr commented 8 years ago

Issue #37 has shown that there are many reason why a system can become unstable. If the basic design is not the problem and we are talking about improving an already reliable solution further, a watchdog can make the difference in some cases, especially if the system is in a location not easily accessible. If you know your way around, activating the watchdog is about adding 3 lines of code, but some of us don't want to bother with the details. That's where Firmata can help, because there are some details to consider:

Most of these aspects can be covered with an #ifdef approach, the rest with documentation.

Here is the basic solution for AVR:

...
// Watchdog, only for AVR-based boards
// Note: Some board (e.g. Pro Mini ATmega328 5V 16MHz) do not have a bootlader with WDT support
// causing the board to get stuck until power cycled after the WDT has been triggered. You
// can update the default bootloader, e.g. using optiboot.
#include <avr/wdt.h>
...
void setup()
{  
...
#ifdef _AVR_WDT_H_
  // enable watchdog
  // Note: Some board do not support a WDT timeout of 8 seconds. You should choose 
  // the maximum supported timeout of your board from the header file avr/wdt.h.
  wdt_enable(WDTO_8S);
#endif
}
...
void loop()
{
#ifdef _AVR_WDT_H_
  /* reset watchdog */
  wdt_reset();
#endif
...
}

If you want to go one step further you can check the receive frequency from the host, assuming you do some more or less periodic real or pseudo output operations:

#define RECEIVE_TIMEOUT 60000  // [ms]
unsigned long lastReceive = 0;
bool enableWatchdogReset = true;
...
void setup()
{
...
  lastReceive = millis();
  wdt_enable(WDTO_8S);
}
...
void loop()
{  
  if (enableWatchdogReset) {
    wdt_reset();
  }
  ...
  currentMillis = millis();
  while (Firmata.available()) {
    Firmata.processInput();
    if (currentMillis > lastReceive && (currentMillis - lastReceive) > RECEIVE_TIMEOUT) {
      enableWatchdogReset = false;
    }
    lastReceive = currentMillis;
  }
...
}

The code and documentation required for cross-platform usage of the watchdog could be collected in a separate header file so that the implementation in the .ino file will still need about 3 lines of code.

soundanalogous commented 8 years ago

I can see how this would be useful. However I think a general purpose watchdog timer abstraction would be beneficial for Arduino users in general, not just Firmata users. What I proposed is creating a library that abstracts away the details (or contributing to an existing library such as this one by Adafruit to add additional platforms - if that library supports the necessary api)). Then I can simply wrap that library for use in Firmata.

jnsbyr commented 8 years ago

I agree - a cross platform watchdog timer abstraction is not specific to Firmata. SleepyDog seems to go the first step by combining the two Arduino platforms AVR and SAMD and adding a sleep option. Creating more end user documentation is still required because Firmata is end user friendly but watchdog timer usage is not always straightforward (e.g. unsuitable default bootladers).

PizzaProgram commented 5 months ago

Hi, Is there any improvement / solution / sample (since 2016) to make Configurable Firmata stable with a watchdog code?

The current code can not handle re-connection at all.

(Tested both with Arduino Nano + RPi Pico.)

Both "solutions" are difficult remotely. Restarting the host (RPi) is not a solution either, because if the boards are plugged to an external-powered USB HUB, they never restart, so never reconnect again.

I'm not sure if it's only the .ino code's fault, or the firmata.js code or both.

IMHO it would be essential to have an easy configurable line at the beginning, like the example shows above, and it would be a part of Firmata examples by default in all cases. #define TIMEOUT_RESTART 8000 // [ms]

jnsbyr commented 5 months ago

it would be essential to have an easy configurable line at the beginning

Agree form the user standpoint. But due to the many different hardware platforms that are supported this requires an individual approach for each that needs to be developed and maintained. So it's not impossible, just work.

With the USB in between the Firmata device and the host you have an extra troublemaker. The driver can bug/disconnect and then the device cannot do anything about it and a watchdog reset will not help here. Even the host application will probably not be able to fix this. Only the host OS kernel can do this easily and this is what happens when you unplug and re-plug. If you would use classic serial tty/COM this would not be the problem. This is one of the reasons why I prefer Ethernet or WiFi uplinks - handling IP reconnects is comparatively easy.

Either try to get a core maintainer interested or try to dig a little deeper by yourself. Start with the example above, if you haven't done so already. Make an unused output pin with an LED blink the Arduino way when timeout is detected and another one when Firmata is initialized. Then you will know your code was activated. If the watchdog gets triggered but the connection is not re-established, than the problem is very probably not on Firmata device side but on the USB/host side.

pgrawehr commented 5 months ago

I have never observed this problem and I'm sure firmata itself supports reconnecting without any issues. @PizzaProgram Can you explain a bit more in detail how you're testing and what doesn't work? Preferably use a new issue, as this one is very old and not directly related.