/***************************************************************************** */
/* System configuration */
-/* Period (in ms) of the decrementer handler from the systick interrupt */
-#define DEC_PERIOD 100
-/* Period (in ms) of the handler for the command value update */
-#define CMD_UPD_PERIOD 50
-#define ADC_UPD_PERIOD 17
/* This multiplier is used for all durations and delays which are stored in number of hours */
#define T_MULT (3600 * 1000 / DEC_PERIOD)
#define OVERVOLTAGE_PROTECTION_CYCLES 100
-uint8_t forced_heater_mode = 0; /* flag */
-uint32_t forced_heater_delay = 0;
-uint32_t forced_heater_time = 0;
-uint8_t manual_forced_heater = 0; /* flag */
-int manual_activation_request = 0;
-
-
-uint8_t error_shutdown = 0; /* flag */
-
-
+/* Flags and counters */
+uint8_t forced_heater_mode = 0; /* Flag only */
+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 */
+uint8_t error_shutdown = 0; /* Flag only */
#define EXTERNAL_DISABLE_FORCE 0 /* Input is pulled low when external disable is ON */
-uint8_t external_disable = 0;
-uint8_t temp_shutdown = 0;
-uint8_t overvoltage = 0; /* Used to create a delay when overvoltage is detected,
+uint8_t external_disable = 0; /* Flag only */
+uint8_t temp_shutdown = 0; /* Flag only */
+uint8_t overvoltage = 0; /* Flag and counter. Used to create a delay when overvoltage is detected,
set to OVERVOLTAGE_PROTECTION_CYCLES and decreases to 0 */
enum modes {
temp_OK = 'T', /* Max temperature reached */
};
-/* Water and internaltemperature */
+/* Water temperature */
int water_centi_degrees = 0;
/* Set to ABSOLUTE_MAX_WATER_TEMP - 1.5°C when enbling max temp hysteresis */
struct rtc_time now;
-/***************************************************************************** */
-/* Rx interrupt handler for system configuration over USB */
-void config_rx(uint8_t c)
-{
- /* FAN control */
- if (c == 'f') {
- gpio_set(fan_ctrl);
- } else {
- gpio_clear(fan_ctrl);
- }
-}
+/***************************************************************************** */
+/* Decrementer for forced heating modes timers */
-/* Communication with slave modules over UART1 */
-void comm_rx(uint8_t c)
+void handle_dec_request(uint32_t curent_tick)
{
-}
-
-
-
-/***************************************************************************** */
-/* Decrementer for heating timers */
-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) {
if (forced_heater_delay > 0) {
forced_heater_delay--;
/***************************************************************************** */
-/* Track power production and usage */
+/* Power Tracking */
-/* Average value computed on last NB_VAL ADC values */
+/* Average value computed on the last "NB_VAL" ADC values */
uint32_t solar_prod_value = 0;
uint32_t home_conso_value = 0;
#define NB_VAL 4 /* MUST be a power of 2 */
#define NB_SHIFT 2
-static void track_isnail_values(uint32_t flags)
+/*
+ * Track power production and usage
+ * These are obtained by reading the Tore outputs (i-Snail output through ADC 0 and 1)
+ */
+void track_isnail_values(uint32_t curent_tick)
{
static uint8_t idx = 0;
static uint16_t solar[NB_VAL];
home_conso_value = sum_home << (6 - NB_SHIFT);
}
+
+/*
+ * Track power used by load.
+ * These are obtained from ACS711 through ADC 2
+ * ADC values are between 0 and 1023, no load value is at about 520
+ */
+#define TRACK_MAX 1
+#define TRACK_MIN 0
+static volatile uint16_t load_power_highest = 520;
+static volatile uint16_t load_power_lowest = 520;
+
+void power_track(uint32_t flags)
+{
+ /* Start tracking the higest value */
+ static int tracking = TRACK_MAX;
+ static uint16_t last_value = 0;
+ uint16_t val = 0;
+
+ /* Get load power usage */
+ adc_get_value(&val, LPC_ADC(2));
+
+ /* Four possibilities :
+ * tracking max and value increasing : nothing to do
+ * tracking max and value decreasing : we found the max, store it and change tracking
+ * tracking min and value decreasing : nothing to do
+ * tracking min and value increasing : we found the min, store it and change tracking
+ */
+ if (tracking == TRACK_MAX) {
+ if (last_value > val) {
+ /* Found max */
+ load_power_highest = last_value;
+ tracking = TRACK_MIN;
+ }
+ } else {
+ if (last_value < val) {
+ /* Found min */
+ load_power_lowest = last_value;
+ tracking = TRACK_MAX;
+ }
+ }
+ last_value = val;
+}
+
+
/***************************************************************************** */
/* AC control */
-volatile uint8_t command_val = 0;
volatile int act_cmd = 0; /* Start off */
-int8_t force_cmd = -1; /* Forced command value, for tests */
-int8_t old_cmd = 0; /* Used to store cmd value when entering over-voltage protection mode */
-#define FAN_CNT_START (15 * 1000)
-static uint16_t fan_on = 0;
-uint8_t force_fan = 0; /* Request to force fan ON from test menu */
-
void set_ctrl_duty_cycle(uint8_t value)
{
gpio_clear(ac_ctrl);
}
-static uint32_t clk_cycles_ac_zc = 0;
+
extern uint32_t power_delay[101];
volatile uint32_t zc_cnt = 0;
+/*
+ * 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.
+ */
void zero_cross(uint32_t unused)
{
uint32_t delay = 0;
zc_cnt++;
+ /* Command at 100% ? set Mosfets ON and return. */
if (act_cmd == 100) {
gpio_clear(ac_ctrl);
return;
}
+ /* Set Mosfet OFF */
gpio_set(ac_ctrl);
+ /* Command at 0% ? leave Mosfets OFF (do not start timer) and return. */
if (act_cmd == 0) {
return;
}
/* Set timer to trigger ac out ON at given delay */
- if (0) {
- /* Time based delay */
- delay = clk_cycles_ac_zc * (100 - act_cmd);
- } else {
- /* Power based delay */
- delay = power_delay[100 - act_cmd];
- }
+ delay = power_delay[100 - act_cmd];
timer_set_match(LPC_TIMER_32B1, CHAN0, delay);
timer_restart(LPC_TIMER_32B1);
}
/* This one is used to synchronize to the real zero-crossing detection, if any. */
void zero_cross_detect(uint32_t unused)
{
+ /* Prepare to generate the fake second zero crossing */
timer_restart(LPC_TIMER_32B0);
+ /* And call zero cross handler for this one */
zero_cross(0);
}
+
+
/* 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 */
+#define FAN_COUNTER_START_VAL (15 * 1000)
+static uint16_t fan_on = 0;
+uint8_t force_fan = 0; /* Request to force fan ON from test menu */
+#define PROTECTION_CMD_VAL 100
+
static uint8_t linky_disable = 0;
#define CMD_UP_DELAY_RESET_VALUE 3
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;
- /* Unable to read internal temperature : turn off heating */
- if (error_shutdown == 1) {
- cmd = 0;
- force_fan = 1;
+ /* 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;
}
/* Water max temperature protection with hysteresys */
max_temp_hysteresys = ABSOLUTE_MAX_WATER_TEMP;
}
- /* Forced over-voltage protection or test mode */
- if (force_cmd != -1) {
- cmd = force_cmd;
+ /* Unable to read internal temperature : turn off heating */
+ if ((temp_shutdown == 1) || (error_shutdown == 1)) {
+ cmd = 0;
+ force_fan = 1;
goto cmd_update_end;
- } else if (old_cmd != -1) {
- /* Get back to normal after over-voltage protection */
- cmd = old_cmd;
- old_cmd = -1;
}
/* Which is the current mode ? */
}
cmd_update_end:
+ /* Update FAN command (start or stop FAN) */
if ((cmd > 0) || (force_fan == 1)) {
- fan_on = FAN_CNT_START;
+ fan_on = FAN_COUNTER_START_VAL;
gpio_set(fan_ctrl);
} else {
if (fan_on > 0) {
/* Over-voltage protection */
void overvoltage_protect(uint32_t gpio)
{
- gpio_clear(ac_ctrl);
- timer_stop(LPC_TIMER_32B1);
- set_ctrl_duty_cycle(100);
- force_cmd = 100;
+ gpio_clear(ac_ctrl); /* Turn mosfets ON */
+ timer_stop(LPC_TIMER_32B1); /* Stop mosfets timer */
+ set_ctrl_duty_cycle(100); /* Update "actual command" */
overvoltage = OVERVOLTAGE_PROTECTION_CYCLES;
- gpio_set(fan_ctrl);
- old_cmd = command_val;
+ 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 = heat; /* Debug info */
volatile char* msg = NULL;
-void mode_update(uint32_t unused)
+void mode_update(void)
{
/* Default mode : try to heat the water tank */
mode = heat;
/* Need to enter Forced heating mode ? */
- if (water_centi_degrees < sc_conf.enter_forced_mode_temp) {
+ 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 = forced;
- } 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;
- mode = temp_OK;
+ } 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:
+ case FORCE_TYPE_MIN:
+ case FORCE_TYPE_MAX:
+ if ((water_centi_degrees > sc_conf.auto_forced_target_heater_temp) && ()) {
+ }
+ }
+ if (exit_forced == 1) {
+ status_led(red_off);
+ msg = "Water temp OK, forced mode exit\n";
+ forced_heater_mode = 0;
+ mode = temp_OK;
+ }
}
- /* Do not force if there is a lot of sun, it may be enough to heat again soon */
+ /* 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 = delayed_heat_prod;
forced_heater_mode = 0;
mode = ext_disable;
msg = "Forced mode disabled by external input\n";
}
- /* Get Over-Voltage or Over-Temperature information */
+
+ /* Get Over-Temperature information */
temp_shutdown = (gpio_read(overtemperature_pin) ? 0 : 1);
+ /* Update Over-Voltage information */
if (overvoltage > 0) {
uint8_t ov_tmp = (gpio_read(overvoltage_pin) ? 0 : 1);
if (ov_tmp == 0) {
overvoltage--;
- if (overvoltage == 0) {
- /* Get back to normal */
- force_cmd = -1;
- }
}
}
}
/***************************************************************************** */
-/* Power tracking - load part */
-/* ADC values are between 0 and 1023, no load value is at about 520 */
-#define TRACK_MAX 1
-#define TRACK_MIN 0
-static volatile uint16_t load_power_highest = 520;
-static volatile uint16_t load_power_lowest = 520;
-void power_track(uint32_t flags)
+/* Add a false zero-cross, but make sure to do it only once and only if we are not AC powered */
+void DC_switch_start(void)
{
- /* Start tracking the higest value */
- static int tracking = TRACK_MAX;
- static uint16_t last_value = 0;
- uint16_t val = 0;
-
- /* Get load power usage */
- adc_get_value(&val, LPC_ADC(2));
-
- /* Four possibilities :
- * tracking max and value increasing : nothing to do
- * tracking max and value decreasing : we found the max, store it and change tracking
- * tracking min and value decreasing : nothing to do
- * tracking min and value increasing : we found the min, store it and change tracking
- */
- if (tracking == TRACK_MAX) {
- if (last_value > val) {
- /* Found max */
- load_power_highest = last_value;
- tracking = TRACK_MIN;
- }
+ static int already_done = 0;
+ if ((already_done == 0) && (zc_cnt == 0)) {
+ int ret = add_systick_callback(zero_cross, 10); /* 2 zero-crossing at 50Hz -> 100Hz -> 10ms */
+ uprintf(UART0, "Entering forced DC mode on systick callback %d\n", ret);
+ already_done = 1;
} else {
- if (last_value < val) {
- /* Found min */
- load_power_lowest = last_value;
- tracking = TRACK_MAX;
- }
+ uprintf(UART0, "DC forced mode not available. On: %d, Zc: %d\n", already_done, zc_cnt);
}
- last_value = val;
}
/***************************************************************************** */
-void scialys_systick_and_timers_config(void)
+void update_internal_temp(void)
{
- /* We want 100 Hz (50 Hz but two zero crossings) with 1% granularity */
- clk_cycles_ac_zc = get_main_clock() / (100 * 100);
- uprintf(UART0, "clk_cycles_ac_zc: %d\n", clk_cycles_ac_zc);
-
- /* 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 */
- timer_set_match(LPC_TIMER_32B0, CHAN0, (clk_cycles_ac_zc * 100));
-
- /* Configure the power tracking timer.
- * 20 points per half sin wave should be enough to get the min and max */
- timer_set_match(LPC_TIMER_16B0, CHAN0, (clk_cycles_ac_zc * 5));
- timer_start(LPC_TIMER_16B0);
+ /* Get internal temperatures */
+ ret = temp_read(UART0, &deci_degrees_disp, &deci_degrees_power);
+ if (ret != CONV_OK) {
+ force_fan = 1;
+ }
+ /* Internal temperature protection */
+ if ((deci_degrees_power > MAX_INTERNAL_TEMP) || (deci_degrees_disp > MAX_INTERNAL_TEMP)) {
+ error_shutdown = 1;
+ uprintf(UART0, "Error shutdown !\n");
+ } else if ((deci_degrees_power < ((MAX_INTERNAL_TEMP) / 2)) &&
+ (deci_degrees_disp < ((MAX_INTERNAL_TEMP) / 2))) {
+ error_shutdown = 0;
+ }
+}
- /* Add a systick callback to handle command value update */
- add_systick_callback(handle_cmd_update, CMD_UPD_PERIOD);
- /* Add a systick callback to track ismail values (power consumption and production) */
- add_systick_callback(track_isnail_values, ADC_UPD_PERIOD);
+/* Store data on uSD once every N seconds.
+ * Note : Once every second requires about 1Go per year.
+ */
+#define NB_SEC_STORE 5
+void store_data(uint32_t cur_tick)
+{
+ static uint32_t last_tick_store = 0;
+ static struct sd_data_blob data;
+ static uint8_t nb_val = 0;
- /* Add a systick callback to handle time counting */
- add_systick_callback(handle_dec_request, DEC_PERIOD);
-}
+ if (nb_val == 0) {
+ memset(&data, 0, sizeof(struct sd_data_blob));
+ }
+ /* simple tick wrapping handling */
+ if ((cur_tick < last_tick_store) || ((~0 - last_tick_store) < (NB_SEC_STORE * 1000))) {
+ last_tick_store = 0;
+ }
-/* Add a false zero-cross, but make sure to do it only once and only if we are not AC powered */
-void DC_switch_start(void)
-{
- static int already_done = 0;
- if ((already_done == 0) && (zc_cnt == 0)) {
- int ret = add_systick_callback(zero_cross, 10); /* 2 zero-crossing at 50Hz -> 100Hz -> 10ms */
- uprintf(UART0, "Entering forced DC mode on systick callback %d\n", ret);
- already_done = 1;
- } else {
- uprintf(UART0, "DC forced mode already ON: %d/%d\n", already_done, zc_cnt);
+ /* Each flag is set if seen once within the interval */
+ data.flags |= (fan_on ? 1 : 0) | (force_fan << 1);
+ data.flags |= (error_shutdown << 2) | (temp_shutdown << 3) | (overvoltage << 4);
+ data.flags |= (external_disable << 5);
+ data.flags |= (forced_heater_mode << 6) | (manual_activation_request << 7);
+
+ /* Add current value to the storage value */
+ /* FIXME */
+ nb_val++;
+
+ if (cur_tick > (last_tick_store + (NB_SEC_STORE * 1000))) {
+ /* Divide by the number of values */
+ /* FIXME */
+ scialys_uSD_append_data(&data);
+ uprintf(UART0, "Saved 5s data\n");
+ nb_val = 0;
+ last_tick_store = cur_tick;
}
}
/* Order is important here */
system_init();
- msleep(800); /* Required for uSD to stabilise before SPI is setup .... */
board_io_config();
modules_config();
external_components_config(UART0);
scialys_systick_and_timers_config();
uprintf(UART0, "System setup complete.\n");
- uprintf(UART0, "Max intensity: %d\n", max_intensity);
- msleep(500);
+ msleep(100);
status_led(green_only);
while (1) {
/* Feed the dog */
- if ((solar_prod_value != 0) && (home_conso_value != 0) &&
- (solar_prod_value < 45000) && (home_conso_value < 45000) ) {
- /* FIXME : Make max solar prod and home conso a parameter */
+ if ((solar_prod_value != 0) && (home_conso_value != 0)) {
watchdog_feed();
}
/* Update internal temperatures */
- if (1) {
- /* Get internal temperatures */
- ret = temp_read(UART0, &deci_degrees_disp, &deci_degrees_power);
- if (ret != CONV_OK) {
- if ((ret & CONV_POWER_OK) == 0) {
- force_fan = 1;
- }
- if ((ret & CONV_DISPLAY_OK) == 0) {
- force_fan = 1;
- }
- }
- /* Internal temperature protection */
- if ((deci_degrees_power > MAX_INTERNAL_TEMP) || (deci_degrees_disp > MAX_INTERNAL_TEMP)) {
- error_shutdown = 1;
- uprintf(UART0, "Error shutdown !\n");
- } else if ((deci_degrees_power < ((MAX_INTERNAL_TEMP) / 2)) &&
- (deci_degrees_disp < ((MAX_INTERNAL_TEMP) / 2))) {
- error_shutdown = 0;
- }
- }
+ update_internal_temp();
/* 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) {
- 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);
- } 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;
- }
+ water_centi_degrees = water_temp_update();
/* Update current mode */
- mode_update(0);
+ mode_update();
/* Display */
/* only update twice per second */
last_tick_update = cur_tick;
}
- /* Store data on uSD once every N seconds.
- * Note : Once every second requires about 1Go per year.
- */
- #define NB_SEC_STORE 5
- if (1) {
- static uint32_t last_tick_store = 0;
- static struct sd_data_blob data;
- static uint8_t nb_val = 0;
-
- if (nb_val == 0) {
- memset(&data, 0, sizeof(struct sd_data_blob));
- }
- /* simple tick wrapping handling */
- if ((cur_tick < last_tick_store) || ((~0 - last_tick_store) < (NB_SEC_STORE * 1000))) {
- last_tick_store = 0;
- }
-
- /* Each flag is set if seen once within the interval */
- data.flags |= (fan_on ? 1 : 0) | (force_fan << 1);
- data.flags |= (error_shutdown << 2) | (temp_shutdown << 3) | (overvoltage << 4);
- data.flags |= (external_disable << 5);
- data.flags |= (forced_heater_mode << 6) | (manual_activation_request << 7);
-
- /* Add current value to the storage value */
- /* FIXME */
- nb_val++;
-
- if (cur_tick > (last_tick_store + (NB_SEC_STORE * 1000))) {
- /* Divide by the number of values */
- /* FIXME */
- scialys_uSD_append_data(&data);
- uprintf(UART0, "Saved 5s data\n");
- nb_val = 0;
- last_tick_store = cur_tick;
- }
- }
+ /* Store data on uSD once every N seconds. */
+ store_data(cur_tick);
/* Debug */
if (1) {