diff --git a/main/CMakeLists.txt b/main/CMakeLists.txt index 47ad04b..770bea8 100644 --- a/main/CMakeLists.txt +++ b/main/CMakeLists.txt @@ -128,6 +128,7 @@ set(headers displays/menus/networksettingsmenu.h displays/menus/otamenu.h displays/menus/profilesmenu.h + displays/menus/recoverymenu.h displays/menus/remotecontrolmodesettingsmenu.h displays/menus/selectbuildserverbranch.h displays/menus/selectbuildservermenu.h @@ -376,6 +377,7 @@ set(sources displays/menus/networksettingsmenu.cpp displays/menus/otamenu.cpp displays/menus/profilesmenu.cpp + displays/menus/recoverymenu.cpp displays/menus/remotecontrolmodesettingsmenu.cpp displays/menus/selectbuildserverbranch.cpp displays/menus/selectbuildservermenu.cpp diff --git a/main/bobbyschedulertask.h b/main/bobbyschedulertask.h index 38be85e..e77e2d5 100644 --- a/main/bobbyschedulertask.h +++ b/main/bobbyschedulertask.h @@ -3,11 +3,42 @@ // 3rdparty lib includes #include +#include + class BobbySchedulerTask : public espcpputils::SchedulerTask { public: - using SchedulerTask::SchedulerTask; - void setup() const { SchedulerTask::setup(); m_wasInitialized = true; } + // using SchedulerTask::SchedulerTask; -> we need to add one more parameter + BobbySchedulerTask(const char *name, void (&setupCallback)(), void (&loopCallback)(), + espchrono::millis_clock::duration loopInterval, bool use_in_recovery = false, + bool intervalImportant = false, + std::string (*perfInfo)() = nullptr) : + espcpputils::SchedulerTask(name, setupCallback, loopCallback, loopInterval, intervalImportant, perfInfo), + m_use_in_recovery{use_in_recovery} + { + } + void setup(bool in_recovery = false) + { + m_in_recovery = in_recovery; + if (in_recovery && !m_use_in_recovery) + { + ESP_LOGI("BobbySchedulerTask", "Skipping setup of %s (%s)", name(), m_use_in_recovery ? "use in recovery" : "no use in recovery"); + return; + } + SchedulerTask::setup(); + m_wasInitialized = true; + ESP_LOGI("BobbySchedulerTask", "Task %s initialized", name()); + } + void loop() + { + if (!m_in_recovery || m_use_in_recovery) + { + // ESP_LOGI("BobbySchedulerTask", "Loop %s", name()); + SchedulerTask::loop(); + } + } bool isInitialized() const { return m_wasInitialized; } private: mutable bool m_wasInitialized{false}; + const bool m_use_in_recovery; + bool m_in_recovery{false}; }; diff --git a/main/displays/menus/recoverymenu.cpp b/main/displays/menus/recoverymenu.cpp new file mode 100644 index 0000000..f2759cd --- /dev/null +++ b/main/displays/menus/recoverymenu.cpp @@ -0,0 +1,56 @@ +#include "recoverymenu.h" + +// 3dparty lib includes +#include + +// local includes +#include "actions/rebootaction.h" +#include "bobbycheckbox.h" +#include "bobbyerrorhandler.h" +#include "icons/reboot.h" +#include "newsettings.h" + +namespace { +constexpr char TEXT_REBOOT[] = "Reboot"; +} + +class BasicFeatureFlagMenuItem : public espgui::MenuItem +{ +public: + BasicFeatureFlagMenuItem(ConfiguredFeatureFlag &flag) : + m_flag{flag} + {} + + std::string text() const override + { + return m_flag.isEnabled.nvsName(); + } + + void triggered() override + { + if (auto result = m_flag.isEnabled.write(configs.nvs_handle_user, !m_flag.isEnabled.value()); !result) + BobbyErrorHandler{}.errorOccurred(std::move(result).error()); + } + + const espgui::MenuItemIcon *icon() const override + { + return m_flag.isEnabled.value() ? &espgui::icons::checked : &espgui::icons::unchecked; + } +private: + ConfiguredFeatureFlag &m_flag; +}; + +RecoveryMenu::RecoveryMenu() +{ + using namespace espgui; + + configs.callForEveryFeature([&](ConfiguredFeatureFlag &feature){ + constructMenuItem(feature); + }); + constructMenuItem, RebootAction, StaticMenuItemIcon<&bobbyicons::reboot>>>(); +} + +std::string RecoveryMenu::text() const +{ + return "Recovery Menu"; +} diff --git a/main/displays/menus/recoverymenu.h b/main/displays/menus/recoverymenu.h new file mode 100644 index 0000000..16cc3d1 --- /dev/null +++ b/main/displays/menus/recoverymenu.h @@ -0,0 +1,16 @@ +#pragma once + +// local includes +#include "displays/bobbymenudisplay.h" + +class RecoveryMenu : public BobbyMenuDisplay +{ + using Base = BobbyMenuDisplay; +public: + RecoveryMenu(); + + std::string text() const override; + + void back() override {} + +}; diff --git a/main/main.cpp b/main/main.cpp index 6c41092..50f422f 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -15,6 +15,8 @@ using namespace std::chrono_literals; #include #include #include +#include +#include // local includes #include "bobbycar-common.h" @@ -29,16 +31,18 @@ using namespace std::chrono_literals; #else #include "modes/defaultmode.h" #endif -#include "displays/statusdisplay.h" -#include "displays/lockscreen.h" -#include "displays/potiscalibratedisplay.h" #include "displays/buttoncalibratedisplay.h" +#include "displays/lockscreen.h" +#include "displays/menus/recoverymenu.h" +#include "displays/potiscalibratedisplay.h" +#include "displays/statusdisplay.h" #include "newsettings.h" #include "taskmanager.h" namespace { espchrono::millis_clock::time_point lastStatsPush; std::optional lastStatsUpdate; +RTC_NOINIT_ATTR bool recovery; } // namespace extern "C" void app_main() @@ -48,6 +52,48 @@ extern "C" void app_main() digitalWrite(PINS_LEDBACKLIGHT, ledBacklightInverted ? LOW : HIGH); #endif + if (const auto reset_reason = esp_reset_reason(); reset_reason == ESP_RST_POWERON) + { + recovery = false; + } + + if (recovery) + { + initScreen(); + + ESP_LOGE(TAG, "Recovery mode (%s)", espcpputils::toString(esp_reset_reason()).c_str()); + bootLabel.redraw("Entering recovery mode"); + + if (const auto result = configs.init("bobbycar"); result != ESP_OK) + ESP_LOGE(TAG, "config_init_settings() failed with %s", esp_err_to_name(result)); + + for (auto &task : schedulerTasks) + { + task.setup(recovery); + } + + espgui::switchScreen(); + + recovery = false; + + while (true) + { + const auto now = espchrono::millis_clock::now(); + + for (auto &schedulerTask : schedulerTasks) + { + if (schedulerTask.isInitialized()) + schedulerTask.loop(); + } + + espcpputils::delay(1ms); + }; + } + else + { + recovery = true; + } + initScreen(); bootLabel.redraw("settings"); @@ -67,12 +113,12 @@ extern "C" void app_main() else ESP_LOGE("BOBBY", "init() failed"); - for (const auto &task : schedulerTasks) + for (auto &task : schedulerTasks) { if (checkEnabledByName(task.name())) { bootLabel.redraw(task.name()); - task.setup(); + task.setup(false); } } @@ -113,6 +159,12 @@ extern "C" void app_main() { const auto now = espchrono::millis_clock::now(); + if (recovery && now.time_since_epoch() > 5s) + { + ESP_LOGI(TAG, "Booting successful, disabling recovery..."); + recovery = false; + } + // if (!heap_caps_check_integrity_all(true)) // ESP_LOGW(TAG, "OIS IM OARSCH!!!!!"); @@ -150,5 +202,7 @@ extern "C" void app_main() } } } + + espcpputils::delay(1ms); } } diff --git a/main/newsettings.h b/main/newsettings.h index 536b9de..78919b1 100644 --- a/main/newsettings.h +++ b/main/newsettings.h @@ -470,11 +470,11 @@ public: ConfiguredFeatureFlag garage {"f_garage" }; ConfiguredFeatureFlag cloud {"f_cloud", false, false, "cloud"}; ConfiguredFeatureFlag udpcloud {"f_udpcloud", false, false, "udpcloud"}; - ConfiguredFeatureFlag dnsannounce {"f_dnsannounce"}; + ConfiguredFeatureFlag dnsannounce {"f_dnsannounce", false, false, "dnsannounce"}; ConfiguredFeatureFlag ntp {"f_ntp", false, false, "time"}; ConfiguredFeatureFlag ble {"f_ble", false, false, "ble"}; ConfiguredFeatureFlag ota {"f_ota", false, false, "ota"}; - ConfiguredFeatureFlag webserver {"featureWebserv", true}; + ConfiguredFeatureFlag webserver {"featureWebserv", true, false, "webserver"}; ConfiguredFeatureFlag gschissene_diode {"featurDiodeHin"}; ConfiguredFeatureFlag esp_now {"featureEspNow", false, false, "espnow"}; } feature; diff --git a/main/taskmanager.cpp b/main/taskmanager.cpp index e24414d..b7eb6f2 100644 --- a/main/taskmanager.cpp +++ b/main/taskmanager.cpp @@ -52,43 +52,43 @@ constexpr const char * const TAG = "TASKS"; void not_needed() {} BobbySchedulerTask schedulerTasksArr[] { - BobbySchedulerTask { "wifi", wifi_begin, wifi_update, 100ms }, + BobbySchedulerTask { "wifi", wifi_begin, wifi_update, 100ms, false }, #if defined(FEATURE_DPAD) || defined(FEATURE_DPAD_3WIRESW) || defined(FEATURE_DPAD_5WIRESW) || defined(FEATURE_DPAD_5WIRESW_2OUT) || defined(FEATURE_DPAD_6WIRESW) || defined(DPAD_BOARDCOMPUTER_V2) - BobbySchedulerTask { bobbydpad::dpad_name, bobbydpad::dpad_init, bobbydpad::dpad_update, 20ms }, + BobbySchedulerTask { bobbydpad::dpad_name, bobbydpad::dpad_init, bobbydpad::dpad_update, 20ms, true }, #endif #ifdef FEATURE_ROTARY - BobbySchedulerTask { "rotary", initRotary, updateRotary, 20ms }, + BobbySchedulerTask { "rotary", initRotary, updateRotary, 20ms, false }, #endif #ifdef FEATURE_MOSFETS - BobbySchedulerTask { "mosfets", init_mosfets, update_mosfets, 100ms }, + BobbySchedulerTask { "mosfets", init_mosfets, update_mosfets, 100ms, false }, #endif - BobbySchedulerTask { "time", initTime, updateTime, 100ms }, - BobbySchedulerTask { "potis", initPotis, readPotis, 20ms }, + BobbySchedulerTask { "time", initTime, updateTime, 100ms, false }, + BobbySchedulerTask { "potis", initPotis, readPotis, 20ms, false }, #ifdef FEATURE_BLUETOOTH - BobbySchedulerTask { "bluetooth", bluetooth_init, bluetooth_update, 100ms }, + BobbySchedulerTask { "bluetooth", bluetooth_init, bluetooth_update, 100ms, false }, #ifdef FEATURE_BMS - BobbySchedulerTask { "bms", bms::init, bms::update, 100ms }, + BobbySchedulerTask { "bms", bms::init, bms::update, 100ms, false }, #endif #endif #ifdef FEATURE_CAN - BobbySchedulerTask { "can", can::initCan, can::updateCan, 10ms }, + BobbySchedulerTask { "can", can::initCan, can::updateCan, 10ms, false }, #endif - BobbySchedulerTask { "debuginput", initDebugInput, handleDebugInput, 50ms }, + BobbySchedulerTask { "debuginput", initDebugInput, handleDebugInput, 50ms, true }, #ifdef FEATURE_SERIAL - BobbySchedulerTask { "serial", initSerial, updateSerial, 50ms }, + BobbySchedulerTask { "serial", initSerial, updateSerial, 50ms, false }, #endif - BobbySchedulerTask { "ota", initOta, handleOta, 50ms }, - BobbySchedulerTask { "ble", initBle, handleBle, 100ms }, - BobbySchedulerTask { "webserver", initWebserver, handleWebserver, 100ms }, - BobbySchedulerTask { "ledstrip", initLedStrip, updateLedStrip, 30ms }, - BobbySchedulerTask { "espnow", espnow::initESPNow, espnow::handle, 100ms }, - BobbySchedulerTask { "cloud", initCloud, updateCloud, 50ms }, - BobbySchedulerTask { "udpcloud", udpCloudInit, udpCloudUpdate, 50ms }, - BobbySchedulerTask { "drivingmode", initDrivingMode, updateDrivingMode, 20ms }, - BobbySchedulerTask { "drivingstatistics", initStatistics, calculateStatistics, 100ms }, - BobbySchedulerTask { "dnsannounce", init_dns_announce, handle_dns_announce, 100ms }, - BobbySchedulerTask { "updateDisp", not_needed, updateDisplay, 20ms }, - BobbySchedulerTask { "redrawDisp", not_needed, redrawDisplay, 20ms }, + BobbySchedulerTask { "ota", initOta, handleOta, 50ms, false }, + BobbySchedulerTask { "ble", initBle, handleBle, 100ms, false }, + BobbySchedulerTask { "webserver", initWebserver, handleWebserver, 100ms, false }, + BobbySchedulerTask { "ledstrip", initLedStrip, updateLedStrip, 30ms, false }, + BobbySchedulerTask { "espnow", espnow::initESPNow, espnow::handle, 100ms, false }, + BobbySchedulerTask { "cloud", initCloud, updateCloud, 50ms, false }, + BobbySchedulerTask { "udpcloud", udpCloudInit, udpCloudUpdate, 50ms, false }, + BobbySchedulerTask { "drivingmode", initDrivingMode, updateDrivingMode, 20ms, false }, + BobbySchedulerTask { "drivingstatistics", initStatistics, calculateStatistics, 100ms, false }, + BobbySchedulerTask { "dnsannounce", init_dns_announce, handle_dns_announce, 100ms, false }, + BobbySchedulerTask { "updateDisp", not_needed, updateDisplay, 20ms, true }, + BobbySchedulerTask { "redrawDisp", not_needed, redrawDisplay, 20ms, true }, }; } // namespace