2017年6月25日 星期日

MicroPython 使用 ampy 突然無法上傳檔案問題

昨天被這個惱人的問題搞了一個下午, 當我正在測試 MicroPython 的 WDT 功能時, 欲將改好的 main.py 程式用 ampy 上傳到 ESP8266 模組時, 卻出現如下錯誤訊息 :

D:\test>ampy --port COM4 put main.py
Traceback (most recent call last):
  File "c:\python36\lib\runpy.py", line 193, in _run_module_as_main
    "__main__", mod_spec)
  File "c:\python36\lib\runpy.py", line 85, in _run_code
    exec(code, run_globals)
  File "C:\Python36\Scripts\ampy.exe\__main__.py", line 9, in <module>
  File "c:\python36\lib\site-packages\click\core.py", line 722, in __call__
    return self.main(*args, **kwargs)
  File "c:\python36\lib\site-packages\click\core.py", line 697, in main
    rv = self.invoke(ctx)
  File "c:\python36\lib\site-packages\click\core.py", line 1066, in invoke
    return _process_result(sub_ctx.command.invoke(sub_ctx))
  File "c:\python36\lib\site-packages\click\core.py", line 895, in invoke
    return ctx.invoke(self.callback, **ctx.params)
  File "c:\python36\lib\site-packages\click\core.py", line 535, in invoke
    return callback(*args, **kwargs)
  File "c:\python36\lib\site-packages\ampy\cli.py", line 208, in put
    board_files.put(remote, infile.read())
  File "c:\python36\lib\site-packages\ampy\files.py", line 139, in put
    self._pyboard.exec_("f.write({0})".format(chunk))
  File "c:\python36\lib\site-packages\ampy\pyboard.py", line 257, in exec_
    raise PyboardError('exception', ret, ret_err)
ampy.pyboard.PyboardError: ('exception', b'', b'Traceback (most recent call last):\r\n  File "<stdin>", line 1, in <module>\r\nOSError: 28\r\n')

也有人跟我遇到一樣的問題, 但他的是 OSError 5 :

I can not copy files to the board #21

這仁兄說他不確定是檔案系統出問題還是韌體壞了, 總之重灌韌體就解決問題了 :

"I have solved the problem by updating MicroPython v1.8.7-672-g11a9620 on 2017-04-28. I'm not sure if it was the file system or bad firmware, maybe a bad parameter when uploading the firmware."

但我遇到的這個 OSError 28 是啥意思呢? 我在下面這篇文章中查到這似乎是 "No space left on device" 之意, 應該跟檔案系統有關, 可以從  os.statvfs('') 指令查看個檔案的 block size 大小 :

https://forum.micropython.org/viewtopic.php?t=2615

所以我也決定重灌, 果真重灌後 ampy 就恢復正常可以上傳檔案了. 我猜應該是我的 ESP-01 檔案系統又完蛋了, 導致 ampy 要上傳檔案時發現檔案系統無可用空間就發生錯誤了.

重灌後我用 os.statvfs('') 查詢記憶體空間狀態結果如下  :

MicroPython v1.9.1-8-g7213e78d on 2017-06-12; ESP module with ESP8266
Type "help()" for more information.
>>> import os
>>> os.statvfs('')
(4096, 4096, 92, 90, 90, 0, 0, 0, 0, 255)

此函數是一個查詢指定路徑之檔案系統狀態之系統呼叫, 傳回值為 10 個元素的元組 :

(f_bsize, f_frsize, f_blocks, f_bfree, f_bavail, f_files, f_ffree, f_favail, f_flag, f_namemax)

其定義是 :

f_bsize: 希望的檔案系統 block 數 (資料區塊)
f_frsize: 基本的檔案系統 block 數
f_blocks: 檔案系統全部 block 數
f_bfree: 可用的 block 數
f_bavail: 一般使用者可用之 block 數
f_files: file node 總數 (節點)
f_ffree: 可用之 file node 總數
f_favail: 一般使用者可用之 file node 數
f_flag: 掛載旗標
f_namemax: 檔案名稱最長長度 (字元)

所以重灌後 (4096, 4096, 92, 90, 90, 0, 0, 0, 0, 255)  全部區塊數為 92 個, 可用為 90 個.

參考  :

https://www.tutorialspoint.com/python/os_statvfs.htm
https://docs.python.org/3.4/library/os.html

不過我在 Python 2.x 版文件中卻看到 os.statvfs() 已經被棄置不用了 :

https://docs.python.org/2/library/statvfs.html

"Deprecated since version 2.6: The statvfs module has been removed in Python 3."

奇怪, MicroPython 是 Python 3 也還有這個函數哩!

MicroPython 的檔案系統還可以用 os.stat() 來觀察, 例如 :

for f in os.listdir():   
    print('File: {} stats: {}'.format(f, os.stat(f)))  

這裡利用拜訪 os.listdir() 傳回的串列, 將其元素 (檔案名稱) 傳給 os.stat() 即可顯示該檔案之狀態. 測試結果如下 :

>>> for f in os.listdir():
...     print('File: {} stats: {}'.format(f, os.stat(f)))
...
...
...
File: boot.py stats: (32768, 0, 0, 0, 0, 0, 160, 5346, 5346, 5346)
File: webrepl_cfg.py stats: (32768, 0, 0, 0, 0, 0, 16, 5346, 5346, 5346)
File: main.py stats: (32768, 0, 0, 0, 0, 0, 394, 602, 602, 602)
File: lib stats: (16384, 0, 0, 0, 0, 0, 0, 6288, 6288, 6288)
File: test.txt stats: (32768, 0, 0, 0, 0, 0, 5, 9522, 9522, 9522)

這個 os.stat() 函數會傳回 10 元素的元組, 其中第一個元素標示此為檔案或目錄, 在 MicroPython 檔案為 32768, 目錄為 16384. 第七個元素為檔案大小 (bytes 數), 其中 lib 目錄大小為 0 byte (空目錄).

參考 :

https://forum.micropython.org/viewtopic.php?t=2615
https://docs.python.org/3/library/os.html#os.stat

2017-06-26 補充 :

如果重灌韌體後於 REPL 中使用 os.listdir() 指令查看檔案列表發現有一堆 \x00 如下 :

>>> import os
>>> os.listdir()
['\x00\x00\x00\x00\x00\x00\x00\x00.\x00\x00\x00', '\x00\x00\x00\x00\x00\x00\x00\x00.\x00\x00\x00',
....... (一堆 \x00)


這表示檔案系統已經局部毀損了, 重灌韌體並無法解決檔案系統受損問題, 必須利用下列程式碼重建檔案系統 :

>>> from flashbdev import bdev
>>> uos.VfsFat.mkfs(bdev)
>>> vfs=uos.VfsFat(bdev)
>>> with open("/boot.py", "w") as f:
...     f.write("""\
...     import gc
...     gc.collect()
...     """)
...     f.close()
...

...

重建完成後, 那些 \x00 就不見了, 檔案系統下就只有一個 boot.py :

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

boot.py 內容為 :

import gc
gc.collect()

然後上傳基本架構的 main.py :

#main.py
import network
def connect_wifi(ssid, pwd):
    wlan=network.WLAN(network.STA_IF)
    wlan.active(True)
    if not wlan.isconnected():
        print('connecting to network ...')
        wlan.connect(ssid, pwd)
        while not wlan.isconnected():
            pass
    print('Connected:', wlan.ifconfig())

def get_ip():
  return (network.WLAN(network.STA_IF).ifconfig()[0],
  network.WLAN(network.AP_IF).ifconfig()[0])

def ap_on():
  network.WLAN(network.AP_IF).active(True)

def ap_off():
  network.WLAN(network.AP_IF).active(False)


參考 :

MicroPython v1.9.1 版韌體測試

沒有留言 :