Arduino+キャラクタLCDでレベルメータ風の表示

#はじめに
オーディオLINE出力に接続してレベルメータ風味の表示を行なってみます。

![DSD_2276.JPG](https://qiita-image-store.s3.amazonaws.com/0/211703/5d5f8a68-c510-4e6b-cb47-2d86f33f3123.jpeg)

#回路
入力部は[こちら](https://synapse.kyoto/hard/LevelMeter/page001.html)を参考にしています。
レベルシフトをA4, A5に入力です。

#プログラム
不具合有りで表示ゴミが残ってしまいます。気になる方は改良をお願いします。

```C:VUmeter.c
///////////////////////////////////////////////////////////
// VU METER
// 参考 : https://synapse.kyoto/hard/LevelMeter/page001.html
//
//
// 2018/05/13 First
// 2018/05/18 R1  : データ取得をタイマーで。複数の数値で平均化-> タイマーやめ(ハングする)
// 2018/05/26 R2  : 整形
//
////////////////////////////////////////////////////////////
#include <LiquidCrystal.h>

// * LCD RS pin to digital pin 11
// * LCD Enable pin to digital pin 10
// * LCD D4 pin to digital pin 9
// * LCD D5 pin to digital pin 8
// * LCD D6 pin to digital pin 7
// * LCD D7 pin to digital pin 6
// * LCD R/W pin to ground
// * LCD VSS pin to ground
// * LCD VCC pin to 5V
// * 10K resistor:
// * ends to +5V and ground
// * wiper to LCD VO pin (pin 3)
LiquidCrystal lcd(11, 10, 9, 8, 7, 6);

int NumOfBox( int level );
int PieceOfEnd( int level );

int prevLevelL = 0;
int prevLevelR = 0;
int ClearCount = 0;

#define WAVE_WIDTH 20   // 20 msec
int wave[2][WAVE_WIDTH];  // store wave data (Ring buffer)
int wave_pos;

#define OFS_L   (2)
#define OFS_R   (4)

void DataRead()
{
  wave[0][wave_pos] = (analogRead(A4) - 512 + OFS_L);
  wave[1][wave_pos] = (analogRead(A5) - 512 + OFS_R);
  wave_pos ++;
  if ( wave_pos >= WAVE_WIDTH ) wave_pos = 0;
}

void setup() {
  Serial.begin (115200);
  Serial.println ();

  // put your setup code here, to run once:
  // set up the LCD's number of columns and rows:
  lcd.begin(16, 2);
  LCD_SetChar();
  LCD_Clear();

  // initialize variable
  wave_pos = 0;
  for (int i = 0; i < WAVE_WIDTH; i++)
  {
    wave[0][i] = 512 + OFS_L;
    wave[1][i] = 512 + OFS_R;
  }

}

////////////////////////////////////////////
// Ring bufferのRMSを計算
int RMS(int *wave )
{
  long rms = 0;
  for (int i = 0 ; i < WAVE_WIDTH; i++)
  {
    rms += sqrt_int( (unsigned int)(wave[i] * wave[i]));
  }
  return rms / WAVE_WIDTH;
}


void loop() {

  delay( 100 );
  // Wave読み込み (大体20ms分)
  for (int r = 0; r < WAVE_WIDTH; r++)
  {
    delay( 1 );
    DataRead();
  }
  // RMS計算する
  unsigned int valL, valR;
  valL = RMS( wave[0] );
  valR = RMS( wave[1] );

  // 最大表示桁(14文字分)にクリップ
  if (valL > 70) valL = 70; // 5x14 = 70
  if (valR > 70) valR = 70;

  // バー表示
  DispBar(1, valL, prevLevelL);
  DispBar(0, valR, prevLevelR);

  // 現在レベルを保存する
  prevLevelL = valL;
  prevLevelR = valR;

  // 無音が続けば画面リセットする(表示ゴミが残る不具合に対するやっつけ)
  if ( valL == 0 && valR == 0 )
    ClearCount ++;
  if ( ClearCount >= 50 )
  {
    ClearCount = 0;
    LCD_Clear();
  }

}

///////////////////////////////////////////
// L : 0
// R : 1
void DispBar(int nLR, int nLevel, int nPrev)
{
  int Current, Prev;
  int boxCur, boxPre;
  Current = NumOfBox( nLevel);
  Prev = NumOfBox(nPrev);
  if ( PieceOfEnd( nLevel ) ) Current ++;
  if ( PieceOfEnd( nPrev ) ) Prev ++;

  boxCur = NumOfBox( nLevel );
  boxPre = NumOfBox( nPrev );

  if ( Current > Prev ) // 上書きできる
  {
    lcd.setCursor( boxPre + 2 , nLR);
    for (int i = 0; i < (boxCur - boxPre) ; i++)
      lcd.write((byte)5);

    if ( PieceOfEnd (nLevel) )
      lcd.write((byte)(PieceOfEnd(nLevel)));
  }
  else if ( Current < Prev ) // 消す
  {
    lcd.setCursor( boxCur + 2, nLR);
    if ( PieceOfEnd (nLevel))
    {
      lcd.write((byte)(PieceOfEnd(nLevel)));
      boxPre++;
    }
    for (int i = 0; i < (boxPre - boxCur) ; i++)
      lcd.write((byte)6);   // delete
  }
  else  // 同レベル
  {
    lcd.setCursor( boxCur + 2, 0);
    if ( PieceOfEnd (nLevel))
      lcd.write((byte)(PieceOfEnd (nLevel)));
  }
}

////////////////////////////////////////
// Barの四角数を返す
int NumOfBox( int level )
{
  return level / 5;
}
////////////////////////////////////////
// Barの端数を返す
int PieceOfEnd( int level )
{
  int ret = level % 5;
  if ( ret < 0 ) ret = 0;
  if ( ret > 4 ) ret = 4;
  return ret;
}
////////////////////////////////////////////////////////////
// LCDクリア
int LCD_Clear()
{
  lcd.clear();
  lcd.print("L:");
  lcd.setCursor(0, 1);
  lcd.print("R:");
}

////////////////////////////////////////////////////////////
// Create char for bar gragh
const byte charBar0[8] = {
  B00000,  B00000,  B00000,  B00000,  B00000,  B00000,  B00000,
};
const byte charBar1[8] = {
  B10000,  B10000,  B10000,  B10000,  B10000,  B10000,  B10000,
};
const byte charBar2[8] = {
  B11000,  B11000,  B11000,  B11000,  B11000,  B11000,  B11000,
};
const byte charBar3[8] = {
  B11100,  B11100,  B11100,  B11100,  B11100,  B11100,  B11100,
};
const byte charBar4[8] = {
  B11110,  B11110,  B11110,  B11110,  B11110,  B11110,  B11110,
};
const byte charBar5[8] = {
  B11111,  B11111,  B11111,  B11111,  B11111,  B11111,  B11111,
};
const byte charBar6[8] = {
  B00000,  B00000,  B00000,  B00000,  B00000,  B00000,  B00000,
};
///////////////////////////////////////////////////////////
// 任意キャラクタをセットする
void LCD_SetChar()
{
  lcd.createChar(0, charBar0 );
  lcd.createChar(1, charBar1 );
  lcd.createChar(2, charBar2 );
  lcd.createChar(3, charBar3 );
  lcd.createChar(4, charBar4 );
  lcd.createChar(5, charBar5 );
  lcd.createChar(6, charBar6 );
  lcd.createChar(7, charBar6 );
}
///////////////////////////////////////////////////////////
// 整数の平方根
// 引用 :  https://itchyny.hatenablog.com/entry/20101222/1293028538
int sqrt_int(unsigned int x) {
  int a = 0, c = 0, y = 0, i = 0, t = x;
  while (t >>= 1) {
    ++i;
  }
  for (i += i & 1; i >= 0; i -= 2) {
    c = (y << 1 | 1) <= x >> i;
    a = a << 1 | c;
    y = y << 1 | c;
    x -= c * y << i;
    /* if(c){
      x -= y << i;
      } */
    y += c;
  }
  return a;
}
```

#実行
こんな感じで表示されます。
![DSD_2283.JPG](https://qiita-image-store.s3.amazonaws.com/0/211703/d30ee48b-8d3a-d03b-c0f9-91aba5fd0d9c.jpeg)