WatchDog e reset inaspettati...

Salve, ho realizzato uno sketch e lo sto testando da diverse settimane ormai. Lo sketch fa uso della faunzione watchdog e mi sta creando dei problemi, nel senso che ogni tanto si resetta e non riesco ad individuare la causa. A volte non si resetta per giorni, anche 4 di fila, poi magari si resetta un paio di volte in poche ore, anche dopo una o due ore. Il reset avviene in punti differenti dello sketch e non credo dipenda da quello (comunque è quello che ho in firma). Volevo saper se avevate dei suggerimenti su cosa e come controllare per ridurre i reset a zero.
Uso una scheda Arduino UNO originale con shield Ethernet W5100 non originale e non ho altre periferiche collegate. Alimento il tutto con un caricabatterie per cellulare USB da 1A.
Lato software uso la libreria MsTimer2 per impostare un timer interrupt:

  // Start the timer interrupt
  MsTimer2::set(5, Every5ms); // 5ms period
  MsTimer2::start();

void Every5ms()
{
  for(byte i = 0; i < NUMSENSORS; i++)
    {
      sensors[i]->CheckSensor();
    }
  #ifdef USE_WD
    // If defined, use WatchDog
    CheckWatchdog();
  #endif
}

e la parte che si occupa del watchdog è questa:

byte busyState;
int busyCounter;

#define TIMEOUT 12000 // * 5ms = 1 minute
#define EE_CTR 78
#define EE_STATE 79

void SetupWatchdog()
{
    #ifdef USE_WD
    // Set watchdog timeout to 4 seconds
    wdt_enable(WDTO_4S);
    #endif
    // Initialize counters
    busyCounter = 0;
    busyState = 0;
}

void busy(byte function)
{
    if(function == 0)
    {
        busyCounter = 0;
    }
    if(busyState != function)
    {
        busyState = function;
    }
}

// This is called every 5ms to keep the watchdog from resetting the board
void CheckWatchdog()
{
    // Increment the counter as long as we are executing a function
    busyCounter++;
    
    // Keep resetting the watchdog until busycounter gets really big
    // 12000 * 5ms + 4s = 64 seconds of inactivity before a reset occurs
    // A .connect() function can take up to 15 seconds because of DNS
    // so a normal watchdog would not be long enough.
    if(busyCounter < TIMEOUT)
    {
          // Reset watchdog counter  
          wdt_reset();        
    }
    else
    {
        // While waiting for the reset-by watchdog, save the current busystate
        eeprom_write_byte ((uint8_t*)EE_STATE, busyState);
        // Increment the watchdog reset counter
        byte ctr = eeprom_read_byte ((uint8_t*)EE_CTR) + 1;
        eeprom_write_byte ((uint8_t*)EE_CTR, ctr);
        // Save all counters
        SaveValues();
        // Wait for the reset to come
        while(1); 
    }
}

La funzione busy() viene chiamata in vari punti del loop con un parametro che mi specifica in quale punto del loop sto ed in caso di reset viene salvato nella eeprom. Dopo un reset posso leggere la eeprom e vedere in che punto ero quando il reset è avvenuto e questo punto non è sempre lo stesso, da qui secondo me, il reset non dipende da una funzione specifica dello sketch ma da qualcos'altro che non riesco a capire. Magari stò facendo qualche errore nella gestione del watchdog? I 64s di inattività che ho impostato possono essere troppo pochi? Qualche altra cosa che mi sfugge?
Se poteste indicarmi una soluzione oppure qualche test da fare, vi sarei grato.

... guarda che basta che si ferma da qualche parte per 4 secondi ed il WD resetta senza segnare nulla. E con una Ethernet può capitare che si ferma in attesa di una risposta dalla rete per quel tempo ... ::slight_smile:

Guglielmo

Ma se io ogni 5ms ho un timer interrupt che richiama wdt_reset(), i 4s non ripartono ogni volta? E poi, essendo un interrupt, non dovrebbe essere eseguito anche nel mentre che la ethernet aspetta la risposta dalla rete?

Si ... se veramente il timer scatta e la routine viene richiamata. Hai modo di verificarlo ?

Guglielmo

P.S.: Non sono sicuro che la Ethernet, in punti critici, non disabiliti gli interrupt ... occorrerebbe verificare ... ::slight_smile:

Non saprei come verificare che la routine venga richiamata sempre (in teoria dovrebbe essere chiamata ogni 5ms ed il problema a volte si presenta dopo giorni di attività normale, quindi non saprei come fare a verificare).
Ho provato a cercare informazioni riguardo la libreria MsTimer2 che sembra essere affidabile, quindi presumo che la routine di interrupt venga eseguita.
Nel primo post ho dimenticato di mettere cosa viene eseguito nella routine Every5ms(), che richiama la funzione CheckSensor() nel mio caso per 2 volte perchè ho due sensori:

void S0Sensor::CheckSensor()
{
    // Read the digital input
    bool solarInput = digitalRead(pin);
    // Rising edge?
    if(solarInput && sensorIsOn == false)
    {
        sensorIsOn=true;
    }
    // Falling edge?
    if(!solarInput && sensorIsOn == true)
    {
        // Store the time between the last two pulses
        pulseLength = millis() - lastMillis;
        // Store the time for the next pulse
        lastMillis = millis();
        // Update counters
        todayCnt++;
        sensorIsOn = false;
    }
}

Può essere che questa stessa routine (Every5ms()), in realtà impieghi più di 5ms ad essere eseguita e che quindi salti qualche WDreset? Oppure una volta richiamata una ISR viene eseguita completamente prima di poterla chiamare nuovamente?
Per la Ethernet che disattiva gli interrupt, dovrei spulciare la Ethernet.h e tutti i suoi files, giusto? Devo cercare cli()?

Non dimentichiamo, comunque, che il watchDog non viene usato per fare il debug dei programmi, ma viene usato nel caso il processore si "inchiodi" a causa di disturbi elettromagnetici esterni, tipo un relé, un carico, una lampada neon...
E questo succede in qualsiasi parte del programma, mentre se c'è un errore dovrebbe inchiodarsi sempre li, no?

Infatti, il fatto che all'interno dello sketch il reset avvenga in posti diversi mi fa pensare che la causa non sia lo sketch stesso ma un qualcosa di esterno, come un disturbo oppure un comportamento inatteso della ethernet o cose così. Il programma in se ormai credo non abbia più errori tali da inchiodarsi.
In effetti la scheda è posizionata abbastanza vicino ad apparecchiature che funzionanto in tensione di rete anche se non è in contatto con queste. Potrebbe essere utile foderare Arduino con una specie di gabbia di Faraday? Eventualmente avete qualche suggerimento sulla sua realizzazione pratica?

Il mio Arduino non si inchioda mai! Questo perchè tutti gli ingressi sono in pull-up oppure in pull-down e MAI aperti, oltre che funzionano con una ottima alimentazione.
Ho studiato alcune schede di PLC industriali e non vedo altri accrocchi strani: chiudi tutte gli ingressi/uscite non utilizzati con una resistenza oppure, meglio, cortocircuitali tutti a massa.
Poi facci sapere.

Ottimo. È una cosa molto semplice da realizzare, la metto subito in pratica! Solo una cosa, usare le resistenze interne va bene? Effettivamente ho molti ingressi aperti...

CrazyHorse80:
Solo una cosa, usare le resistenze interne va bene? Effettivamente ho molti ingressi aperti...

No, meglio metterle esternamente, quelle interne hanno un valore spesso troppo alto.

Guglielmo

Grazie, provo e poi vi faccio sapere come va. Hai un valore suggerito?

Se metti tutto a massa non ti servono nemmeno le resistenze: colleghi gli ingressi che non usi a massa e hai finito.
Se invece hai un po' di resistenze da 10k ohm, va bene lo stesso.

Ottimo, allora farò così. Questo weekend realizzo il tutto.

steve-cr:
Se metti tutto a massa non ti servono nemmeno le resistenze: colleghi gli ingressi che non usi a massa e hai finito.

... pericoloso e sconsigliato ... basta un errore di programmazione che mette in OUTPUT ed HIGH uno di quei pin e ... lo bruci (va in corto il +Vcc con la massa attrverso il pin).

Sempre meglio una resistenza ... 4.7K va benissimo e eviti danni in caso di grossolani errori.

Guglielmo

È possibile usare una sola resistenza per tutti gli ingressi? O meglio metterne una su ogni ingresso? Mi sa che mi tocca fare una scappata in un negozio di componentistica... :slight_smile:

CrazyHorse80:
È possibile usare una sola resistenza per tutti gli ingressi? O meglio metterne una su ogni ingresso?

... ragiona sul motivo per cui ti ho detto che è meglio mettere una resistenza e troverai la risposta. :wink:

Pensa solo che succede se, per il solito errore grossolano, li metti in OUTPUT ma alcuni HIGH, alcuni LOW e li hai collegati tutti assieme alla stessa resistenza verso massa ... :smiley:

Guglielmo

P.S.: Lo so, lo so, sono cose che non devono capitare, uno sta attento, ecc. ecc. ... ma "il momento dello scemo" capita a tutti ... :grin:

Infatti ho scritto che dovevo andare a comprare resistenze... :grinning: Era per essere sicuro ma in realtà avevo già capito. Grazie per la conferma.

gpb01:
... pericoloso e sconsigliato ... basta un errore di programmazione che mette in OUTPUT ed HIGH uno di quei pin e ... lo bruci (va in corto il +Vcc con la massa attrverso il pin).

Sempre meglio una resistenza ... 4.7K va benissimo e eviti danni in caso di grossolani errori.

Guglielmo

Sono d'accordo con te. Ma davo per scontato che mettere in OUTPUT e poi in HIGH una porta NON è solo un grossolano errore di programmazione: c'è la aggravante del dolo ! :slight_smile:

Mi è venuto un dubbio : il pin 0 che è RX seriale, lo metto ugualmente a massa? Tenete presente che ho un piccolo screw shield ed i collegamenti li farei li, quindi prima di riprogrammare Arduino lo toglierei ed il pin tornerebbe libero. Il pin 1 invece lo lascio stare dato che è un'uscita oppure no? Lo sketch non fa uso delle seriale se non per il debug che però si può disattivare con le direttive in fase di compilazione. Ah, come sospettavo non ho abbastanza resistenze da 4,7k... Ne ho parecchie da 330ohm ma credo che sia troppo poco.