Lettura analogica valore ballerino

Ciao a tutti,
sto leggendo da un ingresso analogico a cui è collegato un potenziometro; il progetto è una radio FM, il potenziometro fa la funzione di ricerca della stazione.
Utilizzo esp32, ma non cambia nulla rispetto ad arduino; il problema è che il valore che leggo non rimane stabile, con conseguenza che se saltella da un valore al successivo e viceversa, si ha continuamente il cambio della stazione.
Ho messo un filtro RC lato hardware ed ho fatto anche 10 letture consecutive nell'arco di 100 ms, ma non mi cambia la situazione, ho anche ridotto la lettura ADC a 10 bit (1024)
Uso la funzione MAP per convertire i 1024 bit letti in 210, che sono tutte le frequenze possibili da 87 a 108, a passi di 100 Khz. Per le prove sto utilizzando un potenziometro normale da 1 giro, ma a progetto definito dovrei utilizzare un 10 giri di precisione, ma non credo di risolvere con quello.
Sto sbagliando modalità? Quà sotto lo sketch, grazie :slight_smile:

#include <RDA5807.h>        // libreria modulo radio

#define MAX_DELAY_RDS 80   // 40ms - polling method
#define MAX_DELAY_STATUS   2000

#define POT   4             // pin potenziometro

int pot = 0;                // ci salvo lettura analogica
byte pos_pot = 0;           // ci salvo valore lettura potenz
byte pos_po = 0;            // ci salvo valore lettura potenz
byte old_pos_pot = 0;       // vecchia lettura potenz
byte conta_pot = 0;         // per contare letture
byte pos [10];              // ci salvo letture potenz
int totale = 0;             // ci salvo totale letture per media

char aux[80];
char *rdsMsg;
char *stationName;
char *rdsTime;

unsigned long rds_elapsed = millis();
unsigned long status_elapsed = millis();

unsigned long t_lett = 0;   // tempo millis per lettura

RDA5807 rx;

int freq[] = {8700, 8710, 8720, 8730, 8740, 8750, 8760, 8770, 8780, 8790,
              8800, 8810, 8820, 8830, 8840, 8850, 8860, 8870, 8880, 8890,
              8900, 8910, 8920, 8930, 8940, 8950, 8960, 8970, 8980, 8990,
              9000, 9010, 9020, 9030, 9040, 9050, 9060, 9070, 9080, 9090,
              9100, 9110, 9120, 9130, 9140, 9150, 9160, 9170, 9180, 9190,
              9200, 9210, 9220, 9230, 9240, 9250, 9260, 9270, 9280, 9290,
              9300, 9310, 9320, 9330, 9340, 9350, 9360, 9370, 9380, 9390,
              9400, 9410, 9420, 9430, 9440, 9450, 9460, 9470, 9480, 9490,
              9500, 9510, 9520, 9530, 9540, 9550, 9560, 9570, 9580, 9590,
              9600, 9610, 9620, 9630, 9640, 9650, 9660, 9670, 9680, 9690,
              9700, 9710, 9720, 9730, 9740, 9750, 9760, 9770, 9780, 9790,
              9800, 9810, 9820, 9830, 9840, 9850, 9860, 9870, 9880, 9890,
              9900, 9910, 9920, 9930, 9940, 9950, 9960, 9970, 9980, 9990,
              10000, 10010, 10020, 10030, 10040, 10050, 10060, 10070, 10080, 10090,
              10100, 10110, 10120, 10130, 10140, 10150, 10160, 10170, 10180, 10190,
              10200, 10210, 10220, 10230, 10240, 10250, 10260, 10270, 10280, 10290,
              10300, 10310, 10320, 10330, 10340, 10350, 10360, 10370, 10380, 10390,
              10400, 10410, 10420, 10430, 10440, 10450, 10460, 10470, 10480, 10490,
              10500, 10510, 10520, 10530, 10540, 10550, 10560, 10570, 10580, 10590,
              10600, 10610, 10620, 10630, 10640, 10650, 10660, 10670, 10680, 10690,
              10700, 10710, 10720, 10730, 10740, 10750, 10760, 10770, 10780, 10790,
              10800
             };

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

  pinMode(POT,  INPUT);    // mette il pin come ingresso

  Serial.println();
  Serial.println("test radio");
  Serial.println();

  analogReadResolution(10);

  delay(100);                       // attendo alimentazioni

  pot = analogRead(POT);              // lettura del potenz
  pos_pot = (map(pot, 0, 1023, 0, 255));  // faccio scala
  old_pos_pot = pos_pot;              // salvo il valore

  rx.setup();                       // default parametri 32.768kHz passive crystal
  // rx.setup(CLOCK_32K, OSCILLATOR_TYPE_ACTIVE); // 32.768kHz Active Crystal
  // rx.setup(CLOCK_12M, OSCILLATOR_TYPE_PASSIVE);
  // rx.setup(CLOCK_38_4M, OSCILLATOR_TYPE_PASSIVE);

  rx.setBand(0);                    // imposta banda 87–108 MHz (world wide)

  rx.setRDS(false);                 // disabilito RDS

  rx.setVolume(15);                 // imposto volume al max

  delay(100);

  //rx.setFrequency(9950);          // imposta freq 99.5 MHz
  rx.setFrequency(freq[pos_pot]);   // imposta freq

  rx.setGpio(3, 1); //  Mono/Stereo indicator. When Stereo, the GPIO03 (pin 15 of the RDA5807FP) becomes high
  rx.setAFC(true);  // Sets Automatic Frequency Control

  Serial.print("pot val:");
  Serial.print(pot);
  Serial.print(" posiz:");
  Serial.println(pos_pot);

  sprintf(aux, "\nFreq: %u MHz", rx.getFrequency());
  Serial.println(aux);

  t_lett = millis();                // azzero millis
}

void loop()
{
  if ((millis() - t_lett) >= 10)      // ogni 10 ms leggo potenz
  {
    pot = analogRead(POT);            // lettura del potenz
    pos_po = (map(pot, 0, 1023, 0, 255));  // faccio scala

    if (conta_pot < 10)               // fino a che non ho 10 letture
    { // conta fino a 9, ma finisce col contatore a 10
      pos[conta_pot] = pos_po;        // salvo la lettura
      conta_pot ++;                   // aumento il contatore di 1
    }

    if (conta_pot >= 10)              // quando ho 10 letture
    {
      totale = 0;                     // azzero totale
      for (byte i = 0; i < 10; i++)   // sommo tutte le 10 letture
      {
        totale = totale + pos[i];     // sommo tutte le 10 letture
      }
      pos_pot = totale / 10;          // divido per 10 le letture (media)
      conta_pot = 0;                  // azzero contatore
    }

    t_lett = millis();                // azzero millis
  }

  if (pos_pot != old_pos_pot)
  {
    if (pos_pot > 210)
    {
      rx.setFrequency(10800);   // imposta freq massima
    }
    else
    {
      rx.setFrequency(freq[pos_pot]);   // imposta freq
      Serial.print("posiz:");
      Serial.print(pos_pot);
      Serial.print(" ");
    }
    old_pos_pot = pos_pot;              // salvo il valore
    sprintf(aux, "\nFreq: %u MHz", rx.getFrequency());
    Serial.println(aux);
  }



  if (Serial.available() > 0)
  {
    char key = Serial.read();
    switch (key)
    {
      case 'S':
        rx.seek(RDA_SEEK_WRAP, RDA_SEEK_UP, showStatus);  // cerca canale sup che riceve
        break;
      case 's':
        rx.seek(RDA_SEEK_WRAP, RDA_SEEK_DOWN, showStatus);// cerca canale inf che riceve
        break;
      default:
        break;
    }
    showStatus();
  }
} // OOOOOOOOO FINE LOOP OOOOOOOOOOO

void showStatus()
{
  sprintf(aux, "\nFreq: %u MHz", rx.getFrequency());
  Serial.print(aux);
  status_elapsed = millis();
}

Se posso, lascia perdere il potenziometro ed usa un rotary encoder.

Ti eviti tutti sti sbattimenti legati alle letture analogiche soprattutto nel caso in cui ti serva una certa precisione (210 "posizioni" su 1024 significa un range di +/-2 step attorno al valore che vorresti bloccare, ossia di fatto ti servirebbe una precisione dello 0.4%, che con il convertitore onboard non puoi avere.

Ciao docdoc,
concordo con te per l'encoder, lo avevo già fatto così, ma ho sostanzialmente 2 problemi, il primo è che gli encoder vanno a scatti, io volevo montare questo modulo dentro una radio a valvole, che non ha FM, e collegandomi al filo della manopola originale si sentono gli scatti, (potrei provare a modificare l'encoder); secondo problema è che non rimane la frequenza dove viene lasciata, se viene mossa la manopola a radio spenta etc. Avevo pensato al potenziometro perchè era un modo per avere un valore assoluto di posizione...

Hm capisco... Avevi parlato solo di "radio FM" quindi non avevo tutte le informazioni necessarie.

Non so se ci siano encoder che non fanno scatti "meccanici", ma credo che per fare ciò che vuoi sia eventualmente il "prezzo" da pagare. :wink:

Si, visto che parli di una vecchia radio ci sarà una puleggia che muove un indicatore per cui la posizione è "assoluta". A quel punto allora a mio avviso tutto dipende da com'è fatto l'interno della radio ossia l'insieme manopola-puleggia-indicatore, perché teoricamente (penso) tu vorresti poter mantenere l'associazione tra posizione del potenziometro e quella dell'indicatore, il che significa però capire quanti gradi deve poter ruotare l'asse del potenziometro (o quello che sarà) per andare alla posizione minima a quella massima dell'indicatore.

Penso che così "da remoto" sia difficile poterti consigliare, dovremmo poter vedere l'interno della radio e capire cosa si possa provare a fare. Ad esempio lasciare la manopola attuale con il suo asse, e metterci qualcosa per poter rilevare la posizione in quel range.

Poiché colleghi l'encoder a un microcontrollore, ci vuole poco a salvare la frequenza sintonizzata, per esempio dopo 5 secondi senza variazioni.

Ciao Datman,
si, chiaro che posso salvare la frequenza, ma se a radio spenta viene portato il cursore originale da un'altra parte, alla riaccensione non torna più nulla...

Per docdoc, non ho problemi meccanici all'interno della radio, c'è una puleggia che muove un filo, io farei un'altra puleggia calcolando lo spostamento originale, per fare in modo che vengano effettuati i 10 giri del potenziometro di precisione, quello non sarebbe un problema, il range sarebbe 0-vcc.
Cercherò altre idee, se me ne vengono in mente :slight_smile:

La tensione all'estremità del potenziometro è la stessa di riferimento del convertitore?

Si, certo, utilizzo esp32 che funziona a 3,3V e alimento il potenziometro con quella tensione

Eh si, appunto, il problema è la precisione del convertitore ADC di Arduino che non è così elevata.
Se vuoi mantenere l'idea del poteziometro, oltre ad usare un cavo schermato per collegare il potenziometro (per evitare che le fluttuazioni siano anche dovute ad interferenze EM) dovresti prevedere una qualche sorta di "isteresi" negli spostamenti, facendo letture consecutive. Provo a spiegare quello che intendo (spero di essere abbastanza chiaro :wink: ).

Se ad esempio per una certa frequenza FM considero come "centrale" il valore analogico 240 e per la successiva 244 (assumo per difetto che siano distanziate di un valore pari a 4, visto che 1024/210 fa 4.8), oltre a considerare solo il valore medio delle ultime "n" letture (es. 6) distanziate di qualche decina di millisecondi (es. 20, quindi 120 millisecondi), quando leggi il potenziometro dovresti cambiare canale solo se il nuovo valore (parlo sempre la media) supera una prima volta 244 e poi resta per almeno altre "x" letture (es. 4, per cui avresti un "ritardo" totale di di 480 ms, mezzo secondo...) entro quei limiti (se le frequenze sono distanziate di un valore analogico 4, fare +/- 2 attorno al "centro", quindi in quel caso tra 242 e 246).
Ovviamente tutto questo va verificato "sul campo" visto che le variabili aleatorie in gioco non consentono di fare considerazioni più centrate... Ma mi intriga, mgari se ho tempo una sera prendo un ESP e faccio due prove anche io :wink:

Alternative, per usare l'encoder? Quando cambi canale lo memorizzi in EEPROM così quando lo spegni e riaccendi leggi il valore e consideri quello come base.
Se vuoi evitare che qualcuno con la radio spenta sposti il cursore, aggiungi uno stepper e due fine corsa, e quando accendi fai la "calibrazione" andando da un lato all'altro tra i due switch, e poi lo metti nuovamente alla posizione che avevi memorizzato.

Pensa che effetto su chi vede una vecchia radio che si "muove" da sola... :stuck_out_tongue_closed_eyes: :rofl:

Per ora non mi viene in mente altro... :wink:

Eccomi,
allora, mi spiego un po' meglio, altrimenti giustamente non sapete cosa voglio fare...
Ho qualche radio a valvole perchè mi piacciono e le riparo per qualche amico, la voce si è sparsa e ora qualcuno mi chiede di montare questo modulino per poterle riutilizzare in FM; perciò stavo pensando a questa cosa, ma che sia anche veloce da installare e non invasiva, certe persone vogliono mantenere l'originalità della cosa.
L'idea dello stepper mi è già balenata, ma posso farlo solo sulla mia radio personale, e fino lì va bene :smiley:
Ci sono già in commercio dei piccoli kit per questo scopo, ma costicchiano (circa 70€); questi utilizzano la banda originale in AM onde corte come impostazione della frequenza, ovvero "leggono" la frequenza dove è sintonizzata la radio, la elaborano col processore per impostare quella in FM; a me sembrava complicato e stavo provando questa altra strada.

Tornando al topic, posso tranquillamente togliere il MAP e dividere per 4 il valore letto in ADC, quindi avere 0-255 (io mi fermerò a 210 che mi serve, e i passi successivi manterranno la freq alla massima dei 108 Mhz).
Avevo pensato a qualcosa di isteresi anche io, ma non mi è ancora venuto in mente come farlo, ora mi rileggo meglio il tuo post sopra e i prossimi giorni ci provo.

Ho letto in giro che ESP32 ha problemini di precisione sugli ADC, contrariamente ad arduino, per ora avevo scelto ESP32 per comodità di lavorare a TTL3.3 per evitare shift level etc; ma potrei fare anche una prova con arduino o attiny.

Beh intanto se ti interessano 210 valori, puoi comunque riportare nel range 0-209 semplicemente dividendo per 1024/210 (che comunque poi rimappo per evitare che il 210 sia solo quando metti esattamente a fine corsa il potenziometro):

  int pos_pot = pot/(1024.0/210);

Per l'isteresi ovviamente è un po' più complicato, magari intanto prova questa mappatura poi caso mai vedremo.

Hm, non ho un ESP32 sottomano (in realtà ne ho usato solo uno finora) e non ho avuto necessità di usare ADC, quindi non so dirti, ma leggevo di un workaround, vedi se questo ti può aiutare in qualche modo:

PS: in alternativa potresti valutare l'uso di questo:

Ciao,
tutte ottime soluzioni, anche se vado ad aggiungere hardware, allora tanto vale cambiare processore...
Farò ancora qualche prova col programma, una sorta di isteresi, se non arrivo a una cosa decente provo a utilizzare arduino nano o micro, e dovrò aggiungere uno shift level.
Molto interessante quella soluzione su ESP32 per migliorare ADC, col tempo farò anche qualche prova, potrebbe essere utile :slight_smile:

Per ora sospendo il progetto perchè devo trovare un modulo radio FM che mi consenta la variazione continua della frequenza, perchè con questo si verifica un "mute" al cambio di frequenza e quando si gira la manopola non si sente nulla, a meno chè di girare molto lentamente a piccoli step, e la cosa è fastidiosa; anche perchè nella radio dove verrà installato visto che non c'è una scala di riferimento, si dovrà andare a orecchio, quindi mentre si cerca la frequenza, va sentita :smiley:
Grazie per gli spunti :slight_smile:

Ciao a tutti :slight_smile:
ho ripreso questo progetto, ho trovato soluzione al "mute" del modulo, quindi posso andare avanti...
Non ho trovato però soluzione alla precisione di lettura dell'adc, ho spostato tutto il progetto su arduino nano mini, visto la risaputa imprecisione dell'adc di esp32, ma non ho avuto miglioramenti; ho fatto diverse letture per fare una media e anche un filtro per i valori, ma non arrivo ad avere un valore stabile.
Non ho mai utilizzato un convertitore ADC esterno, come diceva docdoc, con questi si avrebbe una lettura ferma? Proverò a fare questo acquisto :slight_smile:

Non è del tutto vero (anzi per nulla se vogliamo puntualizzare visto che è da 12 bit contro i 10 degli Arduino "classici"), ma l'ADC dell'ESP32 è sicuramente più suscettibile a rumore specialmente se non ben configurato come accade usando solo le API Arduino (l'istruzione analogRead() in altre parole).

Io mi sono imbattuto in un problema simile per controllare la posizione di uno stepper con un potenziometro ed ho ottenuto un buon risultato usando direttamente le API ESP-IDF.

Ho usato un task perché la lettura dell'ADC non doveva interferire con il resto del codice, ma si può fare anche senza ovviamente.

Ciao,
in rete ho trovato questa cosa da diverse parti, ok la precisione a 12 bit, ma mi sembrava di aver capito che era un discorso di vref, comunque ho lo stesso problema con arduino :smiley:
Attualmente ho il potenziometro volante direttamente sui pin, non credo di prendere disturbi, ho messo anche res + cond come filtro.
Puoi indicarmi un esempio di lettura quindi? Hai una parte di codice?
Grazie :slight_smile:

Per ESP32 ti ho messo il link ad un progetto wokwi nel post precedente.

Per Arduino mini non ho nulla di pronto, ma online trovi moltissimo materiale.

Rieccomi,
scusa cotestatnt non avevo visto che avevi linkato, sto guardando :wink:
Grazie

Buongiorno,
devo ricredermi da quanto ho detto, in effetti con questo codice ho notato una precisione notevole, il valore a potenziometro fermo rimane stabile (ho messo un cond da 4,7 uF e una resistenza come filtro hardware) mi sembra veramente ottimo.
Se posso chiederti, come posso utilizzare nel loop il valore letto? Non sono a questi livelli di programmazione... Posso fare classico if per verificare se il nuovo valore è diverso dal vecchio? Non vorrei interferisse con la funzione che già lo fa.
Grazie :smiley: