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/statisticsmenu.cpp b/main/displays/menus/statisticsmenu.cpp new file mode 100644 index 0000000..0c3ebb8 --- /dev/null +++ b/main/displays/menus/statisticsmenu.cpp @@ -0,0 +1,90 @@ +#include "statisticsmenu.h" + +// local includes +#include "mainmenu.h" +#include "actions/dummyaction.h" +#include "actioninterface.h" +#include "fmt/core.h" +#include "utils.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: {:.2f}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 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; + } +}; + +StatisticsMenu::StatisticsMenu() +{ + constructMenuItem>(); + constructMenuItem>(); + constructMenuItem>(); + constructMenuItem>(); + constructMenuItem>(); + constructMenuItem>(); + constructMenuItem, SaveKilometersAction>>(); + constructMenuItem, ClearCurrentStatsAction>>(); + 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/globals.cpp b/main/globals.cpp index 40e4ec5..9f80ac9 100644 --- a/main/globals.cpp +++ b/main/globals.cpp @@ -47,3 +47,5 @@ BluetoothSerial bluetoothSerial; ModeInterface *lastMode{}; ModeInterface *currentMode{}; + +DrivingStatistics drivingStatistics; diff --git a/main/globals.h b/main/globals.h index 69d6512..af3f7cc 100644 --- a/main/globals.h +++ b/main/globals.h @@ -56,6 +56,15 @@ extern std::string dns_lastIpAddress_v6_global; extern bool simplified; +struct DrivingStatistics { + float meters_driven; + float currentDrivingTime; + double totalMeters; + uint32_t last_cm_written; +}; + +extern DrivingStatistics drivingStatistics; + extern Settings settings; extern StringSettings stringSettings; extern SettingsPersister settingsPersister; diff --git a/main/main.cpp b/main/main.cpp index ec91246..65ac8c2 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -524,5 +524,33 @@ extern "C" void app_main() dns_lastIpAddress_v6_global = "-"; } #endif + EVERY_N_MILLIS( 10 ) { + + 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; + + if (abs(avgSpeedKmh) > 1) + { + drivingStatistics.currentDrivingTime += duration; + } + + if (drivingStatistics.totalMeters > ((drivingStatistics.last_cm_written / 100.f) + 100)) + { + drivingStatistics.last_cm_written = drivingStatistics.totalMeters * 100; + settings.savedStatistics.totalCentimeters = drivingStatistics.last_cm_written; + saveSettings(); + } + } } } diff --git a/main/presets.h b/main/presets.h index aeaa019..99d81f8 100644 --- a/main/presets.h +++ b/main/presets.h @@ -273,6 +273,10 @@ constexpr Settings::Hybrid defaultHybrid { .deactivationLimit = 950, }; +constexpr Settings::SavedStatistics defaultSavedStatistics { + .totalCentimeters = 0, +}; + constexpr Settings defaultSettings { #ifdef FEATURE_BMS .autoConnectBms = false, @@ -300,7 +304,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..d0b092a 100644 --- a/main/settings.h +++ b/main/settings.h @@ -197,6 +197,10 @@ struct Settings std::array pin; } lockscreen; + struct SavedStatistics { + uint32_t totalCentimeters; + } savedStatistics; + template void executeForEveryCommonSetting(T &&callable); @@ -317,6 +321,7 @@ void Settings::executeForEveryCommonSetting(T &&callable) callable("lockAlwPresetSw", lockscreen.allowPresetSwitch); callable("lockscreenPin", lockscreen.pin); + callable("totalCentimeter", savedStatistics.totalCentimeters); } template diff --git a/main/utils.cpp b/main/utils.cpp index 4acf525..95e4cd0 100644 --- a/main/utils.cpp +++ b/main/utils.cpp @@ -1,4 +1,5 @@ #include "utils.h" +#include "globals.h" using namespace std::chrono_literals; @@ -317,3 +318,32 @@ float wattToAmpere(float watt) { 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; +} diff --git a/main/utils.h b/main/utils.h index 433ef2a..f91f709 100644 --- a/main/utils.h +++ b/main/utils.h @@ -58,3 +58,6 @@ 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 );