2019年7月29日 星期一

MicroPython on ESP32 學習筆記 (十二) : picoweb 使用 template 失敗

今天繼續測試 picoweb 的 template 用法, 結果失敗, 搜尋網路並未找到解決方案, 大軍還得繼續開拔, 不能停在這裡糾纏, 茲將遇到的問題紀錄如下, picoweb 就暫時別過.

首先是把透過 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() 時發生的.

沒有留言 :