2019年2月14日木曜日

ESP32/arduino : WEBから制御可能なNゲージ用 PWM制御パワーパック_改

目的 :
 Nゲージ用 PWM制御パワーパックの改良/機能追加を行う。

機能:(前回作成分も含む)
 1. ボリュームによる 速度制御 (PWM 制御)
 2. 前進/後進 の 切り替え
 3. LED 常時点灯 (列車が動かない程度で PWM制御)
 4. ボタンによる 発車 / 停車 (自動加減速)
 5. 上記機能の WEB 操作 ( WEB操作/コントローラ 切り換え )
 6.USB-シリアル変換モジュール接続

回路:
 PWM パワーパック 回路図2

 前回 から 各種 スイッチ、LED を追加。
 USB-シリアル変換モジュール接続用のコネクタを追加。
 (以前作成した 開発ボード の 変換モジュール(自動書き込み回路付き) を接続することを想定)
 < 回路図 での メモ >
  12V-3.3V 変換回路で、 出力側 100uF (積層セラミック) は、リップルが大きかった為に実装した。
  ポリスイッチ (PSW1 0.65A) は、線路等でのショート対策。
  ショットキーダイオード(D7,D8) は、逆起電力対策。

 外観:
ケースは CDケースを加工して作成したので、シースルー。

WEB画面:

スケッチ、HTML等:

<!DOCTYPE html><html lang='ja'><head><meta charset='UTF-8'>
<style>
       #base          {font-size:16pt; text-align:center; width:600px; border:solid 4px #000000; background-color:#c0c0c0; }
       #radio_box     {font-size:12pt; float:left ;text-align:left; width:30%; margin: 0px 5%  20px 15% ; }
       #dir_box       {font-size:12pt; float:right;text-align:left; width:30%; margin: 0px 15% 20px  5% ; }
       #disp_box      {font-size:12pt; clear:both; text-align:left; margin: 0px 0px 20px  100px ; }
       #pwr_box       {font-size:12pt; text-align:left; margin: 0px 0px 0px 100px ; }
       #slid_box      {font-size:12pt; text-align:left; margin: 10px 0px 10px 50px ; }
       #sw_box        {font-size:12pt; text-align:center; margin: 10px 0% 10px 0% ; }
       output.disp    {margin-left:200px; font-size:14pt; } 
       input.radio    {margin-left:8px; width:30px;}
       input.dir      {margin-left:8px; width:30px;}
       input.value    {margin-left:100px; width:40px; font-size:12pt; }
       input.setbutton{margin-left:8px; width:40px;}
       input.slider   {margin-left:50px; width:400px;}
       input.button   {margin:0px 13%; width:20%;}
       </style>

<title>Power PWN Controller</title></head>

<body>
<div id="base">
  <p>運 行 管 理</p>
  <div id="radio_box">
    <form method="get">
      <input class="radio" type="radio" name="remote" id="rad_lo" value="local" $checked_lo onclick="disp_ctrl(this.value); submit(this.value)">制御盤<br>
      <input class="radio" type="radio" name="remote" id="rad_rm" value="remote" $checked_rm onclick="disp_ctrl(this.value); submit(this.value)">遠隔<br>
    </form>
  </div>
  <div id="dir_box">
    <form method="get">
      <input class="dir" type="radio" name="dir" id="dir_fw" value="foward" $checked_fw onclick="submit(this.value)" disabled >前進<br>
      <input class="dir" type="radio" name="dir" id="dir_bw" value="backward" $checked_bw onclick="submit(this.value)" disabled >後退<br>
    </form>
  </div>
  <div id="disp_box">
    <span> 現在出力 </span>
    <output class='disp' id='o1'> 
    $pwr 
    </output>
  </div>
  <div id="pwr_box">
    <form method="get">
      <span> 出力設定 (0-255)</span> 
      <input class='value'  type='text' name='pwr_v' value= $pwr id='pwr_v' disabled>
      <input class='setbutton' type='submit' name='set' id='set' value='SET' disabled>
    </form> 
  </div>
  <div id="slid_box">
    <form method='get'> 
      <input class='slider' type='range' name='pwr_s' id='pwr_s' value= $pwr min='0' max='255' step='1' disabled
        onchange='setval(this.value);' oninput='setval(this.value)'; onmouseup='submit(this.form)': ontouchend='submit(this.form)'>
    </form> 
  </div>
  <div id="sw_box">
    <form method='get'> 
        <input class='button' type='submit' name='ready' id='ready' value= '準備/解除'  disabled >
        <input class='button' type='button' name='start' id='start' value= '発車/停車'  onclick='sw_start()' disabled >
    </form> 
  </div>
</div>


<script>
var polling = null ;

function setval(pwr){
        if (polling == null) clearInterval(polling);
        pollint = null ;
 var xhr = new XMLHttpRequest();
        xhr.open("get", "?slid="+pwr );
        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('pwr_v').value  = parseInt(xhr.response.getElementById("output1").innerHTML) ;
            document.getElementById('o1').innerHTML = xhr.response.getElementById("output1").innerHTML;
          }
        }

        ntimeout = function(e) {
          xhr.abort() ;
        }
        xhr.send();
}


function getval(){
 var xhrget = new XMLHttpRequest();
        xhrget.open("get", "?pol" );
        xhrget.setRequestHeader('Cache-Control', 'no-cache');
        xhrget.setRequestHeader('If-Modified-Since', 'Thu, 01 Jun 1970 00:00:00 GMT');
        xhrget.responseType = 'document' ;

        xhrget.onreadystatechange = function() {
          if( (xhrget.readyState == 4) && (xhrget.status == 200) ) {
            document.getElementById('o1').innerHTML = xhrget.response.getElementById("output1").innerHTML;
            document.getElementById('pwr_s').value  = parseInt(xhrget.response.getElementById("output1").innerHTML);
            document.getElementById('pwr_v').value  = parseInt(xhrget.response.getElementById("output1").innerHTML);
          }
        }

        ntimeout = function(e) {
          xhrget.abort() ;
        }
        xhrget.send();
}

function sw_start() {
        var xhr_start = new XMLHttpRequest();
        xhr_start.open("get", "?start" );
        xhr_start.setRequestHeader('Cache-Control', 'no-cache');
        xhr_start.setRequestHeader('If-Modified-Since', 'Thu, 01 Jun 1970 00:00:00 GMT');
        xhr_start.responseType = 'document' ;
        
        xhr_start.onreadystatechange = function() {
          if( (xhr_start.readyState == 4) && (xhr_start.status == 200) ) {
            if (polling == null) polling = setInterval(getval,100) ;
          }
        }
        
        ntimeout = function(e) {
          xhr_start.abort() ;
        }
        xhr_start.send();

}
    

function disp_ctrl( radioid ) {
   if(radioid == 'remote') {
      document.getElementById('dir_fw').disabled = false;
      document.getElementById('dir_bw').disabled = false;
      document.getElementById('pwr_v').disabled = false;
      document.getElementById('set').disabled = false;
      document.getElementById('pwr_s').disabled = false;
      document.getElementById('ready').disabled = false;
      document.getElementById('start').disabled = false;
      if (polling == null) clearInterval(polling);
      pollint = null ;
   } else {
      document.getElementById('dir_fw').disabled = true;
      document.getElementById('dir_bw').disabled = true;
      document.getElementById('pwr_v').disabled = true;
      document.getElementById('set').disabled = true;
      document.getElementById('pwr_s').disabled = true;
      document.getElementById('ready').disabled = true;
      if (polling == null) polling = setInterval(getval,100) ;
   }

}


window.onload = function() {
    if(document.getElementById("rad_rm").checked) {
      document.getElementById('dir_fw').disabled = false;
      document.getElementById('dir_bw').disabled = false;
      document.getElementById('pwr_v').disabled = false;
      document.getElementById('set').disabled = false;
      document.getElementById('pwr_s').disabled = false;
      document.getElementById('ready').disabled = false;
      document.getElementById('start').disabled = false;
      if (polling == null) clearInterval(polling);
      pollint = null ;
    } else if(document.getElementById("rad_lo").checked) {
      document.getElementById('dir_fw').disabled = true;
      document.getElementById('dir_bw').disabled = true;
      document.getElementById('pwr_v').disabled = true;
      document.getElementById('set').disabled = true;
      document.getElementById('pwr_s').disabled = true;
      document.getElementById('ready').disabled = true;
      document.getElementById('start').disabled = true;
      polling = setInterval(getval,100) ;
    }
}

</script>

</body>
</html>

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

    <output id='output1'> $pwr</output>

  </body> </html>

/*
 *    WiFi POWER PACK
 *     PWM Control
 */
 
#include <WiFi.h>
#include "FS.h"
#include <SPIFFS.h>
#include <Ticker.h>

// #define DEBUG_WiFi
// #define DEBUG_Hard

// LAN (WiFI) 関連定義 ------------------------------------------
const char* ssid = "hogehoge";
const char* password = "hogehogepaswd";

IPAddress ip(192, 168, 1, 32);        // for fixed IP Address
IPAddress gateway(192,168, 1, 1);     //
IPAddress subnet(255, 255, 255, 0);   //
IPAddress DNS(192, 168, 1, 90);       //

WiFiServer server(80);

// グローバル変数定義 ---------------------------------------------------------
String   html_1 ;                     // HTML 格納用
String   resp_1 ;                     // XHR 応答用
bool     local      = true   ;        // local フラグ
bool     localen    = false  ;        // 制御盤操作許可
bool     dir_bkwd   = 0      ;        // 進行方向フラグ
byte     duty       = 0      ;        // PWM デューティ設定 (0~255)
int      vol_value  = 0      ;        // 制御盤ボリューム値 (0~4095)
int      vol_duty   = 0      ;        // ボリューム値の DUTY換算値
int      loopcnt    = 0      ;        // 定期割り込み回数カウント用
bool     ticker_val = false  ;        // 定期割り込みフラグ 
bool     pre_rdy    = false  ;        // 準備要求スイッチ状態保持用
bool     ready      = false  ;        // 準備要求フラグ ( LED 常時点灯要求)
bool     pre_strt   = false  ;        // 発車要求スイッチ状態保持用
bool     start      = false  ;        // 発車要求フラグ
bool     stop       = false  ;        // 停車要求フラグ
bool     running    = false  ;        // 走行中フラグ
bool     autorun    = false  ;        // 自動走行中フラグ
int      base       = 25     ;        // 準備時 (LED点灯時) の duty 値
int      target     = 145    ;        // 目標デューティ
int      rate       = 5      ;        // 増減速の割合 (1/10 S 当たりの 増加 duty値)

Ticker ticker;

// ------ Define for IO Port --------------------------------------------------
#define PWR_VOL   A7                  // ADC1 A7 (IO35)
#define PWR_OUT    2                  // Degital Output IO2
#define DIR_OUT    4                  // Degital Output IO4 (0:FWD,1:BWD)
#define SW_FWD    27                  // Degital Input  IO27
#define SW_BWD    14                  // Degital Input  IO14    
#define SW_READY  13                  // Degital Input  IO13
#define SW_START  16                  // Degital Input  IO16
#define LED_FWD   17                  // Degital Output IO17
#define LED_BWD   12                  // Degital Output IO12
#define LED_ST1   23                  // Degital Output IO23
#define LED_ST2   19                  // Degital Output IO19
#define LED_ST3   18                  // Degital Output IO18

// ----- Define for LEDC ------------------------------------------------------
#define PWM_FEQ   20000
#define DUTY_bw   8

// ----------------------------------------------------------------------------
void setup()
{
    int lpcnt = 0 ;
    
    pinMode(PWR_OUT,OUTPUT) ;         // Set pin mode for SPEED control
    pinMode(DIR_OUT,OUTPUT) ;         // Set pin mode for Dirction control
    pinMode(LED_FWD,OUTPUT) ;         // Set pin mode for Forward LED
    pinMode(LED_BWD,OUTPUT) ;         // Set pin mode for Backward LED
    pinMode(LED_ST1,OUTPUT) ;         // Set pin mode for status 1 LED
    pinMode(LED_ST2,OUTPUT) ;         // Set pin mode for status 2 LED
    pinMode(LED_ST3,OUTPUT) ;         // Set pin mode for status 3 LED

    // セットアップ中、LED を点灯させる
    digitalWrite(LED_FWD,1) ;
    digitalWrite(LED_BWD,1) ;
    digitalWrite(LED_ST1,1) ;
    digitalWrite(LED_ST2,1) ;
    digitalWrite(LED_ST3,1) ;
    
    Serial.begin(115200);              // シリアル開始
    SPIFFS.begin();                    // SPIFFSを開始


    WiFi.config(ip, gateway, subnet, DNS);   // Set fixed IP address
    delay(100);

    // We start by connecting to a WiFi network -----------------------------
    WiFi.begin(ssid, password);

    Serial.println();
    Serial.print("Connecting to ");
    Serial.println(ssid);

    // WiFi 5秒間 で接続できなければ、リスタートを行う。
    while (WiFi.status() != WL_CONNECTED) {
        delay(500);
        lpcnt+= 1 ;
        if (lpcnt>10) { ESP.restart(); }
        Serial.print(".");
    }

    Serial.println("");
    Serial.println("WiFi connected.");
    Serial.print("IP address: ");
    Serial.println(WiFi.localIP());
    
    server.begin();

    // HTMLファイルの読み込み ---------------------------------------
    File index1 = SPIFFS.open("/pwpk.html", "r");
    if(!index1)
       Serial.println("file open failed");
    else{
      html_1 = index1.readString();    //index.htmlをstringで読み込み
      index1.close();                  //ファイルを閉じる
    }

    // XHR 応答ファイルの読み込み ------------------------------------
    File index2 = SPIFFS.open("/pwpk_resp.html", "r");
    if(!index2)
       Serial.println("file open failed");
    else{
      resp_1 = index2.readString();    //index.htmlをstringで読み込み
      index2.close();                  //ファイルを閉じる
    }      

    // for PWM Control ---------------------------------------------------
    ledcSetup(0, PWM_FEQ, DUTY_bw);    // setup channel 0 with frequency 5000 Hz, 
                                       // 8 bit precission for LEDC timer
    ledcAttachPin(PWR_OUT,0);          // attach pin 2 to channel 0
    ledcWrite(0, 0);                   // initialize channel 0 to off     

    // LED を初期状態に設定。
    digitalWrite(LED_FWD,1) ;
    digitalWrite(LED_BWD,0) ;
    digitalWrite(LED_ST1,0) ;
    digitalWrite(LED_ST2,0) ;
    digitalWrite(LED_ST3,0) ;

    // for Ticker 
    ticker.attach_ms(100, execTicker) ; // 割り込み間隔と割り込み処理を設定。
}

void loop(){
  WiFiClient client = server.available();     // listen for incoming clients
  int pos ;
  int val = 0 ;
  int xhr ;
  String cmd = "" ;
  String htmlwk = "" ;
  String respwk = "" ;

  if (ticker_val) {      // 定期割込みフラグが true だったら、定期処理を実行。
     do_JOB() ;
  }

  if (client) {                               // if you get a client,
    # ifdef DEBUG_WiFi
        Serial.println("***** Client access start *****"); // print a message out the serial port
    #endif
    xhr = 0 ;
    while (client.connected()) {                     // loop while the client's connected
      if (client.available()) {                      // if there's bytes to read from the client,
        String line = client.readStringUntil('\n');  // Get Line data until '\n'
        # ifdef DEBUG_WiFi
            Serial.println(line);
        #endif
 // remote/local のラジオボタンが設定された時の処理 -----------------------------
        if ((pos= line.indexOf("GET /?remote")) != -1) {
          pos += 13 ;
          while(line.charAt(pos) !=' ') {     // remote か local かを取得する
            cmd += line.charAt(pos++) ;
          }
          if (cmd=="local") {                 // local の場合
            local = true ;                    //   local フラグを ON
          } else {                            // remote の場合
            local = false ;                   //   local フラグを OFF
            localen = false ;                 //   locavollen フラグを OFF
          }
        }
 // 進行方向が操作された場合の処理 -------------------------------------- 
        if ((pos= line.indexOf("GET /?dir")) != -1) {
   if (!local) {
     if (!running) {
              pos += 10 ;
              while(line.charAt(pos) !=' ') { 
                cmd += line.charAt(pos++) ;
              }
              if (cmd=="foward") {
                dir_bkwd = false ;
              } else {
                dir_bkwd = true ;
              }
              digitalWrite(LED_FWD,!dir_bkwd) ;
              digitalWrite(LED_BWD,dir_bkwd) ;
              digitalWrite(DIR_OUT,dir_bkwd) ;
     }
   }

        }
 // 出力値がセットされた場合の処理 -----------------------------------------
        if ((pos= line.indexOf("GET /?pwr_v")) != -1) {
   if (!local) {
            pos += 12 ;
            while((line.charAt(pos) >='0') & (line.charAt(pos) <='9')) { 
              cmd += line.charAt(pos++) ;
            }
            val = cmd.toInt() ;
            if (val>256) val = 255 ;
            vol_duty = val ;
   }
        }
 // スライダーが操作された場合の処理 ---------------------------------------------
        if ((pos= line.indexOf("GET /?slid")) != -1) {
   if (!local) {
            pos += 11 ;
            while((line.charAt(pos) >='0') & (line.charAt(pos) <='9')) { 
              cmd += line.charAt(pos++) ;
            }
            val = cmd.toInt() ;
            if (val>256) val = 255 ;
            vol_duty = val ;
   }
          xhr=1;
        }
 // 準備/解除がセットされた場合の処理 -----------------------------------------------
        if ((pos= line.indexOf("GET /?ready")) != -1) {
   if (!local) {
     if (ready == false) {
              ready = true ;
              digitalWrite(LED_ST1  ,1) ;
     }else{
       ready = false ;
              digitalWrite(LED_ST1  ,0) ;
     }
   }
        } 
 // 発車/停車がセットされた場合の処理 -----------------------------------------------
        if ((pos= line.indexOf("GET /?start")) != -1) {
   if (!local) {
     if (running == false) {
              start = true ;
       stop  = false ;
              digitalWrite(LED_ST2  ,1) ;
     } else {
              start = false ;
       stop  = true  ;
              digitalWrite(LED_ST2  ,0) ;
     }
   }
          xhr=1;
        } 
 // ポーリング要求があった場合の処理 ---------------------------------------------
        if ((pos= line.indexOf("GET /?pol")) != -1) {
          xhr=1;                                             // XHR 応答フラグ を ON
        }
 // HTTP リクエストの終わりを受信した場合の処理 -------------------------------
        if (line.length() == 1 && line[0] == '\r'){           // end of HTTP request
   // HTML 送信の場合 ---------------------------------------------------------
          if (xhr == 0) {
            htmlwk = html_1 ;
            htmlwk.replace("$pwr",String(vol_duty)) ;
            # ifdef DEBUG_WiFi
              Serial.print("radio_local = ");
              Serial.println(local);
              Serial.print("pwr  = ");
              Serial.println(vol_duty);
            #endif
     if (local) {
              htmlwk.replace("$checked_lo","checked") ;
              htmlwk.replace("$checked_rm","") ;
     } else {
              htmlwk.replace("$checked_lo","") ;
              htmlwk.replace("$checked_rm","checked") ;
     }
     if (dir_bkwd) {
              htmlwk.replace("$checked_fw","") ;
              htmlwk.replace("$checked_bw","checked") ;
     }else {
              htmlwk.replace("$checked_fw","checked") ;
              htmlwk.replace("$checked_bw","") ;
     }
            send_html(client,htmlwk) ;        // send response to client
   // XHR 応答を送信する場合 --------------------------------------------------------
          } else {
            respwk = resp_1 ;
            respwk.replace("$pwr",String(vol_duty)) ;
            send_html(client,respwk) ;        // send response to client
            # ifdef DEBUG_WiFi
                Serial.print("xhr value :"); Serial.println(vol_duty);
            #endif
          }
          break;                              // break while loop
        }
      }
    }
    delay(1);                                 // give the web browser time to receive the data
    // close the connection:
    client.stop();
    //
    # ifdef DEBUG_WiFi
        Serial.println("Client Disconnected.");
        Serial.println("--------------------------------------------------");
    #endif
  }
}

// - HTML 送信 --------------------------------------------------------
void send_html(WiFiClient client, String html ) {
    client.println("HTTP/1.1 200 OK");
    client.println("Content-type:text/html");
    client.println();

    client.print(html) ;

    # ifdef DEBUG_WiFi
        Serial.println( " --- send html --- ");
    #endif
}

// 定期割込み処理 ---------------------------------------------------------
void execTicker() {
  ticker_val = true ;  // フラグを True にするだけ。
}

// 定期処理 (定期割込みフラグが立っていた時の処理) ------------------------
void do_JOB() {

  // loxal の場合 制御盤情報を取得し、制御を行う。 ------------------------
  if (local == true) {
    // 出力用ボリューム値を取得 -------------------------------------------
    vol_value  = analogRead(PWR_VOL);

    // 制御盤の操作が許可されていた場合 -----------------------------------
    if (localen == true) {
      // 進行方向スイッチのチェック ---------------------------------------
      if (!running) {                         // 走行中は切り替え不可
        if (!digitalRead(SW_FWD))  {          // SW_FWD : Low Active
          dir_bkwd = 0 ;
        }
        if (!digitalRead(SW_BWD))  {          // SW_BWD : Low Activ
          dir_bkwd = 1 ;
        }
        ledcWrite(0, 0) ;                     // 切り替え前に PWM出力を停止 
        delay(10);                            // 
        digitalWrite(LED_FWD,!dir_bkwd) ;     // 前進用 LED 設定
        digitalWrite(LED_BWD,dir_bkwd) ;      // 後退用 LED 設定 
        digitalWrite(DIR_OUT,dir_bkwd) ;      // 進行方向用リレー 設定
        delay(10);                            // 
        ledcWrite(0, duty) ;                  // PWM出力を元に戻す。
      }

      // 準備/解除スイッチのチェック ---------------------------------------
      if (!digitalRead(SW_READY))  {          // SW_READY が押された時 (Low Active)
        if (pre_rdy == false ) {              // 前回割り込み時、SW が OFF
          if (ready == false) {               //   かつ LED常時点灯になっていない場合
            ready = true ;                    //  ON にする
            digitalWrite(LED_ST1  ,1) ;
   } else {                            // LED常時点灯中の場合
     ready = false ;                   //  OFF にする
            digitalWrite(LED_ST1  ,0) ;
   }
 }
        pre_rdy = true ;                      // 前回割り込み情報を更新
      } else {
        pre_rdy = false ;
      }

      // 発車/停車が押下された場合の処理 -----------------------------------------------
      if (!digitalRead(SW_START)) {           // SW_START が押された時 ( Low Active)
        if (pre_strt == false) {              // 前回割込み時、SW が OFF
          if (running == false) {             //   かつ、走行中でない場合
            start = true ;                    //  発車要求フラグを ON
            stop  = false ;                   //
            digitalWrite(LED_ST2  ,1) ;       //
          } else {                            // 走行中の場合
            start = false ;                   //  停車要求フラグを ON
            stop  = true  ;                   //
            digitalWrite(LED_ST2  ,0) ;
          }
 }
        pre_strt = true ;                     // 前回割り込み情報を更新
      } else {
 pre_strt = false ;
      }

      // PWM の デューティを設定 ----------------------------------------------
      if (autorun == false)                   // 自動走行でない場合
        vol_duty = ((vol_value*255)/4095) ;   //   ボリュームの値で 出力制御

    // Local だが、制御盤の操作が許可されていない場合 --------------------------
    } else  {
      vol_duty = 0 ;                          // 出力 OFF
      // ボリューム が 0 の場合
      if (vol_value == 0)                     // ボリュームが 0 に戻されたら
        localen = true ;                      // 制御盤の操作を許可する。 
    }
    // 割り込み処理の実行回数をカウント ---------------------------------------
    loopcnt++ ;
    // 実行回数が 10 回目の場合 (1秒毎) ---------------------------------------
  // remote の場合  ------------------------
  } else {
    // 準備/解除スイッチのチェック 押されたら Local にする --------------------
    if (!digitalRead(SW_READY)) {             // SW_START が押された時 ( Low Active)
      if (pre_rdy == false) {                 // 前回割込み時、SW が OFF
        local   = true ;                      // Local(制御盤操作可能) にする
 localen = false ;                     // 制御盤操作許可は OFF
      }
      pre_rdy = true ;                        // 前回割り込み情報を更新
    } else {
      pre_rdy = false ;
    }
  }

  // 発車 処理 (自動 加減速) REMOTE/LOCAL 共通処理
  if (start == true) {                        // 発車要求フラグ が 真の場合
    autorun = true ;                          //
    if (vol_duty < target)                    // 目標デューティまで、
      vol_duty = vol_duty + rate ;            // 出力を RATE 分加算
    // 加速終了 (定常走行)
    else {                                    // 目標デューティに達したら LED_ST3 点灯
      start = false ;
      digitalWrite(LED_ST3  ,1) ;
    }
  }

  // 停車 処理 (自動 加減速)
  if (stop  == true) {                        // 停車要求フラグが真の場合
    autorun = true ;                          //
    if (vol_duty > 0){                        // 停車するまで
      vol_duty = vol_duty - rate ;            // RATE 分を減算
      if (vol_duty < 0)
        vol_duty = 0 ;
    } else {
      stop  = false ;                         // 停車したら、
      autorun = false;                        // 自動走行中フラグを OFF
      localen = false;                        // 制御盤操作許可を OFF (ボリュームが 0 でない場合の為)
      digitalWrite(LED_ST3  ,0) ;             // LED_ST3 消灯
    }
  }

  // 走行中フラグを更新
  if (vol_duty > 0)
    running = true ;
  else
    running = false ;

  // 常時点灯処理
  if (ready == true)                          // 常時点灯時、BASE を加算
    duty = base + ((255-base)*vol_duty)/255 ;
  else
    duty = vol_duty ;

  // デバッグ用出力 --------------------------------------
  if (loopcnt ==1) {
    # ifdef DEBUG_Hard
      Serial.println("") ;
      Serial.print( "PWR_VOL :") ; Serial.println(vol_value) ;
      Serial.print( "vol_duty :") ; Serial.println(vol_duty) ;
      Serial.print( "duty     :") ; Serial.println(duty) ;
      Serial.print( "running  :") ; Serial.println(running) ;
      Serial.print( "autorun  :") ; Serial.println(autorun) ;
      Serial.print( "stop     :") ; Serial.println(stop ) ;
      Serial.print( "ready    :") ; Serial.println(ready) ;
      Serial.print( "start    :") ; Serial.println(start) ;
    #endif

    // 実行回数をリセット
    loopcnt = 0;
  }
  ledcWrite(0, duty) ;                       // PWM デューティを設定 
  ticker_val = false ;                       // 処理を実行したら、フラグを false にする。
}


以下、メモ
<HTML 本体>
  • 制御盤(local) と 遠隔(remote) の ラジオスイッチ:
    制御盤選択時は、WEBからのボタン,スライダー等は java script で グレーアウト
  • スライダー:
    スライダーは、XHR で 逐次値を送信し、レスポンスされた値を 現在出力、出力設定の値に表示。
  • 発車/停車 ボタン:
    type を button として XHR で 押されたことを通知。
    レスポンスがあったら、ポーリングを開始して 現在出力、出力設定の値、スライダーの値に表示。(ポーリングについては、以下に記述 )
  • ポーリング:
    制御盤(Local) 選択時、発車/停車ボタン押下時に開始。
    100mS 毎に XHR で出力値を要求。(100mS毎は多いか?)
    応答値を 現在出力、出力設定の値、スライダーの値に表示。
    スライダー等、値に表示する場合、応答値(文字列) を 数値に変換する必要がある。以下の様に、parseInt() を使用する。
    document.getElementById('pwr_s').value  = parseInt(xhrget.response.getElementById("output1").innerHTML);
  • 各要素の初期値は、java script の window.onload で設定。
  • 変数( $*** ) は、スケッチで 値(数値や文字列) に変換する。
 <XHR レスポンス>
  • 応答の値の変数($pwr) は スケッチで 値 に変換する。
 <スケッチ>
  • スケッチ内にコメントを記載。以下、主な仕様, 挙動等。
  • クライアント処理内では、フラグの設定等のみで、実際の操作は定期処理内で実施。
  • 定期処理(1回/100mS) で 制御盤のスイッチ等の状態リード, LED 点灯, 出力制御等を実行。
  • 遠隔(remote) 時、準備スイッチ押下で 制御盤(Local) に遷移させる。
    この為、WEB上の表示は 遠隔(remote)のまま(グレーアウトしていない)状態でも Local となる場合がある為、クライアントの処理内でも local でないことを条件に追加。
  • 進行方向の変更は、走行中でない時のみに限定。
  • 準備/解除 スイッチ で 列車が動かない程度に出力。(LED の常時点灯, トグル動作)
  • 発車/停車 スイッチ は、停車時に押下で 自動で加速して定常走行となる。
  • 定常走行 時に押下で 自動で減速して停車する。
  • 発車/停車時の 加速度(rate), 定常時出力(target) は 変数定義の初期値で設定。
  • ボリューム設定で走行中に 発車/停車 スイッチを押下した場合は 自動で停車。ボリュームを '0' にするまで、操作不可。

0 件のコメント:

コメントを投稿