Merge pull request #75 from bobbycar-graz/74-multiple-profiles

Merge "#74 Implement multiple config profiles"
This commit is contained in:
2020-09-20 05:00:47 +02:00
committed by GitHub
21 changed files with 552 additions and 64 deletions

View File

@ -16,6 +16,7 @@ framework = arduino
lib_deps =
TFT_eSPI
https://github.com/Ferdi265/cxx-ring-buffer
https://github.com/TartanLlama/optional
lib_compat_mode = strict
build_unflags =
@ -42,6 +43,13 @@ build_flags =
-DDEFAULT_FIELDWEAKMAX=5
-DDEFAULT_FIELDADVMAX=40
[default_wheels_inverted]
build_flags =
-DDEFAULT_INVERTFRONTLEFT=false
-DDEFAULT_INVERTFRONTRIGHT=true
-DDEFAULT_INVERTBACKLEFT=false
-DDEFAULT_INVERTBACKRIGHT=true
[peters_platine_common]
build_flags =
-DILI9341_DRIVER=1
@ -107,6 +115,10 @@ build_flags =
-DPINS_TX1=5
-DPINS_RX2=22
-DPINS_TX2=23
-DDEFAULT_INVERTFRONTLEFT=false
-DDEFAULT_INVERTFRONTRIGHT=true
-DDEFAULT_INVERTBACKLEFT=true
-DDEFAULT_INVERTBACKRIGHT=false
; -DFEATURE_MOSFETS
; -DPINS_MOSFET0=18
; -DPINS_MOSFET1=19
@ -116,10 +128,21 @@ build_flags =
-DDEVICE_PREFIX=bobbyquad
-DAP_PASSWORD=Passwort_123
-DFEATURE_WEBSERVER
; -DFEATURE_DPAD_3WIRESW
; -DPINS_DPAD_3WIRESW_OUT=0
; -DPINS_DPAD_3WIRESW_IN1=16
; -DPINS_DPAD_3WIRESW_IN2=27
-DFEATURE_DPAD_5WIRESW
-DPINS_DPAD_5WIRESW_OUT=32
-DPINS_DPAD_5WIRESW_IN1=25
-DPINS_DPAD_5WIRESW_IN2=26
-DPINS_DPAD_5WIRESW_IN3=27
-DPINS_DPAD_5WIRESW_IN4=21
-DDPAD_5WIRESW_UP=4
-DDPAD_5WIRESW_DOWN=3
-DDPAD_5WIRESW_CONFIRM=7
-DDPAD_5WIRESW_BACK=0
-DDPAD_5WIRESW_PROFILE0=1
-DDPAD_5WIRESW_PROFILE1=5
-DDPAD_5WIRESW_PROFILE2=2
-DDPAD_5WIRESW_PROFILE3=6
; -DDPAD_5WIRESW_DEBUG
-DDEFAULT_GASMIN=850
-DDEFAULT_GASMAX=3700
-DDEFAULT_BREMSMIN=1300
@ -177,6 +200,7 @@ build_flags =
${peters_platine.build_flags}
${ota_common.build_flags}
${default_limits.build_flags}
${default_wheels_inverted.build_flags}
-DDEVICE_PREFIX=bobbycar
-DAP_PASSWORD=Passwort_123
-DFEATURE_WEBSERVER
@ -235,6 +259,7 @@ build_flags =
${peters_platine.build_flags}
${ota_common.build_flags}
${default_limits.build_flags}
${default_wheels_inverted.build_flags}
-DDEVICE_PREFIX=bobbyquad
-DAP_PASSWORD=Passwort_123
-DFEATURE_WEBSERVER
@ -283,6 +308,7 @@ build_flags =
-DSPI_FREQUENCY=20000000
-DSPI_TOUCH_FREQUENCY=2500000
-DDEFAULT_SWAPSCREENBYTES=false
${default_wheels_inverted.build_flags}
; TODO: actually assign pins
-DPINS_RX1=22
-DPINS_TX1=25
@ -310,6 +336,7 @@ build_flags =
${peters_platine_reversed.build_flags}
${ota_common.build_flags}
${default_limits.build_flags}
${default_wheels_inverted.build_flags}
-DDEVICE_PREFIX=bobbycar
-DAP_PASSWORD=Passwort_123
-DFEATURE_WEBSERVER

View File

@ -0,0 +1,41 @@
#pragma once
#include <HardwareSerial.h>
#include "actioninterface.h"
#include "globals.h"
#include "presets.h"
namespace {
class EraseNvsAction : public virtual ActionInterface
{
public:
void triggered() override
{
const auto profile = settingsPersister.currentlyOpenProfileIndex();
if (!settingsPersister.erase())
{
Serial.println("EraseNvsAction::triggered() erase failed");
return;
}
settings = presets::defaultSettings;
if (!profile)
return;
if (!settingsPersister.openProfile(*profile))
{
Serial.println("EraseNvsAction::triggered() openProfile failed");
return;
}
if (!settingsPersister.load(settings))
{
Serial.println("EraseNvsAction::triggered() load failed");
return;
}
}
};
}

View File

@ -0,0 +1,27 @@
#pragma once
#include <HardwareSerial.h>
#include "actioninterface.h"
#include "globals.h"
#include "presets.h"
namespace {
template<uint8_t profile>
class SwitchProfileAction : public virtual ActionInterface
{
public:
void triggered() override
{
settings = presets::defaultSettings;
if (settingsPersister.openProfile(profile))
{
if (!settingsPersister.load(settings))
Serial.println("SwitchProfileAction::triggered() load failed");
}
else
Serial.println("SwitchProfileAction::triggered() openProfile failed");
}
};
}

View File

@ -0,0 +1,54 @@
#pragma once
#include "display.h"
#include "globals.h"
#include "widgets/label.h"
#include "dpad5wire.h"
namespace {
#ifdef FEATURE_DPAD_5WIRESW
class DPad5WireDebugDisplay : public Display, public virtual DummyConfirm, public virtual DummyBack
{
public:
void initScreen() override;
void redraw() override;
private:
Label m_label0{30, 100};
Label m_label1{30, 125};
Label m_label2{30, 150};
};
void DPad5WireDebugDisplay::initScreen()
{
tft.fillScreen(TFT_BLACK);
tft.setTextFont(4);
tft.setTextColor(TFT_YELLOW);
tft.drawString("DPad 5wire debug", 5, 5);
tft.fillRect(0, 34, tft.width(), 3, TFT_WHITE);
tft.setTextColor(TFT_WHITE, TFT_BLACK);
m_label0.start();
m_label1.start();
m_label2.start();
}
void DPad5WireDebugDisplay::redraw()
{
m_label0.redraw(String{} +
(std::get<0>(dpad5wire::lastState) ? '1' : '0') + ' ' +
(std::get<1>(dpad5wire::lastState) ? '1' : '0') + ' ' +
(std::get<2>(dpad5wire::lastState) ? '1' : '0') + ' ' +
(std::get<3>(dpad5wire::lastState) ? '1' : '0') + ' ' +
(std::get<4>(dpad5wire::lastState) ? '1' : '0') + ' ' +
(std::get<5>(dpad5wire::lastState) ? '1' : '0') + ' ' +
(std::get<6>(dpad5wire::lastState) ? '1' : '0') + ' ' +
(std::get<7>(dpad5wire::lastState) ? '1' : '0'));
m_label1.redraw(String{raw_gas});
m_label2.redraw(String{raw_brems});
}
#endif
}

View File

@ -66,7 +66,7 @@ using BremsMaxChangeScreen = makeComponent<
SwitchScreenAction<BoardcomputerHardwareSettingsMenu>
>;
#if defined(FEATURE_DPAD) || defined(FEATURE_DPAD_3WIRESW)
#if defined(FEATURE_DPAD) || defined(FEATURE_DPAD_3WIRESW) || defined(FEATURE_DPAD_5WIRESW)
using DPadDebounceChangeScreen = makeComponent<
ChangeValueDisplay<uint8_t>,
StaticText<TEXT_SETDPADDEBOUNCE>,
@ -147,7 +147,7 @@ class BoardcomputerHardwareSettingsMenu :
makeComponent<MenuItem, StaticText<TEXT_SETGASMAX>, SwitchScreenAction<GasMaxChangeScreen>>,
makeComponent<MenuItem, StaticText<TEXT_SETBREMSMIN>, SwitchScreenAction<BremsMinChangeScreen>>,
makeComponent<MenuItem, StaticText<TEXT_SETBREMSMAX>, SwitchScreenAction<BremsMaxChangeScreen>>,
#if defined(FEATURE_DPAD) || defined(FEATURE_DPAD_3WIRESW)
#if defined(FEATURE_DPAD) || defined(FEATURE_DPAD_3WIRESW) || defined(FEATURE_DPAD_5WIRESW)
makeComponent<MenuItem, StaticText<TEXT_SETDPADDEBOUNCE>, SwitchScreenAction<DPadDebounceChangeScreen>>,
#endif
#ifdef FEATURE_GAMETRAK

View File

@ -6,6 +6,7 @@
#include "menuitem.h"
#include "actions/loadsettingsaction.h"
#include "actions/savesettingsaction.h"
#include "actions/erasenvsaction.h"
#include "actions/switchscreenaction.h"
#include "actions/dummyaction.h"
#include "actions/toggleboolaction.h"
@ -40,6 +41,7 @@ class DebugMenu :
public StaticMenuDefinition<
makeComponent<MenuItem, StaticText<TEXT_LOADSETTINGS>, LoadSettingsAction>,
makeComponent<MenuItem, StaticText<TEXT_SAVESETTINGS>, SaveSettingsAction>,
makeComponent<MenuItem, StaticText<TEXT_ERASENVS>, EraseNvsAction>,
makeComponent<MenuItem, StaticText<nullptr>, DummyAction>,
makeComponent<MenuItem, StaticText<TEXT_FRONTCOMMAND>, SwitchScreenAction<FrontCommandDebugMenu>>,
makeComponent<MenuItem, StaticText<TEXT_BACKCOMMAND>, SwitchScreenAction<BackCommandDebugMenu>>,

View File

@ -21,6 +21,7 @@
namespace {
class StatusDisplay;
class SelectModeMenu;
class ProfilesMenu;
class PresetsMenu;
class GraphsMenu;
class BmsMenu;
@ -42,6 +43,7 @@ class MainMenu :
makeComponent<MenuItem, StaticText<TEXT_SELECTMODE>, SwitchScreenAction<SelectModeMenu>, StaticMenuItemIcon<&icons::modes>>,
makeComponent<MenuItem, StaticText<TEXT_MODESETTINGS>, ModeSettingsAction>,
makeComponent<MenuItem, StaticText<TEXT_PRESETS>, SwitchScreenAction<PresetsMenu>, StaticMenuItemIcon<&icons::presets>>,
makeComponent<MenuItem, StaticText<TEXT_PROFILES>, SwitchScreenAction<ProfilesMenu>>,
makeComponent<MenuItem, StaticText<TEXT_GRAPHS>, SwitchScreenAction<GraphsMenu>, StaticMenuItemIcon<&icons::graph>>,
#ifdef FEATURE_BMS
makeComponent<MenuItem, StaticText<TEXT_BMS>, SwitchScreenAction<BmsMenu>, StaticMenuItemIcon<&icons::bms>>,

View File

@ -4,7 +4,7 @@
#include "menudisplay.h"
#include "staticmenudefinition.h"
#include "utils.h"
#include "actions/dummyaction.h"
#include "actions/multiaction.h"
#include "actions/switchscreenaction.h"
#include "icons/back.h"
#include "texts.h"
@ -77,17 +77,17 @@ class PresetsMenu :
public StaticText<TEXT_PRESETS>,
public BackActionInterface<SwitchScreenAction<MainMenu>>,
public StaticMenuDefinition<
makeComponent<MenuItem, StaticText<TEXT_DEFAULTEVERYTHING>, ApplySettingsPresetAction<&presets::defaultSettings>>,
makeComponent<MenuItem, StaticText<TEXT_DEFAULTLIMITS>, ApplyLimitsPresetAction<&presets::defaultLimits>>,
makeComponent<MenuItem, StaticText<TEXT_KIDSLIMITS>, ApplyLimitsPresetAction<&presets::kidsLimits>>,
makeComponent<MenuItem, StaticText<TEXT_DEFAULTCONTROLLERHARDWARE>, ApplyControllerHardwarePresetAction<&presets::defaultControllerHardware>>,
makeComponent<MenuItem, StaticText<TEXT_MOSFETSOFFCONTROLLERHARDWARE>, ApplyControllerHardwarePresetAction<&presets::mosfetsOffControllerHardware>>,
makeComponent<MenuItem, StaticText<TEXT_SPINNERCONTROLLERHARDWARE>, ApplyControllerHardwarePresetAction<&presets::spinnerControllerHardware>>,
makeComponent<MenuItem, StaticText<TEXT_DEFAULTBOARDCOMPUTERHARDWARE>, ApplyBoardcomputerHardwarePresetAction<&presets::defaultBoardcomputerHardware>>,
makeComponent<MenuItem, StaticText<TEXT_DEFAULTDEFAULTMODE>, ApplyDefaultModePresetAction<&presets::defaultDefaultMode>>,
makeComponent<MenuItem, StaticText<TEXT_SINUSOIDALDEFAULTMODE>, ApplyDefaultModePresetAction<&presets::sinusoidalDefaultMode>>,
makeComponent<MenuItem, StaticText<TEXT_DEFAULTTEMPOMATMODE>, ApplyTempomatModePresetAction<&presets::defaultTempomatMode>>,
makeComponent<MenuItem, StaticText<TEXT_DEFAULTLARSMMODE>, ApplyLarsmModePresetAction<&presets::defaultLarsmMode>>,
makeComponent<MenuItem, StaticText<TEXT_DEFAULTEVERYTHING>, MultiAction<ApplySettingsPresetAction<&presets::defaultSettings>, SwitchScreenAction<MainMenu>>>,
makeComponent<MenuItem, StaticText<TEXT_DEFAULTLIMITS>, MultiAction<ApplyLimitsPresetAction<&presets::defaultLimits>, SwitchScreenAction<MainMenu>>>,
makeComponent<MenuItem, StaticText<TEXT_KIDSLIMITS>, MultiAction<ApplyLimitsPresetAction<&presets::kidsLimits>, SwitchScreenAction<MainMenu>>>,
makeComponent<MenuItem, StaticText<TEXT_DEFAULTCONTROLLERHARDWARE>, MultiAction<ApplyControllerHardwarePresetAction<&presets::defaultControllerHardware>, SwitchScreenAction<MainMenu>>>,
makeComponent<MenuItem, StaticText<TEXT_MOSFETSOFFCONTROLLERHARDWARE>, MultiAction<ApplyControllerHardwarePresetAction<&presets::mosfetsOffControllerHardware>, SwitchScreenAction<MainMenu>>>,
makeComponent<MenuItem, StaticText<TEXT_SPINNERCONTROLLERHARDWARE>, MultiAction<ApplyControllerHardwarePresetAction<&presets::spinnerControllerHardware>, SwitchScreenAction<MainMenu>>>,
makeComponent<MenuItem, StaticText<TEXT_DEFAULTBOARDCOMPUTERHARDWARE>, MultiAction<ApplyBoardcomputerHardwarePresetAction<&presets::defaultBoardcomputerHardware>, SwitchScreenAction<MainMenu>>>,
makeComponent<MenuItem, StaticText<TEXT_DEFAULTDEFAULTMODE>, MultiAction<ApplyDefaultModePresetAction<&presets::defaultDefaultMode>, SwitchScreenAction<MainMenu>>>,
makeComponent<MenuItem, StaticText<TEXT_SINUSOIDALDEFAULTMODE>, MultiAction<ApplyDefaultModePresetAction<&presets::sinusoidalDefaultMode>, SwitchScreenAction<MainMenu>>>,
makeComponent<MenuItem, StaticText<TEXT_DEFAULTTEMPOMATMODE>, MultiAction<ApplyTempomatModePresetAction<&presets::defaultTempomatMode>, SwitchScreenAction<MainMenu>>>,
makeComponent<MenuItem, StaticText<TEXT_DEFAULTLARSMMODE>, MultiAction<ApplyLarsmModePresetAction<&presets::defaultLarsmMode>, SwitchScreenAction<MainMenu>>>,
makeComponent<MenuItem, StaticText<TEXT_BACK>, SwitchScreenAction<MainMenu>, StaticMenuItemIcon<&icons::back>>
>
{};

View File

@ -0,0 +1,27 @@
#pragma once
#include "menudisplay.h"
#include "staticmenudefinition.h"
#include "actions/switchprofileaction.h"
#include "actions/switchscreenaction.h"
#include "icons/back.h"
#include "texts.h"
namespace {
class MainMenu;
}
namespace {
class ProfilesMenu :
public MenuDisplay,
public StaticText<TEXT_PROFILES>,
public BackActionInterface<SwitchScreenAction<MainMenu>>,
public StaticMenuDefinition<
makeComponent<MenuItem, StaticText<TEXT_PROFILE0>, SwitchProfileAction<0>>,
makeComponent<MenuItem, StaticText<TEXT_PROFILE1>, SwitchProfileAction<1>>,
makeComponent<MenuItem, StaticText<TEXT_PROFILE2>, SwitchProfileAction<2>>,
makeComponent<MenuItem, StaticText<TEXT_PROFILE3>, SwitchProfileAction<3>>,
makeComponent<MenuItem, StaticText<TEXT_BACK>, SwitchScreenAction<MainMenu>, StaticMenuItemIcon<&icons::back>>
>
{};
}

View File

@ -99,6 +99,7 @@ private:
Label m_labelPerformance{85, bottomLines[2]}; // 40, 15
Label m_labelMode{165, bottomLines[2]}; // 75, 15
Label m_labelName{40, bottomLines[3]}; // 40, 15
Label m_labelProfile{205, bottomLines[3]}; // 35, 15
static const constexpr int bottomLines[4] { 251, 266, 281, 296 };
};
@ -136,6 +137,7 @@ void StatusDisplay::initScreen()
m_labelMode.start();
tft.drawString("Name:", 0, bottomLines[3]);
m_labelName.start();
m_labelProfile.start();
tft.setTextColor(TFT_WHITE, TFT_BLACK);
}
@ -161,6 +163,8 @@ void StatusDisplay::redraw()
m_labelPerformance.redraw(String{performance.last});
m_labelMode.redraw(currentMode->displayName());
m_labelName.redraw(&deviceName[0]);
const auto profile = settingsPersister.currentlyOpenProfileIndex();
m_labelProfile.redraw(profile?String{*profile}:"-");
}
void StatusDisplay::rotate(int offset)

158
src/dpad5wire.h Normal file
View File

@ -0,0 +1,158 @@
#pragma once
#include <tuple>
#include <Arduino.h>
#include "globals.h"
#include "types.h"
#include "actions/switchprofileaction.h"
namespace {
namespace dpad5wire
{
using State = std::tuple<bool, bool, bool, bool, bool, bool, bool, bool>;
template<pin_t OUT, pin_t IN1, pin_t IN2, pin_t IN3, pin_t IN4>
class Helper
{
public:
static constexpr auto OutPin = OUT;
static constexpr auto In1Pin = IN1;
static constexpr auto In2Pin = IN2;
static constexpr auto In3Pin = IN3;
static constexpr auto In4Pin = IN4;
void begin();
State read();
};
template<pin_t OUT, pin_t IN1, pin_t IN2, pin_t IN3, pin_t IN4>
void Helper<OUT, IN1, IN2, IN3, IN4>::begin()
{
pinMode(OUT, OUTPUT);
}
template<pin_t OUT, pin_t IN1, pin_t IN2, pin_t IN3, pin_t IN4>
State Helper<OUT, IN1, IN2, IN3, IN4>::read()
{
digitalWrite(OUT, LOW);
pinMode(IN1, INPUT_PULLUP);
pinMode(IN2, INPUT_PULLUP);
pinMode(IN3, INPUT_PULLUP);
pinMode(IN4, INPUT_PULLUP);
delay(1);
const bool result0 = digitalRead(IN1)==LOW;
const bool result1 = digitalRead(IN2)==LOW;
const bool result2 = digitalRead(IN3)==LOW;
const bool result3 = digitalRead(IN4)==LOW;
digitalWrite(OUT, HIGH);
pinMode(IN1, INPUT_PULLDOWN);
pinMode(IN2, INPUT_PULLDOWN);
pinMode(IN3, INPUT_PULLDOWN);
pinMode(IN4, INPUT_PULLDOWN);
delay(1);
const bool result4 = digitalRead(IN1);
const bool result5 = digitalRead(IN2);
const bool result6 = digitalRead(IN3);
const bool result7 = digitalRead(IN4);
return std::make_tuple(result0, result1, result2, result3, result4, result5, result6, result7);
}
#ifdef FEATURE_DPAD_5WIRESW
Helper<PINS_DPAD_5WIRESW_OUT, PINS_DPAD_5WIRESW_IN1, PINS_DPAD_5WIRESW_IN2, PINS_DPAD_5WIRESW_IN3, PINS_DPAD_5WIRESW_IN4> helper;
State lastState;
millis_t debounceUp, debounceDown, debounceConfirm, debounceBack, debounceProfile0, debounceProfile1, debounceProfile2, debounceProfile3;
void init()
{
helper.begin();
debounceUp = debounceDown = debounceConfirm = debounceBack = debounceProfile0 = debounceProfile1 = debounceProfile2 = debounceProfile3 = millis();
}
void update()
{
const auto state = helper.read();
#ifdef DPAD_5WIRESW_DEBUG
lastState = state;
return;
#endif
const auto now = millis();
if (std::get<DPAD_5WIRESW_UP>(lastState) != std::get<DPAD_5WIRESW_UP>(state) && now-debounceUp > settings.boardcomputerHardware.dpadDebounce)
{
if (std::get<DPAD_5WIRESW_UP>(state))
InputDispatcher::rotate(-1);
std::get<DPAD_5WIRESW_UP>(lastState) = std::get<DPAD_5WIRESW_UP>(state);
debounceUp = now;
}
if (std::get<DPAD_5WIRESW_DOWN>(lastState) != std::get<DPAD_5WIRESW_DOWN>(state) && now-debounceDown > settings.boardcomputerHardware.dpadDebounce)
{
if (std::get<DPAD_5WIRESW_DOWN>(state))
InputDispatcher::rotate(1);
std::get<DPAD_5WIRESW_DOWN>(lastState) = std::get<DPAD_5WIRESW_DOWN>(state);
debounceDown = now;
}
if (std::get<DPAD_5WIRESW_CONFIRM>(lastState) != std::get<DPAD_5WIRESW_CONFIRM>(state) && now-debounceConfirm > settings.boardcomputerHardware.dpadDebounce)
{
InputDispatcher::confirmButton(std::get<DPAD_5WIRESW_CONFIRM>(state));
std::get<DPAD_5WIRESW_CONFIRM>(lastState) = std::get<DPAD_5WIRESW_CONFIRM>(state);
debounceConfirm = now;
}
if (std::get<DPAD_5WIRESW_BACK>(lastState) != std::get<DPAD_5WIRESW_BACK>(state) && now-debounceBack > settings.boardcomputerHardware.dpadDebounce)
{
InputDispatcher::backButton(std::get<DPAD_5WIRESW_BACK>(state));
std::get<DPAD_5WIRESW_BACK>(lastState) = std::get<DPAD_5WIRESW_BACK>(state);
debounceBack = now;
}
if (std::get<DPAD_5WIRESW_PROFILE0>(lastState) != std::get<DPAD_5WIRESW_PROFILE0>(state) && now-debounceProfile0 > settings.boardcomputerHardware.dpadDebounce)
{
if (std::get<DPAD_5WIRESW_PROFILE0>(state))
{
SwitchProfileAction<0>{}.triggered();
}
std::get<DPAD_5WIRESW_PROFILE0>(lastState) = std::get<DPAD_5WIRESW_PROFILE0>(state);
debounceProfile0 = now;
}
if (std::get<DPAD_5WIRESW_PROFILE1>(lastState) != std::get<DPAD_5WIRESW_PROFILE1>(state) && now-debounceProfile1 > settings.boardcomputerHardware.dpadDebounce)
{
if (std::get<DPAD_5WIRESW_PROFILE1>(state))
{
SwitchProfileAction<1>{}.triggered();
}
std::get<DPAD_5WIRESW_PROFILE1>(lastState) = std::get<DPAD_5WIRESW_PROFILE1>(state);
debounceProfile1 = now;
}
if (std::get<DPAD_5WIRESW_PROFILE2>(lastState) != std::get<DPAD_5WIRESW_PROFILE2>(state) && now-debounceProfile2 > settings.boardcomputerHardware.dpadDebounce)
{
if (std::get<DPAD_5WIRESW_PROFILE2>(state))
{
SwitchProfileAction<2>{}.triggered();
}
std::get<DPAD_5WIRESW_PROFILE2>(lastState) = std::get<DPAD_5WIRESW_PROFILE2>(state);
debounceProfile2 = now;
}
if (std::get<DPAD_5WIRESW_PROFILE3>(lastState) != std::get<DPAD_5WIRESW_PROFILE3>(state) && now-debounceProfile3 > settings.boardcomputerHardware.dpadDebounce)
{
if (std::get<DPAD_5WIRESW_PROFILE3>(state))
{
SwitchProfileAction<3>{}.triggered();
}
std::get<DPAD_5WIRESW_PROFILE3>(lastState) = std::get<DPAD_5WIRESW_PROFILE3>(state);
debounceProfile3 = now;
}
}
#endif
}
}

View File

@ -11,7 +11,7 @@
#include "display.h"
#include "modeinterface.h"
#include "settings.h"
#include "settingssaver.h"
#include "settingspersister.h"
#include "types.h"
namespace {
@ -26,7 +26,7 @@ float avgSpeed, avgSpeedKmh, sumCurrent;
char deviceName[32];
Settings settings;
SettingsSaver settingsSaver;
SettingsPersister settingsPersister;
constexpr auto TFT_GREY = 0x5AEB;

View File

@ -12,9 +12,11 @@
#include "globals.h"
#include "modes/defaultmode.h"
#include "modes/tempomatmode.h"
#include "displays/dpad5wiredebugdisplay.h"
#include "screens.h"
#include "dpad.h"
#include "dpad3wire.h"
#include "dpad5wire.h"
#include "rotary.h"
#include "serialhandler.h"
#include "ota.h"
@ -48,18 +50,27 @@ void setup()
initScreen();
#ifdef FEATURE_DPAD
bootLabel.redraw("dpad");
dpad::init();
#endif
#ifdef FEATURE_DPAD_3WIRESW
bootLabel.redraw("dpad3wire");
dpad3wire::init();
#endif
#ifdef FEATURE_DPAD_5WIRESW
bootLabel.redraw("dpad5wire");
dpad5wire::init();
#endif
#ifdef FEATURE_ROTARY
bootLabel.redraw("rotary");
initRotary();
#endif
#ifdef FEATURE_MOSFETS
bootLabel.redraw("mosfets");
pinMode(PINS_MOSFET0, OUTPUT);
pinMode(PINS_MOSFET1, OUTPUT);
pinMode(PINS_MOSFET2, OUTPUT);
@ -69,45 +80,71 @@ void setup()
digitalWrite(PINS_MOSFET2, LOW);
#endif
bootLabel.redraw("settings");
settings = presets::defaultSettings;
if (settingsSaver.init())
loadSettings();
if (settingsPersister.init())
{
if (settingsPersister.openProfile(0))
{
loadSettings();
}
}
bootLabel.redraw("swap front back");
updateSwapFrontBack();
bootLabel.redraw("deviceName");
{
uint8_t macAddress[6];
WiFi.macAddress(&macAddress[0]);
std::sprintf(deviceName, STRING(DEVICE_PREFIX) "_%02hhx%02hhx%02hhx", macAddress[3], macAddress[4], macAddress[5]);
}
bootLabel.redraw("setHostname");
if (!WiFi.setHostname(deviceName))
Serial.println("Could not setHostname");
bootLabel.redraw("softAPsetHostname");
if (!WiFi.softAPsetHostname(deviceName))
Serial.println("Could not softAPsetHostname");
bootLabel.redraw("WiFi mode");
if (!WiFi.mode(settings.wifiSettings.autoWifiMode))
Serial.println("Could not set mode to WIFI_AP_STA");
if (settings.wifiSettings.autoEnableAp)
{
bootLabel.redraw("WiFi softAp");
WifiSoftApAction{}.triggered();
}
bootLabel.redraw("WiFi begin");
if (!WiFi.begin("realraum", "r3alraum"))
Serial.println("Could not begin WiFi");
if (settings.bluetoothSettings.autoBluetoothMode == BluetoothMode::Master)
{
bootLabel.redraw("bluetooth begin master");
BluetoothBeginMasterAction{}.triggered();
#ifdef FEATURE_BMS
if (settings.autoConnectBms)
{
bootLabel.redraw("connect BMS");
BluetoothConnectBmsAction{}.triggered();
}
#endif
} else if (settings.bluetoothSettings.autoBluetoothMode == BluetoothMode::Slave)
}
else if (settings.bluetoothSettings.autoBluetoothMode == BluetoothMode::Slave)
{
bootLabel.redraw("bluetooth begin");
BluetoothBeginAction{}.triggered();
}
bootLabel.redraw("front Serial begin");
controllers.front.serial.get().begin(38400, SERIAL_8N1, PINS_RX1, PINS_TX1);
bootLabel.redraw("back Serial begin");
controllers.back.serial.get().begin(38400, SERIAL_8N1, PINS_RX2, PINS_TX2);
raw_gas = 0;
@ -121,13 +158,21 @@ void setup()
currentMode = &modes::defaultMode;
#ifdef FEATURE_OTA
bootLabel.redraw("ota");
initOta();
#endif
bootLabel.redraw("webserver");
initWebserver();
bootLabel.redraw("potis");
readPotis();
#if defined(FEATURE_DPAD_5WIRESW) && defined(DPAD_5WIRESW_DEBUG)
switchScreen<DPad5WireDebugDisplay>();
return;
#endif
if (gas > 200.f || brems > 200.f)
switchScreen<CalibrateDisplay>(true);
else
@ -146,6 +191,10 @@ void loop()
dpad3wire::update();
#endif
#ifdef FEATURE_DPAD_5WIRESW
dpad5wire::update();
#endif
if (!lastPotiRead || now - lastPotiRead >= 1000/settings.boardcomputerHardware.timersSettings.potiReadRate)
{
readPotis();

View File

@ -25,10 +25,10 @@ constexpr Settings::ControllerHardware defaultControllerHardware {
.enableBackLeft = true,
.enableBackRight = true,
.invertFrontLeft = false,
.invertFrontRight = true,
.invertBackLeft = false,
.invertBackRight = true,
.invertFrontLeft = DEFAULT_INVERTFRONTLEFT,
.invertFrontRight = DEFAULT_INVERTFRONTRIGHT,
.invertBackLeft = DEFAULT_INVERTBACKLEFT,
.invertBackRight = DEFAULT_INVERTBACKRIGHT,
.wheelDiameter = 165,
.numMagnetPoles = 15,
@ -41,10 +41,10 @@ constexpr Settings::ControllerHardware mosfetsOffControllerHardware {
.enableBackLeft = false,
.enableBackRight = false,
.invertFrontLeft = false,
.invertFrontRight = true,
.invertBackLeft = false,
.invertBackRight = true,
.invertFrontLeft = DEFAULT_INVERTFRONTLEFT,
.invertFrontRight = DEFAULT_INVERTFRONTRIGHT,
.invertBackLeft = DEFAULT_INVERTBACKLEFT,
.invertBackRight = DEFAULT_INVERTBACKRIGHT,
.wheelDiameter = 165,
.numMagnetPoles = 15,
@ -57,7 +57,7 @@ constexpr Settings::WifiSettings defaultWifiSettings {
};
constexpr Settings::BluetoothSettings defaultBluetoothSettings {
.autoBluetoothMode = BluetoothMode::Off
.autoBluetoothMode = BluetoothMode::Master
};
constexpr Settings::ControllerHardware spinnerControllerHardware {
@ -66,10 +66,10 @@ constexpr Settings::ControllerHardware spinnerControllerHardware {
.enableBackLeft = true,
.enableBackRight = true,
.invertFrontLeft = false,
.invertFrontRight = false,
.invertBackLeft = false,
.invertBackRight = false,
.invertFrontLeft = DEFAULT_INVERTFRONTLEFT,
.invertFrontRight = !DEFAULT_INVERTFRONTRIGHT,
.invertBackLeft = DEFAULT_INVERTBACKLEFT,
.invertBackRight = !DEFAULT_INVERTBACKRIGHT,
.wheelDiameter = 165,
.numMagnetPoles = 15,
@ -90,7 +90,7 @@ constexpr Settings::BoardcomputerHardware defaultBoardcomputerHardware {
.gasMax = DEFAULT_GASMAX,
.bremsMin = DEFAULT_BREMSMIN,
.bremsMax = DEFAULT_BREMSMAX,
#if defined(FEATURE_DPAD) || defined(FEATURE_DPAD_3WIRESW)
#if defined(FEATURE_DPAD) || defined(FEATURE_DPAD_3WIRESW) || defined(FEATURE_DPAD_5WIRESW)
.dpadDebounce = 25,
#endif
#ifdef FEATURE_GAMETRAK

View File

@ -1,5 +1,7 @@
#pragma once
#include <include/tl/optional.hpp>
#include "displays/menus/aboutmenu.h"
#include "displays/menus/accesspointwifisettingsmenu.h"
#include "displays/menus/bluetoothsettingsmenu.h"
@ -25,6 +27,7 @@
#include "displays/menus/mosfetsmenu.h"
#include "displays/menus/motorfeedbackdebugmenu.h"
#include "displays/menus/motorstatedebugmenu.h"
#include "displays/menus/profilesmenu.h"
#include "displays/menus/presetsmenu.h"
#include "displays/menus/boardcomputerhardwaresettingsmenu.h"
#include "displays/menus/selectmodemenu.h"
@ -35,6 +38,7 @@
#include "displays/menus/wifisettingsmenu.h"
#include "displays/bmsdisplay.h"
#include "displays/calibratedisplay.h"
#include "displays/dpad5wiredebugdisplay.h"
#include "displays/gameoflifedisplay.h"
#include "displays/gametrakcalibratedisplay.h"
#include "displays/lockscreen.h"
@ -48,9 +52,12 @@
#include "globals.h"
#include "utils.h"
#include "widgets/label.h"
#include "icons/logo.h"
namespace {
Label bootLabel{32, 250};
union X {
X() {}
~X() { ((Display&)statusDisplay).~Display(); }
@ -95,6 +102,7 @@ union X {
BackLeftMotorFeedbackDebugMenu backLeftMotorFeedbackDebugMenu;
BackRightMotorFeedbackDebugMenu backRightMotorFeedbackDebugMenu;
BoardcomputerHardwareSettingsMenu boardcomputerHardwareSettingsMenu;
ProfilesMenu profilesMenu;
PresetsMenu presetsMenu;
SelectModeMenu selectModeMenu;
SettingsMenu settingsMenu;
@ -107,6 +115,9 @@ union X {
BmsDisplay bmsDisplay;
#endif
CalibrateDisplay calibrateDisplay;
#if defined(FEATURE_DPAD_5WIRESW) && defined(DPAD_5WIRESW_DEBUG)
DPad5WireDebugDisplay dPad5WireDebugDisplay;
#endif
GameOfLifeDisplay gameOfLifeDisplay;
#ifdef FEATURE_GAMETRAK
GametrakCalibrateDisplay gametrakCalibrateDisplay;
@ -167,7 +178,7 @@ union X {
GasMaxChangeScreen changeGasMax;
BremsMinChangeScreen changeBremsMin;
BremsMaxChangeScreen changeBremsMax;
#if defined(FEATURE_DPAD) || defined(FEATURE_DPAD_3WIRESW)
#if defined(FEATURE_DPAD) || defined(FEATURE_DPAD_3WIRESW) || defined(FEATURE_DPAD_5WIRESW)
DPadDebounceChangeScreen dPadDebounceChangeScreen;
#endif
#ifdef FEATURE_GAMETRAK
@ -249,6 +260,7 @@ template<> decltype(displays.frontLeftMotorFeedbackDebugMenu) &
template<> decltype(displays.frontRightMotorFeedbackDebugMenu) &getRefByType<decltype(displays.frontRightMotorFeedbackDebugMenu)>() { return displays.frontRightMotorFeedbackDebugMenu; }
template<> decltype(displays.backLeftMotorFeedbackDebugMenu) &getRefByType<decltype(displays.backLeftMotorFeedbackDebugMenu)>() { return displays.backLeftMotorFeedbackDebugMenu; }
template<> decltype(displays.backRightMotorFeedbackDebugMenu) &getRefByType<decltype(displays.backRightMotorFeedbackDebugMenu)>() { return displays.backRightMotorFeedbackDebugMenu; }
template<> decltype(displays.profilesMenu) &getRefByType<decltype(displays.profilesMenu)>() { return displays.profilesMenu; }
template<> decltype(displays.presetsMenu) &getRefByType<decltype(displays.presetsMenu)>() { return displays.presetsMenu; }
template<> decltype(displays.selectModeMenu) &getRefByType<decltype(displays.selectModeMenu)>() { return displays.selectModeMenu; }
template<> decltype(displays.settingsMenu) &getRefByType<decltype(displays.settingsMenu)>() { return displays.settingsMenu; }
@ -262,6 +274,9 @@ template<> decltype(displays.wifiSettingsMenu) &
template<> decltype(displays.bmsDisplay) &getRefByType<decltype(displays.bmsDisplay)>() { return displays.bmsDisplay; }
#endif
template<> decltype(displays.calibrateDisplay) &getRefByType<decltype(displays.calibrateDisplay)>() { return displays.calibrateDisplay; }
#if defined(FEATURE_DPAD_5WIRESW) && defined(DPAD_5WIRESW_DEBUG)
template<> decltype(displays.dPad5WireDebugDisplay) &getRefByType<decltype(displays.dPad5WireDebugDisplay)>() { return displays.dPad5WireDebugDisplay; }
#endif
template<> decltype(displays.gameOfLifeDisplay) &getRefByType<decltype(displays.gameOfLifeDisplay)>() { return displays.gameOfLifeDisplay; }
#ifdef FEATURE_GAMETRAK
template<> decltype(displays.gametrakCalibrateDisplay) &getRefByType<decltype(displays.gametrakCalibrateDisplay)>() { return displays.gametrakCalibrateDisplay; }
@ -322,7 +337,7 @@ template<> decltype(displays.changeGasMin) &
template<> decltype(displays.changeGasMax) &getRefByType<decltype(displays.changeGasMax)>() { return displays.changeGasMax; }
template<> decltype(displays.changeBremsMin) &getRefByType<decltype(displays.changeBremsMin)>() { return displays.changeBremsMin; }
template<> decltype(displays.changeBremsMax) &getRefByType<decltype(displays.changeBremsMax)>() { return displays.changeBremsMax; }
#if defined(FEATURE_DPAD) || defined(FEATURE_DPAD_3WIRESW)
#if defined(FEATURE_DPAD) || defined(FEATURE_DPAD_3WIRESW) || defined(FEATURE_DPAD_5WIRESW)
template<> decltype(displays.dPadDebounceChangeScreen) &getRefByType<decltype(displays.dPadDebounceChangeScreen)>() { return displays.dPadDebounceChangeScreen; }
#endif
#ifdef FEATURE_GAMETRAK
@ -402,10 +417,12 @@ void initScreen()
{
tft.init();
tft.fillScreen(TFT_WHITE);
tft.setTextColor(TFT_BLACK);
tft.setTextColor(TFT_BLACK, TFT_WHITE);
tft.setTextFont(4);
tft.pushImage(0, 40, icons::logo.WIDTH, icons::logo.HEIGHT, icons::logo.buffer);
tft.drawString("Bobbycar-OS", 32, 200, 4);
tft.drawString("booting...", 32, 225, 4);
tft.drawString("Bobbycar-OS", 32, 200);
tft.drawString("booting...", 32, 225);
bootLabel.start();
}
void updateDisplay()

View File

@ -53,7 +53,7 @@ struct Settings
struct BoardcomputerHardware {
int16_t sampleCount;
int16_t gasMin, gasMax, bremsMin, bremsMax;
#if defined(FEATURE_DPAD) || defined(FEATURE_DPAD_3WIRESW)
#if defined(FEATURE_DPAD) || defined(FEATURE_DPAD_3WIRESW) || defined(FEATURE_DPAD_5WIRESW)
uint8_t dpadDebounce;
#endif
#ifdef FEATURE_GAMETRAK
@ -141,7 +141,7 @@ void Settings::executeForEverySetting(T &&callable)
callable("gasMax", boardcomputerHardware.gasMax);
callable("bremsMin", boardcomputerHardware.bremsMin);
callable("bremsMax", boardcomputerHardware.bremsMax);
#if defined(FEATURE_DPAD) || defined(FEATURE_DPAD_3WIRESW)
#if defined(FEATURE_DPAD) || defined(FEATURE_DPAD_3WIRESW) || defined(FEATURE_DPAD_5WIRESW)
callable("dpadDebounce", boardcomputerHardware.dpadDebounce);
#endif
#ifdef FEATURE_GAMETRAK

View File

@ -64,7 +64,7 @@ struct GasMinAccessor : public RefAccessorSaveSettings<int16_t> { int16_t &getRe
struct GasMaxAccessor : public RefAccessorSaveSettings<int16_t> { int16_t &getRef() const override { return settings.boardcomputerHardware.gasMax; } };
struct BremsMinAccessor : public RefAccessorSaveSettings<int16_t> { int16_t &getRef() const override { return settings.boardcomputerHardware.bremsMin; } };
struct BremsMaxAccessor : public RefAccessorSaveSettings<int16_t> { int16_t &getRef() const override { return settings.boardcomputerHardware.bremsMax; } };
#if defined(FEATURE_DPAD) || defined(FEATURE_DPAD_3WIRESW)
#if defined(FEATURE_DPAD) || defined(FEATURE_DPAD_3WIRESW) || defined(FEATURE_DPAD_5WIRESW)
struct DPadDebounceAccessor : public RefAccessorSaveSettings<uint8_t> { uint8_t &getRef() const override { return settings.boardcomputerHardware.dpadDebounce; } };
#endif
#ifdef FEATURE_GAMETRAK

View File

@ -6,36 +6,41 @@
#include <nvs_flash.h>
#include <nvs.h>
#include <include/tl/optional.hpp>
#include "settings.h"
#include "bluetoothmode.h"
#include "unifiedmodelmode.h"
namespace {
class SettingsSaver
class SettingsPersister
{
public:
bool init();
bool erase();
bool openProfile(uint8_t index);
void closeProfile();
bool load(Settings &settings);
bool save(Settings &settings);
tl::optional<uint8_t> currentlyOpenProfileIndex() const;
private:
nvs_handle my_handle;
struct CurrentlyOpenProfile {
nvs_handle handle;
uint8_t profileIndex;
};
tl::optional<CurrentlyOpenProfile> m_profile;
};
bool SettingsSaver::init()
bool SettingsPersister::init()
{
esp_err_t err = nvs_flash_init();
if (err == ESP_ERR_NVS_NO_FREE_PAGES || err == ESP_ERR_NVS_NEW_VERSION_FOUND)
{
Serial.printf("nvs_flash_init() returned: %s, trying to erase\r\n", esp_err_to_name(err));
err = nvs_flash_erase();
if (err != ESP_OK)
{
Serial.printf("nvs_flash_erase() returned: %s, aborting\r\n", esp_err_to_name(err));
return false;
}
err = nvs_flash_init();
return erase();
}
if (err != ESP_OK)
@ -44,16 +49,55 @@ bool SettingsSaver::init()
return false;
}
err = nvs_open("bobbycar", NVS_READWRITE, &my_handle);
return true;
}
bool SettingsPersister::erase()
{
esp_err_t err = nvs_flash_erase();
if (err != ESP_OK)
{
Serial.printf("nvs_flash_erase() returned: %s, aborting\r\n", esp_err_to_name(err));
return false;
}
err = nvs_flash_init();
if (err != ESP_OK)
{
Serial.printf("nvs_flash_init() returned: %s\r\n", esp_err_to_name(err));
return false;
}
return true;
}
bool SettingsPersister::openProfile(uint8_t index)
{
closeProfile();
nvs_handle handle;
esp_err_t err = nvs_open((String{"bobbycar"}+index).c_str(), NVS_READWRITE, &handle);
if (err != ESP_OK)
{
Serial.printf("nvs_open() returned: %s\r\n", esp_err_to_name(err));
return false;
}
m_profile = {handle, index};
return true;
}
void SettingsPersister::closeProfile()
{
if (!m_profile)
return;
nvs_close(m_profile->handle);
m_profile = tl::nullopt;
}
template<typename T> struct nvsGetterHelper;
template<> struct nvsGetterHelper<int8_t> { static constexpr auto nvs_get = &nvs_get_i8; };
template<> struct nvsGetterHelper<uint8_t> { static constexpr auto nvs_get = &nvs_get_u8; };
@ -118,13 +162,19 @@ template<> struct nvsGetterHelper<wifi_mode_t> { static esp_err_t nvs_get(nvs_ha
return err;
}};
bool SettingsSaver::load(Settings &settings)
bool SettingsPersister::load(Settings &settings)
{
if (!m_profile)
{
Serial.println("SettingsPersister::load() no profile open currently!");
return false;
}
bool result{true};
settings.executeForEverySetting([&](const char *key, auto &value)
{
esp_err_t err = nvsGetterHelper<std::remove_reference_t<decltype(value)>>::nvs_get(my_handle, key, &value);
esp_err_t err = nvsGetterHelper<std::remove_reference_t<decltype(value)>>::nvs_get(m_profile->handle, key, &value);
if (err != ESP_OK)
{
Serial.printf("nvs_get_i32() for %s returned: %s\r\n", key, esp_err_to_name(err));
@ -168,13 +218,19 @@ template<> struct nvsSetterHelper<wifi_mode_t> { static esp_err_t nvs_set(nvs_ha
return nvs_set_u8(handle, key, uint8_t(value));
}};
bool SettingsSaver::save(Settings &settings)
bool SettingsPersister::save(Settings &settings)
{
if (!m_profile)
{
Serial.println("SettingsPersister::save() no profile open currently!");
return false;
}
bool result{true};
settings.executeForEverySetting([&](const char *key, auto value)
{
esp_err_t err = nvsSetterHelper<decltype(value)>::nvs_set(my_handle, key, value);
esp_err_t err = nvsSetterHelper<decltype(value)>::nvs_set(m_profile->handle, key, value);
if (err != ESP_OK)
{
Serial.printf("nvs_get_i32() for %s returned: %s\r\n", key, esp_err_to_name(err));
@ -185,4 +241,12 @@ bool SettingsSaver::save(Settings &settings)
return result;
}
tl::optional<uint8_t> SettingsPersister::currentlyOpenProfileIndex() const
{
if (m_profile)
return m_profile->profileIndex;
return tl::nullopt;
}
}

View File

@ -36,6 +36,7 @@ constexpr char TEXT_TURNOFFDISCHARGE[] = "Turn off discharge";
//DebugMenu
constexpr char TEXT_LOADSETTINGS[] = "Load settings";
constexpr char TEXT_SAVESETTINGS[] = "Save settings";
constexpr char TEXT_ERASENVS[] = "Erase NVS";
constexpr char TEXT_FRONTCOMMAND[] = "Front command";
constexpr char TEXT_BACKCOMMAND[] = "Back command";
constexpr char TEXT_FRONTLEFTCOMMAND[] = "Front left command";
@ -56,6 +57,7 @@ constexpr char TEXT_STATUS[] = "Status";
constexpr char TEXT_SELECTMODE[] = "Select mode";
constexpr char TEXT_MODESETTINGS[] = "Mode settings";
constexpr char TEXT_PRESETS[] = "Presets";
constexpr char TEXT_PROFILES[] = "Profiles";
constexpr char TEXT_GRAPHS[] = "Graphs";
//constexpr char TEXT_BMS[] = "BMS";
constexpr char TEXT_SETTINGS[] = "Settings";
@ -254,6 +256,14 @@ constexpr char TEXT_SWAPSCREENBYTES[] = "Swap screen bytes";
constexpr char TEXT_TIMERS[] = "Timers";
//constexpr char TEXT_BACK[] = "Back";
//ProfilesMenu
//constexpr char TEXT_PROFILES[] = "Profiles";
constexpr char TEXT_PROFILE0[] = "Profile 0";
constexpr char TEXT_PROFILE1[] = "Profile 1";
constexpr char TEXT_PROFILE2[] = "Profile 2";
constexpr char TEXT_PROFILE3[] = "Profile 3";
//constexpr char TEXT_BACK[] = "Back";
//PresetsMenu
//constexpr char TEXT_PRESETS[] = "Presets";
constexpr char TEXT_DEFAULTEVERYTHING[] = "Default everything";

View File

@ -266,12 +266,12 @@ void updateSwapFrontBack()
void loadSettings()
{
settingsSaver.load(settings);
settingsPersister.load(settings);
}
void saveSettings()
{
settingsSaver.save(settings);
settingsPersister.save(settings);
}
void updateAccumulators()

View File

@ -8,7 +8,7 @@ namespace {
class Label
{
public:
Label(int x, int y) : m_x{x}, m_y{y} {}
Label(int x, int y);
int x() const { return m_x; };
int y() const { return m_y; };
@ -29,6 +29,12 @@ private:
int m_lastHeight;
};
Label::Label(int x, int y) :
m_x{x},
m_y{y}
{
}
void Label::start()
{
m_lastStr.clear();
@ -71,7 +77,7 @@ void Label::redraw(const String &str, bool forceRedraw)
void Label::clear()
{
if (m_lastWidth || m_lastHeight)
tft.fillRect(m_x, m_y, m_lastWidth, m_lastHeight, TFT_BLACK);
tft.fillRect(m_x, m_y, m_lastWidth, m_lastHeight, tft.textbgcolor);
start();
}