Publicidade:

segunda-feira, 9 de novembro de 2015

Arduino - Como tratar interrupções externas dentro de objetos

Quem já tentou tratar uma interrupção dentro de uma classe sabe que não é muito simples fazer isso. Nesse vídeo mostro como redefinir a função attachInterrupt para anexar objetos ao invés de funções.




MyInterrupt.h

/* -*- mode: jde; c-basic-offset: 2; indent-tabs-mode: nil -*- */

/*
  Part of the Wiring project - http://wiring.uniandes.edu.co

  Copyright (c) 2004-05 Hernando Barragan

  This library is free software; you can redistribute it and/or
  modify it under the terms of the GNU Lesser General Public
  License as published by the Free Software Foundation; either
  version 2.1 of the License, or (at your option) any later version.

  This library is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  Lesser General Public License for more details.

  You should have received a copy of the GNU Lesser General
  Public License along with this library; if not, write to the
  Free Software Foundation, Inc., 59 Temple Place, Suite 330,
  Boston, MA  02111-1307  USA
  
  Modified 24 November 2006 by David A. Mellis
  Modified 1 August 2010 by Mark Sproul
*/

#include <inttypes.h>
#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/pgmspace.h>
#include <stdio.h>

#include "wiring_private.h"

class Interrupt;  //declaracao previa
void attachInterrupt(uint8_t interruptNum, Interrupt * interruptObject, int mode); //declaracao previa

class Interrupt {
  public:
    void attach(uint8_t interruptNum, int mode) { attachInterrupt(interruptNum, this, mode); };
    void volatile virtual execInterrupt(uint8_t interruptNum);
};

Interrupt * interruptObjects[EXTERNAL_NUM_INTERRUPTS];
//static volatile voidFuncPtr intFunc[EXTERNAL_NUM_INTERRUPTS];
// volatile static voidFuncPtr twiIntFunc;

void attachInterrupt(uint8_t interruptNum, Interrupt * interruptObject, int mode) {
  
  if(interruptNum < EXTERNAL_NUM_INTERRUPTS) {
    interruptObjects[interruptNum] = interruptObject;
    
    // Configure the interrupt mode (trigger on low input, any change, rising
    // edge, or falling edge).  The mode constants were chosen to correspond
    // to the configuration bits in the hardware register, so we simply shift
    // the mode into place.
      
    // Enable the interrupt.
      
    switch (interruptNum) {
#if defined(__AVR_ATmega32U4__)
 // I hate doing this, but the register assignment differs between the 1280/2560
 // and the 32U4.  Since avrlib defines registers PCMSK1 and PCMSK2 that aren't 
 // even present on the 32U4 this is the only way to distinguish between them.
    case 0:
 EICRA = (EICRA & ~((1<<ISC00) | (1<<ISC01))) | (mode << ISC00);
 EIMSK |= (1<<INT0);
 break;
    case 1:
 EICRA = (EICRA & ~((1<<ISC10) | (1<<ISC11))) | (mode << ISC10);
 EIMSK |= (1<<INT1);
 break; 
    case 2:
        EICRA = (EICRA & ~((1<<ISC20) | (1<<ISC21))) | (mode << ISC20);
        EIMSK |= (1<<INT2);
        break;
    case 3:
        EICRA = (EICRA & ~((1<<ISC30) | (1<<ISC31))) | (mode << ISC30);
        EIMSK |= (1<<INT3);
        break;
    case 4:
        EICRB = (EICRB & ~((1<<ISC60) | (1<<ISC61))) | (mode << ISC60);
        EIMSK |= (1<<INT6);
        break;
#elif defined(EICRA) && defined(EICRB) && defined(EIMSK)
    case 2:
      EICRA = (EICRA & ~((1 << ISC00) | (1 << ISC01))) | (mode << ISC00);
      EIMSK |= (1 << INT0);
      break;
    case 3:
      EICRA = (EICRA & ~((1 << ISC10) | (1 << ISC11))) | (mode << ISC10);
      EIMSK |= (1 << INT1);
      break;
    case 4:
      EICRA = (EICRA & ~((1 << ISC20) | (1 << ISC21))) | (mode << ISC20);
      EIMSK |= (1 << INT2);
      break;
    case 5:
      EICRA = (EICRA & ~((1 << ISC30) | (1 << ISC31))) | (mode << ISC30);
      EIMSK |= (1 << INT3);
      break;
    case 0:
      EICRB = (EICRB & ~((1 << ISC40) | (1 << ISC41))) | (mode << ISC40);
      EIMSK |= (1 << INT4);
      break;
    case 1:
      EICRB = (EICRB & ~((1 << ISC50) | (1 << ISC51))) | (mode << ISC50);
      EIMSK |= (1 << INT5);
      break;
    case 6:
      EICRB = (EICRB & ~((1 << ISC60) | (1 << ISC61))) | (mode << ISC60);
      EIMSK |= (1 << INT6);
      break;
    case 7:
      EICRB = (EICRB & ~((1 << ISC70) | (1 << ISC71))) | (mode << ISC70);
      EIMSK |= (1 << INT7);
      break;
#else  
    case 0:
    #if defined(EICRA) && defined(ISC00) && defined(EIMSK)
      EICRA = (EICRA & ~((1 << ISC00) | (1 << ISC01))) | (mode << ISC00);
      EIMSK |= (1 << INT0);
    #elif defined(MCUCR) && defined(ISC00) && defined(GICR)
      MCUCR = (MCUCR & ~((1 << ISC00) | (1 << ISC01))) | (mode << ISC00);
      GICR |= (1 << INT0);
    #elif defined(MCUCR) && defined(ISC00) && defined(GIMSK)
      MCUCR = (MCUCR & ~((1 << ISC00) | (1 << ISC01))) | (mode << ISC00);
      GIMSK |= (1 << INT0);
    #else
      #error attachInterrupt not finished for this CPU (case 0)
    #endif
      break;

    case 1:
    #if defined(EICRA) && defined(ISC10) && defined(ISC11) && defined(EIMSK)
      EICRA = (EICRA & ~((1 << ISC10) | (1 << ISC11))) | (mode << ISC10);
      EIMSK |= (1 << INT1);
    #elif defined(MCUCR) && defined(ISC10) && defined(ISC11) && defined(GICR)
      MCUCR = (MCUCR & ~((1 << ISC10) | (1 << ISC11))) | (mode << ISC10);
      GICR |= (1 << INT1);
    #elif defined(MCUCR) && defined(ISC10) && defined(GIMSK) && defined(GIMSK)
      MCUCR = (MCUCR & ~((1 << ISC10) | (1 << ISC11))) | (mode << ISC10);
      GIMSK |= (1 << INT1);
    #else
      #warning attachInterrupt may need some more work for this cpu (case 1)
    #endif
      break;
    
    case 2:
    #if defined(EICRA) && defined(ISC20) && defined(ISC21) && defined(EIMSK)
      EICRA = (EICRA & ~((1 << ISC20) | (1 << ISC21))) | (mode << ISC20);
      EIMSK |= (1 << INT2);
    #elif defined(MCUCR) && defined(ISC20) && defined(ISC21) && defined(GICR)
      MCUCR = (MCUCR & ~((1 << ISC20) | (1 << ISC21))) | (mode << ISC20);
      GICR |= (1 << INT2);
    #elif defined(MCUCR) && defined(ISC20) && defined(GIMSK) && defined(GIMSK)
      MCUCR = (MCUCR & ~((1 << ISC20) | (1 << ISC21))) | (mode << ISC20);
      GIMSK |= (1 << INT2);
    #endif
      break;
#endif
    }
  }
}

void detachInterrupt(uint8_t interruptNum) {
  if(interruptNum < EXTERNAL_NUM_INTERRUPTS) {
    // Disable the interrupt.  (We can't assume that interruptNum is equal
    // to the number of the EIMSK bit to clear, as this isn't true on the 
    // ATmega8.  There, INT0 is 6 and INT1 is 7.)
    switch (interruptNum) {
#if defined(__AVR_ATmega32U4__)
    case 0:
        EIMSK &= ~(1<<INT0);
        break;
    case 1:
        EIMSK &= ~(1<<INT1);
        break;
    case 2:
        EIMSK &= ~(1<<INT2);
        break;
    case 3:
        EIMSK &= ~(1<<INT3);
        break; 
    case 4:
        EIMSK &= ~(1<<INT6);
        break; 
#elif defined(EICRA) && defined(EICRB) && defined(EIMSK)
    case 2:
      EIMSK &= ~(1 << INT0);
      break;
    case 3:
      EIMSK &= ~(1 << INT1);
      break;
    case 4:
      EIMSK &= ~(1 << INT2);
      break;
    case 5:
      EIMSK &= ~(1 << INT3);
      break;
    case 0:
      EIMSK &= ~(1 << INT4);
      break;
    case 1:
      EIMSK &= ~(1 << INT5);
      break;
    case 6:
      EIMSK &= ~(1 << INT6);
      break;
    case 7:
      EIMSK &= ~(1 << INT7);
      break;
#else
    case 0:
    #if defined(EIMSK) && defined(INT0)
      EIMSK &= ~(1 << INT0);
    #elif defined(GICR) && defined(ISC00)
      GICR &= ~(1 << INT0); // atmega32
    #elif defined(GIMSK) && defined(INT0)
      GIMSK &= ~(1 << INT0);
    #else
      #error detachInterrupt not finished for this cpu
    #endif
      break;

    case 1:
    #if defined(EIMSK) && defined(INT1)
      EIMSK &= ~(1 << INT1);
    #elif defined(GICR) && defined(INT1)
      GICR &= ~(1 << INT1); // atmega32
    #elif defined(GIMSK) && defined(INT1)
      GIMSK &= ~(1 << INT1);
    #else
      #warning detachInterrupt may need some more work for this cpu (case 1)
    #endif
      break;
#endif
    }
      
    interruptObjects[interruptNum] = 0;
  }
}

/*
void attachInterruptTwi(void (*userFunc)(void) ) {
  twiIntFunc = userFunc;
}
*/

#if defined(__AVR_ATmega32U4__)
ISR(INT0_vect) {
 if(interruptObjects[EXTERNAL_INT_0])
  interruptObjects[EXTERNAL_INT_0]->execInterrupt(EXTERNAL_INT_0);
}

ISR(INT1_vect) {
 if(interruptObjects[EXTERNAL_INT_1])
  interruptObjects[EXTERNAL_INT_1]->execInterrupt(EXTERNAL_INT_1);
}

ISR(INT2_vect) {
    if(interruptObjects[EXTERNAL_INT_2])
  interruptObjects[EXTERNAL_INT_2]->execInterrupt(EXTERNAL_INT_2);
}

ISR(INT3_vect) {
    if(interruptObjects[EXTERNAL_INT_3])
  interruptObjects[EXTERNAL_INT_3]->execInterrupt(EXTERNAL_INT_3);
}

ISR(INT6_vect) {
    if(interruptObjects[EXTERNAL_INT_4])
  interruptObjects[EXTERNAL_INT_4]->execInterrupt(EXTERNAL_INT_4);
}

#elif defined(EICRA) && defined(EICRB)

ISR(INT0_vect) {
  if(interruptObjects[EXTERNAL_INT_2])
    interruptObjects[EXTERNAL_INT_2]->execInterrupt(EXTERNAL_INT_2);
}

ISR(INT1_vect) {
  if(interruptObjects[EXTERNAL_INT_3])
    interruptObjects[EXTERNAL_INT_3]->execInterrupt(EXTERNAL_INT_3);
}

ISR(INT2_vect) {
  if(interruptObjects[EXTERNAL_INT_4])
    interruptObjects[EXTERNAL_INT_4]->execInterrupt(EXTERNAL_INT_4);
}

ISR(INT3_vect) {
  if(interruptObjects[EXTERNAL_INT_5])
    interruptObjects[EXTERNAL_INT_5]->execInterrupt(EXTERNAL_INT_5);
}

ISR(INT4_vect) {
  if(interruptObjects[EXTERNAL_INT_0])
    interruptObjects[EXTERNAL_INT_0]->execInterrupt(EXTERNAL_INT_0);
}

ISR(INT5_vect) {
  if(interruptObjects[EXTERNAL_INT_1])
    interruptObjects[EXTERNAL_INT_1]->execInterrupt(EXTERNAL_INT_1);
}

ISR(INT6_vect) {
  if(interruptObjects[EXTERNAL_INT_6])
    interruptObjects[EXTERNAL_INT_6]->execInterrupt(EXTERNAL_INT_6);
}

ISR(INT7_vect) {
  if(interruptObjects[EXTERNAL_INT_7])
    interruptObjects[EXTERNAL_INT_7]->execInterrupt(EXTERNAL_INT_7);
}

#else

ISR(INT0_vect) {
  if(interruptObjects[EXTERNAL_INT_0])
    interruptObjects[EXTERNAL_INT_0]->execInterrupt(EXTERNAL_INT_0);
}

ISR(INT1_vect) {
  if(interruptObjects[EXTERNAL_INT_1])
    interruptObjects[EXTERNAL_INT_1]->execInterrupt(EXTERNAL_INT_1);
}

#if defined(EICRA) && defined(ISC20)
ISR(INT2_vect) {
  if(interruptObjects[EXTERNAL_INT_2])
    interruptObjects[EXTERNAL_INT_2]->execInterrupt(EXTERNAL_INT_2);
}
#endif

#endif

/*
ISR(TWI_vect) {
  if(twiIntFunc)
    twiIntFunc();
}
*/






Sketch


#include "MyInterrupt.h"

class Abc : public Interrupt{
  private:
  
  public:
    Abc(){
      attach(0, CHANGE);
      attach(1, CHANGE);
    }
    
    void volatile execInterrupt(uint8_t interruptNum){
      Serial.print("Abc: ");
      Serial.print(interruptNum);
      Serial.print(" - millis: ");
      Serial.println(millis());
    }
};



Abc * abc; 

void setup() {
  Serial.begin(9600);
  abc = new Abc();
}

void loop() {
  // put your main code here, to run repeatedly:
}


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