第27回 LED点滅制御のプログラム試作

今回は、最初にプログラム全体の仕組みを検討して、プログラムの作成を始めます。

今回の説明内容

これからいよいよ天気情報と鉄道運行情報をLEDに表示するプログラムを作成していきますが、その前にプログラム全体をどのように作っていくか検討します。

プログラムの作り方ですが、最初にプログラム全体の流れを検討します。その後は、必要な要素技術(例えばLEDの点滅やWebからの情報取得)を検証して、最後に1本のプログラムにまとめたいと思います。

実際にまだプログラムは完成していないので、記事を書きながら考えていきたいと思います。第1回の記事で、出来上がりのイメージの動画をアップしましたが、とりあえず動画用に作成したプログラムで、実際に完成したプログラムではないんですよね。

今回の説明内容は以下になります。

  • プログラム全体の構成を検討
  • これから詳細に検討する動作確認項目
  • LEDの点滅制御方法の検討
  • PHPでLED点滅制御プログラムを作成
  • PythonでLED点滅制御プログラムを作成

 

プログラム全体の構成を検討

LEDの点滅制御方法を検討する前に、全体のプログラムで行う最低限の処理を考えてみます。

LEDの点滅処理ですが、Webから取得した情報に対応した特定のパターンで点滅させることになります。点滅パターンをあらかじめ定義しておいて、取得した情報に応じたパターンを作成して、実際の点滅はそのパターンに従って点滅させる、という方法になると思います。だいたい以下のことができれば最低限の動作はできそうな気がします。(気がします、って…)

  1. 「晴れ」とか「曇り」とかのLEDの点滅パターンを定義する
  2. Webから情報を取得する
  3. Webから取得した情報に応じてLEDの点滅パターンを生成する。例えば「晴れのち曇り」であれば、(1)の「晴れ」のパターン + 「曇り」のパターンなど
  4. (3)のパターンに従ってLEDを点滅させる

最低限、このぐらいができれば動作しそうです。ただこれだけではちょっとイマイチですよね。上のプログラムを作っても、起動した時にWebから情報を取得してLEDを点滅させますので、その後は情報が更新されません。そのため天気予報の情報は1日に数回、定時更新する必要がありそうです。また鉄道運行情報は、出かけるちょっと前の時点の情報を知りたいですよね。そのためスイッチが押されたら情報更新する機能も追加したいです。これらの追加機能は、最低限の機能を実装してから追加していく方針にしたいと思います。さらにいろいろ考えていくと課題なども出てきそうです。それらについてはチャレンジ課題として最後にまとめたいと思います。

 

これから詳細に検討する動作確認項目

これから何回かにわたって、最低限動作に必要な項目の確認をしていきたいと思います。一つはLEDの点滅制御、もう一つはWebから情報を取得して解析する方法です。これらができれば、あとは組み合わせて最低限の動作はできると思います。

ということで、今回はLEDの点滅制御方法の検討、次回はWebから情報を取得して解析する方法の検討をしたいと思います。

 

LEDの点滅制御方法の検討

それでは、LEDの点滅制御方法の検討を始めましょう。

まず点滅パターンです。パターンはいろいろあると思いますが、とりあえず以下のような感じで考えてみました。

今日は「晴れ」など1種類の天気の場合

点滅させることも考えましたが、点滅は「晴れ時々曇り」などの「時々」の表現に使いたいと思いますので、1種類の天気の場合は、その天気のLEDを点灯させたままにしたいと思います。

「晴れのち曇り」など「のち」の天気の場合

この場合は、「晴れ」のLEDを1秒間点灯させたあとに「曇り」のLEDを1秒間点灯させる、というパターンにしたいと思います。なんとなくですが、2種類の天気の点灯をした後は、1秒間LEDを消灯させたいと思います。つまり、「晴れのち曇り」の場合は、1秒間「晴れ」を点灯、1秒間「曇り」を点灯、1秒間休み、という感じです。

「晴れ時々曇り」などの「時々」の天気の場合

「時々」は点滅で表現したいと思います。点滅のパターンは、0.25秒刻みで、点灯、消灯の繰り返しにします。なお第1回の動画では、「晴れ時々曇り」のパターンは、「晴れ」1秒間点灯、次に「晴れ」を消灯してから「曇り」を0.25秒刻みで点滅させていました。ただ、「晴れ時々曇り」というのは「晴れ」が主体でその合間に雲が出る、という感じなので、「晴れ」は点灯させたまま「曇り」を点滅させる、というパターンにしてみようかと思います。これは出来上がりを見て必要があれば見直したいと思います。

ということで、天気の点滅パターンをまとめると以下のようになります。

Led pattern fine

Led patter cloudy after fine

Led pattern fine partially cloudy

次にこのパターンをプログラム制御する方法を考えてみます。最小の時間単位は0.25秒ですので、すべて0.25秒単位で考えてみます。

まず「晴れ時々曇り」の「時々曇り」の部分ですが、これは0.25秒刻みの点滅になります。点灯を1、消灯を0と決めると「晴れ時々曇り」では、曇りは2秒間、0.25秒刻みで点滅しますので、この部分は「0 1 0 1 0 1 0 1」となります。

Pattern partially cloudy

上のパターンで、1秒消灯部分も0.25秒単位で表現できますよね。「1秒間の消灯」という代わりに「0.25秒間の4回分の消灯」と考えます。

Pattern partially cloudy 2

同様に1秒間点灯する場合は「0.25秒間4回分の点灯」と考えることができます。このように考えると、すべての点灯パターンが0.25秒刻みの点滅パターンで表現できることになります。

LEDの点滅制御をまとめてみます。

まず最初に以下のように基本パターンを用意しておきます。

Pattern basic

次にWebから天気予報の情報を取得します。取得した情報が「晴れ時々曇り」であれば、各LEDの点滅パターンは上の基本パターンを組み合わせて、以下のように構成します。

Pattern all

これで各LEDの点滅パターンが用意できましたので、これに従ってLEDの点滅制御をすればOKそうです。このパターンの点滅制御は以下のように繰り返し処理で行います。

Led control

実際にプログラムする際には、基本パターンを配列にして、例えば [1, 1, 1, 1] や [0, 1, 0, 1]などの配列(タプル/リスト)にして、それを組み合わせて、各LEDのパターン配列を生成、その配列を元に上のような制御をする、という方法で実装してみたいと思います。

 

PHPでLED点滅制御プログラムを作成

まずPHPでプログラムを作成してみます。先ほど説明したように、最初に基本パターンを配列で用意して、それらを天気予報のに応じてつなぎ合わせます。基本パターン、LEDの点滅パターンは連想配列に入れることにします。

C言語など、比較的アセンブラに近い言語を使われている方は連想配列に慣れていないかもしれませんので、ここで簡単に説明しておきます。

通常、配列の要素を指定する場合、数字で何番目かを指定します。例えば unsigned char num[10]; でunsigned char型の配列を用意した場合、例えば3番目のnumを指定する場合は添字で num[2] のように指定します。

ここで、具体例を考えてみます。例えば商品の値段を格納する場合、 unsigned long price_list[]; とした宣言して、price_list[0]にはリンゴ(フジ)の値段、price_list[1]には温州みかんの値段、などと代入していくとします。このようにすると品物が増えた場合とか管理が大変ですよね。このような場合は連想配列が有効です。連想配列は、キーと値の組み合わせの形で格納する配列です。

例えば、PHPでは連想配列は以下のように キー => 値 の組をカンマで区切って書きます。

$price_list = [
    "apple_fuji" => 138,
    "unshu_mikan" => 398,
];

順番は気にせずに、データベースのような形で配列に格納できます。この配列から例えばリンゴ(フジ)の値段を取り出す場合は、

$price = $price_list["apple_fuji"];

と書きます。では、連想配列に入っているキーと値を全て取得したい場合や全て表示をしたい場合はどうすればいいでしょうか。連想配列が実装されているプログラミング言語であれば、全てを取得する命令が用意されています。PHPでは「foreach」文、Pythonでは「for 〜 in 〜」文です。使い方は似ていますので、PHPで説明します。

例えば以下のように連想配列にキーと値を格納した場合を考えます。

$price_list = [
    "apple_fuji" => 138,
    "unshu_mikan" => 398,
    "hassaku" => 498,
];

この場合、以下のように書くと、格納されているキーと値の組み合わせ数の分、「処理」を実行してくれます。

foreach( $price_list as $item => $price ) {
    処理
}

なお、「処理」の部分では、毎回、キーが$itemに、値が$priceに格納されるので、$itemと$priceを使用して処理ができます。また、$item => $priceの変数名は任意です。例えば foreach( $price_list as $key => $value ) とした場合は、キーが$keyに、値が$valueに格納されます。

上の連想配列の例では、1回目は、$itemに”apple_fuji”、$priceに138、2回目は、$itemに”unshu_mikan”、$priceに398、3回目は、$itemに”hassaku”、$priceに498が入ります。

LEDの基本パターンや、それらを組み合わせた各LEDのパターンは、この連想配列に入れることにします。

なお、天気表示用LEDのGPIO番号の連想配列とパターン格納用の連想配列は、例えば「晴れ」であれば両方とも “fine” をキーにして格納することにします。

テスト用ですので、予報が「晴れ時々曇り」の状態を示す制御をしてみます。以下のようにプログラムを作成してみました。

解説が必要と思われるところをプログラムの後に説明します。

#!/usr/bin/php

<?php

// LED点滅パターンの基本パターンを連想配列に定義
$basic_pat = [
    "on"    => [1, 1, 1, 1], // 点灯したままのパターン
    "off"   => [0, 0, 0, 0], // 消灯したままのパターン
    "blink" => [0, 1, 0, 1], // 点滅するパターン
];

// LEDのGPIO番号を連想配列に定義
$gpio_led = [
    "fine"  => 21, // 晴れ用LED
    "cloud" => 20, // 曇り用LED
    "rain"  => 16, // 雨用LED
    "snow"  => 12, // 雪用LED
];

// GPIOモードの設定
// $gpio_ledで定義したすべてのピンを出力モードに設定する
foreach($gpio_led as $wheather => $gpio_num) {
    `gpio -g mode $gpio_num out`;
}


// 予報が「晴れ時々曇り」の場合の各LED点滅パターンを設定する
// キーはGPIO番号の配列と同じにして、あとで同じキーで取り出せるようにする

// 最初に空の配列を作成
$led_pat = [];

// 「晴れ」のパターンを"fine"をキーにして連想配列に追加する
$led_pat += ["fine" => array_merge($basic_pat["on"], $basic_pat["on"], $basic_pat["off"])];

// 「曇り」のパターンを"cloud"をキーにして連想配列に追加する
$led_pat += ["cloud" => array_merge($basic_pat["blink"], $basic_pat["blink"], $basic_pat["off"])];

// 「雨」のパターンを"rain"をキーにして連想配列に追加する
$led_pat += ["rain" => array_merge($basic_pat["off"], $basic_pat["off"], $basic_pat["off"])];

// 「雪」のパターンを"snow"をキーにして連想配列に追加する
$led_pat += ["snow" => array_merge($basic_pat["off"], $basic_pat["off"], $basic_pat["off"])];


// 全体で3回繰り返す
for($repeat=0; $repeat<3; $repeat++) {
    // 天気表示バターン$led_patに入っている要素数分、点滅制御する
    for($num=0; $num<count($led_pat["fine"]); $num++) {
        // 各LEDの点灯・消灯の出力設定をする
        foreach($gpio_led as $weather => $gpio_pin_number) {
            $value = $led_pat[$weather][$num];
            `gpio -g write $gpio_pin_number $value`;
        }
        // 0.25秒間待つ
        usleep(250000);
    }
}

// GPIOピンの出力を0にする
foreach($gpio_led as $weather => $gpio) {
    `gpio -g write $gpio 0`;
}

?>
31行目

ここでは例として「晴れ時々曇り」のパターンについて、各LEDの点滅パターンを連想配列$led_patに格納しますが、最初に念のため空にしておきます。空にするには要素数ゼロの配列を代入すればOKです。

34行目

$led_patに、”fine”をキーとして、晴れのパターンを格納します。格納、というのはすでにある連想配列$led_patに追加することになります。追加は + でOKです。$led_pat = $led_pat + 晴れのパターン と書くのが普通ですが、+=表記が可能ですので、この表記にしました。また基本パターンの配列を連結しますが、連結は array_merge(配列1, 配列2, ...)を使用します。引数の配列を連結した新しい配列が返り値になります。

43行目まで処理が終わると、連想配列$led_patは以下のようになります。

$led_pat = [
    "fine"  => [1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0],
    "cloud" => [0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0],
    "rain"  => [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
    "snow"  => [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
]
47行目

テストですので、点滅制御は3回分にしました。

49行目

これから、$led_patに入っている点滅パターンの値(例えば晴れであれば、[1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0])を一つ一つ処理していきます。何回繰り返せばいいかというと、[1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0]分ですので、count() 関数で引数に配列を渡して要素数を数えます。

51行目

上の図「点滅パターン制御方法(一部拡大)」の処理の通り、パターンの最初の値に従って各GPIOピンの出力を設定します。連想配列$gpio_ledの格納しているすべての要素に対して処理を行えばOKです。

56行目

LEDの出力設定が完了しましたので、0.25秒待ちます。

 

PythonでLED点滅制御プログラムを作成

上のプログラムと全く同じ考えで、Pythonのプログラムを作成してみました。必要と思われる箇所について、プログラムの後に説明します。

#!/usr/bin/python
# coding: utf-8
 
# GPIOモジュールインポート
import RPi.GPIO as GPIO
# timeモジュールインポート
import time
 
# LED点滅パターンの基本パターンを辞書に定義
basicPat = {
    "on":    (1, 1, 1, 1),
    "off":   (0, 0, 0, 0),
    "blink": (0, 1, 0, 1)
}

# GPIO番号の定義(GPIO番号)
gpioLed = {
    "fine":  21,
    "cloud": 20,
    "rain":  16,
    "snow":  12
}

# GPIOモード設定
# GPIO番号指定をBCM(GPIO番号)に設定
GPIO.setmode(GPIO.BCM)

# GPIOの初期化(天気用LEDのみ)
gpioPins = gpioLed.values()
GPIO.setup(gpioPins, GPIO.OUT)

#   LED点滅処理
try:
    # 点滅パターン格納用の辞書を初期化
    ledPat = {}

    # 「晴れ」のパターンを"fine"をキーに辞書に追加
    ledPat.update({"fine": basicPat["on"] + basicPat["on"] + basicPat["off"]})
    # 「曇り」のパターンを"cloud"をキーに辞書に追加
    ledPat.update({"cloud": basicPat["blink"] + basicPat["blink"] + basicPat["off"]})
    # 「雨」のパターンを"rain"をキーに辞書に追加
    ledPat.update({"rain":  basicPat["off"] + basicPat["off"] + basicPat["off"]})
    # 「雪」のパターンを"snow"をキーに辞書に追加 
    ledPat.update({"snow":  basicPat["off"] + basicPat["off"] + basicPat["off"]})

    # 全体で3回繰り返す
    for repeat in range(0, 3):
        # 天気表示パターンledPatに入っている点滅パターンの要素数分、点滅制御する
        for num in range(0, len(ledPat["fine"])):
            # 各LEDの点灯・消灯の出力設定をする
            for weather, gpioPinNumber in gpioLed.items():
                GPIO.output(gpioPinNumber, ledPat[weather][num])

            # 0.25秒待つ
            time.sleep(0.25)


#   GPIOリセット
finally:
    GPIO.cleanup()
10行目

PHPでは連想配列呼んでいましたが、Pythonでは辞書と呼んでいます。普通の辞書って、見出語とその後に意味が書かれていますよね。これは、キーと値が書かれている、とも考えられます。ということで、キーと値の集合を辞書と呼んでいるのだと思います。フォーマットは、辞書 = { キー : 値, キー : 値, ... } のようになります。

29行目

GPIO.setup関数でGPIOピンの初期化をしますが、この関数に渡すピン番号配列の引数は、辞書gpioLedの値として格納されていますので、value()関数で取り出します。

以上で、LEDの点滅制御の目処がつきました。

なお、鉄道運用状況表示用のLED点滅制御も、この方法でいけそうですので、後で拡張したいと思います。

« »