From 8b5e23cecb795aaf5785530352e27eb6cd91ea83 Mon Sep 17 00:00:00 2001 From: Mark van Renswoude Date: Fri, 31 Jan 2020 12:22:06 +0100 Subject: [PATCH] Cleanup of the code Got rid of the last bits of shouting defines --- doc/DeskControl UI mockup - LCD version.psd | Bin 147732 -> 181778 bytes src/include/config.h | 17 ++++- src/include/metrics.h | 79 ++++++++++++++++++++ src/lib/{motorstate.cpp => control.cpp} | 37 ++++++--- src/lib/{motorstate.h => control.h} | 14 ++-- src/lib/screen.cpp | 31 ++++++++ src/lib/screen.h | 4 + src/lib/screen/calibrate.cpp | 2 +- src/lib/screen/home.cpp | 63 ++++------------ src/lib/screen/home.h | 1 - src/lib/screen/manual.cpp | 2 +- src/lib/screen/menu.cpp | 2 +- src/lib/screen/move-overcurrent.cpp | 2 +- src/lib/screen/move-sensorerror.cpp | 2 +- src/lib/screen/move.cpp | 64 +++++----------- src/main.cpp | 25 ++++--- 16 files changed, 218 insertions(+), 127 deletions(-) create mode 100644 src/include/metrics.h rename src/lib/{motorstate.cpp => control.cpp} (66%) rename src/lib/{motorstate.h => control.h} (62%) diff --git a/doc/DeskControl UI mockup - LCD version.psd b/doc/DeskControl UI mockup - LCD version.psd index 6071a0add2095aafd5ecf9fa6cb165ba43288f30..4ac78a241bea8af71b221373fb0a0a64b8f2e802 100644 GIT binary patch literal 181778 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 +#include "fonts/FreeSansBold18pt7b.trimmed.h" + +class Metrics +{ + public: + /* + + Small font + Uses the built-in Adafruit GFX 5x8 font. + + */ + static constexpr const GFXfont* SmallFont = nullptr; + + static const uint16_t SmallFontBaseline = 0; + static const uint16_t SmallFontHeight = 8; + static const uint16_t SmallFontMaxHeight = Metrics::SmallFontHeight; + + + /* + + Large font + Uses a trimmed version of the FreeSansBold 18pt font. + + */ + static constexpr const GFXfont* LargeFont = &FreeSansBold18pt7bTrimmed; + + static const uint16_t LargeFontBaseline = 25; + static const uint16_t LargeFontHeight = Metrics::LargeFontBaseline; + static const uint16_t LargeFontMaxHeight = 42; + + + + /* + + Text line + Defines the height of a standard line of text including margins. + Suitable for applying a background color. + + */ + static const uint16_t LargeTextLineMargin = 7; + static const uint16_t LargeTextLineYOffset = Metrics::LargeFontBaseline + Metrics::LargeTextLineMargin; + static const uint16_t LargeTextLineHeight = (Metrics::LargeFontHeight + (2 * Metrics::LargeTextLineMargin)); + + static const uint16_t SmallTextLineMargin = 7; + static const uint16_t SmallTextLineHeight = (Metrics::SmallFontHeight + (2 * Metrics::SmallTextLineMargin)); + + + /* + + Arrows + + */ + static const uint16_t HArrowWidth = 10; + static const uint16_t HArrowHeight = 20; + + static const uint16_t VArrowWidth = Metrics::HArrowHeight; + static const uint16_t VArrowHeight = Metrics::HArrowWidth; + + static const uint16_t ArrowMargin = 8; + + static const uint16_t LargeTextLineHArrowYOffset = (Metrics::LargeTextLineHeight - Metrics::HArrowHeight) / 2; + static const uint16_t LargeTextLineVArrowYOffset = (Metrics::LargeTextLineHeight - Metrics::VArrowHeight) / 2; + + + + /* + + Screen layout + Shared amongst screens for a consistent layout + + */ + static const uint16_t MiddleLargeTextLineY = 100; +}; + +#endif \ No newline at end of file diff --git a/src/lib/motorstate.cpp b/src/lib/control.cpp similarity index 66% rename from src/lib/motorstate.cpp rename to src/lib/control.cpp index c6d4860..759b4f3 100644 --- a/src/lib/motorstate.cpp +++ b/src/lib/control.cpp @@ -1,4 +1,4 @@ -#include "./motorstate.h" +#include "./control.h" #include "./motor.h" #include "./state.h" #include "./settings.h" @@ -6,18 +6,18 @@ #include "include/config.h" -void motorStateMoveTo(uint16_t height) +void controlMoveTo(uint16_t height) { - dl("motorStateMoveTo: "); dln(height); + dl("controlMoveTo: "); dln(height); State.MoveTarget = height; State.MoveDirection = height > State.CurrentHeight ? Direction::Up : Direction::Down; } -bool motorStateCheckTargetReached() +bool controlCheckTargetReached() { - dl("motorStateCheckTargetReached: direction = "); dl((uint8_t)State.MoveDirection); dl(", currentHeight = "); dln(State.CurrentHeight); + dl("controlCheckTargetReached: direction = "); dl((uint8_t)State.MoveDirection); dl(", currentHeight = "); dln(State.CurrentHeight); switch (State.MoveDirection) { @@ -27,7 +27,7 @@ bool motorStateCheckTargetReached() if (State.CurrentHeight - State.MoveTarget <= Config::HeightMeasurementDeltaOnTarget) State.CurrentHeight = State.MoveTarget; - motorStateStop(); + controlStop(); return true; } break; @@ -38,7 +38,7 @@ bool motorStateCheckTargetReached() if (State.MoveTarget - State.CurrentHeight <= Config::HeightMeasurementDeltaOnTarget) State.CurrentHeight = State.MoveTarget; - motorStateStop(); + controlStop(); return true; } break; @@ -51,13 +51,13 @@ bool motorStateCheckTargetReached() } -bool motorStateCheckOverCurrent() +bool controlCheckOverCurrent() { if (motorIsOverCurrent()) { - dln("motorStateCheckOverCurrent: overcurrent detected!"); + dln("controlCheckOverCurrent: overcurrent detected!"); - motorStateStop(); + controlStop(); return true; } @@ -65,15 +65,28 @@ bool motorStateCheckOverCurrent() } -void motorStateStop() +void controlStop() { - dln("motorStateStop"); + dln("controlStop"); motorStop(); State.MoveDirection = Direction::None; } +void controlSnapToPreset() +{ + for (uint8_t i = 0; i < 2; i++) + { + if (abs(State.CurrentHeight - Settings.Height.Preset[i]) <= Config::HeightMeasurementDeltaOnTarget) + { + State.CurrentHeight = Settings.Height.Preset[i]; + break; + } + } +} + + void getDisplayHeight(char* buffer, uint16_t value) { uint8_t displayValue = (value + Settings.Height.Offset) / 10; diff --git a/src/lib/motorstate.h b/src/lib/control.h similarity index 62% rename from src/lib/motorstate.h rename to src/lib/control.h index f423958..bf60a46 100644 --- a/src/lib/motorstate.h +++ b/src/lib/control.h @@ -1,13 +1,15 @@ -#ifndef __motorstate -#define __motorstate +#ifndef __control +#define __control #include // High-level functions to control the motor and update the global state -extern void motorStateMoveTo(uint16_t height); -extern bool motorStateCheckTargetReached(); -extern bool motorStateCheckOverCurrent(); -extern void motorStateStop(); +extern void controlMoveTo(uint16_t height); +extern bool controlCheckTargetReached(); +extern bool controlCheckOverCurrent(); +extern void controlStop(); + +extern void controlSnapToPreset(); // Formats a height value as "0.00m" (always exactly 5 characters long). diff --git a/src/lib/screen.cpp b/src/lib/screen.cpp index 2a2f759..1b8e64e 100644 --- a/src/lib/screen.cpp +++ b/src/lib/screen.cpp @@ -1,5 +1,6 @@ #include "./screen.h" #include "include/config.h" +#include "include/metrics.h" Adafruit_GFX* BaseScreen::getDisplay() @@ -26,6 +27,36 @@ uint16_t BaseScreen::printCentered(const char* text, int16_t y) } +void BaseScreen::drawArrowLeft(int16_t x, int16_t y, uint16_t color) +{ + this->getDisplay()->fillTriangle( + x + Metrics::HArrowWidth, y, // Top right + x, y + (Metrics::HArrowHeight / 2), // Middle left + x + Metrics::HArrowWidth, y + Metrics::HArrowHeight, // Bottom right + color); +} + + +void BaseScreen::drawArrowUp(int16_t x, int16_t y, uint16_t color) +{ + this->getDisplay()->fillTriangle( + x + (Metrics::VArrowWidth / 2), y, // Top middle + x, y + Metrics::VArrowHeight, // Bottom left + x + Metrics::VArrowWidth, y + Metrics::VArrowHeight, // Bottom right + color); +} + + +void BaseScreen::drawArrowDown(int16_t x, int16_t y, uint16_t color) +{ + this->getDisplay()->fillTriangle( + x, y, // Top left + x + Metrics::VArrowWidth, y, // Top right + x + (Metrics::VArrowWidth / 2), y + Metrics::VArrowHeight, // Bottom middle + color); +} + + void ScreenManager::init() { pinMode(Config::DisplayPinBL, OUTPUT); diff --git a/src/lib/screen.h b/src/lib/screen.h index 00e74e9..15f08b7 100644 --- a/src/lib/screen.h +++ b/src/lib/screen.h @@ -36,6 +36,10 @@ class BaseScreen uint16_t printCentered(const char* text, int16_t y); + void drawArrowLeft(int16_t x, int16_t y, uint16_t color); + void drawArrowUp(int16_t x, int16_t y, uint16_t color); + void drawArrowDown(int16_t x, int16_t y, uint16_t color); + private: ScreenManager* screenManager; }; diff --git a/src/lib/screen/calibrate.cpp b/src/lib/screen/calibrate.cpp index a765882..6d286c1 100644 --- a/src/lib/screen/calibrate.cpp +++ b/src/lib/screen/calibrate.cpp @@ -3,7 +3,7 @@ void CalibrateScreen::onShow() { - auto display = this->getDisplay(); + //auto display = this->getDisplay(); // TODO: implement CalibrateScreen } diff --git a/src/lib/screen/home.cpp b/src/lib/screen/home.cpp index ce9256d..6b63bbf 100644 --- a/src/lib/screen/home.cpp +++ b/src/lib/screen/home.cpp @@ -1,31 +1,12 @@ #include "./home.h" #include "./move.h" -#include "fonts/FreeSansBold18pt7b.trimmed.h" #include "include/config.h" +#include "include/metrics.h" #include "lib/settings.h" -#include "lib/motorstate.h" +#include "lib/control.h" #include "./menu.h" -#define HOME_FONT_BASELINE 25 -#define HOME_FONT_HEIGHT 42 - -#define PRESET_MARGIN 7 - -// HOME_FONT_BASELINE is not a mistake, as there are no characters used which -// go below the baseline. -#define PRESET_LINEHEIGHT (HOME_FONT_BASELINE + (2 * PRESET_MARGIN)) - -#define HOME_ARROW_WIDTH 10 -#define HOME_ARROW_HEIGHT 20 -#define HOME_ARROW_MARGIN 8 -#define HOME_ARROW_YOFFSET ((PRESET_LINEHEIGHT - HOME_ARROW_HEIGHT) / 2) - -#define HOME_INDICATOR_OFFSET (HOME_ARROW_WIDTH + (2 * HOME_ARROW_MARGIN)) - -#define HOME_MENU_Y 100 - - void HomeScreen::onShow() { @@ -33,7 +14,7 @@ void HomeScreen::onShow() auto display = this->getDisplay(); - display->setFont(&FreeSansBold18pt7bTrimmed); + display->setFont(Metrics::LargeFont); display->fillScreen(Config::ColorHomeBackground); this->drawPreset1(); @@ -52,19 +33,20 @@ void HomeScreen::onButton(Button button) this->idle = false; this->showTime = State.CurrentTime; - // TODO: should preset buttons activate immediately? - return; + // Preset buttons activate immediately + if (button == Button::Menu) + return; } switch (button) { case Button::Up: - motorStateMoveTo(Settings.Height.Preset[0]); + controlMoveTo(Settings.Height.Preset[0]); this->getScreenManager()->show(); break; case Button::Down: - motorStateMoveTo(Settings.Height.Preset[1]); + controlMoveTo(Settings.Height.Preset[1]); this->getScreenManager()->show(); break; @@ -87,21 +69,20 @@ void HomeScreen::onTick() void HomeScreen::drawPreset1() { - //this->drawPreset(0, Settings.Height.Preset[0]); - this->drawPreset(0, State.CurrentHeight); + this->drawPreset(0, Settings.Height.Preset[0]); } void HomeScreen::drawPreset2() { - this->drawPreset(Config::DisplayHeight - PRESET_LINEHEIGHT, Settings.Height.Preset[1]); + this->drawPreset(Config::DisplayHeight - Metrics::LargeTextLineHeight, Settings.Height.Preset[1]); } void HomeScreen::drawNonPresetHeight() { auto display = this->getDisplay(); - auto y = PRESET_LINEHEIGHT; + auto y = Metrics::LargeTextLineHeight; if (State.CurrentHeight != Settings.Height.Preset[0] && State.CurrentHeight != Settings.Height.Preset[1]) @@ -112,16 +93,6 @@ void HomeScreen::drawNonPresetHeight() } -void HomeScreen::drawArrow(int16_t y, uint16_t color) -{ - this->getDisplay()->fillTriangle( - HOME_ARROW_MARGIN + HOME_ARROW_WIDTH, y + HOME_ARROW_YOFFSET, // Top right - HOME_ARROW_MARGIN, y + HOME_ARROW_YOFFSET + (HOME_ARROW_HEIGHT / 2), // Middle left - HOME_ARROW_MARGIN + HOME_ARROW_WIDTH, y + HOME_ARROW_YOFFSET + HOME_ARROW_HEIGHT, // Bottom right - color); -} - - void HomeScreen::drawPreset(int16_t y, uint16_t value) { auto display = this->getDisplay(); @@ -143,10 +114,10 @@ void HomeScreen::drawPreset(int16_t y, uint16_t value) arrowColor = Config::ColorPresetArrow; } - display->fillRect(0, y, Config::DisplayWidth, PRESET_LINEHEIGHT, backgroundColor); + display->fillRect(0, y, Config::DisplayWidth, Metrics::LargeTextLineHeight, backgroundColor); if (arrowColor) - this->drawArrow(y, arrowColor); + this->drawArrowLeft(Metrics::ArrowMargin, y + Metrics::LargeTextLineHArrowYOffset, arrowColor); display->setTextColor(textColor); this->drawHeight(y, value); @@ -155,12 +126,10 @@ void HomeScreen::drawPreset(int16_t y, uint16_t value) void HomeScreen::drawHeight(int16_t y, uint16_t value) { - auto display = this->getDisplay(); - char textValue[6]; getDisplayHeight(&textValue[0], value); - this->printCentered(&textValue[0], y + HOME_FONT_BASELINE + PRESET_MARGIN); + this->printCentered(&textValue[0], y + Metrics::LargeTextLineYOffset); } @@ -168,8 +137,8 @@ void HomeScreen::drawMenu() { auto display = this->getDisplay(); - this->drawArrow(HOME_MENU_Y, Config::ColorHomeMenuArrow); + this->drawArrowLeft(Metrics::ArrowMargin, Metrics::MiddleLargeTextLineY, Config::ColorHomeMenuArrow); display->setTextColor(Config::ColorHomeMenuText); - this->printCentered("Menu", HOME_MENU_Y + HOME_FONT_BASELINE + PRESET_MARGIN); + this->printCentered("Menu", Metrics::MiddleLargeTextLineY + Metrics::LargeTextLineYOffset); } diff --git a/src/lib/screen/home.h b/src/lib/screen/home.h index 1989b29..55ee460 100644 --- a/src/lib/screen/home.h +++ b/src/lib/screen/home.h @@ -26,7 +26,6 @@ class HomeScreen : public BaseScreen void drawPreset2(); void drawNonPresetHeight(); - void drawArrow(int16_t y, uint16_t color); void drawPreset(int16_t y, uint16_t value); void drawHeight(int16_t y, uint16_t value); void drawMenu(); diff --git a/src/lib/screen/manual.cpp b/src/lib/screen/manual.cpp index 21e38d9..18bd6b3 100644 --- a/src/lib/screen/manual.cpp +++ b/src/lib/screen/manual.cpp @@ -3,7 +3,7 @@ void ManualScreen::onShow() { - auto display = this->getDisplay(); + //auto display = this->getDisplay(); // TODO: implement ManualScreen } diff --git a/src/lib/screen/menu.cpp b/src/lib/screen/menu.cpp index c5196ff..f54e86f 100644 --- a/src/lib/screen/menu.cpp +++ b/src/lib/screen/menu.cpp @@ -3,7 +3,7 @@ void MenuScreen::onShow() { - auto display = this->getDisplay(); + //auto display = this->getDisplay(); // TODO: implement MenuScreen } diff --git a/src/lib/screen/move-overcurrent.cpp b/src/lib/screen/move-overcurrent.cpp index 62efee0..26d3f59 100644 --- a/src/lib/screen/move-overcurrent.cpp +++ b/src/lib/screen/move-overcurrent.cpp @@ -3,7 +3,7 @@ void MoveOverCurrentScreen::onShow() { - auto display = this->getDisplay(); + //auto display = this->getDisplay(); // TODO: implement MoveOverCurrentScreen } diff --git a/src/lib/screen/move-sensorerror.cpp b/src/lib/screen/move-sensorerror.cpp index a65351b..dae888b 100644 --- a/src/lib/screen/move-sensorerror.cpp +++ b/src/lib/screen/move-sensorerror.cpp @@ -3,7 +3,7 @@ void MoveSensorErrorScreen::onShow() { - auto display = this->getDisplay(); + //auto display = this->getDisplay(); // TODO: implement MoveSensorErrorScreen } diff --git a/src/lib/screen/move.cpp b/src/lib/screen/move.cpp index cde9714..668d574 100644 --- a/src/lib/screen/move.cpp +++ b/src/lib/screen/move.cpp @@ -1,45 +1,29 @@ #include "./move.h" #include "./home.h" -#include "fonts/FreeSansBold18pt7b.trimmed.h" #include "include/config.h" +#include "include/metrics.h" #include "lib/settings.h" -#include "lib/motorstate.h" +#include "lib/control.h" -#define MOVE_FONT_BASELINE 25 -#define MOVE_FONT_HEIGHT 42 - -#define MOVE_REFRESHRATE 1000 - -#define MOVE_LINE_MARGIN 7 -#define MOVE_LINE_HEIGHT (MOVE_FONT_BASELINE + (2 * MOVE_LINE_MARGIN)) - -#define MOVE_STOP_SMALL_TEXT_SIZE -#define MOVE_STOP_SMALL_LINE_HEIGHT 8 - -#define MOVE_ARROW_WIDTH 20 -#define MOVE_ARROW_HEIGHT 10 -#define MOVE_ARROW_YOFFSET ((MOVE_LINE_HEIGHT - MOVE_ARROW_HEIGHT) / 2) - void MoveScreen::onShow() { auto display = this->getDisplay(); - //auto startY = (Config::DisplayHeight - (MOVE_LINE_HEIGHT * 3)) / 2; - auto startY = MOVE_LINE_HEIGHT; - auto arrowY = startY + MOVE_LINE_HEIGHT + MOVE_ARROW_YOFFSET; - auto arrowX = (Config::DisplayWidth - MOVE_ARROW_WIDTH) / 2; - auto stopY = Config::DisplayHeight - MOVE_LINE_HEIGHT; + auto startY = Metrics::LargeTextLineHeight; + auto arrowY = startY + Metrics::LargeTextLineHeight + Metrics::LargeTextLineVArrowYOffset; + auto arrowX = (Config::DisplayWidth - Metrics::VArrowWidth) / 2; + auto stopY = Config::DisplayHeight - Metrics::LargeTextLineHeight; display->fillScreen(Config::ColorMoveBackground); // Stop - display->setFont(nullptr); + display->setFont(Metrics::SmallFont); display->setTextColor(Config::ColorMoveStop); - this->printCentered("Press any button to", stopY - MOVE_STOP_SMALL_LINE_HEIGHT); + this->printCentered("Press any button to", stopY - Metrics::SmallTextLineHeight); - display->setFont(&FreeSansBold18pt7bTrimmed); - this->printCentered("STOP", stopY + MOVE_FONT_BASELINE + MOVE_LINE_MARGIN); + display->setFont(Metrics::LargeFont); + this->printCentered("STOP", stopY + Metrics::LargeTextLineYOffset); char targetHeightText[6]; getDisplayHeight(&targetHeightText[0], State.MoveTarget); @@ -48,27 +32,17 @@ void MoveScreen::onShow() // Target and arrow if (State.MoveDirection == Direction::Up) { - this->currentHeightY = startY + (MOVE_LINE_HEIGHT * 2); + this->currentHeightY = startY + (Metrics::LargeTextLineHeight * 2); - this->printCentered(&targetHeightText[0], startY + MOVE_FONT_BASELINE + MOVE_LINE_MARGIN); - - display->fillTriangle( - arrowX + (MOVE_ARROW_WIDTH / 2), arrowY, // Top middle - arrowX, arrowY + MOVE_ARROW_HEIGHT, // Bottom left - arrowX + MOVE_ARROW_WIDTH, arrowY + MOVE_ARROW_HEIGHT, // Bottom right - Config::ColorMoveArrow); + this->printCentered(&targetHeightText[0], startY + Metrics::LargeTextLineYOffset); + this->drawArrowUp(arrowX, arrowY, Config::ColorMoveArrow); } else { this->currentHeightY = startY; - this->printCentered(&targetHeightText[0], startY + (MOVE_LINE_HEIGHT * 2) + MOVE_FONT_BASELINE + MOVE_LINE_MARGIN); - - display->fillTriangle( - arrowX, arrowY, // Top left - arrowX + MOVE_ARROW_WIDTH, arrowY, // Top right - arrowX + (MOVE_ARROW_WIDTH / 2), arrowY + MOVE_ARROW_HEIGHT, // Bottom middle - Config::ColorMoveArrow); + this->printCentered(&targetHeightText[0], startY + (Metrics::LargeTextLineHeight * 2) + Metrics::LargeTextLineYOffset); + this->drawArrowDown(arrowX, arrowY, Config::ColorMoveArrow); } this->lastRefresh = State.CurrentTime; @@ -78,7 +52,7 @@ void MoveScreen::onShow() void MoveScreen::onButton(Button button) { - motorStateStop(); + controlStop(); this->getScreenManager()->show(); } @@ -93,7 +67,7 @@ void MoveScreen::onTick() // Don't update every tick, monitoring the current height is more // important and the flicker would be unpleasant as well. - if (State.CurrentTime - this->lastRefresh >= MOVE_REFRESHRATE) + if (State.CurrentTime - this->lastRefresh >= Config::DisplayMoveRefreshRate) { this->drawCurrentHeight(); this->lastRefresh = State.CurrentTime; @@ -109,8 +83,8 @@ void MoveScreen::drawCurrentHeight() getDisplayHeight(¤tHeightText[0], State.CurrentHeight); if (this->lastTextWidth > 0) - display->fillRect((Config::DisplayWidth - this->lastTextWidth) / 2, this->currentHeightY, this->lastTextWidth, MOVE_LINE_HEIGHT, Config::ColorMoveBackground); + display->fillRect((Config::DisplayWidth - this->lastTextWidth) / 2, this->currentHeightY, this->lastTextWidth, Metrics::LargeTextLineHeight, Config::ColorMoveBackground); display->setTextColor(Config::ColorMoveTarget); - this->lastTextWidth = this->printCentered(¤tHeightText[0], this->currentHeightY + MOVE_FONT_BASELINE); + this->lastTextWidth = this->printCentered(¤tHeightText[0], this->currentHeightY + Metrics::LargeFontBaseline); } \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index 1607d59..e33d884 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -11,7 +11,7 @@ #include "./lib/vl53l0x.h" #include "./lib/state.h" #include "./lib/motor.h" -#include "./lib/motorstate.h" +#include "./lib/control.h" #include "./lib/screen/home.h" #include "./lib/screen/calibrate.h" #include "./lib/screen/move-overcurrent.h" @@ -85,13 +85,18 @@ void setup() State.CurrentTime = millis(); - // TODO: check if close to either preset, then use the preset height - State.CurrentHeight = currentHeight; if (initialized) + { + State.CurrentHeight = currentHeight; + controlSnapToPreset(); + screenManager.show(); + } else + { screenManager.show(); + } } @@ -146,8 +151,7 @@ inline uint16_t testHeightSensor() uint8_t closeCount = 0; uint16_t measurement; - // TODO: while (closeCount < 3) - while (closeCount < 1) + while (true) { if (heightSensorGetRange(&measurement)) { @@ -169,7 +173,10 @@ inline uint16_t testHeightSensor() closeCount = 0; } - delay(500); + if (closeCount < Config::HeightMeasurementDeltaStableCount) + delay(500); + else + break; } initSequenceSuccess(InitSequenceStep::HeightSensorTest); @@ -194,7 +201,7 @@ void loop() if (State.MoveDirection != Direction::None) { - if (motorStateCheckOverCurrent()) + if (controlCheckOverCurrent()) screenManager.show(); else updateHeight(); @@ -230,13 +237,13 @@ void updateHeight() State.CurrentHeight = measurement; lastValidMeasurement = State.CurrentTime; - if (motorStateCheckTargetReached()) + if (controlCheckTargetReached()) screenManager.show(); } else if (State.CurrentTime - lastValidMeasurement >= Config::HeightMeasurementAbortTimeout) { dln("Out of range timeout!"); - motorStateStop(); + controlStop(); screenManager.show(); }