# ESP32 MicroPython: HTTP Webserver with Picoweb
# ESP32 MicroPython Tutorial: HTTP Webserver with Picoweb
發現作者另外還安裝了兩個套件 :
upip.install('micropython-uasyncio')
upip.install('micropython-pkg_resources')
我補安裝這兩個套件後重開機再試就成功了. 為了保留之前的測試紀錄, 把測試成功的紀錄寫在這裡, 前篇失敗的那篇參考 :
# MicroPython on ESP32 學習筆記 (九) : upip 安裝網頁框架 picoweb 失敗
安裝前須先讓 ESP32 連上 Internet :
import network
sta=network.WLAN(network.STA_IF)
sta.active(True)
sta.connect('TonyNote8', 'blablabla')
完整的安裝程序如下 :
1. 安裝 picoweb :
>>> import upip
>>> upip.install('picoweb')
Installing to: /lib/
Warning: micropython.org SSL certificate is not validated
Installing picoweb 1.7.1 from https://files.pythonhosted.org/packages/1b/4f/f7d35f90521e95d9d2307f69ff523133d7d4dd6da7ce1ce0c8382e7255fa/picoweb-1.7.1.tar.gz
Installing pycopy-uasyncio 3.1.1 from https://files.pythonhosted.org/packages/5f/24/fb08acdd7ebf1626dcdb3cdaf0d3f463c254a4a3aa2cab70b0ee6562a83d/pycopy-uasyncio-3.1.1.tar.gz
Installing pycopy-pkg_resources 0.2.1 from https://files.pythonhosted.org/packages/05/4a/5481a3225d43195361695645d78f4439527278088c0822fadaaf2e93378c/pycopy-pkg_resources-0.2.1.tar.gz
Installing pycopy-uasyncio.core 2.3 from https://files.pythonhosted.org/packages/6a/96/80a86b1ea4e2b8c7e130068a56f9e8b5bbb28369e48de56a753778e14faf/pycopy-uasyncio.core-2.3.tar.gz
安裝完成匯入 picoweb, 沒有出現錯誤訊息 :
>>> import picoweb
>>>
檢查根目錄發現多出一個 lib 子目錄, 底下存放了剛剛安裝的第三方套件 picoweb 與其相依套件 uasyncio 與 pkg_resources :
>>> os.listdir()
['boot.py', 'webrepl_cfg.py', 'main.py', 'lib']
>>> os.chdir('lib')
>>> os.listdir()
['picoweb', 'uasyncio', 'pkg_resources.py']
關於 picoweb 用法範例, 參考 pfacon 的 GitHub :
# https://github.com/pfalcon/picoweb
# https://github.com/pfalcon/picoweb/tree/master/examples
2. 安裝 pycopy-ulogging 套件 :
這是 picoweb 運作必須用到的記錄檔套件 :
>>> upip.install('pycopy-ulogging')
Installing to: /lib/
Installing pycopy-ulogging 0.3 from https://files.pythonhosted.org/packages/56/85/47a6790260c85f0dad460124d1f9a6dbdaa0b0ac33b0ac89194f6f106276/pycopy-ulogging-0.3.tar.gz
沒有安裝此套件的話, 在執行 App 時會出現如下錯誤 :
>>> app.run(debug=True, host="192.168.43.177", port=80)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/lib/picoweb/__init__.py", line 284, in run
ImportError: no module named 'ulogging'
3. 安裝 utemplate 套件 :
此套件提供網頁模版 (template) 功能, 支援在模板網頁中嵌入傳入之變數, 若 App 有用到模版必須安裝, 如果沒用到模版並不影響 App 的執行.
>>> upip.install('utemplate')
Installing to: /lib/
Warning: micropython.org SSL certificate is not validated
Installing utemplate 1.3.1 from https://files.pythonhosted.org/packages/75/11/59ac69a862232afc9ffc1cadcc95395cfb7b28c17610edc061039d4f03f8/utemplate-1.3.1.tar.gz
此時再去檢查 /lib 目錄, 會發現新增了 ulogging.py 與 utemplate 這兩個套件 :
>>> os.chdir('/lib')
>>> os.listdir()
['picoweb', 'uasyncio', 'pkg_resources.py', 'ulogging.py', 'utemplate']
關於 utemplate 的用法參考 :
# https://github.com/pfalcon/utemplate
以上三個步驟與昨天程序相同, 但只做到這樣的話, 目前的相依套件版本會在執行 App 時會發生錯誤, 還要安裝下面兩個套件才行 (如果以後改版修正此問題後應該就不需要了).
4. 安裝 micropython-uasyncio 與 micropython-pkg_resources 套件 :
這兩個套件是今天測試成功的關鍵, 注意其版本編號 :
>>> upip.install('micropython-uasyncio')
Installing to: /lib/
Warning: micropython.org SSL certificate is not validated
Installing micropython-uasyncio 2.0 from https://micropython.org/pi/uasyncio/uasyncio-2.0.tar.gz
Installing micropython-uasyncio.core 2.0 from https://micropython.org/pi/uasyncio.core/uasyncio.core-2.0.tar.gz
>>> upip.install('micropython-pkg_resources')
Installing to: /lib/
Installing micropython-pkg_resources 0.2.1 from https://micropython.org/pi/pkg_resources/pkg_resources-0.2.1.tar.gz
這時再去檢查 /lib 目錄, 發現並沒有變化 :
>>> os.chdir('/lib')
>>> os.listdir()
['picoweb', 'uasyncio', 'pkg_resources.py', 'ulogging.py', 'utemplate']
可見此步驟所安裝的兩個 picoweb 相依套件應該是覆蓋了前面步驟 1 安裝 picoweb 時所安裝之 pycopy-uasyncio 與 pycopy-pkg_resources.py. 比對套件版本, pkg_resources.py 版本相同, 都是 0.2.1, 而 uasyncio 就不同, 此處為 2.0, 而步驟 1 的是 3.1.1, 版本比較新結果卻不行, 要此處舊版的 2.0 才行. 這樣就可以順利執行網頁應用程式了.
MicroPython 開發者 pfacon 在 GitHub 提供了一些 picoweb 的範例, 參考 :
# https://github.com/pfalcon/picoweb/tree/master/examples
# https://github.com/pfalcon/picoweb
測試 1 : 顯示 Hello World 網頁
其實 picoweb 的用法跟 Flask 很像, 幾乎只要將 Flask 改成 picoweb 即可. 關於 Flask 的基本用法參考 :
# Python Web Flask 實戰開發教學 - 簡介與環境建置
首先呼叫 picoweb.WebApp() 並傳入 __name__ 參數建立一個 WebApp 物件 app :
>>> app=picoweb.WebApp(__name__)
>>> type(app)
<class 'WebApp'>
>>> dir(app)
['__class__', '__init__', '__module__', '__qualname__', '__dict__', 'init', 'mount', 'run', 'pkg', 'url_map', 'handle_static', 'mounts', 'inited', 'template_loader', 'headers_mode', 'parse_headers', '_handle', 'route', 'add_url_rule', '_load_template', 'render_template', 'render_str', 'sendfile']
然後用裝飾器 (decorator) @ 定義 App 的路由 (routing), 此測試只有根目錄一個路由, 只要用戶瀏覽根目錄就回應 'Hello World', 然後緊跟著要定義此路由之處理函數 hello(), 需傳入兩個參數來處理要求與回應, 第一個是要求 req, 第二個是回應 resp, 呼叫 resp.awrite() 送出回應訊息 :
... def hello(req, resp):
... yield from picoweb.start_response(resp)
... yield from resp.awrite("Hello World!")
...
...
...
然後呼叫 STA 介面的 ifconfig() 查詢所分配到的 IP 作為網頁伺服器的 host 位址 :
>>> sta.ifconfig()
('192.168.43.177', '255.255.255.0', '192.168.43.1', '192.168.43.1')
最後呼叫 WebApp 物件的 run() 方法, 傳入伺服器的網址與埠號 (預設是 5000) 即可 :
>>> app.run(debug=True, host="192.168.43.177", port=80)
* Running on http://192.168.43.177:80/
可見網頁伺服器成功運行了.
接著用連線到同一熱點的電腦瀏覽器, 輸入 ESP32 Web 伺服器網址 192.168.43.177 即顯示 Hello World 網頁 :
REPL 顯示如下兩筆要求訊息, 第一筆是要求 HTML 網頁本身, 第二筆是要求網頁上的小圖示 favicon.ico :
INFO:picoweb:952.000 <HTTPRequest object at 3ffc47a0> <StreamWriter <socket>> "GET /"
INFO:picoweb:953.000 <HTTPRequest object at 3ffc84d0> <StreamWriter <socket>> "GET /favicon.ico"
完整程式如下 :
#main.py
import network
import picoweb
sta=network.WLAN(network.STA_IF)
sta.active(True)
sta.connect('TonyNote8', 'blablabla')
while not sta.isconnected():
pass
host=sta.ifconfig()[0]
print(host)
app=picoweb.WebApp(__name__)
@app.route("/")
def hello(req, resp):
yield from picoweb.start_response(resp)
yield from resp.awrite("Hello World!")
if __name__ == "__main__":
app.run(debug=True, host=host, port=80)
注意, 呼叫 sta.connect() 後必須用迴圈或 time.sleep() 等待直到連線成功再取得 host, 否則會取到 0.0.0.0. 將此程式存成 main.py, 用 ampy 或 WebREPL 上傳到 ESP32 即可. 程式最後的 if __name__ 判斷目的是用 python 執行此程式時才呼叫 app.run(), 在 import 時不會.
接下來要用 picoweb 來建立一個網頁伺服器, 讓使用者可以連線到 ESP32 本身的 AP (192.168.4.1) 輸入要連線的 WiFi 熱點, 前面的測試中我們是使用底層的 socket 模組來做, 參考 :
# MicroPython on ESP32 學習筆記 (七) : socket 與網頁伺服器
測試 2 : 用內部 AP 建立 Web 伺服器設定要連線之熱點 (GET)
#main.py
import network
import picoweb
html="""
<!DOCTYPE html>
<html>
<head><title>AP Setup</title></head>
<body>
%s
</body>
</html>
"""
form="""
<form method=get action='/update_ap'>
<table border="0">
<tr>
<td>SSID</td>
<td><input name=ssid type=text></td>
</tr>
<tr>
<td>PWD </td>
<td><input name=pwd type=text></td>
</tr>
<tr>
<td></td>
<td align=right><input type=submit value=Connect></td>
</tr>
</table>
</form>
"""
app=picoweb.WebApp(__name__)
@app.route("/")
def hello(req, resp):
yield from picoweb.start_response(resp)
yield from resp.awrite(html % form)
@app.route("/update_ap")
def update_ap(req, resp):
req.parse_qs() #剖析請求表單
ssid=req.form['ssid'] #取得表單中的參數
pwd=req.form['pwd'] #取得表單中的參數
print(ssid,pwd)
sta.connect(ssid, pwd)
print('Connecting to AP=', ssid, ' ...')
while not sta.isconnected():
pass
print('Connected IP=', sta.ifconfig()[0])
yield from picoweb.start_response(resp)
yield from resp.awrite(html % 'Connected:IP=' + sta.ifconfig()[0])
if __name__ == "__main__":
app.run(debug=True, host='192.168.4.1', port=80)
此程式宣告了兩個路由 : 根目錄 / (顯示設定表單) 以及 /update_ap (連線所輸入之熱點). 注意, 上面程式碼中最重要的部分是, 如果客戶端請求中的表單是以 GET 方法提交, 則需呼叫請求物件 req 的 query_qs() 方法來剖析請求表單, 然後用 form[] 即可取得請求表單中的參數. 如果是用 POST 方法提交, 則需呼叫 yield from req.read_form_data(), 參考開發者 pfacon 的範例 :
# https://github.com/pfalcon/picoweb/blob/master/examples/example_form.py
將上面的程式存成 main.py 上傳 ESP32, 重開機後以手機連線 ESP32 本身 AP (ESP32_XXXX), 開啟瀏覽器連線 192.168.4.1 應該會顯示一個 SSID 與 PWD 的表單, 輸入後按 Connect 即可.
Putty REPL 介面輸出如下 :
connect('TonyNote8','blablabla')
Started webrepl in normal mode
I (429) phy: phy_version: 4100, 2a5dd04, Jan 23 2019, 21:00:07, 0, 0
* Running on http://192.168.4.1:80/
dhcps: send_nak>>udp_sendto result 0
INFO:picoweb:27.000 <HTTPRequest object at 3ffc4850> <StreamWriter <socket>> "GET /"
INFO:picoweb:37.000 <HTTPRequest object at 3ffc9420> <StreamWriter <socket>> "GET /update_ap?ssid=TonyNote8&pwd=blablabla"
TonyNote8 a5572056
Connecting to AP= TonyNote8 ...
測試 3 : 用內部 AP 建立 Web 伺服器設定要連線之熱點 (POST)
#main.py
import network
import picoweb
import time
html="""
<!DOCTYPE html>
<html>
<head><title>AP Setup</title></head>
<body>
%s
</body>
</html>
"""
form="""
<form method=post action='/update_ap'>
<table border="0">
<tr>
<td>SSID</td>
<td><input name=ssid type=text></td>
</tr>
<tr>
<td>PWD </td>
<td><input name=pwd type=text></td>
</tr>
<tr>
<td></td>
<td align=right><input type=submit value=Connect></td>
</tr>
</table>
</form>
"""
app=picoweb.WebApp(__name__)
@app.route("/")
def hello(req, resp):
yield from picoweb.start_response(resp)
yield from resp.awrite(html % form)
@app.route("/update_ap")
def update_ap(req, resp):
if req.method == "POST":
yield from req.read_form_data() #取得請求表單
else:
req.parse_qs()
ssid=req.form['ssid'] #取得表單中的參數
pwd=req.form['pwd'] #取得表單中的參數
print(ssid,pwd)
sta.connect(ssid, pwd)
print('Connecting to AP=', ssid, ' ...')
while not sta.isconnected():
pass
print('Connected IP=', sta.ifconfig()[0])
yield from picoweb.start_response(resp)
yield from resp.awrite(html % 'Connected:IP=' + sta.ifconfig()[0])
if __name__ == "__main__":
app.run(debug=True, host='192.168.4.1', port=80)
注意上面 HTML 的表單中的 method 是 POST, 因此 update_ap() 處理此呼叫時是執行 yield from req.read_form_data() 取得請求表單中的資料. 取得表單參數一樣是用 reg.from['param'] 不變.
REPL 介面輸出訊息如下 :
* Running on http://192.168.4.1:80/
INFO:picoweb:51.000 <HTTPRequest object at 3ffc4850> <StreamWriter <socket>> "GET /"
INFO:picoweb:58.000 <HTTPRequest object at 3ffc9510> <StreamWriter <socket>> "POST /update_ap"
TonyNote8 blablabla
Connecting to AP= TonyNote8 ...
Connected IP= 192.168.43.177
參考 :
# ESP32 MicroPython: HTTP Webserver with Picoweb
# ESP32 MicroPython: Serving HTML from the file system in Picoweb
# ESP32 MicroPython: Changing the HTTP response content-type of Picoweb route
# ESP32 Picoweb: Serving JSON content
# ESP32 Picoweb: Changing the returned HTTP code
# ESP32 Picoweb: Obtaining the HTTP Method of the request
# https://forum.micropython.org/viewtopic.php?t=3651
# ESP32 Picoweb教程:修改返回的HTTP代码
# Getting Started with MicroPython on ESP32 – Hello World, GPIO, and WiFi
# ESP32 MicroPython教程:使用Picoweb实现HTTP Webserver
沒有留言:
張貼留言