Esperimenti Arduino: encoder

Come rapida appendice al mio post precedente "Costruire un tastierino numerico con Arduino: prima parte" ho voluto provare l'esperimento di aggiungere un encoder rotativo al mio prototipo.

Un encoder rotativo è sostanzialmente una manopola che trasmette al microcontrollore degli impulsi che descrivono la sua rotazione a passi discreti (un "click" alla volta). Rispetto ad un potenziometro ha il vantaggio di consentire la rotazione infinita in entrambe le direzioni, oltre al discorso dei passi discreti citato prima. E, come valore aggiunto, spesso gli encoder contengono anche un pulsante (pressione verticale sul perno).

L'obiettivo non mi è ancora del tutto chiaro, sto sperimentando. Un possibile utilizzo molto interessante è per lo scrolling: anziché premere le frecce su&giù per scorrere lungo un listato posso girare la manopola, e dietro le quinte arduino trasmette al computer l'equivalente delle frecce direzionali. Altri possibili utilizzi sono le funzioni multimediali (volume, più pausa/play sul pulsante).

A livello pratico, un encoder presenta due uscite (escludendo il pulsante).

Durante la rotazione, ad ogni click queste due uscite vengono messe a GND per alcuni millisecondi e poi riportate a flottanti (devono essere configurati i pullup su Arduino per fissare a VCC il valore a riposo). I segnali generati dai due pin sono sfasati, e la fase dipende dal verso di rotazione. Analizzando quindi l'evoluzione dei due segnali si può identificare l'effettuazione di un "click" e la sua direzione.

Fortunatamente non saremo costretti a gestire tutta la questione via software perché c'è già una comoda libreria per Arduino che gestisce l'interfaccia per noi: EncoderButton. La possiamo installare dal package manager di Arduino.

Di seguito un esempio di codice. Nella parte iniziale del programma importiamo la libreria, prepariamo le costanti usate per indicare i pin utilizzati e inizializziamo l'oggetto EncoderButton.

#include <EncoderButton.h>

// Encoder inputs
const byte ENC_A = 0;
const byte ENC_B = 1;
const byte ENC_BUTTON = 21;
EncoderButton eb1(ENC_A, ENC_B, ENC_BUTTON);
volatile int scrollSpeed;

Prima di proseguire predisponiamo i metodi di callback dei due eventi generati dal componente (rotazione e pressione pulsante). In questo esempio per ciascuna rotazione voglio inviare un certo numero di pressioni di freccia su o giù (a seconda del verso di rotazione) usando la libreria Keyboard già usata nella scorsa puntata, e alla pressione del pulsante voglio cambiare la velocità di scroll (ovvero il numero di pressioni di una freccia per ciascun "click" dell'encoder).

void onEb1Encoder(EncoderButton& eb) {
  if (eb.increment() > 0){
    for (int i = 0; i < scrollSpeed; i++){
      Keyboard.press(KEY_UP_ARROW);
      Keyboard.release(KEY_UP_ARROW);
    }
  }
  else {
    for (int i = 0; i < scrollSpeed; i++){
      Keyboard.press(KEY_DOWN_ARROW);
      Keyboard.release(KEY_DOWN_ARROW);
    }
  }
}

void onEncoderButtonClick(EncoderButton& eb){
  if (scrollSpeed == 1){
    scrollSpeed = 5;
  }
  else {
    scrollSpeed = 1;
  }
}

Nel setup configuriamo i pin come pullup e associamo le routine di callback al componente.

void setup(){
  ...
  pinMode(ENC_BUTTON, INPUT_PULLUP);
  pinMode(ENC_A, INPUT_PULLUP);
  pinMode(ENC_B, INPUT_PULLUP);  
  eb1.setEncoderHandler(onEb1Encoder);
  eb1.setClickHandler(onEncoderButtonClick);
  scrollSpeed = 1;
  ...
}

Nel loop principale dobbiamo solo chiamare un metodo per consentire l'aggiornamento continuo dello stato del componente e la chiamata agli eventuali eventi:

void loop(){
  ...
  eb1.update();
  ...
}

A questo punto non ci resta che collegare l'encoder all'Arduino aggiungendo 5 fili al groviglio, e tutto dovrebbe funzionare.

Alla prossima!