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" を指定しているが、特に意味はない。
<?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 コマンド で 確認できる )




0 件のコメント:

コメントを投稿