秋月電子の謎SOC基板で取得した家の電力利用状況を、小型液晶画面に表示しよう

# はじめに
[秋月電子の謎SOC基板を使って、家の電力計の値を取得してHome Assistantで可視化しよう](https://qiita.com/tonasuzuki/items/40fa0d722f7468ec79f9)の続きです。[Home Assistant](https://www.home-assistant.io/)に電力量を蓄積できましたので、いつでも電力量を見られるように、常時表示する電力量専用の液晶画面を作りました。

![esp-echonetview.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/679798/f1a0a209-86f1-ad14-853d-26a8b5ec1ae7.jpeg)

# ESPHomeを使おう
[ESPHome](https://esphome.io/)は、この記事を閲覧されている方にお馴染みの超お手軽SoC[ESP-WROOM-32](https://ja.wikipedia.org/wiki/ESP32) (以下ESP32)を使って、[Home Assistant](https://www.home-assistant.io/)に接続する機器を自作できるアドオンです。
ESPHomeは、Home Assistantのアドオンとして組み込まれるため、設定からESP32への書き込みまですべてHome Assistant上で行えます。そして液晶に表示するためのコードは、Arduino的な専用スクリプトで処理部分のみ追記すればいいので、超簡単です。

## 表示デバイスを用意する
デバイスとして、[ESP32-WROOM-32E 開発ボード](https://akizukidenshi.com/catalog/g/g115673/)と、[ILI9341搭載TFT液晶ボード](https://akizukidenshi.com/catalog/g/g116265/) (以下ILI9341)を利用しました。(実際には、同型のタッチパネルなしモデルを使用しています。実際にどのような液晶が使えるかは、[ESPHome](https://esphome.io/)のサイトで確認できます)。以下図のように接続しています。(ESP32とILI9341の接続は、最後に載せたyamlスクリプトもご確認ください)

| 液晶ピン番号 | 液晶信号名 | ESP32開発ボードピン番号 | ESP32信号名 |
|-----------:|:------------:|-----------:|:------------:|
| 1| VCC(3.3V) | 2 | 3V3 |
| 2| GND | 38 | GND |
| 3| CS | 29 | GPIO5 |
| 4| RESET | 28 | GPIO17 |
| 5| DC/RS | 27 | GPIO16 |
| 6| SDI(MOSI) | 37 | GPIO23 |
| 7| SCK | 30 | GPIO18 |
| 8| LED | 8 | GPIO32 |
| 9| SDO(MISO) | 31 |GPIO19 |

![ESP32-ILI9341_diagram.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/679798/81af4291-98b5-7da8-e78d-4d5d374bb5ad.jpeg)


あと細かいことですが、ESP32はWIFI4(2.4GHz帯)を使用しますので、接続可能なWIFI環境を用意しておきます。

# ESPHomeアドオンをHome Assistantにインストールする
[Home Assistant](https://www.home-assistant.io/)のweb画面から、「設定」⇒「アドオン」⇒「アドオンストア」で、「ESPHome」を選ぶだけです。インストール完了すると、画面左のメニューに「ESPHome」が追加されます。
# ESP32をデバイスとして追加する
- まずは[ESP32-WROOM-32E 開発ボード](https://akizukidenshi.com/catalog/g/g115673/)を、Home Assistantが稼働しているサーバーに、USBケーブルで接続します。
- Home Assistantの画面左のメニュー「ESPHome」を開き、「NEW DEVICE」をクリックします。説明内容を確認し、「CONTINUE」をクリックします。
- デバイスの名前を入力します(例えばESP-echonetviewのように、既存の機器と重ならないような名前を付けます)。「Configuration created!」の画面が表示されるので、「INSTALL」をクリックします。
- 「How do you want to install ... on your device?」が表示されます。「Plug into this computer running ESPHome Dashboard」を選び、Pick Server Portで、USBケーブルでつないだESP32-WROOM-32E 開発ボードのデバイス名を選択します。(例えば「CP2102 USB to UART Briged Controller」(/dev/ttyUSB0)など、USB経由のシリアル(UART)接続デバイスを選びます)
- 後は、ESPHomeが自動で環境をビルドして、USBケーブル経由でESP32にファームウェアを書き込みます。環境によりますが、最初は5～10分程度かかります。
- 無事完了すれば、ESPHomeの画面に、デバイスが追加されます。
- 上記の作業を完了すれば、サーバとESP32間は無線LANで通信を行いますので、サーバーにUSBケーブルを接続しなくても単体で動作します。

# デバイスの設定yamlファイルを作成する
- 文字表示のために、TTFフォントが必要です。Home Assistantサーバの homeassistant/esphome/ にTTFファイルをコピーしておきます。(今回は[VeraMono.ttf](https://github.com/chrissimpkins/codeface/tree/master/fonts/bitstream-vera-sans-mono)を使用しています。ESP32はそれほど多くのメモリを積んでいないので、小さな(100KB未満くらい)のTTFファイルを探して入れましょう)
- 上記で作成したデバイスの「EDIT」をクリックして、以下を追記します(既存の記載をそのまま残して追記しましょう)。(細かい部分は環境に合わせてテキトウに修正してください)追記したら、「INSTALL」でESP32を更新します。
- グラフは「graph:」エントリ、画面の位置情報、グラフの凡例などの文字列は「display:」エントリで設定しています。自分の好みに合わせて変更してみてください。
- グラフやパーセンテージ表示は、上限30Aで決め打ちしています。変更したい場合は、「display:」「graph:」エントリあたりを変更してみてください。

```
esp32:
  board: esp32dev
  framework:
    type: arduino

# SPI configuration
spi:
    clk_pin: GPIO18
    mosi_pin: GPIO23
    miso_pin: GPIO19
    interface: hardware

# Home Assistant Sensor
sensor:
  - platform: homeassistant
    id: ha_measured_power
    name: "Echonet Measured Power"
    entity_id: sensor.echonet_measured_power
  - platform: homeassistant
    id: ha_integrated_power
    name: "Echonet Integrated Power"
    entity_id: sensor.echonet_integrated_power

time:
  - platform: homeassistant
    id: esptime

# Define a PWM output on the ESP32
output:
  - platform: ledc
    pin: GPIO32
    id: gpio_32_backlight_pwm

# Define a monochromatic, dimmable light for the backlight
light:
  - platform: monochromatic
    output: gpio_32_backlight_pwm
    name: "Display Backlight"
    id: back_light
    restore_mode: ALWAYS_ON

font:
  - file: 'VeraMono.ttf'
    id: mono10
    size: 10
  - file: 'VeraMono.ttf'
    id: mono16
    size: 16
  - file: 'VeraMono.ttf'
    id: font_pwr
    size: 56
  - file: 'VeraMono.ttf'
    id: font_per
    size: 52

color:
  - id: graph_line
    red: 25%
    green: 100%
    blue: 50%
  - id: title_blue
    red: 25%
    green: 50%
    blue: 100%

graph:
  # Show bare-minimum auto-ranged graph
  - id: measured_power_graph
    duration: 2h
    width: 280
    height: 120
    max_value: 3000
    min_value: 0
    x_grid: 30min
    y_grid: 500
    traces:
    - sensor: ha_measured_power
      line_type: SOLID
      line_thickness: 2
      continuous: true
      color: graph_line

display:
  - platform: ili9xxx
    model: ILI9341
    cs_pin: GPIO5
    dc_pin: GPIO16
    reset_pin: GPIO17
    auto_clear_enabled: True 
    rotation: 270
    update_interval: 10s
    lambda: |-
      it.fill(COLOR_OFF);
      auto back_blue = Color(16, 16, 96);
      it.filled_rectangle (0,  0,320,20 ,back_blue);
      it.filled_rectangle (0,224,320,240,back_blue);
      it.strftime(319, 0, id(mono16), TextAlign::TOP_RIGHT, "%y-%m-%d %H:%M", id(esptime).now());
      it.print (  4,   0, id(mono16), id(title_blue), TextAlign::TOP_LEFT, "Measured POWER");

      it.printf( 87,  76, id(font_pwr), TextAlign::BASELINE_CENTER, "%.0fW", id(ha_measured_power).state);
      it.printf(259,  76, id(font_per), TextAlign::BASELINE_CENTER, "%.0f%%", id(ha_measured_power).state/30 );
      it.printf(159, 240, id(mono16), TextAlign::BASELINE_CENTER, "TOTAL:%.1f KW", id(ha_integrated_power).state);
      
      it.graph (30  , 94, id(measured_power_graph));
      it.print (26  , 98, id(mono10), TextAlign::BASELINE_RIGHT , "3000");
      it.print (26  ,138, id(mono10), TextAlign::BASELINE_RIGHT , "2000");
      it.print (26  ,178, id(mono10), TextAlign::BASELINE_RIGHT , "1000");
      it.print (26  ,218, id(mono10), TextAlign::BASELINE_RIGHT , "0");
      it.print (92  ,224, id(mono10), TextAlign::BASELINE_CENTER, "-1:30");
      it.print (162 ,224, id(mono10), TextAlign::BASELINE_CENTER, "-1:00");
      it.print (232 ,224, id(mono10), TextAlign::BASELINE_CENTER, "-0:30");
      it.print (302 ,224, id(mono10), TextAlign::BASELINE_CENTER, "-0:00");
```