Big overhaul of the UI framework (#1749)

* Starting GUI render refactor to be more immediate mode

Update TemperatureAdjust.cpp

.

Cleanup Soldering

Sleep

SolderingProfiles

Soldering Rework

Rough pass GUI

Temp Adjust

Cleanup old OperatingMode

Debug Menu

* Update TemperatureAdjust.cpp

* Roughing some transition work

* Fixup! Hook in the init starter helper

* Better home screen button handler

* FIXUP! Fix typo's

.

* Update SettingsMenu.cpp

* More settings rework

* More settings rendering

* Fixup

* Transitions

Update SolderingProfile.cpp

Hook in transistions

* Update TemperatureAdjust.cpp

* Update push.yml

* Add auto-repeat to settings menu

* Miniware: Use IT for I2C writes

* Update USBPDDebug_HUSB238.cpp

* Force write screen on side animation cancel

.

* Refactor moving down the settings list

* Update settingsGUI.cpp

* Update I2C_Wrapper.cpp

* Update OLED.cpp

* Rework button handling

* Fix PD debug at boot

* Fixup not showing right menu options

* silence some warnings

* Style cleanup

* Fkit use bit-bang I2C for Miniware

* Update GUIRendering.md

* Fixup transition on enter soldering mode

* Save Settings

* Fixes for some animations not running

Dont bail on animations if keypress is still held

* Fixup settings acceleration

* OLED Up animation

* Link up/down on debug meny

* Make all accelerometers I2C bus aware

Update accelerometers_common.h

* Make I2C mag optional

* Miniware -> Only Bit-Bang I2C

* Fixup for scrollbar

FIXUP! Debug menu returns to home screen

FIXUP! Up oled animation

Fix temp exit

* Settings menu -> Both buttons return a menu layer

* Merge fixup

* Update BMA223.cpp

* Re-Enable OLED sleep

* Save Setting on temp adjust exit

* WiP on startup mode

* Some autostart working

* Add hibernation mode & more autostart fixes

* If cant CJC; go to startup

* Hibernate in sleep

* Cleanup scroll indicator

* FIXUP! Ensure startup warnings are linked in

* FIXUP! Ensure we render out temp change before timing out

* Ensure 100ms delay between CJC samples

* Fix not re-calculating menu length on entering menu

* Implement NegotiationinProgress for USB-PD

* Mask heating until PD finishes negotiation

* Fixup staying in hibernate correctly

* Warning timeout

* Show reset settings warning

* Correctly compensate help text start time

* Update GUIThread.cpp

* Update USBPD.cpp

* .

* Fixup sleep time

* Update printSleepCountdown.cpp

* replacing countdown with big plus while in boost mode

* bringing back the + 1 since it was missing when not in boost mode

* Bail on USB-PD check after 3 seconds incase of DC source

* Fix hibernate

* Update PIDThread.cpp

* did center plus symbol (boost mode)

* Big refactor to not make settings increment handler handle the "is last item" return

* Fixup boot logo

* Fix flashing

* Fixup recalculate the menu length on long hold

* Fixup missing menu entries

* Fix junk left on screen after user confirmation

* Re-order button handler to use custom, then default order to allow setting associated setting

* Attach setting for settings using custom handler

* Fix swap +/- keys

* Fix boost temp

* Implement last menu option for Language selector

* Wait for init before CJC runs

* Check last setting via increment value

* Update BSP.cpp

* removed = from >=

Otherwise incrementing would stop and the scroll bar would already flash at the second to last value.

* (Hacky) Fix for Settings reset

---------

Co-authored-by: discip <53649486+discip@users.noreply.github.com>
This commit is contained in:
Ben V. Brown
2024-02-18 09:42:08 +11:00
committed by GitHub
parent 21a1012102
commit baf2f26e59
50 changed files with 1717 additions and 6610 deletions

View File

@@ -1,39 +1,49 @@
#include "OperatingModes.h"
void performCJCC(void) {
OperatingMode performCJCC(const ButtonState buttons, guiContext *cxt) {
// Calibrate Cold Junction Compensation directly at boot, before internal components get warm.
OLED::refresh();
osDelay(50);
// While we wait for the pre-start checks to finish, we cant run CJC (as the pre-start checks control the tip)
if (preStartChecks() == 0) {
OLED::setCursor(0, 0);
OLED::print(translatedString(Tr->CJCCalibrating), FontStyle::SMALL);
return OperatingMode::CJCCalibration;
}
if (!isTipDisconnected() && abs(int(TipThermoModel::getTipInC() - getHandleTemperature(0) / 10)) < 10) {
uint16_t setoffset = 0;
// Take 16 samples, only sample
if (cxt->scratch_state.state1 < 16) {
if ((xTaskGetTickCount() - cxt->scratch_state.state4) > TICKS_100MS) {
cxt->scratch_state.state3 += getTipRawTemp(1);
cxt->scratch_state.state1++;
cxt->scratch_state.state4 = xTaskGetTickCount();
}
OLED::setCursor(0, 0);
OLED::print(translatedString(Tr->CJCCalibrating), FontStyle::SMALL);
OLED::setCursor(0, 8);
OLED::print(SmallSymbolDot, FontStyle::SMALL);
for (uint8_t x = 0; x < (cxt->scratch_state.state1 / 4); x++) {
OLED::print(SmallSymbolDot, FontStyle::SMALL);
}
return OperatingMode::CJCCalibration;
}
// If the thermo-couple at the end of the tip, and the handle are at
// equilibrium, then the output should be zero, as there is no temperature
// differential.
while (setoffset == 0) {
uint32_t offset = 0;
for (uint8_t i = 0; i < 16; i++) {
offset += getTipRawTemp(1);
// cycle through the filter a fair bit to ensure we're stable.
OLED::clearScreen();
OLED::setCursor(0, 0);
OLED::print(translatedString(Tr->CJCCalibrating), FontStyle::SMALL);
OLED::setCursor(0, 8);
OLED::print(SmallSymbolDot, FontStyle::SMALL);
for (uint8_t x = 0; x < (i / 4); x++) {
OLED::print(SmallSymbolDot, FontStyle::SMALL);
}
OLED::refresh();
osDelay(100);
}
setoffset = TipThermoModel::convertTipRawADCTouV(offset / 16, true);
uint16_t setOffset = TipThermoModel::convertTipRawADCTouV(cxt->scratch_state.state3 / 16, true);
setSettingValue(SettingsOptions::CalibrationOffset, setOffset);
if (warnUser(translatedString(Tr->CalibrationDone), buttons)) {
// Preventing to repeat calibration at boot automatically (only one shot).
setSettingValue(SettingsOptions::CalibrateCJC, 0);
saveSettings();
return OperatingMode::InitialisationDone;
}
setSettingValue(SettingsOptions::CalibrationOffset, setoffset);
OLED::clearScreen();
warnUser(translatedString(Tr->CalibrationDone), 3 * TICKS_SECOND);
OLED::refresh();
// Preventing to repeat calibration at boot automatically (only one shot).
setSettingValue(SettingsOptions::CalibrateCJC, 0);
saveSettings();
return OperatingMode::CJCCalibration;
}
// Cant run calibration without the tip and for temps to be close
return OperatingMode::StartupWarnings;
}

View File

@@ -1,111 +1,102 @@
#include "OperatingModes.h"
extern osThreadId GUITaskHandle;
extern osThreadId MOVTaskHandle;
extern osThreadId PIDTaskHandle;
extern OperatingMode currentMode;
extern osThreadId GUITaskHandle;
extern osThreadId MOVTaskHandle;
extern osThreadId PIDTaskHandle;
void showDebugMenu(void) {
currentMode = OperatingMode::debug;
uint8_t screen = 0;
ButtonState b;
for (;;) {
OLED::clearScreen(); // Ensure the buffer starts clean
OLED::setCursor(0, 0); // Position the cursor at the 0,0 (top left)
OLED::print(SmallSymbolVersionNumber, FontStyle::SMALL); // Print version number
OLED::setCursor(0, 8); // second line
OLED::print(DebugMenu[screen], FontStyle::SMALL);
switch (screen) {
case 0: // Build Date
break;
case 1: // Device ID
{
uint64_t id = getDeviceID();
OperatingMode showDebugMenu(const ButtonState buttons, guiContext *cxt) {
OLED::setCursor(0, 0); // Position the cursor at the 0,0 (top left)
OLED::print(SmallSymbolVersionNumber, FontStyle::SMALL); // Print version number
OLED::setCursor(0, 8); // second line
OLED::print(DebugMenu[cxt->scratch_state.state1], FontStyle::SMALL);
switch (cxt->scratch_state.state1) {
case 0: // Build Date
break;
case 1: // Device ID
{
uint64_t id = getDeviceID();
#ifdef DEVICE_HAS_VALIDATION_CODE
// If device has validation code; then we want to take over both lines of the screen
OLED::clearScreen(); // Ensure the buffer starts clean
OLED::setCursor(0, 0); // Position the cursor at the 0,0 (top left)
OLED::print(DebugMenu[screen], FontStyle::SMALL);
OLED::drawHex(getDeviceValidation(), FontStyle::SMALL, 8);
OLED::setCursor(0, 8); // second line
// If device has validation code; then we want to take over both lines of the screen
OLED::clearScreen(); // Ensure the buffer starts clean
OLED::setCursor(0, 0); // Position the cursor at the 0,0 (top left)
OLED::print(DebugMenu[cxt->scratch_state.state1], FontStyle::SMALL);
OLED::drawHex(getDeviceValidation(), FontStyle::SMALL, 8);
OLED::setCursor(0, 8); // second line
#endif
OLED::drawHex((uint32_t)(id >> 32), FontStyle::SMALL, 8);
OLED::drawHex((uint32_t)(id & 0xFFFFFFFF), FontStyle::SMALL, 8);
} break;
case 2: // ACC Type
OLED::print(AccelTypeNames[(int)DetectedAccelerometerVersion], FontStyle::SMALL);
break;
case 3: // Power Negotiation Status
OLED::print(PowerSourceNames[getPowerSourceNumber()], FontStyle::SMALL);
break;
case 4: // Input Voltage
printVoltage();
break;
case 5: // Temp in °C
OLED::printNumber(TipThermoModel::getTipInC(), 6, FontStyle::SMALL);
break;
case 6: // Handle Temp in °C
OLED::printNumber(getHandleTemperature(0) / 10, 6, FontStyle::SMALL);
OLED::print(SmallSymbolDot, FontStyle::SMALL);
OLED::printNumber(getHandleTemperature(0) % 10, 1, FontStyle::SMALL);
break;
case 7: // Max Temp Limit in °C
OLED::printNumber(TipThermoModel::getTipMaxInC(), 6, FontStyle::SMALL);
break;
case 8: // System Uptime
OLED::printNumber(xTaskGetTickCount() / TICKS_100MS, 8, FontStyle::SMALL);
break;
case 9: // Movement Timestamp
OLED::printNumber(lastMovementTime / TICKS_100MS, 8, FontStyle::SMALL);
break;
case 10: // Tip Resistance in Ω large to pad over so that we cover ID left overs
OLED::printNumber(getTipResistanceX10() / 10, 6, FontStyle::SMALL);
OLED::print(SmallSymbolDot, FontStyle::SMALL);
OLED::printNumber(getTipResistanceX10() % 10, 1, FontStyle::SMALL);
break;
case 11: // Raw Tip in µV
OLED::printNumber(TipThermoModel::convertTipRawADCTouV(getTipRawTemp(0), true), 8, FontStyle::SMALL);
break;
case 12: // Tip Cold Junction Compensation Offset in µV
OLED::printNumber(getSettingValue(SettingsOptions::CalibrationOffset), 8, FontStyle::SMALL);
break;
case 13: // High Water Mark for GUI
OLED::printNumber(uxTaskGetStackHighWaterMark(GUITaskHandle), 8, FontStyle::SMALL);
break;
case 14: // High Water Mark for Movement Task
OLED::printNumber(uxTaskGetStackHighWaterMark(MOVTaskHandle), 8, FontStyle::SMALL);
break;
case 15: // High Water Mark for PID Task
OLED::printNumber(uxTaskGetStackHighWaterMark(PIDTaskHandle), 8, FontStyle::SMALL);
break;
break;
OLED::drawHex((uint32_t)(id >> 32), FontStyle::SMALL, 8);
OLED::drawHex((uint32_t)(id & 0xFFFFFFFF), FontStyle::SMALL, 8);
} break;
case 2: // ACC Type
OLED::print(AccelTypeNames[(int)DetectedAccelerometerVersion], FontStyle::SMALL);
break;
case 3: // Power Negotiation Status
OLED::print(PowerSourceNames[getPowerSourceNumber()], FontStyle::SMALL);
break;
case 4: // Input Voltage
printVoltage();
break;
case 5: // Temp in °C
OLED::printNumber(TipThermoModel::getTipInC(), 6, FontStyle::SMALL);
break;
case 6: // Handle Temp in °C
OLED::printNumber(getHandleTemperature(0) / 10, 6, FontStyle::SMALL);
OLED::print(SmallSymbolDot, FontStyle::SMALL);
OLED::printNumber(getHandleTemperature(0) % 10, 1, FontStyle::SMALL);
break;
case 7: // Max Temp Limit in °C
OLED::printNumber(TipThermoModel::getTipMaxInC(), 6, FontStyle::SMALL);
break;
case 8: // System Uptime
OLED::printNumber(xTaskGetTickCount() / TICKS_100MS, 8, FontStyle::SMALL);
break;
case 9: // Movement Timestamp
OLED::printNumber(lastMovementTime / TICKS_100MS, 8, FontStyle::SMALL);
break;
case 10: // Tip Resistance in Ω
OLED::printNumber(getTipResistanceX10() / 10, 6, FontStyle::SMALL); // large to pad over so that we cover ID left overs
OLED::print(SmallSymbolDot, FontStyle::SMALL);
OLED::printNumber(getTipResistanceX10() % 10, 1, FontStyle::SMALL);
break;
case 11: // Raw Tip in µV
OLED::printNumber(TipThermoModel::convertTipRawADCTouV(getTipRawTemp(0), true), 8, FontStyle::SMALL);
break;
case 12: // Tip Cold Junction Compensation Offset in µV
OLED::printNumber(getSettingValue(SettingsOptions::CalibrationOffset), 8, FontStyle::SMALL);
break;
case 13: // High Water Mark for GUI
OLED::printNumber(uxTaskGetStackHighWaterMark(GUITaskHandle), 8, FontStyle::SMALL);
break;
case 14: // High Water Mark for Movement Task
OLED::printNumber(uxTaskGetStackHighWaterMark(MOVTaskHandle), 8, FontStyle::SMALL);
break;
case 15: // High Water Mark for PID Task
OLED::printNumber(uxTaskGetStackHighWaterMark(PIDTaskHandle), 8, FontStyle::SMALL);
break;
break;
#ifdef HALL_SENSOR
case 16: // Raw Hall Effect Value
{
int16_t hallEffectStrength = getRawHallEffect();
if (hallEffectStrength < 0) {
hallEffectStrength = -hallEffectStrength;
}
OLED::printNumber(hallEffectStrength, 6, FontStyle::SMALL);
} break;
case 16: // Raw Hall Effect Value
{
int16_t hallEffectStrength = getRawHallEffect();
if (hallEffectStrength < 0) {
hallEffectStrength = -hallEffectStrength;
}
OLED::printNumber(hallEffectStrength, 6, FontStyle::SMALL);
} break;
#endif
default:
break;
}
OLED::refresh();
b = getButtonState();
if (b == BUTTON_B_SHORT) {
return;
} else if (b == BUTTON_F_SHORT) {
screen++;
#ifdef HALL_SENSOR
screen = screen % 17;
#else
screen = screen % 16;
#endif
}
GUIDelay();
default:
break;
}
if (buttons == BUTTON_B_SHORT) {
cxt->transitionMode = TransitionAnimation::Down;
return OperatingMode::HomeScreen;
} else if (buttons == BUTTON_F_SHORT) {
cxt->scratch_state.state1++;
#ifdef HALL_SENSOR
cxt->scratch_state.state1 = cxt->scratch_state.state1 % 17;
#else
cxt->scratch_state.state1 = cxt->scratch_state.state1 % 16;
#endif
}
return OperatingMode::DebugMenuReadout; // Stay in debug menu
}

View File

@@ -2,14 +2,10 @@
#include "Buttons.hpp"
#include "OperatingModes.h"
#define MOVEMENT_INACTIVITY_TIME (60 * configTICK_RATE_HZ)
#define BUTTON_INACTIVITY_TIME (60 * configTICK_RATE_HZ)
uint8_t buttonAF[sizeof(buttonA)];
uint8_t buttonBF[sizeof(buttonB)];
uint8_t disconnectedTipF[sizeof(disconnectedTip)];
extern OperatingMode currentMode;
bool showExitMenuTransition = false;
uint8_t buttonAF[sizeof(buttonA)];
uint8_t buttonBF[sizeof(buttonB)];
uint8_t disconnectedTipF[sizeof(disconnectedTip)];
bool showExitMenuTransition = false;
void renderHomeScreenAssets(void) {
@@ -24,59 +20,50 @@ void renderHomeScreenAssets(void) {
}
}
void handleButtons(bool *buttonLockout) {
ButtonState buttons = getButtonState();
if (buttons != BUTTON_NONE) {
OLED::setDisplayState(OLED::DisplayState::ON);
}
if (buttons != BUTTON_NONE && *buttonLockout) {
buttons = BUTTON_NONE;
OperatingMode handleHomeButtons(const ButtonState buttons, guiContext *cxt) {
if (buttons != BUTTON_NONE && cxt->scratch_state.state1 == 0) {
return OperatingMode::HomeScreen; // Ignore button press
} else {
*buttonLockout = false;
cxt->scratch_state.state1 = 1;
}
switch (buttons) {
case BUTTON_NONE:
// Do nothing
break;
case BUTTON_BOTH:
// Not used yet
// In multi-language this might be used to reset language on a long hold
// or some such
break;
case BUTTON_B_LONG:
// Show the version information
showDebugMenu();
cxt->transitionMode = TransitionAnimation::Up;
return OperatingMode::DebugMenuReadout;
break;
case BUTTON_F_LONG:
#ifdef PROFILE_SUPPORT
if (!isTipDisconnected()) {
gui_solderingProfileMode(); // enter profile mode
*buttonLockout = true;
cxt->transitionMode = TransitionAnimation::Left;
return OperatingMode::SolderingProfile;
} else {
return OperatingMode::HomeScreen;
}
#else
gui_solderingTempAdjust();
saveSettings();
cxt->transitionMode = TransitionAnimation::Left;
return OperatingMode::TemperatureAdjust;
#endif
break;
case BUTTON_F_SHORT:
if (!isTipDisconnected()) {
gui_solderingMode(0); // enter soldering mode
*buttonLockout = true;
cxt->transitionMode = TransitionAnimation::Left;
return OperatingMode::Soldering;
}
break;
case BUTTON_B_SHORT:
currentMode = OperatingMode::settings;
enterSettingsMenu(); // enter the settings menu
{
OLED::useSecondaryFramebuffer(true);
showExitMenuTransition = true;
}
*buttonLockout = true;
cxt->transitionMode = TransitionAnimation::Right;
return OperatingMode::SettingsMenu;
break;
default:
break;
}
return OperatingMode::HomeScreen;
}
void drawDetailedHomeScreen(uint32_t tipTemp) {
@@ -181,55 +168,22 @@ void drawSimplifiedHomeScreen(uint32_t tipTemp) {
}
}
}
void drawHomeScreen(bool buttonLockout) {
OperatingMode drawHomeScreen(const ButtonState buttons, guiContext *cxt) {
for (;;) {
currentMode = OperatingMode::idle;
handleButtons(&buttonLockout);
currentTempTargetDegC = 0; // ensure tip is off
getInputVoltageX10(getSettingValue(SettingsOptions::VoltageDiv), 0);
uint32_t tipTemp = TipThermoModel::getTipInC();
currentTempTargetDegC = 0; // ensure tip is off
getInputVoltageX10(getSettingValue(SettingsOptions::VoltageDiv), 0);
uint32_t tipTemp = TipThermoModel::getTipInC();
// Preemptively turn the display on. Turn it off if and only if
// the tip temperature is below 50 degrees C *and* motion sleep
// detection is enabled *and* there has been no activity (movement or
// button presses) in a while.
// This is zero cost really as state is only changed on display updates
OLED::setDisplayState(OLED::DisplayState::ON);
if ((tipTemp < 50) && getSettingValue(SettingsOptions::Sensitivity) &&
(((xTaskGetTickCount() - lastMovementTime) > MOVEMENT_INACTIVITY_TIME) && ((xTaskGetTickCount() - lastButtonTime) > BUTTON_INACTIVITY_TIME))) {
OLED::setDisplayState(OLED::DisplayState::OFF);
setStatusLED(LED_OFF);
} else {
OLED::setDisplayState(OLED::DisplayState::ON);
if (tipTemp > 55) {
setStatusLED(LED_COOLING_STILL_HOT);
} else {
setStatusLED(LED_STANDBY);
}
}
// Clear the lcd buffer
OLED::clearScreen();
if (OLED::getRotation()) {
OLED::setCursor(50, 0);
} else {
OLED::setCursor(-1, 0);
}
if (getSettingValue(SettingsOptions::DetailedIDLE)) {
drawDetailedHomeScreen(tipTemp);
} else {
drawSimplifiedHomeScreen(tipTemp);
}
if (showExitMenuTransition) {
OLED::useSecondaryFramebuffer(false);
OLED::transitionSecondaryFramebuffer(false);
showExitMenuTransition = false;
} else {
OLED::refresh();
GUIDelay();
}
// Setup LCD Cursor location
if (OLED::getRotation()) {
OLED::setCursor(50, 0);
} else {
OLED::setCursor(-1, 0);
}
if (getSettingValue(SettingsOptions::DetailedIDLE)) {
drawDetailedHomeScreen(tipTemp);
} else {
drawSimplifiedHomeScreen(tipTemp);
}
return handleHomeButtons(buttons, cxt);
}

View File

@@ -3,6 +3,3 @@
//
#include "OperatingModes.h"
// Global variables
OperatingMode currentMode = OperatingMode::idle;

View File

@@ -24,29 +24,64 @@ extern "C" {
#include "pd.h"
#endif
// Exposed modes
enum OperatingMode {
idle = 0,
soldering = 1,
boost = 2,
sleeping = 3,
settings = 4,
debug = 5
enum class OperatingMode {
StartupLogo = 0, // Showing the startup logo
CJCCalibration, // Cold Junction Calibration
StartupWarnings, // Startup checks and warnings
InitialisationDone, // Special state we use just before we to home screen at first startup. Allows jumping to extra startup states
HomeScreen, // Home/Idle screen that is the main launchpad to other modes
Soldering, // Main soldering operating mode
SolderingProfile, // Soldering by following a profile, used for reflow for example
Sleeping, // Sleep state holds iron at lower sleep temp
Hibernating, // Like sleeping but keeps heater fully off until woken
SettingsMenu, // Settings Menu
DebugMenuReadout, // Debug metrics
TemperatureAdjust, // Set point temperature adjustment
UsbPDDebug, // USB PD debugging information
ThermalRunaway, // Thermal Runaway warning state.
};
enum class TransitionAnimation {
None = 0,
Right = 1,
Left = 2,
Down = 3,
Up = 4,
};
// Generic context struct used for gui functions to be able to retain state
struct guiContext {
TickType_t viewEnterTime; // Set to ticks when this view state was first entered
OperatingMode previousMode;
TransitionAnimation transitionMode;
// Below is scratch state, this is retained over re-draws but blown away on state change
struct scratch {
uint16_t state1; // 16 bit state scratch
uint16_t state2; // 16 bit state scratch
uint32_t state3; // 32 bit state scratch
uint32_t state4; // 32 bit state scratch
uint16_t state5; // 16 bit state scratch
uint16_t state6; // 16 bit state scratch
} scratch_state;
};
// Main functions
void performCJCC(void); // Used to calibrate the Cold Junction offset
void gui_solderingTempAdjust(void); // For adjusting the setpoint temperature of the iron
int gui_SolderingSleepingMode(bool stayOff, bool autoStarted); // Sleep mode
void gui_solderingMode(uint8_t jumpToSleep); // Main mode for hot pointy tool
void gui_solderingProfileMode(); // Profile mode for hot likely-not-so-pointy tool
void showDebugMenu(void); // Debugging values
void showPDDebug(void); // Debugging menu that shows PD adaptor info
void showWarnings(void); // Shows user warnings if required
void drawHomeScreen(bool buttonLockout) __attribute__((noreturn)); // IDLE / Home screen
void renderHomeScreenAssets(void); // Called to act as start delay and used to render out flipped images for home screen graphics
OperatingMode gui_SolderingSleepingMode(const ButtonState buttons, guiContext *cxt); // Sleep mode
OperatingMode gui_solderingMode(const ButtonState buttons, guiContext *cxt); // Main mode for hot pointy tool
OperatingMode gui_solderingTempAdjust(const ButtonState buttons, guiContext *cxt); // For adjusting the setpoint temperature of the iron
OperatingMode drawHomeScreen(const ButtonState buttons, guiContext *cxt); // IDLE / Home screen
OperatingMode gui_SettingsMenu(const ButtonState buttons, guiContext *cxt); //
OperatingMode gui_solderingProfileMode(const ButtonState buttons, guiContext *cxt); // Profile mode for hot likely-not-so-pointy tool
OperatingMode performCJCC(const ButtonState buttons, guiContext *cxt); // Used to calibrate the Cold Junction offset
OperatingMode showDebugMenu(const ButtonState buttons, guiContext *cxt); // Debugging values
OperatingMode showPDDebug(const ButtonState buttons, guiContext *cxt); // Debugging menu that shows PD adaptor info
OperatingMode showWarnings(const ButtonState buttons, guiContext *cxt); // Shows user warnings if required
// Common helpers
int8_t getPowerSourceNumber(void); // Returns number ID of power source
TemperatureType_t getTipTemp(void); // Returns temperature of the tip in *C/*F (based on user settings)
int8_t getPowerSourceNumber(void); // Returns number ID of power source
void renderHomeScreenAssets(void); // Called to act as start delay and used to render out flipped images for home screen graphics
extern bool heaterThermalRunaway;
#endif

View File

@@ -0,0 +1,275 @@
#include "OperatingModes.h"
#include "ScrollMessage.hpp"
#define HELP_TEXT_TIMEOUT_TICKS (TICKS_SECOND * 3)
/*
* The settings menu is the most complex bit of GUI code we have
* The menu consists of a two tier menu
* Main menu -> Categories
* Secondary menu -> Settings
*
* For each entry in the menu
*/
/**
* Prints two small lines (or one line for CJK) of short description for
* setting items and prepares cursor after it.
* @param settingsItemIndex Index of the setting item.
* @param cursorCharPosition Custom cursor char position to set after printing
* description.
*/
static void printShortDescription(SettingsItemIndex settingsItemIndex, uint16_t cursorCharPosition) {
// print short description (default single line, explicit double line)
uint8_t shortDescIndex = static_cast<uint8_t>(settingsItemIndex);
OLED::printWholeScreen(translatedString(Tr->SettingsShortNames[shortDescIndex]));
// prepare cursor for value
// make room for scroll indicator
OLED::setCursor(cursorCharPosition * FONT_12_WIDTH - 2, 0);
}
// Render a menu, based on the position given
// This will either draw the menu item, or the help text depending on how long its been since button press
void render_menu(const menuitem *item, guiContext *cxt) {
// If recent interaction or not help text draw the entry
if ((xTaskGetTickCount() - lastButtonTime < HELP_TEXT_TIMEOUT_TICKS) || item->description == 0) {
if (item->shortDescriptionSize > 0) {
printShortDescription(item->shortDescriptionIndex, item->shortDescriptionSize);
}
item->draw();
} else {
uint16_t *isRenderingHelp = &(cxt->scratch_state.state6);
*isRenderingHelp = 1;
// Draw description
const char *description = translatedString(Tr->SettingsDescriptions[item->description - 1]);
drawScrollingText(description, (xTaskGetTickCount() - lastButtonTime) - HELP_TEXT_TIMEOUT_TICKS);
}
}
uint16_t getMenuLength(const menuitem *menu, const uint16_t stop) {
// walk this menu to find the length
uint16_t counter = 0;
for (uint16_t pos = 0; pos < stop; pos++) {
// End of list
if (menu[pos].draw == nullptr) {
return counter;
}
// Otherwise increment for each visible item (null == always, or if not check function)
if (menu[pos].isVisible == nullptr || menu[pos].isVisible()) {
counter++;
}
}
return counter;
}
OperatingMode moveToNextEntry(guiContext *cxt) {
uint16_t *mainEntry = &(cxt->scratch_state.state1);
uint16_t *subEntry = &(cxt->scratch_state.state2);
uint16_t *currentMenuLength = &(cxt->scratch_state.state5);
uint16_t *isRenderingHelp = &(cxt->scratch_state.state6);
if (*isRenderingHelp) {
*isRenderingHelp = 0;
} else {
*currentMenuLength = 0; // Reset menu length
// Scroll down
// We can increment freely _once_
cxt->transitionMode = TransitionAnimation::Down;
if (*subEntry == 0) {
(*mainEntry) += 1;
if (rootSettingsMenu[*mainEntry].draw == nullptr) {
// We are off the end of the menu now
saveSettings();
cxt->transitionMode = TransitionAnimation::Left;
return OperatingMode::HomeScreen;
}
// Check if visible
if (rootSettingsMenu[*mainEntry].isVisible != nullptr && !rootSettingsMenu[*mainEntry].isVisible()) {
// We need to move on as this one isn't visible
return moveToNextEntry(cxt);
}
} else {
(*subEntry) += 1;
// If the new entry is null, we need to exit
if (subSettingsMenus[*mainEntry][(*subEntry) - 1].draw == nullptr) {
(*subEntry) = 0; // Reset back to the main menu
cxt->transitionMode = TransitionAnimation::Left;
// Have to break early to avoid the below check underflowing
return OperatingMode::SettingsMenu;
}
// Check if visible
if (subSettingsMenus[*mainEntry][(*subEntry) - 1].isVisible != nullptr && !subSettingsMenus[*mainEntry][(*subEntry) - 1].isVisible()) {
// We need to move on as this one isn't visible
return moveToNextEntry(cxt);
}
}
}
return OperatingMode::SettingsMenu;
}
OperatingMode gui_SettingsMenu(const ButtonState buttons, guiContext *cxt) {
// Render out the current settings menu
// State 1 -> Root menu
// State 2 -> Sub entry
// Draw main entry if sub-entry is 0, otherwise draw sub-entry
uint16_t *mainEntry = &(cxt->scratch_state.state1);
uint16_t *subEntry = &(cxt->scratch_state.state2);
uint32_t *autoRepeatAcceleration = &(cxt->scratch_state.state3);
uint32_t *autoRepeatTimer = &(cxt->scratch_state.state4);
uint16_t *currentMenuLength = &(cxt->scratch_state.state5);
uint16_t *isRenderingHelp = &(cxt->scratch_state.state6);
const menuitem *currentMenu;
// Draw the currently on screen item
uint16_t currentScreen;
if (*subEntry == 0) {
// Drawing main menu
currentMenu = rootSettingsMenu;
currentScreen = *mainEntry;
} else {
// Drawing sub menu
currentMenu = subSettingsMenus[*mainEntry];
currentScreen = (*subEntry) - 1;
}
render_menu(&(currentMenu[currentScreen]), cxt);
// Update the cached menu length if unknown
if (*currentMenuLength == 0) {
// We walk the current menu to find the length
*currentMenuLength = getMenuLength(currentMenu, 128 /* Max length of any menu*/);
}
if (*isRenderingHelp == 0) {
// Draw scroll
// Get virtual pos by counting entries from start to _here_
uint16_t currentVirtualPosition = getMenuLength(currentMenu, currentScreen + 1);
if (currentVirtualPosition > 0) {
currentVirtualPosition--;
}
// The height of the indicator is screen res height / total menu entries
uint8_t indicatorHeight = OLED_HEIGHT / *currentMenuLength;
if (indicatorHeight == 0) {
indicatorHeight = 1; // always at least 1 pixel
}
uint16_t position = (OLED_HEIGHT * (uint16_t)currentVirtualPosition) / *currentMenuLength;
bool showScrollbar = true;
// Store if its the last option for this setting
bool isLastOptionForSetting = false;
if ((int)currentMenu[currentScreen].autoSettingOption < (int)SettingsOptions::SettingsOptionsLength) {
isLastOptionForSetting = isLastSettingValue(currentMenu[currentScreen].autoSettingOption);
}
// Last settings menu entry, reset scroll show back so it flashes
if (isLastOptionForSetting) {
showScrollbar = false;
}
// Or Flash it
showScrollbar |= (xTaskGetTickCount() % (TICKS_SECOND / 4) < (TICKS_SECOND / 8));
if (showScrollbar) {
OLED::drawScrollIndicator((uint8_t)position, indicatorHeight);
}
}
// Now handle user button input
auto callIncrementHandler = [&]() {
if (currentMenu[currentScreen].incrementHandler != nullptr) {
currentMenu[currentScreen].incrementHandler();
} else if ((int)currentMenu[currentScreen].autoSettingOption < (int)SettingsOptions::SettingsOptionsLength) {
nextSettingValue(currentMenu[currentScreen].autoSettingOption);
}
return false;
};
//
OperatingMode newMode = OperatingMode::SettingsMenu;
switch (buttons) {
case BUTTON_NONE:
(*autoRepeatAcceleration) = 0; // reset acceleration
(*autoRepeatTimer) = 0; // reset acceleration
break;
case BUTTON_BOTH:
if (*subEntry == 0) {
saveSettings();
cxt->transitionMode = TransitionAnimation::Left;
return OperatingMode::HomeScreen;
} else {
cxt->transitionMode = TransitionAnimation::Left;
*subEntry = 0;
return OperatingMode::SettingsMenu;
}
break;
case BUTTON_F_LONG:
if (xTaskGetTickCount() + (*autoRepeatAcceleration) > (*autoRepeatTimer) + PRESS_ACCEL_INTERVAL_MAX) {
callIncrementHandler();
// Update the check for if its the last version
bool isLastOptionForSetting = false;
if ((int)currentMenu[currentScreen].autoSettingOption < (int)SettingsOptions::SettingsOptionsLength) {
isLastOptionForSetting = isLastSettingValue(currentMenu[currentScreen].autoSettingOption);
}
if (isLastOptionForSetting) {
(*autoRepeatTimer) = TICKS_SECOND * 2;
} else {
(*autoRepeatTimer) = 0;
}
(*autoRepeatTimer) += xTaskGetTickCount();
(*autoRepeatAcceleration) += PRESS_ACCEL_STEP;
*currentMenuLength = 0; // Reset incase menu visible changes
}
break;
case BUTTON_F_SHORT:
// Increment setting
if (*isRenderingHelp) {
*isRenderingHelp = 0;
} else {
*currentMenuLength = 0; // Reset incase menu visible changes
if (*subEntry == 0) {
// In a root menu, if its null handler we enter the menu
if (currentMenu[currentScreen].incrementHandler != nullptr) {
currentMenu[currentScreen].incrementHandler();
} else {
(*subEntry) += 1;
cxt->transitionMode = TransitionAnimation::Right;
}
} else {
callIncrementHandler();
}
}
break;
case BUTTON_B_LONG:
if (xTaskGetTickCount() + (*autoRepeatAcceleration) > (*autoRepeatTimer) + PRESS_ACCEL_INTERVAL_MAX) {
(*autoRepeatTimer) = xTaskGetTickCount();
(*autoRepeatAcceleration) += PRESS_ACCEL_STEP;
} else {
break;
}
/* Fall through*/
case BUTTON_B_SHORT:
// Increment menu item
newMode = moveToNextEntry(cxt);
break;
default:
break;
}
if ((PRESS_ACCEL_INTERVAL_MAX - (*autoRepeatAcceleration)) < PRESS_ACCEL_INTERVAL_MIN) {
(*autoRepeatAcceleration) = PRESS_ACCEL_INTERVAL_MAX - PRESS_ACCEL_INTERVAL_MIN;
}
// Otherwise we stay put for next render iteration
return newMode;
}

View File

@@ -1,56 +1,95 @@
#include "HUB238.hpp"
#include "OperatingModes.h"
void showWarnings(void) {
OperatingMode showWarnings(const ButtonState buttons, guiContext *cxt) {
// Display alert if settings were reset
if (settingsWereReset) {
warnUser(translatedString(Tr->SettingsResetMessage), 10 * TICKS_SECOND);
}
#ifdef DEVICE_HAS_VALIDATION_SUPPORT
if (getDeviceValidationStatus()) {
// Warn user this device might be counterfeit
warnUser(translatedString(Tr->DeviceFailedValidationWarning), 10 * TICKS_SECOND);
}
#endif
#ifndef NO_WARN_MISSING
// We also want to alert if accel or pd is not detected / not responding
// In this case though, we dont want to nag the user _too_ much
// So only show first 2 times
while (DetectedAccelerometerVersion == AccelType::Scanning) {
osDelay(5);
resetWatchdog();
}
// Display alert if accelerometer is not detected
if (DetectedAccelerometerVersion == AccelType::None) {
if (getSettingValue(SettingsOptions::AccelMissingWarningCounter) < 2) {
nextSettingValue(SettingsOptions::AccelMissingWarningCounter);
saveSettings();
warnUser(translatedString(Tr->NoAccelerometerMessage), 10 * TICKS_SECOND);
switch (cxt->scratch_state.state1) {
case 0: // Settings reset warning
if (settingsWereReset) {
if (warnUser(translatedString(Tr->SettingsResetMessage), buttons)) {
settingsWereReset = false;
cxt->scratch_state.state1 = 1;
}
} else {
cxt->scratch_state.state1 = 1;
}
}
break;
case 1: // Device validations
#ifdef DEVICE_HAS_VALIDATION_SUPPORT
if (getDeviceValidationStatus()) {
// Warn user this device might be counterfeit
if (warnUser(translatedString(Tr->DeviceFailedValidationWarning), buttons)) {
cxt->scratch_state.state1 = 2;
}
} else {
cxt->scratch_state.state1 = 2;
}
#else
cxt->scratch_state.state1 = 2;
#endif
break;
case 2: // Accelerometer detection
if (DetectedAccelerometerVersion == AccelType::Scanning) {
break;
}
// Display alert if accelerometer is not detected
if (DetectedAccelerometerVersion == AccelType::None) {
if (getSettingValue(SettingsOptions::AccelMissingWarningCounter) < 2) {
if (warnUser(translatedString(Tr->NoAccelerometerMessage), buttons)) {
cxt->scratch_state.state1 = 3;
nextSettingValue(SettingsOptions::AccelMissingWarningCounter);
saveSettings();
}
} else {
cxt->scratch_state.state1 = 3;
}
} else {
cxt->scratch_state.state1 = 3;
}
break;
case 3:
#ifdef POW_PD
// We expect pd to be present
resetWatchdog();
if (!USBPowerDelivery::fusbPresent()) {
if (getSettingValue(SettingsOptions::PDMissingWarningCounter) < 2) {
nextSettingValue(SettingsOptions::PDMissingWarningCounter);
saveSettings();
warnUser(translatedString(Tr->NoPowerDeliveryMessage), 10 * TICKS_SECOND);
// We expect pd to be present
if (!USBPowerDelivery::fusbPresent()) {
if (getSettingValue(SettingsOptions::PDMissingWarningCounter) < 2) {
if (warnUser(translatedString(Tr->NoPowerDeliveryMessage), buttons)) {
nextSettingValue(SettingsOptions::PDMissingWarningCounter);
saveSettings();
cxt->scratch_state.state1 = 4;
}
} else {
cxt->scratch_state.state1 = 4;
}
} else {
cxt->scratch_state.state1 = 4;
}
}
#endif /*POW_PD*/
#else
#if POW_PD_EXT == 1
if (!hub238_probe()) {
if (getSettingValue(SettingsOptions::PDMissingWarningCounter) < 2) {
nextSettingValue(SettingsOptions::PDMissingWarningCounter);
saveSettings();
warnUser(translatedString(Tr->NoPowerDeliveryMessage), 10 * TICKS_SECOND);
if (!hub238_probe()) {
if (getSettingValue(SettingsOptions::PDMissingWarningCounter) < 2) {
if (warnUser(translatedString(Tr->NoPowerDeliveryMessage), buttons)) {
cxt->scratch_state.state1 = 4;
nextSettingValue(SettingsOptions::PDMissingWarningCounter);
saveSettings();
}
} else {
cxt->scratch_state.state1 = 4;
}
} else {
cxt->scratch_state.state1 = 4;
}
}
#else
cxt->scratch_state.state1 = 4;
#endif /*POW_PD_EXT==1*/
// If tip looks to be shorted, yell at user and dont auto dismiss
if (isTipShorted()) {
warnUser(translatedString(Tr->WarningTipShorted), portMAX_DELAY);
#endif /*POW_PD*/
break;
default:
// We are off the end, warnings done
return OperatingMode::StartupLogo;
}
#endif /*NO_WARN_MISSING*/
return OperatingMode::StartupWarnings; // Stay in warnings
}

View File

@@ -1,64 +1,70 @@
#include "OperatingModes.h"
extern OperatingMode currentMode;
int gui_SolderingSleepingMode(bool stayOff, bool autoStarted) {
#ifndef NO_SLEEP_MODE
OperatingMode gui_SolderingSleepingMode(const ButtonState buttons, guiContext *cxt) {
#ifdef NO_SLEEP_MODE
return OperatingMode::Soldering;
#endif
// Drop to sleep temperature and display until movement or button press
currentMode = OperatingMode::sleeping;
for (;;) {
// user moved or pressed a button, go back to soldering
// If in the first two seconds we disable this to let accelerometer warm up
// user moved or pressed a button, go back to soldering
// If in the first two seconds we disable this to let accelerometer warm up
#ifdef POW_DC
if (checkForUnderVoltage()) {
// return non-zero on error
return 1;
}
#endif
if (getSettingValue(SettingsOptions::TemperatureInF)) {
currentTempTargetDegC = stayOff ? 0 : TipThermoModel::convertFtoC(min(getSettingValue(SettingsOptions::SleepTemp), getSettingValue(SettingsOptions::SolderingTemp)));
} else {
currentTempTargetDegC = stayOff ? 0 : min(getSettingValue(SettingsOptions::SleepTemp), getSettingValue(SettingsOptions::SolderingTemp));
}
// draw the lcd
TemperatureType_t tipTemp = getTipTemp();
OLED::clearScreen();
OLED::setCursor(0, 0);
if (getSettingValue(SettingsOptions::DetailedSoldering)) {
OLED::print(translatedString(Tr->SleepingAdvancedString), FontStyle::SMALL);
OLED::setCursor(0, 8);
OLED::print(translatedString(Tr->SleepingTipAdvancedString), FontStyle::SMALL);
OLED::printNumber(tipTemp, 3, FontStyle::SMALL);
OLED::printSymbolDeg(FontStyle::SMALL);
OLED::print(SmallSymbolSpace, FontStyle::SMALL);
printVoltage();
OLED::print(SmallSymbolVolts, FontStyle::SMALL);
} else {
OLED::print(translatedString(Tr->SleepingSimpleString), FontStyle::LARGE);
OLED::printNumber(tipTemp, 3, FontStyle::LARGE);
OLED::printSymbolDeg(FontStyle::EXTRAS);
}
OLED::refresh();
GUIDelay();
if (!shouldBeSleeping(autoStarted)) {
return 0;
}
if (shouldShutdown()) {
// shutdown
currentTempTargetDegC = 0;
// we want to exit soldering mode
return 1;
}
if (checkForUnderVoltage()) {
return OperatingMode::HomeScreen; // return non-zero on error
}
#endif
return 0;
if (cxt->scratch_state.state4) {
// Hibernating mode
currentTempTargetDegC = 0;
} else {
if (getSettingValue(SettingsOptions::TemperatureInF)) {
currentTempTargetDegC = TipThermoModel::convertFtoC(min(getSettingValue(SettingsOptions::SleepTemp), getSettingValue(SettingsOptions::SolderingTemp)));
} else {
currentTempTargetDegC = min(getSettingValue(SettingsOptions::SleepTemp), getSettingValue(SettingsOptions::SolderingTemp));
}
}
// draw the lcd
uint16_t tipTemp = getSettingValue(SettingsOptions::TemperatureInF) ? TipThermoModel::getTipInF() : TipThermoModel::getTipInC();
OLED::clearScreen();
OLED::setCursor(0, 0);
if (getSettingValue(SettingsOptions::DetailedSoldering)) {
OLED::print(translatedString(Tr->SleepingAdvancedString), FontStyle::SMALL);
OLED::setCursor(0, 8);
OLED::print(translatedString(Tr->SleepingTipAdvancedString), FontStyle::SMALL);
OLED::printNumber(tipTemp, 3, FontStyle::SMALL);
if (getSettingValue(SettingsOptions::TemperatureInF)) {
OLED::print(SmallSymbolDegF, FontStyle::SMALL);
} else {
OLED::print(SmallSymbolDegC, FontStyle::SMALL);
}
OLED::print(SmallSymbolSpace, FontStyle::SMALL);
printVoltage();
OLED::print(SmallSymbolVolts, FontStyle::SMALL);
} else {
OLED::print(translatedString(Tr->SleepingSimpleString), FontStyle::LARGE);
OLED::printNumber(tipTemp, 3, FontStyle::LARGE);
OLED::printSymbolDeg(FontStyle::EXTRAS);
}
OLED::refresh();
GUIDelay();
if (!shouldBeSleeping()) {
return cxt->previousMode;
}
if (shouldShutdown()) {
// shutdown
currentTempTargetDegC = 0;
return OperatingMode::HomeScreen;
}
if (cxt->scratch_state.state4) {
return OperatingMode::Hibernating;
} else {
return OperatingMode::Sleeping;
}
}

View File

@@ -1,10 +1,62 @@
#include "OperatingModes.h"
#include "SolderingCommon.h"
// State 1 = button locking
// State 2 = boost mode
// State 3 = buzzer timer
extern OperatingMode currentMode;
void gui_solderingMode(uint8_t jumpToSleep) {
OperatingMode handleSolderingButtons(const ButtonState buttons, guiContext *cxt) {
if (cxt->scratch_state.state1 == 1) {
// Buttons are currently locked
if (buttons == BUTTON_F_LONG) {
if (getSettingValue(SettingsOptions::BoostTemp) && (getSettingValue(SettingsOptions::LockingMode) == 1)) {
cxt->scratch_state.state2 = 1;
}
} else if (buttons == BUTTON_BOTH_LONG) {
// Unlocking
if (warnUser(translatedString(Tr->UnlockingKeysString), buttons)) {
cxt->scratch_state.state1 = 0;
}
} else if (buttons != BUTTON_NONE) {
// Do nothing and display a lock warning
warnUser(translatedString(Tr->WarningKeysLockedString), buttons);
}
return OperatingMode::Soldering;
}
// otherwise we are unlocked
switch (buttons) {
case BUTTON_NONE:
cxt->scratch_state.state2 = 0;
break;
case BUTTON_BOTH:
/*Fall through*/
case BUTTON_B_LONG:
cxt->transitionMode = TransitionAnimation::Right;
return OperatingMode::HomeScreen;
case BUTTON_F_LONG:
// if boost mode is enabled turn it on
if (getSettingValue(SettingsOptions::BoostTemp)) {
cxt->scratch_state.state2 = 1;
}
break;
case BUTTON_F_SHORT:
case BUTTON_B_SHORT:
cxt->transitionMode = TransitionAnimation::Left;
return OperatingMode::TemperatureAdjust;
case BUTTON_BOTH_LONG:
if (getSettingValue(SettingsOptions::LockingMode) != 0) {
// Lock buttons
if (warnUser(translatedString(Tr->LockingKeysString), buttons)) {
cxt->scratch_state.state1 = 1;
}
}
break;
default:
break;
}
return OperatingMode::Soldering;
}
OperatingMode gui_solderingMode(const ButtonState buttons, guiContext *cxt) {
/*
* * Soldering (gui_solderingMode)
* -> Main loop where we draw temp, and animations
@@ -19,98 +71,58 @@ void gui_solderingMode(uint8_t jumpToSleep) {
* --> Double button to exit
* --> Long hold double button to toggle key lock
*/
bool boostModeOn = false;
bool buttonsLocked = false;
bool converged = false;
currentMode = OperatingMode::soldering;
TickType_t buzzerEnd = 0;
if (jumpToSleep) {
if (gui_SolderingSleepingMode(jumpToSleep == 2, true) == 1) {
lastButtonTime = xTaskGetTickCount();
return; // If the function returns non-0 then exit
// Update the setpoints for the temperature
if (cxt->scratch_state.state2) {
if (getSettingValue(SettingsOptions::TemperatureInF)) {
currentTempTargetDegC = TipThermoModel::convertFtoC(getSettingValue(SettingsOptions::BoostTemp));
} else {
currentTempTargetDegC = (getSettingValue(SettingsOptions::BoostTemp));
}
} else {
if (getSettingValue(SettingsOptions::TemperatureInF)) {
currentTempTargetDegC = TipThermoModel::convertFtoC(getSettingValue(SettingsOptions::SolderingTemp));
} else {
currentTempTargetDegC = (getSettingValue(SettingsOptions::SolderingTemp));
}
}
for (;;) {
ButtonState buttons = getButtonState();
if (buttonsLocked && (getSettingValue(SettingsOptions::LockingMode) != 0)) { // If buttons locked
switch (buttons) {
case BUTTON_NONE:
boostModeOn = false;
break;
case BUTTON_BOTH_LONG:
// Unlock buttons
buttonsLocked = false;
warnUser(translatedString(Tr->UnlockingKeysString), TICKS_SECOND);
break;
case BUTTON_F_LONG:
// if boost mode is enabled turn it on
if (getSettingValue(SettingsOptions::BoostTemp) && (getSettingValue(SettingsOptions::LockingMode) == 1)) {
boostModeOn = true;
currentMode = OperatingMode::boost;
}
break;
// fall through
case BUTTON_BOTH:
case BUTTON_B_LONG:
case BUTTON_F_SHORT:
case BUTTON_B_SHORT:
// Do nothing and display a lock warning
warnUser(translatedString(Tr->WarningKeysLockedString), TICKS_SECOND / 2);
break;
default:
break;
}
} else { // Button not locked
switch (buttons) {
case BUTTON_NONE:
// stay
boostModeOn = false;
currentMode = OperatingMode::soldering;
break;
case BUTTON_BOTH:
case BUTTON_B_LONG:
return; // exit on back long hold
case BUTTON_F_LONG:
// if boost mode is enabled turn it on
if (getSettingValue(SettingsOptions::BoostTemp)) {
boostModeOn = true;
currentMode = OperatingMode::boost;
}
break;
case BUTTON_F_SHORT:
case BUTTON_B_SHORT: {
uint16_t oldTemp = getSettingValue(SettingsOptions::SolderingTemp);
gui_solderingTempAdjust(); // goto adjust temp mode
if (oldTemp != getSettingValue(SettingsOptions::SolderingTemp)) {
saveSettings(); // only save on change
}
} break;
case BUTTON_BOTH_LONG:
if (getSettingValue(SettingsOptions::LockingMode) != 0) {
// Lock buttons
buttonsLocked = true;
warnUser(translatedString(Tr->LockingKeysString), TICKS_SECOND);
}
break;
default:
break;
}
// Update status
int error = currentTempTargetDegC - TipThermoModel::getTipInC();
if (error >= -10 && error <= 10) {
// converged
if (!cxt->scratch_state.state5) {
setBuzzer(true);
cxt->scratch_state.state3 = xTaskGetTickCount() + TICKS_SECOND / 3;
cxt->scratch_state.state5 = true;
}
// else we update the screen information
setStatusLED(LED_HOT);
} else {
setStatusLED(LED_HEATING);
cxt->scratch_state.state5 = false;
}
if (cxt->scratch_state.state3 != 0 && xTaskGetTickCount() >= cxt->scratch_state.state3) {
setBuzzer(false);
}
OLED::clearScreen();
// Draw in the screen details
if (getSettingValue(SettingsOptions::DetailedSoldering)) {
// Draw in the screen details
if (getSettingValue(SettingsOptions::DetailedSoldering)) {
if (OLED::getRotation()) {
OLED::setCursor(50, 0);
} else {
OLED::setCursor(-1, 0);
}
gui_drawTipTemp(true, FontStyle::LARGE);
if (cxt->scratch_state.state2) { // Boost mode is on
if (OLED::getRotation()) {
OLED::setCursor(50, 0);
OLED::setCursor(34, 0);
} else {
OLED::setCursor(-1, 0);
OLED::setCursor(50, 0);
}
gui_drawTipTemp(true, FontStyle::LARGE);
OLED::print(LargeSymbolPlus, FontStyle::LARGE);
} else {
#ifndef NO_SLEEP_MODE
if (getSettingValue(SettingsOptions::Sensitivity) && getSettingValue(SettingsOptions::SleepTime)) {
if (OLED::getRotation()) {
@@ -121,69 +133,43 @@ void gui_solderingMode(uint8_t jumpToSleep) {
printCountdownUntilSleep(getSleepTimeout());
}
#endif
if (boostModeOn) {
if (OLED::getRotation()) {
OLED::setCursor(38, 8);
} else {
OLED::setCursor(55, 8);
}
OLED::print(SmallSymbolPlus, FontStyle::SMALL);
if (OLED::getRotation()) {
OLED::setCursor(32, 8);
} else {
if (OLED::getRotation()) {
OLED::setCursor(32, 8);
} else {
OLED::setCursor(47, 8);
}
OLED::print(PowerSourceNames[getPowerSourceNumber()], FontStyle::SMALL, 2);
OLED::setCursor(47, 8);
}
detailedPowerStatus();
} else {
basicSolderingStatus(boostModeOn);
OLED::print(PowerSourceNames[getPowerSourceNumber()], FontStyle::SMALL, 2);
}
OLED::refresh();
// Update the setpoints for the temperature
if (boostModeOn) {
if (getSettingValue(SettingsOptions::TemperatureInF)) {
currentTempTargetDegC = TipThermoModel::convertFtoC(getSettingValue(SettingsOptions::BoostTemp));
} else {
currentTempTargetDegC = (getSettingValue(SettingsOptions::BoostTemp));
}
} else {
if (getSettingValue(SettingsOptions::TemperatureInF)) {
currentTempTargetDegC = TipThermoModel::convertFtoC(getSettingValue(SettingsOptions::SolderingTemp));
} else {
currentTempTargetDegC = (getSettingValue(SettingsOptions::SolderingTemp));
}
}
detailedPowerStatus();
if (checkExitSoldering()) {
setBuzzer(false);
return;
}
// Update status
int error = currentTempTargetDegC - TipThermoModel::getTipInC();
if (error >= -10 && error <= 10) {
// converged
if (!converged) {
setBuzzer(true);
buzzerEnd = xTaskGetTickCount() + TICKS_SECOND / 3;
converged = true;
}
setStatusLED(LED_HOT);
} else {
setStatusLED(LED_HEATING);
converged = false;
}
if (buzzerEnd != 0 && xTaskGetTickCount() >= buzzerEnd) {
setBuzzer(false);
}
// slow down ui update rate
GUIDelay();
} else {
basicSolderingStatus(cxt->scratch_state.state2);
}
// Check if we should bail due to undervoltage for example
if (checkExitSoldering()) {
setBuzzer(false);
cxt->transitionMode = TransitionAnimation::Right;
return OperatingMode::HomeScreen;
}
#ifdef NO_SLEEP_MODE
if (shouldShutdown()) {
// shutdown
currentTempTargetDegC = 0;
cxt->transitionMode = TransitionAnimation::Right;
return OperatingMode::HomeScreen;
}
#endif
if (shouldBeSleeping()) {
return OperatingMode::Sleeping;
}
if (heaterThermalRunaway) {
currentTempTargetDegC = 0; // heater control off
heaterThermalRunaway = false;
cxt->transitionMode = TransitionAnimation::Right;
return OperatingMode::ThermalRunaway;
}
return handleSolderingButtons(buttons, cxt);
}

View File

@@ -2,222 +2,212 @@
#include "OperatingModes.h"
#include "SolderingCommon.h"
extern OperatingMode currentMode;
void gui_solderingProfileMode() {
OperatingMode gui_solderingProfileMode(const ButtonState buttons, guiContext *cxt) {
/*
* * Soldering (gui_solderingMode)
* * Soldering
* -> Main loop where we draw temp, and animations
* PID control
* --> Long hold back button to exit
* --> Double button to exit
*/
currentMode = OperatingMode::soldering;
TickType_t buzzerEnd = 0;
uint16_t tipTemp = 0;
bool waitForRelease = true;
TickType_t phaseStartTime = xTaskGetTickCount();
uint16_t tipTemp = 0;
uint8_t profilePhase = 0;
uint16_t phaseElapsedSeconds = 0;
uint16_t phaseTotalSeconds = 0;
uint16_t phaseStartTemp = 0;
uint16_t phaseEndTemp = getSettingValue(SettingsOptions::ProfilePreheatTemp);
// If this is during init, start at preheat
if (cxt->scratch_state.state1 == 0) {
cxt->scratch_state.state5 = getSettingValue(SettingsOptions::ProfilePreheatTemp);
}
uint16_t phaseTicksPerDegree = TICKS_SECOND / getSettingValue(SettingsOptions::ProfilePreheatSpeed);
uint16_t profileCurrentTargetTemp = 0;
for (;;) {
ButtonState buttons = getButtonState();
if (buttons) {
if (waitForRelease) {
buttons = BUTTON_NONE;
}
} else {
waitForRelease = false;
}
switch (buttons) {
case BUTTON_NONE:
break;
case BUTTON_BOTH:
case BUTTON_B_LONG:
return; // exit on back long hold
case BUTTON_F_LONG:
case BUTTON_F_SHORT:
case BUTTON_B_SHORT:
// Not used yet
break;
default:
break;
}
tipTemp = getTipTemp();
// if start temp is unknown (preheat), we're setting it now
if (phaseStartTemp == 0) {
phaseStartTemp = tipTemp;
// if this is hotter than the preheat temperature, we should fail
if (phaseStartTemp >= 55) {
warnUser(translatedString(Tr->TooHotToStartProfileWarning), 10 * TICKS_SECOND);
return;
}
}
phaseElapsedSeconds = (xTaskGetTickCount() - phaseStartTime) / TICKS_SECOND;
// have we finished this phase?
if (phaseElapsedSeconds >= phaseTotalSeconds && tipTemp == phaseEndTemp) {
profilePhase++;
phaseStartTemp = phaseEndTemp;
phaseStartTime = xTaskGetTickCount();
phaseElapsedSeconds = 0;
if (profilePhase > getSettingValue(SettingsOptions::ProfilePhases)) {
// done with all phases, lets go to cooldown
phaseTotalSeconds = 0;
phaseEndTemp = 0;
phaseTicksPerDegree = TICKS_SECOND / getSettingValue(SettingsOptions::ProfileCooldownSpeed);
} else {
// set up next phase
switch (profilePhase) {
case 1:
phaseTotalSeconds = getSettingValue(SettingsOptions::ProfilePhase1Duration);
phaseEndTemp = getSettingValue(SettingsOptions::ProfilePhase1Temp);
break;
case 2:
phaseTotalSeconds = getSettingValue(SettingsOptions::ProfilePhase2Duration);
phaseEndTemp = getSettingValue(SettingsOptions::ProfilePhase2Temp);
break;
case 3:
phaseTotalSeconds = getSettingValue(SettingsOptions::ProfilePhase3Duration);
phaseEndTemp = getSettingValue(SettingsOptions::ProfilePhase3Temp);
break;
case 4:
phaseTotalSeconds = getSettingValue(SettingsOptions::ProfilePhase4Duration);
phaseEndTemp = getSettingValue(SettingsOptions::ProfilePhase4Temp);
break;
case 5:
phaseTotalSeconds = getSettingValue(SettingsOptions::ProfilePhase5Duration);
phaseEndTemp = getSettingValue(SettingsOptions::ProfilePhase5Temp);
break;
default:
break;
}
if (phaseStartTemp < phaseEndTemp) {
phaseTicksPerDegree = (phaseTotalSeconds * TICKS_SECOND) / (phaseEndTemp - phaseStartTemp);
} else {
phaseTicksPerDegree = (phaseTotalSeconds * TICKS_SECOND) / (phaseStartTemp - phaseEndTemp);
}
}
}
// cooldown phase done?
if (profilePhase > getSettingValue(SettingsOptions::ProfilePhases)) {
if (TipThermoModel::getTipInC() < 55) {
// we're done, let the buzzer beep too
setStatusLED(LED_STANDBY);
if (buzzerEnd == 0) {
setBuzzer(true);
buzzerEnd = xTaskGetTickCount() + TICKS_SECOND / 3;
}
}
}
// determine current target temp
if (phaseStartTemp < phaseEndTemp) {
if (profileCurrentTargetTemp < phaseEndTemp) {
profileCurrentTargetTemp = phaseStartTemp + ((xTaskGetTickCount() - phaseStartTime) / phaseTicksPerDegree);
}
} else {
if (profileCurrentTargetTemp > phaseEndTemp) {
profileCurrentTargetTemp = phaseStartTemp - ((xTaskGetTickCount() - phaseStartTime) / phaseTicksPerDegree);
}
}
OLED::clearScreen();
// Draw in the screen details
if (getSettingValue(SettingsOptions::DetailedSoldering)) {
// print temperature
if (OLED::getRotation()) {
OLED::setCursor(48, 0);
} else {
OLED::setCursor(0, 0);
}
OLED::printNumber(tipTemp, 3, FontStyle::SMALL);
OLED::print(SmallSymbolSlash, FontStyle::SMALL);
OLED::printNumber(profileCurrentTargetTemp, 3, FontStyle::SMALL);
OLED::printSymbolDeg(FontStyle::SMALL);
// print phase
if (profilePhase > 0 && profilePhase <= getSettingValue(SettingsOptions::ProfilePhases)) {
if (OLED::getRotation()) {
OLED::setCursor(36, 0);
} else {
OLED::setCursor(55, 0);
}
OLED::printNumber(profilePhase, 1, FontStyle::SMALL);
}
// print time progress / preheat / cooldown
if (OLED::getRotation()) {
OLED::setCursor(42, 8);
} else {
OLED::setCursor(0, 8);
}
if (profilePhase == 0) {
OLED::print(translatedString(Tr->ProfilePreheatString), FontStyle::SMALL);
} else if (profilePhase > getSettingValue(SettingsOptions::ProfilePhases)) {
OLED::print(translatedString(Tr->ProfileCooldownString), FontStyle::SMALL);
} else {
OLED::printNumber(phaseElapsedSeconds / 60, 1, FontStyle::SMALL);
OLED::print(SmallSymbolColon, FontStyle::SMALL);
OLED::printNumber(phaseElapsedSeconds % 60, 2, FontStyle::SMALL, false);
OLED::print(SmallSymbolSlash, FontStyle::SMALL);
// blink if we can't keep up with the time goal
if (phaseElapsedSeconds < phaseTotalSeconds + 2 || (xTaskGetTickCount() / TICKS_SECOND) % 2 == 0) {
OLED::printNumber(phaseTotalSeconds / 60, 1, FontStyle::SMALL);
OLED::print(SmallSymbolColon, FontStyle::SMALL);
OLED::printNumber(phaseTotalSeconds % 60, 2, FontStyle::SMALL, false);
}
}
detailedPowerStatus();
} else {
basicSolderingStatus(false);
}
OLED::refresh();
// Update the setpoints for the temperature
if (getSettingValue(SettingsOptions::TemperatureInF)) {
currentTempTargetDegC = TipThermoModel::convertFtoC(profileCurrentTargetTemp);
} else {
currentTempTargetDegC = profileCurrentTargetTemp;
}
if (checkExitSoldering() || (buzzerEnd != 0 && xTaskGetTickCount() >= buzzerEnd)) {
setBuzzer(false);
return;
}
// Update LED status
if (profilePhase == 0) {
setStatusLED(LED_HEATING);
} else if (profilePhase > getSettingValue(SettingsOptions::ProfilePhases)) {
setStatusLED(LED_COOLING_STILL_HOT);
} else {
setStatusLED(LED_HOT);
}
// slow down ui update rate
GUIDelay();
switch (buttons) {
case BUTTON_BOTH:
case BUTTON_B_LONG:
cxt->transitionMode = TransitionAnimation::Right;
return OperatingMode::HomeScreen; // exit on back long hold
case BUTTON_F_LONG:
case BUTTON_F_SHORT:
case BUTTON_B_SHORT:
case BUTTON_NONE:
// Not used yet
break;
default:
break;
}
if (getSettingValue(SettingsOptions::TemperatureInF)) {
tipTemp = TipThermoModel::getTipInF();
} else {
tipTemp = TipThermoModel::getTipInC();
}
// If time of entering is unknown; then we start now
if (cxt->scratch_state.state3 == 0) {
cxt->scratch_state.state3 = xTaskGetTickCount();
}
// if start temp is unknown (preheat), we're setting it now
if (cxt->scratch_state.state6 == 0) {
cxt->scratch_state.state6 = tipTemp;
// if this is hotter than the preheat temperature, we should fail
if (cxt->scratch_state.state6 >= 55) {
warnUser(translatedString(Tr->TooHotToStartProfileWarning), buttons);
return OperatingMode::HomeScreen;
}
}
uint16_t phaseElapsedSeconds = (xTaskGetTickCount() - cxt->scratch_state.state3) / TICKS_SECOND;
// have we finished this phase?
if (phaseElapsedSeconds >= cxt->scratch_state.state2 && tipTemp == cxt->scratch_state.state5) {
cxt->scratch_state.state1++;
cxt->scratch_state.state6 = cxt->scratch_state.state5;
cxt->scratch_state.state3 = xTaskGetTickCount();
phaseElapsedSeconds = 0;
if (cxt->scratch_state.state1 > getSettingValue(SettingsOptions::ProfilePhases)) {
// done with all phases, lets go to cooldown
cxt->scratch_state.state2 = 0;
cxt->scratch_state.state5 = 0;
phaseTicksPerDegree = TICKS_SECOND / getSettingValue(SettingsOptions::ProfileCooldownSpeed);
} else {
// set up next phase
switch (cxt->scratch_state.state1) {
case 1:
cxt->scratch_state.state2 = getSettingValue(SettingsOptions::ProfilePhase1Duration);
cxt->scratch_state.state5 = getSettingValue(SettingsOptions::ProfilePhase1Temp);
break;
case 2:
cxt->scratch_state.state2 = getSettingValue(SettingsOptions::ProfilePhase2Duration);
cxt->scratch_state.state5 = getSettingValue(SettingsOptions::ProfilePhase2Temp);
break;
case 3:
cxt->scratch_state.state2 = getSettingValue(SettingsOptions::ProfilePhase3Duration);
cxt->scratch_state.state5 = getSettingValue(SettingsOptions::ProfilePhase3Temp);
break;
case 4:
cxt->scratch_state.state2 = getSettingValue(SettingsOptions::ProfilePhase4Duration);
cxt->scratch_state.state5 = getSettingValue(SettingsOptions::ProfilePhase4Temp);
break;
case 5:
cxt->scratch_state.state2 = getSettingValue(SettingsOptions::ProfilePhase5Duration);
cxt->scratch_state.state5 = getSettingValue(SettingsOptions::ProfilePhase5Temp);
break;
default:
break;
}
if (cxt->scratch_state.state6 < cxt->scratch_state.state5) {
phaseTicksPerDegree = (cxt->scratch_state.state2 * TICKS_SECOND) / (cxt->scratch_state.state5 - cxt->scratch_state.state6);
} else {
phaseTicksPerDegree = (cxt->scratch_state.state2 * TICKS_SECOND) / (cxt->scratch_state.state6 - cxt->scratch_state.state5);
}
}
}
// cooldown phase done?
if (cxt->scratch_state.state1 > getSettingValue(SettingsOptions::ProfilePhases)) {
if (TipThermoModel::getTipInC() < 55) {
// we're done, let the buzzer beep too
setStatusLED(LED_STANDBY);
if (cxt->scratch_state.state4 == 0) {
setBuzzer(true);
cxt->scratch_state.state4 = xTaskGetTickCount() + TICKS_SECOND / 3;
}
}
}
// determine current target temp
if (cxt->scratch_state.state6 < cxt->scratch_state.state5) {
if (profileCurrentTargetTemp < cxt->scratch_state.state5) {
profileCurrentTargetTemp = cxt->scratch_state.state6 + ((xTaskGetTickCount() - cxt->viewEnterTime) / phaseTicksPerDegree);
}
} else {
if (profileCurrentTargetTemp > cxt->scratch_state.state5) {
profileCurrentTargetTemp = cxt->scratch_state.state6 - ((xTaskGetTickCount() - cxt->viewEnterTime) / phaseTicksPerDegree);
}
}
// Draw in the screen details
if (getSettingValue(SettingsOptions::DetailedSoldering)) {
// print temperature
if (OLED::getRotation()) {
OLED::setCursor(48, 0);
} else {
OLED::setCursor(0, 0);
}
OLED::printNumber(tipTemp, 3, FontStyle::SMALL);
OLED::print(SmallSymbolSlash, FontStyle::SMALL);
OLED::printNumber(profileCurrentTargetTemp, 3, FontStyle::SMALL);
if (getSettingValue(SettingsOptions::TemperatureInF)) {
OLED::print(SmallSymbolDegF, FontStyle::SMALL);
} else {
OLED::print(SmallSymbolDegC, FontStyle::SMALL);
}
// print phase
if (cxt->scratch_state.state1 > 0 && cxt->scratch_state.state1 <= getSettingValue(SettingsOptions::ProfilePhases)) {
if (OLED::getRotation()) {
OLED::setCursor(36, 0);
} else {
OLED::setCursor(55, 0);
}
OLED::printNumber(cxt->scratch_state.state1, 1, FontStyle::SMALL);
}
// print time progress / preheat / cooldown
if (OLED::getRotation()) {
OLED::setCursor(42, 8);
} else {
OLED::setCursor(0, 8);
}
if (cxt->scratch_state.state1 == 0) {
OLED::print(translatedString(Tr->ProfilePreheatString), FontStyle::SMALL);
} else if (cxt->scratch_state.state1 > getSettingValue(SettingsOptions::ProfilePhases)) {
OLED::print(translatedString(Tr->ProfileCooldownString), FontStyle::SMALL);
} else {
OLED::printNumber(phaseElapsedSeconds / 60, 1, FontStyle::SMALL);
OLED::print(SmallSymbolColon, FontStyle::SMALL);
OLED::printNumber(phaseElapsedSeconds % 60, 2, FontStyle::SMALL, false);
OLED::print(SmallSymbolSlash, FontStyle::SMALL);
// blink if we can't keep up with the time goal
if (phaseElapsedSeconds < cxt->scratch_state.state2 + 2 || (xTaskGetTickCount() / TICKS_SECOND) % 2 == 0) {
OLED::printNumber(cxt->scratch_state.state2 / 60, 1, FontStyle::SMALL);
OLED::print(SmallSymbolColon, FontStyle::SMALL);
OLED::printNumber(cxt->scratch_state.state2 % 60, 2, FontStyle::SMALL, false);
}
}
detailedPowerStatus();
} else {
basicSolderingStatus(false);
}
// Update the setpoints for the temperature
if (getSettingValue(SettingsOptions::TemperatureInF)) {
currentTempTargetDegC = TipThermoModel::convertFtoC(profileCurrentTargetTemp);
} else {
currentTempTargetDegC = profileCurrentTargetTemp;
}
if (checkExitSoldering() || (cxt->scratch_state.state4 != 0 && xTaskGetTickCount() >= cxt->scratch_state.state4)) {
setBuzzer(false);
return OperatingMode::HomeScreen;
}
if (heaterThermalRunaway) {
currentTempTargetDegC = 0; // heater control off
heaterThermalRunaway = false;
return OperatingMode::ThermalRunaway;
}
// Update LED status
if (cxt->scratch_state.state1 == 0) {
setStatusLED(LED_HEATING);
} else if (cxt->scratch_state.state1 > getSettingValue(SettingsOptions::ProfilePhases)) {
setStatusLED(LED_COOLING_STILL_HOT);
} else {
setStatusLED(LED_HOT);
}
return OperatingMode::SolderingProfile;
}

View File

@@ -1,120 +1,106 @@
#include "OperatingModes.h"
void gui_solderingTempAdjust(void) {
TickType_t lastChange = xTaskGetTickCount();
currentTempTargetDegC = 0; // Turn off heater while adjusting temp
TickType_t autoRepeatTimer = 0;
uint8_t autoRepeatAcceleration = 0;
#ifndef PROFILE_SUPPORT
bool waitForRelease = false;
ButtonState buttons = getButtonState();
OperatingMode gui_solderingTempAdjust(const ButtonState buttonIn, guiContext *cxt) {
if (buttons != BUTTON_NONE) {
// Temp adjust entered by long-pressing F button.
waitForRelease = true;
currentTempTargetDegC = 0; // Turn off heater while adjusting temp
uint16_t *waitForRelease = &(cxt->scratch_state.state1);
uint32_t *autoRepeatTimer = &(cxt->scratch_state.state3);
uint16_t *autoRepeatAcceleration = &(cxt->scratch_state.state2);
ButtonState buttons = buttonIn;
if (*waitForRelease == 0) {
// When we first enter we wait for the user to release buttons before enabling changes
if (buttons != BUTTON_NONE) {
buttons = BUTTON_NONE;
} else {
(*waitForRelease)++;
}
}
#else
ButtonState buttons;
#endif
for (;;) {
OLED::setCursor(0, 0);
OLED::clearScreen();
buttons = getButtonState();
if (buttons) {
lastChange = xTaskGetTickCount();
#ifndef PROFILE_SUPPORT
if (waitForRelease) {
buttons = BUTTON_NONE;
}
} else {
waitForRelease = false;
#endif
}
int16_t delta = 0;
switch (buttons) {
case BUTTON_NONE:
// stay
autoRepeatAcceleration = 0;
break;
case BUTTON_BOTH:
// exit
return;
break;
case BUTTON_B_LONG:
if (xTaskGetTickCount() - autoRepeatTimer + autoRepeatAcceleration > PRESS_ACCEL_INTERVAL_MAX) {
delta = -getSettingValue(SettingsOptions::TempChangeLongStep);
autoRepeatTimer = xTaskGetTickCount();
autoRepeatAcceleration += PRESS_ACCEL_STEP;
}
break;
case BUTTON_B_SHORT:
delta = -getSettingValue(SettingsOptions::TempChangeShortStep);
break;
case BUTTON_F_LONG:
if (xTaskGetTickCount() - autoRepeatTimer + autoRepeatAcceleration > PRESS_ACCEL_INTERVAL_MAX) {
delta = getSettingValue(SettingsOptions::TempChangeLongStep);
autoRepeatTimer = xTaskGetTickCount();
autoRepeatAcceleration += PRESS_ACCEL_STEP;
}
break;
case BUTTON_F_SHORT:
delta = getSettingValue(SettingsOptions::TempChangeShortStep);
break;
default:
break;
}
if ((PRESS_ACCEL_INTERVAL_MAX - autoRepeatAcceleration) < PRESS_ACCEL_INTERVAL_MIN) {
autoRepeatAcceleration = PRESS_ACCEL_INTERVAL_MAX - PRESS_ACCEL_INTERVAL_MIN;
}
// If buttons are flipped; flip the delta
if (getSettingValue(SettingsOptions::ReverseButtonTempChangeEnabled)) {
delta = -delta;
}
if (delta != 0) {
// constrain between the set temp limits, i.e. 10-450 C
int16_t newTemp = getSettingValue(SettingsOptions::SolderingTemp);
newTemp += delta;
// Round to nearest increment of delta
delta = abs(delta);
newTemp = (newTemp / delta) * delta;
OLED::setCursor(0, 0);
if (getSettingValue(SettingsOptions::TemperatureInF)) {
if (newTemp > MAX_TEMP_F) {
newTemp = MAX_TEMP_F;
}
if (newTemp < MIN_TEMP_F) {
newTemp = MIN_TEMP_F;
}
} else {
if (newTemp > MAX_TEMP_C) {
newTemp = MAX_TEMP_C;
}
if (newTemp < MIN_TEMP_C) {
newTemp = MIN_TEMP_C;
}
}
setSettingValue(SettingsOptions::SolderingTemp, (uint16_t)newTemp);
int16_t delta = 0;
switch (buttons) {
case BUTTON_NONE:
// stay
(*autoRepeatAcceleration) = 0;
break;
case BUTTON_BOTH:
// exit
saveSettings();
cxt->transitionMode = TransitionAnimation::Right;
return cxt->previousMode;
case BUTTON_B_LONG:
if (xTaskGetTickCount() - (*autoRepeatTimer) + (*autoRepeatAcceleration) > PRESS_ACCEL_INTERVAL_MAX) {
delta = -getSettingValue(SettingsOptions::TempChangeLongStep);
(*autoRepeatTimer) = xTaskGetTickCount();
(*autoRepeatAcceleration) += PRESS_ACCEL_STEP;
}
if (xTaskGetTickCount() - lastChange > (TICKS_SECOND * 2)) {
return; // exit if user just doesn't press anything for a bit
break;
case BUTTON_B_SHORT:
delta = -getSettingValue(SettingsOptions::TempChangeShortStep);
break;
case BUTTON_F_LONG:
if (xTaskGetTickCount() - (*autoRepeatTimer) + (*autoRepeatAcceleration) > PRESS_ACCEL_INTERVAL_MAX) {
delta = getSettingValue(SettingsOptions::TempChangeLongStep);
(*autoRepeatTimer) = xTaskGetTickCount();
(*autoRepeatAcceleration) += PRESS_ACCEL_STEP;
}
if (OLED::getRotation()) {
OLED::print(getSettingValue(SettingsOptions::ReverseButtonTempChangeEnabled) ? LargeSymbolPlus : LargeSymbolMinus, FontStyle::LARGE);
} else {
OLED::print(getSettingValue(SettingsOptions::ReverseButtonTempChangeEnabled) ? LargeSymbolMinus : LargeSymbolPlus, FontStyle::LARGE);
}
OLED::print(LargeSymbolSpace, FontStyle::LARGE);
OLED::printNumber(getSettingValue(SettingsOptions::SolderingTemp), 3, FontStyle::LARGE);
OLED::printSymbolDeg(FontStyle::EXTRAS);
OLED::print(LargeSymbolSpace, FontStyle::LARGE);
if (OLED::getRotation()) {
OLED::print(getSettingValue(SettingsOptions::ReverseButtonTempChangeEnabled) ? LargeSymbolMinus : LargeSymbolPlus, FontStyle::LARGE);
} else {
OLED::print(getSettingValue(SettingsOptions::ReverseButtonTempChangeEnabled) ? LargeSymbolPlus : LargeSymbolMinus, FontStyle::LARGE);
}
OLED::refresh();
GUIDelay();
break;
case BUTTON_F_SHORT:
delta = getSettingValue(SettingsOptions::TempChangeShortStep);
break;
default:
break;
}
if ((PRESS_ACCEL_INTERVAL_MAX - (*autoRepeatAcceleration)) < PRESS_ACCEL_INTERVAL_MIN) {
(*autoRepeatAcceleration) = PRESS_ACCEL_INTERVAL_MAX - PRESS_ACCEL_INTERVAL_MIN;
}
// If buttons are flipped; flip the delta
if (getSettingValue(SettingsOptions::ReverseButtonTempChangeEnabled)) {
delta = -delta;
}
if (delta != 0) {
// constrain between the set temp limits, i.e. 10-450 C
int16_t newTemp = getSettingValue(SettingsOptions::SolderingTemp);
newTemp += delta;
// Round to nearest increment of delta
delta = abs(delta);
newTemp = (newTemp / delta) * delta;
if (getSettingValue(SettingsOptions::TemperatureInF)) {
if (newTemp > MAX_TEMP_F) {
newTemp = MAX_TEMP_F;
} else if (newTemp < MIN_TEMP_F) {
newTemp = MIN_TEMP_F;
}
} else {
if (newTemp > MAX_TEMP_C) {
newTemp = MAX_TEMP_C;
} else if (newTemp < MIN_TEMP_C) {
newTemp = MIN_TEMP_C;
}
}
setSettingValue(SettingsOptions::SolderingTemp, (uint16_t)newTemp);
}
if (OLED::getRotation()) {
OLED::print(getSettingValue(SettingsOptions::ReverseButtonTempChangeEnabled) ? LargeSymbolPlus : LargeSymbolMinus, FontStyle::LARGE);
} else {
OLED::print(getSettingValue(SettingsOptions::ReverseButtonTempChangeEnabled) ? LargeSymbolMinus : LargeSymbolPlus, FontStyle::LARGE);
}
OLED::print(LargeSymbolSpace, FontStyle::LARGE);
OLED::printNumber(getSettingValue(SettingsOptions::SolderingTemp), 3, FontStyle::LARGE);
OLED::printSymbolDeg(FontStyle::EXTRAS);
OLED::print(LargeSymbolSpace, FontStyle::LARGE);
if (OLED::getRotation()) {
OLED::print(getSettingValue(SettingsOptions::ReverseButtonTempChangeEnabled) ? LargeSymbolMinus : LargeSymbolPlus, FontStyle::LARGE);
} else {
OLED::print(getSettingValue(SettingsOptions::ReverseButtonTempChangeEnabled) ? LargeSymbolPlus : LargeSymbolMinus, FontStyle::LARGE);
}
if (xTaskGetTickCount() - lastButtonTime > (TICKS_SECOND * 3)) {
saveSettings();
cxt->transitionMode = TransitionAnimation::Right;
return cxt->previousMode; // exit if user just doesn't press anything for a bit
}
return OperatingMode::TemperatureAdjust; // Stay in temp adjust
}

View File

@@ -2,93 +2,85 @@
#ifdef POW_PD
#ifdef HAS_POWER_DEBUG_MENU
void showPDDebug(void) {
OperatingMode showPDDebug(const ButtonState buttons, guiContext *cxt) {
// Print out the USB-PD state
// Basically this is like the Debug menu, but instead we want to print out the PD status
uint8_t screen = 0;
ButtonState b;
for (;;) {
OLED::clearScreen(); // Ensure the buffer starts clean
OLED::setCursor(0, 0); // Position the cursor at the 0,0 (top left)
OLED::print(SmallSymbolPDDebug, FontStyle::SMALL); // Print Title
OLED::setCursor(0, 8); // second line
if (screen == 0) {
// Print the PD state machine
OLED::print(SmallSymbolState, FontStyle::SMALL);
OLED::print(SmallSymbolSpace, FontStyle::SMALL);
OLED::printNumber(USBPowerDelivery::getStateNumber(), 2, FontStyle::SMALL, true);
OLED::print(SmallSymbolSpace, FontStyle::SMALL);
// Also print vbus mod status
if (USBPowerDelivery::fusbPresent()) {
if (USBPowerDelivery::negotiationComplete() || (xTaskGetTickCount() > (TICKS_SECOND * 10))) {
if (!USBPowerDelivery::isVBUSConnected()) {
OLED::print(SmallSymbolNoVBus, FontStyle::SMALL);
} else {
OLED::print(SmallSymbolVBus, FontStyle::SMALL);
}
uint16_t *screen = &(cxt->scratch_state.state1);
OLED::setCursor(0, 0); // Position the cursor at the 0,0 (top left)
OLED::print(SmallSymbolPDDebug, FontStyle::SMALL); // Print Title
OLED::setCursor(0, 8); // second line
if ((*screen) == 0) {
// Print the PD state machine
OLED::print(SmallSymbolState, FontStyle::SMALL);
OLED::print(SmallSymbolSpace, FontStyle::SMALL);
OLED::printNumber(USBPowerDelivery::getStateNumber(), 2, FontStyle::SMALL, true);
OLED::print(SmallSymbolSpace, FontStyle::SMALL);
// Also print vbus mod status
if (USBPowerDelivery::fusbPresent()) {
if (USBPowerDelivery::negotiationComplete() || (xTaskGetTickCount() > (TICKS_SECOND * 10))) {
if (!USBPowerDelivery::isVBUSConnected()) {
OLED::print(SmallSymbolNoVBus, FontStyle::SMALL);
} else {
OLED::print(SmallSymbolVBus, FontStyle::SMALL);
}
}
}
} else {
// Print out the Proposed power options one by one
auto lastCaps = USBPowerDelivery::getLastSeenCapabilities();
if (((*screen) - 1) < 11) {
int voltage_mv = 0;
int min_voltage = 0;
int current_a_x100 = 0;
int wattage = 0;
if ((lastCaps[(*screen) - 1] & PD_PDO_TYPE) == PD_PDO_TYPE_FIXED) {
voltage_mv = PD_PDV2MV(PD_PDO_SRC_FIXED_VOLTAGE_GET(lastCaps[(*screen) - 1])); // voltage in mV units
current_a_x100 = PD_PDO_SRC_FIXED_CURRENT_GET(lastCaps[(*screen) - 1]); // current in 10mA units
} else if (((lastCaps[(*screen) - 1] & PD_PDO_TYPE) == PD_PDO_TYPE_AUGMENTED) && ((lastCaps[(*screen) - 1] & PD_APDO_TYPE) == PD_APDO_TYPE_AVS)) {
voltage_mv = PD_PAV2MV(PD_APDO_AVS_MAX_VOLTAGE_GET(lastCaps[(*screen) - 1]));
min_voltage = PD_PAV2MV(PD_APDO_PPS_MIN_VOLTAGE_GET(lastCaps[(*screen) - 1]));
// Last value is wattage
wattage = PD_APDO_AVS_MAX_POWER_GET(lastCaps[(*screen) - 1]);
} else if (((lastCaps[(*screen) - 1] & PD_PDO_TYPE) == PD_PDO_TYPE_AUGMENTED) && ((lastCaps[(*screen) - 1] & PD_APDO_TYPE) == PD_APDO_TYPE_PPS)) {
voltage_mv = PD_PAV2MV(PD_APDO_PPS_MAX_VOLTAGE_GET(lastCaps[(*screen) - 1]));
min_voltage = PD_PAV2MV(PD_APDO_PPS_MIN_VOLTAGE_GET(lastCaps[(*screen) - 1]));
current_a_x100 = PD_PAI2CA(PD_APDO_PPS_CURRENT_GET(lastCaps[(*screen) - 1])); // max current in 10mA units
}
// Skip not used entries
if (voltage_mv == 0) {
(*screen) += 1;
} else {
// print out this entry of the proposal
OLED::printNumber(*screen, 2, FontStyle::SMALL, true); // print the entry number
OLED::print(SmallSymbolSpace, FontStyle::SMALL);
if (min_voltage > 0) {
OLED::printNumber(min_voltage / 1000, 2, FontStyle::SMALL, true); // print the voltage
OLED::print(SmallSymbolMinus, FontStyle::SMALL);
}
OLED::printNumber(voltage_mv / 1000, 2, FontStyle::SMALL, true); // print the voltage
OLED::print(SmallSymbolVolts, FontStyle::SMALL);
OLED::print(SmallSymbolSpace, FontStyle::SMALL);
if (wattage) {
OLED::printNumber(wattage, 3, FontStyle::SMALL, true); // print the current in 0.1A res
OLED::print(SmallSymbolWatts, FontStyle::SMALL);
} else {
OLED::printNumber(current_a_x100 / 100, 2, FontStyle::SMALL, true); // print the current in 0.1A res
OLED::print(SmallSymbolDot, FontStyle::SMALL);
OLED::printNumber(current_a_x100 % 100, 2, FontStyle::SMALL, false); // print the current in 0.1A res
OLED::print(SmallSymbolAmps, FontStyle::SMALL);
}
}
} else {
// Print out the Proposed power options one by one
auto lastCaps = USBPowerDelivery::getLastSeenCapabilities();
if ((screen - 1) < 11) {
int voltage_mv = 0;
int min_voltage = 0;
int current_a_x100 = 0;
int wattage = 0;
if ((lastCaps[screen - 1] & PD_PDO_TYPE) == PD_PDO_TYPE_FIXED) {
voltage_mv = PD_PDV2MV(PD_PDO_SRC_FIXED_VOLTAGE_GET(lastCaps[screen - 1])); // voltage in mV units
current_a_x100 = PD_PDO_SRC_FIXED_CURRENT_GET(lastCaps[screen - 1]); // current in 10mA units
} else if (((lastCaps[screen - 1] & PD_PDO_TYPE) == PD_PDO_TYPE_AUGMENTED) && ((lastCaps[screen - 1] & PD_APDO_TYPE) == PD_APDO_TYPE_AVS)) {
voltage_mv = PD_PAV2MV(PD_APDO_AVS_MAX_VOLTAGE_GET(lastCaps[screen - 1]));
min_voltage = PD_PAV2MV(PD_APDO_PPS_MIN_VOLTAGE_GET(lastCaps[screen - 1]));
// Last value is wattage
wattage = PD_APDO_AVS_MAX_POWER_GET(lastCaps[screen - 1]);
} else if (((lastCaps[screen - 1] & PD_PDO_TYPE) == PD_PDO_TYPE_AUGMENTED) && ((lastCaps[screen - 1] & PD_APDO_TYPE) == PD_APDO_TYPE_PPS)) {
voltage_mv = PD_PAV2MV(PD_APDO_PPS_MAX_VOLTAGE_GET(lastCaps[screen - 1]));
min_voltage = PD_PAV2MV(PD_APDO_PPS_MIN_VOLTAGE_GET(lastCaps[screen - 1]));
current_a_x100 = PD_PAI2CA(PD_APDO_PPS_CURRENT_GET(lastCaps[screen - 1])); // max current in 10mA units
}
// Skip not used entries
if (voltage_mv == 0) {
screen++;
} else {
// print out this entry of the proposal
OLED::printNumber(screen, 2, FontStyle::SMALL, true); // print the entry number
OLED::print(SmallSymbolSpace, FontStyle::SMALL);
if (min_voltage > 0) {
OLED::printNumber(min_voltage / 1000, 2, FontStyle::SMALL, true); // print the voltage
OLED::print(SmallSymbolMinus, FontStyle::SMALL);
}
OLED::printNumber(voltage_mv / 1000, 2, FontStyle::SMALL, true); // print the voltage
OLED::print(SmallSymbolVolts, FontStyle::SMALL);
OLED::print(SmallSymbolSpace, FontStyle::SMALL);
if (wattage) {
OLED::printNumber(wattage, 3, FontStyle::SMALL, true); // print the current in 0.1A res
OLED::print(SmallSymbolWatts, FontStyle::SMALL);
} else {
OLED::printNumber(current_a_x100 / 100, 2, FontStyle::SMALL, true); // print the current in 0.1A res
OLED::print(SmallSymbolDot, FontStyle::SMALL);
OLED::printNumber(current_a_x100 % 100, 2, FontStyle::SMALL, false); // print the current in 0.1A res
OLED::print(SmallSymbolAmps, FontStyle::SMALL);
}
}
} else {
screen = 0;
}
(*screen) = 0;
}
OLED::refresh();
b = getButtonState();
if (b == BUTTON_B_SHORT) {
return;
} else if (b == BUTTON_F_SHORT) {
screen++;
}
GUIDelay();
}
if (buttons == BUTTON_B_SHORT) {
return OperatingMode::InitialisationDone;
} else if (buttons == BUTTON_F_SHORT) {
(*screen) += 1;
}
return OperatingMode::UsbPDDebug;
}
#endif
#endif

View File

@@ -2,7 +2,7 @@
#include "OperatingModes.h"
#if POW_PD_EXT == 1
#ifdef HAS_POWER_DEBUG_MENU
void showPDDebug(void) {
OperatingMode showPDDebug(const ButtonState buttons, guiContext *cxt) {
// Print out the USB-PD state
// Basically this is like the Debug menu, but instead we want to print out the PD status
uint8_t screen = 0;
@@ -44,13 +44,14 @@ void showPDDebug(void) {
OLED::refresh();
b = getButtonState();
if (b == BUTTON_B_SHORT) {
return;
return OperatingMode::InitialisationDone;
} else if (b == BUTTON_F_SHORT) {
screen++;
}
GUIDelay();
}
return OperatingMode::UsbPDDebug;
}
#endif
#endif

View File

@@ -1,5 +1,6 @@
#include "OperatingModeUtilities.h"
#include "OperatingModes.h"
#include "SolderingCommon.h"
#include "TipThermoModel.h"
void gui_drawTipTemp(bool symbol, const FontStyle font) {

View File

@@ -1,18 +1,19 @@
#ifndef OPERATING_MODE_UTILITIES_H_
#define OPERATING_MODE_UTILITIES_H_
#include "Buttons.hpp"
#include "OLED.hpp"
#include <stdbool.h>
void GUIDelay(); //
bool checkForUnderVoltage(void); //
uint32_t getSleepTimeout(void); //
bool shouldBeSleeping(bool inAutoStart); //
bool shouldShutdown(void); //
void gui_drawTipTemp(bool symbol, const FontStyle font); //
void printVoltage(void); //
void warnUser(const char *warning, const TickType_t timeout); //
void gui_drawBatteryIcon(void); //
bool checkForUnderVoltage(void); //
uint16_t min(uint16_t a, uint16_t b); //
void printCountdownUntilSleep(int sleepThres); //
void GUIDelay(); //
bool checkForUnderVoltage(void); //
uint32_t getSleepTimeout(void); //
bool shouldBeSleeping(); //
bool shouldShutdown(void); //
void gui_drawTipTemp(bool symbol, const FontStyle font); //
void printVoltage(void); //
bool warnUser(const char *warning, const ButtonState buttons); //
void gui_drawBatteryIcon(void); //
bool checkForUnderVoltage(void); //
uint16_t min(uint16_t a, uint16_t b); //
void printCountdownUntilSleep(int sleepThres); //
#endif

View File

@@ -1,8 +1,12 @@
#include "Buttons.hpp"
#include "OperatingModeUtilities.h"
void warnUser(const char *warning, const TickType_t timeout) {
#include "OperatingModes.h"
bool warnUser(const char *warning, const ButtonState buttons) {
OLED::clearScreen();
OLED::printWholeScreen(warning);
OLED::refresh();
waitForButtonPressOrTimeout(timeout);
// Also timeout after 5 seconds
if ((xTaskGetTickCount() - lastButtonTime) > TICKS_SECOND * 5) {
return true;
}
return buttons != BUTTON_NONE;
}

View File

@@ -4,6 +4,7 @@
#include "SolderingCommon.h"
#include "OperatingModes.h"
#include "Types.h"
#include "configuration.h"
#include "history.hpp"
@@ -106,30 +107,8 @@ bool checkExitSoldering(void) {
}
}
#endif
#ifdef NO_SLEEP_MODE
// No sleep mode, but still want shutdown timeout
if (shouldShutdown()) {
// shutdown
currentTempTargetDegC = 0;
lastMovementTime = xTaskGetTickCount(); // We manually move the movement time to now such that shutdown timer is reset
return true; // we want to exit soldering mode
}
#endif
if (shouldBeSleeping(false)) {
if (gui_SolderingSleepingMode(false, false)) {
return true; // If the function returns non-0 then exit
}
}
// If we have tripped thermal runaway, turn off heater and show warning
if (heaterThermalRunaway) {
currentTempTargetDegC = 0; // heater control off
warnUser(translatedString(Tr->WarningThermalRunaway), 10 * TICKS_SECOND);
heaterThermalRunaway = false;
return true;
}
return false;
}

View File

@@ -1,8 +1,11 @@
#ifndef SOLDERING_COMMON_H
#define SOLDERING_COMMON_H
#include "Types.h"
#include <stdint.h>
#ifndef SOLDERING_COMMON_H_
#define SOLDERING_COMMON_H_
void detailedPowerStatus();
void basicSolderingStatus(bool boostModeOn);
bool checkExitSoldering();
void detailedPowerStatus();
void basicSolderingStatus(bool boostModeOn);
bool checkExitSoldering();
TemperatureType_t getTipTemp(void);
#endif //SOLDERING_COMMON_H
#endif // SOLDERING_COMMON_H_

View File

@@ -17,4 +17,4 @@ void printCountdownUntilSleep(int sleepThres) {
OLED::print(SmallSymbolSeconds, FontStyle::SMALL);
}
}
#endif
#endif

View File

@@ -4,15 +4,13 @@
TickType_t lastHallEffectSleepStart = 0;
extern TickType_t lastMovementTime;
bool shouldBeSleeping(bool inAutoStart) {
bool shouldBeSleeping() {
#ifndef NO_SLEEP_MODE
// Return true if the iron should be in sleep mode
if (getSettingValue(SettingsOptions::Sensitivity) && getSettingValue(SettingsOptions::SleepTime)) {
if (inAutoStart) {
// In auto start we are asleep until movement
if (lastMovementTime == 0 && lastButtonTime == 0) {
return true;
}
// In auto start we are asleep until movement
if (lastMovementTime == 0 && lastButtonTime == 0) {
return true;
}
if (lastMovementTime > 0 || lastButtonTime > 0) {
if (((xTaskGetTickCount() - lastMovementTime) > getSleepTimeout()) && ((xTaskGetTickCount() - lastButtonTime) > getSleepTimeout())) {