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**************************************************************
*************************************************************************************************************/

2 comentários:

  1. keep getting error in compiling

    stray'(\302)' in program

    help

    ResponderExcluir
    Respostas
    1. In English
      This error can happen when you copy the code from some website and something comes along, some html instruction for example.

      Copy the code and paste it into some notepad, then copy it again from the notepad and then paste it into the Arduino ide. This will make the code clean for the Arduino.

      Em Portugues
      Esse erro pode acontecer quando você copia o codigo de algum site e vem alguma coisa junto, alguma instrução de html por exemplo.

      Copie o codigo e cole em algum bloco de notas, depois copie novamente do bloco de notas e dai cole na ide do arduino. Isso fara que o codigo va limpo para o arduino.

      Excluir