Tentativo di strumentazione digitale scooter

Dopo un pò di mesi di studio ed esperimenti, mi pareva di vedere la luce in fondo al tunnel, invece no, buio pesto. Ho scritto con immensa fatica questo codice con l'intento di ampliare le funzionalità del quadro strumenti del mio scooter stampando su un display 8x2, 4 informazioni aggiuntive:

  • numero giri;
  • temperatura esterma;
  • voltmetro batteria;
  • data e ora.

Ho pensato di far ciclare le 4 funzioni tramite un codice che identifica le variazioni di stato di un pulsante. Apparentemente funziona tutto bene, le funzioni, a parte "data e ora" (non ho ancora il modulo RTC), restituiscono i valori desiderati quando interrogati, ma solo una volta. Cioè il display o meglio il programma non aggiorna le variabili. Perchè?

Il codice:

void loop() 

{
  
  StatoPulsante = digitalRead(BUTTON);                        // legge il valore del BUTTON e lo conserva  
  delay(15);                                                  // Aspetto 15ms per far alzare il dito
  if (StatoPulsante != StatoPulsantePrecedente) {             // compara lo stato del pulsante attuale con il precedente  
  if (StatoPulsante == HIGH) {                                // se lo stato è cambiato incrementa il contatore  
                                                              // se lo stato corrente è alto, il pulsante è passato da off a on  
      ContatorePulsantePremuto++;  
      if (ContatorePulsantePremuto == 1) {                    // controlla se il pulsante è stato premuto una volta 
        durationhigh = pulseIn(pin, HIGH);                    // imposto funzione lettura numero giri
        durationlow = pulseIn(pin, LOW);
        durationgiro=durationhigh+durationlow;
      if (durationhigh>0 && durationlow>0){
        durationgiro=durationhigh+durationlow;
        giri = 60000000/durationgiro;
        Serial.println(giri);                                 // stampa sulla console "giri" 
          
          lcd.clear();
          lcd.setCursor(2, 0);
          lcd.print("Giri");
          lcd.setCursor(2, 1);
          lcd.print(giri);
        } 
} 
      
      if (ContatorePulsantePremuto == 2) {                   // controlla se il pulsante è stato premuto due volte  
        sensors.requestTemperatures();                       // Invia il comando di lettura delle temperatura
        Serial.print("Temperatura di: ");                    // stampa a video la temperatura  
        Serial.print(sensors.getTempCByIndex(0));
        Serial.println(" C"); 
        
        lcd.clear();
        lcd.setCursor(2, 0);
        lcd.print("Temp");
        lcd.setCursor(0, 1);
        lcd.print(sensors.getTempCByIndex(0));
        lcd.setCursor(6, 1);
        lcd.print("\337C""C");
           
}   
      
      if (ContatorePulsantePremuto == 3) {                   // controlla se il pulsante è stato premuto tre volte  
      lettura = analogRead(analogPin);    
      voltage=(13.3/1024)*lettura;                          // 13.75 sono i volts teorici della batteria a pieno carico
      if (voltage<=12.20){                                   // ed in base al valore della batteria stampa due messaggi diversi
        Serial.print(voltage);
        Serial.println(" batteria scarica");

      lcd.clear();
      lcd.setCursor(2, 0);
      lcd.print("Batt");
      lcd.setCursor(0, 1);
      lcd.print(voltage);
      lcd.setCursor(5, 1);
      lcd.print("V !");
        } 
      else {
      Serial.println(voltage);  
      
      lcd.clear();
      lcd.setCursor(2, 0);
      lcd.print("Batt");
      lcd.setCursor(2, 1);
      lcd.print(voltage);
      lcd.setCursor(6, 1);
      lcd.print("V");
      }                           
}
                         

       if (ContatorePulsantePremuto == 4) {                   // controlla se il pulsante è stato premuto quattro volte  
        Serial.println("data e ora");                              // stampa sulla console "data e ora" 
        
      }  
    }   
  }  
 
  // salva lo stato corrente nella variabile che indica lo stato precedente per il loop successivo   
  
   StatoPulsantePrecedente = StatoPulsante;   

   // si riavvia il ciclo  
  
  if (ContatorePulsantePremuto > 4) {  
    
    // inizializzazione delle variabili  

    ContatorePulsantePremuto = 0;  
    StatoPulsante = 0;  
    StatoPulsantePrecedente = 0;  
    
  }  
}

la butto lì, senza garanzie perché non ho provato il codice...
nell'ultimo if (ContatorePulsantePremuto > 4) metterei >= e toglierei l'azzeramento delle variabili StatoPulsante

fratt:
la butto lì, senza garanzie perché non ho provato il codice...
nell'ultimo if (ContatorePulsantePremuto > 4) metterei >= e toglierei l'azzeramento delle variabili StatoPulsante

Ho fatto come mi hai indicato:

if (ContatorePulsantePremuto >= 4) {  
    
    // inizializzazione delle variabili  

    ContatorePulsantePremuto = 0;  
     StatoPulsantePrecedente = 0;  
    
  }

ma niente, la variabile di turno viene stampata una volta sola e poi si aggiorna solo quando ci ripasso sopra al giro successivo.

Grazie

ah no, avevo capito male...
prova così

// Sketch display con 4 funzioni da implementare nel quadro strumenti del mio scooter

// includo librerie sketch

#include <LiquidCrystal.h>
#include <OneWire.h>
#include <DallasTemperature.h>

// definisco hardware

LiquidCrystal lcd(12, 11, 5, 4, 3, 2);
#define BUTTON 13                                 // pin di input a cui è collegato il pulsante 
#define ONE_WIRE_BUS 10                              // pin di input a cui è collegato il sensore temperatura
OneWire oneWire(ONE_WIRE_BUS);
DallasTemperature sensors(&oneWire);                // Passaggio oneWire reference alla Dallas Temperature.

// variabili menù lcd

int ContatorePulsantePremuto = 0;                   // conta il numero di volte che il pulsante è premuto buttonPushCounter   
int StatoPulsante = 0;                              // imposta stato corrente del pulsante 
int StatoPulsantePrecedente = 0;                    // stato precedente del pulsante 

// variabili voltmetro

int analogPin = A0;                                 // imposto il pin A0 come ingresso analogico per misura batteria
int lettura = 0;                                   
float voltage;
                                   
// variabili lettura numero giri motore

int pin = 8;                                        // imposto il pin D8 come ingresso  per lettura numero giri motore
unsigned long durationhigh;
unsigned long durationlow;
unsigned long durationgiro;
unsigned long giri;


void setup()

{ 
  lcd.begin(8, 2);                                  //impostiamo il numero di colonne ed il numero di righe di lcd
  pinMode(BUTTON, INPUT);                           // imposta input
  pinMode(pin, INPUT);
  sensors.begin();                                  // avvia libreria DS18B20
  Serial.begin(9600);                               // apre la porta seriale e la inizializza a 9600 bps 
  Serial.println("Messaggio");                 // messaggio inizializzazione
  lcd.clear();
  lcd.setCursor(1, 0);
  lcd.print("Avvio");
  lcd.setCursor(0, 1);
  lcd.print("Display");           
} 
 
void loop()

{
 
  StatoPulsante = digitalRead(BUTTON);                        // legge il valore del BUTTON e lo conserva 
  delay(15);                                                  // Aspetto 15ms per far alzare il dito
  if (StatoPulsante != StatoPulsantePrecedente) {             // compara lo stato del pulsante attuale con il precedente 
    if (StatoPulsante == HIGH) {                                // se lo stato è cambiato incrementa il contatore 
                                                              // se lo stato corrente è alto, il pulsante è passato da off a on 
      ContatorePulsantePremuto++; 
    }

    // salva lo stato corrente nella variabile che indica lo stato precedente per il loop successivo   
    StatoPulsantePrecedente = StatoPulsante;   
  }

      if (ContatorePulsantePremuto == 1) {                    // controlla se il pulsante è stato premuto una volta
        durationhigh = pulseIn(pin, HIGH);                    // imposto funzione lettura numero giri
        durationlow = pulseIn(pin, LOW);
        durationgiro=durationhigh+durationlow;
      if (durationhigh>0 && durationlow>0){
        durationgiro=durationhigh+durationlow;
        giri = 60000000/durationgiro;
        Serial.println(giri);                                 // stampa sulla console "giri"
         
          lcd.clear();
          lcd.setCursor(2, 0);
          lcd.print("Giri");
          lcd.setCursor(2, 1);
          lcd.print(giri);
        }
      }
     
      if (ContatorePulsantePremuto == 2) {                   // controlla se il pulsante è stato premuto due volte 
        sensors.requestTemperatures();                       // Invia il comando di lettura delle temperatura
        Serial.print("Temperatura di: ");                    // stampa a video la temperatura 
        Serial.print(sensors.getTempCByIndex(0));
        Serial.println(" C");
       
        lcd.clear();
        lcd.setCursor(2, 0);
        lcd.print("Temp");
        lcd.setCursor(0, 1);
        lcd.print(sensors.getTempCByIndex(0));
        lcd.setCursor(6, 1);
        lcd.print("\337C""C");
           
      }   
     
      if (ContatorePulsantePremuto == 3) {                   // controlla se il pulsante è stato premuto tre volte 
      lettura = analogRead(analogPin);   
      voltage=(13.3/1024)*lettura;                          // 13.75 sono i volts teorici della batteria a pieno carico
      if (voltage<=12.20){                                   // ed in base al valore della batteria stampa due messaggi diversi
        Serial.print(voltage);
        Serial.println(" batteria scarica");

      lcd.clear();
      lcd.setCursor(2, 0);
      lcd.print("Batt");
      lcd.setCursor(0, 1);
      lcd.print(voltage);
      lcd.setCursor(5, 1);
      lcd.print("V !");
        }
      else {
      Serial.println(voltage); 
     
      lcd.clear();
      lcd.setCursor(2, 0);
      lcd.print("Batt");
      lcd.setCursor(2, 1);
      lcd.print(voltage);
      lcd.setCursor(6, 1);
      lcd.print("V");
      }                           
      }
                         

       if (ContatorePulsantePremuto == 4) {                   // controlla se il pulsante è stato premuto quattro volte 
        Serial.println("data e ora");                              // stampa sulla console "data e ora"
       
      } 
 

   // si riavvia il ciclo 
 
  if (ContatorePulsantePremuto > 4) { 
   
    // inizializzazione delle variabili 

    ContatorePulsantePremuto = 0; 
    //StatoPulsante = 0; 
    //StatoPulsantePrecedente = 0; 
   
  } 
}

reindenta il tutto per renderlo più comprensibile

dimenticavo, nei 4 if conviene mettere anche gli else oppure usare lo switch-case, così eviti dei confronti inutili ad ogni ciclo.

Nulla il display non fa il refresh della variabile di turno. In questo modo non vedrò mai il contagiri contare in real time.

Credo che il problema sia che tutti i vari

if (ContatorePulsantePremuto == X)

li hai messi dentro all'if che verifica se hai appena premuto un pulsante, per cui girano solo immediatamente dopo la pressione.

Spostali quindi oltre:

void loop()
{
  StatoPulsante = digitalRead(BUTTON);                        // legge il valore del BUTTON e lo conserva
  delay(15);                                                  // Aspetto 15ms per far alzare il dito
  if (StatoPulsante != StatoPulsantePrecedente) {             // compara lo stato del pulsante attuale con il precedente
    if (StatoPulsante == HIGH) {                                // se lo stato è cambiato incrementa il contatore
      // se lo stato corrente è alto, il pulsante è passato da off a on
      ContatorePulsantePremuto++;

      if (ContatorePulsantePremuto > 4) {

        // inizializzazione delle variabili

        ContatorePulsantePremuto = 0;
        StatoPulsante = 0;
        StatoPulsantePrecedente = 0;

      }


      // salva lo stato corrente nella variabile che indica lo stato precedente per il loop successivo

      StatoPulsantePrecedente = StatoPulsante;
    }
  }

  if (ContatorePulsantePremuto == 1) {                    // controlla se il pulsante è stato premuto una volta
    durationhigh = pulseIn(pin, HIGH);                    // imposto funzione lettura numero giri
    durationlow = pulseIn(pin, LOW);
    durationgiro = durationhigh + durationlow;
    if (durationhigh > 0 && durationlow > 0) {
      durationgiro = durationhigh + durationlow;
      giri = 60000000 / durationgiro;
      Serial.println(giri);                                 // stampa sulla console "giri"

      lcd.clear();
      lcd.setCursor(2, 0);
      lcd.print("Giri");
      lcd.setCursor(2, 1);
      lcd.print(giri);
    }
  }

  if (ContatorePulsantePremuto == 2) {                   // controlla se il pulsante è stato premuto due volte
    sensors.requestTemperatures();                       // Invia il comando di lettura delle temperatura
    Serial.print("Temperatura di: ");                    // stampa a video la temperatura
    Serial.print(sensors.getTempCByIndex(0));
    Serial.println(" C");

    lcd.clear();
    lcd.setCursor(2, 0);
    lcd.print("Temp");
    lcd.setCursor(0, 1);
    lcd.print(sensors.getTempCByIndex(0));
    lcd.setCursor(6, 1);
    lcd.print("\337C""C");

  }

  if (ContatorePulsantePremuto == 3) {                   // controlla se il pulsante è stato premuto tre volte
    lettura = analogRead(analogPin);
    voltage = (13.3 / 1024) * lettura;                    // 13.75 sono i volts teorici della batteria a pieno carico
    if (voltage <= 12.20) {                                // ed in base al valore della batteria stampa due messaggi diversi
      Serial.print(voltage);
      Serial.println(" batteria scarica");

      lcd.clear();
      lcd.setCursor(2, 0);
      lcd.print("Batt");
      lcd.setCursor(0, 1);
      lcd.print(voltage);
      lcd.setCursor(5, 1);
      lcd.print("V !");
    }
    else {
      Serial.println(voltage);

      lcd.clear();
      lcd.setCursor(2, 0);
      lcd.print("Batt");
      lcd.setCursor(2, 1);
      lcd.print(voltage);
      lcd.setCursor(6, 1);
      lcd.print("V");


      if (ContatorePulsantePremuto == 4) {                   // controlla se il pulsante è stato premuto quattro volte
        Serial.println("data e ora");                              // stampa sulla console "data e ora"

      }
    }
  }
}

E usa la formattazione automatica, altrimenti non si capisce un tubo!

Ho capito ciò che intendi. Tu hai già corretto il codice? L'ho provato, ma premendo sul pulsante adesso non mi parte nessuna funzione.

Prova a spostare questa graffa:

      }
      StatoPulsantePrecedente = StatoPulsante;
  }

Ok switcha, ma Arduino adesso risponde con lentezza e bisogna pigiare più volte per far commutare le varie funzioni. Inoltre il display adesso sfarfalla per via del refresh.
Sto capendo forse che è tutto il mio codice a far schifo.

Per eliminare il flickering puoi togliere un po' di lcd.clear(), cercando di farle solo se strettamente necessario. Ad esempio potresti metterne una sola dopo ContatorePulsantePremuto++;

Per il resto ovviamente dipende dal codice. Certe cose, tipo pulseIn() richiedono tempo, per cui mentre vengono eseguite Arduino non può accorgersi che hai premuto il tasto. Dovresti ripensare un po' lo sketch, usare magari i pin di interrupt ma è una cosa che stravolge parecchio il tuo sketch attuale. Vediamo se qualcuno ha magari qualche altra proposta.

La logica di Sukko è la stessa che volevo implementario io... però lui è stato più bravo di me a correggere il codice...

Per il refresh potresti provare una di queste soluzioni:

  • mettere un delay più lungo, così hai meno refresh
  • fare il refresh solo se il valore da visualizzare è diverso da quello precedente
  • refreshare solo la riga dove c'è il valore e non l'intero schermo

Grazie ragazzi,

in effetti fratt potrei anche non refreshare ne la temperatura ne la batteria, mentre per x i giri e la data e ora è essenziale il refresh. Ma come procedo?

per fare un lavoro fatto bene dovresti fare il refresh solo se il valore da visualizzare è diverso da quello precedente. così eviti un sacco di refresh inutili.
qualcosa del tipo:
se lettura-attuale != lettura-precedente allora riscrivo il valore

inoltre puoi riscrivere solo la seconda riga, visto che la prima non varia.

Capisco....ma sempre all'indell'ifbutton dello stato button?
Grazie

più o meno così...
prevedi all'inizio qualche variabile in più

unsigned long giri_prec = 0;
unsigned long giri = 0;

poi per ogni if..

  if (ContatorePulsantePremuto == 1) {                    // controlla se il pulsante è stato premuto una volta
    durationhigh = pulseIn(pin, HIGH);                    // imposto funzione lettura numero giri
    durationlow = pulseIn(pin, LOW);
    durationgiro = durationhigh + durationlow;
    if (durationhigh > 0 && durationlow > 0) {
      durationgiro = durationhigh + durationlow;
      giri = 60000000 / durationgiro;
      Serial.println(giri);                                 // stampa sulla console "giri"

      if (abs(giri - giri_prec) > 100) {
        lcd.clear();
        lcd.setCursor(2, 0);
        lcd.print("Giri");
        lcd.setCursor(2, 1);
        lcd.print(giri);
        giri_prec = giri;
      }
    }
  }

invece del semplice confronto conviene prevedere uno scarto al di sotto del quale non avviene il refresh, altrimenti rischi che i refresh siano comunque troppi.
io per i giri ho messo 100, poi vedi tu.
per l'ora puoi considerare uno scarto di 1 minuto, per la temeratura mezzo grado o un grado, ecc...

Grazie, mi hai dato degli ottimi spunti su cui lavorare x i prossimi mesi :slight_smile: ...
Speriamo bene.
Grazie mille

Nicola

tienici informati!

Stamattina mi sono svegliato con un chiodo fisso :slight_smile: . Ho variato il codice. In primis ho usato lo switch case che mi sembra snellisca il comportamento di Arduino. Poi ho cominciato ad inserire gli if per non far refreshare il display. Per ora ho inserito gli if in "giri" e "voltage", ma non mi pare cambi qualcosa:

void loop() {
  StatoPulsante = digitalRead(BUTTON);                        // legge il valore del BUTTON e lo conserva
  delay(15);                                                  // Aspetto 15ms per far alzare il dito
  if (StatoPulsante != StatoPulsantePrecedente) {             // compara lo stato del pulsante attuale con il precedente
    if (StatoPulsante == HIGH) {                                // se lo stato è cambiato incrementa il contatore

      ContatorePulsantePremuto++;                              // se lo stato corrente è alto, il pulsante è passato da off a on

      if (ContatorePulsantePremuto > 4) {

        // inizializzazione delle variabili

        ContatorePulsantePremuto = 0;
        StatoPulsante = 0;
        StatoPulsantePrecedente = 0;

      }
    }
    StatoPulsantePrecedente = StatoPulsante;                  // salva lo stato corrente nella variabile che indica lo stato precedente per il loop successivo
  }

  switch (ContatorePulsantePremuto) {
    case 1:  // controlla se il pulsante è stato premuto una volta

      durationhigh = pulseIn(pin, HIGH);                    // imposto funzione lettura numero giri
      durationlow = pulseIn(pin, LOW);
      durationgiro = durationhigh + durationlow;
      if (durationhigh > 0 && durationlow > 0) {
        durationgiro = durationhigh + durationlow;
        giri = 60000000 / durationgiro;
        Serial.println(giri);                                 // stampa sulla console "giri"
        lcd.clear();
        lcd.setCursor(2, 0);
        lcd.print("Giri");
        lcd.setCursor(2, 1);
        lcd.print(giri);
        if (abs(giri - giri_prec) > 500) {
          lcd.setCursor(2, 1);
          lcd.print(giri);
          giri_prec = giri;

        }
      }

      break;

    case 2:  // controlla se il pulsante è stato premuto due volte
      sensors.requestTemperatures();                       // Invia il comando di lettura delle temperatura
      Serial.print("Temperatura di: ");                    // stampa a video la temperatura
      Serial.print(sensors.getTempCByIndex(0));
      Serial.println(" C");

      lcd.clear();
      lcd.setCursor(2, 0);
      lcd.print("Temp");
      lcd.setCursor(0, 1);
      lcd.print(sensors.getTempCByIndex(0));
      lcd.setCursor(6, 1);
      lcd.print("\337C""C");
      break;

    case 3:  // controlla se il pulsante è stato premuto tre volte
      lettura = analogRead(analogPin);
      voltage = (13.3 / 1024) * lettura;                    // 13.75 sono i volts teorici della batteria a pieno carico
      Serial.print(voltage);
      Serial.println("V");

      lcd.clear();
      lcd.setCursor(2, 0);
      lcd.print("Batt");
      lcd.setCursor(0, 1);
      lcd.print(voltage);
      lcd.setCursor(5, 1);
      lcd.print("V");
      if (abs(voltage - voltage_prec) > 1) {
        lcd.setCursor(0, 1);
        lcd.print(voltage);
        lcd.setCursor(5, 1);
        lcd.print("V");
      }

      break;

    case 4:  // controlla se il pulsante è stato premuto quattro volte
      Serial.println("data e ora");                              // stampa sulla console "data e ora"
      break;

  }
}

Ho due domande:

  1. if (abs(giri - giri_prec) > 500) legge il valore assoluto dell'operazione, ma è in grado di operare sia se giri diminuisce rispetto a giri_prec?

  2. volendo applicare la stessa logica alla funzione temperatura, come faccio dato che la variabile sensors.getTempCByIndex(0) non è stata impostata da me?

Grazie

  1. in teoria sì... il valore assoluto di (2000 - 100) e (100 - 2000) è sempre 1900...
    a meno che l'aver dichiarato unsigned le 2 variabili non crei qualche problema... sono un po' arrugginito su questi aspetti di programmazione in c...
    riesci a stampare su monitor seriale così vedi che succede in tempo reale?

  2. puoi assegnare il valore sensors.getTempCByIndex(0) ad una variabile invece di stamparlo

temperatura = sensors.getTempCByIndex(0);
if (abs(temperatura - temperatura_precedente) > 1) {
  // ecc ecc
}