Загрузка данных


#include <WiFi.h>
#include <WebServer.h>
#include <Preferences.h>
#include <ArduinoOTA.h>

// ---- Данные домашней сети (используются только при первом запуске,
//      дальше берутся из NVS) ----
#define HOME_SSID     "LegendaryBoka"
#define HOME_PASSWORD "ВАШ_ПАРОЛЬ"

#define TELNET_PORT 23 // порт, который указываете в Termius

Preferences preferences;
WebServer server(80);

String ssid = "";
String password = "";
String deviceName = "esp32-ota"; // имя устройства, под которым оно будет видно в Arduino IDE как сетевой порт

// ---------- Телнет-мост: дублирует Serial-вывод по Wi-Fi ----------
// Используйте dbg.print/println/printf вместо Serial.print/println/printf —
// тогда текст будет виден и в мониторе порта (USB), и в Termius (по Wi-Fi).

class TelnetSerial : public Print {
  public:
    TelnetSerial() : server(TELNET_PORT) {}

    void begin() {
      server.begin();
      server.setNoDelay(true);
    }

    // Вызывайте в loop(): принимает нового клиента и отклоняет "лишних"
    void handle() {
      if (server.hasClient()) {
        if (!client || !client.connected()) {
          if (client) client.stop();
          client = server.available();
          client.println("=== Подключено к ESP32 (телнет-монитор) ===");
        } else {
          WiFiClient reject = server.available();
          reject.stop(); // одновременно поддерживается только один клиент
        }
      }
      // Символы, введённые в Termius, тоже пробрасываем в Serial —
      // удобно, если ваш скетч читает команды из Serial.read()
      while (client && client.connected() && client.available()) {
        Serial.write(client.read());
      }
    }

    size_t write(uint8_t c) override {
      Serial.write(c);
      if (client && client.connected()) {
        client.write(c);
      }
      return 1;
    }

    using Print::write;

  private:
    WiFiServer server;
    WiFiClient client;
};

TelnetSerial dbg;

// ---------- Работа с NVS (энергонезависимая память) ----------

void loadCredentials() {
  preferences.begin("wifi-config", true); // true = только чтение
  ssid = preferences.getString("ssid", "");
  password = preferences.getString("password", "");
  deviceName = preferences.getString("devname", "esp32-ota");
  preferences.end();

  // Если это первый запуск и в NVS ещё ничего не сохранено —
  // используем встроенные данные домашней сети и сохраняем их в NVS.
  if (ssid.length() == 0) {
    ssid = HOME_SSID;
    password = HOME_PASSWORD;
    saveCredentialsInternal(ssid, password);
  }
}

void saveCredentialsInternal(const String &newSsid, const String &newPassword) {
  preferences.begin("wifi-config", false); // false = чтение и запись
  preferences.putString("ssid", newSsid);
  preferences.putString("password", newPassword);
  preferences.end();
}

void clearCredentials() {
  preferences.begin("wifi-config", false);
  preferences.clear();
  preferences.end();
}

// ---------- Подключение к Wi-Fi ----------

void connectToWiFi() {
  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid.c_str(), password.c_str());

  dbg.print("Подключение к сети \"");
  dbg.print(ssid);
  dbg.print("\"");

  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    dbg.print(".");
  }
  dbg.println();
}

// ---------- Настройка OTA ----------

void setupOTA() {
  ArduinoOTA.setHostname(deviceName.c_str());
  // Раскомментируйте и задайте пароль, если хотите защитить OTA:
  // ArduinoOTA.setPassword("your_ota_password");

  ArduinoOTA.onStart([]() {
    String type = (ArduinoOTA.getCommand() == U_FLASH) ? "sketch" : "filesystem";
    dbg.println("Начало OTA обновления: " + type);
  });

  ArduinoOTA.onEnd([]() {
    dbg.println("\nOTA обновление завершено");
  });

  ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) {
    dbg.printf("Прогресс: %u%%\r", (progress / (total / 100)));
  });

  ArduinoOTA.onError([](ota_error_t error) {
    dbg.printf("Ошибка [%u]: ", error);
    if (error == OTA_AUTH_ERROR) dbg.println("ошибка авторизации");
    else if (error == OTA_BEGIN_ERROR) dbg.println("ошибка начала записи");
    else if (error == OTA_CONNECT_ERROR) dbg.println("ошибка подключения");
    else if (error == OTA_RECEIVE_ERROR) dbg.println("ошибка приёма данных");
    else if (error == OTA_END_ERROR) dbg.println("ошибка завершения записи");
  });

  ArduinoOTA.begin();
  dbg.println("OTA готово, устройство доступно для заливки по сети");
}

void setup() {
  Serial.begin(115200);
  delay(500);

  loadCredentials();
  connectToWiFi();

  dbg.println("Wi-Fi подключён!");
  dbg.print("IP адрес: ");
  dbg.println(WiFi.localIP());

  setupOTA();
  dbg.begin();
  Serial.print("Телнет-монитор доступен на ");
  Serial.print(WiFi.localIP());
  Serial.print(":");
  Serial.println(TELNET_PORT);

  // Страница для сброса сохранённых настроек Wi-Fi (на случай, если
  // в будущем понадобится сменить сеть):
  // http://<IP-адрес>/reset
  server.on("/reset", []() {
    clearCredentials();
    server.send(200, "text/plain", "Настройки Wi-Fi сброшены. Перезагрузка...");
    delay(1000);
    ESP.restart();
  });
  server.begin();
}

void loop() {
  ArduinoOTA.handle();
  server.handleClient();
  dbg.handle();

  // ---- сюда добавляйте основной код вашего проекта, используя
  //      dbg.print(...) / dbg.println(...) вместо Serial.print/println ----
}