Rework of the main algorithm, to be tested
authorNathael Pajani <nathael.pajani@ed3l.fr>
Wed, 13 Dec 2023 22:02:41 +0000 (23:02 +0100)
committerNathael Pajani <nathael.pajani@ed3l.fr>
Wed, 13 Dec 2023 22:02:41 +0000 (23:02 +0100)
v10/comm.h
v10/config.c
v10/config.h
v10/interface.c
v10/main.c
v10/version.h

index d2fc29d..c1d35bb 100644 (file)
@@ -22,6 +22,7 @@
 #ifndef COMM_H
 #define COMM_H
 
+#include "data.h"
 
 /***************************************************************************** */
 /* Rx interrupt handler for system configuration over USB */
index dc834b7..c35240b 100644 (file)
@@ -242,8 +242,7 @@ void scialys_systick_and_timers_config(void)
 
        /* Configure the fake zero-cross generation timer to 100Hz.
         * Do not start it now, it will be started by the first real zero-cross detected in order to
-        *   generate the other half zc (not seen by system due to hardware design)
-        * Also sarted by config when DC mode is set */
+        *   generate the other half zc (not seen by system due to hardware design) */
        timer_set_match(LPC_TIMER_32B0, CHAN0, (clk_cycles_ac_zc * 100));
 
        /* Configure the power tracking timer.
@@ -419,7 +418,7 @@ uint32_t sunny_days_prod_value = 0;
  * grid_power_limit is in kVA, max_intensity in mA */
 void update_max_intensity(void)
 {
-       max_intensity = sc_conf.grid_power_limit * 1000 * 1000 / 230;
+       max_intensity = (uint32_t)(sc_conf.grid_power_limit * 1000 * 1000) / 230;
        uprintf(UART0, "Max intensity is %dmA\n", max_intensity);
 }
 /* Update sunny_days_prod_value (Watt to mA) */
index d97ed99..4fdb31c 100644 (file)
@@ -52,6 +52,7 @@
 /* This struct is stored to user flash when user validates new configuration
  * and read from user flash upon statup
  * Default values are defined within config.c
+ * Note that temperature values are stored as hundredth of degrees centigrade (1/100)
  *
  * Meaning of fields :
  *
index 5ccac17..f1db754 100644 (file)
@@ -213,7 +213,7 @@ extern int water_centi_degrees;
 extern uint32_t solar_prod_value;
 extern uint32_t home_conso_value;
 extern volatile uint8_t command_val;
-extern int manual_activation_request;
+extern uint8_t manual_activation_request;
 
 static int interface_mode = MODE_RUN;
 
@@ -240,7 +240,7 @@ void interface_update(char heat_mode)
        if (interface_mode == MODE_RUN) {
                if (button_pressed != 0) {
                        if (button_pressed & BUTTON_OK) {
-                               manual_activation_request = -1; /* Tells main loop that a request has been triggered */
+                               manual_activation_request = 1; /* Tells main loop that a request has been triggered */
                        }
                        if (button_pressed & BUTTON_UP) {
                                interface_mode = MODE_CONFIG;
index 5ae9c57..c61764a 100644 (file)
@@ -22,6 +22,7 @@
  *************************************************************************** */
 
 
+#include "lib/utils.h"
 #include "config.h"
 #include "interface.h"
 #include "time.h"
@@ -31,7 +32,7 @@
 #include "uSD.h"
 
 
-#define MODULE_VERSION    0x0A
+#define MODULE_VERSION   0x0A
 #define MODULE_NAME "Scialys uC"
 
 
 /* This multiplier is used for all durations and delays which are stored in number of hours */
 #define T_MULT  (3600 * 1000 / DEC_PERIOD)
 
-/* Max water temperature. Internal protection which cannot be overriden by configuration */
-#define ABSOLUTE_MAX_WATER_TEMP (90 * 100)  /* Hundredth of degrees C : 90°C */
+/* Max water temperature. Internal protection which cannot be overriden by configuration
+ * Note that configuration should not allow temperature above 90°C */
+#define ABSOLUTE_MAX_WATER_TEMP (92 * 100)  /* Hundredth of degrees C : 92°C */
 
-/* Number of main loops during which we keep the mosfets ON in order to keep them safe */
-#define OVERVOLTAGE_PROTECTION_CYCLES 100
 
 
-/* Flags and counters */
-uint8_t forced_heater_mode = 0; /* Flag only */
+/* Flags and counters for forced modes */
+uint8_t forced_heater_mode = 0; /* Flag and force type (enum force_types) */
+uint8_t manual_activation_request = 0; /* Flag */
+uint16_t forced_target_temp = 0;
 uint32_t forced_heater_delay = 0; /* Flag and counter */
-uint32_t forced_heater_time = 0; /* Flag and counter */
-uint8_t manual_forced_heater = 0; /* Flag only */
-int manual_activation_request = 0; /* Flag and counter */
+uint32_t forced_heater_duration = 0; /* Flag and counter */
+uint8_t forced_cmd_val = 0;
+
+/* Error flags */
 int8_t internal_temp_error_shutdown = 0; /* Flag and error code */
 uint8_t external_disable = 0; /* Flag only */
 uint8_t mosfet_temp_shutdown = 0; /* Flag only */
@@ -67,12 +70,15 @@ enum modes {
        mode_forced = 'F', /* Auto Forced heating */
        mode_manual = 'M', /* Manual Forced heating */
        mode_delayed_heat_prod = 'P', /* Pause */
-       mode_overprod = 'O', /* Over production, try to start other loads */
        mode_temp_OK = 'T', /* Max temperature reached */
        mode_overvoltage = 'V', /* Overvoltage detected */
        mode_overtemp = 'H', /* Mosfet over-temperature detected */
 };
 
+/* Current running mode */
+volatile char mode = mode_heat;
+volatile char old_mode = mode_heat;
+
 /* Water temperature */
 int water_centi_degrees = 0;
 
@@ -92,24 +98,30 @@ struct rtc_time now;
 
 
 /***************************************************************************** */
-/* Decrementer for forced heating modes timers */
+/* Decrementer
+ *  - decrease forced heating modes timers
+ *  - decrease overvoltage protection timer
+ */
 
 void handle_dec_request(uint32_t curent_tick)
 {
-       /* Manual forced mode */
-       if (manual_activation_request > 0) {
-               manual_activation_request--;
-       }
-       /* Automatic forced mode */
-       if (forced_heater_mode == 1) {
+       /* Forced mode */
+       if (forced_heater_mode > FORCE_TYPE_OFF) {
                if (forced_heater_delay > 0) {
                        forced_heater_delay--;
                } else {
-                       if (forced_heater_time > 0) {
-                               forced_heater_time--;
+                       if (forced_heater_duration > 0) {
+                               forced_heater_duration--;
                        }
                }
        }
+       /* Update Over-Voltage information to exit over-voltage protection mode */
+       if (overvoltage > 0) {
+               uint8_t ov_tmp = (gpio_read(overvoltage_pin) ? 1 : 0);
+               if (ov_tmp == 0) {
+                       overvoltage--;
+               }
+       }
 }
 
 
@@ -117,7 +129,12 @@ void handle_dec_request(uint32_t curent_tick)
 /***************************************************************************** */
 /* AC control */
 
-volatile int act_cmd = 0; /* Start off */
+/* Handle the power command */
+volatile uint8_t act_cmd = 0; /* Start off */
+volatile uint8_t command_val = 0; /* Value displayed on interface */
+
+extern uint32_t power_delay[101];
+volatile uint32_t zc_cnt = 0;
 
 void set_ctrl_duty_cycle(uint8_t value)
 {
@@ -131,20 +148,33 @@ void set_ctrl_duty_cycle(uint8_t value)
        }
 }
 
-void ac_switch_on(uint32_t flags)
+/* Over-voltage protection */
+/* Number of DEC_PERIOD during which we keep the mosfets ON in order to keep them safe */
+#define OVERVOLTAGE_PROTECTION_CYCLES  100
+#define PROTECTION_CMD_VAL 100
+void overvoltage_protect(uint32_t gpio)
 {
-       gpio_clear(ac_ctrl);
+       gpio_clear(ac_ctrl); /* Turn mosfets ON */
+       timer_stop(LPC_TIMER_32B1); /* Stop mosfets timer */
+       set_ctrl_duty_cycle(PROTECTION_CMD_VAL); /* Update "actual command" */
+       overvoltage = OVERVOLTAGE_PROTECTION_CYCLES;
+       gpio_set(fan_ctrl); /* Turn on FAN immediatly */
+       /* Change current working mode */
+       old_mode = mode;
+       mode = mode_overvoltage;
 }
 
 
-extern uint32_t power_delay[101];
-volatile uint32_t zc_cnt = 0;
+/* Used as timer callback to turn ON mosfets */
+void ac_switch_on(uint32_t flags)
+{
+       gpio_clear(ac_ctrl);
+}
 
-/*
- * Zero crossing handler (either detected or fake one)
+/* Zero crossing handler (either detected or fake one)
  * If command is at 100%, only set (keep ?) Mosfets ON (gpio_clear)
  * Else, start with mosfets OFF, in order not to have to break established current.
- * FIXME : If U and I not in sync, we should sync to I nul, not V nul.
+ * FIXME : If U and I not in sync, we should sync to I nul, not U nul.
  */
 void zero_cross(uint32_t unused)
 {
@@ -178,17 +208,14 @@ void zero_cross_detect(uint32_t unused)
 }
 
 
-
-/* Handle the power command */
-volatile uint8_t command_val = 0;
-int8_t old_cmd = 0; /* Used to store cmd value when entering over-voltage protection mode */
+/***************************************************************************** */
+/* Update current command depending on current mode and power production and consumption */
 #define FAN_COUNTER_START_VAL (15 * 1000)
-static uint16_t fan_on = 0;
-uint8_t force_fan = 0;
+volatile uint16_t fan_on = 0;
+volatile uint8_t force_fan = 0;
 extern uint8_t test_force_fan; /* Request to force fan ON from test menu */
-#define PROTECTION_CMD_VAL 100
 
-static uint8_t linky_disable = 0;
+volatile uint8_t linky_test = 0;
 #define CMD_UP_DELAY_RESET_VALUE  3
 void handle_cmd_update(uint32_t curent_tick)
 {
@@ -196,135 +223,99 @@ void handle_cmd_update(uint32_t curent_tick)
        static uint8_t cmd_up_delay = CMD_UP_DELAY_RESET_VALUE;
        int delta = solar_prod_value - home_conso_value;
 
-       /* Forced over-voltage protection or test mode */
-       if (overvoltage != 0) {
-               cmd = PROTECTION_CMD_VAL;
-               goto cmd_update_end;
-       } else if (old_cmd != -1) {
-               /* Get back to normal after over-voltage protection */
-               cmd = old_cmd;
-               old_cmd = -1;
-       }
-
-       /* Reset force_fan to current test value */
-       force_fan = test_force_fan;
-
-       /* Mosfet temperature limit reached, turn ON all mosfets to prevent internal damage of board */
-       if (mosfet_temp_shutdown == 1) {
-               cmd = PROTECTION_CMD_VAL;
-               force_fan = 1;
-               goto cmd_update_end;
-       }
-
-       /* 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); /* 1.5 °C */
-               }
-               goto cmd_update_end;
-       } else if (water_centi_degrees < max_temp_hysteresys) {
-               /* Remove Hysteresys */
-               max_temp_hysteresys = ABSOLUTE_MAX_WATER_TEMP;
-       }
-
-       /* Unable to read internal temperature or temperature above max internal temp : turn off heating */
-       if (internal_temp_error_shutdown < 0) {
-               cmd = 0;
-               force_fan = 1;
-               goto cmd_update_end;
-       }
-
        /* Which is the current mode ? */
-       if (forced_heater_mode == 1) {
-               /* Forced heating mode */
-               if ((forced_heater_delay == 0) && (forced_heater_time > 0)) {
-                       cmd = sc_conf.auto_forced_mode_value;
-                       goto cmd_update_limited_end;
-               }
-               if (manual_forced_heater > 0) {
-                       cmd = sc_conf.manual_forced_mode_value;
-                       goto cmd_update_limited_end;
-               }
-               /* Entering forced heating mode from temperature getting below threshold,
-                * wait some delay before effective forced heating */
-               if (forced_heater_time == 0) {
-                       forced_heater_delay = sc_conf.auto_forced_heater_delay * T_MULT;
-                       forced_heater_time = forced_heater_delay + (sc_conf.auto_forced_heater_duration * T_MULT);
-               }
-       }
-       if (delta < -1280) {
-               /* Much more power used than produced */
-               if (cmd > 10) {
-                       cmd -= 10;
-               } else {
-                       cmd = 0;
-               }
-       } else if (delta < -64) {
-               /* More power used than produced */
-               if (cmd > 0) {
-                       cmd--;
-               }
-       } else if (delta > 1280) {
-               /* Much more production than used power */
-               if (cmd <= 98) {
-                       cmd += 2;
-               } else {
-                       cmd = 100;
-               }
-       } else if (delta > 192) {
-               /* More production, but do not rise too quickly when getting close
-                * to balance to prevent oscilations */
-               if (cmd_up_delay > 0) {
-                       cmd_up_delay--;
-               } else {
-                       cmd_up_delay = CMD_UP_DELAY_RESET_VALUE;
-                       if (cmd < 100) {
-                               cmd++;
+       switch (mode) {
+               case mode_overvoltage:
+                       /* End over-voltage protection ? */
+                       if (overvoltage == 0) {
+                               mode = old_mode;
+                       } else {
+                               cmd = PROTECTION_CMD_VAL;
                        }
-               }
-       }
-
-cmd_update_limited_end:
-       command_val = cmd;
-       /* Limit external power used when in force mode ! */
-       if (delta < -max_intensity) {
-               cmd = 0;
-               linky_disable |= 16;
-               if ((forced_heater_delay == 0) && (forced_heater_time > 0)) {
-                       /* Wait for 100 * 100ms (10s) before trying to heat again */
-                       forced_heater_delay = 100;
-               }
-               if (manual_forced_heater > 0) {
-                       /* Re-increment by one the delay */
-                       manual_activation_request++;
-               }
-       }
-       /* Test for Linky - FIXME */
-#if 0
-       linky_disable = 0;
-       if ((cmd > 48) && (cmd < 100)) {
-               if (water_centi_degrees > (50 * 100)) {
-                       if (delta > 6000) {
-                               linky_disable = 2;
-                               cmd = 100;
+                       break;
+               case mode_overtemp:
+                       /* End over-temperature protection ? */
+                       if ((mosfet_temp_shutdown == 0) && (internal_temp_error_shutdown >= 0)) {
+                               mode = old_mode;
                        } else {
-                               linky_disable = 4;
-                               cmd = 48;
+                               cmd = 0;
+                               force_fan = 1;
                        }
-               } else {
-                       linky_disable = 8;
-                       cmd = 100;
-               }
+                       break;
+               case mode_manual:
+               case mode_forced:
+                       /* Forced heating mode */
+                       if ((forced_heater_delay == 0) && (forced_heater_duration > 0)) {
+                               /* Limit external power used when in force mode ! */
+                               if (delta >= 0) {
+                                       cmd = forced_cmd_val;
+                               } else if (delta < -max_intensity) {
+                                       if (cmd >= 10) {
+                                               cmd -= 10;
+                                       } else {
+                                               cmd = 0;
+                                       }
+                               } else if (cmd < forced_cmd_val) {
+                                       if (cmd == 0) {
+                                               /* Start with a high enough value to avoid triggering overvoltage protection */
+                                               cmd = min(forced_cmd_val, 40);
+                                       } else {
+                                               cmd++;
+                                       }
+                               }
+                               break;
+                       }
+                       /* FALTHROUGH : automatic force delay period, try to use what is available anyway */
+               case mode_ext_disable:
+               case mode_delayed_heat_prod:
+                       /* automatic force disabled due to current production, try to use what is available */
+               case mode_heat:
+                       /* Set load power according to extra power production */
+                       if (delta < -1280) {
+                               /* Much more power used than produced */
+                               if (cmd > 10) {
+                                       cmd -= 10;
+                               } else {
+                                       cmd = 0;
+                               }
+                       } else if (delta < -64) {
+                               /* More power used than produced */
+                               if (cmd > 0) {
+                                       cmd--;
+                               }
+                       } else if (delta > 1280) {
+                               /* Much more production than used power */
+                               if (cmd == 0) {
+                                       /* Start with a high enough value to avoid triggering overvoltage protection */
+                                       cmd = 40;
+                               } else if (cmd <= 98) {
+                                       cmd += 2;
+                               } else {
+                                       cmd = 100;
+                               }
+                       } else if (delta > 192) {
+                               /* More production, but do not rise too quickly when getting close
+                                * to balance to prevent oscilations */
+                               if (cmd_up_delay > 0) {
+                                       cmd_up_delay--;
+                               } else {
+                                       cmd_up_delay = CMD_UP_DELAY_RESET_VALUE;
+                                       if (cmd < 100) {
+                                               cmd++;
+                                       }
+                               }
+                       }
+                       break;
+               case mode_temp_OK:
+               default:
+                       cmd = 0;
+                       break;
        }
-#endif
 
-cmd_update_end:
+       /* Reset force_fan to current test value */
+       force_fan |= test_force_fan;
        /* Update FAN command (start or stop FAN) */
-       if ((cmd > 0) || (force_fan == 1)) {
+       if ((cmd > 0) || force_fan) {
                fan_on = FAN_COUNTER_START_VAL;
                gpio_set(fan_ctrl);
        } else {
@@ -334,121 +325,169 @@ cmd_update_end:
                        gpio_clear(fan_ctrl);
                }
        }
-       //command_val = cmd;
+
+#if 0
+       /* Test for Linky - FIXME */
+       linky_test = 0;
+       if ((cmd > 48) && (cmd < 100)) {
+               if (water_centi_degrees > (50 * 100)) {
+                       linky_test = 2;
+                       cmd = 48;
+               } else {
+                       linky_test = 4;
+                       cmd = 100;
+               }
+       }
+#endif
+
+       /* Store command for display */
+       command_val = cmd;
        /* Set Control Output duty cycle */
        set_ctrl_duty_cycle(cmd);
 }
 
-/* Over-voltage protection */
-void overvoltage_protect(uint32_t gpio)
-{
-       gpio_clear(ac_ctrl); /* Turn mosfets ON */
-       timer_stop(LPC_TIMER_32B1); /* Stop mosfets timer */
-       set_ctrl_duty_cycle(PROTECTION_CMD_VAL); /* Update "actual command" */
-       overvoltage = OVERVOLTAGE_PROTECTION_CYCLES;
-       gpio_set(fan_ctrl); /* Turn on FAN immediatly */
-       old_cmd = command_val; /* Save current command value (to be restored after overvoltage protection */
-}
-
 
+/***************************************************************************** */
 /* Update current working mode depending on environnement */
-volatile char mode = mode_heat; /* Debug info */
+
+/* There's three possible scenario and a few limits which define the command value.
+ * Possible scenario by order of priority :
+ * - Manual forced mode :
+ *   This mode is entered when the user presses the "OK" button on the interface when the interface
+ *   is in mode "run", which sets manual_activation_request to 1
+ *   This mode lasts for as much as sc_conf.manual_activation_duration (hours) or until
+ *   sc_conf.manual_target_heater_temp is reached
+ *   Which target is used depends on sc_conf.manual_force_type.
+ *   In this mode, heating is made at sc_conf.manual_forced_mode_value, unless local energy
+ *   consumption goes beyond sc_conf.grid_power_limit
+ * - Automatic force mode :
+ *   This mode is entered when the water temperature goes below sc_conf.enter_forced_mode_temp
+ *   This mode lasts for as much as sc_conf.auto_forced_heater_duration (hours) or until
+ *   sc_conf.auto_forced_target_heater_temp is reached
+ *   Which target is used depends on sc_conf.auto_force_type.
+ *   In this mode, heating starts only after a delay of sc_conf.auto_forced_heater_delay (hours) in
+ *   order to wait for possible automatic heating from the local energy produced. This is usefull to
+ *   prevent heating while another appliance is being used for a short time, or right after the
+ *   morning showers while the local energy production system may produce enough energy during the
+ *   day to come.
+ *   Automatic heating can happen during the delay period, and in this case automatic force mode is
+ *   only delayed further if sc_conf.source_has_power_ok is reached by the local energy production
+ *   and exited only if sc_conf.auto_forced_target_heater_temp is reached by the water temperature.
+ *   In this mode, heating is made at sc_conf.auto_forced_mode_value unless local energy
+ *   consumption goes beyond sc_conf.grid_power_limit
+ * - Automatic heating :
+ *   In this mode (which is the main purpose of this module) heating is controlled by the difference
+ *   between local energy production and local energy consumption.
+ *
+ * Limits :
+ * - Water temperature :
+ *   There are two "software" temperature limits and one "hardware" limit.
+ *   The hardware limit comes from the water tank heating system and cannot be overriden.
+ *   It is unkown and irrelevant to this algorithm. It is the only real "security" limit.
+ *   The two other limits are software ones : one hard-coded upper limit of 90°C which is not
+ *   advertised to the user, and one configurable through sc_conf.conf_max_temp.
+ *   Reching these limits should stop heating completely, unless there's an overvoltage condition.
+ * - External disable switch :
+ */
+
 volatile char* msg = NULL;
 void mode_update(void)
 {
-       /* Default mode : try to heat the water tank */
-       mode = mode_heat;
-
-       /* Need to enter Forced heating mode ? */
-       if ((water_centi_degrees < sc_conf.enter_forced_mode_temp) && (sc_conf.auto_force_type != FORCE_TYPE_OFF)) {
-               if (forced_heater_mode == 0) {
-                       msg = "Water temp low, entering forced mode\n";
-                       forced_heater_mode = 1;
-               }
-               status_led(red_on);
-               mode = mode_forced;
-       } else if (forced_heater_mode == 1) {
-               int exit_forced = 0;
-               /* End of forced heating ? (target reached) ? */
-               switch (sc_conf.auto_force_type) {
-                       case FORCE_TYPE_TIMER:
-                       case FORCE_TYPE_TARGET:
-                               if (water_centi_degrees > sc_conf.auto_forced_target_heater_temp) {
-                                       exit_forced = 1;
-                               }
-                               break;
-                       case FORCE_TYPE_MIN:
-                               if (water_centi_degrees > sc_conf.auto_forced_target_heater_temp) {
-                                       exit_forced = 1;
-                               }
-                       case FORCE_TYPE_MAX:
-                               if (water_centi_degrees > sc_conf.auto_forced_target_heater_temp) {
-                                       exit_forced = 1;
-                               }
-               }
-               if (exit_forced == 1) {
-                       status_led(red_off);
-                       msg = "Water temp OK, forced mode exit\n";
-                       forced_heater_mode = 0;
-                       mode = mode_temp_OK;
-               } else {
-                       mode = mode_forced;
-               }
+       /* Check protections first */
+       if ((mode == mode_overvoltage) || (mode == mode_overtemp)) {
+               return;
        }
-
-       /* Do not force if there is a lot of sun, it may be enough to heat again soon
-        * 'sunny_days_prod_value' is computed from 'sc_conf.source_has_power_ok'
-        */
-       if ((solar_prod_value > sunny_days_prod_value) && (forced_heater_mode == 1)) {
-               mode = mode_delayed_heat_prod;
-               forced_heater_mode = 0;
+       /* Mosfet temperature limit reached or internal temperature above max internal temp */
+       if ((mosfet_temp_shutdown != 0) || (internal_temp_error_shutdown < 0)) {
+               old_mode = mode;
+               mode = mode_overtemp;
+               return;
        }
 
-       /* Do not force heating if disabled by external command */
-       external_disable = (gpio_read(ext_disable_in_pin) ? 0 : 1); /* Invert : input is pulled low when external disable is ON */
-       if ((external_disable == 1) && (forced_heater_mode != 0)) {
-               forced_heater_mode = 0;
-               mode = mode_ext_disable;
-               msg = "Forced mode disabled by external input\n";
+       /* Water temperature reached config limit ? */
+       if (water_centi_degrees >= sc_conf.conf_max_temp) {
+               mode = mode_temp_OK; /* Max temperature reached */
+               return;
        }
 
-       /* Get Over-Temperature information */
-       mosfet_temp_shutdown = (gpio_read(overtemperature_pin) ? 0 : 1); /* Invert : Temp switch is NC type */
-       if (mosfet_temp_shutdown != 0) {
-               mode = mode_overtemp;
-       }
+       /* Default mode : try to heat the water tank */
+       mode = mode_heat;
 
-       /* Update Over-Voltage information - Only used to exit over-voltage protection mode */
-       if (overvoltage > 0) {
-               uint8_t ov_tmp = (gpio_read(overvoltage_pin) ? 1 : 0);
-               if (ov_tmp == 0) {
-                       overvoltage--;
-               }
-               mode = mode_overvoltage;
+       /* Did the user request a forced heating ? */
+       if (manual_activation_request == 1) {
+               msg = "Entering manual forced mode\n";
+               forced_heater_mode = sc_conf.manual_force_type;
+               forced_target_temp = sc_conf.manual_target_heater_temp;
+               forced_heater_duration = sc_conf.manual_activation_duration * T_MULT;
+               forced_cmd_val = sc_conf.manual_forced_mode_value;
+               forced_heater_delay = 0;
+               manual_activation_request = 2;
        }
 
-       if (sc_conf.never_force == 1) {
-               forced_heater_mode = 0;
+       /* Do we need automatic forced heating ? */
+       if ((forced_heater_mode == FORCE_TYPE_OFF) && (water_centi_degrees < sc_conf.enter_forced_mode_temp)) {
+               forced_heater_mode = sc_conf.auto_force_type;
+               if (forced_heater_mode != FORCE_TYPE_OFF) {
+                       msg = "Water temp low, entering forced mode\n";
+                       forced_target_temp = sc_conf.auto_forced_target_heater_temp;
+                       forced_heater_duration = sc_conf.auto_forced_heater_duration * T_MULT;
+                       forced_cmd_val = sc_conf.auto_forced_mode_value;
+                       forced_heater_delay = sc_conf.auto_forced_heater_delay * T_MULT;
+               }
        }
 
-       /* Did the user request a forced heating ? */
-       if (manual_activation_request != 0) {
-               forced_heater_mode = 1;
-               mode = mode_manual;
-               if (manual_activation_request == -1) {
-                       msg = "Entering manual forced mode\n";
-                       manual_activation_request = sc_conf.manual_activation_duration * T_MULT;
-                       manual_forced_heater = 1;
+       /* Handle specific cases when forced heating is ON */
+       if (forced_heater_mode != FORCE_TYPE_OFF) {
+               int exit_forced = 0; /* Need to reach 2 to exit forced heating */
+               /* End of forced heating ? (target reached) ? */
+               if (water_centi_degrees > forced_target_temp) {
+                       if ((forced_heater_mode == FORCE_TYPE_MIN) || (forced_heater_mode == FORCE_TYPE_TARGET)) {
+                               msg = "Water temp OK, forced mode exit\n";
+                               exit_forced = 2;
+                       } else {
+                               exit_forced = 1;
+                       }
+               }
+               if (forced_heater_duration == 0) {
+                       if ((forced_heater_mode == FORCE_TYPE_MIN) || (forced_heater_mode == FORCE_TYPE_TIMER)) {
+                               msg = "Water heating timer elapsed, forced mode exit\n";
+                               exit_forced = 2;
+                       } else {
+                               exit_forced++;
+                       }
                }
-               if (manual_activation_request < 10) {
-                       msg = "Leaving manual forced mode\n";
+               if (exit_forced >= 2) {
+                       /* Exit condition reached */
+                       forced_heater_mode = FORCE_TYPE_OFF;
                        manual_activation_request = 0;
+                       mode = mode_heat;
+               } else {
+                       if (manual_activation_request == 2) {
+                               mode = mode_manual;
+                       } else if (solar_prod_value > sunny_days_prod_value) {
+                               /* Do not force if there is a lot of sun, it may be enough to heat again soon
+                                * 'sunny_days_prod_value' is computed from 'sc_conf.source_has_power_ok'
+                                */
+                               mode = mode_delayed_heat_prod;
+                               if (forced_heater_delay < 100) {
+                                       forced_heater_delay = 100; /* FIXME : add some time (N * DEC_PERIOD) to the delay value */
+                               }
+                       } else {
+                               mode = mode_forced;
+                       }
+               }
+               /* Do not force heating if disabled by external command */
+               if (external_disable == 1) {
+                       forced_heater_mode = FORCE_TYPE_OFF;
+                       mode = mode_ext_disable;
+                       msg = "Forced mode disabled by external input\n";
+               }
+               /* Do not force heating if disabled by configuration */
+               if (sc_conf.never_force == 1) {
+                       forced_heater_mode = FORCE_TYPE_OFF;
+                       mode = mode_heat;
+                       msg = "Forced mode disabled by configuration\n";
                }
-       }
-
-       /* Command at 100% and still more production than energy used ? */
-       if ((command_val == 100) && (solar_prod_value > home_conso_value)) {
-               mode = mode_overprod;
        }
 }
 
@@ -475,8 +514,9 @@ void store_data(uint32_t cur_tick)
 
        /* Each flag is set if seen once within the interval */
        data.flags |= (fan_on ? 1 : 0) | (force_fan << 1);
-       data.flags |= (((internal_temp_error_shutdown < 0) ? 1 : 0) << 2);
-       data.flags |= (mosfet_temp_shutdown << 3) | (overvoltage << 4);
+       data.flags |= ((internal_temp_error_shutdown < 0) ? (0x01 << 2) : 0);
+       data.flags |= (mosfet_temp_shutdown << 3);
+       data.flags |= ((overvoltage > 0) ? (0x01 << 4) : 0);
        data.flags |= (external_disable << 5);
        data.flags |= (forced_heater_mode << 6) | (manual_activation_request << 7);
 
@@ -499,7 +539,7 @@ void store_data(uint32_t cur_tick)
                data.deci_degrees_disp = data.deci_degrees_disp / nb_val;
                data.load_power_lowest = data.load_power_lowest / nb_val;
                data.load_power_highest = data.load_power_highest / nb_val;
-               /* Add "fixed ones */
+               /* Add "fixed" ones */
                data.command_val = command_val;
                data.act_cmd = act_cmd;
                data.mode = mode;
@@ -513,6 +553,7 @@ void store_data(uint32_t cur_tick)
        }
 }
 
+/* Send data on UEXT communication port for slave module or communication extention */
 void slave_send_data(void)
 {
        static struct scialys_data data;
@@ -539,6 +580,7 @@ void slave_send_data(void)
        comm_tx(&data);
 }
 
+
 /***************************************************************************** */
 int main(void)
 {
@@ -555,13 +597,18 @@ int main(void)
 
        scialys_systick_and_timers_config();
 
+       /* Changing load type is not allowed without turning the module off, so checking outside of main loop is OK */
+       if (sc_conf.load_type == LOAD_TYPE_DC) {
+               add_systick_callback(zero_cross_detect, 20);
+       }
+
        uprintf(UART0, "System setup complete.\n");
        msleep(100);
        status_led(green_only);
 
        while (1) {
                /* Feed the dog */
-               if ((solar_prod_value != 0) && (home_conso_value != 0)) {
+               if ((solar_prod_value != 0) && (home_conso_value != 0) && (water_centi_degrees < ABSOLUTE_MAX_WATER_TEMP)) {
                        watchdog_feed();
                }
 
@@ -571,14 +618,20 @@ int main(void)
                /* Update water tank temperature */
                water_centi_degrees = water_temp_update(UART0);
 
+               /* Get Over-Temperature information */
+               mosfet_temp_shutdown = (gpio_read(overtemperature_pin) ? 0 : 1); /* Invert : Temp switch is NC type */
+
+               /* Read external disable pin */
+               external_disable = (gpio_read(ext_disable_in_pin) ? 0 : 1); /* Invert : input is pulled low when external disable is ON */
+
                /* Update current mode */
                mode_update();
 
                /* Display */
                /* only update twice per second */
                cur_tick = systick_get_tick_count();
-               if ((cur_tick < last_tick_update) || ((~0 - last_tick_update) < 500)) {
-                       /* simple tick wrapping handling */
+               /* simple tick wrapping handling */
+               if (cur_tick < last_tick_update) {
                        last_tick_update = 0;
                }
                if (cur_tick > (last_tick_update + 500)) {
@@ -594,7 +647,7 @@ int main(void)
 
                /* Data Log */
                if (1) {
-                       external_disable |= linky_disable;
+                       external_disable |= linky_test;
                        uprintf(UART0, "#%c:%d:%d:%d:%d:%d:%d:",
                                                        mode, loop++, solar_prod_value, home_conso_value,
                                                        water_centi_degrees, deci_degrees_power, deci_degrees_disp);
index c6d1575..07f4504 100644 (file)
@@ -1,3 +1,3 @@
 #define MODULE_VERSION_STR  "v0.10.2"
 #define SOFT_VERSION_STR  "T"
-#define COMPILE_VERSION "75"
+#define COMPILE_VERSION "76"