2022年11月25日 星期五

MicroPython 學習筆記 : ESP8266/ESP32 網路存取測試 (二)

本篇繼續 MicroPython 網路存取之網頁伺服器測試, 我五年前直接使用 socket 模組測試過網頁伺服器, 這回則改用陳會安老師大作 "超簡單Python/MicroPython物聯網應用" 這本書中提供的 xtools.py 與 ESP8266WebServer.py 等函式庫重測, 程式碼會更精簡, 我把這當作是睽違五年後的 MicroPython 複習, 看看能否順便將之前想做的物聯網專案一併做完.   



5. 用 ESP8266WebServer 模組建立網頁伺服器 :

ESP8266WebServer.py 是一個極輕量的網頁伺服器模組, 可用來在 ESP8266/ESP32 開發板上建立網頁伺服器, 這比直接用 socket 實作簡單方便得多, 且支援網頁模板用法, 但目前僅支援客戶端用用 GET 方法提出請求. 此模組最新版本可在 GitHub 下載 (8 KB) :


但是我使用最新版去測試時會出現如下的 TypeError :

Traceback (most recent call last):
  File "<stdin>", line 24, in <module>
  File "ESP8266WebServer.py", line 60, in handleClient
  File "ESP8266WebServer.py", line 215, in handle
TypeError: function takes 2 positional arguments but 5 were given

應該是新版的 handlers 函式介面的參數增加所致, 故以下測試使用 "超簡單Python/MicroPython物聯網應用" 這本書範例程式所附的舊版模組, 我將其放在 GitHub (7 KB) :


直接用 Socket 建立網頁伺服器的原始做法參考以前的文章 : 


下面先來測試可回應 'Hello World!' 的網頁伺服器. 

首先來檢視 ESP8266WebServer 模組的內容 :

MicroPython v1.19.1 on 2022-06-18; ESP module with ESP8266

Type "help()" for more information.
>>> import ESP8266WebServer  
>>> dir(ESP8266WebServer)
['__class__', '__name__', 'close', 'machine', 'network', 'socket', 'uselect', 'os', 'poller', 'server', 'handlers', 'notFoundHandler', 'docPath', 'tplData', 'mimeTypes', 'begin', 'handleClient', 'handle', '__sendPage', 'err', 'ok', '__fileExist', 'onPath', 'onNotFound', 'setDocPath', 'setTplData']
>>> help(ESP8266WebServer)
object <module 'ESP8266WebServer'> is of type module
  handle -- <function handle at 0x3ffeff30>
  mimeTypes -- {'.png': 'image/png', '.jpg': 'image/jpg', '.css': 'text/css'}
  close -- <function close at 0x3ffefdf0>
  os -- <module 'uos'>
  __name__ -- ESP8266WebServer
  socket -- <module 'lwip'>
  ok -- <function ok at 0x3ffeff10>
  network -- <module 'network'>
  onPath -- <function onPath at 0x3ffeff40>
  onNotFound -- <function onNotFound at 0x3ffeff50>
  __fileExist -- <function __fileExist at 0x3ffeff20>
  err -- <function err at 0x3ffeff00>
  notFoundHandler -- None
  __sendPage -- <function __sendPage at 0x3ffefef0>
  docPath -- /
  begin -- <function begin at 0x3ffefec0>
  uselect -- <module 'uselect'>
  handlers -- {}
  tplData -- {}
  setDocPath -- <function setDocPath at 0x3ffeff60>
  setTplData -- <function setTplData at 0x3fff00b0>
  handleClient -- <function handleClient at 0x3ffefee0>
  server -- <socket state=0 timeout=-1 incoming=0 off=0>
  machine -- <module 'umachine'>
  poller -- <poll>

常用函式如下表 : 


 ESP8266WebServer 函式 說明
 begin(port) 指定 port=80 啟動網頁伺服器
 ok(socket, code, mime, html) 以 code='200', mime='text/html' 回應 html 字串
 onPath(path, handler) 指定請求路徑 path 時之處理函式 handler
 setDocPath(path) 指定存放 HTML 文件之路徑 path
 setTplData(data) 指定要傳給模板檔案 index.p.html 的字典變數 data
 handleClient() 在無限迴圈中呼叫此函式以監聽是否有客戶端請求出現


使用 ESP8266WebServer 建立網頁伺服器首先要匯入 xtools, config, 以及 ESP8266WebServer, 然後呼叫 xtools.connect_wifi_led() 連線 WiFi 基地台 :

>>> import xtools   
>>> import config   
>>> ip=xtools.connect_wifi_led(config.SSID, config.PASSWORD)   
>>> print("ip : ", ip)    
network config: ('192.168.43.54', '255.255.255.0', '192.168.43.1', '192.168.43.1')
ip :  192.168.43.54

這個 ip 就是伺服器的網址. 接著用長字串定義要回應的網頁內容變數 html :

>>> html="""  
<html>  
<head> 
   <meta charset="utf-8">
   <meta name="viewport" content="width=device-width,initial-scale=1">
</head>
<body>
   <h1>Hello World!</h1>
</body>
</html>"""

接著定義一個伺服器根目錄請求處理函式 handleRoot() : 

>>> def handleRoot(socket, args):    
    global html    
    ESP8266WebServer.ok(socket, "200", "text/html", html)     

此函式要傳入兩個參數 socket 與 args, 其中 socket 為模組內建立的 Socket 物件, 而 args 則是 GET 請求所傳遞的參數字典, 例如若請求網址為 /?para1=a&para2=b 則傳入之 args 為 {'para1': 'a', 'para2': 'b'}, 此例因為是請求根目錄沒有傳遞參數所以用不到, 而是直接用 global 宣告取用外部全域變數 html (即要回應之網頁內容) 後呼叫 ESP8266WebServer.ok() 函式將 html 網頁內容回應給客戶端. 

然後呼叫 ESP8266WebServer.begin() 啟動網頁伺服器, 傳入參數是埠號, 一般是 80 埠 : 

>>> ESP8266WebServer.begin(80)    

接下來呼叫 ESP8266WebServer.onPath() 定義路由 (routing), 其第一參數為資源路徑, 此處為網站根目錄 '/'; 第二參數為對應此資源請求之路由處理函式, 也就是上面所定義的回應函式 handleRoot : 

>>> ESP8266WebServer.onPath("/", handleRoot)       

最後在無限迴圈中呼叫 ESP8266WebServer.handleClient() 函式監視是否有客戶端請求即可 : 

>>> while True:     
    ESP8266WebServer.handleClient()     

這時在瀏覽器網址列輸入上面的伺服器連線網址即可看到 'Hello World!' 頁面了 : 


連線時會出現找不到 favicon.ico, 這是正常的, 因為網站上並未提供此圖示檔 :

/favicon.ico
Not Found.

限於通訊能力, ESP8266/ESP32 的 STA 介面只能容許五個客戶端同時連線. 由於伺服器是一個無限迴圈, 所以如果要停止伺服器只能用按板子上的 Reset 鍵方式終止此迴圈. 

以上測試完整程式碼如下 : 


測試 5-1 : 回應 Hello World! 的簡單網頁伺服器 [看原始碼] 

import ESP8266WebServer
import xtools
import config

ip=xtools.connect_wifi_led(config.SSID, config.PASSWORD) 

html="""
<html>
<head>
   <meta charset="utf-8">
   <meta name="viewport" content="width=device-width,initial-scale=1">
</head>
<body>
   <h1>Hello World!</h1>
</body>
</html>"""

def handleRoot(socket, args):       # 目錄請求之處理函式
    global html
    ESP8266WebServer.ok(socket, "200", "text/html", html)   # 回應網頁
    
ESP8266WebServer.begin(80)
ESP8266WebServer.onPath("/", handleRoot)     # 定義根目錄請求之處理函式
while True:
    ESP8266WebServer.handleClient()    # 監聽 80 埠是否有客戶端請求

ESP8266WebServer 模組支援網頁文件路徑功能, 這樣就可以把網頁檔案單獨存成 .htm 或 .html 檔放在開發板的特定檔案路徑下, 然後呼叫 ESP8266WebServer.setDocPath() 函式指定網頁檔路徑即可, 不需要像上面範例那樣在 MicroPython 程式中定義 html 變數來存放回應網頁. 

首先在本機工作目錄下建立一個 www 資料夾, 將上面範例的 html 變數內容存成 www 底下的 index.htm 檔 : 

<!--  index.htm -->
<html>
<head>
   <meta charset="utf-8">
   <meta name="viewport" content="width=device-width,initial-scale=1">
</head>
<body>
   <h1>Hello World!</h1>
</body>
</html>

然後將 www 資料夾上傳到板子的根目錄下 : 





這是因為 Thonny 的上傳功能只能傳到根目錄下的關係. 最後用下列指令指定網頁文件檔案在 www 下, 替換上面範例的 ESP8266WebServer.onPath() 而且也不需要定義 handlerRoot() 函式 :

ESP8266WebServer.setDocPath("/www")  

完整程式碼如下 : 


測試 5-2 : 使用 setDocPath() 設定網頁文件路徑 [看原始碼] 

import ESP8266WebServer
import xtools
import config

ip=xtools.connect_wifi_led(config.SSID, config.PASSWORD) 
  
ESP8266WebServer.begin(80)
ESP8266WebServer.setDocPath("/www") 
while True:
    ESP8266WebServer.handleClient()

注意, 這時在瀏覽器網址列輸入網址時必須輸入完整的網頁路徑 (例如 192.168.43.54/www/index.htm) 才會顯示與上面範例 1 之網頁結果 :


如果只輸入 IP (例如 192.168.43.54) 會得到 "Bad request" 回應 (400) :




如果只輸入到資料夾 192.168.43.54/www 則顯示 "Not found" : 



這是測試時必須注意的, 否則會誤以為程式有問題. 不過這與我的期待不符, 我以為呼叫 setDocPath() 後輸入網址 192.168.43.54 就應該要自動到 www 底下找預設的首頁檔 index.htm 或 index.html 才對, 但實測結果卻不是這樣. 其實不呼叫 setDocPath() 還是可以運作, 所以覺得此版的 setDocPath() 功能也許尚不完整. 

參考 :


另外 ESP8266WebServer 模組也提供網頁模板 (Template) 功能, 可透過呼叫 setTplData() 函式將字典型態的變數傳遞給模板網頁, 用法如下 :
  • 模板網頁檔名必須以 .p.htm 或 .p.html 結尾
  • 所傳遞之變數 var (即字典的 key) 以中括號 {var} 嵌入模板網頁中
首先將上面 /www 底下的 index.htm 改名為 index.p.html, 注意, 與上面兩個範例用 .htm 或 .html 都可以的情況不同, 此處一定要用 .p.html, 如果用 .p.htm 變數將無法傳遞, 而是直接顯示變數名稱. 其次, 編輯 index.p.html 網頁內容, 在 Hello 後面嵌入一個變數 name 為 Hello {name} :

<!--  index.p.html -->
<html>
<head>
   <meta charset="utf-8">
   <meta name="viewport" content="width=device-width,initial-scale=1">
</head>
<body>
   <h1>Hello {name}</h1>
</body>
</html>

然後將 /www 資料夾上傳到板子 :




接下來就可以呼叫 xtools.connect_wifi_led() 將板子連上 WiFi, 呼叫 ESP8266WebServer.begin() 啟動網頁伺服器, 呼叫 ESP8266WebServer.setDocPath() 設定 HTML 文件資料夾 (雖然這指令如上例所述似乎可有可無) : 

ESP8266WebServer.begin(80)
ESP8266WebServer.setDocPath("/www") 

然後定義要傳給模板網頁 index.p.html 的變數 name, 將其放入 data 字典中傳給 ESP8266WebServer.setTplData() :

data={"name": "Tony"}
ESP8266WebServer.setTplData(data)

最後在無限迴圈中呼叫 ESP8266WebServer.handleClient() 函式監視是否有客戶端請求 : 

while True:
    ESP8266WebServer.handleClient()

完整程式碼如下 : 


測試 5-3 : 呼叫 setTplData() 函式傳遞變數給 .p.html 網頁模板 [看原始碼] 

import ESP8266WebServer
import xtools
import config

ip=xtools.connect_wifi_led(config.SSID, config.PASSWORD)  
ESP8266WebServer.begin(80)              
ESP8266WebServer.setDocPath("/www") 
data={"name": "Tony"}
ESP8266WebServer.setTplData(data)    
while True:
    ESP8266WebServer.handleClient()

與上面範例 2 一樣, 瀏覽器網址列必須輸入完整的網頁路徑才會顯示正確的結果 (例如此處為 192.168.43.54/www/index.p.html), 否則會出現 Bad request 或 Not found. 

結果如下 :



可見字典 data 中的變數 name 已經傳遞到模版網頁 index.p.html 並內嵌到 {name} 裡面了. 

如果有多個頁面可以用超連結或按鈕來切換, 下面範例使用兩個網頁 page1.htm 與 page2.htm 利用超連結來切換, 先在本機建立 www2 資料夾, 裡面放兩個網頁檔 :

 第一頁網頁內容如下 : 

<!--  page1.html -->
<html>
<head>
   <meta charset="utf-8">
   <meta name="viewport" content="width=device-width,initial-scale=1">
</head>
<body>
   <h1>這是第 1 頁</h1>
   <h1><a href="/www2/page2.html">前往第 2 頁</a></h1>
</body>
</html>

第二頁網頁內容如下 : 

<!--  page2.html -->
<html>
<head>
   <meta charset="utf-8">
   <meta name="viewport" content="width=device-width,initial-scale=1">
</head>
<body>
   <h1>這是第 2 頁</h1>
   <h1><a href="/www2/page1.html">返回第 1 頁</a></h1>
</body>
</html>

將此 www2 資料夾上傳板子, 然後如上面範例 2 啟動伺服器, 完整程式碼如下 :


測試 5-4 : 用超連結切換多頁面 [看原始碼] 

import ESP8266WebServer
import xtools
import config

ip=xtools.connect_wifi_led(config.SSID, config.PASSWORD) 

ESP8266WebServer.begin(80)              
ESP8266WebServer.setDocPath("/www2") 

while True:
    ESP8266WebServer.handleClient()

瀏覽 page1.htm 完整網址 (例如 192.168.43.54/www2/page1.html), 結果如下 :



按 "前往第 2 頁" 超連結會切換到 page2.htm 網頁 : 



按 "返回第 1 頁" 又回到 page1.html 了.

前面的測試顯示, 我使用的這版 ESP8266WebServer.setDocPath() 並不會自動去指定資料夾下找尋首頁檔, 而是必須用完整的網頁路徑. 解決辦法就是以 RESTful 方式使用多個請求處理函式. 

下面範例改編自前一個測試, 網站總共有三個網頁 : index.html, page1.html, 以及 page2.html, 透過超連結以 RESTful 網址 /, /page1, 以及 /page2 切換, 首先在本機鍵一個 www3 資料夾, 編輯如下三個網頁檔後以 utf-8 編碼存檔 (因為網頁內容有中文) :

下面是首頁檔案 : 

<!--  index.html -->
<html>
<head>
   <meta charset="utf-8">
   <meta name="viewport" content="width=device-width,initial-scale=1">
</head>
<body>
   <h1>這是首頁</h1>
   <h1><a href="/page1">前往第 1 頁</a></h1>
</body>
</html>

下面是第一頁檔案 : 

<!--  page1.html -->
<html>
<head>
   <meta charset="utf-8">
   <meta name="viewport" content="width=device-width,initial-scale=1">
</head>
<body>
   <h1>這是第 1 頁</h1>
   <h1><a href="/page2">前往第 2 頁</a></h1>
   <h1><a href="/">返回首頁</a></h1>
</body>
</html>

下面是第二頁檔案 : 

<!--  page2.html -->
<html>
<head>
   <meta charset="utf-8">
   <meta name="viewport" content="width=device-width,initial-scale=1">
</head>
<body>
   <h1>這是第 2 頁</h1>
   <h1><a href="page1">返回第 1 頁</a></h1>
   <h1><a href="/">返回首頁</a></h1>
</body>
</html>

將 www3 資料夾上傳到板子 : 




接下來修改 MicroPython 程式, 為 /, /page1, 與 /page2 這三個網址建立請求處理函式 :

def handleRoot(socket, args): 
    with open('./www3/index.html', 'r', encoding='utf-8') as f:
        html=f.read()
        print(html)
    ESP8266WebServer.ok(socket, "200", "text/html", html)
def handlePage1(socket, args): 
    with open('./www3/page1.html', 'r', encoding='utf-8') as f:
        html=f.read()
        print(html)
    ESP8266WebServer.ok(socket, "200", "text/html", html) 
def handlePage2(socket, args): 
    with open('./www3/page2.html', 'r', encoding='utf-8') as f:
        html=f.read()
        print(html)
    ESP8266WebServer.ok(socket, "200", "text/html", html)

這三個函式都使用 open() 開啟位於 www3 資料夾下的網頁檔 (須指定 utf-8 編碼), 讀取後傳給 ESP8266WebServer.ok() 回應給客戶端. 

然後呼叫 onPath() 將網址對應到請求處理函式 :

ESP8266WebServer.onPath("/", handleRoot) 
ESP8266WebServer.onPath("/page1", handlePage1) 
ESP8266WebServer.onPath("/page2", handlePage2)

完整程式碼如下 : 


測試 5-5 : 用多個 onPath() 切換多個 RESTful 網址 [看原始碼] 

import ESP8266WebServer
import xtools
import config

ip=xtools.connect_wifi_led(config.SSID, config.PASSWORD) 

ESP8266WebServer.begin(80)              

def handleRoot(socket, args): 
    with open('./www3/index.html', 'r', encoding='utf-8') as f:
        html=f.read()
        print(html)
    ESP8266WebServer.ok(socket, "200", "text/html", html)
def handlePage1(socket, args): 
    with open('./www3/page1.html', 'r', encoding='utf-8') as f:
        html=f.read()
        print(html)
    ESP8266WebServer.ok(socket, "200", "text/html", html) 
def handlePage2(socket, args): 
    with open('./www3/page2.html', 'r', encoding='utf-8') as f:
        html=f.read()
        print(html)
    ESP8266WebServer.ok(socket, "200", "text/html", html)

ESP8266WebServer.onPath("/", handleRoot)               # / 處理函式
ESP8266WebServer.onPath("/page1", handlePage1)   # /page1 處理函式
ESP8266WebServer.onPath("/page2", handlePage2)   # /page2 處理函式

while True:
    ESP8266WebServer.handleClient()

瀏覽器網址列只要輸入 ip 即顯示首頁 index.html :



按 "前往第 1 頁" 會切換到 page1.html : 



按 "前往第 2 頁" 會切換到 page2.html : 



按 "返回首頁" 切換至 index.html, 按 "返回第 1 頁" 切換至 page1.html; 按 "返回第 2 頁" 切換至 page2.html, 這樣就完全不用去管網址列的網頁檔路徑了. 

下面範例使用如下超連結按鈕傳遞參數 led 來控制板載 LED (GPIO2) 的明滅 :

/?led=on : 開啟 LED
/?led=off : 關閉 LED

傳入 URL 請求處理函式的第二個參數 args 將是如下的字典 :

{'led': 'on'}
{'led': 'off'}

這樣就可以透過判斷 led 參數之值 args['led'] 是 'on' 還是 'off' 來設定 GPIO2 的值以控制板載 LED 的明滅了. 

首先在本機建立一個 www4 資料夾, 建立一個模板網頁 index.p.html :

<!--  index.p.html -->
<!DOCTYPE html>
<html>
<head>
   <meta charset="utf-8">
   <meta name="viewport" content="width=device-width,initial-scale=1">
</head>
<body>
   <h1>LED 狀態 : {0}</h1>
   <a href="/?led=on"><button style="font-size:40px;">點亮</button></a>
   <a href="/?led=off"><button style="font-size:40px;">熄滅</button></a>
</body>
</html>

此模板網頁中有兩個超連結按鈕, 透過 href 屬性設定 GET 方法的請求網址 /?led=on 與 /?led=off 分別用來控制板載 LED (GPIO2) 的明滅. 其次, LED 狀態後面的 {0} 用在程式中以字串的 format() 方法將 LED 狀態字串 ('點亮' 或 '熄滅') 嵌入此位置. 

將此 www4 資料夾用 Thonny 上傳到開發板根目錄下 :




然後從 machine 模組匯入 Pin 類別, 先將板載 LED 設為關閉 (滅) :

from machine import Pin
led=Pin(2, Pin.OUT)
led.value(0)

接著撰寫根目錄請求處理函式 :

def handleRoot(socket, args): 
    print(args)
    with open('./www4/index.p.html', 'r', encoding='utf-8') as f:
        html=f.read()
    state='熄滅' 
    if 'led' in args:
        if args['led']=='on':
            state='點亮'
            led.value(1)
        elif args['led']=='off':
            state='熄滅'
            led.value(0)
    response=html.format(state)
    ESP8266WebServer.ok(socket, "200", "text/html", response)

此函式從 /www4 底下讀取模板網頁 index.p.html 為 html 字串, 然後判斷傳入參數字典 args 是否有 led 參數, 有的話就依據其值為 'on' 或 'off' 來控制 LED 的明滅, 並將狀態字串利用 format() 方法嵌入 html 字串的 {0} 位置, 完整原始碼如下 : 


測試 5-6 : 用超連結按鈕點亮與熄滅板載 LED [看原始碼] 

import ESP8266WebServer
import xtools
import config
from machine import Pin 

ip=xtools.connect_wifi_led(config.SSID, config.PASSWORD) 
led=Pin(2, Pin.OUT)
led.value(1)     # 預設狀態: 熄滅
ESP8266WebServer.begin(80)              
ESP8266WebServer.setDocPath("/www4")

def handleRoot(socket, args): 
    print(args)
    with open('./www4/index.p.html', 'r', encoding='utf-8') as f:
        html=f.read()
    state='熄滅' 
    if 'led' in args:
        if args['led']=='on':
            state='點亮'
            led.value(0)    # GPIO2 板載 LED 輸出 low 為亮
        elif args['led']=='off':
            state='熄滅'
            led.value(1)    # GPIO2 板載 LED 輸出 high 為滅
    response=html.format(state)
    ESP8266WebServer.ok(socket, "200", "text/html", response)

ESP8266WebServer.onPath("/", handleRoot) 
data={"state": "熄滅"}
ESP8266WebServer.setTplData(data)

while True:
    ESP8266WebServer.handleClient()

注意, 由於板載 LED 使用 sink current 接法, 亦即 LED 是陽極經限流電阻接 GPIO2, 陰極則接至 VCC, 故輸出 0 為點亮 LED, 輸出 1 熄滅 LED (負邏輯), 這在 NodeMCU, D1 mini, 或 Witty Cloud 開發板皆是如此 (但 Witty Cloud 上的全彩 LED 則使用 source current, 即正邏輯). 

執行後在瀏覽器輸入網址, 例如 192.168.2.141 會顯示 index.p.html 網頁, 預設狀態是熄滅 : 



按 "點亮" 鈕板載 LED 亮, 且網頁中 LED 狀態也顯示 "點亮" :




按 "熄滅" 鈕板載 LED 會暗掉, LED 狀態恢復為 "熄滅". 

上面的範例每次按 "點亮" 或 "熄滅" 按鈕都會重新載入 index.p.html 網頁, 下面改用 jQuery 函式庫所提供的 Ajax 非同步功能向伺服器提出請求, 利用伺服器回傳的資料更新網頁中特定元素的內容, 不需要重新載入網頁即可改變網頁內容. 

首先在本機建立一個 www5 資料夾, 並在底下建立一個 index.html 網頁 :

<!--  index.html -->
<!DOCTYPE html>
<html>
<head>
   <meta charset="utf-8">
   <meta name="viewport" content="width=device-width,initial-scale=1">
   <script src="https://code.jquery.com/jquery-3.6.1.min.js"></script>
</head>
<body>
   <h1>LED 狀態 : <span id='status'>熄滅</span></h1>
   <button id="btn_on" style="font-size:40px;">點亮</button>
   <button id="btn_off" style="font-size:40px;">熄滅</button>
   <script>
     $(function(){
       $("#btn_on").click(function() {
         $.get({
           url: "/on",
           dataType: "html",
           success: function(data) {
             $("#status").html(data);
             }     
           });
         });
       $("#btn_off").click(function() {
         $.get({
           url: "/off",
           dataType: "html",
           success: function(data) {
             $("#status").html(data);
             }     
           });
         });
       });
   </script>
</body>
</html>

此網頁在 head 標籤內從 jQuery 官網 CDN 匯入 jQuery 函式庫 (目前最新為 3.6.1 版), 雖然也可以下載後上傳到 ESP8266/ESP32 開發板, 但這會佔 Flash 空間語頻寬, 不建議這麼做. 其次, 網頁中的 LED 狀態顯示改用一個具有 id=status 屬性的 span 元素取代, 預設狀態為 "熄滅". 按鈕也直接使用具有 id 屬性 btn_on 與 btn_off 之 button 元素, 去除超連結. 在按下 "點亮" 與 "熄滅" 按鈕時呼叫 jQuery 的捷徑函式 $.get() 對 /on 與 /off 這兩個網址提出 Ajax 請求, 利用傳回來 data 更新 span 元素的內容. 

關於 jQuery 的 Ajax 函式參考 : 


將 www5 資料夾上傳開發板 :




在 MicroPython 網頁伺服器程式中要針對根目錄 / 與兩個按鈕請求 URL 進行路由處理 : 

def handleRoot(socket, args):
    with open('./www5/index.html', 'r', encoding='utf-8') as f:
        html=f.read()
    ESP8266WebServer.ok(socket, "200", "text/html", html)
def handleOn(socket, args): 
    led.value(0) 
    ESP8266WebServer.ok(socket, "200", "text/html", "點亮")
def handleOff(socket, args): 
    led.value(1) 
    ESP8266WebServer.ok(socket, "200", "text/html", "熄滅")

ESP8266WebServer.onPath("/", handleRoot) 
ESP8266WebServer.onPath("/on", handleOn) 
ESP8266WebServer.onPath("/off", handleOff)

完整程式碼如下 :


測試 5-7 : 用 jQuery 的 Ajax 功能點亮與熄滅板載 LED [看原始碼] 

import ESP8266WebServer
import xtools
import config
from machine import Pin 
         
def handleRoot(socket, args):
    with open('./www5/index.html', 'r', encoding='utf-8') as f:
        html=f.read()
    ESP8266WebServer.ok(socket, "200", "text/html", html)

def handleOn(socket, args): 
    led.value(0)   # GPIO2 板載 LED 輸出 low 為亮
    ESP8266WebServer.ok(socket, "200", "text/html", "點亮")

def handleOff(socket, args): 
    led.value(1)   # GPIO2 板載 LED 輸出 high 為滅
    ESP8266WebServer.ok(socket, "200", "text/html", "熄滅")

ip=xtools.connect_wifi_led(config.SSID, config.PASSWORD) 
led=Pin(2, Pin.OUT)
led.value(1)
ESP8266WebServer.setDocPath("/www5")
ESP8266WebServer.begin(80)   
ESP8266WebServer.onPath("/", handleRoot) 
ESP8266WebServer.onPath("/on", handleOn) 
ESP8266WebServer.onPath("/off", handleOff) 

while True:
    ESP8266WebServer.handleClient()

結果與上面超連結按鈕範例一樣 (但 index.html 網頁只會載入一次). 

沒有留言:

張貼留言