2020年10月26日 星期一

四、2 使用超連結(href)的方式控制單一輸出

在上一節的《四、1 直接使用GET(URI)的方式控制輸出已示範過如何使用瀏覽器的GET請求行(request Line)中的URI(Uniform Resource Identifier)去控制ESP32開發模組板上內建LED的亮滅不過這種方式對使用者來說不是很方便既然我們的ESP32模組板系統已經具有伺服器(Server)的功能了那就應該讓我們的系統工作在伺服器的環境底下才是在本小節中將示範使用html語法中超連結的方式讓使用者在連接上系統的伺服器之後會看到一個簡單的網頁並可使用其中的超連結文字去控制ESP32開發板上內建於GPIO2上LED的亮滅


範例程式功能與動作說明

1、以ESP32建立一無線WiFi  AP存取點,此AP存取點SSID名稱為  『ESP32_softAP01』,而且不使用密碼該AP存取點內建伺服器的IP位址為:[ 192.168.4.1 ]

2、當客戶端裝置連線上ESP32內建伺服器首頁時(也就是只鍵入IP網址[ 192.168.4.1 ]),系統會回應下面【圖四、2-1】的首頁網頁畫面給客戶端。畫面中標記1的[Soft AP模式] 使用超連結(href)控制單一輸出實習』是本實習的提示訊息內容而下方標記3的『[LED熄滅]』部分則會依操作狀況改變其文字部分的顏色是藍色在此是當使用者連上網頁首頁或者是點選標記2下面的熄滅這個超連結選項ESP32開發模組板回應的訊息。


圖四、2-1 系統首頁網頁/點選”熄滅”超連結選項畫面


3、當使用者點選上圖中標記2上方的點亮這個超連結選項系統會回應下面【圖四、2-2】的網頁畫面給客戶端畫面中標記2的『[LED點亮]』回應訊息一望即知其代表的意思此時其文字部分的顏色會變為紅色


圖四、2-2點選”點亮”超連結選項後畫面

電路圖

本實習所使用的電路和上小節一樣由於只控制一顆LED燈為了方便起見所以是採用內建於ESP32開發模組板上GPIO2的LED燈,所以就不需要再外接了!



程式列表與說明

以下是這個範例的完整程式列表內容:

  1. /*

  2. ESP32 soft AP 範例四 : 四、2  使用超連結(href)的方式控制單一輸出

  3. */

  4.  

  5. #include <WiFi.h>

  6. #include  "index.h"

  7. const char* ssid     = "ESP32_softAP01";

  8. const char* password = "12345678";

  9. const int LED_Pin=2;

  10. String  LED_Status="<font color=blue>LED 熄滅</font>";

  11.  

  12. WiFiServer server(80);

  13.  

  14. // 初始化設定程式開始:

  15. void setup()

  16. {

  17.     Serial.begin(115200);

  18.     pinMode(LED_Pin, OUTPUT);      // set the LED pin mode

  19.     delay(10);

  20.  

  21.     // We start by connecting to a WiFi network

  22.     WiFi.softAP(ssid);

  23.     Serial.println("Setting softAP ...");

  24.     Serial.println();

  25.     Serial.print("Your softAP is : ");

  26.     Serial.println(ssid);

  27.     Serial.println("IP address: ");

  28.     Serial.println(WiFi.softAPIP());

  29.     

  30.     server.begin();

  31. }   // 初始化設定程式結束.

  32.  

  33. // 主迴圈程式開始:

  34. void loop(){

  35.  

  36.   WiFiClient client = server.available();   // listen for incoming clients

  37.   // wait for a client (web browser) to connect

  38.   if (client)

  39.   {

  40.     Serial.println("\n[Client connected]");

  41.     while (client.connected())

  42.     {

  43.       // read line by line what the client (web browser) is requesting

  44.       if (client.available())

  45.       {

  46.         String line = client.readStringUntil('\r');

  47.         // Check to see if the client request was "GET /H" or "GET /L":

  48.         if (line.indexOf("GET /ON") >= 0) {

  49.           Serial.println(line);

  50.           digitalWrite(2, HIGH);               // GET /H turns the LED on

  51.           LED_Status="<font color=red>LED 點亮</font>";

  52.         }

  53.         if (line.indexOf("GET /OFF") >= 0) {

  54.           Serial.println(line);

  55.           digitalWrite(2, LOW);                // GET /L turns the LED off

  56.           LED_Status="<font color=blue>LED 熄滅</font>";

  57.         }

  58.         // wait for end of client's request, that is marked with an empty line

  59.         if (line.length() == 1 && line[0] == '\n')

  60.         {

  61.      String tmpString = MAIN_page;    // 取出html網頁回應程式

  62.      tmpString.replace("%LED_Status%", LED_Status ); // 帶入LED顯

  63. 示狀態至回應網頁

  64.      client.print( tmpString );

  65.           break;

  66.         }

  67.       }

  68.     }

  69.     delay(1); // give the web browser time to receive the data

  70.  

  71.     // close the connection:

  72.     client.stop();

  73.     Serial.println("[Client disonnected]");

  74.   }

  75. }   // 主迴圈程式結束.

  76.  

  77. // 以下部分為「index.h」標籤頁面的內容:

  78. const char MAIN_page[] PROGMEM = R"=====(

  79. HTTP/1.1 200 OK

  80. Content-Type:text/html

  81.  

  82. <!DOCTYPE html>

  83. <html>

  84.  <head>

  85.   <meta name='viewport' content='width=device-width, initial-scale=1.0'/>

  86.   <meta charset='utf-8'>

  87.   <style>

  88.     body {font-size:100%;} 

  89.     #main {display: table; margin: auto;  padding: 0 10px 0 10px; } 

  90.   </style>

  91.   <title>Soft AP模式-使用超連結(href)控制單一輸出實習</title>

  92.  </head>

  93.  

  94.  <body> 

  95.    <div id='main'>

  96.      <h2><center>[Soft AP模式] <br>

  97.          使用超連結(href)控制單一輸出實習<br><br>

  98.  

  99.          [%LED_Status%]<br>

  100.          請按這裡 <a href='ON'> 點亮 </a> LED.<br>

  101.          請按這裡 <a href='OFF'> 熄滅 </a> LED.<br>

  102.          </center>

  103.      </h3>

  104.    </div> 

  105.  </body>

  106. </html>

  107. )=====";

程式名稱:ESP32_softAP42_1Href.ino


為了配合回應給客客戶端的動態回應訊息在此新增了一個字串變數


10. String  LED_Status="<font  color=blue>LED 熄滅</font>";


這個字串變數使用了html語法中的font元素好讓『LED熄滅』這個回應訊息的文字顏色為藍色。至於初始化程式(setup())部分(15~31行)則是完全一樣

而在主迴圈(loop())的部分(35~76行)和上一個範例程式主要的不同之處有二,一是49~58行在判斷客戶端所傳來的URI是代表點亮LED的字串『"GET /ON"』﹖還是熄滅LED的『"GET /OFF"』﹖再依照測試結果控制LED的亮滅,並把訊息顯示在Arduino IDE的監控式窗;然後把兩種不同的回應訊息依測試結果放到字串變數「LED_Status」上,在此『LED點亮』這個回應訊息的文字顏色會設定成紅色,以便和LED熄滅時有所區別。


49.        if (line.indexOf("GET /ON") >= 0) {

50.          Serial.println(line);

51.          digitalWrite(2, HIGH);               // GET /H turns the LED on

52.          LED_Status="<font color=red>LED 點亮</font>";

53.        }

54.        if (line.indexOf("GET /OFF") >= 0) {

55.          Serial.println(line);

56.          digitalWrite(2, LOW);                // GET /L turns the LED off

57.          LED_Status="<font color=blue>LED 熄滅</font>";

58.        }


再來就是回傳到網頁部分的字串變數改為「LED_Status」:


62.    tmpString.replace("%LED_Status%", LED_Status ); // 帶入LED顯


後面剩下的78~107行是「index.h」這個頁面的內容,是一個完整的html網頁程式,在此一樣是使用C++語言中稱為原始字串 (Raw String) 的語法


const  char  MAIN_page[]  PROGMEM = R"=====( ……….. )=====";


這個html網頁程式的主體(即94~105行的<body>…</body>中間)部分如下


94. <body> 

95.   <div id='main'>

96.     <h2><center>[Soft AP模式] <br>

97.         使用超連結(href)控制單一輸出實習<br><br>

98.

99.         [%LED_Status%]<br>

100.         請按這裡 <a href='ON'> 點亮 </a> LED.<br>

101.         請按這裡 <a href='OFF'> 熄滅 </a> LED.<br>

102.         </center>

103.     </h2>

104.   </div> 

105. </body>


其中的96、97行會實現【圖四、2-1】畫面上方的『[Soft AP模式] 使用超連結(href)控制單一輸出實習』的提示訊息,而99行則會將前面62行所傳來的「LED_Status」字串變數帶入這個html網頁程式中,將最後的輸出結果回應給客戶端的使用者。至於100101兩行分別是兩個控制LED亮滅的超連結點


執行結果:

圖四、2-3 程式執行後客戶端瀏覽器不同超連結選項顯示畫面

在程式執行之後,開啟手機/平板/筆電的WiFi功能,在看到”ESP32_softAP01”這個AP存取點後點選它,接著啟動瀏覽器,並在網址輸入欄中輸入[ 192.168.4.1 ]這個IP位址並前往。如果連線正常將會看到【圖四、2-3】的畫面,左邊是系統內建伺服器的首頁畫面,同時也是點選「”熄滅”」這個超連結選項時顯示的網頁頁面,此時ESP32開發板上內建於GPIO2的LED是熄滅狀態而畫面右邊則是點選「”點亮”」這個超連結選項時,客戶端所看到的網頁畫面同時也會點亮ESP32開發板上的LED。


2020年10月22日 星期四

四、1 直接使用GET(URI)的方式控制輸出

 四、 ESP32存取點(AP)模式輸出控制及應用

四、1 直接使用GET(URI)的方式控制輸出

在前面的《三、存取點(AP)模式內建網路伺服器(WebServer)設計—GET請的解析》一節中曾說明過,在HTTP 的GET請求中最重要的部分是請求行(request Line) ,所謂的請求行主要包括兩個部份,其中一部分是所謂的URI(Uniform Resource Identifier),即請求的內容,也就是跟著網頁位址後面的訊息例如我們使用瀏覽器連上伺服器網頁首頁時送出的URI內容便是”/而當我們在網址輸入欄鍵入「192.168.4.1/test」這樣的網址資料時URI則是”/test”在本單元中我們就以這個特性示範如何去控制ESP32開發模組板上內建LED的亮滅


電路圖

圖四、1-1 不同類型ESP32開發模組板內建LED(GPIO2)位置


本實習所使用的LED燈為了方便起見所以是採用內建於ESP32開發模組板上GPIO2的LED燈,也就是說所並不需要再外接了!不過並不是所有的ESP32開發模組板上都內建有這顆接在GPIO2的LED燈,上面【圖四、1-1】中的兩款是本實習建議使用的ESP32開發模組板左邊是「WeMos D1 R32」板右邊是常見的「ESP32 DEVKIT」板這顆接在GPIO2的LED燈的位置已標示在圖片中使用者應該很容易找到才是


範例程式功能與動作說明

1、以ESP32建立一無線WiFi  AP存取點,此AP存取點SSID名稱為  『ESP32_softAP01』,而且不使用密碼。

2、此AP存取點內建伺服器的IP位址為:[ 192.168.4.1 ]

3、當客戶端裝置連線上ESP32內建伺服器首頁時(也就是只鍵入IP網址[ 192.168.4.1 ]),系統會回應下面【圖四、1-2】的畫面給客戶端。畫面上方的[Soft AP模式] 使用Get控制單一輸出實習』部分固定不變是用來提示本實習的內容而下方的『請輸入指令(On/Off)』部分則會依操作狀況改變在此是當使用者連上網頁首頁或者是輸入錯誤指令時ESP32開發模組板回應的訊息。


圖四、1-2 系統首頁畫面


4、如果輸入的是: [ 192.168.4.1 /on],則如【圖四、1-3】所示ESP32開發模組板上內建的LED會點亮,而且系統會回應『GPIO2:High --> LED點亮』的訊息給客戶端。


圖四、1-3 使用URI指令「/on」點亮ESP32開發板內建LED畫面


5、如果輸入的是: [ 192.168.0.100 /LedOff],則如【圖四、1-4】所示ESP32開發模組板上內建的LED會熄滅,而且系統會回應『GPIO2:Low --> LED熄滅』的訊息給客戶端。


圖四、1-4 使用URI指令「/off」熄滅ESP32開發板內建LED畫面


6、假如使用者輸入的內容不在前面幾種的條件裡面,那麼系統會則如【圖四、1-2】所示會回應『請輸入指令(On/Off)』這樣的訊息給客戶端,告訴使用者應該輸入什麼樣的指令在此On/Off指令碼中的英文字元大小寫都可以使用以方便使用者操作。


程式列表與說明

以下是這個範例的完整程式內容:

  1. /*

  2. ESP32 soft AP 範例三 : 四、1 直接使用GET(URI)的方式控制輸出

  3. */

  4.  

  5. #include <WiFi.h>

  6. #include  "index.h"

  7.  

  8. const char* ssid     = "ESP32_softAP01";

  9. const char* password = "12345678";

  10. const byte  indLED=2;

  11. String  echoBack="";

  12.  

  13. WiFiServer server(80);

  14.  

  15. // 初始化設定程式開始:

  16. void setup()

  17. {

  18.     Serial.begin(115200);

  19.     pinMode(indLED,OUTPUT);

  20.     delay(10);

  21.  

  22.     // We start by connecting to a WiFi network

  23.     WiFi.softAP(ssid);

  24.     Serial.println("Setting softAP ...");

  25.     Serial.println();

  26.     Serial.print("Your softAP is : ");

  27.     Serial.println(ssid);

  28.     Serial.print("IP address: ");

  29.     Serial.println(WiFi.softAPIP());

  30.     

  31.     server.begin();         // 啟動伺服器功能

  32. }   // 初始化設定程式結束.

  33.  

  34. // 主迴圈程式開始:

  35. void loop(){

  36.   

  37.   WiFiClient client = server.available();   // 監聽是否有客戶端到來

  38.   // wait for a client (web browser) to connect

  39.   if (client)

  40.   {

  41.     echoBack="請輸入指令(On/Off)";

  42.     Serial.println("\n[Client connected]");

  43.     while (client.connected())

  44.     {

  45.       // read line by line what the client (web browser) is requesting

  46.       if (client.available())

  47.       {

  48.         String line = client.readStringUntil('\r');

  49.         line.toUpperCase();

  50.         // Check to see if the client request was "GET /H" or "GET /L":

  51.         if (line.indexOf("GET /ON") >= 0) {

  52.           echoBack="GPIO2:High --> LED點亮";

  53.           Serial.println(line);

  54.           Serial.println(echoBack);

  55.           digitalWrite(indLED, HIGH);       // GET /H turns the LED on

  56.         }

  57.        if (line.indexOf("GET /OFF") >= 0) {

  58.           echoBack="GPIO2:Low --> LED熄滅";

  59.           Serial.println(line);

  60.           Serial.println(echoBack);

  61.           digitalWrite(indLED, LOW); // GET /L turns the LED off

  62.         }

  63.  

  64.         // 測試是否為客戶端請求結束空白行:

  65.         if (line.length() == 1 && line[0] == '\n')

  66.         {

  67.           String tmpString = MAIN_page;    // 取出html網頁回應程式

  68.           tmpString.replace("%echoBack%", echoBack ); //帶入回應字串變數 // 至回應網頁html程式中

  69.           client.print( tmpString );

  70.           break;

  71.       

  72.  }

  73.       }

  74.     }

  75.     delay(1); // give the web browser time to receive the data

  76.     // close the connection:

  77.     client.stop();

  78.     Serial.println("Client Disconnected.");

  79.   }

  80. }   // 主迴圈程式結束.

  81.  

  82. // 以下部分為「index.h」標籤頁面的內容:

  83. const char MAIN_page[] PROGMEM = R"=====(

  84. HTTP/1.1 200 OK

  85. Content-Type:text/html

  86.  

  87. <!DOCTYPE html>

  88. <html>

  89.  <head>

  90.  <meta name='viewport' content='width=device-width, initial-scale=1.0'/>

  91.  <meta charset='utf-8'>

  92.  <link rel="icon" href="data:,">

  93.  <style>

  94.    body {font-size:100%;} 

  95.    #main {display: table; margin: auto;  padding: 0 10px 0 10px; } 

  96.  </style>

  97.    <title>Soft AP模式-使用Get控制單一輸出實習</title>

  98.  </head>

  99.  

  100.  <body> 

  101.    <div id='main'>

  102.      <h2><center>[Soft AP模式] <br>

  103.          使用Get控制單一輸出實習<br><br>

  104.          %echoBack%

  105.          </center>

  106.      </h2>

  107.    </div> 

  108.  </body>

  109. </html>

  110. )=====";

程式名稱:ESP32_softAP4_getRUI.ino


本範例和上一個在初始化程式(setup())部分幾乎完全一樣只是多了19行的設定GPIO2為輸出的功能:


19.    pinMode(indLED,OUTPUT);


而在主迴圈(loop())的部分則多了第41行的預先設定回傳訊息用字串變數echoBack的內容為"請輸入指令(On/Off)"的程式:


41.     echoBack="請輸入指令(On/Off)";


當有客戶端裝置連線到伺服器,主迴圈程式剩下的部分便會開始接收與回應客戶端的請求訊息。43~74行程式是以while()迴圈的方式來持續的接收客戶端傳來的請求訊息,直到接收到代表請求結束的空行字串為止。48~49行程式會逐行讀取客戶端送來的訊息,並把該行字串訊息所有的英文字元轉換成大寫,以方便後面的判斷動作這樣也可以減少使用者因為輸入大小寫的問題而產生錯誤。


48.      String line = client.readStringUntil('\r'); // 逐行讀取客戶端送來的訊息

49.      line.toUpperCase();     // 將該行訊息所有的英文字元轉換成大寫


接著的51~56行程式在判斷客戶端所傳來的URI是否為代表點亮LED的字串『"GET /ON"』﹖如果是,則先將回應訊息字串變數「echoBack」的內容改為『" GPIO2:High --> LED點亮"』,並把訊息顯示在Arduino IDE的監控式窗,最後再點亮接在GPIO2的LED(55行)。


51.        if (line.indexOf("GET /ON") >= 0) {

52.          echoBack="GPIO2:High --> LED點亮";

53.          Serial.println(line);

54.          Serial.println(echoBack);

55.          digitalWrite(indLED, HIGH);         // GET /H turns the LED on

56.        }


再下來的57~62行程式應改很容看出是在測試所傳來的URI是否為代表熄滅LED的字串『"GET /OFF"』﹖如果是,則會將回應訊息字串變數「echoBack」的內容改為『" GPIO2:Low --> LED熄滅"』外,一樣並把訊息顯示在Arduino IDE的監控式窗之後,再讓接在GPIO2的LED熄滅。


57.       if (line.indexOf("GET /OFF") >= 0) {

58.          echoBack="GPIO2:Low --> LED熄滅";

59.          Serial.println(line);

60.          Serial.println(echoBack);

61.          digitalWrite(indLED, LOW);          // GET /L turns the LED off

62        }


當程式測試接收到代表結束請求傳送的空白行字串時(65~72行),便會把網頁首頁程式MAIN_page[]傳送到tmpString這個字串變數中同時也把回應訊息字串變數「echoBack」帶入到tmpString這個網頁html程式內在送出回應網頁程式至客戶端後以”break”的整令結束while()迴圈。


65.     if (line.length() == 1 && line[0] == '\n')  // 測試HTTP請求是否結束

66.     {

67.        String tmpString = MAIN_page;         // 取出html網頁回應程式

68.        tmpString.replace("%echoBack%", echoBack); // 帶入回應字串變數

69. 至回應網頁html程式中

70.        client.print( tmpString ); // 送出回應網頁程式至客戶端

71.        break;

72.      }


後面剩下的83~110行是「index.h」這個頁面的內容,是一個完整的html網頁程式,在此一樣是使用C++語言中稱為原始字串 (Raw String) 的語法


const  char  MAIN_page[]  PROGMEM = R"=====( ……….. )=====";


這個html網頁程式的主體(即99~107行的<body>…</body>中間)部分如下


99. <body> 

100.   <div id='main'>

101.     <h2><center>[Soft AP模式] <br>

102.         使用Get控制單一輸出實習<br><br>

103.         %echoBack%

104.         </center>

105.     </h2>

106   </div> 

107. </body>


其中的101、102行用來實現【圖四、1-2】畫面上方的『[Soft AP模式] 使用Get控制單一輸出實習』的提示訊息,而103行則會將前面68行所傳來的「echoBack」字串變數帶入程式中,將最後的測試及輸出結果回應給客戶端的使用者。


執行結果:

在程式執行之後開啟手機/平板/筆電的WiFi功能,在看到”ESP32_softAP01”這個AP存取點後點選它,接著啟動瀏覽器,並在網址輸入欄中輸入[ 192.168.4.1 ]這個IP位址並前往。如果連線正常將會看到【圖四、1-2】畫面也就是系統內建伺服器的首頁畫面

假如在網址輸入欄中輸入[ 192.168.4.1/on ] 的內容並前往如果連線正常將會看到【圖四、1-3】的畫面此時ESP32開發板上內建於GPIO2上的LED會點亮。當在瀏覽器輸入[ 192.168.4.1/off ]的內容時出現的是【圖四、1-4】的畫面也就是讓LED熄滅的狀態

如果按照我們前面所訂定的功能與規範使用者在手機瀏覽器網址輸入欄中輸入的是前三者之外的內容例如輸入[ 192.168.4.1/o ]也就是錯誤的RUI請求那麼系統應該會回應【圖四、1-2】畫面;但是如果輸入的是[ 192.168.4.1/onoff ] 或[ 192.168.4.1/offon ]會是什麼結果呢如果讀者有去實作本範例出來將會發現前者會點亮LED而後者則是令LED熄滅也就是說這個範例所能提供的控制動作並不是太精準當然我們可以把程式改良到很精確但是由於使用者操作起來也不是很方便除非只是想設計一個非常簡易的系統否則這種方式不建議採用在此只是想讓讀者能熟悉當ESP32定位在一個網頁伺服器時該如何檢測瀏覽器RUI的請求並給予正確的回應


三、使用Line Notify傳送照片之安全監控系統之二---低功耗篇

在前一個章節中 , 我們建構了一個標準照片擷取 、 傳送與儲存的按全監控裝置 , 不過假如我們使用的場域中有許多的地方都必須按裝這類的裝置時 , 例如在一個有許多門 、 窗的家庭或辦公室 , 由於我們的系統使用無線WiFi作為信號傳輸之用 , 所以信號的傳輸除非裝置離WiFi分享...