ESP32 OTA с порталом захвата SPIFFS

esp32 ota spiffs

Мне нужна помощь в модификации моего текущего кода для поддержки интерфейса Captive Portal, а также библиотеки ElegantOTA, которую я использую. Моя текущая проблема заключается в том, что я не могу получить доступ к странице ElegantOTA, когда у меня активен код авторизованного портала. Что мне удалось сделать после загрузки скетча, так это получить доступ к порталу авторизации, но не к странице ElegantOTA/update. Вместо этого он просто перенаправлял меня на страницу index.html при добавлении /update к URL-адресу авторизованного портала esp32.

У меня закончились идеи, потому что я не могу перейти на страницу /update, работающую на моей плате разработки ESP32 (я использую плату DOIT DEVKIT V1, чтобы прояснить ситуацию на случай возникновения путаницы). Если бы кто-нибудь мог помочь мне понять, что не так с моим кодом и почему он продолжает перенаправлять меня на index.html вместо страницы /update, я был бы очень признателен.

Вот вся папка .ino плюс папка с данными, если вы хотите попробовать загрузить все:

CaptivePortal-OTA.zip

Вот код, который я пытаюсь исправить:

// Добавляем необходимые библиотеки для корректной работы
#include <AsyncTCP.h>
#include <DNSServer.h>
#include <ESPAsyncWebServer.h>
#include <WiFi.h>
#include <WiFiClient.h>
#include <AsyncElegantOTA.h>

// Библиотеки SPI Flash File System для загрузки html-файлов в ESP32
#include <FS.h>
#include <SPIFFS.h>

// Последовательные контакты, необходимые для передачи полученных команд от SPIFFS к Arduino Uno
#define RXp2 16
#define TXp2 17


DNSServer dnsServer;
AsyncWebServer server(80);

// Сетевые учетные данные, используемые для создания локальной сети WiFi:
// SSID сети и пароль авторизованного портала.
const char ssid[] = "ESP32 Access Point";
const char password[] = "PasswordWasChanged";



/**
 * @brief List SPIFFS directory in Serial Monitor for storage mounting.
 *
 * @param fs File system handle.
 * @param dirname Directory name.
 * @param levels Maximum number of subdirectories to recurse into.
 */
void listDir(fs::FS &fs, const char* dirname, uint8_t levels)
{
   Serial.printf("Listing directory: %s\r\n", dirname);

   File root = fs.open(dirname);
   if (!root)
   {
      Serial.println("− failed to open directory");
      return;
   }
   if (!root.isDirectory())
   {
      Serial.println(" − not a directory");
      return;
   }

   File file = root.openNextFile();
   while (file)
   {
      if (file.isDirectory())
      {
         Serial.print("  DIR : ");
         Serial.println(file.name());
         if (levels)
         {
            listDir(fs, file.name(), levels - 1);
         }
      }
      else
      {
         Serial.print("  FILE: ");
         Serial.print(file.name());
         Serial.print("\tSIZE: ");
         Serial.println(file.size());
      }
      file = root.openNextFile();
   }
}

/**
 * @brief Captive Request Handler for Captive Portal.
 *
 * Main calls for all files go in this class.
 */
class CaptiveRequestHandler : public AsyncWebHandler
{
public:
   CaptiveRequestHandler();

   virtual ~CaptiveRequestHandler() = default;

   /**
    * @brief Is this class able to handle asynchronous requests?
    *
    * @param request Asynchronous request to check whether we can process it.
    * @return true The client will automatically download the page.
    * @return false The client must manually download the page.
    */
   bool canHandle(AsyncWebServerRequest* request);

   /**
    * @brief When a client connects, this request should send them the main page.
    * @param request Asynchronous request to process.
    */
   void handleRequest(AsyncWebServerRequest* request);
};

CaptiveRequestHandler::CaptiveRequestHandler()
{
   // Определяет все основные HTML-страницы, которые будут возвращены как один из асинхронных ответов.

   // Предоставляем страницу 404 на случай, если страница не может быть найдена.
   server.onNotFound([] (AsyncWebServerRequest* request)
   {
      // Этот файл намеренно отсутствует.
      AsyncWebServerResponse* response = request->beginResponse(SPIFFS, "/notFound.html", "text/html");
      request->send(response);
   });

   // Каталог .data/ является корневым каталогом сервера SPIFFS и содержит следующие файлы.

   server.on("/", HTTP_GET, [] (AsyncWebServerRequest* request)
   {
      AsyncWebServerResponse* response = request->beginResponse(SPIFFS, "/index.html", "text/html");
      request->send(response);
      Serial.println("Client Connected and Requested Main Page");
   });
   server.on("/lightswitch.html", HTTP_GET, [] (AsyncWebServerRequest* request)
   {
      AsyncWebServerResponse* response = request->beginResponse(SPIFFS, "/lightswitch.html", "text/html");
      request->send(response);
      Serial.println("Client Requested Lightswitch Page");
   });
   server.on("/whistle.html", HTTP_GET, [] (AsyncWebServerRequest* request)
   {
      AsyncWebServerResponse* response = request->beginResponse(SPIFFS, "/whistle.html", "text/html");
      request->send(response);
      Serial.println("Client Requested Whistle Page");
   });

   server.on("/stereo.html", HTTP_GET, [] (AsyncWebServerRequest* request)
   {
      AsyncWebServerResponse* response = request->beginResponse(SPIFFS, "/stereo.html", "text/html");
      request->send(response);
      Serial.println("Client Requested Stereo Page");
   });

   // Определение всех файлов CSS и PNG

   server.on("/web.css", HTTP_GET, [] (AsyncWebServerRequest* request)
   {
      AsyncWebServerResponse* response = request->beginResponse(SPIFFS, "/web.css", "text/css");
      request->send(response);
   });

   server.on("/layout.css", HTTP_GET, [] (AsyncWebServerRequest* request)
   {
      AsyncWebServerResponse* response = request->beginResponse(SPIFFS, "/layout.css", "text/css");
      request->send(response);
   });

   server.on("/header-line.png", HTTP_GET, [] (AsyncWebServerRequest* request)
   {
      AsyncWebServerResponse* response = request->beginResponse(SPIFFS, "/header-line.png", "image/png");
      request->send(response);
   });
}

bool CaptiveRequestHandler::canHandle(AsyncWebServerRequest* request)
{
   return true;
}

void CaptiveRequestHandler::handleRequest(AsyncWebServerRequest* request)
{
   // Когда кто-то подключается (клиент), какую страницу ему следует предоставить (index.html) и следует ли ее загружать (false)
   request->send(SPIFFS, "/index.html", String(), false);
}

/**
 * @brief Defines the actions that the buttons on the various pages perform when clicked.
 *
 * When a button is pressed, send a command through Serial2 to the listening Arduino Uno.
 * Example: Flip the lightswitch on or off, or create a shrill whistling sound.
 */
void setupServer()
{
   // Обработка при нажатии одной из кнопок Lightswitch.

   server.on("/on",      HTTP_GET, [] (AsyncWebServerRequest* request) { Serial2.write("ON"); });
   server.on("/off",     HTTP_GET, [] (AsyncWebServerRequest* request) { Serial2.write("OFF"); });
   server.on("/lock",    HTTP_GET, [] (AsyncWebServerRequest* request) { Serial2.write("LOCK"); });
   server.on("/unlock",  HTTP_GET, [] (AsyncWebServerRequest* request) { Serial2.write("UNLOCK"); });
   server.on("/rlock",   HTTP_GET, [] (AsyncWebServerRequest* request) { Serial2.write("LOCK REMOTE"); });
   server.on("/runlock", HTTP_GET, [] (AsyncWebServerRequest* request) { Serial2.write("UNLOCK REMOTE"); });
   server.on("/hlock",   HTTP_GET, [] (AsyncWebServerRequest* request) { Serial2.write("HIDDEN LOCK"); });
   server.on("/hunlock", HTTP_GET, [] (AsyncWebServerRequest* request) { Serial2.write("HIDDEN UNLOCK"); });
   server.on("/alock",   HTTP_GET, [] (AsyncWebServerRequest* request) { Serial2.write("LOCK ALL"); });
   server.on("/aunlock", HTTP_GET, [] (AsyncWebServerRequest* request) { Serial2.write("UNLOCK ALL"); });

   // Обработка при нажатии одной из кнопок свистка.

   server.on("/TooterTutor",  HTTP_GET, [] (AsyncWebServerRequest* request) { Serial2.write("TooterTutor"); });
   server.on("/AndOrButt",    HTTP_GET, [] (AsyncWebServerRequest* request) { Serial2.write("AndOrButt"); });
   server.on("/DieSpiderDie", HTTP_GET, [] (AsyncWebServerRequest* request) { Serial2.write("DieSpiderDie"); });
   server.on("/StompGoblin",  HTTP_GET, [] (AsyncWebServerRequest* request) { Serial2.write("StompGoblin"); });

   // Обработка при нажатии одной из кнопок Stereo.
   // Передает серию ИК-информации на стереосистему с ИК-управлением.
   // Все еще в процессе!

   server.on("/stereo_power",   HTTP_GET, [] (AsyncWebServerRequest* request) { Serial2.write("stereo_power"); });
   server.on("/stereo_cd",      HTTP_GET, [] (AsyncWebServerRequest* request) { Serial2.write("stereo_cd"); });
   server.on("/stereo_mute",    HTTP_GET, [] (AsyncWebServerRequest* request) { Serial2.write("stereo_mute"); });
   server.on("/stereo_aux",     HTTP_GET, [] (AsyncWebServerRequest* request) { Serial2.write("stereo_aux"); });
   server.on("/stereo_bt",      HTTP_GET, [] (AsyncWebServerRequest* request) { Serial2.write("stereo_bt"); });
   server.on("/stereo_fm",      HTTP_GET, [] (AsyncWebServerRequest* request) { Serial2.write("stereo_fm"); });
   server.on("/stereo_rr",      HTTP_GET, [] (AsyncWebServerRequest* request) { Serial2.write("stereo_rr"); });
   server.on("/stereo_play",    HTTP_GET, [] (AsyncWebServerRequest* request) { Serial2.write("stereo_play"); });
   server.on("/stereo_ff",      HTTP_GET, [] (AsyncWebServerRequest* request) { Serial2.write("stereo_ff"); });
   server.on("/stereo_rpt",     HTTP_GET, [] (AsyncWebServerRequest* request) { Serial2.write("stereo_rpt"); });
   server.on("/stereo_stop",    HTTP_GET, [] (AsyncWebServerRequest* request) { Serial2.write("stereo_stop"); });
   server.on("/stereo_prog",    HTTP_GET, [] (AsyncWebServerRequest* request) { Serial2.write("stereo_prog"); });
   server.on("/stereo_voldown", HTTP_GET, [] (AsyncWebServerRequest* request) { Serial2.write("stereo_voldown"); });
   server.on("/stereo_bass",    HTTP_GET, [] (AsyncWebServerRequest* request) { Serial2.write("stereo_bass"); });
   server.on("/stereo_volup",   HTTP_GET, [] (AsyncWebServerRequest* request) { Serial2.write("stereo_volup"); });
}

/**
 * @brief Set up the Serial and Serial2 loggers, as well as SPIFFS and so forth.
 */
void setup()
{
   // Запускаем как Serial (последовательный монитор), так и Serial2 (связь с Arduino Uno)
   Serial.begin(115200);
   Serial2.begin(9600, SERIAL_8N1, RXp2, TXp2);

   // Начните с IR_SEND_PIN в качестве вывода отправки, и если NO_LED_FEEDBACK_CODE НЕ определено, включите светодиод обратной связи на выводе светодиода обратной связи по умолчанию
   //IrSender.begin();

   // Если SPIFFS не смонтирована, выдать ошибку в Serial Monitor.
   if (!SPIFFS.begin(true))
   {
      Serial.println("An Error has occurred while mounting SPIFFS");
      return;
   }

   // Список содержимого SPIFFS в Serial Monitor.
   listDir(SPIFFS, "/", 0);

   // Запуск режима точки доступа с ssid и паролем, определенными ранее, и вывод информации
   Serial.println();
   Serial.println("Setting up AP Mode");
   WiFi.mode(WIFI_AP);
   WiFi.softAP(ssid, password);

   auto wifi_ip_address = WiFi.softAPIP();

   // Выводим IP-адрес точки доступа, а затем настраиваем асинхронный веб-сервер
   Serial.print("AP IP address: ");
   Serial.println(wifi_ip_address);

   Serial.println("Setting up Async WebServer");
   setupServer();

   // Запускаем DNS-сервер и добавляем CaptiveRequestHandler, который был определен ранее
   Serial.println("Starting DNS Server");
   dnsServer.start(53, "*", wifi_ip_address);

   // Только при запросе от точки доступа
// server.addHandler(новый CaptiveRequestHandler()).setFilter(ON_AP_FILTER);
   // Запускаем ElegantOTA
   AsyncElegantOTA.begin(&server);

   // Запускаем сервер
   server.begin();
   Serial.println("All Done!");
}

void loop()
{
   dnsServer.processNextRequest();
}

, 👍-1

Обсуждение

слово **обновление** отсутствует в вашем коде, @jsotola