Commit c8ccd519 authored by Mark van Renswoude's avatar Mark van Renswoude

Implemented menu and presets configuration

parent 9ab3da27
......@@ -18,8 +18,8 @@ https://www.pololu.com/product/2995
| Identifier | Description | Arduino pin | ATMega328p pin |
|------------|----------------------------|-------------|----------------|
| PWM | Motor PWM | 14 | 23 - PC0 |
| DIR | Direction | 15 | 24 - PC1 |
| PWM | Motor PWM | 14 / A0 | 23 - PC0 |
| DIR | Direction | 15 / A1 | 24 - PC1 |
| SLP | Sleep (active low) | 16 | 25 - PC2 |
| CS | Current sensing | 17 / A3 | 26 - PC3 |
......
......@@ -151,6 +151,9 @@ class Config
static const uint16_t ColorMenuHeaderText = ColorWhite;
static const uint16_t ColorMenuHeaderBackground = ColorSoftBlue;
static const uint16_t ColorMenuText = ColorWhite;
static const uint16_t ColorMenuBackground = ColorBlack;
static const uint16_t ColorMenuSelectedText = ColorWhite;
static const uint16_t ColorMenuSelectedBackground = ColorDarkBlue;
......
......@@ -9,7 +9,8 @@ enum class ScreenId
MoveOvercurrent,
MoveSensorError,
Menu,
Manual
Manual,
Presets
};
#endif
\ No newline at end of file
......@@ -28,6 +28,7 @@ void motorStop()
{
digitalWrite(Config::MotorPinPWM, LOW);
digitalWrite(Config::MotorPinSleep, LOW);
digitalWrite(Config::MotorPinDirection, LOW);
}
......
#include "./calibrate.h"
#include "./home.h"
#include "include/metrics.h"
#include "lib/settings.h"
void BaseHeightEntryScreen::onShow()
{
this->display->setFont(Metrics::LargeFont);
this->display->setTextSize(Metrics::LargeFontTextSize);
this->display->fillScreen(Config::ColorHomeBackground);
this->drawTitle();
this->initHeights();
this->drawArrowUp(Metrics::ArrowMargin, Metrics::LargeTextLineHeight + Metrics::LargeTextLineVArrowYOffset, Config::ColorCalibrateIndicators);
this->drawArrowRight(Metrics::ArrowMargin, Metrics::MiddleLargeTextLineY + Metrics::LargeTextLineHArrowYOffset, Config::ColorCalibrateIndicators);
this->display->setTextColor(Config::ColorCalibrateIndicators);
this->display->setCursor(Metrics::ArrowMargin, Config::DisplayHeight - Metrics::LargeTextLineHeight + Metrics::LargeTextLineYOffset);
this->display->print("OK");
this->drawSetHeight();
}
// Credit: https://stackoverflow.com/questions/101439/the-most-efficient-way-to-implement-an-integer-based-power-function-powint-int
uint16_t ipow(uint16_t base, uint8_t exp)
{
int result = 1;
for (;;)
{
if (exp & 1)
result *= base;
exp >>= 1;
if (!exp)
break;
base *= base;
}
return result;
}
void BaseHeightEntryScreen::onButton(Button button)
{
switch (button)
{
case Button::Top:
{
uint16_t height = this->getHeight();
uint16_t increment = ipow(10, 3 - this->editingDigit);
uint16_t modulus = increment * 10;
uint16_t remainder = height % modulus;
uint16_t offset = height - remainder;
height = offset + ((remainder + increment) % modulus);
if (height > 1999)
height %= 2000;
this->setHeight(height);
this->drawSetHeight();
break;
}
case Button::Middle:
{
this->editingDigit++;
if (this->editingDigit > 3)
this->editingDigit = 0;
this->drawSetHeight();
break;
}
case Button::Bottom:
{
if (!this->isValidHeight())
return;
if (this->nextPage())
{
this->editingDigit = 0;
this->drawTitle();
this->drawSetHeight();
}
break;
}
}
}
void BaseHeightEntryScreen::onTick()
{
}
void BaseHeightEntryScreen::drawTitle()
{
this->drawLargeTextLineCentered(this->getTitle(), 0, Config::ColorMenuHeaderText, Config::ColorMenuHeaderBackground);
}
void BaseHeightEntryScreen::drawSetHeight()
{
char heightText[7];
uint16_t height = this->getHeight();
if (height > 999)
heightText[0] = '0' + ((height / 1000) % 10);
else
heightText[0] = '0';
heightText[1] = '.';
heightText[2] = '0' + ((height / 100) % 10);
heightText[3] = '0' + ((height / 10) % 10);
heightText[4] = '0' + (height % 10);
heightText[5] = 'm';
heightText[6] = 0;
int16_t textX;
int16_t textY;
uint16_t textW;
uint16_t textH;
this->display->getTextBounds(&heightText[0], 0, 0, &textX, &textY, &textW, &textH);
textX = (Config::DisplayWidth - textW) / 2;
uint8_t editingChar = this->editingDigit;
// Skip the dot
if (editingChar > 0)
editingChar++;
if (this->lastTextWidth > 0)
this->display->fillRect((Config::DisplayWidth - this->lastTextWidth) / 2, Metrics::MiddleLargeTextLineY, this->lastTextWidth, Metrics::LargeTextLineHeight, Config::ColorCalibrateBackground);
this->lastTextWidth = textW;
this->display->setTextColor(this->isValidHeight() ? Config::ColorCalibrateValue : Config::ColorCalibrateInvalidValue);
this->display->setCursor(textX, Metrics::MiddleLargeTextLineY + Metrics::LargeTextLineYOffset);
// Draw each character ourselves so we can keep track of where the line should be drawn
for (uint8_t i = 0; i < sizeof(heightText); i++)
{
int16_t cursorStart = this->display->getCursorX();
this->display->print(heightText[i]);
if (i == editingChar)
this->display->drawFastHLine(cursorStart, Metrics::MiddleLargeTextLineY + Metrics::LargeTextLineHeight - 1, this->display->getCursorX() - cursorStart, Config::ColorCalibrateDigitMarker);
}
}
\ No newline at end of file
#ifndef __screen_baseheightentry
#define __screen_baseheightentry
#include "../screen.h"
#include "../control.h"
class BaseHeightEntryScreen : public BaseScreen
{
public:
BaseHeightEntryScreen(ScreenManager* screenManager, Adafruit_GFX* display) : BaseScreen(screenManager, display) { }
void onShow();
void onButton(Button button);
void onTick();
protected:
virtual void initHeights() = 0;
virtual uint16_t getHeight() = 0;
virtual void setHeight(uint16_t value) = 0;
virtual bool nextPage() = 0;
virtual bool isValidHeight() = 0;
virtual const char* getTitle() = 0;
private:
void drawTitle();
void drawSetHeight();
uint16_t lastTextWidth = 0;
uint8_t editingDigit = 0;
};
#endif
......@@ -4,110 +4,28 @@
#include "lib/settings.h"
void CalibrateScreen::onShow()
{
this->display->setFont(Metrics::LargeFont);
this->display->setTextSize(Metrics::LargeFontTextSize);
this->display->fillScreen(Config::ColorHomeBackground);
this->drawTitle();
void CalibrateScreen::initHeights()
{
this->height[CalibrateStepCurrent] = Control.getCurrentHeight();
this->height[CalibrateStepMax] = Settings.Height.Maximum;
this->height[CalibrateStepMin] = Settings.Height.Minimum;
this->drawArrowUp(Metrics::ArrowMargin, Metrics::LargeTextLineHeight + Metrics::LargeTextLineVArrowYOffset, Config::ColorCalibrateIndicators);
this->drawArrowRight(Metrics::ArrowMargin, Metrics::MiddleLargeTextLineY + Metrics::LargeTextLineHArrowYOffset, Config::ColorCalibrateIndicators);
this->display->setTextColor(Config::ColorCalibrateIndicators);
this->display->setCursor(Metrics::ArrowMargin, Config::DisplayHeight - Metrics::LargeTextLineHeight + Metrics::LargeTextLineYOffset);
this->display->print("OK");
this->drawSetHeight();
}
// Credit: https://stackoverflow.com/questions/101439/the-most-efficient-way-to-implement-an-integer-based-power-function-powint-int
uint16_t ipow(uint16_t base, uint8_t exp)
bool CalibrateScreen::nextPage()
{
int result = 1;
for (;;)
{
if (exp & 1)
result *= base;
exp >>= 1;
if (!exp)
break;
this->step = (CalibrateStep)(this->step + 1);
if (this->step < __CalibrateStepCount)
return true;
base *= base;
}
Settings.Height.Offset = this->height[CalibrateStepCurrent] - Control.getCurrentMeasurement();
Settings.Height.Maximum = this->height[CalibrateStepMax];
Settings.Height.Minimum = this->height[CalibrateStepMin];
writeSettingsHeights();
return result;
}
void CalibrateScreen::onButton(Button button)
{
switch (button)
{
case Button::Top:
{
uint16_t height = this->height[this->step];
uint16_t increment = ipow(10, 3 - this->editingDigit);
uint16_t modulus = increment * 10;
uint16_t remainder = height % modulus;
uint16_t offset = height - remainder;
height = offset + ((remainder + increment) % modulus);
if (height > 1999)
height %= 2000;
this->height[this->step] = height;
this->drawSetHeight();
break;
}
case Button::Middle:
{
this->editingDigit++;
if (this->editingDigit > 3)
this->editingDigit = 0;
this->drawSetHeight();
break;
}
case Button::Bottom:
{
if (!this->isValidHeight())
return;
this->step = (CalibrateStep)(this->step + 1);
if (this->step == CalibrateStepCount)
{
Settings.Height.Offset = this->height[CalibrateStepCurrent] - Control.getCurrentMeasurement();
Settings.Height.Maximum = this->height[CalibrateStepMax];
Settings.Height.Minimum = this->height[CalibrateStepMin];
writeSettingsHeights();
this->screenManager->show<HomeScreen>();
}
else
{
this->drawTitle();
this->drawSetHeight();
}
break;
}
}
}
void CalibrateScreen::onTick()
{
this->screenManager->show<HomeScreen>();
return false;
}
......@@ -132,78 +50,20 @@ bool CalibrateScreen::isValidHeight()
}
void CalibrateScreen::drawTitle()
const char* CalibrateScreen::getTitle()
{
switch (this->step)
{
case CalibrateStepCurrent:
this->drawLargeTextLineCentered("Calibrate", 0, Config::ColorMenuHeaderText, Config::ColorMenuHeaderBackground);
break;
return "Calibrate";
case CalibrateStepMin:
this->drawLargeTextLineCentered("Minimum", 0, Config::ColorMenuHeaderText, Config::ColorMenuHeaderBackground);
break;
return "Minimum";
case CalibrateStepMax:
this->drawLargeTextLineCentered("Maximum", 0, Config::ColorMenuHeaderText, Config::ColorMenuHeaderBackground);
break;
return "Maximum";
default:
break;
}
}
void CalibrateScreen::drawSetHeight()
{
char heightText[7];
uint16_t height = this->height[this->step];
if (height > 999)
heightText[0] = '0' + ((height / 1000) % 10);
else
heightText[0] = '0';
heightText[1] = '.';
heightText[2] = '0' + ((height / 100) % 10);
heightText[3] = '0' + ((height / 10) % 10);
heightText[4] = '0' + (height % 10);
heightText[5] = 'm';
heightText[6] = 0;
int16_t textX;
int16_t textY;
uint16_t textW;
uint16_t textH;
this->display->getTextBounds(&heightText[0], 0, 0, &textX, &textY, &textW, &textH);
textX = (Config::DisplayWidth - textW) / 2;
uint8_t editingChar = this->editingDigit;
// Skip the dot
if (editingChar > 0)
editingChar++;
if (this->lastTextWidth > 0)
this->display->fillRect((Config::DisplayWidth - this->lastTextWidth) / 2, Metrics::MiddleLargeTextLineY, this->lastTextWidth, Metrics::LargeTextLineHeight, Config::ColorCalibrateBackground);
this->lastTextWidth = textW;
this->display->setTextColor(this->isValidHeight() ? Config::ColorCalibrateValue : Config::ColorCalibrateInvalidValue);
this->display->setCursor(textX, Metrics::MiddleLargeTextLineY + Metrics::LargeTextLineYOffset);
// Draw each character ourselves so we can keep track of where the line should be drawn
for (uint8_t i = 0; i < sizeof(heightText); i++)
{
int16_t cursorStart = this->display->getCursorX();
this->display->print(heightText[i]);
if (i == editingChar)
this->display->drawFastHLine(cursorStart, Metrics::MiddleLargeTextLineY + Metrics::LargeTextLineHeight - 1, this->display->getCursorX() - cursorStart, Config::ColorCalibrateDigitMarker);
return nullptr;
}
}
\ No newline at end of file
......@@ -2,17 +2,18 @@
#define __screen_calibrate
#include "include/screenids.h"
#include "./baseheightentry.h"
#include "../screen.h"
#include "../control.h"
enum CalibrateStep : uint8_t
enum CalibrateStep
{
CalibrateStepCurrent = 0,
CalibrateStepMax,
CalibrateStepMin,
CalibrateStepCount
__CalibrateStepCount
};
......@@ -21,27 +22,26 @@ enum CalibrateStep : uint8_t
* Configures the absolute height (calculates the offset) and
* the allowed minimum and maximum absolute height.
*/
class CalibrateScreen : public BaseScreen
class CalibrateScreen : public BaseHeightEntryScreen
{
public:
CalibrateScreen(ScreenManager* screenManager, Adafruit_GFX* display) : BaseScreen(screenManager, display) { }
void onShow();
void onButton(Button button);
void onTick();
CalibrateScreen(ScreenManager* screenManager, Adafruit_GFX* display) : BaseHeightEntryScreen(screenManager, display) { }
ScreenId screenId() { return ScreenId::Calibrate; };
private:
protected:
void initHeights();
uint16_t getHeight() { return this->height[this->step]; }
void setHeight(uint16_t value) { this->height[this->step] = value; };
bool nextPage();
bool isValidHeight();
void drawTitle();
void drawSetHeight();
const char* getTitle();
private:
CalibrateStep step = CalibrateStepCurrent;
uint16_t height[CalibrateStepCount];
uint16_t lastTextWidth = 0;
uint8_t editingDigit = 0;
uint16_t height[__CalibrateStepCount];
};
#endif
#include "./menu.h"
#include "./home.h"
#include "./calibrate.h"
#include "./manual.h"
#include "./presets.h"
#include "include/config.h"
#include "include/metrics.h"
......@@ -11,16 +15,102 @@ void MenuScreen::onShow()
this->display->fillScreen(Config::ColorHomeBackground);
this->drawLargeTextLineCentered("Menu", 0, Config::ColorMenuHeaderText, Config::ColorMenuHeaderBackground);
// TODO: implement MenuScreen
for (uint8_t i = 0; i < (uint8_t)MenuItem::__Count; i++)
this->drawMenuItem(i);
}
void MenuScreen::onButton(Button button)
{
this->screenManager->show<HomeScreen>();
uint8_t previousIndex = this->itemIndex;
switch (button)
{
case Button::Middle:
this->activateMenuItem((MenuItem)this->itemIndex);
return;
case Button::Top:
if (this->itemIndex == 0)
this->itemIndex = (uint8_t)MenuItem::__Count;
this->itemIndex--;
break;
case Button::Bottom:
this->itemIndex++;
if (this->itemIndex == (uint8_t)MenuItem::__Count)
this->itemIndex = 0;
break;
}
this->drawMenuItem(previousIndex);
this->drawMenuItem(this->itemIndex);
}
void MenuScreen::onTick()
{
}
void MenuScreen::drawMenuItem(uint8_t index)
{
uint16_t y = (index + 2) * Metrics::LargeTextLineHeight;
bool isSelected = index == this->itemIndex;
this->drawLargeTextLineCentered(
this->getMenuItemTitle((MenuItem)index), y,
isSelected ? Config::ColorMenuSelectedText : Config::ColorMenuText,
isSelected ? Config::ColorMenuSelectedBackground : Config::ColorMenuBackground);
}
const char* MenuScreen::getMenuItemTitle(MenuItem item)
{
switch (item)
{
case MenuItem::Manual:
return "Manual";
case MenuItem::Presets:
return "Presets";
case MenuItem::Calibrate:
return "Calibrate";
case MenuItem::Exit:
return "Exit";
default:
return nullptr;
}
}
void MenuScreen::activateMenuItem(MenuItem item)
{
switch (item)
{
case MenuItem::Manual:
// TODO: show manual screen
//this->screenManager->show<ManualScreen>();
break;
case MenuItem::Presets:
this->screenManager->show<PresetsScreen>();
break;
case MenuItem::Calibrate:
this->screenManager->show<CalibrateScreen>();
break;
case MenuItem::Exit:
this->screenManager->show<HomeScreen>();
break;
default:
break;
}
}
\ No newline at end of file
......@@ -6,6 +6,18 @@
#include "../Control.h"
enum class MenuItem
{
Manual = 0,
Presets,
Calibrate,
Exit,
__Count
};
/*
* Menu screen
* Shows the menu which allows access to the calibration and
......@@ -23,6 +35,11 @@ class MenuScreen : public BaseScreen
ScreenId screenId() { return ScreenId::Menu; };
private:
void drawMenuItem(uint8_t index);
const char* getMenuItemTitle(MenuItem item);
void activateMenuItem(MenuItem item);
uint8_t itemIndex = 0;
};
#endif
#include "./presets.h"
#include "./home.h"
#include "include/metrics.h"
#include "lib/settings.h"
void PresetsScreen::initHeights()
{
this->height[0] = Settings.Height.Preset[0];
this->height[1] = Settings.Height.Preset[1];
}
bool PresetsScreen::nextPage()
{
this->preset++;
if (this->preset < 2)
return true;
Settings.Height.Preset[0] = this->height[0];
Settings.Height.Preset[1] = this->height[1];
writeSettingsHeights();
this->screenManager->show<HomeScreen>();
return false;
}
void PresetsScreen::setHeight(uint16_t value)
{
if (value < Settings.Height.Minimum)
this->height[this->preset] = Settings.Height.Minimum;
else if (value > Settings.Height.Maximum)
this->height[this->preset] = Settings.Height.Maximum;
else
this->height[this->preset] = value;
}
const char* PresetsScreen::getTitle()
{
switch (this->preset)
{
case 0:
return "Preset 1";
case 1:
return "Preset 2";
default:
return nullptr;
}
}
\ No newline at end of file
#ifndef __screen_presets
#define __screen_presets
#include "include/screenids.h"
#include "./baseheightentry.h"
#include "../screen.h"
#include "../control.h"
/*
* Presets screen
* Configures the presets.
*/
class PresetsScreen : public BaseHeightEntryScreen
{
public:
PresetsScreen(ScreenManager* screenManager, Adafruit_GFX* display) : BaseHeightEntryScreen(screenManager, display) { }
ScreenId screenId() { return ScreenId::Presets; };
protected:
void initHeights();
uint16_t getHeight() { return this->height[this->preset]; }