LCD 16x2 e pulsante: problema nel cambio dei dati - sensori TMP36

Salve a tutti, sto cercando di realizzare un piccolo progetto con un display LCD 16x2 e un pulsante per controllare la visualizzazione dei dati di tre sensori di temperatura (TMP36). Penso di aver commesso degli errori concettuali nel codice.
L'idea è la seguente:

  • Se il pulsante non viene premuto, ogni 3 secondi il display cambia automaticamente i dati mostrati, passando al sensore successivo (sensore 1 → sensore 2 → sensore 3 → sensore 1, e così via).

  • Se il pulsante viene premuto, il display deve immediatamente passare ai dati del sensore successivo, senza aspettare il cambio automatico, e poi continuare il ciclo normalmente.

Problema: il pulsante non ha alcun effetto.
Se non lo premo, il display cambia i dati ogni 3 secondi come previsto. Se lo premo, invece, non succede nulla.
Ecco il codice che ho scritto:

#include <LiquidCrystal.h>
LiquidCrystal lcd(12, 11, 5, 4, 3, 2);
int TMPPin1 = A0;
int TMPPin2 = A1;
int TMPPin3 = A2;
int pinP = 6;
int statoP = 0;
int statoS = 0;

void setup(){
  pinMode(pinP, INPUT);
  lcd.begin(16, 2);
  Serial.begin(9600);
}

void loop(){
  statoP = digitalRead(pinP);
  int valTMP1 = analogRead(TMPPin1);
  int valTMP2 = analogRead(TMPPin2);
  int valTMP3 = analogRead(TMPPin3);
  
  float valTemp1 = (((valTMP1*5.0)/1023.0)-0.5)*100;
  float valTemp2 = (((valTMP2*5.0)/1023.0)-0.5)*100;
  float valTemp3 = (((valTMP3*5.0)/1023.0)-0.5)*100;
  
  if (statoP == HIGH) {
   statoS = (statoS + 1) % 3;
  }
  else {
 	 Serial.print("Temperatura: ");
 	 Serial.print(valTemp1);
   	Serial.println(" C");
 	 lcd.setCursor(0, 0);
  		lcd.print("Temperatura: ");
	 lcd.setCursor(0, 1);
  		lcd.print(valTemp1);
  		lcd.print(" C");
  	delay(3000);
  
    Serial.print("Temperatura: ");
    Serial.print(valTemp2);
      Serial.println(" C");
    lcd.setCursor(0, 0);
      	lcd.print("Temperatura: ");
    lcd.setCursor(0, 1);
      	lcd.print(valTemp2);
      	lcd.print(" C");
    delay(3000);

    Serial.print("Temperatura: ");
    Serial.print(valTemp3);
      Serial.println(" C");
    lcd.setCursor(0, 0);
      	lcd.print("Temperatura: ");
    lcd.setCursor(0, 1);
      	lcd.print(valTemp3);
      	lcd.print(" C");
    delay(3000);
  }
    if (statoS == 0) {
    Serial.print("Temperatura: ");
    Serial.print(valTemp2);
      Serial.println(" C");
    lcd.setCursor(0, 0);
      	lcd.print("Temperatura: ");
    lcd.setCursor(0, 1);
      	lcd.print(valTemp2);
      	lcd.print(" C");
    }
    else if (statoS == 1) {
    Serial.print("Temperatura: ");
    Serial.print(valTemp3);
      Serial.println(" C");
    lcd.setCursor(0, 0);
      	lcd.print("Temperatura: ");
    lcd.setCursor(0, 1);
      	lcd.print(valTemp3);
      	lcd.print(" C");
    }
    else {
    Serial.print("Temperatura: ");
 	Serial.print(valTemp1);
      Serial.println(" C");	
 	lcd.setCursor(0, 0);
  		lcd.print("Temperatura: ");
	lcd.setCursor(0, 1);
  		lcd.print(valTemp1);
  		lcd.print(" C");
    }
}

Ho provato a cercare altri post inerenti al mio progetto, ma non ne ho trovato di così simili da potermi aiutare concretamente.
Sono abbastanza sicuro che qualcosa non torni nella logica generale del codice, ma non saprei nemmeno da dove iniziare per risolvere il problema. Il mio dubbio principale risiede nell'uso di statoS, che ritengo anche essere una delle cause principali del mancato funzionamento del pulsante (ma spero che questo siate voi a dirmelo :face_with_peeking_eye:). Qualcuno potrebbe darmi una mano?
Grazie mille in anticipo!

Allego immagine del circuito simulato su Tinkercad.

È il solito problema dell'uso di delay()

L'unica è studiare

Comincia col cercare "blink without delay"

1 Like

Grazie mille!!! :grinning_face_with_smiling_eyes:
Tutto funziona ora.

Le scritte fisse sul display puoi metterle nel setup.

Interessante sarebbe vederecosa ne è uscito, dal mio suggerimento

Certo. Ho usato la funzione millis() per il delay e sistemato un po' la struttura del codice.

#include <LiquidCrystal.h>

LiquidCrystal lcd(12, 11, 5, 4, 3, 2);
const int TMPPin1 = A0;
const int TMPPin2 = A1;
const int TMPPin3 = A2;
const int pinP = 6;
int statoP = 0;
int statoS = 0;

unsigned long previousMillis = 0;
const long interval = 3000; 

void setup() {
  pinMode(pinP, INPUT);
  lcd.begin(16, 2);
  Serial.begin(9600);
}

void loop() {
  unsigned long currentMillis = millis();
  statoP = digitalRead(pinP);
  int valTMP1 = analogRead(TMPPin1);
  int valTMP2 = analogRead(TMPPin2);
  int valTMP3 = analogRead(TMPPin3);

  float valTemp1 = (((valTMP1 * 5.0) / 1023.0) - 0.5) * 100;
  float valTemp2 = (((valTMP2 * 5.0) / 1023.0) - 0.5) * 100;
  float valTemp3 = (((valTMP3 * 5.0) / 1023.0) - 0.5) * 100;

  if (statoP == HIGH) {
    statoS = (statoS + 1) % 3; 
    delay(200); 
  	lcd.clear();
  }

  if ((currentMillis - previousMillis) >= interval) {
    previousMillis = currentMillis;
       statoS = (statoS + 1) % 3;  
  	   lcd.clear();
  }  

  if (statoS == 0) {
  	  Serial.print("Temperatura: ");
  	  Serial.print(valTemp1);
 	     Serial.println(" C");
 	   lcd.setCursor(0, 0);
   		lcd.print("Temperatura: ");
  	  lcd.setCursor(0, 1);
  	    lcd.print(valTemp1);
  	    lcd.print(" C");
 	  }
   else if (statoS == 1) {
  	  Serial.print("Temperatura: ");
  	  Serial.print(valTemp2);
  	    Serial.println(" C");
 	   lcd.setCursor(0, 0);
   		lcd.print("Temperatura: ");
 	   lcd.setCursor(0, 1);
 	     lcd.print(valTemp2);
 	     lcd.print(" C");
 	  }
  	else if (statoS == 2) {
      Serial.print("Temperatura: ");
 		Serial.print(valTemp3);
  	    Serial.println(" C");	
       lcd.setCursor(0, 0);
   		lcd.print("Temperatura: ");
	   lcd.setCursor(0, 1);
 	 	lcd.print(valTemp3);
 	 	lcd.print(" C");
 	  }
}
1 Like

Bravo

Se te la senti si può affinare ancora e molto

Dipende dal tuo obiettivo
Se è solo ottenere il risultato minimo
O realizzare un programma di livello superiore (imparando nel frattempo)

Assolutamente si! Cosa mi consiglieresti di studiare per migliorare ulteriormente il codice?
Ho dato un'occhiata molto veloce alla gestione del pulsante con il debounce seguendo il thread di 'blink without delay', potrebbe essere un punto di partenza?

Intanto potersi eliminare proprio il debounce software a favore di un semplicissimo debounce hardware ... basta un condensatore ed una resistenza ... :wink:

Guglielmo

1 Like

Se usi una matrice di 3 elementi per valTemp[], puoi eliminare 2 dei 3 blocchi di visualizzazione.
Le scritte fisse, poi, mettile nel setup.
Prima di ogni analogRead() fanne uno senza leggere il valore e attendi 2ms per evitare interferenze fra i 3 canali.

2 Likes

Esatto
Usa un vettore e ciclalo con un for
Non usare variabili int per conservare nomi di piedini
Oltretutto inizializzati da macro

A0 è una macro che rappresenta un numero
E comunque 0 va bene lo stesso, provare per credere

1 Like

Ci tengo intanto a ringraziarvi per i suggerimenti e i consigli dati.
Questo è il codice modificato:

#include <LiquidCrystal.h>

LiquidCrystal lcd(12, 11, 5, 4, 3, 2);

const uint8_t TMPPins[] = {0, 1, 2};  
const uint8_t pinP = 6; 
uint8_t statoP = 0;
uint8_t statoS = 0;

unsigned long previousMillis = 0;
const unsigned long interval = 3000;

void setup() {
  pinMode(pinP, INPUT);  
  lcd.begin(16, 2);
  Serial.begin(9600);
  lcd.setCursor(0, 0);
  lcd.print("Temperatura:");
}

void loop() {
  unsigned long currentMillis = millis();
  statoP = digitalRead(pinP);
  if (statoP == HIGH) {
    statoS = (statoS + 1) % 3;  
    lcd.clear();
    lcd.setCursor(0, 0);
    lcd.print("Temperatura:");
  }

  if (currentMillis - previousMillis >= interval) {
    previousMillis = currentMillis;
    statoS = (statoS + 1) % 3;
    lcd.clear();
    lcd.setCursor(0, 0);
    lcd.print("Temperatura:");
  }

  float valTemp[3];
  for (uint8_t i = 0; i < 3; i++) {
    analogRead(TMPPins[i]); 
    delay(2);
    int valTMP = analogRead(TMPPins[i]);
    valTemp[i] = (((valTMP * 5.0) / 1023.0) - 0.5) * 100;
  }
  
  Serial.print("Temperatura: ");
  Serial.print(valTemp[statoS]);
  Serial.println(" C");

  lcd.setCursor(0, 1);
  lcd.print(valTemp[statoS]);
  lcd.print(" C");
}

Al posto di int ho usato uint8_t, dato che le variabili sono sempre positive e occupano pochi valori (tranne valTMP), ho usato un vettore per TMPPins e poi per valTemp, la scritta fissa "Temperatura:" l'ho messa nel setup, la lettura dei sensori con un ciclo for, e ho aggiunto una lettura di scarto di TMPPins con un delay di 2ms. Ho anche spostato la visualizzazione dei dati alla fine.
Penso di aver realizzato correttamente il debounce hardware (il pulsante funziona ancora... penso sia un buon segno :eyes:) ma non ne sono così sicuro, quindi allego un'immagine del circuito.

Bravo, di nuovo

Altre ispirazioni:

Se metti la lettura direttamente nel calcolo della scala sui 5v...

E se metti quel calcolo direttamente nella stampa...

non ti serve più l'array di misure

Se non hai l'array non ti serve un indice di array

Se poi ci pensi un attimo ti accorgi che

Il contenuto dell'array di piedini coincide con il suo indice: ( i[1] è uguale a 1)

Quindi non ti serve nemmeno l'array di piedini...

Ma solo il suo indice, usato direttamente

E in realtà nemmeno lui

Perchè coincide per "coincidenza casuale" con lo stato
provare per credere...

Come vedi abbiamo sfrondato il programma,
non con un bisturi: proprio con la motosega

Adesso se lo confronti con l'originale è l'ombra di se stesso

Anche se guardi la dimensione del compilato e l'occupazione di memoria...

Anzi, fammi sapere, chè sono curioso...

1 Like

Il condensatore antirimbalzo deve stare in parallelo al pulsante, non in serie!

1 Like

Grazie ancora e scusate per l'update un po' in ritardo, la settimana è sempre piena...
Ecco il nuovo codice:

#include <LiquidCrystal.h>

LiquidCrystal lcd(12, 11, 5, 4, 3, 2);

const uint8_t pinP = 6;
uint8_t statoS = 0;

unsigned long previousMillis = 0;
const unsigned long interval = 3000;

void setup() {
  pinMode(pinP, INPUT);
  lcd.begin(16, 2);
  Serial.begin(9600);
  lcd.setCursor(0, 0);
  lcd.print("Temperatura:");
}

void loop() {
  unsigned long currentMillis = millis();

  if (digitalRead(pinP) == HIGH) {
    statoS = (statoS + 1) % 3;
    lcd.clear();
    lcd.setCursor(0, 0);
    lcd.print("Temperatura:");
  }

  if (currentMillis - previousMillis >= interval) {
    previousMillis = currentMillis;
    statoS = (statoS + 1) % 3;
    lcd.clear();
    lcd.setCursor(0, 0);
    lcd.print("Temperatura:");
  }

  int valTMP = analogRead(statoS);
  float valTemp = (((valTMP * 5.0) / 1023.0) - 0.5) * 100;

  Serial.print("Temperatura: ");
  Serial.print(valTemp);
  Serial.println(" C");

  lcd.setCursor(0, 1);
  lcd.print(valTemp);
  lcd.print(" C");
}

Per quanto riguarda la differenza di occupazione di memoria tra il nuovo codice e il primo, sarei curioso anch'io... basta sommare l'occupazione di memoria di int, uint8_t, float e unsigned long? Se sì, il codice iniziale occupava 30 byte, mentre quello sopra 20 (sempre se ho contato bene :grimacing:).
Per quanto riguarda il condensatore, allego sempre foto :melting_face:

Di nuovo:
Bravo

E buona domenica

1 Like

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.