Sensore Umidità del terreno SEN0114 DF-Robot

di Leone Magliocchetti Lombi

I sensori analogici, soprattutto quelli di fascia più economica, sono noti per non essere costanti in fase di lettura. Ho pensato perciò di condividere questa simpatica funzione che ho sviluppato per le piattaforme Arduino per una lettura più “stabile” con qualsiasi sensore analogico.

Il problema: osservando in sequenza i valori restituiti da un sensore analogico, come ad esempio un sensore di luminosità o di umidità del terreno, ci accorgiamo presto delle variazioni o dei picchi nella lettura che spesso vanno al di là della “media”. In un momento di grande frustrazione (avevo perso alcune ore per cercare di interpretare i dati troppo differenti di una sonda pH economica per il mio sistema idroponico) ho cominciato a cercare online delle soluzioni per stabilizzare la lettura analogica. Non trovandone una che soddisfacesse le mie esigenze, ho deciso di svilupparne una che fosse facile da richiamare ma abbastanza evoluta per fini statistici di un certo livello. 

Questa funzione restituisce una lettura “stabilizzata” di un qualsiasi sensore analogico perché esegue un numero elevato di letture e restituisce una media al netto dello scarto dei valori massimi e minimi.  Ah, presente inoltre un sistema di controllo interno che vi avvisa se per caso vi siete sbagliati ad indicare un numero totale di scarti superiore a quello delle letture. In questo caso la funzione effettuerà comunque una lettura singola del sensore e ne restituirà il valore. 

Una volta copia/incollata nel vostro codice basterà semplicemente specificare all'interno dell'argomento tra le parentesi in questo ordine:  

  • Il Pin a cui è collegato il sensore da leggere
  • Il numero di letture su cui volete basare la media (fino ad un massimo di 100)
  • Il numero di valori che volete scartare (discards).

Per esempio con la semplice riga di codice:

int valore = smartAnalog(A0, 20, 2);
Un super-economico sensore analogico di luminosità.
La sue letture "salterine" possono essere stabilizzate
tramite una lettura "media" dei suoi valori.

abbiamo indicato (tra le parentesi) che dal pin A0 saranno prese 20 letture in sequenza, da cui verranno scartati i due valori più alti (massimo) ed i due valori più bassi (minimo). Infine verrà eseguita una media dei valori restanti. La funzione restituirà alla variabile "valore" dell’esempio la media appena calcolata, e questo valore sarà poi utilizzabile nel resto del vostro sketch. Ovviamente possiamo personalizzare la richiesta, ad esempio indicando un qualsiasi altro pin analogico (A1, A2... A5) ed un qualsiasi numero di letture e scarti, secondo le vostre necessità statistiche. 

Secondo me il vantaggio di questa funzione è che, una volta aggiunta nel vostro codice, la potrete usare per leggere qualsiasi sensore analogico presente nel vostro sistema (tutti quelli che avete collegato). Non c’è neanche più bisogno di gestire la parte di codice del “analog.Read” perché sarà la funzione a prendersi in carico questa parte.

Come già detto per utilizzare questa funzione basta fare copia incolla del codice seguente al di fuori del vostro ciclo " void loop () ". Poco importa se sopra od in basso. Personalmente io preferisco aggiungere tutte le funzioni sempre in fondo, dopo la funzione " void loop () ". 

Funzione smartAverage da copia/incollare al di fuori della funzione" void loop () "

Selezionare interamente il codice seguente ed aggiungerlo al di fuori della funzione "void loop()". Andare poi a vedere più in basso gli esempi di richiamo della funzione. Da notare che ho escluso con i commenti le linee di codice utili al debugging che inviano alla porta seriale tutti i risultati dei passaggi intermedi. L'ho fatto con proposito nel caso foste interessati a capire meglio le fasi intermedie della funzione, o per riutilizzare parti di codice nei vostri progetti.

 
 
int smartAverage(int pin, int readings, int discards){
/*
 * Calculate the average of a series of reading from an Analog Pin.
 * In the argument you must specify (n. of pin, number of readings, number of discards)
 * e.g. value = average(A0, 10, 2);
 * Will read 10 values from A0, discard the 2 max and 2 min, and calculate
 * average based on remaining values.
 * NB: discarded values are the max and mins one.
 * For more documentation: ConnectedGardens.it
 * Author: Leone Magliocchetti Lombi
 * 
 */
/* no need to edit the rest of this function */
int analogValue;
int i, j, ph;
int array[100];
int n = 0;
unsigned long sum=0;
unsigned long average=0;
//quality control of instructions:
if ((discards*2)>=readings){ //if the number of discards requested is equal or higher than readings
 Serial.println("");
 Serial.print("You should verify your instructions for average function request on Pin ");
 Serial.print(pin);
 Serial.println("");
 Serial.print("A single sensor reading will be performed instead.");
 Serial.println(""); 
 analogValue = analogRead(pin);
 return(analogValue);
}
 
 //Serial.print("Read values are: ");
for (int x=0; x<readings ; x++) {
 analogValue = analogRead(pin);
 array[x]=analogValue;
 n = n + 1 ;
 //Serial.print(analogValue);Serial.print(" "); 
}
 
for ( i=0; i<n ;i++) {
 
 for( j=0; j<n-1; j++) {
 
 if(array[j]>array[j+1]) {
 ph=array[j];
 array[j]=array[j+1];
 array[j+1]=ph;
 }
 }
 
 }
 
 //Serial.println(" ");Serial.print("Ascending ordered values are: ");
for (int x=0; x<readings ; x++) {
 //Serial.print(array[x]);
 //Serial.print(" ");
}
 
 //Serial.println(" ");Serial.print("Average after discards is: ");
for (int x= 0+discards; x<readings-discards; x++){
 sum = sum + array[x];
}
average = sum/(readings-(discards*2));
 //Serial.println(average);
 //Serial.print("based on: "); Serial.print(readings);Serial.print(" readings and : ");
 //Serial.print(discards);
 //Serial.print(" discards of max and min values (");
 //Serial.print(discards*2);
 //Serial.println(" discarded values in total) "); 
 
return(average);
}
 

Esempio 1: indicare il pin direttamente nell'argomento della funzione

Se abbiamo copiato l'intero codice precedente possiamo ora tornare all'interno della nostra funzione loop. Per questo primo esempio non è necessaria alcuna inizializzazione o setup di sorta.

void setup() {
 
Serial.begin(9600);
 
}
 
void loop() {
 
int value = smartAverage(A0, 10, 2);
Serial.print(" > Sensor Value is: ");
Serial.println(value); // visualizza a schermo il valore
 
delay(4000);
 
}

Ecco, come potete vedere la funzione smartAverage richiede praticamente la stessa lunghezza di codice di un qualsiasi analogRead ma restituisce un'informazione più elaborata. Possiamo indicare alla funzione direttamente il numero di pin a cui è collegato al sensore. Nell'esempio fornito vengono quindi effettuate dieci letture, e prima di calcolare la media verranno scartati i due valori più alti e più bassi. 

Esempio 2: attribuire un nome al Pin, ed utilizzare più sensori richiamando sempre la stessa funzione

Spesso per comodità e rigore attribuiamo un nome ad ogni pin utilizzato. Benissimo, la funzione smartAverage "si beve" anche il nome tranquillamente, come abbiamo fatto nel caso del sensore collegato al pin A0, chiamandolo "sensorPin". 

 
int sensorPin = A0;
 
void setup() {
 
 Serial.begin(9600);
 
}
 
void loop() {
 
int value = smartAverage(sensorPin, 10, 1);
 Serial.print(" > Sensor Value is: "); Serial.println(value);
 
int value2 = smartAverage(A1, 10, 2);
 Serial.print(" > Sensor2 Value is: "); Serial.println(value2);
 
int value3 = smartAverage(A2, 20, 2);
 Serial.print(" > Sensor2 Value is: "); Serial.println(value3);
 
delay(4000);
 
}
 

La funzione smartAverage legge indistintamente anche il nome del pin contenuto in una variabile. Nei tre esempi successivi sono stati anche variati i parametri rispetto all'esempio precedente:

  • per calcolare "value" verrà scartata solamente una lettura massima e minima
  • "value2" sarà calcolata al netto di 4 scarti
  • "value3" sarà calcolata sulla base di 20 letture e 4 scarti 

Non penso siano necessari altri esempi, probabilmente incontreremo questa funzione in vari esperimenti ed applicazioni!