Publicidade:

Mostrando postagens com marcador Eletrônica. Mostrar todas as postagens
Mostrando postagens com marcador Eletrônica. Mostrar todas as postagens

sábado, 6 de junho de 2015

Arduino - Teclado Matricial com 74HC595 e 74HC165

Esse post é uma continuação de alguns outros posts. Então antes de ver esse post, veja os seguintes posts antes:


http://fabianoallex.blogspot.com.br/2015/06/arduino-74hc595-e-74hc165-juntos.html

e

http://fabianoallex.blogspot.com.br/2015/05/arduino-texto-com-teclado-matricial-4x3.html

A ideia aqui é mostrar algo prático para ser usado com o 74HC595 e o 74HC165 juntos. E como eu já tinha feito um post falando de teclados matriciais, resolve utilizá-lo como exemplo.

Vídeo:




código-fonte:

/*
Criado em 06/06/2015
 Por: 
   Fabiano A. Arndt 
   http://www.youtube.com/fabianoallex
   http://fabianoallex.blogspot.com.br
   fabianoallex@gmail.com
*/

/********************************************************************************************
*******************CLASSE Expansor74HC595_74HC165     INICIO*********************************
*********************************************************************************************/

class Expansor74HC595_74HC165 {
  private:
    int  _pin_clock;
    int  _pin_latch;
    int  _pin_data;
    byte* _pins_out;
    byte* _pins_in;
    int _num_cis_out;
    int _num_cis_in;
  public:
    Expansor74HC595_74HC165(int pin_clock, int pin_latch, int pin_data, int num_cis_out, int num_cis_in){
      _pin_clock = pin_clock;
      _pin_latch = pin_latch;
      _pin_data  = pin_data;
      
      _num_cis_out = num_cis_out;
      _num_cis_in  = num_cis_in;
      
      _pins_out    = new byte[num_cis_out];
      _pins_in     = new byte[num_cis_in];
      
      pinMode(_pin_clock,OUTPUT);
      pinMode(_pin_latch,OUTPUT);
      
      clear();
    }
   
    void clear(){
      for (int i=0; i<_num_cis_out; i++){
        _pins_out[i] = B00000000;
      }
      update();
    }
   
    void update(){
      digitalWrite(_pin_clock, LOW); 
      digitalWrite(_pin_latch, LOW); 
      digitalWrite(_pin_latch, HIGH);
      
      for(int i=max(_num_cis_in, _num_cis_out) * 8 - 1; i>=0;  i-- ) {   //max -->o for vai até o que tiver mais, ou entradas, ou saidas
        int pos = i / 8;
        int pin = 7-(i % 8);  
        
        if (i < _num_cis_in * 8){
          pinMode(_pin_data, INPUT);
          
          if ( digitalRead(_pin_data) ){
            _pins_in[pos] |= (1 << pin);  //set a bit HIGH
          } else { 
            _pins_in[pos] &= ~(1 << pin); //set a bit LOW
          }
        }
        
        if (i < _num_cis_out * 8){
          pinMode(_pin_data, OUTPUT);
          digitalWrite(_pin_data,   (_pins_out[pos] & (1 << pin)) != 0   );
        }        
        digitalWrite(_pin_clock, HIGH); 
        digitalWrite(_pin_clock, LOW); 
      }      
      digitalWrite(_pin_latch, LOW); 
      digitalWrite(_pin_latch, HIGH);
      pinMode(_pin_data, INPUT);
    }
    
    int read(int  pin){
      int pos = pin / 8;
      pin     = 7-(pin % 8);  
      
      if (pos > _num_cis_out) {
        pos = pos - _num_cis_out;
        return (  (_pins_in[pos] & (1 << pin)) != 0   );
      } else {
        return (  (_pins_out[pos] & (1 << pin)) != 0   );
      }
    }
    
    byte readByte(int num_ci) { 
      if (num_ci >= _num_cis_out) {
        num_ci = num_ci - _num_cis_out;
        return _pins_in[num_ci];
      } else {
        return _pins_out[num_ci];
      }
    }
    
    void write(int pin, int value){
      if (pin >= _num_cis_out*8) { return; }
      
      int pos = pin / 8;  //pos -> indica qual ci será atualizado.
      pin     = 7-(pin % 8);
 
      if (pos > _num_cis_out) {
        return; //se estiver tentando escrever um pino de entrada, apenas retorna, sem fazer nada.
      } else {
        if (value){
          _pins_out[pos] |= (1 << pin);  //set a bit HIGH
        } else {
          _pins_out[pos] &= ~(1 << pin); //set a bit LOW
        }
      }
    }
    
    void writeByte(int num_ci, byte b, int first = MSBFIRST) {  
      if (num_ci > _num_cis_out) {
        return; //se estiver tentando escrever um pino de entrada, apenas retorna, sem fazer nada.
      }
      
      if (first == LSBFIRST) {
        byte r=0;
        for(int i=0;i<8;i++) {
          r |= ((b>>i) & 0b1)<<(7-i);
        }
        b = r;
      }
       
      _pins_out[num_ci] = b; 
    } ;
};

/********************************************************************************************
*******************CLASSE Expansor74HC595_74HC165     FIM ***********************************
*********************************************************************************************/

const int PIN_CLOCK = 4; 
const int PIN_LATCH = 7; 
const int PIN_DATA  = 12; 

 
Expansor74HC595_74HC165 * exp1;


/****************************************************************
*********funcao pra ler teclado matricial com 595 e 165**********
*****************************************************************/

#define GET_PIN(num_ci, pos)  num_ci*8+pos
#define col1 GET_PIN(3, 7)   //pino do CI 3 (QUARTO CI) 165 - 31
#define col2 GET_PIN(3, 6)   //pino do CI 3 (QUARTO CI) 165 - 30
#define col3 GET_PIN(3, 5)   //pino do CI 3 (QUARTO CI) 165 - 29
#define lin1 GET_PIN(1, 4)   //pino do CI 1 (SEGUNDO CI) 595 - 12
#define lin2 GET_PIN(1, 3)   //pino do CI 1 (SEGUNDO CI) 595 - 11
#define lin3 GET_PIN(1, 2)   //pino do CI 1 (SEGUNDO CI) 595 - 10
#define lin4 GET_PIN(1, 1)   //pino do CI 1 (SEGUNDO CI) 595 - 9
 
 
char get_tecla(){
  int l[]={lin1, lin2, lin3, lin4}; // Array de 4 posições contendo os 4 pinos de linhas
  int i = 0, k = 0, t = 0;
   
  for (i=0; i<4; i++) {
    exp1->write(lin1, LOW); 
    exp1->write(lin2, LOW);
    exp1->write(lin3, LOW);
    exp1->write(lin4, LOW);
    exp1->write(l[i],HIGH); 
    
    exp1->update();
    exp1->update();
    
    if(exp1->read(col1)) { t = i*3+1; break; }
    if(exp1->read(col2)) { t = i*3+2; break; }
    if(exp1->read(col3)) { t = i*3+3; break; }
  }
   
  if (t > 0 ){
    if (t >= 1 && t<=9){ return char(t+48);   }  //48--> ASCII: o charactere '1' na tabela ascii é 49º item, o '2' é o 50º item e assim por diante
    if (t==10)         { return '*'; }
    if (t==11)         { return '0'; }
    if (t==12)         { return '#'; }
  }
   
  return '\0';
}
  
/****************************************************************
*********fim da funcao pra ler teclado matricial com 595 e 165***
*****************************************************************/
  
  
/****************************************************************
********************************setup e loop**********************
*****************************************************************/

void setup() {
  exp1   = new Expansor74HC595_74HC165(PIN_CLOCK, PIN_LATCH, PIN_DATA, 2, 2);
  
  Serial.begin(9600);
}
  
unsigned long millis_alt = 0;
  
void loop() {
  if (  (millis() - millis_alt) > 1000  ){
    exp1->write(15, !exp1->read(15));
    exp1->update();
    millis_alt = millis();
  }
  
  if (get_tecla() != '\0'){
    Serial.println( get_tecla() );
  }
}

/****************************************************************
********************************fim setup e loop******************
*****************************************************************/

sexta-feira, 5 de junho de 2015

Arduino - 74HC595 e 74HC165 Juntos

Quando se fala em expansão de portas do Arduino, uma das possibilidades é fazer isso através desses dois CIs, o 74HC595 e o 74HC165. Pra quem ainda não os conhece, são dois CIs que com apenas três pinos do Arduino permitem expandir saídas (595) e entradas (165). Tanto um quanto o outro, permitem que sejam conectados vários CIs em cascata, o que dá a possibilidade de infinitas portas.

Datasheets aqui:

http://www.nxp.com/products/logic/shift_registers/74HC595N.html
http://www.nxp.com/products/logic/shift_registers/74HC165N.html

Pra quem quiser ver um exemplo de como expandir apenas saídas, utilizando apenas 74HC595, criei um classe que permite o uso de maneira bem facilitada, que pode ser visto nesse link aqui.

Nesse post de hoje, vou demonstrar como criar uma Classe no Arduino, que permita nos mesmos 3 pinos ligar 595 e 165 juntos, e serem acessados compartilhando os mesmos 3 pinos, Latch, Clock e Data (sim, o pino Data é o mesmo para entradas e saídas :D ).

Como meu foco é mais disponibilizar o código que facilite a programação, não vou entrar muito nos detalhes da implementação, mas vou deixar aqui o link para o site no qual me baseei para chegar nessa solução:

http://homepages.which.net/~paul.hills/Software/ShiftRegister/ShiftRegisterBody.html

A imagem abaixo foi retirado do site acima, e demonstra como fazer a ligação entre os CIs.


Obs: output enable (que é ligado ao pino 13 do 595) não precisa ser ligado ao Arduino. Os Pinos 13 dos 595s devem ser ligado diretamente ao Ground. Outro detalhes é que o pino identificado como Strobe no esquema acima, no arduino é identificado como Latch.

As principais regras ao ligar são:

 - Primeiro devem ser ligados os 595 e após o último 595, ligar os 165.
 - Não ligar os CIs intercalados.
 - Não há limites para quantidades de cada um, podendo ser apenas um ou outro. Mas para ser usado apenas um dos CIs, recomendo utilizar a classe só para 595, que mostrei acima. Em breve criarei uma classe ser usado apenas por 165.
 - Todas as entradas dos CIs 165 devem obrigatoriamente ser ligadas a resistores PULL DOWN ou PULL UP. Caso alguma entrada fique "em aberto" a entrada ficará flutuando, inclusive interferindo nas demais entradas do CI.

Para testar o circuito acima, montei o mesmo em uma protoboard, mas não liguei todas as saídas e entradas, pois deixaria o circuito muito poluído e com muitas conexões, então preferi focar nas ligações de conexões dos próprios componentes.



Mas para testar as saídas liguei apenas um led na protoboard, onde testo uma saída por vez, como poderá ser visto no vídeo que postarei em breve. Nos Caso das entradas, liguei algumas diretamente ao vcc e outras diretamente ao ground.

Identificação dos pinos

No exemplo acima, foi utilizado 4 cis, dois 595 e dois 165.

Cada um dos pinos é identificado por um número, começando de 0 (zero) e indo até a quantidade de CIs multiplicado por 8 menos 1. No nosso exemplo temos 32 pinos adicionais, sendo os pinos de 0 a 15 os pinos de saídas e os pinos de 16 a 31 os pinos de entradas.

Se for ligado um botão no último pino do ultimo CI 165, faremos a leitura da seguinte maneira:

  int valor = exp->read(31);

Já para leitura de todas as entradas de uma única vez de um determinado CI 165, faremos o seguinte:

  byte valor = exp->readByte(3);  //leitura de todos os bits do quarto CI 

Da mesma maneira que é possível fazer a leitura de um bit por vez ou vários de uma vez, é possível ainda fazer a escrita da mesma forma, utilizando os métodos write() e writeByte().   exemplo:

exp->writeByte(1, B00011001); ou 
exp->writeByte(1, B10011000, LSBFIRST); 

exp->read(9, HIGH); ou
exp->read(9, LOW); 

Tanto os métodos write, writeByte, read e readByte, manipulam apenas os dados que estão em cache na classe. Para que as leituras e escritas tenham efeito é necessário chamar o método update(). Ele é quem envia e recebe os dados para os CIs.

o método update() deve ser chamado antes de serem feitas as leituras das entradas, e após serem feitas as escritas nas saídas.

Vídeo:



Código-Fonte:

/*
Criado em 04/06/2015
 Por: 
   Fabiano A. Arndt 
   http://www.youtube.com/fabianoallex
   http://fabianoallex.blogspot.com.br
   fabianoallex@gmail.com
*/

/********************************************************************************************
*******************CLASSE Expansor74HC595_74HC165     INICIO*********************************
*********************************************************************************************/

class Expansor74HC595_74HC165 {
  private:
    int  _pin_clock;
    int  _pin_latch;
    int  _pin_data;
    byte* _pins_out;
    byte* _pins_in;
    int _num_cis_out;
    int _num_cis_in;
  public:
    Expansor74HC595_74HC165(int pin_clock, int pin_latch, int pin_data, int num_cis_out, int num_cis_in){
      _pin_clock = pin_clock;
      _pin_latch = pin_latch;
      _pin_data  = pin_data;
      
      _num_cis_out = num_cis_out;
      _num_cis_in  = num_cis_in;
      
      _pins_out    = new byte[num_cis_out];
      _pins_in     = new byte[num_cis_in];
      
      pinMode(_pin_clock,OUTPUT);
      pinMode(_pin_latch,OUTPUT);
      
      clear();
    }
   
    void clear(){
      for (int i=0; i<_num_cis_out; i++){
        _pins_out[i] = B00000000;
      }
      update();
    }
   
    void update(){
      digitalWrite(_pin_clock, LOW); 
      digitalWrite(_pin_latch, LOW); 
      digitalWrite(_pin_latch, HIGH);
      
      for(int i=max(_num_cis_in, _num_cis_out) * 8 - 1; i>=0;  i-- ) {   //max -->o for vai até o que tiver mais, ou entradas, ou saidas
        int pos = i / 8;
        int pin = 7-(i % 8);  
        
        if (i < _num_cis_in * 8){
          pinMode(_pin_data, INPUT);
          
          if ( digitalRead(_pin_data) ){
            _pins_in[pos] |= (1 << pin);  //set a bit HIGH
          } else { 
            _pins_in[pos] &= ~(1 << pin); //set a bit LOW
          }
        }
        
        if (i < _num_cis_out * 8){
          pinMode(_pin_data, OUTPUT);
          digitalWrite(_pin_data,   (_pins_out[pos] & (1 << pin)) != 0   );
        }        
        digitalWrite(_pin_clock, HIGH); 
        digitalWrite(_pin_clock, LOW); 
      }      
      digitalWrite(_pin_latch, LOW); 
      digitalWrite(_pin_latch, HIGH);
      pinMode(_pin_data, INPUT);
    }
    
    int read(int  pin){
      int pos = pin / 8;
      pin     = 7-(pin % 8);  
      
      if (pos > _num_cis_out) {
        pos = pos - _num_cis_out;
        return (  (_pins_in[pos] & (1 << pin)) != 0   );
      } else {
        return (  (_pins_out[pos] & (1 << pin)) != 0   );
      }
    }
    
    byte readByte(int num_ci) { 
      if (num_ci >= _num_cis_out) {
        num_ci = num_ci - _num_cis_out;
        return _pins_in[num_ci];
      } else {
        return _pins_out[num_ci];
      }
    }
    
    void write(int pin, int value){
      if (pin >= _num_cis_out*8) { return; }
      
      int pos = pin / 8;  //pos -> indica qual ci será atualizado.
      pin     = 7-(pin % 8);
 
      if (pos > _num_cis_out) {
        return; //se estiver tentando escrever um pino de entrada, apenas retorna, sem fazer nada.
      } else {
        if (value){
          _pins_out[pos] |= (1 << pin);  //set a bit HIGH
        } else {
          _pins_out[pos] &= ~(1 << pin); //set a bit LOW
        }
      }
    }
    
    void writeByte(int num_ci, byte b, int first = MSBFIRST) {  
      if (num_ci > _num_cis_out) {
        return; //se estiver tentando escrever um pino de entrada, apenas retorna, sem fazer nada.
      }
      
      if (first == LSBFIRST) {
        byte r=0;
        for(int i=0;i<8;i++) {
          r |= ((b>>i) & 0b1)<<(7-i);
        }
        b = r;
      }
       
      _pins_out[num_ci] = b; 
    } ;
};

/********************************************************************************************
*******************CLASSE Expansor74HC595_74HC165     FIM ***********************************
*********************************************************************************************/


const int PIN_CLOCK = 4; 
const int PIN_LATCH = 7; 
const int PIN_DATA  = 12; 

 
Expansor74HC595_74HC165 * exp1;
  
void setup() {
  exp1   = new Expansor74HC595_74HC165(PIN_CLOCK, PIN_LATCH, PIN_DATA, 2, 2);
  
  Serial.begin(9600);
}
  
void loop() {
  exp1->writeByte(0, B00000000);
  exp1->writeByte(0, B11001101, LSBFIRST);
  
  exp1->write(8, HIGH);
  exp1->write(9, HIGH);
  
  exp1->write(10, HIGH);
  exp1->write(11, HIGH);
  
  exp1->write(14, HIGH);
  exp1->write(15, !exp1->read(15));  //blink
  
  
    
  exp1->update();  //AS ESCRITAS DEVEM SER FEITAS ANTES DO UPDATE. E AS LEITURAS APOS O UPDATE
   
  Serial.println(exp1->readByte(0), BIN);  //LE O PRIMEIRO CI (595)
  Serial.println(exp1->readByte(1), BIN);  //LE O SEGUNDO CI (595)
  Serial.println(exp1->readByte(2), BIN);  //LE O TERCEIRO CI (165)
  Serial.println(exp1->readByte(3), BIN);  //LE O QUARTO CI (165)
  Serial.println("");
  
  delay(1000);
}

terça-feira, 2 de junho de 2015

Arduino - Classe para 74HC595 Corrigida


Nesse post (clique aqui) foi mostrado como criar uma classe pra facilitar o uso do registrador de deslocamento 74HC595 com o Arduino.

Apesar de funcionar perfeitamente, a classe ocupava muita memória para armazenar o estados de todas as saídas.

o código abaixo, foi reescrito de forma a ocupar menos memória e ser mais simples. Alguns métodos foram renomeados.

segue código-fonte:


class Expansor74HC595 {
  private:
    int  _pin_clock;
    int  _pin_latch;
    int  _pin_data;
    int  _num_cis;
    byte* _pins;
    int  _auto_send;
  public:
    Expansor74HC595(int pin_clock, int pin_latch, int pin_data, int num_cis){
      _pin_clock        = pin_clock;
      _pin_latch        = pin_latch;
      _pin_data         = pin_data;
      _num_cis          = num_cis;
      _auto_send        = true;
      _pins             = new byte[_num_cis];

      pinMode(_pin_clock,OUTPUT);
      pinMode(_pin_latch,OUTPUT);
      pinMode(_pin_data, OUTPUT);
      clear();
    };
  
    void startWrite() { 
      _auto_send = false; 
    };
    
    void clear() { 
      for (int i=0; i<_num_cis; i++){ 
        _pins[i] = 0b00000000; 
      }
      
      send();  
    };
  
    int read(int pin)  { 
      if (pin >= _num_cis * 8) {return LOW;} 
      
      int pos = pin / 8;
      pin = pin % 8;

            
      return (_pins[pos] & (1 << pin)) != 0;
    };
  
    byte readByte(int num_ci) { 
      return _pins[num_ci];
    };
   
    void send(){
      
      digitalWrite(_pin_latch, LOW); 
      for(int i=_num_cis-1; i>=0; i--) {  shiftOut(_pin_data, _pin_clock, MSBFIRST, readByte(i) );  }
      digitalWrite(_pin_latch, HIGH);
      _auto_send = true;
      
    };
    
    void writeByte(int num_ci, byte b, int first = MSBFIRST) {  
      
      if (first == MSBFIRST){
        byte reversed;
        for(int i=0;i<8;i++){
          reversed |= ((b>>i) & 0b1)<<(7-i);
        }
        b = reversed;
      }
      
      _pins[num_ci] = b; 
      if (_auto_send) { send(); } 
    } ;
    
    void write(int pin, int value) {
      if (pin >= _num_cis * 8) { return; }
      
      int pos = pin / 8;
      pin     = pin % 8;

      if (value){
        _pins[pos] |= (1 << pin);  //set a bit HIGH
      } else {
        _pins[pos] &= ~(1 << pin); //set a bit LOW
      }

      if (_auto_send) { send(); }
    };
};
 

const int PIN_CLOCK = 2; 
const int PIN_LATCH = 3; 
const int PIN_DATA  = 4; 

Expansor74HC595 *exp1;
 
void setup() {
  exp1   = new Expansor74HC595(PIN_CLOCK, PIN_LATCH, PIN_DATA, 3);
  
  Serial.begin(9600);
}
 
void loop() {
  for (byte b=0; b<=255; b++) {                    //i=0-> B00000000   i=255-> B11111111
    exp1->startWrite();
    exp1->writeByte(0, b);
    exp1->writeByte(1, b, LSBFIRST);
    exp1->write(23, HIGH);
    exp1->send();
  
    delay(500);
  }
  
  exp1->clear();
  delay(500); 
}

sábado, 30 de maio de 2015

Arduino - Digitar Texto com Controle Remoto + LCD

No post anterior mostrei como digitar texto em um display LCD com um teclado matricial 4x3. Nesse vídeo mostro como fazer a mesma coisa, mas utilizando um controle remoto.


Código-Fonte:

obs. 01: O Controle remoto utilizado foi o da Claro TV. Caso for usar outro tipo de controle remoto, é necessário descobrir a codificação de cada uma das teclas e fazer as alterações na função get_tecla. Esse controle remoto, retorna dois códigos diferentes para a mesma tecla (em alguns casos), como pode ser visto no código. 

obs. 02: o controle remoto ao ser mantido pressionado, não fica enviando a informação de maneira continua, então o arduino, por um pequeno período de tempo, entende que o botão foi solto e pressionado várias vezes, causando um  comportamento indesejado, por isso foi criada a variável global time_read = 150;  150, significa que 150 milissegundos após o botão ser "solto" a função get_tecla, ainda vai continuar retornando o ultimo caractere pressionado. é possível que em diferentes controles remotos, esse tempo varie. Não fiz nenhum teste com outros controles, mas que for usar, é interessante testar isso antes.

obs. 03: é preciso baixar e instalar a biblioteca IRRemote. Não vou entrar em detalhes aqui de como instalar e configurar a biblioteca, pois uma rápida busca no google, certamente irá ajudar.

#include <IRremote.h> 
const int RECV_PIN = 11;
unsigned long last = millis();
 
IRrecv irrecv(RECV_PIN);
decode_results results;

unsigned long millis_last_read = 0;
char char_last_read = '\0';
unsigned long time_read = 150;

char get_tecla() {
  char c = '\0';
  
  if ( (millis() - time_read) > millis_last_read ){
    if (irrecv.decode(&results)) {
      if (results.value == 2152998656 || results.value == 2153031424){ c = '0'; }  
      if (results.value == 2152998657 || results.value == 2153031425){ c = '1'; }
      if (results.value == 2152998658 || results.value == 2153031426){ c = '2'; }
      if (results.value == 2152998659 || results.value == 2153031427){ c = '3'; }
      if (results.value == 2152998660 || results.value == 2153031428){ c = '4'; }
      if (results.value == 2152998661 || results.value == 2153031429){ c = '5'; }
      if (results.value == 2152998662 || results.value == 2153031430){ c = '6'; }
      if (results.value == 2152998663 || results.value == 2153031431){ c = '7'; }
      if (results.value == 2152998664 || results.value == 2153031432){ c = '8'; }
      if (results.value == 2152998665 || results.value == 2153031433){ c = '9'; }
      if (results.value == 551538735)                                { c = '*'; }
      if (results.value == 2152998666 || results.value == 2153031434){ c = '#'; }
        
      irrecv.resume(); // Receive the next value
      
      millis_last_read = millis();
      char_last_read = c;
    }
  } else {
    c = char_last_read;
  }
  
  //Serial.println(c);
  
  return c;
}
 
 
const char teclas[][12] = {"1 ", "2abc", "3def", "4ghi", "5jkl", "6mno", "7pqrs", "8tuv", "9wxyz", "*", "0", "#"};
const unsigned long time_char = 1200;  //1200 milissegundos pra desconsiderar a ultima tecla
 
class TecladoTelefone{
  private:
    unsigned long _millis_last_char;
    char _last_char;
    String _palavra;
     
    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 _back_space(){
      if (_palavra.length() >= 1){
        _last_char = _palavra[_palavra.length()-1];
        _palavra   = _palavra.substring(0, _palavra.length()-1);
      } else {
        _last_char = '\0';
      }
    }
     
  public:
    TecladoTelefone(){
      _millis_last_char = millis();
      _last_char = '\0';
    }
     
    int is_timing(){
      return ( (millis() - time_char) < _millis_last_char );
    }
   
    void add(char c){
      int pos = -1;
       
      if (c == '1'){ pos = 0;}
      if (c == '2'){ pos = 1;}
      if (c == '3'){ pos = 2;}
      if (c == '4'){ pos = 3;}
      if (c == '5'){ pos = 4;}
      if (c == '6'){ pos = 5;}
      if (c == '7'){ pos = 6;}
      if (c == '8'){ pos = 7;}
      if (c == '9'){ pos = 8;}
      if (c == '*'){ pos = 9;}
      if (c == '0'){ pos = 10;}
      if (c == '#'){ pos = 11;}
       
      if (pos == -1){ return; }
       
      if (pos == 9){
        _back_space();
        return;
      }
       
      const char * t = teclas[pos];
       
      if ( is_timing() ) {
         
        int i = 0;
        while (t[i] != '\0'){
          if (_last_char == t[i]){
            _set_last_char(  (t[i+1] == '\0') ? _last_char = t[0] : _last_char = t[i+1] , 0 );
            return ;
          }
          i++;
        }
      }
       
      _set_last_char ( c, 1);
    }
     
    char get_last_char(){ return _last_char; }
     
    String get_palavra( ) {
       
      if (_last_char != '\0') {
        return _palavra + _last_char;
      }
       
      return _palavra;
    }
};
 
 
#include <LiquidCrystal.h> 
  
LiquidCrystal lcd(9, 8, 10, 7, 6, 5); //Configura os pinos do Arduino para se comunicar com o LCD
 
 
TecladoTelefone * teclado;
 
void setup() {
  Serial.begin(9600);
  irrecv.enableIRIn(); 
  teclado = new TecladoTelefone();
  lcd.begin(16, 2); 
}
 
char tecla_anterior = '\0';
 
void loop() {
  char tecla = get_tecla();
   
  if (tecla != tecla_anterior){
    if (tecla) {
      teclado->add(tecla);
    }
  }
   
  tecla_anterior = tecla;
   
  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);
  delay(10);
  lcd.noCursor();    
}

sexta-feira, 29 de maio de 2015

Arduino - Teclado 4x3 + texto + lcd

Esse vídeo é continução do vídeo anterior, onde tinha demonstrado como digitar texto com teclado matricial, parecido com de telefone celular.

Vídeo anterior, com mais detalhes sobre a montagem do teclado:
http://fabianoallex.blogspot.com.br/2015/05/arduino-texto-com-teclado-matricial-4x3.html

Nessa versão implementei a função de backspace e mostrei o resultado num display LCD.



código-fonte:


#define col1 10 
#define col2 11 
#define col3 12 
#define lin1 3 
#define lin2 4
#define lin3 5
#define lin4 6 


char get_tecla(){
  int l[]={lin1, lin2, lin3, lin4}; // Array de 4 posições contendo os 4 pinos de linhas
  int i = 0, k = 0, t = 0;
  
  for (i=0; i<4; i++) {
    digitalWrite(lin1,LOW); 
    digitalWrite(lin2,LOW);
    digitalWrite(lin3,LOW);
    digitalWrite(lin4,LOW);
    digitalWrite(l[i],HIGH); 
    
    if(digitalRead(col1)) { t = i*3+1; break; }
    if(digitalRead(col2)) { t = i*3+2; break; }
    if(digitalRead(col3)) { t = i*3+3; break; }
  }
  
  if (t > 0 ){
    if (t >= 1 && t<=9){ return char(t+48);   }  //48--> ASCII: o charactere '1' na tabela ascii é 49º item, o '2' é o 50º item e assim por diante
    if (t==10)         { return '*'; }
    if (t==11)         { return '0'; }
    if (t==12)         { return '#'; }
  }
  
  return '\0';
}


const char teclas[][12] = {"1 ", "2abc", "3def", "4ghi", "5jkl", "6mno", "7pqrs", "8tuv", "9wxyz", "*", "0", "#"};
const unsigned long time_char = 1200;  //1200 milissegundos pra desconsiderar a ultima tecla

class TecladoTelefone{
  private:
    unsigned long _millis_last_char;
    char _last_char;
    String _palavra;
    
    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 _back_space(){
      if (_palavra.length() >= 1){
        _last_char = _palavra[_palavra.length()-1];
        _palavra   = _palavra.substring(0, _palavra.length()-1);
      } else {
        _last_char = '\0';
      }
    }
    
  public:
    TecladoTelefone(){
      _millis_last_char = millis();
      _last_char = '\0';
    }
    
    int is_timing(){
      return ( (millis() - time_char) < _millis_last_char );
    }
  
    void add(char c){
      int pos = -1;
      
      if (c == '1'){ pos = 0;}
      if (c == '2'){ pos = 1;}
      if (c == '3'){ pos = 2;}
      if (c == '4'){ pos = 3;}
      if (c == '5'){ pos = 4;}
      if (c == '6'){ pos = 5;}
      if (c == '7'){ pos = 6;}
      if (c == '8'){ pos = 7;}
      if (c == '9'){ pos = 8;}
      if (c == '*'){ pos = 9;}
      if (c == '0'){ pos = 10;}
      if (c == '#'){ pos = 11;}
      
      if (pos == -1){ return; }
      
      if (pos == 9){
        _back_space();
        return;
      }
      
      const char * t = teclas[pos];
      
      if ( is_timing() ) {
        
        int i = 0;
        while (t[i] != '\0'){
          if (_last_char == t[i]){
            _set_last_char(  (t[i+1] == '\0') ? _last_char = t[0] : _last_char = t[i+1] , 0 );
            return ;
          }
          i++;
        }
      }
      
      _set_last_char ( c, 1);
    }
    
    char get_last_char(){ return _last_char; }
    
    String get_palavra( ) {
      
      if (_last_char != '\0') {
        return _palavra + _last_char;
      }
      
      return _palavra;
    }
};


#include <LiquidCrystal.h> 
 
LiquidCrystal lcd(9, 8, 13, 2, 1, 0); //Configura os pinos do Arduino para se comunicar com o LCD


TecladoTelefone * teclado;

void setup() {
  //Serial.begin(9600);
  
    // colunas INPUT
  pinMode(col1,INPUT);
  pinMode(col2,INPUT);
  pinMode(col3,INPUT);
  
  // linhas OUTPUT 
  pinMode(lin1,OUTPUT); 
  pinMode(lin2,OUTPUT);
  pinMode(lin3,OUTPUT); 
  pinMode(lin4,OUTPUT); 
  
  teclado = new TecladoTelefone();
  
  lcd.begin(16, 2); 
}

char tecla_anterior = '\0';

void loop() {

  char tecla = get_tecla();
  
  if (tecla != tecla_anterior){
    if (tecla) {
      teclado->add(tecla);
    }
  }
  
  tecla_anterior = tecla;
  
  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);
  delay(10);
  lcd.noCursor();    
}

domingo, 24 de maio de 2015

Arduino - Texto com teclado matricial 4x3 (teclado de telefone)

Nesse video mostro dois exemplos, no primeiro é mostrado como ligar um teclado matricial de 4 linhas e 3 colunas no Arduino. Nesse exemplo utilizo um teclado feito a partir de um teclado de telefone, que foi cortado, e identificadas as linhas e colunas, furada a placa e soldado um fio pra cada linha e pra cada coluna. A outra extremidade do fio foi soldada em uma placa, para que fosse possível conectar o teclado numa protoboard para entao utilizá-la com o Arduino.

Vídeo



Imagens do teclado





Na imagem abaixo é possível ver como foi feita a ligação. Os fios foram soldados em um placa, os 3 fios brancos são os fios das colunas, e os 4 fios amarelos são os fios das linhas, sendo o fio amarelo mais da direita referente a linha 1, conforme identificado pelo L1 na placa, e mais a esquerda, a coluna 3, conforme identificado pelo C3 na placa. 

Os fios referentes às linhas (fios amarelos) foram ligados diretamente ao Arduino, nos pinos 3, 4, 5 e 6. Os fios referentes às colunas foram ligados na protoboard no qual cada fio é ligado a um resistor de pulldown (10K cada um), e então ligados ao Arduino nos pinos 10, 11 e 12.





Código 01


Nesse código é mostrado como ligar um teclado matricial 4x3 no Arduino.


#define col1 10 
#define col2 11 
#define col3 12 
#define lin1 3 
#define lin2 4
#define lin3 5
#define lin4 6 


char get_tecla(){
  int l[]={lin1, lin2, lin3, lin4}; // Array de 4 posições contendo os 4 pinos de linhas
  int i = 0, k = 0, t = 0;
  
  for (i=0; i<4; i++) {
    digitalWrite(lin1,LOW); 
    digitalWrite(lin2,LOW);
    digitalWrite(lin3,LOW);
    digitalWrite(lin4,LOW);
    digitalWrite(l[i],HIGH); 
    
    if(digitalRead(col1)) { t = i*3+1; break; }
    if(digitalRead(col2)) { t = i*3+2; break; }
    if(digitalRead(col3)) { t = i*3+3; break; }
  }
  
  if (t > 0 ){
    if (t >= 1 && t<=9){ return char(t+48);   }  //48--> ASCII: o charactere '1' na tabela ascii é 49º item, o '2' é o 50º item e assim por diante
    if (t==10)         { return '*'; }
    if (t==11)         { return '0'; }
    if (t==12)         { return '#'; }
  }
  
  return '\0';
}


void setup() {

  // colunas INPUT
  pinMode(col1,INPUT);
  pinMode(col2,INPUT);
  pinMode(col3,INPUT);
  
  // linhas OUTPUT 
  pinMode(lin1,OUTPUT); 
  pinMode(lin2,OUTPUT);
  pinMode(lin3,OUTPUT); 
  pinMode(lin4,OUTPUT); 
  
  Serial.begin(9600); 
}

void loop() {
  if (get_tecla() != '\0'){
    Serial.println( get_tecla() );
  }
}


Código 02

Código referente a digitação de texto através do teclado Matricial.

o Código não está completo, não tendo informado letras Maisúsculas, nem caracteres especiais, mas facilmente adicionados, modificando a constante teclas, introduzindo os demais caracteres que sejam desejados. Também não foi implementado nenhuma função referente a Backspace (para apagar o texto), nem para limpar. Talvez futuramente eu venha implementar essas melhorias no código, e assim que eu for fazendo, vou disponibilzando o código aqui no blog.


#define col1 10 
#define col2 11 
#define col3 12 
#define lin1 3 
#define lin2 4
#define lin3 5
#define lin4 6 


char get_tecla(){
  int l[]={lin1, lin2, lin3, lin4}; // Array de 4 posições contendo os 4 pinos de linhas
  int i = 0, k = 0, t = 0;
  
  for (i=0; i<4; i++) {
    digitalWrite(lin1,LOW); 
    digitalWrite(lin2,LOW);
    digitalWrite(lin3,LOW);
    digitalWrite(lin4,LOW);
    digitalWrite(l[i],HIGH); 
    
    if(digitalRead(col1)) { t = i*3+1; break; }
    if(digitalRead(col2)) { t = i*3+2; break; }
    if(digitalRead(col3)) { t = i*3+3; break; }
  }
  
  if (t > 0 ){
    if (t >= 1 && t<=9){ return char(t+48);   }  //48--> ASCII: o charactere '1' na tabela ascii é 49º item, o '2' é o 50º item e assim por diante
    if (t==10)         { return '*'; }
    if (t==11)         { return '0'; }
    if (t==12)         { return '#'; }
  }
  
  return '\0';
}


const char teclas[][12] = {"1 ", "2abc", "3def", "4ghi", "5jkl", "6mno", "7pqrs", "8tuv", "9wxyz", "*", "0", "#"};
const unsigned long time_char = 1200;  //1200 milissegundos pra desconsiderar a ultima tecla

class TecladoTelefone{
  private:
    unsigned long _millis_last_char;
    char _last_char;
    String _palavra;
    
    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();
    }
    
  public:
    TecladoTelefone(){
      _millis_last_char = millis();
      _last_char = '\0';
    }
  
    void add(char c){
      int pos = -1;
      
      if (c == '1'){ pos = 0;}
      if (c == '2'){ pos = 1;}
      if (c == '3'){ pos = 2;}
      if (c == '4'){ pos = 3;}
      if (c == '5'){ pos = 4;}
      if (c == '6'){ pos = 5;}
      if (c == '7'){ pos = 6;}
      if (c == '8'){ pos = 7;}
      if (c == '9'){ pos = 8;}
      if (c == '*'){ pos = 9;}
      if (c == '0'){ pos = 10;}
      if (c == '#'){ pos = 11;}
      
      if (pos == -1){ return; }
      
      const char * t = teclas[pos];
      
      if ( (millis() - time_char) < _millis_last_char ) {
        
        int i = 0;
        while (t[i] != '\0'){
          if (_last_char == t[i]){
            _set_last_char(  (t[i+1] == '\0') ? _last_char = t[0] : _last_char = t[i+1] , 0 );
            return ;
          }
          i++;
        }
      }
      
      _set_last_char ( c, 1);
    }
    
    char get_last_char(){ return _last_char; }
    
    String get_palavra( ) {
      return _palavra + _last_char;
    }
};


TecladoTelefone * teclado;

void setup() {
  Serial.begin(9600);
  
    // colunas INPUT
  pinMode(col1,INPUT);
  pinMode(col2,INPUT);
  pinMode(col3,INPUT);
  
  // linhas OUTPUT 
  pinMode(lin1,OUTPUT); 
  pinMode(lin2,OUTPUT);
  pinMode(lin3,OUTPUT); 
  pinMode(lin4,OUTPUT); 
  
  teclado = new TecladoTelefone();
}

char tecla_anterior = '\0';

void loop() {

  char tecla = get_tecla();
  
  if (tecla != tecla_anterior){
    if (tecla) {
      teclado->add(tecla);
    }
  }
  
  tecla_anterior = tecla;
  
  
  Serial.println(teclado->get_palavra());
}

domingo, 3 de maio de 2015

Arduino - Ponte H para Motor - IBT 2 - BTS7960

Demonstração de uso da Ponte H IBT 2






Especificações
  • Tensão: 6V-27V
  • Corrente: 43A
  • Input level: 3.3V-5V

pinos

1 - PWM Direção 1
2 - PWM Direção 2
3 - Ativar / Desativar PWM Direção 1 - HIGH ativa
4 - Ativar / Desativar PWM Direção 2 - HIGH ativa
5 - Direção 1 - Side Currente Alarm Output
6 - Direção 2 - Side Currente Alarm Output
7 - VCC
8 - Ground


Para o teste foram utilizados os seguintes pinos

1 - Ligado ao pino 5 (PWM) do Arduino
2 - Ligado ao pino 6 (PWM) do Arduino
3 - VCC
4 - VCC
5 - Desconectado
6 - Desconectado
7 - VCC
8 - Ground


Código-Fonte:

/*
IBT-2 Motor Control Board driven by Arduino.
 
Speed and direction controlled by a potentiometer attached to analog input 0.
One side pin of the potentiometer (either one) to ground; the other side pin to +5V
 
Connection to the IBT-2 board:
IBT-2 pin 1 (RPWM) to Arduino pin 5(PWM)
IBT-2 pin 2 (LPWM) to Arduino pin 6(PWM)
IBT-2 pins 3 (R_EN), 4 (L_EN), 7 (VCC) to Arduino 5V pin
IBT-2 pin 8 (GND) to Arduino GND
IBT-2 pins 5 (R_IS) and 6 (L_IS) not connected
*/
 
int SENSOR_PIN = A0; // center pin of the potentiometer
 
int RPWM_Output = 5; // Arduino PWM output pin 5; connect to IBT-2 pin 1 (RPWM)
int LPWM_Output = 6; // Arduino PWM output pin 6; connect to IBT-2 pin 2 (LPWM)
 
void setup() {
  pinMode(RPWM_Output, OUTPUT);
  pinMode(LPWM_Output, OUTPUT);
}
 
void loop() {
  int sensorValue = analogRead(SENSOR_PIN);
 
  // sensor value is in the range 0 to 1023
  // the lower half of it we use for reverse rotation; the upper half for forward rotation
  if (sensorValue < 512)
  {
    // reverse rotation
    int reversePWM = -(sensorValue - 511) / 2;
    analogWrite(LPWM_Output, 0);
    analogWrite(RPWM_Output, reversePWM);
  } else {
    // forward rotation
    int forwardPWM = (sensorValue - 512) / 2;
    analogWrite(LPWM_Output, forwardPWM);
    analogWrite(RPWM_Output, 0);
  }
}


Referência: http://www.hessmer.org/blog/wp-content/uploads/2013/12/IBT-2-Input-Ports.jpg

sexta-feira, 1 de maio de 2015

Arduino - Teclado 4x4 com Pino Analógico








Esquema

Os botões devem ser ligados conforme imagem abaixo, de forma que um dos terminais de cada um dos botões estejam em um linha e o outro terminal em uma coluna.



Montagem do circuito na PCB

Atenção!!

O teclado que eu utilizei, foi retirado de um ponto eletrônico portátil que não estava mais sendo usado. Não sei se ele tem a mesma construção de outros teclados, principalmente destes vendidos para ser utilizado com o Arduino, portanto, talvez sejam necessárias algumas alterações, para rodar com outros teclados.

Inicialmente a placa foi feita sem o capacitor representado no circuito acima, mas durante os testes percebi que ainda haviam oscilações que retornavam leituras com valores errados. Pra tentar minimizar as leituras erradas inclui o capacitor de 100nF.








Para testar a placa primeiramente rodei o código abaixo para identificar o valor lido em cada uma das teclas. Ao executar é verificada uma oscilação entre leituras de um mesmo botão, não sendo possível identificar o valor correto para cada botão.


Código 01


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

void loop() {
   Serial.println(analogRead(A5));
   delay(100);
}





Para tentar chegar em um valor mais aproximado do real, rodeio o código abaixo, no qual faço a leitura 200 vezes, e depois faço a média de todos esses valores, chegando então em um valor que dá um retorno com menos variações.

código 02:

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

void loop() {
  const int LEITURAS = 200;
  long soma = 0;
  long media = 0;
  int leitura = analogRead(A5);
  
  for (int i=0;i < LEITURAS; i++){ soma += leitura; }
  
  media = soma / LEITURAS;
  
  Serial.println(media);
  delay(100);
}




Abaixo está o código que foi mostrado no vídeo. Mas quem for tentar reproduzir, é necessário fazer vários testes, já que eventualmente é possível que falhas ocorram, portanto, essa solução ainda não é 100% confiável, mas ainda sim serve como base para algumas aplicações. 


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

char getTecla(){
  const int LEITURAS = 300;
  long soma = 0;
  long media = 0;
  int maior = 0;
  int menor = 1023;
  
  for (int i = 0; i < LEITURAS; i++) { 
    int leitura = analogRead(A5);                //PINO ANALOGICO A5. MUDAR AQUI
    if (leitura <= 368) { soma=0; break; }
    
    menor = (menor > leitura) ? leitura : menor;
    maior = (maior < leitura) ? leitura : maior;
    soma += leitura;
    
    if ((maior-menor) >= 4){ soma=0; break; }
  }
    
  
  
  media = soma / LEITURAS;
  
  char c = ' ';
  if (media <= 1023 && media >= 971) {c = '1'; }
  if (media <= 970 && media >= 886)  {c = '2'; }
  if (media <= 885 && media >= 814)  {c = '3'; }
  if (media <= 813 && media >= 739)  {c = 'A'; }
  if (media <= 738 && media >= 673)  {c = '4'; }
  if (media <= 672 && media >= 631)  {c = '5'; }
  if (media <= 630 && media >= 595)  {c = '6'; }
  if (media <= 594 && media >= 552)  {c = 'B'; }
  if (media <= 551 && media >= 513)  {c = '7'; }
  if (media <= 512 && media >= 489)  {c = '8'; }
  if (media <= 488 && media >= 466)  {c = '9'; }
  if (media <= 465 && media >= 438)  {c = 'C'; }
  if (media <= 437 && media >= 413)  {c = '*'; }
  if (media <= 412 && media >= 398)  {c = '0'; }
  if (media <= 397 && media >= 383)  {c = '#'; }
  if (media <= 382 && media >= 369)  {c = 'D'; }
  
  
  //somente para testes, retirar ao ser usado
  if (c != ' '){
    Serial.print(media);
    Serial.print(" - ");
    Serial.print(maior);
    Serial.print(" - ");
    Serial.print(menor);
    Serial.print(" - ");
    Serial.print(maior-menor);
    Serial.print(" - ");
  }
  
  return c;
}

void loop() {
  
  char c = getTecla();
  if (c != ' '){
     Serial.println(c);
     delay(150);
  }
}


segunda-feira, 20 de abril de 2015

Arduino - Database com SD Card + Ethernet Shield

Nesse vídeo mostro um exemplo de como criar um banco de dados simples com o Arduino, utilizando Ethernet Shield com SD Card, utilizando a biblioteca EDB (Extended Database Library). os arquivos html são salvos no cartão de memória. 

 Os dados são retornados para o browser através de javascript no formato JSON.



Baixar a biblioteca EDB

https://code.google.com/p/arduino-edb/downloads/list

Depois de instalada a biblioteca acima, é preciso corrigir um bug que existe no código-fonte da biblioteca. abra o arquivo EDB.cpp e modifique a seguinte função, incluindo a linha sinalizada pelo +:

EDB_Status EDB::open(unsigned long head_ptr)
{
EDB_head_ptr = head_ptr;
+ EDB_table_ptr = sizeof(EDB_Header) + EDB_head_ptr;
readHead();
return EDB_OK;
}


modifique também a parte onde está

#include "WProgram.h"
#include <EDB.h>

para

#include "Arduino.h"
#include <EDB.h>



Salve os arquivos HTML abaixo na raiz do cartão de memória


arquivo aut.htm

HTTP/1.1 401 Authorization Required
WWW-Authenticate: Basic realm="Area Restrita"
Content-Type: text/html
Connnection: close
 
<!DOCTYPE HTML>\n
<html><head><title>Error</title>
</head><body><h1>401 Acesso nao autorizado</h1></body></html>



arquivo cab.htm

HTTP/1.1 200 OK
Content-Type: text/html
Connection: keep-alive


arquivo logoff.htm:

HTTP/1.1 401 Authorization Required
Content-Type: text/html
Connnection: close

<!DOCTYPE HTML>
<html><head><title>Logoff</title>
</head><body><h1>Você não está mais conectado</h1></body></html>



arquivo plist.htm:

<!DOCTYPE html>
<html>
<head>
  <style type="text/css">
  tr, td {
    border-bottom: 1px solid black;
padding: 5px;
margin: 0px;
  }
  label{
position:absolute;
left:19px;
margin-right:5px;
  }
  body{font-family: arial}
  h1 {color: #FF0000;}
  </style>
  <script src="/js_proc_list"></script>
  <script>
function init(){ 
 if (document.URL.indexOf("insert_proc") > -1){ window.location = '/'; }
 if (document.URL.indexOf("del_proc") > -1)   { window.location = '/';}
 var obj = JSON.parse(json_proc);
 var html = '<table>';
 for(var i=0; i<obj.processos.length;i++){
        html += '<tr>' + 
 '<td>' + obj.processos[i].id_proc + '</td>' + 
 '<td>' + obj.processos[i].proc + '</td>' + 
 '<td>' + '<a href="del_proc?p='+obj.processos[i].id_proc+'" onclick=\"return confirm(\'Deseja realmente excluir?\');\">Excluir</a></td>' + 
 '</tr>';
 }
 html += '</table>';
 document.getElementById("lproc").innerHTML = html;
}
  </script>
</head>
<body onload="init();">
  <div><a href="/">Home</a> | <a href="/logoff" onclick="return confirm('Deseja realmente sair?');">Logoff</a></div>
  <br>
  <form action="/insert_proc" method="GET">
    Nome Processo:
    <input type="text" name="p" maxlength="15" id="p"/><input type="submit" value="incluir">
  </form>
  <br>
  <div id="lproc"></div>
</body>
</html>


código-fonte (sketch)


/**********************************************************************************
************************************BIBLIOTECAS************************************
**********************************************************************************/
#include <SPI.h>
#include <Ethernet.h>
#include <SD.h>
#include <EDB.h>
/**********************************************************************************
********************************FIM BIBLIOTECAS************************************
**********************************************************************************/

/****************************************************************************************
 ****************************DECLARACAO DAS TABELAS INICIO*******************************
 ****************************************************************************************/
#define TABLE_SIZE_PROC 512
#define TABLE_NAME_PROC "p.db"

EDB  edb_proc(&writer_proc, &reader_proc);

/******************STRUCTS DAS TABELAS***************************************************/
struct Struct_proc {
  int id_proc;
  char proc[15];
} struct_proc;

/******************INICIALIZA BANCO******************************************************/

void iniciar_banco_dados(){
  char * f = TABLE_NAME_PROC;
  File file_proc;
  if (SD.exists(f)){
    file_proc = SD.open(f, FILE_WRITE);
    edb_proc.open(0);
  } else {
    file_proc = SD.open(f, FILE_WRITE);
    edb_proc.create(0, TABLE_SIZE_PROC, sizeof(struct_proc));
  }
  file_proc.close();
}

/******************WRITER/READER PROC******************************************************/
void writer_proc (unsigned long address, byte data) {
  File file_proc;
  file_proc = SD.open(TABLE_NAME_PROC, FILE_WRITE);
  file_proc.seek(address); 
  file_proc.write(data); 
  file_proc.flush();
  file_proc.close();
}

byte reader_proc (unsigned long address) { 
  File file_proc;
  file_proc = SD.open(TABLE_NAME_PROC, FILE_WRITE);
  file_proc.seek(address); 
  byte b = file_proc.read();   
  file_proc.close();
  return    b;
}

/*********operacoes de inclusao/alteracao/leitura/exclusão processos******************/
void edb_insert_proc(int id_proc, char * proc) {
  struct_proc.id_proc = id_proc; 
  strcpy( struct_proc.proc, proc );
  edb_proc.appendRec(EDB_REC struct_proc);
}

void edb_delete_proc(int id){
  for (int recno=1; recno <= edb_proc.count(); recno++) {
    edb_proc.readRec(recno, EDB_REC struct_proc);
    if (struct_proc.id_proc == id) {
      edb_proc.deleteRec(recno);
      break;
    }
  }
}

void edb_update_proc(int id_proc, char * proc){ 
  struct_proc.id_proc   = id_proc; 
  strcpy( struct_proc.proc, proc );
  edb_proc.updateRec(id_proc, EDB_REC struct_proc);
}

void edb_read_proc(unsigned long recno_proc){
  edb_proc.readRec(recno_proc, EDB_REC struct_proc);
}

int edb_max_proc_id() {
  int max=0;
  Struct_proc s;
  for (int recno=1; recno <= edb_proc.count(); recno++) {
    edb_proc.readRec(recno, EDB_REC s);
    
    if (s.id_proc > max) { max = s.id_proc; }
  }
  return max;
}


/****************************************************************************************
 ****************************DECLARACAO DAS TABELAS FIM**********************************
 ****************************************************************************************/

 /****************************************************************************************
 ****************************SD CARD INICIO***********************************************
 ****************************************************************************************/

boolean iniciar_sd_card() {
  pinMode(10, OUTPUT);
  digitalWrite(10, HIGH);
   
  if (!SD.begin(4)) { return false; }
   
  return true;
}

/****************************************************************************************
 ****************************SD CARD FIM**************************************************
 ****************************************************************************************/


/**********************************************************************************
*************************ROTINAS USUARIO E SENHA***********************************
***********************************************************************************/

boolean validar_usuario(char * linebuf) {
  /*
  nesse exemplo o usuario e senha estão definidos dentro do código fonte.
  mas o usuário e senha poderiam ser autenticados de diversas maneiras,
  lendo os dados de um servidor web, arquivo texto, etc, bastando apenas atribuir
  o valor lido para a variável usuario_senha. 
  */
  
  char usuario_senha[] = "admin:admin";
  int t = strlen(usuario_senha);
  
  int tamanhoEnc = (((t-1) / 3) + 1) * 4;   //tamanho da string codificada
  char out[tamanhoEnc];
  base64_encode(out, usuario_senha, t+1 );
  
  //---desconta é usado pra eliminar os caracteres '='
  int desconta = 0;
  if ((t%3) == 1) { desconta = 2; }
  if ((t%3) == 2) { desconta = 1; }
  
  char out2[tamanhoEnc-desconta];
  
  byte i;
  for (i=0; i<(tamanhoEnc-desconta);i++){ out2[i] = out[i]; }
  out2[i] = '\0';
  
  return ( strstr(linebuf, out2)>0 );
}

/**********************************************************************************
*************************FIM ROTINA USUARIO E SENHA********************************
***********************************************************************************/

/**********************************************************************************
***********************************PAGINAS HTML************************************
***********************************************************************************/
EthernetServer * server;

void iniciar_ethernet(){
  byte ip[4]      = {192,168,200,188};
  //byte gateway[4] = {192,168,200,254};
  //byte subnet[4]  = {255,255,255,0};
  byte mac[6]     = {0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED};
  int  porta      = 80;

  server = new EthernetServer(porta);

  //Ethernet.begin(mac, ip, gateway, subnet);         //caso necessario gateway utilizar essa linha
  Ethernet.begin(mac, ip);
  server->begin();
}

void write_from_file(EthernetClient &client, char * file_name){
  File webFile = SD.open(file_name);
  if (webFile) {
    while(webFile.available()) {
      client.write(webFile.read()); 
    }
    webFile.close();
  }
}

void html_cab_200_ok(EthernetClient &client){
  client.println(F("HTTP/1.1 200 OK\n"
                   "Content-Type: text/html\n"
                   "Connection: keep-alive\n\n"));
}

void html_logoff(EthernetClient &client){
  write_from_file(client, "logoff.htm");
}

void html_autenticar(EthernetClient &client) {
  write_from_file(client, "aut.htm");
}

void html_proc_list(EthernetClient &client) {
  html_cab_200_ok(client);
  write_from_file(client, "plist.htm");
}

void js_proc_list(EthernetClient &client){
  //cabecalho para javascript
  client.println(F("HTTP/1.1 200 OK\n"
                   "Content-Type: text/javascript\n"
                   "Connection: keep-alive\n\n"));
                              
  client.print("var json_proc=\'{\"processos\":[");  
  char virgula = ' ';
  for (int recno = 1; recno <= edb_proc.count(); recno++) {
    edb_proc.readRec(recno, EDB_REC struct_proc);
    
    client.print(virgula);
    client.print("{\"id_proc\":\"");
    client.print(struct_proc.id_proc);
    client.print("\", \"proc\":\"");
    client.print(struct_proc.proc);
    client.print("\"}");
    
    virgula = ',';
  }  
  
  client.print("]}\';");  
}

void delete_proc(char * linebuf) {  
  char * p = strstr(linebuf, "p=");  // /GET ?p=123
  char r[5] = {'\0'};
  
  if (p) {
    byte i=0;
    while (p[i+2] >= '0' && p[i+2] <= '9') {
      r[i] = p[i+2];
      i++;
      if (i==4) {break;}
    }
    r[i]='\0';
  }

  if (r[0] >= 48 && r[0] <= 57) {  //48->0  57->9
    edb_delete_proc(atoi(r));  //apaga o registro
  }  
}

void insert_proc(char * linebuf) {  
  char * p = strstr(linebuf, "p=");  // /GET ?p=123
  char r[16] = {'\0'};
  
  if (p) {
    byte i=0;
    while (p[i+2] != ' ') {
      r[i] = (p[i+2] == '+') ? r[i] = ' ' : r[i] = p[i+2];
      i++;
      if (i==15) {break;}
    }
    r[i]='\0';
  }

  edb_insert_proc(edb_max_proc_id()+1, r);  //inclui registro
}

void exec_ethernet(){
  EthernetClient client = server->available();
  if (client) {
    char linebuf[80];
    memset(linebuf, 0, sizeof(linebuf));
    
    int     charCount          = 0;
    boolean autenticado        = false;
    boolean currentLineIsBlank = true;
    boolean logoff             = false;
    boolean jsProcList         = false;
    boolean listProc           = false;
    
    while (client.connected()) {
      if (client.available()) {
        char c = client.read();
        
        linebuf[charCount] = c;
        
        if (charCount<sizeof(linebuf)-1) { charCount++; }
        Serial.write(c);
        
        if (c == '\n' && currentLineIsBlank) {
          if (autenticado && !logoff ) {
            if(jsProcList) { js_proc_list(client);   } 
            if(listProc)   { html_proc_list(client); }
          } else {
            logoff ? html_logoff(client) : html_autenticar(client);
          }
          break;
        }
        if (c == '\n') { 
          currentLineIsBlank = true;               
          
          if (strstr(linebuf, "Authorization: Basic")>0 ) { if ( validar_usuario(linebuf) )   {  autenticado = true; } }
          if (strstr(linebuf, "GET /logoff"         )>0 ) { logoff      = true; }
          if (strstr(linebuf, "GET / "              )>0 ) { listProc    = true; }
          if (strstr(linebuf, "GET /js_proc_list"   )>0 ) { jsProcList  = true; }
          if (strstr(linebuf, "GET /del_proc"       )>0 ) { listProc    = true; delete_proc(linebuf); }
          if (strstr(linebuf, "GET /insert_proc"    )>0 ) { listProc    = true; insert_proc(linebuf); }
          
          memset(linebuf, 0, sizeof(linebuf));
          charCount = 0;
        } else if (c != '\r') {
          currentLineIsBlank = false;    // you've gotten a character on the current line
        }
      }
    }
    
    delay(1);           // give the web browser time to receive the data
    client.stop();      // close the connection:
  }
}
/**********************************************************************************
**************************************** FIM PAGINAS HTML**************************
***********************************************************************************/


/**********************************************************************************
*************************BASE 64 CODE/DECODE DO USUARIO E SENHA********************
***********************************************************************************/

void a3_to_a4(unsigned char * a4, unsigned char * a3);
void a4_to_a3(unsigned char * a3, unsigned char * a4);
unsigned char b64_lookup(char c);

int base64_encode(char *output, char *input, int inputLen) {
  const char b64_alphabet[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
  int i=0, j=0, encLen=0;
  unsigned char a3[3], a4[4];

  while(inputLen--) {
    a3[i++] = *(input++);
    if(i == 3) {
      a3_to_a4(a4, a3);
      for(i=0; i<4; i++) { output[encLen++] = b64_alphabet[a4[i]]; }
      i = 0;
    }
  }

  if (i) {
    for(j = i; j < 3; j++)     { a3[j] = '\0';                           }
    a3_to_a4(a4, a3);
    for(j = 0; j < i + 1; j++) { output[encLen++] = b64_alphabet[a4[j]]; }
    while((i++ < 3))           { output[encLen++] = '=';                 }
  }
  output[encLen] = '\0';
  return encLen;
}

void a3_to_a4(unsigned char * a4, unsigned char * a3) {
  a4[0] = (a3[0]  & 0xfc) >> 2;
  a4[1] = ((a3[0] & 0x03) << 4) + ((a3[1] & 0xf0) >> 4);
  a4[2] = ((a3[1] & 0x0f) << 2) + ((a3[2] & 0xc0) >> 6);
  a4[3] = (a3[2] & 0x3f);
}

unsigned char b64_lookup(char c) {
  if(c >='A' && c <='Z') return c - 'A';
  if(c >='a' && c <='z') return c - 71;
  if(c >='0' && c <='9') return c + 4;
  if(c == '+') return 62;
  if(c == '/') return 63;
  return -1;
}

/**********************************************************************************
*************************FIM BASE 64 CODE/DECODE DO USUARIO E SENHA****************
***********************************************************************************/

/**********************************************************************************
**************************************** VOID / LOOP ******************************
***********************************************************************************/

void setup() {
  Serial.begin(9600);
  iniciar_sd_card();
  iniciar_banco_dados();
  iniciar_ethernet();
}

void loop() {
  exec_ethernet();
}

/**********************************************************************************
*************************************FIM VOID / LOOP*******************************
***********************************************************************************/