Loading files
This file is pinned to the selected release. Download the original to open it in your design tool.
07e931b46This file is pinned to the selected release. Download the original to open it in your design tool.
// ═══════════════════════════════════════════════════════════
// PatternFlow - ArduinoOTA wireless flashing
//
// Wraps the standard ArduinoOTA library so the main sketch only has to
// call PatternflowOta::begin() in setup() and PatternflowOta::handle()
// in loop(). When PF_OTA_ENABLED is 0 every entry point compiles to a
// no-op and no Wi-Fi stack is pulled in for OTA.
//
// Wi-Fi is owned by PatternflowWifi (non-blocking connect + auto-reconnect);
// begin() runs on the connect edge from loop() and just attaches the OTA
// listener to the already-up connection. The listener binds once and
// survives reconnects.
//
// Cost when idle: one non-blocking UDP poll per loop iteration on
// port 3232. Negligible compared to pattern render time. Cost during
// upload: the upload chunk receive runs synchronously inside handle(),
// so frame rate dips while flashing — flashing only takes a few
// seconds so this is invisible in practice.
//
// License: MIT
// ═══════════════════════════════════════════════════════════
#pragma once
#include <Arduino.h>
#include "config.h"
#include "core_wifi.h"
#if PF_OTA_ENABLED
#include <WiFi.h>
#include <ArduinoOTA.h>
#endif
namespace PatternflowOta {
#if PF_OTA_ENABLED
inline bool initialized = false;
inline bool inProgress = false;
inline unsigned int progressPct = 0;
#endif
inline bool isCompiledIn() {
#if PF_OTA_ENABLED
return true;
#else
return false;
#endif
}
inline bool isInProgress() {
#if PF_OTA_ENABLED
return inProgress;
#else
return false;
#endif
}
inline unsigned int progressPercent() {
#if PF_OTA_ENABLED
return progressPct;
#else
return 0;
#endif
}
inline const char* hostname() {
return PF_OTA_HOSTNAME;
}
// Start the OTA listener. Wi-Fi is owned by PatternflowWifi; this runs on
// the connect edge from loop(). The listener binds once and keeps working
// across reconnects, so the initialized guard makes repeat calls no-ops.
inline void begin() {
#if PF_OTA_ENABLED
if (initialized) return;
if (!PatternflowWifi::linkUp()) return;
ArduinoOTA.setHostname(PF_OTA_HOSTNAME);
// Name service startup/retry belongs to core_names.h on the network task.
ArduinoOTA.setMdnsEnabled(false);
// Empty string = no authentication. setPassword with an empty string
// would still set an MD5 hash internally and confuse the handshake,
// so we guard.
if (PF_OTA_PASSWORD[0] != '\0') {
ArduinoOTA.setPassword(PF_OTA_PASSWORD);
}
ArduinoOTA.onStart([]() {
inProgress = true;
progressPct = 0;
const char* type = (ArduinoOTA.getCommand() == U_FLASH)
? "sketch" : "filesystem";
Serial.printf("[OTA] Upload started (%s)\n", type);
});
ArduinoOTA.onEnd([]() {
inProgress = false;
Serial.println("\n[OTA] Upload complete; device will reboot");
});
ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) {
if (total > 0) {
unsigned int next = (progress * 100u) / total;
if (next != progressPct) {
progressPct = next;
Serial.printf("[OTA] %u%%\r", progressPct);
}
}
});
ArduinoOTA.onError([](ota_error_t error) {
inProgress = false;
const char* reason = "unknown";
switch (error) {
case OTA_AUTH_ERROR: reason = "auth"; break;
case OTA_BEGIN_ERROR: reason = "begin"; break;
case OTA_CONNECT_ERROR: reason = "connect"; break;
case OTA_RECEIVE_ERROR: reason = "receive"; break;
case OTA_END_ERROR: reason = "end"; break;
}
Serial.printf("[OTA] Error: %s (%u)\n", reason, (unsigned)error);
});
ArduinoOTA.begin();
initialized = true;
bool authed = (PF_OTA_PASSWORD[0] != '\0');
Serial.printf("[OTA] Ready — hostname \"%s.local\", IP %s, auth %s\n",
PF_OTA_HOSTNAME, WiFi.localIP().toString().c_str(),
authed ? "ON (see PF_OTA_PASSWORD)" : "OFF");
#endif
}
inline void handle() {
#if PF_OTA_ENABLED
if (initialized && WiFi.status() == WL_CONNECTED) {
ArduinoOTA.handle();
}
#endif
}
} // namespace PatternflowOta