せっかく Wi-Fi機能のあるデバイス使ってるんだから、プログラム書き込みもコードレスでやりたい。って事で、OTA(Over The Air)書き込みも試してみる。やり方は何種類かあるみたいだが、こちら を参考に一番シンプルな技法を試してみた。ベースにしたのは標準のスケッチサンプルに収録の BasicOTAというやつで、一度これを USB経由で書き込んで ESP32を再起動するとシリアルポートと別にネットワークポートが開くので、次からは USB接続ナシで書き込む事が可能になる(但し、ESP32に給電する別電源が必要)。
BasicOTAがうまく行ったので、作り込んで来たここまでのスケッチに OTA環境を足してみる。BasicOTAのスケッチから適宜必要な部分をコピーして移植していった。以下が動作確認のとれた最終スケッチだが、OTA化と同時に想定するポイント(分岐器)を3つに増やし、信号出力PIN番号も一部変えてある。
[ スケッチ:Esp32_AsyncWebServer_OTA ]
#include <WiFi.h>
#include "ESPAsyncWebServer.h"
#include "SPIFFS.h"
#include <ArduinoJson.h>
// for Servo
#include <ESP32Servo.h>
#include <ServoEasing.hpp>
// for OTA
#include <ESPmDNS.h>
#include <NetworkUdp.h>
#include <ArduinoOTA.h>
const char ssid[] = "********";
const char pass[] = "********";
const IPAddress ip(192,168,3,17);
const IPAddress gateway(192,168,1,1); // デフォルトゲートウェイ
const IPAddress subnet(255,255,255,0);
const int rel_pins[] = {0,26,27,32};
const int srv_pins[] = {0,16,17,18};
const int SIZEOF_REL_PINS = sizeof(rel_pins)/sizeof(rel_pins[0]);
const int srv_min = 1450;
const int srv_max = 2400;
const int srv_deg = 150;
const int srv_sec = 4000;
AsyncWebServer server(80); // ポート設定
// Jsonオブジェクトの初期化
StaticJsonDocument<512> doc;
// ブラウザから受信する変数
uint8_t pnt_number; // ポイント配置番号
uint8_t pnt_status; // ポイント状態制御
// for Servo
ServoEasing servo[SIZEOF_REL_PINS]; // オブジェクト配列定義
void setup()
{
Serial.begin(115200);
for(int i =1; i < SIZEOF_REL_PINS; i++) {
pinMode(rel_pins[i], OUTPUT); // pinを出力設定に
pinMode(srv_pins[i], OUTPUT); // pinを出力設定に
}
// SPIFFSのセットアップ
if(!SPIFFS.begin(true)){
Serial.println("An Error has occurred while mounting SPIFFS");
return;
}
WiFi.config(ip, gateway, subnet);
WiFi.mode(WIFI_STA); // for OTA
WiFi.begin (ssid, pass);
// for OTA
while (WiFi.waitForConnectResult() != WL_CONNECTED) {
Serial.println("Connection Failed! Rebooting...");
delay(5000);
ESP.restart();
}
// for OTA
ArduinoOTA
.onStart([]() {
String type;
if (ArduinoOTA.getCommand() == U_FLASH) {
type = "sketch";
} else { // U_SPIFFS
type = "filesystem";
}
// NOTE: if updating SPIFFS this would be the place to unmount SPIFFS using SPIFFS.end()
Serial.println("Start updating " + type);
})
.onEnd([]() {
Serial.println("\nEnd");
})
.onProgress([](unsigned int progress, unsigned int total) {
Serial.printf("Progress: %u%%\r", (progress / (total / 100)));
})
.onError([](ota_error_t error) {
Serial.printf("Error[%u]: ", error);
if (error == OTA_AUTH_ERROR) { Serial.println("Auth Failed"); }
else if (error == OTA_BEGIN_ERROR) { Serial.println("Begin Failed"); }
else if (error == OTA_CONNECT_ERROR) { Serial.println("Connect Failed"); }
else if (error == OTA_RECEIVE_ERROR) { Serial.println("Receive Failed"); }
else if (error == OTA_END_ERROR) { Serial.println("End Failed"); }
});
ArduinoOTA.begin();
// 各種情報を表示
Serial.print("SSID: ");
Serial.println(ssid);
Serial.print("AP IP address: ");
Serial.println(ip);
// GETリクエストに対するハンドラーを登録
server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
request->send(SPIFFS, "/index.html"); // rootにアクセスされた時のレスポンス
});
server.on("/style.css", HTTP_GET, [](AsyncWebServerRequest *request){
request->send(SPIFFS, "/style.css", "text/css"); // style.cssにアクセスされた時のレスポンス
});
// Pointの制御変数の変更リクエスト
server.on(
"/post_test",
HTTP_POST,
[](AsyncWebServerRequest * request){},
NULL,
[](AsyncWebServerRequest * request, uint8_t *data, size_t len, size_t index, size_t total) {
String resjson = "";
for (size_t i = 0; i < len; i++) {
//Serial.write(data[i]);
resjson.concat(char(data[i]));
}
Serial.println(resjson);
DeserializationError error = deserializeJson(doc, resjson);
if(error){
Serial.println("deserializeJson() faild");
request->send(400);
}
else{
pnt_number = doc["PNT_NUMBER"];
pnt_status = doc["PNT_STATUS"];
request->send(200);
}
});
pnt_number = 0;
pnt_status = 0;
// サーバースタート
server.begin();
Serial.println("Server start!");
// for Servo
for(int i =1; i < SIZEOF_REL_PINS; i++) {
servo[i].attach(srv_pins[i], srv_min, srv_max); // attach(int pin, int min, int max)
servoGo(i, 0); // initialize
relayGo(i, 0); // initialize
}
setEasingTypeForAllServos(EASE_CUBIC_IN_OUT); // EASE_LINEAR is default
} // End setup()
void loop() {
ArduinoOTA.handle(); // for OTA
if(pnt_number > 0) {
// Servo
if (srv_pins[pnt_number] > 0) { servoGo(pnt_number, pnt_status); }
// Relay
if (rel_pins[pnt_number] > 0) { relayGo(pnt_number, pnt_status); }
}
pnt_number = 0;
delay(10);
} // End loop()
void servoGo(int pnt_number, int pnt_status) {
servo[pnt_number].setEaseToD(pnt_status*srv_deg, srv_sec);
synchronizeAllServosStartAndWaitForAllServosToStop();
}
void relayGo(int pnt_number, int pnt_status) {
if(pnt_status == 0){
digitalWrite(rel_pins[pnt_number], LOW);
} else {
digitalWrite(rel_pins[pnt_number], HIGH);
}
}