第25回 Raspbery PiのGPIOを制御する (PHP編)

今回はPHPを使ってLEDの点滅制御とスイッチの読み取りを行ってみます。PHPで制御、とはいっても前回使用したgpioコマンドをPHPから使用しますので、それほど難しいことはないと思います。

目次

今回の説明内容

今回はPHPを使ってRaspberry PiのGPIOピン制御をしてみます。PHPでGPIOを制御する、とはいっても実際には前回使用したgpioコマンドをPHPから実行する形になりますので、GPIO制御に関しては新しいことは出てきません。

なおこのシリーズの最初に書きましたが、プログラミングの基本的な説明はしませんのでご了承ください。また、PHPについてはプログラムのポイントは簡単に説明しますが、基本的な文法については入門書や入門サイトなどを参照いただければと思います。

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

  • 前提とする知識
  • PHPを実行する方法
  • PHPからgpioコマンドを実行する
  • PHPでLEDを制御する
  • PHPでスイッチ状態を読み取る
  • LEDの点灯とスイッチのテストプログラム

前提とする知識

これからプログラムでRaspberry PiのGPIOを制御していきますが、いずれかのプログラミング言語でプログラムを作成したことがあることを前提としています。

言語はPHPとPythonを使用しますが、PHPやPythonの文法は知らなくても大丈夫です。どのプログラミング言語でもサポートされている変数、型、配列、条件判断文、繰り返し構文など、基本的な文法しか使いません。一つのプログラミング言語で理解できていれば、PHPやPythonの基本的なプログラムは十分理解できると思います。

PHPを実行する方法

PHPはインタープリタ型と呼ばれるプログラミング言語で、コンパイルすることなくソースコードをそのまま実行してくれます。例えばPHPで「Hello, world!」と画面に表示するプログラムを作成した場合、以下のようにソースコードがそのまま実行されます。

Program language interpreter

これに対するのはコンパイラ型の言語で、有名なところですとC言語です。C言語のようなコンパイラ言語は、プログラム(ソースコード)は直接実行せず、コンパイラというツールでソースコードをプロセッサが実行できる形式に変換してから、その変換したコードを実行します。

Program language compiler

ただし、PHPはインタープリタ型言語とは言っても、PHPのソースコードをプロセッサが実行できる実行形式に変換して実行することもできます。実際にPHPのコンパイラもリリースされているようです。

と前置きはこれぐらいにしまして、早速Raspberry PiでPHPを動かしてみましょう。

インタープリタ型のPHPは、プログラムを入力してすぐに実行結果を確認することができます。まずはその動作確認をしてみましょう。

Raspberry Piにssh接続したら、コマンドプロンプトから php -a と入力します。

pi@raspberrypi ~ $ php -a

php > 

というように php > と表示されて入力待ちの状態となります。ここにPHPのコードを入力してリターンキーを押すと、すぐに実行されて結果が表示されるものはその結果が表示されます。以前、シェルコマンドにechoコマンドがあることを説明しましたが、PHPにも同様にechoコマンドがあります。以下のように入力すると

pi@raspberrypi ~ $ php -a
Interactive mode enabled

php > echo 3 + 4;

以下のように3+4の結果である、7が表示されます。

pi@raspberrypi ~ $ php -a
Interactive mode enabled

php > echo 3 + 4;
7
php >

このようにプログラム(の断片)をすぐに実行できますので、ちょっとした動作確認できます。何か確認したい時はこのようにするとよいと思います。

phpコマンドを終了するには「exit」と入力するか、Controlキー + Dを押します(Control + DはLinuxでは入力の終了を意味します)。

これでPHPの動作確認はできましたが、実際にはPHPでソースコードを書いてそれを実行することになります。PHPのソースコードのファイル名は、拡張子が php とするのが一般的ですので、例えば test.php というファイル名でプログラムファイルを作成します。

それでは、動作確認用に「Hello, world!」と表示するPHPの以下のソースコードファイルを新規作成します。

<!--?php
    echo "Hello, world!";
?-->

ここで、新規にファイルを作成する手順について、デスクトップタイプとリモート接続タイプの機器構成それぞれについて説明します。

デスクトップタイプの場合、テキストエディタを起動すると新規ファイルが開いた状態になりますので、プログラムを入力します。入力後、保存メニューを選択、ファイル名を「test.php」として保存します。

リモート接続タイプの場合、Atomエディタを起動します。AtomエディタのPackagesメニュー → Ftp-Remote → Toggleを選択します。忘れてしまった場合は、第13回の記事を参照してください。

Raspberry Piに接続後、以下のように/home/pi/devフォルダを展開し、devフォルダを右クリックします。

AtomエディタFTP-REMOTE新規ファイル作成

右クリックするとメニューが表示されますので、「New File」を選択します。

Atomエディタ新規ファイル作成

新規ファイル名を聞かれますので、「/home/pi/dev/test.php」に設定してりターンキーを押します。

Atomエディタ新規ファイル名入力

新規ファイルに入力できるようになりますので、プログラムを入力します。

PHPのプログラムは <?phpで始まり、?>で終わります。この間にコードを書くことになります。PHPの各行は ; (セミコロン)で終わります。echoは引数を画面に表示する命令になります。

次のこのPHPのプログラムを実行します。ターミナルアプリを開いて、devフォルダに移動します(cd dev)。

作成したソースコードファイルの実行方法は2通りあります。

ひとつはphpコマンドを使う方法で、test.phpを実行したい場合は、

pi@raspberrypi ~/dev $ php test.php

と入力して実行します。それでは実際に試してみましょう。

pi@raspberrypi ~/dev $ php test.php
Hello, world!
pi@raspberrypi ~/dev $ 

Hello, world!と表示されましたか。何かエラーになったり表示されない場合はもう一度ソースコードを確認してみてください。

なお、ソースコードが正しくてもエラーになる報告をコメントでいただきました。エラーは以下のような内容です。

-bash: ./test.php: /usr/bin/php^M: 誤ったインタプリタです: そのようなファイルやディレクトリはありません

このエラーが発生した場合は、エディタの設定を確認してみてください。確認方法は以下の補足記事で説明しています。

第25回補足 「/usr/bin/php/^M: 誤ったインタプリタです」の対策

プログラムを実行するもうひとつの方法は、test.php自体を実行するというものです。test.php自体を実行する、というのはちょっとわかりづらいかもしれませんが、例えばディレクトリの内容を表示する「ls」コマンドは「ls」というファイルを実行しています。この「ls」と同様に「test.php」を直接実行しよう、というわけです。

test.phpを直接実行するには、以下の知識が必要になります。

  • 起動コマンドの設定
  • ファイルの実行権限
  • .と..
  • ユーザディレクトリの実行ファイルの実行方法

それではひとつずつ説明していきます。

Linuxでは先ほど作成したPHPなどのソースコードのファイル(スクリプトファイルとも呼びます)を実行する場合、どのコマンドで実行するか指定しておく必要があります。

test.php は php コマンドで実行しますので、ファイルの先頭に「phpコマンドで実行してください」って書いておく必要があります。この書き方は、先頭は「#!」ではじめ、その後にコマンドを書きます。Raspbianではphpは /usr/bin/ディレクトリにありますので、以下のように書くことになります。

#!/usr/bin/php

<!--?php
    echo "Hello, world!";
?-->

これでファイルの起動コマンドの設定が終わりました。なおこのような記述を追加しても php test.php というようにphpコマンドでも実行できます。

次にファイルの実行権限です。

かなり前の説明なので忘れてしまったかもしれませんが、ファイルのアクセス権について復習します。詳しくは第11回の説明をご確認いただくことにして、ここでは簡単に復習しておきます。

ファイルのアクセス権ですが、ls -l コマンドで確認することができます。このアクセス権は以下のような意味を持っていることを説明しました。

Access rights

それでは、先ほど作成した test.php のアクセス権を確認してみましょう。

pi@raspberrypi ~/dev $ ls -l test.php
-rw-r--r-- 1 pi pi   34  5月 12 08:12 test.php

ということで、このファイルは実行権限がないですよね。このファイルを直接実行するには実行権限をつけてしまえばOKです。

ファイルのアクセス権を設定するには、chmod (Change Mode)コマンドを使用します。chmodコマンドのパラメータはたくさんあるのでここでは最低限のものを説明します。

何か権限を追加する場合は「+」、削除する場合は「-」を指定し、その後に読み取り(r)、書き込み(w)、実行(x)などの種類を指定します。ここでは実行権限を追加しますので、バラメータは「+x」となります。test.phpに実行権限を追加するには以下のようにします。

pi@raspberrypi ~/dev $ chmod +x test.php

実行権限が追加されたか確認してみましょう。

pi@raspberrypi ~/dev $ ls -l test.php
-rwxr-xr-x 1 pi pi   34  5月 12 08:12 test.php

「x」がついて実行権限がつきましたよね。それでは「ls」コマンドのように実行してみましょう!

pi@raspberrypi ~/dev $ test.php
-bash: test.php: コマンドが見つかりません

ファイルの実行権限が付いているのに実行できないとはどういうことなんでしょうか。この原因を理解するには、コマンドの「パス」という概念を理解する必要があります。

今まで「ls」とコマンドを入力してディレクトリの内容を確認していましたが、lsコマンドは実際には /binディレクトリにあります。正確には「/bin/ls」と入力する必要がありますが、毎回「/bin/ls」と入力するのも面倒ですし、そもそもどのコマンドがどのディレクトリにあるかなんて覚えてられないですよね。

ということで、ユーザがコマンドを入力した時、どのディレクトリを探すか、というコマンドを検索するパスの設定がされています。ではRaspbianではどのディレクトリがコマンドのパスになっているのでしょうか。

このコマンドのパスは「$PATH」という変数に格納されていて、echoコマンドで内容を確認することができます。以下のように入力してみてください。

pi@raspberrypi ~/dev $ echo $PATH
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/local/games:/usr/games

こんな感じで表示されましたでしょうか。パスは複数設定されていて「:」(コロン)で区切られています。test.phpはどこにあるかというと、ホームディレクトリに作成したと思いますので、「/home/pi」ディレクトリにあります(pwdコマンドで確認できますよね)。でも/home/piディレクトリはこのパスのリストにはありませんよね。

ということで、test.phpに実行権限を設定しても、このtest.phpがコマンドパスにはないので「コマンドが見つかりません」といってきたわけです。

解決方法としては、/home/piをコマンドパスに追加する方法がありますが、これはLinuxでは避けるようになっています。理由としてはセキュリティの問題からこのようになっています。

例えば、悪意のあるユーザやウェブサイトが、ユーザのホームディレクトリに、すべてのファイルを削除する「ls」というコマンドを仕込んだとします。コマンドパスにユーザディレクトリがあり、それが優先されるようになっていると、ユーザがディレクトリの内容を確認しようとして「ls」と入力しただけで、先ほどのすべてのファイルを削除する「ls」が実行されてしまい、すべてのファイルが削除されてしまいます。このような状況を避けるためにユーザディレクトリはコマンドパスに含まれていませんので、ユーザディレクトリにある実行ファイルを実行するには、パスを指定する必要があります。

ホームディレクトリは「/home/pi」ですので、以下のように入力すればtest.phpが実行できます。

pi@raspberrypi ~/dev $ /home/pi/test.php

Hello, world!

でもこれだとパスの入力が面倒ですよね。そこでもうちょっとだけ入力を簡略化する方法があります。lsコマンドで「-a」オプションを指定すると不可視ファイルなども見ることができます。「ls -a」と入力してみましょう。

pi@raspberrypi ~ $ ls -l
.  ..  ._test.txt  test.php

左上の方に「.」と「..」というものがありますよね。設定によっては「./」や「../」と表示されているかもしれません。/はディレクトリであることを意味しています。

この「.」と「..」ですが、これは以下を意味しています。

記号 意味
. 現在のディレクトリ
.. 一つ上のディレクトリ
(親ディレクトリ)

「.」は今いるディレクトリを意味していますので、test.phpを実行するには、「今いるディレクトリのtest.php」という意味で、以下のように実行すればよいことになります。

pi@raspberrypi ~ $ ./test.php

Hello, world!

Linuxでは、一般的にユーザが作成したプログラムは、「./ファイル名」と入力して実行しますので、このシリーズでもこの慣例に従うことにします。

これでPHPのプログラムファイルを実行する準備ができました。

PHPからgpioコマンドを実行する

PHPでRaspberry PiのGPIOコマンドを制御する方法はいろいろありますが、このシリーズではgpioコマンドを実行することにします。

gpioはコマンドプロンプトから実行するシェルコマンドですが、PHPからシェルコマンドを呼び出す関数は幾つかあります。system()、exec()、shell_exec()です。あまり深く考えてはいませんが、このシリーズではshell_exec()関数を使用することにします。関数の引数にコマンドを指定すると、実行結果が返り値になります。例えば「ls -l」コマンドを実行してその結果を変数に入れるには以下のようにします。

$directory = shell_exec('ls -l');

なお、shell_execは、` (バッククウォート)で囲んでも同じ意味になります。上の例では

$directory = `ls -l`;

なおバッククウォートの入力ですが、Shiftキーを押しながら@キーを押します。(日本語キーボードの場合で、Mac/Win同じです)

Backquote mac

この仕組みを利用してgpioコマンドを実行することにします。例えば、GPIO21番を出力に指定するには以下のようにします。

`gpio -g mode 21 out`;

エラーの確認のために返り値をもらってもいいですが、特に何か処理するわけでもないのでこのように書いていきたいと思います。

PHPでLEDを制御する

これからプログラムを作成していきますが、テスト用のファイルなどいろいろと作成していきますので、開発用のディレクトリを作成しておきましょう。ディレクトリ名は好きなもので構いませんが、ここでは第13回で開発用にディレクトリを作成しましたので、「dev」ディレクトリを使用します。ディレクトリを作成していない場合は、mkdirコマンドで作成します。devディレクトリを作成する場合は、「mkdir dev」です。

それではdevディレクトリに移動しましょう。

pi@raspberrypi ~ $ cd dev
pi@raspberrypi ~/dev $ 

まず、GPIO21に接続しているLEDを1秒間点灯させてみます。gpioコマンドの使い方は確認済みですので、あまり難しいところはないと思います。以下、コードになります。

#!/usr/bin/php

<!--?php
    `gpio -g mode 21 out`;
    `gpio -g write 21 1`;
    sleep(1);
    `gpio -g write 21 0`;
    `gpio reset`;
?-->

gpioコマンド使い方の手順通りに設定しています。4行目でGPIO21を出力に設定、5行目でGPIO21をONにして、6行目で1秒待ちます。sleep()関数は、引数の数字分(単位は秒)そのまま待ちます。7行目でGOIO21をOFFにして、最後に8行目でgpioをリセットします。

これがLED点滅制御の基本となります。

PHPでスイッチ状態を読み取る

shell_exec()は実行結果が返り値になりますので、gpioコマンドでreadをしてそれを表示すればよいことになります。スイッチのテストをするために別のファイル、例えば switch.php などの名前でファイルを作成します。

#!/usr/bin/php

<!--?php
    `gpio -g mode 18 in`;
    echo `gpio -g read 18`;
    `gpio reset`;
?-->

スイッチの状態は5行目で gpio -g read 18 で読み取った値をecho関数で表示しています。

先ほどと同様に、switch.phpに実行権限を追加して./switch.phpで実行します。実行するとき、スイッチを放した状態にしたり、押した状態にしたりして値を確認してみてください。

これで、スイッチの状態を読み取る事ができるようになりました。

LEDの点灯とスイッチのテストプログラム

それでは、全LEDの点灯テストとスイッチの状態を読み取るテストプログラムを作成してみましょう。テスト内容ですが、最初にLEDを順番に1秒間ずつ点灯させていき、最後にスイッチを1秒ごとに3回読み取る、ということにしたいと思います。

最初は素直にプログラムを書いてみます。

#!/usr/bin/php

<!--?php
    // GPIOの初期化(LED)
    `gpio -g mode 21 out`;
    `gpio -g mode 20 out`;
    `gpio -g mode 16 out`;
    `gpio -g mode 12 out`;
    `gpio -g mode 25 out`;
    `gpio -g mode 24 out`;
    `gpio -g mode 23 out`;

    // GPIOの初期化(スイッチ)
    `gpio -g mode 18 in`;

    // LEDを順番に点灯させる
    // 晴れ用LED
    `gpio -g write 21 1`;
    sleep(1);
    `gpio -g write 21 0`;

    // 曇り用LED
    `gpio -g write 20 1`;
    sleep(1);
    `gpio -g write 20 0`;

    // 雨用LED
    `gpio -g write 16 1`;
    sleep(1);
    `gpio -g write 16 0`;

    // 雪用LED
    `gpio -g write 12 1`;
    sleep(1);
    `gpio -g write 12 0`;

    // 鉄道運行表示用 緑LED
    `gpio -g write 25 1`;
    sleep(1);
    `gpio -g write 25 0`;

    // 鉄道運行表示用 青LED
    `gpio -g write 24 1`;
    sleep(1);
    `gpio -g write 24 0`;

    // 鉄道運行表示用 赤LED
    `gpio -g write 23 1`;
    sleep(1);
    `gpio -g write 23 0`;

    // スイッチを1秒に感覚で3回読み取る
    for($i=0; $i<3; $i++) {
        echo `gpio -g read 18`;
        sleep(1);
    }

    // ------------------
    //   GPIOをリセット
    // ------------------
    `gpio unexportall`;
    `gpio reset`;

?-->

ほとんど解説するところはないと思います。//で始まる行はコメントになります。53行目からスイッチを1秒間隔で3回読み取るためにfor文を使用しています。PHPのfor文は他の言語と同様です。なお変数は$で始めます。

ただ、このプログラムはメンテが大変ですよね。LEDの位置が変わったり、数が変わったりすると結構いろいろ変更するところが出てきてしまいます。そこで、GPIO番号は最初に変数(LEDは配列)として定義して、その変数を使用して制御するように書き換えました。コードの後に説明が必要な行について解説してあります。

#!/usr/bin/php

<!--?php

// ------------------------------
//   GPIOの番号の定義(GPIO番号)
// ------------------------------
// LEDのGPIO番号を配列に定義
$gpio_led = [21, 20, 16, 12, 25, 24, 23];

// スイッチのGPIO番号を定義
$gpio_switch = 18; 

// -------------------
//   GPIOのモード設定
// -------------------
// LEDのGPIOを出力モードに設定
foreach($gpio_led as $gpio_num) {
    `gpio -g mode $gpio_num out`;
}

// スイッチのGPIOを入力モードに設定
`gpio -g mode $gpio_switch in`;

// -------------------------
//   LEDを配列の順番に点灯
// -------------------------
// 最初のLEDのGPIO番号を配列から取得して点灯させる
// 点灯後は1秒間そのままにする
$current_led = reset($gpio_led);
`gpio -g write $current_led 1`;
sleep(1);

// 配列から順次GPIO番号を取得点滅制御する
// 配列要素がなければ(next()からfalseが返ったら)終了
while( $next_led = next($gpio_led) ) {
    // 現在点灯しているLEDを消灯させる
    `gpio -g write $current_led 0`;
    // 次のLEDを点灯させる
    `gpio -g write $next_led 1`;
    // 点灯させたLEDのGPIO番号を現在のLED番号にする
    $current_led = $next_led;
    // 1秒間そのままにする
    sleep(1);
}

// 最後のLEDを消灯
`gpio -g write $current_led 0`;

// -----------------------------------
//   スイッチの状態を1秒ごとに3回表示
// -----------------------------------
for($i=0; $i<3; $i++) {
    echo `gpio -g read $gpio_switch`;
    sleep(1);
}

// ------------------
//   GPIOをリセット
// ------------------
`gpio unexportall`;
`gpio reset`;

?-->

9行目
LEDを接続しているGPIO番号を配列に入れておきます。GPIOの初期化や出力制御はこの配列を使って行います。順番が変わった場合やLEDを増やした場合はこの配列のみを編集すればいいようにしておきます。

12行目
スイッチは一つしかありませんので、変数に入れておくだけにします。

18〜20行目
foreach文の例です。$arrayは[1, 2, 3]の配列とします。

foreach($array as $i) {
   echo $i;
}

このようにすると、foreach文は配列のすべての要素に対して、それぞれの要素を変数$iに入れながら要素分繰り返します。この場合結果は1 2 3 となります。

31行目
ここから$gpio_led配列に入れた要素を一つずつ取り出していきます。reset()関数は引数の配列の最初の要素を取り出します。これを一度変数($current_led)に入れておきます。

37〜46行目
次のLEDのGPIO番号を$gpio_led配列から取り出しますが、要素がなくなると false が返ってきますので、それを利用して while文で繰り返します。while文の中は、現在点灯させているLEDを消灯、次のLEDを点灯させて1秒待つということを繰り返します。

49行目
while文では最後のLEDは点灯させたままになりますので、最後のLEDを消灯します。

これでPHPでのLEDの点滅制御、スイッチ状態の読み取りができるようになりました。

更新履歴

日付 内容
2015.12.23 新規投稿
2019.5.12 phpインタラクティブモードコマンドをphp7に変更
Atomエディタの説明追加
通知の設定
通知タイミング
guest
0 コメント
本文中にフィードバック
全てのコメントを見る
初心
初心
7 年 前

常識的なことかもしれませんが、詰まった点がありましたので参考までに報告させていただきます。

PHPファイルを実行する際にtest.phpファイルを
作成しますが。
Windows上のエディタ(サクラエディタ)で以下のコードを入力し、
ラズベリーパイのディレクトリに置いて実行しました。

#!/usr/bin/php

実行した際に以下のエラーが返ってきました。
「 -bash: ./test.php: /usr/bin/php^M: 誤ったインタ のようなファイルや ディレクトリはありません」

原因は改行コードがCR+LFになってたためで、LFに変換したら解消しました。
冒頭の通り、初心者が詰まった一例として報告させていただきます。

管理者
管理者
返信  初心
7 年 前

初心さま、
コメントどうもありがとうございます!

すみません、この点気づいていませんでした。Linuxの世界はUTF-8、改行コードはLFが基本ですので、エディタでその設定をする旨説明すべきでした。失礼いたしました…

お忙しい中、ご連絡いただきどうもありがとうございました。記事の内容を更新しておこうと思います。ただすみません、仕事の関係で週末の更新になってしまうと思います。

また何かお気付きの点がございましたら、お手数でなければご連絡いただければと思います!

目次