grbl1.1+Arduino CNCシールドV3.5+bCNCを使用中。
BluetoothモジュールおよびbCNCのPendant機能でスマホからもワイヤレス操作可能。
その他、電子工作・プログラミング、機械学習などもやっています。
MacとUbuntuを使用。

CNCマシン全般について:
国内レーザー加工機と中国製レーザー加工機の比較
中国製レーザーダイオードについて
CNCミリングマシンとCNCルーターマシンいろいろ
その他:
利用例や付加機能など:
CNCルーター関係:



*CNCマシンの制作記録は2016/04/10〜の投稿に書いてあります。


ラベル SPIFFS の投稿を表示しています。 すべての投稿を表示
ラベル SPIFFS の投稿を表示しています。 すべての投稿を表示

2017年10月20日金曜日

ESP32:SPIFFSファイルアップローダー

ようやくESP32のSPIFFファイルアップローダーが出来上がったようです。これでESP8266のように、Webサーバーをたてたときにindex.htmlなどの付属する外部ファイルを、Arduino IDEを使ってESP32内にアップロードできるようになりました。
ファイルアップローダーについてのページはこちら

インストール方法:
リンク先のこのページから、ESP32FS_v0.1.zipをダウンロードし解凍。
Macの場合は、toolsディレクトリを以下のようにつくって、そこに入れろということです。

~/Documents/Arduino/tools/ESP32FS>tool>esp32fs.jar

以前、ESP8266のときには、Arduino.app内にインストールしたので、Arduino.appを右クリックして「パッケージの内容を表示」してから、
Arduino.app>Contents>Java>tools>ESP32FS>tool>esp32fs.jar
となるようにインストールしても大丈夫そうです。
ただし、今回のアップローダーをインストールする前に、最新のArduino-esp32ライブラリをインストールし直したほうがいいかもしれません(古いのを捨てた後、再度このページからインストールし直しました)。

インストール後、Arduino IDEでさっそく試してみました。
まずは、「スケッチの例>SPIFFS>SPIFFS_Test」のサンプルを開き、いくつか内容を書き換えて別名保存します。とりあえず、HTMLファイル(index.html)だけをアップロードしてみました。

ファイルの追加とアップロード:
用意したindex.htmlを「スケッチ>ファイルを追加」で追加します。
追加されたかどうか、「スケッチ>スケッチのフォルダを表示」で確認。
そうすると、こんな感じで自動生成されたdataフォルダ内にindex.htmlが追加されています。
あとは、この状態でindex.htmlをESP32内へアップロードします。
ファイルアップローダーがきちんと所定の場所へインストールされていれば、上のように「ESP32 Sketch Data Upload」が表示されているので、これでアップロードします。このへんはESP8266のときと同じ要領

アップロードされた内容のチェック:
アップロードされた内容をチェックするために、以下のreadFile()のところを少し変更。file.readString()を使って読み込み、シリアル出力させてindex.htmlの中身をチェック。

void readFile(fs::FS &fs, const char * path){
    Serial.printf("Reading file: %s\n", path);

    File file = fs.open(path);
    if(!file || file.isDirectory()){
        Serial.println("Failed to open file for reading");
        return;
    }

    Serial.print("Read from file: ");
    while(file.available()){
        //Serial.print(file.read());
        Serial.print(file.readString());
    }
}

あらかじめ、String contentsなどと文字列型変数を用意しておいて、Serial.print(file.readString());の代わりに、contents=file.readString();とすれば変数contentsへ文字列として渡すことができるかと思います。
そして、loop()内にシリアル通信キー入力で内容確認できるようにしてみました。

int val;

void loop(){
  if(Serial.available()>0){
    val=Serial.read();
    if(val=='l'){
      listDir(SPIFFS, "/", 0);
    }else if(val=='r'){
      readFile(SPIFFS, "/index.html");
    }else if(val=='d'){
      deleteFile(SPIFFS, "/index.html");
    }
  }
}

このようにサンプルのSPIFFS_Test.inoを多少内容変更して別名保存後ESP32へアップロード。
シリアルモニターを開いて、'l'でルートディレクトリ内を表示、'r'でindex.htmlの読み込み、'd'でindex.htmlを消去。
そうすると、シリアルモニターの画面では、

ルート内ディレクトリの表示(1〜2行目)、
index.htmlの読み込みと内容表示(3〜18行目)、
index.htmlの消去(20〜21行目)、
再度ルート内ディレクトリの表示(22行目)
という順番で出力され、問題なく機能しているようです。
これで、やっとESP8266同様ファイルアップロードが使えるようになったわけですが、同時にMicropythonのバイナリデータもアップロードできるようになったので、もしかするとMicropythonを搭載したほうがいろいろと便利かもしれません。

最近はもっぱらDeep Learningばかりで、ESP32やRaspberry Pi Zero Wもあまりやっていませんが、ESP32もけっこう環境が整ってきたようなので、そのうち何かに使ってみようと思います。

AliExpress.com Product - Lolin ESP32 OLED Module For Arduino ESP32 OLED WiFi + Bluetooth Dual ESP-32 ESP-32S ESP8266 OLED Module Board1206円(送料無料)この液晶画面がついているESP32 Lolin(Wemos)が便利そうです。

2017年4月30日日曜日

ESP32:Webサーバから外部ファイルを読み込み

ESP32も少しずつ試してはいるのですが、SPIFFSファイルアップロードの仕方がわからない。ESP8266の場合、HTMLファイルなどをアップロードするためには以前やったように「esp8266fs.jar」を使えばよかったのですが、ESP32には「esp8266fs.jar」のようなものはまだないのでしょうか?
Arduino IDEには、「ライブラリのインポート」では一応「FS」があり、インポートはできるようになっているけれども、ファイルのアップロード自体どうすればいいのか?
追記:2017年10月
どうやらESP32のファイルアップローダーできたみたいです。ファイルアップローダーの使い方についてはこちらへ

インクルードされたライブラリもESP8266のとは違うので、ESP8266と同じ要領ではできなさそう。
ネットで調べてみても、これといったものがないし、フォーラムにもいくつか載っていたけれども、具体的にどうすればいいのかサンプルなどがないのでわかりにくい。
Virtual filesystem compornent(vfs)
partitions
この辺を見ればいいのかもしれないけれども、かなりわかりにくい。SPIFFS用のパーティションのオフセットは0x210000らしい。
おそらく、SPIFFSやFATなどのファイルシステムAPIをパーティション0x210000へflashするとつかえるようになるのかもしれない。Lua-RTOS-ESP32が唯一使えるというような投稿もあるけど、具体的なサンプルなどがないのでわからない。だれか成功例のサンプルをどこかに挙げてもらいたいですね。その他、JavaScriptをベースにするEspruinoやDuktape、あるいはMicroPythonを載せてしまうという手もあるけど。あとは、サンプルのあるSDカードから読み込む方法となるのかも。

ESP8266に関しては、かなりのサンプルが見つかるので扱いやすいけど、ESP32のほうはまだそんなにないというか、仕組みもESP8266よりも複雑みたいで、そのぶん扱いにくいのかも。しかたないのでしばらく待つことにして、かわりに以下の方法で。

他のWebサーバからESP32のRAMへ読み込ませる:
ソース(C言語/Arduino言語)の中にHTML(CSS、JavaScript)を埋め込んで毎回アップロードするのは面倒なので、どうせ読み込んだHTMLデータはString変数へ入れられるので、わざわざROMにアップロードしておかなくてもいいはず。電源を落とせば、RAMの場合消えてしまうけど、毎回電源を入れるたびに、setup()内で外部から読み込ませれば同じことかもしれない。
ということで、ESP32からHTTPリクエストを出して他のサーバに置いてあるHTMLファイルを読み込ませようという実験をしてみました。

まずHTMLファイルをアップロードできる無料Webサーバに、今回の場合data.htmlというファイルをあげておきます。data.htmlには、ESP32(Webサーバ)にリクエストがあった場合のレスポンス用HTMLが書いてあります。ブラウザ上にスイッチのON/OFFなどの画面が出てきて制御できるということになります。

#include <WiFi.h>
#include <ESPmDNS.h>
#include <WiFiClient.h>
#define ledPin 23

const char* ssid = "*****";
const char* password = "*****";

const char* host = "www.abc.com";//無料サーバのホスト
String url ="/esp32/data.html";//読み込ませるデータのurl
String header = "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\n";
String footer = "\r\n\r\n";
String webPage = "";
const uint16_t port = 80;

WiFiServer server(port);

void setup(void){  
    Serial.begin(115200);
    pinMode(ledPin, OUTPUT);
    //webPage += "<!DOCTYPE HTML>\r\n<html>No Contents, yet.</html>\r\n\r\n";

    WiFi.begin(ssid, password);

    while (WiFi.status() != WL_CONNECTED) {
        delay(500);
        Serial.print(".");
    }
    //固定IPアドレス
    WiFi.config(IPAddress(192,168,3,20),IPAddress(),IPAddress());
    Serial.print("Connected to ");
    Serial.println(ssid);
    Serial.print("IP address: ");
    Serial.println(WiFi.localIP());
    
    //DNSサーバ名(esp32.local)
    if (!MDNS.begin("esp32")) {
        Serial.println("Error setting up MDNS responder!");
        while(1) {
            delay(1000);
        }
    }
    Serial.println("mDNS responder started");

    server.begin();
    Serial.println("TCP server started");
    MDNS.addService("http", "tcp", port);
    httpRequest();//ここで外部ファイルをロードしておく
}

void loop(void){
    WiFiClient client = server.available();
    if (!client) {
        return;
    }
    Serial.println("");
    Serial.println("New client");

    while(client.connected() && !client.available()){
        delay(1);
    }

    String req = client.readStringUntil('\r');

    int addr_start = req.indexOf(' ');
    int addr_end = req.indexOf(' ', addr_start + 1);
    if (addr_start == -1 || addr_end == -1) {
        Serial.print("Invalid request: ");
        Serial.println(req);
        return;
    }
    req = req.substring(addr_start + 1, addr_end);
    Serial.print("Request: ");
    Serial.println(req);
    client.flush();

    String s;
    if (req == "/"){//ルートへアクセスした場合はロードしていないHTMLを表示
        IPAddress ip = WiFi.localIP();
        String ipStr = String(ip[0]) + '.' + String(ip[1]) + '.' + String(ip[2]) + '.' + String(ip[3]);
        s = header;
        s += "<!DOCTYPE HTML>\r\n<html>Hello from ESP32 at ";
        s += ipStr;
        s += "<div><a href=\"/on\">TEST: ON</a></div>";
        s += "<div><a href=\"/off\">TEST:OFF</a></div>";
        s += "<div><a href=\"/load\">LOAD DATA</a></div></html>";
        s += footer;
    }else if(req == "/load"){//ロードする画面
        httpRequest();//外部サーバへリクエスト
        s = header;
        s += "<!DOCTYPE HTML>\r\n<html>";
        s += "<div><a href=\"/on\">TEST: ON</a></div>";
        s += "<div><a href=\"/off\">TEST:OFF</a></div>";
        s += "<div>HTML Data Loaded</div></html>";
        s += footer;
    }else if(req == "/on"){//ロードされた画面
        s = header;
        s += webPage;
        s += footer;
        digitalWrite(ledPin, HIGH);
    }else if(req == "/off"){//ロードされた画面
        s = header;
        s += webPage;
        s += footer;
        digitalWrite(ledPin, LOW);
    }else{
        s = "HTTP/1.1 404 Not Found\r\n\r\n";
    }
    client.print(s);
}

void httpRequest(){//ロード用のリクエスト
    Serial.print("connecting to ");
    Serial.println(host);
    WiFiClient myclient;

    if (!myclient.connect(host, port)) {
        Serial.println("connection failed");
        delay(5000);
        return;
    }

    myclient.print(String("GET ") + url + " HTTP/1.1\r\n" +
                 "Host: " + host + "\r\n" +
                 "Connection: close\r\n\r\n");
    
    delay(100);
    
    boolean start=false;
    while(myclient.available()){
        String line = myclient.readStringUntil('\r');
        if(line.indexOf("<!DOCTYPE html>")>0){
          start=true;
        }
        if(start){
            webPage+=line;
        }
    }
    Serial.println("closing connection");
    myclient.stop();
}

サンプルのつぎはぎなので、もしかすると矛盾があるかもしれないけれども、一応機能しました。
ESP32の電源を入れると、setup()内の最後に書いてあるhttpRequest()によって外部サーバにリクエストを出して、そのURLのHTMLを読み込みます。読み込んだ内容はwebPageという変数に入れてしまいます。これが表示用のHTMLデータとなります。
今回の場合はESP32がWebサーバとして機能しており、静的IPアドレス192.168.3.20に固定され、それ以下のディレクトリにアクセスすると:

/(ルート):もともとソースに書き込まれた単純なHTMLを表示
/load :再度HTMLデータを読み込むとき(表示はもともとのHTML)
/on :ロードしたHTMLによる画面表示、23番ピンに接続したLEDをON
/off :ロードしたHTMLによる画面表示、23番ピンに接続したLEDをOFF

となります。
外部サーバからのレスポンスには「HTTP/1.1 200 OK」などのheaderの情報が含まれるので、
line.indexOf("<!DOCTYPE html>")
をつかってHTMLデータの始まりを確認してから、変数webPageに入れていきます。今回の場合は、CSSやJavaScriptもHTMLファイル内に書き込んであるので、このHTMLファイルだけを読み込めばOKということにしています。

ちょっとだけHTMLを書き換えて確かめたいときは、このやり方のほうが早いのかもしれません。毎回ESP32にアップロードするのは確かに面倒。こうすれば、ESP32にはHTML(CSSやJavaScript)データをアップロードしなくてもすむので楽かなと。
ESP8266の場合も、ファイルアップローダを使うとけっこう時間(5分とか)かかるので、この方法のほうが早そうです。
AliExpress.com Product - Official DOIT ESP32 Development Board WiFi+Bluetooth Ultra-Low Power Consumption Dual Core ESP-32 ESP-32S ESP 32 Similar ESP8266976円(送料無料)最近だとAliExpressなら1000円以下になったみたいです。技適マークつきで、秋月のと同じタイプだと思います。

2017年4月17日月曜日

ESP8266ファイルシステム:SPIFFSについて

ESP8266にHTMLファイルなどの外部ファイルを添付する際に、SPIFFSを使いますが、いまいちよくわからない部分もあったので、覚書として書いておきます。Arduino IDEでのやり方です。

Toolのインストール(下準備):
基本はこのESP8266のGitに書いてあるので、ここを見ればだいたい分かるはず。
まず、アップロードTool「esp8266fs.jar」をダウンロード(こちらのサイトここから)。
現在のバージョンは0.3.0のようです。
ダウンロードした「ESP8266FS-0.3.0.zip」を解凍。
Macならアプリケーション>Arduinoを右クリックで「パッケージの内容を表示」。
Arduino>Contents>Java>tools>ESP8266FS>tool>esp8266fs.jar
となるような場所にファイルもしくはフォルダごと移動し、Arduino IDEを再起動で準備OK。

外部ファイルのアップロード:
Arduino IDEのメインのソースとなるinoファイルに添付したい外部ファイル(HTMLファイルなど)を取り込みます。
「ファイルを追加...」で任意の場所から外部ファイルを選択(今回はデスクトップ上にあるindex.htmlを選択)。


 外部ファイルを追加したら、「スケッチのフォルダを表示」で中身確認。

こんな感じ↑で、メインのinoファイルと同じディレクトリにdataフォルダが自動的に作成されており、その中に追加した外部ファイル(この場合index.html)が入っていることを確認できます。

つぎに、先ほどのツールを使って外部ファイルだけ(この場合index.htmlファイルだけ)をESP8266へ先にアップロードしておきます。

Arduino IDEのコンソール画面には、こんな感じでアップロード状況が表示されます。
3%くらいずつアップロードしているようで、かなり時間かかります。

この段階では、まだ外部ファイルだけのアップロードなので、メインのinoファイルに書かれたプログラムはアップロードされていないないはず。

最初に外部ファイルだけアップロードするときにこのような画面が出てきて、ファイルsizeなどが表示されています。この場合3052なので、約3kバイトという感じでしょうか。実際のindex.htmlを「情報を見る」で見てみると、8765バイトあるのですが、この差についてはよくわかりません。

メインのプログラムに外部ファイル読み込みコマンドを追加:
外部ファイルを読み込むためのプログラムを追加しておきます。
ESP8266では、Webサーバの表示用HTMLデータを用いることがよくあります。HTMLデータを読み込んだのちにString変数に入れておき、それをクライアント側に送信ということになります。
以下はinoファイルの冒頭部分です。

#include <ESP8266WiFi.h>
#include <ESP8266WebServer.h>
#include <ESP8266mDNS.h>
#include <WiFiClient.h>
#include "FS.h"

#define ledPin 13
#define relayPin 2

MDNSResponder mdns;

const char* ssid = "*****";
const char* password = "*****";
String webPage = "";
ESP8266WebServer server(80);

void setup(void){
  SPIFFS.begin();
  File f = SPIFFS.open("/index.html", "r");
  if (!f) {
    Serial.println("file open failed");
  }else{
    webPage = f.readString();
    f.close();
  }

最初のほうで、
#include "FS.h"
を呼んでおき、
setup()内で、

 SPIFFS.begin();
  File f = SPIFFS.open("/index.html", "r");
  if (!f) {
    Serial.println("file open failed");
  }else{
    webPage = f.readString();
    f.close();
  }

と書いて、index.htmlを読み込みつつ、String変数のwebPageに入れてしまいます。
Arduino IDEでのディレクトリは、data/index.htmlでしたが、ESP8266内では/index.htmlで読み込めます。
あとはこのままメインのプログラムと一緒に「マイコンボードに書き込む」でアップロードすればOK。

外部ファイルの確認や消去方法:
外部ファイルがアップロードされているのかどうかなど確認するには、SPIFFSのGitに詳しく書かれているので参考にするといいと思いますが、実際確認してみるプログラムを書いてみました。
#include "FS.h"

int val;
String filename;

void setup(){
  Serial.begin(115200);
  SPIFFS.begin();
}

void loop(){
  if(Serial.available()>0){
    val=Serial.read();
    if(val=='a'){//ファイル名やサイズの確認
      Dir dir = SPIFFS.openDir("/");
      while (dir.next()) {
        filename=dir.fileName();
        Serial.println("File Name: "+dir.fileName());
        File f = dir.openFile("r");
        Serial.println(f.size());
      }
      Serial.println("---a end");
      Serial.println("");
    }else if(val=='b'){//ファイルが存在するかどうか?
      if(SPIFFS.exists(filename)){
        Serial.println("File exists");
      }else{
        Serial.println("not exists");
      }
      Serial.println("---b end");
      Serial.println("");
    }else if(val=='c'){//ファイル削除
      SPIFFS.remove(filename);
      Serial.println("File removed");
      Serial.println("---c end");
      Serial.println("");
    }
  }
}


外部ファイルをアップロードしたESP8266に、このプログラムをアップロードします。この場合、inoファイルに書かれているプログラムはこれだけなので、もうすでに何らかのプログラムが書き込まれている場合は上書きされてしまうため要注意。
シリアルモニタを開いて、
a を入力でファイル名、サイズ(---a endで終了)
b を入力でそのファイルが存在するかどうか?(---b endで終了)
c を入力でそのファイルを削除(---c endで終了)
という感じです。
特にaはサンプルとほぼ同じですが、 while(dir.next())があるため、複数ファイル(html、css、jsファイルなど)あるならば、順次複数出力するのだと思います。

以下がシリアルモニタの出力結果の画面。


最初に、aでファイル名(ディレクトリも含め)、8765バイト(Macのファイルの「情報を見る」と同じ結果)。
次に、bで/index.htmlファイルが存在するかどうか?File existsで存在確認。
そして、cで/index.htmlファイルを削除。File removedで消去済み。
ファイル削除後、bで/index.htmlファイルの存在の再確認。もちろんnot existsで不在確認。
ちなみに、aを入力すると特に返答なし(ファイルがないため)。

たまに、複数あるESP8266にどの外部ファイルがアップロードされているか(あるいはアップロードされていないか)忘れてしまうときがあります。そのようなときには、内容確認としてこのようなことができれば便利かと。
さらに、ファイルの中身まで確認したいというなら、
File f=SPIFFS.open("/index.html","r");
String contents=f.readString();
Serial.println(contents);
などとすればいいのかもしれません。


人気の投稿