#include <LiquidCrystal_I2C.h>

#define IN_LEFT     A1  // analog input for left channel
#define T_REFRESH   25
#define T_PEAKHOLD  (50 * T_REFRESH)

byte  fill[6] = {0x20, 0x00, 0x01, 0x02, 0x03, 0xFF};
byte  peak[7] = {0x20, 0x00, 0x04, 0x05, 0x06, 0x07, 0x20};
byte block[8][8] =
{
  {0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10},
  {0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18},
  {0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C},
  {0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E},

  {0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08},
  {0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04},
  {0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02},
  {0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01},
};

int lmax[2];
int dly[2];
long lastT = 0;

LiquidCrystal_I2C  lcd(0x27, 16, 2 );

void  setup()
{
  lcd.init();
  lcd.backlight();
 
  for (int j = 0; j < 8; j++)
    lcd.createChar(j, block[j]); 
}

void  loop() {

  if (millis() < lastT)
    return;

  lastT += T_REFRESH;

  int anL = map(sqrt(analogRead(IN_LEFT) * 16), 0, 128, 0, 100);

  bar(1, anL);
}

void  bar(int row, int lev)
{

  lcd.setCursor(0, row);
  lcd.write('L');

  for (int i = 1; i < 16; i++)
  {
    int f = constrain(lev - i * 5, 0, 5);
    int p = constrain(lmax[row] - i * 5, 0, 6);
    if (f)
      lcd.write(fill[f]);
    else
      lcd.write(peak[p]);
  }

  if (lev > lmax[row])
  {
    lmax[row] = lev;
    dly[row]  = -(T_PEAKHOLD) / T_REFRESH;
  }
  else
  {
    if (dly[row] > 0)
      lmax[row] -= dly[row];

    if (lmax[row] < 0)
      lmax[row] = 0;
    else
      dly[row]++;
  }
}