在ESP8266的SPIFFS檔案系統存放網頁檔案(三):Wi-Fi無線網路燈光控制與調光器

本單元的程式修改自《超圖解物聯網IoT實作入門》第13章「使用ESP-01模組開發Arduino物聯網」單元,程式架構不變,只是把原本引用自CDN網路的jQuery程式和CSS樣式,改成存入ESP8266控制板的SPIFFS記憶體區域。

使用ESP-01模組開發Arduino物聯網

由於ESP8266的快閃記憶體空間有限(以4MB為例,可分配給SPIFFS使用的大小為3MB),網頁的資源(如:HTML, JavaScript和CSS)存入ESP8266之前,最好先壓縮。網頁的GIF, JPEG和PNG影像檔本身已經過最佳化處理,所以不必再壓縮。

使用GZip壓縮網頁資源:節省檔案空間與網路頻寬

GZip是Linux/UNIX系統常見的壓縮格式(副檔名以.gz結尾),相當於Windows系統上的Zip壓縮格式。GZip和Zip的主要差別是,Zip可以壓縮資料夾和多個檔案,GZip只能壓縮單一檔案。因此,在Linux/Unix上,壓縮目錄或多個檔案之前,需要先透過tar工具程式把目錄或多個檔案封裝(打包)成單一的.tar檔,再使用gzip工具壓縮。

gzip壓縮前後,檔案大小比較。

Windows系統的免費7-Zip工具有支援GZip格式壓縮和解壓縮。Mac OS X系統可直接雙按.gz檔將它解壓縮,或者在終端機視窗中,輸入”gunzip -d 壓縮檔路徑和檔名“命令,也能將它解壓縮,解壓縮之後,原本的.gz壓縮檔會被刪除。

為了節省網路頻寬,所有瀏覽器都支援.gz壓縮格式(以微軟的IE為例,從古早的4.0版就支援了)。不過,網頁設計師並不需要自行以gzip壓縮網頁相關檔案,因為網站伺服器軟體(如:Apache, Nginx, IIS,…等)都能自動壓縮內容。您正在閱讀的這篇文章,也是經過網站伺服器自動壓縮之後才傳送到您的瀏覽器。

當瀏覽器發出連結網站請求時,會透過HTTP檔頭的Accept-Encoding欄位,告訴網站伺服器它所接受的編碼類型:

HTTP檔頭的Accept-Encoding欄位

網站伺服器收到上面的請求,得知用戶端支援gzip壓縮,就能先以gzip壓縮內容再回應用戶。

上傳jQuery和CSS樣式與影像檔到SPIFFS區域

ESP8266的網站伺服器程式無法即時壓縮內容,因此我們必須事先自行壓縮檔案,再上傳到ESP8266。底下是本單元的data資料夾內容(請按此連結下載本單元的原始檔):

data資料夾內容

修改書本範例網頁的HTML,把原本引用CDN的CSS和jQuery檔案路徑,改成直接引用本機檔案的路徑。以jQuery UI為例,原本的路徑是:

<script src="https://code.jquery.com/ui/1.11.4/jquery-ui.min.js"></script>

請改成(資源檔名不要用.gz結尾):

<script src="js/jquery-ui.min.js"></script>

開啟ESP01.ino檔案之後,執行主功能表的「工具→ESP8266 Sketch Data Upload」(ESP8266草稿碼資料上傳)指令,上傳網頁資源。

相較於上一篇貼文中「處理檔案讀取」的handleFileRead()函式,本單元的函式需要在請求的資源路徑後面加入“.gz”結尾:

資源路徑後面加上.gz結尾

可傳回.gz檔的handleFileRead()函式:

bool handleFileRead(String path){
  Serial.println("handleFileRead: " + path);
  if(path.endsWith("/")) path += "index.htm";
  String contentType = getContentType(path);
  String pathWithGz = path + ".gz";   // 路徑結尾加上".gz"

  // 確認請求路徑或者.gz結尾的資源存在
  if( SPIFFS.exists(path) || SPIFFS.exists(pathWithGz) ){
    if(SPIFFS.exists(pathWithGz)) {
      path += ".gz";
}

    File file = SPIFFS.open(path, "r");
    server.streamFile(file, contentType);
file.close();

    return true;
  }
  return false;
}

控制網頁的jQuery程式說明,請參閱書本13-8頁。

編譯並上傳主程式之後,使用瀏覽器開啟“jarvis.local”,即可看到和書本13-8頁相同的網頁和操作模式。

延伸閱讀

Posts created 467

17 thoughts on “在ESP8266的SPIFFS檔案系統存放網頁檔案(三):Wi-Fi無線網路燈光控制與調光器

  1. 老師:

    檔案上傳到SPIFFS後會消失嗎?

    另外,上傳網頁檔案後再燒程式碼進ESP,會顯示錯誤
    我就拔掉插頭重插,就可以了,這樣做對嗎?

    最後,如果對老師的書中內容有疑問,可以直接在這裡發問嗎?

  2. 老師您好:

    網頁上傳到SPIFFS後
    如果要ESP8266傳送資料來改變網頁內容該怎麼處理?可否指點一下,謝謝。

    1. 跟上文一樣,程式分成用戶端和伺服器端兩部份。伺服器端程式如同12-31頁所示,透過server.send()方法送出HTML或文字資料給前端;前端網頁可採用jQuery程式庫對伺服器端程式請求資料,再動態拼湊成網頁。

      thanks,
      jeffrey

  3. 不好意思請問一下 在ESP8266的SPIFFS檔案系統存放網頁檔案這邊使用esp8266 那我手邊只有arduino yun板也能做到類似這篇得實驗嗎

  4. Hello Mr.Cubie,
    I’ve tried to establish my own webserver using your methods,and I found out that it worked pretty well on phones,while when visited by firefox, I got a bunch of LmacRxBlk:1 errors on the serial output. And I tried to analyse it with fiddler and I found that all the response of requests of images were 404.
    I guess that is because the brouser is requesting too frequently even when the 8266 is sending stream files.
    I am wondering if you can help me out .

    Thanks

    1. 其实你可以直接用简体中文留言 🙂

      如同你的推测,造成 LmacRxBlk:1 错误的原因是浏览器的连线超出ESP8266的负荷。

      ESP8266只能同时应付5个连线请求,然而,浏览器在读取网页时,会同时向后台发起不同资源(.html, .css, .js, .png,…)的请求,导致ESP8266崩溃。

      你可以尝试把CSS和JavaScript嵌入HTML,也把所有图片转成base64编码字元(这是免费的线上图像base64转换工具),直接嵌入HTML。如此一来,每次连线时,都只会有一个连线请求。

      或者,请尝试改用ESPAsyncWebServer,我尚未尝试过,不过,根据此项目网页的描述,它能应付同时多个连线请求,而且运行速度飞快。

      thanks,
      jeffrey

  5. 非常感谢!诚然如您所说,目前我能在github上找到的解决方案就只有这两种。我已经下载了那个github的async web server的arduino库,正在学习使用。
    Thanks

  6. 您好,我已经使用成功esp async web server 这个库,并且非常建议您尝试一下,连接稳定,速度非常快!

    Regards.

  7. 老師您好:
    想請問一下 把網頁上傳到SPIFF後 要怎麼對網頁進行控制
    我用的板子是NodeMCU 程式是用ESP8266WebServer資料庫
    我的網頁是控制開關 在按下開後要輸出一個高電位的電壓這樣
    ON
    要怎麼對這行進行控制

  8. 您好,以這個範例來做改變,當我增加一組開關時就無法動作了,請問這樣是哪裡的問題呢??

  9. 老師您好,對於網路並不是很了解,有些問題想請教
    sever.on()方法是傳入一個路徑的字元陣列,以及函式指標(?)的Handler function名稱
    在根據Handler內寫的sever.send()或者server.streamFile()方法,在用戶端請求的時候回傳網頁資源給用戶,這樣理解對嗎?
    這樣的話根據\”在ESP8266的SPIFFS檔案系統存放網頁檔案(一)\”的程式,在54、55行都有在on()的時候呼叫streamFile()對吧? 但是本篇文章卻只有.onNotFound()的時候,有去呼叫handleFileRead(),裡面才有去呼叫streamFile(), 我的問題是一般用戶端請求時,伺服器是如何知道他有哪些網頁資源的呢?

    1. 伺服器並不知道有哪些路徑可用,它是收到用戶端的請求之後,再去比對指定路徑的資源是否存在。

      thanks,
      jeffrey

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *

Related Posts

Begin typing your search term above and press enter to search. Press ESC to cancel.

Back To Top