2018年10月6日土曜日

ESP32/arduino:LED調光_スライダーで明るさを調整 2

目的:
LED 調光を、SPIFFS を使用して HTML を別ファイルにする。

方法:
"ESP32/arduino : SPIFFS アップローダーを使用する "に従って HTML を別ファイルにする。
変数部分(LEDの明るさ :led_brightness) は、送信前に変数名の文字列を数値に置換して送信する。

2019/06/26追加
XHR を多重で発行すると通信がハングアップする事があるようなので、レスポンスが返るまで発行しない様に修正。
 → 本問題は、Arduino core for the ESP32 の Ver 1.0.2 の問題と思われる。
   Ver.1.0.3 以降では問題無い。


例: (スケッチ、HTML、レスポンス用HTML )
/*
 *    WiFi LED ON/OFF TEST
 *     PWM Control
 *     slider control2
 */
 
#include <WiFi.h>
#include "FS.h"
#include <SPIFFS.h>

#define DEBUG

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;
String resp_1;
byte led_brightness = 0 ;

void setup()
{
    Serial.begin(115200);
    SPIFFS.begin();          //SPIFFSを開始
    pinMode(2, OUTPUT);      // set the LED pin mode
    
    WiFi.config(ip, gateway, subnet, DNS);   // Set fixed IP address
    delay(10);

    // We start by connecting to a WiFi network -----------------------------
    Serial.println();
    Serial.print("Connecting to ");
    Serial.println(ssid);

    WiFi.begin(ssid, password);

    while (WiFi.status() != WL_CONNECTED) {
        delay(500);
        Serial.print(".");
    }

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

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

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

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

void loop(){
  WiFiClient client = server.available();     // listen for incoming clients
  int pos ;
  int val ;
  int xhr ;
  String cmd = "" ;
  String htmlwk = "" ;
  String respwk = "" ;
  
  if (client) {                                               // if you get a client,
    # ifdef DEBUG
        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
            Serial.println(line);
        #endif
        if ((pos= line.indexOf("GET /?slid")) != -1) {
          pos += 11 ;
          while((line.charAt(pos) >='0') & (line.charAt(pos) <='9')) { 
            cmd += line.charAt(pos++) ;
          }
          val = cmd.toInt() ;
          if (val>256) val = 255 ;
          led_brightness = (byte)val ;
          xhr=1;
          # ifdef DEBUG
              Serial.print("led_brightness : ");
              Serial.println(led_brightness) ;
          #endif
        }
        if ((pos= line.indexOf("GET /?led_v")) != -1) {
          pos += 12 ;
          while((line.charAt(pos) >='0') & (line.charAt(pos) <='9')) { 
            cmd += line.charAt(pos++) ;
          }
          val = cmd.toInt() ;
          if (val>256) val = 255 ;
          led_brightness = (byte)val ;
          # ifdef DEBUG
              Serial.print("led_brightness : ");
              Serial.println(led_brightness) ;
          #endif
        }
        if ((pos=line.indexOf("GET /?on")) != -1) {                 // Client request was "GET /?on" 
          led_brightness += 1 ;
          # ifdef DEBUG
              Serial.print("led_brightness : ");
              Serial.println(led_brightness);
          #endif
        }
        if ((pos=line.indexOf("GET /?off")) != -1) {                // Client request was "GET /?off"
          led_brightness = 0 ;
          # ifdef DEBUG
              Serial.print("led_brightness : ");
              Serial.println(led_brightness);
          #endif
        }
        ledcWrite(0, 8191*led_brightness/255) ;                     // set PWM value to channel#0

        if (line.length() == 1 && line[0] == '\r'){           // end of HTTP request
          if (xhr == 0) {
            htmlwk = html_1 ;
            htmlwk.replace("$led_brightness",String(led_brightness)) ;
            send_html(client,htmlwk) ;                             // send response to client
          } else {
            respwk = resp_1 ;
            respwk.replace("$led_brightness",String(led_brightness)) ;
            send_html(client,respwk) ;                             // send response to client
          }
          break;                                              // break while loop
        }
      }
    }
    delay(1);                                        // give the web browser time to receive the data
    // close the connection:
    client.stop();
    # ifdef DEBUG
        Serial.println("Client Disconnected.");
        Serial.println("--------------------------------------------------");
    #endif
  }
}

// ------------------------------------------------------------------

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
        Serial.println( " --- send html --- ");
    #endif
}


<!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='80' max='255' step='1' onchange='setval(this.value)' oninput='setval(this.value)' >
  </form> 

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

  </div>


<script>
var xhr_busy = 0 ;

function setval(ledval){
  if (xhr_busy == 0) {
    xhr_busy = 1 ;
    var xhr = new XMLHttpRequest();
    xhr.open('get', '?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_busy = 0 ;
        }
    }
    xhr.ontimeout = function(e) {
        xhr.abort() ;
        xhr_busy = 0 ;
    }
    xhr.send();
  }
}

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


<!DOCTYPE html><html lang='ja'>
  <head> <title>Color LED Controller</title></head>
  <html> <body>
    <output id='output1'>$led_brightness</output>
  </body> </html>

以下、変更点の覚書など。
--- < スケッチ > ---
 ( 行# 8,9 )
SPIFFS を使用するための include。

 ( 行# 23,24 )
HTML本体,レスポンス用HTML を格納する String 変数。

 ( 行# 30)
SPIFFS ファイルシステムのマウントを行う。

 ( 行# 55~62)
HTML本体を読み込み、String 変数に格納。

 ( 行# 64~71)
レスポンス用HTMLを読み込み、String 変数に格納。

 ( 行# 85,86 )
変数部分を値に置き換える為の ワーク用 HTML本体,レスポンス用HTML を格納する String 変数。

 ( 行# 144~146 )
 HTML本体を送信する。
 フラッシュメモリから読みだした HTML をワーク用の String 変数にコピーした後、変数部分(値を変えたい部分) を値に置き換え、送信する。

 ( 行# 148~150 )
レスポンス用 HTMLを送信する。
 フラッシュメモリから読みだした HTML をワーク用の String 変数にコピーした後、変数部分(値を変えたい部分) を値に置き換え、送信する。

 ( 行# 168~178 )
HTML送信用関数。

--- < HTML本体 > ---
 ( 行# 14,19 )
変数部分は、$付きの名前にした。

2019/06/26追加
 ( 行# 30,33,34,45,50 )
XHR 発行中を示すフラグ xhr_busy を準備。
XHR 発行中は '1' にして 多重で発行しない様にする。

--- < レスポンス用HTML > --- ( 行# 4 )
変数部分は、$付きの名前にした。

9 件のコメント:

  1. 過去のプログラムは動作確認ができて、内容も順番に確認してきました。
    このプログラムで悩んでます。
    アップロードできているはずです。Hard resetting via RTS pin...
    thlmのファイル名は、index.htmlとresp.htmlで良いですか?WiFi connected.
    +IPaddressの表示は、OK
    ブラウザー表示しません。接続エラーのメッセージも無。
    F5の更新で
    GET /favicon.ico HTTP/1.1
    Host: 192.168.0.123
    Connection: keep-alive
    Pragma: no-cache

    のメッセージの表示?
    お助けお願いしたいです。よろしくお願いします。
    ESP32をもう少し、勉強していきます。

    返信削除
    返信
    1. 参考にして頂き、ありがとうございます。
      HTML のファイル名は、test_pwm_slid.html と resp_slid.html になります。
      スケッチ本体の 56行目 と 65行目 でファイル名を指定しています。
      2つの HTML のファイルは "DATA" ディレクトリ の下に格納します。

      ファイル名 もしくは スケッチ本体の記述を変更して 試してください。
      よろしくお願いいたします。

      削除
    2. 早い回答ありがとうございます。返答がいただけるだけでも助かるのに・・・
      ファイル名の件、確認実行します。
      (現在までのプログラムはすべて動いているので大丈夫と思います。)

      よく、プログラムを見なければいけませんね!
      コメント記載か何かで、思い込んでいました。
      「まず、動かして!」「次は理解」の考えではどこかで詰まりますね!

      現在、「スマホからIPアドレス等のWiFi設定を行う」を勉強中です。
      全てが目からウロコです。
      passwordの間違いでなかなか動きませんでしたが、(1,l)お恥ずかしい!
      それで現在、何度かの再接続でAPモードに戻るを追加してみようと試行中です。
      俺のレベルで行けるかな?
      問題がも山の様に有りますが、一つづつ解決していきたいと思ってます。
      カベを乗り越えれない場合は。また御指南をお願いしたいです。

      削除
    3. 私に判る事であれば、答えたいと思います。(と言っても私も素人ですし、すぐには応答出来ない事もあるかと思いますが、、)
      問題等ありましたら、投稿ください。

      削除
  2. 温かいコメントありがとうございます。
    よろしくお願いします。
    「スマホからIPアドレス等のWiFi設定を行う」には、ほど遠く
    「 LED調光_WEBからと外付けのボリュームからを切り替えて調光」
    のお勉強中ですが、"GET /?pol"は、何の動作で?
    THLMも少しがじった程度で、scripの復習を行おうと思っています。

    返信削除
    返信
    1. local 時、定期的にgetval() を実行(109,128行目)し、XHR で GET /?pol を発行して led_brightness (ボリュームの値) を取得します。

      削除
  3. 返信ありがとうございます。
    XHRは、THMLのscriptですね!復習中です。
    「接続不具合(Arduino core for the ESP32 Ver 1.0.2)」でHTMLも書き換えました。

    ボリュームの習得は100msで割り込み。ローカル判断で演算。割り込み可。でわないですか?
    「get/polを発行してVR取得」????凡人には理解できるようにお願いします。
    あと、resp_slidのhtmlの勉強中です。が?、
    get/?pol の発行が多くてシリアルモニターの更新が早すぎて、
    確認が大変でしたのでお聞きした次第です。
    質問ばかりですみません。頑張って理解していますが自分の技量では?見捨てないでください。

    返信削除
  4. ボリュームの値の取得は理解されている通り、100mS毎の割込み(Ticker) で取得し、
    演算(0~255 の範囲に変換)し、変数(led_brightness) に保持します。
    ( Local 時は、ボリュームの値を 一度'0'にした後でないと更新しません )

    XHR(XMLHttpRequest)は ブラウザのボリューム値(brightbess value) を更新するために使用します。 (ESP32側からこの値のみを更新することができない為)

    ブラウザのボリューム値更新は、
     ブラウザ側:
      local 時、Javascript で定期的(100mS毎)に getval を実行。
      (setInterval(getval,100))
      getval は XHR で GET /?pol を発行し、レスポンスを待つ。
     ESP32:
      GET /?pol 受信により、レスポンス用HTML を使用して led_brightness の値を
      返す。
     ブラウザ側:
      レスポンスで帰ってきた値 (led_brightness) を brightness value (id:o1) に
      セットする。
    という流れになります。
    ( Local 時、ブラウザ側から100mS毎に GET /?pol が発行され、ボリューム値を応答する。)

    以上で判るでしょうか?
    XMLHttpRequest については、いろいろ解説されているサイトがありますので、調べてください。

    返信削除
    返信
    1. 忙しいと思われる中、くわしわかるように返答を頂いて助かります。
      出先なので、帰って良く理解します。
      鉄道模型をやられる方なのですね。
      自分も友達の手伝いをしているときに、この世界にのめり込みました。
      初めは、配線の手伝いだけでしたが、順番に制御などを。踏切などから作りだしました。PICで行っていたのが懐かしいです。
      ESP32は、PCやモバイルで制御や確認ができるので、PIC環境を離れESP32に移行中です。
      周りには、相談出来る人もおらず独学でやっています。
      なので、返答を頂けて本当に嬉しくおもっています。
      話しがそれますたが、これからもよろしくお願いします。
      現在、WiFiモード切替など勉強中です。

      削除