problema con countdown

ciao, ho apportato un po di modifiche al mio progetto (che prima funzionava) ma ho il countdown per uscire dallo "stato 2" che anche se impostato a qualsiasi minutaggio (20 60 110) mi va su "tempo scaduto" e torna a "stato 0" dopo 3/4 secondi.
sul display per quei pochi secondi leggo i minuti giusti.
c'ho ragionato tutto il giorno ma non ne vengo a capo, mi potete aiutare? grazie

parte 1:

#include <StepperMotor.h>//libreria motore passopasso
#include <LiquidCrystal_I2C.h> //libreria display
#include <EEPROM.h> //libreria scrittura su memoria assenza rete

//#define DEBUG //decommentare per attivare la scrittura su seriale per PC
#define ledmovimento  2 //modulo lampeggiante (led rosso)
#define ledstato  3 //led giallo di notifica (spento=sitema a riposo) (acceso=sistema attivo) (fadein fadeout statomanuale a tempo) (lampeggiante= anomalia posizione motore)
#define reletermostato  4 //relè contatto termostato
#define startsonoff  5 //comando da remoto (SONOFF)
#define finecorsa  6 //finecorsa per motore passopasso
#define riarmo  7 //pulsante per spegnere il sistema
#define _PIN1  8 //pin motore
#define _PIN2  9 //pin motore
#define _PIN3  10 //pin motore
#define _PIN4  11 //pin motore
#define releoff 13 //contatto per spegnimento sonoff
#define minpiu A0 //pulsante incremento minuti manuale
#define minmeno A1 //pulsante decremento minuti manuale
#define attesamodval 5000 //attesa ultima modifica per scrittura su eeprom
#define offintervallo 60000  //variabile conto alla rovescia

unsigned int minutimanuale; // tempo manuale
unsigned int off; //variabile per manuale
unsigned int tempomanuale; //tempo durata riscaldamento manuale in millisecondi
byte stato; //per funzione relè
byte punto0 = 0; //variabile per reset motore
byte statoprec = 0; //variabile stato precedente
byte flag = 0; //variabile per incremento temperatura
byte flag1 = 0; //variabile per decremento temperatura
byte flag2 = 0; //variabile per scrittura eeprom
byte flag3 = 0; //variabile per pulsante ritardato (riarmo)
byte flag4 = 0; //variabile per comando remoto/locale
byte brightness = 0;    // luminosità led a 0 alla partenza fadein
byte fadeAmount = 10;    // velocità fadein

bool fadein = false; //definizione stato fade-in
bool blinking = false; //definizione stato per lampeggio

unsigned long modval = 0; //variabile per tempo attesa scrittura eeprom
unsigned long timeout; //tempo di accensione riscaldamento manuale
unsigned long iniziomanuale; //variabile per temporizzatore manuale
unsigned long iniziopulsante; //variabile per ritardo pulsante
unsigned long tempopulsante; //variabile per ritardo pulsante
unsigned long blinkInterval = 110; // millisecondi di lampeggio
unsigned long currentMillis; // variabile per lampeggio
unsigned long previousMillis; //variabile per lampeggio
unsigned long tempoffinterval; //variabile conto alla rovescia
unsigned long offtime; //variabile conto alla rovescia

LiquidCrystal_I2C lcd(0x27, 16, 2); // associazione parametri display
StepperMotor stepper(_PIN1, _PIN2, _PIN3, _PIN4); //assegnazione pin motore

void setup()
{
  pinMode(startsonoff, INPUT_PULLUP); //pulsante avvio sistema
  pinMode(finecorsa, INPUT_PULLUP); //pulsante lampeggio movimento/reset motore
  pinMode(riarmo, INPUT); //pulsante touch riarmo
  pinMode(ledstato, OUTPUT); // led stato sistema prenotato
  pinMode(reletermostato, OUTPUT); //relè termostato riscaldamento
  pinMode(ledmovimento, OUTPUT); //relè led lampeggio movimento
  pinMode(minpiu, INPUT); //pulsante incremento temperatura
  pinMode(minmeno, INPUT); //pulsante decremento temperatura
  pinMode(releoff, OUTPUT); //relè spegnimento sonoff
  pinMode(_PIN1, OUTPUT); //pin motore
  pinMode(_PIN2, OUTPUT); //pin motore
  pinMode(_PIN3, OUTPUT); //pin motore
  pinMode(_PIN4, OUTPUT); //pin motore
  digitalWrite (reletermostato, HIGH); //stato relè spento ad accensione
  digitalWrite (releoff, HIGH); //stato relè spento ad accensione
  stato = 0; //per funzione relè
  flag = 0; //per funzione minuti +
  flag1 = 0; //per funzione minuti -
  flag2 = 0; //per funzione scrittura eeprom
  flag3 = 0; //per funzione ritardo pulsante
  tempopulsante = 1000; //millisecondi ritardo pulsante
  minutimanuale = EEPROM.read(0); //lettura memoria eeprom per minuti manuale impostati
  delay (50);
  tempomanuale = (minutimanuale * 60000); //conversione minuti in millisecondi
  off = minutimanuale; //impostato tempo manuale su variabile
  stepper.setPeriod(1); //velocità motore passopasso in millisecondi (1 massimo)

  lcd.init(); // inizializzo la comunicazione del display

  digitalWrite (ledmovimento, LOW); //stato relè lampeggio led rosso spento
#ifdef DEBUG
  Serial.begin(9600); // inizializzo la comunicazione seriale
#endif
  lcd.backlight(); //accensione retroilluminazione display
  lcd.begin(16, 2); //parametri display
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("AZZERAMENTO");
  lcd.setCursor(0, 1);
  lcd.print("IN CORSO");

  stepper.move (4096); //leggero allontanamento da finecorsa (1 giro)
  punto0 = digitalRead(finecorsa); //variabile per reset iniziale
  delay (10);

  while (punto0 == HIGH) //riposizionamento a punto 0 se il finecorsa non è premuto
  {
#ifdef DEBUG
    Serial.println(" azzeramento in corso ");
#endif
    stepper.move (-200);
    delay (1);
    punto0 = digitalRead(finecorsa); //controllo stato finecorsa
  }
  stepper.stop ();
#ifdef DEBUG
  Serial.println(" azzeramento eseguito ");
#endif
  lcd.clear();
  lcd.setCursor(0, 0 );
  lcd.print("     SPENTO     "); //scrittura testo standard
  lcd.noBacklight();
}

void loop()
{

  // codice per fade in su stato MANUALE
  if (fadein) {
    analogWrite(ledstato, brightness);    //imposto uscita con funzione analogica
    brightness = brightness + fadeAmount; //aumento luminosità
    if (brightness == 0 || brightness == 170) { //diminuzione luminosità
      fadeAmount = -fadeAmount ;
    }
    delay(30);
  }

  if (stato == 2) // condizione funzione manuale attivo
    fadein = true; // start lampeggio
  else
    fadein = false; // stop lampeggio
  //fine codice fade in
  // codice per lampeggio
  if (blinking) {
    if (((unsigned long)(currentMillis - previousMillis)) >= (blinkInterval)) { //calcolo tempo per lampeggio anomalia motore
      digitalWrite((ledstato), !digitalRead((ledstato))); // led giallo
      previousMillis = currentMillis; // sets the time we wait "from"
    }
  }
  int reading = digitalRead(finecorsa); //lettura stato finecorsa

  if (reading == HIGH) // condizione a finecorsa non premuto
    blinking = true; // start lampeggio
  else
    blinking = false; // stop lampeggio
  //fine codice lampeggio

  if ((digitalRead(startsonoff) == LOW) && (stato == 0)) //funzione per effetto relè stato 1
  {
#ifdef DEBUG
    Serial.println("stato=1"); //stato automatico attivo
#endif
    lcd.clear();
    lcd.backlight(); //accensione retroilluminazione display
    lcd.setCursor(0, 0);
    lcd.print("   AUTOMATICO   "); //scrittura stato su display
    stato = 1;
    flag4 = 1;
  }

parte 2:

  if ((digitalRead(riarmo) == HIGH) && (flag3 == 0) ) //pressione pulsante riarmo per stato 0
  {
    iniziopulsante = millis();
    flag3 = 1;
#ifdef DEBUG
    Serial.println("avvio timer");
#endif
  }
  if ((((digitalRead(riarmo) == HIGH) && ((millis() - iniziopulsante) >= (tempopulsante)) && (flag3 == 1)) or ((digitalRead(startsonoff) == HIGH) && (flag4 == 1)))  && (statoprec != 0)) //esegue funzione pulsante riarmo dopo tempo trascorso o da remoto
  {
#ifdef DEBUG
    Serial.println("stato=0"); //stato spento attivo
#endif
    lcd.clear();
    lcd.setCursor(0, 0);
    lcd.print("     SPENTO     "); //scrittura stato su display
    lcd.noBacklight();
    flag3 = 2;
    stato = 0;
    flag4 = 0;
    if (digitalRead(startsonoff) == LOW) //controllo che il comando remoto sia ancora acceso e lo spengo
    {
      delay (200);
      digitalWrite (releoff, LOW); //accensione relè per spegnimento sonoff
      delay (400);
      digitalWrite (releoff, HIGH); //spegnimento relè
      delay (1000);
    }
    off = minutimanuale;
  }

  if ((digitalRead(riarmo) == HIGH) && (flag3 == 1) && (statoprec == 0) && ((millis() - iniziopulsante) >= (tempopulsante))) //funzione relè manuale a tempo
  {
#ifdef DEBUG
    Serial.println ("stato=2"); //stato manuale temporizzato attivo
#endif
    lcd.clear();
    lcd.backlight(); //accensione retroilluminazione display
    lcd.setCursor(0, 0);
    lcd.print("MANUALE      min"); //scrittura stato su display
    lcd.setCursor(10, 0);
    if (off < 10) lcd.print(" ");
    lcd.print(minutimanuale); //scrittura stato su display
    if (off < 100) lcd.print(" ");
    lcd.setCursor(0, 1);
    lcd.print("   min rimanenti"); //scrittura stato su display
    lcd.setCursor(0, 1); //scrittura timer su display
    if (off < 10) lcd.print(" ");
    lcd.print(off);
    if (off < 100) lcd.print(" ");
    stato = 2;
    statoprec = 2;
    flag3 = 2;
    flag4 = 0;
  }
  if ((digitalRead (riarmo) == LOW) && (flag3 != 0 ) ) //reset variabile pulsante riarmo
  {
    flag3 = 0;
#ifdef DEBUG
    Serial.println("flag a 0");
#endif
  }

  if (stato == 1) //accensione
  {
    digitalWrite (reletermostato, LOW); //accensione relè
    if ((digitalRead(finecorsa) == LOW) && (stato == 1) && (ledmovimento == LOW))
    {
      digitalWrite(ledstato, HIGH); //accensione led notifica

    }

    if ((stato != statoprec) && (stato == 1) && (statoprec != 2))  //funzione per avviamento motore chiudiporta
    {
#ifdef DEBUG
      Serial.print("avviamento motore");
#endif
      lcd.clear();
      lcd.setCursor(0, 0);
      lcd.print("  NON TOCCARE");
      lcd.setCursor(0, 1);
      lcd.print("LA PORTA!!!!!!!");
      digitalWrite (ledmovimento, HIGH); //accensione led lampeggiante)
      stepper.move (20480); //rotazione motore 4096 per 1 giro (max 5 giri a step)
      delay (10);
      stepper.move (20480);
      delay (10);
      stepper.move (19430); //fine chiusura porta
      delay (700);
      stepper.move (-20480); //inizio ritorno in posizione di riposo
      delay (10);
      delay (10);
      stepper.move (-18430);
      punto0 = digitalRead(finecorsa); //variabile per battuta finecorsa
      while (punto0 == HIGH) // ritorno fino a pressione finecorsa
      {
#ifdef DEBUG
        Serial.println(" ritorno in posizione di riposo ");
#endif
        stepper.move (-200);
        delay (1);
        punto0 = digitalRead(finecorsa);
      }
      stepper.stop ();
      digitalWrite (ledmovimento, LOW); //spegnimento led movimento
      digitalWrite (ledstato, HIGH); //accensione led stato prenotato
#ifdef DEBUG
      Serial.println(" riposizionamento eseguito ");
#endif
      lcd.clear();
      lcd.setCursor(0, 0 );
      lcd.print("   AUTOMATICO   "); //scrittura testo standard
      lcd.backlight(); //accensione retroilluminazione display
      statoprec = 1;
      off = minutimanuale; //reset countdown
    }
  }

  if (stato == 0) //riarmo sistema
  {
    if ((digitalRead(finecorsa) == LOW) && (stato == 0))
    {
      digitalWrite(ledstato, LOW); //spegnimento led notifica
    }
    digitalWrite(reletermostato, HIGH); //spegnimento relè riscaldamento
    statoprec = 0;
    timeout = millis();
  }

  if (stato == 2) //funzione riscaldamento mezza stagione a tempo
  {
    iniziomanuale = millis();
    digitalWrite (reletermostato, LOW); //accensione relè
    /////////////////
    offtime = millis();
    if (((unsigned long)(offtime)) > (tempoffinterval + offintervallo)) { //diminuzione minuto timer sul display
      off = (off - 1);
      lcd.setCursor(0, 1); //scrittura timer su display
      if (off < 10) lcd.print(" ");
      lcd.print(off);
      if (off < 100) lcd.print(" ");
      tempoffinterval = millis();
    }
    //////////////////////////////
    if (((unsigned long)(iniziomanuale)) > (timeout + tempomanuale)) //tempo massimo riscaldamento
    {
#ifdef DEBUG
      Serial.println(" tempo scaduto ");
#endif
      stato = 0;
      off = minutimanuale; //reset countdown
      timeout = millis();
      lcd.clear();
      lcd.setCursor(0, 0);
      lcd.print("     SPENTO     "); //scrittura stato su display
      lcd.noBacklight();
    }
    {
      if ((digitalRead(minpiu) == HIGH) && (flag == 0)) //aumento minuti
      {
        minutimanuale = (minutimanuale + 1);
        flag = 1;
        flag2 = 1;
        modval = millis();
#ifdef DEBUG
        Serial.println( minutimanuale);
#endif
        lcd.setCursor(10, 0);
        if (minutimanuale < 10) lcd.print(" ");
        lcd.print(minutimanuale); //scrittura minuti impostati su display
        if (minutimanuale < 100) lcd.print(" ");
      }

      if ((digitalRead(minpiu) == LOW) && (flag == 1))
      {
        flag = 0;
      }
      if ((digitalRead(minmeno) == HIGH) && (flag1 == 0)) //diminuzione minuti
      {
        minutimanuale = (minutimanuale - 1);
        flag1 = 1;
        flag2 = 1;
        modval = millis();

#ifdef DEBUG
        Serial.println(minutimanuale);
#endif
        lcd.setCursor(10, 0);
        if (minutimanuale < 10) lcd.print(" ");
        lcd.print(minutimanuale); //scrittura minuti impostati su display
        if (minutimanuale < 100) lcd.print(" ");
      }

      if ((digitalRead(minmeno) == LOW) && (flag1 == 1))
      {
        flag1 = 0;
      }

      if ((flag2 == 1) && (( millis() - modval) > attesamodval)) //scrittura su eeprom
      {
        EEPROM.write(0, minutimanuale);
        flag2 = 0;
#ifdef DEBUG
        Serial.println ("scrittura eeprom");
#endif
      }
    }
  }
  delay (10);// attesa per migliorare la fluidità del programma
}

Il codice è piuttosto incasinato (rinuncio a seguire la logica), contiene elementi in esubero (vedi variabili ìiniziomanuale' e 'offtime' con la stessa funzione), commenti errati, coppie di parentesi eccedenti ecc.

Gli stati non sono mutuamente esclusivi tra loro (possibilità di effetti collaterali per logica troppo complessa). Gli eventi riconosciuti all'interno di ogni stato non sono mutuamente esclusivi tra loro (di nuovo possibilità di effetti collaterali per logica troppo complessa). Il codice è un "monoblocco", è troppo lungo e complesso da seguire, sarebbe da suddividere in piccole funzioni semplici con nomi chiari e descrittivi di cosa si sta facendo.

Ma soprattutto in qualche punto la funzione millis è usata in modo sbagliato (le condizioni soffrono del problema dell'overflow), mentre in qualche altro punto è usata correttamente.

Ad esempio il controllo che hai evidenziato andrebbe riscritto così:

if ((offtime - tempoffinterval) >= offintervallo) {

Claudio_FF:
Il codice è piuttosto incasinato (rinuncio a seguire la logica), contiene elementi in esubero (vedi variabili ìiniziomanuale' e 'offtime' con la stessa funzione), commenti errati, coppie di parentesi eccedenti ecc.

Gli stati non sono mutuamente esclusivi tra loro (possibilità di effetti collaterali per logica troppo complessa). Gli eventi riconosciuti all'interno di ogni stato non sono mutuamente esclusivi tra loro (di nuovo possibilità di effetti collaterali per logica troppo complessa). Il codice è un "monoblocco", è troppo lungo e complesso da seguire, sarebbe da suddividere in piccole funzioni semplici con nomi chiari e descrittivi di cosa si sta facendo.

Ma soprattutto in qualche punto la funzione millis è usata in modo sbagliato (le condizioni soffrono del problema dell'overflow), mentre in qualche altro punto è usata correttamente.

Ad esempio il controllo che hai evidenziato andrebbe riscritto così:

if ((offtime - tempoffinterval) >= offintervallo) {

questo codice è frutto di mille prove e modifiche, quindi sicuramente è "sporco" o cercato di pulirlo in base alle mie conoscenze ma sicuramente qualcosa è sfuggito.

ora che mi ci fai pensare invece di fargli fare quel controllo li po trei dirgli semplicemente

if (off==0){
"tempo scaduto" e i successivi comandi

Aggiungo anche che se vuoi postare un codice lungo qui nel forum, invece di metterlo in due post separati mettilo come allegato. E' più semplice per tutti.

ciao, ho apportato la modifica suggerita da @Claudio_FF, ho cercato di ripulire il codice e sistemare i commenti.
ora ho il problema che a volte (circa 4 su 10 prove fatte) quando mi passa dallo stato 2 allo stato 0 (sia che faccia io la commutazione manualmente sia allo scadere del tempo), arduino si resetta e rifà il ciclo SETUP. :o

allego file come suggerito da@docdoc

upgrade_4.ino (13.5 KB)

docdoc:
Se vuoi postare un codice lungo qui nel forum, invece di metterlo in due post separati mettilo come allegato. E' più semplice per tutti.

Tranne che per chi sta usando Android... :frowning:

Datman:
Tranne che per chi sta usando Android... :frowning:

Azz! Ma eliminare il limite dei 9000 caratteri non è possibile?

Magari fosse quello il problema!
Il problema è che, con questo telefono come con il precedente, non riesco ad aprire i file .ino, se non salvandoli e rinominando l'estensione in .txt! Purtroppo non c'è verso, mi sembra, di far aprire i file .ino come se fossero di testo.

ciao...per iniziare ti suggerisco di rivedere il tipo delle variabili minutimanuale e tempomanuale.
sono entrambe unsigned int...solo che la prima la salvi anche nella eeprom con EEPROM.write() che salva 1 byte...quindi o usi una variabile byte oppure usi la EEPROM.put().
per la seconda variabile fai un :

tempomanuale = (minutimanuale * 60000);

se la variabile minutimanuale vale 2 che valore avrà tempomanuale?

Datman:
Il problema è che, con questo telefono come con il precedente, non riesco ad aprire i file .ino, se non salvandoli e rinominando l'estensione in .txt! Purtroppo non c'è verso, mi sembra, di far aprire i file .ino come se fossero di testo.

Se parli di metterlo come allegato, a Chrome (ed al sito) non frega nulla se è ino, txt o altro. tu sfogli, selezioni e salvi.

Se parli di incollare il testo nel messaggio, allora non ho capito: se da un qualsiasi file manager (anche quello predefinito di Android) clicchi su un file .ino a me chiede "Apri come.." e poi la scelta tra Testo, Immagine, Audio, Video. Seleziono Testo e lo apro, quindi faccio copia/incolla in Chrome.

Dico che se lo scarico e cerco di aprirlo, Android 8 (già prima con il 4) si rifiuta. Devo scaricarlo e andare ad aprirlo lì con x-plore. Ho provato anche a installare qualche applicazione, ma non ho risolto il problema. Con windows, invece, se non avessi l'IDE potrei comunque aprirli come file di testo.

Datman, io ho Android 7 (e comunque lo facevo anche prima) e funziona: non devi cercare di aprirlo direttamente dalla notifica ma tu lo scarichi, finisce in Download, quindi con File Manager entri nella cartella e clicchi sul file .ino e a quel punto non conoscendo il tipo di file ti chiede come aprirlo.

Ma siamo ormai OT, se vuoi andiamo in PM.

ORSO2001:
ciao...per iniziare ti suggerisco di rivedere il tipo delle variabili minutimanuale e tempomanuale.
sono entrambe unsigned int...solo che la prima la salvi anche nella eeprom con EEPROM.write() che salva 1 byte...quindi o usi una variabile byte oppure usi la EEPROM.put().
per la seconda variabile fai un :

tempomanuale = (minutimanuale * 60000);

se la variabile minutimanuale vale 2 che valore avrà tempomanuale?

grazie,allora sia la variabile "minutimanuale" sia "off" le ho messe in byte tanto non avranno mai valore superiore a 255 (quindi risolvo il problema della eeprom

per la tua domanda su "tempomanuale" ti risponderei 120000, ma da come mi hai posto la domanda sento puzza di fregatura :smiley:
ma rianalizzando il codice, anche il valore "tempomanuale" è diventato inutile dopo le modifiche fatte l'altro giorno ovvero eliminato un altro "conto alla rovescia" sostituendolo con if (off==0).

quindi ho apportato tutte queste modifiche e ricaricato il codice... ora lo torno a testare per un po e vedo se fa ancora scherzi

edit: ma da cosa può essere causato un reset di arduino? tralasciando i collegamenti elettrici che sono giusti?

sparo la mia anche se in materia di elettronica sono "na pippa"...e nessuna di queste cose vengono eseguite:

  • reset per autoprotezione da corto circuito
  • reset per autoprotezione da sovra assorbimento
  • reset della porta USB sempre per sovra assorbimento
  • il pin di reset messo a massa
  • reset da sovra temperatura
  • calo della tensione di alimentazione (alimentatore non adeguato)