Add TOR slave module support (alpha v0.1 version)
authorNathael Pajani <nathael.pajani@ed3l.fr>
Fri, 24 Nov 2023 23:02:32 +0000 (00:02 +0100)
committerNathael Pajani <nathael.pajani@ed3l.fr>
Fri, 24 Nov 2023 23:09:31 +0000 (00:09 +0100)
17 files changed:
tor/.gitignore [new file with mode: 0644]
tor/Makefile [new file with mode: 0644]
tor/comm.c [new file with mode: 0644]
tor/comm.h [new file with mode: 0644]
tor/config.c [new file with mode: 0644]
tor/config.h [new file with mode: 0644]
tor/data.h [new file with mode: 0644]
tor/interface.c [new file with mode: 0644]
tor/interface.h [new file with mode: 0644]
tor/main.c [new file with mode: 0644]
tor/param.c [new file with mode: 0644]
tor/param.h [new file with mode: 0644]
tor/sensors.c [new file with mode: 0644]
tor/sensors.h [new file with mode: 0644]
tor/time.c [new file with mode: 0644]
tor/time.h [new file with mode: 0644]
tor/version.h [new file with mode: 0644]

diff --git a/tor/.gitignore b/tor/.gitignore
new file mode 100644 (file)
index 0000000..6361591
--- /dev/null
@@ -0,0 +1,2 @@
+dump
+logs
diff --git a/tor/Makefile b/tor/Makefile
new file mode 100644 (file)
index 0000000..d3cc3d6
--- /dev/null
@@ -0,0 +1,26 @@
+# Makefile for apps
+
+MODULE = $(shell basename $(shell cd .. && pwd && cd -))
+NAME = $(shell basename $(CURDIR))
+
+# Add this to your ~/.vimrc in order to get proper function of :make in vim :
+# let $COMPILE_FROM_IDE = 1
+ifeq ($(strip $(COMPILE_FROM_IDE)),)
+       PRINT_DIRECTORY = --no-print-directory
+else
+       PRINT_DIRECTORY =
+       LANG = C
+endif
+
+.PHONY: $(NAME).bin
+$(NAME).bin:
+       @make -C ../../.. ${PRINT_DIRECTORY} NAME=$(NAME) MODULE=$(MODULE) apps/$(MODULE)/$(NAME)/$@
+
+clean mrproper:
+       @make -C ../../.. ${PRINT_DIRECTORY} $@
+
+appclean:
+       rm ../../../objs/apps/$(MODULE)/$(NAME)/* $(NAME).bin $(NAME).elf
+
+temp:
+       @$(shell ../../../update_version.sh && touch interface.c)
diff --git a/tor/comm.c b/tor/comm.c
new file mode 100644 (file)
index 0000000..a3a1c89
--- /dev/null
@@ -0,0 +1,85 @@
+/****************************************************************************
+ *   apps/scialys/tor/comm.c
+ *
+ * Copyright 2016-2023 Nathael Pajani <nathael.pajani@ed3l.fr>
+ *
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ *************************************************************************** */
+
+#include "core/system.h"
+#include "core/systick.h"
+
+#include "drivers/serial.h"
+
+#include "comm.h"
+
+
+/***************************************************************************** */
+/* Rx interrupt handler for system configuration over USB */
+void config_rx(uint8_t c)
+{
+}
+
+
+/***************************************************************************** */
+/* Rx interrupt handler for communication with master scialys module over UART1 */
+
+volatile uint8_t comm_ok = 0;
+volatile uint8_t packet_valid = 0;
+volatile uint8_t data[MSG_LEN];
+
+void comm_rx(uint8_t c)
+{
+       static uint8_t data_idx = 0;
+
+       /* Start of packet */
+       if (data_idx == 0) {
+               if (c == '#') {
+                       data[0] = c;
+                       data_idx = 1;
+               }
+               comm_ok = 1;
+               return;
+       }
+       if (data_idx > 0) {
+               data[data_idx++] = c;
+               comm_ok = 1;
+               /* Full header received, check for header validity */
+               if (data_idx == 4) {
+                       uint8_t sum = data[0] + data[1] + data[2] + data[3];
+                       if (sum != 0) {
+                               data_idx = 0;
+                               return;
+                       }
+               }
+               /* Full message received, validate data checksum */
+               if (data_idx >= MSG_LEN) {
+                       int i = 0;
+                       uint8_t sum = 0;
+                       for (i = 4; i < MSG_LEN; i++) {
+                               sum += data[i];
+                       }
+                       data_idx = 0;
+                       if (sum == data[3]) {
+                               packet_valid = 1;
+                       }
+               }
+       }
+}
+
+
+
+
diff --git a/tor/comm.h b/tor/comm.h
new file mode 100644 (file)
index 0000000..0e9d409
--- /dev/null
@@ -0,0 +1,39 @@
+/****************************************************************************
+ *   apps/scialys/tor/comm.h
+ *
+ * Copyright 2016-2023 Nathael Pajani <nathael.pajani@ed3l.fr>
+ *
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ *************************************************************************** */
+
+#ifndef COMM_H
+#define COMM_H
+
+
+/* Rx interrupt handler for system configuration over USB */
+void config_rx(uint8_t c);
+
+
+/* Communication with master scialys module over UART1 */
+#define MSG_LEN  32
+extern volatile uint8_t comm_ok;
+extern volatile uint8_t packet_valid;
+extern volatile uint8_t data[MSG_LEN];
+void comm_rx(uint8_t c);
+
+
+#endif /* COMM_H */
+
diff --git a/tor/config.c b/tor/config.c
new file mode 100644 (file)
index 0000000..00b2053
--- /dev/null
@@ -0,0 +1,416 @@
+/****************************************************************************
+ *   apps/scialys/tor/config.c
+ *
+ * Copyright 2016-2023 Nathael Pajani <nathael.pajani@ed3l.fr>
+ *
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ *************************************************************************** */
+
+#include "core/system.h"
+#include "core/systick.h"
+#include "core/pio.h"
+
+#include "drivers/serial.h"
+#include "drivers/gpio.h"
+#include "drivers/i2c.h"
+
+
+#include "extdrv/status_led.h"
+#include "extdrv/tmp101_temp_sensor.h"
+
+#include "core/user_information_block.h"
+#include "core/iap.h"
+
+#include "config.h"
+#include "interface.h"
+#include "comm.h"
+#include "sensors.h"
+#include "time.h"
+
+
+
+/***************************************************************************** */
+/* Pins configuration */
+/* This pins definition block is passed to set_pins() for pins configuration.
+ */
+const struct pio_config common_pins[] = {
+       /* UART 0 */
+       { LPC_UART0_RX_PIO_0_1,  LPC_IO_DIGITAL },
+       { LPC_UART0_TX_PIO_0_2,  LPC_IO_DIGITAL },
+       /* UART 1 */
+       { LPC_UART1_RX_PIO_0_8,  LPC_IO_DIGITAL },
+       { LPC_UART1_TX_PIO_0_9,  LPC_IO_DIGITAL },
+       /* I2C 0 */
+       { LPC_I2C0_SCL_PIO_0_10, (LPC_IO_DIGITAL | LPC_IO_OPEN_DRAIN_ENABLE) },
+       { LPC_I2C0_SDA_PIO_0_11, (LPC_IO_DIGITAL | LPC_IO_OPEN_DRAIN_ENABLE) },
+       /* GPIO */
+       { LPC_GPIO_0_0, LPC_IO_DIGITAL },  /* Relay 2 off */
+       { LPC_GPIO_0_3, LPC_IO_DIGITAL },  /* Relay 1 on */
+       { LPC_GPIO_0_4, LPC_IO_DIGITAL },  /* Relay 1 off */
+       { LPC_GPIO_0_5, LPC_IO_DIGITAL },  /* Relay 3 on */
+       { LPC_GPIO_0_6, LPC_IO_DIGITAL },  /* Relay 3 off */
+       { LPC_GPIO_0_12, LPC_IO_DIGITAL }, /* ISP / User button OK */
+       { LPC_GPIO_0_20, LPC_IO_DIGITAL }, /* RGB Leds */
+       { LPC_GPIO_0_21, LPC_IO_DIGITAL }, /* Oled Reset */
+       { LPC_GPIO_0_28, LPC_IO_DIGITAL }, /* Relay 2 on */
+       { LPC_GPIO_1_2, LPC_IO_DIGITAL },  /* Button 4 */
+       { LPC_GPIO_1_3, LPC_IO_DIGITAL },  /* Button 3 */
+       { LPC_GPIO_1_4, LPC_IO_DIGITAL },  /* Button 2 */
+       { LPC_GPIO_1_5, LPC_IO_DIGITAL },  /* Button 1 */
+       ARRAY_LAST_PIO,
+};
+
+/* Internal status leds */
+const struct pio status_light_green = LPC_GPIO_0_26;
+const struct pio status_light_red = LPC_GPIO_0_27;
+
+/* Outputs */
+const struct pio relay1_off_pin = LPC_GPIO_0_4;
+const struct pio relay1_on_pin = LPC_GPIO_0_3;
+const struct pio relay2_off_pin = LPC_GPIO_0_0;
+const struct pio relay2_on_pin = LPC_GPIO_0_28;
+const struct pio relay3_off_pin = LPC_GPIO_0_6;
+const struct pio relay3_on_pin = LPC_GPIO_0_5;
+
+
+
+/***************************************************************************** */
+/* Basic system init and configuration */
+static volatile int got_wdt_int = 0;
+void wdt_callback(void)
+{
+       got_wdt_int = 1;
+}
+
+const struct wdt_config wdconf = {
+       .clk_sel = WDT_CLK_IRC,
+       .intr_mode_only = 0,
+       .callback = wdt_callback,
+       .locks = 0,
+       .nb_clk = 0x03FFFFFF, /* 0x3FF to 0x03FFFFFF */
+       .wdt_window = 0,
+       .wdt_warn = 0x3FF,
+};
+
+void system_init()
+{
+       /* Configure the Watchdog */
+       watchdog_config(&wdconf);
+       system_set_default_power_state();
+       clock_config(SELECTED_FREQ);
+       set_pins(common_pins);
+       gpio_on();
+       status_led_config(&status_light_green, &status_light_red);
+       /* System tick timer MUST be configured and running in order to use the sleeping
+        * functions */
+       systick_timer_on(1); /* 1ms */
+       systick_start();
+}
+
+/* Define our fault handler. This one is not mandatory, the dummy fault handler
+ * will be used when it's not overridden here.
+ * Note : The default one does a simple infinite loop. If the watchdog is deactivated
+ * the system will hang.
+ */
+void fault_info(const char* name, uint32_t len)
+{
+       uprintf(UART0, name);
+       while (1);
+}
+
+
+/***************************************************************************** */
+/* GPIO */
+
+void board_io_config(void)
+{
+       /* Immediatly turn off relay commands */
+       config_gpio(&relay1_on_pin, 0, GPIO_DIR_OUT, 1);
+       config_gpio(&relay1_off_pin, 0, GPIO_DIR_OUT, 1);
+       config_gpio(&relay2_on_pin, 0, GPIO_DIR_OUT, 1);
+       config_gpio(&relay2_off_pin, 0, GPIO_DIR_OUT, 1);
+       config_gpio(&relay3_on_pin, 0, GPIO_DIR_OUT, 1);
+       config_gpio(&relay3_off_pin, 0, GPIO_DIR_OUT, 1);
+
+}
+
+
+/***************************************************************************** */
+/* Internal modules */
+
+extern void config_rx(uint8_t c);
+extern void comm_rx(uint8_t c);
+
+/* Configure micro-controller internal modules */
+void modules_config(void)
+{
+       uart_on(UART0, 115200, config_rx);
+       uart_on(UART1, 115200, comm_rx);
+       i2c_on(I2C0, I2C_CLK_100KHz, I2C_MASTER);
+}
+
+
+extern void handle_dec_request(uint32_t curent_tick);
+
+/* Add systick callbacks */
+void scialys_tor_systick_config(void)
+{
+       /* Add a systick callback to handle time counting */
+       add_systick_callback(handle_dec_request, DEC_PERIOD);
+}
+
+
+/***************************************************************************** */
+/* External components */
+
+uint8_t interface_board_present = 0;
+
+/* RTC and time */
+#define RTC_ADDR   0xA2
+
+
+/* Configure external components */
+int external_components_config(uint32_t uart)
+{
+       /* RTC : must be done first to be able to access RTC RAM for uSD init */
+       scialys_time_config(I2C0, RTC_ADDR);
+       scialys_time_init_check(uart);
+
+       /* TMP101 sensor config on display board */
+       interface_board_present = interface_board_temp_sensor_config(uart);
+
+       /* Configure interface board if present */
+       if (interface_board_present == 1) {
+               interface_config(uart);
+       }
+
+       return 0;
+}
+
+
+/***************************************************************************** */
+/* App config and/or default values :
+ * Load or update user configuration from internal User Flash */
+
+/* The configuration is stored in the first user info page.
+ * The third user info page is used to store the "last used" uSD block only once per
+ *  week in order to prevent deterioration of the flash block. This information is
+ *  stored in the RTC RAM backed by the supercapa each time a new block is used.
+ */
+
+struct scialys_tor_config sc_conf;
+
+/* This function sets the pointer to the user information block in flash and makes a copy of
+ * the board config part to RAM so that access to these is quicker.
+ * If in need of memory, the config could be used from flash at the expense of execution time
+ */
+void scialys_tor_read_internal_config(void)
+{
+       struct scialys_tor_config* conf;
+       uint8_t* mem = NULL;
+       uint8_t sum = 0, i = 0;
+
+       conf = get_user_info();
+       memcpy(&sc_conf, conf, sizeof(struct scialys_tor_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_tor_config));
+       }
+       /* Checksum */
+       mem = (uint8_t*)(&sc_conf);
+       for (i = 0, sum = 0; i < sizeof(struct scialys_tor_config); i++) {
+               sum += mem[i];
+       }
+       if (sum != 0) {
+               /* Checksum error : erase config in order to start with a default one */
+               memset(&sc_conf, 0, sizeof(struct scialys_tor_config));
+               uprintf(UART0, "User config read from flash has bad checksum.\n");
+       }
+}
+
+/* Update the board config in user flash */
+int scialys_tor_user_flash_update(void)
+{
+       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;
+       mem = (uint8_t*)(&sc_conf);
+       for (i = 0, sum = 0; i < sizeof(struct scialys_tor_config); i++) {
+               sum += mem[i];
+       }
+       sc_conf.checksum = 256 - sum;
+
+       /* Erase the first user flash information page */
+       ret = iap_erase_info_page(0, 0);
+       if (ret != 0) {
+               uprintf(UART0, "User config update error: %d\n", ret);
+               return -1;
+       }
+       size = ((sizeof(struct scialys_tor_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  GRID_POWER_LIMIT  6
+
+#define SOURCE_POWER_MAX 3000 /* in Watts */
+
+#define LOAD_POWER_MAX   3000 /* in Watts */
+
+#define DEFAULT_ACT_DELAY  300 /* in seconds */
+#define DEFAULT_ACT_HYSTERESYS  300 /* in seconds */
+#define DEFAULT_RELAY_POWER1  500 /* in Watts */
+#define DEFAULT_RELAY_POWER2 1000 /* in Watts */
+#define DEFAULT_RELAY_POWER3 1500 /* in Watts */
+
+/* Duration of manual forced activation */
+#define MANUAL_ACTIVATION_DURATION  3  /* Three hours */
+
+/* Compute max_intensity from config grid_power_limit.
+ * 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;
+       uprintf(UART0, "Max intensity is %dmA\n", max_intensity);
+}
+/* Update power_ok_intensity (Watt to mA) */
+void update_power_ok_intensity(void)
+{
+       power_ok_intensity = (((uint32_t)(sc_conf.source_has_power_ok) * 1000) / 230);
+}
+
+/* Update relays intensity (Watt to mA) */
+void update_relay_intensity(void)
+{
+       if (sc_conf.pmax_relay_1 > sc_conf.pmax_relay_2) {
+               if (sc_conf.pmax_relay_1 > sc_conf.pmax_relay_3) {
+                       /* relay 1 is max */
+                       relay_intensity_max = (((uint32_t)(sc_conf.pmax_relay_1) * 1000) / 230);
+                       relay_intensity_max_num = 1;
+                       if (sc_conf.pmax_relay_2 > sc_conf.pmax_relay_3) {
+                               relay_intensity_min = (((uint32_t)(sc_conf.pmax_relay_3) * 1000) / 230);
+                               relay_intensity_min_num = 3;
+                               relay_intensity_med = (((uint32_t)(sc_conf.pmax_relay_2) * 1000) / 230);
+                               relay_intensity_med_num = 2;
+                       } else {
+                               relay_intensity_min = (((uint32_t)(sc_conf.pmax_relay_2) * 1000) / 230);
+                               relay_intensity_min_num = 2;
+                               relay_intensity_med = (((uint32_t)(sc_conf.pmax_relay_3) * 1000) / 230);
+                               relay_intensity_med_num = 3;
+                       }
+               } else {
+                       /* relay 3 is max */
+                       relay_intensity_max = (((uint32_t)(sc_conf.pmax_relay_3) * 1000) / 230);
+                       relay_intensity_max_num = 3;
+                       relay_intensity_min = (((uint32_t)(sc_conf.pmax_relay_2) * 1000) / 230);
+                       relay_intensity_min_num = 2;
+                       relay_intensity_med = (((uint32_t)(sc_conf.pmax_relay_1) * 1000) / 230);
+                       relay_intensity_med_num = 1;
+               }
+       } else {
+               if (sc_conf.pmax_relay_1 > sc_conf.pmax_relay_3) {
+                       /* relay 2 is mmax */
+                       relay_intensity_max = (((uint32_t)(sc_conf.pmax_relay_2) * 1000) / 230);
+                       relay_intensity_max_num = 2;
+                       relay_intensity_min = (((uint32_t)(sc_conf.pmax_relay_3) * 1000) / 230);
+                       relay_intensity_min_num = 3;
+                       relay_intensity_med = (((uint32_t)(sc_conf.pmax_relay_1) * 1000) / 230);
+                       relay_intensity_med_num = 1;
+               } else {
+                       /* relay 1 is min */
+                       relay_intensity_min = (((uint32_t)(sc_conf.pmax_relay_1) * 1000) / 230);
+                       relay_intensity_min_num = 1;
+                       if (sc_conf.pmax_relay_2 > sc_conf.pmax_relay_3) {
+                               relay_intensity_max = (((uint32_t)(sc_conf.pmax_relay_2) * 1000) / 230);
+                               relay_intensity_max_num = 2;
+                               relay_intensity_med = (((uint32_t)(sc_conf.pmax_relay_3) * 1000) / 230);
+                               relay_intensity_med_num = 3;
+                       } else {
+                               relay_intensity_max = (((uint32_t)(sc_conf.pmax_relay_3) * 1000) / 230);
+                               relay_intensity_max_num = 3;
+                               relay_intensity_med = (((uint32_t)(sc_conf.pmax_relay_2) * 1000) / 230);
+                               relay_intensity_med_num = 2;
+                       }
+               }
+       }
+       /* Note : do we need to turn off all relay to start again with new values ?
+        * Or maybe reset ?
+        * Current behavior is to let user reset board if order changed.
+        */
+}
+
+
+void scialys_tor_check_config(void)
+{
+       /* 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.grid_power_limit = GRID_POWER_LIMIT;
+                       sc_conf.source_power_max = SOURCE_POWER_MAX;
+                       sc_conf.source_has_power_ok = SOURCE_POWER_MAX;
+                       sc_conf.load_power_max = LOAD_POWER_MAX;
+                       sc_conf.activation_delay = DEFAULT_ACT_DELAY;
+                       sc_conf.activation_hysteresys = DEFAULT_ACT_HYSTERESYS;
+                       sc_conf.pmax_relay_1 = DEFAULT_RELAY_POWER1;
+                       sc_conf.pmax_relay_2 = DEFAULT_RELAY_POWER2;
+                       sc_conf.pmax_relay_3 = DEFAULT_RELAY_POWER3;
+                       sc_conf.manual_activation_duration = MANUAL_ACTIVATION_DURATION;
+                       sc_conf.conf_version = CONFIG_VERSION;
+                       sc_conf.config_ok = CONFIG_OK;
+               case 0x01: /* Old config is 0x00 add parts from 0x01 and newer */
+       }
+}
+
+/* Read main configuration - or set to default one */
+void read_scialys_tor_main_config(void)
+{
+       scialys_tor_read_internal_config();
+       /* 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);
+       }
+       /* Check that config read from flash is OK */
+       if (sc_conf.config_ok == CONFIG_OK) {
+               uprintf(UART0, "Internal config read OK\n");
+       } else {
+               /* Config is either blank or not up to date, update missing parts with defaults */
+               scialys_tor_check_config();
+       }
+
+       update_power_ok_intensity();
+       update_max_intensity();
+       update_relay_intensity();
+}
+
diff --git a/tor/config.h b/tor/config.h
new file mode 100644 (file)
index 0000000..4a479d3
--- /dev/null
@@ -0,0 +1,164 @@
+/****************************************************************************
+ *   apps/scialys/tor/config.h
+ *
+ * Copyright 2016-2023 Nathael Pajani <nathael.pajani@ed3l.fr>
+ *
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ *************************************************************************** */
+
+#ifndef CONFIG_H
+#define CONFIG_H
+
+#include "core/system.h"
+#include "core/systick.h"
+#include "core/pio.h"
+
+#include "drivers/serial.h"
+#include "drivers/gpio.h"
+#include "drivers/i2c.h"
+
+#include "extdrv/status_led.h"
+
+#include "lib/stdio.h"
+
+
+/* Main frequency (core) */
+#define SELECTED_FREQ  FREQ_SEL_48MHz
+
+/* Period (in ms) of the decrementer handler from the systick interrupt */
+#define DEC_PERIOD  1000
+
+
+/***************************************************************************** */
+/* Configuration storage */
+
+/* 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
+ *
+ * Meaning of fields :
+ *
+ * grid_power_limit :
+ *   Maximum power which can be used from the external (paid) power source, specified (in kWatt)
+ * source_power_max :
+ *   Maximum power which can be produced by installation (in Watts)
+ * source_has_power_ok :
+ *   Power production value to be considered as enough to trigger one of the loads
+ * load_power_max :
+ *   Maximum power used by the load. Currently unused by code (in Watts)
+ *
+ * activation_delay :
+ *   Duration between start of power OK and load activation, in seconds
+ * activation_hysteresys :
+ *   Minimum load activation duration, in seconds
+ *
+ * pmax_relay_1, pmax_relay_2, pmax_relay_3 :
+ *   Power consumption expected when the corresponding relay is activated
+ * manual_activation_duration :
+ *   duration (hours) of manual forced relay activation
+ *
+ */
+#define CONFIG_OK  0x4F4B  /* "OK" in ASCII */
+#define CONFIG_VERSION  0x01
+struct scialys_tor_config {
+       /* Config version and info */
+       uint8_t conf_version;
+       uint8_t checksum;
+       uint16_t config_ok;
+       /* Config  */
+       uint16_t grid_power_limit;
+       uint16_t source_power_max;
+       uint16_t source_has_power_ok;
+       uint16_t load_power_max;
+       uint16_t activation_delay;
+       uint16_t activation_hysteresys;
+       uint16_t pmax_relay_1;
+       uint16_t pmax_relay_2;
+       uint16_t pmax_relay_3;
+       uint16_t manual_activation_duration;
+} __attribute__((packed));
+
+extern struct scialys_tor_config sc_conf;
+
+extern int32_t max_intensity;
+void update_max_intensity(void);
+
+extern uint32_t power_ok_intensity;
+void update_power_ok_intensity(void);
+
+extern int32_t relay_intensity_min;
+extern uint8_t relay_intensity_min_num;
+extern int32_t relay_intensity_med;
+extern uint8_t relay_intensity_med_num;
+extern int32_t relay_intensity_max;
+extern uint8_t relay_intensity_max_num;
+void update_relay_intensity(void);
+
+/* Check that current config is valid */
+void scialys_tor_check_config(void);
+
+/* Read main configuration from internal user flash - or set to default one */
+void read_scialys_tor_main_config(void);
+
+int scialys_tor_user_flash_update(void);
+
+extern uint8_t interface_board_present;
+
+
+/***************************************************************************** */
+/* Pins configuration */
+extern const struct pio status_led_green;
+extern const struct pio status_led_red;
+
+/* Outputs */
+extern const struct pio relay1_on_pin;
+extern const struct pio relay1_off_pin;
+extern const struct pio relay2_on_pin;
+extern const struct pio relay2_off_pin;
+extern const struct pio relay3_on_pin;
+extern const struct pio relay3_off_pin;
+
+
+
+/***************************************************************************** */
+/* Configuration */
+
+/* Configure the watchdog, clocks, systick, power and common pins */
+void system_init();
+
+/* Define our fault handler. This one is not mandatory, the dummy fault handler
+ * will be used when it's not overridden here.
+ * Note : The default one does a simple infinite loop. If the watchdog is deactivated
+ * the system will hang.
+ */
+void fault_info(const char* name, uint32_t len);
+
+
+/* Configure micro-controller internal modules */
+void modules_config(void);
+
+/* Configure GPIO for specific board usage */
+void board_io_config(void);
+
+/* Configure external components */
+int external_components_config(uint32_t uart);
+
+/* Add systick callbacks and update and start timers */
+void scialys_tor_systick_config(void);
+
+
+#endif /* CONFIG_H */
+
diff --git a/tor/data.h b/tor/data.h
new file mode 100644 (file)
index 0000000..d9366f2
--- /dev/null
@@ -0,0 +1,84 @@
+/****************************************************************************
+ *   apps/scialys/v10/data.h
+ *
+ * Copyright 2016-2023 Nathael Pajani <nathael.pajani@ed3l.fr>
+ *
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ *************************************************************************** */
+
+#ifndef DATA_H
+#define DATA_H
+
+
+/* Scialys data, as sent on UART1 or stored on SD card.
+ *
+ * Header fields :
+ * The start byte is dedicated to UART communication and is always '#"
+ *   Note that this byte may be repeated within the data packet, so the rest of the header
+ *   should be checked before handling a packet
+ * The first checksum byte is so that the sum of all header bytes is zero
+ * The version byte indicates the version of this structure.
+ *   The length is fixed so knowing the version gives the length.
+ * The data checksum is equal to the sum of all the data bytes
+ *
+ * Data fields :
+ * ...
+ * The flags can be expanded to a "struct flags" (see below) by shifting one bit to each byte.
+ *
+ */
+
+#define DATA_START '#'
+#define DATA_VERSION 0x01
+#define DATA_HEADER_LEN 4
+
+struct scialys_data {
+       /* Header */
+       uint8_t start;
+       uint8_t cksum;
+       uint8_t version;
+       uint8_t data_cksum;
+       /* Data */
+    uint32_t solar_prod_value;
+    uint32_t home_conso_value;
+    int water_centi_degrees;
+    int deci_degrees_power;
+    int deci_degrees_disp;
+    uint16_t load_power_lowest;
+    uint16_t load_power_highest;
+    uint8_t command_val;
+    uint8_t act_cmd;
+    uint8_t mode;
+    uint8_t flags;
+} __attribute__ ((__packed__));
+
+/*
+struct flags {
+    uint8_t fan_on;
+    uint8_t force_fan;
+    uint8_t error_shutdown;
+    uint8_t temp_shutdown;
+    uint8_t overvoltage;
+    uint8_t external_disable;
+    uint8_t forced_heater_mode;
+    uint8_t manual_activation_request;
+};
+*/
+
+void scialys_add_header(struct scialys_data* data);
+
+
+#endif /* DATA_H */
+
diff --git a/tor/interface.c b/tor/interface.c
new file mode 100644 (file)
index 0000000..d893e8e
--- /dev/null
@@ -0,0 +1,271 @@
+/****************************************************************************
+ *   apps/scialys/tor/interface.c
+ *
+ * Copyright 2016-2023 Nathael Pajani <nathael.pajani@ed3l.fr>
+ *
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ *************************************************************************** */
+
+#include "core/system.h"
+#include "core/systick.h"
+#include "core/pio.h"
+
+#include "drivers/serial.h"
+#include "drivers/gpio.h"
+#include "drivers/i2c.h"
+
+#include "extdrv/status_led.h"
+#include "extdrv/ws2812.h"
+#include "extdrv/ssd130x_oled_driver.h"
+#include "extdrv/ssd130x_oled_buffer.h"
+
+#include "lib/stdio.h"
+#include "lib/font.h"
+#include "lib/time.h"
+
+#include "config.h"
+#include "interface.h"
+#include "version.h"
+#include "time.h"
+#include "param.h"
+
+/***************************************************************************** */
+/* Buttons inputs on front panel */
+const struct pio button_up = LPC_GPIO_1_5;
+#define GPIO_BUTTON_UP  5
+const struct pio button_right = LPC_GPIO_1_3;
+#define GPIO_BUTTON_RIGHT  3
+const struct pio button_left = LPC_GPIO_1_4;
+#define GPIO_BUTTON_LEFT  4
+const struct pio button_down = LPC_GPIO_1_2;
+#define GPIO_BUTTON_DOWN 2
+/* ISP  /User button OK */
+const struct pio button_ok = LPC_GPIO_0_12;
+#define GPIO_BUTTON_OK  12
+
+
+/* Led control data pin */
+const struct pio ws2812_data_out_pin = LPC_GPIO_0_20;
+/* Oled Reset */
+const struct pio oled_reset = LPC_GPIO_0_21;
+
+
+/***************************************************************************** */
+/* Oled Display */
+static uint8_t gddram[ 4 + GDDRAM_SIZE ];
+
+#define DISPLAY_ADDR   0x78
+struct oled_display display = {
+       .bus_type = SSD130x_BUS_I2C,
+       .address = DISPLAY_ADDR,
+       .bus_num = I2C0,
+       .charge_pump = SSD130x_INTERNAL_PUMP,
+       .video_mode = SSD130x_DISP_NORMAL,
+       .contrast = 128,
+       .scan_dir = SSD130x_SCAN_BOTTOM_TOP,
+       .read_dir = SSD130x_RIGHT_TO_LEFT,
+       .display_offset_dir = SSD130x_MOVE_TOP,
+       .display_offset = 4,
+       .gddram = gddram,
+};
+
+#define ROW(x)   VERTICAL_REV(x)
+DECLARE_FONT(font);
+
+void display_char(uint8_t line, uint8_t col, uint8_t c)
+{
+       uint8_t tile = (c > FIRST_FONT_CHAR) ? (c - FIRST_FONT_CHAR) : 0;
+       uint8_t* tile_data = (uint8_t*)(&font[tile]);
+       ssd130x_buffer_set_tile(gddram, col, line, tile_data);
+}
+int display_line(uint8_t line, uint8_t col, const char* text)
+{
+       int len = strlen((char*)text);
+       int i = 0;
+
+       for (i = 0; i < len; i++) {
+               uint8_t tile = (text[i] > FIRST_FONT_CHAR) ? (text[i] - FIRST_FONT_CHAR) : 0;
+               uint8_t* tile_data = (uint8_t*)(&font[tile]);
+               ssd130x_buffer_set_tile(gddram, col++, line, tile_data);
+               if (col >= (OLED_LINE_CHAR_LENGTH)) {
+                       col = 0;
+                       line++;
+                       if (line >= SSD130x_NB_PAGES) {
+                               len = i;
+                               break;
+                       }
+               }
+       }
+       return len;
+}
+
+int erase_line(uint8_t line)
+{
+       uint8_t* tile_data = (uint8_t*)(&font[0]);
+       uint8_t col = 0;
+       int i = 0;
+
+       for (i = 0; i < OLED_LINE_CHAR_LENGTH; i++) {
+               ssd130x_buffer_set_tile(gddram, col++, line, tile_data);
+       }
+       /* Update Oled display */
+       return ssd130x_display_full_screen(&display);
+}
+
+void erase_screen_content(void)
+{
+       /* Erase screen data ram */
+       ssd130x_buffer_set(gddram, 0x00);
+}
+
+
+/***************************************************************************** */
+/* Display the arrows above and below a value which can be changed in
+ * param menu
+ */
+void display_arrows(uint8_t line1, uint8_t line2, uint8_t col)
+{
+       char text[DISP_LLEN];
+       snprintf(text, DISP_LLEN, "%c", 0x60);
+       display_line(line1, col, text);
+       snprintf(text, DISP_LLEN, "%c", 0x7f);
+       display_line(line2, col, text);
+}
+
+
+
+/***************************************************************************** */
+volatile uint8_t button_pressed = 0;
+void button_callback(uint32_t gpio)
+{
+       switch (gpio) {
+               case GPIO_BUTTON_OK:
+                       button_pressed |= BUTTON_OK;
+                       break;
+               case GPIO_BUTTON_UP:
+                       button_pressed |= BUTTON_UP;
+                       break;
+               case GPIO_BUTTON_DOWN:
+                       button_pressed |= BUTTON_DOWN;
+                       break;
+               case GPIO_BUTTON_LEFT:
+                       button_pressed |= BUTTON_LEFT;
+                       break;
+               case GPIO_BUTTON_RIGHT:
+                       button_pressed |= BUTTON_RIGHT;
+                       break;
+       }
+}
+
+#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 */
+       /* Activate on Falling edge (button press) */
+       set_gpio_callback(button_callback, &button_up, EDGE_FALLING);
+       set_gpio_callback(button_callback, &button_left, EDGE_FALLING);
+       set_gpio_callback(button_callback, &button_right, EDGE_FALLING);
+       set_gpio_callback(button_callback, &button_down, EDGE_FALLING);
+       set_gpio_callback(button_callback, &button_ok, EDGE_FALLING);
+
+       /* WS2812B Leds on display board */
+       ws2812_config(&ws2812_leds, &ws2812_data_out_pin);
+
+       /* Configure and start display */
+       config_gpio(&oled_reset, 0, GPIO_DIR_OUT, 1); /* Release reset signal */
+       ssd130x_display_on(&display);
+       erase_screen_content();
+       ssd130x_display_full_screen(&display);
+
+       ws2812_set_pixel(&ws2812_leds, 0, 0x05, 0x15, 0x08);
+       ws2812_send_frame(&ws2812_leds, 0);
+
+       uprintf(uart, "Interface config OK\n");
+
+       return 0;
+}
+
+
+/***************************************************************************** */
+ /* usual menu and switch to config menu */
+
+extern volatile uint8_t relay_states[4];
+extern int32_t delta_prod_value;
+
+static int interface_mode = MODE_RUN;
+
+char line[DISP_LLEN];
+
+void interface_update(void)
+{
+       if (interface_board_present == 0) {
+               /* DEBUG */ uprintf(UART0, "No interface board ??\n"); /* DEBUG */
+               return;
+       }
+
+       /* Debug */
+       if (button_pressed != 0) {
+               uprintf(UART0, "Button : 0x%02x\n", button_pressed);
+       }
+
+       if (interface_mode == MODE_RUN) {
+               uint32_t delta_abs = (delta_prod_value >= 0) ? delta_prod_value : -delta_prod_value;
+               if (button_pressed != 0) {
+                       if (button_pressed & BUTTON_UP) {
+                               interface_mode = MODE_CONFIG;
+                       }
+                       button_pressed = 0;
+               }
+
+               /* Start with a blank screen */
+               erase_screen_content();
+
+               /* Update time and time display on internal memory */
+               rtc_pcf85363_time_read(&rtc_conf, &now);
+               dprintf(0, 0, "%02xh%02x:%02x", now.hour, now.min, now.sec);
+
+               /* Display info */
+               dprintf(2, 0, "Relay1: %s", ((relay_states[1] == 1) ? "On" : "Off"));
+               dprintf(3, 0, "Relay2: %s", ((relay_states[2] == 1) ? "On" : "Off"));
+               dprintf(4, 0, "Relay3: %s", ((relay_states[3] == 1) ? "On" : "Off"));
+               dprintf(5, 0, "Delta: % 2d,%03dA", (delta_prod_value / 1000), ((delta_abs % 1000) / 10));
+               dprintf(7, 0, MODULE_VERSION_STR " - " SOFT_VERSION_STR "." COMPILE_VERSION);
+
+               /* Update RGB leds */
+               /* FIXME : use for error signal */
+               ws2812_set_pixel(&ws2812_leds, 0, relay_states[1] * 100, relay_states[2] * 100, relay_states[3] * 100);
+               if (delta_prod_value > 0) {
+                       ws2812_set_pixel(&ws2812_leds, 1, 0, 0, (delta_prod_value/1000));
+               } else {
+                       ws2812_set_pixel(&ws2812_leds, 1, ((-delta_prod_value)/1000), 0, 0);
+               }
+               ws2812_send_frame(&ws2812_leds, 0);
+       } else {
+               /* Config mode. Mode entered by button action, which implies that interface board is present. */
+               interface_mode = config_interface_handle();
+       }
+       /* Update Oled display */
+       ssd130x_display_full_screen(&display);
+}
+
+
diff --git a/tor/interface.h b/tor/interface.h
new file mode 100644 (file)
index 0000000..a5a9e56
--- /dev/null
@@ -0,0 +1,84 @@
+/****************************************************************************
+ *   apps/scialys/tor/interface.h
+ *
+ * Copyright 2016-2023 Nathael Pajani <nathael.pajani@ed3l.fr>
+ *
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ *************************************************************************** */
+
+#ifndef INTERFACE_H
+#define INTERFACE_H
+
+#include "core/system.h"
+#include "core/pio.h"
+
+#include "extdrv/ssd130x_oled_driver.h"
+
+#define OLED_LINE_CHAR_LENGTH  (SSD130x_NB_COL / 8)
+#define DISP_LLEN   (OLED_LINE_CHAR_LENGTH + 1)
+
+
+/***************************************************************************** */
+/* Pins configuration */
+/* Buttons inputs on front panel */
+extern const struct pio button_up;
+extern const struct pio button_left;
+extern const struct pio button_right;
+extern const struct pio button_down;
+extern const struct pio button_ok;
+
+
+/* Exported vars */
+extern volatile uint8_t button_pressed;
+#define BUTTON_OK  (0x01 << 0)
+#define BUTTON_UP  (0x01 << 1)
+#define BUTTON_DOWN  (0x01 << 2)
+#define BUTTON_RIGHT  (0x01 << 3)
+#define BUTTON_LEFT  (0x01 << 4)
+
+/***************************************************************************** */
+/* Configuration */
+
+/* Configure interface board */
+int interface_config(uint32_t uart);
+
+enum interface_modes {
+    MODE_RUN = 0,
+    MODE_CONFIG,
+    MODE_DISPLAY,
+};
+
+extern char line[];
+#define dprintf(l, c, format, ...) \
+       snprintf(line, DISP_LLEN, format, ##__VA_ARGS__); \
+       display_line(l, c, line);
+
+/* Interface content update */
+void interface_update(void);
+
+void erase_screen_content(void);
+
+int display_line(uint8_t line, uint8_t col, const char* text);
+
+int erase_line(uint8_t line);
+
+/* Display the arrows above and below a value which can be changed in
+ * param menu
+ */
+void display_arrows(uint8_t line1, uint8_t line2, uint8_t col);
+
+#endif /* INTERFACE_H */
+
diff --git a/tor/main.c b/tor/main.c
new file mode 100644 (file)
index 0000000..45d7d61
--- /dev/null
@@ -0,0 +1,296 @@
+/****************************************************************************
+ *   apps/scialys/tor/main.c
+ *
+ * Scialys system for solar-panel power generation tracking and fair use.
+ *  TOR extension support
+ *
+ * Copyright 2016-2023 Nathael Pajani <nathael.pajani@ed3l.fr>
+ *
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ *************************************************************************** */
+
+
+#include "config.h"
+#include "interface.h"
+#include "time.h"
+#include "sensors.h"
+#include "comm.h"
+#include "data.h"
+
+
+#define MODULE_VERSION    0x0A
+#define MODULE_NAME "Scialys uC"
+
+
+
+/***************************************************************************** */
+/* System configuration */
+
+/* This multiplier is used for all durations and delays which are stored in number of hours */
+#define T_MULT  (3600 * 1000 / DEC_PERIOD)
+
+
+
+
+/* Flags and counters */
+int manual_activation_request_r1 = 0; /* Flag and counter */
+int manual_activation_request_r2 = 0; /* Flag and counter */
+int manual_activation_request_r3 = 0; /* Flag and counter */
+
+
+/* Internal temperatures */
+int deci_degrees_disp = 0; /* Display board sensor */
+
+
+/* Value in mA computed upon startup from config ext_source_power_limit */
+int32_t max_intensity = 0;
+
+/* Value in mA computed upon startup from config source_has_power_ok */
+uint32_t power_ok_intensity = 0;
+
+/* RTC and time */
+struct rtc_time now;
+
+
+volatile int32_t start_delay = -1; /* Uses sc_conf.activation_delay */
+volatile int32_t hysteresys = 0;; /* Uses sc_conf.activation_hysteresys */
+
+/***************************************************************************** */
+/* Decrementer for internal timers */
+
+void handle_dec_request(uint32_t curent_tick)
+{
+       /* Manual forced mode */
+       if (manual_activation_request_r1 > 0) {
+               manual_activation_request_r1--;
+       }
+       /* Start delay and update hysteresys */
+       if (start_delay > 0) {
+               start_delay--;
+       }
+       if (hysteresys > 0) {
+               hysteresys--;
+       }
+}
+
+
+
+/***************************************************************************** */
+/* AC control */
+
+#define RELAY_ON  1
+#define RELAY_OFF 0
+volatile uint8_t relay_states[4] = { 0, RELAY_OFF, RELAY_OFF, RELAY_OFF};
+
+void relay_pin_toggle(const struct pio relay_pin)
+{
+       gpio_clear(relay_pin);
+       msleep(12);
+       gpio_set(relay_pin);
+}
+
+void change_relay_state(uint8_t relay_num, uint8_t state)
+{
+       uprintf(UART0, "R%d:%d\n", relay_num, state);
+       switch (relay_num | (state << 2)) {
+               case 1:
+                       relay_pin_toggle(relay1_off_pin);
+                       relay_states[1] = RELAY_OFF;
+                       break;
+               case 2:
+                       relay_pin_toggle(relay2_off_pin);
+                       relay_states[2] = RELAY_OFF;
+                       break;
+               case 3:
+                       relay_pin_toggle(relay3_off_pin);
+                       relay_states[3] = RELAY_OFF;
+                       break;
+               case 5:
+                       relay_pin_toggle(relay1_on_pin);
+                       relay_states[1] = RELAY_ON;
+                       break;
+               case 6:
+                       relay_pin_toggle(relay2_on_pin);
+                       relay_states[2] = RELAY_ON;
+                       break;
+               case 7:
+                       relay_pin_toggle(relay3_on_pin);
+                       relay_states[3] = RELAY_ON;
+                       break;
+       }
+}
+
+struct scialys_data sc_data;
+
+int32_t relay_intensity_min;
+uint8_t relay_intensity_min_num;
+int32_t relay_intensity_med;
+uint8_t relay_intensity_med_num;
+int32_t relay_intensity_max;
+uint8_t relay_intensity_max_num;
+
+
+int32_t delta_prod_value; /* Also refered to in interface.c */
+
+void ac_load_update(void)
+{
+       uint8_t i = 0, sum = 0;
+       uint8_t* data = (uint8_t*)&sc_data;
+       /* First, check that packet is valid. already done in comm_rx, but data may
+        *   have changed between comm_rx interrupt handler and memcpy call.
+        * Alternative will be to use a mutex.
+        */
+       sum = sc_data.start + sc_data.cksum + sc_data.version + sc_data.data_cksum;
+       if (sum != 0) {
+               return;
+       }
+       for (i = 4; i < MSG_LEN; i++) {
+               sum += data[i];
+       }
+       if (sum != sc_data.data_cksum) {
+               return;
+       }
+       /* Packet is valid, compute delta */
+       delta_prod_value = (sc_data.solar_prod_value - sc_data.home_conso_value);
+
+       if (delta_prod_value > relay_intensity_min ) {
+               /* Do we apply now ? */
+               if (start_delay < 0) {
+                       /* Start decreasing start_delay */
+                       uprintf(UART0, "start_delay\n");
+                       start_delay = sc_conf.activation_delay;
+               }
+               if (start_delay > 0) {
+                       return;
+               }
+               /* start_delay reached 0 */
+               /* Increase power usage ? */
+               if (delta_prod_value > relay_intensity_max) {
+                       if (relay_states[relay_intensity_max_num] == RELAY_OFF) {
+                               change_relay_state(relay_intensity_max_num, RELAY_ON);
+                       } else if (delta_prod_value > (relay_intensity_med + relay_intensity_min)) {
+                               change_relay_state(relay_intensity_med_num, RELAY_ON);
+                               change_relay_state(relay_intensity_min_num, RELAY_ON);
+                       } else if (relay_states[relay_intensity_med_num] == RELAY_OFF) {
+                               relay_states[relay_intensity_med_num] = RELAY_ON;
+                       } else {
+                               change_relay_state(relay_intensity_min_num, RELAY_ON);
+                       }
+               } else if (delta_prod_value > relay_intensity_med) {
+                       if (relay_states[relay_intensity_med_num] == RELAY_OFF) {
+                               change_relay_state(relay_intensity_med_num, RELAY_ON);
+                       } else {
+                               change_relay_state(relay_intensity_min_num, RELAY_ON);
+                       }
+               } else {
+                       change_relay_state(relay_intensity_min_num, RELAY_ON);
+               }
+               start_delay = -1;
+               hysteresys = sc_conf.activation_hysteresys;
+       } else if (delta_prod_value < -relay_intensity_min) {
+               /* Do we apply now ? */
+               if (hysteresys > 0) {
+                       return;
+               }
+               uprintf(UART0, "hyst end\n");
+               /* Decrease power usage ? */
+               int32_t delta_conso = -delta_prod_value;
+               if (delta_conso > relay_intensity_max) {
+                       if (relay_states[relay_intensity_max_num] == RELAY_ON) {
+                               change_relay_state(relay_intensity_max_num, RELAY_OFF);
+                       } else if (delta_conso > (relay_intensity_med + relay_intensity_min)) {
+                               change_relay_state(relay_intensity_med_num, RELAY_OFF);
+                               change_relay_state(relay_intensity_min_num, RELAY_OFF);
+                       } else if (relay_states[relay_intensity_med_num] == RELAY_ON) {
+                               relay_states[relay_intensity_med_num] = RELAY_OFF;
+                       } else {
+                               change_relay_state(relay_intensity_min_num, RELAY_OFF);
+                       }
+               } else if (delta_conso > relay_intensity_med) {
+                       if (relay_states[relay_intensity_med_num] == RELAY_ON) {
+                               change_relay_state(relay_intensity_med_num, RELAY_OFF);
+                       } else {
+                               change_relay_state(relay_intensity_min_num, RELAY_OFF);
+                       }
+               } else {
+                       change_relay_state(relay_intensity_min_num, RELAY_OFF);
+               }
+       }
+}
+
+uint8_t internal_temp_error_shutdown = 0;
+
+/***************************************************************************** */
+int main(void)
+{
+       uint32_t last_tick_update = 0, cur_tick = 0;
+
+       /* Order is important here */
+       system_init();
+       board_io_config();
+       modules_config();
+       external_components_config(UART0);
+
+       change_relay_state(1, RELAY_OFF);
+       change_relay_state(2, RELAY_OFF);
+       change_relay_state(3, RELAY_OFF);
+
+       read_scialys_tor_main_config();
+
+       scialys_tor_systick_config();
+
+       uprintf(UART0, "System setup complete.\n");
+       uprintf(UART0, "Imin (%d): %d, Imed (%d): %d, Imax (%d): %d\n",
+                               relay_intensity_min_num, relay_intensity_min,
+                               relay_intensity_med_num, relay_intensity_med,
+                               relay_intensity_max_num, relay_intensity_max);
+       msleep(100);
+       status_led(green_only);
+
+       while (1) {
+               /* Feed the dog */
+               if (comm_ok == 1) {
+                       watchdog_feed();
+                       comm_ok = 0;
+               }
+
+
+               /* Check for received packet */
+               if (packet_valid == 1) {
+                       memcpy(&sc_data, (char*)data, MSG_LEN);
+                       /* Update AC load statuses */
+                       ac_load_update();
+               }
+
+               /* 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 */
+                       last_tick_update = 0;
+               }
+               if (cur_tick > (last_tick_update + 500)) {
+                       /* Update internal temperatures */
+                       internal_temp_error_shutdown = update_internal_temp(UART0, &deci_degrees_disp);
+                       /* Update display */
+                       interface_update();
+                       last_tick_update = cur_tick;
+               }
+       }
+       return 0;
+}
+
+
+
diff --git a/tor/param.c b/tor/param.c
new file mode 100644 (file)
index 0000000..a26ce37
--- /dev/null
@@ -0,0 +1,635 @@
+/****************************************************************************
+ *   apps/scialys/tor/param.c
+ *
+ * Copyright 2016-2023 Nathael Pajani <nathael.pajani@ed3l.fr>
+ *
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ *************************************************************************** */
+
+#include "core/system.h"
+#include "core/systick.h"
+#include "core/pio.h"
+
+#include "drivers/serial.h"
+#include "drivers/gpio.h"
+#include "drivers/i2c.h"
+
+#include "extdrv/status_led.h"
+#include "extdrv/ws2812.h"
+#include "extdrv/ssd130x_oled_driver.h"
+#include "extdrv/ssd130x_oled_buffer.h"
+
+#include "lib/stdio.h"
+#include "lib/font.h"
+#include "lib/time.h"
+
+#include "config.h"
+#include "interface.h"
+#include "version.h"
+#include "time.h"
+
+
+/***************************************************************************** */
+ /* Menu part */
+
+enum menu_list {
+       MAIN_MENU,
+       AUTO_FORCE,
+       MANUAL_FORCE,
+       LIMITS,
+       DATE_CONFIG,
+       SAVE_RAZ,
+       NB_MENU, /* This one must be the last */
+};
+static const char* menu_titles[] = {
+       [MAIN_MENU] = "Configuration",
+       [AUTO_FORCE] =   "MF auto.",
+       [MANUAL_FORCE] = "MF manuelle",
+       [LIMITS] = "Regl. Limites",
+       [DATE_CONFIG] = "Regl. Heure",
+       [SAVE_RAZ] = "Enreg. / RaZ",
+};
+static uint8_t current_menu = MAIN_MENU;
+static uint8_t current_entry = AUTO_FORCE;
+
+/* Auto Force */
+enum auto_force_list {
+       AUTO_FORCE_DELAY,
+       AUTO_FORCE_HYSTERESYS,
+       AUTO_FORCE_NB_MENU, /* This one must be the last */
+};
+static const char* auto_force_titles[] = {
+       [AUTO_FORCE_DELAY] = "Delay",
+       [AUTO_FORCE_HYSTERESYS] = "Hysteresys",
+};
+static uint8_t auto_force_cur_menu = AUTO_FORCE_DELAY;
+static uint8_t auto_force_cur_entry = AUTO_FORCE_DELAY;
+
+
+/* Manual Force */
+enum manual_force_list {
+       MANUAL_FORCE_R1,
+       MANUAL_FORCE_R2,
+       MANUAL_FORCE_R3,
+       MANUAL_FORCE_DURATION,
+       MANUAL_FORCE_NB_MENU, /* This one must be the last */
+};
+static const char* manual_force_titles[] = {
+       [MANUAL_FORCE_R1] = "Relais 1",
+       [MANUAL_FORCE_R2] = "Relais 2",
+       [MANUAL_FORCE_R3] = "Relais 3",
+       [MANUAL_FORCE_DURATION] = "Duree",
+};
+static uint8_t manual_force_cur_menu = MANUAL_FORCE_DURATION;
+static uint8_t manual_force_cur_entry = MANUAL_FORCE_DURATION;
+
+/* Limits */
+enum limits_menu_list {
+       PMAX_GRID,
+       PMAX_PROD,
+       POK_PROD,
+       PR1,
+       PR2,
+       PR3,
+       LIM_NB_MENU, /* This one must be the last */
+};
+static const char* limits_modes_titles[] = {
+       [PMAX_GRID] = "P Max Abon.",
+       [PMAX_PROD] = "P Max Prod.",
+       [POK_PROD] = "P OK Prod.",
+       [PR1] = "P Charge 1",
+       [PR2] = "P Charge 2",
+       [PR3] = "P Charge 3",
+};
+static uint8_t limits_cur_menu = PMAX_GRID;
+static uint8_t limits_cur_entry = PMAX_GRID;
+
+enum conf_menu_list {
+       SAVE_CONFIG,
+       RESET_CONFIG,
+       CONF_NB_MENU, /* This one must be the last */
+};
+static const char* conf_titles[] = {
+       [SAVE_CONFIG] = "Enregistrer",
+       [RESET_CONFIG] = "RaZ config",
+};
+static uint8_t conf_cur_menu = SAVE_CONFIG;
+static uint8_t conf_cur_entry = SAVE_CONFIG;
+
+
+
+/* Current sub-menu level. main menu is level 0 */
+int sub_menu_level = 0;
+
+#define   BUTTONS_ACTS_DEFAULT()  \
+                       if (button & BUTTON_LEFT) { sub_menu_level = 1; } \
+                       if (button & BUTTON_OK) { sub_menu_level = 0; current_menu = MAIN_MENU; }
+
+/* Configuraton menu handler
+ * Return MODE_RUN to get back to normal mode, or MODE_CONFIG to stay in configuration mode
+ */
+int config_interface_handle(void)
+{
+       int interface_mode = MODE_CONFIG;
+       uint8_t button = 0;
+
+       if (current_menu >= NB_MENU) {
+               current_menu = MAIN_MENU;
+       }
+       button = button_pressed;
+       button_pressed = 0;
+       /* Start config with a blank screen */
+       erase_screen_content();
+
+       /* Display current menu title */
+       display_line(0, 1, menu_titles[current_menu]);
+
+       switch (current_menu) {
+               case MAIN_MENU: {
+                               int i = 0;
+                               /* Display main menu */
+                               for (i = 1; i < NB_MENU; i++) {
+                                       if (i != current_entry) {
+                                               display_line(i + 1, 2, menu_titles[i]);
+                                       } else {
+                                               dprintf(i + 1, 0, " >%s", menu_titles[i]);
+                                       }
+                               }
+                               /* Button up/down : select next/previous entry */
+                               if (button & BUTTON_UP) {
+                                       current_entry -= 1;
+                               }
+                               if (button & BUTTON_DOWN) {
+                                       current_entry += 1;
+                               }
+                               if (current_entry >= NB_MENU) {
+                                       current_entry = 1;
+                               } else if (current_entry == 0) { /* For this one, first menu_title is "config" */
+                                       current_entry = NB_MENU - 1;
+                               }
+                               /* Enter sub-menu */
+                               if (button & BUTTON_RIGHT) {
+                                       current_menu = current_entry;
+                                       sub_menu_level = 1;
+                               }
+                               /* Exit menu */
+                               if (button & (BUTTON_LEFT | BUTTON_OK)) {
+                                       interface_mode = MODE_RUN;
+                               }
+                       }
+                       break;
+               case DATE_CONFIG: {
+                               static uint8_t date_idx = 0; /* Where to put the arrows */
+                               uint8_t line_num = 0;
+                               /* Read current date and time */
+                               rtc_pcf85363_time_read_dec(&rtc_conf, &now);
+                               /* Display date and time */
+                               dprintf(3, 4, "20%02u-%02u-%02u", now.year, now.month, now.day);
+                               dprintf(5, 6, "%02uh%02u:%02u", now.hour, now.min, now.sec);
+                               /* Display arrows on top/bottom of current field */
+                               line_num = 2 + ((date_idx > 2) ? 2 : 0);
+                               display_arrows(line_num, (line_num + 2), (7 + ((date_idx % 3) * 3)));
+                               /* Update current field ? */
+                               if ((button & BUTTON_RIGHT) && (date_idx < 5)) {
+                                       date_idx++;
+                               }
+                               if ((button & BUTTON_LEFT) && (date_idx > 0)) {
+                                       date_idx--;
+                               }
+                               if (button & (BUTTON_UP | BUTTON_DOWN)) {
+                                       int8_t add = 1;
+                                       if (button & BUTTON_DOWN) {
+                                               add = -1;
+                                       }
+                                       switch (date_idx) {
+                                               case 0:
+                                                       now.year += add;
+                                                       if (now.year > 99) { now.year = 0; }
+                                                       break;
+                                               case 1:
+                                                       now.month += add;
+                                                       if (now.month > 12) { now.month = 1; }
+                                                       if (now.month == 0) { now.month = 12; }
+                                                       break;
+                                               case 2:
+                                                       now.day += add;
+                                                       if (now.day > 31) { now.day = 1; }
+                                                       if (now.day == 0) { now.day = 31; }
+                                                       break;
+                                               case 3:
+                                                       now.hour += add;
+                                                       if (now.hour > 100) { now.hour = 23; }
+                                                       if (now.hour > 23) { now.hour = 0; }
+                                                       break;
+                                               case 4:
+                                                       now.min += add;
+                                                       if (now.min > 100) { now.min = 59; }
+                                                       if (now.min > 59) { now.min = 0; }
+                                                       break;
+                                               case 5:
+                                                       now.sec += add;
+                                                       if (now.sec > 59) { now.sec = 0; }
+                                                       break;
+                                       }
+                                       rtc_pcf85363_time_write_dec(&rtc_conf, &now);
+                               }
+                               if (button & BUTTON_OK) {
+                                       sub_menu_level = 0;
+                                       current_menu = MAIN_MENU;
+                               }
+                       }
+                       break;
+               case LIMITS: {
+                               if (sub_menu_level == 1) {
+                                       int i = 0;
+                                       for (i = 0; i < LIM_NB_MENU; i++) {
+                                               if (i != limits_cur_entry) {
+                                                       display_line(i + 2, 3, limits_modes_titles[i]);
+                                               } else {
+                                                       dprintf(i + 2, 0, "  >%s", limits_modes_titles[i]);
+                                               }
+                                       }
+                                       if (button & BUTTON_UP) {
+                                               limits_cur_entry -= 1;
+                                       }
+                                       if (button & BUTTON_DOWN) {
+                                               limits_cur_entry += 1;
+                                       }
+                                       if (limits_cur_entry == 0xFF) {
+                                               limits_cur_entry = LIM_NB_MENU - 1;
+                                       } else if (limits_cur_entry >= LIM_NB_MENU) {
+                                               limits_cur_entry = 0;
+                                       }
+                                       if (button & (BUTTON_OK | BUTTON_RIGHT)) {
+                                               limits_cur_menu = limits_cur_entry;
+                                               sub_menu_level = 2;
+                                       }
+                                       if (button & BUTTON_LEFT) {
+                                               sub_menu_level = 0;
+                                               current_menu = MAIN_MENU;
+                                       }
+                               } else { /* sub_menu_level == 2 */
+                                       display_line(1, 1, limits_modes_titles[limits_cur_menu]);
+                                       switch (limits_cur_menu) {
+                                               case PMAX_GRID:
+                                                       display_arrows(4, 6, 11);
+                                                       dprintf(5, 1, "P. abo.: %2u kW", sc_conf.grid_power_limit);
+                                                       if (button & BUTTON_UP) {
+                                                               sc_conf.grid_power_limit++;
+                                                               if (sc_conf.grid_power_limit > 36) {
+                                                                       sc_conf.grid_power_limit = 0;
+                                                               }
+                                                       }
+                                                       if (button & BUTTON_DOWN) {
+                                                               sc_conf.grid_power_limit--;
+                                                               if (sc_conf.grid_power_limit > 36) {
+                                                                       sc_conf.grid_power_limit = 36;
+                                                               }
+                                                       }
+                                                       /* This one is used in main loop after computation */
+                                                       update_max_intensity();
+                                                       BUTTONS_ACTS_DEFAULT();
+                                                       break;
+                                               case PMAX_PROD:
+                                                       display_arrows(4, 6, 8);
+                                                       dprintf(5, 1, "P. : %4u W", sc_conf.source_power_max);
+                                                       if (button & BUTTON_UP) {
+                                                               sc_conf.source_power_max += 50;
+                                                               if (sc_conf.source_power_max > 36000) {
+                                                                       sc_conf.source_power_max = 0;
+                                                               }
+                                                       }
+                                                       if (button & BUTTON_DOWN) {
+                                                               sc_conf.source_power_max -= 50;
+                                                               if (sc_conf.source_power_max > 36000) {
+                                                                       sc_conf.source_power_max = 36000;
+                                                               }
+                                                       }
+                                                       BUTTONS_ACTS_DEFAULT();
+                                                       break;
+                                               case POK_PROD:
+                                                       display_arrows(4, 6, 8);
+                                                       dprintf(5, 1, "P. : %4u W", sc_conf.source_has_power_ok);
+                                                       if (button & BUTTON_UP) {
+                                                               sc_conf.source_has_power_ok += 50;
+                                                               if (sc_conf.source_has_power_ok > 36000) {
+                                                                       sc_conf.source_has_power_ok = 0;
+                                                               }
+                                                       }
+                                                       if (button & BUTTON_DOWN) {
+                                                               sc_conf.source_has_power_ok -= 50;
+                                                               if (sc_conf.source_has_power_ok > 36000) {
+                                                                       sc_conf.source_has_power_ok = 36000;
+                                                               }
+                                                       }
+                                                       /* This one is used in main loop after computation */
+                                                       update_power_ok_intensity();
+                                                       BUTTONS_ACTS_DEFAULT();
+                                                       break;
+#define PMAX_RELAY  1800
+                                               case PR1:
+                                                       display_arrows(4, 6, 8);
+                                                       dprintf(5, 1, "PR1. : %4u W", sc_conf.pmax_relay_1);
+                                                       if (button & BUTTON_UP) {
+                                                               sc_conf.pmax_relay_1 += 50;
+                                                               if (sc_conf.pmax_relay_1 > PMAX_RELAY) {
+                                                                       sc_conf.pmax_relay_1 = 0;
+                                                               }
+                                                       }
+                                                       if (button & BUTTON_DOWN) {
+                                                               sc_conf.pmax_relay_1 -= 50;
+                                                               if (sc_conf.pmax_relay_1 > PMAX_RELAY) {
+                                                                       sc_conf.pmax_relay_1 = PMAX_RELAY;
+                                                               }
+                                                       }
+                                                       update_relay_intensity();
+                                                       BUTTONS_ACTS_DEFAULT();
+                                                       break;
+                                               case PR2:
+                                                       display_arrows(4, 6, 8);
+                                                       dprintf(5, 1, "PR2. : %4u W", sc_conf.pmax_relay_2);
+                                                       if (button & BUTTON_UP) {
+                                                               sc_conf.pmax_relay_2 += 50;
+                                                               if (sc_conf.pmax_relay_2 > PMAX_RELAY) {
+                                                                       sc_conf.pmax_relay_2 = 0;
+                                                               }
+                                                       }
+                                                       if (button & BUTTON_DOWN) {
+                                                               sc_conf.pmax_relay_2 -= 50;
+                                                               if (sc_conf.pmax_relay_2 > PMAX_RELAY) {
+                                                                       sc_conf.pmax_relay_2 = PMAX_RELAY;
+                                                               }
+                                                       }
+                                                       update_relay_intensity();
+                                                       BUTTONS_ACTS_DEFAULT();
+                                                       break;
+                                               case PR3:
+                                                       display_arrows(4, 6, 8);
+                                                       dprintf(5, 1, "PR3. : %4u W", sc_conf.pmax_relay_3);
+                                                       if (button & BUTTON_UP) {
+                                                               sc_conf.pmax_relay_3 += 50;
+                                                               if (sc_conf.pmax_relay_3 > PMAX_RELAY) {
+                                                                       sc_conf.pmax_relay_3 = 0;
+                                                               }
+                                                       }
+                                                       if (button & BUTTON_DOWN) {
+                                                               sc_conf.pmax_relay_3 -= 50;
+                                                               if (sc_conf.pmax_relay_3 > PMAX_RELAY) {
+                                                                       sc_conf.pmax_relay_3 = PMAX_RELAY;
+                                                               }
+                                                       }
+                                                       update_relay_intensity();
+                                                       BUTTONS_ACTS_DEFAULT();
+                                                       break;
+                                       }
+                               }
+                       }
+                       break;
+               case AUTO_FORCE: {
+                               if (sub_menu_level == 1) {
+                                       int i = 0;
+                                       for (i = 0; i < AUTO_FORCE_NB_MENU; i++) {
+                                               if (i != auto_force_cur_entry) {
+                                                       display_line(i + 2, 3, auto_force_titles[i]);
+                                               } else {
+                                                       dprintf(i + 2, 0, " ->%s", auto_force_titles[i]);
+                                               }
+                                       }
+                                       if (button & BUTTON_UP) {
+                                               auto_force_cur_entry -= 1;
+                                       }
+                                       if (button & BUTTON_DOWN) {
+                                               auto_force_cur_entry += 1;
+                                       }
+                                       if (auto_force_cur_entry == 0xFF) {
+                                               auto_force_cur_entry = AUTO_FORCE_NB_MENU - 1;
+                                       } else if (auto_force_cur_entry >= AUTO_FORCE_NB_MENU) {
+                                               auto_force_cur_entry = 0;
+                                       }
+                                       if (button & (BUTTON_OK | BUTTON_RIGHT)) {
+                                               auto_force_cur_menu = auto_force_cur_entry;
+                                               sub_menu_level = 2;
+                                       }
+                                       if (button & BUTTON_LEFT) {
+                                               sub_menu_level = 0;
+                                               current_menu = MAIN_MENU;
+                                       }
+                               } else { /* sub_menu_level == 2 */
+                                       display_line(1, 1, auto_force_titles[auto_force_cur_menu]);
+                                       switch (auto_force_cur_menu) {
+                                               case AUTO_FORCE_DELAY:
+                                                       display_arrows(4, 6, 9);
+                                                       dprintf(5, 1, "Delais: %d s", sc_conf.activation_delay);
+                                                       if (button & BUTTON_UP) {
+                                                               sc_conf.activation_delay += 10;
+                                                               if (sc_conf.activation_delay >= 600) {
+                                                                       sc_conf.activation_delay = 10;
+                                                               }
+                                                       }
+                                                       if (button & BUTTON_DOWN) {
+                                                               sc_conf.activation_delay -= 10;
+                                                               if (sc_conf.activation_delay > 600) {
+                                                                       sc_conf.activation_delay = 600;
+                                                               }
+                                                       }
+                                                       BUTTONS_ACTS_DEFAULT();
+                                                       break;
+                                               case AUTO_FORCE_HYSTERESYS:
+                                                       display_arrows(4, 6, 9);
+                                                       dprintf(5, 1, "Hyst. : %d s", sc_conf.activation_hysteresys);
+                                                       if (button & BUTTON_UP) {
+                                                               sc_conf.activation_hysteresys += 10;
+                                                               if (sc_conf.activation_hysteresys >= 600) {
+                                                                       sc_conf.activation_hysteresys = 10;
+                                                               }
+                                                       }
+                                                       if (button & BUTTON_DOWN) {
+                                                               sc_conf.activation_hysteresys -= 10;
+                                                               if (sc_conf.activation_hysteresys > 600 ) {
+                                                                       sc_conf.activation_hysteresys = 600;
+                                                               }
+                                                       }
+                                                       BUTTONS_ACTS_DEFAULT();
+                                                       break;
+                                       }
+                               }
+                       }
+                       break;
+               case MANUAL_FORCE: {
+                               if (sub_menu_level == 1) {
+                                       int i = 0;
+                                       for (i = 0; i < MANUAL_FORCE_NB_MENU; i++) {
+                                               if (i != manual_force_cur_entry) {
+                                                       display_line(i + 2, 3, manual_force_titles[i]);
+                                               } else {
+                                                       dprintf(i + 2, 0, " ->%s", manual_force_titles[i]);
+                                               }
+                                       }
+                                       if (button & BUTTON_UP) {
+                                               manual_force_cur_entry -= 1;
+                                       }
+                                       if (button & BUTTON_DOWN) {
+                                               manual_force_cur_entry += 1;
+                                       }
+                                       if (manual_force_cur_entry == 0xFF) {
+                                               manual_force_cur_entry = MANUAL_FORCE_NB_MENU - 1;
+                                       } else if (manual_force_cur_entry >= MANUAL_FORCE_NB_MENU) {
+                                               manual_force_cur_entry = 0;
+                                       }
+                                       if (button & (BUTTON_OK | BUTTON_RIGHT)) {
+                                               manual_force_cur_menu = manual_force_cur_entry;
+                                               sub_menu_level = 2;
+                                       }
+                                       if (button & BUTTON_LEFT) {
+                                               sub_menu_level = 0;
+                                               current_menu = MAIN_MENU;
+                                       }
+                               } else { /* sub_menu_level == 2 */
+                                       display_line(1, 1, manual_force_titles[manual_force_cur_menu]);
+                                       switch (manual_force_cur_menu) {
+                                               case MANUAL_FORCE_R1:
+                                                       display_arrows(4, 6, 12);
+                                                       dprintf(5, 1, "Activate R1");
+                                                       if (button & BUTTON_UP) {
+                                                       }
+                                                       if (button & BUTTON_DOWN) {
+                                                       }
+                                                       BUTTONS_ACTS_DEFAULT();
+                                                       break;
+                                               case MANUAL_FORCE_R2:
+                                                       display_arrows(4, 6, 12);
+                                                       dprintf(5, 1, "Activate R2");
+                                                       if (button & BUTTON_UP) {
+                                                       }
+                                                       if (button & BUTTON_DOWN) {
+                                                       }
+                                                       BUTTONS_ACTS_DEFAULT();
+                                                       break;
+                                               case MANUAL_FORCE_R3:
+                                                       display_arrows(4, 6, 12);
+                                                       dprintf(5, 1, "Activate R3");
+                                                       if (button & BUTTON_UP) {
+                                                       }
+                                                       if (button & BUTTON_DOWN) {
+                                                       }
+                                                       BUTTONS_ACTS_DEFAULT();
+                                                       break;
+                                               case MANUAL_FORCE_DURATION:
+                                                       display_arrows(4, 6, 9);
+                                                       dprintf(5, 1, "Duree : %d h", sc_conf.manual_activation_duration);
+                                                       if (button & BUTTON_UP) {
+                                                               sc_conf.manual_activation_duration++;
+                                                               if (sc_conf.manual_activation_duration > 5) {
+                                                                       sc_conf.manual_activation_duration = 1;
+                                                               }
+                                                       }
+                                                       if (button & BUTTON_DOWN) {
+                                                               sc_conf.manual_activation_duration--;
+                                                               if (sc_conf.manual_activation_duration < 1 ) {
+                                                                       sc_conf.manual_activation_duration = 5;
+                                                               }
+                                                       }
+                                                       BUTTONS_ACTS_DEFAULT();
+                                                       break;
+                                       }
+                               }
+                       }
+                       break;
+               case SAVE_RAZ: {
+                               if (sub_menu_level == 1) {
+                                       int i = 0;
+                                       for (i = 0; i < CONF_NB_MENU; i++) {
+                                               if (i != conf_cur_entry) {
+                                                       display_line(i + 2, 3, conf_titles[i]);
+                                               } else {
+                                                       dprintf(i + 2, 0, " ->%s", conf_titles[i]);
+                                               }
+                                       }
+                                       if (button & BUTTON_UP) {
+                                               conf_cur_entry -= 1;
+                                       }
+                                       if (button & BUTTON_DOWN) {
+                                               conf_cur_entry += 1;
+                                       }
+                                       if (conf_cur_entry == 0xFF) {
+                                               conf_cur_entry = CONF_NB_MENU - 1;
+                                       } else if (conf_cur_entry >= CONF_NB_MENU) {
+                                               conf_cur_entry = 0;
+                                       }
+                                       if (button & (BUTTON_OK | BUTTON_RIGHT)) {
+                                               conf_cur_menu = conf_cur_entry;
+                                               sub_menu_level = 2;
+                                       }
+                                       if (button & BUTTON_LEFT) {
+                                               sub_menu_level = 0;
+                                               current_menu = MAIN_MENU;
+                                       }
+                               } else { /* sub_menu_level == 2 */
+                                       display_line(1, 1, conf_titles[conf_cur_menu]);
+                                       switch (conf_cur_menu) {
+                                               case SAVE_CONFIG: {
+                                                               display_line(2, 1, "Sauver config ?");
+                                                               display_line(5, 1, "Valider = OK");
+                                                               display_line(6, 1, "Gauche = Annul");
+                                                               if (button & BUTTON_LEFT) {
+                                                                       sub_menu_level = 0;
+                                                                       current_menu = MAIN_MENU;
+                                                               }
+                                                               if (button & BUTTON_OK) {
+                                                                       int ret = 0;
+                                                                       erase_screen_content();
+                                                                       display_line(4, 2, "Saving ...");
+                                                                       ret = scialys_tor_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: {
+                                                               display_line(2, 1, "Efface Config ?");
+                                                               display_line(5, 1, "Valider = OK");
+                                                               display_line(6, 1, "Gauche = Annul");
+                                                               if (button & BUTTON_LEFT) {
+                                                                       sub_menu_level = 0;
+                                                                       current_menu = MAIN_MENU;
+                                                               }
+                                                               if (button & BUTTON_OK) {
+                                                                       erase_screen_content();
+                                                                       display_line(4, 8, "Chargement ...");
+                                                                       sc_conf.conf_version = 0x00;
+                                                                       scialys_tor_check_config();
+                                                                       uprintf(UART0, "\nSystem defaults reloaded from menu\n");
+                                                                       msleep(500);
+                                                                       sub_menu_level = 0;
+                                                                       current_menu = MAIN_MENU;
+                                                               }
+                                                       }
+                                                       break;
+                                       }
+                               }
+                       }
+                       break;
+               default:
+                       interface_mode = MODE_RUN;
+                       current_menu = MAIN_MENU;
+                       break;
+       }
+       return interface_mode;
+}
+
+
diff --git a/tor/param.h b/tor/param.h
new file mode 100644 (file)
index 0000000..701e038
--- /dev/null
@@ -0,0 +1,35 @@
+/****************************************************************************
+ *   apps/scialys/tor/param.h
+ *
+ * Copyright 2016-2023 Nathael Pajani <nathael.pajani@ed3l.fr>
+ *
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ *************************************************************************** */
+
+#ifndef PARAM_H
+#define PARAM_H
+
+
+/***************************************************************************** */
+/* Configuration menu */
+
+/* Configuraton menu handler
+ * Return MODE_RUN to get back to normal mode, or MODE_CONFIG to stay in configuration mode
+ */
+int config_interface_handle(void);
+
+#endif /* PARAM_H */
+
diff --git a/tor/sensors.c b/tor/sensors.c
new file mode 100644 (file)
index 0000000..fff0f81
--- /dev/null
@@ -0,0 +1,94 @@
+/****************************************************************************
+ *   apps/scialys/tor/sensors.c
+ *
+ * Copyright 2016-2023 Nathael Pajani <nathael.pajani@ed3l.fr>
+ *
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ *************************************************************************** */
+
+#include "core/system.h"
+#include "core/systick.h"
+#include "core/pio.h"
+
+#include "drivers/gpio.h"
+#include "drivers/i2c.h"
+#include "drivers/adc.h"
+#include "drivers/ssp.h"
+
+#include "extdrv/tmp101_temp_sensor.h"
+#include "extdrv/max31855_thermocouple.h"
+
+#include "config.h"
+#include "sensors.h"
+
+
+/***************************************************************************** */
+/* External components */
+
+/* TMP101 I2C temperature sensor on display board */
+#define TMP101_ADDR0  0x90  /* Pin Addr0 (pin5 of tmp101) connected to GND */
+static struct tmp101_sensor_config tmp101_sensor_display = {
+       .bus_num = I2C0,
+       .addr = TMP101_ADDR0,
+       .resolution = TMP_RES_ELEVEN_BITS,
+};
+
+extern uint8_t interface_board_present ;
+
+/*
+ * Read TMP101 sensors (if present)
+ * Return value :
+ *  1 if temperature is read
+ *  -1 on temperature read OK
+ */
+int8_t update_internal_temp(uint32_t uart, int* deci_degrees_disp)
+{
+       int ret = 0;
+
+       if (interface_board_present != 0) {
+               ret = tmp101_sensor_read(&tmp101_sensor_display, NULL, deci_degrees_disp);
+               if (ret != 0) {
+                       uprintf(uart, "TMP101 read error on display board : %d\n", ret);
+                       return -1;
+               }
+       }
+
+       return 1;
+}
+
+
+
+/* TMP101 sensor config on display board */
+int interface_board_temp_sensor_config(uint32_t uart)
+{
+       int ret = 0;
+
+       ret = tmp101_sensor_config(&tmp101_sensor_display);
+       if (ret != 0) {
+               uprintf(uart, "Temp config error on display board: %d\n", ret);
+               return 0;
+       } else {
+               ret = tmp101_sensor_set_continuous_conversion(&tmp101_sensor_display);
+               if (ret != 0) {
+                       uprintf(uart, "Temp config part 2 error on display board: %d\n", ret);
+                       return 0;
+               }
+       }
+       return 1;
+}
+
+
+
diff --git a/tor/sensors.h b/tor/sensors.h
new file mode 100644 (file)
index 0000000..8085b50
--- /dev/null
@@ -0,0 +1,50 @@
+/****************************************************************************
+ *   apps/scialys/tor/sensors.h
+ *
+ * Copyright 2016-2023 Nathael Pajani <nathael.pajani@ed3l.fr>
+ *
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ *************************************************************************** */
+
+#ifndef SENSORS_H
+#define SENSORS_H
+
+#include "core/system.h"
+#include "core/systick.h"
+#include "core/pio.h"
+
+#include "drivers/serial.h"
+#include "drivers/gpio.h"
+#include "drivers/i2c.h"
+
+#include "extdrv/status_led.h"
+
+#include "lib/stdio.h"
+
+
+
+/***************************************************************************** */
+/* Internal Temperature sensors */
+
+#define CONV_OK   1
+int8_t update_internal_temp(uint32_t uart, int* deci_degrees_disp);
+
+/* TMP101 sensor config on display board */
+int interface_board_temp_sensor_config(uint32_t uart);
+
+
+#endif /* SENSORS_H */
+
diff --git a/tor/time.c b/tor/time.c
new file mode 100644 (file)
index 0000000..4f7108c
--- /dev/null
@@ -0,0 +1,98 @@
+/****************************************************************************
+ *   apps/scialys/tor/time.c
+ *
+ * Copyright 2016-2023 Nathael Pajani <nathael.pajani@ed3l.fr>
+ *
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ *************************************************************************** */
+
+#include "core/system.h"
+
+#include "drivers/serial.h"
+#include "drivers/i2c.h"
+
+#include "lib/stdio.h"
+#include "lib/errno.h"
+
+#include "time.h"
+
+
+/***************************************************************************** */
+/* RTC */
+struct rtc_pcf85363a_config rtc_conf = {
+    .mode = PCF85363A_MODE_RTC,
+    .config_marker = PCF85363A_CONFIGURED_1,
+    .batt_ctrl = PCF85363A_CONF_BATT_TH_2_8V,
+};
+/* Oldest acceptable time in RTC. BCD coded. */
+const struct rtc_time oldest = {
+    .year = 0x22,
+    .month = 0x11,
+    .day = 0x10,
+    .hour = 0x13,
+    .min = 0x37,
+};
+int time_valid = 0;
+int rtc_conf_ok = 0;
+
+int scialys_time_init_check(uint32_t uart)
+{
+    int ret = 0;
+    ret = rtc_pcf85363a_config(&rtc_conf);
+       if (ret < 0) {
+               uprintf(uart, "RTC config error: %d\n", ret);
+               return -1;
+       }
+       rtc_conf_ok = 1;
+
+    ret = rtc_pcf85363a_is_up(&rtc_conf, &oldest);
+    if (ret == 1) {
+        /* Time is valid (newer than source code time) */
+        char buff[30];
+        rtc_pcf85363_time_read(&rtc_conf, &now);
+        rtc_pcf85363_time_to_str(&now, buff, 30);
+        /* Debug */
+        uprintf(uart, "Using time from RTC: %s\n", buff);
+        time_valid = 1;
+    } else if (ret == -EFAULT) {
+               /* Time is older than source code time */
+        time_valid = 0;
+               uprintf(uart, "RTC time too old, provide valid time.\n");
+               /* FIXME: remove this, time should come from control master */
+               memcpy(&now, &oldest, sizeof(struct rtc_time));
+               rtc_pcf85363_time_write(&rtc_conf, &now);
+    } else {
+               /* RTC config error */
+               rtc_conf_ok = 0;
+       }
+       return ret;
+}
+
+int scialys_time_update(uint32_t uart, struct rtc_time* now)
+{
+       rtc_pcf85363_time_write(&rtc_conf, now);
+       return 0;
+}
+
+
+/***************************************************************************** */
+/* RTC init */
+void scialys_time_config(uint32_t i2c_bus_num, uint8_t rtc_addr)
+{
+    rtc_conf.bus_num = i2c_bus_num;
+    rtc_conf.addr = rtc_addr;
+}
+
diff --git a/tor/time.h b/tor/time.h
new file mode 100644 (file)
index 0000000..70a509e
--- /dev/null
@@ -0,0 +1,34 @@
+/****************************************************************************
+ *   apps/scialys/tor/time.h
+ *
+ * Copyright 2016-2023 Nathael Pajani <nathael.pajani@ed3l.fr>
+ *
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ *************************************************************************** */
+
+#ifndef RTC_TIME_H
+#define RTC_TIME_H
+
+#include "extdrv/rtc_pcf85363a.h"
+
+extern struct rtc_pcf85363a_config rtc_conf;
+extern struct rtc_time now;
+
+int scialys_time_init_check(uint32_t uart);
+void scialys_time_config(uint32_t i2c_bus_num, uint8_t rtc_addr);
+
+#endif /* RTC_TIME_H */
+
diff --git a/tor/version.h b/tor/version.h
new file mode 100644 (file)
index 0000000..68a3e80
--- /dev/null
@@ -0,0 +1,3 @@
+#define MODULE_VERSION_STR  "v0.1"
+#define SOFT_VERSION_STR  "T"
+#define COMPILE_VERSION "01"