diff --git a/README.md b/README.md index 7262e21..6c19ebb 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,13 @@ # bobbycar-boardcomputer-firmware -![Build Status](https://github.com/bobbycar-graz/bobbycar-boardcomputer-firmware/actions/workflows/main.yml/badge.svg) -[![Codacy Badge](https://api.codacy.com/project/badge/Grade/04c264db40c44276833f43b9c083dc14)](https://app.codacy.com/gh/bobbycar-graz/bobbycar-boardcomputer-firmware?utm_source=github.com&utm_medium=referral&utm_content=bobbycar-graz/bobbycar-boardcomputer-firmware&utm_campaign=Badge_Grade_Dashboard) +## Build status + +| Service | Status | +| :--- | ---: | +| Actions (master) | ![Build Status](https://github.com/bobbycar-graz/bobbycar-boardcomputer-firmware/actions/workflows/main.yml/badge.svg) | +| Actions (cloud) | ![Build Status (cloud)](https://github.com/bobbycar-graz/bobbycar-boardcomputer-firmware/actions/workflows/main.yml/badge.svg?branch=cloud) | +| Codacy | [![Codacy Badge](https://api.codacy.com/project/badge/Grade/04c264db40c44276833f43b9c083dc14)](https://app.codacy.com/gh/bobbycar-graz/bobbycar-boardcomputer-firmware?utm_source=github.com&utm_medium=referral&utm_content=bobbycar-graz/bobbycar-boardcomputer-firmware&utm_campaign=Badge_Grade_Dashboard) | + ## How to clone ? (READ THIS OR YOU WILL FAIL) diff --git a/config_comred.cmake b/config_comred.cmake index e21a2f1..f2dbf95 100644 --- a/config_comred.cmake +++ b/config_comred.cmake @@ -115,6 +115,8 @@ set(BOBBYCAR_BUILDFLAGS -DLEDSTRIP_WRONG_DIRECTION -DLEDSTRIP_ANIMATION_DEFAULT=1 -DLEDS_PER_METER=60 + -DOLD_NVS + -DFEATURE_DNS_NS ) if (EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/ignore/lockscreen_plugin.cmake") diff --git a/config_feedc0de.cmake b/config_feedc0de.cmake index bbe0077..ec39b1a 100644 --- a/config_feedc0de.cmake +++ b/config_feedc0de.cmake @@ -88,11 +88,13 @@ set(BOBBYCAR_BUILDFLAGS -DLEDBACKLIGHT_INVERTED -DFEATURE_GARAGE -DFEATURE_NTP -# -DFEATURE_WIRELESS_CONFIG + -DFEATURE_WIRELESS_CONFIG -DFEATURE_LEDSTRIP -DPINS_LEDSTRIP=26 -DLEDSTRIP_LENGTH=200 # -DLEDSTRIP_WRONG_DIRECTION -# -DLEDSTRIP_ANIMATION_DEFAULT=0 + -DLEDSTRIP_ANIMATION_DEFAULT=0 -DLEDS_PER_METER=144 + -DOLD_NVS + -DFEATURE_DNS_NS ) diff --git a/config_greyhash.cmake b/config_greyhash.cmake index d4705dd..abf4e7a 100644 --- a/config_greyhash.cmake +++ b/config_greyhash.cmake @@ -91,4 +91,6 @@ set(BOBBYCAR_BUILDFLAGS -DLEDSTRIP_DEFAULT_BRIGHTNESS=100 # -DLEDSTRIP_WRONG_DIRECTION # -DLEDSTRIP_ANIMATION_DEFAULT=0 + -DOLD_NVS +# -DFEATURE_DNS_NS ) diff --git a/config_mick.cmake b/config_mick.cmake index c9902c3..16bf55d 100644 --- a/config_mick.cmake +++ b/config_mick.cmake @@ -89,4 +89,6 @@ set(BOBBYCAR_BUILDFLAGS # -DPINS_LEDSTRIP=26 # -DLEDSTRIP_WRONG_DIRECTION # -DLEDSTRIP_ANIMATION_DEFAULT=0 + -DOLD_NVS +# -DFEATURE_DNS_NS ) diff --git a/config_peter.cmake b/config_peter.cmake index 003bc61..e26573a 100644 --- a/config_peter.cmake +++ b/config_peter.cmake @@ -95,6 +95,8 @@ set(BOBBYCAR_BUILDFLAGS # -DLEDSTRIP_WRONG_DIRECTION -DLEDSTRIP_ANIMATION_DEFAULT=2 -DLEDS_PER_METER=144 + -DOLD_NVS + -DFEATURE_DNS_NS ) if (EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/ignore/lockscreen_plugin.cmake") diff --git a/export.sh b/export.sh index 89ded47..2ca8801 100644 --- a/export.sh +++ b/export.sh @@ -10,6 +10,7 @@ fi . ${BOBBY_ROOT}/esp-idf/export.sh complete -W "$(./switchconf.sh --list)" ./switchconf.sh +complete -W "$(./switchconf.sh --list)" switchconf BOBBY_INIT_FAILED= @@ -48,3 +49,7 @@ then echo "run ./switchconf.sh to fix all listed issues" return fi +export PATH=$PATH:$(pwd)/tools +alias open-ide=open_ide +alias switchconf=./switchconf.sh +alias bobby-build="idf.py build" diff --git a/main/CMakeLists.txt b/main/CMakeLists.txt index 1d64070..7047a3e 100644 --- a/main/CMakeLists.txt +++ b/main/CMakeLists.txt @@ -25,6 +25,8 @@ set(headers dpad5wire.h dpad6wire.h dpad.h + dnsannounce.h + drivingstatistics.h esptexthelpers.h feedbackparser.h globals.h @@ -55,6 +57,7 @@ set(headers webserver_ota.h webserver_settings.h webserver_stringsettings.h + webserver_dumpnvs.h wifi_bobbycar.h wifitexthelpers.h accessors/globalaccessors.h @@ -98,6 +101,7 @@ set(headers displays/menus/aboutmenu.h displays/menus/accesspointwifisettingsmenu.h displays/menus/batterymenu.h + displays/menus/batterydebugmenu.h displays/menus/blesettingsmenu.h displays/menus/bluetoothsettingsmenu.h displays/menus/bmsmenu.h @@ -137,6 +141,7 @@ set(headers displays/menus/selectmodemenu.h displays/menus/settingsmenu.h displays/menus/stationwifisettingsmenu.h + displays/menus/statisticsmenu.h displays/menus/tempomatmodesettingsmenu.h displays/menus/timersmenu.h displays/menus/timesettingsmenu.h @@ -150,6 +155,7 @@ set(headers displays/starfielddisplay.h displays/statusdisplay.h displays/updatedisplay.h + displays/menus/ledstripselectotamode.h icons/alert.h icons/battery.h icons/bluetooth.h @@ -173,6 +179,7 @@ set(headers icons/time.h icons/update.h icons/wifi.h + icons/statistics.h modes/defaultmode.h modes/gametrakmode.h modes/ignoreinputmode.h @@ -207,6 +214,8 @@ set(sources dpad5wire.cpp dpad6wire.cpp dpad.cpp + dnsannounce.cpp + drivingstatistics.cpp esptexthelpers.cpp feedbackparser.cpp globals.cpp @@ -238,6 +247,7 @@ set(sources webserver_ota.cpp webserver_settings.cpp webserver_stringsettings.cpp + webserver_dumpnvs.cpp wifi_bobbycar.cpp wifitexthelpers.cpp accessors/globalaccessors.cpp @@ -281,6 +291,7 @@ set(sources displays/menus/aboutmenu.cpp displays/menus/accesspointwifisettingsmenu.cpp displays/menus/batterymenu.cpp + displays/menus/batterydebugmenu.cpp displays/menus/blesettingsmenu.cpp displays/menus/bluetoothsettingsmenu.cpp displays/menus/bmsmenu.cpp @@ -313,6 +324,7 @@ set(sources displays/menus/motorstatedebugmenu.cpp displays/menus/otamenu.cpp displays/menus/selectotabuildmenu.cpp + displays/menus/statisticsmenu.cpp displays/menus/presetsmenu.cpp displays/menus/profilesmenu.cpp displays/menus/selectbatterytypemenu.cpp @@ -325,6 +337,7 @@ set(sources displays/menus/timesettingsmenu.cpp displays/menus/wifiscanmenu.cpp displays/menus/wifisettingsmenu.cpp + displays/menus/ledstripselectotamode.cpp displays/metersdisplay.cpp displays/pingpongdisplay.cpp displays/poweroffdisplay.cpp @@ -356,6 +369,7 @@ set(sources icons/time.cpp icons/update.cpp icons/wifi.cpp + icons/statistics.cpp modes/defaultmode.cpp modes/gametrakmode.cpp modes/ignoreinputmode.cpp diff --git a/main/accessors/settingsaccessors.h b/main/accessors/settingsaccessors.h index 7280cbe..f1860dc 100644 --- a/main/accessors/settingsaccessors.h +++ b/main/accessors/settingsaccessors.h @@ -171,6 +171,8 @@ struct LedsStVOFrontLengthAccessor : public RefAccessorSaveSettings { i struct EnableLedstripStVOFrontlight : public RefAccessorSaveSettings { bool &getRef() const override { return settings.ledstrip.stvoFrontEnable; } }; struct AnimationMultiplierAccessor : public RefAccessorSaveSettings { int16_t &getRef() const override { return settings.ledstrip.animationMultiplier; } }; struct LedstripBrightnessAccessor : public RefAccessorSaveSettings { uint8_t &getRef() const override { return settings.ledstrip.brightness; } }; +struct LedstripEnableBlinkAnimation : public RefAccessorSaveSettings { bool &getRef() const override { return settings.ledstrip.enableAnimBlink; } }; +struct LedstripOtaAnimationAccessor : public RefAccessorSaveSettings { OtaAnimationModes &getRef() const override { return settings.ledstrip.otaMode; } }; #endif // Battery diff --git a/main/battery.cpp b/main/battery.cpp index d058eea..b17931a 100644 --- a/main/battery.cpp +++ b/main/battery.cpp @@ -88,17 +88,31 @@ float getBatteryPercentage(float batVoltage, BatteryCellType cellType) CURVE(2.80, 2.50, 2.5, 2.60) break; } + case BatteryCellType::BAK_25R: + { + const float expected_ah = 2.5; + if(cellVoltage > 4.15){ + return 100; + } + CURVE(4.15, 4.06, 0, 0.25) + CURVE(4.06, 3.96, 0.25, 0.5) + CURVE(3.96, 3.88, 0.5, 0.75) + CURVE(3.88, 3.77, 0.75, 1) + CURVE(3.77, 3.68, 1, 1.25) + CURVE(3.68, 3.62, 1.25, 1.5) + CURVE(3.62, 3.56, 1.5, 1.75) + CURVE(3.56, 3.47, 1.75, 2) + CURVE(3.47, 3.31, 2, 2.25) + CURVE(3.31, 2.50, 2.25, 2.5) + break; + } } return 0.f; } float getRemainingWattHours() { - float target_mah = 2000; //default - if(BatteryCellType(settings.battery.cellType) == BatteryCellType::_22P) target_mah = 2200; - if(BatteryCellType(settings.battery.cellType) == BatteryCellType::HG2) target_mah = 3000; - if(BatteryCellType(settings.battery.cellType) == BatteryCellType::MH1) target_mah = 3200; - if(BatteryCellType(settings.battery.cellType) == BatteryCellType::VTC5) target_mah = 2600; + float target_mah = getTarget_mAh(); float avgVoltage = 0; for (auto &controller : controllers) @@ -110,6 +124,23 @@ float getRemainingWattHours() return (target_mah / 1000.f) * 3.7 * settings.battery.cellsParallel * settings.battery.cellsSeries * (getBatteryPercentage(avgVoltage, BatteryCellType(settings.battery.cellType)) / 100); } +float getPercentageByWh(float wh) +{ + const float maxWh = (getTarget_mAh() / 1000.f) * 3.7 * settings.battery.cellsParallel * settings.battery.cellsSeries; + return maxWh / wh; +} + +float getTarget_mAh() +{ + float target_mah = 2000; //default + if(BatteryCellType(settings.battery.cellType) == BatteryCellType::_22P) target_mah = 2200; + if(BatteryCellType(settings.battery.cellType) == BatteryCellType::HG2) target_mah = 3000; + if(BatteryCellType(settings.battery.cellType) == BatteryCellType::MH1) target_mah = 3200; + if(BatteryCellType(settings.battery.cellType) == BatteryCellType::VTC5) target_mah = 2600; + if(BatteryCellType(settings.battery.cellType) == BatteryCellType::BAK_25R) target_mah = 2500; + return target_mah; +} + std::string getBatteryPercentageString() { float avgVoltage = 0; @@ -123,9 +154,15 @@ std::string getBatteryPercentageString() return output; } +std::string getBatteryAdvancedPercentageString() +{ + std::string output = fmt::format("Battery: {:.1f}%", getPercentageByWh(drivingStatistics.batteryWhEstimate)); + return output; +} + std::string getBatteryRemainingWattHoursString() { - return fmt::format("{:.1f} Wh", getRemainingWattHours()); + return fmt::format("{:.1f}Wh", getRemainingWattHours()); } std::string getBatteryCellTypeString() @@ -137,3 +174,14 @@ std::string getRemainingRangeString() { return fmt::format("{:.1f} km", getRemainingWattHours() / settings.battery.watthoursPerKilometer); } + +std::string getBatteryDebugString() +{ + float avgVoltage = 0; + for (auto &controller : controllers) + { + avgVoltage += controller.getCalibratedVoltage(); + } + avgVoltage = avgVoltage / controllers.size(); + return fmt::format("{:.1f}V {}A", avgVoltage, sumCurrent); +} diff --git a/main/battery.h b/main/battery.h index 4a69a6a..bcb57ec 100644 --- a/main/battery.h +++ b/main/battery.h @@ -12,7 +12,8 @@ x(_22P) \ x(HG2) \ x(MH1) \ - x(VTC5) + x(VTC5) \ + x(BAK_25R) DECLARE_TYPESAFE_ENUM(BatteryCellType, : uint8_t, BatteryCellTypeValues) float getBatteryPercentage(float batVoltage, BatteryCellType cellType); @@ -26,3 +27,10 @@ std::string getBatteryRemainingWattHoursString(); std::string getBatteryCellTypeString(); std::string getRemainingRangeString(); + +std::string getBatteryDebugString(); + +std::string getBatteryAdvancedPercentageString(); + +float getPercentageByWh(float wh); +float getTarget_mAh(); diff --git a/main/buildserver.h b/main/buildserver.h index b190c78..1885f8f 100644 --- a/main/buildserver.h +++ b/main/buildserver.h @@ -24,9 +24,8 @@ namespace { static std::string url_for_latest = ""; static std::array availableVersions = {}; bool request_running = false; - uint16_t request_failed = false; + std::string request_failed; bool parsing_finished = false; - static bool redownload = true; cpputils::DelayedConstruction request; std::string get_ota_url_from_index(uint16_t index) @@ -70,20 +69,9 @@ namespace { return url_for_latest; } - std::string fix_url(std::string url) - { - std::string fixed_url = url; - if (fixed_url.find("http") == std::string::npos) - { - fixed_url = fmt::format("http://{}", fixed_url); - } - return fixed_url; - } - std::string get_descriptor_url(std::string base_url) { - std::string url = fix_url(base_url); - return fmt::format("{}/otaDescriptor?username={}", url, OTA_USERNAME); + return fmt::format("{}/otaDescriptor?username={}", base_url, OTA_USERNAME); } void parse_response_into_variables(std::string response) @@ -111,10 +99,9 @@ namespace { index = 0; - url_for_latest = fix_url(fmt::format("{}{}", stringSettings.otaServerUrl, doc["latest"].as())); - url_for_hashes = fix_url(fmt::format("{}{}", stringSettings.otaServerUrl, doc["url"].as())); + url_for_latest = fmt::format("{}{}", stringSettings.otaServerUrl, doc["latest"].as()); + url_for_hashes = fmt::format("{}{}", stringSettings.otaServerUrl, doc["url"].as()); parsing_finished = true; - redownload = false; } void setup_request() @@ -125,11 +112,6 @@ namespace { void start_descriptor_request(std::string server_base_url) { - if (!redownload) - { - parsing_finished = true; - return; - } if (!request.constructed()) { ESP_LOGW("BOBBY", "request is im oarsch"); @@ -144,7 +126,7 @@ namespace { return; } request_running = true; - request_failed = false; + request_failed = {}; url_for_latest.clear(); url_for_hashes.clear(); availableVersions = {}; @@ -157,7 +139,7 @@ namespace { { ESP_LOGW("BOBBY", "request is im oarsch"); request_running = false; - request_failed = true; + request_failed = "request is im oarsch"; return; } @@ -173,10 +155,7 @@ namespace { if (const auto result = request->result(); !result) { ESP_LOGW("BOBBY", "request failed: %.*s", result.error().size(), result.error().data()); - std::string failed_str = result.error().data(); - auto statuscode = failed_str.substr(failed_str.length() - 3); - request_running = false; - request_failed = std::stoi(statuscode); + request_failed = result.error(); return; } @@ -184,7 +163,7 @@ namespace { ESP_LOGW("BOBBY", "Request finished: %s", content.c_str()); parse_response_into_variables(content); request_running = false; - request_failed = false; + request_failed = {}; } bool get_request_running() diff --git a/main/displays/menus/batterydebugmenu.cpp b/main/displays/menus/batterydebugmenu.cpp new file mode 100644 index 0000000..e568c22 --- /dev/null +++ b/main/displays/menus/batterydebugmenu.cpp @@ -0,0 +1,44 @@ +#include "batterydebugmenu.h" + +// local includes +#include "debugmenu.h" +#include "accessors/settingsaccessors.h" +#include "fmt/core.h" + +class CurrentBatteryStatusText : public virtual espgui::TextInterface { public: std::string text() const override { return getBatteryPercentageString(); } }; +class CurrentAdvancedBatteryPercentageText : public virtual espgui::TextInterface { public: std::string text() const override { return getBatteryAdvancedPercentageString(); } }; + +class BatteryDebugText : public virtual espgui::TextInterface { public: std::string text() const override { return getBatteryDebugString(); } }; +class BatteryDebug2Text : public virtual espgui::TextInterface { + public: std::string text() const override { + float avgVoltage = 0; + for (auto &controller : controllers) + { + avgVoltage += controller.getCalibratedVoltage(); + } + avgVoltage = avgVoltage / controllers.size(); + + auto watt = sumCurrent * avgVoltage; + auto w_per_kmh = watt / avgSpeedKmh; + return fmt::format("{:.0f} {:.0f}W/kmh", avgSpeedKmh, w_per_kmh); + } +}; +class BatteryDebug3Text : public virtual espgui::TextInterface { public: std::string text() const override { return fmt::format("{}fA {}bA", fixCurrent(controllers.front.feedback.left.dcLink + controllers.front.feedback.right.dcLink), fixCurrent(controllers.back.feedback.left.dcLink + controllers.back.feedback.right.dcLink)); } }; + +using namespace espgui; + +BatteryDebugMenu::BatteryDebugMenu() +{ + constructMenuItem>(); + constructMenuItem>(); + constructMenuItem>(); + constructMenuItem>(); + constructMenuItem>(); + constructMenuItem>(); + constructMenuItem, SwitchScreenAction, StaticMenuItemIcon<&espgui::icons::back>>>(); +} + +void BatteryDebugMenu::back() +{ + switchScreen(); +} diff --git a/main/displays/menus/batterydebugmenu.h b/main/displays/menus/batterydebugmenu.h new file mode 100644 index 0000000..8a1a705 --- /dev/null +++ b/main/displays/menus/batterydebugmenu.h @@ -0,0 +1,24 @@ +#pragma once + +// 3rdparty lib includes +#include +#include +#include +#include +#include + +// Local includes +#include "utils.h" +#include "icons/settings.h" +#include "texts.h" +#include "battery.h" + +class BatteryDebugMenu : + public espgui::MenuDisplay, + public espgui::StaticText +{ +public: + BatteryDebugMenu(); + + void back() override; +}; diff --git a/main/displays/menus/batterymenu.cpp b/main/displays/menus/batterymenu.cpp index 3b3ae97..cf3e420 100644 --- a/main/displays/menus/batterymenu.cpp +++ b/main/displays/menus/batterymenu.cpp @@ -7,6 +7,7 @@ #include "mainmenu.h" #include "displays/calibratevoltagedisplay.h" #include "accessors/settingsaccessors.h" +#include "fmt/core.h" class CurrentBatteryStatusText : public virtual espgui::TextInterface { public: std::string text() const override { return getBatteryPercentageString(); } }; diff --git a/main/displays/menus/debugmenu.cpp b/main/displays/menus/debugmenu.cpp index 8b065e6..6e3d6ad 100644 --- a/main/displays/menus/debugmenu.cpp +++ b/main/displays/menus/debugmenu.cpp @@ -14,6 +14,7 @@ #include "actions/savesettingsaction.h" #include "actions/erasenvsaction.h" #include "icons/lock.h" +#include "icons/battery.h" #include "debugcolorhelpers.h" #include "esptexthelpers.h" #include "displays/menus/commanddebugmenu.h" @@ -22,6 +23,7 @@ #include "displays/menus/motorfeedbackdebugmenu.h" #include "displays/menus/dynamicdebugmenu.h" #include "displays/menus/mainmenu.h" +#include "displays/menus/batterydebugmenu.h" using namespace espgui; @@ -50,6 +52,8 @@ DebugMenu::DebugMenu() constructMenuItem, SwitchScreenAction, BackFeedbackColor>>(); constructMenuItem>(); constructMenuItem, SwitchScreenAction>>(); + constructMenuItem>(); + constructMenuItem, SwitchScreenAction, StaticMenuItemIcon<&bobbyicons::battery>>>(); constructMenuItem, SwitchScreenAction, StaticMenuItemIcon<&espgui::icons::back>>>(); } diff --git a/main/displays/menus/ledstripmenu.cpp b/main/displays/menus/ledstripmenu.cpp index 271c308..f0ea464 100644 --- a/main/displays/menus/ledstripmenu.cpp +++ b/main/displays/menus/ledstripmenu.cpp @@ -17,6 +17,7 @@ #include "accessors/settingsaccessors.h" #ifdef FEATURE_LEDSTRIP #include "ledstrip.h" +#include "displays/menus/ledstripselectotamode.h" #endif #include "displays/ledstripcolorsdisplay.h" #include "displays/menus/mainmenu.h" @@ -126,6 +127,7 @@ LedstripMenu::LedstripMenu() constructMenuItem, SwitchScreenAction>>(); constructMenuItem, SwitchScreenAction>>(); + if (!simplified) { constructMenuItem, SwitchScreenAction>>(); } constructMenuItem, SwitchScreenAction>>(); if (!simplified) { constructMenuItem, SwitchScreenAction>>(); } if (!simplified) { constructMenuItem, SwitchScreenAction>>(); } diff --git a/main/displays/menus/ledstripselectblinkmenu.h b/main/displays/menus/ledstripselectblinkmenu.h index e565bf2..3f284e9 100644 --- a/main/displays/menus/ledstripselectblinkmenu.h +++ b/main/displays/menus/ledstripselectblinkmenu.h @@ -11,7 +11,11 @@ #include "actions/dummyaction.h" #include "actions/ledstripblinkactions.h" #include "actions/switchscreenaction.h" +#include "actions/toggleboolaction.h" +#include "checkboxicon.h" #include "ledstripdefines.h" +#include "accessors/settingsaccessors.h" +#include "ledstripmenu.h" #ifdef FEATURE_LEDSTRIP class currentSelectedBlinkAnimationText : public virtual TextInterface { public: std::string text() const override { @@ -53,6 +57,7 @@ namespace { constructMenuItem, LedstripAnimationBlinkLeftAction>>(); constructMenuItem, LedstripAnimationBlinkRightAction>>(); constructMenuItem, LedstripAnimationBlinkBothAction>>(); + constructMenuItem, ToggleBoolAction, CheckboxIcon, LedstripEnableBlinkAnimation>>(); constructMenuItem, SwitchScreenAction, StaticMenuItemIcon<&espgui::icons::back>>>(); } }; diff --git a/main/displays/menus/ledstripselectotamode.cpp b/main/displays/menus/ledstripselectotamode.cpp new file mode 100644 index 0000000..e69de29 diff --git a/main/displays/menus/ledstripselectotamode.h b/main/displays/menus/ledstripselectotamode.h new file mode 100644 index 0000000..ad40792 --- /dev/null +++ b/main/displays/menus/ledstripselectotamode.h @@ -0,0 +1,44 @@ +#pragma once + +// Local includes +#include "menudisplay.h" +#include "utils.h" +#include "menuitem.h" +#include "ledstrip.h" +#include "icons/back.h" +#include "texts.h" +#include "actions/switchscreenaction.h" +#include "accessors/settingsaccessors.h" +#include "ledstripmenu.h" + +#ifdef FEATURE_LEDSTRIP +using namespace espgui; + +template +class LedstripChangeOtaAnimModeAction : public virtual ActionInterface +{ +public: + void triggered() override + { + settings.ledstrip.otaMode = mode; + saveSettings(); + } +}; + +namespace { + class ledstripOtaAnimationChangeMenu : + public MenuDisplay, + public StaticText, + public BackActionInterface> + { + public: + ledstripOtaAnimationChangeMenu() + { + constructMenuItem, LedstripChangeOtaAnimModeAction>>(); + constructMenuItem, LedstripChangeOtaAnimModeAction>>(); + constructMenuItem, LedstripChangeOtaAnimModeAction>>(); + constructMenuItem, SwitchScreenAction, StaticMenuItemIcon<&espgui::icons::back>>>(); + } + }; +} // Namespace +#endif diff --git a/main/displays/menus/mainmenu.cpp b/main/displays/menus/mainmenu.cpp index 78df003..9625b8c 100644 --- a/main/displays/menus/mainmenu.cpp +++ b/main/displays/menus/mainmenu.cpp @@ -23,6 +23,7 @@ #include "displays/garagedisplay.h" #include "displays/menus/otamenu.h" #include "displays/poweroffdisplay.h" +#include "displays/menus/statisticsmenu.h" #include "actions/rebootaction.h" #include "displays/menus/debugmenu.h" #include "icons/battery.h" @@ -43,16 +44,18 @@ #endif #include "icons/poweroff.h" #include "icons/reboot.h" +#include "icons/statistics.h" using namespace espgui; MainMenu::MainMenu() { constructMenuItem, SwitchScreenAction, StaticMenuItemIcon<&espgui::icons::back>>>(); - constructMenuItem, SwitchScreenAction, StaticMenuItemIcon<&bobbyicons::modes>>>(); #ifdef FEATURE_LEDSTRIP constructMenuItem, SwitchScreenAction, StaticMenuItemIcon<&bobbyicons::neopixel>>>(); #endif + constructMenuItem, SwitchScreenAction, StaticMenuItemIcon<&bobbyicons::statistics>>>(); + constructMenuItem, SwitchScreenAction, StaticMenuItemIcon<&bobbyicons::modes>>>(); if (SHOWITEM) { constructMenuItem, ModeSettingsAction>>(); } if (SHOWITEM) { constructMenuItem, SwitchScreenAction, StaticMenuItemIcon<&bobbyicons::presets>>>(); } if (SHOWITEM) { constructMenuItem, SwitchScreenAction>>(); } diff --git a/main/displays/menus/otamenu.cpp b/main/displays/menus/otamenu.cpp index 4770618..812e4cf 100644 --- a/main/displays/menus/otamenu.cpp +++ b/main/displays/menus/otamenu.cpp @@ -17,18 +17,6 @@ #include "displays/menus/mainmenu.h" #ifdef FEATURE_OTA -namespace { - -class RedownloadJsonAction : public virtual espgui::ActionInterface -{ -public: - void triggered() override - { - redownload = true; - } -}; - -} // namespace using namespace espgui; @@ -37,7 +25,6 @@ OtaMenu::OtaMenu() constructMenuItem, SwitchScreenAction, StaticMenuItemIcon<&bobbyicons::presets>>>(); constructMenuItem, SwitchScreenAction, StaticMenuItemIcon<&bobbyicons::update>>>(); constructMenuItem, SwitchScreenAction>>(); - constructMenuItem, RedownloadJsonAction>>(); constructMenuItem, SwitchScreenAction, StaticMenuItemIcon<&espgui::icons::back>>>(); } diff --git a/main/displays/menus/selectbatterytypemenu.cpp b/main/displays/menus/selectbatterytypemenu.cpp index 315fe78..0f332b0 100644 --- a/main/displays/menus/selectbatterytypemenu.cpp +++ b/main/displays/menus/selectbatterytypemenu.cpp @@ -26,6 +26,7 @@ BatteryTypeMenu::BatteryTypeMenu() constructMenuItem, BatterySelectTypeAction>>(); constructMenuItem, BatterySelectTypeAction>>(); constructMenuItem, BatterySelectTypeAction>>(); + constructMenuItem, BatterySelectTypeAction>>(); constructMenuItem, SwitchScreenAction, StaticMenuItemIcon<&espgui::icons::back>>>(); } diff --git a/main/displays/menus/selectbuildservermenu.cpp b/main/displays/menus/selectbuildservermenu.cpp index be80491..9cbb7eb 100644 --- a/main/displays/menus/selectbuildservermenu.cpp +++ b/main/displays/menus/selectbuildservermenu.cpp @@ -29,7 +29,6 @@ public: stringSettings.otaUrl = m_buildserver_url; } saveSettings(); - redownload = true; url_for_latest.clear(); url_for_hashes.clear(); availableVersions = {}; diff --git a/main/displays/menus/selectbuildservermenu.h b/main/displays/menus/selectbuildservermenu.h index be4ce41..2ef0ecf 100644 --- a/main/displays/menus/selectbuildservermenu.h +++ b/main/displays/menus/selectbuildservermenu.h @@ -12,6 +12,7 @@ #include "buildserver.h" #ifdef FEATURE_OTA + class SelectBuildServerMenu : public espgui::MenuDisplay, public espgui::StaticText diff --git a/main/displays/menus/selectotabuildmenu.h b/main/displays/menus/selectotabuildmenu.h index 746f104..4a95969 100644 --- a/main/displays/menus/selectotabuildmenu.h +++ b/main/displays/menus/selectotabuildmenu.h @@ -90,7 +90,7 @@ public: auto filename = serverUrl.substr(last_slash_index+1); auto hash = filename.substr(0, filename.length() - 4); menuitem.setHash(hash); - menuitem.setUrl(fix_url(serverUrl)); + menuitem.setUrl(serverUrl); constructMenuItem, SwitchScreenAction, StaticMenuItemIcon<&espgui::icons::back>>>(); } else @@ -108,10 +108,10 @@ void SelectBuildMenu::update() if(get_request_running()) { check_descriptor_request(); - if (request_failed) + if (!request_failed.empty()) { - this->buildMenuRequestError(fmt::format("Error: {}", request_failed)); - request_failed = false; + this->buildMenuRequestError(request_failed); + request_failed = {}; } } diff --git a/main/displays/menus/statisticsmenu.cpp b/main/displays/menus/statisticsmenu.cpp new file mode 100644 index 0000000..479fba5 --- /dev/null +++ b/main/displays/menus/statisticsmenu.cpp @@ -0,0 +1,133 @@ +#include "statisticsmenu.h" + +// local includes +#include "mainmenu.h" +#include "actions/dummyaction.h" +#include "actioninterface.h" +#include "fmt/core.h" +#include "utils.h" +#include "icons/time.h" +#include "icons/reboot.h" +#include "icons/update.h" +#include "drivingstatistics.h" + +using namespace espgui; + +class WhPerKmText : public virtual espgui::TextInterface { + public: std::string text() const override { + float avgVoltage = 0; + for (auto &controller : controllers) + { + avgVoltage += controller.getCalibratedVoltage(); + } + avgVoltage = avgVoltage / controllers.size(); + + auto watt = sumCurrent * avgVoltage; + auto w_per_kmh = watt / avgSpeedKmh; + return fmt::format("{:.0f} Wh/km", w_per_kmh); + } +}; + +class UptimeText : public virtual espgui::TextInterface { + public: std::string text() const override { + return get_current_uptime_string(); + } +}; + +class CurrentKilometersText : public virtual espgui::TextInterface { + public: std::string text() const override { + return fmt::format("Curr: {:.2f}m", drivingStatistics.meters_driven); + } +}; + +class TotalKilometersText : public virtual espgui::TextInterface { + public: std::string text() const override { + return fmt::format("total: {:.1f}km", drivingStatistics.totalMeters / 1000.f ); + } +}; + +class TotalMetersText : public virtual espgui::TextInterface { + public: std::string text() const override { + return fmt::format("total: {:.0f}m", drivingStatistics.totalMeters ); + } +}; + +class CurrentDrivingTimeText : public virtual espgui::TextInterface { + public: std::string text() const override { + return get_current_driving_time_string(); + } +}; + +class SavedTotalCentimetersText : public virtual espgui::TextInterface { + public: std::string text() const override { + return fmt::format("saved: {}cm", settings.savedStatistics.totalCentimeters ); + } +}; + +class CurrentWhUsedText : public virtual espgui::TextInterface { + public: std::string text() const override { + return fmt::format("Curr: {:.2f}Wh", drivingStatistics.wh_used ); + } +}; + +class AverageWhUsedText : public virtual espgui::TextInterface { + public: std::string text() const override { + return fmt::format("Avg: {:.1f}Wh/km", drivingStatistics.wh_used / (drivingStatistics.meters_driven / 1000.f) ); + } +}; + +class EfficiencyText : public virtual espgui::TextInterface { + public: std::string text() const override { + return fmt::format("Efficiency: {}", getEfficiencyClassString()); + } +}; + +class EfficiencyTextColor : public virtual espgui::ColorInterface { +public: + int color() const override + { + return getEfficiencyClassColor(); + } +}; + +class SaveKilometersAction : public virtual ActionInterface { +public: + void triggered() override { + drivingStatistics.last_cm_written = drivingStatistics.totalMeters * 100; + settings.savedStatistics.totalCentimeters = drivingStatistics.last_cm_written; + saveSettings(); + } +}; + +class ClearCurrentStatsAction : public virtual ActionInterface { +public: + void triggered() override { + drivingStatistics.meters_driven = 0.; + drivingStatistics.currentDrivingTime = 0; + drivingStatistics.wh_used = 0; + drivingStatistics.batteryWhEstimate = 0; + } +}; + +StatisticsMenu::StatisticsMenu() +{ + constructMenuItem>(); + constructMenuItem>>(); + constructMenuItem>(); + constructMenuItem>(); + constructMenuItem>(); + constructMenuItem>(); +// constructMenuItem>(); + constructMenuItem>(); + constructMenuItem>(); + constructMenuItem>(); + constructMenuItem>(); + constructMenuItem, SaveKilometersAction, StaticMenuItemIcon<&bobbyicons::update>>>(); + constructMenuItem, ClearCurrentStatsAction, StaticMenuItemIcon<&bobbyicons::reboot>>>(); + constructMenuItem, SwitchScreenAction, StaticMenuItemIcon<&espgui::icons::back>>>(); +} + +void StatisticsMenu::back() +{ + switchScreen(); +} diff --git a/main/displays/menus/statisticsmenu.h b/main/displays/menus/statisticsmenu.h new file mode 100644 index 0000000..661790d --- /dev/null +++ b/main/displays/menus/statisticsmenu.h @@ -0,0 +1,22 @@ +#pragma once + +// 3rdparty lib includes +#include +#include +#include +#include +#include + +// Local includes +#include "utils.h" +#include "texts.h" + +class StatisticsMenu : + public espgui::MenuDisplay, + public espgui::StaticText +{ +public: + StatisticsMenu(); + + void back() override; +}; diff --git a/main/displays/menus/wifisettingsmenu.cpp b/main/displays/menus/wifisettingsmenu.cpp index 613de4b..a7ab8dd 100644 --- a/main/displays/menus/wifisettingsmenu.cpp +++ b/main/displays/menus/wifisettingsmenu.cpp @@ -10,14 +10,26 @@ #include "displays/menus/stationwifisettingsmenu.h" #include "displays/menus/accesspointwifisettingsmenu.h" #include "displays/menus/settingsmenu.h" +#include "globals.h" using namespace espgui; +#ifdef FEATURE_DNS_NS +class ResendDNSRequest : public virtual ActionInterface +{ +public: + void triggered() override { dns_lastIpAddress_v4 = "---"; dns_lastIpAddress_v6 = "---"; dns_lastIpAddress_v6_global = "---"; } +}; +#endif + WifiSettingsMenu::WifiSettingsMenu() { constructMenuItem, SwitchScreenAction>>(); constructMenuItem, SwitchScreenAction>>(); constructMenuItem, SwitchScreenAction>>(); +#ifdef FEATURE_DNS_NS + constructMenuItem, ResendDNSRequest>>(); +#endif constructMenuItem, SwitchScreenAction, StaticMenuItemIcon<&espgui::icons::back>>>(); } diff --git a/main/displays/statusdisplay.cpp b/main/displays/statusdisplay.cpp index 3efd362..56c2756 100644 --- a/main/displays/statusdisplay.cpp +++ b/main/displays/statusdisplay.cpp @@ -1,8 +1,5 @@ #include "statusdisplay.h" -// esp-idf includes -#include - // 3rdparty lib includes #include #include @@ -15,6 +12,7 @@ #else #include "displays/metersdisplay.h" #endif +#include "drivingstatistics.h" using namespace espgui; @@ -81,7 +79,13 @@ void StatusDisplay::redraw() m_batterypercent.redraw(getBatteryPercentageString()); m_watthoursleft.redraw(getBatteryRemainingWattHoursString()); + const uint16_t efficiencyColor = getEfficiencyClassColor(); +// if (abs(avgSpeedKmh) > 2) + { + tft.setTextColor(efficiencyColor, TFT_BLACK); + } m_kilometersleft.redraw(getRemainingRangeString()); + tft.setTextColor(TFT_WHITE, TFT_BLACK); m_frontStatus.redraw(controllers.front); m_backStatus.redraw(controllers.back); @@ -150,7 +154,6 @@ clearIp: void StatusDisplay::confirm() { - ESP_LOGI(TAG, "called"); switchScreen(); } diff --git a/main/displays/statusdisplay.h b/main/displays/statusdisplay.h index 5f64f92..4aa8c61 100644 --- a/main/displays/statusdisplay.h +++ b/main/displays/statusdisplay.h @@ -78,8 +78,8 @@ private: espgui::ProgressBar m_progressBarBrems{150, 15, 90, 15, 0, 1000}; espgui::Label m_batterypercent{0, 30}; - espgui::Label m_watthoursleft{110, 30}; - espgui::Label m_kilometersleft{175, 30}; + espgui::Label m_watthoursleft{107, 30}; + espgui::Label m_kilometersleft{176, 30}; BoardStatus m_frontStatus{45}; BoardStatus m_backStatus{145}; diff --git a/main/dnsannounce.cpp b/main/dnsannounce.cpp new file mode 100644 index 0000000..1dfc8b1 --- /dev/null +++ b/main/dnsannounce.cpp @@ -0,0 +1,101 @@ +// 3rd party +#ifdef FEATURE_DNS_NS +#include +#include +#include + +// local +#include "dnsannounce.h" +#include "espwifistack.h" +#include "cpputils.h" +#include "lwip/dns.h" +#include "globals.h" + +void handle_dns_announce() +{ + const auto staStatus = wifi_stack::get_sta_status(); + const auto randDNSName = cpputils::randomNumber(espcpputils::esp_random_device{}); + if (staStatus == wifi_stack::WiFiStaStatus::CONNECTED) + { + EVERY_N_SECONDS ( 2 ) { + // Get IPv4 + if (const auto result = wifi_stack::get_ip_info(TCPIP_ADAPTER_IF_STA); result) + { + std::string curIpAddress = wifi_stack::toString(result->ip); + if (curIpAddress == "0.0.0.0") goto lookupIPv6; + if (dns_lastIpAddress_v4 != curIpAddress) + { + dns_lastIpAddress_v4 = curIpAddress; + ip_addr_t tmpIpResolved; + std::string toLookup = fmt::format("{}__{}.{}.announce.bobbycar.cloud", randDNSName, curIpAddress, OTA_USERNAME); + ESP_LOGI("BOBBY", "Trying to look up %s", toLookup.c_str()); + if (const auto err = dns_gethostbyname(toLookup.c_str(), &tmpIpResolved, NULL, NULL); err != ERR_OK && err != ERR_INPROGRESS) + { + ESP_LOGW("BOBBY", "There is a error in the matrix (dns ipv4 lookup failed) -> %d", err); + dns_lastIpAddress_v4 = "-"; + dns_lastIpAddress_v6 = "-"; + dns_lastIpAddress_v6_global = "-"; + } + } + } + else + { + ESP_LOGW("BOBBY", "get_ip_info() failed with %.*s", result.error().size(), result.error().data()); + } + lookupIPv6: + esp_ip6_addr_t tmpv6addr; + if (const auto result = esp_netif_get_ip6_linklocal(wifi_stack::esp_netifs[ESP_IF_WIFI_STA], &tmpv6addr); result == ESP_OK) + { + std::string curIpV6Address = wifi_stack::toString(tmpv6addr); + std::replace(curIpV6Address.begin(), curIpV6Address.end(), ':', '-'); + if (dns_lastIpAddress_v6 != curIpV6Address) + { + dns_lastIpAddress_v6 = curIpV6Address; + ip_addr_t tmpIpResolved; + std::string toLookup = fmt::format("{}__{}.{}.announce6.bobbycar.cloud", randDNSName, curIpV6Address, OTA_USERNAME); + ESP_LOGI("BOBBY", "Trying to look up %s", toLookup.c_str()); + if (const auto err = dns_gethostbyname(toLookup.c_str(), &tmpIpResolved, NULL, NULL); err != ERR_OK && err != ERR_INPROGRESS) + { + ESP_LOGW("BOBBY", "There is a error in the matrix (dns ipv6 local lookup failed) -> %d", err); + dns_lastIpAddress_v4 = "-"; + dns_lastIpAddress_v6 = "-"; + dns_lastIpAddress_v6_global = "-"; + } + } + } + + if (const auto result = esp_netif_get_ip6_global(wifi_stack::esp_netifs[ESP_IF_WIFI_STA], &tmpv6addr); result == ESP_OK) + { + std::string curIpV6Address = wifi_stack::toString(tmpv6addr); + if (dns_lastIpAddress_v6_global != curIpV6Address) + { + dns_lastIpAddress_v6_global = curIpV6Address; + std::replace(curIpV6Address.begin(), curIpV6Address.end(), ':', '-'); + ip_addr_t tmpIpResolved; + std::string toLookup = fmt::format("{}global__{}.{}.announce6.bobbycar.cloud", randDNSName, curIpV6Address, OTA_USERNAME); + ESP_LOGI("BOBBY", "Trying to look up %s", toLookup.c_str()); + if (const auto err = dns_gethostbyname(toLookup.c_str(), &tmpIpResolved, NULL, NULL); err != ERR_OK && err != ERR_INPROGRESS) + { + ESP_LOGW("BOBBY", "There is a error in the matrix (dns ipv6 global lookup failed) -> %d", err); + dns_lastIpAddress_v4 = "-"; + dns_lastIpAddress_v6 = "-"; + dns_lastIpAddress_v6_global = "-"; + } + } + } + } + + EVERY_N_SECONDS( 120 ) { + dns_lastIpAddress_v4 = "-"; + dns_lastIpAddress_v6 = "-"; + dns_lastIpAddress_v6_global = "-"; + } + } + else + { + dns_lastIpAddress_v4 = "-"; + dns_lastIpAddress_v6 = "-"; + dns_lastIpAddress_v6_global = "-"; + } +} +#endif diff --git a/main/dnsannounce.h b/main/dnsannounce.h new file mode 100644 index 0000000..e9da219 --- /dev/null +++ b/main/dnsannounce.h @@ -0,0 +1,2 @@ +#pragma once +void handle_dns_announce(); diff --git a/main/drivingstatistics.cpp b/main/drivingstatistics.cpp new file mode 100644 index 0000000..4852217 --- /dev/null +++ b/main/drivingstatistics.cpp @@ -0,0 +1,163 @@ +#include "drivingstatistics.h" + +// 3rd party +#include +#include "TFT_eSPI.h" + +// Local +#include "globals.h" +#include "battery.h" +#include "utils.h" + +float getAvgWhPerKm() +{ + return drivingStatistics.wh_used / (drivingStatistics.meters_driven / 1000.f); +} + +std::string getEfficiencyClassString() +{ + const float avgWhPerKm = getAvgWhPerKm(); + if (avgWhPerKm <= 14) + { + return "A+++"; + } + else if (avgWhPerKm <= 16) + { + return "A++"; + } + else if (avgWhPerKm <= 18) + { + return "A+"; + } + else if (avgWhPerKm <= 20) + { + return "A"; + } + else if (avgWhPerKm <= 24) + { + return "B"; + } + else if (avgWhPerKm <= 28) + { + return "C"; + } + else if (avgWhPerKm <= 32) + { + return "D"; + } + else if (avgWhPerKm <= 36) + { + return "E"; + } + else if (avgWhPerKm <= 40) + { + return "F"; + } + else + { + return "G"; + } +} + +uint16_t getEfficiencyClassColor() +{ + const float avgWhPerKm = getAvgWhPerKm(); + if (avgWhPerKm <= 14) + { + return 0x1700; + } + else if (avgWhPerKm <= 16) + { + return 0x3640; + } + else if (avgWhPerKm <= 18) + { + return 0x5560; + } + else if (avgWhPerKm <= 20) + { + return 0x6CA0; + } + else if (avgWhPerKm <= 24) + { + return 0x83E0; + } + else if (avgWhPerKm <= 28) + { + return 0x9B20; + } + else if (avgWhPerKm <= 32) + { + return 0xB240; + } + else if (avgWhPerKm <= 36) + { + return 0xC980; + } + else if (avgWhPerKm <= 40) + { + return 0xE0C0; + } + else + { + return 0xF800; + } +} + +void calculateStatistics() +{ + EVERY_N_MILLIS( 10 ) { + static bool saveTotal = false; + + if ((settings.savedStatistics.totalCentimeters / 100.f) > drivingStatistics.totalMeters) + { + drivingStatistics.totalMeters = settings.savedStatistics.totalCentimeters / 100.f; + drivingStatistics.last_cm_written = settings.savedStatistics.totalCentimeters; + } + + static auto last_km_calculation = espchrono::millis_clock::now(); + const auto duration = espchrono::ago(last_km_calculation).count() / 1000.0f; + last_km_calculation = espchrono::millis_clock::now(); + + const float meters_driven_now = (abs(avgSpeedKmh) / 3.6) * duration; + drivingStatistics.meters_driven += meters_driven_now; + drivingStatistics.totalMeters += meters_driven_now; // Udate meters driven + + if (abs(avgSpeedKmh) > 1) + { + if (!saveTotal && abs(avgSpeedKmh) > 5) + { + saveTotal = true; + } + drivingStatistics.currentDrivingTime += duration; + + float avgVoltage = 0; + for (auto &controller : controllers) + { + avgVoltage += controller.getCalibratedVoltage(); + } + avgVoltage = avgVoltage / controllers.size(); + + auto watt = sumCurrent * avgVoltage; + const float ws_driven_now = watt * duration; + drivingStatistics.wh_used += ws_driven_now / 3600; // Wh + drivingStatistics.batteryWhEstimate -= ws_driven_now / 3600; + } + else + { + drivingStatistics.wh_used += (13 * duration) / 3600; // Wh + drivingStatistics.batteryWhEstimate = getRemainingWattHours(); + } + + if ((drivingStatistics.totalMeters > ((drivingStatistics.last_cm_written / 100.f) + 100)) || (saveTotal && abs(avgSpeedKmh) < 0.5)) + { + if (saveTotal) + { + saveTotal = false; + } + drivingStatistics.last_cm_written = drivingStatistics.totalMeters * 100; // Save total Meters + settings.savedStatistics.totalCentimeters = drivingStatistics.last_cm_written; + saveSettings(); + } + } +} diff --git a/main/drivingstatistics.h b/main/drivingstatistics.h new file mode 100644 index 0000000..bd1fd08 --- /dev/null +++ b/main/drivingstatistics.h @@ -0,0 +1,7 @@ +#pragma once +#include + +void calculateStatistics(); +float getAvgWhPerKm(); +std::string getEfficiencyClassString(); +uint16_t getEfficiencyClassColor(); diff --git a/main/globals.cpp b/main/globals.cpp index 7907e66..9f80ac9 100644 --- a/main/globals.cpp +++ b/main/globals.cpp @@ -25,6 +25,12 @@ bool simplified = #endif ; +#ifdef FEATURE_DNS_NS +std::string dns_lastIpAddress_v4 = ""; +std::string dns_lastIpAddress_v6 = ""; +std::string dns_lastIpAddress_v6_global = ""; +#endif + Settings settings; StringSettings stringSettings; SettingsPersister settingsPersister; @@ -41,3 +47,5 @@ BluetoothSerial bluetoothSerial; ModeInterface *lastMode{}; ModeInterface *currentMode{}; + +DrivingStatistics drivingStatistics; diff --git a/main/globals.h b/main/globals.h index 5c251a9..9bed979 100644 --- a/main/globals.h +++ b/main/globals.h @@ -48,8 +48,25 @@ extern char deviceName[32]; #include GLOBALS_PLUGIN #endif +#ifdef FEATURE_DNS_NS +extern std::string dns_lastIpAddress_v4; +extern std::string dns_lastIpAddress_v6; +extern std::string dns_lastIpAddress_v6_global; +#endif + extern bool simplified; +struct DrivingStatistics { + float meters_driven; + float currentDrivingTime; + double totalMeters; + uint32_t last_cm_written; + float wh_used; + float batteryWhEstimate; +}; + +extern DrivingStatistics drivingStatistics; + extern Settings settings; extern StringSettings stringSettings; extern SettingsPersister settingsPersister; diff --git a/main/icons/statistics.cpp b/main/icons/statistics.cpp new file mode 100644 index 0000000..83b9471 --- /dev/null +++ b/main/icons/statistics.cpp @@ -0,0 +1,44 @@ +#include "statistics.h" + +namespace bobbyicons { +const espgui::Icon<24, 24> statistics{ + { + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x2040, 0xC244, 0xCA44, 0x2860, 0x0000, // 0x0010 (16) pixels + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, // 0x0020 (32) pixels + 0x0000, 0x0000, 0x0000, 0xBA23, 0xF2C5, 0xF2C5, 0xC224, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, // 0x0030 (48) pixels + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x5901, 0xEAC5, 0xF2C5, 0xF2C5, 0xF2C5, 0xA1C3, // 0x0040 (64) pixels + 0x6101, 0x0000, 0x7962, 0xA1C3, 0x1020, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x50E1, 0x8162, 0x8162, 0x50E1, // 0x0050 (80) pixels + 0x6101, 0xCA44, 0xF2C5, 0xF2C5, 0xF2C5, 0xF2C5, 0xF2C5, 0xF2C5, 0xF2C5, 0xE2A4, 0xF2C5, 0xF2C5, 0x8182, 0x0000, 0x0000, 0x0000, // 0x0060 (96) pixels + 0x0000, 0x0000, 0x0000, 0xB203, 0xF2C5, 0xF2C5, 0xF2C5, 0xF2C5, 0xEAC5, 0xF2C5, 0xF2C5, 0xF2C5, 0xF2C5, 0xF2C5, 0xEAC5, 0xF2C5, // 0x0070 (112) pixels + 0xF2C5, 0xF2C5, 0xF2C5, 0xF2C5, 0x99C3, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xBA23, 0xF2C5, 0xF2C5, 0xF2C5, 0xF2C5, // 0x0080 (128) pixels + 0xF2C5, 0xF2C5, 0xEAC5, 0xBA23, 0x8162, 0x3080, 0x0000, 0x1820, 0x7142, 0xB203, 0xEAA5, 0xF2C5, 0xCA64, 0x0800, 0x0000, 0x0000, // 0x0090 (144) pixels + 0x0000, 0x0000, 0x0000, 0xA1C3, 0xF2C5, 0xF2C5, 0xF2C5, 0xF2C5, 0xF2C5, 0xDA84, 0x58E1, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, // 0x00A0 (160) pixels + 0x0000, 0x0000, 0x3880, 0xC244, 0xF2C5, 0xA9E3, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x58E1, 0xF2E5, 0xF2E5, 0xF2E5, 0xF2E5, // 0x00B0 (176) pixels + 0xE2C4, 0x40A0, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xBA23, 0xEAE5, 0x50E1, 0x0000, // 0x00C0 (192) pixels + 0x0000, 0x0000, 0x0000, 0x0000, 0xC263, 0xF324, 0xF324, 0xF324, 0x81A2, 0x0000, 0x0000, 0x0000, 0x0000, 0x7982, 0xBA63, 0xC283, // 0x00D0 (208) pixels + 0xB243, 0x79A2, 0x0000, 0x0000, 0x0800, 0xD2A4, 0xB243, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xB283, 0xF364, 0xF364, 0xDB04, // 0x00E0 (224) pixels + 0x0000, 0x0000, 0x0000, 0x2860, 0xCAC3, 0xF364, 0xF364, 0xF364, 0xF364, 0xF364, 0xD2E3, 0x5101, 0x0000, 0x6141, 0xD2E4, 0x0000, // 0x00F0 (240) pixels + 0x0000, 0x0000, 0x0000, 0x0000, 0xDB23, 0xF384, 0xF384, 0xAA82, 0x0000, 0x0000, 0x0000, 0xBAC3, 0xF384, 0xD323, 0x9A42, 0x9A22, // 0x0100 (256) pixels + 0xC2C3, 0xF384, 0xF384, 0xE364, 0x4900, 0x0000, 0xC2C3, 0x0000, 0x0000, 0x0000, 0x0000, 0x6981, 0xF3C4, 0xF3C4, 0xF3C4, 0x8A02, // 0x0110 (272) pixels + 0x0000, 0x0000, 0x4920, 0xF3C4, 0xCB23, 0x1020, 0x0000, 0x0000, 0x0000, 0x81E1, 0xF3C4, 0xF3C4, 0xCB03, 0x0000, 0x79C1, 0x0000, // 0x0120 (288) pixels + 0x0000, 0x0000, 0x9262, 0xF3E3, 0xF403, 0xF403, 0xF403, 0x71C1, 0x0000, 0x0000, 0x8A41, 0xF403, 0x5961, 0x0000, 0x0000, 0x0000, // 0x0130 (304) pixels + 0x0000, 0x0000, 0xB2E2, 0xF403, 0xF403, 0x5140, 0x0000, 0x0000, 0x0000, 0x71E1, 0xF423, 0xF423, 0xF423, 0xF423, 0xF423, 0x8A41, // 0x0140 (320) pixels + 0x0000, 0x0000, 0x9261, 0xF423, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x61A1, 0xF423, 0xF423, 0x9A82, 0x0000, 0x0000, // 0x0150 (336) pixels + 0x0000, 0xD3C2, 0xF463, 0xF463, 0xF463, 0xF463, 0xF463, 0xB322, 0x0000, 0x0000, 0x4920, 0xF463, 0x7201, 0x0000, 0x0000, 0x0000, // 0x0160 (352) pixels + 0x0000, 0x0000, 0x4940, 0xF463, 0xF463, 0xAB02, 0x0000, 0x0000, 0x0000, 0xAB21, 0xF4A3, 0xF4A3, 0xF4A3, 0xF4A3, 0xF4A3, 0xE463, // 0x0170 (368) pixels + 0x1880, 0x0000, 0x0000, 0xA302, 0xE442, 0x7A41, 0x1860, 0x61E0, 0x0000, 0x0000, 0x8261, 0xF4A3, 0xF4A3, 0xCBC2, 0x0000, 0x0000, // 0x0180 (384) pixels + 0x0000, 0x0000, 0x8AA1, 0xE462, 0xF4E2, 0xF4E2, 0xF4E2, 0xF4E2, 0xB361, 0x0000, 0x0000, 0x0000, 0x8261, 0xC3C2, 0xCBE2, 0x8AA1, // 0x0190 (400) pixels + 0x0000, 0x0000, 0xCBE2, 0xF4E2, 0xF4E2, 0xF4E2, 0x92C1, 0x0000, 0x0000, 0x0000, 0x0000, 0x0820, 0x7A60, 0xD462, 0xFD02, 0xFD02, // 0x01A0 (416) pixels + 0xF502, 0x9B21, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x8AE1, 0xF502, 0xFD02, 0xFD02, 0xFD02, 0xCC02, 0x0000, // 0x01B0 (432) pixels + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x59C0, 0xED02, 0xFD42, 0xFD42, 0xFD42, 0xCC41, 0x6220, 0x0000, 0x0000, 0x0000, 0x20C0, // 0x01C0 (448) pixels + 0xABA1, 0xF542, 0xFD42, 0xFD42, 0xD462, 0xABA1, 0x3920, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xABA1, 0xFD82, // 0x01D0 (464) pixels + 0xFD82, 0xFD82, 0xFD82, 0xFD82, 0xE522, 0xD4C1, 0xDCC1, 0xF542, 0xFD82, 0xFD82, 0xFD82, 0xD481, 0x0000, 0x0000, 0x0000, 0x0000, // 0x01E0 (480) pixels + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xC481, 0xFDA2, 0xFDA2, 0xFDA2, 0xFDA2, 0xFDA2, 0xFDA2, 0xFDA2, 0xFDA2, 0xFDA2, // 0x01F0 (496) pixels + 0xFDA2, 0xFDA2, 0xCCA1, 0x28E0, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xC4A1, 0xFDE1, // 0x0200 (512) pixels + 0xFDE1, 0xFDE1, 0xF5C1, 0xFDE1, 0xFDE1, 0xFDE1, 0xFDE1, 0xFDE1, 0xFDE1, 0xFDE1, 0x51E0, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, // 0x0210 (528) pixels + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xA3E1, 0xFE21, 0xE5A1, 0xB461, 0x3960, 0x5200, 0x8320, 0xA3C0, 0xE581, 0xFE21, // 0x0220 (544) pixels + 0xFE21, 0xE581, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0020, 0x0020, // 0x0230 (560) pixels + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x3960, 0xC4C1, 0xF601, 0x8340, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, // 0x0240 (576) pixels + } +}; +} // namespace bobbyicons diff --git a/main/icons/statistics.h b/main/icons/statistics.h new file mode 100644 index 0000000..4870b3f --- /dev/null +++ b/main/icons/statistics.h @@ -0,0 +1,7 @@ +#pragma once + +#include "icon.h" + +namespace bobbyicons { +extern const espgui::Icon<24, 24> statistics; +} // namespace bobbyicons diff --git a/main/ledstrip.cpp b/main/ledstrip.cpp index 3264dba..7a8d34f 100644 --- a/main/ledstrip.cpp +++ b/main/ledstrip.cpp @@ -7,6 +7,8 @@ #include "cpputils.h" #include "espchrono.h" #include "ledstripdefines.h" +#include "utils.h" +#include "ota.h" using namespace std::chrono_literals; @@ -30,37 +32,101 @@ void updateLedStrip() { EVERY_N_MILLISECONDS( 20 ) { gHue++; } static bool have_disabled_beeper = false; + const bool enAnim = settings.ledstrip.enableAnimBlink; if (cpputils::is_in(blinkAnimation, LEDSTRIP_OVERWRITE_BLINKLEFT, LEDSTRIP_OVERWRITE_BLINKRIGHT, LEDSTRIP_OVERWRITE_BLINKBOTH)) { std::fill(std::begin(leds), std::end(leds), CRGB{0, 0, 0}); - if (espchrono::millis_clock::now().time_since_epoch() % 750ms < 375ms) + if (espchrono::millis_clock::now().time_since_epoch() % 750ms < 375ms || enAnim) { + const auto anim_to_fill = time_to_percent(750ms, 500ms, 100ms, settings.ledstrip.enableFullBlink ? (leds.size() / 2) : settings.ledstrip.bigOffset - settings.ledstrip.smallOffset, settings.ledstrip.enableFullBlink); if (settings.ledstrip.enableBeepWhenBlink) { - for (Controller &controller : controllers) - controller.command.buzzer.freq = 3; + if (espchrono::millis_clock::now().time_since_epoch() % 750ms < 375ms) + for (Controller &controller : controllers) + controller.command.buzzer.freq = 3; + else + for (Controller &controller : controllers) + controller.command.buzzer.freq = 0; } - auto color = CRGB{255, 255, 0}; + auto color = CRGB{255, 200, 0}; const auto center = (std::begin(leds) + (leds.size() / 2) + settings.ledstrip.centerOffset); - if (blinkAnimation != LEDSTRIP_OVERWRITE_BLINKRIGHT && !settings.ledstrip.enableFullBlink) + if (settings.ledstrip.enableFullBlink) { - std::fill(center - settings.ledstrip.bigOffset, center - settings.ledstrip.smallOffset, color); + // Full + if (BLINK_LEFT_EXPR) + { + // Blink left + if (!enAnim) + { + std::fill(std::begin(leds), center, color); + } + else + { + std::fill(std::begin(leds)+anim_to_fill, center, color); + } + } + if (BLINK_RIGHT_EXPR) + { + // Blink right + if (!enAnim) + { + std::fill(center, std::end(leds), color); + } + else + { + std::fill(center, std::end(leds) - anim_to_fill, color); + } + } } - else if(blinkAnimation != LEDSTRIP_OVERWRITE_BLINKRIGHT && settings.ledstrip.enableFullBlink) + else { - std::fill(std::begin(leds), center, color); - } - if (blinkAnimation != LEDSTRIP_OVERWRITE_BLINKLEFT && !settings.ledstrip.enableFullBlink) - { - std::fill(center + settings.ledstrip.smallOffset, center + settings.ledstrip.bigOffset, color); - } - else if(blinkAnimation != LEDSTRIP_OVERWRITE_BLINKLEFT && settings.ledstrip.enableFullBlink) - { - std::fill(center, std::end(leds), color); + // Only in the back + if (BLINK_LEFT_EXPR) + { + // Blink left + if (!enAnim) + { + std::fill(center - settings.ledstrip.bigOffset, center - settings.ledstrip.smallOffset, color); + } + else + { + std::fill(center - settings.ledstrip.smallOffset - anim_to_fill, center - settings.ledstrip.smallOffset, color); + } + } + if (BLINK_RIGHT_EXPR) + { + // Blink right + if (!enAnim) + { + std::fill(center + settings.ledstrip.smallOffset, center + settings.ledstrip.bigOffset, color); + } + else + { + std::fill(center + settings.ledstrip.smallOffset, center + settings.ledstrip.smallOffset + anim_to_fill, color); + } + } } +// Old way to blink +// if (blinkAnimation != LEDSTRIP_OVERWRITE_BLINKRIGHT && !settings.ledstrip.enableFullBlink) +// { +// std::fill(center - settings.ledstrip.bigOffset, center - settings.ledstrip.smallOffset, color); +// } +// else if(blinkAnimation != LEDSTRIP_OVERWRITE_BLINKRIGHT && settings.ledstrip.enableFullBlink) +// { +// std::fill(std::begin(leds), center, color); +// } +// if (blinkAnimation != LEDSTRIP_OVERWRITE_BLINKLEFT && !settings.ledstrip.enableFullBlink) +// { +// std::fill(center + settings.ledstrip.smallOffset, center + settings.ledstrip.bigOffset, color); +// } +// else if(blinkAnimation != LEDSTRIP_OVERWRITE_BLINKLEFT && settings.ledstrip.enableFullBlink) +// { +// std::fill(center, std::end(leds), color); +// } + } else { if (settings.ledstrip.enableBeepWhenBlink) { @@ -93,10 +159,10 @@ void updateLedStrip() { std::fill(std::begin(leds), std::end(leds), color); } - else + else if(!settings.ledstrip.enableAnimBlink) { - std::fill(center - settings.ledstrip.bigOffset, center - settings.ledstrip.smallOffset, color); - std::fill(center + settings.ledstrip.smallOffset, center + settings.ledstrip.bigOffset, color); + std::fill(center - settings.ledstrip.bigOffset - 2, center - settings.ledstrip.smallOffset + 2, color); + std::fill(center + settings.ledstrip.smallOffset - 2, center + settings.ledstrip.bigOffset + 2, color); } } else @@ -147,7 +213,7 @@ void updateLedStrip() void showAnimation() { - if (settings.ledstrip.enableLedAnimation && !simplified) + if (settings.ledstrip.enableLedAnimation && !simplified && (!asyncOtaTaskStarted || settings.ledstrip.otaMode != OtaAnimationModes::None)) { if (animation_type == LEDSTRIP_ANIMATION_TYPE_DEFAULTRAINBOW) showDefaultLedstrip(); else if (animation_type == LEDSTRIP_ANIMATION_TYPE_BETTERRAINBOW) showBetterRainbow(); @@ -155,12 +221,47 @@ void showAnimation() else if (animation_type == LEDSTRIP_ANIMATION_TYPE_CUSTOMCOLOR) showCustomColor(); else showDefaultLedstrip(); } + else if (asyncOtaTaskStarted && settings.ledstrip.otaMode != OtaAnimationModes::None) + { + // show ota animation + showOtaAnimation(); + } else { std::fill(std::begin(leds), std::end(leds), CRGB{0, 0, 0}); } } +void showOtaAnimation() +{ + std::fill(std::begin(leds), std::end(leds), CRGB{0,0,0}); + const auto leds_count = leds.size(); + const int one_percent = leds_count / 100; + float percentage = 0; + + const auto progress = asyncOta->progress(); + if (const auto totalSize = asyncOta->totalSize(); totalSize && *totalSize > 0) + { + percentage = (float(progress) / *totalSize * 100); + if (settings.ledstrip.otaMode == OtaAnimationModes::GreenProgressBar) + { + int numLeds = one_percent * percentage; + if (numLeds >= leds_count) + { + numLeds = leds_count - 1; + } + std::fill(std::begin(leds), std::begin(leds) + numLeds, CRGB{0,255,0}); + } + else if (settings.ledstrip.otaMode == OtaAnimationModes::ColorChangeAll) + { + const uint8_t redChannel = 255 - (2.55 * percentage); + const uint8_t greenChannel = 2.55 * percentage; + + std::fill(std::begin(leds), std::end(leds), CRGB{redChannel, greenChannel, 0}); + } + } +} + void showBetterRainbow() { fill_rainbow(&*std::begin(leds), leds.size(), gHue); diff --git a/main/ledstrip.h b/main/ledstrip.h index aea82f8..925fcb7 100644 --- a/main/ledstrip.h +++ b/main/ledstrip.h @@ -7,6 +7,7 @@ #include #ifdef FEATURE_LEDSTRIP +#define crgb_iterator __gnu_cxx::__normal_iterator> enum Bobbycar_Side { FRONT_RIGHT, @@ -19,6 +20,13 @@ enum Bobbycar_Side FRONT }; +enum OtaAnimationModes +{ + None, + GreenProgressBar, + ColorChangeAll +}; + extern std::vector leds; extern uint8_t gHue; @@ -30,8 +38,8 @@ void showAnimation(); void showBetterRainbow(); void showSpeedSyncAnimation(); void showCustomColor(); +void showOtaAnimation(); void initLedStrip(); - void updateLedStrip(); #endif diff --git a/main/ledstripdefines.h b/main/ledstripdefines.h index efbdde8..283f1e3 100644 --- a/main/ledstripdefines.h +++ b/main/ledstripdefines.h @@ -18,3 +18,6 @@ #define LEDSTRIP_ANIMATION_TYPE_BETTERRAINBOW 1 #define LEDSTRIP_ANIMATION_TYPE_SPEEDSYNCANIMATION 2 #define LEDSTRIP_ANIMATION_TYPE_CUSTOMCOLOR 3 + +#define BLINK_LEFT_EXPR blinkAnimation != LEDSTRIP_OVERWRITE_BLINKRIGHT +#define BLINK_RIGHT_EXPR blinkAnimation != LEDSTRIP_OVERWRITE_BLINKLEFT diff --git a/main/main.cpp b/main/main.cpp index 2199d4f..1398b6f 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -71,6 +71,10 @@ using namespace std::chrono_literals; #include "modes/defaultmode.h" #include "displays/statusdisplay.h" #include "displays/calibratedisplay.h" +#ifdef FEATURE_DNS_NS +#include "dnsannounce.h" +#endif +#include "drivingstatistics.h" namespace { std::optional lastWifiUpdate; @@ -434,5 +438,9 @@ extern "C" void app_main() lastLedstripUpdate = now; } #endif +#ifdef FEATURE_DNS_NS + handle_dns_announce(); +#endif + calculateStatistics(); } } diff --git a/main/presets.h b/main/presets.h index aeaa019..5644db6 100644 --- a/main/presets.h +++ b/main/presets.h @@ -13,6 +13,7 @@ #include "settings.h" #include "stringsettings.h" #include "ledstripdefines.h" +#include "ledstrip.h" using namespace std::chrono_literals; @@ -245,7 +246,9 @@ constexpr Settings::Ledstrip defaultLedstrip { .stvoFrontLength = 10, .stvoFrontEnable = false, .animationMultiplier = 10, - .brightness = 255 + .brightness = 255, + .enableAnimBlink = false, + .otaMode = OtaAnimationModes::GreenProgressBar }; #endif @@ -273,6 +276,10 @@ constexpr Settings::Hybrid defaultHybrid { .deactivationLimit = 950, }; +constexpr Settings::SavedStatistics defaultSavedStatistics { + .totalCentimeters = 0, +}; + constexpr Settings defaultSettings { #ifdef FEATURE_BMS .autoConnectBms = false, @@ -300,7 +307,8 @@ constexpr Settings defaultSettings { #endif .battery = defaultBattery, .hybrid = defaultHybrid, - .lockscreen = defaultLockscreen + .lockscreen = defaultLockscreen, + .savedStatistics = defaultSavedStatistics, }; StringSettings makeDefaultStringSettings(); diff --git a/main/settings.h b/main/settings.h index 2a3d1b0..7f3a714 100644 --- a/main/settings.h +++ b/main/settings.h @@ -21,6 +21,7 @@ #include "bluetoothmode.h" #endif #include "unifiedmodelmode.h" +#include "ledstrip.h" enum class LarsmModeMode : uint8_t { Mode1, Mode2, Mode3, Mode4 }; @@ -170,6 +171,8 @@ struct Settings bool stvoFrontEnable; int16_t animationMultiplier; uint8_t brightness; + bool enableAnimBlink; + OtaAnimationModes otaMode; } ledstrip; #endif @@ -197,6 +200,10 @@ struct Settings std::array pin; } lockscreen; + struct SavedStatistics { + uint32_t totalCentimeters; + } savedStatistics; + template void executeForEveryCommonSetting(T &&callable); @@ -298,6 +305,8 @@ void Settings::executeForEveryCommonSetting(T &&callable) callable("ledstvoen", ledstrip.stvoFrontEnable); callable("ledAnimMul", ledstrip.animationMultiplier); callable("ledbrightness", ledstrip.brightness); + callable("enAnimBlink", ledstrip.enableAnimBlink); + callable("ledOtaAnim", ledstrip.otaMode); #endif callable("batteryCS", battery.cellsSeries); @@ -317,6 +326,7 @@ void Settings::executeForEveryCommonSetting(T &&callable) callable("lockAlwPresetSw", lockscreen.allowPresetSwitch); callable("lockscreenPin", lockscreen.pin); + callable("totalCentimeter", savedStatistics.totalCentimeters); } template diff --git a/main/settingspersister.cpp b/main/settingspersister.cpp index 605e7d5..5922637 100644 --- a/main/settingspersister.cpp +++ b/main/settingspersister.cpp @@ -189,6 +189,16 @@ template<> struct nvsGetterHelper { static esp_err_t nvs_get(n *out_value = UnifiedModelMode(tempValue); return err; }}; +#if defined(FEATURE_LEDSTRIP) && defined(FEATURE_OTA) +template<> struct nvsGetterHelper { static esp_err_t nvs_get(nvs_handle handle, const char* key, OtaAnimationModes* out_value) +{ + uint8_t tempValue; + esp_err_t err = nvs_get_u8(handle, key, &tempValue); + if (err == ESP_OK) + *out_value = OtaAnimationModes(tempValue); + return err; +}}; +#endif template<> struct nvsGetterHelper { static esp_err_t nvs_get(nvs_handle handle, const char* key, wifi_mode_t* out_value) { uint8_t tempValue; @@ -345,6 +355,12 @@ template<> struct nvsSetterHelper { static esp_err_t nvs_set(n { return nvs_set_u8(handle, key, uint8_t(value)); }}; +#if defined(FEATURE_LEDSTRIP) && defined(FEATURE_OTA) +template<> struct nvsSetterHelper { static esp_err_t nvs_set(nvs_handle handle, const char* key, OtaAnimationModes value) +{ + return nvs_set_u8(handle, key, uint8_t(value)); +}}; +#endif template<> struct nvsSetterHelper { static esp_err_t nvs_set(nvs_handle handle, const char* key, wifi_mode_t value) { return nvs_set_u8(handle, key, uint8_t(value)); diff --git a/main/stringsettings.h b/main/stringsettings.h index 82fac93..7745a38 100644 --- a/main/stringsettings.h +++ b/main/stringsettings.h @@ -44,6 +44,9 @@ struct StringSettings std::array otaServers; std::string otaServerUrl; #endif +#ifdef FEATURE_DNS_NS + std::string dns_key; +#endif }; template @@ -108,6 +111,9 @@ void StringSettings::executeForEveryCommonSetting(T &&callable) callable("otaserver", otaServerUrl); #endif +#ifdef FEATURE_DNS_NS + callable("dnskey", dns_key); +#endif } template diff --git a/main/texts.h b/main/texts.h index 7398618..7d3837f 100644 --- a/main/texts.h +++ b/main/texts.h @@ -7,6 +7,7 @@ constexpr char TEXT_BACK[] = "Back"; //AccessPointWifiSettingsMenu constexpr char TEXT_ACCESSPOINTWIFISETTINGS[] = "Access Point WiFi settings"; constexpr char TEXT_WIFIAPENABLED[] = "AP enabled"; +constexpr char TEXT_RESEND_DNS[] = "Resend DNS"; //constexpr char TEXT_BACK[] = "Back"; #ifdef TEXTS_PLUGIN @@ -91,6 +92,7 @@ constexpr char TEXT_POWEROFF[] = "Poweroff"; constexpr char TEXT_REBOOT[] = "Reboot"; constexpr char TEXT_DEBUG[] = "Debug"; constexpr char TEXT_BATTERY[] = "Battery"; +constexpr char TEXT_BATTERYDEBUG[] = "Bat Debug Menu"; //BatteryMenu constexpr char TEXT_CELL_SERIES[] = "Cells (Series)"; @@ -102,6 +104,7 @@ constexpr char TEXT_BATTERY_TYPE_22P[] = "22P cells"; constexpr char TEXT_BATTERY_TYPE_HG2[] = "HG2 cells"; constexpr char TEXT_BATTERY_TYPE_MH1[] = "MH1 cells"; constexpr char TEXT_BATTERY_TYPE_VTC5[] = "VTC5 cells"; +constexpr char TEXT_BATTERY_TYPE_BAK_25R[] = "BAK / 25R cells"; constexpr char TEXT_BATTERY_WHKM[] = "Wh per km"; constexpr char TEXT_BATTERY_APPLYCALIB[] = "Apply calibration"; constexpr char TEXT_VOLTAGECALIBRATION_RESET[] = "Reset calibration"; @@ -290,6 +293,12 @@ constexpr char TEXT_STVO_ENABLEFRONTLIGHT[] = "StVO Front Enable"; constexpr char TEXT_ANIMATION_MULTIPLIER[] = "Animation Multiplier"; constexpr char TEXT_LEDSTRIP_BRIGHTNESS[] = "Ledstrip Brightness"; constexpr char TEXT_LEDSTRIP_ALLCUSTOMOFF[] = "All custom off"; +constexpr char TEXT_LEDSTRIP_EN_BLINK_ANIM[] = "Animated Blink"; +constexpr char TEXT_LEDSTRIP_CHANGE_OTA_ANIM[] = "Change Ota animation"; + +constexpr char TEXT_OTAANIM_NONE[] = "None"; +constexpr char TEXT_OTAANIM_PROGRESS[] = "Progress Bar"; +constexpr char TEXT_OTAANIM_COLOR[] = "Color change"; //constexpr char TEXT_BACK[] = "Back"; //LedstripSelectAnimationMenu @@ -489,11 +498,15 @@ constexpr char TEXT_OTA_NOBUILDSERVERAVAILABLE[] = "E:No server saved."; constexpr char TEXT_OTA_NOBUILDSERVERSELECTED[] = "E:No server selected."; constexpr char TEXT_OTA_NOCONNECTION[] = "E:No internet."; constexpr char TEXT_OTA_WAITFORRESPONSE[] = "Wait for response..."; -constexpr char TEXT_REDOWNLOAD[] = "Reload list"; //LedstripColorMenu constexpr char TEXT_LEDSTRIPCOLORMENU[] = "Customize Ledstrip"; +//StatisticsMenu +constexpr char TEXT_STATISTICSMENU[] = "Statistics"; +constexpr char TEXT_STATSSAVE[] = "Save kilometers"; +constexpr char TEXT_STATSCLEAR[] = "Clear current km"; + #ifdef FEATURE_CAN constexpr char TEXT_POWERSUPPLY[] = "Powersupply"; #endif diff --git a/main/utils.cpp b/main/utils.cpp index b148e7a..0711c16 100644 --- a/main/utils.cpp +++ b/main/utils.cpp @@ -1,4 +1,5 @@ #include "utils.h" +#include "globals.h" using namespace std::chrono_literals; @@ -306,3 +307,68 @@ void readPotis() gametrakDist = cpputils::mapValueClamped(raw_gametrakDist, settings.boardcomputerHardware.gametrakDistMin, settings.boardcomputerHardware.gametrakDistMax, 0., 1000.); #endif } + +float wattToAmpere(float watt) { + float voltage = std::max(controllers.front.feedback.batVoltage, controllers.back.feedback.batVoltage); + if (voltage > 50) voltage = 50; + if (voltage < 30) voltage = 30; + return watt / voltage; +} + +float wattToMotorCurrent(float watt) { + return wattToAmpere(watt) / 4; +} + +std::string get_current_uptime_string() { + const auto uptime_time_point = espchrono::utc_clock::now(); + const auto dateTimeUptime = espchrono::toDateTime(uptime_time_point); + std::string out = fmt::format("Up: {:02d}:{:02d}:{:02d}", dateTimeUptime.hour, dateTimeUptime.minute, dateTimeUptime.second); + return out; +} + +void secondsToHMS( const float seconds, uint16_t &h, uint16_t &m, uint16_t &s ) +{ + uint32_t t = seconds; + + s = t % 60; + + t = (t - s)/60; + m = t % 60; + + t = (t - m)/60; + h = t; +} + +std::string get_current_driving_time_string() +{ + uint16_t hour{}; + uint16_t minute{}; + uint16_t second{}; + secondsToHMS(drivingStatistics.currentDrivingTime, hour, minute, second); + std::string out = fmt::format("Drive: {:02d}:{:02d}:{:02d}", hour, minute, second); + return out; +} + +uint8_t time_to_percent(std::chrono::duration> repeat, std::chrono::duration> riseTime, std::chrono::duration> fullTime, size_t numLeds, bool invert) +{ + const auto now = espchrono::millis_clock::now().time_since_epoch() % repeat; + int activated = invert ? numLeds : 0; + if (now <= riseTime) + { + if (invert) + { + activated = numLeds - ((now*numLeds) / riseTime); + } + else + { + activated = (now*numLeds) / riseTime; + } + } + else if (now < riseTime + fullTime) + { + activated = invert ? 0 : numLeds; + } + else + activated = invert ? numLeds : 0; + return activated; +} diff --git a/main/utils.h b/main/utils.h index fe15667..b88a65c 100644 --- a/main/utils.h +++ b/main/utils.h @@ -56,3 +56,9 @@ bool loadSettings(); bool saveSettings(); void updateAccumulators(); void readPotis(); +float wattToAmpere(float watt); +float wattToMotorCurrent(float watt); +std::string get_current_uptime_string(); +std::string get_current_driving_time_string(); +void secondsToHMS( const float seconds, uint16_t &h, uint8_t &m, uint8_t &s ); +uint8_t time_to_percent(std::chrono::duration> repeat, std::chrono::duration> riseTime, std::chrono::duration> fullTime, size_t numLeds, bool invert); diff --git a/main/webserver.h b/main/webserver.h index 16e4d27..954f187 100644 --- a/main/webserver.h +++ b/main/webserver.h @@ -22,6 +22,10 @@ #endif #include "webserver_settings.h" #include "webserver_stringsettings.h" +#ifdef OLD_NVS +#include "webserver_dumpnvs.h" +using namespace dump_nvs_handler; +#endif #ifdef FEATURE_WEBSERVER namespace { @@ -30,6 +34,7 @@ httpd_handle_t httpdHandle; void initWebserver(); void handleWebserver(); esp_err_t webserver_reboot_handler(httpd_req_t *req); + } namespace { @@ -63,6 +68,9 @@ void initWebserver() httpd_uri_t { .uri = "/saveSettings", .method = HTTP_GET, .handler = webserver_saveSettings_handler, .user_ctx = NULL }, httpd_uri_t { .uri = "/stringSettings", .method = HTTP_GET, .handler = webserver_stringSettings_handler, .user_ctx = NULL }, httpd_uri_t { .uri = "/saveStringSettings", .method = HTTP_GET, .handler = webserver_saveStringSettings_handler, .user_ctx = NULL }, +#ifdef OLD_NVS + httpd_uri_t { .uri = "/dumpnvs", .method = HTTP_GET, .handler = webserver_dump_nvs_handler, .user_ctx = NULL }, +#endif }) { const auto result = httpd_register_uri_handler(httpdHandle, &uri); diff --git a/main/webserver_displaycontrol.h b/main/webserver_displaycontrol.h index ed4b153..3721d7d 100644 --- a/main/webserver_displaycontrol.h +++ b/main/webserver_displaycontrol.h @@ -77,7 +77,8 @@ esp_err_t webserver_root_handler(httpd_req_t *req) #endif "Settings - " - "String Settings"; + "String Settings - " + "Dump NVS"; } { diff --git a/main/webserver_dumpnvs.cpp b/main/webserver_dumpnvs.cpp new file mode 100644 index 0000000..e69de29 diff --git a/main/webserver_dumpnvs.h b/main/webserver_dumpnvs.h new file mode 100644 index 0000000..2f00540 --- /dev/null +++ b/main/webserver_dumpnvs.h @@ -0,0 +1,199 @@ +#pragma once + +// esp-idf includes +#ifdef FEATURE_WEBSERVER +#include +#endif +#include + +// 3rdparty lib includes +#include +#include +#include +#include +#include +#include +#include +#include + +// local includes +#include "globals.h" +#include "webserver_lock.h" +#include "settingsutils.h" + +#ifdef FEATURE_WEBSERVER +namespace dump_nvs_handler { +esp_err_t webserver_dump_nvs_handler(httpd_req_t *req); +} // namespace + +using esphttpdutils::HtmlTag; +using namespace espchrono; + +namespace dump_nvs_handler { + +template +typename std::enable_if< + !std::is_same::value && + !std::is_integral::value && + !std::is_same>::value && + !std::is_same::value && + !std::is_same::value && + !std::is_same::value && + !std::is_same::value +#if defined(FEATURE_LEDSTRIP) && defined(FEATURE_OTA) + && !std::is_same::value +#endif +, bool>::type +showInputForSetting(std::string_view key, T value, JsonObject &body) +{ + body[key] = nullptr; + return false; +} + +template +typename std::enable_if< + std::is_same::value +, bool>::type +showInputForSetting(std::string_view key, T value, JsonObject &body) +{ + body[key] = value; + return true; +} + +template +typename std::enable_if< + !std::is_same::value && + std::is_integral::value +, bool>::type +showInputForSetting(std::string_view key, T value, JsonObject &body) +{ + body[key] = value; + return true; +} + +template +typename std::enable_if< + std::is_same>::value +, bool>::type +showInputForSetting(std::string_view key, T value, JsonObject &body) +{ + std::string array_str = fmt::format("{}{}{}{}", value[0], value[1], value[2], value[3]); + body[key] = array_str; + return true; +} + +template +typename std::enable_if< + std::is_same::value +, bool>::type +showInputForSetting(std::string_view key, T value, JsonObject &body) +{ + body[key] = value; + return true; +} + +template +typename std::enable_if< + std::is_same::value +, bool>::type +showInputForSetting(std::string_view key, T value, JsonObject &body) +{ + body[key] = value.count(); + return true; +} + +template +typename std::enable_if< + std::is_same::value +, bool>::type +showInputForSetting(std::string_view key, T value, JsonObject &body) +{ + body[key] = toString(espchrono::DayLightSavingMode(value)); + return true; +} + +template +typename std::enable_if< + std::is_same::value +, bool>::type +showInputForSetting(std::string_view key, T value, JsonObject &body) +{ + body[key] = int(value); + return true; +} + +#if defined(FEATURE_LEDSTRIP) && defined(FEATURE_OTA) + +template +typename std::enable_if< + std::is_same::value +, bool>::type +showInputForSetting(std::string_view key, T value, JsonObject &body) +{ + body[key] = int(value); + return true; +} +#endif + +esp_err_t webserver_dump_nvs_handler(httpd_req_t *req) +{ + espcpputils::LockHelper helper{webserver_lock->handle, std::chrono::ceil(5s).count()}; + if (!helper.locked()) + { + constexpr const std::string_view msg = "could not lock webserver_lock"; + ESP_LOGE(TAG, "%.*s", msg.size(), msg.data()); + CALL_AND_EXIT(esphttpdutils::webserver_resp_send, req, esphttpdutils::ResponseStatus::BadRequest, "text/plain", msg); + } + + DynamicJsonDocument doc(6144); + const auto profile = settingsPersister.currentlyOpenProfileIndex(); + const auto switchBackProfile = profile ? int(*profile) : 0; + + JsonObject json_settings = doc.createNestedObject("settings"); + settings.executeForEveryCommonSetting([&](std::string_view key, const auto &value){ + showInputForSetting(key, value, json_settings); + }); + + JsonObject json_stringSettings = doc.createNestedObject("stringSettings"); + stringSettings.executeForEveryCommonSetting([&](std::string_view key, const auto &value){ + showInputForSetting(key, value, json_stringSettings); + }); + + JsonObject profiles = doc.createNestedObject("profiles"); + + // Profile settings + for (uint8_t profile_num = 0; profile_num < 4; profile_num++) { + +#ifdef SIMPLIFIED_TRIGGER_TRIGGERONPRESET + if (profile_num == SIMPLIFIED_TRIGGER_TRIGGERONPRESET) { + continue; + } +#endif + switchProfile(profile_num); + + const auto cur_profile = settingsPersister.currentlyOpenProfileIndex(); + const auto profile_str = cur_profile ? std::to_string(*cur_profile) : "-"; + + JsonObject profile = profiles.createNestedObject(profile_str); + JsonObject profile_stringSettings = profile.createNestedObject("stringSettings"); + JsonObject profile_settings = profile.createNestedObject("settings"); + + stringSettings.executeForEveryProfileSetting([&](const char *key, auto &value){ + showInputForSetting(key, value, profile_stringSettings); + }); + + settings.executeForEveryProfileSetting([&](const char *key, auto &value){ + showInputForSetting(key, value, profile_settings); + }); + } + + switchProfile(switchBackProfile); + + std::string body; + serializeJson(doc, body); + CALL_AND_EXIT(esphttpdutils::webserver_resp_send, req, esphttpdutils::ResponseStatus::Ok, "application/json", body) +} +} // namespace + +#endif + diff --git a/main/webserver_ota.h b/main/webserver_ota.h index 9e19ff5..5ea061e 100644 --- a/main/webserver_ota.h +++ b/main/webserver_ota.h @@ -70,7 +70,8 @@ esp_err_t webserver_ota_handler(httpd_req_t *req) body += "Display control - " "Update - " "Settings - " - "String Settings"; + "String Settings - " + "Dump NVS"; } if (const esp_app_desc_t *app_desc = esp_ota_get_app_description()) diff --git a/main/webserver_settings.h b/main/webserver_settings.h index 98e5da3..1fa9f24 100644 --- a/main/webserver_settings.h +++ b/main/webserver_settings.h @@ -145,7 +145,8 @@ esp_err_t webserver_settings_handler(httpd_req_t *req) "Update - " #endif "Settings - " - "String Settings"; + "String Settings - " + "Dump NVS"; } HtmlTag divTag{"div", "class=\"form-table\"", body}; diff --git a/main/webserver_stringsettings.h b/main/webserver_stringsettings.h index b27a4eb..5c45351 100644 --- a/main/webserver_stringsettings.h +++ b/main/webserver_stringsettings.h @@ -84,7 +84,8 @@ esp_err_t webserver_stringSettings_handler(httpd_req_t *req) "Update - " #endif "Settings - " - "String Settings"; + "String Settings - " + "Dump NVS"; } HtmlTag divTag{"div", "class=\"form-table\"", body}; diff --git a/sdkconfig_comred b/sdkconfig_comred index 4fa47f2..5476968 100644 --- a/sdkconfig_comred +++ b/sdkconfig_comred @@ -594,7 +594,7 @@ CONFIG_ESP_HTTP_CLIENT_ENABLE_HTTPS=y # # HTTP Server # -CONFIG_HTTPD_MAX_REQ_HDR_LEN=512 +CONFIG_HTTPD_MAX_REQ_HDR_LEN=1024 CONFIG_HTTPD_MAX_URI_LEN=512 CONFIG_HTTPD_ERR_RESP_NO_DELAY=y CONFIG_HTTPD_PURGE_BUF_LEN=32 diff --git a/sdkconfig_feedc0de b/sdkconfig_feedc0de index 267c6ed..1c899b1 100644 --- a/sdkconfig_feedc0de +++ b/sdkconfig_feedc0de @@ -594,7 +594,7 @@ CONFIG_ESP_HTTP_CLIENT_ENABLE_HTTPS=y # # HTTP Server # -CONFIG_HTTPD_MAX_REQ_HDR_LEN=512 +CONFIG_HTTPD_MAX_REQ_HDR_LEN=1024 CONFIG_HTTPD_MAX_URI_LEN=512 CONFIG_HTTPD_ERR_RESP_NO_DELAY=y CONFIG_HTTPD_PURGE_BUF_LEN=32 diff --git a/sdkconfig_greyhash b/sdkconfig_greyhash index e803f64..e0d56f9 100644 --- a/sdkconfig_greyhash +++ b/sdkconfig_greyhash @@ -594,7 +594,7 @@ CONFIG_ESP_HTTP_CLIENT_ENABLE_HTTPS=y # # HTTP Server # -CONFIG_HTTPD_MAX_REQ_HDR_LEN=512 +CONFIG_HTTPD_MAX_REQ_HDR_LEN=1024 CONFIG_HTTPD_MAX_URI_LEN=512 CONFIG_HTTPD_ERR_RESP_NO_DELAY=y CONFIG_HTTPD_PURGE_BUF_LEN=32 diff --git a/sdkconfig_mick b/sdkconfig_mick index 43175a1..8662ee9 100644 --- a/sdkconfig_mick +++ b/sdkconfig_mick @@ -594,7 +594,7 @@ CONFIG_ESP_HTTP_CLIENT_ENABLE_HTTPS=y # # HTTP Server # -CONFIG_HTTPD_MAX_REQ_HDR_LEN=512 +CONFIG_HTTPD_MAX_REQ_HDR_LEN=1024 CONFIG_HTTPD_MAX_URI_LEN=512 CONFIG_HTTPD_ERR_RESP_NO_DELAY=y CONFIG_HTTPD_PURGE_BUF_LEN=32 diff --git a/sdkconfig_peter b/sdkconfig_peter index 4fa47f2..5476968 100644 --- a/sdkconfig_peter +++ b/sdkconfig_peter @@ -594,7 +594,7 @@ CONFIG_ESP_HTTP_CLIENT_ENABLE_HTTPS=y # # HTTP Server # -CONFIG_HTTPD_MAX_REQ_HDR_LEN=512 +CONFIG_HTTPD_MAX_REQ_HDR_LEN=1024 CONFIG_HTTPD_MAX_URI_LEN=512 CONFIG_HTTPD_ERR_RESP_NO_DELAY=y CONFIG_HTTPD_PURGE_BUF_LEN=32 diff --git a/app-flash.sh b/tools/bobby-app-flash similarity index 100% rename from app-flash.sh rename to tools/bobby-app-flash diff --git a/coredump.sh b/tools/bobby-coredump similarity index 100% rename from coredump.sh rename to tools/bobby-coredump diff --git a/flash.sh b/tools/bobby-flash similarity index 100% rename from flash.sh rename to tools/bobby-flash diff --git a/monitor.sh b/tools/bobby-monitor similarity index 100% rename from monitor.sh rename to tools/bobby-monitor diff --git a/open_ide.sh b/tools/open_ide similarity index 58% rename from open_ide.sh rename to tools/open_ide index f1940c5..ce36652 100755 --- a/open_ide.sh +++ b/tools/open_ide @@ -5,4 +5,4 @@ then source export.sh --skip-source-check fi -qtcreator "bobbycar-boardcomputer-firmware" 2>&1 >/dev/null & +qtcreator . 2>&1 >/dev/null &