2020年5月19日 星期二

六、4 存取點(AP)模式內建網頁伺服器(WebServer)設計—使用html語法


在上一個範例中當客戶端使用瀏覽器連上ESP8266內建的伺服器時我們只是簡單的回應了一段純粹的文字這樣的伺服器網頁離我們一般看到的確有些差距雖然說ESP8266只是一顆資源有限的單晶片微電腦不過要在它上面建立一個簡單的網頁還是可以的一般設計網頁程式時都是使用html這種語法在本章節中會示範如何針對不同的應用去設計一些對應的網頁伺服器程式

六、4-1以html語法回應客戶主網頁請求

本範例和上一個一樣也就是當手機瀏覽器連線到它時會回傳下面的訊息


"ESP8266 AP模式--網路伺服器1"


差別是我們改用網頁(html語法)的方式去實現它而已!


接下來請用記事本編輯下面這個網頁程式(或是從附錄的程式中去開啟程式名稱為ESP8266_Web_Page_html1.html)完成之後存檔成任意好記但是延伸檔名必須是「.html」的檔案,而且文字的編碼格式要選擇:[UTF-8],否則有些網頁瀏覽器會無法顯示其中的中文部分!這是一個簡單的html語法網頁程式,由於本書的重點在教導ESP8266程式的設計,有關網頁程式的部分只會簡單的說明,如果沒學過網頁程式設計的讀者請自行參考相關的網頁設計書籍。

 

<!DOCTYPE html>

<html>

  <head>

    <title>ESP8266的第一個html網頁程式</title>

  </head>

<body>

  <Center>  

    <h1><b>ESP8266 AP模式--網路伺服器1</b></h1>

  </center>

</body>

</html>  



網頁程式名稱ESP8266_Web_Page_html1.html


圖六、10 電腦瀏覽器開啟『ESP8266_Web_Page_html1.html』網頁程式畫面


當我們用瀏覽器去開啟它時,可以看到【圖六、10】的畫面,其中標記1是由其中3~5行的程式所產生的網頁標題(title)內容


  <head>

    <title>ESP8266的第一個html網頁程式</title>

  </head>


標記2則是我們要顯示的訊息


<body>

  <Center>  

    <h1><b>ESP8266 AP模式--網路伺服器1</b></h1>

  </center>

</body>


其中” <body>……………</body> ”這一對標籤中的內容便是整個網頁程式的主體部分在此只有一行而已而<h1>代表使用瀏覽器內建最大的字體


前面的ESP8266_Web_Page_html1.html』程式內容看起來有向右內縮及空白空間等等編輯語法,讓程式看起來比較容易理解跟美觀,當然也就比較好偵錯與維護;可是在網頁設計的html語言中那些都是不必要的,實際傳送出去的資料其實就是一連串的標籤指令與文字所組成的字串,程式真正送出的內容如下:


 “ <!DOCTYPE html><html><head><title>ESP8266的第一個html網頁程式</title></head><body>  <Center><h1><b>ESP8266 AP模式--網路伺服器1</b></h1> </center><body></html>” 


因此當我們要回應客戶的請求時只要在程式中建構同樣一段字串再傳送出去就可以了


範例程式功能與動作說明:

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

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

3、當有客戶端裝置連線上ESP8266時,會以html語法的格式回應『ESP8266 AP模式--網路伺服器1』這樣的訊息給客戶端(手機、平板或筆電上的瀏覽器),以便客戶端的瀏覽器可看見較完整的文字訊息。


程式說明與列表

接下來就讓我們看一下這個範例程式(程式名稱:softAP3_webServer22.ino),

這個範例程式和前一個基本上差不多,主要是多了20~32行這一段用以建構回傳字串的部分,在此是使用了下面這段在C++語言中所謂的原始字串 (Raw String) 語法,將整個網頁程式字串包在後面的左右括弧「R”=====(……..)=====”」中間”…”的部分,而左右括弧的兩邊一共用了5個等號(“=”),缺一個都不行!這種語法可以保持程式原來的樣貌,增加程式設計的方便性,而這點是Arduino IDE這個軟體所無法提供的。


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


程式在此是定義了一個名稱為「MAIN_page[]」的字元常數陣列,而「PROGMEM」的意思是這字元常數陣列最後會放在程式記憶體中,這樣就不會占用到ESP8266內部的RAM主記憶體,以免程式太大時造成記憶體溢位的問題!

最後再修改回應客戶請求的自訂函式如下:


void handleRoot(){

  String s=MAIN_page;

  server.send(200,"text/html",s);

}


而這裡只是把「server.send()」最後部分的發送訊息,由原始字串「"ESP8266 AP模式--網路伺服器1"」改成代表html網頁程式的字串”s”,此外還必須將第二個引數也就是回傳的訊息內容格式,改成代表網頁html語法的「"text/html"」,所以當客戶端的瀏覽器收到回應訊息後,會依照網頁程式的格式去處理它。


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



//   soft AP 範例5 : 以html網頁的方式回應客戶端的首頁連線請求

 

#include  <ESP8266WiFi.h> // 引入ESP8266專用的WiFi函式庫

#include  <ESP8266WebServer.h> // 新增的ES8266網頁伺服器函式庫

// #include  "index.h" // 引入網頁程式碼

 

String softSsid = "ESP_softAP01"; // 設定AP存取點的SSID名稱

const char* softPassword = "12345678"; // 設定AP存取點的密碼值

 

IPAddress local_IP(192,168,0,100); // 設定新的伺服器IP位址

IPAddress gateway(192,168,0,1); // 設定新的伺服器閘道位址

IPAddress subnet(255,255,255,0); // 設定新的伺服器網路遮罩位址

 

ESP8266WebServer  server(80); // 宣告一伺服器物件並設定通信埠號碼為網頁專用的80

 

WiFiClient  client;

 

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

// 將我們的HTML 網頁程式內容直接建構在program memory內:

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

<!DOCTYPE html>

<html>

  <head>

    <title>ESP8266的第一個html網頁程式</title>

  </head>

<body>

  <Center>  

    <h1><b>ESP8266 AP模式--網路伺服器1</b></h1>

  </center>

</body>

</html>  

)=====";

 

void setup() {

  Serial.begin(115200); // 初始化串列通訊埠

  Serial.println();

  Serial.print("Setting soft-AP with configuratin... ");

  WiFi.softAPConfig(local_IP,gateway,subnet); // 設定新的本的IP位址

  boolean result=WiFi.softAP(softSsid);

  if(result==true)

  {  // 初始化設定AP存取點成功

    Serial.println("soft-AP ready!");

    Serial.print("softAP = "); // 在Arduino IDE串列監控視窗中顯示目前

    Serial.println(softSsid);       // 顯示SSID名稱

    Serial.print("softIP = "); // 在Arduino IDE串列監控視窗中顯示目前使用的

    Serial.println(WiFi.softAPIP()); // 本地IP位址在此為[ 192.168.0.100 ]

    server.on("/",handleRoot); // 定義處理客戶首頁連結請求的事件對應函式

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

  }  

  else

    Serial.println("soft-AP failed");

}

 

void loop() {

  server.handleClient(); // 呼叫客戶端連線處理函式

}

// 處理首頁’/’請求的自訂函式

void handleRoot(){

  String s=MAIN_page;

  server.send(200,"text/html",s);

}

程式名稱softAP3_webServer22.ino


◎ 執行結果:

當我們用手機連上ESP8266的AP存取點,並開啟瀏覽器連結上「 192.168.0.100」這個IP位址後,便可看到【圖六、11】的網頁首頁畫面,看起來是不是跟前面的在電腦上出現的【圖六、10】畫面一樣?

圖六、11 手機瀏覽器開啟『ESP8266_Web_Page_html1.html』網頁程式畫面


這種利用C++語言中所謂的原始字串(Raw String)語法在Arduino IDE的環境下設計html網頁程式是個不錯的方法不過最後的程式碼是儲存在程式記憶體中(Flash ROM),這是無法隨意更改的因此只適合那些不會變動的網頁程式此外由於html網頁程式和Arduino程式的語法結構差異很大如果將兩種程式碼放在同一個程式中會不容易偵錯與維護所以把它們分開處理會比較好

由於Arduino IDE是一個以專案(Project)為導向的整合開發環境,本來就可以把許多不同的程式合併在同一個專案底下,這也就是為什麼每次我們開啟一個新的Arduino程式時,在電腦上看到的是新增一個資料夾,而不是單一個程式,只是這個資料夾裡面開始的時候只有一個檔案而已!

如果在Arduino IDE同一個專案中新增其它的程式時,可以像【圖六、12】所示以滑鼠點擊標記1的下拉箭頭後,會出現標記2的彈出視窗,接著點選標記3的『新增標籤』選項,這時Arduino IDE的下方如【圖六、13】所示會出現一行輸入新檔案名稱的欄位,在此我們將這個檔案的名稱定為『index.h』;雖然說html語法的網頁程式副檔名應該是”.html”,但是在這裡我們是將這個程式以引用(include)的方式加入原來的Arduino程式,所以必須使用”.h”作為程式的副檔名。在按下[確定]按鈕之後,Arduino IDE便會新增加了一個標題名稱為『index.h』的標籤頁(標記5),這時我們便可以在這裡撰寫及設計我們的html網頁程式,不必去考慮編輯程式時的相容問題。


圖六、12 在Arduino IDE中新增網頁程式index.h

圖六、13 Arduino IDE中輸入新增網頁程式index.h


在實作這個範例程式時,可以把前面提到的20~32行這一段程式直接剪下來貼在『index.h』的標籤頁上,然後將第3行的引入行註解(即”//”符號)去掉,改成下面的形式就可以了。在完成這個名為「index.h」程式的設計並儲存檔案之後,可以在這個程式的資料夾中看到,除了原來的Arduino「softAP3_webServer22.ino」程式之外,還多了一個名稱為「index.h」的程式【圖六、14】。


/ #include  "index.h" // 引入網頁程式碼


圖六、14 資料夾新增「index.h」程式


六、4-2 以html+CSS語法回應客戶端主網頁請求

一般在設計網頁程式時除了使用html語法之外還常會加上JavaScript和CSS(Cascading Style Sheets串接式樣表)等語法以增加網頁的美觀與功能在ESP8266的系統中這些語法同樣也可以用來設計我們的網頁程式接著我們就以前一個範例為樣本加上一點的CSS成分來示範一下為了和前面的範例區隔這次顯示的訊息文字如下

ESP8266 AP模式--網路伺服器2

在上一個範例的第28行中


    <h1><b>ESP8266 AP模式--網路伺服器1</b></h1>


我們已經將要顯示訊息的字體放到最大了(也就是<h1>)可是當我們實際用手機連線去觀看時(即前面的圖六、11】),會發現還是不怎麼大!這時我們就可以使用CSS語法來加強一下。

和前面一樣我們可以先用記事本來設計我們想要的網頁式樣與內容之後,再移植到ESP8266的程式上面,這樣會比較容易上手。接著就請先用記事本編輯完成下列網頁程式(或是開啟附錄程式:ESP8266_Web_Page_html2.html),存檔時不要忘記將檔案編碼格式設定為[UTF-8]格式:


 

<!DOCTYPE html>

<html>

    <head>

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

     <meta charset='utf-8'>

     <style>body {font-size:140%;} #main {display:table; margin:auto; 

        padding:0 10px 0 10px;} h4,{text-align:center;} 

.button {padding:10px 10px 10px 10px; width:100%; background- color:#4caf50; font-size:120%;}

</style>

     <title>ESP8266的第二個html網頁程式</title>

    </head>

    <body>

<center>

     <h4>ESP8266 AP模式--網路伺服器2</h4>

    </body>

</html>



網頁程式名稱ESP8266_Web_Page_html2.html


要將CSS語法與html文件連結的方法有好幾種在此使用的是在html文件的<head>......</head>標籤元素中嵌入CSS式樣表方式並用<style>…….</style>屬性設定要使用的CSS式樣表在上面的” ESP8266_Web_Page_html2.html”程式中6~10行便是這個<style>的區塊在這裡我們宣告了好幾個式樣表不過和本範例有關的主要有兩項分別是

body {font-size:140%}

h4,{text-align:center;}


前者會讓<body>.....</body>這個屬性的區域中也就是網頁程式主體內所使用到的字形變大140%後者則是令<h4>這種層級的文字會置中對齊經過這樣的設定之後不管是電腦上或是手機上的瀏覽器在開啟這個網頁頁面時會看到夠大的訊息在螢幕畫面上


圖六、15 電腦瀏覽器開啟『ESP8266_Web_Page_html2.html』網頁程式畫面


當用電腦瀏覽器開啟上述的網頁程式之後,可看到【圖六、15】的畫面,在網頁程式的第15行:

 

     <h4>ESP8266 AP模式--網路伺服器2</h4>


我們使用的是「<h4>」的字體,跟前面的「<h1>」來比可說是小很多「但是在瀏覽器的畫面上的效果卻是大很多!


範例程式功能與動作說明

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

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

3、當有客戶端裝置連線上ESP8266時,會以html+CSS語法的格式回應『ESP8266 AP模式--網路伺服器2』這樣的訊息給客戶端(手機、平板或筆電上的瀏覽器),以便客戶端的瀏覽器可看見較完整的文字訊息。


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



//   soft AP 範例6 : 以html+CSS的方式回應客戶端的首頁連線請求

 

#include  <ESP8266WiFi.h> // 引入ESP8266專用的WiFi函式庫

#include  "index.h" // 引入網頁程式碼

 

String softSsid = "ESP_softAP01"; // 設定AP存取點的SSID名稱

//String softSsid = "ESP"+String(ESP.getChipId());

const char* softPassword = "12345678"; // 設定AP存取點的密碼值

 

IPAddress local_IP(192,168,0,100); // 設定新的伺服器IP位址

IPAddress gateway(192,168,0,1); // 設定新的伺服器閘道位址

IPAddress subnet(255,255,255,0); // 設定新的伺服器網路遮罩位址

 

WiFiServer  server(80); // 宣告一伺服器物件並設定通信埠號碼為網頁專用的80

WiFiClient  client; // 宣告一客戶端物件

 

void setup() {

  Serial.begin(115200); // 初始化串列通訊埠

  Serial.println();

  Serial.print("Setting soft-AP with configuratin... ");

  WiFi.softAPConfig(local_IP,gateway,subnet); // 設定新的本的IP位址

  if(result==true)

  {  // 初始化設定AP存取點成功

    Serial.println("soft-AP ready!");

    Serial.print("softAP = "); // 在Arduino IDE串列監控視窗中顯示目前

    Serial.println(softSsid);       // 顯示SSID名稱

    Serial.print("softIP = "); // 在Arduino IDE串列監控視窗中顯示目前使用的

    Serial.println(WiFi.softAPIP()); // 本地IP位址在此為[ 192.168.0.100 ]

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

  }  

  else

    Serial.println("soft-AP failed");

}

 

void loop() {

  client=server.available(); // 測試是否有客戶端裝置連線

  if(!client) // 

    return; // 若無則結束迴圈

 

  String request="",requests=""; // 設定讀取客戶端資訊的暫存字串變數

  while(client.connected()) // 測試客戶端裝置是否還在連線

  {

      request=client.readStringUntil('\r'); // 讀取客戶端裝置傳來的一整行訊息資料

      requests+=request; // 將讀取資料合併成更大的字串

      if(request=="\n") // 測試是否已到傳送資料的最後一行

            break; // 中斷while迴圈

  }

  Serial.println("Request end!");

  Serial.print("Request = ");

  Serial.println(requests); // 將讀取到的所有資訊顯示在電腦的Arduino序列監控視窗

  // 

  client.flush(); // 清除客戶端傳送過來的所有資料

  String s=MAIN_page; // 取得網頁首頁的html檔案

  client.print(s); // 向客戶端裝置回傳網頁訊息

  delay(5);

}

// 以下為 “index.h”部分的程式內容

// 將我們的HTML 網頁程式內容直接建構在program memory內:

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

<!DOCTYPE html>

<html>

  <head>

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

    <meta charset='utf-8'>

    <style>body {font-size:140%;} #main {display:table; margin:auto; 

          padding:0 10px 0 10px;} h4,{text-align:center;} 

      .button {padding:10px 10px 10px 10px; width:100%; background-   color:#4caf50;  font-size:120%;}

    </style>

    <title>ESP8266的第二個html網頁程式</title>

  </head>

  <body>

      <h4>ESP8266 AP模式--網路伺服器2</h4>

  </body>

</html>

)=====";



程式名稱softAP3_webServer32.ino


程式說明

如果和前面幾個範例程式比較可以發現好像少用了一個函式庫(即“ESP8266WebServer.h”)在此除了示範CSS語法的使用之外也想讓讀者知道即使只用最基礎的『ESP8266WiF.h』函式庫,一樣可以達到建立網頁的目的,只是有些地方需要我們自己處理,不過這樣程式會更有彈性,這點會在後面的章節為各位示範。

在程式的14、15行我們分別宣告了一個網路伺服器和客戶端的物件:


WiFiServer  server(80); // 宣告一伺服器物件並設定通信埠號碼為網頁專用的80

WiFiClient  client; // 宣告一客戶端物件


只不過這個伺服器類別的名稱由之前的「ESP8266WebServer」換成🡪這個內建在『ESP8266WiF.h』函式庫的「WiFiServer」,整個程式初始化(setup())部分和前面幾個範例幾乎完全一模一樣,只是少了:


    server.on("/",handleRoot); // 定義處理客戶首頁連結請求的事件對應函式


這行專用以處理首頁連接請求的程式而已

在主迴圈(loop())中,36~38行程式利用server.available()」這個物件變數的方法去偵測是否有客戶端連線到本機的伺服器上:


  client=server.available();


如果沒有則會繼續等待直到有客戶端裝置出現為止

當有客戶端連線到本機之後,41~47行的程式利用while()迴圈的方式取得連線的客戶端裝置所傳送來的資訊,其中43行程式:


  request=client.readStringUntil('\r');


會以行為單位(即遇到結尾字元‘\r’)讀取客戶端裝置所傳送來的資訊,然後再將他們合併至”requests”這個字串以待後面使用

這個while()迴圈在兩種情形下會停止執行:

  1. 是客戶端裝置中斷連線。

  2. 已讀取到客戶端裝置所傳送來資訊的最後一行。


所以while()迴圈括弧內的測試條件:

client.connected();


就是執行第一項的測試而對客戶端裝置而言使用的瀏覽器發送資訊結束時會送出一段空的字串資料也就是說當伺服器測試到傳送過來的資料是一段空字串時(內容只有一個換行字元‘\n’)就應該停止接收動作這也就是4546行程式的功能


      if(request=="\n") // 測試是否已到傳送資料的最後一行

            break; // 中斷while迴圈


接著的程式會把這個”requests”這個字串也就客戶端裝置所傳送來資訊顯示在Arduino IDE串列監控視窗中並從另外的「index.h」中取得網頁首頁的html檔案內容,再以「client.print()」這個函式把檔案回傳給連線的客戶端裝置


  String s=MAIN_page; // 取得網頁首頁的html檔案

  client.print(s); // 向客戶端裝置回傳網頁訊息


最後面的59~72行程式是「index.h」的內容記,得要另外開啟一頁的標籤頁面來放這段程式,否則在編譯程式時會出現錯誤的訊息!


◎ 執行結果:

當使用者再次以手機連線並開啟瀏覽器時,可看到和【圖六、15】相同的畫面,這時螢幕中的訊息大小應該會很適中,而電腦上的Arduino IDE串列監控視窗之內容則如【圖六、16】所示。


圖六、16 html+CSS程式啟動後Arduino IDE串列監控視窗之內容


在【圖六、16】中用紅線圈起來的標記1部分:


GET  /  HTTP/1.1


就是前面提到的瀏覽器所發出的首頁請求行(request Line)其中包括請求的方式「GET」,及請求的內容:「/」,而後面的「HTTP/1.1」則是代表目前所使用的HTTP通訊協定版本為1.1版。

而標記2的部分:

GET  /favicon.ico  HTTP/1.1


則是瀏覽器在連線請求成功後發出的第二項請求也就是要求回傳一般會顯示在瀏覽器URL輸入列左邊用來代表該網頁特徵的小圖示基本上這次的請求對我們設計程式不會有太大影響可是常會被誤以為客戶端裝置又發出了第二次的請求(因為又有一個「GET」出現),以致造成錯誤的解讀。

如果不希望瀏覽器發出第二項的請求可以在程式的6465中間插入一行html的程式


   <link  rel='icon'  href='data:,\'>  


更新後的部分如下:

     <meta charset='utf-8'>

     <link rel='icon' href='data:,\'>  

<style>body {font-size:140%;} #main {display:table; margin:auto; 


這樣就可以一勞永逸避免這個情形再出現了


沒有留言:

張貼留言