diff --git a/icons/battery.png b/icons/battery.png new file mode 100644 index 0000000..972da02 Binary files /dev/null and b/icons/battery.png differ diff --git a/main/CMakeLists.txt b/main/CMakeLists.txt index b458dde..6c73bfb 100644 --- a/main/CMakeLists.txt +++ b/main/CMakeLists.txt @@ -23,6 +23,7 @@ set(headers actions/wifiscanaction.h actions/switchprofileaction.h actions/updateswapfrontbackaction.h + battery.h bluetoothmode.h ble_bobby.h bletexthelpers.h @@ -36,10 +37,12 @@ set(headers changevaluedisplay_wifi_power_t.h cloud.h debugcolorhelpers.h + displays/calibratevoltagedisplay.h displays/gameoflifedisplay.h displays/graphdisplay.h displays/menus/aboutmenu.h displays/menus/accesspointwifisettingsmenu.h + displays/menus/batterymenu.h displays/menus/blesettingsmenu.h displays/menus/bluetoothsettingsmenu.h displays/menus/bmsmenu.h @@ -63,6 +66,7 @@ set(headers displays/menus/presetsmenu.h displays/menus/profilesmenu.h displays/menus/selectmodemenu.h + displays/menus/selectbatterytypemenu.h displays/menus/settingsmenu.h displays/menus/stationwifisettingsmenu.h displays/menus/tempomatmodesettingsmenu.h @@ -95,6 +99,7 @@ set(headers displays/statusdisplay.h displays/updatedisplay.h icons/alert.h + icons/battery.h icons/bluetooth.h icons/bms.h icons/buzzer.h @@ -189,6 +194,7 @@ target_compile_options(${COMPONENT_TARGET} PRIVATE -fstack-reuse=all -fstack-protector-all + -fdiagnostics-color=always -Wno-unused-function -Wno-deprecated-declarations -Wno-missing-field-initializers diff --git a/main/accessors/settingsaccessors.h b/main/accessors/settingsaccessors.h index 4bb5e4e..46ad53a 100644 --- a/main/accessors/settingsaccessors.h +++ b/main/accessors/settingsaccessors.h @@ -168,6 +168,13 @@ struct AnimationMultiplierAccessor : public RefAccessorSaveSettings { i struct LedstripBrightnessAccessor : public RefAccessorSaveSettings { uint8_t &getRef() const override { return settings.ledstrip.brightness; } }; #endif +// Battery +struct BatterySeriesCellsAccessor : public RefAccessorSaveSettings { uint8_t &getRef() const override { return settings.battery.cellsSeries; } }; +struct BatteryParallelCellsAccessor : public RefAccessorSaveSettings { uint8_t &getRef() const override { return settings.battery.cellsParallel; } }; +struct BatteryWHperKMAccessor : public RefAccessorSaveSettings { uint16_t &getRef() const override { return settings.battery.watthoursPerKilometer; } }; +struct BatteryApplyCalibrationAccessor : public RefAccessorSaveSettings { bool &getRef() const override { return settings.battery.applyCalibration; } }; + + struct LockscreenAllowPresetSwitchAccessor : public RefAccessorSaveSettings { bool &getRef() const override { return settings.lockscreen.allowPresetSwitch; } }; template struct LockscreenPinDigitAccessor : public RefAccessorSaveSettings { int8_t &getRef() const override { return settings.lockscreen.pin[index]; } }; diff --git a/main/battery.h b/main/battery.h new file mode 100644 index 0000000..f4b4c0c --- /dev/null +++ b/main/battery.h @@ -0,0 +1,154 @@ +#pragma once + +// 3rdparty lib includes +#include +#include + +// local includes +#include "globals.h" +#include "cpputils.h" + +#define BatteryCellTypeValues(x) \ + x(_22P) \ + x(HG2) \ + x(MH1) \ + x(VTC5) +DECLARE_TYPESAFE_ENUM(BatteryCellType, : uint8_t, BatteryCellTypeValues) + +namespace { + +float mapFloat(float x, float in_min, float in_max, float out_min, float out_max) { + return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min; +} +#define CURVE(higherVoltage,lowerVoltage,fromAh,toAh) if(cellVoltage >= lowerVoltage && cellVoltage <= higherVoltage) return 100 * (expected_ah - mapFloat(cellVoltage, higherVoltage, lowerVoltage, fromAh, toAh)) / expected_ah; + +float getBatteryPercentage(float batVoltage, BatteryCellType cellType) +{ + const float cellVoltage = batVoltage / settings.battery.cellsSeries; + + switch (cellType) { + case BatteryCellType::_22P: { + const float expected_ah = 2.2; + if(cellVoltage > 4.15){ + return 100; + } + CURVE(4.15, 4.04, 0, 0.25) + CURVE(4.04, 3.95, 0.25, 0.5) + CURVE(3.95, 3.86, 0.5, 0.75) + CURVE(3.86, 3.74, 0.75, 1.0) + CURVE(3.74, 3.64, 1.0, 1.25) + CURVE(3.64, 3.59, 1.25, 1.5) + CURVE(3.59, 3.54, 1.5, 1.75) + CURVE(3.54, 3.43, 1.75, 2.0) + CURVE(3.43, 3.35, 2.0, 2.1) + CURVE(3.35, 2.50, 2.1, 2.2) + break; + } + case BatteryCellType::MH1: { + const float expected_ah = 3.2; + if(cellVoltage > 4.15){ + return 100; + } + CURVE(4.15, 4.09, 0, 0.25) + CURVE(4.09, 4.04, 0.25, 0.5) + CURVE(4.04, 3.95, 0.5, 0.75) + CURVE(3.95, 3.88, 0.75, 1.0) + CURVE(3.88, 3.79, 1.0, 1.25) + CURVE(3.79, 3.70, 1.25, 1.5) + CURVE(3.70, 3.65, 1.5, 1.75) + CURVE(3.65, 3.60, 1.75, 2.0) + CURVE(3.60, 3.56, 2.0, 2.25) + CURVE(3.56, 3.50, 2.25, 2.5) + CURVE(3.50, 3.40, 2.5, 2.75) + CURVE(3.40, 3.30, 2.75, 3.0) + CURVE(3.30, 2.5, 3.0, 3.2) + break; + } + case BatteryCellType::HG2: { + const float expected_ah = 3.0; + if(cellVoltage > 4.15){ + return 100; + } + CURVE(4.15, 4.08, 0, 0.25) + CURVE(4.08, 4.01, 0.25, 0.5) + CURVE(4.01, 3.92, 0.5, 0.75) + CURVE(3.92, 3.84, 0.75, 1.0) + CURVE(3.84, 3.75, 1.0, 1.25) + CURVE(3.75, 3.67, 1.25, 1.5) + CURVE(3.67, 3.62, 1.5, 1.75) + CURVE(3.62, 3.55, 1.75, 2.0) + CURVE(3.55, 3.44, 2.0, 2.25) + CURVE(3.44, 3.30, 2.25, 2.5) + CURVE(3.30, 3.05, 2.5, 2.75) + CURVE(3.05, 2.50, 2.75, 3.0) + break; + } + case BatteryCellType::VTC5: { + const float expected_ah = 2.6; + if(cellVoltage > 4.15){ + return 100; + } + CURVE(4.15, 4.08, 0, 0.25) + CURVE(4.08, 3.98, 0.25, 0.5) + CURVE(3.98, 3.89, 0.5, 0.75) + CURVE(3.89, 3.79, 0.75, 1.0) + CURVE(3.79, 3.71, 1.0, 1.25) + CURVE(3.71, 3.64, 1.25, 1.5) + CURVE(3.64, 3.53, 1.5, 1.75) + CURVE(3.53, 3.44, 1.75, 2.0) + CURVE(3.44, 3.20, 2.0, 2.25) + CURVE(3.20, 2.80, 2.25, 2.5) + CURVE(2.80, 2.50, 2.5, 2.60) + 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 avgVoltage = 0; + for (auto &controller : controllers) + { + avgVoltage += controller.getCalibratedVoltage(settings.battery.applyCalibration); + } + avgVoltage = avgVoltage / controllers.size(); + + return (target_mah / 1000.f) * 3.7 * settings.battery.cellsParallel * settings.battery.cellsSeries * (getBatteryPercentage(avgVoltage, BatteryCellType(settings.battery.cellType)) / 100); +} + +std::string getBatteryPercentageString() +{ + float avgVoltage = 0; + for (auto &controller : controllers) + { + avgVoltage += controller.getCalibratedVoltage(settings.battery.applyCalibration); + } + avgVoltage = avgVoltage / controllers.size(); + + std::string output = fmt::format("Battery: {:.1f}%", getBatteryPercentage(avgVoltage, BatteryCellType(settings.battery.cellType))); + return output; +} + +std::string getBatteryRemainingWattHoursString() +{ + return fmt::format("{:.1f} Wh", getRemainingWattHours()); +} + +std::string getBatteryCellTypeString() +{ + return fmt::format("Cells: {}", toString(BatteryCellType(settings.battery.cellType))); +} + +std::string getRemainingRangeString() +{ + return fmt::format("{:.1f} km", getRemainingWattHours() / settings.battery.watthoursPerKilometer); +} + +} // namespace diff --git a/main/ble_bobby.h b/main/ble_bobby.h index 218868d..e72c746 100644 --- a/main/ble_bobby.h +++ b/main/ble_bobby.h @@ -81,11 +81,11 @@ void handleBle() { auto arr = doc.createNestedArray("v"); if (controllers.front.feedbackValid) - arr.add(fixBatVoltage(controllers.front.feedback.batVoltage)); + arr.add(controllers.front.getCalibratedVoltage(settings.battery.applyCalibration)); else arr.add(nullptr); if (controllers.back.feedbackValid) - arr.add(fixBatVoltage(controllers.back.feedback.batVoltage)); + arr.add(controllers.back.getCalibratedVoltage(settings.battery.applyCalibration)); else arr.add(nullptr); } diff --git a/main/cloud.h b/main/cloud.h index 0a423da..a4ba268 100644 --- a/main/cloud.h +++ b/main/cloud.h @@ -109,7 +109,7 @@ void cloudCollect() } cloudBuffer += fmt::format(",[{:.02f},{:.02f}", - fixBatVoltage(controller.feedback.batVoltage), + controller.getCalibratedVoltage(settings.battery.applyCalibration), fixBoardTemp(controller.feedback.boardTemp)); constexpr const auto addMotor = [](const bobbycar::protocol::serial::MotorState &command, diff --git a/main/controller.h b/main/controller.h index ca2772a..cc415b6 100644 --- a/main/controller.h +++ b/main/controller.h @@ -24,16 +24,19 @@ class HardwareSerial; #endif namespace { + struct Controller { Controller( #ifdef FEATURE_SERIAL HardwareSerial &serial, #endif - bool &enableLeft, bool &enableRight, bool &invertLeft, bool &invertRight) : + bool &enableLeft, bool &enableRight, bool &invertLeft, bool &invertRight, + int16_t &voltageCalib30V, int16_t &voltageCalib50V) : #ifdef FEATURE_SERIAL serial{serial}, #endif - enableLeft{enableLeft}, enableRight{enableRight}, invertLeft{invertLeft}, invertRight{invertRight} + enableLeft{enableLeft}, enableRight{enableRight}, invertLeft{invertLeft}, invertRight{invertRight}, + voltageCalib30V{voltageCalib30V}, voltageCalib50V{voltageCalib50V} { } // Controller(const Controller &) = delete; @@ -43,6 +46,7 @@ struct Controller { std::reference_wrapper serial; #endif bool &enableLeft, &enableRight, &invertLeft, &invertRight; + int16_t &voltageCalib30V, &voltageCalib50V; bobbycar::protocol::serial::Command command{}; @@ -52,8 +56,23 @@ struct Controller { bool feedbackValid{}; bobbycar::protocol::serial::Feedback feedback{}; + #ifdef FEATURE_SERIAL FeedbackParser parser{serial, feedbackValid, feedback}; #endif + + float getCalibratedVoltage(bool applyCalibration) const + { + float voltage = feedback.batVoltage; + if (applyCalibration) + { + voltage = ((voltage - float(voltageCalib30V)) * (20.f / (float(voltageCalib50V) - float(voltageCalib30V))) + 30.f); + } + else + { + voltage = voltage / 100.; + } + return voltage; + } }; } diff --git a/main/debugtexthelpers.h b/main/debugtexthelpers.h index b553089..ecb135f 100644 --- a/main/debugtexthelpers.h +++ b/main/debugtexthelpers.h @@ -48,7 +48,7 @@ public: using RightCommand = CommandTexts; //struct BatVoltageText : public virtual TextInterface { public: std::string text() const override { std::string line{"batVoltage: "}; if (controller::get().feedbackValid) line += std::to_string(controller::get().feedback.batVoltage); return line; } }; - struct BatVoltageFixedText : public virtual TextInterface { public: std::string text() const override { std::string line{"batVoltage: "}; if (controller::get().feedbackValid) line += fmt::format("{:.2f}V", fixBatVoltage(controller::get().feedback.batVoltage)); return line; } }; + struct BatVoltageFixedText : public virtual TextInterface { public: std::string text() const override { std::string line{"batVoltage: "}; if (controller::get().feedbackValid) line += fmt::format("{:.2f}V", controller::get().getCalibratedVoltage(settings.battery.applyCalibration)); return line; } }; //struct BoardTempText : public virtual TextInterface { public: std::string text() const override { std::string line{"boardTemp: "}; if (controller::get().feedbackValid) line += std::to_string(controller::get().feedback.boardTemp); return line; } }; struct BoardTempFixedText : public virtual TextInterface { public: std::string text() const override { std::string line{"boardTemp: "}; if (controller::get().feedbackValid) line += fmt::format("{:.2f}C", fixBoardTemp(controller::get().feedback.boardTemp)); return line; } }; struct TimeoutCntSerialText : public virtual TextInterface { public: std::string text() const override { std::string line{"timeoutCntSerial: "}; if (controller::get().feedbackValid) line += std::to_string(controller::get().feedback.timeoutCntSerial); return line; } }; diff --git a/main/displays/calibratevoltagedisplay.h b/main/displays/calibratevoltagedisplay.h new file mode 100644 index 0000000..67c9646 --- /dev/null +++ b/main/displays/calibratevoltagedisplay.h @@ -0,0 +1,91 @@ +#pragma once + +// Local includes +#include "menudisplay.h" +#include "utils.h" +#include "menuitem.h" +#include "icons/back.h" +#include "icons/settings.h" +#include "texts.h" +#include "actions/dummyaction.h" +#include "actions/switchscreenaction.h" +#include "battery.h" +#include "menus/batterymenu.h" +#include "widgets/label.h" +#include "globals.h" + +using namespace espgui; + +namespace { + class CalibrateVoltageDisplay; + class BatteryMenu; + + class Save30VCalibrationAction : public virtual ActionInterface + { + public: + void triggered() override { + settings.battery.front30VoltCalibration = controllers.front.feedback.batVoltage; + settings.battery.back30VoltCalibration = controllers.back.feedback.batVoltage; + saveSettings(); + } + }; + + class Save50VCalibrationAction : public virtual ActionInterface + { + public: + void triggered() override { + settings.battery.front50VoltCalibration = controllers.front.feedback.batVoltage; + settings.battery.back50VoltCalibration = controllers.back.feedback.batVoltage; + saveSettings(); + } + }; + + class ResetCalibrationAction : public virtual ActionInterface + { + public: + void triggered() override { + settings.battery.front30VoltCalibration = 3000; + settings.battery.back30VoltCalibration = 3000; + settings.battery.front50VoltCalibration = 5000; + settings.battery.back50VoltCalibration = 5000; + saveSettings(); + } + }; + + float convertToFloat(int16_t value) + { + return value/100.; + } + + class BatteryVoltageCalibrationFront30VText : public virtual TextInterface { public: std::string text() const override { return fmt::format("30V Front: {}", convertToFloat(settings.battery.front30VoltCalibration)); } }; + class BatteryVoltageCalibrationBack30VText : public virtual TextInterface { public: std::string text() const override { return fmt::format("30V Back: {}", convertToFloat(settings.battery.back30VoltCalibration)); } }; + class BatteryVoltageCalibrationFront50VText : public virtual TextInterface { public: std::string text() const override { return fmt::format("50V Front: {}", convertToFloat(settings.battery.front50VoltCalibration)); } }; + class BatteryVoltageCalibrationBack50VText : public virtual TextInterface { public: std::string text() const override { return fmt::format("50V Back: {}", convertToFloat(settings.battery.back50VoltCalibration)); } }; + class BatteryVoltageCalibratedText : public virtual TextInterface { public: std::string text() const override { if (settings.battery.applyCalibration) return fmt::format("F{:.2f}V B{:.2f}", controllers.front.getCalibratedVoltage(settings.battery.applyCalibration), controllers.back.getCalibratedVoltage(settings.battery.applyCalibration)); else return "Not activated"; } }; +} + +namespace { + class CalibrateVoltageDisplay : + public MenuDisplay, + public StaticText, + public BackActionInterface> + { + public: + CalibrateVoltageDisplay() + { + constructMenuItem, Save30VCalibrationAction>>(); + constructMenuItem, Save50VCalibrationAction>>(); + constructMenuItem, ToggleBoolAction, CheckboxIcon, BatteryApplyCalibrationAccessor>>(); + constructMenuItem, SwitchScreenAction, StaticMenuItemIcon<&espgui::icons::back>>>(); + + constructMenuItem>(); + + constructMenuItem>(); + constructMenuItem>(); + constructMenuItem>(); + constructMenuItem>(); + constructMenuItem>(); + constructMenuItem, ResetCalibrationAction>>(); + } + }; +} // Namespace diff --git a/main/displays/menus/batterymenu.h b/main/displays/menus/batterymenu.h new file mode 100644 index 0000000..e98a449 --- /dev/null +++ b/main/displays/menus/batterymenu.h @@ -0,0 +1,71 @@ +#pragma once + +// Local includes +#include "menudisplay.h" +#include "utils.h" +#include "menuitem.h" +#include "icons/back.h" +#include "icons/settings.h" +#include "texts.h" +#include "actions/dummyaction.h" +#include "actions/switchscreenaction.h" +#include "mainmenu.h" +#include "battery.h" +#include "selectbatterytypemenu.h" +#include "displays/calibratevoltagedisplay.h" + +// Helper +class currentBatteryStatus : public virtual TextInterface { public: std::string text() const override { return getBatteryPercentageString(); } }; + +using namespace espgui; + +namespace { + class BatteryMenu; + class CalibrateVoltageDisplay; + + using BatteryCellSeriesChangeScreen = makeComponent< + ChangeValueDisplay, + StaticText, + BatterySeriesCellsAccessor, + BackActionInterface>, + SwitchScreenAction + >; + + using BatteryCellParallelChangeScreen = makeComponent< + ChangeValueDisplay, + StaticText, + BatteryParallelCellsAccessor, + BackActionInterface>, + SwitchScreenAction + >; + + using BatteryWHperKMChangeScreen = makeComponent< + ChangeValueDisplay, + StaticText, + BatteryWHperKMAccessor, + BackActionInterface>, + SwitchScreenAction + >; +} + +namespace { + class BatteryMenu : + public MenuDisplay, + public StaticText, + public BackActionInterface> + { + public: + BatteryMenu() + { + constructMenuItem>(); + constructMenuItem>(); + constructMenuItem, SwitchScreenAction>>(); + constructMenuItem, SwitchScreenAction>>(); + constructMenuItem, SwitchScreenAction>>(); + constructMenuItem, SwitchScreenAction>>(); + constructMenuItem>(); + constructMenuItem, SwitchScreenAction, StaticMenuItemIcon<&icons::settings>>>(); + constructMenuItem, SwitchScreenAction, StaticMenuItemIcon<&espgui::icons::back>>>(); + } + }; +} // Namespace diff --git a/main/displays/menus/ledstripmenu.h b/main/displays/menus/ledstripmenu.h index a85a0fb..bb680ae 100644 --- a/main/displays/menus/ledstripmenu.h +++ b/main/displays/menus/ledstripmenu.h @@ -113,9 +113,10 @@ public: constructMenuItem, ToggleBoolAction, CheckboxIcon, EnableBeepWhenBlinkAccessor>>(); constructMenuItem, ToggleBoolAction, CheckboxIcon, EnableFullBlinkAccessor>>(); - constructMenuItem, ToggleBoolAction, CheckboxIcon, EnableLedstripStVOAccessor>>(); - constructMenuItem, SwitchScreenAction>>(); - constructMenuItem, SwitchScreenAction>>(); + if (!simplified) { constructMenuItem, ToggleBoolAction, CheckboxIcon, EnableLedstripStVOAccessor>>(); } + constructMenuItem, ToggleBoolAction, CheckboxIcon, EnableLedstripStVOFrontlight>>(); + if (!simplified) { constructMenuItem, SwitchScreenAction>>(); } + if (!simplified) { constructMenuItem, SwitchScreenAction>>(); } constructMenuItem, SwitchScreenAction>>(); constructMenuItem, SwitchScreenAction>>(); diff --git a/main/displays/menus/ledstripselectanimationmenu.h b/main/displays/menus/ledstripselectanimationmenu.h index da001ad..6d07fb7 100644 --- a/main/displays/menus/ledstripselectanimationmenu.h +++ b/main/displays/menus/ledstripselectanimationmenu.h @@ -11,6 +11,22 @@ #include "actions/dummyaction.h" #include "actions/ledstripanimationactions.h" #include "actions/switchscreenaction.h" +#include "ledstrip.h" +#include "ledstripdefines.h" + +class currentSelectedAnimationText : public virtual TextInterface { public: std::string text() const override { + switch (animation_type) { + case LEDSTRIP_ANIMATION_TYPE_DEFAULTRAINBOW: + return TEXT_ANIMATION_DEFAULTRAINBOW; + case LEDSTRIP_ANIMATION_TYPE_BETTERRAINBOW: + return TEXT_ANIMATION_BETTERRAINBOW; + case LEDSTRIP_ANIMATION_TYPE_SPEEDSYNCANIMATION: + return TEXT_ANIMATION_SPEEDSYNCANIMATION; + default: + return "Animation Unkown"; + } + }; +}; using namespace espgui; @@ -27,6 +43,8 @@ namespace { public: LedstripSelectAnimationMenu() { + constructMenuItem>(); + constructMenuItem>(); constructMenuItem, LedstripAnimationDefaultRainbowAction>>(); constructMenuItem, LedstripAnimationBetterRainbowAction>>(); constructMenuItem, LedstripAnimationSyncToSpeedAction>>(); diff --git a/main/displays/menus/ledstripselectblinkmenu.h b/main/displays/menus/ledstripselectblinkmenu.h index 336d291..06122e4 100644 --- a/main/displays/menus/ledstripselectblinkmenu.h +++ b/main/displays/menus/ledstripselectblinkmenu.h @@ -11,6 +11,29 @@ #include "actions/dummyaction.h" #include "actions/ledstripblinkactions.h" #include "actions/switchscreenaction.h" +#include "ledstripdefines.h" + +class currentSelectedBlinkAnimationText : public virtual TextInterface { public: std::string text() const override { + switch (blinkAnimation) { + case LEDSTRIP_OVERWRITE_BLINKLEFT: +#ifndef LEDSTRIP_WRONG_DIRECTION + return TEXT_ANIMATION_BLINKLEFT; +#else + return TEXT_ANIMATION_BLINKRIGHT; +#endif + case LEDSTRIP_OVERWRITE_BLINKRIGHT: +#ifndef LEDSTRIP_WRONG_DIRECTION + return TEXT_ANIMATION_BLINKRIGHT; +#else + return TEXT_ANIMATION_BLINKLEFT; +#endif + case LEDSTRIP_OVERWRITE_BLINKBOTH: + return TEXT_ANIMATION_BLINKBOTH; + default: + return TEXT_ANIMATION_BLINKNONE; + } + }; +}; using namespace espgui; @@ -27,6 +50,8 @@ namespace { public: LedstripSelectBlinkMenu() { + constructMenuItem>(); + constructMenuItem>(); constructMenuItem, LedstripAnimationBlinkNoneAction>>(); constructMenuItem, LedstripAnimationBlinkLeftAction>>(); constructMenuItem, LedstripAnimationBlinkRightAction>>(); diff --git a/main/displays/menus/mainmenu.h b/main/displays/menus/mainmenu.h index e5c3020..37d4518 100644 --- a/main/displays/menus/mainmenu.h +++ b/main/displays/menus/mainmenu.h @@ -3,11 +3,13 @@ // local includes #include "menudisplay.h" #include "menuitem.h" +#include "batterymenu.h" #include "actions/switchscreenaction.h" #include "actions/modesettingsaction.h" #include "actions/rebootaction.h" #include "texts.h" #include "icons/back.h" +#include "icons/battery.h" #include "icons/modes.h" #include "icons/presets.h" #include "icons/graph.h" @@ -44,6 +46,7 @@ class GarageDisplay; class UpdateDisplay; class PoweroffDisplay; class DebugMenu; +class BatteryMenu; } // namespace using namespace espgui; @@ -66,6 +69,7 @@ public: if (!simplified) { constructMenuItem, SwitchScreenAction, StaticMenuItemIcon<&icons::presets>>>(); } if (!simplified) { constructMenuItem, SwitchScreenAction>>(); } if (!simplified) { constructMenuItem, SwitchScreenAction, StaticMenuItemIcon<&icons::graph>>>(); } + if (!simplified) { constructMenuItem, SwitchScreenAction, StaticMenuItemIcon<&icons::battery>>>(); } #if defined(FEATURE_CAN) && defined(FEATURE_POWERSUPPLY) if (!simplified) { constructMenuItem, SwitchScreenAction>>(); } #endif diff --git a/main/displays/menus/selectbatterytypemenu.h b/main/displays/menus/selectbatterytypemenu.h new file mode 100644 index 0000000..a0033c8 --- /dev/null +++ b/main/displays/menus/selectbatterytypemenu.h @@ -0,0 +1,48 @@ +#pragma once + +// Local includes +#include "menudisplay.h" +#include "utils.h" +#include "menuitem.h" +#include "icons/back.h" +#include "texts.h" +#include "actions/switchscreenaction.h" +#include "batterymenu.h" +#include "battery.h" +#include "actioninterface.h" + +// Helper +class currentBatteryType : public virtual TextInterface { public: std::string text() const override { return getBatteryCellTypeString(); } }; + +using namespace espgui; + +namespace { + class BatteryTypeMenu; + class BatteryMenu; + + template + class BatterySelectTypeAction : public virtual ActionInterface + { + public: + void triggered() override { settings.battery.cellType = uint8_t(T); saveSettings(); } + }; +} // namespace + +namespace { + class BatteryTypeMenu : + public MenuDisplay, + public StaticText, + public BackActionInterface> + { + public: + BatteryTypeMenu() + { + constructMenuItem>(); + constructMenuItem, BatterySelectTypeAction>>(); + constructMenuItem, BatterySelectTypeAction>>(); + constructMenuItem, BatterySelectTypeAction>>(); + constructMenuItem, BatterySelectTypeAction>>(); + constructMenuItem, SwitchScreenAction, StaticMenuItemIcon<&espgui::icons::back>>>(); + } + }; +} // Namespace diff --git a/main/displays/metersdisplay.h b/main/displays/metersdisplay.h index fa8875a..92e621a 100644 --- a/main/displays/metersdisplay.h +++ b/main/displays/metersdisplay.h @@ -74,8 +74,8 @@ void MetersDisplay::redraw() tft.setTextFont(2); m_sumCurrentLabel.redraw(std::to_string(sumCurrent) + 'A'); - meters[0].redraw(fixBatVoltage(controllers.front.feedback.batVoltage), 35, 50); - meters[1].redraw(fixBatVoltage(controllers.back.feedback.batVoltage), 35, 50); + meters[0].redraw(controllers.front.getCalibratedVoltage(settings.battery.applyCalibration), 35, 50); + meters[1].redraw(controllers.back.getCalibratedVoltage(settings.battery.applyCalibration), 35, 50); meters[2].redraw(fixCurrent(controllers.front.feedback.left.dcLink), -10, 10); meters[3].redraw(fixCurrent(controllers.front.feedback.right.dcLink), -10, 10); meters[4].redraw(fixCurrent(controllers.back.feedback.left.dcLink), -10, 10); diff --git a/main/displays/statusdisplay.h b/main/displays/statusdisplay.h index d7ae2be..e788984 100644 --- a/main/displays/statusdisplay.h +++ b/main/displays/statusdisplay.h @@ -16,6 +16,7 @@ #include "widgets/label.h" #include "widgets/progressbar.h" #include "icons/alert.h" +#include "battery.h" // forward declares namespace { @@ -94,8 +95,12 @@ private: Label m_labelBrems{90, 15}; // 60, 15 ProgressBar m_progressBarBrems{150, 15, 90, 15, 0, 1000}; - BoardStatus m_frontStatus{42}; - BoardStatus m_backStatus{142}; + Label m_batterypercent{0, 30}; + Label m_watthoursleft{110, 30}; + Label m_kilometersleft{175, 30}; + + BoardStatus m_frontStatus{45}; + BoardStatus m_backStatus{145}; Label m_labelWifiStatus{35, bottomLines[0]}; // 120, 15 Label m_labelLimit0{205, bottomLines[0]}; // 35, 15 @@ -126,6 +131,10 @@ void StatusDisplay::initScreen() m_labelBrems.start(); m_progressBarBrems.start(); + m_batterypercent.start(); + m_watthoursleft.start(); + m_kilometersleft.start(); + m_frontStatus.start(); m_backStatus.start(); @@ -161,6 +170,10 @@ void StatusDisplay::redraw() m_labelBrems.redraw(brems ? fmt::format("{:.2f}", *brems) : "?"); m_progressBarBrems.redraw(brems ? *brems : 0); + m_batterypercent.redraw(getBatteryPercentageString()); + m_watthoursleft.redraw(getBatteryRemainingWattHoursString()); + m_kilometersleft.redraw(getRemainingRangeString()); + m_frontStatus.redraw(controllers.front); m_backStatus.redraw(controllers.back); @@ -290,7 +303,7 @@ void StatusDisplay::BoardStatus::redraw(const Controller &controller) if (controller.feedbackValid) { - m_labelVoltage.redraw(fmt::format("{:.2f}V", fixBatVoltage(controller.feedback.batVoltage))); + m_labelVoltage.redraw(fmt::format("{:.2f}V", controller.getCalibratedVoltage(settings.battery.applyCalibration))); m_labelTemperature.redraw(fmt::format("{:.2f}C", fixBoardTemp(controller.feedback.boardTemp))); m_leftMotor.redraw(controller.feedback.left); m_rightMotor.redraw(controller.feedback.right); diff --git a/main/globals.h b/main/globals.h index 8429f1c..457d6fb 100644 --- a/main/globals.h +++ b/main/globals.h @@ -60,13 +60,15 @@ public: #ifdef FEATURE_SERIAL Serial1, #endif - settings.controllerHardware.enableFrontLeft, settings.controllerHardware.enableFrontRight, settings.controllerHardware.invertFrontLeft, settings.controllerHardware.invertFrontRight + settings.controllerHardware.enableFrontLeft, settings.controllerHardware.enableFrontRight, settings.controllerHardware.invertFrontLeft, settings.controllerHardware.invertFrontRight, + settings.battery.front30VoltCalibration, settings.battery.front50VoltCalibration }, Controller { #ifdef FEATURE_SERIAL Serial2, #endif - settings.controllerHardware.enableBackLeft, settings.controllerHardware.enableBackRight, settings.controllerHardware.invertBackLeft, settings.controllerHardware.invertBackRight + settings.controllerHardware.enableBackLeft, settings.controllerHardware.enableBackRight, settings.controllerHardware.invertBackLeft, settings.controllerHardware.invertBackRight, + settings.battery.back30VoltCalibration, settings.battery.back50VoltCalibration } }} {} diff --git a/main/icons/battery.h b/main/icons/battery.h new file mode 100644 index 0000000..315c4ac --- /dev/null +++ b/main/icons/battery.h @@ -0,0 +1,46 @@ +#pragma once + +#include "icon.h" + +namespace { +namespace icons { +const Icon<24, 24> battery{{ +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 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, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, // 0x0030 (48) pixels +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, // 0x0040 (64) pixels +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, // 0x0050 (80) pixels +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, // 0x0060 (96) pixels +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, // 0x0070 (112) pixels +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x001F, 0x001F, 0x001F, 0x001F, 0x001F, 0x001F, 0x001F, // 0x0080 (128) pixels +0x001F, 0x001F, 0x001F, 0x001F, 0x001F, 0x001F, 0x001F, 0x001F, 0x001F, 0x001F, 0x001F, 0x001F, 0x001F, 0x0000, 0x0000, 0x0000, // 0x0090 (144) pixels +0x001F, 0x001F, 0x001F, 0x001F, 0x001F, 0x001F, 0x001F, 0x001F, 0x001F, 0x001F, 0x001F, 0x001F, 0x001F, 0x001F, 0x001F, 0x001F, // 0x00A0 (160) pixels +0x001F, 0x001F, 0x001F, 0x001F, 0x001F, 0x001F, 0x0000, 0x0000, 0x001F, 0x001F, 0x001F, 0x001F, 0x0000, 0x0000, 0x0000, 0x0000, // 0x00B0 (176) pixels +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x001F, 0x001F, 0x001F, 0x0000, 0x0000, // 0x00C0 (192) pixels +0x001F, 0x001F, 0x0000, 0x0000, 0x001F, 0x001F, 0x001F, 0x001F, 0x001F, 0x001F, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, // 0x00D0 (208) pixels +0x0000, 0x0000, 0x0000, 0x0000, 0x001F, 0x001F, 0x001F, 0x0000, 0x001F, 0x001F, 0x0000, 0x001F, 0x001F, 0x001F, 0x001F, 0x001F, // 0x00E0 (224) pixels +0x001F, 0x001F, 0x001F, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x001F, 0x001F, 0x001F, 0x001F, // 0x00F0 (240) pixels +0x001F, 0x001F, 0x0000, 0x001F, 0x001F, 0x001F, 0x001F, 0x001F, 0x001F, 0x001F, 0x001F, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, // 0x0100 (256) pixels +0x0000, 0x0000, 0x0000, 0x0000, 0x001F, 0x001F, 0x001F, 0x001F, 0x001F, 0x001F, 0x0000, 0x001F, 0x001F, 0x001F, 0x001F, 0x001F, // 0x0110 (272) pixels +0x001F, 0x001F, 0x001F, 0x001F, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x001F, 0x001F, 0x001F, 0x001F, // 0x0120 (288) pixels +0x001F, 0x001F, 0x0000, 0x001F, 0x001F, 0x001F, 0x001F, 0x001F, 0x001F, 0x001F, 0x001F, 0x001F, 0x0000, 0x0000, 0x0000, 0x0000, // 0x0130 (304) pixels +0x0000, 0x0000, 0x0000, 0x0000, 0x001F, 0x001F, 0x001F, 0x001F, 0x001F, 0x001F, 0x0000, 0x001F, 0x001F, 0x001F, 0x001F, 0x001F, // 0x0140 (320) pixels +0x001F, 0x001F, 0x001F, 0x001F, 0x001F, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x001F, 0x001F, 0x001F, 0x001F, // 0x0150 (336) pixels +0x001F, 0x001F, 0x0000, 0x001F, 0x001F, 0x001F, 0x001F, 0x001F, 0x001F, 0x001F, 0x001F, 0x001F, 0x001F, 0x0000, 0x0000, 0x0000, // 0x0160 (352) pixels +0x0000, 0x0000, 0x0000, 0x0000, 0x001F, 0x001F, 0x001F, 0x001F, 0x001F, 0x001F, 0x0000, 0x0000, 0x001F, 0x001F, 0x001F, 0x001F, // 0x0170 (368) pixels +0x001F, 0x001F, 0x001F, 0x001F, 0x001F, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x001F, 0x001F, 0x001F, 0x0000, // 0x0180 (384) pixels +0x001F, 0x001F, 0x001F, 0x001F, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, // 0x0190 (400) pixels +0x0000, 0x0000, 0x0000, 0x001F, 0x001F, 0x001F, 0x0000, 0x0000, 0x001F, 0x001F, 0x001F, 0x001F, 0x001F, 0x001F, 0x001F, 0x001F, // 0x01A0 (416) pixels +0x001F, 0x001F, 0x001F, 0x001F, 0x001F, 0x001F, 0x001F, 0x001F, 0x001F, 0x001F, 0x001F, 0x001F, 0x001F, 0x001F, 0x0000, 0x0000, // 0x01B0 (432) pixels +0x0000, 0x001F, 0x001F, 0x001F, 0x001F, 0x001F, 0x001F, 0x001F, 0x001F, 0x001F, 0x001F, 0x001F, 0x001F, 0x001F, 0x001F, 0x001F, // 0x01C0 (448) pixels +0x001F, 0x001F, 0x001F, 0x001F, 0x001F, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, // 0x01D0 (464) pixels +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, // 0x01E0 (480) pixels +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, // 0x01F0 (496) pixels +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, // 0x0200 (512) pixels +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, // 0x0210 (528) pixels +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, // 0x0220 (544) pixels +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, // 0x0230 (560) pixels +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, // 0x0240 (576) pixels +}}; +} +} diff --git a/main/ledstrip.h b/main/ledstrip.h index 6f2b430..cdaa50c 100644 --- a/main/ledstrip.h +++ b/main/ledstrip.h @@ -1,5 +1,4 @@ #pragma once -#define FEATURE_LEDSTRIP #ifdef FEATURE_LEDSTRIP // 3rdparty lib includes #include diff --git a/main/presets.h b/main/presets.h index d75cc85..eea3b68 100644 --- a/main/presets.h +++ b/main/presets.h @@ -253,6 +253,18 @@ constexpr Settings::LockscreenSettings defaultLockscreen { .pin = { 1, 2, 3, 4 } }; +constexpr Settings::Battery defaultBattery { + .cellsSeries = 12, + .cellsParallel = 4, + .cellType = 0, + .watthoursPerKilometer = 20, + .front30VoltCalibration = 30, + .back30VoltCalibration = 30, + .front50VoltCalibration = 50, + .back50VoltCalibration = 50, + .applyCalibration = true +}; + constexpr Settings defaultSettings { #ifdef FEATURE_BMS .autoConnectBms = false, @@ -278,6 +290,7 @@ constexpr Settings defaultSettings { #ifdef FEATURE_LEDSTRIP .ledstrip = defaultLedstrip, #endif + .battery = defaultBattery, .lockscreen = defaultLockscreen }; diff --git a/main/settings.h b/main/settings.h index 6d92256..1b88a38 100644 --- a/main/settings.h +++ b/main/settings.h @@ -170,6 +170,19 @@ struct Settings int16_t animationMultiplier; uint8_t brightness; } ledstrip; + + struct Battery { + uint8_t cellsSeries; + uint8_t cellsParallel; + uint8_t cellType; + uint16_t watthoursPerKilometer; + int16_t front30VoltCalibration; + int16_t back30VoltCalibration; + int16_t front50VoltCalibration; + int16_t back50VoltCalibration; + bool applyCalibration; + } battery; + #endif struct LockscreenSettings { @@ -275,10 +288,20 @@ void Settings::executeForEveryCommonSetting(T &&callable) callable("ledstvofoff", ledstrip.stvoFrontOffset); callable("ledstvoflen", ledstrip.stvoFrontLength); callable("ledstvoen", ledstrip.stvoFrontEnable); - callable("ledAnimMultiplier", ledstrip.animationMultiplier); + callable("ledAnimMul", ledstrip.animationMultiplier); callable("ledbrightness", ledstrip.brightness); #endif + callable("batteryCS", battery.cellsSeries); + callable("batteryCP", battery.cellsParallel); + callable("batteryType", battery.cellType); + callable("whkm", battery.watthoursPerKilometer); + callable("batF30VCal", battery.front30VoltCalibration); + callable("batB30VCal", battery.back30VoltCalibration); + callable("batF50VCal", battery.front50VoltCalibration); + callable("batB50VCal", battery.back50VoltCalibration); + callable("applyBatCal", battery.applyCalibration); + callable("lockAlwPresetSw", lockscreen.allowPresetSwitch); callable("lockscreenPin", lockscreen.pin); } diff --git a/main/statistics.h b/main/statistics.h index 662dfed..d03d891 100644 --- a/main/statistics.h +++ b/main/statistics.h @@ -30,13 +30,13 @@ void pushStats() statistics::sumCurrent.push_back(sumCurrent); if (controllers.front.feedbackValid) { - statistics::frontVoltage.push_back(fixBatVoltage(controllers.front.feedback.batVoltage)); + statistics::frontVoltage.push_back(controllers.front.getCalibratedVoltage(settings.battery.applyCalibration)); statistics::frontLeftCurrent.push_back(fixCurrent(controllers.front.feedback.left.dcLink)); statistics::frontRightCurrent.push_back(fixCurrent(controllers.front.feedback.right.dcLink)); } if (controllers.back.feedbackValid) { - statistics::backVoltage.push_back(fixBatVoltage(controllers.back.feedback.batVoltage)); + statistics::backVoltage.push_back(controllers.back.getCalibratedVoltage(settings.battery.applyCalibration)); statistics::backLeftCurrent.push_back(fixCurrent(controllers.back.feedback.left.dcLink)); statistics::backRightCurrent.push_back(fixCurrent(controllers.back.feedback.right.dcLink)); } diff --git a/main/texts.h b/main/texts.h index 383719c..a452db2 100644 --- a/main/texts.h +++ b/main/texts.h @@ -86,6 +86,29 @@ constexpr char TEXT_UPDATE[] = "Update"; constexpr char TEXT_POWEROFF[] = "Poweroff"; constexpr char TEXT_REBOOT[] = "Reboot"; constexpr char TEXT_DEBUG[] = "Debug"; +constexpr char TEXT_BATTERY[] = "Battery"; + +//BatteryMenu +constexpr char TEXT_CELL_SERIES[] = "Cells (Series)"; +constexpr char TEXT_CELL_PARALLEL[] = "Cells (Parallel)"; +constexpr char TEXT_SELECT_CELL_TYPE[] = "Select Cell Type"; +constexpr char TEXT_CELL_TYPE[] = "Cell Type"; +constexpr char TEXT_BATTERY_CALIBRATE[] = "Calibrate Voltages"; +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_WHKM[] = "Wh per km"; +constexpr char TEXT_BATTERY_APPLYCALIB[] = "Apply calibration"; +constexpr char TEXT_VOLTAGECALIBRATION_RESET[] = "Reset calibration"; + +//CalibrateVoltageDisplay +constexpr char TEXT_VOLTAGECALIBRATION_30V[] = "Calibrate 30.0V"; +constexpr char TEXT_VOLTAGECALIBRATION_50V[] = "Calibrate 50.0V"; +constexpr char TEXT_VOLTAGECALIBRATION_VALUE_30_FRONT[] = "30V Front"; +constexpr char TEXT_VOLTAGECALIBRATION_VALUE_30_BACK[] = "30V Back"; +constexpr char TEXT_VOLTAGECALIBRATION_VALUE_50_FRONT[] = "50V Front"; +constexpr char TEXT_VOLTAGECALIBRATION_VALUE_50_BACK[] = "50V Back"; //SettingsMenu //constexpr char TEXT_SETTINGS[] = "Settings"; diff --git a/main/utils.h b/main/utils.h index e032f4e..d65f413 100644 --- a/main/utils.h +++ b/main/utils.h @@ -59,11 +59,6 @@ float fixCurrent(int16_t value) return -value/50.; } -float fixBatVoltage(int16_t value) -{ - return value/100.; -} - float fixBoardTemp(int16_t value) { return value/10.;