millis(); ma non sempre la stessa domanda!

non è la solita domanda su come si usa il millis o almeno fin ora non mi è capitato di trovare risposta

tipica applicazione di millis(); con bluetooth e secondo seriale

#include <SoftwareSerial.h>
SoftwareSerial bt (6, 7);

int data = 0;
int ventola = 8;
unsigned long startTime1 = 0;
unsigned long currentTime = 0;
int interval = 5000;



void setup() {
  Serial.begin(19200);
  bt.begin(9600);
  pinMode(ventola, OUTPUT);
}

void loop() {

  if (bt.available() > 0) {
    data = bt.read();
    Serial.print("comando ricevuto: ");
    Serial.println(data);
  }

  if (data == 100) {
    digitalWrite(ventola, HIGH);
    startTime1 = currentTime;
    data = 0;
  }
// qui:
  if (currentTime - startTime1 > interval) {
    digitalWrite(ventola, LOW);
    Serial.println("dentro?"); //come posso farlo entrare solo una volta? e non farlo entrare all inizio?
  }

  Serial.println(currentTime - startTime1);
  currentTime = millis();
}

la mia domanda, che potete vedere anche nel commento al codice è: come faccio a fargli eseguire quella condizione segnata solo una volta? e sopratutto come potrei fare a non farlo entrare inizialmente?

p.s: so che potrei risparmiarmi una variabile eliminando currentTime

Ci sono almeno DUE thread attuali che parlano di ciò che chiedi ... prova un po' a cercare ...

Guglielmo

io sinceramente ho trovato solo questa ma l unico suggerimento che ho visto è stato quello delle flag. se la funzione fosse solo una ok, ma ho un codice un pò più lungo e lo complicherei in maniera incredibile mettendo svariate flags quindi ho pensato esista un modo più semplice che magari mi sta sfuggendo

Nel codice che hai messo vedo un solo punto dove hai scritto "//qui:" ... e, comunque, quella o di flag o di variabile di stato è la sola strada.

Guglielmo

Ti serve comunque una variabile che ti dice se la ventola è accesa o spenta.
Così come è scritto, se invii il 100 sulla seriale mentre la ventola è accesa, ti prolunga l'accensione della ventola fino a 5 secondi dopo aver ricevuto il comando.
Basta una booleana ventaccesa che metti a true quando accendi la ventola ed a false quando la spegni.
A questo punto potrai verificare se sono trascorsi i 5 secondi solo quando questa variabile è true, quindi solo quando la ventola è accesa. Puoi mettere un if fuori dall'if che verifica i millisecondi o più semplicemente un and in quello stesso if.

Quindi

if (currentTime - startTime1 > interval)

diventa

if ventaccesa && (currentTime - startTime1 > interval)

e ovviamente dopo il digitalWrite che spegne la ventola mettere un ventaccesa = false.

Se vuoi che il comando seriale agisca solo se giunge quando la ventola è spenta e venga ignorato quando la ventola è accesa, basta fare una cosa analoga nell'altro if.
Ricordati di mettere ventaccesa = true dopo il digitalWrite che accende la ventola.

beh mi sono scervellato per una buona ora oggi pomeriggio cercando una soluzione alternativa alle flag ma alla fine con solo due variabili in più il codice gira molto bene. se a qualcuno va di dare un occhiata qui c è il codice completo

#include <SoftwareSerial.h>
SoftwareSerial bt (6, 7);

int data = 0;
int ventola = 8;
int buzzer = 9;
unsigned long startTime1 = 0;
unsigned long startTime2 = 0;
unsigned long currentTime = 0;
unsigned long countdown = 0;
boolean timer1 = false;
boolean timer2 = false;
int interval = 5000;
int times [2] = {};
// times [0] = hours
// times [1] = minutes

//ora
void setup() {
  Serial.begin(19200);
  bt.begin(9600);
  pinMode(ventola, OUTPUT);
}

void loop() {

  if (bt.available() > 0) {
    data = bt.read();
    Serial.print("comando ricevuto: ");
    Serial.println(data);
  }

  //ventola

  if (data == 100) {
    digitalWrite(ventola, HIGH);
    startTime1 = currentTime;
    timer1 = true;
    data = 0;
  }

  if (data == 101) {
    digitalWrite(ventola, LOW);
    data = 0;
  }

  if (currentTime - startTime1 > interval & timer1 == true) {
    digitalWrite(ventola, LOW);
    timer1 = false;
  }


  //sveglia

  if (data == 120) {
    bt.print(121);

    while (bt.available() == 0) {
      ; //wait
    }

    Serial.println("ore e minuti:");
    for (int i = 0; i < 2; i++) {
      times [i] = bt.read();
      Serial.println(times [i]);
      delay(30); //dovuto alla trasmissione bt
    }

    countdown = (times [0] * 3600) + (times [1] * 60); //seconds
    countdown = countdown * 100; // milliseconds, c è uno zero in meno per velocizzare il debug
    startTime2 = currentTime;
    timer2 = true;
    data = 0;
  }


  if (currentTime - startTime2 > countdown  & timer2 == true) {
    for (int i = 0; i < 10; i++) {
      digitalWrite(ventola, HIGH);  // per adesso sto comandando un led ma poi ci metto un buzzer
      delay(100);
      digitalWrite(ventola, LOW);
      delay(100);
    }
    timer2 = false;
  }

  currentTime = millis();
}

Approfitto dell'argomento "tempo" per chiedere quale sia la miglior soluzione per un "ritardo" temporale di qualche giorno senza che sia richiesta un'accuratezza estrema.
Ho letto qualcosa e visto che ci sono doverse soluzioni che presentano vantaggi e svantaggi come ad esempio che la funzione delay() ferma effettivamente tutto il ciclio della MCU. Per me quest non sarebbe un problema non dovendo monitorare altro.

puoi usare un delay fino a quasi 50 giorni, quindi non hai problemi.
Ma veramente e' quello che vuoi ?
Se ti studi Blink Without Delay fai la stessa cosa senza precluderti l'uso della MCU per altro

Testato:
puoi usare un delay fino a quasi 50 giorni, quindi non hai problemi.
Ma veramente e' quello che vuoi ?
Se ti studi Blink Without Delay fai la stessa cosa senza precluderti l'uso della MCU per altro

infatti avendo letto qualcosa mi stanno sorgendo perplessità in merito al delay...

usa il millis():

unsigned long tempo;

void setup() {

  tempo = millis(); 
  ecc ecc
}

void loop()
{

if ((millis() - tempo) > 86400000) {
  ecc ecc
tempo = millis();
      
   }
 }

dove 86400000 è 1 giorno (24H) in millisecondi. Tu moltiplicalo x quanti giorni vuoi.

Ciao

attento alle moltiplicazioni, una cosa del tipo 86400000*3 non funziona :wink:

Testato:
attento alle moltiplicazioni, una cosa del tipo 86400000*3 non funziona :wink:

Perchè ? Parliamo di UL quindi 232 ... ::slight_smile:

Guglielmo

non per quel motivo :slight_smile:
prova a pensare cosa succede se ad esempio fai delay(60x1000) :slight_smile:

Testato:
prova a pensare cosa succede se ad esempio fai delay(60x1000) :slight_smile:

Perché, che succede se fai delay(60UL * 1000UL); ?

Guglielmo

P.S.:E non sono neanche sicuro che serva mettere UL dato che il risultato già deve finire in un UL e quindi il compilatore applica l'integer auto promotion.

appunto, UL
serve serve,
il compilatore inserisce il risultato in un int

non credo che l'autore del topic lo avrebbe inserito
non serve su entrambi i numeri, basta su uno :wink:

Ah .. ok .. intendevi quello .. chissà quale buco credevo avessi scoperto :smiley: :smiley: :smiley:

Guglielmo

Testato:
appunto, UL
serve serve,
il compilatore inserisce il risultato in un int

non credo che l'autore del topic lo avrebbe inserito
non serve su entrambi i numeri, basta su uno :wink:

In teoria lo dovrei mettere nella mia variabile "countdown" che è un UL. Mentre ci sono vorrei capire bene il motivo per cui il compilatore effettua le operazioni come se fossero int. Posso immaginare che lo faccia per velocizzare e risparmiare memoria? Cioè lui li tratta come int se non diversamente specificato

P.S: come funzionano le librerie del tipo deep sleep? Credo di averne vista una nel playground, funziona bene?

Il problema esiste solo se usi numero in modo diretto, non se assegni un valore ad una variabile, quindi sulla tua countdown non serve.

In pratica usare un numero in modo diretto per il compilatore è comunque una variabile, e gli viene assegnato int come tipo di dato.

Il perché proprio int non lo so, sarà qualche ragione storica derivante dalla notte dei tempi ?

X guglielmo, integer auto promotion.
Da quel che so io avviene sugli operandi, non sul risultato, quello infatti spiega il perché UL basta su un solo termine, ma nel caso specifico dove entrambi gli operandi rientrano in int il risultato resta int

Se ricordo bene dovrebbe essergli assegnato il tipo più piccolo in grado di contenerlo. Il problema è che sia 60 che 1000 stanno in un (signed) int, il loro prodotto no.

Testato:
X guglielmo, integer auto promotion.
Da quel che so io avviene sugli operandi, non sul risultato, quello infatti spiega il perché UL basta su un solo termine, ma nel caso specifico dove entrambi gli operandi rientrano in int il risultato resta int

Veramente il compilatore sa esattamente che il risultato DEVE essere UL dato che è dichiarato nel prototipo della funziona delay(), il problema è che ho fatto un po' di prove (... non con la delay, con una delle assegnazioni a variabile) e ... la cosa è un po' più complessa ...

void setup() {
  // put your setup code here, to run once:
  unsigned long tot;
  unsigned int a, b;
  
  delay(1000);
  Serial.begin(9600);
  
  a = 1000;
  b = 60;
  tot = a * b;
  Serial.print("Totale = ");
  Serial.println(tot);

  tot = 1000 * 60;
  Serial.print("Totale = ");
  Serial.println(tot);
}

void loop() {
  // put your main code here, to run repeatedly:

}

nella prima moltiplicazione, essendo coinvolte due variabli non segnate e il risultato è non segnato, fa l'auto promotion.

Nella seconda moltiplicazione, 1000 e 60 vengono probabilmente viste come interi segnati e ... NON fa più l'auto promotion.

Stessa cosa, a riprova, se si modifica il programma così:

void setup() {
  // put your setup code here, to run once:
  unsigned long tot;
  int a, b;
  
  delay(1000);
  Serial.begin(9600);
  
  a = 1000;
  b = 60;
  tot = a * b;
  Serial.print("Totale = ");
  Serial.println(tot);

  tot = 1000 * 60;
  Serial.print("Totale = ");
  Serial.println(tot);
}

void loop() {
  // put your main code here, to run repeatedly:

}

Quindi, fa l'auto promotion se la destinazione e le origine sono o entrambe segnate o entrambe non segnate, ma non gestisce il caso misto.

Molto probabilmente delay((unsigned int)1000 * (unsigned int)60); funziona ... provo e vi faccio poi sapere ... ::slight_smile:

Guglielmo