ESPRebootManager is a centralized reboot coordinator for ESP32 projects. Any module can request a reboot, registered guard callbacks vote whether reboot is safe, and registered evaluation callbacks report the final decision.
- Multi-guard callback API:
onRebootRequest(...). - Multi-evaluation event API:
onEvaluation(...). - Deferred guard votes: any guard can defer and force evaluation restart from guard #1.
- Async request handling on a dedicated FreeRTOS task.
- Single active request policy with deterministic
Busyresponses. - Polling support for missed events via
isRebootRequested(),rebootStatus(), andlastEvaluation(). - Bounded reason/detail buffers for deterministic memory usage.
- PlatformIO: add
https://github.com/ESPToolKit/esp-reboot-manager.gittolib_deps. - Arduino IDE: install as ZIP from this repository.
#include <ESPRebootManager.h>
#include <cstdio>#include <ESPRebootManager.h>
ESPRebootManager rebootManager;
void setup() {
ESPRebootManagerConfig config;
config.taskName = "reboot-manager";
config.taskStackSizeBytes = 6 * 1024;
config.taskPriority = 1;
config.taskCoreId = tskNO_AFFINITY;
config.callbackTimeoutMs = 1000;
rebootManager.init(config);
rebootManager.onRebootRequest([](const RebootRequestContext& ctx) {
RebootVote vote;
if (ctx.reason[0] == '\0') {
vote.allow = false;
std::snprintf(vote.detail, sizeof(vote.detail), "empty reason is not allowed");
}
return vote;
});
rebootManager.onEvaluation([](const RebootEvaluation& evaluation) {
if (evaluation.accepted) {
Serial.printf("reboot accepted: id=%lu reason=%s\n",
static_cast<unsigned long>(evaluation.requestId),
evaluation.reason);
} else {
Serial.printf("reboot rejected: code=%u blocker=%s detail=%s\n",
static_cast<unsigned>(evaluation.code),
evaluation.blockerName,
evaluation.detail);
}
});
(void)rebootManager.requestReboot("firmware-update", 1500);
}basic_reboot_request: minimal accepted request flow with guard + evaluation callbacks.busy_and_retry: demonstratesBusysubmit status and retrying once the manager returns toIdle.guard_blocking: shows a guard rejecting reboot until a maintenance window opens.callback_timeout: demonstratesCallbackTimeoutbehavior when a guard runs longer thancallbackTimeoutMs.
bool init(const ESPRebootManagerConfig& config = {})void deinit()bool isInitialized() constRebootCallbackId onRebootRequest(GuardCallback cb)RebootCallbackId onEvaluation(EvaluationCallback cb)bool offRebootRequest(RebootCallbackId id)bool offEvaluation(RebootCallbackId id)RebootSubmitResult requestReboot(const char* reason, uint32_t delayMs = 0)bool isRebootRequested() constRebootRequestStatus rebootStatus() constRebootEvaluation lastEvaluation() const
Accepted path:
Idle -> Requested -> Evaluating -> Delaying -> Rebooting -> Idle
Deferred path:
Idle -> Requested -> Evaluating -> Deferred -> Evaluating -> ...
If any guard rejects (or times out), the flow is:
Idle -> Requested -> Evaluating -> Idle
allow=true: guard accepts current pass.allow=false: request is blocked immediately and reboot does not proceed.defer=true: request is deferred immediately, remaining guards are skipped for the current pass, and evaluation restarts from the first guard afterdeferTimeoutMs(minimum 1ms when omitted/0).
This repository follows the firmware formatting baseline from esptoolkit-template:
.clang-formatis the source of truth for C/C++/INO layout..editorconfigenforces tabs (tab_width = 4), LF endings, and final newline.- Format all tracked firmware sources with
bash scripts/format_cpp.sh.
MIT - see LICENSE.md.