Attiny85 - dopo certo tempo entra in un limbo [risolto]

Ciao a tutti, ho un problema con l'attiny85.

Il programma fa questo:

una volta al secondo il watchdog sveglia il micro; il micro genera un numero random, se è diverso da 10 torna a dormire, diversamente fa dei giochi di luce con un led rgb in pwm per un certo tempo e poi si riaddormenta.

Ora, tutto funziona ma solo per circa 14 minuti, poi tutto si blocca, il micro entra in un limbo e non si riaddormenta più, nè accende alcun led.

Lo sketch

#include <avr/sleep.h>
#include <avr/wdt.h>


volatile boolean f_wdt = 1;

byte FirstStart = false;
int OLDrandNumberRED;
int OLDrandNumberGREEN;
int OLDrandNumberBLUE;

int NEWrandNumberRED;
int NEWrandNumberGREEN;
int NEWrandNumberBLUE;

int FadeRate = 10; // in millis
byte maxpwm = 250;
int i, j, k;
byte Red = 0;
byte Green = 1;
byte Blue = 4;
int counter = 0;
int keyloop = 0;

void setup() {
  //Serial.begin(9600); // used for debug
  pinMode(Red, OUTPUT);
  pinMode(Green, OUTPUT);
  pinMode(Blue, OUTPUT);
  pinMode(2, INPUT); // pin non usati settati come input
  pinMode(3, INPUT);
  pinMode(5, INPUT);
  digitalWrite(2, HIGH); // pull-up sui pin non usati
  digitalWrite(3, HIGH);
  digitalWrite(5, HIGH);
  randomSeed(100);  
  OLDrandNumberRED = random(maxpwm);
  OLDrandNumberGREEN = random(maxpwm);
  OLDrandNumberBLUE = random(maxpwm);
  setup_watchdog(6); // approximately 1 seconds sleep
}

void loop() {
  if (f_wdt == 1) { // wait for timed out watchdog / flag is set when a watchdog timeout occurs
    if (keyloop != 10) {
      keyloop = random(11);
      // Serial.println(keyloop);
      f_wdt = 0;     // reset flag
      pinMode(Red, INPUT);
      pinMode(Green, INPUT);
      pinMode(Blue, INPUT);
      system_sleep();
      pinMode(Red, OUTPUT);
      pinMode(Green, OUTPUT);
      pinMode(Blue, OUTPUT);
    }


    if (keyloop == 10) {
      if (FirstStart == false) {    // fade in
        if ( i < OLDrandNumberRED) {
          i++;
          analogWrite(Red, i);
        }
        if ( j < OLDrandNumberGREEN) {
          j++;
          analogWrite(Green, j);
        }
        if ( k < OLDrandNumberBLUE) {
          k++;
          analogWrite(Blue, k);
        }
        if ( i >= OLDrandNumberRED && j >= OLDrandNumberGREEN && k >= OLDrandNumberBLUE ) {
          FirstStart = true;
        }
        delay(FadeRate);
      }

      if (FirstStart == true) {   // loop

        if ( counter < 5) {

          NEWrandNumberRED = random(maxpwm);
          NEWrandNumberGREEN = random(maxpwm);
          NEWrandNumberBLUE = random(maxpwm);

          // Serial.print("OLDrandNumberRED = "); Serial.println(OLDrandNumberRED);
          // Serial.print("NEWrandNumberRED = "); Serial.println(NEWrandNumberRED);
          // Serial.print("OLDrandNumberGREEN = "); Serial.println(OLDrandNumberGREEN);
          // Serial.print("NEWrandNumberGREEN = "); Serial.println(NEWrandNumberGREEN);
          // Serial.print("OLDrandNumberBLUE = "); Serial.println(OLDrandNumberBLUE);
          // Serial.print("NEWrandNumberBLUE = "); Serial.println(NEWrandNumberBLUE);
          // delay(3000);

          if ( NEWrandNumberRED <  OLDrandNumberRED) {
            for ( i = OLDrandNumberRED; i >= NEWrandNumberRED; i--) {
              analogWrite(Red, i);
              //   Serial.print("i-- = "); Serial.println(i);
              delay(FadeRate);
            }
          }

          if ( NEWrandNumberRED >  OLDrandNumberRED) {
            for ( i = OLDrandNumberRED; i <= NEWrandNumberRED; i++) {
              analogWrite(Red, i);
              //   Serial.print("i++ = "); Serial.println(i);
              delay(FadeRate);

            }
          }

          if ( NEWrandNumberGREEN <  OLDrandNumberGREEN) {
            for ( j = OLDrandNumberGREEN; j >= NEWrandNumberGREEN; j--) {
              analogWrite(Green, j);
              //  Serial.print("j-- = "); Serial.println(j);
              delay(FadeRate);
            }
          }

          if ( NEWrandNumberGREEN >  OLDrandNumberGREEN) {
            for ( j = OLDrandNumberGREEN; j <= NEWrandNumberGREEN; j++) {
              analogWrite(Green, j);
              //  Serial.print("j++ = "); Serial.println(j);
              delay(FadeRate);
            }
          }

          if ( NEWrandNumberBLUE <  OLDrandNumberBLUE) {
            for ( k = OLDrandNumberBLUE; k >= NEWrandNumberBLUE; k--) {
              analogWrite(Blue, k);
              //  Serial.print("k-- = "); Serial.println(k);
              delay(FadeRate);
            }
          }

          if ( NEWrandNumberBLUE >  OLDrandNumberBLUE) {
            for ( k = OLDrandNumberBLUE; k <= NEWrandNumberBLUE; k++) {
              analogWrite(Blue, k);
              //  Serial.print("k++ = "); Serial.println(k);
              delay(FadeRate);
            }
          }

          OLDrandNumberRED = NEWrandNumberRED;
          OLDrandNumberGREEN = NEWrandNumberGREEN;
          OLDrandNumberBLUE = NEWrandNumberBLUE;

          counter++;
          // Serial.println(counter);
        }
        if (counter >= 5) {   // fade out
          if ( i > 0) {
            i--;
            analogWrite(Red, i);
          }
          if ( j > 0) {
            j--;
            analogWrite(Green, j);
          }
          if ( k > 0) {
            k--;
            analogWrite(Blue, k);
          }
          delay(FadeRate);
          if ( i == 0 && j == 0 && k == 0) {
            counter = 0;
            FirstStart = false;
            keyloop = 0;
            f_wdt = 0;     // reset flag
            pinMode(Red, INPUT);
            pinMode(Green, INPUT);
            pinMode(Blue, INPUT);
            system_sleep();
          }
        }
      }
    }
  }

}
//***************************************************************************
// set system into the sleep state
// system wakes up when wtchdog is timed out
void system_sleep() {
  ADCSRA &= ~(1<<ADEN); //Disable ADC, saves ~230uA

  set_sleep_mode(SLEEP_MODE_PWR_DOWN); // sleep mode is set here
  sleep_enable();

  sleep_mode();                        // System sleeps here

  sleep_disable();                     // System continues execution here when watchdog timed out
  ADCSRA |= (1<<ADEN); //Enable ADC
}

// 0=16ms, 1=32ms,2=64ms,3=128ms,4=250ms,5=500ms
// 6=1 sec,7=2 sec, 8=4 sec, 9= 8sec
void setup_watchdog(int ii) {

  byte bb;
  int ww;
  if (ii > 9 ) ii = 9;
  bb = ii & 7;
  if (ii > 7) bb |= (1 << 5);
  bb |= (1 << WDCE);
  ww = bb; 

  MCUSR &= ~(1 << WDRF); //This bit is set if a Watchdog Reset occurs. The bit is reset by a Power-on Reset, or by writing a logic zero to the flag
  // start timed sequence
  WDTCR |= (1 << WDCE) | (1 << WDE); // WDCE: Watchdog Change Enable --- WDE: Watchdog Enable
  // set new watchdog timeout value
  WDTCR = bb;
  WDTCR |= _BV(WDIE);  
}

// Watchdog Interrupt Service / is executed when watchdog timed out
ISR(WDT_vect) {
  f_wdt = 1; // set global flag
}

Altre info:

Attiny85 -1MHz (internal oscillator; BOD disabled)

fuse:

tinyx5.menu.cpu.attiny85at1.bootloader.low_fuses=0x62
tinyx5.menu.cpu.attiny85at1.bootloader.high_fuses=0xD7
tinyx5.menu.cpu.attiny85at1.bootloader.extended_fuses=0xFF

il led è collegato ai pin 0, 1 e 4

alimentazione diretta a 3V, 2 batterie stilo cariche

Ho provato a:

inserire condensatori tra Vcc e GND da 100n, 200n, 1u, 10u ---> non cambia niente

staccare tutti i led --> non cambia niente

modificare il codice mettendo il pin4 come input, di fatto togliendo un colore --> funziona per 33 min circa

Francamente non so più cosa provare, perlomeno per capire dov'è il problema ::slight_smile:

qualche idea??

P.S. lo sleep l'ho inserito seguendo questo sketch, che comunque funziona

P.P.S eliminando lo sleep, quindi facendo giochi di luce di continuo il micro funziona correttamente

ciao

Da quanto ne so la funzione random restituisce un numero che random non è.
Nell'ipotesi che il problema stia li (il 10 "non esce" più) potresti mettere un led (ammesso che tu abbia pin liberi) da accendere ogni volta che il micro esce dallo sleep, almeno così puoi verificare che il micro "è vivo".
spero di essermi spiegato.

ciao
pippo72

Ciao pippo72, allora per verificare gli stati del micro guardo l'assorbimento: 4.7uA in sleep, circa 1mA in modalità attiva e led spenti. Potrei mettere un led extra come dici per vedere in che punto del codice si pianta comunque.

Non credo sia un problema legato ai numeri random, se fosse così vedrei sempre un'alternanza tra sleep e risveglio, invece passati i 14 minuti il consumo rimane fisso a 1mA.

Mi viene da pensare ci sia un problema tra pwm e watchdog ma non vedo come visto che girano su timer diversi..

Credo che il problema sia logico.

Supponiamo che in un certo momento OLDrandNumberRED sia X e che NEWrandNumberRED sia anch'esso X: il confronto ora come ora non contempla questo caso e il canale RED si blocca.

Lo stesso può accadere per gli altri canali GREEN e BLUE.

@cyberhs: è vero, non ho fatto il confronto quando i due numeri sono uguali ma la parte che ho segnato come //loop ha un contatore indipendente che non fa parte di nessun if quindi ad un certo punto dovrebbe comunque uscire (counter > 5) ed entrare nel //fade out

o mi sfugge qualcosa?

aggiornamento:

ho provato a mettere i led di controllo come suggerito da pippo72 in questo modo

digitalWrite(2,HIGH);     
      system_sleep();
      digitalWrite(2,LOW);

un led verde per il primo sleep, cioè quando crea un numero tra 0 e 10.
un led rosso per il secondo sleep, cioè dopo il fade out.

Ho notato che arrivati a circa 14 minuti avviene l'ultimo fade out ma il led rosso non si accende ed arriva il limbo :sweat_smile:

mi sono accorto anche che lo sleep dopo il fade out non serve, quindi l'ho tolto ma il problema persiste.

Provando a correggere come suggerito da cyberhs ho aggiunto questa riga (per ogni colore), la prima cosa che mi è venuta in mente, ma non funziona

if(  NEWrandNumberBLUE ==  OLDrandNumberBLUE ) { NEWrandNumberBLUE =  OLDrandNumberBLUE + 2; }

altra cosa: in stato di limbo, se metto un condensatore (100nF) tra Vcc e GND, tutto magicamente riparte (per altri 14 min poi si pianta di nuovo)

Mi viene da pensare che il micro è posseduto da un anima vagante antenato della Longines. :grinning:

Seriamente, se si trattasse di un memory leak non ripartirebbe certo inserendo un condensatore a caldo.
Penso invece che inserire un condensatore a caldo potrebbe attivare il reset specie se l'alimentazione ha alta impedenza.

C'è da controllare se il micro ha un fuse per abilitare il reset per bassa tensione, per tagliare la testa al toro io una prova con alimentatore al posto delle batterie la farei.

Ciao.

In base ai fuse impostati il BOD dovrebbe essere disabilitato. Faccio una prova con l'alimentazione a 3.3V

edit: niente da fare, si blocca sempre. Se inserisco il condensatore la magia si ripete :smiley:

Trovo strano che l'inserimento a caldo di un condensatore da 100nF tra Vcc e GND riavvi il micro, cioè ciò non deve accadere.
Per intanto il condensatore da Vcc e GND lo lasci montato (occhio che devi minimizzare il percorso dei fili a cui connetti il condensatore) perché ci deve stare sia su breadboard o millefori.

Anche il pin di reset deve avere il suo condensatore da 100nF.

L'unica cosa logica quando il comportamento è illogico e andare per esclusione, quindi prima escludi che possa trattarsi di un problema hardware e poi passi al software.

Mostra un foto del cablaggio, tanto per gradire.

Allora, con i condensatori montati il riavvio a caldo non avviene più ma il blocco continua, sia a batteria che ad alimentazione. Ho provato a togliere lo sleep e simularlo con un delay, stesso risultato.

Però a me il codice continua a sembrare giusto..

allego le foto

WP_20150825_003.jpg

WP_20150825_004.jpg

WP_20150825_005.jpg

Forse ho trovato il problema.. ho fatto un debug con la seriale utilizzando arduino e il codice che simula il watchdog.

In pratica durante il fade out, a circa 14 minuti dall'accensione, compariva un numero negativo ( i = -1) che bloccava di fatto il proseguimento del codice.. Adesso ho corretto il confronto con <= 0 e sembra tutto ok.

Però quel -1 non dovrebbe saltare fuori, vuol dire che la funzione random ogni tanto fa cilecca secondo voi?

Sembrerebbe qualcosa che va in overflow... come quando si conteggiano i millis() usando int invece di unsigned long ...

Non saprei, guardando i reference non ho trovato niente a riguardo.. adesso lascio acceso l'aggeggio per tutta la giornata e vedo come si comporta

Sembra che sia tutto ok, il micro non si è mai bloccato in quasi 10 ore di funzionamento. Risolto.

Grazie a tutti per le dritte :slight_smile: