Problema a me incomprensibile di programmazione

Sto facendo una funzioncina per fare il debounce ai tasti (non potendo usare la libreria già fatta, perchè i tasti sono collegati al PCF8574AP con relativa libreria).
Il punto è che il codice che ho fatto funziona, ma smette di funzionare nel momento in cui provo a convertire il codice in funzione.

Questo è il codice semplice:

#include <Wire.h>
#include <IOexp.h>

IOexp tastiera(0x39);
boolean tasto;
unsigned long tempo;

void setup()
{
  Wire.begin();
  Serial.begin(9600);
}

void loop()
{
  tasto = tastiera.read(P0);
  if (tasto == true && (millis() >= (tempo + 300)))
  {
    Serial.println("Tasto destro");
    tempo = millis();
  }
}

Questo è lo stesso codice convertito in modo da poter essere richiamato come funzione la dove serve (in questo caso la stessa funzione serve per 3 diversi tasti):

#include <Wire.h>
#include <IOexp.h>

#define DX 1 //Tasto sul pin 4 del PCF, corrispondente a P0
#define SX 2 //Tasto sul pin 5 del PCF, corrispondente a P1
#define Dec 4 //Tasto sul pin 6 del PCF, corrispondente a P2

IOexp tastiera(0x39);

unsigned long tempo;

void setup()
{
	Wire.begin();
	Serial.begin(9600);
}

void loop()
{
	if (LeggiTasto(SX)) //Leggo il tasto già con i controlli di debounce
	{ //Se la funzione ritorna true, faccio l'azione
		Serial.println("Tasto sinistro");
	}
	if (LeggiTasto(DX)) //Leggo il tasto già con i controlli di debounce
	{ //Se la funzione ritorna true, faccio l'azione
		Serial.println("Tasto destro");
	}
	if (LeggiTasto(Dec)) //Leggo il tasto già con i controlli di debounce
	{ //Se la funzione ritorna true, faccio l'azione
		Serial.println("Tasto decremento");
	}
}

boolean LeggiTasto(byte tasto)
{
	if (tastiera.read(tasto) && (millis() >= (tempo + 300)))
	{
		tempo = millis();
		return true;
	}
}

Non ho spiegato che problema mi da: apparentemente funziona esattamente come il codice sopra, in realtà dopo esattamente 65535 millisecondi premendo un qualunque tasto, questo risponde a raffica anzichè col debounce.
Sicuramente il fatto che il problema si manifesta esattamente dopo 65535 la dice lunga, questo mi fa pensare a problemi di conversioni di variabili o cose simili, ma dato che l'unica variabile che è implicata nel tempo è "tempo" ed è infatti dichiarata come unsigned long, non riesco a capire dove sia il problema.
Inoltre sottolineo che lo stesso codice, se utilizzato come codice e non come funzione, si comporta correttamente!! :astonished:

dopo il 300 nel while scrivi LU, in pratica lo forzi ad essere un unsigned long:

if (tasto == true && (millis() >= (tempo + 300LU)))

lesto:
dopo il 300 nel while scrivi LU, in pratica lo forzi ad essere un unsigned long:

if (tasto == true && (millis() >= (tempo + 300LU)))

Avevo già provato una cosa simile mettendo (unsigned long)300 ma non aveva funzionato. E comunque non mi spiego come la stessa riga se usata "come codice" funziona, se la metto dentro una funzione invece no.

Comunque faccio una prova e ti faccio sapere,
grazie.

ciao

ma scusa il codice ti compila? perchè la funzione non ha il return false (forse è implicito?)..

eppoi scusami, ma se leggi 3 tasti ogni tasto deve avere il tuo time, altrimenti poi succedono casini (ma mi sembra starno che accadano in modo deterministico)

Si, il codice si compila correttamente. Il resturn false direi che è implicito dal momento che normalmente funziona.
Si in effetti il tempo "in comune" non è il massimo, ma per adesso non mi sono addentrato troppo in questa problematica dato che c'è quest altro problema a monte. (comunque grazie del suggerimento)

Ho provato "if (tasto == true && (millis() >= (tempo + 300LU)))" ma si comporta esattamente allo stesso modo.

metti il return false, e prova a usare il codice solo con un tasto

ok

Cavolo sembra funzionare!!!

Qualche suggerimento per mettere la variabile "tempo" distinta per ogni tasto?
Banalmente potrei passare alla funzione la variabile tempo oltre che il tasto in questione, ma poi come faccio a far tornare indietro il risultato in 2 variabili? La mia conoscenza limitata di programmazione si ferma a una funzione dove puoi dargli in pasto quante variabili vuoi, ma ne ritorna indietro sempre una. Nel caso specifico la funzione è dichiarata come boolean e infatti ritorna true o false. Io avrei bisogno che mi restituisse anche "a che punto è" la variabile tempo relativa ad un tasto specifico per poi ridargliela in pasto al loop successivo o quando mi serve lavorare su quel tasto.

Ad ogni modo usare una variabile "tempo" globale o usarne una per ciascun tasto non è che impedisce la lettura contemporanea di più tasti (qualora per assurdo uno ne tenesse premuti 2 insieme), infatti in entrambi i casi ottengo risultati random perchè nel loop comunque leggo tutti i tasti e prima o poi viene eseguita l'azione relativa a ciascun tasto.

secondo me l'ideale sarebbe avere una struttura Tasto che contenga l'unsigned long tempo, byte pin ed eventuali altre info che ti interessano. poi crei un'array di queste strutture(una cella per ogni tasto), che nel setup() inizializzi inserendo il valore del pin e inizializzi tempo a 0 o ai millis() attuali.
poi ogni loop() hai una funzione che scorre l'array di strutture, e per ogni struttura esegua la funzione desiderata.

se non sai cos'è una struttura (lascia stare per ora la parte sulle liste se non sai cos'è un puntatore) Cprogramminglanguage.net

  1. devi usare una variabile del tempo per ogni pulsante
  2. 300 ms sono troppi, ne bastano 10 e in questo caso puoi anche fare un delay(10); leggi il tasto, asetti 10 ms e rileggi. Se é ancora HIGH é schiacciato.
  3. se la variabile la definisci globale (fuori dalla funzione come l' hai fatto nel esempio) é a disposizione in tutte le funzioni
  4. il Tuo programma ha piú errori concettuali:
unsigned long tempo;
....
boolean LeggiTasto(byte tasto)
{
	if (tastiera.read(tasto) && (millis() >= (tempo + 300)))
	{
		tempo = millis();
		return true;
	}
}

*La prima volta che chiami la funzione Leggi Tasto la cariabile tempo non ha valore. ( adesso non so che valore da se uno casuale o semicasuale).
Oltre questo solo quando la condizione é vera (pulsante schiacciato e millis() maggiore di un x piú 300) caricherai la variabile tempo con un valore.

  • da un True ogni 300ms se il tasto rimane schiacciato perché non controlli se era giá schiacciato. Qua sta a Te se vuoi eseguire l' azione solo la prima volta che schiacci il tasto o di continuo finche il tasto viene mollato; presuppongo la prima opzione.

2 soluzioni:
*1) usare un semplice delay di 10 ms

boolean LeggiTasto(byte tasto)
{
	if (tastiera.read(tasto)&& StatoTasto [tasto]==0) // lege la prima volta il tasto schiacciato
	{
        delay(10);	
        if (tastiera.read(tasto)) // lege la seconda volta il tasto schiacciato
	       {
               StatoTasto [tasto]++;
               return true;
	       }
               else 
               {
               return false;
               }
        }
       if (tastiera.read(tasto)&& StatoTasto [tasto]==1) // lege la seconda volta il tasto schiacciato
       {
        return false;
        }   
       
        if (!tastiera.read(tasto)&& StatoTasto [tasto]==1) // lege la prima volta il tasto rilasciato
	{
        delay(10);	
        if (!tastiera.read(tasto)) // lege la seconda volta il tasto rilasciato
	       {
               StatoTasto [tasto]=0;    //Dopo il tempo di debounce il tasto é ancora mollato
               return false;
               }
               else 
               {
               return false;    // Dopo il tempo di debounce il tasto é schiacciato; serve un ulteriore controllo al prossimo giro.
               }
        }
	}
}

*2) usare una variabile di stato millis().

le cose devono essere sequenziali:

  • tasto schiacciato e StatoTasto== 0 -> StatoTasto=1 e memorizza tempo return false;
  • tasto schiacciato, StatoTasto== 1 e tempo passato -> StatoTasto=2 return true;
  • tasto mollato e StatoTasto== 2 -> StatoTasto=3 e memorizza tempo return false;
  • tasto mollato, StatoTasto== 3 e tempo passato -> StatoTasto=0 return false;
    definisci le seguenti variabili all inizio: una per ogni entrata
unsigned long tempo[8];  // ritardo per ogni tastio
StatoTasto[8] ;  // stato di ogni tasto

Non ho compilato il codice percui potrebbero essere errori di battitura.

Ciao Uwe

lesto:
secondo me l'ideale sarebbe avere una struttura Tasto che contenga l'unsigned long tempo, byte pin ed eventuali altre info che ti interessano. poi crei un'array di queste strutture(una cella per ogni tasto), che nel setup() inizializzi inserendo il valore del pin e inizializzi tempo a 0 o ai millis() attuali.
poi ogni loop() hai una funzione che scorre l'array di strutture, e per ogni struttura esegua la funzione desiderata.

se non sai cos'è una struttura (lascia stare per ora la parte sulle liste se non sai cos'è un puntatore) Cprogramminglanguage.net

Interessante, mi piacerebbe approfondire questo aspetto. Ho letto il link e più o meno ho capito come potrei creare la struttura, ma non ho capito come questa cosa potrebbe risolvere il problema di dare in pasto ad una funzione quante variabili vuoi (o una struttura di esse) se poi posso ritornare indietro sempre e solo una variabile.

Mit globalen Variablen hast Du das Problem nicht.
Grüße Uwe
Con variabili globali non c'hai quel problema
Ciao Uwe

uwefed:
2) 300 ms sono troppi, ne bastano 10 e in questo caso puoi anche fare un delay(10); leggi il tasto, asetti 10 ms e rileggi. Se é ancora HIGH é schiacciato.

La funzione per come l'avevo intesa io mi fa sia da debounce che da delay per un tasto premuto. La funzione nella sua semplicità utilizza i 300ms impostati in modo che faccia da debounce, evitando doppie o più letture e nello stesso tempo mi fa da "hold" così che se uno tiene premuto un tasto non fa input a raffica ma ogni 300ms, così ad esempio posso usare la funzione per scorrere le voci di un menù. Capisco che la funzione così concepita è un compromesso tra semplicità e funzionalità (poco flessibile).

uwefed:
*La prima volta che chiami la funzione Leggi Tasto la cariabile tempo non ha valore. ( adesso non so che valore da se uno casuale o semicasuale).
Oltre questo solo quando la condizione é vera (pulsante schiacciato e millis() maggiore di un x piú 300) caricherai la variabile tempo con un valore.

La variabile tempo all'inizio direi che ha valore zero infatti non mi ha mai dato problemi

uwefed:

  • da un True ogni 300ms se il tasto rimane schiacciato perché non controlli se era giá schiacciato. Qua sta a Te se vuoi eseguire l' azione solo la prima volta che schiacci il tasto o di continuo finche il tasto viene mollato; presuppongo la prima opzione.

La seconda, come dicevo sopra

uwefed:
*1) usare un semplice delay di 10 ms

Non vorrei immetere delay all'interno del programma.

Non ho capito la soluzione 2 ma penso sia quelle che eventualmente vorrei intraprendere dato che usa millis.

uwefed:
Mit globalen Variablen hast Du das Problem nicht.
Grüße Uwe

Cosa vuol dire? Buona notte a domani? XD

mechrekt:

uwefed:
Mit globalen Variablen hast Du das Problem nicht.
Grüße Uwe

Cosa vuol dire? Buona notte a domani? XD

Questo succede perché scrivo anche sul Forum tedesco;
Scusami, volevo dire:
Se usi variabili globali non hai problemi.
Il comando return restituisce un valore a ogni chiamata di una funzione, ma quello non vuol dire che la funzione non possa usare delle variabili globali o modificarle.

risultato = funzione(parametri);
Visto che puoi usare solo una variabile dove memorizzare il risultato non puoi rendere piú di un valore.

Ciao Uwe

Ma chi sei, superman!!!

Ok, penso di aver capito.
Ho provato ad applicare il discorso delle strutture al mio programmino ed ho fatto questo ma ottengo un errore che non capisco, magari è di sintassi:

struct tasto
{
	int piedino;
	unsigned long tempo;
};

struct tasto TastoDX, TastoSX, TastoDec;
TastoDX.piedino = 1;
.
.

L'errore che ottengo dal compilatore di arduino è: "error: expected constructor, destructor, or type conversion before '.' token"

PS: l'errore si riferisce alla riga TastoDX.piedino = 1;

Dunque, qualcosa è cambiato mantenendo questa roba globale:

struct tasto
{
  int piedino;
  unsigned long tempo;
};

struct tasto TastoDX, TastoSX, TastoDec;

e mettendo l'assegnazione di questa roba all'interno del setup():

  TastoDX.piedino = 1;
  TastoSX.piedino = 2;
  TastoDec.piedino = 4;

Fino a qua compilo senza errori, spero di aver fatto bene.

Aggiungendo la definizione della funzione (anche se la lascio vuota)

boolean LeggiTasto(tasto Tasto)
{  }

ottengo questi messaggi di errore:

Tastiera_IOExp_Func_Struct:1: error: 'tasto' was not declared in this scope
Tastiera_IOExp_Func_Struct.cpp: In function 'boolean LeggiTasto(tasto)':
Tastiera_IOExp_Func_Struct:23: error: 'boolean LeggiTasto(tasto)' redeclared as different kind of symbol
Tastiera_IOExp_Func_Struct:1: error: previous declaration of 'boolean LeggiTasto'

no uwe è alto atesino, io sono più a sud :wink:
comunque consiglio, invece di passarlo come boolean prova a passare la funzione come void così non hai problemi sul return visto che in teoria mi ricordo che col boolean non hai problemi, oppure mettila a int e come return metti 1 o 0

perchè il valore è un int quindi dovresti fare

boolean LeggiTasto(int Tasto) { }