Pythonでe-Paperの操作をしてみた。


JetsonNanoにe-Paperを取り付ける際にGPIOが90度曲がるようにする拡張ボードを取り付けた所、画面が上下逆さまになってしまいました。画面の回転に関してはメーカーのwikiに記載があったのですが、オブジェクト指向で理解するのに時間がかかった。そもそもオブジェクト指向が苦手で、箱をイメージすると何時もエラーメッセージが表示される経験をしました。CやJAVAのコンパイルの次にぶつかった壁だと思います。

■GPIO拡張ボード
https://www.amazon.co.jp/gp/product/B093CZ5NLV/ref=ppx_yo_dt_b_asin_title_o02_s00?ie=UTF8&psc=1

■4.01inch e-Paper HAT (F)
https://www.waveshare.com/wiki/4.01inch_e-Paper_HAT_(F)

■Python, Pillowで画像を回転するrotate
https://note.nkmk.me/python-pillow-rotate/

モジュールのページも何度も読み返して最終的に辿り着いた答えは、.rotate()はImageオブジェクトに使うということ。メーカーの例題ではImageオブジェクトをImageDrowオブジェクトに入れているので、最終的なオブジェクトには.rotate()はつかえないとのエラーメッセージが表示してました。英語だから気がつかなかった。メーカーのwikiにある.transpose(Image.ROTATE_180)でも同じ動作でした。
それと、オブジェクトに要素を入れてから回転させないと意味が無い様子。座標が回転するのかと思ったけど、画像が回転するらしいです。

Himage = Image.new('RGB', (epd.width, epd.height), 0xffffff)  # 255: clear the frame
draw = ImageDraw.Draw(Himage)

#省略

Himage = Himage.rotate(180) #Himage = Himage.transpose(Image.ROTATE_180)でも可
epd.display(epd.getbuffer(Himage))

次に躓いたのがインクリメント記号です。何故便利な記号が使えないのかは、プログラミング言語の特性なのかもしれません。for文等の文法もインクリメントよりもレンジで記載するので、必要ないのかもしれません。

■pythonではインクリメント・デクリメントに++や–は使えない
https://qiita.com/motokitsu/items/0e1cf895252b63e0b4e5
■pythonではインクリメント・デクリメントに++や–は使えない
https://qiita.com/motokitsu/items/0e1cf895252b63e0b4e5

フルカラーe-Paperに色のブロックをランダムに配置しようと試行錯誤した所、ランダム関数よりも色を配列に代入して配列をランダムにシャッフルした方が早そうでした。

■Pythonでリストの要素をシャッフル(random.shuffle, sample)
https://note.nkmk.me/python-random-shuffle/

#!/usr/bin/python
# -*- coding:utf-8 -*-
import sys
import os
picdir = os.path.join(os.path.dirname(os.path.dirname(os.path.realpath(__file__))), 'pic')
libdir = os.path.join(os.path.dirname(os.path.dirname(os.path.realpath(__file__))), 'lib')
if os.path.exists(libdir):
    sys.path.append(libdir)

import logging
from waveshare_epd import epd4in01f
import time
from PIL import Image,ImageDraw,ImageFont
import traceback
import netifaces
import random

logging.basicConfig(level=logging.DEBUG)

try:
    logging.info("epd4in01f Demo")
    epd = epd4in01f.EPD()

    logging.info("init and Clear")
    epd.init()
    epd.Clear()
    font24 = ImageFont.truetype(os.path.join(picdir, 'Font.ttc'), 24)
    font18 = ImageFont.truetype(os.path.join(picdir, 'Font.ttc'), 18)
    font30 = ImageFont.truetype(os.path.join(picdir, 'Font.ttc'), 40)

    # Drawing on the Horizontal image
    logging.info("1.Drawing on the Horizontal image...")
    Himage = Image.new('RGB', (epd.width, epd.height), 0xffffff)  # 255: clear the frame
    draw = ImageDraw.Draw(Himage)
    now_time = time.strftime('%H:%M:%S - %Y/%m/%d')
    draw.text((0, 0), now_time, font = font30, fill = epd.BLACK)
    ip = netifaces.ifaddresses('wlan0')[netifaces.AF_INET][0]['addr']
    draw.text((360, 360), ip, font = font30, fill = epd.BLACK)
    cs = [ epd.RED, epd.BLACK, epd.YELLOW, epd.GREEN, epd.ORANGE, epd.BLUE]
    random.shuffle(cs)
    cc = 0
    for y in range(40, 360, 40):
        for x in range(0, 630, 40):
            if cc < 5:
                draw.rectangle((x, y, x+40, y+40), fill = cs[cc])
                cc += 1
            else:
                draw.rectangle((x, y, x+40, y+40), fill = cs[cc])
                random.shuffle(cs)
                cc = 0

    Himage = Himage.rotate(180)
    epd.display(epd.getbuffer(Himage))


except IOError as e:
    logging.info(e)

except KeyboardInterrupt:
    logging.info("ctrl + c:")
    epd4in01f.epdconfig.module_exit()
    exit()

三色e-Paperにライフゲームを作ってみました。一番簡単に組める練習用のプログラムではないかと思います。学生時代にN88-BASICでライフゲームを組みました。懐かしい想い出です。スクリプト言語という意味では、随分と発展したなぁ、と感慨深いです。

■ライフゲーム アルゴリズム初心者向けの基礎と入門(C#, Pythonとか)
https://algoful.com/Archive/Algorithm/LifeGame

配列を使って盤面を作りました。配列も、空の配列は作る事が出来ない様なので、0を入れました。本当は空っぽの配列を作って、中に要素が入っているモノだけを抽出出来れば簡単そうだったのですが。

■【Python】多次元配列に値を代入するときの注意点
https://qiita.com/oyoshi0022/items/7475951f465d20ad4970

for文とif文を使い、二次元配列のx座標とy座標に当たる数字の配列に0か1のどちらが入っているかで分岐させました。ライフゲームは周囲8マスに生き物がどの程度いるかを計算して次の世代を配置して行くゲームなので、表示用の座標とは別に次世代を計算するための配列を作り、生物がいるマスの周囲8マスに1を追加していきます。計算用の配列は1を足す時にエラーにならないように大きく配列を作ります。
最後にもう一度計算用の配列の中身を取り出して次世代を上書きします。

■if文を使った条件分岐
https://www.javadrive.jp/python/if/index1.html

[解決!Python]for文で繰り返し処理を行うには
https://atmarkit.itmedia.co.jp/ait/articles/2009/18/news023.html

定時で呼び出して実行させることも考えたのですが、状態を引き渡す必要があるので、ファイルに書き出したりするのが面倒なため、そのままfor文で複数回実行させるようにしました。

#!/usr/bin/python
# -*- coding:utf-8 -*-
import sys
import os
picdir = os.path.join(os.path.dirname(os.path.dirname(os.path.realpath(__file__))), 'pic')
libdir = os.path.join(os.path.dirname(os.path.dirname(os.path.realpath(__file__))), 'lib')
if os.path.exists(libdir):
    sys.path.append(libdir)

import logging
from waveshare_epd import epd2in13bc
import time
from PIL import Image,ImageDraw,ImageFont
import traceback
import netifaces

logging.basicConfig(level=logging.DEBUG)

try:
    logging.info("epd2in13bc Demo")

    epd = epd2in13bc.EPD()

    start_time = time.time()
    read_time = 0

    lifegame = [[0]*14 for i in range(43)]
    lifegame[5][5] = 1
    lifegame[5][6] = 1
    lifegame[5][7] = 1
    lifegame[6][6] = 1
    lifegame[20][7] = 1
    lifegame[21][7] = 1
    lifegame[22][7] = 1
    lifegame[21][8] = 1
    lifegame[10][10] = 1
    lifegame[9][9] = 1
    lifegame[9][10] = 1
    lifegame[9][11] = 1
    lifegame[8][10] = 1
    lifegame[38][7] = 1
    lifegame[38][8] = 1
    lifegame[38][9] = 1
    lifegame[37][8] = 1
    lifegame[28][4] = 1
    lifegame[29][4] = 1
    lifegame[30][4] = 1
    lifegame[29][3] = 1

    soul = [[0]*14 for i in range(43)]

    for roop in range(120):

        logging.info("time.counter")
        if read_time > 0:
            start_time = time.time()

        logging.info("init and Clear")
        epd.init()
        epd.Clear()
        #logging.info("sleep 3mm")
        #time.sleep(3)

        # Drawing on the image
        logging.info("Drawing")
        font20 = ImageFont.truetype(os.path.join(picdir, 'Font.ttc'), 20)
        font18 = ImageFont.truetype(os.path.join(picdir, 'Font.ttc'), 18)

        # Drawing on the Horizontal image
        logging.info("1.Drawing on the Horizontal image...")
        HBlackimage = Image.new('1', (epd.height, epd.width), 255)  # 298*126
        HRYimage = Image.new('1', (epd.height, epd.width), 255)
        #298*126 ryimage:yellow image
        drawblack = ImageDraw.Draw(HBlackimage)
        drawry = ImageDraw.Draw(HRYimage)

        ip = netifaces.ifaddresses('wlan0')[netifaces.AF_INET][0]['addr']
        drawblack.text((70, 80), ip, font = font20, fill = 0)
        now_time = time.strftime('%H:%M:%S - %Y/%m/%d')
        drawblack.text((5, 0), now_time, font = font20, fill = 0)
        #drawblack.text((70, 0), roop, font = font20, fill = 0)

        for y in range(12):
            for x in range(41):
                if lifegame[(x + 1)][(y + 1)] < 1:
                    drawry.rectangle(( x*5+2, y*5+22, x*5+7, y*5+27), outline = 0)

                else:
                    drawblack.rectangle(( x*5+2, y*5+22, x*5+7, y*5+27), fill = 0)
                    #
                    soul[x][y] += 1
                    soul[x + 1][y] += 1
                    soul[x + 2][y] += 1
                    soul[x][y + 1] += 1
                    soul[x + 2][y + 1] += 1
                    soul[x][y + 2] += 1
                    soul[x + 1][y + 2] += 1
                    soul[x + 2][y + 2] += 1

        #drawry.rectangle(( x*5+2, y*5+25, x*5+7, y*5+30), fill = 0)
        #drawry.rectangle((0, 25, 212, 80), fill = 0)

        epd.display(epd.getbuffer(HBlackimage), epd.getbuffer(HRYimage))

        for y in range(14):
            for x in range(43):

                if soul[x][y] < 2:
                    lifegame[x][y] = 0
                elif soul[x][y] > 3:
                    lifegame[x][y] = 0
                elif lifegame[x][y] == 1 and 1 < soul[x][y] < 4:
                    lifegame[x][y] = 1
                elif lifegame[x][y] == 0 and soul[x][y] == 3:
                    lifegame[x][y] = 1

                soul[x][y] = 0

        read_time = time.time() - start_time
        logging.info(read_time)
        logging.info(roop)
        time.sleep(300 - read_time)

    logging.info("Clear...")
    #epd.init()
    #epd.Clear()

    logging.info("Goto Sleep...")
    #epd.sleep()

except IOError as e:
    logging.info(e)

except KeyboardInterrupt:
    logging.info("ctrl + c:")
    epd2in13bc.epdconfig.module_exit()
    exit()

コメント

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です