さやまのものづくり日記

電子工作やプログラミングなど趣味で作ったのもを紹介していくブログです。

raspberrypiでGUIカウントダウンダイマーを作る

今回作ったもの

今回は設定した時間分カウントダウンをし、画面にGUI表示するソフトをpythonとraspberrypiで作ってみました。
pythonで使えるtkinterというUGI作成パッケージを使用してカウントダウンしたものをターミナル上ではなく、GUIで表示します。

環境

Raspberry pi 3B+
python 3.7.3
tkinter
pillow

準備

sudo apt update
sudo apt remove python3-pil
sudo apt install python3-tk
pip3 install pillow

詰まった部分

画像ライブラリのpillowはpythonをインストールしたときにはじめから入っているのですが、それだと上手く動かなかったので、アンインストールしてから最新版を再インストールしました。

完成品

カウントが10からスタートして0まで表示されます。 f:id:sayama_engner:20210501154529g:plain

ソースコード

import math
import json
import requests
import os
from tkinter import *
import tkinter.ttk as ttk
from datetime import datetime, timedelta
from time import sleep

target_time = 20


# メインウィンドウ作成
root = Tk()

# メインウィンドウサイズ
root.geometry("1024x600")

# メインウィンドウタイトル
root.title("count")

# MainFrame クラス


class MainFrame(ttk.Frame):
    # コンストラクタ
    def __init__(self, master=None, **kwargs):
        # 親クラスのコンストラクタを呼び出す
        super().__init__(master, **kwargs)

        # create_widgets を呼び出す
        self.create_widgets()

    # ウィジェットを作成
    def create_widgets(self):
        # フレームを作成
        self.frame = Frame(self, bg="white", bd=0, height=460, relief="flat")

        # タイマーカウント表示
        self.wp = Label(self.frame, text="", bg="white", font=("", 40, "bold"), anchor="w")
        self.wp.place(width=740, x=150, y=350)

        # フレームを配置
        self.frame.grid(row=0, column=0, columnspan=8, sticky="news")
        self.rowconfigure(0,weight=1)
        self.rowconfigure(1,weight=1)
        self.rowconfigure(2,weight=1)
        self.rowconfigure(3,weight=1)
        self.rowconfigure(4, weight=1)
        for i in range(8):
            self.columnconfigure(i, weight=1)


# メインフレームを配置
app = MainFrame(root)
app.pack(side=TOP, expand=1, fill=BOTH)

# メインウィンドウを閉じる


def wm_close():
    root.destroy()


# 閉じるボタン作成
btn = Button(root, text=" X ", font=('', 16), relief=FLAT, command=wm_close)

# 画面がリサイズされたとき


def change_size(event):
    # ボタンの位置を右上に
    btn.place(x=root.winfo_width() - 60, y=14)


# 画面のリサイズをバインドする
root.bind('<Configure>', change_size)

# メインウィンドウの最大化
#root.attributes("-zoom", "1")
root.attributes("-fullscreen", "1")


counter_time = 0
count_time_flag = 1

def count_down_time():
    global counter_time
    global count_time_flag
    if count_time_flag == 1:
        counter_time = 5
        count_time_flag = 0  # フラグを下げてカウントがリセットされないようにする

    counter_time -= 1
    #print(counter_time)
    if counter_time < 0:
        return 0
    t = "インターバル残り時間:{0}".format(counter_time)
    app.wp.configure(text=t)
    root.after(1000,  count_down_time)

 
count_down_time()

# メインループ
root.mainloop()

参考サイト

qiita.com

Raspberry piにモーニングコールをしてもらう

やったこと

今回はRaspberry piに、朝の起きる時刻になると設定した音声を喋ってもらう機能を実装しました。

環境

Raspberry pi 3B+
python 3.7.3
Open JTalk1.08

準備

発話の準備

jtalkを使って喋らせるので、こちらの記事を参考にしながらjtalkの環境構築を進めました。 pacoは使えなかったので普通にmake installしました。パッケージの管理はできなくなりますが問題ありません。

プログラム

#!/usr/bin/env python
# -*- coding:utf-8 -*-
import shlex
import subprocess
from datetime import datetime


CMD_SAY = 'jsay'

def Mornigcall():
    text = '起きてください 会社に遅れます'
    text = CMD_SAY + ' ' + text
    proc = subprocess.Popen(shlex.split(text))
    proc.communicate()
    return


def SayDateTime():
    d = datetime.now()
    text = '現在時刻は%s時%s分%s秒です' % (d.hour, d.minute, d.second)
    text = CMD_SAY + ' ' + text
    proc = subprocess.Popen(shlex.split(text))
    proc.communicate()
    return


def main():
    for num in range(6):
        SayDateTime()
        Mornigcall()
    return


if __name__ == "__main__":
    main()

ファイル名は「mornigcall.py」です。ひとまず6回繰り返すようにしました。

自動起動の設定

朝の起きる時刻になったら発話するプログラムを自動で起動します。 コマンドラインからcrontab -eを実行して出てきたファイルの一番下に以下のように記述します。注意点としてフルパスで記述する必要があります。 f:id:sayama_engner:20210329110930p:plain
定期自動実行に関してはcronの使い方(pythonスクリプト)を参考にしました。 これで毎朝7時にモーニングコールで起こしてもらえます。

参考

www.tapun.net

Pythonでシェルコマンドを実行する - Mobile Factory Tech Blog

cron の使い方(pythonスクリプト) - Qiita

Raspberry piでものを上下させると増えるカウンターを作ってみた

やったこと

Raspberry pi 3B+に赤外線距離センサをつけて、ものを近づけて遠ざけるとカウントが増えるプログラムを作成しました。Raspberrypiで何か生活を便利にするものを作ろうと思っていますが、いきなり高度なものはモチベーションが続かないので簡単なものから作って積み上げていこうと思います。今回はその第1段です。

環境

python 3.7.3

Raspberry pi 3B+

VL53L0x(赤外線距離センサ)

ジャンパーワイヤ

準備

距離センサの選定

赤外線距離センサは、最小検出距離の短さと小ささからAmazonに売っているVL53L0xを選びました。

VL53L0xをPythonで使えるようにするまで

こちらのサイトのとおりに環境構築を進めていきました。
pythonでいきなり使えるようになるわけではなくて、センサの発売元であるSTmicro Electronicsが配布しているAPI(C言語で書かれている)をダウンロードしてコンパイルしないといけないようです。コンパイルが終わったらAPIpythonで使えるようにするためのオープンソースプロジェクトをGithubからクローンします。
今回は自分でプログラムを作成するので、「Counter」というディレクトリを作成し、VL53L0X.piとbinを「Counter」ディレクトリにコピーしました。

カウントするプログラムを作成する

プログラムの設計としては、以下の図に示すような状態をとり、それぞれの条件判定によって状態を遷移していきます。「inital」はプログラムを起動したときの初期状態、「above」は基準よりものが上にある状態、「down」はものを近づけている(下げている)状態、「below」は基準より下に下がっている状態、「up」はものが上がっている状態になります。

f:id:sayama_engner:20210327165008p:plain
本プログラムの状態遷移図
普通に書こうとするとifとwhileの大量の入れ子が発生して見づらくなるので状態で管理します。 こうして状態で管理すると、次の状態に遷移するときの判定だけを見ていれば良いので、見やすく、機能の追加もしやすいです。
本来は距離データは移動平均などを取る必要がありますが、計測してみたらあまり振れ幅がなかったので、今回の用途ではしきい値を調整することでうまく判定できるようにしました。

書いたコードは以下です。

#!/usr/bin/env python
# -*- coding: utf-8 -*-
import time  # time(sleepを使うためのモジュール)のインポート
import VL53L0X  # VL53L0X(spi通信を行うためのモジュール)のインポート

counter = 0
state_dict = {"inital": 0, "above": 1, "down": 2, "below": 3, "up": 4}
above_dist = 16
below_dist = 4
state = 0


def Counter():
    global state
    global counter
    global above_dist
    global below_dist
    if state == state_dict["inital"]:
        print("inital state")
        if dist > above_dist and dist < above_dist+4:  # この範囲に入ったらスタート
            state = state_dict["above"]
    elif state == state_dict["above"]:
        # print("above state")
        if dist < above_dist:
            state = state_dict["down"]
    elif state == state_dict["down"]:
        # print("down state")
        if dist < below_dist:
            state = state_dict["below"]
    elif state == state_dict["below"]:
        # print("below state")
        if dist > below_dist:
            state = state_dict["up"]
    elif state == state_dict["up"]:
        # print("up state")
        if dist > above_dist:
            state = state_dict["above"]
            counter += 1
            print("%d 回" % counter)



if __name__ == '__main__':
    """VL53L0Xのインスタンスを作成"""
    tof = VL53L0X.VL53L0X(address=0x29)

    # 距離の取得を開始する
    tof.start_ranging(VL53L0X.VL53L0X_BETTER_ACCURACY_MODE)

    while True:
        try:
            dist = tof.get_distance()/float(10)  # VL53L0Xから距離[cm]を取得する
            Counter()
        except KeyboardInterrupt:
            break

完成品

プログラムを実行してものを近づけたり離したりするとカウントが上がります。 f:id:sayama_engner:20210327173232g:plain

参考サイト

5ドル!ラズパイ・ゼロ(Raspberry pi Zero)でIoT (44) 距離センサ5 I2C VL53L0X | 電子工作の環境向上

sites.google.com