From 3dc47965cb14c83ed2d5dc25e385443bf5d0677c Mon Sep 17 00:00:00 2001 From: Nathael Pajani Date: Sat, 25 Nov 2023 00:02:32 +0100 Subject: [PATCH] Add TOR slave module support (alpha v0.1 version) --- tor/.gitignore | 2 + tor/Makefile | 26 ++ tor/comm.c | 85 +++++++ tor/comm.h | 39 +++ tor/config.c | 416 +++++++++++++++++++++++++++++++ tor/config.h | 164 +++++++++++++ tor/data.h | 84 +++++++ tor/interface.c | 271 +++++++++++++++++++++ tor/interface.h | 84 +++++++ tor/main.c | 296 ++++++++++++++++++++++ tor/param.c | 635 ++++++++++++++++++++++++++++++++++++++++++++++++ tor/param.h | 35 +++ tor/sensors.c | 94 +++++++ tor/sensors.h | 50 ++++ tor/time.c | 98 ++++++++ tor/time.h | 34 +++ tor/version.h | 3 + 17 files changed, 2416 insertions(+) create mode 100644 tor/.gitignore create mode 100644 tor/Makefile create mode 100644 tor/comm.c create mode 100644 tor/comm.h create mode 100644 tor/config.c create mode 100644 tor/config.h create mode 100644 tor/data.h create mode 100644 tor/interface.c create mode 100644 tor/interface.h create mode 100644 tor/main.c create mode 100644 tor/param.c create mode 100644 tor/param.h create mode 100644 tor/sensors.c create mode 100644 tor/sensors.h create mode 100644 tor/time.c create mode 100644 tor/time.h create mode 100644 tor/version.h diff --git a/tor/.gitignore b/tor/.gitignore new file mode 100644 index 0000000..6361591 --- /dev/null +++ b/tor/.gitignore @@ -0,0 +1,2 @@ +dump +logs diff --git a/tor/Makefile b/tor/Makefile new file mode 100644 index 0000000..d3cc3d6 --- /dev/null +++ b/tor/Makefile @@ -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 index 0000000..a3a1c89 --- /dev/null +++ b/tor/comm.c @@ -0,0 +1,85 @@ +/**************************************************************************** + * apps/scialys/tor/comm.c + * + * Copyright 2016-2023 Nathael Pajani + * + * + * 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 . + * + *************************************************************************** */ + +#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 index 0000000..0e9d409 --- /dev/null +++ b/tor/comm.h @@ -0,0 +1,39 @@ +/**************************************************************************** + * apps/scialys/tor/comm.h + * + * Copyright 2016-2023 Nathael Pajani + * + * + * 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 . + * + *************************************************************************** */ + +#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 index 0000000..00b2053 --- /dev/null +++ b/tor/config.c @@ -0,0 +1,416 @@ +/**************************************************************************** + * apps/scialys/tor/config.c + * + * Copyright 2016-2023 Nathael Pajani + * + * + * 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 . + * + *************************************************************************** */ + +#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 index 0000000..4a479d3 --- /dev/null +++ b/tor/config.h @@ -0,0 +1,164 @@ +/**************************************************************************** + * apps/scialys/tor/config.h + * + * Copyright 2016-2023 Nathael Pajani + * + * + * 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 . + * + *************************************************************************** */ + +#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 index 0000000..d9366f2 --- /dev/null +++ b/tor/data.h @@ -0,0 +1,84 @@ +/**************************************************************************** + * apps/scialys/v10/data.h + * + * Copyright 2016-2023 Nathael Pajani + * + * + * 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 . + * + *************************************************************************** */ + +#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 index 0000000..d893e8e --- /dev/null +++ b/tor/interface.c @@ -0,0 +1,271 @@ +/**************************************************************************** + * apps/scialys/tor/interface.c + * + * Copyright 2016-2023 Nathael Pajani + * + * + * 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 . + * + *************************************************************************** */ + +#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 index 0000000..a5a9e56 --- /dev/null +++ b/tor/interface.h @@ -0,0 +1,84 @@ +/**************************************************************************** + * apps/scialys/tor/interface.h + * + * Copyright 2016-2023 Nathael Pajani + * + * + * 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 . + * + *************************************************************************** */ + +#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 index 0000000..45d7d61 --- /dev/null +++ b/tor/main.c @@ -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 + * + * + * 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 . + * + *************************************************************************** */ + + +#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 index 0000000..a26ce37 --- /dev/null +++ b/tor/param.c @@ -0,0 +1,635 @@ +/**************************************************************************** + * apps/scialys/tor/param.c + * + * Copyright 2016-2023 Nathael Pajani + * + * + * 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 . + * + *************************************************************************** */ + +#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 index 0000000..701e038 --- /dev/null +++ b/tor/param.h @@ -0,0 +1,35 @@ +/**************************************************************************** + * apps/scialys/tor/param.h + * + * Copyright 2016-2023 Nathael Pajani + * + * + * 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 . + * + *************************************************************************** */ + +#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 index 0000000..fff0f81 --- /dev/null +++ b/tor/sensors.c @@ -0,0 +1,94 @@ +/**************************************************************************** + * apps/scialys/tor/sensors.c + * + * Copyright 2016-2023 Nathael Pajani + * + * + * 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 . + * + *************************************************************************** */ + +#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 index 0000000..8085b50 --- /dev/null +++ b/tor/sensors.h @@ -0,0 +1,50 @@ +/**************************************************************************** + * apps/scialys/tor/sensors.h + * + * Copyright 2016-2023 Nathael Pajani + * + * + * 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 . + * + *************************************************************************** */ + +#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 index 0000000..4f7108c --- /dev/null +++ b/tor/time.c @@ -0,0 +1,98 @@ +/**************************************************************************** + * apps/scialys/tor/time.c + * + * Copyright 2016-2023 Nathael Pajani + * + * + * 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 . + * + *************************************************************************** */ + +#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 index 0000000..70a509e --- /dev/null +++ b/tor/time.h @@ -0,0 +1,34 @@ +/**************************************************************************** + * apps/scialys/tor/time.h + * + * Copyright 2016-2023 Nathael Pajani + * + * + * 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 . + * + *************************************************************************** */ + +#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 index 0000000..68a3e80 --- /dev/null +++ b/tor/version.h @@ -0,0 +1,3 @@ +#define MODULE_VERSION_STR "v0.1" +#define SOFT_VERSION_STR "T" +#define COMPILE_VERSION "01" -- 2.43.0