Uso di millis() per iniziare

Cia a tutti.
Stavo provando a usare il millis al posto del delay.

Quindi ho iniziato con una cosa semplice..la simulazione di un pulsante che dopo 600 millisecondi commuta l'uscita alta a bassa...
ma non funziona in nessun modo...

Quello che funziona con il delay è questo

if(readString.startsWith("GET /porta1")) {
          digitalWrite(5, HIGH); 
          delay(600);
          digitalWrite(5, LOW);
}

Con il millis ho provato questo:

unsigned long buttontime = 0;

void setup(){
pinMode(6, OUTPUT);
buttontime = millis();

void loop(){
         if(readString.startsWith("GET /porta2")) {
          digitalWrite(6, HIGH);
          if((millis()>buttontime+600))
          digitalWrite(6, LOW);
}

ma non va in nessun modo, non da nemmeno l'uscita alta, resta sempre low.

Mi potete dire cosa sbaglio?
Grazie

Io direi di leggere meglio questo:
MILLIS

Facci sapere

E anche ripassare le basi di programmazione. :wink:

f.schiano:
Io direi di leggere meglio questo:
MILLIS

Facci sapere

E l'ho letto, ma non mi quadra il discorso..

ad un certo punto dice

Returns
Number of milliseconds since the program started (unsigned long)

Quindi
unsigned long time;

void setup(){
Serial.begin(9600);
}
void loop(){
Serial.print("Time: ");
time = millis();
//prints time since program started
Serial.println(time);
// wait a second so as not to send massive amounts of data
delay(1000);
}

Non riesco ad adattarlo al mio problema...

Se non sbaglio io dovrei avere una variabile che mi da il tempo in cui il pin 6 è diventato HIGH, e da li sommargli un ritardo di 600 millisecondi per portare il PIN 6 a LOW.

Sto facendo un gran casino....

dr4gone:

          if((millis()>buttontime+600))

Intanto qui almeno sposterei una parentesi:

          if ( millis() > (buttontime+600) )

:sweat_smile: :sweat_smile: eheheh.. effettivamente era un errore da fesso.. non me ne ero proprio reso conto :smiley:

Cmq.. il problema persiste.. non riesco a capire come si possa fare.

Secondo me sbagli completamente approccio di programmazione.
Intanto facci capire una cosa. Lo stato HIGH del pin lo hai fin dalla partenza del programma oppure lo imposti tu in qualche modo?
Se lo imposti tu, vuoi che torni LOW dopo 600 ms?

Vediamo, potresti fare così (codice NON testato, l'ho scritto a "getto"):

int contatore;
byte registro;

void setup(){
    pinMode(6, OUTPUT);
    registro = 0;
}

void loop(){
    if(readString.startsWith("GET /porta2")) {
        if (registro == 0) {
            contatore = millis()+600;
            digitalWrite(6, HIGH);
            registro = 1;
        }
    }
    if (registro == 1) {
        if(millis()>contatore) {
            digitalWrite(6, LOW);
            registro = 0;
        }
    }
}
  1. Legge la porta
  2. se la porta ha dato l'OK metti il pin 6 a HIGH e calcoli la fine del temporizzatore;
  3. per controllo, il punto 2) lo esegui solo se non lo hai già eseguito (registro=1)
  4. controlli se hai un conto di tempo attivo (registro =1): se sì, controlli se è passato il tempo, nel caso resetti tutto

leo72:
Secondo me sbagli completamente approccio di programmazione.
Intanto facci capire una cosa. Lo stato HIGH del pin lo hai fin dalla partenza del programma oppure lo imposti tu in qualche modo?
Se lo imposti tu, vuoi che torni LOW dopo 600 ms?

In poche parole sto usando una webinterface che quando riceve una GET : if(readString.startsWith("GET /porta2")) fa scattare lo stato su HIGH e dopo 600 milliscondi deve ritornare LOW

Lo stato è Normalmente basso. Praticamente devo sumulare la pressione di un pulsante per l'apertura di un'elettroserratura. "Apri,aspetta600 millisecondi e chiudi"

Cosa che funziona perfettamente con questo codice:

if(readString.startsWith("GET /porta1")) {
          digitalWrite(5, HIGH); 
          delay(600);
          digitalWrite(5, LOW);

Vediamo, potresti fare così (codice NON testato, l'ho scritto a "getto"):

int contatore;

byte registro;

void setup(){
    pinMode(6, OUTPUT);
    registro = 0;
}

void loop(){
    if(readString.startsWith("GET /porta2")) {
        if (registro == 0) {
            contatore = millis()+600;
            digitalWrite(6, HIGH);
            registro = 1;
        }
    }
    if (registro == 1) {
        if(millis()>contatore) {
            digitalWrite(6, LOW);
            registro = 0;
        }
    }
}



1) Legge la porta
2) se la porta ha dato l'OK metti il pin 6 a HIGH e calcoli la fine del temporizzatore;
3) per controllo, il punto 2) lo esegui solo se non lo hai già eseguito (registro=1)
4) controlli se hai un conto di tempo attivo (registro =1): se sì, controlli se è passato il tempo, nel caso resetti tutto

Perfetto.. l'idea è questa, però con questo codice che hai scritto , l'uscita non commuta mai in LOW, una volta che mando la get resta HIGH, appena testato.

Te l'avevo detto, era codice non testato. Cmq la logica è questa, lavoraci sopra un po', altrimenti non imparerai mai :stuck_out_tongue:

Dopo un po di sbattimento ho pensato a questo.. (ho incluso dei serial print per seguire passo passo l'evoluzione del codice)

int contatore;
int stato;

void setup(){
pinMode(6, OUTPUT);
stato = 0;

void loop(){
 if(readString.startsWith("GET /manda_impulso")) {
            Serial.println("stato inziale");
            Serial.println(stato);
        if (stato == 0) {
            contatore = millis()+600;
            digitalWrite(6, HIGH);
            stato++;
             Serial.println("stato dopo uscita alta");
             Serial.println(stato);
             Serial.println("tempo (millis)");
             Serial.println(millis());
             Serial.println("tempo incrementato (contatore)");
             Serial.println(contatore);
        }
    }
          if (stato == 1) {
            if (millis()<contatore) {
            digitalWrite(6, LOW);
            stato--;
            Serial.println("stato dopo uscita bassa");
            Serial.println(stato);
        }
        }

Dal serial monitor esce questo:

GET /manda_impulso HTTP/1.1
stato inziale
0
stato dopo uscita alta
1
tempo (millis)
11309
tempo incrementato (contatore)
11864
stato dopo uscita bassa
0

Quindi la logica fila, il codice sembra fare quello che serve, ma il risultato non si vede...
E' come se fosse che quel ritardo di 600 millisecondi non venga preso in considerazione.
Infatti la cosa assurda è che se metto 600, 1000, o 60000 non cambia nulla,o meglio cambia solo sul serialmonitor, ma il led che uso per simulare l'impulso resta acceso per pochissimo.. diciamo che si accende e si spegne subito dopo, indifferentemente dall'intervallo di tempo che gli fornisco...

Mi sa che il millis non funziona così. Come se quel 600 non lo riconoscesse come una pausa...
Mi sapete dire come mai?


P.S.
Ripensandoci meglio, e pensando alla natura multistasking del millis(), forse credo che sia naturale che così non possa funzionare...

Scrivendo una cosa del genere:

if(readString.startsWith("GET /manda_impulso")) {
digitalWrite(5, HIGH);
delay(600);
digitalWrite(5, LOW);
}
Funziona perchè il delay() è proprio un interrupt, che apunto interrompe completamente il loop.
Dall'altro lato il millis, non interrompe niente di per se, conta e basta, quindi mentre lui conta, il loop va avanti, setta immediatamente lo stato=1 perchè l'uscita è andata alta, e non aspetta i 600 ms...
Questo if : if (millis()<contatore) { doveva fare appunto quello, ma mi sa che non lo fa :roll_eyes: .

Che dite?
Ci sto sbattendo la testa da un po di tempo..
Voi dite che è semplice..
Ma a me non sembra proprio.

Direi che hai fatto un bel casino :grin:
Intanto questa "int contatore;" deve diventare "long int contatore;" poi la logica del programma è la seguente:

loop()
{
// in mezzo quello che ti pare

if (condizione_di_avvio == true)
{
contatore = millis() + 600;
uscita = HIGH
// inserire reset condizione di avvio, p.e. "condizione_di_avvio = false"
}

if (contatore < millis())
{
uscita = LOW;
}

// in mezzo quello che ti pare
}

Un bacchettatina anche ad astrobeed :wink:
E' vero, ho sbagliato mettendo int contatore ma anche long int contatore non va bene: long int non esiste, basta scrivere long :stuck_out_tongue:
Cmq ora che mi ricordo va usato unsigned long contatore per evitare i problemi di segno se lo sketch è in esecuzione da molto tempo.

leo72:
Un bacchettatina anche ad astrobeed :wink:

Sei sicuro ? :slight_smile:
E' vero che sarebbe meglio usare "unsigned long int", ma anche "long int" va bene, unico inconveniente è che dopo 25 giorni il valore diventa negativo, e viene accettato dal compilatore perché quella è la sintassi standard del C, provare per credere. :slight_smile:

Giusto per buttare altra benzina (tanto costa poco) sul fuoco.
Vedo che spesso e volentieri quasi tutti dichiarate le variabili con "int" anche quando non serve, un intero è un numero a 16 bit (da 0 a 65535), se la dimensione della variabile rientra in un numero compreso tra 0 e 255, caso tipico di un flag, è molto meglio dichiararla come "char" oppure "unsigned char", nel primo caso può essere sia un carattere ASCII che un valore numerico compreso tra -127 e +128, nel secondo caso è un valore numerico compreso tra 0 e 255.
Il vantaggio nell'usare char al posto di int è innanzitutto nel risparmio della RAM, 1 byte invece di due, e poi nella velocità operativa visto che l'ATmega 328 è un micro a 8 bit e per trattare valori a due byte deve fare più operazioni che si traducono in più cicli macchina e maggiore lentezza del programma, ancora peggio quando si parla di long int e float.
In wiring sono previsti char e byte, quest'ultima corrisponde alla unsigned char e non fa parte della sintassi standard C.

astrobeed:
Sei sicuro ? :slight_smile:

La sintassi del linguaggio presente in "Reference" cita come tipi /unsigned) int e (unsigned) long :stuck_out_tongue: XD XD

Tornando al discorso del tipo di variabili, questo è un approccio che vedo in molti hanno perché sono abituati a ragionare in termini di GB, la capacità dei computer odierni. Oggigiorno nessuno ottimizza il codice, se un computer non ce la fa ad eseguire un programma, non si tende a migliorare il software, si "consiglia" (leggi "obbliga") di aggiornare l'hardware!
Un tempo, quando c'erano i computer ad 8 bit tipo il C64 e lo Spectrum, essi avevano hardware che durava per anni e l'unico modo per poter migliorare un programma era ottimizzarne il codice. Basti vedere i primi giochi per questi computer e paragonarli a quelli prodotti verso la fine della loro vita commerciale! Era incredibile il progresso che i programmi facevano!

Con gli Atmega non si pensa che si hanno solo 32 KB di spazio, che poi sono 31,5 dato che 512 byte se ne vanno per il bootloader, e solo 1 KB di SRAM per le variabili del proprio codice! Significa in uno sketch breve la cosa non ha peso ma se si iniziano a scrivere programmi voluminosi, dichiarare un array di tipo integer con magari 100 elementi si è già consumato circa 200 byte della SRAM, ovvero 1/10 del totale!

leo72:
La sintassi del linguaggio presente in "Reference" cita come tipi /unsigned) int e (unsigned) long :stuck_out_tongue: XD X

Si ma wiring non è C puro, contiene varie deleghe alla sintassi standard, però il sistema è realizzato in modo da poter scrivere codice anche C ANSI, e non fosse così sarebbe impossibile superare i limiti di Arduino imposti da wiring, quindi non è un errore scrivere "long int" o solo "long".

Ehi !! Molto interessanti queste considerazioni sull'ottimizzazione del codice per risparmiare preziosi byte..
Il problema è che per ottimizzare un codice prima bisogna farlo funzionare :grin: :grin: :grin: :grin: e questo non funziona!!! :smiley:

Quindi mi sono permesso di scrivere un piccolissimo sketch che simula la cosa completamente, in modo tale che se avete tempo mi potreste aiutare ad uscirne fuori salvaguardando la mia salute mentale...

Se aprite il serial monitor e scrivete H (maiuscolo) parte lo sketch, mi sono permesso di mettere anche dei Serial.println che secondo me sono impportanti per capire quale sia il problema, che secondo me è proprio questo: fa il confronto poco dopo e non aspetta i 600 ms..!!

Tempo iniziale
1168
Tempo incrementato
1768
tempo per il confronto
1226

int ins_val; 
unsigned long contatore;
boolean stato;

void setup()
{
pinMode(6, OUTPUT);
stato = true;
Serial.begin(9600);

}

void loop()
{
if (Serial.available() > 0) {
  ins_val = Serial.read();
if (ins_val == 'H') {
  if(stato == true) {
           contatore = millis() + 600;
           digitalWrite(6, HIGH);
           stato = false;
           Serial.println("Tempo iniziale");
           Serial.println(millis());
           Serial.println("Tempo incrementato");
           Serial.println(contatore);
        }
    }

if (contatore < millis()) 
            {
            digitalWrite(6, LOW);
            stato = false;
        }
Serial.println("tempo dopo il confronto"); 
Serial.println(millis());
  }
}

Quindi se avreste la pazienza di aiutarmi proprio con questo esempio pratico sarebbe cosa gradita :smiley:

Ecco il tuo programma modificato in modo che funzioni, non ho fatto altro che seguire la traccia che ti avevo scritto, ho modificato il pin out per usare il led di Arduino, così ha un riscontro visivo immediato senza dover collegare nulla.

int ins_val; 
unsigned long contatore;
boolean stato;

void setup()
{
pinMode(13, OUTPUT);
stato = true;
Serial.begin(9600);
contatore = 0;
}

void loop()
{
 if (Serial.available() > 0)
  {
   ins_val = Serial.read();
   if (ins_val == 'H') stato = true;
  }   
  
 if(stato == true)
  {
   contatore = millis() + 1000;
   digitalWrite(13, HIGH);
   stato = false;
   Serial.println("Tempo iniziale");
   Serial.println(millis());
  }

 if (contatore < millis() & contatore > 0)
  {
   digitalWrite(13, LOW);
   stato = false;
   Serial.println("tempo dopo il confronto"); 
   Serial.println(millis());
   contatore = 0;
  }
}

Grazie mille, gli ho dato un'occhiata veloce perchè ora non posso provarlo.
Stasera quando rientro lo proverò sicuramente..

Tanto per capire cosa hai fatto:
Hai aggiunto questa condizione all' if: & contatore > 0 (ed è questo il punto chiave, la sua assenza faceva si che non scattasse mai il LOW) giusto?
E poi hai resettato le variabili contatore e stato.
Alla fine mi è mancato lo spunto finale per arrivarci da solo!
GRAZIE MILLE :wink:

dr4gone:
Hai aggiunto questa condizione all' if: & contatore > 0 (ed è questo il punto chiave, la sua assenza faceva si che non scattasse mai il LOW) giusto?

No, la seconda condizione serve solo per evitare che una volta scaduto il tempo venga sempre eseguita la if, la condizione risulterebbe sempre vera.
Guardati attentamente la traccia che ho scritto qualche post indietro, è la stessa cosa del programma solo scritta in modo più semplice e comprensibile.