come funziona void

salve a tutti ho scoperto che oltre al void setup e void loop posso creare un void tutto mio tipo void gianluca ,
ma non sono riuscito a capire come farlo partire e chiudere
non ho trovato niente su google come funziona?

Il linguaggio utilizzato da Arduino sono il C e il C++.
Le funzioni sono parte di questo linguaggio.
Per capirne il funzionamento puoi dare uno sguardo qui --> Funzioni in C: void, double e le altre | Guida C | Programmazione HTML.it
oppure qui --> http://cpiupiu.altervista.org/
Qui c'è un buona guida al C in generale ma è applicabile con alcuni accorgimenti ad Arduino --> http://blacklight.gotdns.org/guidac.pdf

pippopippoli:
salve a tutti ho scoperto che oltre al void setup e void loop posso creare un void tutto mio tipo void gianluca ,
ma non sono riuscito a capire come farlo partire e chiudere
non ho trovato niente su google come funziona?

Dato il nome deciso da te, dove vuoi eseguirlo, scrivi il nome e poi apri e chiudi la tonda.
Esempio stupido:

const byte pinLed=13;
void setup()
{ delay(1000);
  pinMode(pinLed,OUTPUT);
}

void ledOn()
{ digitalWrite(pinLed,HIGH);
}

void ledOff()
{ digitalWrite(pinLed,LOW);
}

void loop()
{ ledOn();
  delay(1000);
  ledOff();
  delay(1000);
}

Aggiungo a quanto ti hanno già detto: "void" da solo significa "nulla", e messo prima del nome di una funzione indica che tale funzione non restituisce al chiamante nessun valore, vuol dire che vengono chiamate, eseguono il codice che contengono e poi restituiscono il controllo al programma chiamante.

Scusa se mi inserisco, forse e' una domanda stupida, ma poniamo il caso che io voglia farmi delle funzioni tipo quella, ma che devono restituire qualcosa (ad esempio, una funzione che, richiamata solo quando mi serve, mi vada a leggere dei sensori analogici, esempio, NTC o fotocellula o volt, quello che vuoi, e che sia in grado di riportare il valore letto al programma principale sotto forma di variabile) ... quale sarebbe il modo migliore di farlo ? ... intendo, sempre come funzione richiamabile a comando come la void, non come parte del programma che continua a girare ...

Hai vari modi.
Se ti serve un solo parametro dichiari la funzione in base al tipo di parametro da restituire e usi il comando return all'interno della funzione.
Se sono più parametri li puoi passare alla funzione come puntatori e la funzione li manipola poi restituendoli modificati.
Potresti infine usare dei parametri globali singoli o organizzati in una struttura (C) o in una classe (C++).

Etemenanki:
Scusa se mi inserisco, forse e' una domanda stupida, ma poniamo il caso che io voglia farmi delle funzioni tipo quella, ma che devono restituire qualcosa ...

Così :

tipo_da_restituire nome_funzione(tipo_parametri parametri) {
   ....
   ....
   <<implementazione>>
   ....
   ....
   return valore_da_restituire;
}

... o non ho capito la domanda ??? :roll_eyes:

Guglielmo

gpb01: Fondamentalmente mi chiedevo se c'erano funzioni tipo la "void" (che non restituisce nulla), ma gia previste per restituire valori ... oppure se era necessario usare sempre "void", ma trasferendo i valori in un modo diverso, dato che di per se non lo fa (a quanto ho capito)

O in alternativa, se per fare una cosa simile, fosse necessario scriversi una specifica libreria ... cosa molto piu complessa ...

EDIT: PaoloP, potrebbe essere anche un solo parametro, al limite ... ad esempio, se fosse una funzione per leggere una temperatura solo quando serve, il dato sarebbe, alla fine, solo una variabile numerica contenente un singolo valore ...

Etemenanki:
gpb01: Fondamentalmente mi chiedevo se c'erano funzioni tipo la "void" (che non restituisce nulla), ma gia previste per restituire valori ... oppure se era necessario usare sempre "void", ma trasferendo i valori in un modo diverso, dato che di per se non lo fa (a quanto ho capito) ...

Scusa, sarò io che non capisco ... ma quando, ad esempio, usi analogRead() ... quella è una funzione che ti ritorna un valore ... per l'esattezza un int ... quindi ... tu che vuoi sapere esattamente ???

Guglielmo

@Guglielmo, credo @Eteme voglia fare una procedura (funzione con void) a cui passa dei parametri modificabili ma non ritorna nulla.

Si, @Eteme puoi, come detto da @PaoloP

Riassumendo:

  1. se non devi restituire nulla fai una funzione void (che in altri linguaggi si chiamano procedura o sub)
  2. se devi ritornare un solo valore fai una funzione dichiarando davanti al posto del void il tipo del ritorno (e in altri linguaggi le chiamano funzioni)
  3. se devi ritornare più valori, di solito fai una funzione (void oppure no) che manipola i parametri (alcuni o tutti) che passi. Quindi quei parametri saranno dal punto di vista della funzione sia in input che in output. Alcuni parametri però in C vengono passati per valore (sono una copia del valore originale) perciò bisogna usare i puntatori per permettere alla funzione di cambiare i valori della variabile passata.

Se esiste il modo di scriversi una funzione "complessa", diversa da quelle gia esistenti, in grado di eseguire piu comandi, e trasferire anche valori, all'occorrenza ...

Ad esempio, il tuo riferimento all'analogread, quella e' una funzione gia esistente, che legge un valore analogico da un'ingresso (se non sbaglio), e lo mette in una variabile, ma fondamentalmente fa solo quello ... poniamo invece, ad esempio, il caso che tu debba eseguire anche altre funzioni connesse alla tua lettura, funzioni per le quali non hai un comando gia pronto ... se ho capito bene il discorso della "void", questa ti permette di crearti delle funzioni (chiamiamole "pacchetti di comandi", se vuoi), e di richiamarle quando ti servono, ma non restituisce nulla al programma ...

Se invece io volessi, sempre come esempio, eseguire diverse funzioni, il cui risultato finale e' una lettura analogica, e poi avere questo risultato trasferito indietro al corpo principale del programma, ma senza doverle riscrivere ogni volta che servono, e senza che rimangano sempre in esecuzione, il modo piu semplice che mi viene in mente e' di metterle in una "funzione" (o insieme di funzioni), e richiamarla solo nei momenti in cui mi serve ... in questo caso, per restituire il valore ad una variabile nel corpo principale una volta che la "funzione" e' terminata, il sistema migliore quale sarebbe ? ... creare una funzione specifica, o usare un "return" che spedisca il valore ad una variabile "esterna" alla mia "funzione" ?

Faccio un'esempio stupido, metti che debba campionare una serie di temperature dopo aver spostato una sonda ed atteso un tempo di stabilizzazione, ma che il tutto lo debba fare solo quando premi un pulsante (era una cosa che avevo fatto secoli fa in basic, e' il primo esempio che mi e' venuto in mente :P) ... dovrei fare qualcosa di simile:

un "corpo" principale
....
....
.... (istruzioni)
quando il pulsante 1 e' premuto, richiama "funzione1"
elabora "x"
....
....
.... (altre istruzioni)
ecc

ed una sottofunzione tipo:

"funzione1"
sposta la sonda
attendi 10 secondi
(loop) leggi 10 valori e mettili in un'array
fai la media del contenuto dell'array
"x"=risultato
ritira la sonda
torna a principale (ovviamente nel punto da cui e' stata lanciata "funzione1")
fine "funzione1"

che venga richiamata dal corpo principale solo quando serve (potrebbero anche essere piu funzioni differenti, per eventi diversi) ... e cosi via ... (scusa il linguaggio "da profano" ... lo so che magari a voi la domanda sembra stupida, come quando chiedono a me che differenza c'e' fra tensione e corrente :stuck_out_tongue: ... ma sto ancora cercando di impararla, la programmazione delle MCU) ... a livello di programmazione Arduino, o comunque MCU Atmel, e' una cosa possibile, o non e' supportata e bisogna usare una struttura completamente differente ?

Etem,
come fare te l'ho fatto vedere 3 post sopra, inoltre ...
... non hai limiti al numero di funzioni che puoi creare e le chiami solo quando ti servono.

Ogni funzione, può o non può tornare un valore. Se NON torna alcun valore, per farlo capire al compilatore, si usa la parolina magica "void" che, appunto, indica che il compilatore NON deve aspettarsi indietro nessun valore dalla funzione. Se la funzione ritorna un valore, allora devi dire quale è il tipo di valore che ritorna : byte, char, int, long int. .... e così via.

La funzione è quindi un blocchetto che fa sempre una determinata cosa ... la analogRead() legge il valore del convertitore ADC e ti ritorna questo valore in un int che tu assegni ad una variabile, quindi ... nel tuo programma, quando ti serve di leggerlo farai :

mio_valore = analogRead(Ax);

il controllo passerà temporaneamente alla funzione che, fatta la lettura, restituirà il valore che verrà assegnato alla tua variabile.

Allo stesso identico modo tu ti crei la TUA funzione che, nel tuo esempio, campiona la temperatura e ti ritorna il risultato ...
... quando ti serve la chiami, quella va in esecuzione, ritorna indietro il valore e tu assegni questo valore ad una tua variabile e prosegui con il tuo programma. Se ti riserve, la richiami di nuovo e così via ...

Più chiaro adesso ?

Guglielmo

@Eteme non capisco bene.
Quello che fa la tua funzione dipende da te e può essere complessa quanto vuoi. Può richiamare a sua volta altre funzioni da te create.
L'istruzione return si usa solo nel caso di funzione non void che deve ritornare un dato del tipo dichiarato.

Scusa @Guglielmo se mi sovrappongo :slight_smile:

Per incasinare il discorso ]:smiley: diciamo pure che il C++ permette l’overloading delle funzioni.
Ovvero puoi avere più funzioni con lo stesso nome ma parametri in ingresso diversi e il compilatore sceglie quella che corrisponde ai parametri.
Il tipo di funzione (int, float, ecc), il parametro restituito e il codice interno può essere diverso da funzione a funzione.

gpb01:
Se la funzione ritorna un valore, allora devi dire quale è il tipo di valore che ritorna : byte, char, int, long int. .... e così via.

Questo lo devo dichiarare nel "setup", solo all'inizio, se non ho capito male :stuck_out_tongue:

... mio_valore = analogRead(Ax);

il controllo passerà temporaneamente alla funzione che, fatta la lettura, restituirà il valore che verrà assegnato alla tua variabile.
...

Più chiaro adesso ?

Un po piu chiaro, si (abbi pazienza, io sono cresciuto a gwbasic e basta :P) ... era la struttura della chiamata che mi fregava nel ragionamento ... quindi, secondo il tuo esempio, la chiamata della funzione (analogread) e' l'azione stessa di assegnarla come valore alla variabile ... io invece ragionavo (sbagliando) con la sequenza "chiama funzione > esegui funzione > funzione assegna valore a variabile > ritorna > usa variabile", (e li mi incasinavo :P) ... mentre il tuo esempio "mio_valore = analogRead(Ax);", chiama la funzione semplicemente assegnando il valore della funzione alla variabile ... inizia a calare la nebbia :wink:

Solo un dubbio, questo tipo di struttura di richiamo comporta sempre che la funzione restituisce il controllo al punto in cui e' stata chiamata, giusto ? ... se invece dovesse condizionare il punto di rientro al valore restituito, dovrei farlo dopo il rientro usando degli if o dei case, non potrei farlo dall'interno della funzione stessa ...

L'istruzione return si usa solo nel caso di funzione non void che deve ritornare un dato del tipo dichiarato.

Questa devo studiarmela con calma ...

Per incasinare il discorso ]:slight_smile: diciamo pure che il C++ permette l’overloading delle funzioni.
Ovvero puoi avere più funzioni con lo stesso nome ma parametri in ingresso diversi e il compilatore sceglie quella che corrisponde ai parametri.
Il tipo di funzione (int, float, ecc), il parametro restituito e il codice interno può essere diverso da funzione a funzione.

Grazie, mi mancava un'altro po di confusione ... :stuck_out_tongue: XD

Etemenanki:
Questo lo devo dichiarare nel "setup", solo all'inizio, se non ho capito male :stuck_out_tongue:

No, la dovresti dichiarare subito dopo gli #include e i #define come prototipo di funzione ma ci pensa l'IDE di Arduino a farlo, quindi sei esonerato da questo compito.

Etemenanki:
....
Questo lo devo dichiarare nel "setup", solo all'inizio, se non ho capito male :stuck_out_tongue:

Il guaio è che usi l'IDE che, per semplificare la vita a chi inizia, nasconde tutta una parte ... ma va bene ...

Allora, normalmente in C si dovrebbe sempre dichiarare in testa al programma (più o meno dove dichiari le costanti, le variabili globali, ecc) le funzioni che usi, descrivendo i parametri che esse vogliono e il valore che esse ritornano, cioè ... occorrerebbero i "prototipi delle funzioni" es. :

int analogRead(int);

questo è il prototipo, poi, da qualche parte c'è l'implementazione della funzione :

int analogRead(int pinNumber) {
   ....
   ....
   return il_valore
}

tutto questo l'IDE te lo nasconde e, quando dichiari una TUA funzione ... di nascosto il prototipo lo fa lui. Se usi Atmel Studio invece ... o altri ambienti di sviluppo, i prototipi li devi mettere tu.

Etemenanki:
Un po piu chiaro, si (abbi pazienza, io sono cresciuto a gwbasic e basta :P) ... era la struttura della chiamata che mi fregava nel ragionamento ... quindi, secondo il tuo esempio, la chiamata della funzione (analogread) e' l'azione stessa di assegnarla come valore alla variabile ...

Allora .. nessuno ti vieta di chiamare la funzione direttamente

analogRead(pin);

... solo che così ... ti perdi il valore di ritorno, visto che il compilatore non sa a chi dare l'int di ritorno e quindi lo butta via XD

Se la funzione torna un valore, magari quel valore ti serve (o magari no) ... e quindi tu dici al compilatore di prendere il valore di ritorno ed assegnarlo ad una variabile.

Etemenanki:
Solo un dubbio, questo tipo di struttura di richiamo comporta sempre che la funzione restituisce il controllo al punto in cui e' stata chiamata, giusto ? ... se invece dovesse condizionare il punto di rientro al valore restituito, dovrei farlo dopo il rientro usando degli if o dei case, non potrei farlo dall'interno della funzione stessa ...

Si, in pratica, con la solita regola da destra a sinistra, l'espressione viene valutata ... e dato che incontra l'assegnazione ad una variabile di un qualche cosa che non conosce ... prima chiama la funzione (in pratica si salva il punto in cui sta e salta al punto d'ingresso della funzione) e, quando la funzione è terminata torna dove aveva lasciato assegnando il valore che ora ha :wink:

Etemenanki:
Questa devo studiarmela con calma ...

Immagina void come un "tipo vuoto, inesistente" ... se la funzione deve solo fare delle cose ma non ti deve ritornare nulla (es. pulisci LCD ... deve solo pulire la memoria del LCD ma non ti deve tornare nulla) allora la dichiari di tipo void ovvero da cui non ti aspetti nulla in ritorno e che non contiene il ritorno di un valore (l'istruzione return la può contenere, non deve contenere return valore). Negli altri casi, in cui ti aspetti un valore di ritorno, la dichiarerai del tipo del valore che ti aspetti ti ritorna (byte, char, int ....)

Etemenanki:
Grazie, mi mancava un'altro po di confusione ... :stuck_out_tongue: XD

Al momento NON stare a sentire chi ti parla di C++ ... ]:smiley:
... già hai da lavorare parecchio sulle basi ... poi per il C++ c'è sempre tempo :wink:

Guglielmo

Etemenanki:

L'istruzione return si usa solo nel caso di funzione non void che deve ritornare un dato del tipo dichiarato.

Questa devo studiarmela con calma ...

{ ... da qualche parte nel loop
hai dichiarato qui o all'inizio
int x=1; int y=2; int z;
fai
z = somma(x,y);
}

dove sotto costruisci

int somma (int a, int b) {int c = a+b; return c}

una cosa di queste insomma...

Esempio concreto:

void flashLed(int flash, byte ledPin) {
  for (int i = 0; i < flash; i++) {
    digitalWrite(ledPin, 1);
    delay(50);
    digitalWrite(ledPin, 0);
    delay(50);
  }
}

Hai una funzione che accetta dei parametri, fa "qualcosa" e non ti restituisce nulla indietro. Semplice e pulita.
Mettiamo invece ora che tu debba sapere dopo l'attivazione di un relé che accende un dispositivo, la lettura di una tensione che ti indicherà un qualcosa.

boolean accendiDevice() {
  digitalWrite(pin, HIGH);
  delay(500);
  if (analogRead(sensorPin) < 250) { //errore
    return false;
  } else { //tutto OK
    return true;
  }
}

Questa funzione "fa qualcosa" ma non solo, ti dice anche com'è andata l'operazione.
Se tu ad esempio nel codice scrivi qualcosa del tipo:

if (accendiDevice() == FALSE) {
    Serial.println("ERRORE");
}

La "risposta" della funzione diventa l'elemento di test dell'if.

In realtà, per non confondere le idee a Etem, occorre dire che anche nell'ultimo caso spiegato da Leo c'è un valore di ritorno che viene comunque usato ...

if (accendiDevice() == FALSE) {
    Serial.println("ERRORE");
}

... il compilatore esamina da destra a sinistra quell'IF ... FALSE è una costante e quindi ha un valore, poi c'è l'operatore e poi c'è la funzione ... il compilatore non sa il valore della funzione quindi genera un codice che, arrivato a quel punto salta nella funzione, la esegue, prende il valore di ritorno e, tornato indietro, quel valore viene usato come se li ci fosse una variabile che ha assunto quel valore ... quindi può fare il confronto ed agire di conseguenza :wink:

Guglielmo