conf = get_user_info();
memcpy(&sc_conf, conf, sizeof(struct scialys_config));
if (sc_conf.config_ok != CONFIG_OK) {
+ uprintf(UART0, "User config read from flash does not have magic : 0x%04x.\n", sc_conf.config_ok);
memset(&sc_conf, 0, sizeof(struct scialys_config));
}
/* Checksum */
if (sum != 0) {
/* Checksum error : erase config in order to start with a default one */
memset(&sc_conf, 0, sizeof(struct scialys_config));
+ uprintf(UART0, "User config read from flash has bad checksum.\n");
}
/* Config version error ? */
if (sc_conf.conf_version != CONFIG_VERSION) {
sc_conf.config_ok = 0;
+ uprintf(UART0, "User config read from flash has bad config version : %d.\n", sc_conf.conf_version);
}
}
uint8_t* mem = NULL;
int ret = 0;
uint8_t sum = 0, i = 0;
+ uint32_t size = 0;
/* Compute and store checksum */
sc_conf.checksum = 0;
uprintf(UART0, "User config update error: %d\n", ret);
return -1;
}
- ret = iap_copy_ram_to_flash((uint32_t)get_user_info(), (uint32_t)&sc_conf, sizeof(struct scialys_config));
+ size = ((sizeof(struct scialys_config) + 3) & ~0x03);
+ ret = iap_copy_ram_to_flash((uint32_t)get_user_info(), (uint32_t)&sc_conf, size);
if (ret != 0) {
uprintf(UART0, "User config update error: %d\n", ret);
return -2;
}
+ uprintf(UART0, "Scialys module user config update done\n");
return 0;
}
/* Maximum power which can be used from the external (paid) power source, specified in kW or kVA */
-#define EXT_SOURCE_POWER_LIMIT 6
+#define GRID_POWER_LIMIT 6
+
+#define SOURCE_POWER_MAX 3000 /* in Watt */
+
+#define LOAD_POWER_MAX 2700 /* in Watt */
/* mA prod value above which the system will not enter forced mode, waiting for home
* to stop using power in order to start automatic heating */
-#define SUNNY_DAYS_PROD_VALUE 3000
+uint32_t sunny_days_prod_value = 0;
+
+#define MAX_TEMP_CONFIG (80 * 100) /* 80 °C */
-/* If temperature falls bellow FORCE_HEATER_TEMP value, we enter forced heater mode, until
+/* If temperature falls bellow ENTER_FORCE_HEATER_TEMP value, we enter forced heater mode, until
* TARGET_FORCED_HEATER_TEMP is reached.
* When in forced heater mode, the heater is controlled to heat at FORCED_MODE_VALUE which
* is between 0 and 100.
*/
-#define FORCE_HEATER_TEMP (30 * 100) /* 30 °C */
+#define ENTER_FORCE_HEATER_TEMP (30 * 100) /* 30 °C */
#define TARGET_FORCED_HEATER_TEMP (50 * 100) /* 50 °C */
#define FORCED_MODE_VALUE 100 /* A fraction of 100 */
/* Delay before automatic forced heating */
#define MANUAL_TYPE_TARGET 2
#define DEFAULT_MANUAL_FORCE_TYPE MANUAL_TYPE_TARGET
-
-/* Read main configuration - or set to default one */
-void read_scialys_main_config(void)
+void check_config(void)
{
- read_internal_config();
- /* Check that config read from flash is OK */
- if (sc_conf.config_ok == CONFIG_OK) {
- uprintf(UART0, "Internal config read OK\n");
- return;
- }
/* If config version error (old config) then only update new fields */
switch (sc_conf.conf_version) {
/* Do not change order.
* Do not add breaks.
*/
default:
+ case 0x00:
/* Set base default config */
uprintf(UART0, "Fallback to default config\n");
- sc_conf.ext_source_power_limit = EXT_SOURCE_POWER_LIMIT;
- sc_conf.sunny_days_prod_value = SUNNY_DAYS_PROD_VALUE;
- sc_conf.force_heater_temp = FORCE_HEATER_TEMP;
- sc_conf.target_forced_heater_temp = TARGET_FORCED_HEATER_TEMP;
+ sc_conf.grid_power_limit = GRID_POWER_LIMIT;
+ sc_conf.source_power_max = SOURCE_POWER_MAX;
+ sc_conf.load_power_max = LOAD_POWER_MAX;
+ sc_conf.conf_max_temp = MAX_TEMP_CONFIG;
+ sc_conf.enter_force_heater_temp = ENTER_FORCE_HEATER_TEMP;
+ sc_conf.auto_forced_target_heater_temp = TARGET_FORCED_HEATER_TEMP;
sc_conf.auto_forced_mode_value = FORCED_MODE_VALUE;
sc_conf.forced_heater_delay = FORCED_HEATER_DELAY;
sc_conf.forced_heater_duration = FORCED_HEATER_DURATION;
sc_conf.conf_version = CONFIG_VERSION;
sc_conf.config_ok = CONFIG_OK;
}
+
+ /* Update sunny_days_prod_value (Watt to mA) */
+ sunny_days_prod_value = (((uint32_t)(sc_conf.load_power_max / 3) * 1000) / 230);
+}
+
+/* Read main configuration - or set to default one */
+void read_scialys_main_config(void)
+{
+ read_internal_config();
+ /* Check that config read from flash is OK */
+ if (sc_conf.config_ok == CONFIG_OK) {
+ uprintf(UART0, "Internal config read OK\n");
+ return;
+ }
+ /* Check that it is up to date */
+ check_config();
}
int external_config(uint32_t uart)
{
int ret = 0;
+ int retcode = 0;
/* uSD card */
uSD_config((struct pio *)&uSD_cs, SSP_BUS_0);
if (ret != 0) {
uprintf(uart, "Temp config error on power board: %d\n", ret);
power_board_present = 0;
- return -1;
- }
- ret = tmp101_sensor_set_continuous_conversion(&tmp101_sensor_power);
- if (ret != 0) {
- uprintf(uart, "Temp config error on power board: %d\n", ret);
- power_board_present = 0;
- return -2;
+ retcode -= 1;
+ } else {
+ ret = tmp101_sensor_set_continuous_conversion(&tmp101_sensor_power);
+ if (ret != 0) {
+ uprintf(uart, "Temp config error on power board: %d\n", ret);
+ power_board_present = 0;
+ retcode -= 2;
+ }
}
/* TMP101 sensor config on display board */
if (ret != 0) {
uprintf(uart, "Temp config error on display board: %d\n", ret);
interface_board_present = 0;
+ retcode -= 4;
} else {
ret = tmp101_sensor_set_continuous_conversion(&tmp101_sensor_display);
if (ret != 0) {
uprintf(uart, "Temp config error on display board: %d\n", ret);
interface_board_present = 0;
+ retcode -= 8;
} else {
interface_board_present = 1;
}
if (interface_board_present == 1) {
ret = interface_config(uart);
if (ret != 0) {
- return -3;
+ retcode -= 16;
}
}
- return 0;
+ return retcode;
}
}
}
+#define SCIALYS_WS2812_NB_LEDS 2
+uint8_t ws2812_leds_data[SCIALYS_WS2812_NB_LEDS * 3];
+struct ws2812_conf ws2812_leds = {
+ .nb_leds = SCIALYS_WS2812_NB_LEDS,
+ .led_data = ws2812_leds_data,
+ .inverted = 0,
+};
+
int interface_config(uint32_t uart)
{
/* Buttons inputs on front panel */
set_gpio_callback(button_callback, &button_ok, EDGE_RISING);
/* WS2812B Leds on display board */
- ws2812_config(&ws2812_data_out_pin);
+ ws2812_config(&ws2812_leds, &ws2812_data_out_pin);
/* Configure and start display */
config_gpio(&oled_reset, 0, GPIO_DIR_OUT, 1); /* Release reset signal */
erase_screen_content();
ssd130x_display_full_screen(&display);
- ws2812_set_pixel(0, 0x05, 0x15, 0x08);
- ws2812_send_frame(0);
+ ws2812_set_pixel(&ws2812_leds, 0, 0x05, 0x15, 0x08);
+ ws2812_send_frame(&ws2812_leds, 0);
uprintf(uart, "Interface config OK\n");
enum menu_list {
MAIN_MENU,
- MANUAL_MODE,
+ FORCE_MODE,
DATE_CONFIG,
LIMITS,
- FUNCTIONS,
- TEST_MODE,
+ RESET_CONFIG,
SAVE_CONFIG,
+ TEST_MODE,
NB_MENU, /* This one must be the last */
};
static const char* menu_titles[] = {
[MAIN_MENU] = "Menu principal",
- [MANUAL_MODE] = "Marche Forcee",
+ [FORCE_MODE] = "Marche Forcee",
[DATE_CONFIG] = "Regl. Heure",
- [LIMITS] = "Limites",
- [FUNCTIONS] = "Reglages",
- [TEST_MODE] = "Test mode",
+ [LIMITS] = "Regl. Limites",
[SAVE_CONFIG] = "Save config",
+ [RESET_CONFIG] = "Reset config",
+ [TEST_MODE] = "Test mode",
};
static uint8_t current_menu = MAIN_MENU;
-static uint8_t current_entry = MANUAL_MODE;
+static uint8_t current_entry = FORCE_MODE;
-enum func_menu_list {
- FUNC_LOAD_TYPE,
- FUNC_CMD_TYPE,
- FUNC_NB_MENU, /* This one must be the last */
+enum force_mode_list {
+ MANUAL_FORCE_MODE,
+ AUTO_FORCE_MODE,
+ FORCE_NB_MENU, /* This one must be the last */
};
-static const char* func_modes_titles[] = {
- [FUNC_LOAD_TYPE] = "Load Type",
- [FUNC_CMD_TYPE] = "CMD type",
+static const char* force_modes_titles[] = {
+ [MANUAL_FORCE_MODE] = "Manuelle",
+ [AUTO_FORCE_MODE] = "Automatique",
};
-static uint8_t func_cur_menu = FUNC_LOAD_TYPE;
-static uint8_t func_cur_entry = FUNC_LOAD_TYPE;
+static uint8_t force_cur_menu = MANUAL_FORCE_MODE;
+static uint8_t force_cur_entry = MANUAL_FORCE_MODE;
enum limits_menu_list {
- TEMP_FORCED_START,
- TEMP_FORCED_TARGET,
+ TEMP_MAX,
+ PMAX_GRID,
+ PMAX_PROD,
+ PMAX_CONSO,
+ LOAD_TYPE,
FORCED_MODE_VAL,
- SUNNY_DAY_VALUE,
LIM_NB_MENU, /* This one must be the last */
};
static const char* limits_modes_titles[] = {
- [TEMP_FORCED_START] = "Water Min",
- [TEMP_FORCED_TARGET] = "Water Target",
+ [TEMP_MAX] = "Temp Max",
+ [PMAX_GRID] = "Pmax Abon.",
+ [PMAX_PROD] = "Pmax Prod.",
+ [PMAX_CONSO] = "P Charge",
+ [LOAD_TYPE] = "Type Charge",
[FORCED_MODE_VAL] = "Force Val",
- [SUNNY_DAY_VALUE] = "Sun Prod",
};
-static uint8_t limits_cur_menu = TEMP_FORCED_START;
-static uint8_t limits_cur_entry = TEMP_FORCED_START;
+static uint8_t limits_cur_menu = TEMP_MAX;
+static uint8_t limits_cur_entry = TEMP_MAX;
enum test_menu_list {
TEST_FAN,
int i = 0;
for (i = 1; i < NB_MENU; i++) {
if (i != current_entry) {
- display_line(i + 1, 3, menu_titles[i]);
+ display_line(i + 1, 2, menu_titles[i]);
} else {
- snprintf(line, DISP_LLEN, " ->%s", menu_titles[i]);
+ snprintf(line, DISP_LLEN, " >%s", menu_titles[i]);
display_line(i + 1, 0, line);
}
}
case DATE_CONFIG: {
static uint8_t date_idx = 0;
struct rtc_time now;
- snprintf(line, DISP_LLEN, "^");
+ snprintf(line, DISP_LLEN, "%c", 0x60);
display_line(3, 6 + (date_idx * 3), line);
rtc_pcf85363_time_read(&rtc_conf, &now);
snprintf(line, DISP_LLEN, "%02xh%02x:%02x", now.hour, now.min, now.sec);
display_line(4, 6, line);
- snprintf(line, DISP_LLEN, "V");
+ snprintf(line, DISP_LLEN, "%c", 0x7F);
display_line(5, 6 + (date_idx * 3), line);
if ((button & BUTTON_RIGHT) && (date_idx < 2)) {
date_idx++;
if (i != limits_cur_entry) {
display_line(i + 2, 3, limits_modes_titles[i]);
} else {
- snprintf(line, DISP_LLEN, " ->%s", limits_modes_titles[i]);
+ snprintf(line, DISP_LLEN, " >%s", limits_modes_titles[i]);
display_line(i + 2, 0, line);
}
}
sub_menu_level = 0;
current_menu = MAIN_MENU;
}
- } else {
+ } else { /* sub_menu_level == 2 */
+ display_line(1, 1, limits_modes_titles[limits_cur_menu]);
+ switch (limits_cur_menu) {
+ case TEMP_MAX:
+ snprintf(line, DISP_LLEN, " %c", 0x60);
+ display_line(3, 1, line);
+ snprintf(line, DISP_LLEN, "Abs. max: %d %cC", sc_conf.conf_max_temp / 100, 0x24);
+ display_line(4, 1, line);
+ snprintf(line, DISP_LLEN, " %c", 0x7f);
+ display_line(5, 1, line);
+ if (button & BUTTON_UP) {
+ if (sc_conf.conf_max_temp <= 8500) {
+ sc_conf.conf_max_temp += 500;
+ } else {
+ sc_conf.conf_max_temp = 9000;
+ }
+ }
+ if (button & BUTTON_DOWN) {
+ if (sc_conf.conf_max_temp > 500) {
+ sc_conf.conf_max_temp -= 500;
+ } else {
+ sc_conf.conf_max_temp = 0;
+ }
+ }
+ if (button & BUTTON_LEFT) {
+ sub_menu_level = 1;
+ }
+ if (button & BUTTON_OK) {
+ sub_menu_level = 0;
+ current_menu = MAIN_MENU;
+ }
+ break;
+ case FORCED_MODE_VAL:
+ break;
+ case PMAX_GRID:
+ /* remember to update max_intensity */
+ break;
+ case PMAX_PROD:
+ /* remember to update sunny_days_prod_value */
+ break;
+ case PMAX_CONSO:
+ break;
+ case LOAD_TYPE:
+ break;
+ }
}
}
break;
- case FUNCTIONS: {
+ case FORCE_MODE: {
if (sub_menu_level == 1) {
int i = 0;
- for (i = 0; i < FUNC_NB_MENU; i++) {
- if (i != func_cur_entry) {
- display_line(i + 2, 3, func_modes_titles[i]);
+ for (i = 0; i < FORCE_NB_MENU; i++) {
+ if (i != force_cur_entry) {
+ display_line(i + 2, 3, force_modes_titles[i]);
} else {
- snprintf(line, DISP_LLEN, " ->%s", func_modes_titles[i]);
+ snprintf(line, DISP_LLEN, " ->%s", force_modes_titles[i]);
display_line(i + 2, 0, line);
}
}
if (button & BUTTON_UP) {
- func_cur_entry -= 1;
+ force_cur_entry -= 1;
}
if (button & BUTTON_DOWN) {
- func_cur_entry += 1;
+ force_cur_entry += 1;
}
- if (func_cur_entry >= FUNC_NB_MENU) {
- func_cur_entry = 0;
- } else if (func_cur_entry == 0xFF) {
- func_cur_entry = FUNC_NB_MENU - 1;
+ if (force_cur_entry >= FORCE_NB_MENU) {
+ force_cur_entry = 0;
+ } else if (force_cur_entry == 0xFF) {
+ force_cur_entry = FORCE_NB_MENU - 1;
}
if (button & (BUTTON_OK | BUTTON_RIGHT)) {
- func_cur_menu = func_cur_entry;
+ force_cur_menu = force_cur_entry;
sub_menu_level = 2;
}
if (button & BUTTON_LEFT) {
if (force_cmd >= 0) {
force_cmd = -1;
} else {
- force_cmd = 0;
+ force_cmd = 50;
}
default:
break;
}
break;
case SAVE_CONFIG: {
- user_flash_update();
+ snprintf(line, DISP_LLEN, "Sauver config ?");
+ display_line(2, 1, line);
+ snprintf(line, DISP_LLEN, "Valider = OK");
+ display_line(5, 1, line);
+ snprintf(line, DISP_LLEN, "Gauche = Annul");
+ display_line(6, 1, line);
+ if (button & BUTTON_LEFT) {
+ sub_menu_level = 0;
+ current_menu = MAIN_MENU;
+ }
+ if (button & BUTTON_OK) {
+ int ret = 0;
+ erase_screen_content();
+ snprintf(line, DISP_LLEN, "Saving ...");
+ display_line(4, 2, line);
+ ssd130x_display_full_screen(&display);
+ ret = user_flash_update();
+ uprintf(UART0, "\nFlash update from menu: %d\n", ret);
+ msleep(1500);
+ sub_menu_level = 0;
+ current_menu = MAIN_MENU;
+ }
+ }
+ break;
+ case RESET_CONFIG: {
+ snprintf(line, DISP_LLEN, "Efface Config ?");
+ display_line(2, 1, line);
+ snprintf(line, DISP_LLEN, "Valider = OK");
+ display_line(5, 1, line);
+ snprintf(line, DISP_LLEN, "Gauche = Annul");
+ display_line(6, 1, line);
+ if (button & BUTTON_LEFT) {
+ sub_menu_level = 0;
+ current_menu = MAIN_MENU;
+ }
+ if (button & BUTTON_OK) {
+ erase_screen_content();
+ snprintf(line, DISP_LLEN, "Chargement ...");
+ display_line(4, 8, line);
+ ssd130x_display_full_screen(&display);
+ sc_conf.conf_version = 0x00;
+ check_config();
+ uprintf(UART0, "\nSystem defaults reloaded from menu\n");
+ msleep(500);
+ sub_menu_level = 0;
+ current_menu = MAIN_MENU;
+ }
}
break;
- case MANUAL_MODE:
- manual_activation_request = -1;
- /* Fallback */
default:
interface_mode = MODE_RUN;
current_menu = MAIN_MENU;
display_line(0, 0, line);
/* Display info */
- snprintf(line, DISP_LLEN, "Water:% 2d.%03d %cC", (water_centi_degrees / 100), (abs_centi % 100), 0x1F);
+ snprintf(line, DISP_LLEN, "Water:% 2d.%03d %cC", (water_centi_degrees / 100), (abs_centi % 100), 0x24);
display_line(2, 0, line);
snprintf(line, DISP_LLEN, "Prod :% 2d,%03dA", (solar_prod_value / 1000), ((solar_prod_value % 1000) / 10));
display_line(3, 0, line);
/* Update RGB leds */
/* FIXME : use for error signal */
- ws2812_set_pixel(0, (home_conso_value / 2000), (solar_prod_value / 2000), 0);
- ws2812_set_pixel(1, 0, 0, command_val);
- ws2812_send_frame(0);
+ ws2812_set_pixel(&ws2812_leds, 0, (home_conso_value / 2000), (solar_prod_value / 2000), 0);
+ ws2812_set_pixel(&ws2812_leds, 1, 0, 0, command_val);
+ ws2812_send_frame(&ws2812_leds, 0);
} else {
/* Config mode. Mode entered by button action, which implies that interface board is present. */
config_interface_handle();
#define T_MULT (3600 * 1000 / DEC_PERIOD)
/* Max water temperature. Internal protection which cannot be overriden by configuration */
-#define MAX_WATER_TEMP (90 * 100) /* Hundredth of degrees C : 90°C */
+#define ABSOLUTE_MAX_WATER_TEMP (90 * 100) /* Hundredth of degrees C : 90°C */
/* Internal system max temperature : turn off heating when reached */
#define MAX_INTERNAL_TEMP (80 * 10) /* Tenth of degrees C : 80°C */
/* Water and internaltemperature */
int water_centi_degrees = 0;
-int deci_degrees_power = 0;
-int deci_degrees_disp = 0;
+
+/* Set to ABSOLUTE_MAX_WATER_TEMP - 1.5°C when enbling max temp hysteresis */
+int max_temp_hysteresys = 0;
+
+/* Internal temperatures */
+int deci_degrees_power = 0; /* Power board sensor */
+int deci_degrees_disp = 0; /* Display board sensor */
/* Value in mA computed upon startup from config ext_source_power_limit */
static int32_t max_intensity = 0;
goto cmd_update_end;
}
- /* Water max temperature protection */
- if (water_centi_degrees > MAX_WATER_TEMP) {
+ /* Water max temperature protection with hysteresys */
+ if ((water_centi_degrees > ABSOLUTE_MAX_WATER_TEMP) ||
+ (water_centi_degrees > sc_conf.conf_max_temp) ||
+ (water_centi_degrees > max_temp_hysteresys)) {
cmd = 0;
+ max_temp_hysteresys = (ABSOLUTE_MAX_WATER_TEMP - 150); /* 1.5 °C */
+ if (max_temp_hysteresys > sc_conf.conf_max_temp) {
+ max_temp_hysteresys = (sc_conf.conf_max_temp - 150); /* still 1.5°C */
+ }
goto cmd_update_end;
+ } else if (water_centi_degrees < max_temp_hysteresys) {
+ /* Remove Hysteresys */
+ max_temp_hysteresys = ABSOLUTE_MAX_WATER_TEMP;
}
/* Forced over-voltage protection or test mode */
mode = heat;
/* Need to enter Forced heating mode ? */
- if (water_centi_degrees < sc_conf.force_heater_temp) {
+ if (water_centi_degrees < sc_conf.enter_force_heater_temp) {
if (forced_heater_mode == 0) {
msg = "Water temp low, entering forced mode\n";
forced_heater_mode = 1;
}
status_led(red_on);
mode = forced;
- } else if ((water_centi_degrees > sc_conf.target_forced_heater_temp) && (forced_heater_mode == 1)) {
+ } else if ((water_centi_degrees > sc_conf.auto_forced_target_heater_temp) && (forced_heater_mode == 1)) {
status_led(red_off);
msg = "Water temp OK, forced mode exit\n";
forced_heater_mode = 0;
}
/* Do not force if there is a lot of sun, it may be enough to heat again soon */
- if ((solar_prod_value > sc_conf.sunny_days_prod_value) && (forced_heater_mode == 1)) {
+ if ((solar_prod_value > sunny_days_prod_value) && (forced_heater_mode == 1)) {
mode = delayed_heat_prod;
forced_heater_mode = 0;
}
board_io_config();
modules_config();
external_config(UART0);
+
read_scialys_main_config();
- /* Compute max_intensity from config ext_source_power_limit.
- * ext_source_power_limit is in kVA, max_intensity in mA */
- max_intensity = sc_conf.ext_source_power_limit * 1000 * 1000 / 230;
+ /* Compute max_intensity from config grid_power_limit.
+ * grid_power_limit is in kVA, max_intensity in mA */
+ max_intensity = sc_conf.grid_power_limit * 1000 * 1000 / 230;
+
scialys_systick_and_timers_config();
- uprintf(UART0, "System setup complete. %d\n", -max_intensity);
+ uprintf(UART0, "System setup complete.\n");
+ uprintf(UART0, "Max intensity: %d\n", max_intensity);
msleep(500);
status_led(green_only);
}
/* Update water tank temperature */
+ #define WTA_NB_VAL 8 /* Choose a multiple of 2 */
+ #define WTA_SHIFT 3 /* Set shift according to WTA_NB_VAL */
if (1) {
- int ret = 0, old_water_temp = 0;
- old_water_temp = water_centi_degrees;
- /* Get thermocouple value */
+ static int water_centi_average[WTA_NB_VAL];
+ static int wta_idx = 0;
+ int ret = 0, i = 0;
+ /* Get thermocouple value, store in table only if valid */
ret = max31855_sensor_read(&thermocouple, NULL, &water_centi_degrees);
if (ret != 0) {
uprintf(UART0, "Water Temp read error : %d\n", ret);
- /* Do not act upon iinvalid temp value */
- water_centi_degrees = old_water_temp;
+ } else {
+ water_centi_average[wta_idx++] = water_centi_degrees;
+ if (wta_idx >= WTA_NB_VAL) {
+ wta_idx = 0;
+ }
+ }
+ water_centi_degrees = 0;
+ /* Average water tank temperature */
+ for (i = 0; i < WTA_NB_VAL; i++) {
+ water_centi_degrees += water_centi_average[i];
}
+ water_centi_degrees >>= WTA_SHIFT;
}
/* Update current mode */