/* Reflow Oven Controller * * Copyright (C) 2020 Mario Hüttel * * This file is part of the Reflow Oven Controller Project. * * The reflow oven controller is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * The Reflow Oven Control Firmware is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with the reflow oven controller project. * If not, see . */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static char IN_SECTION(.ccm.bss) display_buffer[4][21] = {0}; static struct lcd_menu IN_SECTION(.ccm.bss) reflow_menu; #define reflow_menu_ptr (&reflow_menu) static void update_display_buffer(uint8_t row, const char *data) { int i; if (row > 4) return; if (!data) return; for (i = 0; data[i] && i < LCD_CHAR_WIDTH; i++) { display_buffer[row][i] = data[i]; } display_buffer[row][i] = 0; } static void reflow_menu_monitor(struct lcd_menu *menu, enum menu_entry_func_entry entry_type, void *parent) { static void *my_parent; static uint64_t my_timestamp = 0; char line[17]; float tmp; int res; const char *prefix; if (entry_type == MENU_ENTRY_FIRST_ENTER) { my_parent = parent; menu_display_clear(menu); } if (systick_ticks_have_passed(my_timestamp, 250)) { my_timestamp = systick_get_global_tick(); adc_pt1000_get_current_resistance(&tmp); snprintf(line, sizeof(line), "Res: %.1f " LCD_OHM_SYMBOL_STRING, tmp); menu->update_display(0, line); res = temp_converter_convert_resistance_to_temp(tmp, &tmp); switch (res) { case -1: prefix = "<"; break; case 1: prefix = ">"; break; default: prefix = ""; break; } snprintf(line, sizeof(line), "Temp: %s%.1f " LCD_DEGREE_SYMBOL_STRING "C", prefix, tmp); menu->update_display(1, line); (void)safety_controller_get_analog_mon_value(ERR_AMON_UC_TEMP, &tmp); snprintf(line, sizeof(line), "Tj: %.1f " LCD_DEGREE_SYMBOL_STRING "C", tmp); menu->update_display(2, line); (void)safety_controller_get_analog_mon_value(ERR_AMON_VREF, &tmp); snprintf(line, sizeof(line), "Vref: %.1f mV", tmp); menu->update_display(3, line); } if (menu->inputs.push_button == BUTTON_SHORT_RELEASED || menu->inputs.push_button == BUTTON_LONG) { menu_entry_dropback(menu, my_parent); } } static void reflow_menu_about(struct lcd_menu *menu, enum menu_entry_func_entry entry_type, void *parent) { static void *my_parent; static int page = 0; static int last_page = -1; static uint32_t uptime_secs; uint32_t new_uptime_secs; uint32_t uptime_mins; uint32_t uptime_hours; uint32_t uptime_days; int16_t rot_delta; uint32_t ser1, ser2, ser3; enum button_state push_button; bool button_ready; if (entry_type == MENU_ENTRY_FIRST_ENTER) { uptime_secs = 0ULL; page = 0; last_page = -1; my_parent = parent; menu_display_clear(menu); menu_ack_rotary_delta(menu); } rot_delta = menu_get_rotary_delta(menu); if (rot_delta >= 4) { menu_ack_rotary_delta(menu); if (page < 4) { page++; menu_display_clear(menu); } } else if (rot_delta <= -4) { menu_ack_rotary_delta(menu); if (page > 0) { page--; menu_display_clear(menu); } } switch (page) { case 0: if (last_page == 0) break; last_page = 0; menu_lcd_output(menu, 0, LCD_SHIMATTA_STRING " Shimatta"); menu_lcd_output(menu, 1, "Oven Controller"); menu_lcd_output(menu, 2, "(c) Mario H\xF5ttel"); menu_lcd_output(menu, 3, "Page 1/5"); break; case 1: if (last_page == 1) break; last_page = 1; menu_lcd_output(menu, 0, "Version Number:"); menu_lcd_outputf(menu, 1, "%.*s", LCD_CHAR_WIDTH, xstr(GIT_VER)); if (strlen(xstr(GIT_VER)) > LCD_CHAR_WIDTH) { menu_lcd_outputf(menu, 2, "%s", &xstr(GIT_VER)[LCD_CHAR_WIDTH]); } #ifdef DEBUGBUILD menu_lcd_output(menu, 3, "Page 2/5 [DEBUG]"); #else menu_lcd_output(menu, 3, "Page 2/5"); #endif break; case 2: if (last_page == 2) break; last_page = 2; menu_lcd_output(menu, 0, "Compile Info"); menu_lcd_output(menu, 1, __DATE__); menu_lcd_output(menu, 2, __TIME__); menu_lcd_output(menu, 3, "Page 3/5"); break; case 3: if (last_page == 3) break; last_page = 3; unique_id_get(&ser1, &ser2, &ser3); menu_lcd_outputf(menu, 0, "Serial: %08X", ser1); menu_lcd_outputf(menu, 1, " %08X", ser2); menu_lcd_outputf(menu, 2, " %08X", ser3); menu_lcd_output(menu, 3, "Page 4/5"); break; case 4: last_page = 4; systick_get_uptime_from_tick(&uptime_days, &uptime_hours, &uptime_mins, &new_uptime_secs); if (new_uptime_secs != uptime_secs) { uptime_secs = new_uptime_secs; menu_lcd_output(menu, 0, "Uptime:"); menu_lcd_outputf(menu, 1, "%lu day%s %02lu:%02lu:%02lu", uptime_days, (uptime_days == 1 ? "" : "s"), uptime_hours, uptime_mins, uptime_secs); menu_lcd_output(menu, 3, "Page 5/5"); } break; default: page = 0; last_page = -1; break; } push_button = menu_get_button_state(menu); button_ready = menu_get_button_ready_state(menu); if (push_button == BUTTON_IDLE) button_ready = true; if (button_ready && (push_button == BUTTON_SHORT_RELEASED || push_button == BUTTON_LONG)) { menu_entry_dropback(menu, my_parent); } } static void reflow_menu_err_flags(struct lcd_menu *menu, enum menu_entry_func_entry entry_type, void *parent) { static void *my_parent = NULL; static uint8_t offset; static uint64_t timestamp; static bool end_of_list_reached = true; bool state; enum button_state push_button; int16_t rot; uint32_t i; char name[64]; int32_t line_counter; bool skip_err_flag_prefix; bool try_ack = false; enum safety_flag flag; bool button_ready; const char *err_flag_prefix = "ERR_FLAG_"; push_button = menu_get_button_state(menu); rot = menu_get_rotary_delta(menu); if (entry_type != MENU_ENTRY_CONTINUE) { if (entry_type == MENU_ENTRY_FIRST_ENTER) { my_parent = parent; offset = 0; end_of_list_reached = true; } } else { if (push_button == BUTTON_IDLE && rot == 0) { if (!systick_ticks_have_passed(timestamp, 150)) return; } } button_ready = menu_get_button_ready_state(menu); if (push_button == BUTTON_SHORT_RELEASED && button_ready) { menu_entry_dropback(menu, my_parent); } if (push_button == BUTTON_LONG && button_ready) { try_ack = true; } if (rot >= 4 || rot <= -4) { menu_ack_rotary_delta(menu); if (rot > 0) { if (!end_of_list_reached) offset++; } else { if (offset > 0) offset--; } } menu_display_clear(menu); line_counter = -offset; for (i = 0; i < safety_controller_get_flag_count(); i++) { (void)safety_controller_get_flag_by_index(i, &state, &flag); if (try_ack) safety_controller_ack_flag(flag); if (state) { if (line_counter >= 0 && line_counter < 4) { safety_controller_get_flag_name_by_index(i, name, sizeof(name)); if (!strncmp(name, err_flag_prefix, 9)) { skip_err_flag_prefix = true; } else { skip_err_flag_prefix = false; } menu_lcd_outputf(menu, line_counter, "%s", (skip_err_flag_prefix ? &name[9] : name)); } line_counter++; } } end_of_list_reached = (line_counter > 4 ? false : true); timestamp = systick_get_global_tick(); } static void reflow_menu_root_entry(struct lcd_menu *menu, enum menu_entry_func_entry entry_type, void *parent) { (void)parent; static struct menu_list list; bool menu_changed = false; static const char * const root_entry_names[] = { "About", "Monitoring", "Error Flags", NULL }; static const menu_func_t root_entry_funcs[] = { reflow_menu_about, reflow_menu_monitor, reflow_menu_err_flags, }; enum button_state push_button; int16_t rot_delta; if (entry_type != MENU_ENTRY_CONTINUE) { menu_changed = true; menu_display_clear(menu); update_display_buffer(0, "Main Menu"); menu_ack_rotary_delta(menu); if (entry_type == MENU_ENTRY_FIRST_ENTER) { list.entry_names = root_entry_names; list.submenu_list = root_entry_funcs; list.update_display = menu->update_display; list.currently_selected = 0; menu_list_compute_count(&list); } } push_button = menu_get_button_state(menu); rot_delta = menu_get_rotary_delta(menu); if (menu_get_button_ready_state(menu) && push_button == BUTTON_SHORT_RELEASED) { /* Enter currently selected menu_entry */ menu_list_enter_selected_entry(&list, menu); } if (rot_delta >= 4) { menu_list_scroll_down(&list); menu_ack_rotary_delta(menu); menu_changed = true; } else if (rot_delta <= -4) { menu_list_scroll_up(&list); menu_ack_rotary_delta(menu); menu_changed = true; } if (menu_changed) menu_list_display(&list, 1, 3); } int reflow_menu_handle() { int32_t rot_delta; enum button_state button; static enum lcd_fsm_ret lcd_ret = LCD_FSM_NOP; rot_delta = rotary_encoder_get_change_val(); button = button_read_event(); menu_handle(reflow_menu_ptr, (int16_t)rot_delta, button); if (lcd_ret == LCD_FSM_CALL_AGAIN || lcd_tick_100us >= 5) { lcd_ret = lcd_fsm_write_buffer(display_buffer); lcd_tick_100us = 0UL; } if (lcd_ret == LCD_FSM_CALL_AGAIN) return 0; else return 1; } void reflow_menu_init() { rotary_encoder_setup(); button_init(); lcd_init(); menu_init(reflow_menu_ptr, reflow_menu_root_entry, update_display_buffer); }