Publicidade:

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

sábado, 12 de setembro de 2015

Arduino - DIY - Módulo Gravador Attiny85

Como fazer um módulo gravador para Attiny que encaixa no Arduino.

Download: https://docs.google.com/uc?export=download&id=0B9ZzXhiNwNSrUkVpbXpKYWdCbFU

Para ver o artigo sobre os módulos para 74HC595, clique no link abaixo.

http://fabianoallex.blogspot.com.br/2015/08/arduino-modulo-expansor-74hc595.html


Vídeo:




Fotos:













Veja nesse vídeo como fazer a montagem dos componentes:


quinta-feira, 13 de agosto de 2015

Arduino - Modulo Expansor 74HC595



Download:

Módulo com 01 CI: https://docs.google.com/uc?export=download&id=0B9ZzXhiNwNSrV2tMNGNBNkpDV1E
Módulo com 02 CI:  https://docs.google.com/uc?export=download&id=0B9ZzXhiNwNSrZk1oVGZyV3VYSU0


Vídeo demonstrando os módulos funcionando.



Fotos:













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 = 13; 
const int PIN_LATCH = 12; 
const int PIN_DATA  = 11; 
 
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->writeByte(2, b);
    exp1->send();
   
    delay(500);
  }
   
  exp1->clear();
  delay(500); 
}

Arduino - NTP - Acessando Servidor de Hora pela Internet ou Rede

Olá Pessoal,

No vídeo abaixo mostro a execução de um sketch que solicita a hora atual a um servidor de horas. Esse Sketch foi baseado inicialmente no exemplo que vem com o Arduino, como mostrado na imagem abaixo e posteriormente alterado para que fosse possível utilizar o DNS com o Arduino 1.0. e disponibilizado nesse endereço: https://gitlab.com/mharizanov/Udp-NTP-Client-with-DNS-on-Arduino/blob/master/arduino_NTP_DNS.ino




Baseado nesse exemplo, fiz algumas modificações para que fosse possível extrair além de hora, minuto e segundo, extrair também dia da semana, dia, mês e ano. Pra isso foi preciso criar uma função que lesse o Unix Time e extraísse data e hora completa. No código, essa função se chama void localTime(...). Essa função foi baseada no funcionamento interno da Biblioteca DateTime (http://playground.arduino.cc/Code/DateTime), que não teve, nesse exemplo, a necessidade de ser utilizada totalmente.

No Brasil um serviço disponível é o http://ntp.br/ o qual possui alguns IPs que fornecem a hora via protocolo NTP (https://pt.wikipedia.org/wiki/Network_Time_Protocol).

Servidores NTP.br
a.st1.ntp.br200.160.7.186 e 2001:12ff:0:7::186
b.st1.ntp.br201.49.148.135
c.st1.ntp.br200.186.125.195
d.st1.ntp.br200.20.186.76
a.ntp.br200.160.0.8 e 2001:12ff::8
b.ntp.br200.189.40.8
c.ntp.br200.192.232.8
gps.ntp.br200.160.7.193 e 2001:12ff:0:7::193

Há Vários outros sites com esse serviço e há ainda a possibilidade de acessar através de um servidor próprio e interno.

A partir desse exemplo, é possível atualizar um RTC, ou mesmo ter data e hora sem a necessidade de um modulo RTC. Mas é importante observar que não é aconselhável fazer essa consulta repetidamente para toda vez que quiser saber a hora ou mostrar ela em algum lugar. O ideal seria ter um período de, por exemplo, a cada 24 horas fazer essa atualização. Uma estratégia pra quem quer ter a data e hora no Arduino sem um rtc, seria ter uma variável que armazenasse o Unix Time lido, e outra que armazenasse o millis do Arduino da ultima vez que foi feita a leitura da hora no servidor. Com isso, toda vez que quiser saber a hora atual, bastaria fazer a seguinte conta:

[millis atual] - [millis lido ao ler a hora]

Com isso, bastaria transformar o resultado acima em segundos e somar ao Unix Time e então fazer a extração da data e hora como mostrado no vídeo. Quem sabe eu faça um vídeo futuramente mostrando como fazer isso.

Outro detalhe importante, é saber o fuso horário retornado pelo servidor. Caso seja diferente, haverá a necessidade de ajustar o Unix time retornado ao fuso horário local.

vídeo:




código-fonte:


/*
alterado por Fabiano A. Arndt,
baseados nos créditos abaixo.

fabianoallex@gmail.com
www.youtube.com/fabianoallex
*/

/*
Udp NTP Client with DNS
I updated the example code to use DNS resolution from the Arduino 1.0 IDE
Basically tries to use the load-balanced NTP servers which are
"magically" returned from DNS queries on pool.ntp.org
If DNS fails, fall back to the hardcoded IP address
for time.nist.gov
bearpaw7 (github), 01DEC2011
This code is also in the public domain.
*/

/*
Udp NTP Client

Get the time from a Network Time Protocol (NTP) time server
Demonstrates use of UDP sendPacket and ReceivePacket 
For more on NTP time servers and the messages needed to communicate with them, 
see http://en.wikipedia.org/wiki/Network_Time_Protocol

created 4 Sep 2010 
by Michael Margolis
modified 17 Sep 2010
by Tom Igoe

This code is in the public domain.

*/

/**********************************************************************************
************************************BIBLIOTECAS************************************
**********************************************************************************/
#include <SPI.h>   
#include <Ethernet.h>
#include <EthernetUdp.h>
#include "Dns.h"
/**********************************************************************************
************************************FIM BIBLIOTECAS********************************
**********************************************************************************/

/**********************************************************************************
************************************ETHERNET CONFIG/FUNCTIONS**********************
***********************************************************************************
se for rodar numa rede com firewall, verifique se os ip utilizado está liberado.
Servidores da NTP.br
a.st1.ntp.br 200.160.7.186 e 2001:12ff:0:7::186
b.st1.ntp.br 201.49.148.135
c.st1.ntp.br 200.186.125.195
d.st1.ntp.br 200.20.186.76
a.ntp.br 200.160.0.8 e 2001:12ff::8
b.ntp.br 200.189.40.8
c.ntp.br 200.192.232.8
**********************************************************************************/

byte mac[] = {  0x00, 0x00, 0xAA, 0xBB, 0xCC, 0xDD};
unsigned int localPort = 8888;   // local port to listen for UDP packets
IPAddress timeServer(200, 192, 232, 8);  // time.nist.gov NTP server (fallback) - segunda tentativa caso a primeira de erro

const int NTP_PACKET_SIZE= 48;    // NTP time stamp is in the first 48 bytes of the message
byte packetBuffer[ NTP_PACKET_SIZE];   // buffer to hold incoming and outgoing packets 

//host para a primeira tentativa
const char* host = "ntp02.oal.ul.pt";  // servidor da NTP.br - ver lista acima para todos os servidores da NTP.br
//const char* host = "192.168.200.254";  // servidor interno 01 - caso tenha um servidor de hora interno, pode ser configurado o nome ou ip na variavel host
//const char* host = "192.168.200.253";  // servidor interno 02

EthernetUDP Udp;
DNSClient Dns;
IPAddress rem_add;

// send an NTP request to the time server at the given address 
unsigned long sendNTPpacket(IPAddress& address) {
  // set all bytes in the buffer to 0
  memset(packetBuffer, 0, NTP_PACKET_SIZE); 

  // Initialize values needed to form NTP request
  // (see URL above for details on the packets)
  packetBuffer[0] = 0b11100011;   // LI, Version, Mode
  packetBuffer[1] = 0;    // Stratum, or type of clock
  packetBuffer[2] = 6;    // Polling Interval
  packetBuffer[3] = 0xEC;  // Peer Clock Precision

  // 8 bytes of zero for Root Delay & Root Dispersion
  packetBuffer[12] = 49; 
  packetBuffer[13] = 0x4E;
  packetBuffer[14] = 49;
  packetBuffer[15] = 52;
 
  // all NTP fields have been given values, now
  // you can send a packet requesting a timestamp: 
  Udp.beginPacket(address, 123); //NTP requests are to port 123
  Udp.write(packetBuffer,NTP_PACKET_SIZE);
  Udp.endPacket(); 
}

/**********************************************************************************
************************************FIM ETHERNET CONFIG/FUNCTIONS******************
**********************************************************************************/

/**********************************************************************************
************************************UNIX TIME FUNCTIONS****************************
***********************************************************************************
localTime --> converte o unix time para ano, mes, dia semana, dia, hora, 
              minuto e segundo
              função baseada na biblioteca dateTime, pois não era necessário usar 
              toda a biblioteca.
              (http://playground.arduino.cc/Code/DateTime)
              descrição original: 
                convert the given timep to time components
                this is a more compact version of the C library localtime function
**********************************************************************************/
#define LEAP_YEAR(_year) ((_year%4)==0)
static  byte monthDays[] = {31, 28, 31, 30 , 31, 30, 31, 31, 30, 31, 30, 31};

void localTime(unsigned long *timep, byte *psec, byte *pmin, byte *phour, byte *pday, byte *pwday, byte *pmonth, byte *pyear) {
  unsigned long long epoch =* timep;
  byte year;
  byte month, monthLength;
  unsigned long days;
  
  *psec  =  epoch % 60;
  epoch  /= 60; // now it is minutes
  *pmin  =  epoch % 60;
  epoch  /= 60; // now it is hours
  *phour =  epoch % 24;
  epoch  /= 24; // now it is days
  *pwday =  (epoch+4) % 7;
  
  year = 70;  
  days = 0;
  while((unsigned)(days += (LEAP_YEAR(year) ? 366 : 365)) <= epoch) { year++; }
  *pyear=year; // *pyear is returned as years from 1900
  
  days  -= LEAP_YEAR(year) ? 366 : 365;
  epoch -= days; // now it is days in this year, starting at 0
  
  for (month=0; month<12; month++) {
    monthLength = ( (month==1) && LEAP_YEAR(year) ) ? 29 : monthDays[month];  // month==1 -> february
    if (epoch >= monthLength) { epoch -= monthLength; } else { break; }
  }
  
  *pmonth = month;  // jan is month 0
  *pday   = epoch+1;  // day of month
}

/**********************************************************************************
************************************FIM UNIX TIME FUNCTIONS************************
**********************************************************************************/

/**********************************************************************************
**************************************** FUNÇÕES FORMATAR DATA/HORA ***************
**********************************************************************************/
String zero(int a){ if(a>=10) {return (String)a+"";} else { return "0"+(String)a;} }

String diaSemana(byte dia){
  String str[] = {"Domingo", "Segunda-feira", "Terça-feira", "Quarta-feira", "Quinta-feira", "Sexta-feira", "Sabado"};
  return str[dia];
}
/**********************************************************************************
****************************************FIIM FUNÇÕES FORMATAR DATA/HORA ***********
**********************************************************************************/

/**********************************************************************************
**************************************** SETUP / LOOP *****************************
**********************************************************************************/
void setup() {
  pinMode(4, OUTPUT);      //ANTES DE INICIAR O ETHERNET, DESABILITA O SDCARD
  digitalWrite(4, HIGH);

  Serial.begin(9600);
  if (Ethernet.begin(mac) == 0) { Serial.println("Failed to configure Ethernet using DHCP"); }
  
  Udp.begin(localPort);
  Dns.begin(Ethernet.dnsServerIP() );
}

void loop() {
  if(Dns.getHostByName(host, rem_add) == 1 ){
    Serial.println("DNS resolve...");  
    Serial.print(host);
    Serial.print(" = ");
    Serial.println(rem_add);
    sendNTPpacket(rem_add);
  } else {
    Serial.println("DNS fail...");
    Serial.print("time.nist.gov = ");
    Serial.println(timeServer); // caso a primeira tentativa não retorne um host válido
    sendNTPpacket(timeServer);  // send an NTP packet to a time server
  }
  
  delay(1000); //aguarda um segundo, para receber os dados enviados.
  
  if ( Udp.parsePacket() ) {  
    // We've received a packet, read the data from it
    Udp.read(packetBuffer, NTP_PACKET_SIZE);  // read the packet into the buffer
  
    // the timestamp starts at byte 40 of the received packet and is four bytes,
    // or two words, long. First, esxtract the two words:
    unsigned long highWord = word(packetBuffer[40], packetBuffer[41]);
    unsigned long lowWord = word(packetBuffer[42], packetBuffer[43]);  
    
    // combine the four bytes (two words) into a long integer
    // this is NTP time (seconds since Jan 1 1900):
    unsigned long secsSince1900 = highWord << 16 | lowWord;  
    Serial.print("Segundos desde 1 de Jan. de 1900 = " );
    Serial.println(secsSince1900);     
  
    Serial.print("Unix time = ");
    const unsigned long seventyYears = 2208988800UL;      // Unix time starts on Jan 1 1970. In seconds, that's 2208988800:
    unsigned long epoch = secsSince1900 - seventyYears;  //desconta 70 anos
    // print Unix time:
    Serial.println(epoch);         

    byte ano, mes, dia, dia_semana, hora, minuto, segundo;            
    localTime(&epoch, &segundo, &minuto, &hora, &dia, &dia_semana, &mes, &ano); //extrai data e hora do unix time
    
    Serial.print("Ano: ");
    Serial.println(ano+1900);
    Serial.print("Mes: ");
    Serial.println(mes+1);
    Serial.print("Dia da semana: ");
    Serial.println(dia_semana);
    Serial.print("Dia: ");
    Serial.println(dia);
    Serial.print("Hora: ");
    Serial.println(hora);
    Serial.print("minunto: ");
    Serial.println(minuto);
    Serial.print("segundo: ");
    Serial.println(segundo);
    
    String s = diaSemana(dia_semana) + ", " + zero(dia) + "/" + zero(mes+1) + "/" + (ano+1900) + " " + zero(hora) + ":" + zero(minuto) + ":" + zero(segundo);
    
    Serial.println(s);
    Serial.println(" ");
  }
  
  delay(10000); //atualiza novamente em 10 segundos
}



/**********************************************************************************
**************************************** FIM SETUP / LOOP *************************
**********************************************************************************/

quinta-feira, 23 de julho de 2015

Arduino - Voltímetro com display de 7 Segmentos + 74hc595

No post anterior (aqui) mostrei como fazer um voltímetro com o Arduino utilizando displays de 7 segmentos. Para isso foi necessário utilizar 12 pinos do Arduino.

Visando reduzir a quantidade de pinos utilizados, alterei o circuito para trabalhar com o 74HC595, que é um expansor de portas bem útil (veja aqui outros exemplos desse CI).

O primeiro 74HC595 foi utilizado para acionar cada um dos 8 segmentos do display, enquanto que o segundo foi utilizado pra controlar qual dos displays será exibido a cada vez. Como no exemplo foi utilizado apenas 4 displays, sobraram 4 pinos do segundo 595, sendo possível expandir esse exemplo para até oito displays.

Para saber como fazer as ligações entre os dois expansores e o Arduino, veja o esquema abaixo.



Ligações entre Arduino / 74HC595

Arduino - pino 08 ---> 74HC595 - DS - DATA PIN        (Ligar apenas no primeiro 595)
Arduino - pino 09 ---> 74HC595 - STCP- LATCH PIN  (Ligar em todos os 595)
Arduino - pino 10 ---> 74HC595 - SHCP- CLOCK PIN (Ligar em todos os 595)

Ligações entre os 74HC595 e os Displays

PRIMEIRO 74HC595 - Q0 --> DP (segmento de ponto decimal)
PRIMEIRO 74HC595 - Q1 --> g
PRIMEIRO 74HC595 - Q2 --> f
PRIMEIRO 74HC595 - Q3 --> e
PRIMEIRO 74HC595 - Q4 --> d
PRIMEIRO 74HC595 - Q5 --> c
PRIMEIRO 74HC595 - Q6 --> b
PRIMEIRO 74HC595 - Q7 --> a

SEGUNDO 74HC595 - Q0 --> primeiro display
SEGUNDO 74HC595 - Q1 --> segundo display
SEGUNDO 74HC595 - Q2 --> terceiro display
SEGUNDO 74HC595 - Q3 --> quarto display
SEGUNDO 74HC595 - Q4..Q7 --> não utilizados (mas poderiam ser utilizados para gerenciar mais displays)

Verifique como ligar os demais pinos no esquema abaixo, ou verifique nesse site aqui.






Código-fonte:

/************************************************************************************************************
**********************************expansor 74hc595***********************************************************
************************************************************************************************************/

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 = 10;   //SHCP
const int PIN_LATCH = 9;    //STCP
const int PIN_DATA  = 8;    //DS
 
Expansor74HC595 *exp1;

/************************************************************************************************************
**********************************fim expansor 74hc595*******************************************************
************************************************************************************************************/
  
/************************************************************************************************************
**********************************display de 7 segmentos*****************************************************
************************************************************************************************************/
const int  d7seg_595_index        = 0;   //em qual dos 595´s será usado pra armazenar os segmentos a, b, c, d, e, f, g e dp
const int  d7seg_595_pin_enable[] = {8,9,10,11};               //pinos do 595 para habilitar os displays
const byte d7seg_digits[]         = {B11111100, B01100000, B11011010, B11110010, B01100110, 
                                     B10110110, B10111110, B11100000, B11111110, B11110110};  //Babcdefg.  0--9

void d7seg_write(int digit, int pos, boolean point=false) {
  exp1->startWrite();
  exp1->write(d7seg_595_pin_enable[pos], HIGH);
  exp1->writeByte(d7seg_595_index, point ? d7seg_digits[digit] | (1 << 0) : d7seg_digits[digit], LSBFIRST);  // | (1 << 0) --> alterar o bit que representa o dp (ponto) do display. envia para o 595
  exp1->send();
  
  delay(1);   
  
  exp1->write(d7seg_595_pin_enable[pos], LOW);
}

void d7seg_write_number(float f, int decimals=0) {
  f = (f+0.000001) * pow(10, decimals);
  for(int i=0; i<sizeof(d7seg_595_pin_enable)/sizeof(int); i++) {  
    d7seg_write( (unsigned int)(f/pow(10, i)) % 10, i,  (i!=0)&&(decimals==i) );  
  }
}
/************************************************************************************************************
**********************************fim display de 7 segmentos*************************************************
************************************************************************************************************/

/************************************************************************************************************
**********************************voltimetro*****************************************************************
************************************************************************************************************/
const unsigned long r1   = 1000000;  //resistor de 1M
const unsigned long r2   = 100000;   //resistor de 100K
const unsigned int  aRef = 5;        //referencia de 5v
float               tensao = 0;      //tensao lida
unsigned long       millis_ref = 0;
const unsigned long time_refresh = 500; //faz nova leitura a cada 500 ms

float get_tensao(int pin){
  return (analogRead(pin) * aRef) / 1023.0 * ( (r1+r2)/r2 );
}
/************************************************************************************************************
**********************************fim voltimetro*************************************************************
************************************************************************************************************/

/************************************************************************************************************
**********************************setup/loop*****************************************************************
************************************************************************************************************/
void setup(){
  exp1   = new Expansor74HC595(PIN_CLOCK, PIN_LATCH, PIN_DATA, 2);
}  

void loop() {
  if ( (millis()-millis_ref) > time_refresh ) { //intervalo de tempo pra atualizar a leitura. 
    tensao = get_tensao(A0); //calculo da tensão lida
    millis_ref = millis();
  }
  float f = tensao;
  d7seg_write_number(f, f>=1000 ? 0 : (f>=100 ? 1 : (f>=10 ? 2 : 3) ) );  //de acordo com o numero, mostra 0, 1, 2 ou 3 casas decimais
}

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

Veja também: montagem dos 74hc595


quarta-feira, 22 de julho de 2015

Arduino - Voltímetro com Display de 7 Segmentos

No post anterior (aqui) falei sobre como mostrar números com casas decimais em displays de 7 segmentos. Aproveitando esse mesmo exemplo, fiz um voltímetro, onde é possível ler a tensão de uma fonte externa e mostrar seu valor nos displays.

Vídeo:



código-fonte:

/************************************************************************************************************
**********************************display de 7 segmentos*****************************************************
************************************************************************************************************/
const int  d7seg_pin_segments[] = {6,7,8,9,10,11,12,13};   //pinos para os segmentos:  --> a b c d e f g .
const int  d7seg_pin_enable[]   = {2,3,4,5};               //pinos para habilitar os displays
const byte d7seg_digits[]       = {B11111100, B01100000, B11011010, B11110010, B01100110, 
                                   B10110110, B10111110, B11100000, B11111110, B11110110};  //Babcdefg.  0--9

void d7seg_write(int digit, int pos, boolean point=false) {
  digitalWrite(d7seg_pin_enable[pos], HIGH);
  for(int i=0;i<7;i++) { digitalWrite(d7seg_pin_segments[i],   d7seg_digits[digit] & (1 << (7-i))    ); }
  digitalWrite(d7seg_pin_segments[7], point);
  delay(1);  //alterar aqui pro valor mais adequado
  digitalWrite(d7seg_pin_enable[pos], LOW);
}

void d7seg_write_number(float f, int decimals=0) {
  f = (f+0.000001) * pow(10, decimals);
  for(int i=0; i<sizeof(d7seg_pin_enable)/sizeof(int); i++) {  
    d7seg_write( (unsigned int)(f/pow(10, i)) % 10, i,  (i!=0)&&(decimals==i) );  
  }
}
/************************************************************************************************************
**********************************fim display de 7 segmentos*************************************************
************************************************************************************************************/

/************************************************************************************************************
**********************************voltimetro*****************************************************************
************************************************************************************************************/
const unsigned long r1   = 1000000;  //resistor de 1M
const unsigned long r2   = 100000;   //resistor de 100K
const unsigned int  aRef = 5;        //referencia de 5v
float               tensao = 0;      //tensao lida
unsigned long       millis_ref = 0;
const unsigned long time_refresh = 500; //faz nova leitura a cada 500 ms

float get_voltage(int pin){
  return (analogRead(pin) * aRef) / 1023.0 * ( (r1+r2)/r2 );
}

void show_voltage(){
  if ( (millis()-millis_ref) > time_refresh ) { //intervalo de tempo pra atualizar a leitura. 
    tensao = get_voltage(A0); //calculo da tensão lida
    millis_ref = millis();
  }
  float f = tensao;
  d7seg_write_number(f, f>=1000 ? 0 : (f>=100 ? 1 : (f>=10 ? 2 : 3) ) );  //de acordo com o numero, mostra 0, 1, 2 ou 3 casas decimais
}
/************************************************************************************************************
**********************************fim voltimetro*************************************************************
************************************************************************************************************/

/************************************************************************************************************
**********************************setup/loop*****************************************************************
************************************************************************************************************/
void setup(){
  for(int i=2; i<=13; i++) { pinMode(i, OUTPUT); }
}  

void loop() {
  show_voltage();
}

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




Referência: http://br-arduino.org/2015/06/voltimetro-com-arduino-como-montar-programar-e-calibrar.html

terça-feira, 21 de julho de 2015

Arduino - Display de 7 Segmentos com Casas Decimais

Nesse post vou mostrar um exemplos simples e funcional de como mostrar números com casas decimais em displays de 7 segmentos diretamente com o Arduino, sem nenhum drive entre o Arduino e os displays.

O Exemplo mostrado no vídeo possui 4 displays em série, mas nada impede que sejam adicionados outros, bastando apenas modificar no código, a constante d7seg_pin_enable, onde deve ser informado os pinos a mais utilizados. No exemplo, foram utilizados os pinos, 2, 3, 4 e 5. E fisicamente, devem ser incluídos os componentes (displays, transistores e resistores) adicionais no circuito, seguindo o esquema abaixo.

Da mesma maneira que é possível incluir mais, pode-se também utilizar esse código, com menos displays, 3, 2 ou 1.

Os dígitos do display (números de zero a nove) são definidos através da constante d7seg_digits, no qual foram necessários apenas 10 bytes, sendo um byte para cada dígito que pode ser representado no em cada um dos displays.

Como um byte tem oito bits, foi possível definir um bit para cada segmento do display. Em termos de economia de memória essa é uma excelente tática, ao contrário de vários outros exemplos que podem ser encontrados na internet, os quais muitas vezes, não possuem os devidos cuidados na hora de definir os tipos corretos de dados para representar cada um dos dígitos. O único inconveniente ao utilizar bytes para representar dígitos, é a "dificuldade" que o programador menos experiente pode sentir ao tentar ler um determinado bit (segmento) na hora de ligar/desligar o display. O segredo para isso, é utilizar operadores de bitwise, o qual podemos ver nessa parte do código:

d7seg_digits[digit] & (1 << (7-i))

onde o "7-i", indica qual bit do byte que será lido.



No exemplo do vídeo tem um potenciômetro ligado ao pino A0. o valor lido no A0, que por padrão retorna um valor entre 0 e 1023, é convertido pra uma escala que vai entre 0 e 2000. Quando o valor a ser mostrado é maior que 1000, nenhuma casa decimal é mostrada. Já quando o número está entre 100 e 999 o valor a ser mostrado no display terá uma casa decimal, já para valores inferiores a 99, o valor aparece com 2 casas decimais. Isso foi feito pra não "perder" dígitos no caso de valores pequenos tendo uma maior precisão do valor lido em caso de números menores.





const int  d7seg_pin_segments[] = {6,7,8,9,10,11,12,13};   //pinos para os segmentos:  --> a b c d e f g .
const int  d7seg_pin_enable[]   = {2,3,4,5};               //pinos para habilitar os displays
const byte d7seg_digits[]       = {B11111100, B01100000, B11011010, B11110010, B01100110, 
                                   B10110110, B10111110, B11100000, B11111110, B11110110};  //Babcdefg.  0--9

void d7seg_write(int digit, int pos, boolean point=false) {
  digitalWrite(d7seg_pin_enable[pos], HIGH);
  for(int i=0;i<7;i++) { digitalWrite(d7seg_pin_segments[i],   d7seg_digits[digit] & (1 << (7-i))    ); }
  digitalWrite(d7seg_pin_segments[7], point);
  delay(1);  //alterar aqui pro valor mais adequado
  digitalWrite(d7seg_pin_enable[pos], LOW);
}

void d7seg_write_number(float f, int decimals=0) {
  f = (f+0.000001) * pow(10, decimals);
  for(int i=0; i<sizeof(d7seg_pin_enable)/sizeof(int); i++) {  
    d7seg_write( (unsigned int)(f/pow(10, i)) % 10, i,  (i!=0)&&(decimals==i) );  
  }
}

void setup(){
  for(int i=2; i<=13; i++) { pinMode(i, OUTPUT); }
}

float               f = 0;      //tensao lida
unsigned long       millis_ref = 0;
const unsigned long time_refresh = 500; //faz nova leitura a cada 500 ms

void loop() {
  
  if ( (millis()-millis_ref) > time_refresh ) { //intervalo de tempo pra atualizar a leitura. 
    f = ((analogRead(A0)) / 1023.0 * 2000.0);
    millis_ref = millis();
  }
  
  d7seg_write_number(f, f>=1000 ? 0 : (f>=100 ? 1 : 2) );  //de acordo com o numero, mostra 0, 1 ou 2 casas decimais

}

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