Publicidade:

quarta-feira, 25 de maio de 2016

Arduino - Entrada de Texto com Rotary Encoder

Em algumas aplicações, temos eventualmente a necessidade de que o usuário digite algum texto. A maneira mais comum de se fazer isso é utilizando um teclado. Mas nem sempre essa é uma alternativa viável. Algum tempo atrás mostrei como digitar texto com teclado matricial numérico, como pode ser visto nesse link:  http://fabianoallex.blogspot.com.br/2015/05/arduino-teclado-4x3-texto-lcd.html e nesse outro, uma versão parecida, mas utilizando um controle remoto: http://fabianoallex.blogspot.com.br/2015/05/arduino-digitar-texto-com-controle.html. Porém, as vezes, nem uma dessas soluções é a mais viável de se utilizar, caso a entrada de dados seja algo muito eventual, ou ainda quando queremos algo que não ocupe tanto espaço.

Pensando nisso, aproveitei que estava com a mão na massa com o meu último artigo sobre Rotary Encoders, e desenvolvi uma classe pra escrever texto em um display LCD através de um Rotary Encoder.

Para maiores detalhes sobre o Rotary Encoder veja o artigo completo aqui: http://fabianoallex.blogspot.com.br/2016/05/arduino-rotary-encoder.html

Vídeo:



Código-Fonte:

/*
Fabiano A. Arndt - 2016
www.youtube.com/user/fabianoallex
www.facebook.com/dicasarduino
fabianoallex@gmail.com
*/
  
#include <LiquidCrystal.h>

/*************************************************************************************************************
************************************CLASSE ROTARY ENCODER*****************************************************
*************************************************************************************************************/
#define ROTARY_NO_BUTTON     255
   
struct RotaryEncoderLimits{
  int min;
  int max;
};
   
class RotaryEncoder {
  private:
    byte _pin_clk;
    byte _pin_dt;
    byte _pin_sw;
    volatile byte _num_results;
    volatile int _result;
    volatile int * _results;
    byte _index_result;
    RotaryEncoderLimits * _limits;
       
    boolean _a;
    boolean _b;
  public:
    RotaryEncoder(byte pin_clk, byte pin_dt, byte pin_sw = ROTARY_NO_BUTTON, byte num_results=1, RotaryEncoderLimits * limits=0){   //parametro do botao opcional
      _pin_clk = pin_clk;
      _pin_dt = pin_dt;
      _pin_sw = pin_sw;
      pinMode(_pin_clk, INPUT);
      pinMode(_pin_dt, INPUT);
      if (_pin_sw != ROTARY_NO_BUTTON){ 
        pinMode(_pin_sw, INPUT); 
        digitalWrite(_pin_sw, HIGH);
      }
      if (num_results == 0) { num_results = 1; }
      _num_results = num_results;
      _results = new int[_num_results];
      for (int i; i<_num_results; i++){ _results[i] = (limits) ? limits[i].min : 0; }
      _index_result = 0;
      _limits = limits;
      _a = false;
      _b = false;
    }
    byte getIndex() { return _index_result; }
    void next()     { _index_result++; if (_index_result >= _num_results) { _index_result = 0; } } 
    void update_a() {
      _result = 0;
      delay (1);
      if( digitalRead(_pin_clk) != _a ) { 
        _a = !_a;
        if ( _a && !_b ) { _result = -1; }
      }
      if (_results[_index_result]+_result >= _limits[_index_result].min && 
          _results[_index_result]+_result <= _limits[_index_result].max ) {
        _results[_index_result] += _result;
      }
    }
    void update_b() {
      _result = 0;
      delay (1);
      if( digitalRead(_pin_dt) != _b ) {  
        _b = !_b;
        if ( _b && !_a ) { _result = +1; }
      }
      if (_results[_index_result]+_result >= _limits[_index_result].min && 
          _results[_index_result]+_result <= _limits[_index_result].max ) {
        _results[_index_result] += _result;
      }
    }
    int read(){ return _result; }                                        //retorn -1, 0 ou 1.
    int getValue(int index=-1) {                                         //retorna o valor da variável corrente ou a passada como parametro
      if (index < 0 ){ return _results[_index_result]; }
      return _results[index];
    }
    void setValue(int value)           { _results[_index_result] = value; }         //caso a variável inicializa em determinado valor diferente de zero, utilizar esse método.
    void setValue(int index, int value){ _results[index] = value; }         //caso a variável inicializa em determinado valor diferente de zero, utilizar esse método.
    int buttonRead(){ return (_pin_sw == ROTARY_NO_BUTTON) ? LOW : digitalRead(_pin_sw); }
};
/*************************************************************************************************************
************************************FIM CLASSE ROTARY ENCODER*************************************************
*************************************************************************************************************/

/*************************************************************************************************************
************************************CLASSE ROTARY KEYBOARD****************************************************
*************************************************************************************************************/
const char teclas[] = {"abcdefghijklmnopqrstuvxwyz 0123456789"};  //caracteres a serem escolhidos
const unsigned long time_char = 1200;  //1200 milissegundos pra desconsiderar a ultima tecla
 
class RotaryKeyBoard {
  private:
    unsigned long   _millis_last_char;
    char            _last_char;
    String          _palavra;
    RotaryEncoder * _re;
    
    void _set_last_char(char c, int ind_palavra){
      if ( ind_palavra == 1 && _last_char != '\0' ) { _palavra += _last_char; }
      _last_char = c;
      _millis_last_char = millis();
    }    
    void _add(char c) { 
      if ( is_timing() ) {
        _set_last_char(  (teclas[_re->getValue(0)] == '\0') ? _last_char = teclas[0] : _last_char = teclas[_re->getValue(0)] , 0 );
        return ;
      }
      _set_last_char (c, 1); 
    }
  public:
    RotaryKeyBoard(RotaryEncoder * re){
      _re = re;
      _millis_last_char = millis();
      _last_char = '\0';
      _palavra = "";
      update();
      backspace();
      _re->setValue(0, 0);
    }
    
    char get_last_char() { return _last_char; }
    int is_timing()      { return ( (millis() - time_char) < _millis_last_char );  }
    String get_palavra() { return (_last_char) ? _palavra + _last_char : _palavra; }
    
    void backspace(){
      if (_palavra.length() >= 1){
        _last_char = _palavra[_palavra.length()-1];
        _palavra   = _palavra.substring(0, _palavra.length()-1);
      } else {
        _last_char = '\0';
      }
    }
    void update() {
      static char tecla_anterior = '\0';
      char tecla = teclas[_re->getValue(0)];
      if (tecla != tecla_anterior){
        if (tecla) { _add(tecla); }
      }
      tecla_anterior = tecla;
    }
};
/*************************************************************************************************************
************************************FIM CLASSE ROTARY KEYBOARD************************************************
*************************************************************************************************************/

/*************************************************************************************************************
************************************DECLARACOES DOS OBJETOS***************************************************
*************************************************************************************************************/
LiquidCrystal       lcd(12, 11, 10, 9, 8, 7);
RotaryEncoderLimits lim[] = { {0, sizeof(teclas)-1 }  };  //limites máximos e mínimos que as variaveis podem atingir
RotaryEncoder       re(A0, A1, 4, 1, lim);  //pino clk, pino dt, pino sw, variaveis
RotaryKeyBoard      teclado(&re);
/*************************************************************************************************************
************************************FIM DECLARACOES DOS OBJETOS***********************************************
*************************************************************************************************************/
 
/*************************************************************************************************************
************************************TRATAMENTO DAS INTERRUPÇÕES***********************************************
*************************************************************************************************************/
ISR(PCINT1_vect) {
  volatile static byte lastVal_a0 = LOW;
  volatile static byte lastVal_a1 = LOW;
  byte val_a0 = digitalRead(A0);
  byte val_a1 = digitalRead(A1);
  if (lastVal_a0 != val_a0){ re.update_a(); lastVal_a0 = val_a0; }
  if (lastVal_a1 != val_a1){ re.update_b(); lastVal_a1 = val_a1; }
}

void setup_interrupts(){
  pinMode(A0,INPUT);   // set Pin as Input (default)
  digitalWrite(A0,HIGH);  // enable pullup resistor
  pinMode(A1,INPUT);   // set Pin as Input (default)
  digitalWrite(A1,HIGH);  // enable pullup resistor
  cli();
  PCICR |= 0b00000010; // habilita a porta C - Pin Change Interrupts
  PCMSK1 |= 0b00000011; // habilita interrupção da porta c nos pinos: PCINT8 (A0) e PCINT9(A1)
  sei();
}
/*************************************************************************************************************
************************************FIM TRATAMENTO DAS INTERRUPÇÕES*******************************************
*************************************************************************************************************/

/*************************************************************************************************************
************************************SETUP / LOOP**************************************************************
*************************************************************************************************************/
void setup() {
  setup_interrupts();
  lcd.begin(16, 2);
  Serial.begin(9600);
}
 
void loop() {
  teclado.update();
  
  lcd.clear();
  lcd.setCursor(0, 0); 
  lcd.print(teclado.get_palavra());
  lcd.cursor();
  lcd.setCursor(teclado.get_palavra().length() - (teclado.is_timing() ? 1 : 0 ), 0);
  
  //controla o click do botao do enconder
  static byte b = HIGH;                                           //pra ler apenas uma vez o botao ao pressionar
  if( re.buttonRead() == LOW && b != re.buttonRead() ) {
    teclado.backspace();  
    delay(200);
  }
  b = re.buttonRead();
  
  delay(100);
}
/*************************************************************************************************************
************************************FIM SETUP / LOOP**************************************************************
*************************************************************************************************************/

Nenhum comentário:

Postar um comentário