首先是把透過 ESP32 本身 AP 建立網頁伺服器來設定外部 AP 的實驗改成用 template 來做, 作法參考 :
# https://github.com/pfalcon/picoweb/blob/master/example_webapp2.py
# How to make ESP32 as HTTP webserver using MicroPython ?
不使用 template 功能的作法參考 :
# MicroPython on ESP32 學習筆記 (十) : upip 安裝網頁框架 picoweb 成功
測試 1 : 使用 template 建立 AP 連線設定網頁 (NG)
首先將原本的 main.py 中的網頁部分移除, 分別寫成兩個模板檔案, 第一個是顯示表單的網頁模板檔 ap_set_form.tpl, 此模板不需傳入變數, 就是單純的網頁而已 :
<!DOCTYPE html>
<html>
<head><title>AP Setup</title></head>
<body>
<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>
</body>
</html>
第二個是顯示設定結果的模板檔案 ap_set_result.tpl, 此模板虛傳入一個變數 ap, 其內容是一個含有所設定之 ssid 與 pwd 變數 :
{% args ap %}
<!DOCTYPE html>
<html>
<head><title>AP Connected</title></head>
<body>
SSID {{ap['ssid']}} connected:IP={{ap['ip']}}
</body>
</html>
然後用 ampy 或 WebREPL 先在 ESP32 根目錄下建立一個 templates 目錄, 將這兩個 tpl 檔上傳到 templates 子目錄下. 如果使用 WebREPL, 因為它只能傳到根目錄下, 上傳完畢後再用 os.rename() 將其移到 templates 下面, 例如 :
>>> import os
>>> os.mkdir('templates')
>>> os.listdir()
['boot.py', 'webrepl_cfg.py', 'lib', 'main.py', 'templates', 'ap_set_form.tpl', 'ap_set_result.tpl']
>>> os.rename('ap_set_form.tpl','templates/ap_set_form.tpl')
>>> os.rename('ap_set_result.tpl','templates/ap_set_result.tpl')
>>> os.listdir('templates')
['ap_set_form.tpl', 'ap_set_result.tpl']
然後將 main.py 改寫如下 :
#main.py
import network
import picoweb
sta=network.WLAN(network.STA_IF)
sta.active(True)
ap=network.WLAN(network.AP_IF)
ap.active(True)
ap.config(authmode=4, password='micropythoN')
app=picoweb.WebApp(__name__)
@app.route("/")
def index(req, resp):
yield from picoweb.start_response(resp, content_type="text/html")
yield from app.render_template(resp, "ap_set_form.tpl")
@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])
ap={"ssid":ssid,"ip":sta.ifconfig()[0]}
yield from picoweb.start_response(resp, content_type="text/html")
yield from app.render_template(resp, "ap_set_result.tpl", (ap,))
if __name__ == "__main__":
app.run(debug=True, host='192.168.4.1', port=80)
此程式先開啟 AP 與 STA 介面, 然後將兩個路由 / 與 /update_ap 改為呼叫 render_template() 並傳入相對之網頁模板與變數.
將 main.py 上傳至根目錄後重開機, 伺服器有正常運作監聽 192.168.4.1 的 80 埠, 以手機連線 ESP32 本身的 AP (ESP32_XXXX) 後, 瀏覽器輸入 192.168.4.1 卻顯示無法連線, REPL 則輸出下列 AttributeError 錯誤訊息 :
* Running on http://192.168.4.1:80/
dhcps: send_nak>>udp_sendto result 0
INFO:picoweb:28.000 <HTTPRequest object at 3ffc4e60> <StreamWriter <socket>> "GET /"
ERROR:picoweb:28.000 <HTTPRequest object at 3ffc4e60> <StreamWriter <socket>> AttributeError("'StreamWriter' object has no attribute 'awritestr'",)
Traceback (most rece
nt call last):
File "/lib/picoweb/__init__.py", line 204, in _handle
File "main.py", line 13, in index
File "/lib/picoweb/__init__.py", line 249, in render_template
AttributeError: 'StreamWriter' object has no attribute 'awritestr'
查詢網路並無相關訊息, 所以我就把 pfacon 的範例拿來測試看看, 也是不行 (錯誤訊息一樣, 都是沒有 awritestr 屬性), 如下列測試 2 所示.
測試 2 : 用 template 顯示平方數網頁
此範例參考 pfacon 範例稍微修改, 程式與模板參考 :
# https://github.com/pfalcon/picoweb/blob/master/example_webapp2.py
# https://github.com/pfalcon/picoweb/tree/master/templates
# https://github.com/pfalcon/picoweb/blob/master/examples/example_unicode.py
首先將 templates 目錄下的 squares.tpl 下載後一字不改, 直接用 ampy 或 WebREPL 上傳至 /templates 下 :
{% args req %}
<html>
Request path: '{{req.path}}'<br>
<table border="1">
{% for i in range(5) %}
<tr><td> {{i}} </td><td> {{"%2d" % i ** 2}} </td></tr>
{% endfor %}
</table>
</html>
然後下載 example_webapp2.py 後修改如下 (主要是為了在筆電測試方便, 在前面加上連線熱點之程式碼, 伺服器改為監聽連線外部 AP 後所獲得之 IP, 其他都沒改) :
#
# This is a picoweb example showing a web page route
# specification using view decorators (Flask style).
#
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 index(req, resp):
yield from picoweb.start_response(resp)
yield from resp.awrite("I can show you a table of <a href='squares'>squares</a>.")
@app.route("/squares")
def squares(req, resp):
yield from picoweb.start_response(resp)
yield from app.render_template(resp, "squares.tpl", (req,))
import ulogging as logging
logging.basicConfig(level=logging.INFO)
if __name__ == "__main__":
app.run(debug=True, host=host, port=80)
將此程式存成 main.py 後上傳, 開啟手機熱點分享後 ESP32 重開機, 然後用筆電瀏覽器連線該 IP 的 port 80 顯示如下網頁 :
REPL 介面顯示兩個請求 :
192.168.43.177
* Running on http://192.168.43.177:80/
dhcps: send_nak>>udp_sendto result 0
INFO:picoweb:17.000 <HTTPRequest object at 3ffc4db0> <StreamWriter <socket>> "GET /"
INFO:picoweb:17.000 <HTTPRequest object at 3ffc9270> <StreamWriter <socket>> "GET /favicon.ico"
但點網頁中的 squares 超連結卻無法顯示結果, REPL 出現如下 AttributeError 錯誤訊息 :
192.168.43.177
* Running on http://192.168.43.177:80/
INFO:picoweb:14.000 <HTTPRequest object at 3ffc4eb0> <StreamWriter <socket>> "GET /"
INFO:picoweb:18.000 <HTTPRequest object at 3ffc93f0> <StreamWriter <socket>> "GET /squares"
ERROR:picoweb:20.000 <HTTPRequest object at 3ffc93f0> <StreamWriter <socket>> AttributeError("'StreamWriter' object has no attribute 'awritestr'",)
Traceback (most recent call last):
File "/lib/picoweb/__init__.py", line 204, in _handle
File "main.py", line 27, in squares
File "/lib/picoweb/__init__.py", line 249, in render_template
AttributeError: 'StreamWriter' object has no attribute 'awritestr'
問題都是在呼叫 render_template() 時發生的.
沒有留言 :
張貼留言