micropythonでOLED (SSD1306) I2C編

この記事は[MicroPython Advent Calendar 2017](https://qiita.com/advent-calendar/2017/micropython)の13日目の投稿です。昨日は [ken5owata](https://qiita.com/ken5owata)さん投稿の[micropythonでADコンバータ](http://ken5owata.hatenablog.com/entry/2017/12/11/234642)でした。

１３日目が空いてしまったので小ネタで埋めます。

８日目の投稿に[ken5owata](https://qiita.com/ken5owata)さんの[micropythonでOLED (SSD1306)](http://ken5owata.hatenablog.com/entry/2017/12/08/011700)がありました。ssd1306 の OLED には I2C で接続するものもあります。今回はAmazonで８００円ぐらいで入手したものを使ってみます。

![IMG_4243.JPG](https://qiita-image-store.s3.amazonaws.com/0/106817/a1a53553-f5dc-0a80-ea7b-c6ac68a665bb.jpeg)

# SSD1306用ドライバの入手と転送

使うSSD1306用ドライバはSPI版のときと同じものです。githubから拾ってきてampyツールで転送しておきます。

[micropython/ssd1306.py at master · micropython/micropython · GitHub](https://github.com/micropython/micropython/blob/master/drivers/display/ssd1306.py)

```bash
$ ampy -p /dev/tty.usbserial-DN01APO0 ssd1306.py
```
※上記はMacに EDSr Developer 32 ボードを繋いだときの ampy コマンドの例です。

# 配線

ESP32 と SSD1306 を接続します。 今回は以下のように配線しました。

| SSD1306 | ESP32 |
|:--------|:------|
| VCC     | 3v3   |
| GND     | GND   |
| SCL     | IO4   |
| SDA     | IO5   |

# 動かしてみる

MicroPython　からの使いかたは SPI の場合とほぼ同じで、SSD1306_SPI の代わりに SSD1306_I2C を使います。８日目のSPIの場合と同じように文字を表示するには以下のようにします。

```python3
>>> from machine import Pin, I2C
>>> from ssd1306 import SSD1306_I2C
>>> i2c = I2C(scl=Pin(4), sda=Pin(5))
>>> oled = SSD1306_I2C(128, 64, i2c)
>>> oled.text('MicroPython', 0, 0)
>>> oled.show()
>>> oled.text('Advent Calender', 0, 10)
>>> oled.show()
>>> oled.text('13 days', 0, 20)
>>> oled.show()
```

![IMG_4244.JPG](https://qiita-image-store.s3.amazonaws.com/0/106817/d48971fc-ab2d-7bd4-f9fa-1d19d4cc6d33.jpeg)

これだけだと面白くないので、[このサイト](https://macsbug.wordpress.com/2016/03/19/3d-cube-in-the-esp8266-and-i2c-oled-128x64-ssd1306/)の 3D Cube プログラムを MicroPython に移植してみました。

![IMG_4256.MOV.gif](https://qiita-image-store.s3.amazonaws.com/0/106817/5f5568f6-93ed-8c39-4e1f-1e144ceed816.gif)

```python3
# 3D CUBE MicroPython version with ESP32 and ssd1306 OLED 

from machine import Pin, I2C
from micropython import const
from time import sleep_ms
from math import sin, cos
from ssd1306 import SSD1306_I2C

X = const(64)
Y = const(32)

f = [[0.0 for _ in range(3)] for _ in range(8)]
cube = ((-20,-20, 20), (20,-20, 20), (20,20, 20), (-20,20, 20),
        (-20,-20,-20), (20,-20,-20), (20,20,-20), (-20,20,-20))

i2c = I2C(scl=Pin(4), sda=Pin(5))
oled = SSD1306_I2C(X * 2, Y * 2, i2c)

while True:
    for angle in range(0, 361, 3):  # 0 to 360 deg 3step
        for i in range(8):
            r  = angle * 0.0174532  # 1 degree
            x1 = cube[i][2] * sin(r) + cube[i][0] * cos(r)  # rotate Y
            ya = cube[i][1]
            z1 = cube[i][2] * cos(r) - cube[i][0] * sin(r)
            x2 = x1
            y2 = ya * cos(r) - z1 * sin(r)  # rotate X
            z2 = ya * sin(r) + z1 * cos(r)
            x3 = x2 * cos(r) - y2 * sin(r)  # rotate Z
            y3 = x2 * sin(r) + y2 * cos(r)
            z3 = z2
            x3 = x3 + X
            y3 = y3 + Y
            f[i][0] = x3  # store new values
            f[i][1] = y3
            f[i][2] = z3
        oled.fill(0)  # clear
        oled.line(int(f[0][0]), int(f[0][1]), int(f[1][0]), int(f[1][1]), 1)
        oled.line(int(f[1][0]), int(f[1][1]), int(f[2][0]), int(f[2][1]), 1)
        oled.line(int(f[2][0]), int(f[2][1]), int(f[3][0]), int(f[3][1]), 1)
        oled.line(int(f[3][0]), int(f[3][1]), int(f[0][0]), int(f[0][1]), 1)
        oled.line(int(f[4][0]), int(f[4][1]), int(f[5][0]), int(f[5][1]), 1)
        oled.line(int(f[5][0]), int(f[5][1]), int(f[6][0]), int(f[6][1]), 1)
        oled.line(int(f[6][0]), int(f[6][1]), int(f[7][0]), int(f[7][1]), 1)
        oled.line(int(f[7][0]), int(f[7][1]), int(f[4][0]), int(f[4][1]), 1)
        oled.line(int(f[0][0]), int(f[0][1]), int(f[4][0]), int(f[4][1]), 1)
        oled.line(int(f[1][0]), int(f[1][1]), int(f[5][0]), int(f[5][1]), 1)
        oled.line(int(f[2][0]), int(f[2][1]), int(f[6][0]), int(f[6][1]), 1)
        oled.line(int(f[3][0]), int(f[3][1]), int(f[7][0]), int(f[7][1]), 1)
        oled.line(int(f[1][0]), int(f[1][1]), int(f[3][0]), int(f[3][1]), 1)  # cross
        oled.line(int(f[0][0]), int(f[0][1]), int(f[2][0]), int(f[2][1]), 1)  # cross
        oled.text('3D CUBE', 0, 0)
        oled.show()  # display
        sleep_ms(1)
```

MicroPython でも十分な速さで cube が回転します。