042-365-0611 お問い合わせフォーム

オープンソースの活用事例紹介 ~デモ機の製作2 【Raspberry Piプログラム】

前回の“Genuino101プログラム”に続き、今回は「Raspberry Piプログラム」の作成について紹介を致します。

Raspberry PiのBluetoothライブラリのインストール

Raspberry PiはRaspbian(原稿執筆時のバージョンはMarch 2017)で動作させ、プログラムはPythonで作成します。PythonでBluetoothを使用するにはライブラリをインストールする必要があります。
Bluetoothのライブラリにはいくつか種類がありますが、今回は「bluepy」というライブラリを使用します。ライブラリのインストールは以下の手順で行います。
(1) はじめにライブラリのインストールに必要なパッケージをインストールします。


sudo apt-get install libglib2.0-dev

(2) ライブラリをインストールします。Pythonのバージョンにより実行するコマンドが異なります。

Python2.7の場合


sudo pip install bluepy

Python3.4の場合


sudo pip3 install bluepy

以上でライブラリのインストールは終了です。

Raspberry Piのプログラム

Raspberry Pi側のプログラムでは、Genuino101に対して動作開始、停止の信号の送出、リレーの励磁信号、接点信号の状態、接点の駆動回数の受信を行います。そして励磁信号が変化した場合、一定時間以内に接点信号が変化するかどうかを判定し、変化がなかった場合は制御回路に異常が発生したとして、エラーメッセージを表示します。また接点の駆動回数がリレーの寿命となる回数分動作したかどうかを判定し、寿命間近となったら注意(寿命まで残り100回以下)として残り寿命の表示エリアの背景を黄色、警告(寿命まで残り10回以下)として背景を赤色で表示します。
このプログラムのリストは以下のようになります。

リスト2 Raspberry Piのプログラム(demo.py)

#! /usr/bin/env python3
# -*- coding: utf-8 -*- 

# モジュールをインポート
import bluepy.btle as btle
import struct
import tkinter as Tk  # Python3.4
import tkinter.messagebox as Msg  # Python3.4
#import Tkinter as Tk  # Python2.7
#import tkMessageBox as Msg  # Python2.7
import sys

# Frameクラスを定義
class Frame(Tk.Frame):
    def __init__(self, master=None):
        Tk.Frame.__init__(self, master)

        #  Variableオブジェクトのインスタンスを生成
        self.stat = Tk.StringVar()
        self.sig = Tk.StringVar()
        self.rly = Tk.StringVar()
        self.eep = Tk.StringVar()
        self.life = Tk.IntVar()
        self.rmlife = Tk.StringVar()

        #  変数の初期化
        self.cnt = 10
        self.pcdata = 0
        self.prv_data = 0
        self.rlydata = 0
        self.eepdata = 0
        self.lifedata = 100000
        self.started = False

        #  Variableに値を設定
        self.var_set()
        
        #  ウィンドウのタイトルを設定
        self.master.title('Relay Control Demo')

        #  リレーの状態を表示するフレームを設定
        f_display = Tk.Frame(self, relief=Tk.RIDGE, bd=4)
        f_display.pack( padx=5, pady=5 )
        
        #  リレーの状態を表示するラベルを設定
        self.fc_display = Tk.Label(f_display, textvariable=self.stat, width=15, relief=Tk.SUNKEN, bd=2, font=('Helvetica', '24'), bg='white')
        self.fc_display.pack()
        
        #  リレーの入出力信号の状態を表示するフレームを設定
        g_display = Tk.Frame(self, relief=Tk.RIDGE, bd=4)
        g_display.pack( padx=5, pady=5 )
        
        #  リレーの入力信号の状態を表示するラベルを設定
        self.g1_display = Tk.Label(g_display, textvariable=self.sig, width=30, relief=Tk.SUNKEN, bd=2, font=('Helvetica', '12'), bg='white')
        self.g1_display.pack()
        
        #  リレーの出力信号の状態を表示するラベルを設定
        self.g2_display = Tk.Label(g_display, textvariable=self.rly, width=30, relief=Tk.SUNKEN, bd=2, font=('Helvetica', '12'), bg='white')
        self.g2_display.pack()
        
        #  リレーの寿命を表示するフレームを設定
        h_display = Tk.Frame(self, relief=Tk.RIDGE, bd=4)
        h_display.pack( padx=5, pady=5 )

        #  リレーの駆動回数を表示するラベルを設定
        self.h1_display = Tk.Label(h_display, textvariable=self.eep, width=30, relief=Tk.SUNKEN, bd=2, font=('Helvetica', '12'), bg='white')
        self.h1_display.grid(row=0,column=0, columnspan=2)
        
        #  リレーの寿命を表示するラベルを設定
        self.h2_display = Tk.Label(h_display, text='Life:', width=15, relief=Tk.SUNKEN, bd=2, font=('Helvetica', '12'), bg='white')
        self.h2_display.grid(row=1,column=0)

        #  リレーの寿命を入力するテキストボックスを設定
        self.lifeentry = Tk.Entry(h_display, textvariable=self.life,  width=14, justify=Tk.RIGHT, relief=Tk.SUNKEN, bd=2, font=('Helvetica', '12'), bg='white')
        self.lifeentry.grid(row=1,column=1)
        self.lifeentry.bind('', self.func)

        #  リレーの残り寿命を表示するラベルを設定
        self.h3_display = Tk.Label(h_display, textvariable=self.rmlife, width=30, relief=Tk.SUNKEN, bd=2, font=('Helvetica', '12'), bg='white')
        self.h3_display.grid(row=2,column=0, columnspan=2)

        #  ボタンを表示するフレームを設定
        f_button = Tk.Frame(self)
        f_button.pack(pady=2)

        #  スタートボタンを設定
        self.b_start = Tk.Button(f_button, text='Start', command=self.start)
        self.b_start.pack(side=Tk.LEFT, padx=1)


        #  ストップボタンを設定
        self.b_stop = Tk.Button(f_button, text='Stop', command=self.stop, state=Tk.DISABLED)
        self.b_stop.pack(side=Tk.LEFT, padx=1)

        #  self.measureを実行
        self.measure()

    #  テキストボックスに入力された値を型変換して変数に代入
    def func(self, ev):
        self.lifedata = int(self.life.get())

    #  Genuino101からデータを取得してVariableに値を設定
    def var_set(self):
        # Genuino101からデータを取得
        try:
            self.pcdata = struct.unpack( 'B', PC.read() )       # 入力信号の状態
            self.rlydata = struct.unpack( 'B', Relay.read() )   # 出力信号の状態
            self.eepdata = struct.unpack( 'I', EEPROM.read() )  # 駆動回数
        # エラー処理
        except btle.BTLEException:
            Msg.showerror(title = 'Error', message = 'BLE Device fail or not found')
            root.destroy()
            sys.exit()
        #  Variableに値を設定
        self.stat.set('Relay Status:OFF')                       # リレーの状態
        self.sig.set('Input Signal:%d' % (self.pcdata))         # 入力信号の状態
        self.rly.set('Output Signal:%d' % (self.rlydata))       # 出力信号の状態
        self.eep.set('Total Count:%d' % (self.eepdata))         # 駆動回数
        self.life.set(self.lifedata)                            # 寿命回数
        self.rmlife.set('Remain Life:%d' % (self.lifedata - int(self.eepdata[0])))  # 残り寿命回数

    #  Startボタンを押したとき
    def start(self):
        # Genuino101にデータを送信
        try:
            Switch.write(struct.pack('<b', 1))
        # エラー処理
        except btle.BTLEException:
            Msg.showerror(title = 'Error', message = 'BLE Device fail or not found')
            root.destroy()
            sys.exit()
        self.started = True
        self.cnt = 10
        # ボタンの状態を変更
        self.b_start.configure(state=Tk.DISABLED)
        self.b_stop.configure(state=Tk.NORMAL)
        # self.countingを実行
        self.counting()

    #  Stopボタンを押したとき
    def stop(self):
        # Genuino101にデータを送信
        try:
            Switch.write(struct.pack('<b', 0))
        # エラー処理
        except btle.BTLEException:
            Msg.showerror(title = 'Error', message = 'BLE Device fail or not found')
            root.destroy()
            sys.exit()
        self.started = False
        # ボタンの状態を変更
        self.b_start.configure(state=Tk.NORMAL)
        self.b_stop.configure(state=Tk.DISABLED)
        #  Variableに値を設定
        self.stat.set('Relay Status:OFF')
    
    # Genuino101からデータを取得
    def measure(self):
        # Genuino101からデータを取得
        try:
            self.pcdata = struct.unpack( 'B', PC.read() )
            self.rlydata = struct.unpack( 'B', Relay.read() )
            self.eepdata = struct.unpack( 'I', EEPROM.read() )
        # エラー処理
        except btle.BTLEException:
            Msg.showerror(title = 'Error', message = 'BLE Device fail or not found')
            root.destroy()
            sys.exit()
        # 入力信号が変化したか判定
        if self.prv_data != self.pcdata:
            self.cnt = 5
            self.prv_data = self.pcdata
        #  Variableに値を設定
        self.sig.set('Input Signal:%d' % (self.pcdata))
        self.rly.set('Output Signal:%d' % (self.rlydata))
        self.eep.set('Total Count:%d' % (self.eepdata))
        self.rmlife.set('Remain Life:%d' % (self.lifedata - int(self.eepdata[0])))
        #  残り寿命の判定
        if (self.lifedata - int(self.eepdata[0])) <
            self.h3_display.configure(bg='red')
        elif (self.lifedata - int(self.eepdata[0])) <
            self.h3_display.configure(bg='yellow')
        else:
            self.h3_display.configure(bg='white')
        #  100m秒後にself.measureを実行
        self.after(100, self.measure)

    # 入力信号が一定時間内に変化するか判定
    def counting(self):
        if self.started:
            #  入力信号が変化しなかったとき
            if self.cnt <=
                self.stat.set('Relay Status:NG')
                self.fc_display.configure(bg='red')
            #  入力信号が変化したとき
            else:
                self.stat.set('Relay Status:OK')
                self.fc_display.configure(bg='white')
            self.cnt -=1
            # 1秒後にself.countingを実行
            self.after(1000, self.counting)

# メインウィンドウを生成
root = Tk.Tk()
# ウィンドウサイズを350×250ドットに指定
root.geometry("350x280")
# メインウィンドウのサイズ変更を禁止
root.resizable(0,0)

# Genuino101と接続
try:
    Rly = btle.Peripheral(":4F:EE:0F:CC:7E")
# エラー処理
except btle.BTLEException:
    Msg.showerror(title = 'Error', message = 'BLE Device fail or not found')
    root.destroy()
    sys.exit()

# キャラクタリスティックのリストを取得
chrts = Rly.getCharacteristics()

# UUIDに対応したキャラクタリスティックのインスタンスを生成
for chrt in chrts:
    # リレー制御開始用キャラクタリスティック
    if chrt.uuid == btle.UUID("19b10011-E8F2-537E-4F6C-D104768A1214"):
        Switch = chrt
    # リレー接点信号用キャラクタリスティック
    elif chrt.uuid == btle.UUID("19B10012-E8F2-537E-4F6C-D104768A1214"):
        PC = chrt
    # リレー励磁信号用キャラクタリスティック
    elif chrt.uuid == btle.UUID("19B10013-E8F2-537E-4F6C-D104768A1214"):
        Relay = chrt
    # リレー駆動回数用キャラクタリスティック
    elif chrt.uuid == btle.UUID("19B10014-E8F2-537E-4F6C-D104768A1214"):
        EEPROM = chrt

# Frameクラスのインスタンスを生成
f = Frame(master=root)
# Frameを配置
f.pack()
# mainloopを実行
root.mainloop()

それではこのプログラムを順番に説明します。

4~11行目
モジュールをインポートしています。
14行目
Frameクラスを定義しています。このクラスでウィンドウ画面を作っています。
15行目
Frameクラスのコンストラクタを定義しています。
16行目
Tk.Frameクラスの__init__メソッドで初期化しています。
18~24行目
このあとのTk.Labelなどで使用するVariableオブジェクトのインスタンスを生成しています。
26~33行目
プログラム中で使用する変数を初期化しています。
35~36行目
Variableに値を設定する関数を実行しています。
38~39行目
ウィンドウのタイトルを設定しています。
41~43行目
リレーの状態を表示するフレームを設定しています。
45~47行目
リレーの状態を表示するラベルを設定しています。
49~51行目
リレーの入出力信号の状態を表示するフレームを設定しています。
53~55行目
リレーの入力信号の状態を表示するラベルを設定しています。
57~59行目
リレーの出力信号の状態を表示するラベルを設定しています。
61~63行目
リレーの寿命を表示するフレームを設定しています。
65~67行目
リレーの駆動回数を表示するラベルを設定しています。
69~71行目
リレーの寿命を表示するラベルを設定しています。
73~76行目
リレーの寿命を入力するテキストボックスを設定しています。
78~80行目
リレーの残り寿命を表示するラベルを設定しています。
82~84行目
ボタンを表示するフレームを設定しています。
86~88行目
スタートボタンを設定しています。
90~92行目
ストップボタンを設定しています。
94~95行目
Genuino101からデータを取得する関数を実行しています。
98行目
テキストボックスに入力された値を型変換して変数に代入する関数です。
99行目
テキストボックスの値を取得し、int型に変換して変数に代入しています。
102行目
Genuino101からデータを取得してVariableに値を設定する関数です。
103~107行目
Genuino101からデータを取得しています。try節内で処理しているので例外が発生した場合、エラー処理が行われます。
108~112行目
Genuino101とBluetoothで通信しているときにエラーが発生するとBTLEExceptionが発生します。そのエクセプションが発生した場合の処理を記述しています。具体的にはエラーが発生したことを通知するメッセージボックスを表示し、メッセージボックスのOKボタンが押されたらウィンドウを閉じ、プログラムを終了します。
113~119行目
Variableに値を設定しています。
122行目
スタートボタンが押された時に呼び出される関数です。
123~125行目
Genuino101にデータを送信し、動作を開始させます。try節内で処理しているので例外が発生した場合、エラー処理が行われます。
126~130行目
Bluetoothの通信エラー(BTLEException)が発生した場合の処理を記述しています。
131行目
Genuino101の動作状態を示す変数にTrueを代入します。
132行目
タイムアウトエラーを計測する変数を初期化しています。
133~135行目
スタートボタンをディセーブルに、ストップボタンをイネーブルにしています。
136~137行目
リレーの入力信号が一定時間内に変化するか判定する関数を実行しています。
140行目
ストップボタンが押された時に呼び出される関数です。
141~143行目
Genuino101にデータを送信し、動作を停止させます。try節内で処理しているので例外が発生した場合、エラー処理が行われます。
144~148行目
Bluetoothの通信エラー(BTLEException)が発生した場合の処理を記述しています。
149行目
Genuino101の動作状態を示す変数にFalseを代入します。
150~152行目
スタートボタンをイネーブルにし、ストップボタンをディセーブルにしています。
153~154行目
リレーの動作状態を示すVariableに値を設定しています。
157行目
Genuino101からデータを取得する関数です。
158~162行目
Genuino101からデータを取得します。try節内で処理しているので例外が発生した場合、エラー処理が行われます。
163~167行目
Bluetoothの通信エラー(BTLEException)が発生した場合の処理を記述しています。
168~171行目
リレー接点信号が変化したかどうか判定しています。変化していた場合はタイムアウトエラーをカウントする変数を初期化しています。
172~176行目
Variableに値を設定しています。
177~183行目
残り寿命の判定をしています。残り寿命が10回以下のとき、残り寿命を表示しているラベルの背景を赤に、100回以下の時は黄色に、それ以上のときは白にしています。
184~185行目
100m秒後にmeasure関数を実行します。
188行目
入力信号が一定時間内に変化するか判定する関数です。
189行目
リレーの動作状態がTrueのときに判定を行います。
190~193行目
一定時間経過しても入力信号が変化しなかった場合の処理です。リレーの状態表示を「NG」とし、背景を赤くします。
194~197行目
一定時間経過するまでに入力信号が変化した場合の処理です。リレーの状態表示を「OK」とし、背景を白くします。
198行目
経過時間をカウントダウンしています。
199~200行目
1秒後にcounting関数を実行します。
202~203行目
メインウィンドウとなるTkインスタンスを生成しています。
204~205行目
ウィンドウサイズを350×280ドットに指定しています。
206~207行目
メインウィンドウのサイズ変更を禁止しています。
209~211行目
Genuino101と接続します。try節内で処理しているので例外が発生した場合、エラー処理が行われます。
212~216行目
Bluetoothの通信エラー(BTLEException)が発生した場合の処理を記述しています。
218~219行目
キャラクタリスティックのリストを取得しています。
221~234行目
取得したキャラクタリスティックのUUIDを調べて、リスト中のキャラクタリスティックのうち制御に必要なキャラクタリスティックを取り出し、そのインスタンスを生成しています。
236~237行目
Frameクラスのインスタンスを生成しています。
238~239行目
Frameを配置しています。
240~241行目
mainloopを実行しています。
※次回は、「動作テスト」について紹介します。
1 2 3

Copyright © 2021 TAHARA ELECTRIC CO., LTD. all right reserved

ページトップ