目的:
WEB画面から raspberry pi に 接続した LED の ON/OFF を行う。
php から Cプログラム へ メッセージキュー を使用したプロセス間通信を使用する。
環境/構成:
環境/構成は、
WEB から LED ON/OFF
と同じ。
プロセス間通信:
プロセス間通信にはいろいろな種類があるが、ここではメッセージキューを使用した通信を行い、php から Cプログラムに LED ON/OFF の指示を行う。
php から のメッセージ送信
php からメッセージを送信するには、
- msg_get_queue (int $key , int $permissions)
で、指定された key でメッセージキューの作成または接続を行い、IDを取得する。
prtmissions
:
0666 (default 値) 。
- msg_send(SysvMessageQueue $queue , int $message_type ,
string|int|float|bool $message , bool $serialize )
で、メッセージの送信を行う。
queue
:
1. で取得した ID
message_type
:
メッセージのタイプ (0 より大きな任意の数値)
message
:
送信するメッセージの本体
serialize
:
ここでは FALSE を指定。(default は TRUE)
c プログラムでのメッセージ受信
- msgget(key_t key, int msgflg);
で、指定された key で メッセージキューの作成または接続を行い、IDを取得する。
msgflg
:
下位9bit は permission。作成を行う場合は IPC_CREAT を指定する。
- msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,int msgflg);
で、メッセージの受信を行う。
msgid
:
1. で取得した ID
msgp
:
メッセージの構造体へのポインタ
msgsz
:
メッセージ構造体の中の データの最大バイト数
msgtyp
:
メッセージのタイプ。0 を指定した場合は キューの最初のメッセージが読み込まれる。
msgflg
:
メッセージフラグ。IPC_NOWAIT を指定した場合、キューにメッセイー時が無い場合に直ちに帰る。(errno には ENOMSG が設定)
phpファイル:
以下に、phpファイル を示す。
ここでは、key に "1111" を指定する。後述の Cプログラムの key と同じにする。
"ON", "OFF" の ボタンが押下された時、led_on, led_off の文字列をメッセージ送信する。
message_type は "1111" を指定しているが、特に意味はない。
<?php
$QKEY = 1111;
$QUEUE = msg_get_queue($QKEY, 0666);
if ($_GET['on'] == "ON")
msg_send($QUEUE, "1111", "led_on\0" , FALSE) ;
if ($_GET['off'] == "OFF")
msg_send($QUEUE, "1111", "led_off\0" , FALSE) ;
?>
<!DOCTYPE html><html lang='ja'>
<head><meta charset='UTF-8'>
<style>
input {margin:8px;width:100px;}
div {font-size:16pt;text-align:center;width:250px;border:solid 4px #93ff93;}
</style>
<title> Color LED Controller </title>
</head>
<body>
<div>
<p>LED CONTROL</p>
<form method='get'>
<input type='submit' name='on' value='ON' />
<input type='submit' name='off' value='OFF' />
</form>
</div>
</body>
</html>
Cプログラム:
Cプログラムでは、php からの メッセージを受信し、メッセージに応じて LED の 点灯/消灯を行う。
メッセージキューの key は php と 同じ "1111" を使用する。
メッセージ受信時の msgtyp (read_type) は '0' を指定し、メッセージタイプに関わらず、先頭から受信する。
while 文で メッセージ受信をループ処理するが、同時にキーボードを監視し、"exit(rtn)"をタイプしたらプログラムを終了するようにする。キーボード入力を メッセージ受信と並行して行うためにキーイベントの取得(kbhit) を行うが、このコードは こちらのコードを使用させて頂いた。
その他説明等、コード中のコメントを参照のこと。
#include <wiringPi.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <termios.h>
#include <fcntl.h>
// *****************************************************************************
// * 定義等 *
// *****************************************************************************
// メッセージキュー 用 定義 ----------------------------------------------------
#define BUFFSIZE 256 // バッファサイズ
#define QKEY (key_t)1111 // メッセージキューの key
//
struct msgbuf{ // メッセージの構造体
long int type; // type は long int であること。
char data[BUFFSIZE]; //
}; //
//
// LED ON/OFF 用 定義 ----------------------------------------------------------
#define led_pin 12 // LED を接続する GPIO #
//
// *****************************************************************************
// * キーイベントの取得 *
// *****************************************************************************
//
int kbhit(void) { //
struct termios oldt, newt; //
int ch; //
int oldf; //
//
tcgetattr(STDIN_FILENO, &oldt); //
newt = oldt; //
newt.c_lflag &= ~(ICANON | ECHO); //
tcsetattr(STDIN_FILENO, TCSANOW, &newt); //
oldf = fcntl(STDIN_FILENO, F_GETFL, 0); //
fcntl(STDIN_FILENO, F_SETFL, oldf | O_NONBLOCK); //
//
ch = getchar(); //
//
tcsetattr(STDIN_FILENO, TCSANOW, &oldt); //
fcntl(STDIN_FILENO, F_SETFL, oldf); //
//
if (ch != EOF) { //
ungetc(ch, stdin); //
return 1; //
} //
//
return 0; //
} //
//
// *****************************************************************************
// * メイン 関数 *
// *****************************************************************************
int main() { //
int msqid; // メッセージキュー ID
struct msgbuf message; // メッセージ
long int read_type = 0 ; // リードするメッセージタイプ
char c ; // 1文字入力用
char buf[128] ; // キーボード入力用のバッファ
int p_buf = 0 ; // 入力位置
//
// メッセージキューの作成,取得 -----------------------------------------------
// 誰でも読み書き可能なキューを作成して、ID を取得する --
errno = 0; //
if( ( //
msqid = msgget(QKEY, 0666 | IPC_CREAT) // 0666:アクセス許可パラメータ
) == -1 ) { //
perror("msgget failure"); // 作成失敗時の処理
exit(EXIT_FAILURE); //
} //
//
// wiringpi の設定 -----------------------------------------------------------
wiringPiSetupGpio() ; //
pinMode (led_pin, OUTPUT) ; //
//
// メッセージを受信して LED の ON/OFF を行う ---------------------------------
while(1){
errno = 0 ; //
if (!(msgrcv( msqid, &message, BUFFSIZE, // メッセージの受信
read_type, IPC_NOWAIT) == -1) //
) //
{ // メッセージ受信時の処理
fprintf(stdout,"received message:\t%s\n",message.data);
if (strcmp(message.data,"led_on") == 0) { //
digitalWrite (led_pin, HIGH) ; // LED 点灯
} //
if (strcmp(message.data,"led_off") == 0) { //
digitalWrite (led_pin, LOW) ; // LED 消灯
} //
}else{ // 受信失敗時
if (errno != ENOMSG) { // メッセージ無し以外は終了
printf("msgrcv : errno = %x\n",errno) ; //
break ; //
} //
} //
// キーイベントを取得し、入力文字列が "exit" ならループを終了する。----------
if (kbhit()) { //
c = getchar() ; // 入力された文字を表示
printf("%c",c) ; //
if ( c == '\n' ) { // リターン入力時
if ( strcmp(buf,"exit") == 0 ) { // "exit" を入力ならループ終了
break ; //
} else { // "exit" でなければ、先頭に戻る
p_buf = 0 ; //
buf[p_buf] = '\0' ; //
} //
} else { // リターンでなければ、
if (p_buf == 255) p_buf = 0 ; // バッファサイズを超える時は先頭へ
buf[p_buf] = c ; // バッファに入力文字を追加
buf[p_buf + 1] = '\0' ; //
p_buf++ ; // 入力位置を +1
} //
} //
} //
//
// メッセージキューを削除して終了 ----------------------------------------------
errno = 0; //
if(msgctl(msqid, IPC_RMID, NULL) == -1){ //
perror("msgctl failure"); //
exit(EXIT_FAILURE); //
} //
//
exit(EXIT_SUCCESS); //
//
} //
//
//
コンパイルは、
gcc -o ledctl ledctl.c -lwiringPi
実行:
raspberry pi で ledctl を実行
./ledctl
し、ブラウザから、
http://"raspberry pi の アドレス"/"html のディレクトリ"/led_ctl.php
にアクセスする。(led_ctl.php は 上で格納した php ファイル)
以下の様な画面が表示される。
ON ボタン押下 で LED ON,
OFF ボタン押下で LED OFF
する。
Cプログラムの終了は、"exit(rtn)" の入力で終了する。
尚、ctrl-C で終了した場合等、メッセージキューが残ったままになる。
コマンドラインがら、
ipcs
で メッセージキュー の状態が確認できる。
メッセージキューの削除は、
sudo ipcrm -q [msqid]
で行う。
( msqid は、ipcs コマンド で 確認できる )