Initializing the DateTime Configuraton Screen
When entering the DateTime settings screen, the interface is initialized with appropriate menu options and callbacks. This screen uses string-based (STR) rendering.
static void updateTimeDisplay(void) {
char buffer[17];
uint8_t displayMode = (timeDateSet.time.ampm != CHAR_24) ? 12 : 24;
sprintf(buffer, PSTR(" %hhuhr %02hhu:%02hhu%c"), displayMode,
timeDateSet.time.hour, timeDateSet.time.mins, timeDateSet.time.ampm);
SetMenuOption(3, buffer, NULL, handleTimeSelection);
}
static void updateDateDisplay(void) {
char buffer[BUFFSIZE_STR_MONTHS + 14];
char monthName[BUFFSIZE_STR_MONTHS];
strcpy(monthName, months[timeDateSet.date.month]);
sprintf(buffer, PSTR(" %02hhu %s 20%02hhu"),
timeDateSet.date.date, monthName, timeDateSet.date.year);
SetMenuOption(1, buffer, NULL, handleDateSelection);
}
static void dateTimeLoader(uint8_t unused) {
(void)unused;
updateDateDisplay();
updateTimeDisplay();
SetMenuOption(5, saved ? PSTR(STR_SAVED) : PSTR(STR_SAVE),
&menu_volume[appConfig.volAlarm][0], persistDateTimeSettings);
addExitOption();
}
void initializeDateTimeMenu(void) {
memcpy(&timeDateSet, &timeDate, sizeof(timeDate_s));
timeMode = appConfig.timeMode;
saved = false;
SetButtonFunc(menuUp, (bool (*)(void))handleDateTimeSelect, menuDown);
SetMenuFunc(navigateUp, handleDateTimeSelect, navigateDown, dateTimeLoader);
SetMenuInfo(7, MENU_TYPE_STR, PSTR(STR_TIMEDATEMENU));
SetMenuPre(&returnToPreviousMenu, initializeDateTimeMenu);
Main_Menu.selected = 1;
AnimotionInto(NULL);
}
Editing Date and Time Using a State Machine
A state machine manages field-by-field editing. Upon confirmation, editing starts from SETTING_NOW_TIMEMODE. Each press cycles through time mode, hour, minute, and AM/PM (if applicable), or date, month, and year for the date section. After the final field, the editor exits back to the main DateTime view.
static void startEditing(void) {
Main_Menu.func.draw = renderDateTimeEdit;
SetMenuFunc(openTimeEditorUp, handleDateTimeSelect, openTimeEditorDown, dateTimeLoader);
}
static void finishEditing(void) {
setting.now = SETTING_NOW_NONE;
Main_Menu.func.draw = NULL;
SetMenuFunc(navigateUp, handleDateTimeSelect, navigateDown, dateTimeLoader);
}
static void handleTimeSelection(void) {
startEditing();
switch (setting.now) {
case SETTING_NOW_NONE:
setting.now = SETTING_NOW_TIMEMODE;
setting.val = timeMode;
break;
case SETTING_NOW_TIMEMODE:
timeMode = (timemode_t)setting.val;
TimeSetMode(&timeDateSet.time, timeMode);
setting.val = timeDateSet.time.hour;
setting.now = SETTING_NOW_HOUR;
break;
case SETTING_NOW_HOUR:
timeDateSet.time.hour = setting.val;
setting.val = timeDateSet.time.mins;
setting.now = SETTING_NOW_MIN;
break;
case SETTING_NOW_MIN:
timeDateSet.time.mins = setting.val;
if (timeMode == TIMEMODE_12HR) {
setting.now = SETTING_NOW_AMPM;
setting.val = (timeDateSet.time.ampm == CHAR_AM);
} else {
timeDateSet.time.ampm = CHAR_24;
finishEditing();
}
break;
default: // SETTING_NOW_AMPM
timeDateSet.time.ampm = setting.val ? CHAR_AM : CHAR_PM;
finishEditing();
break;
}
}
static void handleDateSelection(void) {
startEditing();
switch (setting.now) {
case SETTING_NOW_NONE:
setting.now = SETTING_NOW_DATE;
setting.val = timeDateSet.date.date;
break;
case SETTING_NOW_DATE:
timeDateSet.date.date = setting.val;
if (timeDateSet.date.date < 1) timeDateSet.date.date = 1;
setting.now = SETTING_NOW_MONTH;
setting.val = timeDateSet.date.month;
break;
case SETTING_NOW_MONTH: {
timeDateSet.date.month = (month_t)setting.val;
uint8_t maxDays = TimeMonthDayCount(timeDateSet.date.month, timeDateSet.date.year);
if (timeDateSet.date.date > maxDays)
timeDateSet.date.date = maxDays;
setting.now = SETTING_NOW_YEAR;
setting.val = timeDateSet.date.year;
break;
}
default: // SETTING_NOW_YEAR
timeDateSet.date.year = setting.val;
timeDateSet.date.day = TimeDow(timeDateSet.date.year,
timeDateSet.date.month,
timeDateSet.date.date);
finishEditing();
break;
}
}
Rednering the Active Edit Field on OLED
During editing, the current field is highlighted and its value is dynamically rendered on the OLED display at the apropriate position.
static void renderDateTimeEdit(void) {
uint8_t x, y, width = 16;
switch (setting.now) {
case SETTING_NOW_DATE: x = 30; y = 16; width = 16; break;
case SETTING_NOW_MONTH: x = 54; y = 16; width = 24; break;
case SETTING_NOW_YEAR: x = 102; y = 16; width = 16; break;
case SETTING_NOW_TIMEMODE: x = 30; y = 32; width = 16; break;
case SETTING_NOW_HOUR: x = 70; y = 32; width = 16; break;
case SETTING_NOW_MIN: x = 94; y = 32; width = 16; break;
case SETTING_NOW_AMPM: x = 110; y = 32; width = 8; break;
default: return;
}
OledClearArea(x, y, width);
char displayBuf[5];
if (setting.now == SETTING_NOW_MONTH) {
strcpy(displayBuf, months[setting.val]);
} else if (setting.now == SETTING_NOW_AMPM) {
displayBuf[0] = setting.val ? CHAR_AM : CHAR_PM;
displayBuf[1] = '\0';
} else if (setting.now == SETTING_NOW_TIMEMODE) {
if (setting.val) {
displayBuf[0] = '1'; displayBuf[1] = '2';
} else {
displayBuf[0] = '2'; displayBuf[1] = '4';
}
displayBuf[2] = 'h'; displayBuf[3] = 'r'; displayBuf[4] = '\0';
} else {
sprintf(displayBuf, PSTR("%02hhu"), setting.val);
}
OledShowStr(x, y, displayBuf, OLED_8X8);
}