2020年11月29日日曜日

raspberry pi : WEB から LED ON/OFF ( php - C言語 間通信 )

 目的:

WEB画面から raspberry pi に 接続した LED の ON/OFF を行う。
php から Cプログラム へ メッセージキュー を使用したプロセス間通信を使用する。

環境/構成:

環境/構成は、 WEB から  LED ON/OFF と同じ。 

プロセス間通信:

プロセス間通信にはいろいろな種類があるが、ここではメッセージキューを使用した通信を行い、php から Cプログラムに LED ON/OFF の指示を行う。

php から のメッセージ送信

php からメッセージを送信するには、

  1. msg_get_queue (int $key , int $permissions)
    で、指定された key でメッセージキューの作成または接続を行い、IDを取得する。
    prtmissions : 0666 (default 値) 。
  2. 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 プログラムでのメッセージ受信

  1. msgget(key_t key, int msgflg);
    で、指定された key で メッセージキューの作成または接続を行い、IDを取得する。
    msgflg : 下位9bit は permission。作成を行う場合は IPC_CREAT を指定する。
  2. 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" を指定しているが、特に意味はない。
led_ctl.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
<?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) を行うが、このコードは こちらのコードを使用させて頂いた。
その他説明等、コード中のコメントを参照のこと。

ledctl.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
#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 コマンド で 確認できる )




0 件のコメント:

コメントを投稿