# MicroPython on ESP8266 (六) : 檔案系統測試
另外官方技術文件參考 :
# https://docs.micropython.org/en/latest/esp32/quickref.html
# https://docs.micropython.org/en/latest/library/index.html
# DFRobot MicroPython 教學文件 (Good)
有線部分我覺得 ampy 比較好用 (但需要將 ESP 開發板插入 PC 的 USB), 用法摘要整理如下 :
1. 安裝 adafruit-ampy 套件 :
在 Windows 下用 pip3 安裝 adafruit-ampy 套件 :
C:\Users\User>pip3 install adafruit-ampy
Collecting adafruit-ampy
Downloading https://files.pythonhosted.org/packages/59/99/f8635577c9a11962ec43714b3fc3d4583070e8f292789b4683979c4abfec/adafruit_ampy-1.0.7-py2.py3-none-any.whl
Collecting python-dotenv (from adafruit-ampy)
Downloading https://files.pythonhosted.org/packages/57/c8/5b14d5cffe7bb06bedf9d66c4562bf90330d3d35e7f0266928c370d9dd6d/python_dotenv-0.10.3-py2.py3-none-any.whl
Requirement already satisfied: pyserial in c:\python37\lib\site-packages (from adafruit-ampy) (3.4)
Collecting click (from adafruit-ampy)
Downloading https://files.pythonhosted.org/packages/fa/37/45185cb5abbc30d7257104c434fe0b07e5a195a6847506c074527aa599ec/Click-7.0-py2.py3-none-any.whl (81kB)
Installing collected packages: python-dotenv, click, adafruit-ampy
Successfully installed adafruit-ampy-1.0.7 click-7.0 python-dotenv-0.10.3
2. 常用 ampy 指令 :
安裝完畢後就可以用 ampy --help 查詢 ampy 的指令說明 :
D:\Python\test>ampy --help
Usage: ampy [OPTIONS] COMMAND [ARGS]...
ampy - Adafruit MicroPython Tool
Ampy is a tool to control MicroPython boards over a serial connection.
Using ampy you can manipulate files on the board's internal filesystem and
even run scripts.
Options:
-p, --port PORT Name of serial port for connected board. Can optionally
specify with AMPY_PORT environment variable. [required]
-b, --baud BAUD Baud rate for the serial connection (default 115200).
Can optionally specify with AMPY_BAUD environment
variable.
-d, --delay DELAY Delay in seconds before entering RAW MODE (default 0).
Can optionally specify with AMPY_DELAY environment
variable.
--version Show the version and exit.
--help Show this message and exit.
Commands:
get Retrieve a file from the board.
ls List contents of a directory on the board.
mkdir Create a directory on the board.
put Put a file or folder and its contents on the board.
reset Perform soft reset/reboot of the board.
rm Remove a file from the board.
rmdir Forcefully remove a folder and all its children from the board.
run Run a script and print its output.
常用的指令列表如下 (以 COM8 為例) :
指令 | 說明 |
ampy --help | 顯示 ampy 指令格式說明 |
ampy --port COM8 ls | 顯示 COM4 所連接之 ESP32 根目錄下的檔案與子目錄 |
ampy --port COM8 mkdir lib | 在根目錄下建立子目錄 lib |
ampy --port COM8 mkdir lib\lib0 | 在根目錄的子目錄 lib 下建立孫目錄 lib0 |
ampy --port COM8 ls lib | 顯示根目錄的子目錄 lib 下的檔案與孫目錄 |
ampy --port COM8 rmdir lib | 刪除根目錄下的子目錄 lib |
ampy --port COM8 get boot.py | 顯示根目錄下的檔案 boot.py 內容 |
ampy --port COM8 put main.py | 上傳檔案 main.py 至根目錄下 |
ampy --port COM8 put s.txt /tmp/s.txt | 上傳檔案 s.txt 至 /tmp 目錄下 |
ampy --port COM8 run main.py | 執行根目錄下的 main.py |
ampy --port COM8 rm main.py | 刪除根目錄下的檔案 main.py |
ampy --port COM8 reset | 重啟 (reset) 系統 |
注意! 由於 ampy 指令會使用 COM port (連線到 ESP32 上的 UART 埠), 因此下 ampy 指令之前須關閉 PuTTY 連線 (除了 ampy --help 外), 否則會因為抓不到 COM 埠而出現如下錯誤 :
C:\Users\User>ampy --port COM8 ls
Traceback (most recent call last):
File "c:\python37\lib\runpy.py", line 193, in _run_module_as_main
"__main__", mod_spec)
File "c:\python37\lib\runpy.py", line 85, in _run_code
exec(code, run_globals)
File "C:\Python37\Scripts\ampy.exe\__main__.py", line 9, in <module>
File "c:\python37\lib\site-packages\click\core.py", line 764, in __call__
return self.main(*args, **kwargs)
File "c:\python37\lib\site-packages\click\core.py", line 717, in main
rv = self.invoke(ctx)
File "c:\python37\lib\site-packages\click\core.py", line 1134, in invoke
Command.invoke(self, ctx)
File "c:\python37\lib\site-packages\click\core.py", line 956, in invoke
return ctx.invoke(self.callback, **ctx.params)
File "c:\python37\lib\site-packages\click\core.py", line 555, in invoke
return callback(*args, **kwargs)
File "c:\python37\lib\site-packages\ampy\cli.py", line 99, in cli
_board = pyboard.Pyboard(port, baudrate=baud, rawdelay=delay)
File "c:\python37\lib\site-packages\ampy\pyboard.py", line 147, in __init__
raise PyboardError('failed to access ' + device)
ampy.pyboard.PyboardError: failed to access COM8
不過即使 PuTTY 連線 ESP32 中用 ampy --help 也不會出現如上錯誤, 這是因為 ampy --help 只是 ampy 顯示本身資訊而已, 並未真正連線 ESP32 之故.
3. 修改 pyboard.py :
使用 ampy --help 查詢 ampy 的指令說明不會有問題, 但其他的指令卻可能會出現另一個錯誤訊息, 即使 PuTTY 關掉也是一樣. 例如用 ls 指令查詢根目錄下的內容時出現 "could not enter raw repl" 的錯誤訊息 :
D:\Python\test>ampy --port COM8 ls
b'\x1b[0;32mI (439) cpu_start: Pro cpu up.\x1b[0m\r\n\x1b[0;32mI (439) cpu_start: Application information:\x1b[0m\r\n\x1b[0;32mI (439) cpu_start: Compile time: Jun 25 2019 12:38:11\x1b[0m\r\n\x1b[0;32mI (443) cpu_start: ELF file SHA256: 0000000000000000...\x1b[0m\r\n\x1b[0;32mI (449) cpu_start: ESP-IDF: v3.3-beta1-694-g6b3da6b18\x1b[0m\r\n\x1b[0;32mI (455) cpu_start: Starting app cpu, entry point is 0x40082b54\x1b[0m\r\n\x1b[0;32mI (0) cpu_start: App cpu up.\x1b[0m\r\n\x1b[0;32mI (466) heap_init: Initializing. RAM available for dynamic allocation:\x1b[0m\r\n\x1b[0;32mI (472) heap_init: At 3FFAE6E0 len 00001920 (6 KiB): DRAM\x1b[0m\r\n\x1b[0;32mI (479) heap_init: At 3FFB9ED0 len 00026130 (152 KiB): DRAM\x1b[0m\r\n\x1b[0;32mI (485) heap_init: At 3FFE0440 len 00003AE0 (14 KiB): D/IRAM\x1b[0m\r\n\x1b[0;32mI (491) heap_init: At 3FFE4350 len 0001BCB0 (111 KiB): D/IRAM\x1b[0m\r\n\x1b[0;32mI (498) heap_init: At 400921FC len 0000DE04 (55 KiB): IRAM\x1b[0m\r\n\x1b[0;32mI (504) cpu_start: Pro cpu start user code\x1b[0m\r\n\x1b[0;32mI (74) cpu_start: Starting scheduler on PRO CPU.\x1b[0m\r\n\x1b[0;32mI (0) cpu_start: Starting scheduler on APP CPU.\x1b[0m\r\n\x1b[0;32mI (30) modsocket: Initializing\x1b[0m\r\nI (50) wifi: wifi driver task: 3ffe2f08, prio:23, stack:3584, core=0\r\nI (184) wifi: wifi firmware version: 7240fb7\r\nI (184) wifi: config NVS flash: enabled\r\nI (194) wifi: config nano formating: disabled\r\n\x1b[0;32mI (194) system_api: Base MAC address is not set, read default base MAC address from BLK0 of EFUSE\x1b[0m\r\n\x1b[0;32mI (204) system_api: Base MAC address is not set, read default base MAC address from BLK0 of EFUSE\x1b[0m\r\nI (224) wifi: Init dynamic tx buffer num: 32\r\nI (234) wifi: Init data frame dynamic rx buffer num: 32\r\nI (234) wifi: Init management frame dynamic rx buffer num: 32\r\nI (234) wifi: Init management short buffer num: 32\r\nI (234) wifi: Init static rx buffer size: 1600\r\nI (244) wifi: Init static rx buffer num: 10\r\nI (244) wifi: Init dynamic rx buffer num: 32\r\nStarted webrepl in normal mode\r\nMicroPython v1.11-63-gd889def06 on 2019-06-25; ESP32 module with ESP32\r\nType "help()" for more information.\r\n>>> '
Traceback (most recent call last):
File "c:\python37\lib\runpy.py", line 193, in _run_module_as_main
"__main__", mod_spec)
File "c:\python37\lib\runpy.py", line 85, in _run_code
exec(code, run_globals)
File "C:\Python37\Scripts\ampy.exe\__main__.py", line 9, in <module>
File "c:\python37\lib\site-packages\click\core.py", line 764, in __call__
return self.main(*args, **kwargs)
File "c:\python37\lib\site-packages\click\core.py", line 717, in main
rv = self.invoke(ctx)
File "c:\python37\lib\site-packages\click\core.py", line 1137, in invoke
return _process_result(sub_ctx.command.invoke(sub_ctx))
File "c:\python37\lib\site-packages\click\core.py", line 956, in invoke
return ctx.invoke(self.callback, **ctx.params)
File "c:\python37\lib\site-packages\click\core.py", line 555, in invoke
return callback(*args, **kwargs)
File "c:\python37\lib\site-packages\ampy\cli.py", line 194, in ls
for f in board_files.ls(directory, long_format=long_format, recursive=recursive):
File "c:\python37\lib\site-packages\ampy\files.py", line 162, in ls
self._pyboard.enter_raw_repl()
File "c:\python37\lib\site-packages\ampy\pyboard.py", line 192, in enter_raw_repl
raise PyboardError('could not enter raw repl')
ampy.pyboard.PyboardError: could not enter raw repl
我記得以前在 ESP8266 上使用 ampy 時不曾遇到此問題, 而且我在另一台 Win 10 電腦也沒有這個問題, why? 幸好找到下面這兩篇文章, 其中 "markserrano915" 提出的解決辦法經我測試確實可行, 參考 :
# ESP8266 Micropython "Could not enter raw repl" #19
# [ESP8266] MAC OS AMPY上傳、執行程式碼與無法進入RAW REPL
原來只要修改 ampy 安裝目錄下的 pyboard.py 這個檔案, 在 enter_raw_repl() 函數裡面添加一行 time.sleep(2) 即可. 我的 Python 安裝目錄為 C:\Python37, 而 ampy 是安裝在 Lib\site-packages 下, 因此在 C:\Python37\Lib\site-packages\ampy 下可找到 pyboard.py :
先備份原始的 pyboard.py, 然後用文字編輯器開啟此檔, 搜尋 'enter_raw_repl' 這個函數, 裡面有一個 while 迴圈, 其結束之後有一個空行, 在此空行添加 time.sleep(2), 然後存檔即可 :
def enter_raw_repl(self):
# Brief delay before sending RAW MODE char if requests
if _rawdelay > 0:
time.sleep(_rawdelay)
self.serial.write(b'\r\x03\x03') # ctrl-C twice: interrupt any running program
# flush input (without relying on serial.flushInput())
n = self.serial.inWaiting()
while n > 0:
self.serial.read(n)
n = self.serial.inWaiting()
time.sleep(2) #此行原為空行, 加入此指令 (參數不可小於 0.5)
self.serial.write(b'\r\x01') # ctrl-A: enter raw REPL
這樣就可以順利使用 ampy 了 :
D:\Python\test>ampy --port COM8 ls
/boot.py
/webrepl_cfg.py
雖然不一定會遇到這問題, 但凡有此症頭可試試上面的解決辦法.
4. 測試 ampy 指令 :
首先在根目錄下建一個子目錄 lib :
D:\Python\test>ampy --port COM8 mkdir lib
用 ls 查詢果然出現了新的子目錄 :
D:\Python\test>ampy --port COM8 ls
/boot.py
/lib
/webrepl_cfg.py
接著在 lib 下建立孫目錄 lib0 後用 ls 查詢 lib 內容 :
D:\Python\test>ampy --port COM8 mkdir lib/lib0
D:\Python\test>ampy --port COM8 ls lib
/lib/lib0
注意, 在 Windows 下子目錄區隔符號也可以用倒斜線 \. 其次, 如果只查詢根目錄的話不會顯示所有更下層目錄, 必須指定 ls lib 才行. 移除上層目錄會將底下的所有檔案與目錄都刪除 :
D:\Python\test>ampy --port COM8 rmdir lib
D:\Python\test>ampy --port COM8 ls
/boot.py
/webrepl_cfg.py
可見 lib 與底下的 lib0 都不見了. 接著是用 get 讀取檔案內容, 以 boot.py 為例 :
D:\Python\test>ampy --port COM8 get boot.py
# This file is executed on every boot (including wake-boot from deepsleep)
#import esp
#esp.osdebug(None)
import webrepl
webrepl.start()
然後編輯一個 main.py 檔案, 裡面只有 print('ok') 一個敘述, 然後用 put 指令上傳到 ESP32 的跟目錄下 :
D:\Python\test>ampy --port COM8 put main.py
用 ls 顯示根目錄內容可知已上傳成功 :
D:\Python\test>ampy --port COM8 ls
/boot.py
/main.py
/webrepl_cfg.py
然後用 run 指令執行 main.py :
D:\Python\test>ampy --port COM8 run main.py
ok
結果正確顯示 'ok'.
如果是上傳到子目錄底下, 例如將 binascii.py 上傳到 lib 下面 :
D:\Python\test>ampy --port COM3 put binascii.py /lib/binascii.py
用 rm 指令可刪除檔案 main.py :
D:\Python\test>ampy --port COM8 rm main.py
用 ls 指令確定檔案已刪除 :
D:\Python\test>ampy --port COM8 ls
/boot.py
/webrepl_cfg.py
最後用 reset 指令可執行軟開機重啟 :
D:\Python\test>ampy --port COM8 reset
至於無線的 WebREPL 部分參考之前 ESP8266 的測試, 方式是一樣的 :
# MicroPython on ESP8266 (五) : WiFi 連線與 WebREPL 測試
特別注意 :
不要在 main.py 裡面寫無限迴圈, 否則你只能上傳一次 main.py, 因為之後每次 reset 板子都會自動進入無限迴圈佔據整個程序, 無法回應之後的 ampy 呼叫, ampy 指令全部均無反應, 只有重新燒錄韌體可解決.
5. 檔案處理 :
讀寫文字檔使用內建函數 open() 與 close() :
函數 & 方法 | 說明 |
f=open('filename' [, 'mode']) | 開啟檔案 (預設唯讀 mode='r'), 傳回檔案物件參考 |
f.close() | 關閉參考 f 之所指之檔案物件 (關檔) |
開啟模式有如下三種 :
模式 mode | 說明 |
w | 寫入模式 (取代原檔案內容) |
a | 附加寫入模式 (寫入資料附加於原內容尾端) |
r | 唯讀模式 (預設) |
open() 會傳回一個檔案參考物件, 其方法如下 :
方法 | 說明 |
write(string) | 將字串 string 寫入檔案 |
read() | 傳回全部檔案內容 |
readline() | 逐列讀取檔案內容, 傳回目前指標所指之列內容 |
readlines() | 逐列讀取檔案內容, 將各列內容放在串列中傳回 |
讀取並顯示檔案內容有一個便捷的 with open() as 語法, 讀取結束會自動關檔, 不需呼叫 close() :
with open('test.txt', 'a') as lines:
for line in lines:
print(line)
Python 內建模組中支援檔案目錄管理的模組有 os, os.path, shutil, glob.glob, os.walk 等, 參考 :
# Python 學習筆記 : 檔案處理
但 MicroPython 只是一個 CPython 的子集, 只實作了 os 模組的主要方法, 並沒有全部實作這些功能 :
>>> import os.path
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ImportError: no module named 'os'
>>> import shutil
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ImportError: no module named 'shutil'
>>> import os.walk
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ImportError: no module named 'os'
>>> import glob.glob
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ImportError: no module named 'glob'
os 模組只實作了下列方法 :
os 模組方法 | 說明 |
listdir(dir) | 顯示目錄 dir 內容, 例如 '.', '..' 或 'lib' |
mkdir(dir) | 新增目錄 dir, 例如 'lib' |
rmdir(dir) | 刪除目錄 dir, 例如 'lib' 或 '../lib' |
chdir(dir) | 切換至目錄 dir, 例如 'lib' 或 '../lib' |
getcwd() | 顯示目前工作目錄 |
rename(old, new) | 更改檔名或目錄名稱 old 為 new |
remove(file) | 刪除檔案 file |
例如 :
>>> import os
>>> dir(os)
['__class__', '__name__', 'remove', 'VfsFat', 'chdir', 'dupterm', 'dupterm_notify', 'getcwd', 'ilistdir', 'listdir', 'mkdir', 'mount', 'rename', 'rmdir', 'stat', 'statvfs', 'umount', 'uname', 'urandom']
>>> os.listdir('.')
['boot.py', 'webrepl_cfg.py', 'main.py']
>>> os.mkdir('tmp')
>>> os.listdir('.')
['boot.py', 'webrepl_cfg.py', 'main.py', 'tmp']
>>> os.chdir('tmp')
>>> os.getcwd()
'/tmp'
>>> f=open('test.txt', 'w')
>>> f.write('Hello\n')
6
>>> f.write('World')
5
>>> f.close() #必須關檔才能讀取
>>> os.listdir('.')
['test.txt']
>>> os.rename('test.txt','tmp.txt')
>>> os.listdir('.')
['tmp.txt']
>>> with open('tmp.txt', 'r') as lines:
... for line in lines:
... print(line)
...
...
...
Hello
World
>>> os.remove('tmp.txt')
>>> os.listdir('.')
[]
>>> os.chdir('..')
>>> os.getcwd()
'/'
>>> os.rmdir('tmp')
>>> os.listdir('.')
['boot.py', 'webrepl_cfg.py', 'main.py']
>>> os.rename('test.txt','temp/test.txt')
特別是在使用 WebREPL 介面時, 它只能將檔案上傳到 ESP32 的根目錄下, 就可以用 rename() 將其移至其他目錄.
參考 :
# https://docs.micropython.org/en/latest/esp32/tutorial/intro.html
# https://nick.zoic.org/talk/lca2017/
# BugWorkShop - 甲蟲工作室
# MicroPython on ESP32
# MicroPython Programming with ESP32 and ESP8266 eBook
沒有留言:
張貼留言