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.
// 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;
}
}
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!
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.
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.
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...
Stamattina mi sono svegliato con un chiodo fisso . 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:
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?
volendo applicare la stessa logica alla funzione temperatura, come faccio dato che la variabile sensors.getTempCByIndex(0) non è stata impostata da me?
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?
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
}