こんにちは、ENGかぴです。
ESP32シリーズのSPIFFSライブラリを使用するとSDカードのようにFLASHにアクセスしてデータの読み書きができます。ブラウザー(スマホ)からアクセスポイントの設定に関わる設定値を変更しFLASHに保存して動作させる方法をまとめました。
ESP32-WROOM-32E開発ボード(秋月電子)を使用しArduino IDEで開発を行います。
SPIFFSライブラリの使い方などは下記記事にまとめています。本記事はSPIFFSを使った応用例としてまとめています。
ESP32-WROOM-32EのFLASH(SPIFFS)を使用する方法
ESP32-WROOM-32Eで動作確認したことについてリンクをまとめています。
ESP32-WROOM-32Eで学べるソフト開発と標準ライブラリの使い方
SPIFFSの応用例
ESP32シリーズでアクセスポイントの設定をFLASHから取得したデータを使って行います。ブラウスマホなどのブラウザーでSSIDやパスワードなどアクセスポイントの設定やIPアドレスやゲートウェイ設定も変更できるようにします。
SPIFFSの初期設定
#include <FS.h>
#include <SPIFFS.h>
void setup() {
SPIFFS.begin(); //SPIFFSの初期化
FfsRead(); //フラッシュのデータを読み込み
SetAp(); //フラッシュデータを使ってアクセスポイントの
}
SPIFFSライブラリを使用するためにSPIFFS.hをインクルードします。ファイルを開いて読み書きするためFS.hライブラリをインクルードします。Webページをブラウザー上に表示するためWebServer.hライブラリをインクルードします。
setup()関数内で最初にSPIFFSのbegin()関数を使ってSPIFFSの初期化を行います。ユーザー自作のFfsRead()関数でフラッシュデータの読み込みSetAp()関数でアクセスポイントの設定を行います。
広告
FLASHのデータを読み込む
void FfsRead(void){
File fp = SPIFFS.open(settings,"r");
if(fp){ //ファイルが開いて以下を処理
ssid = (fp.readStringUntil('\n')); //ssid部分を読み込む
pass = fp.readStringUntil('\n'); //password部分を読み込む
ipadr1 = fp.readStringUntil('\n'); //IP部分を読み込む(MSB)
//以下ゲートウェイ設定
fp.close();
}
else{ //ファイルが開けなかった場合
Serial.println("Flash Read error");
ssid = "EngKapi1";
pass = "22223333";
ipadr1 = "192";
}
ssid.trim(); //null文字を削除
pass.trim();
ipadr1.trim();
if( ipadr1.toInt() == 0){ //整数型に直したとき0であれば初期値を入れる
ipadr1 = "192";
}
}
SPIFFSを使ってファイルを開くためにopen()関数を使用します。第1引数にはファイルのパスを指定します。第2引数にデータを読み込む場合は”r”を指定します。
ファイルが開けるとフラッシュに保管したデータ分のサイズをreadStringUntil()関数を使用し引数に(‘\n’)を指定して読み込みます。readStringUntil()関数は引数で指定した文字列を検出するまで読み込む関数です。
読み込んだ後はファイルを閉じるためclose()関数を使用します。
プログラムを初期動作させる場合はファイルが存在しないため初期値を入力しています。
WiFi.softAP(ssid.c_str(),pass.c_str());
IPAddress ip(ipadr1.toInt(), ipadr2.toInt(), ipadr3.toInt(), ipadr4.toInt());
IPAddress gateway(gwadr1.toInt(), gwadr2.toInt(), gwadr3.toInt(), gwadr4.toInt());
FLASHからデータを読み込んだ後WiFiの設定を行います。SSIDやパスワードは文字列の型からchar型に変換するためc_str()関数を指定しています。
WiFiのIPやゲートウェイは整数型で指定する必要があるためtoInt()関数で文字列の型からint型に変換しています。整数型に変換できない場合(数値以外の文字が入っている)場合は0になります。
本記事のソフトは対策としてIPとゲートウェイの最上位ビットから16ビット分が0にならないように対策しています。
FLASHにデータを書き込む
bool FfsWrite(void){
bool ret = true;
File fp = SPIFFS.open(settings,"w");
if( fp ){
fp.println(ssid);
fp.println(pass);
//IP及びゲートウェイを書き込み(省略)
fp.close();
}
else{
Serial.println("Flash Write Error");
ret = false;
}
return ret;
}
SPIFFSを使ってファイルを開くためにopen()関数を使用します。第1引数にはファイルのパスを指定します。第2引数にデータを書き込む場合は”w”を指定します。
書き込んだ後はファイルを閉じるためにclose()関数を使用します。
ファイルを開くとprintln()関数を使って改行コード付きの文字列で書き込みを行います。読み込んだ際に改行コードをデータの区切りとして判断させることができます。
write()関数で書き込む場合はバイナリデータを書き込むことが前提になるためあらかじめデータのサイズなどのフォーマットを統一しておくとよいでしょう。
一見するとテキストデータの方が文字として見やすく感じますが1文字あたり2バイトデータとなるためバイナリデータで管理するよりもデータ容量が大きくなってしまうデメリットもあります。
WebサーバーでHTML情報を取得する
#include <WebServer.h>
WebServer Wserver(80);
void SetAp(void){
//WIFIの設定(省略)
Wserver.on("/",HTTP_GET,HtmlGet); //HTMLの表示
Wserver.on("/",HTTP_POST,HtmlPost); //HTMLからデータを受け取る
Wserver.onNotFound(handleNotFound); //ページが存在しない場合
Wserver.begin();
}
WebServerライブラリを使用するためにインクルードします。WebServerの型で変数を宣言しインスタンスを生成します。初期化関数内でWebServerの設定を行います。
on()関数はURLに対して接続要求がある場合に処理する関数を指定します。第1引数にはURLアドレスの階層を示す文字を指定します。
例えばブラウザーから「192.168.11.2」を指定する場合は”/”となり「192.168.11.2/ti」を指定して表示する場合は”/ti”を指定します。
第2引数にHTTPの種類(HTTP_GETやHTTP_POST)などを指定します。
第3引数に接続要求に対するURLが存在する場合に処理する関数を指定します。
例ではHTMLの表示用の関数としてHtmlGet()を実装し、HTMLからデータを受け取った場合に遷移する関数としてHtmlPost()を指定しています。
onNotFound()はURLが見つからない場合の処理する関数を指定します。
begin()で指定したポートでWebServerが動作開始します。指定しない場合はポート80で動作開始します。
void HtmlGet(void) { //webページの内容を一部抜粋
html += "<input type='text' name='ssid' value='" + ssid + "' placeholder='EngKapi1'style ='width:100px;font-size:20px;'>";
}
void HtmlPost(void){
ssid = Wserver.arg("ssid"); //送信された値を取得
pass = Wserver.arg("pass");
ipadr1 = Wserver.arg("ipadr1");
if( FfsWrite()){ //データを書き込み
FfsRead(); //書き込んだデータを読み返し
readSet(); //変更した内容を表示
}
}
ブラウザーに表示するHtmlGet()ではSSIDの設定用にテキストボックスを実装していますがユーザーがテキストボックス内に入力した値がvalueにセットされます。テキストボックスの値はSSIDの名前でリンクしているためHtmlPost()内でデータの名前で指定することができます。
HtmlPost()ではHTMLから送信された値をarg()関数を使って読み込んでいます。arg()関数の引数に読み込みたいデータの名前を指定します。戻り値は読み込んだ値の文字列となるためHTML内でユーザーが指定した値がセットされます。
HtmlGet()でユーザーが入力したarg()関数によって取得しSPIFFSライブラリを使ってFLASHにアクセスしてデータを書き込みます。
広告
PR:わからないを放置せず、あなたにあったスキルを身に着けるコツを教える テックジム 「書けるが先で、理解が後」を体験しよう!
動作確認
スマホでアクセスポイントとして表示されている「Engkapi」を接続し「192.168.11.3」をスマホのGoogle Chromeでアクセスすると設定項目が表示されます。動作確認としてIPアドレスを「192.168.11.5」に変更し設定値送信のボタンを押すと現在設定値が表示されます。
ESP32-WROOM-32Eをリスタート(電源OFFまたはリセットボタン)すると変更した値でWiFi通信がスタートします。使用するブラウザーによってテキストボックスやボタンの大きさが変化するため可能な限りテキストボックスの幅やボタンの大きさをHTML言語で指定しておく方がよさそうです。
設定がスマホからできて便利だと感じますが、WiFi通信が届かないことやAndroidスマホ(環境によると思います)では接続が継続できずに切断したりするため通信ができない場合の対策についても十分に検討する必要がありそうです。
ソースコード全体
以下のソースコードはコンパイルして動作確認をしております。コメントなど細かな部分で間違っていたりやライブラリの更新などにより動作しなくなったりする可能性があります。参考としてお使いいただければと思います。
#include <FS.h>
#include <SPIFFS.h>
#include <WebServer.h>
const char *ssiddef = "EngKapi1"; //SSID
const char *passdef = "22223333"; //password
String settings = "/settings_dat.txt"; //フラッシュに置くテキストファイル
const IPAddress ip2(192,168,11,2); //IPアドレス
const IPAddress subnet(255,255,255,0); //サブネットマスク
/* 変数の宣言 */
WebServer Wserver(80);
uint32_t beforetimCnt = millis();
/* フラッシュ保存変数 */
String ssid;
String pass;
String ipset;
String ipadr1; String ipadr2; String ipadr3; String ipadr4;
String gwadr1; String gwadr2; String gwadr3; String gwadr4;
String w_ssid;
String w_pass;
String w_ipset;
String w_ipadr1; String w_ipadr2; String w_ipadr3; String w_ipadr4;
String w_gwadr1; String w_gwadr2; String w_gwadr3; String w_gwadr4;
/* プロトタイプ宣言 */
void FfsRead(void);
bool FfsWrite(void);
void SetAp(void);
void HtmlGet(void);
void HtmlPost(void);
void readSet(void);
void handleNotFound(void);
void setup() {
Serial.begin(115200);
SPIFFS.begin();
FfsRead();
SetAp();
Serial.println("start");
}
void loop() {
Wserver.handleClient();
}
/* FFSデータ読み込み処理 */
void FfsRead(void){
File fp = SPIFFS.open(settings,"r");
if(fp){ //ファイルが開いて以下を処理
ssid = (fp.readStringUntil('\n'));
pass = fp.readStringUntil('\n');
ipset = fp.readStringUntil('\n');
ipadr1 = fp.readStringUntil('\n');
ipadr2 = fp.readStringUntil('\n');
ipadr3 = fp.readStringUntil('\n');
ipadr4 = fp.readStringUntil('\n');
gwadr1 = fp.readStringUntil('\n');
gwadr2 = fp.readStringUntil('\n');
gwadr3 = fp.readStringUntil('\n');
gwadr4 = fp.readStringUntil('\n');
fp.close();
}
else{ //ファイルが開けなかった場合
Serial.println("Flash Read error");
ssid = "EngKapi1";
pass = "22223333";
ipset = "MANUAL";
ipadr1 = "192";
ipadr2 = "168";
ipadr3 = "11";
ipadr4 = "2";
gwadr1 = "192";
gwadr2 = "168";
gwadr3 = "11";
gwadr4 = "1";
}
ssid.trim();
pass.trim();
ipset.trim();
ipadr1.trim();
if( ipadr1.toInt() == 0){
ipadr1 = "192";
}
ipadr2.trim();
if( ipadr2.toInt() == 0){
ipadr2 = "168";
}
ipadr3.trim();
if( ipadr3.toInt() == 0){
ipadr3 = "0";
}
ipadr4.trim();
if( ipadr4.toInt() == 0){
ipadr4 = "0";
}
gwadr1.trim();
if( gwadr1.toInt() == 0){
gwadr1 = "192";
}
gwadr2.trim();
if( gwadr2.toInt() == 0){
gwadr2 = "168";
}
gwadr3.trim();
if( gwadr3.toInt() == 0){
gwadr3 = "0";
}
gwadr4.trim();
if( gwadr4.toInt() == 0){
gwadr4 = "1";
}
}
/* WiFiセットアップ処理 */
void SetAp(void){
if( WiFi.softAP(ssid.c_str(),pass.c_str()) == false ){
WiFi.softAP(ssiddef,passdef);
ssid = "EngKapi1";
pass = "22223333";
}
if( ipset =="MANUAL"){
IPAddress ip(ipadr1.toInt(), ipadr2.toInt(), ipadr3.toInt(), ipadr4.toInt());
IPAddress gateway(gwadr1.toInt(), gwadr2.toInt(), gwadr3.toInt(), gwadr4.toInt());
Serial.println(ip);
Serial.println(gateway);
if( WiFi.softAPConfig(ip,gateway,subnet) == false){
WiFi.softAPConfig(ip2,ip2,subnet);
ipadr1 = "192";
ipadr2 = "168";
ipadr3 = "11";
ipadr4 = "2";
gwadr1 = "192";
gwadr2 = "168";
gwadr3 = "11";
gwadr4 = "1";
}
}
Wserver.on("/",HTTP_GET,HtmlGet);
Wserver.on("/",HTTP_POST,HtmlPost);
Wserver.onNotFound(handleNotFound);
Wserver.begin();
Serial.println("WiFi start");
}
/* 設定したIPアドレスで表示するページ */
void HtmlGet(void) {
String html = "";
html += "<!DOCTYPE html>";
html += "<html lang=\"ja\">";
html += "<head>";
html += "<meta charset=\"UTF-8\">";
html += "<title>Flash Settings Ver1.00</title>";
html += "<h2>設定項目</h2>";
html += "</head>";
html += "<body>";
html += "<form method='post'>";
html += "SSID: "; //SSID
html += "<input type='text' name='ssid' value='" + ssid + "' placeholder='EngKapi1'style ='width:100px;font-size:20px;'>";
html += " PASSWORD: "; //パスワード
html += "<input type='text' name='pass' value='" + pass + "' placeholder='22223333'style ='width:100px;font-size:20px;'>";
html += "<br><br>";
//IP取得設定
html += "IPアドレス設定方法: ";
html += "<input type='radio' name='ipset' value='DHCP' ";
if(ipset == "DHCP") {
html += "checked='checked'"; }
html += ">自動(DHCP) ";
html += "<input type='radio' name='ipset' value='MANUAL' ";
if(ipset == "MANUAL") {
html += "checked='checked'"; }
html += ">手動 ";
html += "<br><br>";
html += "<h4>手動の場合はIPアドレス及びゲートウェイアドレスの設定が必要です。</h4>";
//装置IPアドレス設定
html += "IPアドレス: ";
html += "<input type='text' name='ipadr1' value='" + ipadr1 + "' placeholder='192' style ='width:50px;font-size:20px;'>";
html += ".";
html += "<input type='text' name='ipadr2' value='" + ipadr2 + "' placeholder='168' style ='width:50px;font-size:20px;'>";
html += ".";
html += "<input type='text' name='ipadr3' value='" + ipadr3 + "' placeholder='11' style ='width:50px;font-size:20px;'>";
html += ".";
html += "<input type='text' name='ipadr4' value='" + ipadr4 + "' placeholder='2' style ='width:50px;font-size:20px;'>";
html += "<br><br>";
//ゲートウェイアドレス設定
html += "ゲートウェイアドレス: ";
html += "<input type='text' name='gwadr1' value='" + gwadr1 +"' placeholder='192' style ='width:50px;font-size:20px;'>";
html += ".";
html += "<input type='text' name='gwadr2' value='" + gwadr2 +"' placeholder='168' style ='width:50px;font-size:20px;'>";
html += ".";
html += "<input type='text' name='gwadr3' value='" + gwadr3 +"' placeholder='11' style ='width:50px;font-size:20px;'>";
html += ".";
html += "<input type='text' name='gwadr4' value='" + gwadr4 +"' placeholder='1' style ='width:50px;font-size:20px;'>";
html += "<br><br>";
//送信ボタン
html += "<input type='submit' value='設定値送信' style='width:200px;padding:20px;font-size:20px;'><br>";
html += "</form>";
html += "</body>";
html += "</html>";
Wserver.send(200, "text/html", html);
}
/* 入力文字を取得*/
void HtmlPost(void){
ssid = Wserver.arg("ssid"); //送信された値を取得
pass = Wserver.arg("pass");
ipset = Wserver.arg("ipset");
ipadr1 = Wserver.arg("ipadr1");
ipadr2 = Wserver.arg("ipadr2");
ipadr3 = Wserver.arg("ipadr3");
ipadr4 = Wserver.arg("ipadr4");
gwadr1 = Wserver.arg("gwadr1");
gwadr2 = Wserver.arg("gwadr2");
gwadr3 = Wserver.arg("gwadr3");
gwadr4 = Wserver.arg("gwadr4");
if( FfsWrite()){ //データを書き込み
FfsRead(); //書き込んだデータを読み返し
readSet(); //変更した内容を表示
}
}
/* FFSデータ書き込み処理 */
bool FfsWrite(void){
bool ret = true;
File fp = SPIFFS.open(settings,"w");
if( fp ){
fp.println(ssid);
fp.println(pass);
fp.println(ipset);
fp.println(ipadr1);
fp.println(ipadr2);
fp.println(ipadr3);
fp.println(ipadr4);
fp.println(gwadr1);
fp.println(gwadr2);
fp.println(gwadr3);
fp.println(gwadr4);
fp.close();
}
else{
Serial.println("Flash Write Error");
ret = false;
}
return ret;
}
/* 書込み結果をWebページに表示 */
void readSet(void){
String html = "";
html += "<html lang=\"ja\">";
html += "<head>";
html += "<meta charset=\"UTF-8\">";
html += "<h1>現在設定値</h1>";
html += "</head>";
html += "<body>";
html += "SSID: " + ssid + "<br>";
html += "PASSWORD: " + pass + "<br>";
html += "IPアドレス設定方法: " + ipset + "<br>";
html += "IPアドレス: " + ipadr1 + "." + ipadr2 + "." + ipadr3 + "." + ipadr4 + "<br>";
html += "ゲートウェイアドレス: " + gwadr1 + "." + gwadr2 + "." + gwadr3 + "." + gwadr4 + "<br>";
html += "</body>";
html += "</html>";
Wserver.send(200, "text/html", html);
}
/* URLが存在しない場合の処理 */
void handleNotFound(void) {
String message = "File Not Found\n\n";
message += "URI: ";
message += Wserver.uri();
message += "\nMethod: ";
message += (Wserver.method() == HTTP_GET) ? "GET" : "POST";
message += "\nArguments: ";
message += Wserver.args();
message += "\n";
for (uint8_t i = 0; i < Wserver.args(); i++) {
message += " " + Wserver.argName(i) + ": " + Wserver.arg(i) + "\n";
}
Wserver.send(404, "text/plain", message); //テキストファイルであることを示している。
}
関連リンク
Arduinoのライブラリを使って動作確認を行ったことを下記リンクにまとめています。
Arduinoで学べるマイコンのソフト開発と標準ライブラリの使い方
Seeeduino XIAOで学べるソフト開発と標準ライブラリの使い方
ESP32-WROOM-32Eで学べるソフト開発と標準ライブラリの使い方
PR: わからないを放置せず、あなたにあったスキルを身に着けるコツを教える テックジムPython入門講座の申込
最後まで、読んでいただきありがとうございました。
今回はSPIFFSでWIFIの設定を変更して動作させる例としてアクセスポイント設定を変更しましたが、他のセンサーの設定情報などを保管する方法等にも応用することができます。