Merge pull request #124 from bobbycar-graz/cloud

Updated master from cloud
This commit is contained in:
CommanderRedYT
2021-11-18 22:37:51 +01:00
committed by GitHub
67 changed files with 1261 additions and 99 deletions

View File

@ -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)

View File

@ -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")

View File

@ -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
)

View File

@ -91,4 +91,6 @@ set(BOBBYCAR_BUILDFLAGS
-DLEDSTRIP_DEFAULT_BRIGHTNESS=100
# -DLEDSTRIP_WRONG_DIRECTION
# -DLEDSTRIP_ANIMATION_DEFAULT=0
-DOLD_NVS
# -DFEATURE_DNS_NS
)

View File

@ -89,4 +89,6 @@ set(BOBBYCAR_BUILDFLAGS
# -DPINS_LEDSTRIP=26
# -DLEDSTRIP_WRONG_DIRECTION
# -DLEDSTRIP_ANIMATION_DEFAULT=0
-DOLD_NVS
# -DFEATURE_DNS_NS
)

View File

@ -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")

View File

@ -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"

View File

@ -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

View File

@ -171,6 +171,8 @@ struct LedsStVOFrontLengthAccessor : public RefAccessorSaveSettings<int16_t> { i
struct EnableLedstripStVOFrontlight : public RefAccessorSaveSettings<bool> { bool &getRef() const override { return settings.ledstrip.stvoFrontEnable; } };
struct AnimationMultiplierAccessor : public RefAccessorSaveSettings<int16_t> { int16_t &getRef() const override { return settings.ledstrip.animationMultiplier; } };
struct LedstripBrightnessAccessor : public RefAccessorSaveSettings<uint8_t> { uint8_t &getRef() const override { return settings.ledstrip.brightness; } };
struct LedstripEnableBlinkAnimation : public RefAccessorSaveSettings<bool> { bool &getRef() const override { return settings.ledstrip.enableAnimBlink; } };
struct LedstripOtaAnimationAccessor : public RefAccessorSaveSettings<OtaAnimationModes> { OtaAnimationModes &getRef() const override { return settings.ledstrip.otaMode; } };
#endif
// Battery

View File

@ -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);
}

View File

@ -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();

View File

@ -24,9 +24,8 @@ namespace {
static std::string url_for_latest = "";
static std::array<std::string, 10> availableVersions = {};
bool request_running = false;
uint16_t request_failed = false;
std::string request_failed;
bool parsing_finished = false;
static bool redownload = true;
cpputils::DelayedConstruction<AsyncHttpRequest> 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<std::string>()));
url_for_hashes = fix_url(fmt::format("{}{}", stringSettings.otaServerUrl, doc["url"].as<std::string>()));
url_for_latest = fmt::format("{}{}", stringSettings.otaServerUrl, doc["latest"].as<std::string>());
url_for_hashes = fmt::format("{}{}", stringSettings.otaServerUrl, doc["url"].as<std::string>());
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()

View File

@ -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<makeComponent<MenuItem, CurrentBatteryStatusText, DisabledColor, DummyAction>>();
constructMenuItem<makeComponent<MenuItem, EmptyText, DummyAction>>();
constructMenuItem<makeComponent<MenuItem, BatteryDebugText, DisabledColor, DummyAction>>();
constructMenuItem<makeComponent<MenuItem, BatteryDebug2Text, DisabledColor, DummyAction>>();
constructMenuItem<makeComponent<MenuItem, BatteryDebug3Text, DisabledColor, DummyAction>>();
constructMenuItem<makeComponent<MenuItem, CurrentAdvancedBatteryPercentageText, DisabledColor, DummyAction>>();
constructMenuItem<makeComponent<MenuItem, StaticText<TEXT_BACK>, SwitchScreenAction<DebugMenu>, StaticMenuItemIcon<&espgui::icons::back>>>();
}
void BatteryDebugMenu::back()
{
switchScreen<DebugMenu>();
}

View File

@ -0,0 +1,24 @@
#pragma once
// 3rdparty lib includes
#include <menudisplay.h>
#include <menuitem.h>
#include <icons/back.h>
#include <actions/dummyaction.h>
#include <actions/switchscreenaction.h>
// Local includes
#include "utils.h"
#include "icons/settings.h"
#include "texts.h"
#include "battery.h"
class BatteryDebugMenu :
public espgui::MenuDisplay,
public espgui::StaticText<TEXT_BATTERYDEBUG>
{
public:
BatteryDebugMenu();
void back() override;
};

View File

@ -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(); } };

View File

@ -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<makeComponent<MenuItem, StaticText<TEXT_BACKRIGHTFEEDBACK>, SwitchScreenAction<BackRightMotorFeedbackDebugMenu>, BackFeedbackColor<TFT_WHITE>>>();
constructMenuItem<makeComponent<MenuItem, EmptyText, DummyAction>>();
constructMenuItem<makeComponent<MenuItem, StaticText<TEXT_DYNAMICMENU>, SwitchScreenAction<DynamicDebugMenu>>>();
constructMenuItem<makeComponent<MenuItem, EmptyText, DummyAction>>();
constructMenuItem<makeComponent<MenuItem, StaticText<TEXT_BATTERYDEBUG>, SwitchScreenAction<BatteryDebugMenu>, StaticMenuItemIcon<&bobbyicons::battery>>>();
constructMenuItem<makeComponent<MenuItem, StaticText<TEXT_BACK>, SwitchScreenAction<MainMenu>, StaticMenuItemIcon<&espgui::icons::back>>>();
}

View File

@ -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<makeComponent<MenuItem, StaticText<TEXT_SELECTANIMATION>, SwitchScreenAction<LedstripSelectAnimationMenu>>>();
constructMenuItem<makeComponent<MenuItem, StaticText<TEXT_BLINKANIMATION>, SwitchScreenAction<LedstripSelectBlinkMenu>>>();
if (!simplified) { constructMenuItem<makeComponent<MenuItem, StaticText<TEXT_LEDSTRIP_CHANGE_OTA_ANIM>, SwitchScreenAction<ledstripOtaAnimationChangeMenu>>>(); }
constructMenuItem<makeComponent<MenuItem, StaticText<TEXT_ANIMATION_MULTIPLIER>, SwitchScreenAction<animationMultiplierChangeScreen>>>();
if (!simplified) { constructMenuItem<makeComponent<MenuItem, TextWithValueHelper<TEXT_LEDSCOUNT, LedsCountAccessor>, SwitchScreenAction<LedsCountChangeScreen>>>(); }
if (!simplified) { constructMenuItem<makeComponent<MenuItem, TextWithValueHelper<TEXT_CENTEROFFSET, CenterOffsetAccessor>, SwitchScreenAction<CenterOffsetChangeScreen>>>(); }

View File

@ -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<makeComponent<MenuItem, StaticText<TEXT_ANIMATION_BLINKLEFT>, LedstripAnimationBlinkLeftAction>>();
constructMenuItem<makeComponent<MenuItem, StaticText<TEXT_ANIMATION_BLINKRIGHT>, LedstripAnimationBlinkRightAction>>();
constructMenuItem<makeComponent<MenuItem, StaticText<TEXT_ANIMATION_BLINKBOTH>, LedstripAnimationBlinkBothAction>>();
constructMenuItem<makeComponent<MenuItem, StaticText<TEXT_LEDSTRIP_EN_BLINK_ANIM>, ToggleBoolAction, CheckboxIcon, LedstripEnableBlinkAnimation>>();
constructMenuItem<makeComponent<MenuItem, StaticText<TEXT_BACK>, SwitchScreenAction<LedstripMenu>, StaticMenuItemIcon<&espgui::icons::back>>>();
}
};

View File

@ -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 <OtaAnimationModes mode>
class LedstripChangeOtaAnimModeAction : public virtual ActionInterface
{
public:
void triggered() override
{
settings.ledstrip.otaMode = mode;
saveSettings();
}
};
namespace {
class ledstripOtaAnimationChangeMenu :
public MenuDisplay,
public StaticText<TEXT_BLINKANIMATION>,
public BackActionInterface<SwitchScreenAction<LedstripMenu>>
{
public:
ledstripOtaAnimationChangeMenu()
{
constructMenuItem<makeComponent<MenuItem, StaticText<TEXT_OTAANIM_NONE>, LedstripChangeOtaAnimModeAction<OtaAnimationModes::None>>>();
constructMenuItem<makeComponent<MenuItem, StaticText<TEXT_OTAANIM_PROGRESS>, LedstripChangeOtaAnimModeAction<OtaAnimationModes::GreenProgressBar>>>();
constructMenuItem<makeComponent<MenuItem, StaticText<TEXT_OTAANIM_COLOR>, LedstripChangeOtaAnimModeAction<OtaAnimationModes::ColorChangeAll>>>();
constructMenuItem<makeComponent<MenuItem, StaticText<TEXT_BACK>, SwitchScreenAction<LedstripMenu>, StaticMenuItemIcon<&espgui::icons::back>>>();
}
};
} // Namespace
#endif

View File

@ -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<makeComponent<MenuItem, StaticText<TEXT_STATUS>, SwitchScreenAction<StatusDisplay>, StaticMenuItemIcon<&espgui::icons::back>>>();
constructMenuItem<makeComponent<MenuItem, StaticText<TEXT_SELECTMODE>, SwitchScreenAction<SelectModeMenu>, StaticMenuItemIcon<&bobbyicons::modes>>>();
#ifdef FEATURE_LEDSTRIP
constructMenuItem<makeComponent<MenuItem, StaticText<TEXT_LEDSTRIP>, SwitchScreenAction<LedstripMenu>, StaticMenuItemIcon<&bobbyicons::neopixel>>>();
#endif
constructMenuItem<makeComponent<MenuItem, StaticText<TEXT_STATISTICSMENU>, SwitchScreenAction<StatisticsMenu>, StaticMenuItemIcon<&bobbyicons::statistics>>>();
constructMenuItem<makeComponent<MenuItem, StaticText<TEXT_SELECTMODE>, SwitchScreenAction<SelectModeMenu>, StaticMenuItemIcon<&bobbyicons::modes>>>();
if (SHOWITEM) { constructMenuItem<makeComponent<MenuItem, StaticText<TEXT_MODESETTINGS>, ModeSettingsAction>>(); }
if (SHOWITEM) { constructMenuItem<makeComponent<MenuItem, StaticText<TEXT_PRESETS>, SwitchScreenAction<PresetsMenu>, StaticMenuItemIcon<&bobbyicons::presets>>>(); }
if (SHOWITEM) { constructMenuItem<makeComponent<MenuItem, StaticText<TEXT_PROFILES>, SwitchScreenAction<ProfilesMenu>>>(); }

View File

@ -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<makeComponent<MenuItem, StaticText<TEXT_SELECTBUILD>, SwitchScreenAction<SelectBuildMenu>, StaticMenuItemIcon<&bobbyicons::presets>>>();
constructMenuItem<makeComponent<MenuItem, StaticText<TEXT_UPDATENOW>, SwitchScreenAction<UpdateDisplay>, StaticMenuItemIcon<&bobbyicons::update>>>();
constructMenuItem<makeComponent<MenuItem, StaticText<TEXT_SELECTBUILDSERVERMENU>, SwitchScreenAction<SelectBuildServerMenu>>>();
constructMenuItem<makeComponent<MenuItem, StaticText<TEXT_REDOWNLOAD>, RedownloadJsonAction>>();
constructMenuItem<makeComponent<MenuItem, StaticText<TEXT_BACK>, SwitchScreenAction<MainMenu>, StaticMenuItemIcon<&espgui::icons::back>>>();
}

View File

@ -26,6 +26,7 @@ BatteryTypeMenu::BatteryTypeMenu()
constructMenuItem<makeComponent<MenuItem, StaticText<TEXT_BATTERY_TYPE_HG2>, BatterySelectTypeAction<BatteryCellType::HG2>>>();
constructMenuItem<makeComponent<MenuItem, StaticText<TEXT_BATTERY_TYPE_MH1>, BatterySelectTypeAction<BatteryCellType::MH1>>>();
constructMenuItem<makeComponent<MenuItem, StaticText<TEXT_BATTERY_TYPE_VTC5>, BatterySelectTypeAction<BatteryCellType::VTC5>>>();
constructMenuItem<makeComponent<MenuItem, StaticText<TEXT_BATTERY_TYPE_BAK_25R>, BatterySelectTypeAction<BatteryCellType::BAK_25R>>>();
constructMenuItem<makeComponent<MenuItem, StaticText<TEXT_BACK>, SwitchScreenAction<MainMenu>, StaticMenuItemIcon<&espgui::icons::back>>>();
}

View File

@ -29,7 +29,6 @@ public:
stringSettings.otaUrl = m_buildserver_url;
}
saveSettings();
redownload = true;
url_for_latest.clear();
url_for_hashes.clear();
availableVersions = {};

View File

@ -12,6 +12,7 @@
#include "buildserver.h"
#ifdef FEATURE_OTA
class SelectBuildServerMenu :
public espgui::MenuDisplay,
public espgui::StaticText<TEXT_SELECTBUILDSERVERMENU>

View File

@ -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<makeComponent<MenuItem, StaticText<TEXT_BACK>, SwitchScreenAction<OtaMenu>, 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 = {};
}
}

View File

@ -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<makeComponent<MenuItem, WhPerKmText, DummyAction>>();
constructMenuItem<makeComponent<MenuItem, UptimeText, DummyAction, StaticMenuItemIcon<&bobbyicons::time>>>();
constructMenuItem<makeComponent<MenuItem, CurrentKilometersText, DummyAction>>();
constructMenuItem<makeComponent<MenuItem, CurrentDrivingTimeText, DummyAction>>();
constructMenuItem<makeComponent<MenuItem, TotalKilometersText, DummyAction>>();
constructMenuItem<makeComponent<MenuItem, TotalMetersText, DummyAction>>();
// constructMenuItem<makeComponent<MenuItem, SavedTotalCentimetersText, DummyAction>>();
constructMenuItem<makeComponent<MenuItem, CurrentWhUsedText, DummyAction>>();
constructMenuItem<makeComponent<MenuItem, AverageWhUsedText, DummyAction>>();
constructMenuItem<makeComponent<MenuItem, EfficiencyText, EfficiencyTextColor, DummyAction>>();
constructMenuItem<makeComponent<MenuItem, EmptyText, DummyAction>>();
constructMenuItem<makeComponent<MenuItem, StaticText<TEXT_STATSSAVE>, SaveKilometersAction, StaticMenuItemIcon<&bobbyicons::update>>>();
constructMenuItem<makeComponent<MenuItem, StaticText<TEXT_STATSCLEAR>, ClearCurrentStatsAction, StaticMenuItemIcon<&bobbyicons::reboot>>>();
constructMenuItem<makeComponent<MenuItem, StaticText<TEXT_BACK>, SwitchScreenAction<MainMenu>, StaticMenuItemIcon<&espgui::icons::back>>>();
}
void StatisticsMenu::back()
{
switchScreen<MainMenu>();
}

View File

@ -0,0 +1,22 @@
#pragma once
// 3rdparty lib includes
#include <menudisplay.h>
#include <menuitem.h>
#include <icons/back.h>
#include <actions/dummyaction.h>
#include <actions/switchscreenaction.h>
// Local includes
#include "utils.h"
#include "texts.h"
class StatisticsMenu :
public espgui::MenuDisplay,
public espgui::StaticText<TEXT_STATISTICSMENU>
{
public:
StatisticsMenu();
void back() override;
};

View File

@ -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<makeComponent<MenuItem, StaticText<TEXT_GENERICWIFISETTINGS>, SwitchScreenAction<GenericWifiSettingsMenu>>>();
constructMenuItem<makeComponent<MenuItem, StaticText<TEXT_STATIONWIFISETTINGS>, SwitchScreenAction<StationWifiSettingsMenu>>>();
constructMenuItem<makeComponent<MenuItem, StaticText<TEXT_ACCESSPOINTWIFISETTINGS>, SwitchScreenAction<AccessPointWifiSettingsMenu>>>();
#ifdef FEATURE_DNS_NS
constructMenuItem<makeComponent<MenuItem, StaticText<TEXT_RESEND_DNS>, ResendDNSRequest>>();
#endif
constructMenuItem<makeComponent<MenuItem, StaticText<TEXT_BACK>, SwitchScreenAction<SettingsMenu>, StaticMenuItemIcon<&espgui::icons::back>>>();
}

View File

@ -1,8 +1,5 @@
#include "statusdisplay.h"
// esp-idf includes
#include <esp_log.h>
// 3rdparty lib includes
#include <fmt/core.h>
#include <espwifistack.h>
@ -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<MainMenu>();
}

View File

@ -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};

101
main/dnsannounce.cpp Normal file
View File

@ -0,0 +1,101 @@
// 3rd party
#ifdef FEATURE_DNS_NS
#include <randomutils.h>
#include <esprandom.h>
#include <FastLED.h>
// 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<uint16_t>(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

2
main/dnsannounce.h Normal file
View File

@ -0,0 +1,2 @@
#pragma once
void handle_dns_announce();

163
main/drivingstatistics.cpp Normal file
View File

@ -0,0 +1,163 @@
#include "drivingstatistics.h"
// 3rd party
#include <FastLED.h>
#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();
}
}
}

7
main/drivingstatistics.h Normal file
View File

@ -0,0 +1,7 @@
#pragma once
#include <string>
void calculateStatistics();
float getAvgWhPerKm();
std::string getEfficiencyClassString();
uint16_t getEfficiencyClassColor();

View File

@ -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;

View File

@ -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;

44
main/icons/statistics.cpp Normal file
View File

@ -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

7
main/icons/statistics.h Normal file
View File

@ -0,0 +1,7 @@
#pragma once
#include "icon.h"
namespace bobbyicons {
extern const espgui::Icon<24, 24> statistics;
} // namespace bobbyicons

View File

@ -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);

View File

@ -7,6 +7,7 @@
#include <FastLED.h>
#ifdef FEATURE_LEDSTRIP
#define crgb_iterator __gnu_cxx::__normal_iterator<CRGB *, std::vector<CRGB>>
enum Bobbycar_Side
{
FRONT_RIGHT,
@ -19,6 +20,13 @@ enum Bobbycar_Side
FRONT
};
enum OtaAnimationModes
{
None,
GreenProgressBar,
ColorChangeAll
};
extern std::vector<CRGB> leds;
extern uint8_t gHue;
@ -30,8 +38,8 @@ void showAnimation();
void showBetterRainbow();
void showSpeedSyncAnimation();
void showCustomColor();
void showOtaAnimation();
void initLedStrip();
void updateLedStrip();
#endif

View File

@ -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

View File

@ -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<espchrono::millis_clock::time_point> lastWifiUpdate;
@ -434,5 +438,9 @@ extern "C" void app_main()
lastLedstripUpdate = now;
}
#endif
#ifdef FEATURE_DNS_NS
handle_dns_announce();
#endif
calculateStatistics();
}
}

View File

@ -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();

View File

@ -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<int8_t, 4> pin;
} lockscreen;
struct SavedStatistics {
uint32_t totalCentimeters;
} savedStatistics;
template<typename T>
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<typename T>

View File

@ -189,6 +189,16 @@ template<> struct nvsGetterHelper<UnifiedModelMode> { static esp_err_t nvs_get(n
*out_value = UnifiedModelMode(tempValue);
return err;
}};
#if defined(FEATURE_LEDSTRIP) && defined(FEATURE_OTA)
template<> struct nvsGetterHelper<OtaAnimationModes> { 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<wifi_mode_t> { 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<UnifiedModelMode> { 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<OtaAnimationModes> { 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<wifi_mode_t> { 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));

View File

@ -44,6 +44,9 @@ struct StringSettings
std::array<ConfiguredOtaServer, 5> otaServers;
std::string otaServerUrl;
#endif
#ifdef FEATURE_DNS_NS
std::string dns_key;
#endif
};
template<typename T>
@ -108,6 +111,9 @@ void StringSettings::executeForEveryCommonSetting(T &&callable)
callable("otaserver", otaServerUrl);
#endif
#ifdef FEATURE_DNS_NS
callable("dnskey", dns_key);
#endif
}
template<typename T>

View File

@ -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

View File

@ -1,4 +1,5 @@
#include "utils.h"
#include "globals.h"
using namespace std::chrono_literals;
@ -306,3 +307,68 @@ void readPotis()
gametrakDist = cpputils::mapValueClamped<float>(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<long, std::ratio<1,1000>> repeat, std::chrono::duration<long, std::ratio<1,1000>> riseTime, std::chrono::duration<long, std::ratio<1,1000>> 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;
}

View File

@ -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<long, std::ratio<1,1000>> repeat, std::chrono::duration<long, std::ratio<1,1000>> riseTime, std::chrono::duration<long, std::ratio<1,1000>> fullTime, size_t numLeds, bool invert);

View File

@ -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);

View File

@ -77,7 +77,8 @@ esp_err_t webserver_root_handler(httpd_req_t *req)
#endif
"<a href=\"/settings\">Settings</a> - "
"<a href=\"/stringSettings\">String Settings</a>";
"<a href=\"/stringSettings\">String Settings</a> - "
"<a href=\"/dumpnvs\">Dump NVS</a>";
}
{

View File

199
main/webserver_dumpnvs.h Normal file
View File

@ -0,0 +1,199 @@
#pragma once
// esp-idf includes
#ifdef FEATURE_WEBSERVER
#include <esp_http_server.h>
#endif
#include <esp_log.h>
// 3rdparty lib includes
#include <htmlbuilder.h>
#include <fmt/core.h>
#include <espcppmacros.h>
#include <esphttpdutils.h>
#include <espchrono.h>
#include <lockhelper.h>
#include <tickchrono.h>
#include <ArduinoJson.h>
// 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 T>
typename std::enable_if<
!std::is_same<T, bool>::value &&
!std::is_integral<T>::value &&
!std::is_same<T, std::array<int8_t, 4>>::value &&
!std::is_same<T, std::string>::value &&
!std::is_same<T, espchrono::minutes32>::value &&
!std::is_same<T, espchrono::DayLightSavingMode>::value &&
!std::is_same<T, UnifiedModelMode>::value
#if defined(FEATURE_LEDSTRIP) && defined(FEATURE_OTA)
&& !std::is_same<T, OtaAnimationModes>::value
#endif
, bool>::type
showInputForSetting(std::string_view key, T value, JsonObject &body)
{
body[key] = nullptr;
return false;
}
template<typename T>
typename std::enable_if<
std::is_same<T, bool>::value
, bool>::type
showInputForSetting(std::string_view key, T value, JsonObject &body)
{
body[key] = value;
return true;
}
template<typename T>
typename std::enable_if<
!std::is_same<T, bool>::value &&
std::is_integral<T>::value
, bool>::type
showInputForSetting(std::string_view key, T value, JsonObject &body)
{
body[key] = value;
return true;
}
template<typename T>
typename std::enable_if<
std::is_same<T, std::array<int8_t, 4>>::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 T>
typename std::enable_if<
std::is_same<T, std::string>::value
, bool>::type
showInputForSetting(std::string_view key, T value, JsonObject &body)
{
body[key] = value;
return true;
}
template<typename T>
typename std::enable_if<
std::is_same<T, espchrono::minutes32>::value
, bool>::type
showInputForSetting(std::string_view key, T value, JsonObject &body)
{
body[key] = value.count();
return true;
}
template<typename T>
typename std::enable_if<
std::is_same<T, espchrono::DayLightSavingMode>::value
, bool>::type
showInputForSetting(std::string_view key, T value, JsonObject &body)
{
body[key] = toString(espchrono::DayLightSavingMode(value));
return true;
}
template<typename T>
typename std::enable_if<
std::is_same<T, UnifiedModelMode>::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 T>
typename std::enable_if<
std::is_same<T, OtaAnimationModes>::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<espcpputils::ticks>(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

View File

@ -70,7 +70,8 @@ esp_err_t webserver_ota_handler(httpd_req_t *req)
body += "<a href=\"/\">Display control</a> - "
"<b>Update</b> - "
"<a href=\"/settings\">Settings</a> - "
"<a href=\"/stringSettings\">String Settings</a>";
"<a href=\"/stringSettings\">String Settings</a> - "
"<a href=\"/dumpnvs\">Dump NVS</a>";
}
if (const esp_app_desc_t *app_desc = esp_ota_get_app_description())

View File

@ -145,7 +145,8 @@ esp_err_t webserver_settings_handler(httpd_req_t *req)
"<a href=\"/ota\">Update</a> - "
#endif
"<b>Settings</b> - "
"<a href=\"/stringSettings\">String Settings</a>";
"<a href=\"/stringSettings\">String Settings</a> - "
"<a href=\"/dumpnvs\">Dump NVS</a>";
}
HtmlTag divTag{"div", "class=\"form-table\"", body};

View File

@ -84,7 +84,8 @@ esp_err_t webserver_stringSettings_handler(httpd_req_t *req)
"<a href=\"/ota\">Update</a> - "
#endif
"<a href=\"/settings\">Settings</a> - "
"<b>String Settings</b>";
"<b>String Settings</b> - "
"<a href=\"/dumpnvs\">Dump NVS</a>";
}
HtmlTag divTag{"div", "class=\"form-table\"", body};

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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 &