お天気時計の作成(その4:メニュー表示)

Raspberry pi zero 2Wを入手したので備忘録として書きます。

 

メニューの表示

今回は、2つのメニューを表示して、タッチパネルで操作できるプログラムを作成したいと思います、

下記の黄色の部分へシャットダウン(poff)とプログラムの終了(off)のメニューを表示し、タッチしたときに実行できるようにしたいと思います。

 

 

メニュー表示プログラム

以前作成したLCD_disp.pyへ下記の関数を追加します。


def touch_disp(sel):
    if(sel == "end"):
        draw.rectangle(( 8,210, 58,239), fill=COLOR_RED)
    else:
        draw.rectangle(( 8,210, 58,239), fill=COLOR_BLUE)
    draw.text((10,210), "end"  , font=FONT_NOTO_24, fill=COLOR_WHITE)
    if(sel == "poff"):
        draw.rectangle((78,210,128,239), fill=COLOR_RED)
    else:
        draw.rectangle((78,210,128,239), fill=COLOR_BLUE)
    draw.text((80,210), "poff" , font=FONT_NOTO_24, fill=COLOR_WHITE)
        
    disp.image(image)


下図のようにLCDの座標(8,210)~(58,239)にend,(78,210)~(128,239)にpoffと表示させます。

該当の領域をタッチしたときに背景色を青から赤に変更できるようにようにしました。

endの領域をタッチしたときは、プログラムの終了、poffの領域をタッチしたときはシャットダウンを行う処理を追加したいと思います。

タッチ領域の判定プログラム

下記で記載した通り、タッチパネルの座標とLCDの座標はxとyが逆になります。

Raspberry Pi Zero 2 WでLCDタッチパネル - tomtomst【電子工作 DIY】 (hatenablog.com)

①タッチセンサを割り込みで処理できるようにするプログラム

#--------------------------------------------------
from xpt2046 import Touch
from gpiozero import Button, DigitalOutputDevice
import board
import busio
from time import sleep

# touch callback
toggle = True

def touchscreen_press(x, y):
    global touch_x,touch_y
    #print(x,y)
    (touch_x,touch_y) = (x,y)

cs = DigitalOutputDevice(7,active_high=False,initial_value=None)
clk = board.SCK
mosi = board.MOSI
miso = board.MISO
irq = Button(22)

spi = busio.SPI(clk, mosi, miso)    # auxiliary SPI
xpt = Touch(spi, cs=cs, int_pin=irq, int_handler=touchscreen_press)
②タッチ座標の判定プログラム

endの領域(210,8)~(239,58)をタッチした場合、背景を赤くして”end"を返します。

poffの領域(210,78)~(239,128)をタッチした場合、背景を赤くして"poff"を返します。


def touch_sel(x,y):
    sel = ""
    if x >= 210 and x <= 239:
        if y >=  8 and y <=  58:
            touch_disp("end")
            sel = "end"
        if y >= 78 and y <= 128:
            touch_disp("poff")
            sel = "poff"
    return sel

プログラム終了時とシャットダウン時のLCD表示

以前作成したLCD_disp.pyへ下記の関数を追加します。


def poff_disp(poff):
    #POFFと表示する
    draw.rectangle((0,0,320,240), fill=COLOR_BLACK)
    draw.text((4,20), poff , font=FONT_NOTO_64, fill=COLOR_WHITE)
    disp.image(image)

 

 

メインプログラム

メインプログラムを2つの関数に分けて作り直しました。

 

1つ目:起動したときにLCDへ表示する関数。

def setup_main():
    
    LCD_set()     #LCDの初期設定
    LCD_led(23,1) #LCDのLED点灯
    setup_disp()  #起動中表示
    time.sleep(1) #1秒wait
    
    #今の日時を取得
    today_n,time_n,now_M_S,now_S = now_daytime()
    #時計表示
    time_disp(today_n,time_n)
    #温度取得
    humi, temp = Adafruit_DHT.read_retry(sensor, pin)
    if humi is not None and temp is not None:
        #温度表示
        temp_disp(temp,humi) 
    else:
        print('Failed to get reading. Try again!')    

    #お天気を取得
    cor,weather,weather_url = weather_get()
    #お天気をLCDへ表示
    weather_disp(cor,weather,weather_url)
    #weather_text_disp(cor,weather)    
    #weather_icon_disp()    
2つ目:時計、温度と湿度、天気の更新およびタッチ処理を行う関数

時計表示は1分毎に更新)、温度と湿度の表示は10秒毎に更新、天気(アイコン、天気、降水確率)は1時間毎に更新します。

また、下部のメニューをタッチするとプログラム終了またはシャットダウンの表示を行います。


def main_01():
    global touch_x,touch_y
    
    now_S_old = "--"
    (touch_x,touch_y) = (0,0)
    
    while True:   
        #今の日時を取得
        today_n,time_n,now_M_S,now_S = now_daytime()
        
        if now_S != now_S_old: #1秒に1回処理するためにこの処理を追加
            #0秒になったら時計表示
            if now_S == "00" :
                time_disp(today_n,time_n)
                print(today_n,time_n)                
        
            if now_S[1] == "0": #10秒毎に処理
                #温度取得
                humi, temp = Adafruit_DHT.read_retry(sensor, pin)

                if humi is not None and temp is not None:
                    print('Temp={0:0.1f}*C  Humidity={1:0.1f}%'.format(temp, humi))
                    #温度表示
                    temp_disp(temp,humi)
                    #weather_icon_disp()
                else:
                    print('Failed to get reading. Try again!')
                
            if now_M_S == "00:00" :
                #お天気を取得
                cor,weather,weather_url = weather_get()
                #お天気をLCDへ表示
                weather_disp(cor,weather,weather_url)
                #weather_text_disp(cor,weather)
                #weather_icon_disp()
    
        now_S_old = now_S #1秒の切り替わりを判定するために前の秒数を保存

        sel = touch_sel(touch_x,touch_y)
        if sel == "end":
            print(sel)
            poff_disp("STOP")
            return sel
        if sel == "poff":
            print(sel)
            poff_disp("POFF")
            time.sleep(1)
            return sel    
        (touch_x,touch_y) = (0,0)

タッチパネルの検出座標のtouch_xとtouch_yはグローバル変数にしておかないと認識できませんでした。

プログラム終了処理とシャットダウン処理

プログラムの終了は、sys.exit()

シャットダウン処理は、os.system('sudo shutdown -h now')

で行います。


if __name__ == '__main__':

    # libフォルダのライブラリを参照できるようにする。
    import sys
    sys.path.append('./lib')
    from LCD_led import LCD_led
    from LCD_disp import LCD_set,setup_disp,time_disp,now_daytime
    from LCD_disp import temp_disp,weather_disp,weather_text_disp,weather_icon_disp
    from LCD_disp import touch_disp,poff_disp
    from weather_get import weather_get    
    import time

    #---DHT22温度センサの初期設定-----
    import Adafruit_DHT
    sensor = Adafruit_DHT.DHT22
    pin = 26
    #------------------

    setup_main()
    touch_disp("")

    sel = main_01()
    
    # import osでシャットダウンできるようにする
    import os
    import RPi.GPIO as GPIO

    time.sleep(1)
    if sel =="end":
        GPIO.cleanup()
        sys.exit()
    if sel == "poff":
        os.system('sudo shutdown -h now')

動作確認

プログラムの終了処理

ちゃんとタッチした座標を認識してプログラムの終了処理ができました。

ただし、sys.exit()を実行するとエラーを表示します。

最後にこのようなエラーを表示しました。

RuntimeError: Please set pin numbering mode using GPIO.setmode(GPIO.BOARD) or GPIO.setmode(GPIO.BCM)

タッチパネルのプログラムでgpiozeroのライブラリを使用しています。

手動でクリーンナップする必要はないかもしれません。

 GPIO.cleanup()はコメントアウトしたら先ほどのエラーは表示しなくなりました。 LCDのLEDも消えましたので手動でクリーンナップする必要はなさそうです。

 

LCDの動作は以下のようになります。

タッチする前

タッチしたとき

すぐにSTOPを表示

1秒後に画面消灯

 

シャットダウン処理

こちらはエラーもなく正常にシャットダウンしました。

次回は、電源を入れた時にこのプログラムを自動で実行できるようにしたいと思います。