Publicidade:

quinta-feira, 15 de outubro de 2015

Arduino - LCD Big Numbers

Esse post demonstra vários exemplo de como gerar números grandes em displays LCD. Mais abaixo tem um exemplo de como utilizar mais de 8 caracteres customizáveis e ao final mostro um exemplo de um relógio em display LCD com números grandes.

São vários exemplos diferentes.

Vídeo 01:



Vídeo 02:


Código-fonte da primeira versão:

/*
Fabiano A. Arndt - 2015
www.youtube.com/user/fabianoallex
www.facebook.com/dicasarduino
fabianoallex@gmail.com
*/
   
#include <LiquidCrystal.h>
   
/*************************************************************************************************************
*******************************CLASSE LCD BIG NUMBERS*********************************************************
**************************************************************************************************************/
struct LCDNumber {
  byte top;
  byte bottom;
};
 
class LCDBigNumbers {
  private:
    LiquidCrystal * _lcd;
    int _row;
    int _col;
    long _value; /*0..100*/
     
    void _clear(){
      int cont = 1;
      long x = 9;
      while (_value > x){
        cont++;
        x = x * 10 + 9;
      }
      for (int i=0; i<cont; i++) {
        _lcd->setCursor(_col+i, _row);
        _lcd->print( " " );
        _lcd->setCursor(_col+i, _row+1);
        _lcd->print( " " );
      }
    }
  public:
    static byte c0[8];  
    static byte c1[8];  
    static byte c2[8];  
    static byte c3[8];
    static byte c4[8];
    static byte c5[8];  
    static byte c6[8];  
    static byte c7[8];  
    static LCDNumber _lcd_numbers[];
    
    void createChars() {
      _lcd->createChar(0, c0);
      _lcd->createChar(1, c1);
      _lcd->createChar(2, c2);
      _lcd->createChar(3, c3);
      _lcd->createChar(4, c4);
      _lcd->createChar(5, c5);
      _lcd->createChar(6, c6);
      _lcd->createChar(7, c7);
    }
     
    LCDBigNumbers(LiquidCrystal * lcd, int row, int col) {
      _lcd = lcd;      _row = row;      _col = col;
    }
     
    void setRow(int row){
      _clear();
      _row = row;
      setValue(_value);
    }
    void setCol(int col){
      _clear();
      _col = col;
      setValue(_value);
    }
     
    void setValue(long value){
      _clear();
      _value = value;
       
      int cont = 1;
      long x = 9;
      while (abs(_value) > x){
        cont++;
        x = x * 10 + 9;
      }
       
      for (int i=0; i<cont; i++) {
        int n = value / pow(10, cont-1-i);
        value = value - pow(10, cont-1-i) * n;
         
        _lcd->setCursor(_col+i, _row);
        _lcd->write( _lcd_numbers[n].top );
        _lcd->setCursor(_col+i, _row+1);
        _lcd->write( _lcd_numbers[n].bottom );
      }
       
    }
};

byte LCDBigNumbers::c0[8] = {B11111, B10001, B10001, B10001, B10001, B10001, B10001, B10001};
byte LCDBigNumbers::c1[8] = {B10001, B10001, B10001, B10001, B10001, B10001, B10001, B11111};
byte LCDBigNumbers::c2[8] = {B00001, B00001, B00001, B00001, B00001, B00001, B00001, B00001};
byte LCDBigNumbers::c3[8] = {B11111, B00001, B00001, B00001, B00001, B00001, B00001, B11111};
byte LCDBigNumbers::c4[8] = {B11111, B10000, B10000, B10000, B10000, B10000, B10000, B11111};
byte LCDBigNumbers::c5[8] = {B11111, B00001, B00001, B00001, B00001, B00001, B00001, B00001};    
byte LCDBigNumbers::c6[8] = {B11111, B10001, B10001, B10001, B10001, B10001, B10001, B11111};              
byte LCDBigNumbers::c7[8] = {B00001, B00001, B00001, B00001, B00001, B00001, B00001, B11111};


LCDNumber LCDBigNumbers::_lcd_numbers[] = { 
  {0, 1}, //0
  {2, 2}, //1
  {5, 4}, //2
  {3, 7}, //3
  {1, 2}, //4
  {4, 7}, //5
  {4, 1}, //6
  {5, 2}, //7
  {6, 1}, //8
  {6, 7} // 9
};
 

/*************************************************************************************************************
*******************************FIM CLASSE LCD BIG NUMBERS*****************************************************
**************************************************************************************************************/
   
   
LiquidCrystal lcd(12, 11, 5, 4, 3, 2);
LCDBigNumbers lcdNum(&lcd, 0,0); //inclui uma barra no lcd, primeira linha, coluna 8. tamanho 8
   
void setup()   {
  Serial.begin(9600);
  lcdNum.createChars();
  pinMode(44, OUTPUT);
  analogWrite(44, 255/6); //utilizado para aumentar o contraste
  lcd.begin(16, 2);
}
 
long i = 0;
int col = 0;
 
void loop() {
  lcdNum.setValue(i++ * 11);
   
  if (i>=10000) i = 0;
   
  if (i%10 == 0){
    lcdNum.setCol(col++);
    if (col >= 10){
      col = 0;
    }
  }
   
  delay(500);  
}


Código segunda versão:

 /*
Fabiano A. Arndt - 2015
www.youtube.com/user/fabianoallex
www.facebook.com/dicasarduino
fabianoallex@gmail.com
*/
   
#include <LiquidCrystal.h>
   
/*************************************************************************************************************
*******************************CLASSE LCD BIG NUMBERS*********************************************************
**************************************************************************************************************/
struct LCDNumber {
  byte top1;
  byte top2;
  byte top3;
  byte bottom1;
  byte bottom2;
  byte bottom3;
};
 
class LCDBigNumbers {
  private:
    LiquidCrystal * _lcd;
    int _row;
    int _col;
    long _value; /*0..100*/
     
    void _clear(){
      int cont = 1;
      long x = 9;
      while (_value > x){
        cont++;
        x = x * 10 + 9;
      }
      for (int i=0; i<cont; i++) {
        _lcd->setCursor(_col+i, _row);
        _lcd->print( "    " );
        _lcd->setCursor(_col+i, _row+1);
        _lcd->print( "    " );
      }
    }
  public:
    static byte c0[8];  //bottom
    static byte c1[8];  //top
    static byte c2[8];  //fill
    static byte c3[8];
    static byte c4[8];
    static byte c5[8];  //top-bottom 
    static LCDNumber _lcd_numbers[];
    
    void createChars() {
      _lcd->createChar(0, c0);
      _lcd->createChar(1, c1);
      _lcd->createChar(2, c2);
      _lcd->createChar(3, c3);
      _lcd->createChar(4, c4);
      _lcd->createChar(5, c5);
      //_lcd->createChar(6, c6);
      //_lcd->createChar(7, c7);
    }
     
    LCDBigNumbers(LiquidCrystal * lcd, int row, int col) {
      _lcd = lcd;      _row = row;      _col = col;
    }
     
    void setRow(int row){
      _clear();
      _row = row;
      setValue(_value);
    }
    void setCol(int col){
      _clear();
      _col = col;
      setValue(_value);
    }
     
    void setValue(long value){
      _clear();
      _value = value;
       
      int cont = 1;
      long x = 9;
      while (abs(_value) > x){
        cont++;
        x = x * 10 + 9;
      }
       
      for (int i=0; i<cont; i++) {
        int n = value / pow(10, cont-1-i);
        value = value - pow(10, cont-1-i) * n;
         
        _lcd->setCursor(_col+i*4, _row);
        _lcd_numbers[n].top1 == 9 ? _lcd->print(" ") : _lcd->write( _lcd_numbers[n].top1 );
        _lcd_numbers[n].top2 == 9 ? _lcd->print(" ") : _lcd->write( _lcd_numbers[n].top2 );
        _lcd_numbers[n].top3 == 9 ? _lcd->print(" ") : _lcd->write( _lcd_numbers[n].top3 );
        _lcd->setCursor(_col+i*4, _row+1);
        _lcd_numbers[n].bottom1 == 9 ? _lcd->print(" ") : _lcd->write( _lcd_numbers[n].bottom1 );
        _lcd_numbers[n].bottom2 == 9 ? _lcd->print(" ") : _lcd->write( _lcd_numbers[n].bottom2 );
        _lcd_numbers[n].bottom3 == 9 ? _lcd->print(" ") : _lcd->write( _lcd_numbers[n].bottom3 );
      }
    }
};

byte LCDBigNumbers::c0[8] = {B00000, B00000, B00000, B00000, B00000, B11111, B11111, B11111};  //bottom
byte LCDBigNumbers::c1[8] = {B11111, B11111, B11111, B00000, B00000, B00000, B00000, B00000};  //top
byte LCDBigNumbers::c2[8] = {B11111, B11111, B11111, B11111, B11111, B11111, B11111, B11111};  //fill
byte LCDBigNumbers::c3[8] = {B00000, B00000, B00001, B00011, B00011, B00001, B00000, B00000};
byte LCDBigNumbers::c4[8] = {B00000, B00000, B10000, B11000, B11000, B10000, B00000, B00000};
byte LCDBigNumbers::c5[8] = {B11111, B11111, B00000, B00000, B00000, B00000, B11111, B11111};   //top / bottom 

LCDNumber LCDBigNumbers::_lcd_numbers[] = { 
      {2, 1, 2, 2, 0, 2}, //0
      {1, 2, 9, 0, 2, 0}, //1
      {1, 5, 2, 2, 0, 0}, //2
      {1, 5, 2, 0, 0, 2}, //3
      {2, 0, 2, 9, 9, 2}, //4
      {2, 5, 1, 0, 0, 2}, //5
      {2, 5, 1, 2, 0, 2}, //6
      {1, 1, 2, 9, 9, 2}, //7
      {2, 5, 2, 2, 0, 2}, //8
      {2, 5, 2, 0, 0, 2} // 9
    };
/*************************************************************************************************************
*******************************FIM CLASSE LCD BIG NUMBERS*****************************************************
**************************************************************************************************************/
   
   
LiquidCrystal lcd(12, 11, 5, 4, 3, 2);
LCDBigNumbers lcdNum(&lcd, 0,0); //inclui uma barra no lcd, primeira linha, coluna 1
   
void setup()   {
  Serial.begin(9600);
  lcdNum.createChars();
  lcdNum.setCol(1); //muda para a coluna 1
  
  pinMode(44, OUTPUT);  //arduino mega - pino de contraste do display
  analogWrite(44, 255/6); //utilizado para aumentar o contraste
  lcd.begin(16, 2);
}
 
long i = 0;
int col = 0;
 
void loop() {
  lcdNum.setValue(i++ * 11);
   
  if (i>=10000) { i = 0; }

  delay(500);  
}


Código terceira e quarta versão:

obs. para inverter entre a terceira e quarta versão verificar o código comentado onde os caracteres customizados são definidos.

/*
Fabiano A. Arndt - 2015
www.youtube.com/user/fabianoallex
www.facebook.com/dicasarduino
fabianoallex@gmail.com
*/
   
#include <LiquidCrystal.h>
   
/*************************************************************************************************************
*******************************CLASSE LCD BIG NUMBERS*********************************************************
**************************************************************************************************************/
struct LCDNumber {
  byte top1;
  byte top2;
  byte bottom1;
  byte bottom2;
};
 
class LCDBigNumbers {
  private:
    LiquidCrystal * _lcd;
    int _row;
    int _col;
    long _value; /*0..100*/
    byte _dist;  /*distancia entre digitos*/
     
    void _clear(){
      int cont = 1;
      long x = 9;
      while (_value > x){
        cont++;
        x = x * 10 + 9;
      }
      for (int i=0; i<cont; i++) {
        _lcd->setCursor(_col+i, _row);
        _lcd->print( "  " );
        for (byte b=0;b<_dist; b++){ _lcd->print( " " ); }
        _lcd->setCursor(_col+i, _row+1);
        _lcd->print( "  " );
        for (byte b=0;b<_dist; b++){ _lcd->print( " " ); }
      }
    }
  public:
    static byte c0[8];  
    static byte c1[8];  
    static byte c2[8];  
    static byte c3[8];
    static byte c4[8];
    static byte c5[8];  
    static byte c6[8];  
    static byte c7[8];  
    static LCDNumber _lcd_numbers[];
    
    void createChars() {
      _lcd->createChar(0, c0);
      _lcd->createChar(1, c1);
      _lcd->createChar(2, c2);
      _lcd->createChar(3, c3);
      _lcd->createChar(4, c4);
      _lcd->createChar(5, c5);
      _lcd->createChar(6, c6);
      _lcd->createChar(7, c7);
    }
     
    LCDBigNumbers(LiquidCrystal * lcd, int row, int col) {
      _lcd = lcd;      
      _row = row;      
      _col = col;
      _dist = 0;  //distancia entre os numeros
    }
     
    void setRow(int row){
      _clear();
      _row = row;
      setValue(_value);
    }
    void setCol(int col){
      _clear();
      _col = col;
      setValue(_value);
    }
    
    void setDist(int dist){
      _clear();
      _dist = dist;
      setValue(_value);
    }
     
    void setValue(long value){
      _clear();
      _value = value;
       
      int cont = 1;
      long x = 9;
      while (abs(_value) > x){
        cont++;
        x = x * 10 + 9;
      }
       
      for (int i=0; i<cont; i++) {
        int n = value / pow(10, cont-1-i);
        value = value - pow(10, cont-1-i) * n;
         
        _lcd->setCursor(_col+i*(2+_dist), _row);
        _lcd_numbers[n].top1 == 9 ? _lcd->print(" ") : _lcd->write( _lcd_numbers[n].top1 );
        _lcd_numbers[n].top2 == 9 ? _lcd->print(" ") : _lcd->write( _lcd_numbers[n].top2 );
        _lcd->setCursor(_col+i*(2+_dist), _row+1);
        _lcd_numbers[n].bottom1 == 9 ? _lcd->print(" ") : _lcd->write( _lcd_numbers[n].bottom1 );
        _lcd_numbers[n].bottom2 == 9 ? _lcd->print(" ") : _lcd->write( _lcd_numbers[n].bottom2 );
      }
    }
};

//byte LCDBigNumbers::c0[8] = {B11111, B11111, B11000, B11000, B11000, B11000, B11000, B11000}; 
//byte LCDBigNumbers::c1[8] = {B11000, B11000, B11000, B11000, B11000, B11000, B11111, B11111};  
//byte LCDBigNumbers::c2[8] = {B11111, B11111, B00011, B00011, B00011, B00011, B00011, B00011};  
//byte LCDBigNumbers::c3[8] = {B00011, B00011, B00011, B00011, B00011, B00011, B11111, B11111};
//byte LCDBigNumbers::c4[8] = {B11111, B11111, B11000, B11000, B11000, B11000, B11111, B11111};  
//byte LCDBigNumbers::c5[8] = {B11111, B11111, B00011, B00011, B00011, B00011, B11111, B11111};  
//byte LCDBigNumbers::c6[8] = {B11111, B11111, B00000, B00000, B00000, B00000, B00000, B00000};  
//byte LCDBigNumbers::c7[8] = {B11111, B11111, B00000, B00000, B00000, B00000, B11111, B11111};  

byte LCDBigNumbers::c0[8] = {B11111, B11000, B11000, B11000, B11000, B11000, B11000, B11000}; 
byte LCDBigNumbers::c1[8] = {B11000, B11000, B11000, B11000, B11000, B11000, B11000, B11111};  
byte LCDBigNumbers::c2[8] = {B11111, B00011, B00011, B00011, B00011, B00011, B00011, B00011};  
byte LCDBigNumbers::c3[8] = {B00011, B00011, B00011, B00011, B00011, B00011, B00011, B11111};
byte LCDBigNumbers::c4[8] = {B11111, B11000, B11000, B11000, B11000, B11000, B11000, B11111};  
byte LCDBigNumbers::c5[8] = {B11111, B00011, B00011, B00011, B00011, B00011, B00011, B11111};  
byte LCDBigNumbers::c6[8] = {B11111, B00000, B00000, B00000, B00000, B00000, B00000, B00000};  
byte LCDBigNumbers::c7[8] = {B11111, B00000, B00000, B00000, B00000, B00000, B00000, B11111}; 

//byte LCDBigNumbers::c0[8] = {B11111, B10000, B10000, B10000, B10000, B10000, B10000, B10000}; 
//byte LCDBigNumbers::c1[8] = {B10000, B10000, B10000, B10000, B10000, B10000, B10000, B11111};  
//byte LCDBigNumbers::c2[8] = {B11111, B00001, B00001, B00001, B00001, B00001, B00001, B00001};  
//byte LCDBigNumbers::c3[8] = {B00001, B00001, B00001, B00001, B00001, B00001, B00001, B11111};
//byte LCDBigNumbers::c4[8] = {B11111, B10000, B10000, B10000, B10000, B10000, B10000, B11111};  
//byte LCDBigNumbers::c5[8] = {B11111, B00001, B00001, B00001, B00001, B00001, B00001, B11111};  
//byte LCDBigNumbers::c6[8] = {B11111, B00000, B00000, B00000, B00000, B00000, B00000, B00000};  
//byte LCDBigNumbers::c7[8] = {B11111, B00000, B00000, B00000, B00000, B00000, B00000, B11111}; 

LCDNumber LCDBigNumbers::_lcd_numbers[] = { 
      {0, 2, 1, 3}, //0
      {2, 9, 3, 1}, //1
      {7, 5, 4, 7}, //2
      {7, 5, 7, 5}, //3
      {1, 3, 6, 2}, //4
      {4, 7, 7, 5}, //5
      {4, 7, 4, 5}, //6
      {6, 5, 9, 2}, //7
      {4, 5, 4, 5}, //8
      {4, 5, 7, 5} // 9
    };
/*************************************************************************************************************
*******************************FIM CLASSE LCD BIG NUMBERS*****************************************************
**************************************************************************************************************/
   
   
LiquidCrystal lcd(12, 11, 5, 4, 3, 2);
LCDBigNumbers lcdNum(&lcd, 0,0); //inclui uma barra no lcd, primeira linha, coluna 1
   
void setup()   {
  Serial.begin(9600);
  lcdNum.createChars(); //cria os caracteres especiais na memoria
  lcdNum.setCol(1); //muda para a coluna numero 1
  lcdNum.setDist(1); //1 coluna entre um numero e outro
  
  
  pinMode(44, OUTPUT);  //arduino mega - pino de contraste do display
  analogWrite(44, 255/6); //utilizado para aumentar o contraste
  lcd.begin(16, 2);
}
 
long i = 0;
int col = 0;
 
void loop() {
  lcdNum.setValue(i++ * 11);
   
  if (i>=10000) { i = 0; }

  delay(500);  
}



O Código abaixo é uma versão alterada pra mostrar números e letras, a qual não está no vídeo. Ainda precisa de umas melhorias, mas de qualquer maneira, deixo aqui pra quem quiser testar:



/*
Fabiano A. Arndt - 2015
www.youtube.com/user/fabianoallex
www.facebook.com/dicasarduino
fabianoallex@gmail.com
*/
   
#include <LiquidCrystal.h>
   
/*************************************************************************************************************
*******************************CLASSE LCD BIG TEXT*********************************************************
**************************************************************************************************************/
struct LCDChar {
  byte top1;
  byte top2;
  byte bottom1;
  byte bottom2;
};
 
class LCDBigText {
  private:
    LiquidCrystal * _lcd;
    int _row;
    int _col;
    String _value; /*0..100*/
    byte _dist;  /*distancia entre digitos*/
     
    void _clear(){
      
      for (int i=0; i<_value.length(); i++) {
        //_lcd->setCursor(_col+i, _row);
        _lcd->setCursor(_col+i*(2+_dist), _row);
        _lcd->print( " " );
        _lcd->print( " " );
        for (byte b=0;b<_dist; b++){ _lcd->print( " " ); }
        _lcd->setCursor(_col+i*(2+_dist), _row+1);
        //_lcd->setCursor(_col+i, _row+1);
        _lcd->print( " " );
        _lcd->print( " " );
        for (byte b=0;b<_dist; b++){ _lcd->print( " " ); }
      }
    }
  public:
    static byte c0[8];  
    static byte c1[8];  
    static byte c2[8];  
    static byte c3[8];
    static byte c4[8];
    static byte c5[8];  
    static byte c6[8];  
    static byte c7[8];  
    static LCDChar _lcd_chars[];
    
    void createChars() {
      _lcd->createChar(0, c0);
      _lcd->createChar(1, c1);
      _lcd->createChar(2, c2);
      _lcd->createChar(3, c3);
      _lcd->createChar(4, c4);
      _lcd->createChar(5, c5);
      _lcd->createChar(6, c6);
      _lcd->createChar(7, c7);
    }
     
    LCDBigText(LiquidCrystal * lcd, int row, int col) {
      _lcd = lcd;      
      _row = row;      
      _col = col;
      _dist = 0;  //distancia entre os numeros
    }
     
    void setRow(int row){
      _clear();
      _row = row;
      setValue(_value);
    }
    void setCol(int col){
      _clear();
      _col = col;
      setValue(_value);
    }
    
    void setDist(int dist){
      _clear();
      _dist = dist;
      setValue(_value);
    }
     
    void setValue(String value){
      _clear();
      _value = value;
             
      for (int i=0; i<_value.length(); i++) {
        char n = value[i];
        
        //tabela ascii
        if (n >= 97 && n<=122){
          n = n - 97 + 10;
        } else if (n >= 65 && n<=90){
          n = n - 65 + 10;
        } else if (n >= 48 && n<=57) {
          n = n - 48;
        } else {
          n = 36;  //space
        }
        
        if (n ==0 ){
          _lcd->setCursor(_col+i*(2+_dist), _row);
          _lcd->print("  ");
          _lcd->setCursor(_col+i*(2+_dist), _row+1);
          _lcd->print("  ");
        } else {
          _lcd->setCursor(_col+i*(2+_dist), _row);
          _lcd_chars[n].top1 == 9 ? _lcd->print(" ") : _lcd->write( _lcd_chars[n].top1 );
          _lcd_chars[n].top2 == 9 ? _lcd->print(" ") : _lcd->write( _lcd_chars[n].top2 );
          _lcd->setCursor(_col+i*(2+_dist), _row+1);
          _lcd_chars[n].bottom1 == 9 ? _lcd->print(" ") : _lcd->write( _lcd_chars[n].bottom1 );
          _lcd_chars[n].bottom2 == 9 ? _lcd->print(" ") : _lcd->write( _lcd_chars[n].bottom2 );
        }
      }
    }
};

//byte LCDBigText::c0[8] = {B11111, B11111, B11000, B11000, B11000, B11000, B11000, B11000}; 
//byte LCDBigText::c1[8] = {B11000, B11000, B11000, B11000, B11000, B11000, B11111, B11111};  
//byte LCDBigText::c2[8] = {B11111, B11111, B00011, B00011, B00011, B00011, B00011, B00011};  
//byte LCDBigText::c3[8] = {B00011, B00011, B00011, B00011, B00011, B00011, B11111, B11111};
//byte LCDBigText::c4[8] = {B11111, B11111, B11000, B11000, B11000, B11000, B11111, B11111};  
//byte LCDBigText::c5[8] = {B11111, B11111, B00011, B00011, B00011, B00011, B11111, B11111};  
//byte LCDBigText::c6[8] = {B11111, B11111, B00000, B00000, B00000, B00000, B00000, B00000};  
//byte LCDBigText::c7[8] = {B11111, B11111, B00000, B00000, B00000, B00000, B11111, B11111};  

//byte LCDBigText::c0[8] = {B11111, B11000, B11000, B11000, B11000, B11000, B11000, B11000}; 
//byte LCDBigText::c1[8] = {B11000, B11000, B11000, B11000, B11000, B11000, B11000, B11111};  
//byte LCDBigText::c2[8] = {B11111, B00011, B00011, B00011, B00011, B00011, B00011, B00011};  
//byte LCDBigText::c3[8] = {B00011, B00011, B00011, B00011, B00011, B00011, B00011, B11111};
//byte LCDBigText::c4[8] = {B11111, B11000, B11000, B11000, B11000, B11000, B11000, B11111};  
//byte LCDBigText::c5[8] = {B11111, B00011, B00011, B00011, B00011, B00011, B00011, B11111};  
//byte LCDBigText::c6[8] = {B11111, B00000, B00000, B00000, B00000, B00000, B00000, B00000};  
//byte LCDBigText::c7[8] = {B11111, B00000, B00000, B00000, B00000, B00000, B00000, B11111}; 

byte LCDBigText::c0[8] = {B11111, B10000, B10000, B10000, B10000, B10000, B10000, B10000}; 
byte LCDBigText::c1[8] = {B10000, B10000, B10000, B10000, B10000, B10000, B10000, B11111};  
byte LCDBigText::c2[8] = {B11111, B00001, B00001, B00001, B00001, B00001, B00001, B00001};  
byte LCDBigText::c3[8] = {B00001, B00001, B00001, B00001, B00001, B00001, B00001, B11111};
byte LCDBigText::c4[8] = {B11111, B10000, B10000, B10000, B10000, B10000, B10000, B11111};  
byte LCDBigText::c5[8] = {B11111, B00001, B00001, B00001, B00001, B00001, B00001, B11111};  
byte LCDBigText::c6[8] = {B11111, B00000, B00000, B00000, B00000, B00000, B00000, B00000};  
byte LCDBigText::c7[8] = {B11111, B00000, B00000, B00000, B00000, B00000, B00000, B11111}; 

LCDChar LCDBigText::_lcd_chars[] = { 
      {0, 2, 1, 3}, //0
      {2, 9, 3, 1}, //1
      {7, 5, 4, 7}, //2
      {7, 5, 7, 5}, //3
      {1, 3, 6, 2}, //4
      {4, 7, 7, 5}, //5
      {4, 7, 4, 5}, //6
      {6, 5, 9, 2}, //7
      {4, 5, 4, 5}, //8
      {4, 5, 7, 5}, //9
      {0, 2, 0, 2}, //a
      {1, 9, 4, 5}, //b
      {0, 6, 1, 95}, //c
      {9, 3, 4, 5}, //d
      {4, 7, 4, 7}, //e
      {4, 6, 0, 9}, //f
      {0, 7, 1, 5}, //g
      {1, 3, 0, 2}, //h
      {3, 9, 3, 1}, //i
      {9, 2, 1, 3}, //j
      {1, '/', 0, '\\'}, //k
      {2, 9, 3, 1}, //l
      {95, 95, 0, 2}, //m
      {95, 9, 0, 2}, //n
      {0, 2, 1, 3}, //o
      {4, 5, 0, 6}, //p
      {4, 5, 6, 2}, //q
      {4, 5, 0, '\\'}, //r
      {4, 7, 7, 5}, //s
      {3, 1, 5, 4}, //t
      {9, 9, 1, 3}, //u
      {1, 3, '\\', '/'}, //v
      {1, 3, 'w', 'w'}, //w
      {'\\', '/', '/', '\\'}, //x
      {1, 3, 6, 2}, //y
      {6, '/', '/', 95}, //z
      {9, 9, 9, 9} //' '
    };
/*************************************************************************************************************
*******************************FIM CLASSE LCD BIG TEXT*****************************************************
**************************************************************************************************************/
   
   
LiquidCrystal lcd(12, 11, 5, 4, 3, 2);
LCDBigText lcdText(&lcd, 0,0); //inclui uma barra no lcd, primeira linha, coluna 1
   
void setup()   {
  Serial.begin(9600);
  lcdText.createChars(); //cria os caracteres especiais na memoria
  lcdText.setCol(0); //muda para a coluna numero 1
  lcdText.setDist(1); //1 coluna entre um numero e outro
  
  
  pinMode(44, OUTPUT);  //arduino mega - pino de contraste do display
  analogWrite(44, 255/6); //utilizado para aumentar o contraste
  lcd.begin(16, 2);
  
}
 
long i = 0;
int col = 0;
 
void loop() {
  
  lcd.clear();
  
  lcdText.setValue("ARDUINO");
  delay(3000);
  
  for (int i = 0; i < 20; i++) {
    lcd.scrollDisplayLeft();
    delay(600);
  }
  
  lcd.clear();
  
  lcdText.setValue("01234");
  delay(3000);
  
  lcdText.setValue("56789");
  delay(3000);
   
  lcdText.setValue("abcde");
  delay(3000);

  lcdText.setValue("fghij");
  delay(3000);  
  
  lcdText.setValue("klmno");
  delay(3000);
  
  lcdText.setValue("pqrst");
  delay(3000);
  
  lcdText.setValue("uvx");
  delay(3000);
  
  lcdText.setValue("wyz");
  delay(3000);
    
  lcdText.setValue("ABCDE");
  delay(3000);

  lcdText.setValue("FGHIJ");
  delay(3000);  
  
  lcdText.setValue("KLMNO");
  delay(3000);
  
  lcdText.setValue("PQRST");
  delay(3000);
  
  lcdText.setValue("UVX");
  delay(3000);
  
  lcdText.setValue("WYZ");
  delay(3000);
  
  
}

Nesse outro exemplo, é mostrado como juntar mais de um formato em uma mesma sketch. Por padrão o LCD aceita apenas 8 caracteres customizáveis, mas é possível criar mais de oito, desde que se tenha apenas 8 sendo usado por vez. Ao terminar de usar os primeiros 8, pode-se definir os outros 8 caracteres customizáveis.




/*
Fabiano A. Arndt - 2015
www.youtube.com/user/fabianoallex
www.facebook.com/dicasarduino
fabianoallex@gmail.com
*/
    
#include <LiquidCrystal.h>
    
/*************************************************************************************************************
*******************************CLASSE LCD BIG NUMBERS 1*******************************************************
**************************************************************************************************************/
struct LCDNumber_1 {
  byte top;
  byte bottom;
};
  
class LCDBigNumbers_1 {
  private:
    LiquidCrystal * _lcd;
    int _row;
    int _col;
    long _value; /*0..100*/
      
    void _clear(){
      int cont = 1;
      long x = 9;
      while (_value > x){
        cont++;
        x = x * 10 + 9;
      }
      for (int i=0; i<cont; i++) {
        _lcd->setCursor(_col+i, _row);
        _lcd->print( " " );
        _lcd->setCursor(_col+i, _row+1);
        _lcd->print( " " );
      }
    }
  public:
    static byte c0[8];  
    static byte c1[8];  
    static byte c2[8];  
    static byte c3[8];
    static byte c4[8];
    static byte c5[8];  
    static byte c6[8];  
    static byte c7[8];  
    static LCDNumber_1 _lcd_numbers[];
     
    void createChars() {
      _lcd->createChar(0, c0);
      _lcd->createChar(1, c1);
      _lcd->createChar(2, c2);
      _lcd->createChar(3, c3);
      _lcd->createChar(4, c4);
      _lcd->createChar(5, c5);
      _lcd->createChar(6, c6);
      _lcd->createChar(7, c7);
    }
      
    LCDBigNumbers_1(LiquidCrystal * lcd, int row, int col) {
      _lcd = lcd;      _row = row;      _col = col;
    }
      
    void setRow(int row){
      _clear();
      _row = row;
      setValue(_value);
    }
    void setCol(int col){
      _clear();
      _col = col;
      setValue(_value);
    }
      
    void setValue(long value){
      _clear();
      _value = value;
        
      int cont = 1;
      long x = 9;
      while (abs(_value) > x){
        cont++;
        x = x * 10 + 9;
      }
        
      for (int i=0; i<cont; i++) {
        int n = value / pow(10, cont-1-i);
        value = value - pow(10, cont-1-i) * n;
          
        _lcd->setCursor(_col+i, _row);
        _lcd->write( _lcd_numbers[n].top );
        _lcd->setCursor(_col+i, _row+1);
        _lcd->write( _lcd_numbers[n].bottom );
      }
        
    }
};
 
byte LCDBigNumbers_1::c0[8] = {B11111, B10001, B10001, B10001, B10001, B10001, B10001, B10001};
byte LCDBigNumbers_1::c1[8] = {B10001, B10001, B10001, B10001, B10001, B10001, B10001, B11111};
byte LCDBigNumbers_1::c2[8] = {B00001, B00001, B00001, B00001, B00001, B00001, B00001, B00001};
byte LCDBigNumbers_1::c3[8] = {B11111, B00001, B00001, B00001, B00001, B00001, B00001, B11111};
byte LCDBigNumbers_1::c4[8] = {B11111, B10000, B10000, B10000, B10000, B10000, B10000, B11111};
byte LCDBigNumbers_1::c5[8] = {B11111, B00001, B00001, B00001, B00001, B00001, B00001, B00001};    
byte LCDBigNumbers_1::c6[8] = {B11111, B10001, B10001, B10001, B10001, B10001, B10001, B11111};              
byte LCDBigNumbers_1::c7[8] = {B00001, B00001, B00001, B00001, B00001, B00001, B00001, B11111};
 
 
LCDNumber_1 LCDBigNumbers_1::_lcd_numbers[] = { 
  {0, 1}, //0
  {2, 2}, //1
  {5, 4}, //2
  {3, 7}, //3
  {1, 2}, //4
  {4, 7}, //5
  {4, 1}, //6
  {5, 2}, //7
  {6, 1}, //8
  {6, 7} // 9
};
  
 
/*************************************************************************************************************
*******************************FIM CLASSE LCD BIG NUMBERS 1***************************************************
**************************************************************************************************************/
    
    
/*************************************************************************************************************
*******************************CLASSE LCD BIG NUMBERS 2*******************************************************
**************************************************************************************************************/
struct LCDNumber_2 {
  byte top1;
  byte top2;
  byte bottom1;
  byte bottom2;
};
  
class LCDBigNumbers_2 {
  private:
    LiquidCrystal * _lcd;
    int _row;
    int _col;
    long _value; /*0..100*/
    byte _dist;  /*distancia entre digitos*/
      
    void _clear(){
      int cont = 1;
      long x = 9;
      while (_value > x){
        cont++;
        x = x * 10 + 9;
      }
      for (int i=0; i<cont; i++) {
        _lcd->setCursor(_col+i, _row);
        _lcd->print( "  " );
        for (byte b=0;b<_dist; b++){ _lcd->print( " " ); }
        _lcd->setCursor(_col+i, _row+1);
        _lcd->print( "  " );
        for (byte b=0;b<_dist; b++){ _lcd->print( " " ); }
      }
    }
  public:
    static byte c0[8];  
    static byte c1[8];  
    static byte c2[8];  
    static byte c3[8];
    static byte c4[8];
    static byte c5[8];  
    static byte c6[8];  
    static byte c7[8];  
    static LCDNumber_2 _lcd_numbers[];
     
    void createChars() {
      _lcd->createChar(0, c0);
      _lcd->createChar(1, c1);
      _lcd->createChar(2, c2);
      _lcd->createChar(3, c3);
      _lcd->createChar(4, c4);
      _lcd->createChar(5, c5);
      _lcd->createChar(6, c6);
      _lcd->createChar(7, c7);
    }
      
    LCDBigNumbers_2(LiquidCrystal * lcd, int row, int col) {
      _lcd = lcd;      
      _row = row;      
      _col = col;
      _dist = 0;  //distancia entre os numeros
    }
      
    void setRow(int row){
      _clear();
      _row = row;
      setValue(_value);
    }
    void setCol(int col){
      _clear();
      _col = col;
      setValue(_value);
    }
     
    void setDist(int dist){
      _clear();
      _dist = dist;
      setValue(_value);
    }
      
    void setValue(long value){
      _clear();
      _value = value;
        
      int cont = 1;
      long x = 9;
      while (abs(_value) > x){
        cont++;
        x = x * 10 + 9;
      }
        
      for (int i=0; i<cont; i++) {
        int n = value / pow(10, cont-1-i);
        value = value - pow(10, cont-1-i) * n;
          
        _lcd->setCursor(_col+i*(2+_dist), _row);
        _lcd_numbers[n].top1 == 9 ? _lcd->print(" ") : _lcd->write( _lcd_numbers[n].top1 );
        _lcd_numbers[n].top2 == 9 ? _lcd->print(" ") : _lcd->write( _lcd_numbers[n].top2 );
        _lcd->setCursor(_col+i*(2+_dist), _row+1);
        _lcd_numbers[n].bottom1 == 9 ? _lcd->print(" ") : _lcd->write( _lcd_numbers[n].bottom1 );
        _lcd_numbers[n].bottom2 == 9 ? _lcd->print(" ") : _lcd->write( _lcd_numbers[n].bottom2 );
      }
    }
};
 
//byte LCDBigNumbers_2::c0[8] = {B11111, B11111, B11000, B11000, B11000, B11000, B11000, B11000}; 
//byte LCDBigNumbers_2::c1[8] = {B11000, B11000, B11000, B11000, B11000, B11000, B11111, B11111};  
//byte LCDBigNumbers_2::c2[8] = {B11111, B11111, B00011, B00011, B00011, B00011, B00011, B00011};  
//byte LCDBigNumbers_2::c3[8] = {B00011, B00011, B00011, B00011, B00011, B00011, B11111, B11111};
//byte LCDBigNumbers_2::c4[8] = {B11111, B11111, B11000, B11000, B11000, B11000, B11111, B11111};  
//byte LCDBigNumbers_2::c5[8] = {B11111, B11111, B00011, B00011, B00011, B00011, B11111, B11111};  
//byte LCDBigNumbers_2::c6[8] = {B11111, B11111, B00000, B00000, B00000, B00000, B00000, B00000};  
//byte LCDBigNumbers_2::c7[8] = {B11111, B11111, B00000, B00000, B00000, B00000, B11111, B11111};  
 
byte LCDBigNumbers_2::c0[8] = {B11111, B11000, B11000, B11000, B11000, B11000, B11000, B11000}; 
byte LCDBigNumbers_2::c1[8] = {B11000, B11000, B11000, B11000, B11000, B11000, B11000, B11111};  
byte LCDBigNumbers_2::c2[8] = {B11111, B00011, B00011, B00011, B00011, B00011, B00011, B00011};  
byte LCDBigNumbers_2::c3[8] = {B00011, B00011, B00011, B00011, B00011, B00011, B00011, B11111};
byte LCDBigNumbers_2::c4[8] = {B11111, B11000, B11000, B11000, B11000, B11000, B11000, B11111};  
byte LCDBigNumbers_2::c5[8] = {B11111, B00011, B00011, B00011, B00011, B00011, B00011, B11111};  
byte LCDBigNumbers_2::c6[8] = {B11111, B00000, B00000, B00000, B00000, B00000, B00000, B00000};  
byte LCDBigNumbers_2::c7[8] = {B11111, B00000, B00000, B00000, B00000, B00000, B00000, B11111}; 
 
//byte LCDBigNumbers_2::c0[8] = {B11111, B10000, B10000, B10000, B10000, B10000, B10000, B10000}; 
//byte LCDBigNumbers_2::c1[8] = {B10000, B10000, B10000, B10000, B10000, B10000, B10000, B11111};  
//byte LCDBigNumbers_2::c2[8] = {B11111, B00001, B00001, B00001, B00001, B00001, B00001, B00001};  
//byte LCDBigNumbers_2::c3[8] = {B00001, B00001, B00001, B00001, B00001, B00001, B00001, B11111};
//byte LCDBigNumbers_2::c4[8] = {B11111, B10000, B10000, B10000, B10000, B10000, B10000, B11111};  
//byte LCDBigNumbers_2::c5[8] = {B11111, B00001, B00001, B00001, B00001, B00001, B00001, B11111};  
//byte LCDBigNumbers_2::c6[8] = {B11111, B00000, B00000, B00000, B00000, B00000, B00000, B00000};  
//byte LCDBigNumbers_2::c7[8] = {B11111, B00000, B00000, B00000, B00000, B00000, B00000, B11111}; 
 
LCDNumber_2 LCDBigNumbers_2::_lcd_numbers[] = { 
      {0, 2, 1, 3}, //0
      {2, 9, 3, 1}, //1
      {7, 5, 4, 7}, //2
      {7, 5, 7, 5}, //3
      {1, 3, 6, 2}, //4
      {4, 7, 7, 5}, //5
      {4, 7, 4, 5}, //6
      {6, 5, 9, 2}, //7
      {4, 5, 4, 5}, //8
      {4, 5, 7, 5} // 9
    };
/*************************************************************************************************************
*******************************FIM CLASSE LCD BIG NUMBERS 2***************************************************
**************************************************************************************************************/
 
    
LiquidCrystal lcd(12, 11, 5, 4, 3, 2);
LCDBigNumbers_1 lcdNum_1(&lcd, 0,0); //inclui uma barra no lcd, primeira linha, coluna 1
LCDBigNumbers_2 lcdNum_2(&lcd, 0,0); //inclui uma barra no lcd, primeira linha, coluna 1
    
void setup()   {
  Serial.begin(9600);
  lcdNum_1.createChars(); //cria os caracteres especiais na memoria
  lcdNum_2.setDist(1);
   
  pinMode(44, OUTPUT);  //arduino mega - pino de contraste do display
  analogWrite(44, 255/6); //utilizado para aumentar o contraste
  lcd.begin(16, 2);
}
  
long i = 0;
int col = 0;
  
int x = 1;
  
void loop() {
  int c = Serial.read();
    
  if (c == 97)  { lcd.clear(); lcdNum_1.createChars(); x = 1;  } //a
  if (c == 98)  { lcd.clear(); lcdNum_2.createChars(); x = 2;  } //b
   
  if (x == 1) { lcdNum_1.setValue(i++ * 11); }
  if (x == 2) { lcdNum_2.setValue(i++ * 11); }
  
  if (i>=10000) { i = 0; }
  delay(500);  
}



Pra demonstrar com um exemplo um pouco mais prático, criei um relógio que mostra a data e hora em um display LCD, utilizando RTC - DS3231




Código-fonte:

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


LiquidCrystal lcd(12, 11, 5, 4, 3, 2);
DS3231 RTC; 
    
/*************************************************************************************************************
*******************************CLASSE LCD BIG NUMBERS*********************************************************
para mais detalhes: http://fabianoallex.blogspot.com.br/2015/10/arduino-lcd-big-numbers.html
**************************************************************************************************************
**************************************************************************************************************/
struct LCDNumber {
  byte top1;
  byte top2;
  byte bottom1;
  byte bottom2;
};
  
class LCDBigNumbers {
  private:
    LiquidCrystal * _lcd;
    int _row;
    int _col;
    long _value; /*0..100*/
    byte _dist;  /*distancia entre digitos*/
    byte _min_size; /*tamanho minimo.  3--> 001   002 ;;;;  2 --> 01   02*/
      
    void _clear(){
      int cont = 1;
      long x = 9;
      while (_value > x){
        cont++;
        x = x * 10 + 9;
      }
      
      for (int i=0; i<cont; i++) {
        if (cont <= (_min_size-1) && i <= (_min_size -2) ){ //zeros a esquerda
          cont ++;
        }
        
        _lcd->setCursor(_col+i*(2+_dist), _row);
        _lcd->print( " " );
        _lcd->print( " " );
        for (byte b=0;b<_dist; b++){ _lcd->print( " " ); }
        _lcd->setCursor(_col+i*(2+_dist), _row+1);
        _lcd->print( " " );
        _lcd->print( " " );
        for (byte b=0;b<_dist; b++){ _lcd->print( " " ); }
      }
    }
  public:
    static byte c0[8];  
    static byte c1[8];  
    static byte c2[8];  
    static byte c3[8];
    static byte c4[8];
    static byte c5[8];  
    static byte c6[8];  
    static byte c7[8];  
    static LCDNumber _lcd_numbers[];
     
    void createChars() {
      _lcd->createChar(0, c0);
      _lcd->createChar(1, c1);
      _lcd->createChar(2, c2);
      _lcd->createChar(3, c3);
      _lcd->createChar(4, c4);
      _lcd->createChar(5, c5);
      _lcd->createChar(6, c6);
      _lcd->createChar(7, c7);
    }
      
    LCDBigNumbers(LiquidCrystal * lcd, int row, int col) {
      _lcd = lcd;      
      _row = row;      
      _col = col;
      _dist = 0;  //distancia entre os numeros
      _min_size = 1;
    }
      
    void setRow(int row){
      _clear();
      _row = row;
      setValue(_value);
    }
    void setCol(int col){
      _clear();
      _col = col;
      setValue(_value);
    }
     
    void setDist(int dist){
      _clear();
      _dist = dist;
      setValue(_value);
    }
    
    void setMinSize(byte min_size){
      _min_size = min_size;
    }
      
    void setValue(long value){
      _clear();
      _value = value;
        
      int cont = 1;
      long x = 9;
      while (abs(_value) > x){
        cont++;
        x = x * 10 + 9;
      }
      
      for (int i=0; i<cont; i++) {
        int n;
        
        if (cont <= (_min_size-1) && i <= (_min_size -2) ){ //zeros a esquerda
          n = 0;
          cont ++;
        } else {
          
          n = value / pow(10, cont-1-i);
          value = value - pow(10, cont-1-i) * n;
        }
          
        _lcd->setCursor(_col+i*(2+_dist), _row);
        _lcd_numbers[n].top1 == 9 ? _lcd->print(" ") : _lcd->write( _lcd_numbers[n].top1 );
        _lcd_numbers[n].top2 == 9 ? _lcd->print(" ") : _lcd->write( _lcd_numbers[n].top2 );
        _lcd->setCursor(_col+i*(2+_dist), _row+1);
        _lcd_numbers[n].bottom1 == 9 ? _lcd->print(" ") : _lcd->write( _lcd_numbers[n].bottom1 );
        _lcd_numbers[n].bottom2 == 9 ? _lcd->print(" ") : _lcd->write( _lcd_numbers[n].bottom2 );
      }
    }
};
 
//byte LCDBigNumbers::c0[8] = {B11111, B11111, B11000, B11000, B11000, B11000, B11000, B11000}; 
//byte LCDBigNumbers::c1[8] = {B11000, B11000, B11000, B11000, B11000, B11000, B11111, B11111};  
//byte LCDBigNumbers::c2[8] = {B11111, B11111, B00011, B00011, B00011, B00011, B00011, B00011};  
//byte LCDBigNumbers::c3[8] = {B00011, B00011, B00011, B00011, B00011, B00011, B11111, B11111};
//byte LCDBigNumbers::c4[8] = {B11111, B11111, B11000, B11000, B11000, B11000, B11111, B11111};  
//byte LCDBigNumbers::c5[8] = {B11111, B11111, B00011, B00011, B00011, B00011, B11111, B11111};  
//byte LCDBigNumbers::c6[8] = {B11111, B11111, B00000, B00000, B00000, B00000, B00000, B00000};  
//byte LCDBigNumbers::c7[8] = {B11111, B11111, B00000, B00000, B00000, B00000, B11111, B11111};  
 
byte LCDBigNumbers::c0[8] = {B11111, B11000, B11000, B11000, B11000, B11000, B11000, B11000}; 
byte LCDBigNumbers::c1[8] = {B11000, B11000, B11000, B11000, B11000, B11000, B11000, B11111};  
byte LCDBigNumbers::c2[8] = {B11111, B00011, B00011, B00011, B00011, B00011, B00011, B00011};  
byte LCDBigNumbers::c3[8] = {B00011, B00011, B00011, B00011, B00011, B00011, B00011, B11111};
byte LCDBigNumbers::c4[8] = {B11111, B11000, B11000, B11000, B11000, B11000, B11000, B11111};  
byte LCDBigNumbers::c5[8] = {B11111, B00011, B00011, B00011, B00011, B00011, B00011, B11111};  
byte LCDBigNumbers::c6[8] = {B11111, B00000, B00000, B00000, B00000, B00000, B00000, B00000};  
byte LCDBigNumbers::c7[8] = {B11111, B00000, B00000, B00000, B00000, B00000, B00000, B11111}; 
 
//byte LCDBigNumbers::c0[8] = {B11111, B10000, B10000, B10000, B10000, B10000, B10000, B10000}; 
//byte LCDBigNumbers::c1[8] = {B10000, B10000, B10000, B10000, B10000, B10000, B10000, B11111};  
//byte LCDBigNumbers::c2[8] = {B11111, B00001, B00001, B00001, B00001, B00001, B00001, B00001};  
//byte LCDBigNumbers::c3[8] = {B00001, B00001, B00001, B00001, B00001, B00001, B00001, B11111};
//byte LCDBigNumbers::c4[8] = {B11111, B10000, B10000, B10000, B10000, B10000, B10000, B11111};  
//byte LCDBigNumbers::c5[8] = {B11111, B00001, B00001, B00001, B00001, B00001, B00001, B11111};  
//byte LCDBigNumbers::c6[8] = {B11111, B00000, B00000, B00000, B00000, B00000, B00000, B00000};  
//byte LCDBigNumbers::c7[8] = {B11111, B00000, B00000, B00000, B00000, B00000, B00000, B11111}; 
 
LCDNumber LCDBigNumbers::_lcd_numbers[] = { 
      {0, 2, 1, 3}, //0
      {2, 9, 3, 1}, //1
      {7, 5, 4, 7}, //2
      {7, 5, 7, 5}, //3
      {1, 3, 6, 2}, //4
      {4, 7, 7, 5}, //5
      {4, 7, 4, 5}, //6
      {6, 5, 9, 2}, //7
      {4, 5, 4, 5}, //8
      {4, 5, 7, 5} // 9
    };
/*************************************************************************************************************
*******************************FIM CLASSE LCD BIG NUMBERS*****************************************************
**************************************************************************************************************/

/*************************************************************************************************************
*******************************FUNÇÃO TIME********************************************************************
mais detalhes: http://fabianoallex.blogspot.com.br/2015/09/arduino-como-substituir-delay-pelo.html
*************************************************************************************************************
**************************************************************************************************************/
    
int time(long timeHigh, long timeLow, long atraso, long mref = 0) {
  long ajuste = mref % (timeHigh + timeLow);
  long resto  = (millis() + timeHigh + timeLow - ajuste - atraso) % (timeHigh + timeLow);
  return (resto < timeHigh ? HIGH : LOW);
}
/*************************************************************************************************************
*******************************FIM FUNÇÃO TIME****************************************************************
**************************************************************************************************************/



/*************************************************************************************************************
*******************************FUNÇÕES DO RELOGIO*************************************************************
**************************************************************************************************************/

LCDBigNumbers lcdNum01(&lcd, 0, 1); //inclui um número no lcd, primeira linha, coluna 1
LCDBigNumbers lcdNum02(&lcd, 0, 6); //inclui um número no lcd, primeira linha, coluna 6
LCDBigNumbers lcdNum03(&lcd, 0, 11); //inclui um número no lcd, primeira linha, coluna 11
    
int sec = 0;
int date = 0;
unsigned long mclock = 0;
char dot = ' ';
  
void show_clock(){
  if (millis()-mclock > 500) {
    
    DateTime now = RTC.now(); //get the current date-time
  
    if (sec != now.second()){
      lcdNum01.setValue(now.hour());
      lcdNum02.setValue(now.minute());
      lcdNum03.setValue(now.second());
      
      sec = now.second();
    }
    
    dot = (dot == '.') ? dot = ' ' : dot = '.';

    lcd.setCursor(5, 0);
    lcd.print(dot);
    lcd.setCursor(10, 0);
    lcd.print(dot);
    lcd.setCursor(5, 1);
    lcd.print(dot);
    lcd.setCursor(10, 1);
    lcd.print(dot);
    
    mclock = millis();
  }
}


void show_date(){
  if (millis()-mclock > 500) {
    DateTime now = RTC.now(); //get the current date-time
    
    if (date != now.second()){
      lcdNum01.setValue(now.date());
      lcdNum02.setValue(now.month());
      lcdNum03.setValue(now.year()-2000);
      
      date = now.date();
    }
    
    lcd.setCursor(5, 0);
    lcd.print(' ');
    lcd.setCursor(10, 0);
    lcd.print(' ');
    
    lcd.setCursor(5, 1);
    lcd.print('.');
    lcd.setCursor(10, 1);
    lcd.print('.');
    
    mclock = millis();
  }
}

/*************************************************************************************************************
*******************************FIM FUNÇÕES DO RELOGIO*********************************************************
**************************************************************************************************************/

void setup()   {
  Serial.begin(9600);
  lcdNum01.createChars(); //cria os caracteres especiais na memoria
  
  lcdNum01.setMinSize(2); //duas casas 00 01 02
  lcdNum02.setMinSize(2); //duas casas 00 01 02
  lcdNum03.setMinSize(2); //duas casas 00 01 02
   
  pinMode(44, OUTPUT);  //arduino mega - pino de contraste do display
  analogWrite(44, 255/6); //utilizado para aumentar o contraste
  lcd.begin(16, 2);
  
  Wire.begin();
  RTC.begin();
}

void loop() {
  //mostra por 10 segundos a hora e por 5 segundos a data
  if ( time(10000, 5000, 0) ) {
    show_clock();
  } else {
    show_date();
  }
}


quarta-feira, 14 de outubro de 2015

Arduino - LCD Progress Bar - Barra de Progresso

Eventualmente é necessário, em algumas aplicações, mostrar o progresso de determinado processamento, ou valores que variam dentro de um intervalo, como a leitura de potenciômetros ou coisas do gênero. Nesses casos é bem comum o uso de displays LCD.

Pra facilitar a inclusão de barras de progresso em displays LCD, criei uma classe chamada LCDProgressBar, que com poucas linhas é possível incluir uma ou mais barras de progresso, que podem, inclusive, serem mostradas ao mesmo tempo.

A ideia foi criar uma barra customizável, onde o programador indica a posição (linha e coluna) no display e o tamanho que a barra de progresso terá.

A Classe possui um método chamado setPerc(), o qual irá receber um valor que deverá estar entre 0 e 100. A barra será gerada de acordo com o valor passado como parâmetro, sendo que em 0 a barra não aparece e em 100 ela é completamente preenchida.


Vídeo da primeira versão:



vídeo da segunda versão:



código da primeira versão:

/*
Fabiano A. Arndt - 2015
www.youtube.com/user/fabianoallex
www.facebook.com/dicasarduino
fabianoallex@gmail.com
*/

#include <LiquidCrystal.h>

/*************************************************************************************************************
*******************************CLASSE LCD PROGRESS BAR********************************************************
**************************************************************************************************************/
byte c1[8] = {B10000,  B10000,  B10000,  B10000,  B10000,  B10000,  B10000,  B10000};
byte c2[8] = {B11000,  B11000,  B11000,  B11000,  B11000,  B11000,  B11000,  B11000};
byte c3[8] = {B11100,  B11100,  B11100,  B11100,  B11100,  B11100,  B11100,  B11100};
byte c4[8] = {B11110,  B11110,  B11110,  B11110,  B11110,  B11110,  B11110,  B11110};
byte c5[8] = {B11111,  B11111,  B11111,  B11111,  B11111,  B11111,  B11111,  B11111};

class LCDProgressBar {
  private:
    LiquidCrystal * _lcd;
    int _row;
    int _col;
    int _len;
    int _perc; /*0..100*/
  public:
    void createChars() {
      _lcd->createChar(0, c1);
      _lcd->createChar(1, c2);
      _lcd->createChar(2, c3);
      _lcd->createChar(3, c4);
      _lcd->createChar(4, c5);
    }
  
    LCDProgressBar(LiquidCrystal * lcd, int row, int col, int len) {
      _lcd = lcd;      _row = row;      _col = col;      _len = len;
    }
    
    void setPerc(int perc){
      _perc = perc;
      if (perc > 100) { _perc = 100; }
      if (perc < 000) { _perc = 000; }
      
      _lcd->setCursor(_col, _row);
      for (int i=0; i<(_len);i++) { _lcd->print(" "); }
      _lcd->setCursor(_col, _row);
      
      int bars  = 5 * _len * _perc / 100;
      int div   = bars / 5;  //divisao
      int resto = bars % 5;  //resto
      for (int i=0; i<div; i++)  { _lcd->write((byte)4);         }  //pinta todo o quadro
      if (resto > 0 )            { _lcd->write((byte)(resto-1)); }  //pinta o quadro com a quantidade de barras proporcional
    }
};
/*************************************************************************************************************
*******************************FIM CLASSE LCD PROGRESS BAR****************************************************
**************************************************************************************************************/


LiquidCrystal lcd(12, 11, 5, 4, 3, 2);
LCDProgressBar lcdBar1(&lcd, 0, 8,  8); //inclui uma barra no lcd, primeira linha, coluna 8. tamanho 8
LCDProgressBar lcdBar2(&lcd, 1, 12, 4); //inclui outra barra no lcd, segunda linha, coluna 12. tamanho 4

void setup()   {
  Serial.begin(9600);
  lcdBar1.createChars();
  pinMode(44, OUTPUT);
  analogWrite(44, 255/6); //utilizado para aumentar o contraste
  lcd.begin(16, 2);
}

int i = 0;
int perc;

void loop() {
  lcd.setCursor(0, 0);
  int value = i % 100;
  perc = value/100.0 * 100;
  if (value < 010) {lcd.print("00");} else {
  if (value < 100) {lcd.print("0");}       }
  lcd.print(value);
  lcd.print("/");
  lcd.print(100);
  
  lcdBar1.setPerc(perc);  //atualização da primeira barra de progresso
  
  
  lcd.setCursor(0, 1);
  value = (i++) % 200;
  perc = value/200.0 * 100;
  if (value <= 010) {lcd.print("00");} else {
  if (value <  100) {lcd.print("0");}       }
  lcd.print(value);
  lcd.print("/");
  lcd.print(200);
  
  lcdBar2.setPerc(perc); //atualização da segunda barra de progresso
  
  delay(100);  
}




Código 01 da segunda versão :


/*
Fabiano A. Arndt - 2015
www.youtube.com/user/fabianoallex
www.facebook.com/dicasarduino
fabianoallex@gmail.com
*/
 
#include <LiquidCrystal.h>
 
/*************************************************************************************************************
*******************************CLASSE LCD PROGRESS BAR********************************************************
**************************************************************************************************************/
byte c1[8] = {B00000, B00000, B00000, B00000, B00000, B00000, B00000, B11111};
byte c2[8] = {B00000, B00000, B00000, B00000, B00000, B00000, B11111, B11111};
byte c3[8] = {B00000, B00000, B00000, B00000, B00000, B11111, B11111, B11111};
byte c4[8] = {B00000, B00000, B00000, B00000, B11111, B11111, B11111, B11111};
byte c5[8] = {B00000, B00000, B00000, B11111, B11111, B11111, B11111, B11111};
byte c6[8] = {B00000, B00000, B11111, B11111, B11111, B11111, B11111, B11111};
byte c7[8] = {B00000, B11111, B11111, B11111, B11111, B11111, B11111, B11111};
byte c8[8] = {B11111, B11111, B11111, B11111, B11111, B11111, B11111, B11111};
             
 
class LCDProgressBar {
  private:
    LiquidCrystal * _lcd;
    int _row;
    int _col;
    int _len;
    int _perc; /*0..100*/
  public:
    void createChars() {
      _lcd->createChar(0, c1);
      _lcd->createChar(1, c2);
      _lcd->createChar(2, c3);
      _lcd->createChar(3, c4);
      _lcd->createChar(4, c5);
      _lcd->createChar(5, c6);
      _lcd->createChar(6, c7);
      _lcd->createChar(7, c8);
    }
   
    LCDProgressBar(LiquidCrystal * lcd, int row, int col, int len) {
      _lcd = lcd;      _row = row;      _col = col;      _len = len;
    }
     
    void setPerc(int perc){
      _perc = perc;
      if (perc > 100) { _perc = 100; }
      if (perc < 000) { _perc = 000; }
       
      _lcd->setCursor(_col, _row);
      for (int i=0; i<(_len);i++) { _lcd->print(" "); }
      _lcd->setCursor(_col, _row);
       
      int bars  = (8+1) * _len * _perc / 100;
      int div   = bars / 8;  //divisao
      int resto = bars % 8;  //resto
      for (int i=0; i<div; i++)  { _lcd->write((byte)7);         }  //pinta todo o quadro
      if (resto > 0 )            { _lcd->write((byte)(resto-1)); }  //pinta o quadro com a quantidade de barras proporcional
    }
};
/*************************************************************************************************************
*******************************FIM CLASSE LCD PROGRESS BAR****************************************************
**************************************************************************************************************/
 
 
LiquidCrystal lcd(12, 11, 5, 4, 3, 2);
LCDProgressBar lcdBar1(&lcd, 0, 8,  8); //inclui uma barra no lcd, primeira linha, coluna 8. tamanho 8
LCDProgressBar lcdBar2(&lcd, 1, 12, 4); //inclui outra barra no lcd, segunda linha, coluna 12. tamanho 4
 
void setup()   {
  Serial.begin(9600);
  lcdBar1.createChars();
  pinMode(44, OUTPUT);
  analogWrite(44, 255/6); //utilizado para aumentar o contraste
  lcd.begin(16, 2);
}
 
int i = 0;
int perc;
 
void loop() {
  lcd.setCursor(0, 0);
  int value = i % 100;
  perc = value/100.0 * 100;
  if (value < 010) {lcd.print("00");} else {
  if (value < 100) {lcd.print("0");}       }
  lcd.print(value);
  lcd.print("/");
  lcd.print(100);
   
  lcdBar1.setPerc(perc);  //atualização da primeira barra de progresso
   
   
  lcd.setCursor(0, 1);
  value = (i++) % 200;
  perc = value/200.0 * 100;
  if (value <= 010) {lcd.print("00");} else {
  if (value <  100) {lcd.print("0");}       }
  lcd.print(value);
  lcd.print("/");
  lcd.print(200);
   
  lcdBar2.setPerc(perc); //atualização da segunda barra de progresso
   
  delay(100);  
}



Código 02 da segunda versão :


/*
Fabiano A. Arndt - 2015
www.youtube.com/user/fabianoallex
www.facebook.com/dicasarduino
fabianoallex@gmail.com
*/
 
#include <LiquidCrystal.h>
 
/*************************************************************************************************************
*******************************CLASSE LCD PROGRESS BAR********************************************************
**************************************************************************************************************/
byte c1[8] = {B00000, B00000, B00000, B00000, B00000, B00000, B00000, B11111};
byte c2[8] = {B00000, B00000, B00000, B00000, B00000, B00000, B11111, B11111};
byte c3[8] = {B00000, B00000, B00000, B00000, B00000, B11111, B11111, B11111};
byte c4[8] = {B00000, B00000, B00000, B00000, B11111, B11111, B11111, B11111};
byte c5[8] = {B00000, B00000, B00000, B11111, B11111, B11111, B11111, B11111};
byte c6[8] = {B00000, B00000, B11111, B11111, B11111, B11111, B11111, B11111};
byte c7[8] = {B00000, B11111, B11111, B11111, B11111, B11111, B11111, B11111};
byte c8[8] = {B11111, B11111, B11111, B11111, B11111, B11111, B11111, B11111};
             
 
class LCDProgressBar {
  private:
    LiquidCrystal * _lcd;
    int _row;
    int _col;
    int _len;
    int _perc; /*0..100*/
  public:
    void createChars() {
      _lcd->createChar(0, c1);
      _lcd->createChar(1, c2);
      _lcd->createChar(2, c3);
      _lcd->createChar(3, c4);
      _lcd->createChar(4, c5);
      _lcd->createChar(5, c6);
      _lcd->createChar(6, c7);
      _lcd->createChar(7, c8);
    }
   
    LCDProgressBar(LiquidCrystal * lcd, int row, int col, int len) {
      _lcd = lcd;      _row = row;      _col = col;      _len = len;
    }
     
    void setPerc(int perc){
      _perc = perc;
      if (perc > 100) { _perc = 100; }
      if (perc < 000) { _perc = 000; }
       
      _lcd->setCursor(_col, _row);
      for (int i=0; i<(_len);i++) { _lcd->print(" "); }
      _lcd->setCursor(_col, _row);
      
      int bars  = (8+1) * _len * _perc / 100.0;
      int div   = bars / 8.0;  //divisao
      int resto = bars % 8;  //resto
      Serial.println(div);
      for (int i=0; i<div; i++)  { _lcd->write((byte)7);         }  //pinta todo o quadro
      if (resto > 0 )            { _lcd->write((byte)(resto-1)); }  //pinta o quadro com a quantidade de barras proporcional
    }
};
/*************************************************************************************************************
*******************************FIM CLASSE LCD PROGRESS BAR****************************************************
**************************************************************************************************************/
 
 
LiquidCrystal lcd(12, 11, 5, 4, 3, 2);
LCDProgressBar lcdBar1(&lcd, 1, 0,  1); //inclui uma barra no lcd, segunda linha, coluna 0. tamanho 1
LCDProgressBar lcdBar2(&lcd, 1, 2, 1); //inclui outra barra no lcd, segunda linha, coluna 2. tamanho 1
LCDProgressBar lcdBar3(&lcd, 1, 4, 1); //inclui outra barra no lcd, segunda linha, coluna 4. tamanho 1
LCDProgressBar lcdBar4(&lcd, 1, 6, 1); //inclui outra barra no lcd, segunda linha, coluna 6. tamanho 1
LCDProgressBar lcdBar5(&lcd, 1, 8, 1); //inclui outra barra no lcd, segunda linha, coluna 8. tamanho 1
LCDProgressBar lcdBar6(&lcd, 1, 10, 1); //inclui outra barra no lcd, segunda linha, coluna 10. tamanho 1
LCDProgressBar lcdBar7(&lcd, 1, 12, 1); //inclui outra barra no lcd, segunda linha, coluna 12. tamanho 1
LCDProgressBar lcdBar8(&lcd, 1, 14, 1); //inclui outra barra no lcd, segunda linha, coluna 14. tamanho 1

 
void setup()   {
  Serial.begin(9600);
  lcdBar1.createChars();
  pinMode(44, OUTPUT);
  analogWrite(44, 255/6); //utilizado para aumentar o contraste
  lcd.begin(16, 2);
}
 
unsigned int i = 0;
int perc;
 
void loop() {
  lcd.setCursor(0, 0);
  lcd.print("1 2 3 4 5 6 7 8");
  lcd.setCursor(0, 1);
  
  int value = i % 100;
  perc = value/100.0 * 100;
  lcdBar1.setPerc(perc);  //atualização da primeira barra de progresso
  
  
  value = i % 10;
  perc = value/10.0 * 100;
  lcdBar2.setPerc(perc);  //atualização da segunda barra de progresso
  
  
  value = i % 50;
  perc = value/50.0 * 100;
  lcdBar3.setPerc(perc);  //atualização da terceira barra de progresso
  
  
  value = i % 300;
  perc = value/300.0 * 100;
  lcdBar4.setPerc(perc);  //atualização da quarta barra de progresso
   
  
  value = i % 240;
  perc = value/240.0 * 100;
  lcdBar5.setPerc(perc);  //atualização da quinta barra de progresso
  
  
  value = i % 140;
  perc = value/140.0 * 100;
  lcdBar6.setPerc(perc);  //atualização da sexta barra de progresso
  
  
  value = i % 30;
  perc = value/30.0 * 100;
  lcdBar7.setPerc(perc);  //atualização da setima barra de progresso
  
  
  value = (i++) % 180;
  perc = value/180.0 * 100;
  lcdBar8.setPerc(perc); //atualização da oitava barra de progresso
   
  delay(100);  
}


Atualizado 25/05/2016

Em um artigo sobre Rotary Encoder fiz alguns exemplos utilizando as barras de progresso e fiz algumas modificações em uma das barras mostradas aqui.

Veja o artigo aqui: http://fabianoallex.blogspot.com.br/2016/05/arduino-rotary-encoder.html

Vídeo:



Atualizado 27/05/2016

Implementei outra versão das barras de progresso, ela segue o padrão utilizado para mostrar o volume de um som ou a intensidade de um sinal sem fio.

Há duas possibilidades de uso, com altura=1 ou altura=2, que indicará se a barra ocupará uma ou duas linhas do display.

Vídeo:


Código-fonte:


/*
Fabiano A. Arndt - 2016
www.youtube.com/user/fabianoallex
www.facebook.com/dicasarduino
fabianoallex@gmail.com
*/
  
#include <LiquidCrystal.h>
 
/*************************************************************************************************************
************************************CLASSE ROTARY ENCODER*****************************************************
*************************************************************************************************************/
#define ROTARY_NO_BUTTON     255
   
struct RotaryEncoderLimits{
  int min;
  int max;
};
   
class RotaryEncoder {
  private:
    byte _pin_clk;
    byte _pin_dt;
    byte _pin_sw;
    volatile byte _num_results;
    volatile int _result;
    volatile int * _results;
    byte _index_result;
    RotaryEncoderLimits * _limits;
       
    boolean _a;
    boolean _b;
  public:
    RotaryEncoder(byte pin_clk, byte pin_dt, byte pin_sw = ROTARY_NO_BUTTON, byte num_results=1, RotaryEncoderLimits * limits=0){   //parametro do botao opcional
      _pin_clk = pin_clk;
      _pin_dt = pin_dt;
      _pin_sw = pin_sw;
      pinMode(_pin_clk, INPUT);
      pinMode(_pin_dt, INPUT);
      if (_pin_sw != ROTARY_NO_BUTTON){ 
        pinMode(_pin_sw, INPUT); 
        digitalWrite(_pin_sw, HIGH);
      }
      if (num_results == 0) { num_results = 1; }
      _num_results = num_results;
      _results = new int[_num_results];
      for (int i; i<_num_results; i++){ _results[i] = (limits) ? limits[i].min : 0; }
      _index_result = 0;
      _limits = limits;
      _a = false;
      _b = false;
    }
    byte getIndex() { return _index_result; }
    void next()     { _index_result++; if (_index_result >= _num_results) { _index_result = 0; } } 
    void update_a() {
      _result = 0;
      delay (1);
      if( digitalRead(_pin_clk) != _a ) { 
        _a = !_a;
        if ( _a && !_b ) { _result = -1; }
      }
      if (_results[_index_result]+_result >= _limits[_index_result].min && 
          _results[_index_result]+_result <= _limits[_index_result].max ) {
        _results[_index_result] += _result;
      }
    }
    void update_b() {
      _result = 0;
      delay (1);
      if( digitalRead(_pin_dt) != _b ) {  
        _b = !_b;
        if ( _b && !_a ) { _result = +1; }
      }
      if (_results[_index_result]+_result >= _limits[_index_result].min && 
          _results[_index_result]+_result <= _limits[_index_result].max ) {
        _results[_index_result] += _result;
      }
    }
    int read(){ return _result; }                                        //retorn -1, 0 ou 1.
    int getValue(int index=-1) {                                         //retorna o valor da variável corrente ou a passada como parametro
      if (index < 0 ){ return _results[_index_result]; }
      return _results[index];
    }
    void setValue(int value){ _results[_index_result] = value; }         //caso a variável inicializa em determinado valor diferente de zero, utilizar esse método.
    int buttonRead(){ return (_pin_sw == ROTARY_NO_BUTTON) ? LOW : digitalRead(_pin_sw); }
};
/*************************************************************************************************************
************************************FIM CLASSE ROTARY ENCODER*************************************************
*************************************************************************************************************/
  
/*************************************************************************************************************
*******************************CLASSE LCD PROGRESS BAR********************************************************
**************************************************************************************************************/
byte c0[8] = {B00000,  B00000,  B00000,  B00000,  B00000,  B00000,  B00000,  B10000 };
byte c1[8] = {B00000,  B00000,  B00000,  B00000,  B00000,  B00000,  B11000,  B11000 };
byte c2[8] = {B00000,  B00000,  B00000,  B00000,  B00011,  B00011,  B11011,  B11011 };
byte c3[8] = {B00000,  B00000,  B11000,  B11000,  B11000,  B11000,  B11000,  B11000 };
byte c4[8] = {B00011,  B00011,  B11011,  B11011,  B11011,  B11011,  B11011,  B11011 };
byte c5[8] = {B11000,  B11000,  B11000,  B11000,  B11000,  B11000,  B11000,  B11000 };
byte c6[8] = {B11011,  B11011,  B11011,  B11011,  B11011,  B11011,  B11011,  B11011 };
  
class LCDProgressBar {
  private:
    LiquidCrystal * _lcd;  //ponteiro para um objeto lcd
    int _row;
    int _col;
    int _perc; /*0..100*/
    int _heigh;
  public:
    void createChars() { _lcd->createChar(0, c0);  _lcd->createChar(1, c1);  _lcd->createChar(2, c2); _lcd->createChar(3, c3);  _lcd->createChar(4, c4);  _lcd->createChar(5, c5);  _lcd->createChar(6, c6);  }
    LCDProgressBar(LiquidCrystal * lcd, int row, int col, int heigh=1) { 
      _lcd = lcd; 
      _row = row; 
      _col = col; 
      _heigh = (heigh >= 2 && _row > 0) ? 2 : 1;   //heigh assume apenas os valores 2 e 1. e só assume valor = 2 quando estiver posicionado a partir da segunda linha
    }
    void setPerc(int perc) {
      int division = (_heigh == 2) ? 33 / 2 : 33;
      _perc = perc;
      if (perc > 100) { _perc = 100; }
      if (perc < 000) { _perc = 000; } 
      if (_heigh == 2){
        _lcd->setCursor(_col+2, _row-1);
        _lcd->print("  ");
        _lcd->setCursor(_col+2, _row-1);
      }
      _lcd->setCursor(_col, _row);
      _lcd->print((_heigh == 2) ? "    " : "  ");
      _lcd->setCursor(_col, _row);
      if (_perc == 0) {  _lcd->write((byte)0); } else {
        if (_perc > 0 && _perc <= division) { 
          _lcd->write((byte)1); 
        } else { 
          _lcd->write((byte)2); 
          if (_perc > division*2 && _perc < 100/_heigh) { _lcd->write((byte)3); } 
          if (_perc >= 100/_heigh)                      { _lcd->write((byte)4);
          }
        }
      }
      if (_heigh == 2 && _perc > 50){
        if (_perc > 50 && _perc <= (50+division)) { 
          _lcd->write((byte)5); 
        } else { 
          _lcd->write((byte)6); 
          if (_perc > (50+division*2) && _perc < 100) { _lcd->write((byte)5); } 
          if (_perc == 100       )                    { _lcd->write((byte)6); }
        }
        _lcd->setCursor(_col+2, _row-1);
        _lcd->print("  ");
        _lcd->setCursor(_col+2, _row-1);
        if (_perc > 50 && _perc <= (50+division)) { 
          _lcd->write((byte)1); 
        } else { 
          _lcd->write((byte)2); 
          if (_perc > (50+division*2) && _perc < 100 ) { _lcd->write((byte)3); } 
          if (_perc >= 100)                            { _lcd->write((byte)4); }
        }
      }
    }
};
/*************************************************************************************************************
*******************************FIM CLASSE LCD PROGRESS BAR****************************************************
**************************************************************************************************************/

/*************************************************************************************************************
*******************************DECLARACAO DOS OBJETOS*********************************************************
**************************************************************************************************************/
LiquidCrystal lcd(12, 11, 10, 9, 8, 7);
LCDProgressBar lcdBar1(&lcd, 0, 9, 1); //inclui uma barra no lcd, primeira linha, coluna 9. altura 1
LCDProgressBar lcdBar2(&lcd, 1, 11, 2); //inclui outra barra no lcd, segunda linha, coluna 11. altura 2
 
RotaryEncoderLimits lim[] = { {0,30}, {0,20} };  //limites máximos e mínimos que as variaveis podem atingir
RotaryEncoder       re(A0, A1, 4, 2, lim);  //pino clk, pino dt, pino sw, variaveis, limites
/*************************************************************************************************************
*******************************FIM DECLARACAO DOS OBJETOS*****************************************************
**************************************************************************************************************/
 
/*************************************************************************************************************
*******************************TRATAMENTO DAS INTERRUPÇÕES****************************************************
**************************************************************************************************************/
//interrupções dos pinos A0 e A1 via Pin Change Interrupt
ISR(PCINT1_vect) {
  volatile static byte lastVal_a0 = LOW;
  volatile static byte lastVal_a1 = LOW;
  byte val_a0 = digitalRead(A0);
  byte val_a1 = digitalRead(A1);
  if (lastVal_a0 != val_a0){ re.update_a(); lastVal_a0 = val_a0; }
  if (lastVal_a1 != val_a1){ re.update_b(); lastVal_a1 = val_a1; }
}

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

/*************************************************************************************************************
*******************************FIM TRATAMENTO DAS INTERRUPÇÕES****************************************************
**************************************************************************************************************/

void indicador_rotary(){
  char c = ((re.getIndex() == 0)) ? '>' : ' ';
  lcd.setCursor(7, 0); 
  lcd.print(c);
  c = ((re.getIndex() == 1)) ? '>' : ' ';
  lcd.setCursor(7, 1); 
  lcd.print(c);
}
  
void setup() { 
  setup_interrupts();
  
  lcdBar1.createChars();
  lcd.begin(16, 2);
  indicador_rotary();
}
  
void loop() {
  static int value1 = -1;
  static int value2 = -1;
   
  if (value1 != re.getValue(0)) {
    lcd.setCursor(0, 0);
    value1 = re.getValue(0);
    int perc = value1/30.0 * 100;
    if (value1 <  10) {lcd.print("00");} else {
    if (value1 < 100) {lcd.print("0");}       }
    lcd.print(value1);
    lcd.print("/");
    lcd.print(30);
 
    lcdBar1.setPerc(perc);  //atualização da primeira barra de progresso
  }
    
  if (value2 != re.getValue(1)) {
    lcd.setCursor(0, 1);
    value2 = re.getValue(1);
    int perc = value2/20.0 * 100;
    if (value2 <  10) {lcd.print("00");} else {
    if (value2 < 100) {lcd.print("0");}       }
    lcd.print(value2);
    lcd.print("/");
    lcd.print(20);
     
    lcdBar2.setPerc(perc); //atualização da segunda barra de progresso
  }
    
  //controla o click do botao do enconder
  static byte b = HIGH; //pra ler apenas uma vez o botao ao pressionar
  if( re.buttonRead() == LOW && b != re.buttonRead() ) {
    re.next();           //passa para a próxima variável (index)
    indicador_rotary();
    delay(200);          //debounce meia boca
  }
  b = re.buttonRead();
    
  delay(100);
}

e nessa outra versão, uma barra que permite informar valores que variam de um número negativo até um número positivo:

vídeo



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

*/
byte c0[8] = {B11011,  B00000,  B00000,  B00000,  B00000,  B00000,  B00000,  B11011 };
byte c1[8] = {B11011,  B00000,  B00000,  B00100,  B00100,  B00000,  B00000,  B11011 };
byte c2[8] = {B11011,  B11000,  B11000,  B11000,  B11000,  B11000,  B11000,  B11011 };
byte c3[8] = {B11011,  B00011,  B00011,  B00011,  B00011,  B00011,  B00011,  B11011 };
byte c4[8] = {B11011,  B11011,  B11011,  B11011,  B11011,  B11011,  B11011,  B11011 };

  
class LCDProgressBar {
  private:
    LiquidCrystal * _lcd;  //ponteiro para um objeto lcd
    int _row;
    int _col;
    int _perc; /*0..100*/
    int _len;
  public:
    void createChars() { 
      _lcd->createChar(0, c0);  
      _lcd->createChar(1, c1);  
      _lcd->createChar(2, c2); 
      _lcd->createChar(3, c3);  
      _lcd->createChar(4, c4);
    }
    LCDProgressBar(LiquidCrystal * lcd, int row, int col, int len) { 
      _lcd = lcd; 
      _row = row; 
      _col = col; 
      _len = len; 
    }
    void setPerc(int perc) {
      _perc = perc;
      if (_perc >  100) { _perc =  100; }
      if (_perc < -100) { _perc = -100; } 
      
      _lcd->setCursor(_col, _row);
      for (int i=0; i<(_len*2-1);i++) { _lcd->write((byte)0); }   //preenche com caracteres vazio
      _lcd->setCursor(_col + (_len-1), _row);
      _lcd->write((byte)1);                                 //preenche com caracter zero (nenhum valor)
      
      if (_perc > 0){
        _lcd->setCursor(_col + (_len-1), _row); 
        int bars  = (2*(_len-1)+1) * _perc / 100;  
        if (bars >= 1) { _lcd->write((byte)3); }
        int div   = (bars-1) / 2;  //divisao
        int resto = (bars-1) % 2;  //resto
        for (int i=0; i<div; i++) { _lcd->write((byte)4);         }  //pinta todo o quadro
        if (resto > 0)            { _lcd->write((byte)2); }
      } else if (_perc < 0) {
        _lcd->setCursor(_col + (_len-1), _row); 
        int bars  = (2*(_len-1)+1) * (-_perc) / 100;  
        if (bars >= 1) { _lcd->write((byte)2); }
        int div   = (bars-1) / 2;  //divisao
        int resto = (bars-1) % 2;  //resto
        int i = 0;
        for (i=0; i<div; i++) {
          _lcd->setCursor(_col + _len/2-i, _row); 
          _lcd->write((byte)4); 
        }
        if (resto > 0) { 
          _lcd->setCursor(_col + _len/2-i, _row); 
          _lcd->write((byte)3); 
        }
      }
    }
};
/*************************************************************************************************************
*******************************FIM CLASSE LCD PROGRESS BAR****************************************************
**************************************************************************************************************/

/*************************************************************************************************************
*******************************DECLARACAO DOS OBJETOS*********************************************************
**************************************************************************************************************/
LiquidCrystal lcd(12, 11, 10, 9, 8, 7);
LCDProgressBar lcdBar1(&lcd, 0, 9, 3); //inclui uma barra no lcd, primeira linha, coluna 9. tamanho 3 (3*2-1 --> 5)
LCDProgressBar lcdBar2(&lcd, 1, 9, 4); //inclui outra barra no lcd, segunda linha, coluna 11. tamanho 4 (4*2-1 --> 7)
 
RotaryEncoderLimits lim[] = { {-30,30}, {-20,20} };  //limites máximos e mínimos que as variaveis podem atingir
RotaryEncoder       re(A0, A1, 4, 2, lim);  //pino clk, pino dt, pino sw, variaveis, limites
/*************************************************************************************************************
*******************************FIM DECLARACAO DOS OBJETOS*****************************************************
**************************************************************************************************************/
 
/*************************************************************************************************************
*******************************TRATAMENTO DAS INTERRUPÇÕES****************************************************
**************************************************************************************************************/
//interrupções dos pinos A0 e A1 via Pin Change Interrupt
ISR(PCINT1_vect) {
  volatile static byte lastVal_a0 = LOW;
  volatile static byte lastVal_a1 = LOW;
  byte val_a0 = digitalRead(A0);
  byte val_a1 = digitalRead(A1);
  if (lastVal_a0 != val_a0){ re.update_a(); lastVal_a0 = val_a0; }
  if (lastVal_a1 != val_a1){ re.update_b(); lastVal_a1 = val_a1; }
}

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

/*************************************************************************************************************
*******************************FIM TRATAMENTO DAS INTERRUPÇÕES****************************************************
**************************************************************************************************************/

void indicador_rotary(){
  char c = ((re.getIndex() == 0)) ? '>' : ' ';
  lcd.setCursor(7, 0); 
  lcd.print(c);
  c = ((re.getIndex() == 1)) ? '>' : ' ';
  lcd.setCursor(7, 1); 
  lcd.print(c);
}
  
void setup() { 
  setup_interrupts();
  
  lcdBar1.createChars();
  lcd.begin(16, 2);
  
  re.setValue(0, 0);
  re.setValue(0, 1);
  
  indicador_rotary();
}
  
void loop() {
  static int value1 = -1;
  static int value2 = -1;
   
  if (value1 != re.getValue(0)) {
    lcd.setCursor(0, 0);
    value1 = re.getValue(0);
    int perc = value1/30.0 * 100;
    lcd.print("       ");
    lcd.setCursor(0, 0);
    lcd.print(value1);
    lcd.print("/");
    lcd.print(30);
 
    lcdBar1.setPerc(perc);  //atualização da primeira barra de progresso
  }
    
  if (value2 != re.getValue(1)) {
    lcd.setCursor(0, 1);
    value2 = re.getValue(1);
    int perc = value2/20.0 * 100;
    lcd.print("       ");
    lcd.setCursor(0, 1);
    lcd.print(value2);
    lcd.print("/");
    lcd.print(20);
     
    lcdBar2.setPerc(perc); //atualização da segunda barra de progresso
  }
    
  //controla o click do botao do enconder
  static byte b = HIGH; //pra ler apenas uma vez o botao ao pressionar
  if( re.buttonRead() == LOW && b != re.buttonRead() ) {
    re.next();           //passa para a próxima variável (index)
    indicador_rotary();
    delay(200);          //debounce meia boca
  }
  b = re.buttonRead();
    
  delay(100);
}

terça-feira, 13 de outubro de 2015

Arduino - Snake Game (jogo da cobrinha)

Implementação do jogo Snake Game (jogo da cobrinha)

Para o teclado foi utilizado a serial, através de uma arquivo .bat

Por enquanto as explicações estão no vídeo.






Arquivo .bat

MODE COM6 BAUD=9600 PARITY=n DATA=8

:LOOP
  
  CHOICE /C:1235 /M "1: liga; 2: desliga; 3: pisca; 4: sair " 
  IF errorlevel 4 GOTO TOP
  IF errorlevel 3 GOTO RIGHT
  IF errorlevel 2 GOTO BOTTOM
  IF errorlevel 1 GOTO LEFT

  :RIGHT  
  ECHO b > COM6 
  GOTO END

  :LEFT
  ECHO a > COM6  
  GOTO END

  :TOP
  ECHO c > COM6  
  GOTO END

  :BOTTOM
  ECHO d > COM6  
  
  :END
  CLS
GOTO LOOP 

:SAIR




Código-Fonte:

/*************************************************************************************************************
*******************************LEDCONTROL ALTERADA************************************************************
**************************************************************************************************************
mais informações aqui: http://fabianoallex.blogspot.com.br/2015/09/arduino-alteracoes-na-biblioteca.html
**************************************************************************************************************/

//the opcodes for the MAX7221 and MAX7219
#define OP_DECODEMODE  9
#define OP_INTENSITY   10
#define OP_SCANLIMIT   11
#define OP_SHUTDOWN    12
#define OP_DISPLAYTEST 15

class LedControl {
  private :
    byte spidata[16];
    byte * status;
    int SPI_MOSI;
    int SPI_CLK;
    int SPI_CS;
    int maxDevices;
    int _auto_send;
    
    void spiTransfer(int addr, volatile byte opcode, volatile byte data) {
      int offset   = addr*2;
      int maxbytes = maxDevices*2;
      for(int i=0;i<maxbytes;i++)  { spidata[i]=(byte)0; }
      spidata[offset+1] = opcode;
      spidata[offset]   = data;
      
      digitalWrite(SPI_CS,LOW);
      for(int i=maxbytes;i>0;i--) { shiftOut(SPI_MOSI,SPI_CLK,MSBFIRST,spidata[i-1]); }
      digitalWrite(SPI_CS,HIGH);
    }
      
  public:
    LedControl(int dataPin, int clkPin, int csPin, int numDevices) {
      _auto_send  = true;
      SPI_MOSI    = dataPin;
      SPI_CLK     = clkPin;
      SPI_CS      = csPin;
      maxDevices  = numDevices;
      
      pinMode(SPI_MOSI, OUTPUT);
      pinMode(SPI_CLK,  OUTPUT);
      pinMode(SPI_CS,   OUTPUT);
      digitalWrite(SPI_CS, HIGH);
      
      status      = new byte[maxDevices * 8]; //instancia o array de acordo com a quantia de displays usados
      for(int i=0;i<maxDevices * 8 ;i++) { status[i]=0x00; }
      
      for(int i=0;i<maxDevices;i++) {
        spiTransfer(i, OP_DISPLAYTEST,0);
        setScanLimit(i, 7);               //scanlimit is set to max on startup
        spiTransfer(i, OP_DECODEMODE,0);  //decode is done in source
        clearDisplay(i);
        shutdown(i,true);                 //we go into shutdown-mode on startup
      }
    }
    
    void startWrite() {  _auto_send = false;  };
    
    void send() {
      for (int j=0; j<maxDevices; j++) {
        int offset = j*8;
        for(int i=0;i<8;i++) { spiTransfer(j, i+1, status[offset+i]); }
      }
      _auto_send = true;
    }
    
    int getDeviceCount(){ return maxDevices; }
    
    void shutdown(int addr, bool b){
      if(addr<0 || addr>=maxDevices) return;
      spiTransfer(addr, OP_SHUTDOWN, b ? 0 : 1);
    }
    
    void setScanLimit(int addr, int limit){
      if(addr<0 || addr>=maxDevices) return;
      if(limit>=0 && limit<8) spiTransfer(addr, OP_SCANLIMIT,limit);
    }
    
    void setIntensity(int addr, int intensity) {
      if(addr<0 || addr>=maxDevices)   {  return;                                    }
      if(intensity>=0 && intensity<16) {  spiTransfer(addr, OP_INTENSITY, intensity); }
    }
    
    void clearDisplay(int addr){
      if(addr<0 || addr>=maxDevices) return;
      
      int offset = addr*8;
      for(int i=0;i<8;i++) {
        status[offset+i] = 0;
        if (_auto_send) { spiTransfer(addr, i+1, status[offset+i]); }
      }
    }
    
    void setLed(int addr, int row, int column, boolean state) {
      if(addr<0 || addr>=maxDevices)             { return; }
      if(row<0 || row>7 || column<0 || column>7) { return; }
      
      int offset = addr*8;
      byte val = B10000000 >> column;
      
      if(state) { status[offset+row] = status[offset+row] | val; }
      else {
        val=~val;
        status[offset+row] = status[offset+row]&val;
      }
      
      if (_auto_send) { spiTransfer(addr, row+1, status[offset+row]); }
    }
    
    void setRow(int addr, int row, byte value) {
      if(addr<0 || addr>=maxDevices) return;
      if(row<0 || row>7) return;
      int offset = addr*8;
      status[offset+row] = value;
      if (_auto_send) {
        spiTransfer(addr, row+1, status[offset+row]);
      }
    }
    
    void setColumn(int addr, int col, byte value) {
      if(addr<0 || addr>=maxDevices) return;
      if(col<0 || col>7)             return;
      
      byte val;
      for(int row=0; row<8; row++) {
        val=value >> (7-row);
        val=val & 0x01;
        setLed(addr,row,col,val);
      }
    }
};
/*************************************************************************************************************
*******************************FIM LEDCONTROL ALTERADA********************************************************
**************************************************************************************************************/

/*************************************************************************************************************
*******************************BIT ARRAY *********************************************************************
**************************************************************************************************************
mais informações aqui: http://fabianoallex.blogspot.com.br/2015/09/arduino-array-de-bits.html
**************************************************************************************************************/

class BitArray{
  private:
    int _num_bits;   //quantidade de bits a serem gerenciados
    int _num_bytes;  //quantidade de bytes utilizados para armazenar os bits a serem gerenciados
    byte * _bytes;   //array de bytes onde estaram armazenados os bits
  public:
    BitArray(int num_bits){
      _num_bits  = num_bits;
      _num_bytes = _num_bits/8 + (_num_bits%8 ? 1 : 0) + 1;
      _bytes = (byte *)(malloc( _num_bytes * sizeof(byte) ) );
    }
    
    void write(int index, byte value) {
      byte b = _bytes[ index/8 + (index%8 ? 1 : 0) ];
      unsigned int bit = index%8;
      if (value) { b |= (1 << bit); } else { b &= ~(1 << bit);  }
      _bytes[ index/8 + (index%8 ? 1 : 0) ] = b;
    }
    
    void write(byte value) {
      for(int j=0; j<_num_bytes;j++) { _bytes[j] = value ? B11111111 : B00000000;  } 
    }
    
    int read(int index) {
      byte b = _bytes[ index/8 + (index%8 ? 1 : 0) ];
      unsigned int bit = index%8;
      return (b & (1 << bit)) != 0;
    }
    
    ~BitArray(){ free ( _bytes ); }
};


class BitArray2D {
  private:
    unsigned int _rows;
    unsigned int _columns;
    unsigned int _cols_array; //pra cada 8 colunas, 1 byte é usado 
    byte**       _bits;
  public:
    BitArray2D(unsigned int rows, unsigned int columns){
      _rows       = rows;
      _columns    = columns;
      _cols_array = columns/8 + (_columns%8 ? 1 : 0) + 1; //divide por 8 o número de colunas
      _bits = (byte **)malloc(_rows * sizeof(byte *));
      for(int i=0;i<_rows;i++){ _bits[i] = (byte *)malloc(  _cols_array  *  sizeof(byte)); } //cria varios arrays
      clear();
    }
    
    unsigned int rows(){ return _rows; }
    unsigned int columns(){ return _columns; }
    
    void clear() { 
      for(int i=0;i<_rows;i++){      
        for(int j=0; j<_cols_array;j++) { _bits[i][j] = B00000000; }       
      }   
    }
  
    void write(unsigned int row, unsigned int column, int value){
      byte b = _bits[row][ column/8 + (column%8 ? 1 : 0) ];
      unsigned int bit = column%8;
      
      if (value) { b |= (1 << bit); } else { b &= ~(1 << bit);  }
      
      _bits[row][ column/8 + (column%8 ? 1 : 0) ] = b;
    }
    
    void write(byte value){
      for(int i=0;i<_rows;i++){      
        for(int j=0; j<_cols_array;j++) {      
          _bits[i][j] = value ? B11111111 : B00000000;     
        }       
      }  
    }
    
    int read(unsigned int row, unsigned int column){
      byte b = _bits[row][ column/8 + (column%8 ? 1 : 0) ];
      unsigned int bit = column%8;
      
      return (b & (1 << bit)) != 0;
    }
    
    void toggle(unsigned int row, unsigned int column){ write(row, column, !read(row, column)); }
    void toggle(){ for(int i=0;i<_rows;i++){      for(int j=0; j<_columns;j++) {      toggle(i,j);   }   }   }
};

/*************************************************************************************************************
*******************************FIM BIT ARRAY *****************************************************************
**************************************************************************************************************/

/*************************************************************************************************************
************************************CLASSE UNIQUE RANDOM******************************************************
**************************************************************************************************************
mais informações aqui: http://fabianoallex.blogspot.com.br/2015/09/arduino-numeros-aleatorio-repetidos-e.html
*************************************************************************************************************/
 
class UniqueRandom{
  private:
    int _index;
    int _min;
    int _max;
    int _size;
    int* _list;
    void _init(int min, int max) {
      _list = 0; 
      if (min < max) { _min = min; _max = max; } else { _min = max; _max = min; }
      _size = _max - _min; 
      _index = 0;
    }    
  public:
    UniqueRandom(int max)           { _init(0,   max); randomize(); } //construtor com 1 parametro
    UniqueRandom(int min, int max)  { _init(min, max); randomize(); } //construtor com 2 parametros
     
    void randomize() {
      _index = 0;
      
      if (_list == 0) { _list = (int*) malloc(size() * sizeof(int)); }  
      for (int i=0; i<size(); i++) {   _list[i] = _min+i;  }   //preenche a lista do menor ao maior valor
       
      //embaralha a lista
      for (int i=0; i<size(); i++) {  
        int r = random(0, size());     //sorteia uma posição qualquer
        int aux = _list[i];               
        _list[i] = _list[r];
        _list[r] = aux;
      }
    }
     
    int next() {                                  //retorna o proximo numero da lista
      int n = _list[_index++];
      if (_index >= size() ) { _index = 0;} //após recuper o ultimo numero, recomeça na posicao 0
      return n;
    }
     
    int size() { return _size; }
     
    ~UniqueRandom(){ free ( _list ); }  //destrutor
};
/*************************************************************************************************************
************************************FIM CLASSE UNIQUE RANDOM**************************************************
*************************************************************************************************************/


/*************************************************************************************************************
*******************************DISPLAY************************************************************************
**************************************************************************************************************/

/*
rotation indica qual parte do display estará para cima

         TOP
L  . . . . . . . . R
E  . . . . . . . . I
F  . . . . . . . . G
T  . . . . . . . . H
   . . . . . . . . T
   . . . . . . . .
   . . . . . . . .
   . . . . . . . .   
       BOTTOM
    
*/
enum Rotation {TOP, LEFT, BOTTOM, RIGHT};

struct Position {
  int lin;
  int col;
};

struct Display {
  int index;
  Position position; 
  Rotation rotation;
};

/*************************************************************************************************************
*******************************FIM DISPLAY********************************************************************
**************************************************************************************************************/

/*************************************************************************************************************
*******************************CLASSE SNAKE GAME**************************************************************
**************************************************************************************************************/

const int SNAKE_MAX_LEN   = 50;  //tamanho maximo da cobra
const int SNAKE_TIME_INIT = 500; //tempo entre deslocamento da cobra (velocidade)
const int SNAKE_TIME_INC  = 15;  //incremento da velocidade

enum Direction { DIR_STOP, DIR_TOP, DIR_LEFT, DIR_BOTTOM, DIR_RIGHT};
enum SnakeStatus { SNAKE_GAME_ON, SNAKE_GAME_OVER };

class SnakeGame{
  private:
    BitArray2D * _display;
    Position _snake_positions[SNAKE_MAX_LEN];
    Position _apple;
    int _length;
    Direction _direction;
    unsigned long _last_millis;
    int _time;
    int _score;
    SnakeStatus _snakeStatus;
    
    UniqueRandom * _ur;  //utilizado no game over
    
    void _generateApple() {
      int lin, col;
      boolean random_ok = false;
      
      while (!random_ok) {
        random_ok = true;
        lin = random(0, _display->rows()-1);
        col = random(0, _display->columns()-1);
        
        for (int p=0; p<_length; p++){
          if (_snake_positions[p].col==col && _snake_positions[p].lin==lin){ //verifica se gerou em um local que não seja a cobra
            random_ok = false;
            break;
          }
        }
      }
      _apple.lin = lin;
      _apple.col = col;
    }
    
    void _gameOver(){ 
      _snakeStatus = SNAKE_GAME_OVER; 
      _direction   = DIR_STOP;
      _time = 20;
    }
    
    void _inc_length(){
      _length++; _score++;
      _time -= SNAKE_TIME_INC;
    }
    
    void _runGameOver(){
      int r = _ur->next();
      int lin = (r / _display->columns());
      int col = (r % _display->columns());
      
      _display->write(lin, col, HIGH );
      
      if ( r>=(_ur->size()-1) || _direction != DIR_STOP ) {  
        _ur->randomize();
        start(); 
      }
    }
    
    void _run(){
      for (int i=_length-1; i>0; i--){
        _snake_positions[i].lin = _snake_positions[i-1].lin;
        _snake_positions[i].col = _snake_positions[i-1].col;
      }
      
      if (_direction == DIR_TOP )    { _snake_positions[0].lin--;  }
      if (_direction == DIR_BOTTOM ) { _snake_positions[0].lin++;  }
      if (_direction == DIR_LEFT )   { _snake_positions[0].col--;  }
      if (_direction == DIR_RIGHT )  { _snake_positions[0].col++;  }
      
      //verifica se ultrapassou o limite do display
      if (_snake_positions[0].lin < 0)                     { _gameOver(); }
      if (_snake_positions[0].lin >= _display->rows() )    { _gameOver(); }
      if (_snake_positions[0].col < 0)                     { _gameOver(); }
      if (_snake_positions[0].col >= _display->columns() ) { _gameOver(); }
      
      //verifica se colidiu na cobra
      for (int i=_length-1; i>0; i--){
        if (_snake_positions[i].lin == _snake_positions[0].lin && _snake_positions[i].col == _snake_positions[0].col) {
          _gameOver();
        }  
      }
      
      //verifica se comeu a maça
      if (_snake_positions[0].col == _apple.col && _snake_positions[0].lin == _apple.lin){
        _inc_length();
        
        if (_length > SNAKE_MAX_LEN) { _length = SNAKE_MAX_LEN; } else {
          _snake_positions[_length-1].lin = _snake_positions[_length-2].lin;
          _snake_positions[_length-1].col = _snake_positions[_length-2].col;
        }
        _generateApple();
      }
      
      //update display
      for (int lin=0; lin<_display->rows(); lin++) {
        for (int col=0; col<_display->columns(); col++) {
          for (int p=0; p<_length; p++){
            boolean val = _snake_positions[p].col==col && _snake_positions[p].lin==lin;
            _display->write( lin, col,  val );
            if (val) {break;}
          }
        }
      }
      _display->write(_apple.lin, _apple.col, HIGH);
      //--
    }
    
  public:
    SnakeGame(BitArray2D * display){ 
      _display = display;
      _ur = new UniqueRandom( _display->rows() * _display->columns() );
      start();
    }
    
    void start(){
      _length = 1;
      _score  = 0;
      _time = SNAKE_TIME_INIT;
      _last_millis = 0;
      _snake_positions[0].lin = _display->rows() / 2;
      _snake_positions[0].col = _display->columns() / 2;
      _direction = DIR_STOP;
      
      _snakeStatus = SNAKE_GAME_ON;
      
      _generateApple();
    }
    
    void left()   { if (_direction == DIR_RIGHT)  return; _direction = DIR_LEFT;   }
    void right()  { if (_direction == DIR_LEFT)   return; _direction = DIR_RIGHT;  }
    void top()    { if (_direction == DIR_BOTTOM) return; _direction = DIR_TOP;    }
    void bottom() { if (_direction == DIR_TOP)    return; _direction = DIR_BOTTOM; }
    
    int getScore(){ return _score; }
    
    int update(){
      int r = false;
      
      if (millis() - _last_millis > _time) {
        r = true;
        _last_millis = millis();
      
        if (_snakeStatus == SNAKE_GAME_ON)   { _run();         }
        if (_snakeStatus == SNAKE_GAME_OVER) { _runGameOver(); }
      }
      
      return r; //r-->indica se houve mudança no display
    }
};

/*************************************************************************************************************
*******************************FIM CLASSE SNAKE GAME**********************************************************
**************************************************************************************************************/



const int LINHAS  = 16;
const int COLUNAS = 8;


/*
 * * * * * * * *
 * * * * * * * *
 * * * * * * * *
 * * * * * * * *
 * * * * * * * *
 * * * * * * * *
 * * * * * * * *
 * * * * * * * *
 
 * * * * * * * *
 * * * * * * * *
 * * * * * * * *
 * * * * * * * *
 * * * * * * * *
 * * * * * * * *
 * * * * * * * *
 * * * * * * * *
*/

Display displays_8x8[] = {
  {0, {0,0}, TOP},
  {1, {8,0}, BOTTOM}
};

BitArray2D ba(LINHAS, COLUNAS); //8 linhas e 20 colunas
SnakeGame snake(&ba);
 
const int PIN_CLOCK   = 5;
const int PIN_DATA    = 7;
const int PIN_LOAD    = 6;
const int QTD_MAX7219 = 2;
 
LedControl lc = LedControl(PIN_DATA,PIN_CLOCK,PIN_LOAD, QTD_MAX7219);

void update_displays_8x8() {
  lc.startWrite();
  
  for (int lin=0; lin<ba.rows(); lin++) {
    for (int col=0; col<ba.columns(); col++) {
      for (int i=0; i<sizeof(displays_8x8)/sizeof(Display); i++) {
        int l = lin - displays_8x8[i].position.lin;
        int c = col - displays_8x8[i].position.col;
        
        if (l>=0 && l<=7 && c>=0 && c<=7) {
          
          if (displays_8x8[i].rotation == BOTTOM) {            c=7-c; l=7-l;   }
          if (displays_8x8[i].rotation == LEFT)   { int aux=c; c=l;   l=7-aux; }
          if (displays_8x8[i].rotation == RIGHT)  { int aux=l; l=c;   c=7-aux; }

          lc.setLed(displays_8x8[i].index, l, c, ba.read(lin, col) );
        }
      }
    }
  }
  
  lc.send();
}


void setup() {
  Serial.begin(9600);
  
  lc.shutdown(0,false);
  lc.setIntensity(0,8);
  lc.clearDisplay(0);  
  lc.shutdown(1,false);
  lc.setIntensity(1,8);
  lc.clearDisplay(1);  
  
  randomSeed(analogRead(A0));
}

void loop() {
  int c = Serial.read();
   
  if (c == 97)  { snake.left();   } //a -> 
  if (c == 98)  { snake.right();  } //b -> 
  if (c == 99)  { snake.top();    } //c -> 
  if (c == 100) { snake.bottom(); } //d -> 
  
  if ( snake.update() ) { update_displays_8x8(); }
}