Eccoci alla terza puntata del micro gear motor con encoder quella in cui leggerai lo sketch che ho utilizzato per controllarlo
ed in particolare quella in cui vedrai come ho deciso di utilizzare l’encoder per raggiungere la posizione corretta.
Ti anticipo che il lavoro di “fine tuning”, ossia di regolazione puntuale della posizione è stata frutto di numerosissime prove.
Infatti sia per la tipologia di costruzione dell’encoder sia per la risposta del motore ai segnali provenineti da Arduino è stata necessaria una lunga attività di test per ottenere una posizione stabile.
Puoi vedere il risultato nel video che ho pubblicato nel precedente articolo e che ti riporto:
Articoli precedenti dedicati al progetto
Se non hai seguito i precedenti articoli del blog dedicati a questo motore puoi farlo seguendo i link, trovi in questi articoli anche la lista dei materiali necessari per la realizzazione del progetto:
Iniziamo dallo sketch analizzandolo nelle sue parti principali.
Lo sketch del micro gear motor encoder
Per controllare il micro gear motor encoder e leggere i valori dell’encoder montato sull’alberino del motore puoi utilizzare lo sketch seguente:
#define motorA 9 #define motorB 10 int encoderPin1 = 2; int encoderPin2 = 3; volatile int lastEncoded = 0; volatile long encoderValue = 0; volatile long correctEncoderValue =0; long lastencoderValue = 0; int lastMSB = 0; int lastLSB = 0; // 2546 - 1/4 // 5312 - 1/2 // 8090 - 3/4 // 10756 - 1 int oneTurnEncoder = 10756; int oneTurnOffset = 480; /**** Buttons control value * Butt1: 512 * Butt2: 682 * Butt3: 768 * Butt4: 820 * Butt5: 853 */ const int buttonA = 512; const int buttonB = 682; const int buttonC = 768; const int buttonD = 820; const int buttonE = 853; const byte thresholdButtons = 15; void setup() { Serial.begin (115200); pinMode(encoderPin1, INPUT_PULLUP); pinMode(encoderPin2, INPUT_PULLUP); attachInterrupt(0, updateEncoder, CHANGE); attachInterrupt(1, updateEncoder, CHANGE); pinMode(motorA,OUTPUT); pinMode(motorB,OUTPUT); pinMode(A0,INPUT); motorOFF(); } void loop() { int buttonPressed = buttonControl( analogRead(A0) ); Serial.print( " Butt Pressed: " ); Serial.print( buttonPressed ); Serial.print( " - " ); switch ( buttonPressed ) { case 1: Serial.print( "1" ); motorCC(255); delay( 10 ); motorOFF(); break; case 2: Serial.print( "2" ); motorCC(160); delay( 500 ); motorOFF(); break; case 3: Serial.print( "3" ); encoderValue = 0; break; case 4: checkEnc( (oneTurnEncoder-oneTurnOffset) ); break; case 5: checkEnc( (oneTurnEncoder*2)-oneTurnOffset ); break; default: Serial.print( "0" ); motorOFF(); } //motorCC(); delay( 1000 ); motorOFF(); delay( 2000 ); //checkEnc( oneTurnEncoder * numberOfTurn ); //motorCCW(); delay( 1 ); Serial.print(" - "); Serial.print(encoderValue); Serial.println(); delay( 500 ); } /* */ void checkEnc(long int valToEncCheck ) { correctEncoderValue = encoderValue; while ( encoderValue <= (correctEncoderValue+valToEncCheck) ) { motorCC(100); } motorOFF(); } /* */ void motorCC(int speedMotor) { analogWrite( motorA, speedMotor ); digitalWrite( motorB, LOW ); } void motorCCW(int speedMotor) { digitalWrite( motorA, LOW ); analogWrite( motorB, speedMotor ); } void motorOFF() { digitalWrite( motorA, LOW ); digitalWrite( motorB, LOW ); } int buttonControl( int inputVal ) { int minButtA = (buttonA - thresholdButtons); int maxButtA = (buttonA + thresholdButtons); int minButtB = (buttonB - thresholdButtons); int maxButtB = (buttonB + thresholdButtons); int minButtC = (buttonC - thresholdButtons); int maxButtC = (buttonC + thresholdButtons); int minButtD = (buttonD - thresholdButtons); int maxButtD = (buttonD + thresholdButtons); int minButtE = (buttonE - thresholdButtons); int maxButtE = (buttonE + thresholdButtons); Serial.print( inputVal ); Serial.print( " - " ); if ( minButtA <= inputVal && inputVal <= maxButtA ) return 1; if ( minButtB <= inputVal && inputVal <= maxButtB ) return 2; if ( minButtC <= inputVal && inputVal <= maxButtC ) return 3; if ( minButtD <= inputVal && inputVal <= maxButtD ) return 4; if ( minButtE <= inputVal && inputVal <= maxButtE ) return 5; return 0; } void updateEncoder(){ int MSB = digitalRead(encoderPin1); //MSB = most significant bit int LSB = digitalRead(encoderPin2); //LSB = least significant bit int encoded = (MSB << 1) |LSB; //converting the 2 pin value to single number int sum = (lastEncoded << 2) | encoded; //adding it to the previous encoded value if(sum == 0b1101 || sum == 0b0100 || sum == 0b0010 || sum == 0b1011) encoderValue += 1; if(sum == 0b1110 || sum == 0b0111 || sum == 0b0001 || sum == 0b1000) encoderValue -= 1; lastEncoded = encoded; //store this value for next time }
iniziando dalle prime linee definisci i pin utilizzati per collegare il micro gear motor encoder all’arduino leonardo e subito dopo, linee 006-013, imposta le variavili in cui memorizzerai i valori letti dall’encoder;
linee 015-018: ho segnato alcuni valori, come commento, letti nei diversi test che ho eseguito con il motore in mio possesso;
linee 020-021: definisci due variabili oneTurnEncoder e oneTurnOffset che ti serviranno per controllare il numero di impulsi letti ad ogni giro dell’encoder ed un offset, ossia un valore di errore, che mediamente l’encoder mostra ad ogni giro.
Vedrai nelle fasi successive dello sketch come questo secondo valore sarà utile.
Linee 030-036: per ciascuno dei pulsanti presenti sulla breadboard segna il valore letto alla pressione di ciascuno.
Per farlo usa un semplice sketch di esempio per la lettura dei valori analogici e scrittura sul monitor seriale.
Linea 036: definisci una soglia utile alla valutazione dei valori letti alla pressione dei pulsanti, anche di questo valore capirai meglio nelle righe successive;
linee 040-043: imposta la modalità di uso dei pin per l’encoder indicando di richiamare la funzione updateEncoder() ad ogni cambio ( CHANGE ) di stato dei pin a cui l’encoder è connesso.
linee 045-046: imposta i pin relativi al controllo del motore in modalità OUTPUT;
linea 047: imposta il pin A0 in modalità di INPUT;
linea 048: richiami la funzione motorOFF() con cui fermi il motore in fase di setup dello sketch;
Passiamo alla funzione loop()
la linea 052: legge il valore restituito dalla funzione buttonControl() e lo memorizza nella variabile buttonPressed;
la funzione buttonControl(), che leggerai tra qualche riga si occupa di leggere sul pin A0 il valore e convertirlo in un numero da 1 a 5 in funzione del pulsante che hai premuto.
linee 054-056: inviano al monitor seriale il valore restituito dalla funzione buttonControl() per consentirti di visualizzarlo;
linea 058: in funzione del valore letto e memorizzato nella variabile buttonPressed esegue il “case” corrispondente;
case 1:
se il valore letto sul pin analogico è convertito in 1 vuol dire che hai premuto il primo pulsante, quello più in basso e sarà richiamata la funzione motorCC() passandole come valore di velocità il massimo ( 255 ), linea 061;
la rotazione avverrà per 10 millisecondi, linea 062;
e successivamente il motore sarà fermato richiamando la funzione motorOFF();
case 2:
in caso di pressione del secondo pulsante il motore sarà avviato in rotazione per 500 millisecondi, circa 1/2 secondo, ma con una velocità di 160 ( poco più della metà, che corrisponde a 127 );
per poi essere fermato con la funzione motorOFF();
case 3:
il caso 3, ossia pressione del terzo pulsante, serve solo a resettare il contatore dell’encoder ed è molto utile in fase di test e analisi delle letture per capire cosa l’encoder stia leggendo ad ogni giro;
case 4:
la pressione del pulsnte 4 scatena la funzione checkEnc() passandole il valore di encoder da leggere.
A questo punto vanno fatte alcune considerazioni:
- il valore di lettura che gli passi è una somma ( oneTurnEncoder – oneTurnOffset ) ossia il valore definito alla linea 020, corrispondente al numero di letture che l’encoder compie per ogni giro del motore sottratto del valore di “offset” in questo caso di errore che ho rilevato per ogni giro e che puoi impostare alla linea 021.
- manca il richiamo alla funzione motorOFF() in quanto si preoccuperà la checkEnc() di fermare il motore al raggiungimento del valore impostato, ossia un giro;
case 5:
é simile al caso precedente ma con la variante del valore passato che sarà dato dalla moltiplicazione per 2 del valore relativo ad un giro sottratto del singolo valore di offset;
tale accorgimento è legato al fatto che l’offset è un discostamento dal reale valore letto per ciascun giro solo in fase di stop.
Quindi per giri multipli del primo non deve essere calcolato per non falsare la lettura.
Questo spiega anche perché siano state definite due variabili, una per il valore raggiunto dall’encoder per un singolo giro ed una seconda di offset o errore.
default:
quando alcun pulsante è premuto non fai altro che tener fermo il motore con la funzione motorOFF();
La successive funzioni del micro gear motor encoder sono quelle richiamate nella loop() ed eseguite per ciascun caso visto in precedenza.
Iniziamo dalla funzione checkEnc()
il suo scopo è controllare il valore letto sull’encoder e portare il motore a compiere tante rotazioni quante richieste dal parametro valToEncCheck:
linea 104: imposta ul valore della variabile correctEncoderValue come il valore attuale letto sull’encoder prima di eseguire alcuna rotazione;
linea 105: imposta un ciclo while fino a quando il valore letto sull’encoder raggiunge la somma del valore attuale più il valore desiderato.
Questo tipo di operazione ti garantisce che da qualsiasi valore iniziale parta l’encoder raggiungerai il valore desiderato.
linea 106: all’interno del ciclo while richiama la funzione motorCC() passandole il valore di velocità 100 ( leggermente inferiore alla 1/2 della velocità del motore );
linea 108: al termine del ciclo while ferma il motore richiamando la funzione motorOFF();
La funzione motorCC()
la funzione motorCC del micro gear motor encoder si occupa solo di far avanzare il motore alla velocità che gli avrai specificato.
Per ottenere tale movimento imposti la linea 113 in modo che invii al pin A del motore il valore di velocità speedMotor e la linea 114 il valore LOW ( 0 ) fisso.
Tale combinazione farà muovere il motore in senso orario alla velocità desiderata.
La funzione motorCCW()
è fondamentalmente identica alla precedente ma ha lo scopo di far ruotare il motore in senso antiorario invertendo il pin a cui passi LOW e quello a cui passi la velocità desiderata.
In questo sketch non è utilizzata ed ha il solo scopo di completare lo sketch del micro gear motor encoder.
La funzione motorOFF()
l’hai incontrata spesso nella descrizione della loop(), e non solo, il suo scopo è fermare il motore e per farlo imposta a LOW il valore di entrambi i pin.
Questa impostazione permette al driver di non inviare alcun segnale al motore che si ferma.
La funzione buttonControl()
come descritto in precedenza serve a valutare quale pulsante hai premuto e restituire un valore da 1 a 5 corrispondente al pulsante premuto.
Quando nessun pulsante viene premuto il valore restituito è 0 ( zero ).
Il passo principale risiede nelle linee 128-132: in cui per ciascuna riga sono definiti due valori:
- minButtX: rappresenta il valore minimo entro cui il bottone X risulta premuto; è calcolato come la differenza tra il valore di default previsto per il pulsante e la soglia;
- maxButtX: rappresenta il valore massimo entro cui il bottone X risulta premuto; è calcolato come la somma tra il valore di default previsto per il pulsante e la soglia;
avrai già capito che X è solo indicativo in questa spiegazione e che corrisponde alle lettere da A a E una per ciascun pulsante.
le linee 137-141 si occupano di fare la valutazione vera e propria: se il valore di inputVal è compreso ad esempio tra il minimo ed il massimo calcolato per il pulsante A restituisce 1, se è B restituisce 2, ecc..
la linea 143 interviene a chiusura della funzione qualora nessun pulsante sia stato premuto e restituisce “0”;
La funzione updateEncoder()
questa funzione non la dettaglierò in questa occasione in quanto è la medesima di cui hai letto in molti altri articoli presenti nel blog e che ti invito a leggere per capire come funziona nello specifico.
Sappi solo che è quella richiamata dal firmware Arduino ad ogni cambio di stato su uno dei due pin dichiarati per l’encoder alle linee 042-043;
Ora che hai chiaro come funziona lo sketch per il controllo del micro gear motor encoder potrai facimente adattarlo alle tue esigenze e controllare questo motore sia direttamente con le funzioni motorCC, motorCCW e motorOFF sia in modo indiretto indicandogli il numero di giri che desideri compia con la funzione checkEnc.