ESP8266 (ESP-WROOM-02) で I2C LCD 8桁2行 を制御する

## 概要
ESP8266 (ESP-WROOM-02) で I2C で接続可能な LCD 8桁2行 を制御する

## はじめに
部品セットに含まれていた LCD を制御しようという企画なのですが、16桁2行 型番:1602 ということしかわからない orz。
秋月でも扱ってなさそうなので、この LCD は諦めて、もっと安価でメジャーな LCD を選択しました。それが AQM0802 という LCD です。

## 部品調達
上が AQM0802 (8桁2行)、下が 1602 (16桁2行)
![IMG_2421.JPG](https://qiita-image-store.s3.amazonaws.com/0/69410/74298623-12ee-d52d-87eb-a91fc6158e71.jpeg)

AQM0802 は安価に入手可能です。<a href="http://akizukidenshi.com/catalog/g/gP-06669/" target="_blank">I2C接続 小型キャラクタ LCDモジュール (8x2行)</a> 320円
ブレボー用にピッチ変換済みモジュールはこちら。<a href="http://akizukidenshi.com/catalog/g/gM-09109/" target="_blank">ピッチ変換モジュール (完成品)</a> 700円 今回はこれを使用しました。

<a href="http://akizukidenshi.com/download/ds/xiamen/AQM0802.pdf" target="_blank">データシート</a>

以下、データシート抜粋
![スクリーンショット 2016-01-24 22.55.57.png](https://qiita-image-store.s3.amazonaws.com/0/69410/714b5a46-fd21-3cac-10d7-162ab4616b82.png)
カタカナも表示可能のようですが、今回は取り敢えず、オレンジ色網掛け (ASCII互換) 部分にフォーカスします。

![スクリーンショット 2016-01-24 2.42.47.png](https://qiita-image-store.s3.amazonaws.com/0/69410/8f809f9e-b30f-fd16-8750-2a9f64bb30d9.png)


## 回路
 - I2C 用の端子は、ESP8266 データシート上では IO2/IO14 となっていましたが、以下 (pins_arduino.h) で IO4/IO5 として定義されています。
 - よって、今回はデフォルト指定の IO4/IO5 を使用します。

~/Library/Arduino15/packages/esp8266/hardware/esp8266/2.0.0/variants/generic/pins_arduino.h

```cpp
static const uint8_t SDA = 4;
static const uint8_t SCL = 5;
```

 - WROOM02 のリセット(RST) を、LCD のリセットと共用しています。





### 回路図
 - I2C の SDA/SCL は、プルアップが必要です。
 - I2C は接続本数が少なくて、とても楽ですね www


![LCD0802.png](https://qiita-image-store.s3.amazonaws.com/0/69410/f330a058-466c-836c-ee71-553dc2e8509b.png)


### Eagle
<a href="https://github.com/exabugs/eagleLCD" target="_blank">https: //github.com/exabugs/eagleLCD</a>

## スケッチ

 - Wire ライブラリを使用しています。
 - シリアルから入力を受け付けて、LCD に表示します。echo的な。
 - I2C 機器は、それぞれ固有のスレーブ・アドレスを持つようです。<br>AQM0802 では 0x3e がそれに該当します。
 - 初期化のコマンドは、データシートの例を参考にしました。<br>(半分くらい理解できていません。スイマセン。)

```cpp
#include <Wire.h>

#define ADDR 0x3e

uint8_t CONTROLL[] = {
  0x00, // Command
  0x40, // Data
};

const int C_COMM = 0;
const int C_DATA = 1;

uint8_t settings[] = {

  0b00111001, // 0x39 // [Function set] DL(4):1(8-bit) N(3):1(2-line) DH(2):0(5x8 dot) IS(0):1(extension)

  // Internal OSC frequency
  (0b0001 << 4) + 0b0100, // BS(3):0(1/5blas) F(210):(internal Freq:100)

  // Contrast set
  (0b0111 << 4) + 0b0100, // Contrast(3210):4

  // Power/ICON/Contrast control
  (0b0101 << 4) + 0b0110, // Ion(3):0(ICON:off) Bon(2):1(booster:on) C5C4(10):10(contrast set)

  // Follower control
  (0b0110 << 4) + 0b1100, // Fon(3):1(on) Rab(210):100

  // Display ON/OFF control
  (0b00001 << 3) + 0b111, // D(2):1(Display:ON) C(1):1(Cursor:ON) B(0):1(Cursor Blink:ON)
};

const int LINE = 2;

String buff[LINE];

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

//Wire.begin(4, 5); // (SDA,SCL) Default
  Wire.begin(); // (SDA,SCL)

  // LCD初期化
  write(C_COMM, settings, sizeof(settings));
}


void loop() {
  if (0 < Serial.available()) {
    delay(10);

    for (int i = 0; i < LINE - 1; i++) {
      buff[i] = buff[i + 1];
    }
    buff[LINE - 1] = "";

    while (0 < Serial.available()) {
      char ch = Serial.read();
      if (ch == '\n') {
        print2line();
        Serial.println(buff[LINE - 1]);
        break;
      } else if (0x20 <= ch && ch <= 0x7f) {
        // ASCII文字コード
        buff[LINE - 1] = buff[LINE - 1] + ch;
      } else {
        // Do nothing
      }
    }
  }
}

// 2行表示
void print2line() {
  // クリア
  uint8_t cmd[] = {0x01};
  write(C_COMM, cmd, sizeof(cmd));
  delay(1);

  // 書き出し
  for (int i = 0; i < LINE; i++) {
    uint8_t pos = 0x80 | i * 0x40; // 位置
    uint8_t cmd[] = {pos};
    write(C_COMM, cmd, sizeof(cmd));
    write(C_DATA, (uint8_t *)buff[i].c_str(), buff[i].length());
  }
}

void write(int type, uint8_t *data, size_t len) {
  for (int i=0; i < len; i++) {
    Wire.beginTransmission(ADDR);
    Wire.write(CONTROLL[type]);
    Wire.write(data[i]);
    Wire.endTransmission();
    delayMicroseconds(27); // 26.3us
  }
}
```

## 結果
 - 起動後、シリアルから適当に文字を入力してください。
 - LCD に表示されたら成功です。
 - さらに入力すると、LCD 上でスクロールして、2行目に表示されます。

![IMG_2432.JPG](https://qiita-image-store.s3.amazonaws.com/0/69410/8814e1eb-c90c-1632-2a19-6008edb85a9b.jpeg)


## まとめ
 - 今回は I2C を直接操作して LCD を制御しました。<br>(LiquidCrystal ライブラリを使えば、もっと簡単に実現できるかもしれません。)
 - 1602 は、頑張ったのですが、うまくいきませんでした。ザンネン。


## 余談
 - SPI 用で IO15/IO13/IO12/IO14 を使います。
 - I2C 用で IO4/IO5 を使えば、これで、全ての端子が、それぞれキレイに役割を持つことになりました。

```cpp
static const uint8_t SS    = 15;
static const uint8_t MOSI  = 13;
static const uint8_t MISO  = 12;
static const uint8_t SCK   = 14;
```