9#include "freertos/FreeRTOS.h"
10#include "freertos/task.h"
29#include "modules_init.gen.h"
44#include "cdc_msg/MessageTransfer.h"
45#include "driver/rtc_io.h"
47#include "esp_system.h"
50static const char*
TAG =
"BOOT";
62static esp_sleep_wakeup_cause_t
s_wakeupCause = ESP_SLEEP_WAKEUP_UNDEFINED;
71 const char* reasonText;
74 reasonText =
"Secure element alarm";
break;
76 reasonText =
"Secure element offline";
break;
78 reasonText =
"Secure element init failed";
break;
80 reasonText =
"Non-volatile storage unreadable";
break;
82 reasonText =
"Unknown failure";
break;
85 LOG_E(
TAG,
"SYSTEM LOCKDOWN: %s%s%s", reasonText,
86 detail ?
" - " :
"", detail ? detail :
"");
92 const int16_t w =
static_cast<int16_t
>(
s_display->getWidth());
93 const int16_t h =
static_cast<int16_t
>(
s_display->getHeight());
94 constexpr int16_t kMargin = 8;
95 const int16_t modalX = kMargin;
96 const int16_t modalY = kMargin;
97 const int16_t modalW =
static_cast<int16_t
>(w - 2 * kMargin);
98 const int16_t modalH =
static_cast<int16_t
>(h - 2 * kMargin);
101 s_display->fillRect(modalX, modalY, modalW, modalH, 0xFFFF);
102 s_display->drawRect(modalX, modalY, modalW, modalH, 0x0000);
103 s_display->drawRect(modalX + 1, modalY + 1,
104 static_cast<int16_t
>(modalW - 2),
105 static_cast<int16_t
>(modalH - 2), 0x0000);
107 constexpr int16_t kHeaderHeight = 18;
108 s_display->fillRect(
static_cast<int16_t
>(modalX + 2),
109 static_cast<int16_t
>(modalY + 2),
110 static_cast<int16_t
>(modalW - 4),
111 kHeaderHeight, 0x0000);
114 s_display->setCursor(
static_cast<int16_t
>(modalX + 12),
115 static_cast<int16_t
>(modalY + 4));
121 int16_t cursorY =
static_cast<int16_t
>(modalY + kHeaderHeight + 12);
122 s_display->setCursor(
static_cast<int16_t
>(modalX + 10), cursorY);
125 if (detail && *detail) {
126 cursorY =
static_cast<int16_t
>(cursorY + 14);
127 s_display->setCursor(
static_cast<int16_t
>(modalX + 10), cursorY);
131 s_display->setCursor(
static_cast<int16_t
>(modalX + 10),
132 static_cast<int16_t
>(modalY + modalH - 14));
133 s_display->print(
"Power cycle to recover");
144 esp_err_t ret = nvs_flash_init();
145 if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
146 ESP_ERROR_CHECK(nvs_flash_erase());
147 ret = nvs_flash_init();
149 ESP_ERROR_CHECK(ret);
150 return ret == ESP_OK;
162 nvs_handle_t handle = 0;
179 constexpr uint8_t kMaxValid = 0x03;
181 nvs_handle_t handle = 0;
183 esp_err_t openErr = nvs_open(
kBootProfileNs, NVS_READONLY, &handle);
184 esp_err_t readErr = ESP_ERR_NVS_NOT_FOUND;
185 if (openErr == ESP_OK) {
194 bool structuralFault =
false;
195 if (openErr != ESP_OK && openErr != ESP_ERR_NVS_NOT_FOUND) {
196 structuralFault =
true;
198 if (openErr == ESP_OK
200 && readErr != ESP_ERR_NVS_NOT_FOUND
201 && readErr != ESP_ERR_NVS_TYPE_MISMATCH) {
202 structuralFault =
true;
205 if (structuralFault) {
206 ESP_LOGE(
TAG,
"NVS unreadable (open=0x%X read=0x%X), entering lockdown", openErr, readErr);
212 bool present = (openErr == ESP_OK && readErr == ESP_OK);
213 bool valid = present && stored <= kMaxValid;
214 if (
valid && stored == expected) {
219 ESP_LOGW(
TAG,
"Build profile absent, factory reset and seed 0x%02X", expected);
221 ESP_LOGW(
TAG,
"Build profile malformed (0x%02X), factory reset and seed 0x%02X", stored, expected);
223 ESP_LOGW(
TAG,
"Build profile changed (0x%02X -> 0x%02X), factory reset", stored, expected);
242 LOG_W(
TAG,
"Build profile change: wiping TROPIC01 ECC and R-Memory slots");
244 if (!result.sessionReady) {
245 LOG_E(
TAG,
"TROPIC01 factory wipe skipped (SE session unavailable), reset stays pending");
248 LOG_W(
TAG,
"TROPIC01 factory wipe: deleted %u ECC keys, %u R-Memory slots",
249 result.eccDeleted, result.rmemDeleted);
268 LOG_I(
TAG,
"=================================");
270 LOG_I(
TAG,
"Open Hardware Security");
271 LOG_I(
TAG,
"=================================");
299 LOG_D(
TAG,
"Woke from deep sleep, deinit RTC GPIO");
308 LOG_I(
TAG,
"Initializing I2C bus...");
322 LOG_I(
TAG,
"Initializing Power Management...");
325 LOG_I(
TAG,
"Power Management ready (BQ25895)");
329 LOG_E(
TAG,
"Power Management init failed!");
337 LOG_I(
TAG,
"Initializing Sleep Controller...");
340 LOG_I(
TAG,
"Sleep Controller ready (interval: %lus)",
343 LOG_E(
TAG,
"Sleep Controller init failed!");
351 LOG_I(
TAG,
"Initializing WiFi Controller...");
353 if (wifiController && wifiController->
init() && wifiController->
start()) {
354 LOG_I(
TAG,
"WiFi Controller ready");
356 LOG_E(
TAG,
"WiFi Controller init failed!");
364 LOG_I(
TAG,
"Initializing Bluetooth Controller...");
366 if (btController && btController->
init() && btController->
start()) {
367 LOG_I(
TAG,
"Bluetooth Controller ready");
369 LOG_E(
TAG,
"Bluetooth Controller init failed!");
377 LOG_I(
TAG,
"Initializing Keypad...");
380 LOG_I(
TAG,
"Keypad ready (12 keys)");
390 LOG_I(
TAG,
"Initializing Secure Element...");
394 LOG_I(
TAG,
"Secure Element ready (TROPIC01, session active)");
396 LOG_W(
TAG,
"Secure Element initialized but session start failed");
399 LOG_E(
TAG,
"Secure Element init failed!");
437 tropicStorage.init();
438 tropicStorage.start();
440 LOG_I(
TAG,
"TropicStorage cache ready");
448 LOG_I(
TAG,
"Serial Command Interface ready");
458 auto& msg = cdc::msg::MessageTransfer::instance();
462 LOG_I(
TAG,
"Message transfer service ready");
482 LOG_I(
TAG,
"Initializing Display...");
521 modules_register_all();
523 LOG_I(
TAG,
"Initializing modules...");
536 LOG_W(
TAG,
"PluginManager init failed - plugins disabled");
554 LOG_I(
TAG,
"=================================");
555 LOG_I(
TAG,
"System ready. Entering main loop.");
556 LOG_I(
TAG,
"=================================");
578 uint32_t nowMs = esp_timer_get_time() / 1000;
581 cdc::msg::MessageTransfer::instance().tick(nowMs);
586 static UBaseType_t s_minStackFree = 0xFFFFFFFFu;
587 UBaseType_t stackFree = uxTaskGetStackHighWaterMark(
nullptr);
588 if (stackFree < s_minStackFree) {
589 s_minStackFree = stackFree;
590 LOG_I(
TAG,
"main stack low-water: %lu words", (
unsigned long)stackFree);
594 vTaskDelay(pdMS_TO_TICKS(10));
610 ESP_LOGE(
TAG,
"Core service init failed, restarting in 5s");
611 vTaskDelay(pdMS_TO_TICKS(5000));
621 if (profileChanged) {
Native serial commands for direct GPIO / ADC / I2C poking. Independent of plugins - the user controls...
Internationalization with English fallbacks in code and overlay translations loaded at runtime from a...
Discovers, loads, runs and unloads WASM plugins on the badge.
Serial console PLUGIN command bundle. Call once from main.cpp after PluginManager::init() to expose L...
CDC Log: logging over TinyUSB CDC and UART.
#define LOG_W(tag, fmt,...)
#define LOG_D(tag, fmt,...)
void log_init(void)
Logging API implementation.
#define LOG_I(tag, fmt,...)
#define LOG_E(tag, fmt,...)
static EventBus & instance()
Returns singleton event-bus instance.
void process()
Drains queued events and dispatches matching handlers.
void runAllInitializers()
Executes all registered initializers and post-registration housekeeping.
static ModuleRegistry & instance()
Returns the singleton module registry instance.
void dispatchTick(uint32_t nowMs)
Dispatches periodic tick callback to started modules.
static PinManager & instance()
Returns singleton PIN manager instance.
void checkAndResetExpiredLockout()
Clears expired lockout state and resets retry counter.
static ServiceRegistry & instance()
Returns singleton service registry instance.
bool registerService(const char *name, IService *service)
Registers a named service instance.
static constexpr size_t MAX_SERVICES
void triggerLockdown(LockdownReason reason, const char *detail=nullptr)
Latches the lockdown flag. Idempotent and ISR-safe.
void enforceIfLocked()
If locked, runs the shutdown sequence and never returns. Otherwise returns immediately....
static SystemLock & instance()
Returns the process-wide lockdown latch singleton.
void setShutdownHandler(ShutdownHandler handler)
Installs an optional UI handler invoked just before deep sleep. Must be set from main task before mai...
static TropicStorage & instance()
Returns singleton instance of TROPIC metadata cache manager.
virtual bool isTimeSet() const =0
static PluginManager & instance() noexcept
static void init()
Public SerialCmd interface implementation.
static bool process()
Processes one pending input character from the serial console.
static I18n & instance()
Singleton accessor.
bool loadOverlay()
Rescan available languages and (re)load the active overlay.
void restoreOnBoot()
Restores the persisted WiFi intent at boot.
static WifiHandlers & instance()
Returns singleton Wi-Fi handlers instance.
#define BUILD_PROFILE_BYTE
static void initKeypad()
Initializes the keypad input scanner.
static void initDisplay()
Initializes the e-paper display and shows a boot splash.
static void initSystemServices()
Brings up high-level OS services that depend on hardware being ready.
static void initSleepController()
Initializes the sleep controller which manages light/deep sleep.
static void initI2cBus()
Brings up the I2C bus shared by the on-board peripherals.
static cdc::hal::ISecureElement * s_secureElement
static void initSerialCommandInterface()
Initializes the serial command processor over USB CDC.
static cdc::hal::IKeypad * s_keypad
static void wipeTropicForFactoryReset()
Wipes all TROPIC01 R-Memory and ECC slots used by application code, then seeds the build-profile byte...
static esp_sleep_wakeup_cause_t s_wakeupCause
static void handleWakeupAndReleaseRtcGpio()
Caches the wakeup cause and releases RTC GPIO when resuming from deep sleep so that subsequent I2C bu...
static cdc::hal::IDisplay * s_display
static void initMessageTransfer()
Initializes the badge-to-badge message transfer service.
static void initBluetoothController()
Initializes the Bluetooth controller HAL singleton.
void app_main(void)
Main firmware entry point.
static void initPowerManager()
Initializes the BQ25895 power manager and reports current battery state to the log.
static cdc::hal::II2cBus * s_i2cBus
Cached HAL/service singleton pointers used during boot sequence.
static void initHardware()
Brings up all hardware peripherals in dependency order.
static bool initCoreServices()
Stage 1: brings up event bus, USB CDC and the logging subsystem.
static bool checkBuildProfileAndWipeNvs()
Compares the persisted build profile byte against the compiled-in value and triggers a NVS wipe on mi...
static void initRtc()
Initializes the RTC and warns when the system time has not been set.
static void initPluginSystem()
Bring up the WAMR runtime, mount the plugins partition and discover installed plugins....
static void initTropicStorage()
Initializes and registers the TROPIC01 storage cache service.
static void seedBuildProfile()
Persists the current build-profile byte, marking the factory reset complete.
static void runMainLoopIteration()
Single iteration of the cooperative main loop.
static cdc::core::AttestationKeyService s_attestationService
static void lockdownShutdownHandler(cdc::core::LockdownReason reason, const char *detail)
Draws a modal-style system-lockdown halt screen before deep sleep.
static void startApp()
Final startup step: completes USB CDC enumeration and prints banner.
static void initUi()
Initializes the App UI layer with the previously prepared HAL deps.
static void initAttestationService()
Registers the attestation-key service with the global ServiceRegistry.
static cdc::hal::IPowerManager * s_powerManager
static void initModules()
Registers all auto-generated modules and runs their initializers.
static cdc::hal::ISleepController * s_sleepController
static void initWifiController()
Initializes the WiFi controller HAL singleton.
static void initSecureElement()
Initializes the TROPIC01 secure element and starts an active session.
static bool initNvs()
Stage 0: initializes NVS flash storage with automatic erase on version-mismatch or out-of-pages error...
esp_err_t wipeNvs()
Erases the NVS partition and re-initializes it blank.
TropicWipeResult wipeTropic(hal::ISecureElement *se, uint16_t progressEvery=0, void(*onRmemProgress)(uint16_t current, uint16_t total)=nullptr)
Iterates every TROPIC01 ECC slot (0..ECC_SLOT_COUNT-1) and R-Memory slot (0..RMEM_SLOT_COUNT-1),...
LockdownReason
Reason the system entered lockdown.
constexpr const char * kBootProfileNs
constexpr const char * kBootProfileKey
IDisplay * getDisplayInstance()
Returns lazily created singleton display instance.
IWifiController * getWifiControllerInstance()
Returns the singleton Wi-Fi controller service instance.
II2cBus * getI2cBus0()
Returns singleton instance of I2C bus 0.
IKeypad * getKeypadInstance()
Returns the singleton keypad service instance.
IBluetoothController * getBluetoothControllerInstance()
Returns singleton Bluetooth stub when NimBLE is unavailable.
IPowerManager * getPowerManagerInstance()
Returns the singleton power manager instance.
IRtc * getRtcInstance()
Returns the singleton RTC service instance.
ISleepController * getSleepControllerInstance()
Returns the singleton sleep controller service instance.
ISecureElement * getSecureElementInstance()
Returns singleton secure-element stub instance.
void registerPluginSerialCommands()
void registerGpioSerialCommands()
void ui_process(uint32_t nowMs)
Main UI tick: input processing, timeouts, status updates, and rendering.
void ui_init(const UiDeps &deps)
Initializes App UI, builds all core views, and wires callbacks.
void ui_on_modules_ready()
Refreshes module-backed menus once module startup is complete.
hal::ISleepController * sleep
hal::ISecureElement * secureElement
hal::IPowerManager * power
bool usb_cdc_start(void)
Starts USB CDC runtime (or triggers re-enumeration in early-debug mode).