Problema con comando millis

E’ da quasi un mese che sto smanettando con arduino e fino a questo momento non ho avuto nessun problema rilevante nel programmare.
Ma oggi mi sono imbattuto in un problema che non riesco a risolvere.
Ho creato su breadbord un circuito in sono inseriti una sonda di tempertaura e un led.
Inizialmente, programmando arduino (usando il comando delay) volevo creare una cosa del genere:
-quando la temperatura superava i 30°C accendere il led.
-quando la temperatura cominciava a diminuire superando i 30°C il led restava acceso per 5 secondi per poi spegnersi.
-in più riportare la temperatura tramite seriale su schermo con frequenza 100ms.
Il problema era che nel momento in cui entrava in gioco il delay 5000 del led, su schermo non veniva più riportata nessuna temperatura (per ricomparire dopo il delay).
Per risolvere questo problema ho scoperto il comando millis(), ma mi sono bloccato.
Nel momento in cui la temperatura diminuisce e supera i 30°C il led si spegne di colpo senza aspettare i 5 secondi.

#include <OneWire.h> 
boolean a=false;
int DS18S20_Pin = 2;
const byte led = 3;
byte ledState = 0;
unsigned long previousMillis1 = 0;
unsigned long interval1 = 5000;
unsigned long previousMillis2 = 0;
unsigned long interval2 = 100;
//Temperature chip i/o
OneWire S1(2); // on digital pin 2

void setup(void) {
pinMode (led, OUTPUT);
Serial.begin(9600);
delay(2000);
}

void loop(void) {

unsigned long currentMillis = millis();
 
float temperature = getTemp();
if(temperature>30) {digitalWrite(led,HIGH);
                     a=true;}

if(temperature<30) if(a==true) if (currentMillis - previousMillis1 > interval1) {previousMillis1 = currentMillis ; digitalWrite(led,LOW); 
  a=false;}

 
if (currentMillis - previousMillis2 > interval2) {previousMillis2 = currentMillis; Serial.println(temperature);}

}



float getTemp(){
//returns the temperature from one DS18S20 in DEG Celsius

byte data[12];
byte addr[8];

if ( !S1.search(addr)) {
  //no more sensors on chain, reset search
  S1.reset_search();
  return -1000;
}

if ( OneWire::crc8( addr, 7) != addr[7]) {
  Serial.println("CRC is not valid!");
  return -1000;
}

if ( addr[0] != 0x10 && addr[0] != 0x28) {
  Serial.print("Device is not recognized");
  return -1000;
}

S1.reset();
S1.select(addr);
S1.write(0x44,1); // start conversion, with parasite power on at the end

byte present = S1.reset();
S1.select(addr);  
S1.write(0xBE); // Read Scratchpad


for (int i = 0; i < 9; i++) { // we need 9 bytes
 data[i] = S1.read();
}

S1.reset_search();

byte MSB = data[1];
byte LSB = data[0];

float tempRead = ((MSB << 8) | LSB); //using two's compliment
float TemperatureSum = tempRead / 16;

return TemperatureSum;

}

Temporizzatore.ino (1.7 KB)

si non ti funziona perchè quando superi i 30 dovresti resettare/settare anche il previuosmillis1 altrimenti il previousmillis rimane “sostanzialmente indietro” mentro penso sia scorretto resettarlo quando sei a bassa temperatura… prova a fare l’ipotesi in cui la stanza è a 34 gradi ed improvvisamente va a 29… il previous praticamente sarebbe addirittura a 0 quindi entra come una saetta nel triplo if…

 if (temperature < 30) if (a == true) if (currentMillis - previousMillis1 > interval1) {
        previousMillis1 = currentMillis ; digitalWrite(led, LOW);
        a = false;
      }

Questi 3 if alla fine sono paragonabili per funziona ad un “And” &&.

Adesso però corri a leggere il regolamento perchè non ti sei presentato e non hai formattato il codice cosa che ti costerà presumo non meno di 200 frustate :slight_smile:

usa anche per il futuro la “formattazione automatica” del codice: a volte permette di chiarire molto meglio come stanno le cose… non che il tuo caso fosse drammatico ma non mi piace moltissimo il fatto di mettere 3 if sulla stessa riga… neanche 2 istruzioni… e dopo usa il serial print come “debug”… ti verrà automatico con un po’ di esperienza comunque non ti preoccupare.

Prima cosa ti consiglio di risparmiarti le frustate andandoti a presentare e racchiudendo il codice nei tag code :slight_smile:

Secondo, mi sa che quello che chiedi è una cosa di questo genere:

#include <OneWire.h>
boolean a = false;
boolean b = false;
#define DS18S20_Pin 2
#define led 3 
byte ledState = 0;
unsigned long previousMillis1 = 0;
const unsigned int interval1 = 5000;
unsigned long previousMillis2 = 0;
const unsigned int interval2 = 100;
//Temperature chip i/o
OneWire S1(2); // on digital pin 2

void setup(void) {
  pinMode (led, OUTPUT);
  Serial.begin(9600);
  delay(2000);
}

void loop(void) {


  float temperature = getTemp();

  if (temperature > 30) {
    digitalWrite(led, HIGH);
    a = true;
  }

  if (temperature < 30) if (a == true) {
      a = false; 
      previousMillis1 = millis(); 
      b = true;
    }

  if (millis() - previousMillis1 <= interval1 && b == true) {
     digitalWrite(led, HIGH);
  }
  else if (millis() - previousMillis1 >= interval1 && b == true) {
    b = false;
    digitalWrite(led, LOW);
  }

  if (millis() - previousMillis2 >= interval2) {
    previousMillis2 = millis(); 
    Serial.println(temperature);


  }


}



float getTemp() {
  //returns the temperature from one DS18S20 in DEG Celsius

  byte data[12];
  byte addr[8];

  if ( !S1.search(addr)) {
    //no more sensors on chain, reset search
    S1.reset_search();
    return -1000;
  }

  if ( OneWire::crc8( addr, 7) != addr[7]) {
    Serial.println("CRC is not valid!");
    return -1000;
  }

  if ( addr[0] != 0x10 && addr[0] != 0x28) {
    Serial.print("Device is not recognized");
    return -1000;
  }

  S1.reset();
  S1.select(addr);
  S1.write(0x44, 1); // start conversion, with parasite power on at the end

  byte present = S1.reset();
  S1.select(addr);
  S1.write(0xBE); // Read Scratchpad


  for (int i = 0; i < 9; i++) { // we need 9 bytes
    data[i] = S1.read();
  }

  S1.reset_search();

  byte MSB = data[1];
  byte LSB = data[0];

  float tempRead = ((MSB << 8) | LSB); //using two's compliment
  float TemperatureSum = tempRead / 16;

  return TemperatureSum;

}

Grazie mille ad entrambi, (sono andato subito a presentarmi al forum) ma in fine ho capito che devo studiarmi ancora bene la funzione millis() perché non mi è per niente chiara.

Fai bene, fammi sapere se ti va quel codice che l'ho fatto al volo ;) L'importante che la studi. Ciao!

Sì sì, il codice è perfetto, va alla grande

Prom25: Sì sì, il codice è perfetto, va alla grande

;) Ci possono essere anche altre soluzioni con millis, studiatelo e magari ne puoi fare uno pure meglio !

P.S. Come vedi per definire i pin ho messo "#define" usa sempre quello perché è sprecato usare byte o int, perché con "define" non sprechi niente. byte comprende numeri da 0-255 e non ti serve variare in questo range per un solo pin... int non ne parliamo....

Aggiungo solo che anche con "const byte", "const int", ecc... non si spreca RAM. Anzi, in questo modo hai un valore tipizzato, cosa che permette al compilatore di verificare che si stia utilizzando il valore giusto nel contesto giusto.

SukkoPera:
Aggiungo solo che anche con “const byte”, “const int”, ecc… non si spreca RAM. Anzi, in questo modo hai un valore tipizzato, cosa che permette al compilatore di verificare che si stia utilizzando il valore giusto nel contesto giusto.

Infatti proprio oggi stavo approfondendo questa cosa :wink: In effetti, anzi, a volte è da preferire l’uso di un const a un define, il secondo porta a più bag almeno secondo alcuni post che ho letto qui

Io penso che per definire un pin, usare define è più pulito, ma per altre cose, ad esempio un intervallo di tempo, uno scarto, oppure altre costanti tipo un indirizzo eeprom, secondo me meglio const

Usare const o define non comporta nessun spreco di ram/flash, questo perché ci pensa il preprocessore C ad inserire nel codice macchina il reale valore numerico specificato invece di usare una variabile. La reale differenza tra cons e define è sia nella leggibilità del codice che nell'eventuale autocasting nei calcoli numerici. Esempio pratico:

define a 100;

const int b 100;

Tutte e due le righe dicono al compilatore di sostituire il valore 100 ogni volta che incontra gli alias a e b, però se tali valori vengono usati in un calcolo c'è il rischio di ottenere un risultato inatteso se non viene specificato il casting all'interno del calcolo. Partendo da a e b sopra specificati e definiamo c come int vediamo cosa succede con questo calcolo:

int c;

c = a*10; c = b*10;

Nel caso di a * 10 per il compilatore a è un char, prende il tipo dalla dimensioni del numero, e il prodotto di 100x10 non fa 1000, fa 11101000b (-24 ) perché è un prodotto tra char produce sempre uno char anche se la destinazione è un int. Nel caso di b * 10 per il compilatore b è un int è il risultato è 1000 come atteso, nel prodotto tra due tipi diversi di variabile il risultato eredita il tipo da quello maggiore presente nel calcolo. Per farla breve, se il valore da specificare non farà mai parte di calcoli, o è già di grandezza sufficiente per il tipo dato relativo, p.e. >255 per un int, non fa nessuna differenza usare const o define, in caso contrario tocca stare attenti a quello che si fa e nel dubbio inserire il casting forzato nei calcoli. Va da se che per specificare i pin di Arduino va benissimo la define, per specificare costanti numeriche per i calcoli è meglio usare const, differenziazione oltre a prevenire errori migliora la leggibilità del codice.

Grazie per la spiegazione Astro ;) In effetti come dicevo, per definire dei pin o cose che non serviranno mai per calcoli come dici, meglio usare un define, per il resto le constanti è meglio definirle con const

Secondo te (Astro o Sukko) su queste due parti di codice

#define LETTURE 30
#define SCARTO  60

#define MAX(A,B) ((A>B)?A:B)
#define MIN(A,B) ((A<B)?A:B)

static uint32_t current = 0;

uint8_t errata(uint32_t pressione, uint32_t attesa)
{
  uint32_t absolute = MAX(pressione, attesa) - MIN(pressione, attesa);
  return absolute > SCARTO ? 1 : 0;
}

uint32_t GETPressure()
{
  uint32_t pressioni[LETTURE];
  uint32_t totale;
  uint32_t media;
  uint8_t k;
  uint8_t i;

  for (i = 0, totale = 0; i < LETTURE; ++i)
  {
    pressioni[i] = barometer.GetPressure();
    totale += pressioni[i];
  }

  media = totale / LETTURE;

  for (i = 0, k = 0; i < LETTURE; ++i)
  {
    if ( errata(pressioni[i], media) )
    {
      totale -= pressioni[i];
      ++k;
    }
  }

  return totale / (LETTURE - k);
}

E

#define LETTURE1 3
#define SCARTO1  30

#define MAX(A,B) ((A>B)?A:B)
#define MIN(A,B) ((A<B)?A:B)

static float current1 = 0;

uint8_t errata1(uint32_t vento, uint32_t attesa)
{
  uint32_t absolute1 = MAX(vento, attesa) - MIN(vento, attesa);
  return absolute1 > SCARTO1 ? 1 : 0;
}

float GETWind()
{
  float vento[LETTURE1];
  float totale;
  float media;
  uint8_t k;
  uint8_t i;

  for (i = 0, totale = 0; i < LETTURE1; ++i)
  {
    gooddata = 1;
    collectData();
    if (gooddata == 1) {
      //rawdump();
      //outputResults();
      TheData();
    }
    if (KMH >= 178)
    {
      KMH = 5;
    }
    vento[i] = KMH;
    totale += vento[i];
  }

  media = totale / LETTURE1;

  for (i = 0, k = 0; i < LETTURE1; ++i)
  {
    if ( errata(vento[i], media) )
    {
      totale -= vento[i];
      ++k;
    }
  }

  return totale / (LETTURE1 - k);
}

Quei define di scarto e lettura, sarebbe meglio definirli con un const o è indifferente?

Nei due casi che hai postato è ininfluente un modo o l'altro, nel primo caso le define non fanno parte di calcoli che possono generare valori maggiore del loro tipo, nel secondo totale è definito come float pertanto tutti i calcoli che coinvolgono questa variabile e le costanti/define saranno comunque considerati come float salvo casting forzato nel calcolo stesso. Personalmente uso esclusivamente la #define, anche perché non tutti compilatori trattano allo stesso modo le const, in alcuni viene occupata lo stesso la ram, e uso il casting forzato, se necessario, nei calcoli, però è una mia scelta e un mio stile di scrivere codice. Rammento che gli errori dovuti al mancato casting sono un classico della programmazione C, p.e. se fate dei calcoli utilizzando due registri della CPU, nel caso degli AVR sono quasi tutti a 8 bit, e il risultato è maggiore di 255 se non forzate il casting vi perdete i bit oltre gli otto indipendentemente dal fatto che la variabile di destinazione è a 16 bit.

Diciamo che le differenze sono sottili e difficili da capire se non hai bene in mente l'intera catena di compilazione e molti dettagli del linguaggio C. In generale su un compilatore moderno e furbo come GCC in casi semplici come questi non c'è motivo di non usare il const. Come dicevo qualche post fa, e come ribadito da astro, questo obbliga a definire il tipo della variabile, il che permette al compilatore di verificare che non si facciano vakkate :D, utile soprattutto quando si ha poca esperienza.

Per cui in sostanza ti direi di dimenticare il #define. Allo stesso modo, invece delle macro (MIN() e MAX()) sarebbe bene utilizzare delle funzioni inline. Con queste due accortezze dovresti risparmiarti qualche malditesta ;).

Grazie mille delle spiegazioni: A tutti e due +1 +1 = 2 :D

Infatti se usi const e poi magari per sbaglio vuoi modificare quella costante il compilatore si rifiuta e questo aiuta non poco quando ancora si deve fare esperienza ;) Riguardo alle funzioni inline stavo guardando qualcosa ultimamente QUI e QUI

Ho capito più o meno perché utilizzarle, ma non mi è chiaro sempre come sostituirle a dei define

As_Needed: Grazie mille delle spiegazioni: A tutti e due +1 +1 = 2 :D

1+1 = 10 :grin:

astrobeed: 1+1 = 10 :grin:

Già :grin:

Eh, no ... se metti insieme due "1", ottieni "11" ... :P :D

Dato che 1=0 si può anche dire che 1+1 = 0.

Dimostrazione di 1 = 0:

Poniamo a = 1 e b = 0 Scriviamo a = a+b (cioè 1=1+0) Moltiplichiamo entrambi i membri per (a-b) : a^2 - ab = a^2 - b^2 Semplifichiamo sottraendo a² : - ab = -b^2 Semplifichiamo ulteriormente dividendo per -b otteniamo a = b ovvero 1 = 0.

:D :grin: :D

mi ricorda un po quella vecchia battuta dei tre amici in osteria … alla fine della serata il conto e’ di 30 Euro, quindi ognuno di loro da 10 Euro al cameriere, che li porta all’oste … pero’ l’oste e’ loro amico, quindi decide di fargli uno sconto, e rida’ al cameriere 5 Euro da riportargli … siccome loro non possono dividere 5 in 3 parti, tengono un’euro ciascuno, e ne lasciano due di mancia al cameriere … ora, ognuno di loro ha pagato 9 Euro, e due sono andati al cameriere … 3 x 9 = 27, piu i 2 del cameriere che fa 29 … chi si e’ fregato l’Euro che manca per arrivare agli originali 30 Euro che avevano tirato fuori all’inizio ?

:stuck_out_tongue: :smiley: