Attiny85 sleep pin change + watchdog

Buongiorno a tutti gli utenti! sto facendo un progettino nel mio tempo libero con gli attiny85 e i moduli RF. Sono riuscito a fare quello che volevo e a mettere il micro in sleep risvegliandolo con il segnale su un pin. Ora avrei la necessità di risvegliarlo anche se passa un determinato tempo usando watchdog. In pratica si svegli se c'è un evento esterno o in alternativa se passano 12 ore. Come posso usare entrambi i tipi di sleep mode, se possibile?

Buongiorno, essendo il tuo primo post, ti chiederei cortesemente di presentarti QUI (spiegando bene quali conoscenze hai di elettronica e di programmazione ... possibilmente evitando di scrivere solo una riga di saluto) e di leggere con attenzione il REGOLAMENTO ... Grazie.

Guglielmo

P.S.: Il tempo massimo per il watchdog è 8 secondi ... quindi, comunque, ogni 8 secondi al massimo ti si sveglierebbe, altrimenti devi usare un circuitino esterno (NE555 ?) per generare l'impulso su un pin ogni 12 ore.

Ok Guglielmo, avevo letto il regolamento ma mi è sfuggita la sezione di presentazione. In ogni caso ho lasciato anche la mia.

So che al massimo sono 8 secondi ma non si può in qualche modo "accumulare" i secondi fino ad arrivare al tempo desiderato? Con NE555 avrei un dispendio di energia per alimentare appunto il generatore dell'impresa. Ho scordato di specificare che il tutto è alimentato a batteria e la modalità sleep servirebbe a minimizzare i consumi

pazzokli: So che al massimo sono 8 secondi ma non si può in qualche modo "accumulare" i secondi fino ad arrivare al tempo desiderato?

Si può nel senso che comunque ti svegli ogni 8 secondi, incrementi un contatore e, se non sei arrivato a 5400 (12 ore / 8 secondi) ti riaddormenti, mentre se sei arrivato, azzeri il contatore, fai quello che devi fare e poi torni in sleep ;)

Guglielmo

Grande! Dovrebbe funzionare. Tanto incrementare un contatore non penso porti via troppi mA. Ma riesco ad abilitare sia l'interrupt per il PIN sia il watchdog?

si

Dunque, inanzitutto buonasera.
Ho fatto progressi.
Ho messo insieme l’interrupt del watchdog e quello del pin per risvegliare attiny con entrambi gli eventi (dopo un po di ore di studio)
Credo di aver fatto dei pasticci perche’ passano 8 secondi, trasmette quello che deve trasmettere e poi non si sveglia piu’…

NODO_contatto.pde (3.35 KB)

Le ISR DEVONO essere [u]le più veloci possibili[/u] e quindi già mi piace poco che dentro delle ISR tu faccia addirittura una trasmissione seriale ... ... premesso questo, che comunque, perdona, è non buona programmazione, vedo che nell'IRS del watchdog tu lo disabiliti, ma ... mi sembra che non venga più riabilitato (almeno non mi sembra di vedere un wdt_enable()).

Infine per abilitare il WD, non vedo perché fare tutte quelle operazioni con i registri (ed il relativo rischio di errori) quando passando alla wdt_enable() il giusto parametro abiliti il WD e decidi il tempo ... ::)

Guglielmo

... ah, dimenticavo, TUTTE le variabili che sono globali e che usi dentro le ISR DEVONO essere dichiarate "volatile".

Quindi, ad esempio ...

volatile uint8_t msg[6] = {0,0,0,0,0,0};

Però, come detto, meglio che tutto il trattamento lo fai fuori e nelle ISR attivi solo una flag che ti dice che devi fare un qualche cosa ;)

Guglielmo

Grazie Guglielmo, 2 giorni di forum e gia’ 3 risposte!!
Ho spostato tutte le operazioni nella sub loop come da te suggerito. Non sapevo che le ISR devono essere rapide.
Praticamente ora funziona tutto, sia il WTD che il pin mode.
L’unica cosa che ora se voglio che non trasmetta ogni 8 secondi devo mettere un contatore che si incrementa ogni risveglio. Ha senso tutto cio’? O il risparmio di batteria e’ irrisorio?
Se voglio distinguere quale interrupt e’ intervenuto come faccio? Se uso una variabile in ISR come scrivi tu se volatile non la posso sfruttare nel loop

L'unica cosa che ora se voglio che non trasmetta ogni 8 secondi devo mettere un contatore che si incrementa ogni risveglio. Ha senso tutto cio'? O il risparmio di batteria e' irrisorio?

Ci sono due componenti che consumano energia, il Micro e il Tx-RF Il Micro lo puoi mettere in Sleep e risvegliare ogni 8 sec, incrementare un contatore e rimetterlo a nanna, ma il TX ? Anche se non trasmetti, consuma.

Se voglio distinguere quale interrupt e' intervenuto come faccio?

Hai solo due ISR, una del Watchdog.... ISR(WDT_vect) e una del pin--- ISR(PCINT0_vect)

Se uso una variabile in ISR come scrivi tu se volatile non la posso sfruttare nel loop

Veramente Guglielmo ha detto :

TUTTE le variabili che sono globali e che usi dentro le ISR DEVONO essere dichiarate "volatile".

Ricordati che una variabile deve essere dichiarata volatile SOLO se usata sia fuori che dentro una ISR Mentre se è solo fuori da una ISR o solo dentro ad una ISR NON và dichiarata come volatile

Buongiorno Brunello, in realtà il TX non è sempre alimentato, ma uso un NPN che attivo con un pin del micro durante il ciclo della trasmissione. Ok capito, ho letto male la frase di Guglielmo. Avevo erroneamente interpretato che le variabili nelle ISR non possono essere globali ma locali, in realtà diceva altro. Quindi proverò nella ISR del WTD a incrementare un contatore a questo punto dichiarato volatile e in loop eseguo la trasmissione solo se il contatore raggiunge il valore che voglio.

NO, [u]nelle ISR metti SOLO a TRUE una flag/u, poi, nel loop(), quando ti svegli a causa di un interrupt ...

se flag_WD ... resetti la flag a FALSE, incrementi contatore e se contatore >= 5400 fai quello che devi fare, altrimenti ... sleep se flag_pinInt ... resetti la flag a FALSE, fai quello che devi fare e poi sleep

Più banale di così si muore ...

Guglielmo

Non capisco… sara’ banale ma mi si sveglia sempre dopo 8 secondi :frowning:

ISR(PCINT0_vect) {
    // This is called when the interrupt occurs, but I don't need to do anything in it
  pin_flag=true;
    }
    
    // watchdog interrupt
ISR (WDT_vect) 
{
    wdt_disable();  // disable watchdog
    wdt_flag=true;
}  // end of WDT_vect


void loop() {
 
  sleep();
  
  if (pin_flag=true) { //micro svegliato dal contatto
    pin_flag=false;
    digitalWrite(statusLED, HIGH);
    delay(500);
    // legge stato contatto reed e trasmette
    int stato_contatto = digitalRead(pin_contatto);
    msg[0]= 255;
    msg[1]= 255;
    msg[2]=ID1;
    msg[3]=ID2;
    msg[4]=101;
    msg[5]=stato_contatto;
    for (int i=0; i <= 10; i++){
      man.transmitArray(6,msg);
      delay(100);
     } // fine for
    delay(500);
    digitalWrite(statusLED, LOW);
  }
        
   if (wdt_flag=true) { //micro sveglito dal watchdog
    wdt_flag=false;
    conta=conta+1;
   }
   
   if (conta>10) {
      conta=0;
      digitalWrite(statusLED, HIGH);
      delay(500);
    // trasmette tensione alimentazione
    VCC = SS.getVCC();
    volt=int(VCC/100);
    delay(500);
    msg[0]= 255;
    msg[1]= 255;
    msg[2]=ID1;
    msg[3]=ID2;
    msg[4]=86;
    msg[5]=volt;
    for (int i=0; i <= 10; i++){
      man.transmitArray(6,msg);
      delay(100);
     } //fine for
    delay(500);
    digitalWrite(statusLED, LOW);
   }

Mi spieghi perché disabiliti il WD quando ti sveglia ? Una volta abilitato NON lo devi più disabilitare ... continua a girare ed ogni 8 secondi genera un interrupt ...

... poi, certo che si sveglia ogni 8 secondi, [u]è il massimo che si può avere[/u] e, difatti, tu quando ti svegli incrementi il contatore e se il contatore è maggiore di 10 fai qualche cosa (ovvero ogni 80 secondi).

Quindi ?

Guglielmo

No scusa ho scritto male, volevo dire che mi accende il led ogni 8 secondi invece che 80. Comunque si il watchdog non lo disabilito piu'

Se te lo accende ogni volta …
… metti il codice completo (… mi raccomando, racchiuso tra i tag CODE che, in fase di edit, ti inserisce il bottone </> … primo a sinistra), perché, da quello che si vede non è possibile ::slight_smile:

Ah … mi sono accorto che hai sbagliato gli IF … IF variabile == valore e NON = che è un’assegnazione !!!

Quindi …

if (pin_flag == true) { //micro svegliato dal contatto
if (wdt_flag == true) { //micro sveglito dal watchdog

Guglielmo

P.S.: Ancora più semplicemente, essendo quelle flag solo true o false, l’IF si può scrivere …
if (pin_flag) { //micro svegliato dal contatto
if (wdt_flag) { //micro sveglito dal watchdog

… altra cosa … il codice mi sembra più pulito così :

if (wdt_flag) { //micro sveglito dal watchdog
   wdt_flag=false;
   conta=conta+1;
   if (conta>10) {
     conta=0;
     digitalWrite(statusLED, HIGH);
     delay(500);
     // trasmette tensione alimentazione
     VCC = SS.getVCC();
     volt=int(VCC/100);
     delay(500);
     msg[0]= 255;
     msg[1]= 255;
     msg[2]=ID1;
     msg[3]=ID2;
     msg[4]=86;
     msg[5]=volt;
     for (int i=0; i <= 10; i++){
       man.transmitArray(6,msg);
       delay(100);
     } //fine for
     delay(500);
     digitalWrite(statusLED, LOW);
   }
}

… inutile fare l’IF sul contatore se NON c’è stato un risveglio da WD :wink:

Guglielmo

siiii, grazie Guglielmo! era proprio quello.... assegnavo il valore invece di confrontarlo, che erroraccio da VB (C lo uso su necessita' ma il 99% dei miei progettini su pc e' in VB net)

Grazie ancora :)

Salve a tutti, ho letto con estremo interesse tutta la conversazione. mi ritrovo a studiare la stessa cosa cioè come far coesistere il watchdog e l'interrupt esterno mi chiedevo se fosse possibile visionare il codice finale o quanto meno avere delle direttive in tal senso, chiedo scusa per l'eventuale disturbo e grazie mille in anticipo.

Luca