2020年12月9日水曜日

raspberry pi : WEB から LED 調光 ( php - C言語 間通信 )

 目的:

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

環境/構成:

環境/構成は、 WEB から  LED ON/OFF と同じ。 
プロセス間通信については、WEB から  LED ON/OFF ( php - C言語 間通信 ) を参照。
PWM制御については、C言語で LED 調光 (PWM 制御) を参照。

phpファイル(WEB画面):

WEB画面は、ESP32/arduino:LED調光_スライダーで明るさを調整 と同じ画面。


以下に、phpファイル (HTML) を示す。

<?php
  $QKEY = 1111;
  $QUEUE = msg_get_queue($QKEY, 0666);

  session_start() ;
  if ($_SESSION['led_val'] == NULL ) $_SESSION['led_val'] = 0 ;
 
  if ($_GET['led_v'] != NULL) {
    $_SESSION['led_val'] = $_GET['led_v'] ;
    msg_send($QUEUE, "1111", "set_ledval " . $_SESSION['led_val'] . "\0" , FALSE) ;
    }

  if ($_GET['led_s'] != NULL) {
    $_SESSION['led_val'] = $_GET['led_v'] ;
    msg_send($QUEUE, "1111", "set_ledval " . $_SESSION['led_val'] . "\0" , FALSE) ;
    }

  if ($_GET['on'] == "ON") {
    $_SESSION['led_val'] = "255" ;
    msg_send($QUEUE, "1111", "led_on\0" , FALSE) ;
    }
  
  if ($_GET['off'] == "OFF") {
    $_SESSION['led_val'] = "0" ;
    msg_send($QUEUE, "1111", "led_off\0" , FALSE) ;
    }

  $led_brightness = $_SESSION['led_val'] ;
?>

<!DOCTYPE html><html lang='ja'><head><meta charset='UTF-8'>
<style>input.button  {margin:8px;width:100px;}
       input.button2 {margin-left:8px; width:40px;}
       input.text    {margin-left:8px; width:25px;}
       input.slid    {width:230px;}
       div   {font-size:16pt;text-align:center;width:250px;border:solid 4px #93ff93;}
       </style>
<title>Color LED Controller</title></head>

<body>
<div><p>LED ON/OFF</p>
  <form method='get' style='text-align:left' >
    <span style='padding-left:15pt; font-size:8pt ;text-align:left'> LED brightness (0-255)</span>
    <input class='text'  type='text' name='led_v' id='led_v' value=<?=$led_brightness?> >
    <input class='button2' type='submit' name='set' value='SET'>
  </form>

  <form name='slidform' method='get' style='text-align:left'>
    <input class='slid' type='range' name='led_s' value=<?=$led_brightness?> min='0' max='255' step='1' onchange='setval(this.value)' oninput='setval(this.value)' >
  </form> 

  <form method='get'>
    <input class='button' type='submit' name='on' value='ON'><input class='button' type='submit' name='off' value='OFF'><br>
  </form>

  </div>


<script>
function setval(ledval){
  var xhr = new XMLHttpRequest();
  xhr.open('get', 'pwm_resp.php?slid='+ledval );
  xhr.timeout = 1000 ;
  xhr.setRequestHeader('Cache-Control', 'no-cache');
  xhr.setRequestHeader('If-Modified-Since', 'Thu, 01 Jun 1970 00:00:00 GMT');
  xhr.responseType = 'document' ;

  xhr.onreadystatechange = function() {
      if( (xhr.readyState == 4) && (xhr.status == 200) ) {
          document.getElementById('led_v').value = xhr.response.getElementById('output1').innerHTML;
      }
  }
  xhr.ontimeout = function(e) {
      xhr.abort() ;
  }
  xhr.send();
}

</script>
</body>
</html>

<?php 
  $led_brightness = $_GET['slid'] ;
  $QKEY = 1111;
  $QUEUE = msg_get_queue($QKEY, 0666);

  msg_send($QUEUE, "1111", "set_slidval " . $led_brightness . "\0" , FALSE) ;
?>

<!DOCTYPE html><html lang='ja'>
  <head> <title>Color LED Controller</title></head>
  <body>

  <output id='output1'> <?=$led_brightness?> </output>

  </body> </html>

処理の流れ:

  • php 本体では、各 GET 要求を受けたとき、メッセージキューに LED 操作のコマンドを送信する。また、HTML 中の変数($led_brightness) に値を設定する。
  • HTML中の変数($led_brightness) は、php のショートタグ( <?=*** ?> ) を使用して値に置き換える。
  • スライダー操作時は、javascript で XHRを使用して 値を XHRレスポンス用 php に送信する。
  • XHR レスポンス用 php では、送信された値を受け、メッセージキューに LED 操作のコマンドを送信し、受信した値をそのままレスポンスとして返す。ここでは、HTML(テキスト) でレスポンスを返している。

各処理の内容については、上述の参照ページを参照。

Cプログラム(LED調光):

Cプログラムは、 WEB から  LED ON/OFF ( php - C言語 間通信 ) の LED ON, OFF を PWM制御 ( C言語で LED 調光 (PWM 制御) ) に置き換えたものとなる。
プログラムを以下に示す。
#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 ;                               // 入力位置
  char *cmd ;                                    // コマンド用バッファ
  char *prm1 ;                                   // パラメータ1用バッファ
  int  val = 0 ;                                 // 設定値
                                                 //
  // メッセージキューの作成,取得 -----------------------------------------------
  //   誰でも読み書き可能なキューを作成して、ID を取得する                    --
  errno = 0;                                     //
  if( (                                          // 
        msqid = msgget(QKEY, 0666 | IPC_CREAT)   // 0666:アクセス許可パラメータ
      ) == -1 ) {                                //
    perror("msgget failure");                    //   作成失敗時の処理
    exit(EXIT_FAILURE);                          //
  }                                              //
                                                 //
  // wiringpi の設定 -----------------------------------------------------------
  wiringPiSetupGpio() ;                          //
  pinMode (led_pin, PWM_OUTPUT) ;                //   LED 調光
  pwmSetMode(PWM_MODE_MS) ;                      // PWM モード設定
  pwmSetClock(40) ;                              // PWM 分周比設定(レンジ当たりのクロック数)
  pwmSetRange(255) ;                             // PWM 分解能設定(1周期当たりのレンジ数)
                                                 //
  // メッセージを受信して LED の ON/OFF を行う ---------------------------------
  while(1){
    errno = 0 ;                                  //
    if (!(msgrcv( msqid, &message, BUFFSIZE,     // メッセージの受信 
                 read_type, IPC_NOWAIT) == -1)   //
        )                                        //
    {                                            // メッセージ受信時の処理
      // メッセージをコマンドとパラメータに分割 -//
      cmd = strtok(message.data, " ") ;          // 最初の文字列(" "まで) をコマンド
      prm1 = strtok(NULL, " ") ;                 // 次の文字列を パラメータ1
      printf("command : %s %s\n",cmd,prm1);      // 表示
                                                 //
      if (strcmp(cmd,"led_on") == 0) {           //    LED ON
        pwmWrite(led_pin, 256) ;                 //    LED 調光値設定(ON : 256)
      }                                          //
      if (strcmp(cmd,"led_off") == 0) {          //    LED OFF
        pwmWrite(led_pin, 0  ) ;                 //    LED 調光値設定(OFF : 0)
      }                                          //
      if (strcmp(cmd,"set_ledval") == 0) {       //    LED 調光値設定
        val = atoi (prm1) ;                      //    パラメータ1 を数値に変換
        pwmWrite(led_pin, val) ;                 //    LED 調光値設定
      }                                          //
      if (strcmp(cmd,"set_slidval") == 0) {      //    LED スライダー値設定
        val = atoi (prm1) ;                      //    パラメータ1 を数値に変換
        pwmWrite(led_pin, val) ;                 //    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);                            //
                                                 //
}                                                //
                                                 //
                                                 //

メッセージキューに送信されたコマンドに従い、LED の値 を設定する。
本プログラムを sudo 付きで実行し、WEB から 上述のphp(html) を開いて 操作すると、スライダーに応じて LED の明るさが変わる。
端末から "exit(rtn)" を入力すると プログラムは停止する。

0 件のコメント:

コメントを投稿