2019年7月23日 星期二

MicroPython on ESP32 學習筆記 (九) : upip 安裝網頁框架 picoweb 失敗

在前一篇 ESP32 網頁伺服器的測試中, 網頁是直接寫在程式的字串裡面, 但這樣將網頁與程式混雜在一起的作法在維護上不甚理想, 程式邏輯與網頁版面應該分離, 這是許多網站開發框架標榜的 MVC 架構精神. 其實在 MicroPython 裡也有像 CPython 中 Flask 那樣的 MVC 微框架, 叫做 picoweb, 它可以讓我們在設計 ESP32 網頁伺服器時也能遵循 MVC 精神來架構網站系統, 雖然嵌入式系統的 HTTP 伺服器談不上有甚麼分工的必要 (都是一手包啦), 但至少可以讓系統好開發好維護. picoweb 有發布在 Pypi 網站, 參考 :

#  https://pypi.org/project/picoweb/

不過最新的版本應該會是在 GitHub :

https://github.com/pfalcon/picoweb

由於 picoweb 是第三方套件, 必須安裝後才能使用. MicroPython 的內建模組 upip 可用來安裝第三方套件, 使用前須先匯入 :

import upip

呼叫 install() 方法並傳入欲安裝的套件名稱, 可一次安裝多個套件, 每個套件用空格隔開, 指令格式如下 :

upip.install("package1 package2 package3 ...")

參考 :

upip package manager

在線安裝會自動安裝相依套件, 但前提是 ESP32 開發板必須先連上 Internet 才行, 亦即須先開啟 WiFi 的 STA 介面連線外部 AP 熱點連線 Internet, 參考 :

MicroPython on ESP32 學習筆記 (三) : WiFi 連線

要言之, 只要下列四個指令即可連線附近的 AP 了 :

import network
sta=network.WLAN(network.STA_IF)
sta.active(True)
sta.connect('TonyNote8', 'blablabla')

如果使用 bpi::bit 的 MicroPython 韌體的話不需要 import network, 也不用建立 WLAN 物件, 因為它開機就已經建立了一個 STA 介面的 WLAN 物件 wifi.wlan, 可以直接呼叫 connect() 連線外部熱點上網, 只要一個指令即可, 比較方便 (當然用上面的四個指令也可以) :

wifi.wlan.connect('TonyNote8','blablabla')
關於 rpi::bit 開發板的 WiFi 介面操作參考下面這篇 :

MicroPython on ESP32 學習筆記 (三) : WiFi 連線

ESP32 開發板連網成功後就可以安裝 picoweb 套件了 :


1. 安裝 picoweb : 

在安裝 picoweb 之前先用 os.listdir() 看一下檔案系統根目錄下有哪些東西 :

>>> import os 
>>> os.listdir() 
['boot.py', 'webrepl_cfg.py', 'main.py']

然後匯入 upip 來安裝 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/f7d35f90521e95d9d2307f69ff523133d7d4dd6da7ce1ce0c8
382e7255fa/picoweb-1.7.1.tar.gz
Installing pycopy-uasyncio 3.1.1 from https://files.pythonhosted.org/packages/5f/24/fb08acdd7ebf1626dcdb3cdaf0d3f463c254a4a3aa
2cab70b0ee6562a83d/pycopy-uasyncio-3.1.1.tar.gz 
Installing pycopy-pkg_resources 0.2.1 from https://files.pythonhosted.org/packages/05/4a/5481a3225d43195361695645d78f443952727
8088c0822fadaaf2e93378c/pycopy-pkg_resources-0.2.1.tar.gz 
Installing pycopy-uasyncio.core 2.3 from https://files.pythonhosted.org/packages/6a/96/80a86b1ea4e2b8c7e130068a56f9e8b5bbb2836
9e48de56a753778e14faf/pycopy-uasyncio.core-2.3.tar.gz

可見 picoweb 有三個相依套件. 完成後再檢視一下根目錄底下 :

>>> os.listdir() 
['boot.py', 'webrepl_cfg.py', 'main.py', 'lib'] 
>>> os.chdir('lib') 
>>> os.listdir() 
['picoweb', 'uasyncio', 'pkg_resources.py']   

可見多了一個 lib 子目錄, 裡面就是 picoweb 與其相依模組 uasyncio 與 pkg_resources.py. 用 upip 安裝的第三方套件全部都會放在 /lib 底下. 

>>> import uasyncio   
>>> import pkg_resources    
>>>

匯入這兩個相依模組若沒出現錯誤, 表示 picoweb 已順利安裝完成, 可用 import 匯入 picoweb 套件了. 

>>> import picoweb    
>>>


2. 安裝 pycopy-ulogging 套件 : 

因為 picoweb 執行時需要 logging 系統來記錄事件訊息, 此 pycopy-ulogging 是 CPython 的 logging 套件之子集, 需連網用 upip 自 pypi 網站下載安裝, 參考 :

https://pypi.org/project/micropython-logging/

>>> upip.install('micropython-logging')
Installing to: /lib/
Installing micropython-logging 0.3 from https://micropython.org/pi/logging/logging-0.3.tar.gz

https://pypi.org/project/pycopy-ulogging/

>>> import upip 
>>> 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

安裝好匯入 ulogging 檢視其成員 :

>>> import ulogging 
>>> dir(ulogging) 
['__class__', '__name__', '__file__', 'DEBUG', 'info', 'sys', 'debug', 'getLogger', 'CRITICAL', 'ERROR', 'WARNING', 'INFO', 'NOTSET', '_level_dict', '_stream', 'Logger', '_level', '_loggers', 'basicConfig']
>>> os.getcwd() 
'/lib
>>> os.listdir() 
['picoweb', 'uasyncio', 'pkg_resources.py', 'ulogging.py']



3. 安裝  utemplate 模板套件 :

此 utemplate 模板套件用來支援在模板網頁中嵌入傳入之變數 :

>>> upip.install('utemplate')
Installing to: /lib/         
Installing utemplate 1.3.1 from https://files.pythonhosted.org/packages/75/11/59ac69a862232afc9ffc1cadcc95395cfb7b28c17610edc0
61039d4f03f8/utemplate-1.3.1.tar.gz                                     

到這裡用 picoweb 架設 HTTP 伺服器所需的套件就完備了 :

>>> import picoweb           
>>> dir(picoweb) 
['__class__', '__name__', 'micropython', '__file__', '__path__', 'gc', 're', 'sys', 'uerrno', 'uio', 'utime', 'asyncio', 'pkg_resources', 'utils', 'parse_qs', 'get_mime_type', 'sendstream', 'jsonify', 'start_response', 'http_error', 'HTTPRequest', 'WebApp']

可見 picoweb 提供了許多方法與類別, 其中 WebApp 類別是建立網頁應用程式的主角. 首先以顯示 Hello World 的網頁為例說明如何使用 picoweb 微微框架來架設 HTTP 網頁伺服器.


測試 1 : 顯示 Hello World 網頁

picoweb 的用法跟 Flask 很像, 幾乎只要將 flask 改成 picoweb 即可. 參考 :

Python Web Flask 實戰開發教學 - 簡介與環境建置

首先呼叫 WebApp() 並傳入 __name__ 參數建立一個 WebApp 物件 :

>>> 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']

然後定義根目錄首頁函數 index(), 當收到客戶端要求時會回送 "Hello World!", 接著設定事件紀錄檔 :

>>> @app.route("/") 
... def index(req, resp):   
...     yield from picoweb.start_response(resp)   
...     yield from resp.awrite("Hello World!")   
...
...
...
>>> import ulogging as logging 
>>> logging.basicConfig(level=logging.INFO)   

但最後執行 app.run() 卻出現錯誤訊息 :

>>> app.run(debug=True, host="192.168.43.177", port=80)   
* Running on http://127.0.0.1:8081/
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/lib/picoweb/__init__.py", line 299, in run
  File "/lib/uasyncio/core.py", line 163, in run_forever
  File "/lib/uasyncio/core.py", line 129, in run_forever
  File "/lib/uasyncio/__init__.py", line 31, in add_reader
TypeError: function expected at most 3 arguments, got 4

根據下面這篇, 似乎跟 uasyncio 套件在 pypi 與 GitHub 的版本不一致有關 :

picoweb error when client connect

GiHub 最新版本在此 :

# https://github.com/micropython/micropython-lib

但是我照其說明下載 GitHub 上最新的 asyncio 套件底下的 __init__.py 以及 core.py 用 ampy 上傳到 ESP32 的 /lib 子目錄下反而更糟, 在 import picoweb 時就報錯, why?

https://github.com/micropython/micropython-lib/tree/master/uasyncio/uasyncio
https://github.com/micropython/micropython-lib/blob/master/uasyncio.core/uasyncio/core.py

D:\ESP32\asyncio>ampy --port com8 put core.py /lib/uasyncio/core.py
D:\ESP32\asyncio>ampy --port com8 put __init__.py /lib/uasyncio/__init__.py

看來 picoweb 目前的版本有些問題, 要等開發者搞定後再來測試了.
     
參考 :

How to make ESP32 as HTTP webserver using MicroPython ?
ESP32 MicroPython Tutorial: HTTP Webserver with Picoweb
ESP32 MicroPython教程:使用Picoweb实现HTTP Webserver
https://github.com/pfalcon/picoweb/blob/master/example_webapp2.py#L15


2020-03-06 補充 :

最近更新 MicroPython 韌體至 1.12 版, 結果 picoweb 竟然無法在 ESP32 安裝 , 與 ESP8266 一樣是 list index out of range :

>>> import upip
>>> upip.install('picoweb')
Installing to: /lib/
Error installing 'picoweb': list index out of range, packages may be partially installed

沒有留言 :