/* 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 static char workbuff[256]; static void get_controller_folder_path(char *path, size_t size) { uint32_t high; uint32_t mid; uint32_t low; if (!path) return; stm_unique_id_get(&high, &mid, &low); snprintf(path, size, "/%08X-%08X-%08X", (unsigned int)high, (unsigned int)mid, (unsigned int)low); } static void get_controller_settings_path(char *path, size_t size, const char *setting) { char folder[48]; get_controller_folder_path(folder, sizeof(folder)); snprintf(path, size, "%s/%s.conf", folder, setting); } /** * @brief Open or create the controller folder on the SD Card. * @param[in,out] controller_folder * @return 0 if opened, 1 if created and opened, -1 if error. */ static int create_controller_folder(void) { char foldername[48]; int ret = -1; FRESULT filesystem_result; FILINFO fno; get_controller_folder_path(foldername, sizeof(foldername)); /* Check if folder is present */ filesystem_result = f_stat(foldername, &fno); if (filesystem_result == FR_OK && fno.fattrib & AM_DIR) { ret = 0; } else { filesystem_result = f_mkdir(foldername); ret = filesystem_result == FR_OK ? 1 : -1; } return ret; } int sd_card_settings_save_calibration(float sens_deviation, float offset, bool active) { char path[200]; FRESULT res = FR_OK; int ret = 0; FIL file; get_controller_settings_path(path, sizeof(path), "calibration"); if (create_controller_folder() < 0) return -2; if (!active) { res = f_unlink(path); goto check_fresult; } res = f_open(&file, path, FA_CREATE_ALWAYS | FA_WRITE); if (res != FR_OK) goto check_fresult; snprintf(path, sizeof(path), "offset = %f\nsensitivity = %f\n", offset, sens_deviation); ret = f_puts(path, &file); if (ret < 0) goto close_file; ret = 0; close_file: res = f_close(&file); check_fresult: if (res != FR_OK) return -2; return ret; } int sd_card_settings_try_load_calibration(float *sens_deviation, float *offset) { char path[128]; int status = -1; struct config_parser parser; config_parser_handle_t p; enum config_parser_ret res; struct config_parser_entry entry; bool sens_loaded = false; bool offset_loaded = false; if (!sens_deviation || !offset) return -1000; get_controller_settings_path(path, sizeof(path), "calibration"); p = config_parser_open_file(&parser, false, path, workbuff, sizeof(workbuff)); if (!p) return status; do { res = config_parser_get_line(p, &entry, true); if (res == CONFIG_PARSER_OK) { if (!strcmp(entry.name, "offset") && entry.type == CONFIG_PARSER_TYPE_FLOAT) { offset_loaded = true; *offset = entry.value.float_val; } else if (!strcmp(entry.name, "sensitivity") && entry.type == CONFIG_PARSER_TYPE_FLOAT) { sens_loaded = true; *sens_deviation = entry.value.float_val; } } } while (!config_parser_ret_is_abort_condition(res)); config_parser_close_file(p); if (sens_loaded && offset_loaded) status = 0; return status; } enum settings_load_result sd_card_settings_load_pid_oven_parameters(struct oven_pid_settings *settings) { enum settings_load_result ret = SETT_LOAD_ERR; const char * const file_name = SETTINGS_PID_PARAMETER_FILE; struct config_parser parser; config_parser_handle_t p; enum config_parser_ret parse_result; struct config_parser_entry entry; bool kp_loaded = false, kd_loaded = false, ki_loaded = false, int_max_loaded = false, t_sample = false; bool kd_tau_loaded = false; if (!settings) { ret = SETT_LOAD_ERR; goto exit; } workbuff[0] = 0; p = config_parser_open_file(&parser, false, file_name, workbuff, sizeof(workbuff)); if (!p) goto exit; do { parse_result = config_parser_get_line(p, &entry, true); if (parse_result == CONFIG_PARSER_OK) { if (!strcmp(entry.name, "kp")) { kp_loaded = true; settings->kp = entry.value.float_val; } else if (!strcmp(entry.name, "kd")) { kd_loaded = true; settings->kd = entry.value.float_val; } else if (!strcmp(entry.name, "ki")) { ki_loaded = true; settings->ki = entry.value.float_val; } else if (!strcmp(entry.name, "max_int")) { int_max_loaded = true; settings->max_integral = entry.value.float_val; } else if (!strcmp(entry.name, "sample_period")) { t_sample = true; settings->t_sample = entry.value.float_val / 1000.0f; } else if (!strcmp(entry.name, "kd_tau")) { settings->kd_tau = entry.value.float_val; kd_tau_loaded = true; } } } while (!config_parser_ret_is_abort_condition(parse_result)); if (kp_loaded && kd_loaded && ki_loaded && t_sample && int_max_loaded && kd_tau_loaded) ret = SETT_LOAD_SUCCESS; config_parser_close_file(p); exit: return ret; }