【備忘録】【ESP32】MicroPythonでSDカードにpythonコードを書き込み、SDカード上のpythonコードを実行する

# 0. はじめに

（ラズパイが入手困難なこともあり）今更ながら、最近、ESP32にハマっています。今回は、
MicroPythonでSDカードの読み書きとSDカード上のpythonコードを実行する方法を、備忘録としてまとめておきます。

なお、SDカードのアクセスはSPI接続です。



# 1. SDカードアダプタ

数年前に製作した、マイクロSDカードのSDカードアダプタに直接ピンヘッダをハンダ付けした物を使用します。3.3VのMCUなら直接接続できます。

<img width=400 src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/283293/fe8282d1-8d01-0c98-a729-d92d462f878f.png">

2.54mmピッチのピンヘッダがギリフィット。各ピンの意味は下表の通り。
SPIでアクセスする場合は1〜7番ピンのみ接続するため、8・9番ピンのハンダ付けは不要です。

|ピン|SDモード|SPIモード|
|:-:|:-:|:-:|
|1|CD / DAT3|CS|
|2|CMD|MOSI|
|3|VSS1|VSS1<br>(GND)|
|4|VDD|VDD|
|5|CLK|SCK|
|6|VSS2|VSS2<br>(GND)|
|7|DAT0|MISO|
|8|DAT1|-|
|9|DAT2|-|


# 2. ESP32との接続

今回は**slot=2**を使用し、[ESP32の推奨ピン](https://docs.micropython.org/en/latest/library/machine.SDCard.html?highlight=sdcard#esp32)に接続します。


>「MISOに指定したピンをプルアップする必要がある」という記事がありますが、無くても問題なく読み書きできます。ただし、これが原因でSDカードやMCUが破損しても責任は持てませんので、<b>自己責任</b>でお願いします。


|SDカードアダプタ|SPI|ESP32|
|:-:|:-:|:-:|
|1|CS|5|
|2|MOSI|23|
|3|GND|GND|
|4|3v3|3.3V|
|5|SCK|18|
|6|GND|GND|
|7|MISO|19|

<img width=400 src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/283293/8a605fe5-3ce3-98f6-5f1b-203a62c627e3.png">


# 3. SDカードのハンドリング

FAT32でフォーマットしたまっさらなマイクロSDカードを装着しESP32をPCに接続。念のためリセットSWポチ。


ターミナル等からESP32のREPLにアクセス

```python
ets Jul 29 2019 12:21:46

rst:0x1 (POWERON_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)
configsip: 0, SPIWP:0xee
clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
mode:DIO, clock div:2
load:0x3fff0030,len:4540
ho 0 tail 12 room 4
load:0x40078000,len:12788
load:0x40080400,len:4176
entry 0x40080680
MicroPython v1.19.1 on 2022-06-18; ESP32 module (spiram) with ESP32
Type "help()" for more information.
>>> 

>>> from machine import SDCard
>>> import os
>>> sd = SDCard(slot=2) #slot=2を使用、ESP32の推奨ピンに接続のためパラメタ省略
>>> os.mount(sd, '/sd')
>>> os.mkdir('/sd/lib') #libサブフォルダ作成
>>> os.chdir('/sd/lib')
>>> os.listdir()
[]     #当然ながら空っぽ

#pythonコードを書き込む
>>> f = open('hello.py', 'w')
>>> f.write('print("hello!")')
15
>>> f.close()
>>> 

>>> os.listdir()
['hello.py']     #ファイルが作成された
>>> 

#ファイルを読み出す
>>> f.close()
>>> f = open('hello.py', 'r')
>>> print(f.read())
print("hello!")　　#読めました
>>> f.close()

>>> import sys
>>> sys.path
['', '.frozen', '/lib']
>>> sys.path.append('/sd/lib') #pathに追加
>>> sys.path
['', '.frozen', '/lib', '/sd/lib']
>>> 

#SDカード上のhello.pyを実行
>>> import hello
hello!
>>> 
```

以上のコードで、**①SDカードのマウント**、**②テキストファイル書き込み**、**③テキストファイル読み込み**、**④SDカード上のpythonコード実行** ができました。


```python:コード全体
from machine import SDCard
import os
sd = SDCard(slot=2)
os.mount(sd, '/sd')
os.mkdir('/sd/lib')
os.chdir('/sd/lib')
os.listdir()

f = open('hello.py', 'w')
f.write('print("hello!")')
f.close()

os.listdir()

f = open('hello.py', 'r')
print(f.read())
f.close()

import sys
sys.path.append('/sd/lib')
sys.path

import hello
```


# 4. SDカードアクセスの性能

以下のコードにて、SDカード上のテキストファイルのアクセス性能を調べてみた。

- 入力ファイル：1行100文字（UTF-8、改行コード含め101バイト）x 12,800行 ＝ 1,292,800バイト（約1.2MB）`/sd/work.txt` 
- 入力メソッド：readline()
- ファイルopen〜closeまでの時間

```python:read_file.py
import utime
start = utime.ticks_us()
f = open('/sd/work.txt', 'r')
cnt = 0
while line := f.readline():
    cnt += 1

f.close()
print('start', start)
print('end', end := utime.ticks_us())
print('duration', end - start)
print('cnt', cnt)
```

2回測定し、平均で約10.5秒であった。
読み込み性能としては、**約120KB/秒**。かなり遅い？


```sh:結果
# 1回目
start 54995341
end 65450884
duration 10455543
cnt 12800
# 2回目
start 90385829
end 100999955
duration 10614126
cnt 12800
```

[Seeeduino XIAO SAMD21](https://www.seeedstudio.com/Seeeduino-XIAO-Arduino-Microcontroller-SAMD21-Cortex-M0+-p-4426.html)で同じファイルの入力を測定したところ、約20.9秒でした。ESP32の方が2倍速い。

この性能の差は？ と考えたとき、両デバイスのCPUクロック数の違いですかね？
Seeeduino XIAOは**48MHz**、ESP32は**160MHz**か**240MHz**。ちなみに、上記の性能は160MHzのときで、デフォルトは**160MHz**のようです。

```python
import machine
machine.freq()
160000000
```

240MHzに変更して再度測定したところ、秒8.3秒に縮まった。**約152KB/秒**と160MHz時の約27％アップ。

```python
machine.freq(240000000) # 240MHzに設定
```

240MHzにすることで消費電力も多くなるでしょうが、プログラムからCPUクロック数を動的に変えられるとは驚きです。



# 5. おわりに

SDカードのアクセス速度は速くはないが、大量の情報を保持できるというメリットがありますね。ただ、ESP32ならクラウドへ保存する方が一般的なのかも？

SDモード（4ビットバス）接続の場合は、どのくらいアクセスが速くなるのだろうか？　別途、測定してみたいと思います。


**以上**