Come creare un ciclo per leggere sei input analogici

Buongiorno a tutti.
Questo è il mio primo post (mi sono appena iscritto al forum).
Sono un tecnico elettronico hardware con "qualche conoscenza" di programmazione.
Spero di non aver sbagliato la sezione in cui inserire la mia richiesta di aiuto.

Ecco la mia domanda.
Ho bisogno di leggere sei ingressi analogici per conoscere il valore di altrettanti potenziometri da 1K, ad essi collegati.
Questa lettura deve ripetersi all'infinito.
Il valore acquisito verrà in seguito impiegato per controllare altrettanti servo collegati a delle uscite digitali (ed avere qualche stampa di debug a monitor).

Non ho avuto problemi a scrivere un pò di codice per ottenere quanto desideravo e (miracolosamente) funziona! (non è molto ottimizzato e per nulla elegante, ma al momento non mi importa - voglio solo ottenere un risultato, dopo lo modificherò e lo razionalizzero).

Il mio problema è che vorrei inserire le sei istruzioni "analogRead(An);" in un ciclo for, ma non riesco a capire come fare, visto che l'indice di tale ciclo dovrebbe essere il nome del pin analogico, ma questo, essendo alfanumerico, non può essere una semplice variabile "n che va da 1 a 6".
Ho provato diverse cose ma nessuna ha funzionato. Ho provato anche a inserire la sequenza A1A2A3A4A5A6 in un array char, per poi leggerlo a coppie ed usare il risultato come parametro del comando analogRead(), ma non funzona (me lo aspettavo).
Chiedo scusa per la stupidità della domanda, ma non so come proseguire e chiedo aiuto a voi che conosete l'argomento.
Allego il codice scrtto (ripeto, non guardate lo stile o l'ottimizzazione - per ora non mi importa affatto).
Ringrazio in anticipo tutti coloro che avranno la pazienza di insegnarmi una cosa nuova. Saluti.

#include <Servo.h>

Servo servo1, servo2, servo3, servo4, servo5, servo6;

void setup()
{

  //si imposta la seriale
  Serial.begin(9600);

  //Si impostano i pin digitali su cui saranno collegati i servo.
  servo1.attach(41);
  servo2.attach(42);
  servo3.attach(43);
  servo4.attach(44);
  servo5.attach(45);
  servo6.attach(46);

  int LetturaPot1, LetturaPot2, LetturaPot3, LetturaPot4, LetturaPot5, LetturaPot6;   // Valori letti dai potenziometri (0-1024).
  int OhmPot1, OhmPot2, OhmPot3, OhmPot4, OhmPot5, OhmPot6;  //Valori letti sui potenziomentri e rimappati in ohm.
  int PosizioneS1, PosizioneS2, PosizioneS3, PosizioneS4, PosizioneS5, PosizioneS6;   // Posizione in gradi dei servo.
  int LimInf1, LimInf2, LimInf3, LimInf4, LimInf5, LimInf6;    // Limite inferiore dei servo - Da non superare.
  int LimSup1, LimSup2, LimSup3, LimSup4, LimSup5, LimSup6;    // Limite superiore dei servo - Da non superare.

}

void loop()
{

  int LetturaPot1 = analogRead(A1);
  int LetturaPot2 = analogRead(A2);
  int LetturaPot3 = analogRead(A3);
  int LetturaPot4 = analogRead(A4);
  int LetturaPot5 = analogRead(A5);
  int LetturaPot6 = analogRead(A6);

  int OhmPot1 = map(LetturaPot1, 0, 1024, 0, 1000);
  int OhmPot2 = map(LetturaPot2, 0, 1024, 0, 1000);
  int OhmPot3 = map(LetturaPot3, 0, 1024, 0, 1000);
  int OhmPot4 = map(LetturaPot4, 0, 1024, 0, 1000);
  int OhmPot5 = map(LetturaPot5, 0, 1024, 0, 1000);
  int OhmPot6 = map(LetturaPot6, 0, 1024, 0, 1000);

  int PosizioneS1 = map (LetturaPot1, 0, 1024, 0, 180);
  int PosizioneS2 = map (LetturaPot2, 0, 1024, 0, 180);
  int PosizioneS3 = map (LetturaPot3, 0, 1024, 0, 180);
  int PosizioneS4 = map (LetturaPot4, 0, 1024, 0, 180);
  int PosizioneS5 = map (LetturaPot5, 0, 1024, 0, 180);
  int PosizioneS6 = map (LetturaPot6, 0, 1024, 0, 180);

  Serial.print("POT-1  ");
  Serial.print(OhmPot1);
  Serial.print("  ");
  Serial.print(PosizioneS1);
  Serial.print("        ");
  Serial.print("POT-2  ");
  Serial.print(OhmPot2);
  Serial.print("  ");
  Serial.print(PosizioneS2);
  Serial.print("        ");
  Serial.print("POT-3  ");
  Serial.print(OhmPot3);
  Serial.print("  ");
  Serial.print(PosizioneS3);
  Serial.print("        ");
  Serial.print("POT-4  ");
  Serial.print(OhmPot4);
  Serial.print("  ");
  Serial.print(PosizioneS4);
  Serial.print("        ");
  Serial.print("POT-5  ");
  Serial.print(OhmPot5);
  Serial.print("  ");
  Serial.print(PosizioneS5);
  Serial.print("        ");
  Serial.print("POT-6  ");
  Serial.print(OhmPot6);
  Serial.print("  ");
  Serial.println(PosizioneS6);

  servo1.write (PosizioneS1);
  servo2.write (PosizioneS2);
  servo3.write (PosizioneS3);
  servo4.write (PosizioneS4);
  servo5.write (PosizioneS5);
  servo6.write (PosizioneS6);
}

Buonasera e benvenuto, :slight_smile:
essendo il tuo primo post, nel rispetto del regolamento della sezione Italiana del forum (… punto 13, primo capoverso), ti chiedo cortesemente di presentarti IN QUESTO THREAD (spiegando bene quali conoscenze hai di elettronica e di programmazione ... possibilmente evitando di scrivere solo una riga di saluto) e di leggere con molta attenzione tutto il su citato REGOLAMENTO ... Grazie. :slight_smile:

Guglielmo

P.S.: Ti ricordo che, purtroppo, fino a quando non sarà fatta la presentazione nell’apposito thread, nessuno ti potrà rispondere, quindi ti consiglio di farla al più presto. :wink:

Ma perchè le variabili le dichiari nel setup?

In effetti le dichiari sia nel setup che nel loop.
L'int che metti davanti al nome variabile rappresenta la dichiarazione.

Per il ciclo, i pin analogici corrispondono anche a un numero, mi pare dal 15 in su ma bisognerebbe verificare.
Inoltre le letture analogiche in sequenza su pin diversi danno problemi. Dovresti farne una a vuoto ogni volta che cambi pin.

1-jack:
ma questo, essendo alfanumerico, non può essere una semplice variabile "n che va da 1 a 6".

non è vero, ma magari non lo sapevi

A1 A2 eccetera non sono nomi, sono macro
che corrsipondono a numeri

quindi basta cercare che valore hanno (magari stampandoli) e poi sai a che numeri corrispondono i vari pin analogici

però per non saper ne leggere ne scrivere fai così:

crea un array di byte,
lo carichi con le macro A1 A2 etc etc

esempio

byte piedanalogico[]={A1,A2,A3.....}

poi puoi usare analogread(pinanalogico[i])
dove invece del numero di piedino puoi mettere l'elemento dello array

per le variabili:
creale una volta sola prima del setup

se le crei nel setup le perdi quando ne esci
se le crei nella loop le perdi di continuo, dato che dalla loop si esci e si rientra di continuo

e ti aggiungo

puoi fare anche un array di servo

Servo servo[6];

che comandi con un for simile

e un array di letture e un array di valori calcolati

e fai tutto in un solo ciclo

poi, tra magari qualche mese, potrai fare una struttura, che contiene UN servo, il suo piedino, il suo Analoginput, e via così

creare un array di queste strutture

che cicli una volta sola con un solo for

il tu programma diventerebbe di poche righe
ma questo è il futuro...

Buon mattino.

Grazie davvero, Standardoil. Mi hai insegnato diverse cose nuove che non immaginavo !
Così funziona alla grande ed il codice si accorcia un sacco!
Non avrei mai immaginato che A0, A1, ecc si potessero usare in quel modo (o addirittura che il comando potesse accettare la sostituzione con il vero pin fisico del uC Atmel).
Anche se mi riesce difficile capire come un singolo tipo byte possa contenere due caratteri ("A" e "1"), a meno che il compilatore sia così "intelligente" da capire che deve sostituirvi il corrispondente numero del pin. Questo per me è più che logico ma, non penso sia quel che accade.

Anche io avevo pensato ad un array di servo, ma l'avevo abbandonata perchè non mi funzionava. Pensavo che il compilatore non lo consentisse.
Il mio errore era dovuto al fatto che pensavo di dover riempiere i singoli elementi dell'array, dichiarato come tipo Servo, con dei valori. Tipo così:

Servo NumeroServo[] = {Ser1, Ser2, Ser3, ser4, Ser5, Ser6};

Invece, giustamente, si dichiara il tipo e non gli si assegna nulla, proprio come si fa con la "istruzione singola":

Servo ser1;

Grande l'idea di creare una struct per raggruppare tutte le varie variabili legate ad ogni servo e relativo potenziometro. Conosco il comando, ma non avrei mai pensato ad un suo impiego in questo caso, che invece è la cosa più logica. Bello!

Finalmente ho trovato un forum dove esistono delle persone che sanno rispondere con dei suggerimenti davvero utili, che permettono non solo di risolvere il problema specifico, ma che ti insegnano cose nuove e ti permettono di crescere, senza perdersi in sofismi o facendoti perdere la testa in loop infiniti di domande che ti creano altre domande.
Grazie davvero!
Auguro una buona bomenica a tutti. :slight_smile:

1-jack:
Anche se mi riesce difficile capire come un singolo tipo byte possa contenere due caratteri ("A" e "1")

Infatti non li contiene, quei caratteri sono solo nomi/etichette di comodo (definiti altrove) interpretati a tempo di compilazione:

#define  SUPERPIPPO   82

byte a = SUPERPIPPO;

equivale a scrivere:

byte a = 82;

Un'altra possibilità è definire delle costanti, che sono vere e proprie variabili valutate a tempo di esecuzione ma non possono essere alterate:

const byte  SUPERPIPPO =  82;

Il vantaggio di usare nomi/etichette e costanti è che il valore 82 potrebbe essere difficile da ricordare a cosa serva, mentre un nome è molto più significativo (e per convenzione si usa scriverlo in maiuscolo).

Dove leggi HIGH LOW OUTPUT INPUT INPUT_PULLUP ecc, sono tutti nomi predefiniti che corrispondono a qualche valore. In particolare HIGH e LOW sono esattamente la stessa cosa di 1 e 0.

Oltre agli ottimi consigli già ricevuti, ne aggiungo un ulteriore.

Le letture degli ingressi analogici avranno una certa fluttuazione per forza di cosa dovuta a disturbi e fluttuazione dell'alimentazione stessa.
Prevedi fin da ora una soglia di isteresi cosi da non aggiornare continuamente la posizione del servo per variazioni minime.

es:

#define ISTERESI  4
byte piedAnalogico[]={A1,A2,A3.....};
unsigned int ultimoValore[ sizeof(piedAnalogico) ];  // Sizeof va bene cosi solo perché piedAnalogico è array di byte
.....
for(int i=0; i<sizeof(piedAnalogico); i++){
  unsigned int valoreAttuale = analogRead(piedAnalogico[i]);
  if( abs(valoreAttuale - ultimoValore[i]) > ISTERESI  ){
     ultimoValore[i] = valoreAttuale ;
     // Aggiorno la posizione del servo i
     etc etc etc 
  }
}

Ah, giusto, per la stabilità dei valori quando si leggono velocemente più ingressi analogici in sequenza, può essere ulteriormente utile fare una doppia lettura scartando la prima:

analogRead(piedAnalogico[i]);
unsigned int valoreAttuale = analogRead(piedAnalogico[i]);

ClaudioFF
Stai parlando di quello che disse anche FRATT all'inizio?
Infatti mi chiesi cosa intendeva con "una lettura a vuoto".
Semplicemente allora farò una lettura allo stesso canale senza assegnare il valore ottenuto a nulla (perdendolo) ed assegnerò alla variabile solo quello della seconda lettura. OK, grazie.

Cotestatnt
E' vero, hai ragione. infatti il braccio comandato da quei servo, ogni tanto inizia a fremere o fa qualche piccolissimo scattino improvviso.
Avevo scartato a priori influenze ultraterrene o un'attacco di Parkinson. E avevo dato la colpa ai potenziometri instabili, oppure ... appunto a delle interferenze o tolleranze/arrotondamenti nei calcoli per il posizionamento.
Il tuo suggerimento è intelligente.
Solo come speculazione accademica ... facendo così, però, non consentirei degli spostamenti piccoli (inferiori al valore contenuto in isteresi). Forse potrebbe essere meglio pensare invece ad una "persistenza" del valore letto, piuttosto che ad una isteresi. Se il "nuovo valore" è confermato da una seconda (o terza) lettura, difficilmente potrà essere dovuto ad una interferenza/tolleranza/arrotondamento e allora verrà confermato pilotando il servo, altrimenti verrà scartato.
Il contro è che se il sistema è già di per se rumoroso, tre valori potrebbero non ripetersi così spesso e allora questo circo diverrebbe ingovernabile. Impiegandole allora entrambe (piccola isteresi + persistenza) si renderebbe il sistema ben immune alle interferenze.
Questo renderebbe il sistema più lento, ma, appunto era solo una speculazione accademica.
Ance perchè, fortunatamente, con questo ambaradam non devo governare l'attracco dello Space Shuttle e con i suggerimenti ricevuti da voi, ho raggiunto il mio obbiettivo.

Ringrazio tutti coloro che hanno dedicato tempo e competenza alla mia richiesta di aiuto.
Siete uno dei motivi per cui Arduino ha tanto successo, complimenti!

Si le letture a vuoto sono quelle.

Per il rumore, oltre che dai potenziometri, può arrivare dall'alimentazione e anche dalla conversione dell'ADC (rumore di quantizzazione). Per il rumore esterno bastano dei piccoli condensatori sugli ingressi, per quello interno+esterno basta una media mobile degli ultimi N valori letti. Ad ogni nuova lettura la più vecchia va via e entra nella media quella nuova. Tutti questi sistemi rendono naturalmente più lenta la risposta.

1-jack:
infatti il braccio comandato da quei servo, ogni tanto inizia a fremere o fa qualche piccolissimo scattino improvviso.

Quello purtroppo spesso è dovuto anche alla qualità meccanica e/o tolleranze del servomotore e può succedere anche se non vai ad impostare una nuova posizione.
Il movimento del servomotore trascina un piccolo potenziometro posto all'interno che da il feedback di posizione al controller del servo. Se ci sono tolleranze eccessive nei movimenti possono esserci queste microvibrazioni fastidiose ed indesiderate perché il driver tenta continuamente di raggiungere la posizione comandata senza riuscirci.
Io di solito, se l'applicazione lo permette, ovvero quando c'è poca coppia resistente da muovere, con i servo "economici" faccio un attach-movimento-detach (cosa che contribuisce anche a ridurre il consumo) confidando poi sulla resistenza offerta dal riduttore per tenere in posizione il servo, ma ovviamente è una soluzione di ripiego.

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