1
0
forked from me/IronOS

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

@@ -0,0 +1,40 @@
# GUI Rendering
The GUI aims to be somewhat similar to immediate mode rendering, where the screen is re-rendered each sweep.
This is due to a few aims:
1. Functions should try and contain their state to the context struct (helps keep state usage flatter)
2. Allows external events to change the state
3. Means state can be read/write over BLE or other external control interfaces
## Transitions
When changing the view to a new view it can be preferable to transition using an animation.
The tooling provides for left, right and down animations at this point.
The use of these gives a notion of "direction" when navigating the menu.
```
┌───────────┐
│ Debug Menu│
└─────┬─────┘
┌──────────────┐ ┌────┴─────┐ ┌──────────────────┐ ┌─────────────────┐
│Soldering Mode│ │ │ │ │ │ │
│ OR ├───────────┤Home Menu ├───────────┤Settings Main Menu├───────────┤Settings sub menu│
│Reflow Mode│ │ │ │ │ │ │
└──────────────┘ └──────────┘ └──────────────────┘ └─────────┬───────┘
┌─────────┴───────┐
│ │
│Settings sub menu│
│ │
└─────────────────┘
```
The downside of supporting transitions is that for these to work, the code should render the screen _first_ then return the new state.
This ensures there is a good working copy in the buffer before the transition changes the view.
The code that handles the dispatch will run a new render pass again to get the new buffer contents and then transition between the two for you.
At the moment scrolling "Up" isn't implemented but the enumeration is there so that its implementation can follow.

View File

@@ -32,8 +32,179 @@ extern "C" {
#endif
// File local variables
#define MOVEMENT_INACTIVITY_TIME (60 * configTICK_RATE_HZ)
#define BUTTON_INACTIVITY_TIME (60 * configTICK_RATE_HZ)
extern bool heaterThermalRunaway;
ButtonState buttonsAtDeviceBoot; // We record button state at startup, incase of jumping to debug modes
OperatingMode currentOperatingMode = OperatingMode::InitialisationDone; // Current mode we are rendering
guiContext context; // Context passed to functions to aid in state during render passes
OperatingMode handle_post_init_state();
OperatingMode guiHandleDraw(void) {
OLED::clearScreen(); // Clear ready for render pass
// Read button state
ButtonState buttons = getButtonState();
// Enforce screen on if buttons pressed, movement, hot tip etc
if (buttons != BUTTON_NONE) {
OLED::setDisplayState(OLED::DisplayState::ON);
} else {
// Buttons are none; check if we can sleep display
uint32_t tipTemp = TipThermoModel::getTipInC();
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);
}
}
}
// Dispatch button state to gui mode
OperatingMode newMode = currentOperatingMode;
switch (currentOperatingMode) {
case OperatingMode::StartupWarnings:
newMode = showWarnings(buttons, &context);
break;
case OperatingMode::UsbPDDebug:
#ifdef HAS_POWER_DEBUG_MENU
newMode = showPDDebug(buttons, &context);
break;
#else
newMode = OperatingMode::InitialisationDone;
#endif
case OperatingMode::StartupLogo:
showBootLogo();
if (getSettingValue(SettingsOptions::AutoStartMode) == autoStartMode_t::SLEEP) {
lastMovementTime = lastButtonTime = 0; // We mask the values so that sleep goes until user moves again or presses a button
newMode = OperatingMode::Sleeping;
} else if (getSettingValue(SettingsOptions::AutoStartMode) == autoStartMode_t::SOLDER) {
lastMovementTime = lastButtonTime = xTaskGetTickCount(); // Move forward so we dont go to sleep
newMode = OperatingMode::Soldering;
} else if (getSettingValue(SettingsOptions::AutoStartMode) == autoStartMode_t::ZERO) {
lastMovementTime = lastButtonTime = 0; // We mask the values so that sleep goes until user moves again or presses a button
newMode = OperatingMode::Hibernating;
} else {
newMode = OperatingMode::HomeScreen;
}
break;
default:
/* Fallthrough */
case OperatingMode::HomeScreen:
newMode = drawHomeScreen(buttons, &context);
break;
case OperatingMode::Soldering:
context.scratch_state.state4 = 0;
newMode = gui_solderingMode(buttons, &context);
break;
case OperatingMode::SolderingProfile:
newMode = gui_solderingProfileMode(buttons, &context);
break;
case OperatingMode::Sleeping:
newMode = gui_SolderingSleepingMode(buttons, &context);
break;
case OperatingMode::TemperatureAdjust:
newMode = gui_solderingTempAdjust(buttons, &context);
break;
case OperatingMode::DebugMenuReadout:
newMode = showDebugMenu(buttons, &context);
break;
case OperatingMode::CJCCalibration:
newMode = performCJCC(buttons, &context);
break;
case OperatingMode::SettingsMenu:
newMode = gui_SettingsMenu(buttons, &context);
break;
case OperatingMode::InitialisationDone:
newMode = handle_post_init_state();
break;
case OperatingMode::Hibernating:
context.scratch_state.state4 = 1;
gui_SolderingSleepingMode(buttons, &context);
if (lastButtonTime > 0 || lastMovementTime > 0) {
newMode = OperatingMode::Soldering;
}
break;
case OperatingMode::ThermalRunaway:
/*TODO*/
newMode = OperatingMode::HomeScreen;
break;
};
return newMode;
}
void guiRenderLoop(void) {
OperatingMode newMode = guiHandleDraw(); // This does the screen drawing
// Post draw we handle any state transitions
if (newMode != currentOperatingMode) {
context.viewEnterTime = xTaskGetTickCount();
context.previousMode = currentOperatingMode;
// If the previous mode is the startup logo; we dont want to return to it, but instead dispatch out to either home or soldering
if (currentOperatingMode == OperatingMode::StartupLogo) {
if (getSettingValue(SettingsOptions::AutoStartMode)) {
context.previousMode = OperatingMode::Soldering;
} else {
newMode = OperatingMode::HomeScreen;
}
}
memset(&context.scratch_state, 0, sizeof(context.scratch_state));
currentOperatingMode = newMode;
}
// If the transition marker is set, we need to make the next draw occur to the secondary buffer so we have something to transition to
if (context.transitionMode != TransitionAnimation::None) {
OLED::useSecondaryFramebuffer(true);
// Now we need to fill the secondary buffer with the _next_ frame to transistion to
guiHandleDraw();
OLED::useSecondaryFramebuffer(false);
// Now dispatch the transition
switch (context.transitionMode) {
case TransitionAnimation::Down:
OLED::transitionScrollDown(context.viewEnterTime);
break;
case TransitionAnimation::Left:
OLED::transitionSecondaryFramebuffer(false, context.viewEnterTime);
break;
case TransitionAnimation::Right:
OLED::transitionSecondaryFramebuffer(true, context.viewEnterTime);
break;
case TransitionAnimation::Up:
OLED::transitionScrollUp(context.viewEnterTime);
case TransitionAnimation::None:
default:
break; // Do nothing on unknown
}
context.transitionMode = TransitionAnimation::None; // Clear transition flag
}
// Render done, draw it out
OLED::refresh();
}
OperatingMode handle_post_init_state() {
#ifdef HAS_POWER_DEBUG_MENU
#ifdef DEBUG_POWER_MENU_BUTTON_B
if (buttonsAtDeviceBoot == BUTTON_B_LONG || buttonsAtDeviceBoot == BUTTON_B_SHORT) {
#else
if (buttonsAtDeviceBoot == BUTTON_F_LONG || buttonsAtDeviceBoot == BUTTON_F_SHORT) {
#endif
buttonsAtDeviceBoot = BUTTON_NONE;
return OperatingMode::UsbPDDebug;
}
#endif
if (getSettingValue(SettingsOptions::CalibrateCJC) > 0) {
return OperatingMode::CJCCalibration;
}
return OperatingMode::StartupWarnings;
}
/* StartGUITask function */
void startGUITask(void const *argument) {
@@ -47,42 +218,22 @@ void startGUITask(void const *argument) {
bool buttonLockout = false;
renderHomeScreenAssets();
getTipRawTemp(1); // reset filter
memset(&context, 0, sizeof(context));
OLED::setRotation(getSettingValue(SettingsOptions::OrientationMode) & 1);
// If the front button is held down, on supported devices, show PD debugging metrics
#ifdef HAS_POWER_DEBUG_MENU
#ifdef DEBUG_POWER_MENU_BUTTON_B
if (getButtonB()) {
#else
// Read boot button state
if (getButtonA()) {
#endif
showPDDebug();
buttonsAtDeviceBoot = BUTTON_F_LONG;
}
#endif
if (getSettingValue(SettingsOptions::CalibrateCJC) > 0) {
performCJCC();
if (getButtonB()) {
buttonsAtDeviceBoot = BUTTON_B_LONG;
}
uint16_t logoMode = getSettingValue(SettingsOptions::LOGOTime);
uint16_t startMode = getSettingValue(SettingsOptions::AutoStartMode);
// If the boot logo is enabled (but it times out) and the autostart mode is enabled (but not set to sleep w/o heat), start heating during boot logo
if (logoMode && logoMode < logoMode_t::ONETIME && startMode && startMode < autoStartMode_t::ZERO) {
uint16_t sleepTempDegC = getSettingValue(SettingsOptions::SleepTemp);
if (getSettingValue(SettingsOptions::TemperatureInF)) {
sleepTempDegC = TipThermoModel::convertFtoC(sleepTempDegC);
}
// Only heat to sleep temperature (but no higher than 75°C for safety)
currentTempTargetDegC = min(sleepTempDegC, 75);
TickType_t startRender = xTaskGetTickCount();
for (;;) {
guiRenderLoop();
resetWatchdog();
vTaskDelayUntil(&startRender, TICKS_100MS * 4 / 10); // Try and maintain 20-25fps ish update rate, way to fast but if we can its nice
}
showBootLogo();
showWarnings();
if (getSettingValue(SettingsOptions::AutoStartMode)) {
// jump directly to the autostart mode
gui_solderingMode(getSettingValue(SettingsOptions::AutoStartMode) - 1);
buttonLockout = true;
}
drawHomeScreen(buttonLockout);
}

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())) {

View File

@@ -16,6 +16,12 @@
#include "power.hpp"
#include "task.h"
#ifdef POW_PD
#if POW_PD == 1
#include "USBPD.h"
#endif
#endif
static TickType_t powerPulseWaitUnit = 25 * TICKS_100MS; // 2.5 s
static TickType_t powerPulseDurationUnit = (5 * TICKS_100MS) / 2; // 250 ms
TaskHandle_t pidTaskNotification = NULL;
@@ -38,7 +44,9 @@ void startPIDTask(void const *argument __unused) {
currentTempTargetDegC = 0; // Force start with no output (off). If in sleep / soldering this will
// be over-ridden rapidly
pidTaskNotification = xTaskGetCurrentTaskHandle();
pidTaskNotification = xTaskGetCurrentTaskHandle();
TemperatureType_t PIDTempTarget = 0;
// Pre-seed the adc filters
for (int i = 0; i < 32; i++) {
@@ -51,6 +59,17 @@ void startPIDTask(void const *argument __unused) {
resetWatchdog();
ulTaskNotifyTake(pdTRUE, 2000);
}
// Wait for PD if its in the middle of negotiation
#ifdef POW_PD
#if POW_PD == 1
// This is an FUSB based PD capable device
// Wait up to 3 seconds for USB-PD to settle
while (USBPowerDelivery::negotiationInProgress() && xTaskGetTickCount() < (TICKS_SECOND * 3)) {
resetWatchdog();
ulTaskNotifyTake(pdTRUE, TICKS_100MS);
}
#endif
#endif
int32_t x10WattsOut = 0;