-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathota.h
More file actions
114 lines (99 loc) · 3.58 KB
/
Copy pathota.h
File metadata and controls
114 lines (99 loc) · 3.58 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
/**
* @file ota.h
* @brief ArduinoOTA wrapper for wireless firmware updates.
*
* Provides password-protected over-the-air firmware update capability.
* Progress and status are reported via the Logger. OTA runs cooperatively
* within the main loop — ArduinoOTA.handle() is non-blocking when idle.
*
* @note OTA uses Wi-Fi only. Ensure Wi-Fi is connected before calling begin().
*/
#ifndef OTA_H
#define OTA_H
#include <ArduinoOTA.h>
#include "config.h"
#include "logger.h"
/** @brief Module tag for OTA-related log messages. */
static constexpr const char* OTA_TAG = "ota";
/**
* @brief ArduinoOTA lifecycle manager singleton.
*
* Wraps ArduinoOTA setup and event callbacks with structured logging.
* Password is sourced from credentials.h via config.h.
*
* Justified singleton: ArduinoOTA is itself a global singleton.
*/
class OtaManager {
public:
/**
* @brief Get the singleton OtaManager instance.
* @return Reference to the global OtaManager.
*/
static OtaManager& instance() {
static OtaManager mgr;
return mgr;
}
/**
* @brief Initialize ArduinoOTA with hostname, password, and callbacks.
*
* Must be called after Wi-Fi is initialized (not necessarily connected).
* OTA will become available once Wi-Fi connects.
*
* @return Result indicating initialization success.
*/
Result begin() {
ArduinoOTA.setHostname(OTA_HOSTNAME);
ArduinoOTA.setPort(OTA_PORT);
ArduinoOTA.setPassword(OTA_PASSWORD);
ArduinoOTA.onStart([]() {
const char* type = (ArduinoOTA.getCommand() == U_FLASH)
? "firmware"
: "filesystem";
LOG_INFO(OTA_TAG, "OTA update starting (%s)...", type);
});
ArduinoOTA.onEnd([]() {
LOG_INFO(OTA_TAG, "OTA update complete — rebooting");
});
ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) {
static uint8_t lastPercent = 255;
uint8_t percent = static_cast<uint8_t>((progress * 100) / total);
// Log every 25% to avoid flooding
if (percent / 25 != lastPercent / 25) {
lastPercent = percent;
LOG_INFO(OTA_TAG, "OTA progress: %u%%", percent);
}
});
ArduinoOTA.onError([](ota_error_t error) {
const char* errStr = "Unknown";
switch (error) {
case OTA_AUTH_ERROR: errStr = "Auth failed"; break;
case OTA_BEGIN_ERROR: errStr = "Begin failed"; break;
case OTA_CONNECT_ERROR: errStr = "Connect failed"; break;
case OTA_RECEIVE_ERROR: errStr = "Receive failed"; break;
case OTA_END_ERROR: errStr = "End failed"; break;
}
LOG_ERROR(OTA_TAG, "OTA error: %s (code %u)", errStr, error);
});
ArduinoOTA.begin();
LOG_INFO(OTA_TAG, "OTA initialized (hostname: %s, port: %u)",
OTA_HOSTNAME, OTA_PORT);
return {true, nullptr};
}
/**
* @brief Handle OTA requests — call every loop() iteration.
*
* Non-blocking when no OTA is in progress. During active OTA transfer,
* this processes incoming data chunks.
*/
void update() {
ArduinoOTA.handle();
}
// Delete copy/move
OtaManager(const OtaManager&) = delete;
OtaManager& operator=(const OtaManager&) = delete;
OtaManager(OtaManager&&) = delete;
OtaManager& operator=(OtaManager&&) = delete;
private:
OtaManager() = default;
};
#endif // OTA_H