changeset 807:9e2ebfc72e8c

Zusammenf?hren
author heinrichsweikamp
date Sat, 26 Aug 2023 13:37:06 +0200
parents ee3c0029ed34 (current diff) dd7ce655db26 (diff)
children ea3267866120
files Discovery/Src/settings.c
diffstat 18 files changed, 407 insertions(+), 42 deletions(-) [+]
line wrap: on
line diff
--- a/Common/Inc/data_central.h	Sat Aug 26 13:35:15 2023 +0200
+++ b/Common/Inc/data_central.h	Sat Aug 26 13:37:06 2023 +0200
@@ -360,6 +360,14 @@
 		CHARGER_lostConnection
 };
 
+typedef enum {
+    TIMER_STATE_OFF = 0,
+    TIMER_STATE_PRESTART,
+    TIMER_STATE_RUNNING,
+    TIMER_STATE_WAIT_FINISHED,
+    TIMER_STATE_FINISHED,
+} timerState_e;
+
 typedef struct
 {
 	SDiveSettings diveSettings;
@@ -390,6 +398,9 @@
 	uint8_t sensorErrorsRTE;
 
 	uint8_t lastKnownBatteryPercentage;
+
+    timerState_e timerState;
+    int timerStartedS;
 } 	SDiveState;
 
 
@@ -512,4 +523,6 @@
 void setCompassHeading(uint16_t heading);
 
 const SDecoinfo *getDecoInfo(void);
+
+void disableTimer(void);
 #endif // DATA_CENTRAL_H
--- a/Common/Inc/settings.h	Sat Aug 26 13:35:15 2023 +0200
+++ b/Common/Inc/settings.h	Sat Aug 26 13:37:06 2023 +0200
@@ -87,7 +87,7 @@
 
 #define UART_MAX_PROTOCOL		(2u)
 
-#define FUTURE_SPARE_SIZE		(2u)		/* Applied for reuse of old, not used, scooter block (was 32 bytes)*/
+#define FUTURE_SPARE_SIZE		(0u)		/* Applied for reuse of old, not used, scooter block (was 32 bytes)*/
 
 typedef enum
 {
@@ -246,7 +246,8 @@
 	uint8_t ext_sensor_map_Obsolete[5];
 	uint8_t buttonLockActive;									/* redefined in 0xFFFF0025 */
 	int8_t compassDeclinationDeg;
-    uint8_t delaySetpointLow;                                         /* redefined in 0xFFFF0026 */
+	uint8_t delaySetpointLow;
+	uint16_t timerDurationS;										/* redefined in 0xFFFF0026 */
 	uint8_t Future_SPARE[FUTURE_SPARE_SIZE];					/* redefined in 0xFFFF0020 (old scooter Block was 32 byte)*/
 	// new in 0xFFFF0006
 	uint8_t ppo2sensors_deactivated;
--- a/Discovery/Inc/t7.h	Sat Aug 26 13:35:15 2023 +0200
+++ b/Discovery/Inc/t7.h	Sat Aug 26 13:37:06 2023 +0200
@@ -86,4 +86,7 @@
 	 void t7c_refresh(uint32_t FramebufferStartAddress);
 */
 
+void t7_tick(void);
+
+bool t7_isTimerRunning(bool foregroundOnly);
 #endif /* T7_H */
--- a/Discovery/Inc/tHome.h	Sat Aug 26 13:35:15 2023 +0200
+++ b/Discovery/Inc/tHome.h	Sat Aug 26 13:37:06 2023 +0200
@@ -76,6 +76,7 @@
 		CVIEW_SummaryOfLeftCorner,
 		CVIEW_Charger,
 		CVIEW_CcrSummary,
+        CVIEW_Timer,
 		CVIEW_END,
 		CVIEW_T3_Decostop,
 		CVIEW_T3_TTS,
--- a/Discovery/Inc/tStructure.h	Sat Aug 26 13:35:15 2023 +0200
+++ b/Discovery/Inc/tStructure.h	Sat Aug 26 13:37:06 2023 +0200
@@ -304,32 +304,34 @@
 #define StMSYS1_DST			_MB(2,8,1,6,0)
 #define StMSYS1_12HR    _MB(2,8,1,7,0)
 
-#define StMSYS2_English	_MB(2,8,2,1,0)
-#define StMSYS2_German	_MB(2,8,2,2,0)
-#define StMSYS2_French	_MB(2,8,2,3,0)
-#define StMSYS2_Italian	_MB(2,8,2,4,0)
-#define StMSYS2_Espanol	_MB(2,8,2,5,0)
+#define StMSYS_Timer	_MB(2,8,2,1,0)
 
-#define StMSYS3_Units		_MB(2,8,3,1,0)
-#define StMSYS3_Colors	_MB(2,8,3,2,0)
-#define StMSYS3_Debug		_MB(2,8,3,3,0)
+#define StMSYS2_English	_MB(2,8,3,1,0)
+#define StMSYS2_German	_MB(2,8,3,2,0)
+#define StMSYS2_French	_MB(2,8,3,3,0)
+#define StMSYS2_Italian	_MB(2,8,3,4,0)
+#define StMSYS2_Espanol	_MB(2,8,3,5,0)
 
-#define StMSYS4_Info		_MB(2,8,4,1,0)
+#define StMSYS3_Units		_MB(2,8,4,1,0)
+#define StMSYS3_Colors	_MB(2,8,4,2,0)
+#define StMSYS3_Debug		_MB(2,8,4,3,0)
+
+#define StMSYS4_Info		_MB(2,8,5,1,0)
 
-#define StMSYS5_Exit			_MB(2,8,5,1,0)
-#define StMSYS5_LogbookOffset	_MB(2,8,5,7,0)
-#define StMSYS5_ResetAll		_MB(2,8,5,2,0)
-#define StMSYS5_ResetDeco		_MB(2,8,5,3,0)
-#define StMSYS5_Reboot			_MB(2,8,5,4,0)
-#define StMSYS5_Maintenance		_MB(2,8,5,5,0)
-#define StMSYS5_ResetLogbook	_MB(2,8,5,6,0)
-#define StMSYS5_SetBattCharge	_MB(2,8,5,7,0)
-#define StMSYS5_RebootRTE		_MB(2,8,5,8,0)
-#define StMSYS5_RebootMainCPU	_MB(2,8,5,9,0)
-#define StMSYS5_ScreenTest		_MB(2,8,5,10,0)
-#define StMSYS5_SetFactoryBC	_MB(2,8,5,11,0)
-#define StMSYS5_ResetBluetooth	_MB(2,8,5,12,0)
-#define StMSYS5_SetSampleIndx   _MB(2,8,5,13,0)
+#define StMSYS5_Exit			_MB(2,8,6,1,0)
+#define StMSYS5_LogbookOffset	_MB(2,8,6,7,0)
+#define StMSYS5_ResetAll		_MB(2,8,6,2,0)
+#define StMSYS5_ResetDeco		_MB(2,8,6,3,0)
+#define StMSYS5_Reboot			_MB(2,8,6,4,0)
+#define StMSYS5_Maintenance		_MB(2,8,6,5,0)
+#define StMSYS5_ResetLogbook	_MB(2,8,6,6,0)
+#define StMSYS5_SetBattCharge	_MB(2,8,6,7,0)
+#define StMSYS5_RebootRTE		_MB(2,8,6,8,0)
+#define StMSYS5_RebootMainCPU	_MB(2,8,6,9,0)
+#define StMSYS5_ScreenTest		_MB(2,8,6,10,0)
+#define StMSYS5_SetFactoryBC	_MB(2,8,6,11,0)
+#define StMSYS5_ResetBluetooth	_MB(2,8,6,12,0)
+#define StMSYS5_SetSampleIndx   _MB(2,8,6,13,0)
 
  /* PAGE 9 */
 
--- a/Discovery/Inc/text_multilanguage.h	Sat Aug 26 13:35:15 2023 +0200
+++ b/Discovery/Inc/text_multilanguage.h	Sat Aug 26 13:37:06 2023 +0200
@@ -370,6 +370,10 @@
         TXT2BYTE_Clear,
         TXT2BYTE_Reset,
 
+        TXT2BYTE_Timer,
+        TXT2BYTE_Starting,
+        TXT2BYTE_Finished,
+
 		TXT2BYTE_END
 };
 
--- a/Discovery/Src/base.c	Sat Aug 26 13:35:15 2023 +0200
+++ b/Discovery/Src/base.c	Sat Aug 26 13:37:06 2023 +0200
@@ -1850,9 +1850,13 @@
 				{
 					timeout_limit_Surface_in_seconds = settingsGetPointer()->timeoutSurfacemode;
 				}
-				if(timeout_in_seconds  >= timeout_limit_Surface_in_seconds)
-				{
-					gotoSleep();
+				if (timeout_in_seconds >= timeout_limit_Surface_in_seconds) {
+                    if (t7_isTimerRunning(true)) {
+                        // Delay sleep until the timer has elapsed
+                        timeout_in_seconds = timeout_limit_Surface_in_seconds - 1;
+                    } else {
+					    gotoSleep();
+                    }
 				}
 				break;
 			case BaseMenu:
--- a/Discovery/Src/data_central.c	Sat Aug 26 13:35:15 2023 +0200
+++ b/Discovery/Src/data_central.c	Sat Aug 26 13:37:06 2023 +0200
@@ -889,3 +889,9 @@
 
     return decoInfo;
 }
+
+
+void disableTimer(void)
+{
+    stateUsedWrite->timerState = TIMER_STATE_OFF;
+}
--- a/Discovery/Src/data_exchange_main.c	Sat Aug 26 13:35:15 2023 +0200
+++ b/Discovery/Src/data_exchange_main.c	Sat Aug 26 13:37:06 2023 +0200
@@ -1025,6 +1025,8 @@
 			{
 				simulation_exit();
 			}
+            disableTimer();
+
 				// new 170508
 			settingsGetPointer()->bluetoothActive = 0;
 			MX_Bluetooth_PowerOff();
@@ -1047,6 +1049,8 @@
 			}
 			createDiveSettings();
 
+            disableTimer();
+
 			if(pStateReal->warnings.cnsHigh)
 			{
 				if(pStateReal->lifeData.cns >= 130)
--- a/Discovery/Src/settings.c	Sat Aug 26 13:35:15 2023 +0200
+++ b/Discovery/Src/settings.c	Sat Aug 26 13:37:06 2023 +0200
@@ -337,6 +337,7 @@
 	.buttonLockActive = 0,
     .compassDeclinationDeg = 0,
     .delaySetpointLow = false,
+    .timerDurationS = 180,
 };
 
 /* Private function prototypes -----------------------------------------------*/
@@ -544,6 +545,9 @@
         // Disable auto setpoint to avoid a configuration warning being triggered by the new auto setpoint validation
         // This ensures that users don't lose setpoint information if it is not in the right spot for the new configuration
         pSettings->autoSetpoint = false;
+
+        pSettings->timerDurationS = pStandard->timerDurationS;
+
     	// no break;
     case 0xFFFF0026:
     	pSettings->ext_sensor_map[0] = pSettings->ext_sensor_map_Obsolete[0];
@@ -554,7 +558,8 @@
     	pSettings->ext_sensor_map[5] = SENSOR_NONE;
     	pSettings->ext_sensor_map[6] = SENSOR_NONE;
     	pSettings->ext_sensor_map[7] = SENSOR_NONE;
-    	// no break;
+
+        // no break;
     default:
         pSettings->header = pStandard->header;
         break; // no break before!!
@@ -1621,6 +1626,16 @@
         corrections++;
     }
 
+    if (Settings.timerDurationS > 599) {
+        Settings.timerDurationS = 599;
+
+        corrections++;
+    } else if (Settings.timerDurationS < 1) {
+        Settings.timerDurationS = 1;
+
+        corrections++;
+    }
+
     if(corrections)
     {
     	settingsWarning = 1;
--- a/Discovery/Src/simulation.c	Sat Aug 26 13:35:15 2023 +0200
+++ b/Discovery/Src/simulation.c	Sat Aug 26 13:37:06 2023 +0200
@@ -111,6 +111,9 @@
 void simulation_exit(void)
 {
     timer_Stopwatch_Stop();
+
+    disableTimer();
+
     set_stateUsedToReal();
 }
 
--- a/Discovery/Src/t7.c	Sat Aug 26 13:35:15 2023 +0200
+++ b/Discovery/Src/t7.c	Sat Aug 26 13:37:06 2023 +0200
@@ -28,7 +28,6 @@
 
 /* Includes ------------------------------------------------------------------*/
 #include <stdlib.h>
-#include <stdbool.h>
 
 #include "t7.h"
 #include "t3.h"
@@ -48,6 +47,8 @@
 #include "base.h"
 #include "tMenuEditSetpoint.h"
 
+#define TIMER_ACTION_DELAY_S 10
+
 /* Private function prototypes -----------------------------------------------*/
 
 void t7_refresh_surface(void);
@@ -130,6 +131,7 @@
     CVIEW_sensors_mV,
     CVIEW_EADTime,
     CVIEW_SummaryOfLeftCorner,
+    CVIEW_Timer,
     CVIEW_noneOrDebug,
     CVIEW_END,
     CVIEW_END
@@ -146,6 +148,7 @@
     CVIEW_sensors_mV,
 	CVIEW_Charger,
     CVIEW_CcrSummary,
+    CVIEW_Timer,
     CVIEW_END
 };
 
@@ -998,6 +1001,12 @@
         GFX_write_string(&FontT48,&t7surfaceR,text,1);
     }
 
+    if (stateUsed->timerState == TIMER_STATE_RUNNING && selection_customview != CVIEW_Timer) {
+        int timerRemainingS = pSettings->timerDurationS - (current_second() - stateUsed->timerStartedS);
+        snprintf(text, 20, "\001%u:%02u", timerRemainingS / 60, timerRemainingS % 60);
+        GFX_write_string(&FontT54, &t7surfaceR, text, 8);
+    }
+
     /* beta version */
     if( firmwareDataGetPointer()->versionBeta )
     {
@@ -1973,6 +1982,163 @@
 }
 
 
+static void setTimerPrestart(int startTimeS)
+{
+    stateUsedWrite->timerState = TIMER_STATE_PRESTART;
+    stateUsedWrite->timerStartedS = startTimeS;
+}
+
+
+static void setTimerFinished(int startTimeS)
+{
+    stateUsedWrite->timerState = TIMER_STATE_FINISHED;
+    stateUsedWrite->timerStartedS = startTimeS;
+}
+
+
+static void updateTimer(SSettings *settings, int nowS, bool switchedToTimerView)
+{
+    int timerElapsedS = nowS - stateUsed->timerStartedS;
+
+    if (stateUsed->timerState && timerElapsedS < 0) {
+        disableTimer();
+    } else {
+        switch (stateUsed->timerState) {
+        case TIMER_STATE_OFF:
+            if (switchedToTimerView) {
+                setTimerPrestart(nowS);
+            }
+
+            break;
+        case TIMER_STATE_PRESTART:
+            if (timerElapsedS <= TIMER_ACTION_DELAY_S) {
+                if (switchedToTimerView) {
+                    setTimerPrestart(nowS);
+                }
+            } else {
+                if (selection_customview == CVIEW_Timer) {
+                    stateUsedWrite->timerState = TIMER_STATE_RUNNING;
+                    stateUsedWrite->timerStartedS = stateUsed->timerStartedS + TIMER_ACTION_DELAY_S;
+                } else {
+                    disableTimer();
+                }
+            }
+
+            break;
+        case TIMER_STATE_RUNNING:
+            if (timerElapsedS >= settings->timerDurationS) {
+                if (selection_customview == CVIEW_Timer) {
+                    setTimerFinished(stateUsed->timerStartedS + settings->timerDurationS);
+                } else {
+                    stateUsedWrite->timerState = TIMER_STATE_WAIT_FINISHED;
+                }
+            }
+
+            break;
+        case TIMER_STATE_WAIT_FINISHED:
+            if (switchedToTimerView) {
+                setTimerFinished(nowS);
+            }
+
+            break;
+        case TIMER_STATE_FINISHED:
+            if (timerElapsedS <= TIMER_ACTION_DELAY_S) {
+                if (switchedToTimerView) {
+                    setTimerPrestart(stateUsed->timerStartedS);
+                }
+            } else {
+                disableTimer();
+            }
+
+            break;
+        }
+    }
+}
+
+
+bool t7_isTimerRunning(bool includeBackground)
+{
+    return stateUsed->timerState && (selection_customview == CVIEW_Timer || (includeBackground && stateUsed->timerState == TIMER_STATE_RUNNING));
+}
+
+
+static void showTimer(SSettings *settings, int nowS)
+{
+    char heading[32];
+    unsigned headingIndex = 0;
+
+    char data[32];
+    unsigned dataIndex = 0;
+
+    heading[headingIndex++] = '\032';
+    heading[headingIndex++] = '\016';
+    heading[headingIndex++] = '\016';
+
+    data[dataIndex++] = '\t';
+
+    int timerRemainingS = settings->timerDurationS;
+    switch (stateUsed->timerState) {
+    case TIMER_STATE_RUNNING:
+        timerRemainingS = settings->timerDurationS - (nowS - stateUsed->timerStartedS);
+
+        break;
+    case TIMER_STATE_PRESTART:
+    case TIMER_STATE_FINISHED:
+        if (stateUsed->timerState == TIMER_STATE_PRESTART) {
+            heading[headingIndex++] = TXT_2BYTE;
+            heading[headingIndex++] = TXT2BYTE_Starting;
+        } else {
+            heading[headingIndex++] = TXT_2BYTE;
+            heading[headingIndex++] = TXT2BYTE_Finished;
+
+            timerRemainingS = 0;
+        }
+
+        dataIndex += snprintf(&data[dataIndex], 10, "\020%u", TIMER_ACTION_DELAY_S - (nowS - stateUsed->timerStartedS));
+
+        break;
+    default:
+
+        break;
+    }
+
+    char timer[16];
+    snprintf(timer, 10, "\001\020%u:%02u", timerRemainingS / 60, timerRemainingS % 60);
+
+    heading[headingIndex++] = 0;
+
+    data[dataIndex++] = 0;
+
+    t7cY0free.WindowLineSpacing = 48;
+    t7cY0free.WindowNumberOfTextLines = 6;
+    t7cY0free.WindowTab = 375;
+
+    t7cY0free.WindowY0 = t7cC.WindowY0 - 10;
+    if (!settings->FlipDisplay) {
+        t7cY0free.WindowX0 += 10;
+        t7cY0free.WindowY0 += 10;
+        t7cY0free.WindowY1 = 355;
+        GFX_write_string(&FontT24, &t7cY0free, heading, 1);
+        t7cY0free.WindowX0 -= 10;
+        t7cY0free.WindowY0 -= 10;
+    } else {
+        t7cY0free.WindowY1 -= 10;
+        t7cY0free.WindowX1 -= 10;
+        GFX_write_string(&FontT24, &t7cY0free, heading, 1);
+        t7cY0free.WindowY1 += 10;
+        t7cY0free.WindowX1 += 10;
+    }
+
+    t7_colorscheme_mod(data);
+
+    GFX_write_string(&FontT42, &t7cY0free, data, 1);
+
+    t7_colorscheme_mod(timer);
+
+    GFX_write_string(&FontT105, &t7cY0free, timer, 4);
+}
+
+
 void t7_refresh_customview(void)
 {
 	static uint8_t last_customview = CVIEW_END;
@@ -2015,7 +2181,6 @@
 		{
 			t7_change_customview(ACTION_BUTTON_ENTER);
 		}
-		last_customview = selection_customview;
 	}
     switch(selection_customview)
     {
@@ -2513,11 +2678,23 @@
         t7_CcrSummary(pSettings);
 
         break;
+    case CVIEW_Timer:
+        snprintf(text, 100, "\032\f\001%c%c", TXT_2BYTE, TXT2BYTE_Timer);
+        GFX_write_string(&FontT42, &t7cH, text, 0);
+
+        int nowS = current_second();
+
+        updateTimer(pSettings, nowS, last_customview != CVIEW_Timer);
+
+        showTimer(pSettings, nowS);
+
+        break;
     }
+
+    last_customview = selection_customview;
 }
 
 
-
 /* DIVE MODE
  */
 void t7_refresh_divemode(void)
@@ -4483,7 +4660,17 @@
 
 }
 
+
 bool t7_isCompassShowing(void)
 {
     return selection_customview == CVIEW_Compass || selection_custom_field == LLC_Compass;
 }
+
+
+void t7_tick(void)
+{
+    SSettings *settings = settingsGetPointer();
+
+    int nowS = current_second();
+    updateTimer(settings, nowS, false);
+}
--- a/Discovery/Src/tHome.c	Sat Aug 26 13:35:15 2023 +0200
+++ b/Discovery/Src/tHome.c	Sat Aug 26 13:37:06 2023 +0200
@@ -59,7 +59,7 @@
 static uint16_t tHome_tick_count_field;
 static uint16_t tHome_tick_count_o2sens;
 
-const uint8_t cv_changelist[] = {CVIEW_Compass, CVIEW_SummaryOfLeftCorner, CVIEW_Tissues, CVIEW_Profile, CVIEW_EADTime, CVIEW_Gaslist, CVIEW_noneOrDebug, CVIEW_Decolist,CVIEW_sensors,CVIEW_sensors_mV, CVIEW_END};
+const uint8_t cv_changelist[] = {CVIEW_Compass, CVIEW_SummaryOfLeftCorner, CVIEW_Tissues, CVIEW_Profile, CVIEW_EADTime, CVIEW_Gaslist, CVIEW_noneOrDebug, CVIEW_Decolist, CVIEW_sensors,CVIEW_sensors_mV, CVIEW_Timer, CVIEW_END};
 const uint8_t cv_changelist_BS[] = {CVIEW_T3_Decostop, CVIEW_sensors, CVIEW_Compass, CVIEW_T3_MaxDepth,CVIEW_T3_StopWatch, CVIEW_T3_TTS, CVIEW_T3_GasList, CVIEW_T3_ppO2andGas, CVIEW_noneOrDebug,
 									CVIEW_T3_Navigation, CVIEW_T3_DepthData, CVIEW_T3_DecoTTS,
 #ifdef ENABLE_T3_PROFILE_VIEW
@@ -638,8 +638,7 @@
         if(tHome_tick_count_cview > (cview *10))
         {
             tHome_tick_count_cview = 0;
-            if(settingsGetPointer()->design == 7)
-            {
+            if (settingsGetPointer()->design == 7 && !t7_isTimerRunning(false)) {
                 t7_set_customview_to_primary();
             }
             if(settingsGetPointer()->design == 3)
@@ -658,6 +657,8 @@
     		t7_select_customview(CVIEW_sensors);
     	}
     }
+
+    t7_tick();
 }
 
 
--- a/Discovery/Src/tMenuEditCustom.c	Sat Aug 26 13:35:15 2023 +0200
+++ b/Discovery/Src/tMenuEditCustom.c	Sat Aug 26 13:37:06 2023 +0200
@@ -493,6 +493,9 @@
     case CVIEW_SummaryOfLeftCorner:
         text = TXT2BYTE_Summary;
         break;
+    case CVIEW_Timer:
+        text = TXT2BYTE_Timer;
+        break;
     case CVIEW_noneOrDebug:
     	text = TXT2BYTE_DispNoneDbg;
         break;
@@ -581,6 +584,9 @@
         newValue = CVIEW_SummaryOfLeftCorner;
         break;
     case CVIEW_SummaryOfLeftCorner:
+        newValue = CVIEW_Timer;
+        break;
+    case CVIEW_Timer:
         newValue = CVIEW_noneOrDebug;
         break;
     case CVIEW_noneOrDebug:
--- a/Discovery/Src/tMenuEditPlanner.c	Sat Aug 26 13:35:15 2023 +0200
+++ b/Discovery/Src/tMenuEditPlanner.c	Sat Aug 26 13:37:06 2023 +0200
@@ -86,6 +86,9 @@
             settingsGetPointer()->bluetoothActive = 0;
             MX_Bluetooth_PowerOff();
         }
+
+        disableTimer();
+
         simulation_start(tMplan_depth_meter, tMplan_dive_time_minutes);
         exitMenuEdit_to_Home();
         break;
--- a/Discovery/Src/tMenuEditSystem.c	Sat Aug 26 13:35:15 2023 +0200
+++ b/Discovery/Src/tMenuEditSystem.c	Sat Aug 26 13:37:06 2023 +0200
@@ -101,6 +101,87 @@
 
 /* Exported functions --------------------------------------------------------*/
 
+static uint8_t OnAction_Timer(uint32_t editId, uint8_t blockNumber, uint8_t digitNumber, uint8_t digitContent, uint8_t action)
+{
+    SSettings *settings = settingsGetPointer();
+    uint8_t digitContentNew;
+    switch (action) {
+    case ACTION_BUTTON_ENTER:
+
+        return digitContent;
+    case ACTION_BUTTON_ENTER_FINAL:
+        {
+            uint32_t timerM;
+            uint32_t timerS;
+            evaluateNewString(editId, &timerM, &timerS, 0, 0);
+            if (timerM > 9) {
+                timerM = 9;
+            }
+            if (timerS > 59) {
+                timerS = 59;
+            }
+
+            uint16_t timerDurationS = 60 * timerM + timerS;
+
+            if (timerDurationS < 1) {
+                timerDurationS = 1;
+            }
+
+            if (timerDurationS != settings->timerDurationS) {
+                settings->timerDurationS = timerDurationS;
+
+                disableTimer();
+
+                tMenuEdit_newInput(editId, settings->timerDurationS / 60, settings->timerDurationS % 60, 0, 0);
+            }
+
+            return EXIT_TO_MENU;
+        }
+    case ACTION_BUTTON_NEXT:
+        digitContentNew = digitContent + 1;
+        if ((blockNumber == 1 && digitNumber == 0 && digitContentNew > '5') || digitContentNew > '9') {
+            digitContentNew = '0';
+        }
+
+        return digitContentNew;
+    case ACTION_BUTTON_BACK:
+        digitContentNew = digitContent - 1;
+        if (digitContentNew < '0') {
+            if (blockNumber == 1 && digitNumber == 0) {
+                digitContentNew = '5';
+            } else {
+                digitContentNew = '9';
+            }
+        }
+
+        return digitContentNew;
+    }
+
+    return EXIT_TO_MENU;
+}
+
+
+static void openEdit_Timer(void)
+{
+    SSettings *settings = settingsGetPointer();
+
+    char text[32];
+    snprintf(text, 32, "\001%c%c", TXT_2BYTE, TXT2BYTE_Timer);
+    write_topline(text);
+
+    uint16_t yPos = ME_Y_LINE_BASE + get_globalState_Menu_Line() * ME_Y_LINE_STEP;
+    snprintf(text, 32, "%c%c", TXT_2BYTE, TXT2BYTE_Timer);
+    write_label_var(30, 299, yPos, &FontT48, text);
+    write_field_udigit(StMSYS_Timer, 300, 392, yPos, &FontT48, "#:##", settings->timerDurationS / 60, settings->timerDurationS % 60, 0, 0);
+    write_label_var(393, 800, yPos, &FontT48, "\016\016 [m:ss]\017");
+
+    write_buttonTextline(TXT2BYTE_ButtonMinus, TXT2BYTE_ButtonEnter, TXT2BYTE_ButtonPlus);
+
+    setEvent(StMSYS_Timer, (uint32_t)OnAction_Timer);
+    startEdit();
+}
+
+
 void openEdit_System(uint8_t line)
 {
     set_globalState_Menu_Line(line);
@@ -115,15 +196,18 @@
             openEdit_DateTime();
         break;
         case 2:
+            openEdit_Timer();
+        break;
+        case 3:
             openEdit_Language();
         break;
-        case 3:
+        case 4:
             openEdit_Design();
         break;
-        case 4:
+        case 5:
             openEdit_Information();
         break;
-        case 5:
+        case 6:
             openEdit_Reset();
         break;
 /*
--- a/Discovery/Src/tMenuSystem.c	Sat Aug 26 13:35:15 2023 +0200
+++ b/Discovery/Src/tMenuSystem.c	Sat Aug 26 13:37:06 2023 +0200
@@ -164,7 +164,13 @@
         textPointer += 2;
     }
 
-    if((line == 0) || (line == 2))
+    if (line == 0 || line == 2) {
+        textPointer += snprintf(&text[textPointer], 21, "%c%c\t%u:%02u \016\016[m:ss]\017\n\r", TXT_2BYTE, TXT2BYTE_Timer, data->timerDurationS / 60, data->timerDurationS % 60);
+    } else {
+        textPointer += snprintf(&text[textPointer], 3, "\n\r");
+    }
+
+    if((line == 0) || (line == 3))
     {
         text[textPointer++] = TXT_Language;
         text[textPointer++] = '\t';
@@ -179,7 +185,7 @@
         textPointer += 2;
     }
 
-    if((line == 0) || (line == 3))
+    if((line == 0) || (line == 4))
     {
         text[textPointer++] = TXT_2BYTE;
         text[textPointer++] = TXT2BYTE_Layout;
@@ -252,7 +258,7 @@
         textPointer += 2;
     }
 
-    if((line == 0) || (line == 4))
+    if((line == 0) || (line == 5))
     {
         text[textPointer++] = TXT_Information;
         text[textPointer++] = '\t';
@@ -267,7 +273,7 @@
     strcpy(&text[textPointer],"\n\r");
     textPointer += 2;
 
-    if((line == 0) || (line == 5))
+    if((line == 0) || (line == 6))
     {
         text[textPointer++] = TXT_2BYTE;
         text[textPointer++] = TXT2BYTE_ResetMenu;
--- a/Discovery/Src/text_multilanguage.c	Sat Aug 26 13:35:15 2023 +0200
+++ b/Discovery/Src/text_multilanguage.c	Sat Aug 26 13:37:06 2023 +0200
@@ -1883,6 +1883,24 @@
 static uint8_t text_IT_Custom[] = "Custom";
 static uint8_t text_ES_Custom[] = "Custom";
 
+static uint8_t text_EN_Timer[] = "Timer";
+static uint8_t text_DE_Timer[] = "Timer";
+static uint8_t text_FR_Timer[] = "Minuteur";
+static uint8_t text_IT_Timer[] = "Timer";
+static uint8_t text_ES_Timer[] = "Temporizador";
+
+static uint8_t text_EN_Starting[] = "Start in";
+static uint8_t text_DE_Starting[] = "Startet in";
+static uint8_t text_FR_Starting[] = "Démarre en";
+static uint8_t text_IT_Starting[] = "Inizio in";
+static uint8_t text_ES_Starting[] = "Comienza en";
+
+static uint8_t text_EN_Finished[] = "Finished";
+static uint8_t text_DE_Finished[] = "Beendet";
+static uint8_t text_FR_Finished[] = "Fini";
+static uint8_t text_IT_Finished[] = "Finito";
+static uint8_t text_ES_Finished[] = "Terminado";
+
 /* Lookup Table -------------------------------------------------------------*/
 
 const tText text_array[] =
@@ -2167,4 +2185,8 @@
 	{(uint8_t)TXT2BYTE_Set, 	{text_EN_Set, text_DE_Set, text_FR_Set, text_IT_Set, text_ES_Set}},
 	{(uint8_t)TXT2BYTE_Clear, 	{text_EN_Clear, text_DE_Clear, text_FR_Clear, text_IT_Clear, text_ES_Clear}},
 	{(uint8_t)TXT2BYTE_Reset, 	{text_EN_Reset, text_DE_Reset, text_FR_Reset, text_IT_Reset, text_ES_Reset}},
+
+	{(uint8_t)TXT2BYTE_Timer, 	{text_EN_Timer, text_DE_Timer, text_FR_Timer, text_IT_Timer, text_ES_Timer}},
+	{(uint8_t)TXT2BYTE_Starting, 	{text_EN_Starting, text_DE_Starting, text_FR_Starting, text_IT_Starting, text_ES_Starting}},
+	{(uint8_t)TXT2BYTE_Finished, 	{text_EN_Finished, text_DE_Finished, text_FR_Finished, text_IT_Finished, text_ES_Finished}},
 };