ESP32-CAMを主にストリーミング用途として使っていたんだけども、microSDカードスロットを使ってみることに。
ESP32-CAMのmicroSDスロットはHS2に接続されているっぽいのでSDMMCライブラリを使用して取り扱うことができるっぽい。HS2に接続しているってことは比較的高速でアクセスできる?
SDカードに写真を保存する方法は色々紹介されているけど、せっかくWiFiにつながるモジュールなのでWiFiからSDカードにアクセスしたいということで色々調べてみた。
一番簡単そうなのはHTTPで一覧を作成してダウンロードする方法。アップロードも一応できるっぽいし。ただこれだと一個ずつダウンロードするのが面倒そう…
次にWebDAVもESP32用のライブラリが出ていた。Windows上でネットワークの場所の追加をしないといけないのが面倒だけど、一応エクスプローラから使用できるのでファイル操作が楽そう。
あとはFTPサーバのライブラリを作ってる人もいた。これならエクスプローラからそのまま行けそうなので便利かも。ということで試してみることに。
今回使用させてもらったのはESPFtpServerライブラリ。
どうやらESP32上で動かせるFTPサーバは色んな方々が開発を進めていてくれてるようで、それをまとめてくれたもののよう。こちらであればSD_MMCライブラリでも内蔵フラッシュメモリ上のSPIFFSでもFTPサーバでファイル転送ができるらしい。
早速サンプルを使用してみることに。
ライブラリのインストールは手動にしか対応していなのでESPFtpServerからCodeのDownload ZIPでダウンロードしてくる。解凍したら中にはいっているフォルダをArduinoのlibrariesにぶち込む。
このライブラリのサンプルスケッチのフォルダ階層がおかしくてちゃんと認識されないので、"ESPFtpServer-master\examples"の中に"ESPFtpServer"っていうフォルダを作って"ESPFtpServer.ino"を作成したフォルダにぶち込む。
これでサンプルスケッチが認識されるようになった。(Arduinoはinoファイルが同じファイル名のフォルダの中に入っていないとダメな仕様)
スケッチ例→ESPFtpServer→ESPFtpServerでサンプルスケッチを開く。
上の方でファイルシステムを設定するのだけども、今回はESP32-CAM上のmicroSDスロットを使用して試してみたので、define FS_LITTLEFSをコメントアウトして#define FS_SD_MMCのコメントアウトを外して有効にした。
ファイルの更新日時用に用意されてるNTPの設定を日本に合わせて変更する。
const char* ntpServer = "ntp.nict.jp"; const long gmtOffset_sec = 3600 * 9; const int daylightOffset_sec = 0; struct tm timeinfo;
45~48行目あたりにある設定をこんな感じに変更した。
あとはssidとpasswordを設定してとりあえず書き込み。
microSDカードはTeamの16GBを使用した。SDメモリカードフォーマッターでフォーマット済み。SD_MMCライブラリは今のところexFAT非対応なので64GB以上のSDXCには対応していない。でも試しに64GBのmicroSDをFAT32でフォーマットしてみたら使えるようになった。そこまで容量いらないと思うけど…
あとはエクスプローラから"ftp://ESP32のIP"にアクセスするとFTPフォルダエラーが出た。とりあえずOKを押して
エクスプローラの白いところを右クリックしてログイン方法を押してユーザー名とパスワードにesp32と入れてログオンするとフォルダが表示される。(ftp://esp32:esp32@192.168.11.86/のようにユーザー名とパスワードをアドレスに含めてしまってても行ける。)ESP32-CAMの場合フラッシュのLEDにSDMMCで使われてるピンが接続されているため、アクセスするたびにフラッシュが点滅する。
ファイル転送スピードは書き込みは500kb/sぐらいで読み込みが1MB/sぐらい出ていた。文字コードの問題があるみたいで、ファイル名に結構制約がある。試しにFileZillaで文字コードをUTF-8固定にすると日本語のファイル名とかまで行けたのでWindows標準のエクスプローラだとエンコードがうまく設定できてないのかな?
// // OPTS // else if (!strcmp (command, "OPTS")) { for (uint8_t i=0; i <= strlen (parameters); i++){ if(parameters[i] > 0x40){ parameters[i] = parameters[i] & ~(0x20); } } if (!strcmp (parameters, "UTF8 ON")) { client.println ("200 Always in UTF8 mode."); //client.println ("451 Unable to accept OPTS UTF8"); } else{ client.println ("500 Unknown command " + String (parameters)); } }
ということでESPFtpServer.cppを少し修正して"OPTS UTF8 ON"に反応するように変更してみた。807行目あたりにこんな感じでOPTSコマンドへの応答を返すように変更して、Windows 10のエクスプローラからアクセスしてみた。
しかしやっぱりWindowsのエクスプローラ内蔵のFTPクライアントが悪いのか、日本語ファイル名が化ける…他のツールならUTF-8モードにすれば何でも行けるんだけどなぁ…
Windows標準のFTPクライアントの仕様を見ているとどうやらLISTコマンドがMS-DOSスタイルにしか対応していないようで、それですべてのファイルがショートカットみたいに見えるようになることがあるらしい。
while (file) { String fname = file.name(); time_t ftime = file.getLastWrite(); struct tm* ptm = localtime(&ftime); int pos = fname.lastIndexOf("/"); if (pos >= 0) fname.remove(0, pos + 1); char dateStr[9]; char timeStr[8]; int hour = ptm->tm_hour; const char* ampm = "AM"; if (hour >= 12) { ampm = "PM"; if (hour > 12) hour -= 12; } if (hour == 0) hour = 12; sprintf(dateStr, "%02d-%02d-%02d", ptm->tm_mon + 1, ptm->tm_mday, (ptm->tm_year + 1900) % 100); sprintf(timeStr, "%02d:%02d%s", hour, ptm->tm_min, ampm); if (file.isDirectory()) { sprintf(buffer, "%s %s <DIR> %s", dateStr, timeStr, fname.c_str()); } else { sprintf(buffer, "%s %s %12lu %s", dateStr, timeStr, (unsigned long)file.size(), fname.c_str()); } data.println (buffer); nm ++; file = dir.openNextFile (); }
ESPFtpServer.cppのLISTコマンドのESP32用の部分をごっそりUNIXスタイルからMS-DOSスタイルに書き換えてみた。するとエクスプローラーでもちゃんとファイルが見える!ちゃんとフォルダ内のファイルも確認できた。文字コードの問題じゃなかったのか…
色々確認しているときにftpコマンドで接続してみたんだけど、ftpコマンドでopenまではいいんだけど、ユーザー名を入れた瞬間に切断される…
おそらくこのせいでエクスプローラから一発目アクセスしたときにFTPフォルダエラーが出たのかな?ソースを確認してみるとuserIdentity()の戻り値を待っているところでユーザー名が入っていなかったりするとcmdStatus=0で切断されるっぽい。
if (cmdStatus == 3) { // Ftp server waiting for user identity if (userIdentity ()) { cmdStatus = 4; } else { cmdStatus = 3; } }
とりあえずこんな感じでESPFtpServer.cppの100行目付近をいじってcmdStatus=3に変更してみた。これでftpコマンドでもうまく接続できるようになった。これでも何回かミスってるとタイムアウトのほうが効いてくるので問題ないと思う。
ついでにanonymousログインも追加してみた。
if (_FTP_USER == "anonymous"){ client.println ("230 Anonymous login successful"); return true; }
これをESPFtpServer.cppの中のboolean FtpServer::userPassword ()のすぐ下に差し込むと、スケッチでuser名をanonymousにしておけばパスワードは何でも通ってしまうというアレ。
最初にSoftAPモードでテストしていたときにデータ転送できなかった謎も解けた。これはPASVモードのときにサーバのIPアドレスが0.0.0.0になってしまうためだった。300行目ぐらいにある、
dataIp = WiFi.localIP ();
と、なっている箇所を
if (WiFi.getMode() == WIFI_MODE_AP) { dataIp = WiFi.softAPIP(); }else{ dataIp = WiFi.localIP(); }
のように修正した。SoftAPのときはlocalIP()じゃなくてsotAPIPじゃないとサーバ側のIPアドレスが0.0.0.0で報告してしまうためうまく動かなかった。これでSoftAPでもSTAモードでも動作するはず。SoftAPのときはNTPからRTCを更新できないので、Webサーバも一緒に立てて先日の技が使えるかも。
ちなみにサンプルのままでも便利だけど、IPがわかりにくいのでmDNSを追加しておくと便利かも。
他のESP32用のFTPサーバはスケッチ本体だったりしていたのでライブラリになっていることによりほかのスケッチと統合が楽になって扱いやすいかも。これでESP32-CAMでタイムラプスを撮ってそれをFTPでダウンロードができそう。