Publicidade:

quarta-feira, 17 de junho de 2015

Arduino - Ethernet Shield + SDCard + Arquivos


Nesse vídeo mostro como incluir imagens, javascript, e outros tipos de arquivos e mídias na sua aplicação web baseada no Ethernet Shield W5100


31/10/2016 - Observação
Erro de login

Várias pessoas têm me relatado problemas no login, e eu não havia conseguido achar o problema, mas o Rodrigo (nos comentários abaixo) encontrou o problema e postou a solução. Vou manter o código original, e se quem tiver erros, siga o que o Rodrigo recomendou:

//Descobri o problema e mudei o meu código. 
//A função base64_encode não retorna corretamente o primeiro byte codificado, 
//por isso a função validar_usuario sempre recebia a string "out" errada. 
//Tive que trocar a função base64_encode para uma que retornasse o valor 
//inteiro de "out" codificado. Dai minha função validar_usuario ficou assim:

boolean validar_usuario(char * linebuf) {
  char usuario_senha[] = "admin:admin"; //"admin:admin";
  byte t = strlen(usuario_senha);
  int tamanhoEnc = (((t-1) / 3) + 1) * 4; //tamanho da string codificada
  char *out = base64_encode(usuario_senha, t);
  char out2[tamanhoEnc];

  for (t=0; t<(tamanhoEnc); t++) {
    out2[t] = linebuf[21+t];
  }
  return (strstr(out2, out)>0);
} 


Entenda o Mime Types:
https://pt.wikipedia.org/wiki/MIME

Lista de todos (acredito eu) os Mime Types de Arquivos:
http://www.sitepoint.com/web-foundations/mime-types-complete-list/



Para rodar a aplicação, copie os seguintes arquivos para dentro do SDCard



atmel.pdf

Qualquer arquivo PDF chamado atmel.pdf (cuidado com o tamanho do arquivo)

favicon.ico

qualquer arquivo do tipo ico, chamado favicon.ico

uno.jpg

qualquer imagem do tipo jpg, chamada uno.jpg


os demais arquivos, devem ser conforme mostrados abaixo


HTML - index.htm

<!DOCTYPE html>
<html>
<head>
  <link rel="stylesheet" type="text/css" href="/css.css">
  <script src="/js.js"></script>
</head>
<body onload="init();">
  <h1>ARDUINO - ETHERNET SHIELD + SDCARD + ARQUIVOS</h1>
  <br>
  <img src="/uno.jpg" />
  <br>
  <a href="/atmel.pdf">Download datasheet</a> | <a href='/logoff'>Logoff</a>
  <br>
</body>
</html>


Javascript - js.js

function init(){
  window.alert("hello world!");
}



CSS - css.css

body {
    background-color: #FAEBD7;
}
h1 {
    margin-left: 40px;
} 




Sketch - código-fonte:

/*
 Web Server - HTTP Autentication
 Baseado na versão de exemplo do Arduino.
 
   - exemplo que mostra como incluir html, css, 
   javascript e imagem em arquivos no sdcard e também
   como fazer download de um arquivo pdf do sdcard.
  
   by Fabiano A. Arndt (fabianoallex)
*/

/**********************************************************************************
************************************BIBLIOTECAS************************************
**********************************************************************************/

#include <SPI.h>
#include <Ethernet.h>
#include <SD.h>

/**********************************************************************************
********************************FIM BIBLIOTECAS************************************
**********************************************************************************/

/**********************************************************************************
*************************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";                           //usuario e senha para acessar a pagina
  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********************************
***********************************************************************************/

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

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

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

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

void write_from_file(EthernetClient &client, char * filename){
  File webFile = SD.open(filename);
  if (webFile) {
    while(webFile.available()) {
      client.write(webFile.read()); 
    }
    webFile.close();
  } else {
    Serial.print("Erro SD CARD: ");
    Serial.println(filename);
  }
}

void iniciar_ethernet_01(){
  byte ip[4]      = {192,168,200,25};                    //definir aqui o ip
  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
  server->begin();
}

void iniciar_ethernet_02(){
  byte ip[4]      = {192,168,200,25};                    //definir aqui o ip
  byte mac[6]     = {0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED};
  int  porta      = 80;

  server = new EthernetServer(porta);

  Ethernet.begin(mac, ip);
  server->begin();
}

void iniciar_ethernet() {              //escolher apenas um dos dois modos de iniciar o ethernet shield
  iniciar_ethernet_01();               //inicia com ip, gateway, mascara e porta
  //iniciar_ethernet_02();             //inicia só com ip e porta
}

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){
    client.print(F(
                 "HTTP/1.1 401 Authorization Required\n"  
                 "Content-Type: text/html\n"  
                 "Connnection: close\n\n"  
                 "<!DOCTYPE HTML>\n"  
                 "<html><head><title>Logoff</title>\n"  
                 "<script>document.execCommand('ClearAuthenticationCache', 'false');</script>"  //IE logoff
                 "<script>try {"                                                                //mozila logoff
                 "   var agt=navigator.userAgent.toLowerCase();"
                 "   if (agt.indexOf(\"msie\") != -1) { document.execCommand(\"ClearAuthenticationCache\"); }"
                 "   else {"
                 "     var xmlhttp = createXMLObject();"
                 "      xmlhttp.open(\"GET\",\"URL\",true,\"logout\",\"logout\");"
                 "     xmlhttp.send(\"\");"
                 "     xmlhttp.abort();"
                 "   }"
                 " } catch(e) {"
                 "   alert(\"erro ao fazer logoff\");"
                 " }"
                 "function createXMLObject() {"
                 "  try {if (window.XMLHttpRequest) {xmlhttp = new XMLHttpRequest();} else if (window.ActiveXObject) {xmlhttp=new ActiveXObject(\"Microsoft.XMLHTTP\");}} catch (e) {xmlhttp=false;}"
                 "  return xmlhttp;"
                 "}</script>"
                 "</head><body><h1>Voce nao esta mais conectado</h1></body></html>\n"));
}

void html_autenticar(EthernetClient &client) {
  client.print(F("HTTP/1.1 401 Authorization Required\n"  
               "WWW-Authenticate: Basic realm=\"Area Restrita\"\n"  
               "Content-Type: text/html\n"  
               "Connnection: close\n\n"  
               "<!DOCTYPE HTML>\n"  
               "<html><head><title>Error</title>\n"  
               "</head><body><h1>401 Acesso nao autorizado</h1></body></html>\n"));
}

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

  write_from_file(client, filename);
}


void js_file(EthernetClient &client, char * filename){
  client.println(F("HTTP/1.1 200 OK\n"
                   "Content-Type: text/javascript\n"
                   "Connection: keep-alive\n"));

  write_from_file(client, filename);
}

void css_file(EthernetClient &client, char * filename){
  client.println(F("HTTP/1.1 200 OK\n"
                   "Content-Type: text/css\n"
                   "Connection: keep-alive\n"));

  write_from_file(client, filename);
}

void favicon_file(EthernetClient &client, char * filename){
  client.println(F("HTTP/1.1 200 OK\n"
                   "Content-Type: image/x-icon\n"
                   "Connection: keep-alive\n"));

  write_from_file(client, filename);
}


void pdf_file_download(EthernetClient &client, char * filename){
  client.println(F("HTTP/1.1 200 OK\n"
                   "Content-Type: application/download\n"
                   "Connection: close\n"));
    
  write_from_file(client, filename);
}

void jpg_file(EthernetClient &client, char * filename){
  client.println(F("HTTP/1.1 200 OK\n"
                   "Content-Type: file/jpg\n"
                   "Connection: close\n"));
    
  write_from_file(client, filename);
}


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 indCss             = false;
    boolean indJs              = false;
    boolean indPdfDataSheet    = false;
    boolean indJpgUno          = false;
    boolean indFavicon         = 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 (indJs){
              js_file(client, "js.js");                          //js file
            } else if(indCss){
              css_file(client, "css.css");                       //css file
            } else if(indPdfDataSheet){
              pdf_file_download(client, "atmel.pdf");        //datasheet download
            } else if(indJpgUno){
              jpg_file(client, "uno.jpg");                       //jpg file
            } else if(indFavicon){
              jpg_file(client, "favicon.ico");                   //icone do browser
            } else {
              html_autenticado(client, "index.htm");             //página inicial
            }
          } else {
            logoff ? html_logoff(client) : html_autenticar(client);
          }
          break;
        }
        if (c == '\n') { 
          currentLineIsBlank = true;               
          
          if (strstr(linebuf, "GET /logoff"         )>0 ) { logoff = true; }
          if (strstr(linebuf, "Authorization: Basic")>0 ) { if ( validar_usuario(linebuf) )   {  autenticado = true;   } }  
          if (strstr(linebuf, "GET /css.css"        )>0 ) { indCss = true; }
          if (strstr(linebuf, "GET /js.js"          )>0 ) { indJs = true; }
          if (strstr(linebuf, "GET /atmel.pdf"      )>0 ) { indPdfDataSheet = true; }
          if (strstr(linebuf, "GET /uno.jpg"        )>0 ) { indJpgUno = true; }
          if (strstr(linebuf, "GET /favicon.ico"    )>0 ) { indFavicon = true; }
          
          
          memset(linebuf, 0, sizeof(linebuf));
          charCount = 0;
        } else if (c != '\r') {
          currentLineIsBlank = false;    // you've gotten a character on the current line
        }
      }
    }
    
    delay(1);           
    client.stop();      
  }
}
/**********************************************************************************
**************************************** 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;
}

/*
int base64_decode(char * output, char * input, int inputLen) {
  int i = 0, j = 0;
  int decLen = 0;
  unsigned char a3[3];
  unsigned char a4[4];
  while (inputLen--) {
    if (*input == '=') { break; }
    a4[i++] = *(input++);
    if (i == 4) {
      for (i = 0; i <4; i++)  { a4[i] = b64_lookup(a4[i]); }
      a4_to_a3(a3,a4);
      for (i = 0; i < 3; i++) { output[decLen++] = a3[i];  }
      i = 0;
    }
  }

  if (i) {
    for (j=i; j<4; j++)   { a4[j] = '\0';             }
    for (j=0; j<4; j++)   { a4[j] = b64_lookup(a4[j]);}
    a4_to_a3(a3,a4);
    for (j=0; j<i-1; j++) { output[decLen++] = a3[j]; }
  }
  output[decLen] = '\0';
  return decLen;
}
*/

//int base64_enc_len(int plainLen) {
//  int n = plainLen;
//  return (n + 2 - ((n + 2) % 3)) / 3 * 4;
//}

//int base64_dec_len(char * input, int inputLen) {
//  int i = 0;
//  int numEq = 0;
//  for(i = inputLen - 1; input[i] == '='; i--) { numEq++; }
//  return ((6 * inputLen) / 8) - numEq;
//}

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);
}

//void a4_to_a3(unsigned char * a3, unsigned char * a4) {
//  a3[0] = (a4[0] << 2) + ((a4[1] & 0x30) >> 4);
//  a3[1] = ((a4[1] & 0xf) << 4) + ((a4[2] & 0x3c) >> 2);
//  a3[2] = ((a4[2] & 0x3) << 6) + a4[3];
//}

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_ethernet();
}

void loop() {
  exec_ethernet();
}

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

domingo, 7 de junho de 2015

Arduino - Display LCD com 74HC595 e 74HC165

Mais um exemplo do uso dos 74HC595 e 74HC165, agora utilizando um Display LCD.

a montagem do circuito, para expansão de entradas e saídas, pode ser visto nesse link.

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

a explicação sobre o teclado matricial, pode ser visto aqui

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

Display LCD

Para ligar o Display LCD no Arduino, há uma biblioteca específica, que faz a comunicação entre os pinos do Arduino e o Display, porém nesse exemplo, o display não irá se comunicar diretamente com o Arduino, e sim com um dos 595 que são controlados pelo Arduino. Então para conseguir fazer a comunicação entre o Arduino e o Display LCD, foi necessário fazer alterações na biblioteca disponibilizada para tal. Como a biblioteca foi escrita para se comunicar com os pinos do Arduino, foi necessário, substituir todas as chamadas feitas a digitalWrite(pin, value) para exp1->write(pin, value). E todas as referencias a pinMode(pin, mode) foram removidas.

Essas alterações não foram feitas diretamente na biblioteca, o que eu fiz, foi copiar o conteúdo da biblioteca e colar dentro da sketch e fazer as devidas alterações, como pode ser visto no código-fonte abaixo. Ainda não criei uma biblioteca específica para isso, pois ainda não terminei de fazer todos os testes, então por enquanto, é assim mesmo que vai funcionar.

Vídeo




Código-Fonte:


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

/********************************************************************************************
*******************ponteiro para o expansor a ser instanciado  INICIO  **********************
*********************************************************************************************/

Expansor74HC595_74HC165 * exp1;

/********************************************************************************************
*******************ponteiro para o expansor a ser instanciado  FIM  *************************
*********************************************************************************************/

/********************************************************************************************
*******************CLASSE LiquidCrystal  INICIO  ********************************************
*********************************************************************************************/

#include <inttypes.h>
#include "Print.h"

// commands
#define LCD_CLEARDISPLAY 0x01
#define LCD_RETURNHOME 0x02
#define LCD_ENTRYMODESET 0x04
#define LCD_DISPLAYCONTROL 0x08
#define LCD_CURSORSHIFT 0x10
#define LCD_FUNCTIONSET 0x20
#define LCD_SETCGRAMADDR 0x40
#define LCD_SETDDRAMADDR 0x80

// flags for display entry mode
#define LCD_ENTRYRIGHT 0x00
#define LCD_ENTRYLEFT 0x02
#define LCD_ENTRYSHIFTINCREMENT 0x01
#define LCD_ENTRYSHIFTDECREMENT 0x00

// flags for display on/off control
#define LCD_DISPLAYON 0x04
#define LCD_DISPLAYOFF 0x00
#define LCD_CURSORON 0x02
#define LCD_CURSOROFF 0x00
#define LCD_BLINKON 0x01
#define LCD_BLINKOFF 0x00

// flags for display/cursor shift
#define LCD_DISPLAYMOVE 0x08
#define LCD_CURSORMOVE 0x00
#define LCD_MOVERIGHT 0x04
#define LCD_MOVELEFT 0x00

// flags for function set
#define LCD_8BITMODE 0x10
#define LCD_4BITMODE 0x00
#define LCD_2LINE 0x08
#define LCD_1LINE 0x00
#define LCD_5x10DOTS 0x04
#define LCD_5x8DOTS 0x00

class LiquidCrystal : public Print {
public:
  LiquidCrystal(uint8_t rs, uint8_t enable,
  uint8_t d0, uint8_t d1, uint8_t d2, uint8_t d3,
  uint8_t d4, uint8_t d5, uint8_t d6, uint8_t d7);
  LiquidCrystal(uint8_t rs, uint8_t rw, uint8_t enable,
  uint8_t d0, uint8_t d1, uint8_t d2, uint8_t d3,
  uint8_t d4, uint8_t d5, uint8_t d6, uint8_t d7);
  LiquidCrystal(uint8_t rs, uint8_t rw, uint8_t enable,
  uint8_t d0, uint8_t d1, uint8_t d2, uint8_t d3);
  LiquidCrystal(uint8_t rs, uint8_t enable,
  uint8_t d0, uint8_t d1, uint8_t d2, uint8_t d3);

  void init(uint8_t fourbitmode, uint8_t rs, uint8_t rw, uint8_t enable,
     uint8_t d0, uint8_t d1, uint8_t d2, uint8_t d3,
     uint8_t d4, uint8_t d5, uint8_t d6, uint8_t d7);
    
  void begin(uint8_t cols, uint8_t rows, uint8_t charsize = LCD_5x8DOTS);

  void clear();
  void home();

  void noDisplay();
  void display();
  void noBlink();
  void blink();
  void noCursor();
  void cursor();
  void scrollDisplayLeft();
  void scrollDisplayRight();
  void leftToRight();
  void rightToLeft();
  void autoscroll();
  void noAutoscroll();

  void setRowOffsets(int row1, int row2, int row3, int row4);
  void createChar(uint8_t, uint8_t[]);
  void setCursor(uint8_t, uint8_t); 
  virtual size_t write(uint8_t);
  void command(uint8_t);
  
  using Print::write;
private:
  void send(uint8_t, uint8_t);
  void write4bits(uint8_t);
  void write8bits(uint8_t);
  void pulseEnable();

  uint8_t _rs_pin; // LOW: command.  HIGH: character.
  uint8_t _rw_pin; // LOW: write to LCD.  HIGH: read from LCD.
  uint8_t _enable_pin; // activated by a HIGH pulse.
  uint8_t _data_pins[8];

  uint8_t _displayfunction;
  uint8_t _displaycontrol;
  uint8_t _displaymode;

  uint8_t _initialized;

  uint8_t _numlines;
  uint8_t _row_offsets[4];
};



#include <stdio.h>
#include <string.h>
#include <inttypes.h>
#include "Arduino.h"

// When the display powers up, it is configured as follows:
//
// 1. Display clear
// 2. Function set: 
//    DL = 1; 8-bit interface data 
//    N = 0; 1-line display 
//    F = 0; 5x8 dot character font 
// 3. Display on/off control: 
//    D = 0; Display off 
//    C = 0; Cursor off 
//    B = 0; Blinking off 
// 4. Entry mode set: 
//    I/D = 1; Increment by 1 
//    S = 0; No shift 
//
// Note, however, that resetting the Arduino doesn't reset the LCD, so we
// can't assume that its in that state when a sketch starts (and the
// LiquidCrystal constructor is called).

LiquidCrystal::LiquidCrystal(uint8_t rs, uint8_t rw, uint8_t enable,
        uint8_t d0, uint8_t d1, uint8_t d2, uint8_t d3,
        uint8_t d4, uint8_t d5, uint8_t d6, uint8_t d7)
{
  init(0, rs, rw, enable, d0, d1, d2, d3, d4, d5, d6, d7);
}

LiquidCrystal::LiquidCrystal(uint8_t rs, uint8_t enable,
        uint8_t d0, uint8_t d1, uint8_t d2, uint8_t d3,
        uint8_t d4, uint8_t d5, uint8_t d6, uint8_t d7)
{
  init(0, rs, 255, enable, d0, d1, d2, d3, d4, d5, d6, d7);
}

LiquidCrystal::LiquidCrystal(uint8_t rs, uint8_t rw, uint8_t enable,
        uint8_t d0, uint8_t d1, uint8_t d2, uint8_t d3)
{
  init(1, rs, rw, enable, d0, d1, d2, d3, 0, 0, 0, 0);
}

LiquidCrystal::LiquidCrystal(uint8_t rs,  uint8_t enable,
        uint8_t d0, uint8_t d1, uint8_t d2, uint8_t d3)
{
  init(1, rs, 255, enable, d0, d1, d2, d3, 0, 0, 0, 0);
}

void LiquidCrystal::init(uint8_t fourbitmode, uint8_t rs, uint8_t rw, uint8_t enable,
    uint8_t d0, uint8_t d1, uint8_t d2, uint8_t d3,
    uint8_t d4, uint8_t d5, uint8_t d6, uint8_t d7)
{
  _rs_pin = rs;
  _rw_pin = rw;
  _enable_pin = enable;
  
  _data_pins[0] = d0;
  _data_pins[1] = d1;
  _data_pins[2] = d2;
  _data_pins[3] = d3; 
  _data_pins[4] = d4;
  _data_pins[5] = d5;
  _data_pins[6] = d6;
  _data_pins[7] = d7; 

  //pinMode(_rs_pin, OUTPUT);
  // we can save 1 pin by not using RW. Indicate by passing 255 instead of pin#
  if (_rw_pin != 255) { 
    //pinMode(_rw_pin, OUTPUT);
  }
  //pinMode(_enable_pin, OUTPUT);
  
  if (fourbitmode)
    _displayfunction = LCD_4BITMODE | LCD_1LINE | LCD_5x8DOTS;
  else 
    _displayfunction = LCD_8BITMODE | LCD_1LINE | LCD_5x8DOTS;
  
  begin(16, 1);  
}

void LiquidCrystal::begin(uint8_t cols, uint8_t lines, uint8_t dotsize) {
  if (lines > 1) {
    _displayfunction |= LCD_2LINE;
  }
  _numlines = lines;

  setRowOffsets(0x00, 0x40, 0x00 + cols, 0x40 + cols);  

  // for some 1 line displays you can select a 10 pixel high font
  if ((dotsize != LCD_5x8DOTS) && (lines == 1)) {
    _displayfunction |= LCD_5x10DOTS;
  }

  // SEE PAGE 45/46 FOR INITIALIZATION SPECIFICATION!
  // according to datasheet, we need at least 40ms after power rises above 2.7V
  // before sending commands. Arduino can turn on way before 4.5V so we'll wait 50
  delayMicroseconds(50000); 
  // Now we pull both RS and R/W low to begin commands
  exp1->write(_rs_pin, LOW);
  exp1->write(_enable_pin, LOW);
  if (_rw_pin != 255) { 
    exp1->write(_rw_pin, LOW);
  }
  exp1->update();
  
  //put the LCD into 4 bit or 8 bit mode
  if (! (_displayfunction & LCD_8BITMODE)) {
    // this is according to the hitachi HD44780 datasheet
    // figure 24, pg 46

    // we start in 8bit mode, try to set 4 bit mode
    write4bits(0x03);
    delayMicroseconds(4500); // wait min 4.1ms

    // second try
    write4bits(0x03);
    delayMicroseconds(4500); // wait min 4.1ms
    
    // third go!
    write4bits(0x03); 
    delayMicroseconds(150);

    // finally, set to 4-bit interface
    write4bits(0x02); 
  } else {
    // this is according to the hitachi HD44780 datasheet
    // page 45 figure 23

    // Send function set command sequence
    command(LCD_FUNCTIONSET | _displayfunction);
    delayMicroseconds(4500);  // wait more than 4.1ms

    // second try
    command(LCD_FUNCTIONSET | _displayfunction);
    delayMicroseconds(150);

    // third go
    command(LCD_FUNCTIONSET | _displayfunction);
  }

  // finally, set # lines, font size, etc.
  command(LCD_FUNCTIONSET | _displayfunction);  

  // turn the display on with no cursor or blinking default
  _displaycontrol = LCD_DISPLAYON | LCD_CURSOROFF | LCD_BLINKOFF;  
  display();

  // clear it off
  clear();

  // Initialize to default text direction (for romance languages)
  _displaymode = LCD_ENTRYLEFT | LCD_ENTRYSHIFTDECREMENT;
  // set the entry mode
  command(LCD_ENTRYMODESET | _displaymode);

}

void LiquidCrystal::setRowOffsets(int row0, int row1, int row2, int row3)
{
 _row_offsets[0] = row0;
 _row_offsets[1] = row1;
 _row_offsets[2] = row2;
 _row_offsets[3] = row3;
}

/********** high level commands, for the user! */
void LiquidCrystal::clear()
{
  command(LCD_CLEARDISPLAY);  // clear display, set cursor position to zero
  delayMicroseconds(2000);  // this command takes a long time!
}

void LiquidCrystal::home()
{
  command(LCD_RETURNHOME);  // set cursor position to zero
  delayMicroseconds(2000);  // this command takes a long time!
}

void LiquidCrystal::setCursor(uint8_t col, uint8_t row)
{
  const size_t max_lines = sizeof(_row_offsets) / sizeof(*_row_offsets);
  if ( row >= max_lines ) {
    row = max_lines - 1;    // we count rows starting w/0
  }
  if ( row >= _numlines ) {
    row = _numlines - 1;    // we count rows starting w/0
  }
  
  command(LCD_SETDDRAMADDR | (col + _row_offsets[row]));
}

// Turn the display on/off (quickly)
void LiquidCrystal::noDisplay() {
  _displaycontrol &= ~LCD_DISPLAYON;
  command(LCD_DISPLAYCONTROL | _displaycontrol);
}
void LiquidCrystal::display() {
  _displaycontrol |= LCD_DISPLAYON;
  command(LCD_DISPLAYCONTROL | _displaycontrol);
}

// Turns the underline cursor on/off
void LiquidCrystal::noCursor() {
  _displaycontrol &= ~LCD_CURSORON;
  command(LCD_DISPLAYCONTROL | _displaycontrol);
}
void LiquidCrystal::cursor() {
  _displaycontrol |= LCD_CURSORON;
  command(LCD_DISPLAYCONTROL | _displaycontrol);
}

// Turn on and off the blinking cursor
void LiquidCrystal::noBlink() {
  _displaycontrol &= ~LCD_BLINKON;
  command(LCD_DISPLAYCONTROL | _displaycontrol);
}
void LiquidCrystal::blink() {
  _displaycontrol |= LCD_BLINKON;
  command(LCD_DISPLAYCONTROL | _displaycontrol);
}

// These commands scroll the display without changing the RAM
void LiquidCrystal::scrollDisplayLeft(void) {
  command(LCD_CURSORSHIFT | LCD_DISPLAYMOVE | LCD_MOVELEFT);
}
void LiquidCrystal::scrollDisplayRight(void) {
  command(LCD_CURSORSHIFT | LCD_DISPLAYMOVE | LCD_MOVERIGHT);
}

// This is for text that flows Left to Right
void LiquidCrystal::leftToRight(void) {
  _displaymode |= LCD_ENTRYLEFT;
  command(LCD_ENTRYMODESET | _displaymode);
}

// This is for text that flows Right to Left
void LiquidCrystal::rightToLeft(void) {
  _displaymode &= ~LCD_ENTRYLEFT;
  command(LCD_ENTRYMODESET | _displaymode);
}

// This will 'right justify' text from the cursor
void LiquidCrystal::autoscroll(void) {
  _displaymode |= LCD_ENTRYSHIFTINCREMENT;
  command(LCD_ENTRYMODESET | _displaymode);
}

// This will 'left justify' text from the cursor
void LiquidCrystal::noAutoscroll(void) {
  _displaymode &= ~LCD_ENTRYSHIFTINCREMENT;
  command(LCD_ENTRYMODESET | _displaymode);
}

// Allows us to fill the first 8 CGRAM locations
// with custom characters
void LiquidCrystal::createChar(uint8_t location, uint8_t charmap[]) {
  location &= 0x7; // we only have 8 locations 0-7
  command(LCD_SETCGRAMADDR | (location << 3));
  for (int i=0; i<8; i++) {
    write(charmap[i]);
  }
}

/*********** mid level commands, for sending data/cmds */

inline void LiquidCrystal::command(uint8_t value) {
  send(value, LOW);
}

inline size_t LiquidCrystal::write(uint8_t value) {
  send(value, HIGH);
  return 1; // assume sucess
}

/************ low level data pushing commands **********/

// write either command or data, with automatic 4/8-bit selection
void LiquidCrystal::send(uint8_t value, uint8_t mode) {
  exp1->write(_rs_pin, mode);

  // if there is a RW pin indicated, set it low to Write
  if (_rw_pin != 255) { 
    exp1->write(_rw_pin, LOW);
  }
  
  exp1->update();
  
  if (_displayfunction & LCD_8BITMODE) {
    write8bits(value); 
  } else {
    write4bits(value>>4);
    write4bits(value);
  }
}

void LiquidCrystal::pulseEnable(void) {
  exp1->write(_enable_pin, LOW);
  exp1->update();
  delayMicroseconds(1);    
  exp1->write(_enable_pin, HIGH);
  exp1->update();
  delayMicroseconds(1);    // enable pulse must be >450ns
  exp1->write(_enable_pin, LOW);
  exp1->update();
  delayMicroseconds(100);   // commands need > 37us to settle
}

void LiquidCrystal::write4bits(uint8_t value) {
  for (int i = 0; i < 4; i++) {
    //pinMode(_data_pins[i], OUTPUT);
    exp1->write(_data_pins[i], (value >> i) & 0x01);
  }
  exp1->update();

  pulseEnable();
}

void LiquidCrystal::write8bits(uint8_t value) {
  for (int i = 0; i < 8; i++) {
    //pinMode(_data_pins[i], OUTPUT);
    exp1->write(_data_pins[i], (value >> i) & 0x01);
  }
  exp1->update();
  
  pulseEnable();
}


/********************************************************************************************
*******************CLASSE LiquidCrystal  FIM  ***********************************************
*********************************************************************************************/

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



/****************************************************************
*********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***
*****************************************************************/
  
  
//Criando um objeto da classe LiquidCrystal e 
//inicializando com os pinos da interface.
LiquidCrystal lcd(7, 6, 5, 4, 3, 2);  //esses pinos, são os pinos do 595 e não do arduino
  
  
/****************************************************************
********************************setup e loop**********************
*****************************************************************/

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

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

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