to prevent recompilation when re-building all apps.
These may not build anymore (though I updated some today to allow re-compilation)
+++ /dev/null
-# 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} $@
-
+++ /dev/null
-DMX module for solar-panel power generation tracking and fair use.
-
-Copyright 2015 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/>.
- *
- *************************************************************************** */
-
-First prototype version of the Scialys module.
-Used for validation of feasability
-
+++ /dev/null
-/****************************************************************************
- * apps/scialys/alpha/main.c
- *
- * DMX module for solar-panel power generation tracking and fair use.
- *
- * Copyright 2015-2016 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/>.
- *
- *************************************************************************** */
-
-
-/* DMX informations :
- * https://en.wikipedia.org/wiki/DMX512
- *
- * DMX uses an RS485 line with a few glitches (the break and start pulse).
- * The DMX module uses an ADM2482 isolated RS485 bridge from analog devices.
- */
-
-#include "core/system.h"
-#include "core/pio.h"
-#include "core/systick.h"
-#include "lib/stdio.h"
-#include "drivers/serial.h"
-#include "drivers/gpio.h"
-#include "extdrv/status_led.h"
-#include "drivers/adc.h"
-#include "drivers/ssp.h"
-
-#include "extdrv/max31855_thermocouple.h"
-
-
-#define MODULE_VERSION 0x02
-#define MODULE_NAME "DMX Module"
-
-
-#define SELECTED_FREQ FREQ_SEL_48MHz
-
-/* Period of the decrementer handler from the systick interrupt */
-#define DEC_PERIOD 100
-
-/* If temperature falls bellow FORCE_HEATER_TEMP value, we enter forced heater mode, until
- * TARGET_FORCED_HEATER_TEMP is reached.
- * When in forced heater mode, the heated is controlled to heat at FORCED_MODE_VALUE which
- * is between 0 and 255.
- */
-#define FORCE_HEATER_TEMP 40
-#define TARGET_FORCED_HEATER_TEMP 45
-#define NO_FORCED_HEATING_ON_SUNNY_DAYS 750
-#define FORCED_MODE_VALUE 190 /* A fraction of 255 */
-uint32_t forced_heater_mode = 0;
-uint32_t forced_heater_delay = 0;
-uint32_t forced_heater_time = 0;
-
-#define FORCED_HEATER_DELAY (7 * 3600 * 1000 / DEC_PERIOD) /* Delay before automatic forced heating */
-#define FORCED_HEATER_DURATION (3 * 3600 * 1000 / DEC_PERIOD) /* Duration of automatic forced heating */
-
-#define MANUAL_ACTIVATION_DURATION (3600 * 1000 / DEC_PERIOD) /* One hour */
-
-uint32_t never_force = 0;
-
-/***************************************************************************** */
-/* Pins configuration */
-/* pins blocks are passed to set_pins() for pins configuration.
- * Unused pin blocks can be removed safely with the corresponding set_pins() call
- * All pins blocks may be safelly merged in a single block for single set_pins() call..
- */
-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 },
- { LPC_UART0_RTS_PIO_0_0, LPC_IO_DIGITAL },
- /* UART 1 */
- { LPC_UART1_RX_PIO_0_8, LPC_IO_DIGITAL },
- { LPC_UART1_TX_PIO_0_9, LPC_IO_DIGITAL },
- /* SPI */
- { LPC_SSP0_SCLK_PIO_0_14, LPC_IO_DIGITAL },
- { LPC_SSP0_MOSI_PIO_0_17, LPC_IO_DIGITAL },
- { LPC_SSP0_MISO_PIO_0_16, LPC_IO_DIGITAL },
- /* GPIO */
- { LPC_GPIO_0_12, LPC_IO_DIGITAL },
- { LPC_GPIO_0_25, LPC_IO_DIGITAL },
- { LPC_GPIO_0_26, LPC_IO_DIGITAL },
- { LPC_GPIO_0_28, LPC_IO_DIGITAL },
- { LPC_GPIO_0_29, LPC_IO_DIGITAL },
- ARRAY_LAST_PIO,
-};
-
-const struct pio_config adc_pins[] = {
- { LPC_ADC_AD1_PIO_0_31, LPC_IO_ANALOG }, /* ADC1 */
- { LPC_ADC_AD2_PIO_1_0, LPC_IO_ANALOG }, /* ADC2 */
- ARRAY_LAST_PIO,
-};
-
-const struct pio status_led_green = LPC_GPIO_0_28;
-const struct pio status_led_red = LPC_GPIO_0_29;
-
-const struct pio button = LPC_GPIO_0_12; /* ISP button */
-const struct pio ejp_in_pin = LPC_GPIO_0_25;
-#define DAY_IS_EJP 0 /* Input is pulled low when EJP is ON */
-int ejp_in = 0;
-
-/* Thermocouple reading */
-const struct max31855_sensor_config thermo = {
- .ssp_bus_num = 0,
- .chip_select = LPC_GPIO_0_26,
-};
-
-/* DMX signals */
-/* DMX signals are on LPC_GPIO_0_0 for Frame enable pin (RS485 enable) and LPC_GPIO_0_2 for
- * Frame Tx pin (RS485 Tx).
- */
-const struct pio frame_tx_pin = LPC_GPIO_0_2;
-const struct pio frame_en_pin = LPC_GPIO_0_0;
-
-
-
-/***************************************************************************** */
-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 = 0x0FFFFFF, /* 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);
- set_pins(adc_pins);
- gpio_on();
- status_led_config(&status_led_green, &status_led_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);
-}
-
-
-/* Put any data received from RS485 UART in rs485_rx_buff
- */
-void rs485_rx(uint8_t c)
-{
-}
-
-void cmd_rx(uint8_t c)
-{
-}
-
-void dmx_send_frame(uint8_t start_code, uint8_t* slots, uint16_t nb_slots)
-{
- uint16_t sent = 0;
-
- /* Configure pins for Start of frame transmission */
- config_gpio(&frame_tx_pin, LPC_IO_MODE_PULL_UP, GPIO_DIR_OUT, 0);
- config_gpio(&frame_en_pin, LPC_IO_MODE_PULL_UP, GPIO_DIR_OUT, 1);
- /* Send break : 92us minimum
- * Pins default state chosen to generate the break condition */
- usleep(100);
- /* Send Mark-After-Break (MAB) of 12us minimum */
- gpio_set(frame_tx_pin);
- usleep(12);
-
- /* Configure back for RS485.
- * This is done using direct access to the registers as this step is time critical
- */
- /* Make sure IO_Config is clocked */
- io_config_clk_on();
- LPC_IO_CONTROL->pio0_0 = (LPC_IO_FUNC_ALT(2) | LPC_IO_DIGITAL);
- LPC_IO_CONTROL->pio0_2 = (LPC_IO_FUNC_ALT(2) | LPC_IO_DIGITAL);
-
-
- /* Send start code */
- serial_send_quickbyte(0, start_code);
-
- /* And send slots data */
- while (sent < nb_slots) {
- int tmp = serial_write(0, (char*)(slots + sent), (nb_slots - sent));
- if (tmp == -1) {
- break;
- }
- sent += tmp;
- }
-
- /* Config done, power off IO_CONFIG block */
- io_config_clk_off();
-}
-
-
-uint32_t manual_activation_request = 0;
-void manual_activation(uint32_t gpio) {
- manual_activation_request = MANUAL_ACTIVATION_DURATION;
-}
-void handle_dec_request(uint32_t curent_tick) {
- if (manual_activation_request > 0) {
- manual_activation_request--;
- }
- if (forced_heater_mode == 1) {
- if (forced_heater_delay > 0) {
- forced_heater_delay--;
- }
- if (forced_heater_time > 0) {
- forced_heater_time--;
- }
- }
-}
-
-
-/* DMX */
-#define DMX_NB_SLOTS 4
-#define NB_VAL 20
-
-
-enum modes {
- heat = 'C',
- ejp = 'E',
- no_heat_prod = 'P',
- forced = 'F',
- temp_OK = 'T',
- manual = 'M',
- idle_heat = 'L',
- full_heat = 'F',
-};
-
-/***************************************************************************** */
-int main(void)
-{
- uint16_t isnail_solar_values[NB_VAL];
- uint16_t isnail_home_values[NB_VAL];
- uint8_t slots[DMX_NB_SLOTS + 1];
- uint8_t idx = 0;
- uint32_t loop = 0;
- char mode = heat; /* Debug info */
-
- system_init();
- status_led(red_only);
- uart_set_config(0, (LPC_UART_8BIT | LPC_UART_NO_PAR | LPC_UART_2STOP));
- uart_on(UART0, 250000, rs485_rx); /* FIXME : configure for 250 kbits/s and 8N2 */
- uart_on(UART1, 115200, cmd_rx);
- ssp_master_on(thermo.ssp_bus_num, LPC_SSP_FRAME_SPI, 8, 4*1000*1000);
- adc_on(NULL);
-
- /* RS485 config */
- if (1) {
- uint32_t rs485_ctrl = LPC_RS485_ENABLE;
- //rs485_ctrl |= LPC_RS485_DIR_PIN_RTS | LPC_RS485_AUTO_DIR_EN | LPC_RS485_DIR_CTRL_INV;
- rs485_ctrl |= LPC_RS485_DIR_PIN_RTS | LPC_RS485_AUTO_DIR_EN | LPC_RS485_DIR_CTRL_INV;
- uart_set_mode_rs485(0, rs485_ctrl, 0, 1);
- }
-
- /* Thermocouple configuration */
- max31855_sensor_config(&thermo);
- uprintf(UART1, "Thermocouple config OK\n");
-
- /* Activate on Rising edge (button release) */
- set_gpio_callback(manual_activation, &button, EDGE_RISING);
-
- /* Start ADC sampling */
- adc_start_burst_conversion(ADC_MCH(1) | ADC_MCH(2), LPC_ADC_SEQ(0));
-
- /* Configure Input GPIO for EJP mode detection */
- config_gpio(&ejp_in_pin, 0, GPIO_DIR_IN, 0);
-
- status_led(green_only);
- memset(slots, 0, (DMX_NB_SLOTS + 1));
-
- msleep(50);
- /* Read parameters from memory */
- if (1) {
- never_force = 0;
- forced_heater_delay = FORCED_HEATER_DELAY;
- forced_heater_time = FORCED_HEATER_DURATION;
- }
-
- while (1) {
- static uint8_t dmx_val = 0;
- uint32_t moyenne_solar = 0;
- uint32_t moyenne_home = 0;
- uint16_t isnail_val_solar = 0;
- uint16_t isnail_val_home = 0;
- int centi_degrees = 0;
-
- mode = heat;
- /* Always track power consumption and production */
- adc_get_value(&isnail_val_solar, LPC_ADC(1));
- adc_get_value(&isnail_val_home, LPC_ADC(2));
- /* Convert to mA value */
- isnail_val_solar = ((isnail_val_solar * 32) * 2); /* 3.2mV / digit, 50mV -> 1A */
- isnail_val_home = ((isnail_val_home * 32) * 2); /* 3.2mV / digit, 50mV -> 1A */
- /* Store value */
- isnail_solar_values[idx] = isnail_val_solar;
- isnail_home_values[idx++] = isnail_val_home;
- if (idx == NB_VAL) {
- idx = 0;
- }
- /* Compute average value when we sampled enough values */
- if ((idx == 0) || (idx == (NB_VAL / 2))) {
- int i = 0;
- for (i = 0; i < NB_VAL; i++) {
- moyenne_solar += isnail_solar_values[i];
- moyenne_home += isnail_home_values[i];
- }
- moyenne_solar = moyenne_solar / NB_VAL;
- moyenne_home = moyenne_home / NB_VAL;
- } else {
- /* Sleep for a litle more than a period (20ms at 50Hz) */
- msleep(23);
- continue;
- }
-
- /* Feed the dog */
- if ((moyenne_solar != 0) && (moyenne_home != 0)) {
- watchdog_feed();
- }
-
- /* Get thermocouple value */
- if (1) {
- int ret = 0;
- ret = max31855_sensor_read(&thermo, NULL, ¢i_degrees);
- if (ret != 0) {
- uprintf(UART1, "Temp read error : %d\n", ret);
- }
- }
- if (centi_degrees < (FORCE_HEATER_TEMP * 100)) {
- if (forced_heater_mode == 0) {
- uprintf(UART1, "Entering forced mode\n");
- forced_heater_mode = 1;
- }
- status_led(red_on);
- mode = forced;
- } else if ((centi_degrees > (TARGET_FORCED_HEATER_TEMP * 100)) && (forced_heater_mode == 1)) {
- status_led(red_off);
- forced_heater_mode = 0;
- dmx_val = 0;
- uprintf(UART1, "Forced mode exit\n");
- mode = temp_OK;
- }
-
- /* Do not force if there is some sun, it may be enough to heat again */
- if (moyenne_solar > NO_FORCED_HEATING_ON_SUNNY_DAYS) {
- forced_heater_mode = 0;
- mode = no_heat_prod;
- forced_heater_delay = FORCED_HEATER_DELAY;
- }
-
- /* Do not force heating if this is an EJP day */
- ejp_in = gpio_read(ejp_in_pin);
- if (ejp_in == DAY_IS_EJP) {
- forced_heater_mode = 0;
- mode = ejp;
- }
-
- if (never_force == 1) {
- forced_heater_mode = 0;
- }
-
- /* Did the user request a forced heating ? */
- if (manual_activation_request > 1) {
- forced_heater_mode = 1;
- mode = manual;
- if (manual_activation_request == MANUAL_ACTIVATION_DURATION) {
- uprintf(UART1, "Entering manual forced mode for %d ticks\n", manual_activation_request);
- /* Add a systick callback to handle time counting */
- add_systick_callback(handle_dec_request, DEC_PERIOD);
- }
- if (manual_activation_request < 10) {
- uprintf(UART1, "Leaving manual forced mode\n");
- manual_activation_request = 0;
- remove_systick_callback(handle_dec_request);
- }
- }
-
-
- /* Which is the current mode ? */
- if (forced_heater_mode == 1) {
- /* Forced heating mode */
- if ((forced_heater_delay == 0) && (forced_heater_time > 0)) {
- dmx_val = FORCED_MODE_VALUE;
- }
- if (forced_heater_time == 0) {
- forced_heater_delay = FORCED_HEATER_DELAY;
- forced_heater_time = FORCED_HEATER_DURATION;
- }
- } else if (moyenne_solar < moyenne_home) {
- /* Low production mode */
- if (dmx_val > 25) {
- dmx_val -= 25;
- } else {
- dmx_val = 0;
- mode = idle_heat;
- }
- status_led(green_off);
- } else {
- /* High production mode */
- if (dmx_val < 245) {
- dmx_val += 10;
- } else {
- dmx_val = 255;
- mode = full_heat;
- }
- status_led(green_on);
- }
-
- /* Send DMX frame */
- slots[0] = dmx_val;
- if (slots[0] > 255) {
- slots[0] = 255;
- }
- dmx_send_frame(0x00, slots, 1);
- /* Display */
- if (1) {
- int abs_centi = centi_degrees;
- uprintf(UART1, "%c:%d - Is: %d,%04d - Ih: %d,%04d\n", mode, loop++,
- (moyenne_solar / 1000), (moyenne_solar % 1000),
- (moyenne_home / 1000), (moyenne_home % 1000));
- if (centi_degrees < 0) {
- abs_centi = -centi_degrees;
- }
- uprintf(UART1, "Temp : % 4d.%02d\n", (centi_degrees / 100), (abs_centi % 100));
- uprintf(UART1, "DMX: %d\n\n", slots[0]);
- }
- }
- return 0;
-}
-
-
-
+++ /dev/null
-# 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} $@
-
+++ /dev/null
-Scialys system for solar-panel power generation tracking and fair use.
-
-Copyright 2016 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/>.
- *
- *************************************************************************** */
-
-Beta version of Scialys system.
+++ /dev/null
-/****************************************************************************
- * apps/scialys/beta/main.c
- *
- * Scialys system for solar-panel power generation tracking and fair use.
- *
- * Copyright 2016 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 "lib/stdio.h"
-#include "lib/errno.h"
-#include "drivers/serial.h"
-#include "drivers/gpio.h"
-#include "drivers/adc.h"
-#include "drivers/ssp.h"
-#include "drivers/i2c.h"
-#include "drivers/timers.h"
-
-#include "extdrv/status_led.h"
-#include "extdrv/ws2812.h"
-#include "extdrv/max31855_thermocouple.h"
-#include "extdrv/tmp101_temp_sensor.h"
-#include "extdrv/rtc_pcf85363a.h"
-#include "extdrv/ssd130x_oled_driver.h"
-#include "extdrv/ssd130x_oled_buffer.h"
-#include "lib/font.h"
-#include "lib/time.h"
-
-#include "extdrv/sdmmc.h"
-
-#define MODULE_VERSION 0x01
-#define MODULE_NAME "Scialys uC"
-
-
-#define SELECTED_FREQ FREQ_SEL_48MHz
-
-
-/***************************************************************************** */
-/* System configuration
- * Most of the defines in here should go to configuration setting in user flash
- */
-
-/* Period of the decrementer handler from the systick interrupt */
-#define DEC_PERIOD 100
-
-/* If temperature falls bellow FORCE_HEATER_TEMP value, we enter forced heater mode, until
- * TARGET_FORCED_HEATER_TEMP is reached.
- * When in forced heater mode, the heater is controlled to heat at FORCED_MODE_VALUE which
- * is between 0 and 100.
- */
-#define FORCE_HEATER_TEMP 35
-#define MAX_HEATER_TEMP 90
-#define TARGET_FORCED_HEATER_TEMP 50
-#define FORCED_MODE_VALUE 75 /* A fraction of 100 */
-/* mA prod value above which the system will not enter forced mode, waiting for home
- * to stop using power to start automatic heating */
-#define NO_FORCED_HEATING_ON_SUNNY_DAYS 750
-
-uint32_t forced_heater_mode = 0;
-uint32_t forced_heater_delay = 0;
-uint32_t forced_heater_time = 0;
-
-#define FORCED_HEATER_DELAY (2 * 3600 * 1000 / DEC_PERIOD) /* Delay before automatic forced heating */
-#define FORCED_HEATER_DURATION (3 * 3600 * 1000 / DEC_PERIOD) /* Duration of automatic forced heating */
-
-#define MANUAL_ACTIVATION_DURATION (3 * 3600 * 1000 / DEC_PERIOD) /* Three hours */
-
-uint32_t never_force = 0;
-
-
-#define DAY_IS_EJP 0 /* Input is pulled low when EJP is ON */
-int ejp_in = 0;
-
-
-
-/***************************************************************************** */
-/* Pins configuration */
-/* pins blocks are passed to set_pins() for pins configuration.
- * Unused pin blocks can be removed safely with the corresponding set_pins() call
- * All pins blocks may be safelly merged in a single block for single set_pins() call..
- */
-const struct pio_config common_pins[] = {
- /* UART 0 : Config / Debug / USB */
- { LPC_UART0_RX_PIO_0_1, LPC_IO_DIGITAL },
- { LPC_UART0_TX_PIO_0_2, LPC_IO_DIGITAL },
- /* UART 1 : UEXT */
- { LPC_UART1_RX_PIO_0_8, LPC_IO_DIGITAL },
- { LPC_UART1_TX_PIO_0_9, LPC_IO_DIGITAL },
- /* I2C : RTC, Display, UEXT */
- { 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) },
- /* SPI (Thermocouple + uSD card + UEXT) */
- { LPC_SSP0_SCLK_PIO_0_14, LPC_IO_DIGITAL },
- { LPC_SSP0_MOSI_PIO_0_17, LPC_IO_DIGITAL },
- { LPC_SSP0_MISO_PIO_0_16, LPC_IO_DIGITAL },
- /* TIMER_32B0 */
- { LPC_TIMER_32B0_M0_PIO_0_18, (LPC_IO_MODE_PULL_UP | LPC_IO_DIGITAL) }, /* Fan control */
- /* GPIO */
- { LPC_GPIO_0_0, LPC_IO_DIGITAL }, /* Clkout / interrupt from RTC */
- { LPC_GPIO_0_3, LPC_IO_DIGITAL }, /* EJP / External switch input */
- { LPC_GPIO_0_4, LPC_IO_DIGITAL }, /* Zero crossing detection input */
- { LPC_GPIO_0_5, LPC_IO_DIGITAL }, /* Temperature driver warning (Mosfet board only) */
- { LPC_GPIO_0_6, LPC_IO_DIGITAL }, /* Mosfet driver Shutdown (Mosfet board only) */
- { LPC_GPIO_0_7, LPC_IO_DIGITAL }, /* Mosfet / Triac control */
- { LPC_GPIO_0_12, LPC_IO_DIGITAL }, /* ISP / User button OK */
- { LPC_GPIO_0_15, LPC_IO_DIGITAL }, /* Thermocouple chip select */
- { LPC_GPIO_0_23, LPC_IO_DIGITAL }, /* WS2812B RGB Leds control */
- { LPC_GPIO_0_26, LPC_IO_DIGITAL }, /* User button B2 */
- { LPC_GPIO_0_27, LPC_IO_DIGITAL }, /* User button B1 */
- { LPC_GPIO_0_28, LPC_IO_DIGITAL }, /* Charge State */
- { LPC_GPIO_1_1, LPC_IO_DIGITAL }, /* Uext Chip select / Module eeprom select */
- { LPC_GPIO_1_6, LPC_IO_DIGITAL }, /* uSD Card SPI Chip Select */
- ARRAY_LAST_PIO,
-};
-
-const struct pio_config adc_pins[] = {
- { LPC_ADC_AD0_PIO_0_30, LPC_IO_ANALOG }, /* ADC0 */
- { LPC_ADC_AD1_PIO_0_31, LPC_IO_ANALOG }, /* ADC1 */
- { LPC_ADC_AD2_PIO_1_0, LPC_IO_ANALOG }, /* ADC2 */
- { LPC_ADC_AD7_PIO_1_5, LPC_IO_ANALOG }, /* ADC3 */
- ARRAY_LAST_PIO,
-};
-
-const struct pio status_led_green = LPC_GPIO_1_2;
-const struct pio status_led_red = LPC_GPIO_1_3;
-
-/* Inputs */
-/* Buttons */
-const struct pio button_ok = LPC_GPIO_0_12;
-const struct pio button_b1 = LPC_GPIO_0_27;
-const struct pio button_b2 = LPC_GPIO_0_26;
-/* External signals */
-const struct pio rtc_in_pin = LPC_GPIO_0_0;
-const struct pio ejp_in_pin = LPC_GPIO_0_3;
-const struct pio zero_cross_in_pin = LPC_GPIO_0_4;
-const struct pio th_warn_in_pin = LPC_GPIO_0_5;
-const struct pio charge_status_in_pin = LPC_GPIO_0_28;
-
-/* Outputs */
-/* Led control data pin */
-const struct pio ws2812_data_out_pin = LPC_GPIO_0_23;
-/* AC output control (Mosfet / Triac) */
-const struct pio ac_ctrl = LPC_GPIO_0_7;
-/* Fixme : Fan */
-
-
-/* Thermocouple reading */
-const struct max31855_sensor_config thermo = {
- .ssp_bus_num = 0,
- .chip_select = LPC_GPIO_0_15,
-};
-
-/* TMP101 onboard I2C temperature sensor */
-#define TMP101_ADDR 0x94 /* Pin Addr0 (pin5 of tmp101) connected to VCC */
-struct tmp101_sensor_config tmp101_sensor = {
- .bus_num = I2C0,
- .addr = TMP101_ADDR,
- .resolution = TMP_RES_ELEVEN_BITS,
-};
-
-
-#define FAN_ON (10 * 1000)
-const struct lpc_timer_pwm_config fan_pwm_conf = {
- .nb_channels = 1,
- .period_chan = CHAN3,
- .period = FAN_ON,
- .outputs = { CHAN0, },
- .match_values = { 0, },
-};
-
-const struct lpc_tc_config ac_timer_conf = {
- .mode = LPC_TIMER_MODE_TIMER | LPC_TIMER_MODE_MATCH,
- .match_control = { 0, LPC_TIMER_INT_RESET_AND_STOP_ON_MATCH, 0, 0, },
- .match = { 0, 10, 0, 0, },
- .ext_match_config = { 0, LPC_TIMER_SET_ON_MATCH, 0, 0, },
-};
-
-
-/***************************************************************************** */
-/* SD/MMC Card */
-struct sdmmc_card micro_sd = {
- .ssp_bus_num = SSP_BUS_0,
- .card_type = MMC_CARDTYPE_UNKNOWN,
- .block_size = 64,
- .chip_select = LPC_GPIO_1_6,
-};
-
-uint8_t mmc_data[MMC_MAX_SECTOR_SIZE];
-
-
-/***************************************************************************** */
-/* RTC and time */
-#define RTC_ADDR 0xA2
-struct rtc_pcf85363a_config rtc_conf = {
- .bus_num = I2C0,
- .addr = RTC_ADDR,
- .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 = 0x18,
- .month = 0x09,
- .day = 0x08,
- .hour = 0x18,
- .min = 0x00,
-};
-static struct rtc_time now;
-
-
-/***************************************************************************** */
-/* 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);
- set_pins(adc_pins);
- gpio_on();
- status_led_config(&status_led_green, &status_led_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);
-}
-
-
-/***************************************************************************** */
-/* System configuration over USB */
-static uint32_t fan_speed = 0;
-static int act_cmd = 0;
-void config_rx(uint8_t c)
-{
- /* FAN control */
- if (c == 'f') {
- fan_speed = 100;
- timer_set_match(LPC_TIMER_32B0, CHAN0, FAN_ON);
- } else if (c == 'z') {
- fan_speed = 0;
- timer_set_match(LPC_TIMER_32B0, CHAN0, 0);
- } else if ((c >= '0') && (c <= '9')) {
- fan_speed = ((c - '0') * 10);
- if (fan_speed < 60) {
- fan_speed = 0;
- }
- timer_set_match(LPC_TIMER_32B0, CHAN0, (FAN_ON - ((FAN_ON / 100) * (100 - fan_speed))));
- }
-}
-
-
-
-/***************************************************************************** */
-/* System communication over UART1 */
-void cmd_rx(uint8_t c)
-{
-}
-
-
-/***************************************************************************** */
-
-void set_ctrl_duty_cycle(uint8_t value)
-{
- act_cmd = value;
- if (act_cmd > 100) {
- /* 100 is the maximum allowed value */
- act_cmd = 100;
- } else if (act_cmd <= 2) {
- /* Below 3% there are triggering problems which lead to 50% instead of 1% or 2% */
- act_cmd = 0;
- }
-}
-
-static volatile uint8_t ac_ctrl_state = 0;
-void ac_switch_on(uint32_t flags)
-{
- /* Generate a 5us (approx) pulse */
- if (ac_ctrl_state == 1) {
- /* Start of pulse */
- gpio_set(ac_ctrl);
- /* Change state machine state */
- ac_ctrl_state = 0;
- /* And request interrupt in approx 5us to clear ac_ctrl output */
- timer_set_match(LPC_TIMER_32B1, CHAN1, (5 * 480));
- timer_restart(LPC_TIMER_32B1);
- } else {
- /* End of pulse */
- gpio_clear(ac_ctrl);
- }
-}
-
-static uint32_t clk_cycles_ac_zc = 0;
-static volatile uint32_t zc_count = 0; /* Wraps every 1.36 year ... */
-void zero_cross(uint32_t gpio)
-{
- uint32_t delay = 0;
-
- zc_count ++;
-
- if (act_cmd == 100) {
- gpio_set(ac_ctrl);
- return;
- }
- gpio_clear(ac_ctrl);
- if (act_cmd == 0) {
- return;
- }
- /* Set timer to trigger ac out ON at given delay */
- delay = clk_cycles_ac_zc * (100 - act_cmd);
- timer_set_match(LPC_TIMER_32B1, CHAN1, delay);
- timer_restart(LPC_TIMER_32B1);
- /* Set "AC triggering" stame machine state */
- ac_ctrl_state = 1;
-}
-
-
-
-/***************************************************************************** */
-/* System interface */
-enum buttons {
- BUTTON_NONE = 0,
- BUTTON_OK,
- BUTTON_UP,
- BUTTON_DOWN,
-};
-
-
-uint32_t manual_activation_request = 0;
-uint8_t button_pressed = 0;
-void manual_activation(uint32_t gpio) {
- manual_activation_request = MANUAL_ACTIVATION_DURATION;
- button_pressed = BUTTON_OK;
-}
-void manual_up(uint32_t gpio) {
- manual_activation_request = MANUAL_ACTIVATION_DURATION;
- button_pressed = BUTTON_UP;
-}
-void manual_down(uint32_t gpio) {
- manual_activation_request = MANUAL_ACTIVATION_DURATION;
- button_pressed = BUTTON_DOWN;
-}
-void handle_dec_request(uint32_t curent_tick) {
- if (manual_activation_request > 0) {
- manual_activation_request--;
- }
- if (forced_heater_mode == 1) {
- if (forced_heater_delay > 0) {
- forced_heater_delay--;
- }
- if (forced_heater_time > 0) {
- forced_heater_time--;
- }
- }
-}
-
-
-/***************************************************************************** */
-void temp_config(int uart_num)
-{
- int ret = 0;
- ret = tmp101_sensor_config(&tmp101_sensor);
- if (ret != 0) {
- uprintf(uart_num, "Temp config error: %d\n", ret);
- }
-}
-
-
-/***************************************************************************** */
-/* Oled Display */
-#define DISPLAY_ADDR 0x78
-static uint8_t gddram[ 4 + GDDRAM_SIZE ];
-struct oled_display display = {
- .bus_type = SSD130x_BUS_I2C,
- .address = DISPLAY_ADDR,
- .bus_num = I2C0,
- .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);
-}
-#define OLED_LINE_CHAR_LENGTH (SSD130x_NB_COL / 8)
-#define DISPLAY_LINE_LENGTH (OLED_LINE_CHAR_LENGTH + 1)
-int display_line(uint8_t line, uint8_t col, 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) {
- return i;
- }
- }
- }
- return len;
-}
-
-
-
-
-/***************************************************************************** */
-#define NB_VAL 20
-
-enum modes {
- heat = 'C',
- ejp = 'E',
- delayed_heat_prod = 'P',
- forced = 'F',
- temp_OK = 'T',
- manual = 'M',
- idle_heat = 'L',
- full_heat = 'F',
-};
-
-/***************************************************************************** */
-int main(void)
-{
- uint16_t isnail_solar_values[NB_VAL];
- uint16_t isnail_home_values[NB_VAL];
- uint8_t idx = 0;
- uint32_t loop = 0;
- char mode = heat; /* Debug info */
- int ret = 0;
-
- system_init();
- status_led(red_only);
- uart_on(UART0, 115200, config_rx);
- uart_on(UART1, 115200, cmd_rx);
- i2c_on(I2C0, I2C_CLK_100KHz, I2C_MASTER);
- ssp_master_on(thermo.ssp_bus_num, LPC_SSP_FRAME_SPI, 8, 4*1000*1000);
- adc_on(NULL);
- timer_on(LPC_TIMER_32B0, 0, NULL);
- timer_on(LPC_TIMER_32B1, 0, ac_switch_on);
-
- /* Immediatly turn off Mosfet / Triac */
- config_gpio(&ac_ctrl, 0, GPIO_DIR_OUT, 0);
-
- /* Thermocouple configuration */
- max31855_sensor_config(&thermo);
- uprintf(UART0, "Thermocouple config done\n");
-
- /* TMP101 sensor config */
- temp_config(UART0);
-
- /* Activate on Rising edge (button release) */
- set_gpio_callback(manual_activation, &button_ok, EDGE_RISING);
-#if 0
- /* Debug */
- config_gpio(&button_b1, 0, GPIO_DIR_OUT, 0);
- config_gpio(&button_b2, 0, GPIO_DIR_OUT, 0);
-#endif
- set_gpio_callback(manual_up, &button_b1, EDGE_RISING);
- set_gpio_callback(manual_down, &button_b2, EDGE_RISING);
-
- /* Zero cross and alert pin */
- set_gpio_callback(zero_cross, &zero_cross_in_pin, EDGE_FALLING);
-
- /* Start ADC sampling */
- adc_start_burst_conversion(ADC_MCH(0) | ADC_MCH(1) | ADC_MCH(2) | ADC_MCH(7), LPC_ADC_SEQ(0));
-
- /* Configure Input GPIO */
- config_gpio(&ejp_in_pin, 0, GPIO_DIR_IN, 0);
- config_gpio(&rtc_in_pin, 0, GPIO_DIR_IN, 0);
- config_gpio(&charge_status_in_pin, 0, GPIO_DIR_IN, 1);
-
- /* WS2812B Leds on display board */
- ws2812_config(&ws2812_data_out_pin);
-
- /* FAN Config */
- timer_pwm_config(LPC_TIMER_32B0, &fan_pwm_conf);
- timer_start(LPC_TIMER_32B0);
- /* AC Switch Config */
- timer_counter_config(LPC_TIMER_32B1, &ac_timer_conf);
- /* We want 100 Hz (50 Hz but two zero crossings) with 1% granularity */
- clk_cycles_ac_zc = get_main_clock() / (100 * 100);
-
- status_led(green_only);
-
- /* Configure and start display */
- ret = ssd130x_display_on(&display);
- /* Erase screen */
- ssd130x_buffer_set(gddram, 0x00);
- ret = ssd130x_display_full_screen(&display);
-
- /* RTC init */
- ret = rtc_pcf85363a_config(&rtc_conf);
- ret = rtc_pcf85363a_is_up(&rtc_conf, &oldest);
- if (ret == 1) {
- char buff[30];
- rtc_pcf85363_time_read(&rtc_conf, &now);
- rtc_pcf85363_time_to_str(&now, buff, 30);
- /* Debug */
- uprintf(UART0, buff);
- } else if (ret == -EFAULT) {
- memcpy(&now, &oldest, sizeof(struct rtc_time));
- rtc_pcf85363_time_write(&rtc_conf, &now);
- }
-
- /* microSD card init */
- ret = sdmmc_init(µ_sd);
- if (ret == 0) {
- msleep(1);
- ret = sdmmc_init_wait_card_ready(µ_sd);
- if (ret == 0) {
- ret = sdmmc_init_end(µ_sd);
- }
- }
- uprintf(UART0, "uSD init: %d, type: %d, bs: %d\n", ret, micro_sd.card_type, micro_sd.block_size);
- ret = sdmmc_read_block(µ_sd, 0, mmc_data);
- uprintf(UART0, "uSD read: %s\n", mmc_data);
-
- /* Add a systick callback to handle time counting */
- //add_systick_callback(handle_dec_request, DEC_PERIOD);
-
- msleep(50);
- /* Read parameters from memory */
- if (1) {
- never_force = 0;
- forced_heater_delay = 0;
- forced_heater_time = FORCED_HEATER_DURATION;
- }
-
- while (1) {
- static uint8_t command_val = 0;
- static uint8_t n_dec = 0; /* Add some PID like (derivative part) */
- static uint8_t n_inc = 0; /* Add some PID like (derivative part) */
- int moyenne_solar = 0;
- int moyenne_home = 0;
- uint16_t isnail_val_solar = 0;
- uint16_t isnail_val_home = 0;
- uint16_t acs_val_load = 0;
- uint16_t user_potar = 0;
- int water_centi_degrees = 0;
- int tmp101_deci_degrees = 0;
-
- mode = heat;
- tmp101_sensor_start_conversion(&tmp101_sensor);
- /* Always track power consumption and production */
- adc_get_value(&isnail_val_solar, LPC_ADC(1));
- adc_get_value(&isnail_val_home, LPC_ADC(0));
- adc_get_value(&acs_val_load, LPC_ADC(2));
- adc_get_value(&user_potar, LPC_ADC(7));
- /* Convert to mA value */
- isnail_val_solar = ((isnail_val_solar * 32) * 2); /* 3.2mV / digit, 50mV -> 1A */
- isnail_val_home = ((isnail_val_home * 32) * 2); /* 3.2mV / digit, 50mV -> 1A */
- /* Store value */
- isnail_solar_values[idx] = isnail_val_solar;
- isnail_home_values[idx++] = isnail_val_home;
- if (idx == NB_VAL) {
- idx = 0;
- }
- /* Compute average value when we sampled enough values */
- /* FIXME : Improve by substracting oldest value before storing new one in table and adding new one */
- if ((idx == 0) || (idx == (NB_VAL / 2))) {
- int i = 0;
- for (i = 0; i < NB_VAL; i++) {
- moyenne_solar += isnail_solar_values[i];
- moyenne_home += isnail_home_values[i];
- }
- moyenne_solar = moyenne_solar / NB_VAL;
- moyenne_home = moyenne_home / NB_VAL;
- } else {
- /* Sleep for a litle more than a period (20ms at 50Hz) */
- msleep(23);
- continue;
- }
-
- /* Feed the dog */
- if ((moyenne_solar != 0) && (moyenne_home != 0)) {
- watchdog_feed();
- }
-
- /* Get internal temperature */
- if (1) {
- int ret = 0;
- msleep(40);
- ret = tmp101_sensor_read(&tmp101_sensor, NULL, &tmp101_deci_degrees);
- if (ret != 0) {
- uprintf(UART0, "TMP101 read error : %d\n", ret);
- }
- }
- /* If internal temperature is above 30°C, then turn on fan. Turn of when back to under 28.5°C */
- if (tmp101_deci_degrees > 300) {
- fan_speed = 100;
- timer_set_match(LPC_TIMER_32B0, CHAN0, FAN_ON);
- } else if (tmp101_deci_degrees < 285) {
- fan_speed = 0;
- timer_set_match(LPC_TIMER_32B0, CHAN0, 0);
- }
-
- /* Get thermocouple value */
- if (1) {
- int ret = 0;
- ret = max31855_sensor_read(&thermo, NULL, &water_centi_degrees);
- if (ret != 0) {
- uprintf(UART0, "Water Temp read error : %d\n", ret);
- }
- }
-
- /* Need to enter Forced heating mode ? */
- if (water_centi_degrees < (FORCE_HEATER_TEMP * 100)) {
- if (forced_heater_mode == 0) {
- uprintf(UART0, "Entering forced mode\n");
- forced_heater_mode = 1;
- }
- status_led(red_on);
- mode = forced;
- } else if ((water_centi_degrees > (TARGET_FORCED_HEATER_TEMP * 100)) && (forced_heater_mode == 1)) {
- status_led(red_off);
- forced_heater_mode = 0;
- command_val = 0;
- uprintf(UART0, "Forced mode exit\n");
- mode = temp_OK;
- }
-
- /* Do not force if there is a lot of sun, it may be enough to heat again soon */
- if (moyenne_solar > NO_FORCED_HEATING_ON_SUNNY_DAYS) {
- mode = delayed_heat_prod;
- forced_heater_mode = 0;
- /* Note : Do not set forced_heater_mode to 0 in order to keep decrementing the delay for force
- * heating in case the house power usage does not fall below the production value. */
- }
-
- /* Do not force heating if this is an EJP day */
- ejp_in = gpio_read(ejp_in_pin);
- if (ejp_in == DAY_IS_EJP) {
- forced_heater_mode = 0;
- mode = ejp;
- }
-
- if (never_force == 1) {
- forced_heater_mode = 0;
- }
-
- /* Did the user request a forced heating ? */
- if (manual_activation_request > 1) {
- forced_heater_mode = 1;
- mode = manual;
- if (manual_activation_request == MANUAL_ACTIVATION_DURATION) {
- uprintf(UART0, "Entering manual forced mode for %d ticks\n", manual_activation_request);
- /* Add a systick callback to handle time counting */
- add_systick_callback(handle_dec_request, DEC_PERIOD);
- }
- if (manual_activation_request < 10) {
- uprintf(UART0, "Leaving manual forced mode\n");
- manual_activation_request = 0;
- remove_systick_callback(handle_dec_request);
- }
- }
-
-
- /* Which is the current mode ? */
- if (forced_heater_mode == 1) {
- /* Forced heating mode */
- if ((forced_heater_delay == 0) && (forced_heater_time > 0)) {
- command_val = FORCED_MODE_VALUE;
- }
- if (forced_heater_time == 0) {
- forced_heater_delay = FORCED_HEATER_DELAY;
- forced_heater_time = FORCED_HEATER_DURATION;
- }
- } else if (moyenne_solar < (moyenne_home - 75)) {
- /* Low production mode */
- if (command_val > 15) {
- command_val -= ((3 + n_dec) % 15);
- /* Asservissement */
- n_dec++;
- if (n_dec >= 3) {
- n_inc = 0;
- }
- } else {
- command_val = 0;
- mode = idle_heat;
- }
- status_led(green_off);
- } else if (moyenne_solar > (moyenne_home + 75)) {
- /* High production mode */
- if (command_val < 95) {
- command_val += (2 + n_inc);
- /* Asservissement */
- n_inc++;
- if (n_inc >= 5) {
- n_inc = 5;
- n_dec = 0;
- }
- } else {
- command_val = 100;
- mode = full_heat;
- }
- status_led(green_on);
- }
-
- /* Set Control Output duty cycle */
- if (water_centi_degrees > (MAX_HEATER_TEMP * 100)) {
- command_val = 0;
- }
-
- set_ctrl_duty_cycle(command_val);
- /* Debug Nath TMP */
- //set_ctrl_duty_cycle( user_potar / 10 );
- /* Display */
- if (1) {
- char line[DISPLAY_LINE_LENGTH];
- int abs_centi = water_centi_degrees;
- int abs_deci = tmp101_deci_degrees;
- uprintf(UART0, "%c:%d - Is: %d,%04d - Ih: %d,%04d\n", mode, loop++,
- (moyenne_solar / 1000), (moyenne_solar % 1000),
- (moyenne_home / 1000), (moyenne_home % 1000));
- if (water_centi_degrees < 0) {
- abs_centi = -water_centi_degrees;
- }
- uprintf(UART0, "Water Temp : % 4d.%02d\n", (water_centi_degrees / 100), (abs_centi % 100));
- if (tmp101_deci_degrees < 0) {
- abs_deci = -tmp101_deci_degrees;
- }
- uprintf(UART0, "Internal Temp : % 4d.%02d\n", (tmp101_deci_degrees / 10), (abs_deci % 10));
- uprintf(UART0, "ZC_cnt: %d\n", zc_count);
- uprintf(UART0, "ADC: Sol: %dmA, Home: %dmA, Load: %d, User: %d\n",
- isnail_val_solar, isnail_val_home, acs_val_load, user_potar);
- if (button_pressed != 0) {
- uprintf(UART0, "Button : %d\n", button_pressed);
- button_pressed = 0;
- }
- uprintf(UART0, "CMD: %d/%d, Fan: %d, ndec:%d, ninc:%d\n\n", command_val, act_cmd, fan_speed, n_dec, n_inc);
- ws2812_set_pixel(0, (isnail_val_home / 2000), (isnail_val_solar / 2000), fan_speed);
- ws2812_set_pixel(1, 0, 0, (user_potar >> 2));
- ws2812_send_frame(0);
- /* Erase screen (internal copy) */
- ssd130x_buffer_set(gddram, 0x00);
- /* Update time and time display on internal memory */
- rtc_pcf85363_time_read(&rtc_conf, &now);
- snprintf(line, DISPLAY_LINE_LENGTH, "%02xh%02x:%02x", now.hour, now.min, now.sec);
- display_line(0, 0, line);
- /* Display info */
- snprintf(line, DISPLAY_LINE_LENGTH, "Water:% 2d.%03d %cC", (water_centi_degrees / 100), (abs_centi % 100), 0x1F);
- display_line(2, 0, line);
- snprintf(line, DISPLAY_LINE_LENGTH, "Prod :% 2d,%03dA", (isnail_val_solar / 1000), ((isnail_val_solar % 1000) / 10));
- display_line(3, 0, line);
- snprintf(line, DISPLAY_LINE_LENGTH, "Conso:% 2d,%03dA", (isnail_val_home / 1000), ((isnail_val_home % 1000) / 10));
- display_line(4, 0, line);
- snprintf(line, DISPLAY_LINE_LENGTH, "Command: %d%%", act_cmd);
- display_line(5, 0, line);
- snprintf(line, DISPLAY_LINE_LENGTH, "Mode: %c", mode);
- display_line(6, 0, line);
- /* Update Oled display */
- ret = ssd130x_display_full_screen(&display);
- }
- }
- return 0;
-}
-
-
-
+++ /dev/null
-# 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} $@
-
+++ /dev/null
-Scialys system for solar-panel power generation tracking and fair use.
-
-Copyright 2016 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/>.
- *
- *************************************************************************** */
-
-Beta version of Scialys system.
+++ /dev/null
-/****************************************************************************
- * apps/scialys/beta/main.c
- *
- * Scialys system for solar-panel power generation tracking and fair use.
- *
- * Copyright 2016 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 "lib/stdio.h"
-#include "lib/errno.h"
-#include "drivers/serial.h"
-#include "drivers/gpio.h"
-#include "drivers/adc.h"
-#include "drivers/ssp.h"
-#include "drivers/i2c.h"
-#include "drivers/timers.h"
-
-#include "extdrv/status_led.h"
-#include "extdrv/ws2812.h"
-#include "extdrv/max31855_thermocouple.h"
-#include "extdrv/tmp101_temp_sensor.h"
-#include "extdrv/rtc_pcf85363a.h"
-#include "extdrv/ssd130x_oled_driver.h"
-#include "lib/font.h"
-#include "lib/time.h"
-
-#include "extdrv/sdmmc.h"
-
-#define MODULE_VERSION 0x02
-#define MODULE_NAME "Scialys uC"
-
-
-#define SELECTED_FREQ FREQ_SEL_48MHz
-
-
-/***************************************************************************** */
-/* System configuration
- * Most of the defines in here should go to configuration setting in user flash
- */
-
-/* Period of the decrementer handler from the systick interrupt */
-#define DEC_PERIOD 100
-
-/* If temperature falls bellow FORCE_HEATER_TEMP value, we enter forced heater mode, until
- * TARGET_FORCED_HEATER_TEMP is reached.
- * When in forced heater mode, the heater is controlled to heat at FORCED_MODE_VALUE which
- * is between 0 and 100.
- */
-#define FORCE_HEATER_TEMP 28
-#define TARGET_FORCED_HEATER_TEMP 45
-#define FORCED_MODE_VALUE 75 /* A fraction of 100 */
-/* mA prod value above which the system will not enter forced mode, waiting for home
- * to stop using power to start automatic heating */
-#define NO_FORCED_HEATING_ON_SUNNY_DAYS 1000
-
-uint32_t forced_heater_mode = 0;
-uint32_t forced_heater_delay = 0;
-uint32_t forced_heater_time = 0;
-
-#define FORCED_HEATER_DELAY (2 * 3600 * 1000 / DEC_PERIOD) /* Delay before automatic forced heating */
-#define FORCED_HEATER_DURATION (3 * 3600 * 1000 / DEC_PERIOD) /* Duration of automatic forced heating */
-
-#define MANUAL_ACTIVATION_DURATION (3 * 3600 * 1000 / DEC_PERIOD) /* Three hours */
-
-uint32_t never_force = 0;
-
-
-#define DAY_IS_EJP 0 /* Input is pulled low when EJP is ON */
-int ejp_in = 0;
-
-
-
-/***************************************************************************** */
-/* Pins configuration */
-/* pins blocks are passed to set_pins() for pins configuration.
- * Unused pin blocks can be removed safely with the corresponding set_pins() call
- * All pins blocks may be safelly merged in a single block for single set_pins() call..
- */
-const struct pio_config common_pins[] = {
- /* UART 0 : Config / Debug / USB */
- { LPC_UART0_RX_PIO_0_1, LPC_IO_DIGITAL },
- { LPC_UART0_TX_PIO_0_2, LPC_IO_DIGITAL },
- /* UART 1 : UEXT */
- { LPC_UART1_RX_PIO_0_8, LPC_IO_DIGITAL },
- { LPC_UART1_TX_PIO_0_9, LPC_IO_DIGITAL },
- /* I2C : RTC, Display, UEXT */
- { 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) },
- /* SPI (Thermocouple + uSD card + UEXT) */
- { LPC_SSP0_SCLK_PIO_0_14, LPC_IO_DIGITAL },
- { LPC_SSP0_MOSI_PIO_0_17, LPC_IO_DIGITAL },
- { LPC_SSP0_MISO_PIO_0_16, LPC_IO_DIGITAL },
- /* TIMER_32B0 */
- { LPC_TIMER_32B0_M0_PIO_0_18, (LPC_IO_MODE_PULL_UP | LPC_IO_DIGITAL) }, /* Fan control */
- /* GPIO */
- { LPC_GPIO_0_0, LPC_IO_DIGITAL }, /* Clkout / interrupt from RTC */
- { LPC_GPIO_0_3, LPC_IO_DIGITAL }, /* EJP / External switch input */
- { LPC_GPIO_0_4, LPC_IO_DIGITAL }, /* Zero crossing detection input */
- { LPC_GPIO_0_5, LPC_IO_DIGITAL }, /* Temperature driver warning (Mosfet board only) */
- { LPC_GPIO_0_6, LPC_IO_DIGITAL }, /* Mosfet driver Shutdown (Mosfet board only) */
- { LPC_GPIO_0_7, LPC_IO_DIGITAL }, /* Mosfet / Triac control */
- { LPC_GPIO_0_12, LPC_IO_DIGITAL }, /* ISP / User button OK */
- { LPC_GPIO_0_15, LPC_IO_DIGITAL }, /* Thermocouple chip select */
- { LPC_GPIO_0_23, LPC_IO_DIGITAL }, /* WS2812B RGB Leds control */
- { LPC_GPIO_0_26, LPC_IO_DIGITAL }, /* User button B2 */
- { LPC_GPIO_0_27, LPC_IO_DIGITAL }, /* User button B1 */
- { LPC_GPIO_0_28, LPC_IO_DIGITAL }, /* Charge State */
- { LPC_GPIO_1_1, LPC_IO_DIGITAL }, /* Uext Chip select / Module eeprom select */
- { LPC_GPIO_1_6, LPC_IO_DIGITAL }, /* uSD Card SPI Chip Select */
- ARRAY_LAST_PIO,
-};
-
-const struct pio_config adc_pins[] = {
- { LPC_ADC_AD0_PIO_0_30, LPC_IO_ANALOG }, /* ADC0 */
- { LPC_ADC_AD1_PIO_0_31, LPC_IO_ANALOG }, /* ADC1 */
- { LPC_ADC_AD2_PIO_1_0, LPC_IO_ANALOG }, /* ADC2 */
- { LPC_ADC_AD7_PIO_1_5, LPC_IO_ANALOG }, /* ADC3 */
- ARRAY_LAST_PIO,
-};
-
-const struct pio status_led_green = LPC_GPIO_1_2;
-const struct pio status_led_red = LPC_GPIO_1_3;
-
-/* Inputs */
-/* Buttons */
-const struct pio button_ok = LPC_GPIO_0_12;
-const struct pio button_b1 = LPC_GPIO_0_27;
-const struct pio button_b2 = LPC_GPIO_0_26;
-/* External signals */
-const struct pio rtc_in_pin = LPC_GPIO_0_0;
-const struct pio ejp_in_pin = LPC_GPIO_0_3;
-const struct pio zero_cross_in_pin = LPC_GPIO_0_4;
-const struct pio th_warn_in_pin = LPC_GPIO_0_5;
-const struct pio charge_status_in_pin = LPC_GPIO_0_28;
-
-/* Outputs */
-/* Led control data pin */
-const struct pio ws2812_data_out_pin = LPC_GPIO_0_23;
-/* AC output control (Mosfet / Triac) */
-const struct pio ac_ctrl = LPC_GPIO_0_7;
-const struct pio mos_shutdown = LPC_GPIO_0_6;
-
-
-/* Thermocouple reading */
-const struct max31855_sensor_config thermo = {
- .ssp_bus_num = 0,
- .chip_select = LPC_GPIO_0_15,
-};
-
-/* TMP101 onboard I2C temperature sensor */
-#define TMP101_ADDR 0x94 /* Pin Addr0 (pin5 of tmp101) connected to VCC */
-struct tmp101_sensor_config tmp101_sensor = {
- .bus_num = I2C0,
- .addr = TMP101_ADDR,
- .resolution = TMP_RES_ELEVEN_BITS,
-};
-
-
-#define FAN_ON (10 * 1000)
-const struct lpc_timer_pwm_config fan_pwm_conf = {
- .nb_channels = 1,
- .period_chan = CHAN3,
- .period = FAN_ON,
- .outputs = { CHAN0, },
- .match_values = { 0, },
-};
-
-const struct lpc_tc_config ac_timer_conf = {
- .mode = LPC_TIMER_MODE_TIMER | LPC_TIMER_MODE_MATCH,
- .match_control = { 0, LPC_TIMER_INT_RESET_AND_STOP_ON_MATCH, 0, 0, },
- .match = { 0, 10, 0, 0, },
- .ext_match_config = { 0, LPC_TIMER_SET_ON_MATCH, 0, 0, },
-};
-
-
-/***************************************************************************** */
-/* SD/MMC Card */
-struct sdmmc_card micro_sd = {
- .ssp_bus_num = SSP_BUS_0,
- .card_type = MMC_CARDTYPE_UNKNOWN,
- .block_size = 64,
- .chip_select = LPC_GPIO_1_6,
-};
-
-uint8_t mmc_data[MMC_MAX_SECTOR_SIZE];
-
-
-/***************************************************************************** */
-/* RTC and time */
-#define RTC_ADDR 0xA2
-struct rtc_pcf85363a_config rtc_conf = {
- .bus_num = I2C0,
- .addr = RTC_ADDR,
- .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 = 0x16,
- .month = 0x10,
- .day = 0x30,
- .hour = 0x13,
- .min = 0x00,
-};
-static struct rtc_time now;
-
-
-/***************************************************************************** */
-/* 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);
- set_pins(adc_pins);
- gpio_on();
- status_led_config(&status_led_green, &status_led_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);
-}
-
-
-/***************************************************************************** */
-/* Some global flags or variables */
-static uint8_t thermal_warn_flag = 0;
-static uint32_t fan_speed = 0;
-static int act_cmd = 0;
-
-
-/***************************************************************************** */
-/* System configuration over USB */
-void config_rx(uint8_t c)
-{
- /* FAN control */
- if (c == 'f') {
- fan_speed = 100;
- timer_set_match(LPC_TIMER_32B0, CHAN0, FAN_ON);
- } else if (c == 'z') {
- fan_speed = 0;
- timer_set_match(LPC_TIMER_32B0, CHAN0, 0);
- } else if ((c >= '0') && (c <= '9')) {
- fan_speed = ((c - '0') * 10);
- if (fan_speed < 60) {
- fan_speed = 0;
- }
- timer_set_match(LPC_TIMER_32B0, CHAN0, (FAN_ON - ((FAN_ON / 100) * (100 - fan_speed))));
- }
-}
-
-
-
-/***************************************************************************** */
-/* System communication over UART1 */
-void cmd_rx(uint8_t c)
-{
-}
-
-
-
-/***************************************************************************** */
-/* AC control */
-
-void set_ctrl_duty_cycle(uint8_t value)
-{
- if (thermal_warn_flag == 1) {
- /* Thermal warning condition still present ? */
- if (gpio_read(th_warn_in_pin) != 0) {
- thermal_warn_flag = 0;
- } else {
- act_cmd = 0;
- return;
- }
- }
- act_cmd = value;
- if (act_cmd > 100) {
- /* 100 is the maximum allowed value */
- act_cmd = 100;
- } else if (act_cmd <= 2) {
- /* Below 3% there are triggering problems which lead to 50% instead of 1% or 2% */
- act_cmd = 0;
- /* Set mosfet driver shutdown */
- gpio_set(mos_shutdown);
- return;
- }
- /* Clear the shutdown condition from the mosfet driver */
- gpio_clear(mos_shutdown);
-}
-
-void ac_switch_on(uint32_t flags)
-{
- gpio_set(ac_ctrl);
- gpio_set(button_b2);
-}
-
-static uint32_t clk_cycles_ac_zc = 0;
-static volatile uint32_t zc_count = 0; /* Wraps every 1.36 year ... */
-void zero_cross(uint32_t gpio)
-{
- uint32_t delay = 0;
-
- zc_count ++;
- gpio_toggle(button_b1);
-
- gpio_clear(ac_ctrl);
- gpio_clear(button_b2);
- if (act_cmd == 0) {
- return;
- }
- /* Set timer to trigger ac out ON at given delay */
- delay = clk_cycles_ac_zc * act_cmd;
- timer_set_match(LPC_TIMER_32B1, CHAN1, delay);
- timer_restart(LPC_TIMER_32B1);
-}
-
-void th_warning(uint32_t gpio)
-{
- /* Test for condition set or removed */
- if (gpio_read(th_warn_in_pin) == 0) {
- /* Turn off AC output */
- gpio_clear(ac_ctrl);
- /* Set mosfet driver shutdown */
- gpio_set(mos_shutdown);
- act_cmd = 0;
- thermal_warn_flag = 1;
- /* Turn on Fan at max speed */
- timer_set_match(LPC_TIMER_32B0, CHAN0, FAN_ON);
- fan_speed = 100;
- } else {
- /* Only clear the warning flag, the rest will get back to the desired state soon enough. */
- thermal_warn_flag = 0;
- }
-}
-
-
-
-/***************************************************************************** */
-/* System interface */
-enum buttons {
- BUTTON_NONE = 0,
- BUTTON_OK,
- BUTTON_UP,
- BUTTON_DOWN,
-};
-
-
-uint32_t manual_activation_request = 0;
-uint8_t button_pressed = 0;
-void manual_activation(uint32_t gpio) {
- manual_activation_request = MANUAL_ACTIVATION_DURATION;
- button_pressed = BUTTON_OK;
-}
-void manual_up(uint32_t gpio) {
- manual_activation_request = MANUAL_ACTIVATION_DURATION;
- button_pressed = BUTTON_UP;
-}
-void manual_down(uint32_t gpio) {
- manual_activation_request = MANUAL_ACTIVATION_DURATION;
- button_pressed = BUTTON_DOWN;
-}
-void handle_dec_request(uint32_t curent_tick) {
- if (manual_activation_request > 0) {
- manual_activation_request--;
- }
- if (forced_heater_mode == 1) {
- if (forced_heater_delay > 0) {
- forced_heater_delay--;
- }
- if (forced_heater_time > 0) {
- forced_heater_time--;
- }
- }
-}
-
-
-/***************************************************************************** */
-void temp_config(int uart_num)
-{
- int ret = 0;
- ret = tmp101_sensor_config(&tmp101_sensor);
- if (ret != 0) {
- uprintf(uart_num, "Temp config error: %d\n", ret);
- }
-}
-
-
-/***************************************************************************** */
-/* Oled Display */
-#define DISPLAY_ADDR 0x78
-struct oled_display display = {
- .address = DISPLAY_ADDR,
- .bus_num = I2C0,
- .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,
-};
-
-#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_set_tile(&display, col, line, tile_data);
-}
-#define OLED_LINE_CHAR_LENGTH (SSD130x_NB_COL / 8)
-#define DISPLAY_LINE_LENGTH (OLED_LINE_CHAR_LENGTH + 1)
-int display_line(uint8_t line, uint8_t col, 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_set_tile(&display, col++, line, tile_data);
- if (col >= (OLED_LINE_CHAR_LENGTH)) {
- col = 0;
- line++;
- if (line >= SSD130x_NB_PAGES) {
- return i;
- }
- }
- }
- return len;
-}
-
-
-
-
-/***************************************************************************** */
-#define NB_VAL 20
-
-enum modes {
- heat = 'C',
- ejp = 'E',
- delayed_heat_prod = 'P',
- forced = 'F',
- temp_OK = 'T',
- manual = 'M',
- idle_heat = 'L',
- full_heat = 'F',
-};
-
-/***************************************************************************** */
-int main(void)
-{
- uint16_t isnail_solar_values[NB_VAL];
- uint16_t isnail_home_values[NB_VAL];
- uint8_t idx = 0;
- uint32_t loop = 0;
- char mode = heat; /* Debug info */
- int ret = 0;
-
- system_init();
- status_led(red_only);
- uart_on(UART0, 115200, config_rx);
- uart_on(UART1, 115200, cmd_rx);
- i2c_on(I2C0, I2C_CLK_100KHz, I2C_MASTER);
- ssp_master_on(thermo.ssp_bus_num, LPC_SSP_FRAME_SPI, 8, 4*1000*1000);
- adc_on(NULL);
- timer_on(LPC_TIMER_32B0, 0, NULL);
- timer_on(LPC_TIMER_32B1, 0, ac_switch_on);
-
- /* Immediatly turn off Mosfet / Triac */
- config_gpio(&ac_ctrl, 0, GPIO_DIR_OUT, 0);
- config_gpio(&mos_shutdown, 0, GPIO_DIR_OUT, 1);
-
- /* Thermocouple configuration */
- max31855_sensor_config(&thermo);
- uprintf(UART0, "Thermocouple config done\n");
-
- /* TMP101 sensor config */
- temp_config(UART0);
-
- /* Activate on Rising edge (button release) */
- set_gpio_callback(manual_activation, &button_ok, EDGE_RISING);
-#if 0
- set_gpio_callback(manual_up, &button_b1, EDGE_RISING);
- set_gpio_callback(manual_down, &button_b2, EDGE_RISING);
-#else
- config_gpio(&button_b1, 0, GPIO_DIR_OUT, 0);
- config_gpio(&button_b2, 0, GPIO_DIR_OUT, 0);
-#endif
-
- /* Zero cross and alert pin */
- set_gpio_callback(zero_cross, &zero_cross_in_pin, EDGE_FALLING);
- set_gpio_callback(th_warning, &th_warn_in_pin, EDGES_BOTH);
-
- /* Start ADC sampling */
- adc_start_burst_conversion(ADC_MCH(0) | ADC_MCH(1) | ADC_MCH(2) | ADC_MCH(7), LPC_ADC_SEQ(0));
-
- /* Configure Input GPIO */
- config_gpio(&ejp_in_pin, 0, GPIO_DIR_IN, 0);
- config_gpio(&rtc_in_pin, 0, GPIO_DIR_IN, 0);
- config_gpio(&charge_status_in_pin, 0, GPIO_DIR_IN, 1);
-
- /* WS2812B Leds on display board */
- ws2812_config(&ws2812_data_out_pin);
-
- /* FAN Config */
- timer_pwm_config(LPC_TIMER_32B0, &fan_pwm_conf);
- timer_start(LPC_TIMER_32B0);
- /* AC Switch Config */
- timer_counter_config(LPC_TIMER_32B1, &ac_timer_conf);
- /* We want 100 Hz (50 Hz but two zero crossings) with 1% granularity */
- clk_cycles_ac_zc = get_main_clock() / (100 * 100);
-
- status_led(green_only);
-
- /* Configure and start display */
- ret = ssd130x_display_on(&display);
- /* Erase screen */
- ssd130x_display_set(&display, 0x00);
- ret = ssd130x_display_full_screen(&display);
-
- /* RTC init */
- ret = rtc_pcf85363a_config(&rtc_conf);
- ret = rtc_pcf85363a_is_up(&rtc_conf, &oldest);
- if (ret == 1) {
- char buff[30];
- rtc_pcf85363_time_read(&rtc_conf, &now);
- rtc_pcf85363_time_to_str(&now, buff, 30);
- /* Debug */
- uprintf(UART0, buff);
- } else if (ret == -EFAULT) {
- memcpy(&now, &oldest, sizeof(struct rtc_time));
- rtc_pcf85363_time_write(&rtc_conf, &now);
- }
-
- /* microSD card init */
- ret = sdmmc_init(µ_sd);
- if (ret == 0) {
- msleep(1);
- ret = sdmmc_init_wait_card_ready(µ_sd);
- if (ret == 0) {
- ret = sdmmc_init_end(µ_sd);
- }
- }
- uprintf(UART0, "uSD init: %d, type: %d, bs: %d\n", ret, micro_sd.card_type, micro_sd.block_size);
- ret = sdmmc_read_block(µ_sd, 0, mmc_data);
- uprintf(UART0, "uSD read: %s\n", mmc_data);
-
- /* Add a systick callback to handle time counting */
- //add_systick_callback(handle_dec_request, DEC_PERIOD);
-
- msleep(50);
- /* Read parameters from memory */
- if (1) {
- never_force = 0;
- forced_heater_delay = 0;
- forced_heater_time = FORCED_HEATER_DURATION;
- }
-
- /* Clear thermal warning flag if warning pin is high */
- if (gpio_read(th_warn_in_pin) != 0) {
- thermal_warn_flag = 0;
- }
-
- while (1) {
- static uint8_t command_val = 0;
- static uint8_t n_dec = 0; /* Add some PID like (derivative part) */
- static uint8_t n_inc = 0; /* Add some PID like (derivative part) */
- int moyenne_solar = 0;
- int moyenne_home = 0;
- uint16_t isnail_val_solar = 0;
- uint16_t isnail_val_home = 0;
- uint16_t acs_val_load = 0;
- uint16_t user_potar = 0;
- int water_centi_degrees = 0;
- int tmp101_deci_degrees = 0;
-
- mode = heat;
- tmp101_sensor_start_conversion(&tmp101_sensor);
- /* Always track power consumption and production */
- adc_get_value(&isnail_val_solar, LPC_ADC(1));
- adc_get_value(&isnail_val_home, LPC_ADC(0));
- adc_get_value(&acs_val_load, LPC_ADC(2));
- adc_get_value(&user_potar, LPC_ADC(7));
- /* Convert to mA value */
- isnail_val_solar = ((isnail_val_solar * 32) * 2); /* 3.2mV / digit, 50mV -> 1A */
- isnail_val_home = ((isnail_val_home * 32) * 2); /* 3.2mV / digit, 50mV -> 1A */
- /* Store value */
- isnail_solar_values[idx] = isnail_val_solar;
- isnail_home_values[idx++] = isnail_val_home;
- if (idx == NB_VAL) {
- idx = 0;
- }
- /* Compute average value when we sampled enough values */
- /* FIXME : Improve by substracting oldest value before storing new one in table and adding new one */
- if ((idx == 0) || (idx == (NB_VAL / 2))) {
- int i = 0;
- for (i = 0; i < NB_VAL; i++) {
- moyenne_solar += isnail_solar_values[i];
- moyenne_home += isnail_home_values[i];
- }
- moyenne_solar = moyenne_solar / NB_VAL;
- moyenne_home = moyenne_home / NB_VAL;
- } else {
- /* Sleep for a litle more than a period (20ms at 50Hz) */
- msleep(23);
- continue;
- }
-
- /* Feed the dog */
- if ((moyenne_solar != 0) && (moyenne_home != 0)) {
- watchdog_feed();
- }
-
- /* Get internal temperature */
- if (1) {
- int ret = 0;
- msleep(40);
- ret = tmp101_sensor_read(&tmp101_sensor, NULL, &tmp101_deci_degrees);
- if (ret != 0) {
- uprintf(UART0, "TMP101 read error : %d\n", ret);
- }
- }
- /* If internal temperature is above 30°C, then turn on fan. Turn of when back to under 28.5°C */
- if (tmp101_deci_degrees > 300) {
- fan_speed = 100;
- timer_set_match(LPC_TIMER_32B0, CHAN0, FAN_ON);
- } else if (tmp101_deci_degrees < 285) {
- fan_speed = 0;
- timer_set_match(LPC_TIMER_32B0, CHAN0, 0);
- }
-
- /* Get thermocouple value */
- if (1) {
- int ret = 0;
- ret = max31855_sensor_read(&thermo, NULL, &water_centi_degrees);
- if (ret != 0) {
- uprintf(UART0, "Water Temp read error : %d\n", ret);
- }
- }
-
- /* Need to enter Forced heating mode ? */
- if (water_centi_degrees < (FORCE_HEATER_TEMP * 100)) {
- if (forced_heater_mode == 0) {
- uprintf(UART0, "Entering forced mode\n");
- forced_heater_mode = 1;
- }
- status_led(red_on);
- mode = forced;
- } else if ((water_centi_degrees > (TARGET_FORCED_HEATER_TEMP * 100)) && (forced_heater_mode == 1)) {
- status_led(red_off);
- forced_heater_mode = 0;
- command_val = 0;
- uprintf(UART0, "Forced mode exit\n");
- mode = temp_OK;
- }
-
- /* Do not force if there is a lot of sun, it may be enough to heat again soon */
- if (moyenne_solar > NO_FORCED_HEATING_ON_SUNNY_DAYS) {
- mode = delayed_heat_prod;
- forced_heater_mode = 0;
- /* Note : Do not set forced_heater_mode to 0 in order to keep decrementing the delay for force
- * heating in case the house power usage does not fall below the production value. */
- }
-
- /* Do not force heating if this is an EJP day */
- ejp_in = gpio_read(ejp_in_pin);
- if (ejp_in == DAY_IS_EJP) {
- forced_heater_mode = 0;
- mode = ejp;
- }
-
- if (never_force == 1) {
- forced_heater_mode = 0;
- }
-
- /* Did the user request a forced heating ? */
- if (manual_activation_request > 1) {
- forced_heater_mode = 1;
- mode = manual;
- if (manual_activation_request == MANUAL_ACTIVATION_DURATION) {
- uprintf(UART0, "Entering manual forced mode for %d ticks\n", manual_activation_request);
- /* Add a systick callback to handle time counting */
- add_systick_callback(handle_dec_request, DEC_PERIOD);
- }
- if (manual_activation_request < 10) {
- uprintf(UART0, "Leaving manual forced mode\n");
- manual_activation_request = 0;
- remove_systick_callback(handle_dec_request);
- }
- }
-
-
- /* Which is the current mode ? */
- if (forced_heater_mode == 1) {
- /* Forced heating mode */
- if ((forced_heater_delay == 0) && (forced_heater_time > 0)) {
- command_val = FORCED_MODE_VALUE;
- }
- if (forced_heater_time == 0) {
- forced_heater_delay = FORCED_HEATER_DELAY;
- forced_heater_time = FORCED_HEATER_DURATION;
- }
- } else if (moyenne_solar < (moyenne_home - 75)) {
- /* Low production mode */
- if (command_val > 15) {
- command_val -= ((3 + n_dec) % 15);
- /* Asservissement */
- n_dec++;
- if (n_dec >= 3) {
- n_inc = 0;
- }
- } else {
- command_val = 0;
- mode = idle_heat;
- }
- status_led(green_off);
- } else if (moyenne_solar > (moyenne_home + 75)) {
- /* High production mode */
- if (command_val < 95) {
- command_val += (2 + n_inc);
- /* Asservissement */
- n_inc++;
- if (n_inc >= 5) {
- n_inc = 5;
- n_dec = 0;
- }
- } else {
- command_val = 100;
- mode = full_heat;
- }
- status_led(green_on);
- }
-
- /* Set Control Output duty cycle */
- //set_ctrl_duty_cycle(command_val);
- /* Debug Nath TMP */
- set_ctrl_duty_cycle( (user_potar - 5) / 10 );
-
- /* Display */
- if (1) {
- int abs_centi = water_centi_degrees;
- int abs_deci = tmp101_deci_degrees;
-
- if (water_centi_degrees < 0) {
- abs_centi = -water_centi_degrees;
- }
- if (tmp101_deci_degrees < 0) {
- abs_deci = -tmp101_deci_degrees;
- }
-
- if (1) {
- uprintf(UART0, "%c:%d - Is: %d,%04d - Ih: %d,%04d\n", mode, loop++,
- (moyenne_solar / 1000), (moyenne_solar % 1000),
- (moyenne_home / 1000), (moyenne_home % 1000));
- uprintf(UART0, "Water Temp : % 4d.%02d\n", (water_centi_degrees / 100), (abs_centi % 100));
- uprintf(UART0, "Internal Temp : % 4d.%02d\n", (tmp101_deci_degrees / 10), (abs_deci % 10));
- uprintf(UART0, "ADC: Sol: %dmA, Home: %dmA, Load: %d, User: %d\n",
- isnail_val_solar, isnail_val_home, acs_val_load, user_potar);
- if (button_pressed != 0) {
- uprintf(UART0, "Button : %d\n", button_pressed);
- button_pressed = 0;
- }
- uprintf(UART0, "ZC_cnt: %d, user: %d, ThW: %d\n", zc_count, user_potar / 10, thermal_warn_flag);
- uprintf(UART0, "CMD: %d/%d, Fan: %d, ndec:%d, ninc:%d\n\n", command_val, act_cmd, fan_speed, n_dec, n_inc);
- }
-
- if (1) {
- ws2812_set_pixel(0, (isnail_val_home / 2000), (isnail_val_solar / 2000), fan_speed);
- ws2812_set_pixel(1, 0, 0, (user_potar >> 2));
- ws2812_send_frame(0);
- }
-
- if (1) {
- char line[DISPLAY_LINE_LENGTH];
- /* Erase screen (internal copy) */
- ssd130x_display_set(&display, 0x00);
- /* Update time and time display on internal memory */
- rtc_pcf85363_time_read(&rtc_conf, &now);
- snprintf(line, DISPLAY_LINE_LENGTH, "%02xh%02x:%02x", now.hour, now.min, now.sec);
- display_line(0, 0, line);
- /* Display info */
- snprintf(line, DISPLAY_LINE_LENGTH, "Water:% 2d.%03d %cC", (water_centi_degrees / 100), (abs_centi % 100), 0x1F);
- display_line(2, 0, line);
- snprintf(line, DISPLAY_LINE_LENGTH, "Prod :% 2d,%03dA", (isnail_val_solar / 1000), ((isnail_val_solar % 1000) / 10));
- display_line(3, 0, line);
- snprintf(line, DISPLAY_LINE_LENGTH, "Conso:% 2d,%03dA", (isnail_val_home / 1000), ((isnail_val_home % 1000) / 10));
- display_line(4, 0, line);
- snprintf(line, DISPLAY_LINE_LENGTH, "Command: %d%%", act_cmd);
- display_line(5, 0, line);
- snprintf(line, DISPLAY_LINE_LENGTH, "Mode: %c", mode);
- display_line(6, 0, line);
- /* Update Oled display */
- ret = ssd130x_display_full_screen(&display);
- }
- }
- }
- return 0;
-}
-
-
-
--- /dev/null
+# 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} $@
+
--- /dev/null
+DMX module for solar-panel power generation tracking and fair use.
+
+Copyright 2015 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/>.
+ *
+ *************************************************************************** */
+
+First prototype version of the Scialys module.
+Used for validation of feasability
+
--- /dev/null
+/****************************************************************************
+ * apps/scialys/alpha/main.c
+ *
+ * DMX module for solar-panel power generation tracking and fair use.
+ *
+ * Copyright 2015-2016 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/>.
+ *
+ *************************************************************************** */
+
+
+/* DMX informations :
+ * https://en.wikipedia.org/wiki/DMX512
+ *
+ * DMX uses an RS485 line with a few glitches (the break and start pulse).
+ * The DMX module uses an ADM2482 isolated RS485 bridge from analog devices.
+ */
+
+#include "core/system.h"
+#include "core/pio.h"
+#include "core/systick.h"
+#include "lib/stdio.h"
+#include "drivers/serial.h"
+#include "drivers/gpio.h"
+#include "extdrv/status_led.h"
+#include "drivers/adc.h"
+#include "drivers/ssp.h"
+
+#include "extdrv/max31855_thermocouple.h"
+
+
+#define MODULE_VERSION 0x02
+#define MODULE_NAME "DMX Module"
+
+
+#define SELECTED_FREQ FREQ_SEL_48MHz
+
+/* Period of the decrementer handler from the systick interrupt */
+#define DEC_PERIOD 100
+
+/* If temperature falls bellow FORCE_HEATER_TEMP value, we enter forced heater mode, until
+ * TARGET_FORCED_HEATER_TEMP is reached.
+ * When in forced heater mode, the heated is controlled to heat at FORCED_MODE_VALUE which
+ * is between 0 and 255.
+ */
+#define FORCE_HEATER_TEMP 40
+#define TARGET_FORCED_HEATER_TEMP 45
+#define NO_FORCED_HEATING_ON_SUNNY_DAYS 750
+#define FORCED_MODE_VALUE 190 /* A fraction of 255 */
+uint32_t forced_heater_mode = 0;
+uint32_t forced_heater_delay = 0;
+uint32_t forced_heater_time = 0;
+
+#define FORCED_HEATER_DELAY (7 * 3600 * 1000 / DEC_PERIOD) /* Delay before automatic forced heating */
+#define FORCED_HEATER_DURATION (3 * 3600 * 1000 / DEC_PERIOD) /* Duration of automatic forced heating */
+
+#define MANUAL_ACTIVATION_DURATION (3600 * 1000 / DEC_PERIOD) /* One hour */
+
+uint32_t never_force = 0;
+
+/***************************************************************************** */
+/* Pins configuration */
+/* pins blocks are passed to set_pins() for pins configuration.
+ * Unused pin blocks can be removed safely with the corresponding set_pins() call
+ * All pins blocks may be safelly merged in a single block for single set_pins() call..
+ */
+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 },
+ { LPC_UART0_RTS_PIO_0_0, LPC_IO_DIGITAL },
+ /* UART 1 */
+ { LPC_UART1_RX_PIO_0_8, LPC_IO_DIGITAL },
+ { LPC_UART1_TX_PIO_0_9, LPC_IO_DIGITAL },
+ /* SPI */
+ { LPC_SSP0_SCLK_PIO_0_14, LPC_IO_DIGITAL },
+ { LPC_SSP0_MOSI_PIO_0_17, LPC_IO_DIGITAL },
+ { LPC_SSP0_MISO_PIO_0_16, LPC_IO_DIGITAL },
+ /* GPIO */
+ { LPC_GPIO_0_12, LPC_IO_DIGITAL },
+ { LPC_GPIO_0_25, LPC_IO_DIGITAL },
+ { LPC_GPIO_0_26, LPC_IO_DIGITAL },
+ { LPC_GPIO_0_28, LPC_IO_DIGITAL },
+ { LPC_GPIO_0_29, LPC_IO_DIGITAL },
+ ARRAY_LAST_PIO,
+};
+
+const struct pio_config adc_pins[] = {
+ { LPC_ADC_AD1_PIO_0_31, LPC_IO_ANALOG }, /* ADC1 */
+ { LPC_ADC_AD2_PIO_1_0, LPC_IO_ANALOG }, /* ADC2 */
+ ARRAY_LAST_PIO,
+};
+
+const struct pio status_led_green = LPC_GPIO_0_28;
+const struct pio status_led_red = LPC_GPIO_0_29;
+
+const struct pio button = LPC_GPIO_0_12; /* ISP button */
+const struct pio ejp_in_pin = LPC_GPIO_0_25;
+#define DAY_IS_EJP 0 /* Input is pulled low when EJP is ON */
+int ejp_in = 0;
+
+/* Thermocouple reading */
+const struct max31855_sensor_config thermo = {
+ .ssp_bus_num = 0,
+ .chip_select = LPC_GPIO_0_26,
+};
+
+/* DMX signals */
+/* DMX signals are on LPC_GPIO_0_0 for Frame enable pin (RS485 enable) and LPC_GPIO_0_2 for
+ * Frame Tx pin (RS485 Tx).
+ */
+const struct pio frame_tx_pin = LPC_GPIO_0_2;
+const struct pio frame_en_pin = LPC_GPIO_0_0;
+
+
+
+/***************************************************************************** */
+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 = 0x0FFFFFF, /* 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);
+ set_pins(adc_pins);
+ gpio_on();
+ status_led_config(&status_led_green, &status_led_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);
+}
+
+
+/* Put any data received from RS485 UART in rs485_rx_buff
+ */
+void rs485_rx(uint8_t c)
+{
+}
+
+void cmd_rx(uint8_t c)
+{
+}
+
+void dmx_send_frame(uint8_t start_code, uint8_t* slots, uint16_t nb_slots)
+{
+ uint16_t sent = 0;
+
+ /* Configure pins for Start of frame transmission */
+ config_gpio(&frame_tx_pin, LPC_IO_MODE_PULL_UP, GPIO_DIR_OUT, 0);
+ config_gpio(&frame_en_pin, LPC_IO_MODE_PULL_UP, GPIO_DIR_OUT, 1);
+ /* Send break : 92us minimum
+ * Pins default state chosen to generate the break condition */
+ usleep(100);
+ /* Send Mark-After-Break (MAB) of 12us minimum */
+ gpio_set(frame_tx_pin);
+ usleep(12);
+
+ /* Configure back for RS485.
+ * This is done using direct access to the registers as this step is time critical
+ */
+ /* Make sure IO_Config is clocked */
+ io_config_clk_on();
+ LPC_IO_CONTROL->pio0_0 = (LPC_IO_FUNC_ALT(2) | LPC_IO_DIGITAL);
+ LPC_IO_CONTROL->pio0_2 = (LPC_IO_FUNC_ALT(2) | LPC_IO_DIGITAL);
+
+
+ /* Send start code */
+ serial_send_quickbyte(0, start_code);
+
+ /* And send slots data */
+ while (sent < nb_slots) {
+ int tmp = serial_write(0, (char*)(slots + sent), (nb_slots - sent));
+ if (tmp == -1) {
+ break;
+ }
+ sent += tmp;
+ }
+
+ /* Config done, power off IO_CONFIG block */
+ io_config_clk_off();
+}
+
+
+uint32_t manual_activation_request = 0;
+void manual_activation(uint32_t gpio) {
+ manual_activation_request = MANUAL_ACTIVATION_DURATION;
+}
+void handle_dec_request(uint32_t curent_tick) {
+ if (manual_activation_request > 0) {
+ manual_activation_request--;
+ }
+ if (forced_heater_mode == 1) {
+ if (forced_heater_delay > 0) {
+ forced_heater_delay--;
+ }
+ if (forced_heater_time > 0) {
+ forced_heater_time--;
+ }
+ }
+}
+
+
+/* DMX */
+#define DMX_NB_SLOTS 4
+#define NB_VAL 20
+
+
+enum modes {
+ heat = 'C',
+ ejp = 'E',
+ no_heat_prod = 'P',
+ forced = 'F',
+ temp_OK = 'T',
+ manual = 'M',
+ idle_heat = 'L',
+ full_heat = 'F',
+};
+
+/***************************************************************************** */
+int main(void)
+{
+ uint16_t isnail_solar_values[NB_VAL];
+ uint16_t isnail_home_values[NB_VAL];
+ uint8_t slots[DMX_NB_SLOTS + 1];
+ uint8_t idx = 0;
+ uint32_t loop = 0;
+ char mode = heat; /* Debug info */
+
+ system_init();
+ status_led(red_only);
+ uart_set_config(0, (LPC_UART_8BIT | LPC_UART_NO_PAR | LPC_UART_2STOP));
+ uart_on(UART0, 250000, rs485_rx); /* FIXME : configure for 250 kbits/s and 8N2 */
+ uart_on(UART1, 115200, cmd_rx);
+ ssp_master_on(thermo.ssp_bus_num, LPC_SSP_FRAME_SPI, 8, 4*1000*1000);
+ adc_on(NULL);
+
+ /* RS485 config */
+ if (1) {
+ uint32_t rs485_ctrl = LPC_RS485_ENABLE;
+ //rs485_ctrl |= LPC_RS485_DIR_PIN_RTS | LPC_RS485_AUTO_DIR_EN | LPC_RS485_DIR_CTRL_INV;
+ rs485_ctrl |= LPC_RS485_DIR_PIN_RTS | LPC_RS485_AUTO_DIR_EN | LPC_RS485_DIR_CTRL_INV;
+ uart_set_mode_rs485(0, rs485_ctrl, 0, 1);
+ }
+
+ /* Thermocouple configuration */
+ max31855_sensor_config(&thermo);
+ uprintf(UART1, "Thermocouple config OK\n");
+
+ /* Activate on Rising edge (button release) */
+ set_gpio_callback(manual_activation, &button, EDGE_RISING);
+
+ /* Start ADC sampling */
+ adc_start_burst_conversion(ADC_MCH(1) | ADC_MCH(2), LPC_ADC_SEQ(0));
+
+ /* Configure Input GPIO for EJP mode detection */
+ config_gpio(&ejp_in_pin, 0, GPIO_DIR_IN, 0);
+
+ status_led(green_only);
+ memset(slots, 0, (DMX_NB_SLOTS + 1));
+
+ msleep(50);
+ /* Read parameters from memory */
+ if (1) {
+ never_force = 0;
+ forced_heater_delay = FORCED_HEATER_DELAY;
+ forced_heater_time = FORCED_HEATER_DURATION;
+ }
+
+ while (1) {
+ static uint8_t dmx_val = 0;
+ uint32_t moyenne_solar = 0;
+ uint32_t moyenne_home = 0;
+ uint16_t isnail_val_solar = 0;
+ uint16_t isnail_val_home = 0;
+ int centi_degrees = 0;
+
+ mode = heat;
+ /* Always track power consumption and production */
+ adc_get_value(&isnail_val_solar, LPC_ADC(1));
+ adc_get_value(&isnail_val_home, LPC_ADC(2));
+ /* Convert to mA value */
+ isnail_val_solar = ((isnail_val_solar * 32) * 2); /* 3.2mV / digit, 50mV -> 1A */
+ isnail_val_home = ((isnail_val_home * 32) * 2); /* 3.2mV / digit, 50mV -> 1A */
+ /* Store value */
+ isnail_solar_values[idx] = isnail_val_solar;
+ isnail_home_values[idx++] = isnail_val_home;
+ if (idx == NB_VAL) {
+ idx = 0;
+ }
+ /* Compute average value when we sampled enough values */
+ if ((idx == 0) || (idx == (NB_VAL / 2))) {
+ int i = 0;
+ for (i = 0; i < NB_VAL; i++) {
+ moyenne_solar += isnail_solar_values[i];
+ moyenne_home += isnail_home_values[i];
+ }
+ moyenne_solar = moyenne_solar / NB_VAL;
+ moyenne_home = moyenne_home / NB_VAL;
+ } else {
+ /* Sleep for a litle more than a period (20ms at 50Hz) */
+ msleep(23);
+ continue;
+ }
+
+ /* Feed the dog */
+ if ((moyenne_solar != 0) && (moyenne_home != 0)) {
+ watchdog_feed();
+ }
+
+ /* Get thermocouple value */
+ if (1) {
+ int ret = 0;
+ ret = max31855_sensor_read(&thermo, NULL, ¢i_degrees);
+ if (ret != 0) {
+ uprintf(UART1, "Temp read error : %d\n", ret);
+ }
+ }
+ if (centi_degrees < (FORCE_HEATER_TEMP * 100)) {
+ if (forced_heater_mode == 0) {
+ uprintf(UART1, "Entering forced mode\n");
+ forced_heater_mode = 1;
+ }
+ status_led(red_on);
+ mode = forced;
+ } else if ((centi_degrees > (TARGET_FORCED_HEATER_TEMP * 100)) && (forced_heater_mode == 1)) {
+ status_led(red_off);
+ forced_heater_mode = 0;
+ dmx_val = 0;
+ uprintf(UART1, "Forced mode exit\n");
+ mode = temp_OK;
+ }
+
+ /* Do not force if there is some sun, it may be enough to heat again */
+ if (moyenne_solar > NO_FORCED_HEATING_ON_SUNNY_DAYS) {
+ forced_heater_mode = 0;
+ mode = no_heat_prod;
+ forced_heater_delay = FORCED_HEATER_DELAY;
+ }
+
+ /* Do not force heating if this is an EJP day */
+ ejp_in = gpio_read(ejp_in_pin);
+ if (ejp_in == DAY_IS_EJP) {
+ forced_heater_mode = 0;
+ mode = ejp;
+ }
+
+ if (never_force == 1) {
+ forced_heater_mode = 0;
+ }
+
+ /* Did the user request a forced heating ? */
+ if (manual_activation_request > 1) {
+ forced_heater_mode = 1;
+ mode = manual;
+ if (manual_activation_request == MANUAL_ACTIVATION_DURATION) {
+ uprintf(UART1, "Entering manual forced mode for %d ticks\n", manual_activation_request);
+ /* Add a systick callback to handle time counting */
+ add_systick_callback(handle_dec_request, DEC_PERIOD);
+ }
+ if (manual_activation_request < 10) {
+ uprintf(UART1, "Leaving manual forced mode\n");
+ manual_activation_request = 0;
+ remove_systick_callback(handle_dec_request);
+ }
+ }
+
+
+ /* Which is the current mode ? */
+ if (forced_heater_mode == 1) {
+ /* Forced heating mode */
+ if ((forced_heater_delay == 0) && (forced_heater_time > 0)) {
+ dmx_val = FORCED_MODE_VALUE;
+ }
+ if (forced_heater_time == 0) {
+ forced_heater_delay = FORCED_HEATER_DELAY;
+ forced_heater_time = FORCED_HEATER_DURATION;
+ }
+ } else if (moyenne_solar < moyenne_home) {
+ /* Low production mode */
+ if (dmx_val > 25) {
+ dmx_val -= 25;
+ } else {
+ dmx_val = 0;
+ mode = idle_heat;
+ }
+ status_led(green_off);
+ } else {
+ /* High production mode */
+ if (dmx_val < 245) {
+ dmx_val += 10;
+ } else {
+ dmx_val = 255;
+ mode = full_heat;
+ }
+ status_led(green_on);
+ }
+
+ /* Send DMX frame */
+ slots[0] = dmx_val;
+ if (slots[0] > 255) {
+ slots[0] = 255;
+ }
+ dmx_send_frame(0x00, slots, 1);
+ /* Display */
+ if (1) {
+ int abs_centi = centi_degrees;
+ uprintf(UART1, "%c:%d - Is: %d,%04d - Ih: %d,%04d\n", mode, loop++,
+ (moyenne_solar / 1000), (moyenne_solar % 1000),
+ (moyenne_home / 1000), (moyenne_home % 1000));
+ if (centi_degrees < 0) {
+ abs_centi = -centi_degrees;
+ }
+ uprintf(UART1, "Temp : % 4d.%02d\n", (centi_degrees / 100), (abs_centi % 100));
+ uprintf(UART1, "DMX: %d\n\n", slots[0]);
+ }
+ }
+ return 0;
+}
+
+
+
--- /dev/null
+# 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} $@
+
--- /dev/null
+Scialys system for solar-panel power generation tracking and fair use.
+
+Copyright 2016 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/>.
+ *
+ *************************************************************************** */
+
+Beta version of Scialys system.
--- /dev/null
+/****************************************************************************
+ * apps/scialys/beta/main.c
+ *
+ * Scialys system for solar-panel power generation tracking and fair use.
+ *
+ * Copyright 2016 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 "lib/stdio.h"
+#include "lib/errno.h"
+#include "drivers/serial.h"
+#include "drivers/gpio.h"
+#include "drivers/adc.h"
+#include "drivers/ssp.h"
+#include "drivers/i2c.h"
+#include "drivers/timers.h"
+
+#include "extdrv/status_led.h"
+#include "extdrv/ws2812.h"
+#include "extdrv/max31855_thermocouple.h"
+#include "extdrv/tmp101_temp_sensor.h"
+#include "extdrv/rtc_pcf85363a.h"
+#include "extdrv/ssd130x_oled_driver.h"
+#include "extdrv/ssd130x_oled_buffer.h"
+#include "lib/font.h"
+#include "lib/time.h"
+
+#include "extdrv/sdmmc.h"
+
+#define MODULE_VERSION 0x01
+#define MODULE_NAME "Scialys uC"
+
+
+#define SELECTED_FREQ FREQ_SEL_48MHz
+
+
+/***************************************************************************** */
+/* System configuration
+ * Most of the defines in here should go to configuration setting in user flash
+ */
+
+/* Period of the decrementer handler from the systick interrupt */
+#define DEC_PERIOD 100
+
+/* If temperature falls bellow FORCE_HEATER_TEMP value, we enter forced heater mode, until
+ * TARGET_FORCED_HEATER_TEMP is reached.
+ * When in forced heater mode, the heater is controlled to heat at FORCED_MODE_VALUE which
+ * is between 0 and 100.
+ */
+#define FORCE_HEATER_TEMP 35
+#define MAX_HEATER_TEMP 90
+#define TARGET_FORCED_HEATER_TEMP 50
+#define FORCED_MODE_VALUE 75 /* A fraction of 100 */
+/* mA prod value above which the system will not enter forced mode, waiting for home
+ * to stop using power to start automatic heating */
+#define NO_FORCED_HEATING_ON_SUNNY_DAYS 750
+
+uint32_t forced_heater_mode = 0;
+uint32_t forced_heater_delay = 0;
+uint32_t forced_heater_time = 0;
+
+#define FORCED_HEATER_DELAY (2 * 3600 * 1000 / DEC_PERIOD) /* Delay before automatic forced heating */
+#define FORCED_HEATER_DURATION (3 * 3600 * 1000 / DEC_PERIOD) /* Duration of automatic forced heating */
+
+#define MANUAL_ACTIVATION_DURATION (3 * 3600 * 1000 / DEC_PERIOD) /* Three hours */
+
+uint32_t never_force = 0;
+
+
+#define DAY_IS_EJP 0 /* Input is pulled low when EJP is ON */
+int ejp_in = 0;
+
+
+
+/***************************************************************************** */
+/* Pins configuration */
+/* pins blocks are passed to set_pins() for pins configuration.
+ * Unused pin blocks can be removed safely with the corresponding set_pins() call
+ * All pins blocks may be safelly merged in a single block for single set_pins() call..
+ */
+const struct pio_config common_pins[] = {
+ /* UART 0 : Config / Debug / USB */
+ { LPC_UART0_RX_PIO_0_1, LPC_IO_DIGITAL },
+ { LPC_UART0_TX_PIO_0_2, LPC_IO_DIGITAL },
+ /* UART 1 : UEXT */
+ { LPC_UART1_RX_PIO_0_8, LPC_IO_DIGITAL },
+ { LPC_UART1_TX_PIO_0_9, LPC_IO_DIGITAL },
+ /* I2C : RTC, Display, UEXT */
+ { 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) },
+ /* SPI (Thermocouple + uSD card + UEXT) */
+ { LPC_SSP0_SCLK_PIO_0_14, LPC_IO_DIGITAL },
+ { LPC_SSP0_MOSI_PIO_0_17, LPC_IO_DIGITAL },
+ { LPC_SSP0_MISO_PIO_0_16, LPC_IO_DIGITAL },
+ /* TIMER_32B0 */
+ { LPC_TIMER_32B0_M0_PIO_0_18, (LPC_IO_MODE_PULL_UP | LPC_IO_DIGITAL) }, /* Fan control */
+ /* GPIO */
+ { LPC_GPIO_0_0, LPC_IO_DIGITAL }, /* Clkout / interrupt from RTC */
+ { LPC_GPIO_0_3, LPC_IO_DIGITAL }, /* EJP / External switch input */
+ { LPC_GPIO_0_4, LPC_IO_DIGITAL }, /* Zero crossing detection input */
+ { LPC_GPIO_0_5, LPC_IO_DIGITAL }, /* Temperature driver warning (Mosfet board only) */
+ { LPC_GPIO_0_6, LPC_IO_DIGITAL }, /* Mosfet driver Shutdown (Mosfet board only) */
+ { LPC_GPIO_0_7, LPC_IO_DIGITAL }, /* Mosfet / Triac control */
+ { LPC_GPIO_0_12, LPC_IO_DIGITAL }, /* ISP / User button OK */
+ { LPC_GPIO_0_15, LPC_IO_DIGITAL }, /* Thermocouple chip select */
+ { LPC_GPIO_0_23, LPC_IO_DIGITAL }, /* WS2812B RGB Leds control */
+ { LPC_GPIO_0_26, LPC_IO_DIGITAL }, /* User button B2 */
+ { LPC_GPIO_0_27, LPC_IO_DIGITAL }, /* User button B1 */
+ { LPC_GPIO_0_28, LPC_IO_DIGITAL }, /* Charge State */
+ { LPC_GPIO_1_1, LPC_IO_DIGITAL }, /* Uext Chip select / Module eeprom select */
+ { LPC_GPIO_1_6, LPC_IO_DIGITAL }, /* uSD Card SPI Chip Select */
+ ARRAY_LAST_PIO,
+};
+
+const struct pio_config adc_pins[] = {
+ { LPC_ADC_AD0_PIO_0_30, LPC_IO_ANALOG }, /* ADC0 */
+ { LPC_ADC_AD1_PIO_0_31, LPC_IO_ANALOG }, /* ADC1 */
+ { LPC_ADC_AD2_PIO_1_0, LPC_IO_ANALOG }, /* ADC2 */
+ { LPC_ADC_AD7_PIO_1_5, LPC_IO_ANALOG }, /* ADC3 */
+ ARRAY_LAST_PIO,
+};
+
+const struct pio status_led_green = LPC_GPIO_1_2;
+const struct pio status_led_red = LPC_GPIO_1_3;
+
+/* Inputs */
+/* Buttons */
+const struct pio button_ok = LPC_GPIO_0_12;
+const struct pio button_b1 = LPC_GPIO_0_27;
+const struct pio button_b2 = LPC_GPIO_0_26;
+/* External signals */
+const struct pio rtc_in_pin = LPC_GPIO_0_0;
+const struct pio ejp_in_pin = LPC_GPIO_0_3;
+const struct pio zero_cross_in_pin = LPC_GPIO_0_4;
+const struct pio th_warn_in_pin = LPC_GPIO_0_5;
+const struct pio charge_status_in_pin = LPC_GPIO_0_28;
+
+/* Outputs */
+/* Led control data pin */
+const struct pio ws2812_data_out_pin = LPC_GPIO_0_23;
+/* AC output control (Mosfet / Triac) */
+const struct pio ac_ctrl = LPC_GPIO_0_7;
+/* Fixme : Fan */
+
+
+/* Thermocouple reading */
+const struct max31855_sensor_config thermo = {
+ .ssp_bus_num = 0,
+ .chip_select = LPC_GPIO_0_15,
+};
+
+/* TMP101 onboard I2C temperature sensor */
+#define TMP101_ADDR 0x94 /* Pin Addr0 (pin5 of tmp101) connected to VCC */
+struct tmp101_sensor_config tmp101_sensor = {
+ .bus_num = I2C0,
+ .addr = TMP101_ADDR,
+ .resolution = TMP_RES_ELEVEN_BITS,
+};
+
+
+#define FAN_ON (10 * 1000)
+const struct lpc_timer_pwm_config fan_pwm_conf = {
+ .nb_channels = 1,
+ .period_chan = CHAN3,
+ .period = FAN_ON,
+ .outputs = { CHAN0, },
+ .match_values = { 0, },
+};
+
+const struct lpc_tc_config ac_timer_conf = {
+ .mode = LPC_TIMER_MODE_TIMER | LPC_TIMER_MODE_MATCH,
+ .match_control = { 0, LPC_TIMER_INT_RESET_AND_STOP_ON_MATCH, 0, 0, },
+ .match = { 0, 10, 0, 0, },
+ .ext_match_config = { 0, LPC_TIMER_SET_ON_MATCH, 0, 0, },
+};
+
+
+/***************************************************************************** */
+/* SD/MMC Card */
+struct sdmmc_card micro_sd = {
+ .ssp_bus_num = SSP_BUS_0,
+ .card_type = MMC_CARDTYPE_UNKNOWN,
+ .block_size = 64,
+ .chip_select = LPC_GPIO_1_6,
+};
+
+uint8_t mmc_data[MMC_MAX_SECTOR_SIZE];
+
+
+/***************************************************************************** */
+/* RTC and time */
+#define RTC_ADDR 0xA2
+struct rtc_pcf85363a_config rtc_conf = {
+ .bus_num = I2C0,
+ .addr = RTC_ADDR,
+ .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 = 0x18,
+ .month = 0x09,
+ .day = 0x08,
+ .hour = 0x18,
+ .min = 0x00,
+};
+static struct rtc_time now;
+
+
+/***************************************************************************** */
+/* 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);
+ set_pins(adc_pins);
+ gpio_on();
+ status_led_config(&status_led_green, &status_led_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);
+}
+
+
+/***************************************************************************** */
+/* System configuration over USB */
+static uint32_t fan_speed = 0;
+static int act_cmd = 0;
+void config_rx(uint8_t c)
+{
+ /* FAN control */
+ if (c == 'f') {
+ fan_speed = 100;
+ timer_set_match(LPC_TIMER_32B0, CHAN0, FAN_ON);
+ } else if (c == 'z') {
+ fan_speed = 0;
+ timer_set_match(LPC_TIMER_32B0, CHAN0, 0);
+ } else if ((c >= '0') && (c <= '9')) {
+ fan_speed = ((c - '0') * 10);
+ if (fan_speed < 60) {
+ fan_speed = 0;
+ }
+ timer_set_match(LPC_TIMER_32B0, CHAN0, (FAN_ON - ((FAN_ON / 100) * (100 - fan_speed))));
+ }
+}
+
+
+
+/***************************************************************************** */
+/* System communication over UART1 */
+void cmd_rx(uint8_t c)
+{
+}
+
+
+/***************************************************************************** */
+
+void set_ctrl_duty_cycle(uint8_t value)
+{
+ act_cmd = value;
+ if (act_cmd > 100) {
+ /* 100 is the maximum allowed value */
+ act_cmd = 100;
+ } else if (act_cmd <= 2) {
+ /* Below 3% there are triggering problems which lead to 50% instead of 1% or 2% */
+ act_cmd = 0;
+ }
+}
+
+static volatile uint8_t ac_ctrl_state = 0;
+void ac_switch_on(uint32_t flags)
+{
+ /* Generate a 5us (approx) pulse */
+ if (ac_ctrl_state == 1) {
+ /* Start of pulse */
+ gpio_set(ac_ctrl);
+ /* Change state machine state */
+ ac_ctrl_state = 0;
+ /* And request interrupt in approx 5us to clear ac_ctrl output */
+ timer_set_match(LPC_TIMER_32B1, CHAN1, (5 * 480));
+ timer_restart(LPC_TIMER_32B1);
+ } else {
+ /* End of pulse */
+ gpio_clear(ac_ctrl);
+ }
+}
+
+static uint32_t clk_cycles_ac_zc = 0;
+static volatile uint32_t zc_count = 0; /* Wraps every 1.36 year ... */
+void zero_cross(uint32_t gpio)
+{
+ uint32_t delay = 0;
+
+ zc_count ++;
+
+ if (act_cmd == 100) {
+ gpio_set(ac_ctrl);
+ return;
+ }
+ gpio_clear(ac_ctrl);
+ if (act_cmd == 0) {
+ return;
+ }
+ /* Set timer to trigger ac out ON at given delay */
+ delay = clk_cycles_ac_zc * (100 - act_cmd);
+ timer_set_match(LPC_TIMER_32B1, CHAN1, delay);
+ timer_restart(LPC_TIMER_32B1);
+ /* Set "AC triggering" stame machine state */
+ ac_ctrl_state = 1;
+}
+
+
+
+/***************************************************************************** */
+/* System interface */
+enum buttons {
+ BUTTON_NONE = 0,
+ BUTTON_OK,
+ BUTTON_UP,
+ BUTTON_DOWN,
+};
+
+
+uint32_t manual_activation_request = 0;
+uint8_t button_pressed = 0;
+void manual_activation(uint32_t gpio) {
+ manual_activation_request = MANUAL_ACTIVATION_DURATION;
+ button_pressed = BUTTON_OK;
+}
+void manual_up(uint32_t gpio) {
+ manual_activation_request = MANUAL_ACTIVATION_DURATION;
+ button_pressed = BUTTON_UP;
+}
+void manual_down(uint32_t gpio) {
+ manual_activation_request = MANUAL_ACTIVATION_DURATION;
+ button_pressed = BUTTON_DOWN;
+}
+void handle_dec_request(uint32_t curent_tick) {
+ if (manual_activation_request > 0) {
+ manual_activation_request--;
+ }
+ if (forced_heater_mode == 1) {
+ if (forced_heater_delay > 0) {
+ forced_heater_delay--;
+ }
+ if (forced_heater_time > 0) {
+ forced_heater_time--;
+ }
+ }
+}
+
+
+/***************************************************************************** */
+void temp_config(int uart_num)
+{
+ int ret = 0;
+ ret = tmp101_sensor_config(&tmp101_sensor);
+ if (ret != 0) {
+ uprintf(uart_num, "Temp config error: %d\n", ret);
+ }
+}
+
+
+/***************************************************************************** */
+/* Oled Display */
+#define DISPLAY_ADDR 0x78
+static uint8_t gddram[ 4 + GDDRAM_SIZE ];
+struct oled_display display = {
+ .bus_type = SSD130x_BUS_I2C,
+ .address = DISPLAY_ADDR,
+ .bus_num = I2C0,
+ .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);
+}
+#define OLED_LINE_CHAR_LENGTH (SSD130x_NB_COL / 8)
+#define DISPLAY_LINE_LENGTH (OLED_LINE_CHAR_LENGTH + 1)
+int display_line(uint8_t line, uint8_t col, 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) {
+ return i;
+ }
+ }
+ }
+ return len;
+}
+
+
+
+
+/***************************************************************************** */
+#define NB_VAL 20
+
+enum modes {
+ heat = 'C',
+ ejp = 'E',
+ delayed_heat_prod = 'P',
+ forced = 'F',
+ temp_OK = 'T',
+ manual = 'M',
+ idle_heat = 'L',
+ full_heat = 'F',
+};
+
+/***************************************************************************** */
+int main(void)
+{
+ uint16_t isnail_solar_values[NB_VAL];
+ uint16_t isnail_home_values[NB_VAL];
+ uint8_t idx = 0;
+ uint32_t loop = 0;
+ char mode = heat; /* Debug info */
+ int ret = 0;
+
+ system_init();
+ status_led(red_only);
+ uart_on(UART0, 115200, config_rx);
+ uart_on(UART1, 115200, cmd_rx);
+ i2c_on(I2C0, I2C_CLK_100KHz, I2C_MASTER);
+ ssp_master_on(thermo.ssp_bus_num, LPC_SSP_FRAME_SPI, 8, 4*1000*1000);
+ adc_on(NULL);
+ timer_on(LPC_TIMER_32B0, 0, NULL);
+ timer_on(LPC_TIMER_32B1, 0, ac_switch_on);
+
+ /* Immediatly turn off Mosfet / Triac */
+ config_gpio(&ac_ctrl, 0, GPIO_DIR_OUT, 0);
+
+ /* Thermocouple configuration */
+ max31855_sensor_config(&thermo);
+ uprintf(UART0, "Thermocouple config done\n");
+
+ /* TMP101 sensor config */
+ temp_config(UART0);
+
+ /* Activate on Rising edge (button release) */
+ set_gpio_callback(manual_activation, &button_ok, EDGE_RISING);
+#if 0
+ /* Debug */
+ config_gpio(&button_b1, 0, GPIO_DIR_OUT, 0);
+ config_gpio(&button_b2, 0, GPIO_DIR_OUT, 0);
+#endif
+ set_gpio_callback(manual_up, &button_b1, EDGE_RISING);
+ set_gpio_callback(manual_down, &button_b2, EDGE_RISING);
+
+ /* Zero cross and alert pin */
+ set_gpio_callback(zero_cross, &zero_cross_in_pin, EDGE_FALLING);
+
+ /* Start ADC sampling */
+ adc_start_burst_conversion(ADC_MCH(0) | ADC_MCH(1) | ADC_MCH(2) | ADC_MCH(7), LPC_ADC_SEQ(0));
+
+ /* Configure Input GPIO */
+ config_gpio(&ejp_in_pin, 0, GPIO_DIR_IN, 0);
+ config_gpio(&rtc_in_pin, 0, GPIO_DIR_IN, 0);
+ config_gpio(&charge_status_in_pin, 0, GPIO_DIR_IN, 1);
+
+ /* WS2812B Leds on display board */
+ ws2812_config(&ws2812_data_out_pin);
+
+ /* FAN Config */
+ timer_pwm_config(LPC_TIMER_32B0, &fan_pwm_conf);
+ timer_start(LPC_TIMER_32B0);
+ /* AC Switch Config */
+ timer_counter_config(LPC_TIMER_32B1, &ac_timer_conf);
+ /* We want 100 Hz (50 Hz but two zero crossings) with 1% granularity */
+ clk_cycles_ac_zc = get_main_clock() / (100 * 100);
+
+ status_led(green_only);
+
+ /* Configure and start display */
+ ret = ssd130x_display_on(&display);
+ /* Erase screen */
+ ssd130x_buffer_set(gddram, 0x00);
+ ret = ssd130x_display_full_screen(&display);
+
+ /* RTC init */
+ ret = rtc_pcf85363a_config(&rtc_conf);
+ ret = rtc_pcf85363a_is_up(&rtc_conf, &oldest);
+ if (ret == 1) {
+ char buff[30];
+ rtc_pcf85363_time_read(&rtc_conf, &now);
+ rtc_pcf85363_time_to_str(&now, buff, 30);
+ /* Debug */
+ uprintf(UART0, buff);
+ } else if (ret == -EFAULT) {
+ memcpy(&now, &oldest, sizeof(struct rtc_time));
+ rtc_pcf85363_time_write(&rtc_conf, &now);
+ }
+
+ /* microSD card init */
+ ret = sdmmc_init(µ_sd);
+ if (ret == 0) {
+ msleep(1);
+ ret = sdmmc_init_wait_card_ready(µ_sd);
+ if (ret == 0) {
+ ret = sdmmc_init_end(µ_sd);
+ }
+ }
+ uprintf(UART0, "uSD init: %d, type: %d, bs: %d\n", ret, micro_sd.card_type, micro_sd.block_size);
+ ret = sdmmc_read_block(µ_sd, 0, mmc_data);
+ uprintf(UART0, "uSD read: %s\n", mmc_data);
+
+ /* Add a systick callback to handle time counting */
+ //add_systick_callback(handle_dec_request, DEC_PERIOD);
+
+ msleep(50);
+ /* Read parameters from memory */
+ if (1) {
+ never_force = 0;
+ forced_heater_delay = 0;
+ forced_heater_time = FORCED_HEATER_DURATION;
+ }
+
+ while (1) {
+ static uint8_t command_val = 0;
+ static uint8_t n_dec = 0; /* Add some PID like (derivative part) */
+ static uint8_t n_inc = 0; /* Add some PID like (derivative part) */
+ int moyenne_solar = 0;
+ int moyenne_home = 0;
+ uint16_t isnail_val_solar = 0;
+ uint16_t isnail_val_home = 0;
+ uint16_t acs_val_load = 0;
+ uint16_t user_potar = 0;
+ int water_centi_degrees = 0;
+ int tmp101_deci_degrees = 0;
+
+ mode = heat;
+ tmp101_sensor_start_conversion(&tmp101_sensor);
+ /* Always track power consumption and production */
+ adc_get_value(&isnail_val_solar, LPC_ADC(1));
+ adc_get_value(&isnail_val_home, LPC_ADC(0));
+ adc_get_value(&acs_val_load, LPC_ADC(2));
+ adc_get_value(&user_potar, LPC_ADC(7));
+ /* Convert to mA value */
+ isnail_val_solar = ((isnail_val_solar * 32) * 2); /* 3.2mV / digit, 50mV -> 1A */
+ isnail_val_home = ((isnail_val_home * 32) * 2); /* 3.2mV / digit, 50mV -> 1A */
+ /* Store value */
+ isnail_solar_values[idx] = isnail_val_solar;
+ isnail_home_values[idx++] = isnail_val_home;
+ if (idx == NB_VAL) {
+ idx = 0;
+ }
+ /* Compute average value when we sampled enough values */
+ /* FIXME : Improve by substracting oldest value before storing new one in table and adding new one */
+ if ((idx == 0) || (idx == (NB_VAL / 2))) {
+ int i = 0;
+ for (i = 0; i < NB_VAL; i++) {
+ moyenne_solar += isnail_solar_values[i];
+ moyenne_home += isnail_home_values[i];
+ }
+ moyenne_solar = moyenne_solar / NB_VAL;
+ moyenne_home = moyenne_home / NB_VAL;
+ } else {
+ /* Sleep for a litle more than a period (20ms at 50Hz) */
+ msleep(23);
+ continue;
+ }
+
+ /* Feed the dog */
+ if ((moyenne_solar != 0) && (moyenne_home != 0)) {
+ watchdog_feed();
+ }
+
+ /* Get internal temperature */
+ if (1) {
+ int ret = 0;
+ msleep(40);
+ ret = tmp101_sensor_read(&tmp101_sensor, NULL, &tmp101_deci_degrees);
+ if (ret != 0) {
+ uprintf(UART0, "TMP101 read error : %d\n", ret);
+ }
+ }
+ /* If internal temperature is above 30°C, then turn on fan. Turn of when back to under 28.5°C */
+ if (tmp101_deci_degrees > 300) {
+ fan_speed = 100;
+ timer_set_match(LPC_TIMER_32B0, CHAN0, FAN_ON);
+ } else if (tmp101_deci_degrees < 285) {
+ fan_speed = 0;
+ timer_set_match(LPC_TIMER_32B0, CHAN0, 0);
+ }
+
+ /* Get thermocouple value */
+ if (1) {
+ int ret = 0;
+ ret = max31855_sensor_read(&thermo, NULL, &water_centi_degrees);
+ if (ret != 0) {
+ uprintf(UART0, "Water Temp read error : %d\n", ret);
+ }
+ }
+
+ /* Need to enter Forced heating mode ? */
+ if (water_centi_degrees < (FORCE_HEATER_TEMP * 100)) {
+ if (forced_heater_mode == 0) {
+ uprintf(UART0, "Entering forced mode\n");
+ forced_heater_mode = 1;
+ }
+ status_led(red_on);
+ mode = forced;
+ } else if ((water_centi_degrees > (TARGET_FORCED_HEATER_TEMP * 100)) && (forced_heater_mode == 1)) {
+ status_led(red_off);
+ forced_heater_mode = 0;
+ command_val = 0;
+ uprintf(UART0, "Forced mode exit\n");
+ mode = temp_OK;
+ }
+
+ /* Do not force if there is a lot of sun, it may be enough to heat again soon */
+ if (moyenne_solar > NO_FORCED_HEATING_ON_SUNNY_DAYS) {
+ mode = delayed_heat_prod;
+ forced_heater_mode = 0;
+ /* Note : Do not set forced_heater_mode to 0 in order to keep decrementing the delay for force
+ * heating in case the house power usage does not fall below the production value. */
+ }
+
+ /* Do not force heating if this is an EJP day */
+ ejp_in = gpio_read(ejp_in_pin);
+ if (ejp_in == DAY_IS_EJP) {
+ forced_heater_mode = 0;
+ mode = ejp;
+ }
+
+ if (never_force == 1) {
+ forced_heater_mode = 0;
+ }
+
+ /* Did the user request a forced heating ? */
+ if (manual_activation_request > 1) {
+ forced_heater_mode = 1;
+ mode = manual;
+ if (manual_activation_request == MANUAL_ACTIVATION_DURATION) {
+ uprintf(UART0, "Entering manual forced mode for %d ticks\n", manual_activation_request);
+ /* Add a systick callback to handle time counting */
+ add_systick_callback(handle_dec_request, DEC_PERIOD);
+ }
+ if (manual_activation_request < 10) {
+ uprintf(UART0, "Leaving manual forced mode\n");
+ manual_activation_request = 0;
+ remove_systick_callback(handle_dec_request);
+ }
+ }
+
+
+ /* Which is the current mode ? */
+ if (forced_heater_mode == 1) {
+ /* Forced heating mode */
+ if ((forced_heater_delay == 0) && (forced_heater_time > 0)) {
+ command_val = FORCED_MODE_VALUE;
+ }
+ if (forced_heater_time == 0) {
+ forced_heater_delay = FORCED_HEATER_DELAY;
+ forced_heater_time = FORCED_HEATER_DURATION;
+ }
+ } else if (moyenne_solar < (moyenne_home - 75)) {
+ /* Low production mode */
+ if (command_val > 15) {
+ command_val -= ((3 + n_dec) % 15);
+ /* Asservissement */
+ n_dec++;
+ if (n_dec >= 3) {
+ n_inc = 0;
+ }
+ } else {
+ command_val = 0;
+ mode = idle_heat;
+ }
+ status_led(green_off);
+ } else if (moyenne_solar > (moyenne_home + 75)) {
+ /* High production mode */
+ if (command_val < 95) {
+ command_val += (2 + n_inc);
+ /* Asservissement */
+ n_inc++;
+ if (n_inc >= 5) {
+ n_inc = 5;
+ n_dec = 0;
+ }
+ } else {
+ command_val = 100;
+ mode = full_heat;
+ }
+ status_led(green_on);
+ }
+
+ /* Set Control Output duty cycle */
+ if (water_centi_degrees > (MAX_HEATER_TEMP * 100)) {
+ command_val = 0;
+ }
+
+ set_ctrl_duty_cycle(command_val);
+ /* Debug Nath TMP */
+ //set_ctrl_duty_cycle( user_potar / 10 );
+ /* Display */
+ if (1) {
+ char line[DISPLAY_LINE_LENGTH];
+ int abs_centi = water_centi_degrees;
+ int abs_deci = tmp101_deci_degrees;
+ uprintf(UART0, "%c:%d - Is: %d,%04d - Ih: %d,%04d\n", mode, loop++,
+ (moyenne_solar / 1000), (moyenne_solar % 1000),
+ (moyenne_home / 1000), (moyenne_home % 1000));
+ if (water_centi_degrees < 0) {
+ abs_centi = -water_centi_degrees;
+ }
+ uprintf(UART0, "Water Temp : % 4d.%02d\n", (water_centi_degrees / 100), (abs_centi % 100));
+ if (tmp101_deci_degrees < 0) {
+ abs_deci = -tmp101_deci_degrees;
+ }
+ uprintf(UART0, "Internal Temp : % 4d.%02d\n", (tmp101_deci_degrees / 10), (abs_deci % 10));
+ uprintf(UART0, "ZC_cnt: %d\n", zc_count);
+ uprintf(UART0, "ADC: Sol: %dmA, Home: %dmA, Load: %d, User: %d\n",
+ isnail_val_solar, isnail_val_home, acs_val_load, user_potar);
+ if (button_pressed != 0) {
+ uprintf(UART0, "Button : %d\n", button_pressed);
+ button_pressed = 0;
+ }
+ uprintf(UART0, "CMD: %d/%d, Fan: %d, ndec:%d, ninc:%d\n\n", command_val, act_cmd, fan_speed, n_dec, n_inc);
+ ws2812_set_pixel(0, (isnail_val_home / 2000), (isnail_val_solar / 2000), fan_speed);
+ ws2812_set_pixel(1, 0, 0, (user_potar >> 2));
+ ws2812_send_frame(0);
+ /* Erase screen (internal copy) */
+ ssd130x_buffer_set(gddram, 0x00);
+ /* Update time and time display on internal memory */
+ rtc_pcf85363_time_read(&rtc_conf, &now);
+ snprintf(line, DISPLAY_LINE_LENGTH, "%02xh%02x:%02x", now.hour, now.min, now.sec);
+ display_line(0, 0, line);
+ /* Display info */
+ snprintf(line, DISPLAY_LINE_LENGTH, "Water:% 2d.%03d %cC", (water_centi_degrees / 100), (abs_centi % 100), 0x1F);
+ display_line(2, 0, line);
+ snprintf(line, DISPLAY_LINE_LENGTH, "Prod :% 2d,%03dA", (isnail_val_solar / 1000), ((isnail_val_solar % 1000) / 10));
+ display_line(3, 0, line);
+ snprintf(line, DISPLAY_LINE_LENGTH, "Conso:% 2d,%03dA", (isnail_val_home / 1000), ((isnail_val_home % 1000) / 10));
+ display_line(4, 0, line);
+ snprintf(line, DISPLAY_LINE_LENGTH, "Command: %d%%", act_cmd);
+ display_line(5, 0, line);
+ snprintf(line, DISPLAY_LINE_LENGTH, "Mode: %c", mode);
+ display_line(6, 0, line);
+ /* Update Oled display */
+ ret = ssd130x_display_full_screen(&display);
+ }
+ }
+ return 0;
+}
+
+
+
--- /dev/null
+# 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} $@
+
--- /dev/null
+Scialys system for solar-panel power generation tracking and fair use.
+
+Copyright 2016 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/>.
+ *
+ *************************************************************************** */
+
+Beta version of Scialys system.
--- /dev/null
+/****************************************************************************
+ * apps/scialys/beta/main.c
+ *
+ * Scialys system for solar-panel power generation tracking and fair use.
+ *
+ * Copyright 2016 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 "lib/stdio.h"
+#include "lib/errno.h"
+#include "drivers/serial.h"
+#include "drivers/gpio.h"
+#include "drivers/adc.h"
+#include "drivers/ssp.h"
+#include "drivers/i2c.h"
+#include "drivers/timers.h"
+
+#include "extdrv/status_led.h"
+#include "extdrv/ws2812.h"
+#include "extdrv/max31855_thermocouple.h"
+#include "extdrv/tmp101_temp_sensor.h"
+#include "extdrv/rtc_pcf85363a.h"
+#include "extdrv/ssd130x_oled_driver.h"
+#include "lib/font.h"
+#include "lib/time.h"
+
+#include "extdrv/sdmmc.h"
+
+#define MODULE_VERSION 0x02
+#define MODULE_NAME "Scialys uC"
+
+
+#define SELECTED_FREQ FREQ_SEL_48MHz
+
+
+/***************************************************************************** */
+/* System configuration
+ * Most of the defines in here should go to configuration setting in user flash
+ */
+
+/* Period of the decrementer handler from the systick interrupt */
+#define DEC_PERIOD 100
+
+/* If temperature falls bellow FORCE_HEATER_TEMP value, we enter forced heater mode, until
+ * TARGET_FORCED_HEATER_TEMP is reached.
+ * When in forced heater mode, the heater is controlled to heat at FORCED_MODE_VALUE which
+ * is between 0 and 100.
+ */
+#define FORCE_HEATER_TEMP 28
+#define TARGET_FORCED_HEATER_TEMP 45
+#define FORCED_MODE_VALUE 75 /* A fraction of 100 */
+/* mA prod value above which the system will not enter forced mode, waiting for home
+ * to stop using power to start automatic heating */
+#define NO_FORCED_HEATING_ON_SUNNY_DAYS 1000
+
+uint32_t forced_heater_mode = 0;
+uint32_t forced_heater_delay = 0;
+uint32_t forced_heater_time = 0;
+
+#define FORCED_HEATER_DELAY (2 * 3600 * 1000 / DEC_PERIOD) /* Delay before automatic forced heating */
+#define FORCED_HEATER_DURATION (3 * 3600 * 1000 / DEC_PERIOD) /* Duration of automatic forced heating */
+
+#define MANUAL_ACTIVATION_DURATION (3 * 3600 * 1000 / DEC_PERIOD) /* Three hours */
+
+uint32_t never_force = 0;
+
+
+#define DAY_IS_EJP 0 /* Input is pulled low when EJP is ON */
+int ejp_in = 0;
+
+
+
+/***************************************************************************** */
+/* Pins configuration */
+/* pins blocks are passed to set_pins() for pins configuration.
+ * Unused pin blocks can be removed safely with the corresponding set_pins() call
+ * All pins blocks may be safelly merged in a single block for single set_pins() call..
+ */
+const struct pio_config common_pins[] = {
+ /* UART 0 : Config / Debug / USB */
+ { LPC_UART0_RX_PIO_0_1, LPC_IO_DIGITAL },
+ { LPC_UART0_TX_PIO_0_2, LPC_IO_DIGITAL },
+ /* UART 1 : UEXT */
+ { LPC_UART1_RX_PIO_0_8, LPC_IO_DIGITAL },
+ { LPC_UART1_TX_PIO_0_9, LPC_IO_DIGITAL },
+ /* I2C : RTC, Display, UEXT */
+ { 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) },
+ /* SPI (Thermocouple + uSD card + UEXT) */
+ { LPC_SSP0_SCLK_PIO_0_14, LPC_IO_DIGITAL },
+ { LPC_SSP0_MOSI_PIO_0_17, LPC_IO_DIGITAL },
+ { LPC_SSP0_MISO_PIO_0_16, LPC_IO_DIGITAL },
+ /* TIMER_32B0 */
+ { LPC_TIMER_32B0_M0_PIO_0_18, (LPC_IO_MODE_PULL_UP | LPC_IO_DIGITAL) }, /* Fan control */
+ /* GPIO */
+ { LPC_GPIO_0_0, LPC_IO_DIGITAL }, /* Clkout / interrupt from RTC */
+ { LPC_GPIO_0_3, LPC_IO_DIGITAL }, /* EJP / External switch input */
+ { LPC_GPIO_0_4, LPC_IO_DIGITAL }, /* Zero crossing detection input */
+ { LPC_GPIO_0_5, LPC_IO_DIGITAL }, /* Temperature driver warning (Mosfet board only) */
+ { LPC_GPIO_0_6, LPC_IO_DIGITAL }, /* Mosfet driver Shutdown (Mosfet board only) */
+ { LPC_GPIO_0_7, LPC_IO_DIGITAL }, /* Mosfet / Triac control */
+ { LPC_GPIO_0_12, LPC_IO_DIGITAL }, /* ISP / User button OK */
+ { LPC_GPIO_0_15, LPC_IO_DIGITAL }, /* Thermocouple chip select */
+ { LPC_GPIO_0_23, LPC_IO_DIGITAL }, /* WS2812B RGB Leds control */
+ { LPC_GPIO_0_26, LPC_IO_DIGITAL }, /* User button B2 */
+ { LPC_GPIO_0_27, LPC_IO_DIGITAL }, /* User button B1 */
+ { LPC_GPIO_0_28, LPC_IO_DIGITAL }, /* Charge State */
+ { LPC_GPIO_1_1, LPC_IO_DIGITAL }, /* Uext Chip select / Module eeprom select */
+ { LPC_GPIO_1_6, LPC_IO_DIGITAL }, /* uSD Card SPI Chip Select */
+ ARRAY_LAST_PIO,
+};
+
+const struct pio_config adc_pins[] = {
+ { LPC_ADC_AD0_PIO_0_30, LPC_IO_ANALOG }, /* ADC0 */
+ { LPC_ADC_AD1_PIO_0_31, LPC_IO_ANALOG }, /* ADC1 */
+ { LPC_ADC_AD2_PIO_1_0, LPC_IO_ANALOG }, /* ADC2 */
+ { LPC_ADC_AD7_PIO_1_5, LPC_IO_ANALOG }, /* ADC3 */
+ ARRAY_LAST_PIO,
+};
+
+const struct pio status_led_green = LPC_GPIO_1_2;
+const struct pio status_led_red = LPC_GPIO_1_3;
+
+/* Inputs */
+/* Buttons */
+const struct pio button_ok = LPC_GPIO_0_12;
+const struct pio button_b1 = LPC_GPIO_0_27;
+const struct pio button_b2 = LPC_GPIO_0_26;
+/* External signals */
+const struct pio rtc_in_pin = LPC_GPIO_0_0;
+const struct pio ejp_in_pin = LPC_GPIO_0_3;
+const struct pio zero_cross_in_pin = LPC_GPIO_0_4;
+const struct pio th_warn_in_pin = LPC_GPIO_0_5;
+const struct pio charge_status_in_pin = LPC_GPIO_0_28;
+
+/* Outputs */
+/* Led control data pin */
+const struct pio ws2812_data_out_pin = LPC_GPIO_0_23;
+/* AC output control (Mosfet / Triac) */
+const struct pio ac_ctrl = LPC_GPIO_0_7;
+const struct pio mos_shutdown = LPC_GPIO_0_6;
+
+
+/* Thermocouple reading */
+const struct max31855_sensor_config thermo = {
+ .ssp_bus_num = 0,
+ .chip_select = LPC_GPIO_0_15,
+};
+
+/* TMP101 onboard I2C temperature sensor */
+#define TMP101_ADDR 0x94 /* Pin Addr0 (pin5 of tmp101) connected to VCC */
+struct tmp101_sensor_config tmp101_sensor = {
+ .bus_num = I2C0,
+ .addr = TMP101_ADDR,
+ .resolution = TMP_RES_ELEVEN_BITS,
+};
+
+
+#define FAN_ON (10 * 1000)
+const struct lpc_timer_pwm_config fan_pwm_conf = {
+ .nb_channels = 1,
+ .period_chan = CHAN3,
+ .period = FAN_ON,
+ .outputs = { CHAN0, },
+ .match_values = { 0, },
+};
+
+const struct lpc_tc_config ac_timer_conf = {
+ .mode = LPC_TIMER_MODE_TIMER | LPC_TIMER_MODE_MATCH,
+ .match_control = { 0, LPC_TIMER_INT_RESET_AND_STOP_ON_MATCH, 0, 0, },
+ .match = { 0, 10, 0, 0, },
+ .ext_match_config = { 0, LPC_TIMER_SET_ON_MATCH, 0, 0, },
+};
+
+
+/***************************************************************************** */
+/* SD/MMC Card */
+struct sdmmc_card micro_sd = {
+ .ssp_bus_num = SSP_BUS_0,
+ .card_type = MMC_CARDTYPE_UNKNOWN,
+ .block_size = 64,
+ .chip_select = LPC_GPIO_1_6,
+};
+
+uint8_t mmc_data[MMC_MAX_SECTOR_SIZE];
+
+
+/***************************************************************************** */
+/* RTC and time */
+#define RTC_ADDR 0xA2
+struct rtc_pcf85363a_config rtc_conf = {
+ .bus_num = I2C0,
+ .addr = RTC_ADDR,
+ .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 = 0x16,
+ .month = 0x10,
+ .day = 0x30,
+ .hour = 0x13,
+ .min = 0x00,
+};
+static struct rtc_time now;
+
+
+/***************************************************************************** */
+/* 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);
+ set_pins(adc_pins);
+ gpio_on();
+ status_led_config(&status_led_green, &status_led_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);
+}
+
+
+/***************************************************************************** */
+/* Some global flags or variables */
+static uint8_t thermal_warn_flag = 0;
+static uint32_t fan_speed = 0;
+static int act_cmd = 0;
+
+
+/***************************************************************************** */
+/* System configuration over USB */
+void config_rx(uint8_t c)
+{
+ /* FAN control */
+ if (c == 'f') {
+ fan_speed = 100;
+ timer_set_match(LPC_TIMER_32B0, CHAN0, FAN_ON);
+ } else if (c == 'z') {
+ fan_speed = 0;
+ timer_set_match(LPC_TIMER_32B0, CHAN0, 0);
+ } else if ((c >= '0') && (c <= '9')) {
+ fan_speed = ((c - '0') * 10);
+ if (fan_speed < 60) {
+ fan_speed = 0;
+ }
+ timer_set_match(LPC_TIMER_32B0, CHAN0, (FAN_ON - ((FAN_ON / 100) * (100 - fan_speed))));
+ }
+}
+
+
+
+/***************************************************************************** */
+/* System communication over UART1 */
+void cmd_rx(uint8_t c)
+{
+}
+
+
+
+/***************************************************************************** */
+/* AC control */
+
+void set_ctrl_duty_cycle(uint8_t value)
+{
+ if (thermal_warn_flag == 1) {
+ /* Thermal warning condition still present ? */
+ if (gpio_read(th_warn_in_pin) != 0) {
+ thermal_warn_flag = 0;
+ } else {
+ act_cmd = 0;
+ return;
+ }
+ }
+ act_cmd = value;
+ if (act_cmd > 100) {
+ /* 100 is the maximum allowed value */
+ act_cmd = 100;
+ } else if (act_cmd <= 2) {
+ /* Below 3% there are triggering problems which lead to 50% instead of 1% or 2% */
+ act_cmd = 0;
+ /* Set mosfet driver shutdown */
+ gpio_set(mos_shutdown);
+ return;
+ }
+ /* Clear the shutdown condition from the mosfet driver */
+ gpio_clear(mos_shutdown);
+}
+
+void ac_switch_on(uint32_t flags)
+{
+ gpio_set(ac_ctrl);
+ gpio_set(button_b2);
+}
+
+static uint32_t clk_cycles_ac_zc = 0;
+static volatile uint32_t zc_count = 0; /* Wraps every 1.36 year ... */
+void zero_cross(uint32_t gpio)
+{
+ uint32_t delay = 0;
+
+ zc_count ++;
+ gpio_toggle(button_b1);
+
+ gpio_clear(ac_ctrl);
+ gpio_clear(button_b2);
+ if (act_cmd == 0) {
+ return;
+ }
+ /* Set timer to trigger ac out ON at given delay */
+ delay = clk_cycles_ac_zc * act_cmd;
+ timer_set_match(LPC_TIMER_32B1, CHAN1, delay);
+ timer_restart(LPC_TIMER_32B1);
+}
+
+void th_warning(uint32_t gpio)
+{
+ /* Test for condition set or removed */
+ if (gpio_read(th_warn_in_pin) == 0) {
+ /* Turn off AC output */
+ gpio_clear(ac_ctrl);
+ /* Set mosfet driver shutdown */
+ gpio_set(mos_shutdown);
+ act_cmd = 0;
+ thermal_warn_flag = 1;
+ /* Turn on Fan at max speed */
+ timer_set_match(LPC_TIMER_32B0, CHAN0, FAN_ON);
+ fan_speed = 100;
+ } else {
+ /* Only clear the warning flag, the rest will get back to the desired state soon enough. */
+ thermal_warn_flag = 0;
+ }
+}
+
+
+
+/***************************************************************************** */
+/* System interface */
+enum buttons {
+ BUTTON_NONE = 0,
+ BUTTON_OK,
+ BUTTON_UP,
+ BUTTON_DOWN,
+};
+
+
+uint32_t manual_activation_request = 0;
+uint8_t button_pressed = 0;
+void manual_activation(uint32_t gpio) {
+ manual_activation_request = MANUAL_ACTIVATION_DURATION;
+ button_pressed = BUTTON_OK;
+}
+void manual_up(uint32_t gpio) {
+ manual_activation_request = MANUAL_ACTIVATION_DURATION;
+ button_pressed = BUTTON_UP;
+}
+void manual_down(uint32_t gpio) {
+ manual_activation_request = MANUAL_ACTIVATION_DURATION;
+ button_pressed = BUTTON_DOWN;
+}
+void handle_dec_request(uint32_t curent_tick) {
+ if (manual_activation_request > 0) {
+ manual_activation_request--;
+ }
+ if (forced_heater_mode == 1) {
+ if (forced_heater_delay > 0) {
+ forced_heater_delay--;
+ }
+ if (forced_heater_time > 0) {
+ forced_heater_time--;
+ }
+ }
+}
+
+
+/***************************************************************************** */
+void temp_config(int uart_num)
+{
+ int ret = 0;
+ ret = tmp101_sensor_config(&tmp101_sensor);
+ if (ret != 0) {
+ uprintf(uart_num, "Temp config error: %d\n", ret);
+ }
+}
+
+
+/***************************************************************************** */
+/* Oled Display */
+#define DISPLAY_ADDR 0x78
+struct oled_display display = {
+ .address = DISPLAY_ADDR,
+ .bus_num = I2C0,
+ .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,
+};
+
+#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_set_tile(&display, col, line, tile_data);
+}
+#define OLED_LINE_CHAR_LENGTH (SSD130x_NB_COL / 8)
+#define DISPLAY_LINE_LENGTH (OLED_LINE_CHAR_LENGTH + 1)
+int display_line(uint8_t line, uint8_t col, 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_set_tile(&display, col++, line, tile_data);
+ if (col >= (OLED_LINE_CHAR_LENGTH)) {
+ col = 0;
+ line++;
+ if (line >= SSD130x_NB_PAGES) {
+ return i;
+ }
+ }
+ }
+ return len;
+}
+
+
+
+
+/***************************************************************************** */
+#define NB_VAL 20
+
+enum modes {
+ heat = 'C',
+ ejp = 'E',
+ delayed_heat_prod = 'P',
+ forced = 'F',
+ temp_OK = 'T',
+ manual = 'M',
+ idle_heat = 'L',
+ full_heat = 'F',
+};
+
+/***************************************************************************** */
+int main(void)
+{
+ uint16_t isnail_solar_values[NB_VAL];
+ uint16_t isnail_home_values[NB_VAL];
+ uint8_t idx = 0;
+ uint32_t loop = 0;
+ char mode = heat; /* Debug info */
+ int ret = 0;
+
+ system_init();
+ status_led(red_only);
+ uart_on(UART0, 115200, config_rx);
+ uart_on(UART1, 115200, cmd_rx);
+ i2c_on(I2C0, I2C_CLK_100KHz, I2C_MASTER);
+ ssp_master_on(thermo.ssp_bus_num, LPC_SSP_FRAME_SPI, 8, 4*1000*1000);
+ adc_on(NULL);
+ timer_on(LPC_TIMER_32B0, 0, NULL);
+ timer_on(LPC_TIMER_32B1, 0, ac_switch_on);
+
+ /* Immediatly turn off Mosfet / Triac */
+ config_gpio(&ac_ctrl, 0, GPIO_DIR_OUT, 0);
+ config_gpio(&mos_shutdown, 0, GPIO_DIR_OUT, 1);
+
+ /* Thermocouple configuration */
+ max31855_sensor_config(&thermo);
+ uprintf(UART0, "Thermocouple config done\n");
+
+ /* TMP101 sensor config */
+ temp_config(UART0);
+
+ /* Activate on Rising edge (button release) */
+ set_gpio_callback(manual_activation, &button_ok, EDGE_RISING);
+#if 0
+ set_gpio_callback(manual_up, &button_b1, EDGE_RISING);
+ set_gpio_callback(manual_down, &button_b2, EDGE_RISING);
+#else
+ config_gpio(&button_b1, 0, GPIO_DIR_OUT, 0);
+ config_gpio(&button_b2, 0, GPIO_DIR_OUT, 0);
+#endif
+
+ /* Zero cross and alert pin */
+ set_gpio_callback(zero_cross, &zero_cross_in_pin, EDGE_FALLING);
+ set_gpio_callback(th_warning, &th_warn_in_pin, EDGES_BOTH);
+
+ /* Start ADC sampling */
+ adc_start_burst_conversion(ADC_MCH(0) | ADC_MCH(1) | ADC_MCH(2) | ADC_MCH(7), LPC_ADC_SEQ(0));
+
+ /* Configure Input GPIO */
+ config_gpio(&ejp_in_pin, 0, GPIO_DIR_IN, 0);
+ config_gpio(&rtc_in_pin, 0, GPIO_DIR_IN, 0);
+ config_gpio(&charge_status_in_pin, 0, GPIO_DIR_IN, 1);
+
+ /* WS2812B Leds on display board */
+ ws2812_config(&ws2812_data_out_pin);
+
+ /* FAN Config */
+ timer_pwm_config(LPC_TIMER_32B0, &fan_pwm_conf);
+ timer_start(LPC_TIMER_32B0);
+ /* AC Switch Config */
+ timer_counter_config(LPC_TIMER_32B1, &ac_timer_conf);
+ /* We want 100 Hz (50 Hz but two zero crossings) with 1% granularity */
+ clk_cycles_ac_zc = get_main_clock() / (100 * 100);
+
+ status_led(green_only);
+
+ /* Configure and start display */
+ ret = ssd130x_display_on(&display);
+ /* Erase screen */
+ ssd130x_display_set(&display, 0x00);
+ ret = ssd130x_display_full_screen(&display);
+
+ /* RTC init */
+ ret = rtc_pcf85363a_config(&rtc_conf);
+ ret = rtc_pcf85363a_is_up(&rtc_conf, &oldest);
+ if (ret == 1) {
+ char buff[30];
+ rtc_pcf85363_time_read(&rtc_conf, &now);
+ rtc_pcf85363_time_to_str(&now, buff, 30);
+ /* Debug */
+ uprintf(UART0, buff);
+ } else if (ret == -EFAULT) {
+ memcpy(&now, &oldest, sizeof(struct rtc_time));
+ rtc_pcf85363_time_write(&rtc_conf, &now);
+ }
+
+ /* microSD card init */
+ ret = sdmmc_init(µ_sd);
+ if (ret == 0) {
+ msleep(1);
+ ret = sdmmc_init_wait_card_ready(µ_sd);
+ if (ret == 0) {
+ ret = sdmmc_init_end(µ_sd);
+ }
+ }
+ uprintf(UART0, "uSD init: %d, type: %d, bs: %d\n", ret, micro_sd.card_type, micro_sd.block_size);
+ ret = sdmmc_read_block(µ_sd, 0, mmc_data);
+ uprintf(UART0, "uSD read: %s\n", mmc_data);
+
+ /* Add a systick callback to handle time counting */
+ //add_systick_callback(handle_dec_request, DEC_PERIOD);
+
+ msleep(50);
+ /* Read parameters from memory */
+ if (1) {
+ never_force = 0;
+ forced_heater_delay = 0;
+ forced_heater_time = FORCED_HEATER_DURATION;
+ }
+
+ /* Clear thermal warning flag if warning pin is high */
+ if (gpio_read(th_warn_in_pin) != 0) {
+ thermal_warn_flag = 0;
+ }
+
+ while (1) {
+ static uint8_t command_val = 0;
+ static uint8_t n_dec = 0; /* Add some PID like (derivative part) */
+ static uint8_t n_inc = 0; /* Add some PID like (derivative part) */
+ int moyenne_solar = 0;
+ int moyenne_home = 0;
+ uint16_t isnail_val_solar = 0;
+ uint16_t isnail_val_home = 0;
+ uint16_t acs_val_load = 0;
+ uint16_t user_potar = 0;
+ int water_centi_degrees = 0;
+ int tmp101_deci_degrees = 0;
+
+ mode = heat;
+ tmp101_sensor_start_conversion(&tmp101_sensor);
+ /* Always track power consumption and production */
+ adc_get_value(&isnail_val_solar, LPC_ADC(1));
+ adc_get_value(&isnail_val_home, LPC_ADC(0));
+ adc_get_value(&acs_val_load, LPC_ADC(2));
+ adc_get_value(&user_potar, LPC_ADC(7));
+ /* Convert to mA value */
+ isnail_val_solar = ((isnail_val_solar * 32) * 2); /* 3.2mV / digit, 50mV -> 1A */
+ isnail_val_home = ((isnail_val_home * 32) * 2); /* 3.2mV / digit, 50mV -> 1A */
+ /* Store value */
+ isnail_solar_values[idx] = isnail_val_solar;
+ isnail_home_values[idx++] = isnail_val_home;
+ if (idx == NB_VAL) {
+ idx = 0;
+ }
+ /* Compute average value when we sampled enough values */
+ /* FIXME : Improve by substracting oldest value before storing new one in table and adding new one */
+ if ((idx == 0) || (idx == (NB_VAL / 2))) {
+ int i = 0;
+ for (i = 0; i < NB_VAL; i++) {
+ moyenne_solar += isnail_solar_values[i];
+ moyenne_home += isnail_home_values[i];
+ }
+ moyenne_solar = moyenne_solar / NB_VAL;
+ moyenne_home = moyenne_home / NB_VAL;
+ } else {
+ /* Sleep for a litle more than a period (20ms at 50Hz) */
+ msleep(23);
+ continue;
+ }
+
+ /* Feed the dog */
+ if ((moyenne_solar != 0) && (moyenne_home != 0)) {
+ watchdog_feed();
+ }
+
+ /* Get internal temperature */
+ if (1) {
+ int ret = 0;
+ msleep(40);
+ ret = tmp101_sensor_read(&tmp101_sensor, NULL, &tmp101_deci_degrees);
+ if (ret != 0) {
+ uprintf(UART0, "TMP101 read error : %d\n", ret);
+ }
+ }
+ /* If internal temperature is above 30°C, then turn on fan. Turn of when back to under 28.5°C */
+ if (tmp101_deci_degrees > 300) {
+ fan_speed = 100;
+ timer_set_match(LPC_TIMER_32B0, CHAN0, FAN_ON);
+ } else if (tmp101_deci_degrees < 285) {
+ fan_speed = 0;
+ timer_set_match(LPC_TIMER_32B0, CHAN0, 0);
+ }
+
+ /* Get thermocouple value */
+ if (1) {
+ int ret = 0;
+ ret = max31855_sensor_read(&thermo, NULL, &water_centi_degrees);
+ if (ret != 0) {
+ uprintf(UART0, "Water Temp read error : %d\n", ret);
+ }
+ }
+
+ /* Need to enter Forced heating mode ? */
+ if (water_centi_degrees < (FORCE_HEATER_TEMP * 100)) {
+ if (forced_heater_mode == 0) {
+ uprintf(UART0, "Entering forced mode\n");
+ forced_heater_mode = 1;
+ }
+ status_led(red_on);
+ mode = forced;
+ } else if ((water_centi_degrees > (TARGET_FORCED_HEATER_TEMP * 100)) && (forced_heater_mode == 1)) {
+ status_led(red_off);
+ forced_heater_mode = 0;
+ command_val = 0;
+ uprintf(UART0, "Forced mode exit\n");
+ mode = temp_OK;
+ }
+
+ /* Do not force if there is a lot of sun, it may be enough to heat again soon */
+ if (moyenne_solar > NO_FORCED_HEATING_ON_SUNNY_DAYS) {
+ mode = delayed_heat_prod;
+ forced_heater_mode = 0;
+ /* Note : Do not set forced_heater_mode to 0 in order to keep decrementing the delay for force
+ * heating in case the house power usage does not fall below the production value. */
+ }
+
+ /* Do not force heating if this is an EJP day */
+ ejp_in = gpio_read(ejp_in_pin);
+ if (ejp_in == DAY_IS_EJP) {
+ forced_heater_mode = 0;
+ mode = ejp;
+ }
+
+ if (never_force == 1) {
+ forced_heater_mode = 0;
+ }
+
+ /* Did the user request a forced heating ? */
+ if (manual_activation_request > 1) {
+ forced_heater_mode = 1;
+ mode = manual;
+ if (manual_activation_request == MANUAL_ACTIVATION_DURATION) {
+ uprintf(UART0, "Entering manual forced mode for %d ticks\n", manual_activation_request);
+ /* Add a systick callback to handle time counting */
+ add_systick_callback(handle_dec_request, DEC_PERIOD);
+ }
+ if (manual_activation_request < 10) {
+ uprintf(UART0, "Leaving manual forced mode\n");
+ manual_activation_request = 0;
+ remove_systick_callback(handle_dec_request);
+ }
+ }
+
+
+ /* Which is the current mode ? */
+ if (forced_heater_mode == 1) {
+ /* Forced heating mode */
+ if ((forced_heater_delay == 0) && (forced_heater_time > 0)) {
+ command_val = FORCED_MODE_VALUE;
+ }
+ if (forced_heater_time == 0) {
+ forced_heater_delay = FORCED_HEATER_DELAY;
+ forced_heater_time = FORCED_HEATER_DURATION;
+ }
+ } else if (moyenne_solar < (moyenne_home - 75)) {
+ /* Low production mode */
+ if (command_val > 15) {
+ command_val -= ((3 + n_dec) % 15);
+ /* Asservissement */
+ n_dec++;
+ if (n_dec >= 3) {
+ n_inc = 0;
+ }
+ } else {
+ command_val = 0;
+ mode = idle_heat;
+ }
+ status_led(green_off);
+ } else if (moyenne_solar > (moyenne_home + 75)) {
+ /* High production mode */
+ if (command_val < 95) {
+ command_val += (2 + n_inc);
+ /* Asservissement */
+ n_inc++;
+ if (n_inc >= 5) {
+ n_inc = 5;
+ n_dec = 0;
+ }
+ } else {
+ command_val = 100;
+ mode = full_heat;
+ }
+ status_led(green_on);
+ }
+
+ /* Set Control Output duty cycle */
+ //set_ctrl_duty_cycle(command_val);
+ /* Debug Nath TMP */
+ set_ctrl_duty_cycle( (user_potar - 5) / 10 );
+
+ /* Display */
+ if (1) {
+ int abs_centi = water_centi_degrees;
+ int abs_deci = tmp101_deci_degrees;
+
+ if (water_centi_degrees < 0) {
+ abs_centi = -water_centi_degrees;
+ }
+ if (tmp101_deci_degrees < 0) {
+ abs_deci = -tmp101_deci_degrees;
+ }
+
+ if (1) {
+ uprintf(UART0, "%c:%d - Is: %d,%04d - Ih: %d,%04d\n", mode, loop++,
+ (moyenne_solar / 1000), (moyenne_solar % 1000),
+ (moyenne_home / 1000), (moyenne_home % 1000));
+ uprintf(UART0, "Water Temp : % 4d.%02d\n", (water_centi_degrees / 100), (abs_centi % 100));
+ uprintf(UART0, "Internal Temp : % 4d.%02d\n", (tmp101_deci_degrees / 10), (abs_deci % 10));
+ uprintf(UART0, "ADC: Sol: %dmA, Home: %dmA, Load: %d, User: %d\n",
+ isnail_val_solar, isnail_val_home, acs_val_load, user_potar);
+ if (button_pressed != 0) {
+ uprintf(UART0, "Button : %d\n", button_pressed);
+ button_pressed = 0;
+ }
+ uprintf(UART0, "ZC_cnt: %d, user: %d, ThW: %d\n", zc_count, user_potar / 10, thermal_warn_flag);
+ uprintf(UART0, "CMD: %d/%d, Fan: %d, ndec:%d, ninc:%d\n\n", command_val, act_cmd, fan_speed, n_dec, n_inc);
+ }
+
+ if (1) {
+ ws2812_set_pixel(0, (isnail_val_home / 2000), (isnail_val_solar / 2000), fan_speed);
+ ws2812_set_pixel(1, 0, 0, (user_potar >> 2));
+ ws2812_send_frame(0);
+ }
+
+ if (1) {
+ char line[DISPLAY_LINE_LENGTH];
+ /* Erase screen (internal copy) */
+ ssd130x_display_set(&display, 0x00);
+ /* Update time and time display on internal memory */
+ rtc_pcf85363_time_read(&rtc_conf, &now);
+ snprintf(line, DISPLAY_LINE_LENGTH, "%02xh%02x:%02x", now.hour, now.min, now.sec);
+ display_line(0, 0, line);
+ /* Display info */
+ snprintf(line, DISPLAY_LINE_LENGTH, "Water:% 2d.%03d %cC", (water_centi_degrees / 100), (abs_centi % 100), 0x1F);
+ display_line(2, 0, line);
+ snprintf(line, DISPLAY_LINE_LENGTH, "Prod :% 2d,%03dA", (isnail_val_solar / 1000), ((isnail_val_solar % 1000) / 10));
+ display_line(3, 0, line);
+ snprintf(line, DISPLAY_LINE_LENGTH, "Conso:% 2d,%03dA", (isnail_val_home / 1000), ((isnail_val_home % 1000) / 10));
+ display_line(4, 0, line);
+ snprintf(line, DISPLAY_LINE_LENGTH, "Command: %d%%", act_cmd);
+ display_line(5, 0, line);
+ snprintf(line, DISPLAY_LINE_LENGTH, "Mode: %c", mode);
+ display_line(6, 0, line);
+ /* Update Oled display */
+ ret = ssd130x_display_full_screen(&display);
+ }
+ }
+ }
+ return 0;
+}
+
+
+
--- /dev/null
+# 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} $@
+
--- /dev/null
+/****************************************************************************
+ * apps/scialys/beta/main.c
+ *
+ * Scialys system for solar-panel power generation tracking and fair use.
+ *
+ * Copyright 2016 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 "lib/stdio.h"
+#include "lib/errno.h"
+#include "drivers/serial.h"
+#include "drivers/gpio.h"
+#include "drivers/adc.h"
+#include "drivers/ssp.h"
+#include "drivers/i2c.h"
+#include "drivers/timers.h"
+
+#include "extdrv/status_led.h"
+#include "extdrv/ws2812.h"
+#include "extdrv/max31855_thermocouple.h"
+#include "extdrv/tmp101_temp_sensor.h"
+#include "extdrv/rtc_pcf85363a.h"
+#include "extdrv/ssd130x_oled_driver.h"
+#include "lib/font.h"
+#include "lib/time.h"
+
+#include "extdrv/sdmmc.h"
+
+#define MODULE_VERSION 0x03
+#define MODULE_NAME "Scialys uC"
+
+
+#define SELECTED_FREQ FREQ_SEL_48MHz
+
+
+/***************************************************************************** */
+/* System configuration
+ * Most of the defines in here should go to configuration setting in user flash
+ */
+
+/* Period of the decrementer handler from the systick interrupt */
+#define DEC_PERIOD 100
+
+/* If temperature falls bellow FORCE_HEATER_TEMP value, we enter forced heater mode, until
+ * TARGET_FORCED_HEATER_TEMP is reached.
+ * When in forced heater mode, the heater is controlled to heat at FORCED_MODE_VALUE which
+ * is between 0 and 100.
+ */
+#define FORCE_HEATER_TEMP 28
+#define TARGET_FORCED_HEATER_TEMP 45
+#define FORCED_MODE_VALUE 75 /* A fraction of 100 */
+/* mA prod value above which the system will not enter forced mode, waiting for home
+ * to stop using power to start automatic heating */
+#define NO_FORCED_HEATING_ON_SUNNY_DAYS 1000
+
+uint32_t forced_heater_mode = 0;
+uint32_t forced_heater_delay = 0;
+uint32_t forced_heater_time = 0;
+
+#define FORCED_HEATER_DELAY (2 * 3600 * 1000 / DEC_PERIOD) /* Delay before automatic forced heating */
+#define FORCED_HEATER_DURATION (3 * 3600 * 1000 / DEC_PERIOD) /* Duration of automatic forced heating */
+
+#define MANUAL_ACTIVATION_DURATION (3 * 3600 * 1000 / DEC_PERIOD) /* Three hours */
+
+uint32_t never_force = 0;
+
+
+#define DAY_IS_EJP 0 /* Input is pulled low when EJP is ON */
+int ejp_in = 0;
+
+
+
+/***************************************************************************** */
+/* Pins configuration */
+/* pins blocks are passed to set_pins() for pins configuration.
+ * Unused pin blocks can be removed safely with the corresponding set_pins() call
+ * All pins blocks may be safelly merged in a single block for single set_pins() call..
+ */
+const struct pio_config common_pins[] = {
+ /* UART 0 : Config / Debug / USB */
+ { LPC_UART0_RX_PIO_0_1, LPC_IO_DIGITAL },
+ { LPC_UART0_TX_PIO_0_2, LPC_IO_DIGITAL },
+ /* UART 1 : UEXT */
+ { LPC_UART1_RX_PIO_0_8, LPC_IO_DIGITAL },
+ { LPC_UART1_TX_PIO_0_9, LPC_IO_DIGITAL },
+ /* I2C : RTC, Display, UEXT */
+ { 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) },
+ /* SPI (Thermocouple + uSD card + UEXT) */
+ { LPC_SSP0_SCLK_PIO_0_14, LPC_IO_DIGITAL },
+ { LPC_SSP0_MOSI_PIO_0_17, LPC_IO_DIGITAL },
+ { LPC_SSP0_MISO_PIO_0_16, LPC_IO_DIGITAL },
+ /* GPIO */
+ { LPC_GPIO_0_0, LPC_IO_DIGITAL }, /* Clkout / interrupt from RTC */
+ { LPC_GPIO_0_3, LPC_IO_DIGITAL }, /* EJP / External switch input */
+ { LPC_GPIO_0_4, LPC_IO_DIGITAL }, /* Zero crossing detection input */
+ { LPC_GPIO_0_6, LPC_IO_DIGITAL }, /* Mosfet control */
+ { LPC_GPIO_0_7, LPC_IO_DIGITAL }, /* Fan control */
+ { LPC_GPIO_0_12, LPC_IO_DIGITAL }, /* ISP / User button OK */
+ { LPC_GPIO_0_15, LPC_IO_DIGITAL }, /* Thermocouple chip select */
+ { LPC_GPIO_0_21, LPC_IO_DIGITAL }, /* WS2812B RGB Leds control */
+ { LPC_GPIO_0_22, LPC_IO_DIGITAL }, /* User button B4 */
+ { LPC_GPIO_0_23, LPC_IO_DIGITAL }, /* User button B3 */
+ { LPC_GPIO_0_24, LPC_IO_DIGITAL }, /* User button B2 */
+ { LPC_GPIO_0_25, LPC_IO_DIGITAL }, /* User button B1 */
+ { LPC_GPIO_0_28, LPC_IO_DIGITAL }, /* Charge State */
+ { LPC_GPIO_1_1, LPC_IO_DIGITAL }, /* Uext Chip select / Module eeprom select */
+ { LPC_GPIO_1_6, LPC_IO_DIGITAL }, /* uSD Card SPI Chip Select */
+ ARRAY_LAST_PIO,
+};
+
+const struct pio_config adc_pins[] = {
+ { LPC_ADC_AD0_PIO_0_30, LPC_IO_ANALOG }, /* ADC0 */
+ { LPC_ADC_AD1_PIO_0_31, LPC_IO_ANALOG }, /* ADC1 */
+ { LPC_ADC_AD2_PIO_1_0, LPC_IO_ANALOG }, /* ADC2 */
+ { LPC_ADC_AD7_PIO_1_5, LPC_IO_ANALOG }, /* ADC3 */
+ ARRAY_LAST_PIO,
+};
+
+const struct pio status_led_green = LPC_GPIO_0_26;
+const struct pio status_led_red = LPC_GPIO_0_27;
+
+/* Inputs */
+/* Buttons */
+const struct pio button_ok = LPC_GPIO_0_12;
+const struct pio button_b1 = LPC_GPIO_0_25;
+const struct pio button_b2 = LPC_GPIO_0_24;
+const struct pio button_b1 = LPC_GPIO_0_23;
+const struct pio button_b2 = LPC_GPIO_0_22;
+/* External signals */
+const struct pio rtc_in_pin = LPC_GPIO_0_0;
+const struct pio ejp_in_pin = LPC_GPIO_0_3;
+const struct pio zero_cross_in_pin = LPC_GPIO_0_4;
+const struct pio charge_status_in_pin = LPC_GPIO_0_28;
+
+/* Outputs */
+/* Led control data pin */
+const struct pio ws2812_data_out_pin = LPC_GPIO_0_21;
+/* AC output control (Mosfet) */
+const struct pio ac_ctrl = LPC_GPIO_0_6;
+const struct pio fan_ctrl = LPC_GPIO_0_7;
+
+
+/* Thermocouple reading */
+const struct max31855_sensor_config thermo = {
+ .ssp_bus_num = 0,
+ .chip_select = LPC_GPIO_0_15,
+};
+
+/* TMP101 onboard I2C temperature sensor */
+#define TMP101_ADDR 0x94 /* Pin Addr0 (pin5 of tmp101) connected to VCC */
+struct tmp101_sensor_config tmp101_sensor = {
+ .bus_num = I2C0,
+ .addr = TMP101_ADDR,
+ .resolution = TMP_RES_ELEVEN_BITS,
+};
+
+
+const struct lpc_tc_config ac_timer_conf = {
+ .mode = LPC_TIMER_MODE_TIMER | LPC_TIMER_MODE_MATCH,
+ .match_control = { 0, LPC_TIMER_INT_RESET_AND_STOP_ON_MATCH, 0, 0, },
+ .match = { 0, 10, 0, 0, },
+ .ext_match_config = { 0, LPC_TIMER_SET_ON_MATCH, 0, 0, },
+};
+
+
+/***************************************************************************** */
+/* SD/MMC Card */
+struct sdmmc_card micro_sd = {
+ .ssp_bus_num = SSP_BUS_0,
+ .card_type = MMC_CARDTYPE_UNKNOWN,
+ .block_size = 64,
+ .chip_select = LPC_GPIO_1_6,
+};
+
+uint8_t mmc_data[MMC_MAX_SECTOR_SIZE];
+
+
+/***************************************************************************** */
+/* RTC and time */
+#define RTC_ADDR 0xA2
+struct rtc_pcf85363a_config rtc_conf = {
+ .bus_num = I2C0,
+ .addr = RTC_ADDR,
+ .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 = 0x16,
+ .month = 0x10,
+ .day = 0x30,
+ .hour = 0x13,
+ .min = 0x00,
+};
+static struct rtc_time now;
+
+
+/***************************************************************************** */
+/* 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);
+ set_pins(adc_pins);
+ gpio_on();
+ status_led_config(&status_led_green, &status_led_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);
+}
+
+
+/***************************************************************************** */
+/* Some global flags or variables */
+static int act_cmd = 0;
+static int fan_on = 0;
+
+
+/***************************************************************************** */
+/* System configuration over USB */
+void config_rx(uint8_t c)
+{
+ /* FAN control */
+ if (c == 'f') {
+ gpio_set(fan_ctrl);
+ } else {
+ gpio_clear(fan_ctrl);
+ }
+}
+
+
+
+/***************************************************************************** */
+/* System communication over UART1 */
+void cmd_rx(uint8_t c)
+{
+}
+
+
+
+/***************************************************************************** */
+/* AC control */
+
+void set_ctrl_duty_cycle(uint8_t value)
+{
+ act_cmd = value;
+ if (act_cmd > 100) {
+ /* 100 is the maximum allowed value */
+ act_cmd = 100;
+ } else if (act_cmd <= 2) {
+ /* Below 3% there are triggering problems which lead to 50% instead of 1% or 2% */
+ act_cmd = 0;
+ }
+}
+
+void ac_switch_on(uint32_t flags)
+{
+ gpio_set(ac_ctrl);
+ gpio_set(button_b2);
+}
+
+static uint32_t clk_cycles_ac_zc = 0;
+static volatile uint32_t zc_count = 0; /* Wraps every 1.36 year ... */
+void zero_cross(uint32_t gpio)
+{
+ uint32_t delay = 0;
+
+ zc_count ++;
+ gpio_toggle(button_b1);
+
+ gpio_clear(ac_ctrl);
+ gpio_clear(button_b2);
+ if (act_cmd == 0) {
+ return;
+ }
+ /* Set timer to trigger ac out ON at given delay */
+ delay = clk_cycles_ac_zc * act_cmd;
+ timer_set_match(LPC_TIMER_32B1, CHAN1, delay);
+ timer_restart(LPC_TIMER_32B1);
+}
+
+
+
+/***************************************************************************** */
+/* System interface */
+enum buttons {
+ BUTTON_NONE = 0,
+ BUTTON_OK,
+ BUTTON_UP,
+ BUTTON_DOWN,
+};
+
+
+uint32_t manual_activation_request = 0;
+uint8_t button_pressed = 0;
+void manual_activation(uint32_t gpio) {
+ manual_activation_request = MANUAL_ACTIVATION_DURATION;
+ button_pressed = BUTTON_OK;
+}
+void manual_up(uint32_t gpio) {
+ manual_activation_request = MANUAL_ACTIVATION_DURATION;
+ button_pressed = BUTTON_UP;
+}
+void manual_down(uint32_t gpio) {
+ manual_activation_request = MANUAL_ACTIVATION_DURATION;
+ button_pressed = BUTTON_DOWN;
+}
+void handle_dec_request(uint32_t curent_tick) {
+ if (manual_activation_request > 0) {
+ manual_activation_request--;
+ }
+ if (forced_heater_mode == 1) {
+ if (forced_heater_delay > 0) {
+ forced_heater_delay--;
+ }
+ if (forced_heater_time > 0) {
+ forced_heater_time--;
+ }
+ }
+}
+
+
+/***************************************************************************** */
+void temp_config(int uart_num)
+{
+ int ret = 0;
+ ret = tmp101_sensor_config(&tmp101_sensor);
+ if (ret != 0) {
+ uprintf(uart_num, "Temp config error: %d\n", ret);
+ }
+}
+
+
+/***************************************************************************** */
+/* Oled Display */
+#define DISPLAY_ADDR 0x78
+struct oled_display display = {
+ .address = DISPLAY_ADDR,
+ .bus_num = I2C0,
+ .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,
+};
+
+#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_set_tile(&display, col, line, tile_data);
+}
+#define OLED_LINE_CHAR_LENGTH (SSD130x_NB_COL / 8)
+#define DISPLAY_LINE_LENGTH (OLED_LINE_CHAR_LENGTH + 1)
+int display_line(uint8_t line, uint8_t col, 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_set_tile(&display, col++, line, tile_data);
+ if (col >= (OLED_LINE_CHAR_LENGTH)) {
+ col = 0;
+ line++;
+ if (line >= SSD130x_NB_PAGES) {
+ return i;
+ }
+ }
+ }
+ return len;
+}
+
+
+
+
+/***************************************************************************** */
+void scialys_config(void)
+{
+ /* Immediatly turn off Mosfet / Triac */
+ config_gpio(&ac_ctrl, 0, GPIO_DIR_OUT, 0);
+ /* Start with FAN ON */
+ config_gpio(&fan_ctrl, 0, GPIO_DIR_OUT, 1);
+
+ /* Thermocouple configuration */
+ max31855_sensor_config(&thermo);
+ uprintf(UART0, "Thermocouple config done\n");
+
+ /* TMP101 sensor config */
+ temp_config(UART0);
+
+ /* Activate on Rising edge (button release) */
+ set_gpio_callback(manual_activation, &button_ok, EDGE_RISING);
+#if 0
+ set_gpio_callback(manual_up, &button_b1, EDGE_RISING);
+ set_gpio_callback(manual_down, &button_b2, EDGE_RISING);
+#else
+ config_gpio(&button_b1, 0, GPIO_DIR_OUT, 0);
+ config_gpio(&button_b2, 0, GPIO_DIR_OUT, 0);
+#endif
+
+ /* Zero cross */
+ set_gpio_callback(zero_cross, &zero_cross_in_pin, EDGE_FALLING);
+
+ /* Start ADC sampling */
+ adc_start_burst_conversion(ADC_MCH(0) | ADC_MCH(1) | ADC_MCH(2) | ADC_MCH(7), LPC_ADC_SEQ(0));
+
+ /* Configure Input GPIO */
+ config_gpio(&ejp_in_pin, 0, GPIO_DIR_IN, 0);
+ config_gpio(&rtc_in_pin, 0, GPIO_DIR_IN, 0);
+ config_gpio(&charge_status_in_pin, 0, GPIO_DIR_IN, 1);
+
+ /* WS2812B Leds on display board */
+ ws2812_config(&ws2812_data_out_pin);
+
+ /* AC Switch Config */
+ timer_counter_config(LPC_TIMER_32B1, &ac_timer_conf);
+ /* We want 100 Hz (50 Hz but two zero crossings) with 1% granularity */
+ clk_cycles_ac_zc = get_main_clock() / (100 * 100);
+
+ status_led(green_only);
+
+ /* Configure and start display */
+ ret = ssd130x_display_on(&display);
+ /* Erase screen */
+ ssd130x_display_set(&display, 0x00);
+ ret = ssd130x_display_full_screen(&display);
+
+ /* RTC init */
+ ret = rtc_pcf85363a_config(&rtc_conf);
+ ret = rtc_pcf85363a_is_up(&rtc_conf, &oldest);
+ if (ret == 1) {
+ char buff[30];
+ rtc_pcf85363_time_read(&rtc_conf, &now);
+ rtc_pcf85363_time_to_str(&now, buff, 30);
+ /* Debug */
+ uprintf(UART0, buff);
+ } else if (ret == -EFAULT) {
+ memcpy(&now, &oldest, sizeof(struct rtc_time));
+ rtc_pcf85363_time_write(&rtc_conf, &now);
+ }
+
+ /* microSD card init */
+ ret = sdmmc_init(µ_sd);
+ if (ret == 0) {
+ msleep(1);
+ ret = sdmmc_init_wait_card_ready(µ_sd);
+ if (ret == 0) {
+ ret = sdmmc_init_end(µ_sd);
+ }
+ }
+ uprintf(UART0, "uSD init: %d, type: %d, bs: %d\n", ret, micro_sd.card_type, micro_sd.block_size);
+ ret = sdmmc_read_block(µ_sd, 0, mmc_data);
+ uprintf(UART0, "uSD read: %s\n", mmc_data);
+
+ /* Add a systick callback to handle time counting */
+ //add_systick_callback(handle_dec_request, DEC_PERIOD);
+
+ msleep(50);
+ /* Read parameters from memory */
+ if (1) {
+ never_force = 0;
+ forced_heater_delay = 0;
+ forced_heater_time = FORCED_HEATER_DURATION;
+ }
+}
+
+/***************************************************************************** */
+#define NB_VAL 20
+
+enum modes {
+ heat = 'C',
+ ejp = 'E',
+ delayed_heat_prod = 'P',
+ forced = 'F',
+ temp_OK = 'T',
+ manual = 'M',
+ idle_heat = 'L',
+ full_heat = 'F',
+};
+
+/***************************************************************************** */
+int main(void)
+{
+ uint16_t isnail_solar_values[NB_VAL];
+ uint16_t isnail_home_values[NB_VAL];
+ uint8_t idx = 0;
+ uint32_t loop = 0;
+ char mode = heat; /* Debug info */
+ int ret = 0;
+
+ system_init();
+ status_led(red_only);
+ uart_on(UART0, 115200, config_rx);
+ uart_on(UART1, 115200, cmd_rx);
+ i2c_on(I2C0, I2C_CLK_100KHz, I2C_MASTER);
+ ssp_master_on(thermo.ssp_bus_num, LPC_SSP_FRAME_SPI, 8, 4*1000*1000);
+ adc_on(NULL);
+ timer_on(LPC_TIMER_32B1, 0, ac_switch_on);
+
+ scialys_config(void)
+
+ while (1) {
+ static uint8_t command_val = 0;
+ static uint8_t n_dec = 0; /* Add some PID like (derivative part) */
+ static uint8_t n_inc = 0; /* Add some PID like (derivative part) */
+ int moyenne_solar = 0;
+ int moyenne_home = 0;
+ uint16_t isnail_val_solar = 0;
+ uint16_t isnail_val_home = 0;
+ uint16_t acs_val_load = 0;
+ uint16_t user_potar = 0;
+ int water_centi_degrees = 0;
+ int tmp101_deci_degrees = 0;
+
+ mode = heat;
+ tmp101_sensor_start_conversion(&tmp101_sensor);
+ /* Always track power consumption and production */
+ adc_get_value(&isnail_val_solar, LPC_ADC(1));
+ adc_get_value(&isnail_val_home, LPC_ADC(0));
+ adc_get_value(&acs_val_load, LPC_ADC(2));
+ adc_get_value(&user_potar, LPC_ADC(7));
+ /* Convert to mA value */
+ isnail_val_solar = ((isnail_val_solar * 32) * 2); /* 3.2mV / digit, 50mV -> 1A */
+ isnail_val_home = ((isnail_val_home * 32) * 2); /* 3.2mV / digit, 50mV -> 1A */
+ /* Store value */
+ isnail_solar_values[idx] = isnail_val_solar;
+ isnail_home_values[idx++] = isnail_val_home;
+ if (idx == NB_VAL) {
+ idx = 0;
+ }
+ /* Compute average value when we sampled enough values */
+ /* FIXME : Improve by substracting oldest value before storing new one in table and adding new one */
+ if ((idx == 0) || (idx == (NB_VAL / 2))) {
+ int i = 0;
+ for (i = 0; i < NB_VAL; i++) {
+ moyenne_solar += isnail_solar_values[i];
+ moyenne_home += isnail_home_values[i];
+ }
+ moyenne_solar = moyenne_solar / NB_VAL;
+ moyenne_home = moyenne_home / NB_VAL;
+ } else {
+ /* Sleep for a litle more than a period (20ms at 50Hz) */
+ msleep(23);
+ continue;
+ }
+
+ /* Feed the dog */
+ if ((moyenne_solar != 0) && (moyenne_home != 0)) {
+ watchdog_feed();
+ }
+
+ /* Get internal temperature */
+ if (1) {
+ int ret = 0;
+ msleep(40);
+ ret = tmp101_sensor_read(&tmp101_sensor, NULL, &tmp101_deci_degrees);
+ if (ret != 0) {
+ uprintf(UART0, "TMP101 read error : %d\n", ret);
+ }
+ }
+ /* If internal temperature is above 30°C, then turn on fan. Turn of when back to under 28.5°C */
+ if (tmp101_deci_degrees > 300) {
+ fan_on = 1;
+ gpio_set(fan_ctrl);
+ } else if (tmp101_deci_degrees < 285) {
+ fan_on = 0;
+ gpio_clear(fan_ctrl);
+ }
+
+ /* Get thermocouple value */
+ if (1) {
+ int ret = 0;
+ ret = max31855_sensor_read(&thermo, NULL, &water_centi_degrees);
+ if (ret != 0) {
+ uprintf(UART0, "Water Temp read error : %d\n", ret);
+ }
+ }
+
+ /* Need to enter Forced heating mode ? */
+ if (water_centi_degrees < (FORCE_HEATER_TEMP * 100)) {
+ if (forced_heater_mode == 0) {
+ uprintf(UART0, "Entering forced mode\n");
+ forced_heater_mode = 1;
+ }
+ status_led(red_on);
+ mode = forced;
+ } else if ((water_centi_degrees > (TARGET_FORCED_HEATER_TEMP * 100)) && (forced_heater_mode == 1)) {
+ status_led(red_off);
+ forced_heater_mode = 0;
+ command_val = 0;
+ uprintf(UART0, "Forced mode exit\n");
+ mode = temp_OK;
+ }
+
+ /* Do not force if there is a lot of sun, it may be enough to heat again soon */
+ if (moyenne_solar > NO_FORCED_HEATING_ON_SUNNY_DAYS) {
+ mode = delayed_heat_prod;
+ forced_heater_mode = 0;
+ /* Note : Do not set forced_heater_mode to 0 in order to keep decrementing the delay for force
+ * heating in case the house power usage does not fall below the production value. */
+ }
+
+ /* Do not force heating if this is an EJP day */
+ ejp_in = gpio_read(ejp_in_pin);
+ if (ejp_in == DAY_IS_EJP) {
+ forced_heater_mode = 0;
+ mode = ejp;
+ }
+
+ if (never_force == 1) {
+ forced_heater_mode = 0;
+ }
+
+ /* Did the user request a forced heating ? */
+ if (manual_activation_request > 1) {
+ forced_heater_mode = 1;
+ mode = manual;
+ if (manual_activation_request == MANUAL_ACTIVATION_DURATION) {
+ uprintf(UART0, "Entering manual forced mode for %d ticks\n", manual_activation_request);
+ /* Add a systick callback to handle time counting */
+ add_systick_callback(handle_dec_request, DEC_PERIOD);
+ }
+ if (manual_activation_request < 10) {
+ uprintf(UART0, "Leaving manual forced mode\n");
+ manual_activation_request = 0;
+ remove_systick_callback(handle_dec_request);
+ }
+ }
+
+
+ /* Which is the current mode ? */
+ if (forced_heater_mode == 1) {
+ /* Forced heating mode */
+ if ((forced_heater_delay == 0) && (forced_heater_time > 0)) {
+ command_val = FORCED_MODE_VALUE;
+ }
+ if (forced_heater_time == 0) {
+ forced_heater_delay = FORCED_HEATER_DELAY;
+ forced_heater_time = FORCED_HEATER_DURATION;
+ }
+ } else if (moyenne_solar < (moyenne_home - 75)) {
+ /* Low production mode */
+ if (command_val > 15) {
+ command_val -= ((3 + n_dec) % 15);
+ /* Asservissement */
+ n_dec++;
+ if (n_dec >= 3) {
+ n_inc = 0;
+ }
+ } else {
+ command_val = 0;
+ mode = idle_heat;
+ }
+ status_led(green_off);
+ } else if (moyenne_solar > (moyenne_home + 75)) {
+ /* High production mode */
+ if (command_val < 95) {
+ command_val += (2 + n_inc);
+ /* Asservissement */
+ n_inc++;
+ if (n_inc >= 5) {
+ n_inc = 5;
+ n_dec = 0;
+ }
+ } else {
+ command_val = 100;
+ mode = full_heat;
+ }
+ status_led(green_on);
+ }
+
+ /* Set Control Output duty cycle */
+ //set_ctrl_duty_cycle(command_val);
+ /* Debug Nath TMP */
+ set_ctrl_duty_cycle( (user_potar - 5) / 10 );
+
+ /* Display */
+ if (1) {
+ int abs_centi = water_centi_degrees;
+ int abs_deci = tmp101_deci_degrees;
+
+ if (water_centi_degrees < 0) {
+ abs_centi = -water_centi_degrees;
+ }
+ if (tmp101_deci_degrees < 0) {
+ abs_deci = -tmp101_deci_degrees;
+ }
+
+ if (1) {
+ uprintf(UART0, "%c:%d - Is: %d,%04d - Ih: %d,%04d\n", mode, loop++,
+ (moyenne_solar / 1000), (moyenne_solar % 1000),
+ (moyenne_home / 1000), (moyenne_home % 1000));
+ uprintf(UART0, "Water Temp : % 4d.%02d\n", (water_centi_degrees / 100), (abs_centi % 100));
+ uprintf(UART0, "Internal Temp : % 4d.%02d\n", (tmp101_deci_degrees / 10), (abs_deci % 10));
+ uprintf(UART0, "ADC: Sol: %dmA, Home: %dmA, Load: %d, User: %d\n",
+ isnail_val_solar, isnail_val_home, acs_val_load, user_potar);
+ if (button_pressed != 0) {
+ uprintf(UART0, "Button : %d\n", button_pressed);
+ button_pressed = 0;
+ }
+ uprintf(UART0, "ZC_cnt: %d, user: %d\n", zc_count, user_potar / 10);
+ uprintf(UART0, "CMD: %d/%d, Fan: %d, ndec:%d, ninc:%d\n\n", command_val, act_cmd, fan_on, n_dec, n_inc);
+ }
+
+ if (1) {
+ ws2812_set_pixel(0, (isnail_val_home / 2000), (isnail_val_solar / 2000), (fan_on * 20));
+ ws2812_set_pixel(1, 0, 0, (user_potar >> 2));
+ ws2812_send_frame(0);
+ }
+
+ if (1) {
+ char line[DISPLAY_LINE_LENGTH];
+ /* Erase screen (internal copy) */
+ ssd130x_display_set(&display, 0x00);
+ /* Update time and time display on internal memory */
+ rtc_pcf85363_time_read(&rtc_conf, &now);
+ snprintf(line, DISPLAY_LINE_LENGTH, "%02xh%02x:%02x", now.hour, now.min, now.sec);
+ display_line(0, 0, line);
+ /* Display info */
+ snprintf(line, DISPLAY_LINE_LENGTH, "Water:% 2d.%03d %cC", (water_centi_degrees / 100), (abs_centi % 100), 0x1F);
+ display_line(2, 0, line);
+ snprintf(line, DISPLAY_LINE_LENGTH, "Prod :% 2d,%03dA", (isnail_val_solar / 1000), ((isnail_val_solar % 1000) / 10));
+ display_line(3, 0, line);
+ snprintf(line, DISPLAY_LINE_LENGTH, "Conso:% 2d,%03dA", (isnail_val_home / 1000), ((isnail_val_home % 1000) / 10));
+ display_line(4, 0, line);
+ snprintf(line, DISPLAY_LINE_LENGTH, "Command: %d%%", act_cmd);
+ display_line(5, 0, line);
+ snprintf(line, DISPLAY_LINE_LENGTH, "Mode: %c", mode);
+ display_line(6, 0, line);
+ /* Update Oled display */
+ ret = ssd130x_display_full_screen(&display);
+ }
+ }
+ }
+ return 0;
+}
+
+
+
--- /dev/null
+# 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} $@
+
--- /dev/null
+/****************************************************************************
+ * apps/scialys/v05/config.c
+ *
+ * Copyright 2018 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 "drivers/adc.h"
+#include "drivers/ssp.h"
+#include "drivers/timers.h"
+
+
+#include "extdrv/status_led.h"
+#include "extdrv/max31855_thermocouple.h"
+
+#include "lib/stdio.h"
+
+#include "config.h"
+#include "interface.h"
+#include "time.h"
+#include "uSD.h"
+
+#define SELECTED_FREQ FREQ_SEL_48MHz
+
+
+uint8_t config_got_interface = 1;
+
+/***************************************************************************** */
+/* Pins configuration */
+/* pins blocks are passed to set_pins() for pins configuration.
+ * Unused pin blocks can be removed safely with the corresponding set_pins() call
+ * All pins blocks may be safelly merged in a single block for single set_pins() call..
+ */
+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) },
+ /* SPI */
+ { LPC_SSP0_SCLK_PIO_0_14, LPC_IO_DIGITAL },
+ { LPC_SSP0_MOSI_PIO_0_17, LPC_IO_DIGITAL },
+ { LPC_SSP0_MISO_PIO_0_16, LPC_IO_DIGITAL },
+ /* ADC */
+ { LPC_ADC_AD0_PIO_0_30, LPC_IO_ANALOG }, /* Home */
+ { LPC_ADC_AD1_PIO_0_31, LPC_IO_ANALOG }, /* Production */
+ { LPC_ADC_AD2_PIO_1_0, LPC_IO_ANALOG }, /* Load */
+ { LPC_ADC_AD7_PIO_1_5, LPC_IO_ANALOG }, /* Display potentiometer */
+ /* GPIO */
+ { LPC_GPIO_0_0, LPC_IO_DIGITAL }, /* Clkout / interrupt from RTC */
+ { LPC_GPIO_0_3, LPC_IO_DIGITAL }, /* External switch input */
+ { LPC_GPIO_0_4, LPC_IO_DIGITAL }, /* Zero crossing detection input */
+ { LPC_GPIO_0_6, LPC_IO_DIGITAL }, /* Mosfet control */
+ { LPC_GPIO_0_7, LPC_IO_DIGITAL }, /* Fan control */
+ { LPC_GPIO_0_12, LPC_IO_DIGITAL }, /* ISP / User button OK */
+ { LPC_GPIO_0_15, LPC_IO_DIGITAL }, /* Thermocouple chip select */
+ { LPC_GPIO_0_21, LPC_IO_DIGITAL }, /* WS2812B RGB Leds control */
+ { LPC_GPIO_0_28, LPC_IO_DIGITAL }, /* Charge State */
+ { LPC_GPIO_1_1, LPC_IO_DIGITAL }, /* Uext Chip select / Module eeprom select */
+ { LPC_GPIO_1_6, LPC_IO_DIGITAL }, /* uSD Card SPI Chip Select */
+ 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;
+
+/* Inputs */
+/* External signals */
+const struct pio zero_cross_in_pin = LPC_GPIO_0_4;
+const struct pio rtc_in_pin = LPC_GPIO_0_0;
+const struct pio ext_disable_in_pin = LPC_GPIO_0_3;
+const struct pio charge_status_in_pin = LPC_GPIO_0_28;
+
+/* Outputs */
+/* AC output control (Mosfet) */
+const struct pio ac_ctrl = LPC_GPIO_0_6;
+const struct pio fan_ctrl = LPC_GPIO_0_7;
+/* Chip selects */
+const struct pio uSD_cs = LPC_GPIO_1_6; /* uSD card */
+const struct pio th_cs = LPC_GPIO_0_15; /* Thermocouple */
+const struct pio uext_cs = LPC_GPIO_1_1; /* UEXT module (optionnal) */
+
+
+
+/***************************************************************************** */
+/* 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);
+}
+
+
+/***************************************************************************** */
+/* Internal modules */
+const struct lpc_tc_config ac_timer_conf = {
+ .mode = LPC_TIMER_MODE_TIMER | LPC_TIMER_MODE_MATCH,
+ .match_control = { 0, LPC_TIMER_INT_RESET_AND_STOP_ON_MATCH, 0, 0, },
+ .match = { 0, 10, 0, 0, },
+ .ext_match_config = { 0, LPC_TIMER_SET_ON_MATCH, 0, 0, },
+};
+
+extern void config_rx(uint8_t c);
+extern void comm_rx(uint8_t c);
+extern void ac_switch_on(uint32_t flags);
+
+void modules_config(void)
+{
+ uart_on(UART0, 115200, config_rx);
+ uart_on(UART1, 115200, comm_rx);
+ i2c_on(I2C0, I2C_CLK_100KHz, I2C_MASTER);
+ ssp_master_on(SSP_BUS_0, LPC_SSP_FRAME_SPI, 8, 4*1000*1000);
+ adc_on(NULL);
+ adc_start_burst_conversion(ADC_MCH(0) | ADC_MCH(1) | ADC_MCH(2) | ADC_MCH(7), LPC_ADC_SEQ(0));
+ timer_on(LPC_TIMER_32B1, 0, ac_switch_on);
+ timer_counter_config(LPC_TIMER_32B1, &ac_timer_conf);
+}
+
+
+/***************************************************************************** */
+extern void zero_cross(uint32_t gpio);
+void board_io_config(void)
+{
+ /* Immediatly turn off Mosfet / Triac */
+ config_gpio(&ac_ctrl, 0, GPIO_DIR_OUT, 0);
+ /* Start with FAN ON */
+ config_gpio(&fan_ctrl, 0, GPIO_DIR_OUT, 1);
+
+ /* Configure Input GPIO */
+ config_gpio(&ext_disable_in_pin, 0, GPIO_DIR_IN, 0);
+ config_gpio(&rtc_in_pin, 0, GPIO_DIR_IN, 0);
+ config_gpio(&charge_status_in_pin, 0, GPIO_DIR_IN, 1);
+
+ /* Zero-crossing detection */
+ set_gpio_callback(zero_cross, &zero_cross_in_pin, EDGE_FALLING);
+}
+
+
+
+/***************************************************************************** */
+/* Configure external components */
+
+/* Thermocouple reading */
+const struct max31855_sensor_config thermocouple = {
+ .ssp_bus_num = 0,
+ .chip_select = LPC_GPIO_0_15,
+};
+
+/* RTC and time */
+#define RTC_ADDR 0xA2
+
+
+int external_config(uint32_t uart)
+{
+ int ret = 0;
+
+ /* Configure GPIO for specific board usage */
+ board_io_config();
+
+ /* uSD card */
+ uSD_config((struct pio *)&uSD_cs, SSP_BUS_0);
+ uSD_detect(uart);
+
+ /* RTC */
+ time_config(I2C0, RTC_ADDR);
+ time_init_check(uart);
+
+ /* Thermocouple configuration */
+ max31855_sensor_config(&thermocouple);
+
+ /* Configure interface board */
+ ret = interface_config(uart);
+ if (ret != 0) {
+ return -1;
+ }
+ return 0;
+}
--- /dev/null
+/****************************************************************************
+ * apps/blyes/inter_lights/config.h
+ *
+ * Copyright 2018 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 "drivers/adc.h"
+#include "drivers/ssp.h"
+#include "drivers/timers.h"
+
+#include "extdrv/status_led.h"
+#include "extdrv/max31855_thermocouple.h"
+#include "extdrv/ws2812.h"
+
+#include "lib/stdio.h"
+
+
+#define SELECTED_FREQ FREQ_SEL_48MHz
+
+/***************************************************************************** */
+/* Pins configuration */
+extern const struct pio status_led_green;
+extern const struct pio status_led_red;
+
+/* Inputs */
+/* External signals */
+extern const struct pio zero_cross_in_pin;
+extern const struct pio rtc_in_pin;
+extern const struct pio ext_disable_in_pin;
+extern const struct pio charge_status_in_pin;
+
+/* Outputs */
+/* AC output control (Mosfet) */
+extern const struct pio ac_ctrl;
+extern const struct pio fan_ctrl;
+/* Chip selects */
+extern const struct pio uSD_cs; /* uSD card */
+extern const struct pio th_cs; /* Thermocouple */
+extern const struct pio uext_cs; /* UEXT module (optionnal) */
+
+
+
+/***************************************************************************** */
+extern const struct max31855_sensor_config thermocouple;
+
+
+/***************************************************************************** */
+/* 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 modules and main functions */
+void modules_config(void);
+
+/* Configure GPIO for specific board usage */
+void board_io_config(void);
+
+/* Configure external components */
+int external_config(uint32_t uart);
+
+
+#endif /* CONFIG_H */
+
--- /dev/null
+/****************************************************************************
+ * apps/scialys/v05/interface.c
+ *
+ * Copyright 2018 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 "drivers/adc.h"
+
+#include "extdrv/status_led.h"
+#include "extdrv/ws2812.h"
+#include "extdrv/tmp101_temp_sensor.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 "interface.h"
+#include "time.h"
+
+/***************************************************************************** */
+/* Buttons inputs on front panel */
+const struct pio button_up = LPC_GPIO_0_25;
+#define GPIO_BUTTON_UP 25
+const struct pio button_right = LPC_GPIO_0_24;
+#define GPIO_BUTTON_RIGHT 24
+const struct pio button_left = LPC_GPIO_0_23;
+#define GPIO_BUTTON_LEFT 23
+const struct pio button_down = LPC_GPIO_0_22;
+#define GPIO_BUTTON_DOWN 22
+/* 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_21;
+
+
+static uint8_t interface_board_present = 0;
+
+/***************************************************************************** */
+/* TMP101 onboard I2C temperature sensor */
+#define TMP101_ADDR 0x94 /* Pin Addr0 (pin5 of tmp101) connected to VCC */
+struct tmp101_sensor_config tmp101_sensor = {
+ .bus_num = I2C0,
+ .addr = TMP101_ADDR,
+ .resolution = TMP_RES_ELEVEN_BITS,
+};
+
+/***************************************************************************** */
+/* 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_EXT_VCC,
+ .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, char* text)
+{
+ int len = strlen((char*)text);
+ int i = 0;
+
+ if (interface_board_present == 0) {
+ return -1;
+ }
+
+ 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;
+
+ if (interface_board_present == 0) {
+ return -1;
+ }
+
+ 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);
+}
+
+
+int temp_read(uint32_t uart, int* deci_degrees)
+{
+ int ret = 0;
+
+ if (interface_board_present == 0) {
+ return -1;
+ }
+
+ ret = tmp101_sensor_read(&tmp101_sensor, NULL, deci_degrees);
+ if (ret != 0) {
+ uprintf(uart, "TMP101 read error : %d\n", ret);
+ return -1;
+ }
+ return 0;
+}
+
+
+/***************************************************************************** */
+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;
+ }
+}
+
+int interface_config(uint32_t uart)
+{
+ int ret = 0;
+
+ /* TMP101 sensor config */
+ ret = tmp101_sensor_config(&tmp101_sensor);
+ if (ret != 0) {
+ uprintf(uart, "Temp config error: %d\n", ret);
+ interface_board_present = 0;
+ return -1;
+ }
+ ret = tmp101_sensor_set_continuous_conversion(&tmp101_sensor);
+ if (ret != 0) {
+ uprintf(uart, "Temp config error: %d\n", ret);
+ interface_board_present = 0;
+ return -2;
+ }
+ interface_board_present = 1;
+
+ /* Buttons inputs on front panel */
+ /* Activate on Rising edge (button release) */
+ set_gpio_callback(button_callback, &button_up, EDGE_RISING);
+ set_gpio_callback(button_callback, &button_left, EDGE_RISING);
+ set_gpio_callback(button_callback, &button_right, EDGE_RISING);
+ set_gpio_callback(button_callback, &button_down, EDGE_RISING);
+ set_gpio_callback(button_callback, &button_ok, EDGE_RISING);
+
+ /* WS2812B Leds on display board */
+ ws2812_config(&ws2812_data_out_pin);
+
+ /* Configure and start display */
+ ssd130x_display_on(&display);
+ erase_screen_content();
+ ssd130x_display_full_screen(&display);
+
+ ws2812_set_pixel(0, 0x05, 0x15, 0x08);
+ ws2812_send_frame(0);
+
+ uprintf(uart, "Config OK\n");
+
+ return 0;
+}
+
+
+enum interface_modes {
+ MODE_RUN = 0,
+ MODE_CONFIG,
+ MODE_DISPLAY,
+};
+
+extern int water_centi_degrees;
+extern uint32_t moyenne_solar;
+extern uint32_t moyenne_home;
+extern volatile uint8_t command_val;
+
+static int current_mode = MODE_RUN;
+volatile int manual_activation_request = 0;
+void interface_update(char heat_mode)
+{
+ int abs_centi = water_centi_degrees;
+ uint16_t user_potar = 0;
+
+ if (water_centi_degrees < 0) {
+ abs_centi = -water_centi_degrees;
+ }
+
+ /* Get potentiometer value */
+ adc_get_value(&user_potar, LPC_ADC(7));
+
+ if (current_mode == MODE_RUN) {
+ if (button_pressed & BUTTON_OK) {
+ manual_activation_request = -1;
+ }
+ if (interface_board_present != 0) {
+ char line[DISPLAY_LINE_LENGTH];
+
+ /* Start with a blank screen */
+ erase_screen_content();
+
+ /* Update time and time display on internal memory */
+ rtc_pcf85363_time_read(&rtc_conf, &now);
+ snprintf(line, DISPLAY_LINE_LENGTH, "%02xh%02x:%02x", now.hour, now.min, now.sec);
+ display_line(0, 0, line);
+
+ /* Display info */
+ snprintf(line, DISPLAY_LINE_LENGTH, "Water:% 2d.%03d %cC", (water_centi_degrees / 100), (abs_centi % 100), 0x1F);
+ display_line(2, 0, line);
+ snprintf(line, DISPLAY_LINE_LENGTH, "Prod :% 2d,%03dA", (moyenne_solar / 1000), ((moyenne_solar % 1000) / 10));
+ display_line(3, 0, line);
+ snprintf(line, DISPLAY_LINE_LENGTH, "Conso:% 2d,%03dA", (moyenne_home / 1000), ((moyenne_home % 1000) / 10));
+ display_line(4, 0, line);
+ snprintf(line, DISPLAY_LINE_LENGTH, "Command: %d%%", command_val);
+ display_line(5, 0, line);
+ snprintf(line, DISPLAY_LINE_LENGTH, "Mode: %c", heat_mode);
+ display_line(6, 0, line);
+ /* Update Oled display */
+ ssd130x_display_full_screen(&display);
+ }
+
+ /* Update RGB leds */
+ ws2812_set_pixel(0, (moyenne_home / 2000), (moyenne_solar / 2000), 0);
+ ws2812_set_pixel(1, 0, 0, (user_potar >> 2));
+ ws2812_send_frame(0);
+ }
+
+ if (button_pressed != 0) {
+ uprintf(UART0, "Button : 0x%02x\n", button_pressed);
+ button_pressed = 0;
+ }
+
+}
--- /dev/null
+/****************************************************************************
+ * apps/blyes/relay_lights/interface.h
+ *
+ * Copyright 2018 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 DISPLAY_LINE_LENGTH (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;
+
+extern volatile int manual_activation_request;
+
+/* 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);
+
+
+/* Internal Temperature sensor */
+int temp_read(uint32_t uart, int* deci_degrees);
+
+/* Interface content update */
+void interface_update(char mode);
+
+#endif /* INTERFACE_H */
+
--- /dev/null
+/****************************************************************************
+ * apps/scialys/v05/main.c
+ *
+ * Scialys system for solar-panel power generation tracking and fair use.
+ *
+ * Copyright 2016 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 "uSD.h"
+
+
+#define MODULE_VERSION 0x05
+#define MODULE_NAME "Scialys uC"
+
+
+
+/***************************************************************************** */
+/* System configuration */
+
+/* Period (in ms) of the decrementer handler from the systick interrupt */
+#define DEC_PERIOD 100
+/* Period (in ms) of the handler for the command value update */
+#define CMD_UPD_PERIOD 500
+
+/* Max water temperature. Internal protection which cannot be overriden by configuration */
+#define MAX_WATER_TEMP 90
+/* Internal system max temperature : turn off heating when reached */
+#define MAX_INTERNAL_TEMP 80 /* FIXME */
+
+/* Most of the defines in here should go to configuration setting in user flash */
+/* If temperature falls bellow FORCE_HEATER_TEMP value, we enter forced heater mode, until
+ * TARGET_FORCED_HEATER_TEMP is reached.
+ * When in forced heater mode, the heater is controlled to heat at FORCED_MODE_VALUE which
+ * is between 0 and 100.
+ */
+#define FORCE_HEATER_TEMP 30
+#define TARGET_FORCED_HEATER_TEMP 50
+#define FORCED_MODE_VALUE 75 /* A fraction of 100 */
+/* mA prod value above which the system will not enter forced mode, waiting for home
+ * to stop using power to start automatic heating */
+#define SUNNY_DAYS_PROD_VALUE 3000
+
+/* Delay before automatic forced heating */
+#define FORCED_HEATER_DELAY (2 * 3600 * 1000 / DEC_PERIOD)
+/* Duration of automatic forced heating */
+#define FORCED_HEATER_DURATION (3 * 3600 * 1000 / DEC_PERIOD)
+
+/* Duration of manual forced heating */
+#define MANUAL_ACTIVATION_DURATION (3 * 3600 * 1000 / DEC_PERIOD) /* Three hours */
+
+
+uint32_t forced_heater_mode = 0;
+uint32_t forced_heater_delay = 0;
+uint32_t forced_heater_time = 0;
+
+uint8_t error_shutdown = 0;
+
+uint8_t never_force = 0;
+uint32_t sunny_days_prod_value_config = 0;
+
+#define EXTERNAL_DISABLE_FORCE 0 /* Input is pulled low when external disable is ON */
+int external_disable = 0;
+
+enum modes {
+ heat = 'C',
+ ext_disable = 'E',
+ delayed_heat_prod = 'P',
+ forced = 'F',
+ temp_OK = 'T',
+ manual = 'M',
+ idle_heat = 'L',
+ full_heat = 'F',
+};
+
+/* Water and internaltemperature */
+int water_centi_degrees = 0;
+int tmp101_deci_degrees = 0;
+
+/* RTC and time */
+struct rtc_time now;
+
+
+/***************************************************************************** */
+/* Rx interrupt handler for system configuration over USB */
+void config_rx(uint8_t c)
+{
+ /* FAN control */
+ if (c == 'f') {
+ gpio_set(fan_ctrl);
+ } else {
+ gpio_clear(fan_ctrl);
+ }
+}
+
+
+/* Communication with slave modules */
+void comm_rx(uint8_t c)
+{
+}
+
+
+
+/***************************************************************************** */
+/* System communication over UART1 */
+void cmd_rx(uint8_t c)
+{
+}
+
+
+/***************************************************************************** */
+/* Decrementer for heating timers */
+void handle_dec_request(uint32_t curent_tick) {
+ if (manual_activation_request > 0) {
+ manual_activation_request--;
+ }
+ if (forced_heater_mode == 1) {
+ if (forced_heater_delay > 0) {
+ forced_heater_delay--;
+ }
+ if (forced_heater_time > 0) {
+ forced_heater_time--;
+ }
+ }
+}
+
+
+/***************************************************************************** */
+/* Track power production and usage */
+
+/* Average value computed on last 10 ADC values */
+uint32_t moyenne_solar = 0;
+uint32_t moyenne_home = 0;
+/* Last ADC values (snapshot) */
+static uint16_t snapval_solar = 0;
+static uint16_t snapval_home = 0;
+
+#define NB_VAL 20
+static void track_isnail_values(void)
+{
+ static uint16_t isnail_solar_values[NB_VAL];
+ static uint16_t isnail_home_values[NB_VAL];
+ static uint8_t idx = 0;
+
+ /* Get new values */
+ adc_get_value(&snapval_solar, LPC_ADC(1));
+ adc_get_value(&snapval_home, LPC_ADC(0));
+
+ /* Convert to mA value.
+ * ADC range is 0 to 1024 - approx 3.2mV / ADC step
+ * Coil convertion is 1000mA -> 50mV : multily mV value by 20 to get mA value
+ * x * 3.2 * 20 == x * 32 * 2 == x << 6
+ * Increment is 64mA / ADC step */
+ snapval_solar = snapval_solar << 6;
+ snapval_home = snapval_home << 6;
+
+ /* Store value */
+ isnail_solar_values[idx] = snapval_solar;
+ isnail_home_values[idx++] = snapval_home;
+ if (idx == NB_VAL) {
+ idx = 0;
+ }
+
+ /* Compute average once we sampled enough values */
+ if ((idx == 0) || (idx == (NB_VAL / 2))) {
+ int i = 0;
+ moyenne_solar = 0;
+ moyenne_home = 0;
+ for (i = 0; i < NB_VAL; i++) {
+ moyenne_solar += isnail_solar_values[i];
+ moyenne_home += isnail_home_values[i];
+ }
+ moyenne_solar = moyenne_solar / NB_VAL;
+ moyenne_home = moyenne_home / NB_VAL;
+ }
+}
+
+/***************************************************************************** */
+/* AC control */
+
+volatile uint8_t command_val = 0;
+volatile int act_cmd = 0;
+static int fan_on = 0;
+
+void set_ctrl_duty_cycle(uint8_t value)
+{
+ act_cmd = value;
+ /* Below 3% and above 97% there are triggering problems which
+ * lead to 50% instead of desired command */
+ if (act_cmd >= 97) {
+ act_cmd = 97;
+ } else if (act_cmd <= 2) {
+ act_cmd = 0;
+ }
+}
+
+void ac_switch_on(uint32_t flags)
+{
+ gpio_set(ac_ctrl);
+}
+
+static uint32_t clk_cycles_ac_zc = 0;
+volatile uint32_t zc_cnt = 0;
+void zero_cross(uint32_t gpio)
+{
+ uint32_t delay = 0;
+
+ gpio_clear(ac_ctrl);
+ zc_cnt++;
+ if (act_cmd == 0) {
+ return;
+ }
+ /* Set timer to trigger ac out ON at given delay */
+ delay = clk_cycles_ac_zc * (100 - act_cmd);
+ timer_set_match(LPC_TIMER_32B1, CHAN1, delay);
+ timer_restart(LPC_TIMER_32B1);
+}
+
+
+/* Handle the power command */
+#define CMD_UP_DELAY_RESET_VALUE 5
+void handle_cmd_update(uint32_t curent_tick)
+{
+ static uint8_t cmd = 0;
+ static uint8_t cmd_up_delay = CMD_UP_DELAY_RESET_VALUE;
+
+ /* Unable to read internal temperature : turn off heating */
+ if (error_shutdown == 1) {
+ cmd = 0;
+ goto cmd_update_end;
+ }
+
+ /* Water max temperature protection */
+ if (water_centi_degrees > (MAX_WATER_TEMP * 100)) {
+ cmd = 0;
+ goto cmd_update_end;
+ }
+
+ /* Which is the current mode ? */
+ if (forced_heater_mode == 1) {
+ /* Forced heating mode */
+ if ((forced_heater_delay == 0) && (forced_heater_time > 0)) {
+ cmd = FORCED_MODE_VALUE;
+ }
+ /* Entering forced heating mode from temperature getting below threshold,
+ * wait some delay before effective forced heating */
+ if (forced_heater_time == 0) {
+ forced_heater_delay = FORCED_HEATER_DELAY;
+ forced_heater_time = FORCED_HEATER_DELAY + FORCED_HEATER_DURATION;
+ }
+ } else if (moyenne_solar < (moyenne_home - 64)) {
+ /* Low production mode */
+ if (cmd > 0) {
+ cmd--;
+ }
+ } else if (moyenne_solar > (moyenne_home + 1280)) {
+ if (cmd < 100) {
+ cmd++;
+ }
+ } else if (moyenne_solar > (moyenne_home + 192)) {
+ /* High production mode */
+ if (cmd_up_delay > 0) {
+ cmd_up_delay--;
+ } else {
+ cmd_up_delay = CMD_UP_DELAY_RESET_VALUE;
+ if (cmd < 100) {
+ cmd++;
+ }
+ }
+ }
+
+cmd_update_end:
+ command_val = cmd;
+ /* Set Control Output duty cycle */
+ set_ctrl_duty_cycle(100 - cmd);
+}
+
+
+/***************************************************************************** */
+void scialys_config(void)
+{
+ /* We want 100 Hz (50 Hz but two zero crossings) with 1% granularity */
+ clk_cycles_ac_zc = get_main_clock() / (100 * 100);
+
+ /* Read parameters from memory */
+ if (1) {
+ never_force = 0;
+ sunny_days_prod_value_config = SUNNY_DAYS_PROD_VALUE;
+ }
+
+ /* Add a systick callback to handle command value update */
+ add_systick_callback(handle_cmd_update, CMD_UPD_PERIOD);
+
+ /* Add a systick callback to handle time counting */
+ add_systick_callback(handle_dec_request, DEC_PERIOD);
+}
+
+
+/***************************************************************************** */
+int main(void)
+{
+ uint32_t loop = 0;
+ char mode = heat; /* Debug info */
+
+ system_init();
+ board_io_config();
+ modules_config();
+
+ external_config(UART0);
+ scialys_config();
+
+ msleep(1500);
+ status_led(green_only);
+
+ while (1) {
+ uint16_t snapval_load = 0;
+
+ /* Default mode : try to heat the water tank */
+ mode = heat;
+
+ /* Always track power consumption and production */
+ track_isnail_values();
+
+ /* Get load power usage */
+ adc_get_value(&snapval_load, LPC_ADC(2));
+
+ /* Feed the dog */
+ if ((moyenne_solar != 0) && (moyenne_home != 0)) {
+ watchdog_feed();
+ }
+
+ /* Get internal temperature */
+ if (temp_read(UART0, &tmp101_deci_degrees) != 0) {
+ tmp101_deci_degrees = 1500;
+ }
+
+ /* Internal temperature protection */
+ if (tmp101_deci_degrees > (MAX_INTERNAL_TEMP * 10)) {
+ error_shutdown = 1;
+ } else if (tmp101_deci_degrees < ((MAX_INTERNAL_TEMP * 10) / 2)) {
+ error_shutdown = 0;
+ }
+ /* If internal temperature is above 32°C, then turn on fan. Turn off when back to under 28°C */
+ if (tmp101_deci_degrees > 320) {
+ fan_on = 1;
+ gpio_set(fan_ctrl);
+ } else if (tmp101_deci_degrees < 280) {
+ fan_on = 0;
+ gpio_clear(fan_ctrl);
+ }
+
+ /* Get thermocouple value */
+ if (1) {
+ int ret = 0, old_water_temp = 0;
+ old_water_temp = water_centi_degrees;
+ ret = max31855_sensor_read(&thermocouple, NULL, &water_centi_degrees);
+ if (ret != 0) {
+ uprintf(UART0, "Water Temp read error : %d\n", ret);
+ /* Do not act upon iinvalid temp value */
+ water_centi_degrees = old_water_temp;
+ }
+ }
+
+ /* Need to enter Forced heating mode ? */
+ if (water_centi_degrees < (FORCE_HEATER_TEMP * 100)) {
+ if (forced_heater_mode == 0) {
+ uprintf(UART0, "Water temp low, entering forced mode\n");
+ forced_heater_mode = 1;
+ }
+ status_led(red_on);
+ mode = forced;
+ } else if ((water_centi_degrees > (TARGET_FORCED_HEATER_TEMP * 100)) && (forced_heater_mode == 1)) {
+ status_led(red_off);
+ uprintf(UART0, "Water temp OK, forced mode exit\n");
+ forced_heater_mode = 0;
+ mode = temp_OK;
+ }
+
+ /* Do not force if there is a lot of sun, it may be enough to heat again soon */
+ if (moyenne_solar > sunny_days_prod_value_config) {
+ mode = delayed_heat_prod;
+ forced_heater_mode = 0;
+ }
+
+ /* Do not force heating if disabled by external command */
+ external_disable = gpio_read(ext_disable_in_pin);
+ if ((external_disable == EXTERNAL_DISABLE_FORCE) && (forced_heater_mode != 0)) {
+ forced_heater_mode = 0;
+ mode = ext_disable;
+ }
+
+ if (never_force == 1) {
+ forced_heater_mode = 0;
+ }
+
+ /* Did the user request a forced heating ? */
+ if (manual_activation_request != 0) {
+ forced_heater_mode = 1;
+ mode = manual;
+ if (manual_activation_request == -1) {
+ uprintf(UART0, "Entering manual forced mode for %d ticks\n", manual_activation_request);
+ manual_activation_request = MANUAL_ACTIVATION_DURATION;
+ forced_heater_time = FORCED_HEATER_DURATION;
+ }
+ if (manual_activation_request < 10) {
+ uprintf(UART0, "Leaving manual forced mode\n");
+ manual_activation_request = 0;
+ }
+ }
+
+ /* Wait approx 10ms between each loop */
+ msleep(10);
+
+ /* Display */
+ interface_update(mode);
+
+ /* Debug */
+ if (1) {
+ uprintf(UART0, "%c:%d - Is: %d,%04d - Ih: %d,%04d\n", mode, loop++,
+ (moyenne_solar / 1000), (moyenne_solar % 1000),
+ (moyenne_home / 1000), (moyenne_home % 1000));
+ uprintf(UART0, "Water Temp : %d\n", water_centi_degrees);
+ uprintf(UART0, "Internal Temp : %d\n", tmp101_deci_degrees);
+ uprintf(UART0, "ADC: Sol: %dmA, Home: %dmA, Load: %d\n",
+ snapval_solar, snapval_home, snapval_load);
+ uprintf(UART0, "Zc: %d\n", zc_cnt);
+ uprintf(UART0, "CMD: %d/%d, Fan: %d\n\n", command_val, act_cmd, fan_on);
+ }
+
+ }
+ return 0;
+}
+
+
+
--- /dev/null
+/****************************************************************************
+ * apps/blyes/inter_lights/time.c
+ *
+ * Copyright 2018 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 = 0x18,
+ .month = 0x08,
+ .day = 0x09,
+ .hour = 0x13,
+ .min = 0x37,
+};
+int time_valid = 0;
+int rtc_conf_ok = 0;
+
+int 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;
+}
+
+
+/***************************************************************************** */
+/* RTC init */
+void time_config(uint32_t i2c_bus_num, uint8_t rtc_addr)
+{
+ rtc_conf.bus_num = i2c_bus_num;
+ rtc_conf.addr = rtc_addr;
+}
+
--- /dev/null
+/****************************************************************************
+ * apps/blyes/inter_lights/time.h
+ *
+ * Copyright 2018 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 time_init_check(uint32_t uart);
+void time_config(uint32_t i2c_bus_num, uint8_t rtc_addr);
+
+#endif /* RTC_TIME_H */
+
--- /dev/null
+/****************************************************************************
+ * apps/blyes/inter_lights/uSD.c
+ *
+ * Copyright 2018 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/ssp.h"
+
+#include "lib/stdio.h"
+
+#include "uSD.h"
+
+
+/***************************************************************************** */
+/* SD/MMC Card */
+struct sdmmc_card micro_sd = {
+ .card_type = MMC_CARDTYPE_UNKNOWN,
+ .block_size = 64,
+};
+
+static uint8_t got_uSD = 0;
+
+/* Buffer to read / write uSD card */
+uint8_t mmc_data[MMC_BUF_SIZE];
+
+/* Read up to 4 blocks of 16 bytes each */
+int uSD_read(uint32_t offset, uint8_t nb_blocks)
+{
+ int i = 0, ret = 0;
+ if (got_uSD == 0) {
+ return -1;
+ }
+ if (nb_blocks > 4) {
+ return -2;
+ }
+ memset(mmc_data, 0, MMC_BUF_SIZE);
+ for (i = 0; i < nb_blocks; i++) {
+ ret = sdmmc_read_block(µ_sd, (offset + i), (mmc_data + (i * 16)));
+ /* FIXME : check return value */
+ }
+ return 0;
+}
+
+
+/* Write up to 4 blocks of 16 bytes each */
+int uSD_write(uint32_t offset, uint8_t nb_blocks)
+{
+ int i = 0, ret = 0;
+ if (got_uSD == 0) {
+ return -1;
+ }
+ if (nb_blocks > 4) {
+ return -1;
+ }
+ for (i = 0; i < nb_blocks; i++) {
+ ret = sdmmc_write_block(µ_sd, (offset + i), (mmc_data + (i * 16)));
+ /* FIXME : check return value */
+ }
+ return 0;
+}
+
+/* microSD card init */
+int uSD_detect(int uart)
+{
+ int i = 0, ret = 0, step = 0;
+ do {
+ step = 0;
+ ret = sdmmc_init(µ_sd);
+ if (ret == 0) {
+ step = 1;
+ msleep(10);
+ ret = sdmmc_init_wait_card_ready(µ_sd);
+ if (ret <= 1) {
+ step = 2;
+ ret = sdmmc_init_end(µ_sd);
+ }
+ }
+ uprintf(uart, "uSD init(%d): step:%d, ret: %d, type: %d, bs: %d\n",
+ i, step, ret, micro_sd.card_type, micro_sd.block_size);
+ i++;
+ } while (((ret != 0) || (micro_sd.card_type == MMC_CARDTYPE_UNKNOWN)) && (i < 10));
+
+ /* Got uSD ? */
+ if (i >= 10) {
+ uprintf(uart, "uSD init failed, no uSD card present.\n");
+ got_uSD = 0;
+ return -1;
+ }
+
+ /* uSD card detected */
+ got_uSD = 1;
+ /* got_uSD MUST be set to 1 from here on ! */
+ uSD_read(0, 4); /* Read 4 blocks at start of card */
+ /* FIXME : check that the card magic is present */
+ uprintf(uart, "uSD read: %s\n", mmc_data);
+
+ return 0;
+}
+
+
+int uSD_logs_init(int uart)
+{
+ /* FIXME : read 4 blocks at offset FIXME to get the last block used info */
+ return 0;
+}
+
+/***************************************************************************** */
+/* microSD card init */
+void uSD_config(struct pio* uSD_cs, uint32_t ssp_bus_num)
+{
+ memcpy(&(micro_sd.chip_select), uSD_cs, sizeof(struct pio));
+ micro_sd.ssp_bus_num = ssp_bus_num;
+}
+
--- /dev/null
+/****************************************************************************
+ * apps/blyes/inter_lights/usSD.h
+ *
+ * Copyright 2018 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 USD_H
+#define USD_H
+
+#include "extdrv/sdmmc.h"
+
+#define MMC_BUF_SIZE 64
+
+extern struct sdmmc_card micro_sd;
+extern uint8_t mmc_data[MMC_BUF_SIZE];
+
+int uSD_read(uint32_t offset, uint8_t nb_blocks);
+int uSD_write(uint32_t offset, uint8_t nb_blocks);
+
+/***************************************************************************** */
+/* microSD card init */
+int uSD_detect(int uart);
+void uSD_config(struct pio* uSD_cs, uint32_t ssp_bus_num);
+
+
+#endif /* USD_H */
+
--- /dev/null
+# 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} $@
+
--- /dev/null
+/****************************************************************************
+ * apps/scialys/v07/config.c
+ *
+ * Copyright 2018 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 "drivers/adc.h"
+#include "drivers/ssp.h"
+#include "drivers/timers.h"
+
+
+#include "extdrv/status_led.h"
+#include "extdrv/max31855_thermocouple.h"
+
+#include "lib/stdio.h"
+
+#include "config.h"
+#include "interface.h"
+#include "time.h"
+#include "uSD.h"
+
+#define SELECTED_FREQ FREQ_SEL_48MHz
+
+
+uint8_t config_got_interface = 1;
+
+/***************************************************************************** */
+/* Pins configuration */
+/* pins blocks are passed to set_pins() for pins configuration.
+ * Unused pin blocks can be removed safely with the corresponding set_pins() call
+ * All pins blocks may be safelly merged in a single block for single set_pins() call..
+ */
+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) },
+ /* SPI */
+ { LPC_SSP0_SCLK_PIO_0_14, LPC_IO_DIGITAL },
+ { LPC_SSP0_MOSI_PIO_0_17, LPC_IO_DIGITAL },
+ { LPC_SSP0_MISO_PIO_0_16, LPC_IO_DIGITAL },
+ /* ADC */
+ { LPC_ADC_AD0_PIO_0_30, LPC_IO_ANALOG }, /* Home */
+ { LPC_ADC_AD1_PIO_0_31, LPC_IO_ANALOG }, /* Production */
+ { LPC_ADC_AD2_PIO_1_0, LPC_IO_ANALOG }, /* Load */
+ /* GPIO */
+ { LPC_GPIO_0_3, LPC_IO_DIGITAL }, /* External switch input */
+ { LPC_GPIO_0_4, LPC_IO_DIGITAL }, /* Zero crossing detection input */
+ { LPC_GPIO_0_5, LPC_IO_DIGITAL }, /* Fan control */
+ { LPC_GPIO_0_6, LPC_IO_DIGITAL }, /* Mosfet control */
+ { LPC_GPIO_0_7, LPC_IO_DIGITAL }, /* Thermocouple chip select */
+ { LPC_GPIO_0_12, LPC_IO_DIGITAL }, /* ISP / User button OK */
+ { LPC_GPIO_0_18, LPC_IO_DIGITAL }, /* Uext Chip select / Module eeprom select */
+ { LPC_GPIO_0_20, LPC_IO_DIGITAL }, /* Oled_Reset */
+ { LPC_GPIO_0_21, LPC_IO_DIGITAL }, /* Oled Control / I2C SA0 */
+ { LPC_GPIO_1_1, LPC_IO_DIGITAL }, /* uSD Card SPI Chip Select */
+ { 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;
+
+/* Inputs */
+/* External signals */
+const struct pio zero_cross_in_pin = LPC_GPIO_0_4;
+const struct pio ext_disable_in_pin = LPC_GPIO_0_3;
+
+/* Outputs */
+/* AC output control (Mosfet) */
+const struct pio ac_ctrl = LPC_GPIO_0_6;
+const struct pio fan_ctrl = LPC_GPIO_0_5;
+/* Chip selects */
+const struct pio uSD_cs = LPC_GPIO_1_1; /* uSD card */
+const struct pio th_cs = LPC_GPIO_0_7; /* Thermocouple */
+const struct pio uext_cs = LPC_GPIO_0_18; /* UEXT module (optionnal) */
+
+
+
+/***************************************************************************** */
+/* 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);
+}
+
+
+/***************************************************************************** */
+/* Internal modules */
+const struct lpc_tc_config ac_timer_conf_zc = {
+ .mode = LPC_TIMER_MODE_TIMER | LPC_TIMER_MODE_MATCH,
+ .match_control = { LPC_TIMER_INT_RESET_ON_MATCH, 0, 0, 0, },
+};
+const struct lpc_tc_config ac_timer_conf_delay = {
+ .mode = LPC_TIMER_MODE_TIMER | LPC_TIMER_MODE_MATCH,
+ .match_control = { LPC_TIMER_INT_RESET_AND_STOP_ON_MATCH, 0, 0, 0, },
+};
+
+extern void config_rx(uint8_t c);
+extern void comm_rx(uint8_t c);
+extern void ac_switch_on(uint32_t flags);
+extern void zero_cross(uint32_t flags);
+
+void modules_config(void)
+{
+ uart_on(UART0, 115200, config_rx);
+ uart_on(UART1, 115200, comm_rx);
+ i2c_on(I2C0, I2C_CLK_100KHz, I2C_MASTER);
+ ssp_master_on(SSP_BUS_0, LPC_SSP_FRAME_SPI, 8, 4*1000*1000);
+ adc_on(NULL);
+ adc_start_burst_conversion(ADC_MCH(0) | ADC_MCH(1) | ADC_MCH(2), LPC_ADC_SEQ(0));
+ timer_on(LPC_TIMER_32B0, 0, zero_cross);
+ timer_counter_config(LPC_TIMER_32B0, &ac_timer_conf_zc);
+ timer_on(LPC_TIMER_32B1, 0, ac_switch_on);
+ timer_counter_config(LPC_TIMER_32B1, &ac_timer_conf_delay);
+}
+
+
+/***************************************************************************** */
+extern void zero_cross_detect(uint32_t gpio);
+void board_io_config(void)
+{
+ /* Immediatly turn off Mosfet / Triac */
+ config_gpio(&ac_ctrl, 0, GPIO_DIR_OUT, 1);
+ /* Start with FAN ON */
+ config_gpio(&fan_ctrl, 0, GPIO_DIR_OUT, 1);
+
+ /* Configure Input GPIO */
+ config_gpio(&ext_disable_in_pin, 0, GPIO_DIR_IN, 0);
+
+ /* Zero-crossing detection */
+ set_gpio_callback(zero_cross_detect, &zero_cross_in_pin, EDGE_FALLING);
+}
+
+
+
+/***************************************************************************** */
+/* Configure external components */
+
+/* Thermocouple reading */
+const struct max31855_sensor_config thermocouple = {
+ .ssp_bus_num = 0,
+ .chip_select = LPC_GPIO_0_7,
+};
+
+/* RTC and time */
+#define RTC_ADDR 0xA2
+
+
+int external_config(uint32_t uart)
+{
+ int ret = 0;
+
+ /* Configure GPIO for specific board usage */
+ board_io_config();
+
+ /* uSD card */
+ uSD_config((struct pio *)&uSD_cs, SSP_BUS_0);
+ uSD_detect(uart);
+
+ /* RTC */
+ time_config(I2C0, RTC_ADDR);
+ time_init_check(uart);
+
+ /* Thermocouple configuration */
+ max31855_sensor_config(&thermocouple);
+
+ /* Configure interface board */
+ ret = interface_config(uart);
+ if (ret != 0) {
+ return -1;
+ }
+ return 0;
+}
--- /dev/null
+/****************************************************************************
+ * apps/scialys/v07/config.h
+ *
+ * Copyright 2018 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 "drivers/adc.h"
+#include "drivers/ssp.h"
+#include "drivers/timers.h"
+
+#include "extdrv/status_led.h"
+#include "extdrv/max31855_thermocouple.h"
+#include "extdrv/ws2812.h"
+
+#include "lib/stdio.h"
+
+
+#define SELECTED_FREQ FREQ_SEL_48MHz
+
+/***************************************************************************** */
+/* Pins configuration */
+extern const struct pio status_led_green;
+extern const struct pio status_led_red;
+
+/* Inputs */
+/* External signals */
+extern const struct pio zero_cross_in_pin;
+extern const struct pio ext_disable_in_pin;
+
+/* Outputs */
+/* AC output control (Mosfet) */
+extern const struct pio ac_ctrl;
+extern const struct pio fan_ctrl;
+/* Chip selects */
+extern const struct pio uSD_cs; /* uSD card */
+extern const struct pio th_cs; /* Thermocouple */
+extern const struct pio uext_cs; /* UEXT module (optionnal) */
+
+
+
+/***************************************************************************** */
+extern const struct max31855_sensor_config thermocouple;
+
+
+/***************************************************************************** */
+/* 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 modules and main functions */
+void modules_config(void);
+
+/* Configure GPIO for specific board usage */
+void board_io_config(void);
+
+/* Configure external components */
+int external_config(uint32_t uart);
+
+
+#endif /* CONFIG_H */
+
--- /dev/null
+/****************************************************************************
+ * apps/scialys/v07/interface.c
+ *
+ * Copyright 2018 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 "drivers/adc.h"
+
+#include "extdrv/status_led.h"
+#include "extdrv/ws2812.h"
+#include "extdrv/tmp101_temp_sensor.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 "interface.h"
+#include "time.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
+
+
+const struct pio oled_i2c_sa0 = LPC_GPIO_0_21; /* Oled Control / I2C SA0 */
+
+
+static uint8_t interface_board_present = 0;
+
+/***************************************************************************** */
+/* TMP101 onboard I2C temperature sensor */
+#define TMP101_ADDR 0x94 /* Pin Addr0 (pin5 of tmp101) connected to VCC */
+struct tmp101_sensor_config tmp101_sensor = {
+ .bus_num = I2C0,
+ .addr = TMP101_ADDR,
+ .resolution = TMP_RES_ELEVEN_BITS,
+};
+
+/***************************************************************************** */
+/* 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;
+
+ if (interface_board_present == 0) {
+ return -1;
+ }
+
+ 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;
+
+ if (interface_board_present == 0) {
+ return -1;
+ }
+
+ 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);
+}
+
+
+int temp_read(uint32_t uart, int* deci_degrees)
+{
+ int ret = 0;
+
+ if (interface_board_present == 0) {
+ return -1;
+ }
+
+ ret = tmp101_sensor_read(&tmp101_sensor, NULL, deci_degrees);
+ if (ret != 0) {
+ uprintf(uart, "TMP101 read error : %d\n", ret);
+ return -1;
+ }
+ return 0;
+}
+
+
+/***************************************************************************** */
+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;
+ }
+}
+
+int interface_config(uint32_t uart)
+{
+ int ret = 0;
+
+ /* TMP101 sensor config */
+ ret = tmp101_sensor_config(&tmp101_sensor);
+ if (ret != 0) {
+ uprintf(uart, "Temp config error: %d\n", ret);
+ interface_board_present = 0;
+ return -1;
+ }
+ ret = tmp101_sensor_set_continuous_conversion(&tmp101_sensor);
+ if (ret != 0) {
+ uprintf(uart, "Temp config error: %d\n", ret);
+ interface_board_present = 0;
+ return -2;
+ }
+ interface_board_present = 1;
+
+ /* Buttons inputs on front panel */
+ /* Activate on Rising edge (button release) */
+ set_gpio_callback(button_callback, &button_up, EDGE_RISING);
+ set_gpio_callback(button_callback, &button_left, EDGE_RISING);
+ set_gpio_callback(button_callback, &button_right, EDGE_RISING);
+ set_gpio_callback(button_callback, &button_down, EDGE_RISING);
+ set_gpio_callback(button_callback, &button_ok, EDGE_RISING);
+
+
+ /* Configure and start display */
+ config_gpio(&oled_i2c_sa0, 0, GPIO_DIR_OUT, 0); /* Low I2C address bit set to 0 */
+ ssd130x_display_on(&display);
+ erase_screen_content();
+ ssd130x_display_full_screen(&display);
+
+ ws2812_set_pixel(0, 0x05, 0x15, 0x08);
+ ws2812_send_frame(0);
+
+ uprintf(uart, "Config OK\n");
+
+ return 0;
+}
+
+
+
+/***************************************************************************** */
+ /* Menu part */
+
+enum interface_modes {
+ MODE_RUN = 0,
+ MODE_CONFIG,
+ MODE_DISPLAY,
+};
+static int interface_mode = MODE_RUN;
+volatile int manual_activation_request = 0;
+
+enum menu_list {
+ MAIN_MENU,
+ MANUAL_MODE,
+ DATE_CONFIG,
+ LIMITS,
+ FUNCTIONS,
+ TEST_MODE,
+ SAVE_CONFIG,
+ NB_MENU, /* This one must be the last */
+};
+static const char* menu_titles[] = {
+ [MAIN_MENU] = "Menu principal",
+ [MANUAL_MODE] = "Marche Forcee",
+ [DATE_CONFIG] = "Regl. Heure",
+ [LIMITS] = "Limites",
+ [FUNCTIONS] = "Fonctions",
+ [TEST_MODE] = "Test mode",
+ [SAVE_CONFIG] = "Save config",
+};
+static uint8_t current_menu = MAIN_MENU;
+static uint8_t current_entry = MANUAL_MODE;
+
+enum func_menu_list {
+ FUNC_LOAD_TYPE,
+ FUNC_CMD_TYPE,
+ FUNC_NB_MENU, /* This one must be the last */
+};
+static const char* func_modes_titles[] = {
+ [FUNC_LOAD_TYPE] = "Load Type",
+ [FUNC_CMD_TYPE] = "CMD type",
+};
+static uint8_t func_cur_menu = FUNC_LOAD_TYPE;
+static uint8_t func_cur_entry = FUNC_LOAD_TYPE;
+
+enum limits_menu_list {
+ TEMP_FORCED_IN,
+ TEMP_FORCED_TARGET,
+ FORCED_MODE_VAL,
+ SUNNY_DAY_VALUE,
+ LIM_NB_MENU, /* This one must be the last */
+};
+static const char* limits_modes_titles[] = {
+ [TEMP_FORCED_IN] = "Water Min",
+ [TEMP_FORCED_TARGET] = "Water Target",
+ [FORCED_MODE_VAL] = "Forced cmd",
+ [SUNNY_DAY_VALUE] = "Sun Prod",
+};
+static uint8_t limits_cur_menu = TEMP_FORCED_IN;
+static uint8_t limits_cur_entry = TEMP_FORCED_IN;
+
+enum test_menu_list {
+ TEST_FAN,
+ TEST_12V,
+ TEST_CMDV,
+ TEST_NB_MENU, /* This one must be the last */
+};
+static const char* test_modes_titles[] = {
+ [TEST_FAN] = "Fan",
+ [TEST_12V] = "DC 12V",
+ [TEST_CMDV] = "CMD Val",
+};
+extern uint8_t force_fan;
+extern int8_t force_cmd;
+extern uint32_t zc_cnt;
+extern void zero_cross_detect(uint32_t gpio);
+static uint8_t test_cur_entry = TEST_FAN;
+
+
+/* Current sub-menu level. main menu is level 0 */
+int sub_menu_level = 0;
+
+
+void config_interface_handle(void)
+{
+ char line[DISP_LLEN];
+ 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;
+ for (i = 1; i < NB_MENU; i++) {
+ if (i != current_entry) {
+ display_line(i + 1, 3, menu_titles[i]);
+ } else {
+ snprintf(line, DISP_LLEN, " ->%s", menu_titles[i]);
+ display_line(i + 1, 0, line);
+ }
+ }
+ 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) {
+ current_entry = NB_MENU - 1;
+ }
+ if (button & (BUTTON_OK | BUTTON_RIGHT)) {
+ current_menu = current_entry;
+ sub_menu_level = 1;
+ }
+ if (button & BUTTON_LEFT) {
+ interface_mode = MODE_RUN;
+ }
+ }
+ break;
+ case DATE_CONFIG: {
+ static uint8_t date_idx = 0;
+ struct rtc_time now;
+ snprintf(line, DISP_LLEN, "^");
+ display_line(3, 6 + (date_idx * 3), line);
+ rtc_pcf85363_time_read(&rtc_conf, &now);
+ snprintf(line, DISP_LLEN, "%02xh%02x:%02x", now.hour, now.min, now.sec);
+ display_line(4, 6, line);
+ snprintf(line, DISP_LLEN, "V");
+ display_line(5, 6 + (date_idx * 3), line);
+ if ((button & BUTTON_RIGHT) && (date_idx < 2)) {
+ date_idx++;
+ }
+ if ((button & BUTTON_LEFT) && (date_idx > 0)) {
+ date_idx--;
+ }
+ if (button & (BUTTON_UP | BUTTON_DOWN)) {
+ uint32_t seconds = rtc_pcf85363_daytime_to_seconds(&now);
+ uint32_t add[6] = {
+ 3600, 60, 1,
+ (23 * 3600), (23* 3600 + 59 * 60), (23 * 3600 + 59 * 60 + 59),
+ };
+ if (button & BUTTON_UP) {
+ if (seconds < add[date_idx + 3]) {
+ seconds += add[date_idx];
+ } else {
+ seconds -= add[date_idx + 3];
+ }
+ } else {
+ if (seconds < add[date_idx]) {
+ seconds += add[date_idx + 3];
+ } else {
+ seconds -= add[date_idx];
+ }
+ }
+ rtc_pcf85363_seconds_to_daytime(&now, seconds);
+ rtc_pcf85363_time_write(&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 {
+ snprintf(line, DISP_LLEN, " ->%s", limits_modes_titles[i]);
+ display_line(i + 2, 0, line);
+ }
+ }
+ if (button & BUTTON_UP) {
+ limits_cur_entry -= 1;
+ }
+ if (button & BUTTON_DOWN) {
+ limits_cur_entry += 1;
+ }
+ if (limits_cur_entry >= LIM_NB_MENU) {
+ limits_cur_entry = 0;
+ } else if (limits_cur_entry == 0xFF) {
+ limits_cur_entry = LIM_NB_MENU - 1;
+ }
+ 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 {
+ }
+ }
+ break;
+ case FUNCTIONS: {
+ if (sub_menu_level == 1) {
+ int i = 0;
+ for (i = 0; i < FUNC_NB_MENU; i++) {
+ if (i != func_cur_entry) {
+ display_line(i + 2, 3, func_modes_titles[i]);
+ } else {
+ snprintf(line, DISP_LLEN, " ->%s", func_modes_titles[i]);
+ display_line(i + 2, 0, line);
+ }
+ }
+ if (button & BUTTON_UP) {
+ func_cur_entry -= 1;
+ }
+ if (button & BUTTON_DOWN) {
+ func_cur_entry += 1;
+ }
+ if (func_cur_entry >= FUNC_NB_MENU) {
+ func_cur_entry = 0;
+ } else if (func_cur_entry == 0xFF) {
+ func_cur_entry = FUNC_NB_MENU - 1;
+ }
+ if (button & (BUTTON_OK | BUTTON_RIGHT)) {
+ func_cur_menu = func_cur_entry;
+ sub_menu_level = 2;
+ }
+ if (button & BUTTON_LEFT) {
+ sub_menu_level = 0;
+ current_menu = MAIN_MENU;
+ }
+ } else {
+ }
+ }
+ break;
+ case TEST_MODE: {
+ int i = 0;
+ uint8_t states[3];
+
+ states[TEST_FAN] = force_fan;
+ states[TEST_12V] = (zc_cnt != 0) ? 1 : 0;
+ states[TEST_CMDV] = force_cmd;
+
+ for (i = 0; i < TEST_NB_MENU; i++) {
+ if (i != test_cur_entry) {
+ display_line(i + 2, 3, test_modes_titles[i]);
+ } else {
+ snprintf(line, DISP_LLEN, " ->%s : %d", test_modes_titles[i], states[i]);
+ display_line(i + 2, 0, line);
+ }
+ }
+ if (button & BUTTON_UP) {
+ test_cur_entry -= 1;
+ }
+ if (button & BUTTON_DOWN) {
+ test_cur_entry += 1;
+ }
+ if (test_cur_entry >= TEST_NB_MENU) {
+ test_cur_entry = 0;
+ } else if (test_cur_entry == 0xFF) {
+ test_cur_entry = TEST_NB_MENU - 1;
+ }
+ if (button & (BUTTON_LEFT | BUTTON_RIGHT)) {
+ switch (test_cur_entry) {
+ case TEST_FAN:
+ force_fan = !force_fan;
+ break;
+ case TEST_12V:
+ /* Generate a fake zero-cross detect by calling the handler directly */
+ zero_cross_detect(0);
+ break;
+ case TEST_CMDV:
+ if (force_cmd >= 0) {
+ force_cmd = -1;
+ } else {
+ force_cmd = 0;
+ }
+ default:
+ break;
+ }
+ }
+ if (button & BUTTON_OK) {
+ sub_menu_level = 0;
+ current_menu = MAIN_MENU;
+ }
+ }
+ break;
+ case SAVE_CONFIG: {
+ }
+ break;
+ case MANUAL_MODE:
+ manual_activation_request = -1;
+ /* Fallback */
+ default:
+ interface_mode = MODE_RUN;
+ current_menu = MAIN_MENU;
+ break;
+ }
+ /* Update Oled display */
+ ssd130x_display_full_screen(&display);
+}
+
+
+/***************************************************************************** */
+ /* usual menu and switch to config menu */
+
+extern int water_centi_degrees;
+extern uint32_t moyenne_solar;
+extern uint32_t moyenne_home;
+extern volatile uint8_t command_val;
+
+void interface_update(char heat_mode)
+{
+ int abs_centi = water_centi_degrees;
+
+ if (water_centi_degrees < 0) {
+ abs_centi = -water_centi_degrees;
+ }
+
+ /* Debug */
+ if (button_pressed != 0) {
+ uprintf(UART0, "Button : 0x%02x\n", button_pressed);
+ }
+
+ if (interface_mode == MODE_RUN) {
+ if (button_pressed != 0) {
+ if (button_pressed & BUTTON_OK) {
+ manual_activation_request = -1;
+ }
+ if (button_pressed & BUTTON_UP) {
+ interface_mode = MODE_CONFIG;
+ }
+ if (force_cmd >= 0) {
+ if ((button_pressed & BUTTON_RIGHT) && (force_cmd <= 95)) {
+ force_cmd += 5;
+ }
+ if ((button_pressed & BUTTON_LEFT) && (force_cmd >= 5)) {
+ force_cmd -= 5;
+ }
+ }
+ button_pressed = 0;
+ }
+
+ if (interface_board_present != 0) {
+ char line[DISP_LLEN];
+
+ /* Start with a blank screen */
+ erase_screen_content();
+
+ /* Update time and time display on internal memory */
+ rtc_pcf85363_time_read(&rtc_conf, &now);
+ snprintf(line, DISP_LLEN, "%02xh%02x:%02x", now.hour, now.min, now.sec);
+ display_line(0, 0, line);
+
+ /* Display info */
+ snprintf(line, DISP_LLEN, "Water:% 2d.%03d %cC", (water_centi_degrees / 100), (abs_centi % 100), 0x1F);
+ display_line(2, 0, line);
+ snprintf(line, DISP_LLEN, "Prod :% 2d,%03dA", (moyenne_solar / 1000), ((moyenne_solar % 1000) / 10));
+ display_line(3, 0, line);
+ snprintf(line, DISP_LLEN, "Conso:% 2d,%03dA", (moyenne_home / 1000), ((moyenne_home % 1000) / 10));
+ display_line(4, 0, line);
+ snprintf(line, DISP_LLEN, "Command: %d%%", command_val);
+ display_line(5, 0, line);
+ snprintf(line, DISP_LLEN, "Mode: %c", heat_mode);
+ display_line(6, 0, line);
+ /* Update Oled display */
+ ssd130x_display_full_screen(&display);
+ }
+ } else {
+ /* Config mode. Mode entered by button action, which implies that interface board is present. */
+ config_interface_handle();
+ }
+}
+
+
--- /dev/null
+/****************************************************************************
+ * apps/blyes/relay_lights/interface.h
+ *
+ * Copyright 2018 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;
+
+extern volatile int manual_activation_request;
+
+/* 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);
+
+
+/* Internal Temperature sensor */
+int temp_read(uint32_t uart, int* deci_degrees);
+
+/* Interface content update */
+void interface_update(char mode);
+
+#endif /* INTERFACE_H */
+
--- /dev/null
+/****************************************************************************
+ * apps/scialys/v07/main.c
+ *
+ * Scialys system for solar-panel power generation tracking and fair use.
+ *
+ * Copyright 2016 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 "uSD.h"
+
+
+#define MODULE_VERSION 0x05
+#define MODULE_NAME "Scialys uC"
+
+
+
+/***************************************************************************** */
+/* System configuration */
+
+/* Period (in ms) of the decrementer handler from the systick interrupt */
+#define DEC_PERIOD 100
+/* Period (in ms) of the handler for the command value update */
+#define CMD_UPD_PERIOD 200
+
+/* NOTE : All temperatures are stored as decidegrees */
+/* Max water temperature. Internal protection which cannot be overriden by configuration */
+#define MAX_WATER_TEMP 900
+/* Internal system max temperature : turn off heating when reached */
+#define MAX_INTERNAL_TEMP 800 /* FIXME */
+
+#define DEFAULT_INTERNAL_TEMP 600
+#define FAN_ON_INTERNAL_TEMP 350
+#define FAN_OFF_INTERNAL_TEMP 320
+
+/* Most of the defines in here should go to configuration setting in user flash */
+/* If temperature falls bellow FORCE_HEATER_TEMP value, we enter forced heater mode, until
+ * TARGET_FORCED_HEATER_TEMP is reached.
+ * When in forced heater mode, the heater is controlled to heat at FORCED_MODE_VALUE which
+ * is between 0 and 100.
+ */
+#define FORCE_HEATER_TEMP 300
+#define TARGET_FORCED_HEATER_TEMP 500
+#define FORCED_MODE_VALUE 75 /* A fraction of 100 */
+/* mA prod value above which the system will not enter forced mode, waiting for home
+ * to stop using power to start automatic heating */
+#define SUNNY_DAYS_PROD_VALUE 3000
+
+/* Delay before automatic forced heating */
+#define FORCED_HEATER_DELAY (2 * 3600 * 1000 / DEC_PERIOD)
+/* Duration of automatic forced heating */
+#define FORCED_HEATER_DURATION (3 * 3600 * 1000 / DEC_PERIOD)
+
+/* Duration of manual forced heating */
+#define MANUAL_ACTIVATION_DURATION (3 * 3600 * 1000 / DEC_PERIOD) /* Three hours */
+
+
+uint32_t forced_heater_mode = 0;
+uint32_t forced_heater_delay = 0;
+uint32_t forced_heater_time = 0;
+
+uint8_t error_shutdown = 0;
+
+uint8_t never_force = 0;
+uint32_t sunny_days_prod_value_config = 0;
+
+#define EXTERNAL_DISABLE_FORCE 0 /* Input is pulled low when external disable is ON */
+int external_disable = 0;
+
+enum modes {
+ heat = 'C',
+ ext_disable = 'E',
+ delayed_heat_prod = 'P',
+ forced = 'F',
+ temp_OK = 'T',
+ manual = 'M',
+ idle_heat = 'L',
+ full_heat = 'F',
+};
+
+/* Water and internaltemperature */
+int water_centi_degrees = 0;
+int tmp101_deci_degrees = 0;
+
+/* RTC and time */
+struct rtc_time now;
+
+
+/***************************************************************************** */
+/* Rx interrupt handler for system configuration over USB */
+void config_rx(uint8_t c)
+{
+ /* FAN control */
+ if (c == 'f') {
+ gpio_set(fan_ctrl);
+ } else {
+ gpio_clear(fan_ctrl);
+ }
+}
+
+
+/* Communication with slave modules */
+void comm_rx(uint8_t c)
+{
+}
+
+
+
+/***************************************************************************** */
+/* System communication over UART1 */
+void cmd_rx(uint8_t c)
+{
+}
+
+
+/***************************************************************************** */
+/* Decrementer for heating timers */
+void handle_dec_request(uint32_t curent_tick) {
+ if (manual_activation_request > 0) {
+ manual_activation_request--;
+ }
+ if (forced_heater_mode == 1) {
+ if (forced_heater_delay > 0) {
+ forced_heater_delay--;
+ }
+ if (forced_heater_time > 0) {
+ forced_heater_time--;
+ }
+ }
+}
+
+
+/***************************************************************************** */
+/* Track power production and usage */
+
+/* Average value computed on last 10 ADC values */
+uint32_t moyenne_solar = 0;
+uint32_t moyenne_home = 0;
+/* Last ADC values (snapshot) */
+static uint16_t snapval_solar = 0;
+static uint16_t snapval_home = 0;
+
+#define NB_VAL 20
+static void track_isnail_values(void)
+{
+ static uint16_t isnail_solar_values[NB_VAL];
+ static uint16_t isnail_home_values[NB_VAL];
+ static uint8_t idx = 0;
+
+ /* Get new values */
+ adc_get_value(&snapval_solar, LPC_ADC(1));
+ adc_get_value(&snapval_home, LPC_ADC(0));
+
+ /* Convert to mA value.
+ * ADC range is 0 to 1024 - approx 3.2mV / ADC step
+ * Coil convertion is 1000mA -> 50mV : multily mV value by 20 to get mA value
+ * x * 3.2 * 20 == x * 32 * 2 == x << 6
+ * Increment is 64mA / ADC step */
+ snapval_solar = snapval_solar << 6;
+ snapval_home = snapval_home << 6;
+
+ /* Store value */
+ isnail_solar_values[idx] = snapval_solar;
+ isnail_home_values[idx++] = snapval_home;
+ if (idx == NB_VAL) {
+ idx = 0;
+ }
+
+ /* Compute average once we sampled enough values */
+ if ((idx == 0) || (idx == (NB_VAL / 2))) {
+ int i = 0;
+ moyenne_solar = 0;
+ moyenne_home = 0;
+ for (i = 0; i < NB_VAL; i++) {
+ moyenne_solar += isnail_solar_values[i];
+ moyenne_home += isnail_home_values[i];
+ }
+ moyenne_solar = moyenne_solar / NB_VAL;
+ moyenne_home = moyenne_home / NB_VAL;
+ }
+}
+
+/***************************************************************************** */
+/* AC control */
+
+volatile uint8_t command_val = 0;
+volatile int act_cmd = 100; /* Start off */
+int8_t force_cmd = -1; /* Forced command value, for tests */
+static int fan_on = 0;
+int force_fan = 0; /* Request to force fan ON from test menu */
+
+
+void set_ctrl_duty_cycle(uint8_t value)
+{
+ act_cmd = value;
+ /* Below 3% and above 97% there are triggering problems which
+ * lead to 50% instead of desired command */
+ if (act_cmd >= 97) {
+ act_cmd = 100;
+ } else if (act_cmd <= 2) {
+ act_cmd = 0;
+ }
+}
+
+void ac_switch_on(uint32_t flags)
+{
+ gpio_set(ac_ctrl);
+}
+
+static uint32_t clk_cycles_ac_zc = 0;
+volatile uint32_t zc_cnt = 0;
+void zero_cross(uint32_t gpio)
+{
+ uint32_t delay = 0;
+
+ zc_cnt++;
+ if (act_cmd == 100) {
+ gpio_set(ac_ctrl);
+ return;
+ }
+ gpio_clear(ac_ctrl);
+ if (act_cmd == 0) {
+ return;
+ }
+ /* Set timer to trigger ac out ON at given delay */
+ delay = clk_cycles_ac_zc * (100 - act_cmd);
+ timer_set_match(LPC_TIMER_32B1, CHAN0, delay);
+ timer_restart(LPC_TIMER_32B1);
+}
+
+/* This one is used to synchronize to the real zero-crossing detection, if any. */
+void zero_cross_detect(uint32_t gpio)
+{
+ timer_restart(LPC_TIMER_32B0);
+ zero_cross(0);
+}
+
+/* Handle the power command */
+#define CMD_UP_DELAY_RESET_VALUE 5
+void handle_cmd_update(uint32_t curent_tick)
+{
+ static uint8_t cmd = 0;
+ static uint8_t cmd_up_delay = CMD_UP_DELAY_RESET_VALUE;
+
+ /* Unable to read internal temperature : turn off heating */
+ if (error_shutdown == 1) {
+ cmd = 0;
+ goto cmd_update_end;
+ }
+
+ /* Water max temperature protection */
+ if (water_centi_degrees > (MAX_WATER_TEMP * 10)) {
+ cmd = 0;
+ goto cmd_update_end;
+ }
+
+ /* Forced test mode */
+ if (force_cmd != -1) {
+ cmd = force_cmd;
+ goto cmd_update_end;
+ }
+
+ /* Which is the current mode ? */
+ if (forced_heater_mode == 1) {
+ /* Forced heating mode */
+ if ((forced_heater_delay == 0) && (forced_heater_time > 0)) {
+ cmd = FORCED_MODE_VALUE;
+ }
+ /* Entering forced heating mode from temperature getting below threshold,
+ * wait some delay before effective forced heating */
+ if (forced_heater_time == 0) {
+ forced_heater_delay = FORCED_HEATER_DELAY;
+ forced_heater_time = FORCED_HEATER_DELAY + FORCED_HEATER_DURATION;
+ }
+ } else if (moyenne_solar < (moyenne_home - 64)) {
+ /* Low production mode */
+ if (cmd > 0) {
+ cmd--;
+ }
+ } else if (moyenne_solar > (moyenne_home + 1280)) {
+ if (cmd < 100) {
+ cmd++;
+ }
+ } else if (moyenne_solar > (moyenne_home + 192)) {
+ /* High production mode */
+ if (cmd_up_delay > 0) {
+ cmd_up_delay--;
+ } else {
+ cmd_up_delay = CMD_UP_DELAY_RESET_VALUE;
+ if (cmd < 100) {
+ cmd++;
+ }
+ }
+ }
+
+cmd_update_end:
+ command_val = cmd;
+ /* Set Control Output duty cycle */
+ set_ctrl_duty_cycle(100 - cmd);
+}
+
+
+/***************************************************************************** */
+void scialys_config(void)
+{
+ /* We want 100 Hz (50 Hz but two zero crossings) with 1% granularity */
+ clk_cycles_ac_zc = get_main_clock() / (100 * 100);
+ /* And configure the fake zero-cross generation timer to 100Hz */
+ timer_set_match(LPC_TIMER_32B0, CHAN0, (clk_cycles_ac_zc * 100));
+
+ /* Read parameters from memory */
+ if (1) {
+ never_force = 0;
+ sunny_days_prod_value_config = SUNNY_DAYS_PROD_VALUE;
+ }
+
+ /* Add a systick callback to handle command value update */
+ add_systick_callback(handle_cmd_update, CMD_UPD_PERIOD);
+
+ /* Add a systick callback to handle time counting */
+ add_systick_callback(handle_dec_request, DEC_PERIOD);
+}
+
+/* Add a false zero-cross, but make sure to do it only once and only if we are not AC powered */
+void DC_switch_start(void)
+{
+ static int already_done = 0;
+ if ((already_done == 0) && (zc_cnt == 0)) {
+ int ret = add_systick_callback(zero_cross, 10); /* 2 zero-crossing at 50Hz -> 100Hz -> 10ms */
+ uprintf(UART0, "Entering forced DC mode on systick callback %d\n", ret);
+ already_done = 1;
+ } else {
+ uprintf(UART0, "DC forced mode already ON: %d/%d\n", already_done, zc_cnt);
+ }
+}
+
+/***************************************************************************** */
+int main(void)
+{
+ uint32_t loop = 0;
+ char mode = heat; /* Debug info */
+
+ system_init();
+ board_io_config();
+ modules_config();
+
+ external_config(UART0);
+ scialys_config();
+
+ msleep(1500);
+ status_led(green_only);
+
+ while (1) {
+ uint16_t snapval_load = 0;
+
+ /* Default mode : try to heat the water tank */
+ mode = heat;
+
+ /* Always track power consumption and production */
+ track_isnail_values();
+
+ /* Get load power usage */
+ adc_get_value(&snapval_load, LPC_ADC(2));
+
+ /* Feed the dog */
+ if ((moyenne_solar != 0) && (moyenne_home != 0)) {
+ watchdog_feed();
+ }
+
+ /* Get internal temperature */
+ if (temp_read(UART0, &tmp101_deci_degrees) != 0) {
+ /* Set internal temperature to 60°C if there's a temp error, as this will trigger the
+ * internal fan */
+ tmp101_deci_degrees = DEFAULT_INTERNAL_TEMP;
+ }
+
+ /* Internal temperature protection */
+ if (tmp101_deci_degrees > (MAX_INTERNAL_TEMP)) {
+ error_shutdown = 1;
+ } else if (tmp101_deci_degrees < ((MAX_INTERNAL_TEMP) / 2)) {
+ error_shutdown = 0;
+ }
+ /* If internal temperature is above 32°C, then turn on fan. Turn off when back to under 28°C */
+ if ((tmp101_deci_degrees > FAN_ON_INTERNAL_TEMP) || (force_fan != 0)) {
+ fan_on = 1;
+ gpio_set(fan_ctrl);
+ } else if (tmp101_deci_degrees < FAN_OFF_INTERNAL_TEMP) {
+ fan_on = 0;
+ gpio_clear(fan_ctrl);
+ }
+
+ /* Get thermocouple value */
+ if (1) {
+ int ret = 0, old_water_temp = 0;
+ old_water_temp = water_centi_degrees;
+ ret = max31855_sensor_read(&thermocouple, NULL, &water_centi_degrees);
+ if (ret != 0) {
+ uprintf(UART0, "Water Temp read error : %d\n", ret);
+ /* Do not act upon iinvalid temp value */
+ water_centi_degrees = old_water_temp;
+ }
+ }
+
+ /* Need to enter Forced heating mode ? */
+ if (water_centi_degrees < (FORCE_HEATER_TEMP * 10)) {
+ if (forced_heater_mode == 0) {
+ uprintf(UART0, "Water temp low, entering forced mode\n");
+ forced_heater_mode = 1;
+ }
+ status_led(red_on);
+ mode = forced;
+ } else if ((water_centi_degrees > (TARGET_FORCED_HEATER_TEMP * 10)) && (forced_heater_mode == 1)) {
+ status_led(red_off);
+ uprintf(UART0, "Water temp OK, forced mode exit\n");
+ forced_heater_mode = 0;
+ mode = temp_OK;
+ }
+
+ /* Do not force if there is a lot of sun, it may be enough to heat again soon */
+ if (moyenne_solar > sunny_days_prod_value_config) {
+ mode = delayed_heat_prod;
+ forced_heater_mode = 0;
+ }
+
+ /* Do not force heating if disabled by external command */
+ external_disable = gpio_read(ext_disable_in_pin);
+ if ((external_disable == EXTERNAL_DISABLE_FORCE) && (forced_heater_mode != 0)) {
+ forced_heater_mode = 0;
+ mode = ext_disable;
+ }
+
+ if (never_force == 1) {
+ forced_heater_mode = 0;
+ }
+
+ /* Did the user request a forced heating ? */
+ if (manual_activation_request != 0) {
+ forced_heater_mode = 1;
+ mode = manual;
+ if (manual_activation_request == -1) {
+ uprintf(UART0, "Entering manual forced mode for %d ticks\n", manual_activation_request);
+ manual_activation_request = MANUAL_ACTIVATION_DURATION;
+ forced_heater_time = FORCED_HEATER_DURATION;
+ }
+ if (manual_activation_request < 10) {
+ uprintf(UART0, "Leaving manual forced mode\n");
+ manual_activation_request = 0;
+ }
+ }
+
+ /* Display */
+ interface_update(mode);
+
+ /* Debug */
+ if (1) {
+ uprintf(UART0, "%c:%d - Is: %d,%04d - Ih: %d,%04d\n", mode, loop++,
+ (moyenne_solar / 1000), (moyenne_solar % 1000),
+ (moyenne_home / 1000), (moyenne_home % 1000));
+ uprintf(UART0, "Water Temp : %d\n", water_centi_degrees);
+ uprintf(UART0, "Internal Temp : %d\n", tmp101_deci_degrees);
+ uprintf(UART0, "ADC: Sol: %dmA, Home: %dmA, Load: %d\n",
+ snapval_solar, snapval_home, snapval_load);
+ uprintf(UART0, "Zc: %d\n", zc_cnt);
+ uprintf(UART0, "CMD: %d/%d, Fan: %d/%d\n\n", command_val, (100 - act_cmd), fan_on, force_fan);
+ }
+
+ /* Wait approx 10ms between each loop */
+ msleep(10);
+ }
+ return 0;
+}
+
+
+
--- /dev/null
+/****************************************************************************
+ * apps/blyes/inter_lights/time.c
+ *
+ * Copyright 2018 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 = 0x18,
+ .month = 0x08,
+ .day = 0x09,
+ .hour = 0x13,
+ .min = 0x37,
+};
+int time_valid = 0;
+int rtc_conf_ok = 0;
+
+int 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 time_update(uint32_t uart, struct rtc_time* now)
+{
+ rtc_pcf85363_time_write(&rtc_conf, now);
+ return 0;
+}
+
+
+/***************************************************************************** */
+/* RTC init */
+void time_config(uint32_t i2c_bus_num, uint8_t rtc_addr)
+{
+ rtc_conf.bus_num = i2c_bus_num;
+ rtc_conf.addr = rtc_addr;
+}
+
--- /dev/null
+/****************************************************************************
+ * apps/blyes/inter_lights/time.h
+ *
+ * Copyright 2018 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 time_init_check(uint32_t uart);
+void time_config(uint32_t i2c_bus_num, uint8_t rtc_addr);
+
+#endif /* RTC_TIME_H */
+
--- /dev/null
+/****************************************************************************
+ * apps/blyes/inter_lights/uSD.c
+ *
+ * Copyright 2018 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/ssp.h"
+
+#include "lib/stdio.h"
+
+#include "uSD.h"
+
+
+/***************************************************************************** */
+/* SD/MMC Card */
+struct sdmmc_card micro_sd = {
+ .card_type = MMC_CARDTYPE_UNKNOWN,
+ .block_size = 64,
+};
+
+static uint8_t got_uSD = 0;
+
+/* Buffer to read / write uSD card */
+uint8_t mmc_data[MMC_BUF_SIZE];
+
+/* Read up to 4 blocks of 16 bytes each */
+int uSD_read(uint32_t offset, uint8_t nb_blocks)
+{
+ int i = 0, ret = 0;
+ if (got_uSD == 0) {
+ return -1;
+ }
+ if (nb_blocks > 4) {
+ return -2;
+ }
+ memset(mmc_data, 0, MMC_BUF_SIZE);
+ for (i = 0; i < nb_blocks; i++) {
+ ret = sdmmc_read_block(µ_sd, (offset + i), (mmc_data + (i * 16)));
+ /* FIXME : check return value */
+ }
+ return 0;
+}
+
+
+/* Write up to 4 blocks of 16 bytes each */
+int uSD_write(uint32_t offset, uint8_t nb_blocks)
+{
+ int i = 0, ret = 0;
+ if (got_uSD == 0) {
+ return -1;
+ }
+ if (nb_blocks > 4) {
+ return -1;
+ }
+ for (i = 0; i < nb_blocks; i++) {
+ ret = sdmmc_write_block(µ_sd, (offset + i), (mmc_data + (i * 16)));
+ /* FIXME : check return value */
+ }
+ return 0;
+}
+
+/* microSD card init */
+int uSD_detect(int uart)
+{
+ int i = 0, ret = 0, step = 0;
+ do {
+ step = 0;
+ ret = sdmmc_init(µ_sd);
+ if (ret == 0) {
+ step = 1;
+ msleep(10);
+ ret = sdmmc_init_wait_card_ready(µ_sd);
+ if (ret <= 1) {
+ step = 2;
+ ret = sdmmc_init_end(µ_sd);
+ }
+ }
+ uprintf(uart, "uSD init(%d): step:%d, ret: %d, type: %d, bs: %d\n",
+ i, step, ret, micro_sd.card_type, micro_sd.block_size);
+ i++;
+ } while (((ret != 0) || (micro_sd.card_type == MMC_CARDTYPE_UNKNOWN)) && (i < 10));
+
+ /* Got uSD ? */
+ if (i >= 10) {
+ uprintf(uart, "uSD init failed, no uSD card present.\n");
+ got_uSD = 0;
+ return -1;
+ }
+
+ /* uSD card detected */
+ got_uSD = 1;
+ /* got_uSD MUST be set to 1 from here on ! */
+ uSD_read(0, 4); /* Read 4 blocks at start of card */
+ /* FIXME : check that the card magic is present */
+ uprintf(uart, "uSD read: %s\n", mmc_data);
+
+ return 0;
+}
+
+
+int uSD_logs_init(int uart)
+{
+ /* FIXME : read 4 blocks at offset FIXME to get the last block used info */
+ return 0;
+}
+
+/***************************************************************************** */
+/* microSD card init */
+void uSD_config(struct pio* uSD_cs, uint32_t ssp_bus_num)
+{
+ memcpy(&(micro_sd.chip_select), uSD_cs, sizeof(struct pio));
+ micro_sd.ssp_bus_num = ssp_bus_num;
+}
+
--- /dev/null
+/****************************************************************************
+ * apps/blyes/inter_lights/usSD.h
+ *
+ * Copyright 2018 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 USD_H
+#define USD_H
+
+#include "extdrv/sdmmc.h"
+
+#define MMC_BUF_SIZE 64
+
+extern struct sdmmc_card micro_sd;
+extern uint8_t mmc_data[MMC_BUF_SIZE];
+
+int uSD_read(uint32_t offset, uint8_t nb_blocks);
+int uSD_write(uint32_t offset, uint8_t nb_blocks);
+
+/***************************************************************************** */
+/* microSD card init */
+int uSD_detect(int uart);
+void uSD_config(struct pio* uSD_cs, uint32_t ssp_bus_num);
+
+
+#endif /* USD_H */
+
--- /dev/null
+# 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} $@
+
--- /dev/null
+/****************************************************************************
+ * apps/scialys/v07/config.c
+ *
+ * Copyright 2018 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 "drivers/adc.h"
+#include "drivers/ssp.h"
+#include "drivers/timers.h"
+
+
+#include "extdrv/status_led.h"
+#include "extdrv/max31855_thermocouple.h"
+
+#include "lib/stdio.h"
+
+#include "config.h"
+#include "interface.h"
+#include "time.h"
+#include "uSD.h"
+
+#define SELECTED_FREQ FREQ_SEL_48MHz
+
+
+uint8_t config_got_interface = 1;
+
+/***************************************************************************** */
+/* Pins configuration */
+/* pins blocks are passed to set_pins() for pins configuration.
+ * Unused pin blocks can be removed safely with the corresponding set_pins() call
+ * All pins blocks may be safelly merged in a single block for single set_pins() call..
+ */
+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) },
+ /* SPI */
+ { LPC_SSP0_SCLK_PIO_0_14, LPC_IO_DIGITAL },
+ { LPC_SSP0_MOSI_PIO_0_17, LPC_IO_DIGITAL },
+ { LPC_SSP0_MISO_PIO_0_16, LPC_IO_DIGITAL },
+ /* ADC */
+ { LPC_ADC_AD0_PIO_0_30, LPC_IO_ANALOG }, /* Home */
+ { LPC_ADC_AD1_PIO_0_31, LPC_IO_ANALOG }, /* Production */
+ { LPC_ADC_AD2_PIO_1_0, LPC_IO_ANALOG }, /* Load */
+ /* GPIO */
+ { LPC_GPIO_0_3, LPC_IO_DIGITAL }, /* External switch input */
+ { LPC_GPIO_0_4, LPC_IO_DIGITAL }, /* Zero crossing detection input */
+ { LPC_GPIO_0_5, LPC_IO_DIGITAL }, /* Fan control */
+ { LPC_GPIO_0_6, LPC_IO_DIGITAL }, /* Mosfet control */
+ { LPC_GPIO_0_7, LPC_IO_DIGITAL }, /* Thermocouple chip select */
+ { LPC_GPIO_0_12, LPC_IO_DIGITAL }, /* ISP / User button OK */
+ { LPC_GPIO_0_18, LPC_IO_DIGITAL }, /* Uext Chip select / Module eeprom select */
+ { LPC_GPIO_0_20, LPC_IO_DIGITAL }, /* RGB Leds */
+ { LPC_GPIO_0_21, LPC_IO_DIGITAL }, /* Oled Reset */
+ { LPC_GPIO_1_1, LPC_IO_DIGITAL }, /* uSD Card SPI Chip Select */
+ { 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;
+
+/* Inputs */
+/* External signals */
+const struct pio zero_cross_in_pin = LPC_GPIO_0_4;
+const struct pio ext_disable_in_pin = LPC_GPIO_0_3;
+
+/* Outputs */
+/* AC output control (Mosfet) */
+const struct pio ac_ctrl = LPC_GPIO_0_6;
+const struct pio fan_ctrl = LPC_GPIO_0_5;
+/* Chip selects */
+const struct pio uSD_cs = LPC_GPIO_1_1; /* uSD card */
+const struct pio th_cs = LPC_GPIO_0_7; /* Thermocouple */
+const struct pio uext_cs = LPC_GPIO_0_18; /* UEXT module (optionnal) */
+
+
+
+/***************************************************************************** */
+/* 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);
+}
+
+
+/***************************************************************************** */
+/* Internal modules */
+const struct lpc_tc_config ac_timer_conf_zc = {
+ .mode = LPC_TIMER_MODE_TIMER | LPC_TIMER_MODE_MATCH,
+ .match_control = { LPC_TIMER_INT_RESET_ON_MATCH, 0, 0, 0, },
+};
+const struct lpc_tc_config ac_timer_conf_delay = {
+ .mode = LPC_TIMER_MODE_TIMER | LPC_TIMER_MODE_MATCH,
+ .match_control = { LPC_TIMER_INT_RESET_AND_STOP_ON_MATCH, 0, 0, 0, },
+};
+const struct lpc_tc_config ac_timer_conf_power_track = {
+ .mode = LPC_TIMER_MODE_TIMER | LPC_TIMER_MODE_MATCH,
+ .match_control = { LPC_TIMER_INT_RESET_ON_MATCH, 0, 0, 0, },
+};
+
+extern void config_rx(uint8_t c);
+extern void comm_rx(uint8_t c);
+extern void ac_switch_on(uint32_t flags);
+extern void zero_cross(uint32_t flags);
+extern void power_track(uint32_t flags);
+
+void modules_config(void)
+{
+ uart_on(UART0, 115200, config_rx);
+ uart_on(UART1, 115200, comm_rx);
+ i2c_on(I2C0, I2C_CLK_100KHz, I2C_MASTER);
+ ssp_master_on(SSP_BUS_0, LPC_SSP_FRAME_SPI, 8, 4*1000*1000);
+ adc_on(NULL);
+ adc_start_burst_conversion(ADC_MCH(0) | ADC_MCH(1) | ADC_MCH(2), LPC_ADC_SEQ(0));
+ timer_on(LPC_TIMER_32B0, 0, zero_cross);
+ timer_counter_config(LPC_TIMER_32B0, &ac_timer_conf_zc);
+ timer_on(LPC_TIMER_32B1, 0, ac_switch_on);
+ timer_counter_config(LPC_TIMER_32B1, &ac_timer_conf_delay);
+ timer_on(LPC_TIMER_16B0, 0, power_track);
+ timer_counter_config(LPC_TIMER_16B0, &ac_timer_conf_power_track);
+}
+
+
+/***************************************************************************** */
+extern void zero_cross_detect(uint32_t gpio);
+void board_io_config(void)
+{
+ /* Immediatly turn off Mosfet / Triac */
+ config_gpio(&ac_ctrl, 0, GPIO_DIR_OUT, 1);
+ /* Start with FAN ON */
+ config_gpio(&fan_ctrl, 0, GPIO_DIR_OUT, 1);
+
+ /* Configure Input GPIO */
+ config_gpio(&ext_disable_in_pin, 0, GPIO_DIR_IN, 0);
+
+ /* Zero-crossing detection */
+ set_gpio_callback(zero_cross_detect, &zero_cross_in_pin, EDGE_FALLING);
+}
+
+
+
+/***************************************************************************** */
+/* Configure external components */
+
+/* Thermocouple reading */
+const struct max31855_sensor_config thermocouple = {
+ .ssp_bus_num = 0,
+ .chip_select = LPC_GPIO_0_7,
+};
+
+/* RTC and time */
+#define RTC_ADDR 0xA2
+
+
+int external_config(uint32_t uart)
+{
+ int ret = 0;
+
+ /* Configure GPIO for specific board usage */
+ board_io_config();
+
+ /* uSD card */
+ uSD_config((struct pio *)&uSD_cs, SSP_BUS_0);
+ uSD_detect(uart);
+
+ /* RTC */
+ time_config(I2C0, RTC_ADDR);
+ time_init_check(uart);
+
+ /* Thermocouple configuration */
+ max31855_sensor_config(&thermocouple);
+
+ /* Configure interface board */
+ ret = interface_config(uart);
+ if (ret != 0) {
+ return -1;
+ }
+ return 0;
+}
--- /dev/null
+/****************************************************************************
+ * apps/scialys/v07/config.h
+ *
+ * Copyright 2018 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 "drivers/adc.h"
+#include "drivers/ssp.h"
+#include "drivers/timers.h"
+
+#include "extdrv/status_led.h"
+#include "extdrv/max31855_thermocouple.h"
+#include "extdrv/ws2812.h"
+
+#include "lib/stdio.h"
+
+
+#define SELECTED_FREQ FREQ_SEL_48MHz
+
+/***************************************************************************** */
+/* Pins configuration */
+extern const struct pio status_led_green;
+extern const struct pio status_led_red;
+
+/* Inputs */
+/* External signals */
+extern const struct pio zero_cross_in_pin;
+extern const struct pio ext_disable_in_pin;
+
+/* Outputs */
+/* AC output control (Mosfet) */
+extern const struct pio ac_ctrl;
+extern const struct pio fan_ctrl;
+/* Chip selects */
+extern const struct pio uSD_cs; /* uSD card */
+extern const struct pio th_cs; /* Thermocouple */
+extern const struct pio uext_cs; /* UEXT module (optionnal) */
+
+
+
+/***************************************************************************** */
+extern const struct max31855_sensor_config thermocouple;
+
+
+/***************************************************************************** */
+/* 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 modules and main functions */
+void modules_config(void);
+
+/* Configure GPIO for specific board usage */
+void board_io_config(void);
+
+/* Configure external components */
+int external_config(uint32_t uart);
+
+
+#endif /* CONFIG_H */
+
--- /dev/null
+/****************************************************************************
+ * apps/scialys/v07/interface.c
+ *
+ * Copyright 2018 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 "drivers/adc.h"
+
+#include "extdrv/status_led.h"
+#include "extdrv/ws2812.h"
+#include "extdrv/tmp101_temp_sensor.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 "interface.h"
+#include "time.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;
+
+
+static uint8_t interface_board_present = 0;
+static uint8_t power_board_present = 0;
+
+/***************************************************************************** */
+/* TMP101 I2C temperature sensor on Power board */
+#define TMP101_ADDR1 0x94 /* Pin Addr0 (pin5 of tmp101) connected to VCC */
+struct tmp101_sensor_config tmp101_sensor_power = {
+ .bus_num = I2C0,
+ .addr = TMP101_ADDR1,
+ .resolution = TMP_RES_ELEVEN_BITS,
+};
+
+/* TMP101 I2C temperature sensor on display board */
+#define TMP101_ADDR0 0x90 /* Pin Addr0 (pin5 of tmp101) connected to GND */
+struct tmp101_sensor_config tmp101_sensor_display = {
+ .bus_num = I2C0,
+ .addr = TMP101_ADDR0,
+ .resolution = TMP_RES_ELEVEN_BITS,
+};
+
+/***************************************************************************** */
+/* 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;
+
+ if (interface_board_present == 0) {
+ return -1;
+ }
+
+ 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;
+
+ if (interface_board_present == 0) {
+ return -1;
+ }
+
+ 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);
+}
+
+
+int temp_read(uint32_t uart, int* deci_degrees_disp, int* deci_degrees_power)
+{
+ int ret = 0;
+ int conv = 0;
+
+ if (interface_board_present != 0) {
+ ret = tmp101_sensor_read(&tmp101_sensor_display, NULL, deci_degrees_disp);
+ if (ret == 0) {
+ conv |= CONV_DISPLAY_OK;
+ } else {
+ uprintf(uart, "TMP101 read error on display board : %d\n", ret);
+ }
+ }
+ if (power_board_present != 0) {
+ ret = tmp101_sensor_read(&tmp101_sensor_power, NULL, deci_degrees_power);
+ if (ret == 0) {
+ conv |= CONV_POWER_OK;
+ } else {
+ uprintf(uart, "TMP101 read error on power board : %d\n", ret);
+ }
+ }
+ return conv;
+}
+
+
+/***************************************************************************** */
+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;
+ }
+}
+
+int interface_config(uint32_t uart)
+{
+ int ret = 0;
+
+ /* TMP101 sensor config on power board */
+ power_board_present = 1;
+ ret = tmp101_sensor_config(&tmp101_sensor_power);
+ if (ret != 0) {
+ uprintf(uart, "Temp config error on power board: %d\n", ret);
+ power_board_present = 0;
+ } else {
+ ret = tmp101_sensor_set_continuous_conversion(&tmp101_sensor_power);
+ if (ret != 0) {
+ uprintf(uart, "Temp config error on power board: %d\n", ret);
+ power_board_present = 0;
+ }
+ }
+
+ /* TMP101 sensor config on display board */
+ ret = tmp101_sensor_config(&tmp101_sensor_display);
+ if (ret != 0) {
+ uprintf(uart, "Temp config error on display board: %d\n", ret);
+ interface_board_present = 0;
+ return -1;
+ }
+ ret = tmp101_sensor_set_continuous_conversion(&tmp101_sensor_display);
+ if (ret != 0) {
+ uprintf(uart, "Temp config error on display board: %d\n", ret);
+ interface_board_present = 0;
+ return -2;
+ }
+ interface_board_present = 1;
+
+ /* Buttons inputs on front panel */
+ /* Activate on Rising edge (button release) */
+ set_gpio_callback(button_callback, &button_up, EDGE_RISING);
+ set_gpio_callback(button_callback, &button_left, EDGE_RISING);
+ set_gpio_callback(button_callback, &button_right, EDGE_RISING);
+ set_gpio_callback(button_callback, &button_down, EDGE_RISING);
+ set_gpio_callback(button_callback, &button_ok, EDGE_RISING);
+
+ /* WS2812B Leds on display board */
+ ws2812_config(&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(0, 0x05, 0x15, 0x08);
+ ws2812_send_frame(0);
+
+ uprintf(uart, "Config OK\n");
+
+ return 0;
+}
+
+
+
+/***************************************************************************** */
+ /* Menu part */
+
+enum interface_modes {
+ MODE_RUN = 0,
+ MODE_CONFIG,
+ MODE_DISPLAY,
+};
+static int interface_mode = MODE_RUN;
+volatile int manual_activation_request = 0;
+
+enum menu_list {
+ MAIN_MENU,
+ MANUAL_MODE,
+ DATE_CONFIG,
+ LIMITS,
+ FUNCTIONS,
+ TEST_MODE,
+ SAVE_CONFIG,
+ NB_MENU, /* This one must be the last */
+};
+static const char* menu_titles[] = {
+ [MAIN_MENU] = "Menu principal",
+ [MANUAL_MODE] = "Marche Forcee",
+ [DATE_CONFIG] = "Regl. Heure",
+ [LIMITS] = "Limites",
+ [FUNCTIONS] = "Fonctions",
+ [TEST_MODE] = "Test mode",
+ [SAVE_CONFIG] = "Save config",
+};
+static uint8_t current_menu = MAIN_MENU;
+static uint8_t current_entry = MANUAL_MODE;
+
+enum func_menu_list {
+ FUNC_LOAD_TYPE,
+ FUNC_CMD_TYPE,
+ FUNC_NB_MENU, /* This one must be the last */
+};
+static const char* func_modes_titles[] = {
+ [FUNC_LOAD_TYPE] = "Load Type",
+ [FUNC_CMD_TYPE] = "CMD type",
+};
+static uint8_t func_cur_menu = FUNC_LOAD_TYPE;
+static uint8_t func_cur_entry = FUNC_LOAD_TYPE;
+
+enum limits_menu_list {
+ TEMP_FORCED_IN,
+ TEMP_FORCED_TARGET,
+ FORCED_MODE_VAL,
+ SUNNY_DAY_VALUE,
+ LIM_NB_MENU, /* This one must be the last */
+};
+static const char* limits_modes_titles[] = {
+ [TEMP_FORCED_IN] = "Water Min",
+ [TEMP_FORCED_TARGET] = "Water Target",
+ [FORCED_MODE_VAL] = "Forced cmd",
+ [SUNNY_DAY_VALUE] = "Sun Prod",
+};
+static uint8_t limits_cur_menu = TEMP_FORCED_IN;
+static uint8_t limits_cur_entry = TEMP_FORCED_IN;
+
+enum test_menu_list {
+ TEST_FAN,
+ TEST_12V,
+ TEST_CMDV,
+ TEST_NB_MENU, /* This one must be the last */
+};
+static const char* test_modes_titles[] = {
+ [TEST_FAN] = "Fan",
+ [TEST_12V] = "DC 12V",
+ [TEST_CMDV] = "CMD Val",
+};
+extern uint8_t force_fan;
+extern int8_t force_cmd;
+extern uint32_t zc_cnt;
+extern void zero_cross_detect(uint32_t gpio);
+static uint8_t test_cur_entry = TEST_FAN;
+
+
+/* Current sub-menu level. main menu is level 0 */
+int sub_menu_level = 0;
+
+
+void config_interface_handle(void)
+{
+ char line[DISP_LLEN];
+ 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;
+ for (i = 1; i < NB_MENU; i++) {
+ if (i != current_entry) {
+ display_line(i + 1, 3, menu_titles[i]);
+ } else {
+ snprintf(line, DISP_LLEN, " ->%s", menu_titles[i]);
+ display_line(i + 1, 0, line);
+ }
+ }
+ 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) {
+ current_entry = NB_MENU - 1;
+ }
+ if (button & (BUTTON_OK | BUTTON_RIGHT)) {
+ current_menu = current_entry;
+ sub_menu_level = 1;
+ }
+ if (button & BUTTON_LEFT) {
+ interface_mode = MODE_RUN;
+ }
+ }
+ break;
+ case DATE_CONFIG: {
+ static uint8_t date_idx = 0;
+ struct rtc_time now;
+ snprintf(line, DISP_LLEN, "^");
+ display_line(3, 6 + (date_idx * 3), line);
+ rtc_pcf85363_time_read(&rtc_conf, &now);
+ snprintf(line, DISP_LLEN, "%02xh%02x:%02x", now.hour, now.min, now.sec);
+ display_line(4, 6, line);
+ snprintf(line, DISP_LLEN, "V");
+ display_line(5, 6 + (date_idx * 3), line);
+ if ((button & BUTTON_RIGHT) && (date_idx < 2)) {
+ date_idx++;
+ }
+ if ((button & BUTTON_LEFT) && (date_idx > 0)) {
+ date_idx--;
+ }
+ if (button & (BUTTON_UP | BUTTON_DOWN)) {
+ uint32_t seconds = rtc_pcf85363_daytime_to_seconds(&now);
+ uint32_t add[6] = {
+ 3600, 60, 1,
+ (23 * 3600), (23* 3600 + 59 * 60), (23 * 3600 + 59 * 60 + 59),
+ };
+ if (button & BUTTON_UP) {
+ if (seconds < add[date_idx + 3]) {
+ seconds += add[date_idx];
+ } else {
+ seconds -= add[date_idx + 3];
+ }
+ } else {
+ if (seconds < add[date_idx]) {
+ seconds += add[date_idx + 3];
+ } else {
+ seconds -= add[date_idx];
+ }
+ }
+ rtc_pcf85363_seconds_to_daytime(&now, seconds);
+ rtc_pcf85363_time_write(&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 {
+ snprintf(line, DISP_LLEN, " ->%s", limits_modes_titles[i]);
+ display_line(i + 2, 0, line);
+ }
+ }
+ if (button & BUTTON_UP) {
+ limits_cur_entry -= 1;
+ }
+ if (button & BUTTON_DOWN) {
+ limits_cur_entry += 1;
+ }
+ if (limits_cur_entry >= LIM_NB_MENU) {
+ limits_cur_entry = 0;
+ } else if (limits_cur_entry == 0xFF) {
+ limits_cur_entry = LIM_NB_MENU - 1;
+ }
+ 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 {
+ }
+ }
+ break;
+ case FUNCTIONS: {
+ if (sub_menu_level == 1) {
+ int i = 0;
+ for (i = 0; i < FUNC_NB_MENU; i++) {
+ if (i != func_cur_entry) {
+ display_line(i + 2, 3, func_modes_titles[i]);
+ } else {
+ snprintf(line, DISP_LLEN, " ->%s", func_modes_titles[i]);
+ display_line(i + 2, 0, line);
+ }
+ }
+ if (button & BUTTON_UP) {
+ func_cur_entry -= 1;
+ }
+ if (button & BUTTON_DOWN) {
+ func_cur_entry += 1;
+ }
+ if (func_cur_entry >= FUNC_NB_MENU) {
+ func_cur_entry = 0;
+ } else if (func_cur_entry == 0xFF) {
+ func_cur_entry = FUNC_NB_MENU - 1;
+ }
+ if (button & (BUTTON_OK | BUTTON_RIGHT)) {
+ func_cur_menu = func_cur_entry;
+ sub_menu_level = 2;
+ }
+ if (button & BUTTON_LEFT) {
+ sub_menu_level = 0;
+ current_menu = MAIN_MENU;
+ }
+ } else {
+ }
+ }
+ break;
+ case TEST_MODE: {
+ int i = 0;
+ uint8_t states[3];
+
+ states[TEST_FAN] = force_fan;
+ states[TEST_12V] = (zc_cnt != 0) ? 1 : 0;
+ states[TEST_CMDV] = force_cmd;
+
+ for (i = 0; i < TEST_NB_MENU; i++) {
+ if (i != test_cur_entry) {
+ display_line(i + 2, 3, test_modes_titles[i]);
+ } else {
+ snprintf(line, DISP_LLEN, " ->%s : %d", test_modes_titles[i], states[i]);
+ display_line(i + 2, 0, line);
+ }
+ }
+ if (button & BUTTON_UP) {
+ test_cur_entry -= 1;
+ }
+ if (button & BUTTON_DOWN) {
+ test_cur_entry += 1;
+ }
+ if (test_cur_entry >= TEST_NB_MENU) {
+ test_cur_entry = 0;
+ } else if (test_cur_entry == 0xFF) {
+ test_cur_entry = TEST_NB_MENU - 1;
+ }
+ if (button & (BUTTON_LEFT | BUTTON_RIGHT)) {
+ switch (test_cur_entry) {
+ case TEST_FAN:
+ force_fan = !force_fan;
+ break;
+ case TEST_12V:
+ /* Generate a fake zero-cross detect by calling the handler directly */
+ zero_cross_detect(0);
+ break;
+ case TEST_CMDV:
+ if (force_cmd >= 0) {
+ force_cmd = -1;
+ } else {
+ force_cmd = 0;
+ }
+ default:
+ break;
+ }
+ }
+ if (button & BUTTON_OK) {
+ sub_menu_level = 0;
+ current_menu = MAIN_MENU;
+ }
+ }
+ break;
+ case SAVE_CONFIG: {
+ }
+ break;
+ case MANUAL_MODE:
+ manual_activation_request = -1;
+ /* Fallback */
+ default:
+ interface_mode = MODE_RUN;
+ current_menu = MAIN_MENU;
+ break;
+ }
+ /* Update Oled display */
+ ssd130x_display_full_screen(&display);
+}
+
+
+/***************************************************************************** */
+ /* usual menu and switch to config menu */
+
+extern int water_centi_degrees;
+extern uint32_t solar_prod_value;
+extern uint32_t home_conso_value;
+extern volatile uint8_t command_val;
+
+void interface_update(char heat_mode)
+{
+ int abs_centi = water_centi_degrees;
+
+ if (water_centi_degrees < 0) {
+ abs_centi = -water_centi_degrees;
+ }
+
+ /* Debug */
+ if (button_pressed != 0) {
+ uprintf(UART0, "Button : 0x%02x\n", button_pressed);
+ }
+
+ if (interface_mode == MODE_RUN) {
+ if (button_pressed != 0) {
+ if (button_pressed & BUTTON_OK) {
+ manual_activation_request = -1;
+ }
+ if (button_pressed & BUTTON_UP) {
+ interface_mode = MODE_CONFIG;
+ }
+ if (force_cmd >= 0) {
+ if ((button_pressed & BUTTON_RIGHT) && (force_cmd <= 95)) {
+ force_cmd += 5;
+ }
+ if ((button_pressed & BUTTON_LEFT) && (force_cmd >= 5)) {
+ force_cmd -= 5;
+ }
+ }
+ button_pressed = 0;
+ }
+
+ if (interface_board_present != 0) {
+ char line[DISP_LLEN];
+
+ /* Start with a blank screen */
+ erase_screen_content();
+
+ /* Update time and time display on internal memory */
+ rtc_pcf85363_time_read(&rtc_conf, &now);
+ snprintf(line, DISP_LLEN, "%02xh%02x:%02x", now.hour, now.min, now.sec);
+ display_line(0, 0, line);
+
+ /* Display info */
+ snprintf(line, DISP_LLEN, "Water:% 2d.%03d %cC", (water_centi_degrees / 100), (abs_centi % 100), 0x1F);
+ display_line(2, 0, line);
+ snprintf(line, DISP_LLEN, "Prod :% 2d,%03dA", (solar_prod_value / 1000), ((solar_prod_value % 1000) / 10));
+ display_line(3, 0, line);
+ snprintf(line, DISP_LLEN, "Conso:% 2d,%03dA", (home_conso_value / 1000), ((home_conso_value % 1000) / 10));
+ display_line(4, 0, line);
+ snprintf(line, DISP_LLEN, "Command: %d%%", command_val);
+ display_line(5, 0, line);
+ snprintf(line, DISP_LLEN, "Mode: %c", heat_mode);
+ display_line(6, 0, line);
+ snprintf(line, DISP_LLEN, "v0.8 - 0.3.3");
+ display_line(7, 0, line);
+ /* Update Oled display */
+ ssd130x_display_full_screen(&display);
+
+ /* Update RGB leds */
+ /* FIXME : use for error signal */
+ ws2812_set_pixel(0, (home_conso_value / 2000), (solar_prod_value / 2000), 0);
+ ws2812_set_pixel(1, 0, 0, command_val);
+ ws2812_send_frame(0);
+ }
+ } else {
+ /* Config mode. Mode entered by button action, which implies that interface board is present. */
+ config_interface_handle();
+ }
+}
+
+
--- /dev/null
+/****************************************************************************
+ * apps/blyes/relay_lights/interface.h
+ *
+ * Copyright 2018 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;
+
+extern volatile int manual_activation_request;
+
+/* 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);
+
+
+/* Internal Temperature sensors */
+#define CONV_DISPLAY_OK (0x01 << 0)
+#define CONV_POWER_OK (0x01 << 1)
+#define CONV_OK (CONV_POWER_OK | CONV_DISPLAY_OK)
+int temp_read(uint32_t uart, int* deci_degrees_disp, int* deci_degrees_power);
+
+/* Interface content update */
+void interface_update(char mode);
+
+#endif /* INTERFACE_H */
+
--- /dev/null
+/****************************************************************************
+ * apps/scialys/v07/main.c
+ *
+ * Scialys system for solar-panel power generation tracking and fair use.
+ *
+ * Copyright 2016 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 "uSD.h"
+
+
+#define MODULE_VERSION 0x08
+#define MODULE_NAME "Scialys uC"
+
+
+
+/***************************************************************************** */
+/* System configuration */
+
+/* Period (in ms) of the decrementer handler from the systick interrupt */
+#define DEC_PERIOD 100
+/* Period (in ms) of the handler for the command value update */
+#define CMD_UPD_PERIOD 100
+
+/* NOTE : All temperatures are stored as decidegrees */
+/* Max water temperature. Internal protection which cannot be overriden by configuration */
+#define MAX_WATER_TEMP 900
+/* Internal system max temperature : turn off heating when reached */
+#define MAX_INTERNAL_TEMP 800 /* FIXME */
+
+#define DEFAULT_INTERNAL_TEMP 600
+#define FAN_ON_INTERNAL_TEMP_POWER 400
+#define FAN_OFF_INTERNAL_TEMP_POWER 350
+#define FAN_ON_INTERNAL_TEMP 380
+#define FAN_OFF_INTERNAL_TEMP 320
+
+/* Most of the defines in here should go to configuration setting in user flash */
+/* If temperature falls bellow FORCE_HEATER_TEMP value, we enter forced heater mode, until
+ * TARGET_FORCED_HEATER_TEMP is reached.
+ * When in forced heater mode, the heater is controlled to heat at FORCED_MODE_VALUE which
+ * is between 0 and 100.
+ */
+#define FORCE_HEATER_TEMP 300
+#define TARGET_FORCED_HEATER_TEMP 500
+#define FORCED_MODE_VALUE 75 /* A fraction of 100 */
+/* mA prod value above which the system will not enter forced mode, waiting for home
+ * to stop using power to start automatic heating */
+#define SUNNY_DAYS_PROD_VALUE 3000
+
+/* Delay before automatic forced heating */
+#define FORCED_HEATER_DELAY (2 * 3600 * 1000 / DEC_PERIOD)
+/* Duration of automatic forced heating */
+#define FORCED_HEATER_DURATION (3 * 3600 * 1000 / DEC_PERIOD)
+
+/* Duration of manual forced heating */
+#define MANUAL_ACTIVATION_DURATION (3 * 3600 * 1000 / DEC_PERIOD) /* Three hours */
+
+
+uint32_t forced_heater_mode = 0;
+uint32_t forced_heater_delay = 0;
+uint32_t forced_heater_time = 0;
+uint8_t manual_forced_heater = 0;
+
+uint8_t error_shutdown = 0;
+
+uint8_t never_force = 0;
+uint32_t sunny_days_prod_value_config = 0;
+
+#define EXTERNAL_DISABLE_FORCE 0 /* Input is pulled low when external disable is ON */
+int external_disable = 0;
+
+enum modes {
+ heat = 'C', /* Normal heating */
+ ext_disable = 'E', /* Forced heating disabled by external input */
+ forced = 'F', /* Forced heating */
+ manual = 'M',
+ delayed_heat_prod = 'P', /* Pause */
+ overprod = 'S', /* Over production, try to start other loads */
+ temp_OK = 'T', /* Max temperature reached */
+};
+
+/* Water and internaltemperature */
+int water_centi_degrees = 0;
+int deci_degrees_power = 0;
+int deci_degrees_disp = 0;
+
+/* RTC and time */
+struct rtc_time now;
+
+
+/***************************************************************************** */
+/* Rx interrupt handler for system configuration over USB */
+void config_rx(uint8_t c)
+{
+ /* FAN control */
+ if (c == 'f') {
+ gpio_set(fan_ctrl);
+ } else {
+ gpio_clear(fan_ctrl);
+ }
+}
+
+
+/* Communication with slave modules */
+void comm_rx(uint8_t c)
+{
+}
+
+
+
+/***************************************************************************** */
+/* System communication over UART1 */
+void cmd_rx(uint8_t c)
+{
+}
+
+
+/***************************************************************************** */
+/* Decrementer for heating timers */
+void handle_dec_request(uint32_t curent_tick) {
+ if (manual_activation_request > 0) {
+ manual_activation_request--;
+ }
+ if (forced_heater_mode == 1) {
+ if (forced_heater_delay > 0) {
+ forced_heater_delay--;
+ }
+ if (forced_heater_time > 0) {
+ forced_heater_time--;
+ }
+ }
+}
+
+
+/***************************************************************************** */
+/* Track power production and usage */
+
+/* Average value computed on last 10 ADC values */
+uint32_t solar_prod_value = 0;
+uint32_t home_conso_value = 0;
+
+static void track_isnail_values(void)
+{
+ uint16_t snapval_solar = 0;
+ uint16_t snapval_home = 0;
+ /* Get new values */
+ adc_get_value(&snapval_solar, LPC_ADC(1));
+ adc_get_value(&snapval_home, LPC_ADC(0));
+
+ /* Convert to mA value.
+ * ADC range is 0 to 1024 - approx 3.2mV / ADC step
+ * Coil convertion is 1000mA -> 50mV : multily mV value by 20 to get mA value
+ * x * 3.2 * 20 == x * 32 * 2 == x << 6
+ * Increment is 64mA / ADC step */
+ solar_prod_value = snapval_solar << 6;
+ home_conso_value = snapval_home << 6;
+}
+
+/***************************************************************************** */
+/* AC control */
+
+volatile uint8_t command_val = 0;
+volatile int act_cmd = 100; /* Start off */
+int8_t force_cmd = -1; /* Forced command value, for tests */
+static int fan_on = 0;
+int force_fan = 0; /* Request to force fan ON from test menu */
+
+
+void set_ctrl_duty_cycle(uint8_t value)
+{
+ act_cmd = value;
+ /* Below 3% and above 97% there are triggering problems which
+ * lead to 50% instead of desired command */
+ if (act_cmd >= 97) {
+ act_cmd = 100;
+ } else if (act_cmd <= 2) {
+ act_cmd = 0;
+ }
+}
+
+void ac_switch_on(uint32_t flags)
+{
+ gpio_set(ac_ctrl);
+}
+
+static uint32_t clk_cycles_ac_zc = 0;
+volatile uint32_t zc_cnt = 0;
+void zero_cross(uint32_t gpio)
+{
+ uint32_t delay = 0;
+
+ zc_cnt++;
+ if (act_cmd == 100) {
+ gpio_set(ac_ctrl);
+ return;
+ }
+ gpio_clear(ac_ctrl);
+ if (act_cmd == 0) {
+ return;
+ }
+ /* Set timer to trigger ac out ON at given delay */
+ delay = clk_cycles_ac_zc * (100 - act_cmd);
+ timer_set_match(LPC_TIMER_32B1, CHAN0, delay);
+ timer_restart(LPC_TIMER_32B1);
+}
+
+/* This one is used to synchronize to the real zero-crossing detection, if any. */
+void zero_cross_detect(uint32_t gpio)
+{
+ timer_restart(LPC_TIMER_32B0);
+ zero_cross(0);
+}
+
+/* Handle the power command */
+#define CMD_UP_DELAY_RESET_VALUE 3
+void handle_cmd_update(uint32_t curent_tick)
+{
+ static uint8_t cmd = 0;
+ static uint8_t cmd_up_delay = CMD_UP_DELAY_RESET_VALUE;
+
+ /* Unable to read internal temperature : turn off heating */
+ if (error_shutdown == 1) {
+ cmd = 0;
+ goto cmd_update_end;
+ }
+
+ /* Water max temperature protection */
+ if (water_centi_degrees > (MAX_WATER_TEMP * 10)) {
+ cmd = 0;
+ goto cmd_update_end;
+ }
+
+ /* Forced test mode */
+ if (force_cmd != -1) {
+ cmd = force_cmd;
+ goto cmd_update_end;
+ }
+
+ /* Which is the current mode ? */
+ if (forced_heater_mode == 1) {
+ /* Forced heating mode */
+ if ((forced_heater_delay == 0) && (forced_heater_time > 0)) {
+ cmd = FORCED_MODE_VALUE;
+ goto cmd_update_end;
+ }
+ if (manual_forced_heater > 0) {
+ cmd = FORCED_MODE_VALUE;
+ goto cmd_update_end;
+ }
+ /* Entering forced heating mode from temperature getting below threshold,
+ * wait some delay before effective forced heating */
+ if (forced_heater_time == 0) {
+ forced_heater_delay = FORCED_HEATER_DELAY;
+ forced_heater_time = FORCED_HEATER_DELAY + FORCED_HEATER_DURATION;
+ }
+ }
+ if (solar_prod_value < (home_conso_value - 64)) {
+ /* Low production mode */
+ if (cmd > 0) {
+ cmd--;
+ }
+ } else if (solar_prod_value > (home_conso_value + 1280)) {
+ if (cmd < 100) {
+ cmd++;
+ }
+ } else if (solar_prod_value > (home_conso_value + 256)) {
+ /* High production mode */
+ if (cmd_up_delay > 0) {
+ cmd_up_delay--;
+ } else {
+ cmd_up_delay = CMD_UP_DELAY_RESET_VALUE;
+ if (cmd < 100) {
+ cmd++;
+ }
+ }
+ }
+
+cmd_update_end:
+ if ((cmd > 0) || (force_fan == 1)) {
+ fan_on = 1;
+ gpio_set(fan_ctrl);
+ } else {
+ fan_on = 0;
+ gpio_clear(fan_ctrl);
+ }
+ command_val = cmd;
+ /* Set Control Output duty cycle */
+ set_ctrl_duty_cycle(100 - cmd);
+}
+
+
+/***************************************************************************** */
+/* Power tracking - load part */
+
+/* ADC values are between 0 and 1023, no load value is at about 520 */
+static volatile uint16_t load_power_highest = 520;
+static volatile uint16_t load_power_lowest = 520;
+void power_track(uint32_t flags)
+{
+ /* Start tracking the higest value */
+ static int tracking = 1;
+ static uint16_t last_value = 0;
+ uint16_t val = 0;
+
+ /* Get load power usage */
+ adc_get_value(&val, LPC_ADC(2));
+
+ /* Four possibilities :
+ * tracking high and value increasing : nothing to do
+ * tracking high and value decreasing : we found the max, store it and change tracking
+ * tracking low and value decreasing : nothing to do
+ * tracking low and value increasing : we found the min, store it and change tracking
+ */
+ if (tracking == 1) {
+ if (last_value > val) {
+ /* Found max */
+ load_power_highest = last_value;
+ tracking = 0;
+ }
+ } else {
+ if (last_value < val) {
+ /* Found min */
+ load_power_lowest = last_value;
+ tracking = 1;
+ }
+ }
+ last_value = val;
+}
+
+/***************************************************************************** */
+void scialys_config(void)
+{
+ /* We want 100 Hz (50 Hz but two zero crossings) with 1% granularity */
+ clk_cycles_ac_zc = get_main_clock() / (100 * 100);
+
+ /* Configure the fake zero-cross generation timer to 100Hz.
+ * Do not start it now, it will be started by the first real zero-cross detected in order to
+ * generate the other half zc (not seen by system due to hardware design)
+ * Also sarted by config when DC mode is set */
+ timer_set_match(LPC_TIMER_32B0, CHAN0, (clk_cycles_ac_zc * 100));
+
+ /* Configure the power tracking timer.
+ * 20 points per half sin wave should be enough to get the min and max */
+ timer_set_match(LPC_TIMER_16B0, CHAN0, (clk_cycles_ac_zc * 5));
+ timer_start(LPC_TIMER_16B0);
+
+ /* Read parameters from memory */
+ if (1) {
+ never_force = 0;
+ sunny_days_prod_value_config = SUNNY_DAYS_PROD_VALUE;
+ }
+
+ /* Add a systick callback to handle command value update */
+ add_systick_callback(handle_cmd_update, CMD_UPD_PERIOD);
+
+ /* Add a systick callback to handle time counting */
+ add_systick_callback(handle_dec_request, DEC_PERIOD);
+}
+
+/* Add a false zero-cross, but make sure to do it only once and only if we are not AC powered */
+void DC_switch_start(void)
+{
+ static int already_done = 0;
+ if ((already_done == 0) && (zc_cnt == 0)) {
+ int ret = add_systick_callback(zero_cross, 10); /* 2 zero-crossing at 50Hz -> 100Hz -> 10ms */
+ uprintf(UART0, "Entering forced DC mode on systick callback %d\n", ret);
+ already_done = 1;
+ } else {
+ uprintf(UART0, "DC forced mode already ON: %d/%d\n", already_done, zc_cnt);
+ }
+}
+
+/***************************************************************************** */
+int main(void)
+{
+ uint32_t loop = 0;
+ char mode = heat; /* Debug info */
+ int ret = 0;
+
+ system_init();
+ board_io_config();
+ modules_config();
+
+ external_config(UART0);
+ scialys_config();
+
+ msleep(500);
+ status_led(green_only);
+
+ while (1) {
+
+ /* Default mode : try to heat the water tank */
+ mode = heat;
+
+ /* Always track power consumption and production */
+ track_isnail_values();
+
+ /* Feed the dog */
+ if ((solar_prod_value != 0) && (home_conso_value != 0)) {
+ watchdog_feed();
+ }
+
+ /* Get internal temperature */
+ ret = temp_read(UART0, &deci_degrees_disp, &deci_degrees_power);
+ if (ret != CONV_OK) {
+ /* Set internal temperature to 60°C if there's a temp error, as this will trigger the
+ * internal fan */
+ if ((ret & CONV_POWER_OK) == 0) {
+ deci_degrees_power = DEFAULT_INTERNAL_TEMP;
+ }
+ if ((ret & CONV_DISPLAY_OK) == 0) {
+ deci_degrees_disp = DEFAULT_INTERNAL_TEMP;
+ }
+ }
+
+ /* Internal temperature protection */
+ if ((deci_degrees_power > MAX_INTERNAL_TEMP) || (deci_degrees_disp > MAX_INTERNAL_TEMP)) {
+ error_shutdown = 1;
+ } else if ((deci_degrees_power < ((MAX_INTERNAL_TEMP) / 2)) &&
+ (deci_degrees_disp < ((MAX_INTERNAL_TEMP) / 2))) {
+ error_shutdown = 0;
+ }
+
+ /* Get thermocouple value */
+ if (1) {
+ int ret = 0, old_water_temp = 0;
+ old_water_temp = water_centi_degrees;
+ ret = max31855_sensor_read(&thermocouple, NULL, &water_centi_degrees);
+ if (ret != 0) {
+ uprintf(UART0, "Water Temp read error : %d\n", ret);
+ /* Do not act upon iinvalid temp value */
+ water_centi_degrees = old_water_temp;
+ }
+ }
+
+ /* Need to enter Forced heating mode ? */
+ if (water_centi_degrees < (FORCE_HEATER_TEMP * 10)) {
+ if (forced_heater_mode == 0) {
+ uprintf(UART0, "Water temp low, entering forced mode\n");
+ forced_heater_mode = 1;
+ }
+ status_led(red_on);
+ mode = forced;
+ } else if ((water_centi_degrees > (TARGET_FORCED_HEATER_TEMP * 10)) && (forced_heater_mode == 1)) {
+ status_led(red_off);
+ uprintf(UART0, "Water temp OK, forced mode exit\n");
+ forced_heater_mode = 0;
+ mode = temp_OK;
+ }
+
+ /* Do not force if there is a lot of sun, it may be enough to heat again soon */
+ if ((solar_prod_value > sunny_days_prod_value_config) && (forced_heater_mode == 1)) {
+ mode = delayed_heat_prod;
+ forced_heater_mode = 0;
+ }
+
+ /* Do not force heating if disabled by external command */
+ external_disable = gpio_read(ext_disable_in_pin);
+ if ((external_disable == EXTERNAL_DISABLE_FORCE) && (forced_heater_mode != 0)) {
+ forced_heater_mode = 0;
+ mode = ext_disable;
+ uprintf(UART0, "Forced mode disabled by external input\n");
+ }
+
+ if (never_force == 1) {
+ forced_heater_mode = 0;
+ }
+
+ /* Did the user request a forced heating ? */
+ if (manual_activation_request != 0) {
+ forced_heater_mode = 1;
+ mode = manual;
+ if (manual_activation_request == -1) {
+ uprintf(UART0, "Entering manual forced mode for %d ticks\n", manual_activation_request);
+ manual_activation_request = MANUAL_ACTIVATION_DURATION;
+ manual_forced_heater = 1;
+ }
+ if (manual_activation_request < 10) {
+ uprintf(UART0, "Leaving manual forced mode\n");
+ manual_activation_request = 0;
+ }
+ }
+
+ /* Command at 100% and still more production than energy used ? */
+ if ((command_val == 100) && (solar_prod_value > home_conso_value)) {
+ mode = overprod;
+ }
+
+ /* Display */
+ interface_update(mode);
+
+ /* Debug */
+ if (1) {
+ uprintf(UART0, "%c:%d - Is: %d,%04d - Ih: %d,%04d\n", mode, loop++,
+ (solar_prod_value / 1000), (solar_prod_value % 1000),
+ (home_conso_value / 1000), (home_conso_value % 1000));
+ uprintf(UART0, "Water Temp : %d\n", water_centi_degrees);
+ uprintf(UART0, "Internal Temp power : %d\n", deci_degrees_power);
+ uprintf(UART0, "Internal Temp display : %d\n", deci_degrees_disp);
+ uprintf(UART0, "ADC: Sol: %dmA, Home: %dmA\n", solar_prod_value, home_conso_value);
+ uprintf(UART0, "Load: %d - %d\n", load_power_lowest, load_power_highest);
+ uprintf(UART0, "Zc: %d\n", zc_cnt);
+ uprintf(UART0, "CMD: %d/%d, Fan: %d/%d\n\n", command_val, (100 - act_cmd), fan_on, force_fan);
+ }
+
+ /* Wait approx 10ms between each loop */
+ //msleep(10);
+ }
+ return 0;
+}
+
+
+
--- /dev/null
+/****************************************************************************
+ * apps/blyes/inter_lights/time.c
+ *
+ * Copyright 2018 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 = 0x18,
+ .month = 0x08,
+ .day = 0x09,
+ .hour = 0x13,
+ .min = 0x37,
+};
+int time_valid = 0;
+int rtc_conf_ok = 0;
+
+int 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 time_update(uint32_t uart, struct rtc_time* now)
+{
+ rtc_pcf85363_time_write(&rtc_conf, now);
+ return 0;
+}
+
+
+/***************************************************************************** */
+/* RTC init */
+void time_config(uint32_t i2c_bus_num, uint8_t rtc_addr)
+{
+ rtc_conf.bus_num = i2c_bus_num;
+ rtc_conf.addr = rtc_addr;
+}
+
--- /dev/null
+/****************************************************************************
+ * apps/blyes/inter_lights/time.h
+ *
+ * Copyright 2018 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 time_init_check(uint32_t uart);
+void time_config(uint32_t i2c_bus_num, uint8_t rtc_addr);
+
+#endif /* RTC_TIME_H */
+
--- /dev/null
+/****************************************************************************
+ * apps/blyes/inter_lights/uSD.c
+ *
+ * Copyright 2018 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/ssp.h"
+
+#include "lib/stdio.h"
+
+#include "uSD.h"
+
+
+/***************************************************************************** */
+/* SD/MMC Card */
+struct sdmmc_card micro_sd = {
+ .card_type = MMC_CARDTYPE_UNKNOWN,
+ .block_size = 64,
+};
+
+static uint8_t got_uSD = 0;
+
+/* Buffer to read / write uSD card */
+uint8_t mmc_data[MMC_BUF_SIZE];
+
+/* Read up to 4 blocks of 16 bytes each */
+int uSD_read(uint32_t offset, uint8_t nb_blocks)
+{
+ int i = 0, ret = 0;
+ if (got_uSD == 0) {
+ return -1;
+ }
+ if (nb_blocks > 4) {
+ return -2;
+ }
+ memset(mmc_data, 0, MMC_BUF_SIZE);
+ for (i = 0; i < nb_blocks; i++) {
+ ret = sdmmc_read_block(µ_sd, (offset + i), (mmc_data + (i * 16)));
+ /* FIXME : check return value */
+ }
+ return 0;
+}
+
+
+/* Write up to 4 blocks of 16 bytes each */
+int uSD_write(uint32_t offset, uint8_t nb_blocks)
+{
+ int i = 0, ret = 0;
+ if (got_uSD == 0) {
+ return -1;
+ }
+ if (nb_blocks > 4) {
+ return -1;
+ }
+ for (i = 0; i < nb_blocks; i++) {
+ ret = sdmmc_write_block(µ_sd, (offset + i), (mmc_data + (i * 16)));
+ /* FIXME : check return value */
+ }
+ return 0;
+}
+
+/* microSD card init */
+int uSD_detect(int uart)
+{
+ int i = 0, ret = 0, step = 0;
+ do {
+ step = 0;
+ ret = sdmmc_init(µ_sd);
+ if (ret == 0) {
+ step = 1;
+ msleep(10);
+ ret = sdmmc_init_wait_card_ready(µ_sd);
+ if (ret <= 1) {
+ step = 2;
+ ret = sdmmc_init_end(µ_sd);
+ }
+ }
+ uprintf(uart, "uSD init(%d): step:%d, ret: %d, type: %d, bs: %d\n",
+ i, step, ret, micro_sd.card_type, micro_sd.block_size);
+ i++;
+ } while (((ret != 0) || (micro_sd.card_type == MMC_CARDTYPE_UNKNOWN)) && (i < 10));
+
+ /* Got uSD ? */
+ if (i >= 10) {
+ uprintf(uart, "uSD init failed, no uSD card present.\n");
+ got_uSD = 0;
+ return -1;
+ }
+
+ /* uSD card detected */
+ got_uSD = 1;
+ /* got_uSD MUST be set to 1 from here on ! */
+ uSD_read(0, 4); /* Read 4 blocks at start of card */
+ /* FIXME : check that the card magic is present */
+ uprintf(uart, "uSD read: %s\n", mmc_data);
+
+ return 0;
+}
+
+
+int uSD_logs_init(int uart)
+{
+ /* FIXME : read 4 blocks at offset FIXME to get the last block used info */
+ return 0;
+}
+
+/***************************************************************************** */
+/* microSD card init */
+void uSD_config(struct pio* uSD_cs, uint32_t ssp_bus_num)
+{
+ memcpy(&(micro_sd.chip_select), uSD_cs, sizeof(struct pio));
+ micro_sd.ssp_bus_num = ssp_bus_num;
+}
+
--- /dev/null
+/****************************************************************************
+ * apps/blyes/inter_lights/usSD.h
+ *
+ * Copyright 2018 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 USD_H
+#define USD_H
+
+#include "extdrv/sdmmc.h"
+
+#define MMC_BUF_SIZE 64
+
+extern struct sdmmc_card micro_sd;
+extern uint8_t mmc_data[MMC_BUF_SIZE];
+
+int uSD_read(uint32_t offset, uint8_t nb_blocks);
+int uSD_write(uint32_t offset, uint8_t nb_blocks);
+
+/***************************************************************************** */
+/* microSD card init */
+int uSD_detect(int uart);
+void uSD_config(struct pio* uSD_cs, uint32_t ssp_bus_num);
+
+
+#endif /* USD_H */
+
--- /dev/null
+# 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} $@
+
--- /dev/null
+/****************************************************************************
+ * apps/scialys/v07/config.c
+ *
+ * Copyright 2018 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 "drivers/adc.h"
+#include "drivers/ssp.h"
+#include "drivers/timers.h"
+
+
+#include "extdrv/status_led.h"
+#include "extdrv/max31855_thermocouple.h"
+
+#include "lib/stdio.h"
+
+#include "config.h"
+#include "interface.h"
+#include "time.h"
+#include "uSD.h"
+
+#define SELECTED_FREQ FREQ_SEL_48MHz
+
+
+uint8_t config_got_interface = 1;
+
+/***************************************************************************** */
+/* Pins configuration */
+/* pins blocks are passed to set_pins() for pins configuration.
+ * Unused pin blocks can be removed safely with the corresponding set_pins() call
+ * All pins blocks may be safelly merged in a single block for single set_pins() call..
+ */
+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) },
+ /* SPI */
+ { LPC_SSP0_SCLK_PIO_0_14, LPC_IO_DIGITAL },
+ { LPC_SSP0_MOSI_PIO_0_17, LPC_IO_DIGITAL },
+ { LPC_SSP0_MISO_PIO_0_16, LPC_IO_DIGITAL },
+ /* ADC */
+ { LPC_ADC_AD0_PIO_0_30, LPC_IO_ANALOG }, /* Home */
+ { LPC_ADC_AD1_PIO_0_31, LPC_IO_ANALOG }, /* Production */
+ { LPC_ADC_AD2_PIO_1_0, LPC_IO_ANALOG }, /* Load */
+ /* GPIO */
+ { LPC_GPIO_0_0, LPC_IO_DIGITAL }, /* Over Voltage protection input */
+ { LPC_GPIO_0_3, LPC_IO_DIGITAL }, /* External switch input */
+ { LPC_GPIO_0_4, LPC_IO_DIGITAL }, /* Zero crossing detection input */
+ { LPC_GPIO_0_5, LPC_IO_DIGITAL }, /* Fan control */
+ { LPC_GPIO_0_6, LPC_IO_DIGITAL }, /* Mosfet control */
+ { LPC_GPIO_0_7, LPC_IO_DIGITAL }, /* Thermocouple chip select */
+ { LPC_GPIO_0_12, LPC_IO_DIGITAL }, /* ISP / User button OK */
+ { LPC_GPIO_0_18, LPC_IO_DIGITAL }, /* Uext Chip select / Module eeprom select */
+ { 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 }, /* Over Temperature protection input */
+ { LPC_GPIO_1_1, LPC_IO_DIGITAL }, /* uSD Card SPI Chip Select */
+ { 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;
+
+/* Inputs */
+/* External signals */
+const struct pio zero_cross_in_pin = LPC_GPIO_0_4;
+const struct pio ext_disable_in_pin = LPC_GPIO_0_3;
+const struct pio overvoltage_pin = LPC_GPIO_0_0;
+const struct pio overtemperature_pin = LPC_GPIO_0_28;
+
+/* Outputs */
+/* AC output control (Mosfet) */
+const struct pio ac_ctrl = LPC_GPIO_0_6;
+const struct pio fan_ctrl = LPC_GPIO_0_5;
+/* Chip selects */
+const struct pio uSD_cs = LPC_GPIO_1_1; /* uSD card */
+const struct pio th_cs = LPC_GPIO_0_7; /* Thermocouple */
+const struct pio uext_cs = LPC_GPIO_0_18; /* UEXT module (optionnal) */
+
+
+
+/***************************************************************************** */
+/* 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);
+}
+
+
+/***************************************************************************** */
+/* Internal modules */
+const struct lpc_tc_config ac_timer_conf_zc = {
+ .mode = LPC_TIMER_MODE_TIMER | LPC_TIMER_MODE_MATCH,
+ .match_control = { LPC_TIMER_INT_RESET_ON_MATCH, 0, 0, 0, },
+};
+const struct lpc_tc_config ac_timer_conf_delay = {
+ .mode = LPC_TIMER_MODE_TIMER | LPC_TIMER_MODE_MATCH,
+ .match_control = { LPC_TIMER_INT_RESET_AND_STOP_ON_MATCH, 0, 0, 0, },
+};
+const struct lpc_tc_config ac_timer_conf_power_track = {
+ .mode = LPC_TIMER_MODE_TIMER | LPC_TIMER_MODE_MATCH,
+ .match_control = { LPC_TIMER_INT_RESET_ON_MATCH, 0, 0, 0, },
+};
+
+extern void config_rx(uint8_t c);
+extern void comm_rx(uint8_t c);
+extern void ac_switch_on(uint32_t flags);
+extern void zero_cross(uint32_t flags);
+extern void power_track(uint32_t flags);
+
+void modules_config(void)
+{
+ uart_on(UART0, 115200, config_rx);
+ uart_on(UART1, 115200, comm_rx);
+ i2c_on(I2C0, I2C_CLK_100KHz, I2C_MASTER);
+ ssp_master_on(SSP_BUS_0, LPC_SSP_FRAME_SPI, 8, 4*1000*1000);
+ adc_on(NULL);
+ adc_start_burst_conversion(ADC_MCH(0) | ADC_MCH(1) | ADC_MCH(2), LPC_ADC_SEQ(0));
+ timer_on(LPC_TIMER_32B0, 0, zero_cross); /* This one is used to generate the second zero-crossing */
+ timer_counter_config(LPC_TIMER_32B0, &ac_timer_conf_zc);
+ timer_on(LPC_TIMER_32B1, 0, ac_switch_on); /* This one is used to generate the power PMW */
+ timer_counter_config(LPC_TIMER_32B1, &ac_timer_conf_delay);
+ timer_on(LPC_TIMER_16B0, 0, power_track); /* This one is used to call the load power tracking ADC sampler */
+ timer_counter_config(LPC_TIMER_16B0, &ac_timer_conf_power_track);
+}
+
+
+/***************************************************************************** */
+extern void zero_cross_detect(uint32_t gpio);
+extern void overvoltage_protect(uint32_t gpio);
+void board_io_config(void)
+{
+ /* Immediatly turn off Mosfet / Triac */
+ config_gpio(&ac_ctrl, 0, GPIO_DIR_OUT, 1);
+ /* Start with FAN ON */
+ config_gpio(&fan_ctrl, 0, GPIO_DIR_OUT, 1);
+
+ /* Configure Input GPIO */
+ config_gpio(&ext_disable_in_pin, 0, GPIO_DIR_IN, 0);
+ config_gpio(&overtemperature_pin, 0, GPIO_DIR_IN, 0);
+
+ /* Over-voltage protection */
+ set_gpio_callback(overvoltage_protect, &overvoltage_pin, EDGE_RISING);
+
+ /* Zero-crossing detection */
+ set_gpio_callback(zero_cross_detect, &zero_cross_in_pin, EDGE_FALLING);
+}
+
+
+
+/***************************************************************************** */
+/* Configure external components */
+
+/* Thermocouple reading */
+const struct max31855_sensor_config thermocouple = {
+ .ssp_bus_num = 0,
+ .chip_select = LPC_GPIO_0_7,
+};
+
+/* RTC and time */
+#define RTC_ADDR 0xA2
+
+
+int external_config(uint32_t uart)
+{
+ int ret = 0;
+
+ /* Configure GPIO for specific board usage */
+ board_io_config();
+
+ /* uSD card */
+ uSD_config((struct pio *)&uSD_cs, SSP_BUS_0);
+ uSD_detect(uart);
+
+ /* RTC */
+ time_config(I2C0, RTC_ADDR);
+ time_init_check(uart);
+
+ /* Thermocouple configuration */
+ max31855_sensor_config(&thermocouple);
+
+ /* Configure interface board */
+ ret = interface_config(uart);
+ if (ret != 0) {
+ return -1;
+ }
+ return 0;
+}
--- /dev/null
+/****************************************************************************
+ * apps/scialys/v07/config.h
+ *
+ * Copyright 2018 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 "drivers/adc.h"
+#include "drivers/ssp.h"
+#include "drivers/timers.h"
+
+#include "extdrv/status_led.h"
+#include "extdrv/max31855_thermocouple.h"
+#include "extdrv/ws2812.h"
+
+#include "lib/stdio.h"
+
+
+#define SELECTED_FREQ FREQ_SEL_48MHz
+
+/***************************************************************************** */
+/* Pins configuration */
+extern const struct pio status_led_green;
+extern const struct pio status_led_red;
+
+/* Inputs */
+/* External signals */
+extern const struct pio zero_cross_in_pin;
+extern const struct pio ext_disable_in_pin;
+extern const struct pio overvoltage_pin;
+extern const struct pio overtemperature_pin;
+
+
+/* Outputs */
+/* AC output control (Mosfet) */
+extern const struct pio ac_ctrl;
+extern const struct pio fan_ctrl;
+/* Chip selects */
+extern const struct pio uSD_cs; /* uSD card */
+extern const struct pio th_cs; /* Thermocouple */
+extern const struct pio uext_cs; /* UEXT module (optionnal) */
+
+
+
+/***************************************************************************** */
+extern const struct max31855_sensor_config thermocouple;
+
+
+/***************************************************************************** */
+/* 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 modules and main functions */
+void modules_config(void);
+
+/* Configure GPIO for specific board usage */
+void board_io_config(void);
+
+/* Configure external components */
+int external_config(uint32_t uart);
+
+
+#endif /* CONFIG_H */
+
--- /dev/null
+/****************************************************************************
+ * apps/scialys/v07/interface.c
+ *
+ * Copyright 2018 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 "drivers/adc.h"
+
+#include "extdrv/status_led.h"
+#include "extdrv/ws2812.h"
+#include "extdrv/tmp101_temp_sensor.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 "interface.h"
+#include "time.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;
+
+
+static uint8_t interface_board_present = 0;
+static uint8_t power_board_present = 0;
+
+/***************************************************************************** */
+/* TMP101 I2C temperature sensor on Power board */
+#define TMP101_ADDR1 0x94 /* Pin Addr0 (pin5 of tmp101) connected to VCC */
+struct tmp101_sensor_config tmp101_sensor_power = {
+ .bus_num = I2C0,
+ .addr = TMP101_ADDR1,
+ .resolution = TMP_RES_ELEVEN_BITS,
+};
+
+/* TMP101 I2C temperature sensor on display board */
+#define TMP101_ADDR0 0x90 /* Pin Addr0 (pin5 of tmp101) connected to GND */
+struct tmp101_sensor_config tmp101_sensor_display = {
+ .bus_num = I2C0,
+ .addr = TMP101_ADDR0,
+ .resolution = TMP_RES_ELEVEN_BITS,
+};
+
+/***************************************************************************** */
+/* 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;
+
+ if (interface_board_present == 0) {
+ return -1;
+ }
+
+ 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;
+
+ if (interface_board_present == 0) {
+ return -1;
+ }
+
+ 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);
+}
+
+
+int temp_read(uint32_t uart, int* deci_degrees_disp, int* deci_degrees_power)
+{
+ int ret = 0;
+ int conv = 0;
+
+ if (interface_board_present != 0) {
+ ret = tmp101_sensor_read(&tmp101_sensor_display, NULL, deci_degrees_disp);
+ if (ret == 0) {
+ conv |= CONV_DISPLAY_OK;
+ } else {
+ uprintf(uart, "TMP101 read error on display board : %d\n", ret);
+ }
+ }
+ if (power_board_present != 0) {
+ ret = tmp101_sensor_read(&tmp101_sensor_power, NULL, deci_degrees_power);
+ if (ret == 0) {
+ conv |= CONV_POWER_OK;
+ } else {
+ uprintf(uart, "TMP101 read error on power board : %d\n", ret);
+ }
+ }
+ return conv;
+}
+
+
+/***************************************************************************** */
+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;
+ }
+}
+
+int interface_config(uint32_t uart)
+{
+ int ret = 0;
+
+ /* TMP101 sensor config on power board */
+ power_board_present = 1;
+ ret = tmp101_sensor_config(&tmp101_sensor_power);
+ if (ret != 0) {
+ uprintf(uart, "Temp config error on power board: %d\n", ret);
+ power_board_present = 0;
+ } else {
+ ret = tmp101_sensor_set_continuous_conversion(&tmp101_sensor_power);
+ if (ret != 0) {
+ uprintf(uart, "Temp config error on power board: %d\n", ret);
+ power_board_present = 0;
+ }
+ }
+
+ /* TMP101 sensor config on display board */
+ ret = tmp101_sensor_config(&tmp101_sensor_display);
+ if (ret != 0) {
+ uprintf(uart, "Temp config error on display board: %d\n", ret);
+ interface_board_present = 0;
+ return -1;
+ }
+ ret = tmp101_sensor_set_continuous_conversion(&tmp101_sensor_display);
+ if (ret != 0) {
+ uprintf(uart, "Temp config error on display board: %d\n", ret);
+ interface_board_present = 0;
+ return -2;
+ }
+ interface_board_present = 1;
+
+ /* Buttons inputs on front panel */
+ /* Activate on Rising edge (button release) */
+ set_gpio_callback(button_callback, &button_up, EDGE_RISING);
+ set_gpio_callback(button_callback, &button_left, EDGE_RISING);
+ set_gpio_callback(button_callback, &button_right, EDGE_RISING);
+ set_gpio_callback(button_callback, &button_down, EDGE_RISING);
+ set_gpio_callback(button_callback, &button_ok, EDGE_RISING);
+
+ /* WS2812B Leds on display board */
+ ws2812_config(&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(0, 0x05, 0x15, 0x08);
+ ws2812_send_frame(0);
+
+ uprintf(uart, "Config OK\n");
+
+ return 0;
+}
+
+
+
+/***************************************************************************** */
+ /* Menu part */
+
+enum interface_modes {
+ MODE_RUN = 0,
+ MODE_CONFIG,
+ MODE_DISPLAY,
+};
+static int interface_mode = MODE_RUN;
+volatile int manual_activation_request = 0;
+
+enum menu_list {
+ MAIN_MENU,
+ MANUAL_MODE,
+ DATE_CONFIG,
+ FUNCTIONS,
+ LIMITS,
+ SAVE_CONFIG,
+ NB_MENU, /* This one must be the last */
+};
+static const char* menu_titles[] = {
+ [MAIN_MENU] = "Menu principal",
+ [MANUAL_MODE] = "Marche Forcee",
+ [DATE_CONFIG] = "Regl. Heure",
+ [FUNCTIONS] = "Reglages",
+ [LIMITS] = "Limites",
+ [SAVE_CONFIG] = "Save config",
+};
+static uint8_t current_menu = MAIN_MENU;
+static uint8_t current_entry = MANUAL_MODE;
+
+enum func_menu_list {
+ FUNC_CMD_TYPE,
+ FUNC_TENSION,
+ INST_POWER,
+ LOAD_POWER,
+ FORCED_MODE_VAL,
+ FUNC_NB_MENU, /* This one must be the last */
+};
+static const char* func_modes_titles[] = {
+ [FUNC_CMD_TYPE] = "Type Cmd", /* ToR / PWM */
+ [FUNC_TENSION] = "Tension", /* AC / DC */
+ [INST_POWER] = "P Prod", /* Production power (kW) */
+ [LOAD_POWER] = "P Cahrge", /* Load power (kW) */
+ [FORCED_MODE_VAL] = "Force Val", /* Default is 75% */
+};
+static uint8_t func_cur_menu = FUNC_CMD_TYPE;
+static uint8_t func_cur_entry = FUNC_CMD_TYPE;
+
+enum limits_menu_list {
+ TEMP_FORCED_START,
+ TEMP_FORCED_TARGET,
+ SUNNY_DAY_VALUE,
+ LIM_NB_MENU, /* This one must be the last */
+};
+static const char* limits_modes_titles[] = {
+ [TEMP_FORCED_START] = "Water Min",
+ [TEMP_FORCED_TARGET] = "Water Target",
+ [SUNNY_DAY_VALUE] = "Sun Prod",
+};
+static uint8_t limits_cur_menu = TEMP_FORCED_START;
+static uint8_t limits_cur_entry = TEMP_FORCED_START;
+
+
+
+/* Current sub-menu level. main menu is level 0 */
+int sub_menu_level = 0;
+
+
+void config_interface_handle(void)
+{
+ char line[DISP_LLEN];
+ 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;
+ for (i = 1; i < NB_MENU; i++) {
+ if (i != current_entry) {
+ display_line(i + 1, 3, menu_titles[i]);
+ } else {
+ snprintf(line, DISP_LLEN, " ->%s", menu_titles[i]);
+ display_line(i + 1, 0, line);
+ }
+ }
+ 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) {
+ current_entry = NB_MENU - 1;
+ }
+ if (button & BUTTON_RIGHT) {
+ current_menu = current_entry;
+ sub_menu_level = 1;
+ }
+ if (button & (BUTTON_LEFT | BUTTON_OK)) {
+ interface_mode = MODE_RUN;
+ }
+ }
+ break;
+ case DATE_CONFIG: {
+ static uint8_t date_idx = 0;
+ struct rtc_time now;
+ snprintf(line, DISP_LLEN, "^");
+ display_line(3, 6 + (date_idx * 3), line);
+ rtc_pcf85363_time_read(&rtc_conf, &now);
+ snprintf(line, DISP_LLEN, "%02xh%02x:%02x", now.hour, now.min, now.sec);
+ display_line(4, 6, line);
+ snprintf(line, DISP_LLEN, "V");
+ display_line(5, 6 + (date_idx * 3), line);
+ if ((button & BUTTON_RIGHT) && (date_idx < 2)) {
+ date_idx++;
+ }
+ if ((button & BUTTON_LEFT) && (date_idx > 0)) {
+ date_idx--;
+ }
+ if (button & (BUTTON_UP | BUTTON_DOWN)) {
+ uint32_t seconds = rtc_pcf85363_daytime_to_seconds(&now);
+ uint32_t add[6] = {
+ 3600, 60, 1,
+ (23 * 3600), (23* 3600 + 59 * 60), (23 * 3600 + 59 * 60 + 59),
+ };
+ if (button & BUTTON_UP) {
+ if (seconds < add[date_idx + 3]) {
+ seconds += add[date_idx];
+ } else {
+ seconds -= add[date_idx + 3];
+ }
+ } else {
+ if (seconds < add[date_idx]) {
+ seconds += add[date_idx + 3];
+ } else {
+ seconds -= add[date_idx];
+ }
+ }
+ rtc_pcf85363_seconds_to_daytime(&now, seconds);
+ rtc_pcf85363_time_write(&rtc_conf, &now);
+ }
+ if (button & BUTTON_OK) {
+ sub_menu_level = 0;
+ current_menu = MAIN_MENU;
+ }
+ }
+ break;
+ case FUNCTIONS: {
+ if (sub_menu_level == 1) {
+ int i = 0;
+ for (i = 0; i < FUNC_NB_MENU; i++) {
+ if (i != func_cur_entry) {
+ display_line(i + 2, 3, func_modes_titles[i]);
+ } else {
+ snprintf(line, DISP_LLEN, " ->%s", func_modes_titles[i]);
+ display_line(i + 2, 0, line);
+ }
+ }
+ if (button & BUTTON_UP) {
+ func_cur_entry -= 1;
+ }
+ if (button & BUTTON_DOWN) {
+ func_cur_entry += 1;
+ }
+ if (func_cur_entry >= FUNC_NB_MENU) {
+ func_cur_entry = 0;
+ } else if (func_cur_entry == 0xFF) {
+ func_cur_entry = FUNC_NB_MENU - 1;
+ }
+ if (button & (BUTTON_OK | BUTTON_RIGHT)) {
+ func_cur_menu = func_cur_entry;
+ sub_menu_level = 2;
+ }
+ if (button & BUTTON_LEFT) {
+ sub_menu_level = 0;
+ current_menu = MAIN_MENU;
+ }
+ } else {
+ }
+ }
+ 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 {
+ snprintf(line, DISP_LLEN, " ->%s", limits_modes_titles[i]);
+ display_line(i + 2, 0, line);
+ }
+ }
+ if (button & BUTTON_UP) {
+ limits_cur_entry -= 1;
+ }
+ if (button & BUTTON_DOWN) {
+ limits_cur_entry += 1;
+ }
+ if (limits_cur_entry >= LIM_NB_MENU) {
+ limits_cur_entry = 0;
+ } else if (limits_cur_entry == 0xFF) {
+ limits_cur_entry = LIM_NB_MENU - 1;
+ }
+ 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 {
+ }
+ }
+ break;
+ case SAVE_CONFIG: {
+ }
+ break;
+ case MANUAL_MODE:
+ manual_activation_request = -1;
+ /* Fallback */
+ default:
+ interface_mode = MODE_RUN;
+ current_menu = MAIN_MENU;
+ break;
+ }
+ /* Update Oled display */
+ ssd130x_display_full_screen(&display);
+}
+
+
+/***************************************************************************** */
+ /* usual menu and switch to config menu */
+
+extern int water_centi_degrees;
+extern uint32_t solar_prod_value;
+extern uint32_t home_conso_value;
+extern volatile uint8_t command_val;
+
+void interface_update(char heat_mode)
+{
+ int abs_centi = water_centi_degrees;
+
+ if (water_centi_degrees < 0) {
+ abs_centi = -water_centi_degrees;
+ }
+
+ /* Debug */
+ if (button_pressed != 0) {
+ uprintf(UART0, "Button : 0x%02x\n", button_pressed);
+ }
+
+ if (interface_mode == MODE_RUN) {
+ if (button_pressed != 0) {
+ if (button_pressed & BUTTON_OK) {
+ manual_activation_request = -1;
+ }
+ if (button_pressed & BUTTON_UP) {
+ interface_mode = MODE_CONFIG;
+ }
+ button_pressed = 0;
+ }
+
+ if (interface_board_present != 0) {
+ char line[DISP_LLEN];
+
+ /* Start with a blank screen */
+ erase_screen_content();
+
+ /* Update time and time display on internal memory */
+ rtc_pcf85363_time_read(&rtc_conf, &now);
+ snprintf(line, DISP_LLEN, "%02xh%02x:%02x", now.hour, now.min, now.sec);
+ display_line(0, 0, line);
+
+ /* Display info */
+ snprintf(line, DISP_LLEN, "Water:% 2d.%03d %cC", (water_centi_degrees / 100), (abs_centi % 100), 0x1F);
+ display_line(2, 0, line);
+ snprintf(line, DISP_LLEN, "Prod :% 2d,%03dA", (solar_prod_value / 1000), ((solar_prod_value % 1000) / 10));
+ display_line(3, 0, line);
+ snprintf(line, DISP_LLEN, "Conso:% 2d,%03dA", (home_conso_value / 1000), ((home_conso_value % 1000) / 10));
+ display_line(4, 0, line);
+ snprintf(line, DISP_LLEN, "Command: %d%%", command_val);
+ display_line(5, 0, line);
+ snprintf(line, DISP_LLEN, "Mode: %c", heat_mode);
+ display_line(6, 0, line);
+ snprintf(line, DISP_LLEN, "v0.9 - 0.3.5");
+ display_line(7, 0, line);
+ /* Update Oled display */
+ ssd130x_display_full_screen(&display);
+
+ /* Update RGB leds */
+ /* FIXME : use for error signal */
+ ws2812_set_pixel(0, (home_conso_value / 2000), (solar_prod_value / 2000), 0);
+ ws2812_set_pixel(1, 0, 0, command_val);
+ ws2812_send_frame(0);
+ }
+ } else {
+ /* Config mode. Mode entered by button action, which implies that interface board is present. */
+ config_interface_handle();
+ }
+}
+
+
--- /dev/null
+/****************************************************************************
+ * apps/blyes/relay_lights/interface.h
+ *
+ * Copyright 2018 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;
+
+extern volatile int manual_activation_request;
+
+/* 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);
+
+
+/* Internal Temperature sensors */
+#define CONV_DISPLAY_OK (0x01 << 0)
+#define CONV_POWER_OK (0x01 << 1)
+#define CONV_OK (CONV_POWER_OK | CONV_DISPLAY_OK)
+int temp_read(uint32_t uart, int* deci_degrees_disp, int* deci_degrees_power);
+
+/* Interface content update */
+void interface_update(char mode);
+
+#endif /* INTERFACE_H */
+
--- /dev/null
+/****************************************************************************
+ * apps/scialys/testv09/main.c
+ *
+ * Scialys system for solar-panel power generation tracking and fair use.
+ *
+ * Copyright 2016 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 "uSD.h"
+
+
+#define MODULE_VERSION 0x09
+#define MODULE_NAME "Scialys uC"
+
+
+
+/***************************************************************************** */
+/* System configuration */
+
+/* Period (in ms) of the decrementer handler from the systick interrupt */
+#define DEC_PERIOD 100
+/* Period (in ms) of the handler for the command value update */
+#define CMD_UPD_PERIOD 50
+#define ADC_UPD_PERIOD 17
+
+/* Max water temperature. Internal protection which cannot be overriden by configuration */
+#define MAX_WATER_TEMP (90 * 100) /* Hundredth of degrees C : 90°C */
+/* Internal system max temperature : turn off heating when reached */
+#define MAX_INTERNAL_TEMP (80 * 10) /* Tenth of degrees C : 80°C */
+
+/* Number of main loops during which we keep the mosfets ON in order to keep them safe */
+#define OVERVOLTAGE_PROTECTION_CYCLES 100
+
+/* Most of the defines in here should go to configuration setting in user flash */
+/* If temperature falls bellow FORCE_HEATER_TEMP value, we enter forced heater mode, until
+ * TARGET_FORCED_HEATER_TEMP is reached.
+ * When in forced heater mode, the heater is controlled to heat at FORCED_MODE_VALUE which
+ * is between 0 and 100.
+ */
+#define FORCE_HEATER_TEMP (30 * 100)
+#define TARGET_FORCED_HEATER_TEMP (50 * 100)
+#define FORCED_MODE_VALUE 75 /* A fraction of 100 */
+/* mA prod value above which the system will not enter forced mode, waiting for home
+ * to stop using power to start automatic heating */
+#define SUNNY_DAYS_PROD_VALUE 3000
+
+/* Delay before automatic forced heating */
+#define FORCED_HEATER_DELAY (2 * 3600 * 1000 / DEC_PERIOD)
+/* Duration of automatic forced heating */
+#define FORCED_HEATER_DURATION (3 * 3600 * 1000 / DEC_PERIOD)
+
+/* Duration of manual forced heating */
+#define MANUAL_ACTIVATION_DURATION (3 * 3600 * 1000 / DEC_PERIOD) /* Three hours */
+
+
+uint32_t forced_heater_mode = 0;
+uint32_t forced_heater_delay = 0;
+uint32_t forced_heater_time = 0;
+uint8_t manual_forced_heater = 0;
+
+uint8_t error_shutdown = 0;
+
+uint8_t never_force = 0;
+uint32_t sunny_days_prod_value_config = 0;
+
+#define EXTERNAL_DISABLE_FORCE 0 /* Input is pulled low when external disable is ON */
+int external_disable = 0;
+int temp_shutdown = 0;
+int overvoltage = 0;
+
+enum modes {
+ heat = 'C', /* Normal heating */
+ ext_disable = 'E', /* Forced heating disabled by external input */
+ forced = 'F', /* Forced heating */
+ manual = 'M',
+ delayed_heat_prod = 'P', /* Pause */
+ overprod = 'S', /* Over production, try to start other loads */
+ temp_OK = 'T', /* Max temperature reached */
+};
+
+/* Water and internaltemperature */
+int water_centi_degrees = 0;
+int deci_degrees_power = 0;
+int deci_degrees_disp = 0;
+
+/* RTC and time */
+struct rtc_time now;
+
+
+/***************************************************************************** */
+/* Rx interrupt handler for system configuration over USB */
+void config_rx(uint8_t c)
+{
+ /* FAN control */
+ if (c == 'f') {
+ gpio_set(fan_ctrl);
+ } else {
+ gpio_clear(fan_ctrl);
+ }
+}
+
+
+/* Communication with slave modules */
+void comm_rx(uint8_t c)
+{
+}
+
+
+
+/***************************************************************************** */
+/* System communication over UART1 */
+void cmd_rx(uint8_t c)
+{
+}
+
+
+/***************************************************************************** */
+/* Decrementer for heating timers */
+void handle_dec_request(uint32_t curent_tick) {
+ if (manual_activation_request > 0) {
+ manual_activation_request--;
+ }
+ if (forced_heater_mode == 1) {
+ if (forced_heater_delay > 0) {
+ forced_heater_delay--;
+ }
+ if (forced_heater_time > 0) {
+ forced_heater_time--;
+ }
+ }
+}
+
+
+/***************************************************************************** */
+/* Track power production and usage */
+
+/* Average value computed on last 10 ADC values */
+uint32_t solar_prod_value = 0;
+uint32_t home_conso_value = 0;
+#define NB_VAL 4 /* MUST be a power of 2 */
+#define NB_SHIFT 2
+
+static void track_isnail_values(uint32_t flags)
+{
+ static uint8_t idx = 0;
+ static uint16_t solar[NB_VAL];
+ static uint16_t home[NB_VAL];
+ uint16_t snapval_solar = 0;
+ uint16_t snapval_home = 0;
+ int cnt, sum_solar = 0, sum_home = 0;
+
+ /* Get new values */
+ adc_get_value(&snapval_solar, LPC_ADC(1));
+ adc_get_value(&snapval_home, LPC_ADC(0));
+
+ /* Store values */
+ solar[idx] = snapval_solar;
+ home[idx] = snapval_home;
+ idx++;
+ if (idx >= NB_VAL) {
+ idx = 0;
+ }
+
+ /* Compute average value */
+ for (cnt = 0; cnt < NB_VAL; cnt++) {
+ sum_solar += solar[cnt];
+ sum_home += home[cnt];
+ }
+
+ /* Convert to mA value.
+ * ADC range is 0 to 1024 - approx 3.2mV / ADC step
+ * Coil convertion is 1000mA -> 50mV : multily mV value by 20 to get mA value
+ * x * 3.2 * 20 == x * 32 * 2 == x << 6
+ * Increment is 64mA / ADC step */
+ solar_prod_value = sum_solar << (6 - NB_SHIFT);
+ home_conso_value = sum_home << (6 - NB_SHIFT);
+}
+
+/***************************************************************************** */
+/* AC control */
+
+volatile uint8_t command_val = 0;
+volatile int act_cmd = 0; /* Start off */
+int8_t force_cmd = -1; /* Forced command value, for tests */
+static int fan_on = 0;
+int force_fan = 0; /* Request to force fan ON from test menu */
+
+
+void set_ctrl_duty_cycle(uint8_t value)
+{
+ act_cmd = value;
+ /* Below 3% and above 97% there are triggering problems which
+ * lead to 50% instead of desired command */
+ if (act_cmd >= 97) {
+ act_cmd = 100;
+ } else if (act_cmd <= 3) {
+ act_cmd = 0;
+ }
+}
+
+void ac_switch_on(uint32_t flags)
+{
+ gpio_clear(ac_ctrl);
+}
+
+static uint32_t clk_cycles_ac_zc = 0;
+extern uint32_t power_delay[101];
+volatile uint32_t zc_cnt = 0;
+
+void zero_cross(uint32_t unused)
+{
+ uint32_t delay = 0;
+
+ zc_cnt++;
+ gpio_set(ac_ctrl);
+ if (act_cmd == 0) {
+ return;
+ }
+ if (act_cmd == 100) {
+ gpio_clear(ac_ctrl);
+ return;
+ }
+ /* Set timer to trigger ac out ON at given delay */
+ if (0) {
+ /* Time based delay */
+ delay = clk_cycles_ac_zc * (100 - act_cmd);
+ } else {
+ /* Power based delay */
+ delay = power_delay[100 - act_cmd];
+ }
+ timer_set_match(LPC_TIMER_32B1, CHAN0, delay);
+ timer_restart(LPC_TIMER_32B1);
+}
+
+/* This one is used to synchronize to the real zero-crossing detection, if any. */
+void zero_cross_detect(uint32_t unused)
+{
+ timer_restart(LPC_TIMER_32B0);
+ zero_cross(0);
+}
+
+/* Handle the power command */
+#define CMD_UP_DELAY_RESET_VALUE 3
+void handle_cmd_update(uint32_t curent_tick)
+{
+ static uint8_t cmd = 0;
+ static uint8_t cmd_up_delay = CMD_UP_DELAY_RESET_VALUE;
+
+ /* Unable to read internal temperature : turn off heating */
+ if (error_shutdown == 1) {
+ cmd = 0;
+ force_fan = 1;
+ goto cmd_update_end;
+ }
+
+ /* Water max temperature protection */
+ if (water_centi_degrees > MAX_WATER_TEMP) {
+ cmd = 0;
+ goto cmd_update_end;
+ }
+
+ /* Forced test mode */
+ if (force_cmd != -1) {
+ cmd = force_cmd;
+ goto cmd_update_end;
+ }
+
+ /* Which is the current mode ? */
+ if (forced_heater_mode == 1) {
+ /* Forced heating mode */
+ if ((forced_heater_delay == 0) && (forced_heater_time > 0)) {
+ cmd = FORCED_MODE_VALUE;
+ goto cmd_update_end;
+ }
+ if (manual_forced_heater > 0) {
+ cmd = FORCED_MODE_VALUE;
+ goto cmd_update_end;
+ }
+ /* Entering forced heating mode from temperature getting below threshold,
+ * wait some delay before effective forced heating */
+ if (forced_heater_time == 0) {
+ forced_heater_delay = FORCED_HEATER_DELAY;
+ forced_heater_time = FORCED_HEATER_DELAY + FORCED_HEATER_DURATION;
+ }
+ }
+ if ((home_conso_value > 1280) && (solar_prod_value < (home_conso_value - 1280))) {
+ /* Very low production mode */
+ if (cmd >= 10) {
+ cmd -= 10;
+ } else {
+ cmd = 0;
+ }
+ } else if ((home_conso_value > 64) && (solar_prod_value < (home_conso_value - 64))) {
+ /* Low production mode */
+ if (cmd > 0) {
+ cmd--;
+ }
+ } else if (solar_prod_value > (home_conso_value + 1280)) {
+ if (cmd <= 98) {
+ cmd += 2;
+ } else {
+ cmd = 100;
+ }
+ } else if (solar_prod_value > (home_conso_value + 256)) {
+ /* High production mode */
+ if (cmd_up_delay > 0) {
+ cmd_up_delay--;
+ } else {
+ cmd_up_delay = CMD_UP_DELAY_RESET_VALUE;
+ if (cmd < 100) {
+ cmd++;
+ }
+ }
+ }
+
+cmd_update_end:
+ if ((cmd > 0) || (force_fan == 1)) {
+ fan_on = 1;
+ gpio_set(fan_ctrl);
+ } else {
+ fan_on = 0;
+ gpio_clear(fan_ctrl);
+ }
+ command_val = cmd;
+ /* Set Control Output duty cycle */
+ set_ctrl_duty_cycle(cmd);
+}
+
+/* Over-voltage protection */
+void overvoltage_protect(uint32_t gpio)
+{
+ gpio_clear(ac_ctrl);
+ timer_stop(LPC_TIMER_32B1);
+ set_ctrl_duty_cycle(100);
+ force_cmd = 100;
+ overvoltage = OVERVOLTAGE_PROTECTION_CYCLES;
+ gpio_set(fan_ctrl);
+}
+
+
+/* Update current working mode depending on environnement */
+volatile char mode = heat; /* Debug info */
+volatile char* msg = NULL;
+void mode_update(uint32_t unused)
+{
+ /* Default mode : try to heat the water tank */
+ mode = heat;
+
+ /* Need to enter Forced heating mode ? */
+ if (water_centi_degrees < FORCE_HEATER_TEMP) {
+ if (forced_heater_mode == 0) {
+ msg = "Water temp low, entering forced mode\n";
+ forced_heater_mode = 1;
+ }
+ status_led(red_on);
+ mode = forced;
+ } else if ((water_centi_degrees > TARGET_FORCED_HEATER_TEMP) && (forced_heater_mode == 1)) {
+ status_led(red_off);
+ msg = "Water temp OK, forced mode exit\n";
+ forced_heater_mode = 0;
+ mode = temp_OK;
+ }
+
+ /* Do not force if there is a lot of sun, it may be enough to heat again soon */
+ if ((solar_prod_value > sunny_days_prod_value_config) && (forced_heater_mode == 1)) {
+ mode = delayed_heat_prod;
+ forced_heater_mode = 0;
+ }
+
+ /* Do not force heating if disabled by external command */
+ external_disable = gpio_read(ext_disable_in_pin);
+ if ((external_disable == EXTERNAL_DISABLE_FORCE) && (forced_heater_mode != 0)) {
+ forced_heater_mode = 0;
+ mode = ext_disable;
+ msg = "Forced mode disabled by external input\n";
+ }
+ /* Get Over-Voltage or Over-Temperature information */
+ temp_shutdown = gpio_read(overtemperature_pin);
+ if (overvoltage > 0) {
+ uint32_t ov_tmp = gpio_read(overvoltage_pin);
+ if (ov_tmp == 0) {
+ overvoltage--;
+ if (overvoltage == 0) {
+ /* Get back to normal */
+ force_cmd = 0;
+ }
+ }
+ }
+
+ if (never_force == 1) {
+ forced_heater_mode = 0;
+ }
+
+ /* Did the user request a forced heating ? */
+ if (manual_activation_request != 0) {
+ forced_heater_mode = 1;
+ mode = manual;
+ if (manual_activation_request == -1) {
+ msg = "Entering manual forced mode\n";
+ manual_activation_request = MANUAL_ACTIVATION_DURATION;
+ manual_forced_heater = 1;
+ }
+ if (manual_activation_request < 10) {
+ msg = "Leaving manual forced mode\n";
+ manual_activation_request = 0;
+ }
+ }
+
+ /* Command at 100% and still more production than energy used ? */
+ if ((command_val == 100) && (solar_prod_value > home_conso_value)) {
+ mode = overprod;
+ }
+}
+
+/***************************************************************************** */
+/* Power tracking - load part */
+
+/* ADC values are between 0 and 1023, no load value is at about 520 */
+static volatile uint16_t load_power_highest = 520;
+static volatile uint16_t load_power_lowest = 520;
+void power_track(uint32_t flags)
+{
+ /* Start tracking the higest value */
+ static int tracking = 1;
+ static uint16_t last_value = 0;
+ uint16_t val = 0;
+
+ /* Get load power usage */
+ adc_get_value(&val, LPC_ADC(2));
+
+ /* Four possibilities :
+ * tracking high and value increasing : nothing to do
+ * tracking high and value decreasing : we found the max, store it and change tracking
+ * tracking low and value decreasing : nothing to do
+ * tracking low and value increasing : we found the min, store it and change tracking
+ */
+ if (tracking == 1) {
+ if (last_value > val) {
+ /* Found max */
+ load_power_highest = last_value;
+ tracking = 0;
+ }
+ } else {
+ if (last_value < val) {
+ /* Found min */
+ load_power_lowest = last_value;
+ tracking = 1;
+ }
+ }
+ last_value = val;
+}
+
+
+/***************************************************************************** */
+void scialys_config(void)
+{
+ /* We want 100 Hz (50 Hz but two zero crossings) with 1% granularity */
+ clk_cycles_ac_zc = get_main_clock() / (100 * 100);
+ uprintf(UART0, "clk_cycles_ac_zc: %d\n", clk_cycles_ac_zc);
+
+ /* Configure the fake zero-cross generation timer to 100Hz.
+ * Do not start it now, it will be started by the first real zero-cross detected in order to
+ * generate the other half zc (not seen by system due to hardware design)
+ * Also sarted by config when DC mode is set */
+ timer_set_match(LPC_TIMER_32B0, CHAN0, (clk_cycles_ac_zc * 100));
+
+ /* Configure the power tracking timer.
+ * 20 points per half sin wave should be enough to get the min and max */
+ timer_set_match(LPC_TIMER_16B0, CHAN0, (clk_cycles_ac_zc * 5));
+ timer_start(LPC_TIMER_16B0);
+
+ /* Read parameters from memory */
+ if (1) {
+ never_force = 0;
+ sunny_days_prod_value_config = SUNNY_DAYS_PROD_VALUE;
+ }
+
+ /* Add a systick callback to handle command value update */
+ add_systick_callback(handle_cmd_update, CMD_UPD_PERIOD);
+
+ /* Add a systick callback to track ismail values (power consumption and production) */
+ add_systick_callback(track_isnail_values, ADC_UPD_PERIOD);
+
+ /* Add a systick callback to handle time counting */
+ add_systick_callback(handle_dec_request, DEC_PERIOD);
+}
+
+/* Add a false zero-cross, but make sure to do it only once and only if we are not AC powered */
+void DC_switch_start(void)
+{
+ static int already_done = 0;
+ if ((already_done == 0) && (zc_cnt == 0)) {
+ int ret = add_systick_callback(zero_cross, 10); /* 2 zero-crossing at 50Hz -> 100Hz -> 10ms */
+ uprintf(UART0, "Entering forced DC mode on systick callback %d\n", ret);
+ already_done = 1;
+ } else {
+ uprintf(UART0, "DC forced mode already ON: %d/%d\n", already_done, zc_cnt);
+ }
+}
+
+/***************************************************************************** */
+int main(void)
+{
+ uint32_t loop = 0;
+ int ret = 0;
+
+ system_init();
+ board_io_config();
+ modules_config();
+
+ external_config(UART0);
+ scialys_config();
+
+ msleep(500);
+ status_led(green_only);
+
+ while (1) {
+ /* Feed the dog */
+ if ((solar_prod_value != 0) && (home_conso_value != 0)) {
+ watchdog_feed();
+ }
+
+ /* Update internal temperatures */
+ if (1) {
+ /* Get internal temperatures */
+ ret = temp_read(UART0, &deci_degrees_disp, &deci_degrees_power);
+ if (ret != CONV_OK) {
+ if ((ret & CONV_POWER_OK) == 0) {
+ force_fan = 1;
+ }
+ if ((ret & CONV_DISPLAY_OK) == 0) {
+ force_fan = 1;
+ }
+ }
+ /* Internal temperature protection */
+ if ((deci_degrees_power > MAX_INTERNAL_TEMP) || (deci_degrees_disp > MAX_INTERNAL_TEMP)) {
+ error_shutdown = 1;
+ uprintf(UART0, "Error shutdown !\n");
+ } else if ((deci_degrees_power < ((MAX_INTERNAL_TEMP) / 2)) &&
+ (deci_degrees_disp < ((MAX_INTERNAL_TEMP) / 2))) {
+ error_shutdown = 0;
+ }
+ }
+
+ /* Update water tank temperature */
+ if (1) {
+ int ret = 0, old_water_temp = 0;
+ old_water_temp = water_centi_degrees;
+ /* Get thermocouple value */
+ ret = max31855_sensor_read(&thermocouple, NULL, &water_centi_degrees);
+ if (ret != 0) {
+ uprintf(UART0, "Water Temp read error : %d\n", ret);
+ /* Do not act upon iinvalid temp value */
+ water_centi_degrees = old_water_temp;
+ }
+ }
+
+ /* Update current mode */
+ mode_update(0);
+
+ /* Display */
+ if (1) {
+ interface_update(mode);
+ }
+
+ /* Debug */
+ if (1) {
+ uprintf(UART0, "#%c:%d:%d:%d:%d:%d:%d:",
+ mode, loop++, solar_prod_value, home_conso_value,
+ water_centi_degrees, deci_degrees_power, deci_degrees_disp);
+ uprintf(UART0, "%d:%d:%d:%d:%d:%d:%d:%d:%d:%d:%d:%d:%d$\n",
+ load_power_lowest, load_power_highest,
+ zc_cnt, command_val, act_cmd, fan_on, force_fan,
+ error_shutdown, temp_shutdown, overvoltage,
+ external_disable, forced_heater_mode, manual_activation_request);
+ if (msg != NULL) {
+ uprintf(UART0, (char*)msg);
+ msg = NULL;
+ }
+ }
+ }
+ return 0;
+}
+
+
+
--- /dev/null
+#include "lib/stdint.h"
+
+uint32_t power_delay[101] = {
+ [0] = 0,
+ [1] = 30608,
+ [2] = 43360,
+ [3] = 53195,
+ [4] = 61530,
+ [5] = 68911,
+ [6] = 75620,
+ [7] = 81822,
+ [8] = 87626,
+ [9] = 93107,
+ [10] = 98319,
+ [11] = 103305,
+ [12] = 108095,
+ [13] = 112716,
+ [14] = 117188,
+ [15] = 121527,
+ [16] = 125750,
+ [17] = 129867,
+ [18] = 133888,
+ [19] = 137823,
+ [20] = 141680,
+ [21] = 145465,
+ [22] = 149184,
+ [23] = 152843,
+ [24] = 156447,
+ [25] = 159999,
+ [26] = 163505,
+ [27] = 166967,
+ [28] = 170389,
+ [29] = 173774,
+ [30] = 177124,
+ [31] = 180443,
+ [32] = 183732,
+ [33] = 186995,
+ [34] = 190232,
+ [35] = 193446,
+ [36] = 196639,
+ [37] = 199813,
+ [38] = 202969,
+ [39] = 206109,
+ [40] = 209234,
+ [41] = 212347,
+ [42] = 215448,
+ [43] = 218539,
+ [44] = 221621,
+ [45] = 224695,
+ [46] = 227763,
+ [47] = 230827,
+ [48] = 233886,
+ [49] = 236944,
+ [50] = 240000,
+ [51] = 243055,
+ [52] = 246113,
+ [53] = 249172,
+ [54] = 252236,
+ [55] = 255304,
+ [56] = 258378,
+ [57] = 261460,
+ [58] = 264551,
+ [59] = 267652,
+ [60] = 270765,
+ [61] = 273890,
+ [62] = 277030,
+ [63] = 280186,
+ [64] = 283360,
+ [65] = 286553,
+ [66] = 289767,
+ [67] = 293004,
+ [68] = 296267,
+ [69] = 299556,
+ [70] = 302875,
+ [71] = 306225,
+ [72] = 309610,
+ [73] = 313032,
+ [74] = 316494,
+ [75] = 320000,
+ [76] = 323552,
+ [77] = 327156,
+ [78] = 330815,
+ [79] = 334534,
+ [80] = 338319,
+ [81] = 342176,
+ [82] = 346111,
+ [83] = 350132,
+ [84] = 354249,
+ [85] = 358472,
+ [86] = 362811,
+ [87] = 367283,
+ [88] = 371904,
+ [89] = 376694,
+ [90] = 381680,
+ [91] = 386892,
+ [92] = 392373,
+ [93] = 398177,
+ [94] = 404379,
+ [95] = 411088,
+ [96] = 418469,
+ [97] = 426804,
+ [98] = 436639,
+ [99] = 449391,
+ [100] = 480000,
+};
--- /dev/null
+#! /usr/bin/perl -w
+
+use strict;
+use warnings;
+
+use Math::Trig;
+
+my ($clocks, $outfile) = @ARGV;
+my ($fh, $angle, $v, $w, $idx);
+
+if (defined $outfile) {
+ open($fh, '>', $outfile);
+ print $fh "#include \"lib/stdint.h\"\n\n";
+ print $fh "uint32_t power_delay[101] = {\n";
+}
+
+for (my $i = 0; $i <= 100; $i++) {
+ $angle = $i * 0.02;
+ $v = (acos(1 - $angle) / pi * 100);
+ $w = int($v * $clocks);
+ if (not defined $outfile) {
+ print "$v - [$i] = $w,\n";
+ } else {
+ print $fh "\t[$i] = $w,\n";
+ }
+}
+
+if (defined $outfile) {
+ print $fh "};\n";
+ close($fh);
+}
+print "Done\n";
+
+
--- /dev/null
+/****************************************************************************
+ * apps/blyes/inter_lights/time.c
+ *
+ * Copyright 2018 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 = 0x18,
+ .month = 0x08,
+ .day = 0x09,
+ .hour = 0x13,
+ .min = 0x37,
+};
+int time_valid = 0;
+int rtc_conf_ok = 0;
+
+int 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 time_update(uint32_t uart, struct rtc_time* now)
+{
+ rtc_pcf85363_time_write(&rtc_conf, now);
+ return 0;
+}
+
+
+/***************************************************************************** */
+/* RTC init */
+void time_config(uint32_t i2c_bus_num, uint8_t rtc_addr)
+{
+ rtc_conf.bus_num = i2c_bus_num;
+ rtc_conf.addr = rtc_addr;
+}
+
--- /dev/null
+/****************************************************************************
+ * apps/blyes/inter_lights/time.h
+ *
+ * Copyright 2018 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 time_init_check(uint32_t uart);
+void time_config(uint32_t i2c_bus_num, uint8_t rtc_addr);
+
+#endif /* RTC_TIME_H */
+
--- /dev/null
+/****************************************************************************
+ * apps/blyes/inter_lights/uSD.c
+ *
+ * Copyright 2018 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/ssp.h"
+
+#include "lib/stdio.h"
+
+#include "uSD.h"
+
+
+/***************************************************************************** */
+/* SD/MMC Card */
+struct sdmmc_card micro_sd = {
+ .card_type = MMC_CARDTYPE_UNKNOWN,
+ .block_size = 64,
+};
+
+static uint8_t got_uSD = 0;
+
+/* Buffer to read / write uSD card */
+uint8_t mmc_data[MMC_BUF_SIZE];
+
+/* Read up to 4 blocks of 16 bytes each */
+int uSD_read(uint32_t offset, uint8_t nb_blocks)
+{
+ int i = 0, ret = 0;
+ if (got_uSD == 0) {
+ return -1;
+ }
+ if (nb_blocks > 4) {
+ return -2;
+ }
+ memset(mmc_data, 0, MMC_BUF_SIZE);
+ for (i = 0; i < nb_blocks; i++) {
+ ret = sdmmc_read_block(µ_sd, (offset + i), (mmc_data + (i * 16)));
+ /* FIXME : check return value */
+ }
+ return 0;
+}
+
+
+/* Write up to 4 blocks of 16 bytes each */
+int uSD_write(uint32_t offset, uint8_t nb_blocks)
+{
+ int i = 0, ret = 0;
+ if (got_uSD == 0) {
+ return -1;
+ }
+ if (nb_blocks > 4) {
+ return -1;
+ }
+ for (i = 0; i < nb_blocks; i++) {
+ ret = sdmmc_write_block(µ_sd, (offset + i), (mmc_data + (i * 16)));
+ /* FIXME : check return value */
+ }
+ return 0;
+}
+
+/* microSD card init */
+int uSD_detect(int uart)
+{
+ int i = 0, ret = 0, step = 0;
+ do {
+ step = 0;
+ ret = sdmmc_init(µ_sd);
+ if (ret == 0) {
+ step = 1;
+ msleep(10);
+ ret = sdmmc_init_wait_card_ready(µ_sd);
+ if (ret <= 1) {
+ step = 2;
+ ret = sdmmc_init_end(µ_sd);
+ }
+ }
+ uprintf(uart, "uSD init(%d): step:%d, ret: %d, type: %d, bs: %d\n",
+ i, step, ret, micro_sd.card_type, micro_sd.block_size);
+ i++;
+ } while (((ret != 0) || (micro_sd.card_type == MMC_CARDTYPE_UNKNOWN)) && (i < 10));
+
+ /* Got uSD ? */
+ if (i >= 10) {
+ uprintf(uart, "uSD init failed, no uSD card present.\n");
+ got_uSD = 0;
+ return -1;
+ }
+
+ /* uSD card detected */
+ got_uSD = 1;
+ /* got_uSD MUST be set to 1 from here on ! */
+ uSD_read(0, 4); /* Read 4 blocks at start of card */
+ /* FIXME : check that the card magic is present */
+ uprintf(uart, "uSD read: %s\n", mmc_data);
+
+ return 0;
+}
+
+
+int uSD_logs_init(int uart)
+{
+ /* FIXME : read 4 blocks at offset FIXME to get the last block used info */
+ return 0;
+}
+
+/***************************************************************************** */
+/* microSD card init */
+void uSD_config(struct pio* uSD_cs, uint32_t ssp_bus_num)
+{
+ memcpy(&(micro_sd.chip_select), uSD_cs, sizeof(struct pio));
+ micro_sd.ssp_bus_num = ssp_bus_num;
+}
+
--- /dev/null
+/****************************************************************************
+ * apps/blyes/inter_lights/usSD.h
+ *
+ * Copyright 2018 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 USD_H
+#define USD_H
+
+#include "extdrv/sdmmc.h"
+
+#define MMC_BUF_SIZE 64
+
+extern struct sdmmc_card micro_sd;
+extern uint8_t mmc_data[MMC_BUF_SIZE];
+
+int uSD_read(uint32_t offset, uint8_t nb_blocks);
+int uSD_write(uint32_t offset, uint8_t nb_blocks);
+
+/***************************************************************************** */
+/* microSD card init */
+int uSD_detect(int uart);
+void uSD_config(struct pio* uSD_cs, uint32_t ssp_bus_num);
+
+
+#endif /* USD_H */
+
+++ /dev/null
-# 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} $@
-
+++ /dev/null
-/****************************************************************************
- * apps/scialys/beta/main.c
- *
- * Scialys system for solar-panel power generation tracking and fair use.
- *
- * Copyright 2016 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 "lib/stdio.h"
-#include "lib/errno.h"
-#include "drivers/serial.h"
-#include "drivers/gpio.h"
-#include "drivers/adc.h"
-#include "drivers/ssp.h"
-#include "drivers/i2c.h"
-#include "drivers/timers.h"
-
-#include "extdrv/status_led.h"
-#include "extdrv/ws2812.h"
-#include "extdrv/max31855_thermocouple.h"
-#include "extdrv/tmp101_temp_sensor.h"
-#include "extdrv/rtc_pcf85363a.h"
-#include "extdrv/ssd130x_oled_driver.h"
-#include "lib/font.h"
-#include "lib/time.h"
-
-#include "extdrv/sdmmc.h"
-
-#define MODULE_VERSION 0x03
-#define MODULE_NAME "Scialys uC"
-
-
-#define SELECTED_FREQ FREQ_SEL_48MHz
-
-
-/***************************************************************************** */
-/* System configuration
- * Most of the defines in here should go to configuration setting in user flash
- */
-
-/* Period of the decrementer handler from the systick interrupt */
-#define DEC_PERIOD 100
-
-/* If temperature falls bellow FORCE_HEATER_TEMP value, we enter forced heater mode, until
- * TARGET_FORCED_HEATER_TEMP is reached.
- * When in forced heater mode, the heater is controlled to heat at FORCED_MODE_VALUE which
- * is between 0 and 100.
- */
-#define FORCE_HEATER_TEMP 28
-#define TARGET_FORCED_HEATER_TEMP 45
-#define FORCED_MODE_VALUE 75 /* A fraction of 100 */
-/* mA prod value above which the system will not enter forced mode, waiting for home
- * to stop using power to start automatic heating */
-#define NO_FORCED_HEATING_ON_SUNNY_DAYS 1000
-
-uint32_t forced_heater_mode = 0;
-uint32_t forced_heater_delay = 0;
-uint32_t forced_heater_time = 0;
-
-#define FORCED_HEATER_DELAY (2 * 3600 * 1000 / DEC_PERIOD) /* Delay before automatic forced heating */
-#define FORCED_HEATER_DURATION (3 * 3600 * 1000 / DEC_PERIOD) /* Duration of automatic forced heating */
-
-#define MANUAL_ACTIVATION_DURATION (3 * 3600 * 1000 / DEC_PERIOD) /* Three hours */
-
-uint32_t never_force = 0;
-
-
-#define DAY_IS_EJP 0 /* Input is pulled low when EJP is ON */
-int ejp_in = 0;
-
-
-
-/***************************************************************************** */
-/* Pins configuration */
-/* pins blocks are passed to set_pins() for pins configuration.
- * Unused pin blocks can be removed safely with the corresponding set_pins() call
- * All pins blocks may be safelly merged in a single block for single set_pins() call..
- */
-const struct pio_config common_pins[] = {
- /* UART 0 : Config / Debug / USB */
- { LPC_UART0_RX_PIO_0_1, LPC_IO_DIGITAL },
- { LPC_UART0_TX_PIO_0_2, LPC_IO_DIGITAL },
- /* UART 1 : UEXT */
- { LPC_UART1_RX_PIO_0_8, LPC_IO_DIGITAL },
- { LPC_UART1_TX_PIO_0_9, LPC_IO_DIGITAL },
- /* I2C : RTC, Display, UEXT */
- { 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) },
- /* SPI (Thermocouple + uSD card + UEXT) */
- { LPC_SSP0_SCLK_PIO_0_14, LPC_IO_DIGITAL },
- { LPC_SSP0_MOSI_PIO_0_17, LPC_IO_DIGITAL },
- { LPC_SSP0_MISO_PIO_0_16, LPC_IO_DIGITAL },
- /* GPIO */
- { LPC_GPIO_0_0, LPC_IO_DIGITAL }, /* Clkout / interrupt from RTC */
- { LPC_GPIO_0_3, LPC_IO_DIGITAL }, /* EJP / External switch input */
- { LPC_GPIO_0_4, LPC_IO_DIGITAL }, /* Zero crossing detection input */
- { LPC_GPIO_0_6, LPC_IO_DIGITAL }, /* Mosfet control */
- { LPC_GPIO_0_7, LPC_IO_DIGITAL }, /* Fan control */
- { LPC_GPIO_0_12, LPC_IO_DIGITAL }, /* ISP / User button OK */
- { LPC_GPIO_0_15, LPC_IO_DIGITAL }, /* Thermocouple chip select */
- { LPC_GPIO_0_21, LPC_IO_DIGITAL }, /* WS2812B RGB Leds control */
- { LPC_GPIO_0_22, LPC_IO_DIGITAL }, /* User button B4 */
- { LPC_GPIO_0_23, LPC_IO_DIGITAL }, /* User button B3 */
- { LPC_GPIO_0_24, LPC_IO_DIGITAL }, /* User button B2 */
- { LPC_GPIO_0_25, LPC_IO_DIGITAL }, /* User button B1 */
- { LPC_GPIO_0_28, LPC_IO_DIGITAL }, /* Charge State */
- { LPC_GPIO_1_1, LPC_IO_DIGITAL }, /* Uext Chip select / Module eeprom select */
- { LPC_GPIO_1_6, LPC_IO_DIGITAL }, /* uSD Card SPI Chip Select */
- ARRAY_LAST_PIO,
-};
-
-const struct pio_config adc_pins[] = {
- { LPC_ADC_AD0_PIO_0_30, LPC_IO_ANALOG }, /* ADC0 */
- { LPC_ADC_AD1_PIO_0_31, LPC_IO_ANALOG }, /* ADC1 */
- { LPC_ADC_AD2_PIO_1_0, LPC_IO_ANALOG }, /* ADC2 */
- { LPC_ADC_AD7_PIO_1_5, LPC_IO_ANALOG }, /* ADC3 */
- ARRAY_LAST_PIO,
-};
-
-const struct pio status_led_green = LPC_GPIO_0_26;
-const struct pio status_led_red = LPC_GPIO_0_27;
-
-/* Inputs */
-/* Buttons */
-const struct pio button_ok = LPC_GPIO_0_12;
-const struct pio button_b1 = LPC_GPIO_0_25;
-const struct pio button_b2 = LPC_GPIO_0_24;
-const struct pio button_b1 = LPC_GPIO_0_23;
-const struct pio button_b2 = LPC_GPIO_0_22;
-/* External signals */
-const struct pio rtc_in_pin = LPC_GPIO_0_0;
-const struct pio ejp_in_pin = LPC_GPIO_0_3;
-const struct pio zero_cross_in_pin = LPC_GPIO_0_4;
-const struct pio charge_status_in_pin = LPC_GPIO_0_28;
-
-/* Outputs */
-/* Led control data pin */
-const struct pio ws2812_data_out_pin = LPC_GPIO_0_21;
-/* AC output control (Mosfet) */
-const struct pio ac_ctrl = LPC_GPIO_0_6;
-const struct pio fan_ctrl = LPC_GPIO_0_7;
-
-
-/* Thermocouple reading */
-const struct max31855_sensor_config thermo = {
- .ssp_bus_num = 0,
- .chip_select = LPC_GPIO_0_15,
-};
-
-/* TMP101 onboard I2C temperature sensor */
-#define TMP101_ADDR 0x94 /* Pin Addr0 (pin5 of tmp101) connected to VCC */
-struct tmp101_sensor_config tmp101_sensor = {
- .bus_num = I2C0,
- .addr = TMP101_ADDR,
- .resolution = TMP_RES_ELEVEN_BITS,
-};
-
-
-const struct lpc_tc_config ac_timer_conf = {
- .mode = LPC_TIMER_MODE_TIMER | LPC_TIMER_MODE_MATCH,
- .match_control = { 0, LPC_TIMER_INT_RESET_AND_STOP_ON_MATCH, 0, 0, },
- .match = { 0, 10, 0, 0, },
- .ext_match_config = { 0, LPC_TIMER_SET_ON_MATCH, 0, 0, },
-};
-
-
-/***************************************************************************** */
-/* SD/MMC Card */
-struct sdmmc_card micro_sd = {
- .ssp_bus_num = SSP_BUS_0,
- .card_type = MMC_CARDTYPE_UNKNOWN,
- .block_size = 64,
- .chip_select = LPC_GPIO_1_6,
-};
-
-uint8_t mmc_data[MMC_MAX_SECTOR_SIZE];
-
-
-/***************************************************************************** */
-/* RTC and time */
-#define RTC_ADDR 0xA2
-struct rtc_pcf85363a_config rtc_conf = {
- .bus_num = I2C0,
- .addr = RTC_ADDR,
- .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 = 0x16,
- .month = 0x10,
- .day = 0x30,
- .hour = 0x13,
- .min = 0x00,
-};
-static struct rtc_time now;
-
-
-/***************************************************************************** */
-/* 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);
- set_pins(adc_pins);
- gpio_on();
- status_led_config(&status_led_green, &status_led_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);
-}
-
-
-/***************************************************************************** */
-/* Some global flags or variables */
-static int act_cmd = 0;
-static int fan_on = 0;
-
-
-/***************************************************************************** */
-/* System configuration over USB */
-void config_rx(uint8_t c)
-{
- /* FAN control */
- if (c == 'f') {
- gpio_set(fan_ctrl);
- } else {
- gpio_clear(fan_ctrl);
- }
-}
-
-
-
-/***************************************************************************** */
-/* System communication over UART1 */
-void cmd_rx(uint8_t c)
-{
-}
-
-
-
-/***************************************************************************** */
-/* AC control */
-
-void set_ctrl_duty_cycle(uint8_t value)
-{
- act_cmd = value;
- if (act_cmd > 100) {
- /* 100 is the maximum allowed value */
- act_cmd = 100;
- } else if (act_cmd <= 2) {
- /* Below 3% there are triggering problems which lead to 50% instead of 1% or 2% */
- act_cmd = 0;
- }
-}
-
-void ac_switch_on(uint32_t flags)
-{
- gpio_set(ac_ctrl);
- gpio_set(button_b2);
-}
-
-static uint32_t clk_cycles_ac_zc = 0;
-static volatile uint32_t zc_count = 0; /* Wraps every 1.36 year ... */
-void zero_cross(uint32_t gpio)
-{
- uint32_t delay = 0;
-
- zc_count ++;
- gpio_toggle(button_b1);
-
- gpio_clear(ac_ctrl);
- gpio_clear(button_b2);
- if (act_cmd == 0) {
- return;
- }
- /* Set timer to trigger ac out ON at given delay */
- delay = clk_cycles_ac_zc * act_cmd;
- timer_set_match(LPC_TIMER_32B1, CHAN1, delay);
- timer_restart(LPC_TIMER_32B1);
-}
-
-
-
-/***************************************************************************** */
-/* System interface */
-enum buttons {
- BUTTON_NONE = 0,
- BUTTON_OK,
- BUTTON_UP,
- BUTTON_DOWN,
-};
-
-
-uint32_t manual_activation_request = 0;
-uint8_t button_pressed = 0;
-void manual_activation(uint32_t gpio) {
- manual_activation_request = MANUAL_ACTIVATION_DURATION;
- button_pressed = BUTTON_OK;
-}
-void manual_up(uint32_t gpio) {
- manual_activation_request = MANUAL_ACTIVATION_DURATION;
- button_pressed = BUTTON_UP;
-}
-void manual_down(uint32_t gpio) {
- manual_activation_request = MANUAL_ACTIVATION_DURATION;
- button_pressed = BUTTON_DOWN;
-}
-void handle_dec_request(uint32_t curent_tick) {
- if (manual_activation_request > 0) {
- manual_activation_request--;
- }
- if (forced_heater_mode == 1) {
- if (forced_heater_delay > 0) {
- forced_heater_delay--;
- }
- if (forced_heater_time > 0) {
- forced_heater_time--;
- }
- }
-}
-
-
-/***************************************************************************** */
-void temp_config(int uart_num)
-{
- int ret = 0;
- ret = tmp101_sensor_config(&tmp101_sensor);
- if (ret != 0) {
- uprintf(uart_num, "Temp config error: %d\n", ret);
- }
-}
-
-
-/***************************************************************************** */
-/* Oled Display */
-#define DISPLAY_ADDR 0x78
-struct oled_display display = {
- .address = DISPLAY_ADDR,
- .bus_num = I2C0,
- .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,
-};
-
-#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_set_tile(&display, col, line, tile_data);
-}
-#define OLED_LINE_CHAR_LENGTH (SSD130x_NB_COL / 8)
-#define DISPLAY_LINE_LENGTH (OLED_LINE_CHAR_LENGTH + 1)
-int display_line(uint8_t line, uint8_t col, 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_set_tile(&display, col++, line, tile_data);
- if (col >= (OLED_LINE_CHAR_LENGTH)) {
- col = 0;
- line++;
- if (line >= SSD130x_NB_PAGES) {
- return i;
- }
- }
- }
- return len;
-}
-
-
-
-
-/***************************************************************************** */
-void scialys_config(void)
-{
- /* Immediatly turn off Mosfet / Triac */
- config_gpio(&ac_ctrl, 0, GPIO_DIR_OUT, 0);
- /* Start with FAN ON */
- config_gpio(&fan_ctrl, 0, GPIO_DIR_OUT, 1);
-
- /* Thermocouple configuration */
- max31855_sensor_config(&thermo);
- uprintf(UART0, "Thermocouple config done\n");
-
- /* TMP101 sensor config */
- temp_config(UART0);
-
- /* Activate on Rising edge (button release) */
- set_gpio_callback(manual_activation, &button_ok, EDGE_RISING);
-#if 0
- set_gpio_callback(manual_up, &button_b1, EDGE_RISING);
- set_gpio_callback(manual_down, &button_b2, EDGE_RISING);
-#else
- config_gpio(&button_b1, 0, GPIO_DIR_OUT, 0);
- config_gpio(&button_b2, 0, GPIO_DIR_OUT, 0);
-#endif
-
- /* Zero cross */
- set_gpio_callback(zero_cross, &zero_cross_in_pin, EDGE_FALLING);
-
- /* Start ADC sampling */
- adc_start_burst_conversion(ADC_MCH(0) | ADC_MCH(1) | ADC_MCH(2) | ADC_MCH(7), LPC_ADC_SEQ(0));
-
- /* Configure Input GPIO */
- config_gpio(&ejp_in_pin, 0, GPIO_DIR_IN, 0);
- config_gpio(&rtc_in_pin, 0, GPIO_DIR_IN, 0);
- config_gpio(&charge_status_in_pin, 0, GPIO_DIR_IN, 1);
-
- /* WS2812B Leds on display board */
- ws2812_config(&ws2812_data_out_pin);
-
- /* AC Switch Config */
- timer_counter_config(LPC_TIMER_32B1, &ac_timer_conf);
- /* We want 100 Hz (50 Hz but two zero crossings) with 1% granularity */
- clk_cycles_ac_zc = get_main_clock() / (100 * 100);
-
- status_led(green_only);
-
- /* Configure and start display */
- ret = ssd130x_display_on(&display);
- /* Erase screen */
- ssd130x_display_set(&display, 0x00);
- ret = ssd130x_display_full_screen(&display);
-
- /* RTC init */
- ret = rtc_pcf85363a_config(&rtc_conf);
- ret = rtc_pcf85363a_is_up(&rtc_conf, &oldest);
- if (ret == 1) {
- char buff[30];
- rtc_pcf85363_time_read(&rtc_conf, &now);
- rtc_pcf85363_time_to_str(&now, buff, 30);
- /* Debug */
- uprintf(UART0, buff);
- } else if (ret == -EFAULT) {
- memcpy(&now, &oldest, sizeof(struct rtc_time));
- rtc_pcf85363_time_write(&rtc_conf, &now);
- }
-
- /* microSD card init */
- ret = sdmmc_init(µ_sd);
- if (ret == 0) {
- msleep(1);
- ret = sdmmc_init_wait_card_ready(µ_sd);
- if (ret == 0) {
- ret = sdmmc_init_end(µ_sd);
- }
- }
- uprintf(UART0, "uSD init: %d, type: %d, bs: %d\n", ret, micro_sd.card_type, micro_sd.block_size);
- ret = sdmmc_read_block(µ_sd, 0, mmc_data);
- uprintf(UART0, "uSD read: %s\n", mmc_data);
-
- /* Add a systick callback to handle time counting */
- //add_systick_callback(handle_dec_request, DEC_PERIOD);
-
- msleep(50);
- /* Read parameters from memory */
- if (1) {
- never_force = 0;
- forced_heater_delay = 0;
- forced_heater_time = FORCED_HEATER_DURATION;
- }
-}
-
-/***************************************************************************** */
-#define NB_VAL 20
-
-enum modes {
- heat = 'C',
- ejp = 'E',
- delayed_heat_prod = 'P',
- forced = 'F',
- temp_OK = 'T',
- manual = 'M',
- idle_heat = 'L',
- full_heat = 'F',
-};
-
-/***************************************************************************** */
-int main(void)
-{
- uint16_t isnail_solar_values[NB_VAL];
- uint16_t isnail_home_values[NB_VAL];
- uint8_t idx = 0;
- uint32_t loop = 0;
- char mode = heat; /* Debug info */
- int ret = 0;
-
- system_init();
- status_led(red_only);
- uart_on(UART0, 115200, config_rx);
- uart_on(UART1, 115200, cmd_rx);
- i2c_on(I2C0, I2C_CLK_100KHz, I2C_MASTER);
- ssp_master_on(thermo.ssp_bus_num, LPC_SSP_FRAME_SPI, 8, 4*1000*1000);
- adc_on(NULL);
- timer_on(LPC_TIMER_32B1, 0, ac_switch_on);
-
- scialys_config(void)
-
- while (1) {
- static uint8_t command_val = 0;
- static uint8_t n_dec = 0; /* Add some PID like (derivative part) */
- static uint8_t n_inc = 0; /* Add some PID like (derivative part) */
- int moyenne_solar = 0;
- int moyenne_home = 0;
- uint16_t isnail_val_solar = 0;
- uint16_t isnail_val_home = 0;
- uint16_t acs_val_load = 0;
- uint16_t user_potar = 0;
- int water_centi_degrees = 0;
- int tmp101_deci_degrees = 0;
-
- mode = heat;
- tmp101_sensor_start_conversion(&tmp101_sensor);
- /* Always track power consumption and production */
- adc_get_value(&isnail_val_solar, LPC_ADC(1));
- adc_get_value(&isnail_val_home, LPC_ADC(0));
- adc_get_value(&acs_val_load, LPC_ADC(2));
- adc_get_value(&user_potar, LPC_ADC(7));
- /* Convert to mA value */
- isnail_val_solar = ((isnail_val_solar * 32) * 2); /* 3.2mV / digit, 50mV -> 1A */
- isnail_val_home = ((isnail_val_home * 32) * 2); /* 3.2mV / digit, 50mV -> 1A */
- /* Store value */
- isnail_solar_values[idx] = isnail_val_solar;
- isnail_home_values[idx++] = isnail_val_home;
- if (idx == NB_VAL) {
- idx = 0;
- }
- /* Compute average value when we sampled enough values */
- /* FIXME : Improve by substracting oldest value before storing new one in table and adding new one */
- if ((idx == 0) || (idx == (NB_VAL / 2))) {
- int i = 0;
- for (i = 0; i < NB_VAL; i++) {
- moyenne_solar += isnail_solar_values[i];
- moyenne_home += isnail_home_values[i];
- }
- moyenne_solar = moyenne_solar / NB_VAL;
- moyenne_home = moyenne_home / NB_VAL;
- } else {
- /* Sleep for a litle more than a period (20ms at 50Hz) */
- msleep(23);
- continue;
- }
-
- /* Feed the dog */
- if ((moyenne_solar != 0) && (moyenne_home != 0)) {
- watchdog_feed();
- }
-
- /* Get internal temperature */
- if (1) {
- int ret = 0;
- msleep(40);
- ret = tmp101_sensor_read(&tmp101_sensor, NULL, &tmp101_deci_degrees);
- if (ret != 0) {
- uprintf(UART0, "TMP101 read error : %d\n", ret);
- }
- }
- /* If internal temperature is above 30°C, then turn on fan. Turn of when back to under 28.5°C */
- if (tmp101_deci_degrees > 300) {
- fan_on = 1;
- gpio_set(fan_ctrl);
- } else if (tmp101_deci_degrees < 285) {
- fan_on = 0;
- gpio_clear(fan_ctrl);
- }
-
- /* Get thermocouple value */
- if (1) {
- int ret = 0;
- ret = max31855_sensor_read(&thermo, NULL, &water_centi_degrees);
- if (ret != 0) {
- uprintf(UART0, "Water Temp read error : %d\n", ret);
- }
- }
-
- /* Need to enter Forced heating mode ? */
- if (water_centi_degrees < (FORCE_HEATER_TEMP * 100)) {
- if (forced_heater_mode == 0) {
- uprintf(UART0, "Entering forced mode\n");
- forced_heater_mode = 1;
- }
- status_led(red_on);
- mode = forced;
- } else if ((water_centi_degrees > (TARGET_FORCED_HEATER_TEMP * 100)) && (forced_heater_mode == 1)) {
- status_led(red_off);
- forced_heater_mode = 0;
- command_val = 0;
- uprintf(UART0, "Forced mode exit\n");
- mode = temp_OK;
- }
-
- /* Do not force if there is a lot of sun, it may be enough to heat again soon */
- if (moyenne_solar > NO_FORCED_HEATING_ON_SUNNY_DAYS) {
- mode = delayed_heat_prod;
- forced_heater_mode = 0;
- /* Note : Do not set forced_heater_mode to 0 in order to keep decrementing the delay for force
- * heating in case the house power usage does not fall below the production value. */
- }
-
- /* Do not force heating if this is an EJP day */
- ejp_in = gpio_read(ejp_in_pin);
- if (ejp_in == DAY_IS_EJP) {
- forced_heater_mode = 0;
- mode = ejp;
- }
-
- if (never_force == 1) {
- forced_heater_mode = 0;
- }
-
- /* Did the user request a forced heating ? */
- if (manual_activation_request > 1) {
- forced_heater_mode = 1;
- mode = manual;
- if (manual_activation_request == MANUAL_ACTIVATION_DURATION) {
- uprintf(UART0, "Entering manual forced mode for %d ticks\n", manual_activation_request);
- /* Add a systick callback to handle time counting */
- add_systick_callback(handle_dec_request, DEC_PERIOD);
- }
- if (manual_activation_request < 10) {
- uprintf(UART0, "Leaving manual forced mode\n");
- manual_activation_request = 0;
- remove_systick_callback(handle_dec_request);
- }
- }
-
-
- /* Which is the current mode ? */
- if (forced_heater_mode == 1) {
- /* Forced heating mode */
- if ((forced_heater_delay == 0) && (forced_heater_time > 0)) {
- command_val = FORCED_MODE_VALUE;
- }
- if (forced_heater_time == 0) {
- forced_heater_delay = FORCED_HEATER_DELAY;
- forced_heater_time = FORCED_HEATER_DURATION;
- }
- } else if (moyenne_solar < (moyenne_home - 75)) {
- /* Low production mode */
- if (command_val > 15) {
- command_val -= ((3 + n_dec) % 15);
- /* Asservissement */
- n_dec++;
- if (n_dec >= 3) {
- n_inc = 0;
- }
- } else {
- command_val = 0;
- mode = idle_heat;
- }
- status_led(green_off);
- } else if (moyenne_solar > (moyenne_home + 75)) {
- /* High production mode */
- if (command_val < 95) {
- command_val += (2 + n_inc);
- /* Asservissement */
- n_inc++;
- if (n_inc >= 5) {
- n_inc = 5;
- n_dec = 0;
- }
- } else {
- command_val = 100;
- mode = full_heat;
- }
- status_led(green_on);
- }
-
- /* Set Control Output duty cycle */
- //set_ctrl_duty_cycle(command_val);
- /* Debug Nath TMP */
- set_ctrl_duty_cycle( (user_potar - 5) / 10 );
-
- /* Display */
- if (1) {
- int abs_centi = water_centi_degrees;
- int abs_deci = tmp101_deci_degrees;
-
- if (water_centi_degrees < 0) {
- abs_centi = -water_centi_degrees;
- }
- if (tmp101_deci_degrees < 0) {
- abs_deci = -tmp101_deci_degrees;
- }
-
- if (1) {
- uprintf(UART0, "%c:%d - Is: %d,%04d - Ih: %d,%04d\n", mode, loop++,
- (moyenne_solar / 1000), (moyenne_solar % 1000),
- (moyenne_home / 1000), (moyenne_home % 1000));
- uprintf(UART0, "Water Temp : % 4d.%02d\n", (water_centi_degrees / 100), (abs_centi % 100));
- uprintf(UART0, "Internal Temp : % 4d.%02d\n", (tmp101_deci_degrees / 10), (abs_deci % 10));
- uprintf(UART0, "ADC: Sol: %dmA, Home: %dmA, Load: %d, User: %d\n",
- isnail_val_solar, isnail_val_home, acs_val_load, user_potar);
- if (button_pressed != 0) {
- uprintf(UART0, "Button : %d\n", button_pressed);
- button_pressed = 0;
- }
- uprintf(UART0, "ZC_cnt: %d, user: %d\n", zc_count, user_potar / 10);
- uprintf(UART0, "CMD: %d/%d, Fan: %d, ndec:%d, ninc:%d\n\n", command_val, act_cmd, fan_on, n_dec, n_inc);
- }
-
- if (1) {
- ws2812_set_pixel(0, (isnail_val_home / 2000), (isnail_val_solar / 2000), (fan_on * 20));
- ws2812_set_pixel(1, 0, 0, (user_potar >> 2));
- ws2812_send_frame(0);
- }
-
- if (1) {
- char line[DISPLAY_LINE_LENGTH];
- /* Erase screen (internal copy) */
- ssd130x_display_set(&display, 0x00);
- /* Update time and time display on internal memory */
- rtc_pcf85363_time_read(&rtc_conf, &now);
- snprintf(line, DISPLAY_LINE_LENGTH, "%02xh%02x:%02x", now.hour, now.min, now.sec);
- display_line(0, 0, line);
- /* Display info */
- snprintf(line, DISPLAY_LINE_LENGTH, "Water:% 2d.%03d %cC", (water_centi_degrees / 100), (abs_centi % 100), 0x1F);
- display_line(2, 0, line);
- snprintf(line, DISPLAY_LINE_LENGTH, "Prod :% 2d,%03dA", (isnail_val_solar / 1000), ((isnail_val_solar % 1000) / 10));
- display_line(3, 0, line);
- snprintf(line, DISPLAY_LINE_LENGTH, "Conso:% 2d,%03dA", (isnail_val_home / 1000), ((isnail_val_home % 1000) / 10));
- display_line(4, 0, line);
- snprintf(line, DISPLAY_LINE_LENGTH, "Command: %d%%", act_cmd);
- display_line(5, 0, line);
- snprintf(line, DISPLAY_LINE_LENGTH, "Mode: %c", mode);
- display_line(6, 0, line);
- /* Update Oled display */
- ret = ssd130x_display_full_screen(&display);
- }
- }
- }
- return 0;
-}
-
-
-
+++ /dev/null
-# 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} $@
-
+++ /dev/null
-/****************************************************************************
- * apps/scialys/v05/config.c
- *
- * Copyright 2018 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 "drivers/adc.h"
-#include "drivers/ssp.h"
-#include "drivers/timers.h"
-
-
-#include "extdrv/status_led.h"
-#include "extdrv/max31855_thermocouple.h"
-
-#include "lib/stdio.h"
-
-#include "config.h"
-#include "interface.h"
-#include "time.h"
-#include "uSD.h"
-
-#define SELECTED_FREQ FREQ_SEL_48MHz
-
-
-uint8_t config_got_interface = 1;
-
-/***************************************************************************** */
-/* Pins configuration */
-/* pins blocks are passed to set_pins() for pins configuration.
- * Unused pin blocks can be removed safely with the corresponding set_pins() call
- * All pins blocks may be safelly merged in a single block for single set_pins() call..
- */
-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) },
- /* SPI */
- { LPC_SSP0_SCLK_PIO_0_14, LPC_IO_DIGITAL },
- { LPC_SSP0_MOSI_PIO_0_17, LPC_IO_DIGITAL },
- { LPC_SSP0_MISO_PIO_0_16, LPC_IO_DIGITAL },
- /* ADC */
- { LPC_ADC_AD0_PIO_0_30, LPC_IO_ANALOG }, /* Home */
- { LPC_ADC_AD1_PIO_0_31, LPC_IO_ANALOG }, /* Production */
- { LPC_ADC_AD2_PIO_1_0, LPC_IO_ANALOG }, /* Load */
- { LPC_ADC_AD7_PIO_1_5, LPC_IO_ANALOG }, /* Display potentiometer */
- /* GPIO */
- { LPC_GPIO_0_0, LPC_IO_DIGITAL }, /* Clkout / interrupt from RTC */
- { LPC_GPIO_0_3, LPC_IO_DIGITAL }, /* External switch input */
- { LPC_GPIO_0_4, LPC_IO_DIGITAL }, /* Zero crossing detection input */
- { LPC_GPIO_0_6, LPC_IO_DIGITAL }, /* Mosfet control */
- { LPC_GPIO_0_7, LPC_IO_DIGITAL }, /* Fan control */
- { LPC_GPIO_0_12, LPC_IO_DIGITAL }, /* ISP / User button OK */
- { LPC_GPIO_0_15, LPC_IO_DIGITAL }, /* Thermocouple chip select */
- { LPC_GPIO_0_21, LPC_IO_DIGITAL }, /* WS2812B RGB Leds control */
- { LPC_GPIO_0_28, LPC_IO_DIGITAL }, /* Charge State */
- { LPC_GPIO_1_1, LPC_IO_DIGITAL }, /* Uext Chip select / Module eeprom select */
- { LPC_GPIO_1_6, LPC_IO_DIGITAL }, /* uSD Card SPI Chip Select */
- 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;
-
-/* Inputs */
-/* External signals */
-const struct pio zero_cross_in_pin = LPC_GPIO_0_4;
-const struct pio rtc_in_pin = LPC_GPIO_0_0;
-const struct pio ext_disable_in_pin = LPC_GPIO_0_3;
-const struct pio charge_status_in_pin = LPC_GPIO_0_28;
-
-/* Outputs */
-/* AC output control (Mosfet) */
-const struct pio ac_ctrl = LPC_GPIO_0_6;
-const struct pio fan_ctrl = LPC_GPIO_0_7;
-/* Chip selects */
-const struct pio uSD_cs = LPC_GPIO_1_6; /* uSD card */
-const struct pio th_cs = LPC_GPIO_0_15; /* Thermocouple */
-const struct pio uext_cs = LPC_GPIO_1_1; /* UEXT module (optionnal) */
-
-
-
-/***************************************************************************** */
-/* 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);
-}
-
-
-/***************************************************************************** */
-/* Internal modules */
-const struct lpc_tc_config ac_timer_conf = {
- .mode = LPC_TIMER_MODE_TIMER | LPC_TIMER_MODE_MATCH,
- .match_control = { 0, LPC_TIMER_INT_RESET_AND_STOP_ON_MATCH, 0, 0, },
- .match = { 0, 10, 0, 0, },
- .ext_match_config = { 0, LPC_TIMER_SET_ON_MATCH, 0, 0, },
-};
-
-extern void config_rx(uint8_t c);
-extern void comm_rx(uint8_t c);
-extern void ac_switch_on(uint32_t flags);
-
-void modules_config(void)
-{
- uart_on(UART0, 115200, config_rx);
- uart_on(UART1, 115200, comm_rx);
- i2c_on(I2C0, I2C_CLK_100KHz, I2C_MASTER);
- ssp_master_on(SSP_BUS_0, LPC_SSP_FRAME_SPI, 8, 4*1000*1000);
- adc_on(NULL);
- adc_start_burst_conversion(ADC_MCH(0) | ADC_MCH(1) | ADC_MCH(2) | ADC_MCH(7), LPC_ADC_SEQ(0));
- timer_on(LPC_TIMER_32B1, 0, ac_switch_on);
- timer_counter_config(LPC_TIMER_32B1, &ac_timer_conf);
-}
-
-
-/***************************************************************************** */
-extern void zero_cross(uint32_t gpio);
-void board_io_config(void)
-{
- /* Immediatly turn off Mosfet / Triac */
- config_gpio(&ac_ctrl, 0, GPIO_DIR_OUT, 0);
- /* Start with FAN ON */
- config_gpio(&fan_ctrl, 0, GPIO_DIR_OUT, 1);
-
- /* Configure Input GPIO */
- config_gpio(&ext_disable_in_pin, 0, GPIO_DIR_IN, 0);
- config_gpio(&rtc_in_pin, 0, GPIO_DIR_IN, 0);
- config_gpio(&charge_status_in_pin, 0, GPIO_DIR_IN, 1);
-
- /* Zero-crossing detection */
- set_gpio_callback(zero_cross, &zero_cross_in_pin, EDGE_FALLING);
-}
-
-
-
-/***************************************************************************** */
-/* Configure external components */
-
-/* Thermocouple reading */
-const struct max31855_sensor_config thermocouple = {
- .ssp_bus_num = 0,
- .chip_select = LPC_GPIO_0_15,
-};
-
-/* RTC and time */
-#define RTC_ADDR 0xA2
-
-
-int external_config(uint32_t uart)
-{
- int ret = 0;
-
- /* Configure GPIO for specific board usage */
- board_io_config();
-
- /* uSD card */
- uSD_config((struct pio *)&uSD_cs, SSP_BUS_0);
- uSD_detect(uart);
-
- /* RTC */
- time_config(I2C0, RTC_ADDR);
- time_init_check(uart);
-
- /* Thermocouple configuration */
- max31855_sensor_config(&thermocouple);
-
- /* Configure interface board */
- ret = interface_config(uart);
- if (ret != 0) {
- return -1;
- }
- return 0;
-}
+++ /dev/null
-/****************************************************************************
- * apps/blyes/inter_lights/config.h
- *
- * Copyright 2018 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 "drivers/adc.h"
-#include "drivers/ssp.h"
-#include "drivers/timers.h"
-
-#include "extdrv/status_led.h"
-#include "extdrv/max31855_thermocouple.h"
-#include "extdrv/ws2812.h"
-
-#include "lib/stdio.h"
-
-
-#define SELECTED_FREQ FREQ_SEL_48MHz
-
-/***************************************************************************** */
-/* Pins configuration */
-extern const struct pio status_led_green;
-extern const struct pio status_led_red;
-
-/* Inputs */
-/* External signals */
-extern const struct pio zero_cross_in_pin;
-extern const struct pio rtc_in_pin;
-extern const struct pio ext_disable_in_pin;
-extern const struct pio charge_status_in_pin;
-
-/* Outputs */
-/* AC output control (Mosfet) */
-extern const struct pio ac_ctrl;
-extern const struct pio fan_ctrl;
-/* Chip selects */
-extern const struct pio uSD_cs; /* uSD card */
-extern const struct pio th_cs; /* Thermocouple */
-extern const struct pio uext_cs; /* UEXT module (optionnal) */
-
-
-
-/***************************************************************************** */
-extern const struct max31855_sensor_config thermocouple;
-
-
-/***************************************************************************** */
-/* 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 modules and main functions */
-void modules_config(void);
-
-/* Configure GPIO for specific board usage */
-void board_io_config(void);
-
-/* Configure external components */
-int external_config(uint32_t uart);
-
-
-#endif /* CONFIG_H */
-
+++ /dev/null
-/****************************************************************************
- * apps/scialys/v05/interface.c
- *
- * Copyright 2018 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 "drivers/adc.h"
-
-#include "extdrv/status_led.h"
-#include "extdrv/ws2812.h"
-#include "extdrv/tmp101_temp_sensor.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 "interface.h"
-#include "time.h"
-
-/***************************************************************************** */
-/* Buttons inputs on front panel */
-const struct pio button_up = LPC_GPIO_0_25;
-#define GPIO_BUTTON_UP 25
-const struct pio button_right = LPC_GPIO_0_24;
-#define GPIO_BUTTON_RIGHT 24
-const struct pio button_left = LPC_GPIO_0_23;
-#define GPIO_BUTTON_LEFT 23
-const struct pio button_down = LPC_GPIO_0_22;
-#define GPIO_BUTTON_DOWN 22
-/* 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_21;
-
-
-static uint8_t interface_board_present = 0;
-
-/***************************************************************************** */
-/* TMP101 onboard I2C temperature sensor */
-#define TMP101_ADDR 0x94 /* Pin Addr0 (pin5 of tmp101) connected to VCC */
-struct tmp101_sensor_config tmp101_sensor = {
- .bus_num = I2C0,
- .addr = TMP101_ADDR,
- .resolution = TMP_RES_ELEVEN_BITS,
-};
-
-/***************************************************************************** */
-/* 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_EXT_VCC,
- .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, char* text)
-{
- int len = strlen((char*)text);
- int i = 0;
-
- if (interface_board_present == 0) {
- return -1;
- }
-
- 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;
-
- if (interface_board_present == 0) {
- return -1;
- }
-
- 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);
-}
-
-
-int temp_read(uint32_t uart, int* deci_degrees)
-{
- int ret = 0;
-
- if (interface_board_present == 0) {
- return -1;
- }
-
- ret = tmp101_sensor_read(&tmp101_sensor, NULL, deci_degrees);
- if (ret != 0) {
- uprintf(uart, "TMP101 read error : %d\n", ret);
- return -1;
- }
- return 0;
-}
-
-
-/***************************************************************************** */
-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;
- }
-}
-
-int interface_config(uint32_t uart)
-{
- int ret = 0;
-
- /* TMP101 sensor config */
- ret = tmp101_sensor_config(&tmp101_sensor);
- if (ret != 0) {
- uprintf(uart, "Temp config error: %d\n", ret);
- interface_board_present = 0;
- return -1;
- }
- ret = tmp101_sensor_set_continuous_conversion(&tmp101_sensor);
- if (ret != 0) {
- uprintf(uart, "Temp config error: %d\n", ret);
- interface_board_present = 0;
- return -2;
- }
- interface_board_present = 1;
-
- /* Buttons inputs on front panel */
- /* Activate on Rising edge (button release) */
- set_gpio_callback(button_callback, &button_up, EDGE_RISING);
- set_gpio_callback(button_callback, &button_left, EDGE_RISING);
- set_gpio_callback(button_callback, &button_right, EDGE_RISING);
- set_gpio_callback(button_callback, &button_down, EDGE_RISING);
- set_gpio_callback(button_callback, &button_ok, EDGE_RISING);
-
- /* WS2812B Leds on display board */
- ws2812_config(&ws2812_data_out_pin);
-
- /* Configure and start display */
- ssd130x_display_on(&display);
- erase_screen_content();
- ssd130x_display_full_screen(&display);
-
- ws2812_set_pixel(0, 0x05, 0x15, 0x08);
- ws2812_send_frame(0);
-
- uprintf(uart, "Config OK\n");
-
- return 0;
-}
-
-
-enum interface_modes {
- MODE_RUN = 0,
- MODE_CONFIG,
- MODE_DISPLAY,
-};
-
-extern int water_centi_degrees;
-extern uint32_t moyenne_solar;
-extern uint32_t moyenne_home;
-extern volatile uint8_t command_val;
-
-static int current_mode = MODE_RUN;
-volatile int manual_activation_request = 0;
-void interface_update(char heat_mode)
-{
- int abs_centi = water_centi_degrees;
- uint16_t user_potar = 0;
-
- if (water_centi_degrees < 0) {
- abs_centi = -water_centi_degrees;
- }
-
- /* Get potentiometer value */
- adc_get_value(&user_potar, LPC_ADC(7));
-
- if (current_mode == MODE_RUN) {
- if (button_pressed & BUTTON_OK) {
- manual_activation_request = -1;
- }
- if (interface_board_present != 0) {
- char line[DISPLAY_LINE_LENGTH];
-
- /* Start with a blank screen */
- erase_screen_content();
-
- /* Update time and time display on internal memory */
- rtc_pcf85363_time_read(&rtc_conf, &now);
- snprintf(line, DISPLAY_LINE_LENGTH, "%02xh%02x:%02x", now.hour, now.min, now.sec);
- display_line(0, 0, line);
-
- /* Display info */
- snprintf(line, DISPLAY_LINE_LENGTH, "Water:% 2d.%03d %cC", (water_centi_degrees / 100), (abs_centi % 100), 0x1F);
- display_line(2, 0, line);
- snprintf(line, DISPLAY_LINE_LENGTH, "Prod :% 2d,%03dA", (moyenne_solar / 1000), ((moyenne_solar % 1000) / 10));
- display_line(3, 0, line);
- snprintf(line, DISPLAY_LINE_LENGTH, "Conso:% 2d,%03dA", (moyenne_home / 1000), ((moyenne_home % 1000) / 10));
- display_line(4, 0, line);
- snprintf(line, DISPLAY_LINE_LENGTH, "Command: %d%%", command_val);
- display_line(5, 0, line);
- snprintf(line, DISPLAY_LINE_LENGTH, "Mode: %c", heat_mode);
- display_line(6, 0, line);
- /* Update Oled display */
- ssd130x_display_full_screen(&display);
- }
-
- /* Update RGB leds */
- ws2812_set_pixel(0, (moyenne_home / 2000), (moyenne_solar / 2000), 0);
- ws2812_set_pixel(1, 0, 0, (user_potar >> 2));
- ws2812_send_frame(0);
- }
-
- if (button_pressed != 0) {
- uprintf(UART0, "Button : 0x%02x\n", button_pressed);
- button_pressed = 0;
- }
-
-}
+++ /dev/null
-/****************************************************************************
- * apps/blyes/relay_lights/interface.h
- *
- * Copyright 2018 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 DISPLAY_LINE_LENGTH (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;
-
-extern volatile int manual_activation_request;
-
-/* 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);
-
-
-/* Internal Temperature sensor */
-int temp_read(uint32_t uart, int* deci_degrees);
-
-/* Interface content update */
-void interface_update(char mode);
-
-#endif /* INTERFACE_H */
-
+++ /dev/null
-/****************************************************************************
- * apps/scialys/v05/main.c
- *
- * Scialys system for solar-panel power generation tracking and fair use.
- *
- * Copyright 2016 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 "uSD.h"
-
-
-#define MODULE_VERSION 0x05
-#define MODULE_NAME "Scialys uC"
-
-
-
-/***************************************************************************** */
-/* System configuration */
-
-/* Period (in ms) of the decrementer handler from the systick interrupt */
-#define DEC_PERIOD 100
-/* Period (in ms) of the handler for the command value update */
-#define CMD_UPD_PERIOD 500
-
-/* Max water temperature. Internal protection which cannot be overriden by configuration */
-#define MAX_WATER_TEMP 90
-/* Internal system max temperature : turn off heating when reached */
-#define MAX_INTERNAL_TEMP 80 /* FIXME */
-
-/* Most of the defines in here should go to configuration setting in user flash */
-/* If temperature falls bellow FORCE_HEATER_TEMP value, we enter forced heater mode, until
- * TARGET_FORCED_HEATER_TEMP is reached.
- * When in forced heater mode, the heater is controlled to heat at FORCED_MODE_VALUE which
- * is between 0 and 100.
- */
-#define FORCE_HEATER_TEMP 30
-#define TARGET_FORCED_HEATER_TEMP 50
-#define FORCED_MODE_VALUE 75 /* A fraction of 100 */
-/* mA prod value above which the system will not enter forced mode, waiting for home
- * to stop using power to start automatic heating */
-#define SUNNY_DAYS_PROD_VALUE 3000
-
-/* Delay before automatic forced heating */
-#define FORCED_HEATER_DELAY (2 * 3600 * 1000 / DEC_PERIOD)
-/* Duration of automatic forced heating */
-#define FORCED_HEATER_DURATION (3 * 3600 * 1000 / DEC_PERIOD)
-
-/* Duration of manual forced heating */
-#define MANUAL_ACTIVATION_DURATION (3 * 3600 * 1000 / DEC_PERIOD) /* Three hours */
-
-
-uint32_t forced_heater_mode = 0;
-uint32_t forced_heater_delay = 0;
-uint32_t forced_heater_time = 0;
-
-uint8_t error_shutdown = 0;
-
-uint8_t never_force = 0;
-uint32_t sunny_days_prod_value_config = 0;
-
-#define EXTERNAL_DISABLE_FORCE 0 /* Input is pulled low when external disable is ON */
-int external_disable = 0;
-
-enum modes {
- heat = 'C',
- ext_disable = 'E',
- delayed_heat_prod = 'P',
- forced = 'F',
- temp_OK = 'T',
- manual = 'M',
- idle_heat = 'L',
- full_heat = 'F',
-};
-
-/* Water and internaltemperature */
-int water_centi_degrees = 0;
-int tmp101_deci_degrees = 0;
-
-/* RTC and time */
-struct rtc_time now;
-
-
-/***************************************************************************** */
-/* Rx interrupt handler for system configuration over USB */
-void config_rx(uint8_t c)
-{
- /* FAN control */
- if (c == 'f') {
- gpio_set(fan_ctrl);
- } else {
- gpio_clear(fan_ctrl);
- }
-}
-
-
-/* Communication with slave modules */
-void comm_rx(uint8_t c)
-{
-}
-
-
-
-/***************************************************************************** */
-/* System communication over UART1 */
-void cmd_rx(uint8_t c)
-{
-}
-
-
-/***************************************************************************** */
-/* Decrementer for heating timers */
-void handle_dec_request(uint32_t curent_tick) {
- if (manual_activation_request > 0) {
- manual_activation_request--;
- }
- if (forced_heater_mode == 1) {
- if (forced_heater_delay > 0) {
- forced_heater_delay--;
- }
- if (forced_heater_time > 0) {
- forced_heater_time--;
- }
- }
-}
-
-
-/***************************************************************************** */
-/* Track power production and usage */
-
-/* Average value computed on last 10 ADC values */
-uint32_t moyenne_solar = 0;
-uint32_t moyenne_home = 0;
-/* Last ADC values (snapshot) */
-static uint16_t snapval_solar = 0;
-static uint16_t snapval_home = 0;
-
-#define NB_VAL 20
-static void track_isnail_values(void)
-{
- static uint16_t isnail_solar_values[NB_VAL];
- static uint16_t isnail_home_values[NB_VAL];
- static uint8_t idx = 0;
-
- /* Get new values */
- adc_get_value(&snapval_solar, LPC_ADC(1));
- adc_get_value(&snapval_home, LPC_ADC(0));
-
- /* Convert to mA value.
- * ADC range is 0 to 1024 - approx 3.2mV / ADC step
- * Coil convertion is 1000mA -> 50mV : multily mV value by 20 to get mA value
- * x * 3.2 * 20 == x * 32 * 2 == x << 6
- * Increment is 64mA / ADC step */
- snapval_solar = snapval_solar << 6;
- snapval_home = snapval_home << 6;
-
- /* Store value */
- isnail_solar_values[idx] = snapval_solar;
- isnail_home_values[idx++] = snapval_home;
- if (idx == NB_VAL) {
- idx = 0;
- }
-
- /* Compute average once we sampled enough values */
- if ((idx == 0) || (idx == (NB_VAL / 2))) {
- int i = 0;
- moyenne_solar = 0;
- moyenne_home = 0;
- for (i = 0; i < NB_VAL; i++) {
- moyenne_solar += isnail_solar_values[i];
- moyenne_home += isnail_home_values[i];
- }
- moyenne_solar = moyenne_solar / NB_VAL;
- moyenne_home = moyenne_home / NB_VAL;
- }
-}
-
-/***************************************************************************** */
-/* AC control */
-
-volatile uint8_t command_val = 0;
-volatile int act_cmd = 0;
-static int fan_on = 0;
-
-void set_ctrl_duty_cycle(uint8_t value)
-{
- act_cmd = value;
- /* Below 3% and above 97% there are triggering problems which
- * lead to 50% instead of desired command */
- if (act_cmd >= 97) {
- act_cmd = 97;
- } else if (act_cmd <= 2) {
- act_cmd = 0;
- }
-}
-
-void ac_switch_on(uint32_t flags)
-{
- gpio_set(ac_ctrl);
-}
-
-static uint32_t clk_cycles_ac_zc = 0;
-volatile uint32_t zc_cnt = 0;
-void zero_cross(uint32_t gpio)
-{
- uint32_t delay = 0;
-
- gpio_clear(ac_ctrl);
- zc_cnt++;
- if (act_cmd == 0) {
- return;
- }
- /* Set timer to trigger ac out ON at given delay */
- delay = clk_cycles_ac_zc * (100 - act_cmd);
- timer_set_match(LPC_TIMER_32B1, CHAN1, delay);
- timer_restart(LPC_TIMER_32B1);
-}
-
-
-/* Handle the power command */
-#define CMD_UP_DELAY_RESET_VALUE 5
-void handle_cmd_update(uint32_t curent_tick)
-{
- static uint8_t cmd = 0;
- static uint8_t cmd_up_delay = CMD_UP_DELAY_RESET_VALUE;
-
- /* Unable to read internal temperature : turn off heating */
- if (error_shutdown == 1) {
- cmd = 0;
- goto cmd_update_end;
- }
-
- /* Water max temperature protection */
- if (water_centi_degrees > (MAX_WATER_TEMP * 100)) {
- cmd = 0;
- goto cmd_update_end;
- }
-
- /* Which is the current mode ? */
- if (forced_heater_mode == 1) {
- /* Forced heating mode */
- if ((forced_heater_delay == 0) && (forced_heater_time > 0)) {
- cmd = FORCED_MODE_VALUE;
- }
- /* Entering forced heating mode from temperature getting below threshold,
- * wait some delay before effective forced heating */
- if (forced_heater_time == 0) {
- forced_heater_delay = FORCED_HEATER_DELAY;
- forced_heater_time = FORCED_HEATER_DELAY + FORCED_HEATER_DURATION;
- }
- } else if (moyenne_solar < (moyenne_home - 64)) {
- /* Low production mode */
- if (cmd > 0) {
- cmd--;
- }
- } else if (moyenne_solar > (moyenne_home + 1280)) {
- if (cmd < 100) {
- cmd++;
- }
- } else if (moyenne_solar > (moyenne_home + 192)) {
- /* High production mode */
- if (cmd_up_delay > 0) {
- cmd_up_delay--;
- } else {
- cmd_up_delay = CMD_UP_DELAY_RESET_VALUE;
- if (cmd < 100) {
- cmd++;
- }
- }
- }
-
-cmd_update_end:
- command_val = cmd;
- /* Set Control Output duty cycle */
- set_ctrl_duty_cycle(100 - cmd);
-}
-
-
-/***************************************************************************** */
-void scialys_config(void)
-{
- /* We want 100 Hz (50 Hz but two zero crossings) with 1% granularity */
- clk_cycles_ac_zc = get_main_clock() / (100 * 100);
-
- /* Read parameters from memory */
- if (1) {
- never_force = 0;
- sunny_days_prod_value_config = SUNNY_DAYS_PROD_VALUE;
- }
-
- /* Add a systick callback to handle command value update */
- add_systick_callback(handle_cmd_update, CMD_UPD_PERIOD);
-
- /* Add a systick callback to handle time counting */
- add_systick_callback(handle_dec_request, DEC_PERIOD);
-}
-
-
-/***************************************************************************** */
-int main(void)
-{
- uint32_t loop = 0;
- char mode = heat; /* Debug info */
-
- system_init();
- board_io_config();
- modules_config();
-
- external_config(UART0);
- scialys_config();
-
- msleep(1500);
- status_led(green_only);
-
- while (1) {
- uint16_t snapval_load = 0;
-
- /* Default mode : try to heat the water tank */
- mode = heat;
-
- /* Always track power consumption and production */
- track_isnail_values();
-
- /* Get load power usage */
- adc_get_value(&snapval_load, LPC_ADC(2));
-
- /* Feed the dog */
- if ((moyenne_solar != 0) && (moyenne_home != 0)) {
- watchdog_feed();
- }
-
- /* Get internal temperature */
- if (temp_read(UART0, &tmp101_deci_degrees) != 0) {
- tmp101_deci_degrees = 1500;
- }
-
- /* Internal temperature protection */
- if (tmp101_deci_degrees > (MAX_INTERNAL_TEMP * 10)) {
- error_shutdown = 1;
- } else if (tmp101_deci_degrees < ((MAX_INTERNAL_TEMP * 10) / 2)) {
- error_shutdown = 0;
- }
- /* If internal temperature is above 32°C, then turn on fan. Turn off when back to under 28°C */
- if (tmp101_deci_degrees > 320) {
- fan_on = 1;
- gpio_set(fan_ctrl);
- } else if (tmp101_deci_degrees < 280) {
- fan_on = 0;
- gpio_clear(fan_ctrl);
- }
-
- /* Get thermocouple value */
- if (1) {
- int ret = 0, old_water_temp = 0;
- old_water_temp = water_centi_degrees;
- ret = max31855_sensor_read(&thermocouple, NULL, &water_centi_degrees);
- if (ret != 0) {
- uprintf(UART0, "Water Temp read error : %d\n", ret);
- /* Do not act upon iinvalid temp value */
- water_centi_degrees = old_water_temp;
- }
- }
-
- /* Need to enter Forced heating mode ? */
- if (water_centi_degrees < (FORCE_HEATER_TEMP * 100)) {
- if (forced_heater_mode == 0) {
- uprintf(UART0, "Water temp low, entering forced mode\n");
- forced_heater_mode = 1;
- }
- status_led(red_on);
- mode = forced;
- } else if ((water_centi_degrees > (TARGET_FORCED_HEATER_TEMP * 100)) && (forced_heater_mode == 1)) {
- status_led(red_off);
- uprintf(UART0, "Water temp OK, forced mode exit\n");
- forced_heater_mode = 0;
- mode = temp_OK;
- }
-
- /* Do not force if there is a lot of sun, it may be enough to heat again soon */
- if (moyenne_solar > sunny_days_prod_value_config) {
- mode = delayed_heat_prod;
- forced_heater_mode = 0;
- }
-
- /* Do not force heating if disabled by external command */
- external_disable = gpio_read(ext_disable_in_pin);
- if ((external_disable == EXTERNAL_DISABLE_FORCE) && (forced_heater_mode != 0)) {
- forced_heater_mode = 0;
- mode = ext_disable;
- }
-
- if (never_force == 1) {
- forced_heater_mode = 0;
- }
-
- /* Did the user request a forced heating ? */
- if (manual_activation_request != 0) {
- forced_heater_mode = 1;
- mode = manual;
- if (manual_activation_request == -1) {
- uprintf(UART0, "Entering manual forced mode for %d ticks\n", manual_activation_request);
- manual_activation_request = MANUAL_ACTIVATION_DURATION;
- forced_heater_time = FORCED_HEATER_DURATION;
- }
- if (manual_activation_request < 10) {
- uprintf(UART0, "Leaving manual forced mode\n");
- manual_activation_request = 0;
- }
- }
-
- /* Wait approx 10ms between each loop */
- msleep(10);
-
- /* Display */
- interface_update(mode);
-
- /* Debug */
- if (1) {
- uprintf(UART0, "%c:%d - Is: %d,%04d - Ih: %d,%04d\n", mode, loop++,
- (moyenne_solar / 1000), (moyenne_solar % 1000),
- (moyenne_home / 1000), (moyenne_home % 1000));
- uprintf(UART0, "Water Temp : %d\n", water_centi_degrees);
- uprintf(UART0, "Internal Temp : %d\n", tmp101_deci_degrees);
- uprintf(UART0, "ADC: Sol: %dmA, Home: %dmA, Load: %d\n",
- snapval_solar, snapval_home, snapval_load);
- uprintf(UART0, "Zc: %d\n", zc_cnt);
- uprintf(UART0, "CMD: %d/%d, Fan: %d\n\n", command_val, act_cmd, fan_on);
- }
-
- }
- return 0;
-}
-
-
-
+++ /dev/null
-/****************************************************************************
- * apps/blyes/inter_lights/time.c
- *
- * Copyright 2018 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 = 0x18,
- .month = 0x08,
- .day = 0x09,
- .hour = 0x13,
- .min = 0x37,
-};
-int time_valid = 0;
-int rtc_conf_ok = 0;
-
-int 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;
-}
-
-
-/***************************************************************************** */
-/* RTC init */
-void time_config(uint32_t i2c_bus_num, uint8_t rtc_addr)
-{
- rtc_conf.bus_num = i2c_bus_num;
- rtc_conf.addr = rtc_addr;
-}
-
+++ /dev/null
-/****************************************************************************
- * apps/blyes/inter_lights/time.h
- *
- * Copyright 2018 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 time_init_check(uint32_t uart);
-void time_config(uint32_t i2c_bus_num, uint8_t rtc_addr);
-
-#endif /* RTC_TIME_H */
-
+++ /dev/null
-/****************************************************************************
- * apps/blyes/inter_lights/uSD.c
- *
- * Copyright 2018 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/ssp.h"
-
-#include "lib/stdio.h"
-
-#include "uSD.h"
-
-
-/***************************************************************************** */
-/* SD/MMC Card */
-struct sdmmc_card micro_sd = {
- .card_type = MMC_CARDTYPE_UNKNOWN,
- .block_size = 64,
-};
-
-static uint8_t got_uSD = 0;
-
-/* Buffer to read / write uSD card */
-uint8_t mmc_data[MMC_BUF_SIZE];
-
-/* Read up to 4 blocks of 16 bytes each */
-int uSD_read(uint32_t offset, uint8_t nb_blocks)
-{
- int i = 0, ret = 0;
- if (got_uSD == 0) {
- return -1;
- }
- if (nb_blocks > 4) {
- return -2;
- }
- memset(mmc_data, 0, MMC_BUF_SIZE);
- for (i = 0; i < nb_blocks; i++) {
- ret = sdmmc_read_block(µ_sd, (offset + i), (mmc_data + (i * 16)));
- /* FIXME : check return value */
- }
- return 0;
-}
-
-
-/* Write up to 4 blocks of 16 bytes each */
-int uSD_write(uint32_t offset, uint8_t nb_blocks)
-{
- int i = 0, ret = 0;
- if (got_uSD == 0) {
- return -1;
- }
- if (nb_blocks > 4) {
- return -1;
- }
- for (i = 0; i < nb_blocks; i++) {
- ret = sdmmc_write_block(µ_sd, (offset + i), (mmc_data + (i * 16)));
- /* FIXME : check return value */
- }
- return 0;
-}
-
-/* microSD card init */
-int uSD_detect(int uart)
-{
- int i = 0, ret = 0, step = 0;
- do {
- step = 0;
- ret = sdmmc_init(µ_sd);
- if (ret == 0) {
- step = 1;
- msleep(10);
- ret = sdmmc_init_wait_card_ready(µ_sd);
- if (ret <= 1) {
- step = 2;
- ret = sdmmc_init_end(µ_sd);
- }
- }
- uprintf(uart, "uSD init(%d): step:%d, ret: %d, type: %d, bs: %d\n",
- i, step, ret, micro_sd.card_type, micro_sd.block_size);
- i++;
- } while (((ret != 0) || (micro_sd.card_type == MMC_CARDTYPE_UNKNOWN)) && (i < 10));
-
- /* Got uSD ? */
- if (i >= 10) {
- uprintf(uart, "uSD init failed, no uSD card present.\n");
- got_uSD = 0;
- return -1;
- }
-
- /* uSD card detected */
- got_uSD = 1;
- /* got_uSD MUST be set to 1 from here on ! */
- uSD_read(0, 4); /* Read 4 blocks at start of card */
- /* FIXME : check that the card magic is present */
- uprintf(uart, "uSD read: %s\n", mmc_data);
-
- return 0;
-}
-
-
-int uSD_logs_init(int uart)
-{
- /* FIXME : read 4 blocks at offset FIXME to get the last block used info */
- return 0;
-}
-
-/***************************************************************************** */
-/* microSD card init */
-void uSD_config(struct pio* uSD_cs, uint32_t ssp_bus_num)
-{
- memcpy(&(micro_sd.chip_select), uSD_cs, sizeof(struct pio));
- micro_sd.ssp_bus_num = ssp_bus_num;
-}
-
+++ /dev/null
-/****************************************************************************
- * apps/blyes/inter_lights/usSD.h
- *
- * Copyright 2018 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 USD_H
-#define USD_H
-
-#include "extdrv/sdmmc.h"
-
-#define MMC_BUF_SIZE 64
-
-extern struct sdmmc_card micro_sd;
-extern uint8_t mmc_data[MMC_BUF_SIZE];
-
-int uSD_read(uint32_t offset, uint8_t nb_blocks);
-int uSD_write(uint32_t offset, uint8_t nb_blocks);
-
-/***************************************************************************** */
-/* microSD card init */
-int uSD_detect(int uart);
-void uSD_config(struct pio* uSD_cs, uint32_t ssp_bus_num);
-
-
-#endif /* USD_H */
-
+++ /dev/null
-# 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} $@
-
+++ /dev/null
-/****************************************************************************
- * apps/scialys/v07/config.c
- *
- * Copyright 2018 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 "drivers/adc.h"
-#include "drivers/ssp.h"
-#include "drivers/timers.h"
-
-
-#include "extdrv/status_led.h"
-#include "extdrv/max31855_thermocouple.h"
-
-#include "lib/stdio.h"
-
-#include "config.h"
-#include "interface.h"
-#include "time.h"
-#include "uSD.h"
-
-#define SELECTED_FREQ FREQ_SEL_48MHz
-
-
-uint8_t config_got_interface = 1;
-
-/***************************************************************************** */
-/* Pins configuration */
-/* pins blocks are passed to set_pins() for pins configuration.
- * Unused pin blocks can be removed safely with the corresponding set_pins() call
- * All pins blocks may be safelly merged in a single block for single set_pins() call..
- */
-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) },
- /* SPI */
- { LPC_SSP0_SCLK_PIO_0_14, LPC_IO_DIGITAL },
- { LPC_SSP0_MOSI_PIO_0_17, LPC_IO_DIGITAL },
- { LPC_SSP0_MISO_PIO_0_16, LPC_IO_DIGITAL },
- /* ADC */
- { LPC_ADC_AD0_PIO_0_30, LPC_IO_ANALOG }, /* Home */
- { LPC_ADC_AD1_PIO_0_31, LPC_IO_ANALOG }, /* Production */
- { LPC_ADC_AD2_PIO_1_0, LPC_IO_ANALOG }, /* Load */
- /* GPIO */
- { LPC_GPIO_0_3, LPC_IO_DIGITAL }, /* External switch input */
- { LPC_GPIO_0_4, LPC_IO_DIGITAL }, /* Zero crossing detection input */
- { LPC_GPIO_0_5, LPC_IO_DIGITAL }, /* Fan control */
- { LPC_GPIO_0_6, LPC_IO_DIGITAL }, /* Mosfet control */
- { LPC_GPIO_0_7, LPC_IO_DIGITAL }, /* Thermocouple chip select */
- { LPC_GPIO_0_12, LPC_IO_DIGITAL }, /* ISP / User button OK */
- { LPC_GPIO_0_18, LPC_IO_DIGITAL }, /* Uext Chip select / Module eeprom select */
- { LPC_GPIO_0_20, LPC_IO_DIGITAL }, /* Oled_Reset */
- { LPC_GPIO_0_21, LPC_IO_DIGITAL }, /* Oled Control / I2C SA0 */
- { LPC_GPIO_1_1, LPC_IO_DIGITAL }, /* uSD Card SPI Chip Select */
- { 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;
-
-/* Inputs */
-/* External signals */
-const struct pio zero_cross_in_pin = LPC_GPIO_0_4;
-const struct pio ext_disable_in_pin = LPC_GPIO_0_3;
-
-/* Outputs */
-/* AC output control (Mosfet) */
-const struct pio ac_ctrl = LPC_GPIO_0_6;
-const struct pio fan_ctrl = LPC_GPIO_0_5;
-/* Chip selects */
-const struct pio uSD_cs = LPC_GPIO_1_1; /* uSD card */
-const struct pio th_cs = LPC_GPIO_0_7; /* Thermocouple */
-const struct pio uext_cs = LPC_GPIO_0_18; /* UEXT module (optionnal) */
-
-
-
-/***************************************************************************** */
-/* 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);
-}
-
-
-/***************************************************************************** */
-/* Internal modules */
-const struct lpc_tc_config ac_timer_conf_zc = {
- .mode = LPC_TIMER_MODE_TIMER | LPC_TIMER_MODE_MATCH,
- .match_control = { LPC_TIMER_INT_RESET_ON_MATCH, 0, 0, 0, },
-};
-const struct lpc_tc_config ac_timer_conf_delay = {
- .mode = LPC_TIMER_MODE_TIMER | LPC_TIMER_MODE_MATCH,
- .match_control = { LPC_TIMER_INT_RESET_AND_STOP_ON_MATCH, 0, 0, 0, },
-};
-
-extern void config_rx(uint8_t c);
-extern void comm_rx(uint8_t c);
-extern void ac_switch_on(uint32_t flags);
-extern void zero_cross(uint32_t flags);
-
-void modules_config(void)
-{
- uart_on(UART0, 115200, config_rx);
- uart_on(UART1, 115200, comm_rx);
- i2c_on(I2C0, I2C_CLK_100KHz, I2C_MASTER);
- ssp_master_on(SSP_BUS_0, LPC_SSP_FRAME_SPI, 8, 4*1000*1000);
- adc_on(NULL);
- adc_start_burst_conversion(ADC_MCH(0) | ADC_MCH(1) | ADC_MCH(2), LPC_ADC_SEQ(0));
- timer_on(LPC_TIMER_32B0, 0, zero_cross);
- timer_counter_config(LPC_TIMER_32B0, &ac_timer_conf_zc);
- timer_on(LPC_TIMER_32B1, 0, ac_switch_on);
- timer_counter_config(LPC_TIMER_32B1, &ac_timer_conf_delay);
-}
-
-
-/***************************************************************************** */
-extern void zero_cross_detect(uint32_t gpio);
-void board_io_config(void)
-{
- /* Immediatly turn off Mosfet / Triac */
- config_gpio(&ac_ctrl, 0, GPIO_DIR_OUT, 1);
- /* Start with FAN ON */
- config_gpio(&fan_ctrl, 0, GPIO_DIR_OUT, 1);
-
- /* Configure Input GPIO */
- config_gpio(&ext_disable_in_pin, 0, GPIO_DIR_IN, 0);
-
- /* Zero-crossing detection */
- set_gpio_callback(zero_cross_detect, &zero_cross_in_pin, EDGE_FALLING);
-}
-
-
-
-/***************************************************************************** */
-/* Configure external components */
-
-/* Thermocouple reading */
-const struct max31855_sensor_config thermocouple = {
- .ssp_bus_num = 0,
- .chip_select = LPC_GPIO_0_7,
-};
-
-/* RTC and time */
-#define RTC_ADDR 0xA2
-
-
-int external_config(uint32_t uart)
-{
- int ret = 0;
-
- /* Configure GPIO for specific board usage */
- board_io_config();
-
- /* uSD card */
- uSD_config((struct pio *)&uSD_cs, SSP_BUS_0);
- uSD_detect(uart);
-
- /* RTC */
- time_config(I2C0, RTC_ADDR);
- time_init_check(uart);
-
- /* Thermocouple configuration */
- max31855_sensor_config(&thermocouple);
-
- /* Configure interface board */
- ret = interface_config(uart);
- if (ret != 0) {
- return -1;
- }
- return 0;
-}
+++ /dev/null
-/****************************************************************************
- * apps/scialys/v07/config.h
- *
- * Copyright 2018 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 "drivers/adc.h"
-#include "drivers/ssp.h"
-#include "drivers/timers.h"
-
-#include "extdrv/status_led.h"
-#include "extdrv/max31855_thermocouple.h"
-#include "extdrv/ws2812.h"
-
-#include "lib/stdio.h"
-
-
-#define SELECTED_FREQ FREQ_SEL_48MHz
-
-/***************************************************************************** */
-/* Pins configuration */
-extern const struct pio status_led_green;
-extern const struct pio status_led_red;
-
-/* Inputs */
-/* External signals */
-extern const struct pio zero_cross_in_pin;
-extern const struct pio ext_disable_in_pin;
-
-/* Outputs */
-/* AC output control (Mosfet) */
-extern const struct pio ac_ctrl;
-extern const struct pio fan_ctrl;
-/* Chip selects */
-extern const struct pio uSD_cs; /* uSD card */
-extern const struct pio th_cs; /* Thermocouple */
-extern const struct pio uext_cs; /* UEXT module (optionnal) */
-
-
-
-/***************************************************************************** */
-extern const struct max31855_sensor_config thermocouple;
-
-
-/***************************************************************************** */
-/* 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 modules and main functions */
-void modules_config(void);
-
-/* Configure GPIO for specific board usage */
-void board_io_config(void);
-
-/* Configure external components */
-int external_config(uint32_t uart);
-
-
-#endif /* CONFIG_H */
-
+++ /dev/null
-/****************************************************************************
- * apps/scialys/v07/interface.c
- *
- * Copyright 2018 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 "drivers/adc.h"
-
-#include "extdrv/status_led.h"
-#include "extdrv/ws2812.h"
-#include "extdrv/tmp101_temp_sensor.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 "interface.h"
-#include "time.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
-
-
-const struct pio oled_i2c_sa0 = LPC_GPIO_0_21; /* Oled Control / I2C SA0 */
-
-
-static uint8_t interface_board_present = 0;
-
-/***************************************************************************** */
-/* TMP101 onboard I2C temperature sensor */
-#define TMP101_ADDR 0x94 /* Pin Addr0 (pin5 of tmp101) connected to VCC */
-struct tmp101_sensor_config tmp101_sensor = {
- .bus_num = I2C0,
- .addr = TMP101_ADDR,
- .resolution = TMP_RES_ELEVEN_BITS,
-};
-
-/***************************************************************************** */
-/* 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;
-
- if (interface_board_present == 0) {
- return -1;
- }
-
- 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;
-
- if (interface_board_present == 0) {
- return -1;
- }
-
- 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);
-}
-
-
-int temp_read(uint32_t uart, int* deci_degrees)
-{
- int ret = 0;
-
- if (interface_board_present == 0) {
- return -1;
- }
-
- ret = tmp101_sensor_read(&tmp101_sensor, NULL, deci_degrees);
- if (ret != 0) {
- uprintf(uart, "TMP101 read error : %d\n", ret);
- return -1;
- }
- return 0;
-}
-
-
-/***************************************************************************** */
-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;
- }
-}
-
-int interface_config(uint32_t uart)
-{
- int ret = 0;
-
- /* TMP101 sensor config */
- ret = tmp101_sensor_config(&tmp101_sensor);
- if (ret != 0) {
- uprintf(uart, "Temp config error: %d\n", ret);
- interface_board_present = 0;
- return -1;
- }
- ret = tmp101_sensor_set_continuous_conversion(&tmp101_sensor);
- if (ret != 0) {
- uprintf(uart, "Temp config error: %d\n", ret);
- interface_board_present = 0;
- return -2;
- }
- interface_board_present = 1;
-
- /* Buttons inputs on front panel */
- /* Activate on Rising edge (button release) */
- set_gpio_callback(button_callback, &button_up, EDGE_RISING);
- set_gpio_callback(button_callback, &button_left, EDGE_RISING);
- set_gpio_callback(button_callback, &button_right, EDGE_RISING);
- set_gpio_callback(button_callback, &button_down, EDGE_RISING);
- set_gpio_callback(button_callback, &button_ok, EDGE_RISING);
-
-
- /* Configure and start display */
- config_gpio(&oled_i2c_sa0, 0, GPIO_DIR_OUT, 0); /* Low I2C address bit set to 0 */
- ssd130x_display_on(&display);
- erase_screen_content();
- ssd130x_display_full_screen(&display);
-
- ws2812_set_pixel(0, 0x05, 0x15, 0x08);
- ws2812_send_frame(0);
-
- uprintf(uart, "Config OK\n");
-
- return 0;
-}
-
-
-
-/***************************************************************************** */
- /* Menu part */
-
-enum interface_modes {
- MODE_RUN = 0,
- MODE_CONFIG,
- MODE_DISPLAY,
-};
-static int interface_mode = MODE_RUN;
-volatile int manual_activation_request = 0;
-
-enum menu_list {
- MAIN_MENU,
- MANUAL_MODE,
- DATE_CONFIG,
- LIMITS,
- FUNCTIONS,
- TEST_MODE,
- SAVE_CONFIG,
- NB_MENU, /* This one must be the last */
-};
-static const char* menu_titles[] = {
- [MAIN_MENU] = "Menu principal",
- [MANUAL_MODE] = "Marche Forcee",
- [DATE_CONFIG] = "Regl. Heure",
- [LIMITS] = "Limites",
- [FUNCTIONS] = "Fonctions",
- [TEST_MODE] = "Test mode",
- [SAVE_CONFIG] = "Save config",
-};
-static uint8_t current_menu = MAIN_MENU;
-static uint8_t current_entry = MANUAL_MODE;
-
-enum func_menu_list {
- FUNC_LOAD_TYPE,
- FUNC_CMD_TYPE,
- FUNC_NB_MENU, /* This one must be the last */
-};
-static const char* func_modes_titles[] = {
- [FUNC_LOAD_TYPE] = "Load Type",
- [FUNC_CMD_TYPE] = "CMD type",
-};
-static uint8_t func_cur_menu = FUNC_LOAD_TYPE;
-static uint8_t func_cur_entry = FUNC_LOAD_TYPE;
-
-enum limits_menu_list {
- TEMP_FORCED_IN,
- TEMP_FORCED_TARGET,
- FORCED_MODE_VAL,
- SUNNY_DAY_VALUE,
- LIM_NB_MENU, /* This one must be the last */
-};
-static const char* limits_modes_titles[] = {
- [TEMP_FORCED_IN] = "Water Min",
- [TEMP_FORCED_TARGET] = "Water Target",
- [FORCED_MODE_VAL] = "Forced cmd",
- [SUNNY_DAY_VALUE] = "Sun Prod",
-};
-static uint8_t limits_cur_menu = TEMP_FORCED_IN;
-static uint8_t limits_cur_entry = TEMP_FORCED_IN;
-
-enum test_menu_list {
- TEST_FAN,
- TEST_12V,
- TEST_CMDV,
- TEST_NB_MENU, /* This one must be the last */
-};
-static const char* test_modes_titles[] = {
- [TEST_FAN] = "Fan",
- [TEST_12V] = "DC 12V",
- [TEST_CMDV] = "CMD Val",
-};
-extern uint8_t force_fan;
-extern int8_t force_cmd;
-extern uint32_t zc_cnt;
-extern void zero_cross_detect(uint32_t gpio);
-static uint8_t test_cur_entry = TEST_FAN;
-
-
-/* Current sub-menu level. main menu is level 0 */
-int sub_menu_level = 0;
-
-
-void config_interface_handle(void)
-{
- char line[DISP_LLEN];
- 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;
- for (i = 1; i < NB_MENU; i++) {
- if (i != current_entry) {
- display_line(i + 1, 3, menu_titles[i]);
- } else {
- snprintf(line, DISP_LLEN, " ->%s", menu_titles[i]);
- display_line(i + 1, 0, line);
- }
- }
- 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) {
- current_entry = NB_MENU - 1;
- }
- if (button & (BUTTON_OK | BUTTON_RIGHT)) {
- current_menu = current_entry;
- sub_menu_level = 1;
- }
- if (button & BUTTON_LEFT) {
- interface_mode = MODE_RUN;
- }
- }
- break;
- case DATE_CONFIG: {
- static uint8_t date_idx = 0;
- struct rtc_time now;
- snprintf(line, DISP_LLEN, "^");
- display_line(3, 6 + (date_idx * 3), line);
- rtc_pcf85363_time_read(&rtc_conf, &now);
- snprintf(line, DISP_LLEN, "%02xh%02x:%02x", now.hour, now.min, now.sec);
- display_line(4, 6, line);
- snprintf(line, DISP_LLEN, "V");
- display_line(5, 6 + (date_idx * 3), line);
- if ((button & BUTTON_RIGHT) && (date_idx < 2)) {
- date_idx++;
- }
- if ((button & BUTTON_LEFT) && (date_idx > 0)) {
- date_idx--;
- }
- if (button & (BUTTON_UP | BUTTON_DOWN)) {
- uint32_t seconds = rtc_pcf85363_daytime_to_seconds(&now);
- uint32_t add[6] = {
- 3600, 60, 1,
- (23 * 3600), (23* 3600 + 59 * 60), (23 * 3600 + 59 * 60 + 59),
- };
- if (button & BUTTON_UP) {
- if (seconds < add[date_idx + 3]) {
- seconds += add[date_idx];
- } else {
- seconds -= add[date_idx + 3];
- }
- } else {
- if (seconds < add[date_idx]) {
- seconds += add[date_idx + 3];
- } else {
- seconds -= add[date_idx];
- }
- }
- rtc_pcf85363_seconds_to_daytime(&now, seconds);
- rtc_pcf85363_time_write(&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 {
- snprintf(line, DISP_LLEN, " ->%s", limits_modes_titles[i]);
- display_line(i + 2, 0, line);
- }
- }
- if (button & BUTTON_UP) {
- limits_cur_entry -= 1;
- }
- if (button & BUTTON_DOWN) {
- limits_cur_entry += 1;
- }
- if (limits_cur_entry >= LIM_NB_MENU) {
- limits_cur_entry = 0;
- } else if (limits_cur_entry == 0xFF) {
- limits_cur_entry = LIM_NB_MENU - 1;
- }
- 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 {
- }
- }
- break;
- case FUNCTIONS: {
- if (sub_menu_level == 1) {
- int i = 0;
- for (i = 0; i < FUNC_NB_MENU; i++) {
- if (i != func_cur_entry) {
- display_line(i + 2, 3, func_modes_titles[i]);
- } else {
- snprintf(line, DISP_LLEN, " ->%s", func_modes_titles[i]);
- display_line(i + 2, 0, line);
- }
- }
- if (button & BUTTON_UP) {
- func_cur_entry -= 1;
- }
- if (button & BUTTON_DOWN) {
- func_cur_entry += 1;
- }
- if (func_cur_entry >= FUNC_NB_MENU) {
- func_cur_entry = 0;
- } else if (func_cur_entry == 0xFF) {
- func_cur_entry = FUNC_NB_MENU - 1;
- }
- if (button & (BUTTON_OK | BUTTON_RIGHT)) {
- func_cur_menu = func_cur_entry;
- sub_menu_level = 2;
- }
- if (button & BUTTON_LEFT) {
- sub_menu_level = 0;
- current_menu = MAIN_MENU;
- }
- } else {
- }
- }
- break;
- case TEST_MODE: {
- int i = 0;
- uint8_t states[3];
-
- states[TEST_FAN] = force_fan;
- states[TEST_12V] = (zc_cnt != 0) ? 1 : 0;
- states[TEST_CMDV] = force_cmd;
-
- for (i = 0; i < TEST_NB_MENU; i++) {
- if (i != test_cur_entry) {
- display_line(i + 2, 3, test_modes_titles[i]);
- } else {
- snprintf(line, DISP_LLEN, " ->%s : %d", test_modes_titles[i], states[i]);
- display_line(i + 2, 0, line);
- }
- }
- if (button & BUTTON_UP) {
- test_cur_entry -= 1;
- }
- if (button & BUTTON_DOWN) {
- test_cur_entry += 1;
- }
- if (test_cur_entry >= TEST_NB_MENU) {
- test_cur_entry = 0;
- } else if (test_cur_entry == 0xFF) {
- test_cur_entry = TEST_NB_MENU - 1;
- }
- if (button & (BUTTON_LEFT | BUTTON_RIGHT)) {
- switch (test_cur_entry) {
- case TEST_FAN:
- force_fan = !force_fan;
- break;
- case TEST_12V:
- /* Generate a fake zero-cross detect by calling the handler directly */
- zero_cross_detect(0);
- break;
- case TEST_CMDV:
- if (force_cmd >= 0) {
- force_cmd = -1;
- } else {
- force_cmd = 0;
- }
- default:
- break;
- }
- }
- if (button & BUTTON_OK) {
- sub_menu_level = 0;
- current_menu = MAIN_MENU;
- }
- }
- break;
- case SAVE_CONFIG: {
- }
- break;
- case MANUAL_MODE:
- manual_activation_request = -1;
- /* Fallback */
- default:
- interface_mode = MODE_RUN;
- current_menu = MAIN_MENU;
- break;
- }
- /* Update Oled display */
- ssd130x_display_full_screen(&display);
-}
-
-
-/***************************************************************************** */
- /* usual menu and switch to config menu */
-
-extern int water_centi_degrees;
-extern uint32_t moyenne_solar;
-extern uint32_t moyenne_home;
-extern volatile uint8_t command_val;
-
-void interface_update(char heat_mode)
-{
- int abs_centi = water_centi_degrees;
-
- if (water_centi_degrees < 0) {
- abs_centi = -water_centi_degrees;
- }
-
- /* Debug */
- if (button_pressed != 0) {
- uprintf(UART0, "Button : 0x%02x\n", button_pressed);
- }
-
- if (interface_mode == MODE_RUN) {
- if (button_pressed != 0) {
- if (button_pressed & BUTTON_OK) {
- manual_activation_request = -1;
- }
- if (button_pressed & BUTTON_UP) {
- interface_mode = MODE_CONFIG;
- }
- if (force_cmd >= 0) {
- if ((button_pressed & BUTTON_RIGHT) && (force_cmd <= 95)) {
- force_cmd += 5;
- }
- if ((button_pressed & BUTTON_LEFT) && (force_cmd >= 5)) {
- force_cmd -= 5;
- }
- }
- button_pressed = 0;
- }
-
- if (interface_board_present != 0) {
- char line[DISP_LLEN];
-
- /* Start with a blank screen */
- erase_screen_content();
-
- /* Update time and time display on internal memory */
- rtc_pcf85363_time_read(&rtc_conf, &now);
- snprintf(line, DISP_LLEN, "%02xh%02x:%02x", now.hour, now.min, now.sec);
- display_line(0, 0, line);
-
- /* Display info */
- snprintf(line, DISP_LLEN, "Water:% 2d.%03d %cC", (water_centi_degrees / 100), (abs_centi % 100), 0x1F);
- display_line(2, 0, line);
- snprintf(line, DISP_LLEN, "Prod :% 2d,%03dA", (moyenne_solar / 1000), ((moyenne_solar % 1000) / 10));
- display_line(3, 0, line);
- snprintf(line, DISP_LLEN, "Conso:% 2d,%03dA", (moyenne_home / 1000), ((moyenne_home % 1000) / 10));
- display_line(4, 0, line);
- snprintf(line, DISP_LLEN, "Command: %d%%", command_val);
- display_line(5, 0, line);
- snprintf(line, DISP_LLEN, "Mode: %c", heat_mode);
- display_line(6, 0, line);
- /* Update Oled display */
- ssd130x_display_full_screen(&display);
- }
- } else {
- /* Config mode. Mode entered by button action, which implies that interface board is present. */
- config_interface_handle();
- }
-}
-
-
+++ /dev/null
-/****************************************************************************
- * apps/blyes/relay_lights/interface.h
- *
- * Copyright 2018 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;
-
-extern volatile int manual_activation_request;
-
-/* 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);
-
-
-/* Internal Temperature sensor */
-int temp_read(uint32_t uart, int* deci_degrees);
-
-/* Interface content update */
-void interface_update(char mode);
-
-#endif /* INTERFACE_H */
-
+++ /dev/null
-/****************************************************************************
- * apps/scialys/v07/main.c
- *
- * Scialys system for solar-panel power generation tracking and fair use.
- *
- * Copyright 2016 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 "uSD.h"
-
-
-#define MODULE_VERSION 0x05
-#define MODULE_NAME "Scialys uC"
-
-
-
-/***************************************************************************** */
-/* System configuration */
-
-/* Period (in ms) of the decrementer handler from the systick interrupt */
-#define DEC_PERIOD 100
-/* Period (in ms) of the handler for the command value update */
-#define CMD_UPD_PERIOD 200
-
-/* NOTE : All temperatures are stored as decidegrees */
-/* Max water temperature. Internal protection which cannot be overriden by configuration */
-#define MAX_WATER_TEMP 900
-/* Internal system max temperature : turn off heating when reached */
-#define MAX_INTERNAL_TEMP 800 /* FIXME */
-
-#define DEFAULT_INTERNAL_TEMP 600
-#define FAN_ON_INTERNAL_TEMP 350
-#define FAN_OFF_INTERNAL_TEMP 320
-
-/* Most of the defines in here should go to configuration setting in user flash */
-/* If temperature falls bellow FORCE_HEATER_TEMP value, we enter forced heater mode, until
- * TARGET_FORCED_HEATER_TEMP is reached.
- * When in forced heater mode, the heater is controlled to heat at FORCED_MODE_VALUE which
- * is between 0 and 100.
- */
-#define FORCE_HEATER_TEMP 300
-#define TARGET_FORCED_HEATER_TEMP 500
-#define FORCED_MODE_VALUE 75 /* A fraction of 100 */
-/* mA prod value above which the system will not enter forced mode, waiting for home
- * to stop using power to start automatic heating */
-#define SUNNY_DAYS_PROD_VALUE 3000
-
-/* Delay before automatic forced heating */
-#define FORCED_HEATER_DELAY (2 * 3600 * 1000 / DEC_PERIOD)
-/* Duration of automatic forced heating */
-#define FORCED_HEATER_DURATION (3 * 3600 * 1000 / DEC_PERIOD)
-
-/* Duration of manual forced heating */
-#define MANUAL_ACTIVATION_DURATION (3 * 3600 * 1000 / DEC_PERIOD) /* Three hours */
-
-
-uint32_t forced_heater_mode = 0;
-uint32_t forced_heater_delay = 0;
-uint32_t forced_heater_time = 0;
-
-uint8_t error_shutdown = 0;
-
-uint8_t never_force = 0;
-uint32_t sunny_days_prod_value_config = 0;
-
-#define EXTERNAL_DISABLE_FORCE 0 /* Input is pulled low when external disable is ON */
-int external_disable = 0;
-
-enum modes {
- heat = 'C',
- ext_disable = 'E',
- delayed_heat_prod = 'P',
- forced = 'F',
- temp_OK = 'T',
- manual = 'M',
- idle_heat = 'L',
- full_heat = 'F',
-};
-
-/* Water and internaltemperature */
-int water_centi_degrees = 0;
-int tmp101_deci_degrees = 0;
-
-/* RTC and time */
-struct rtc_time now;
-
-
-/***************************************************************************** */
-/* Rx interrupt handler for system configuration over USB */
-void config_rx(uint8_t c)
-{
- /* FAN control */
- if (c == 'f') {
- gpio_set(fan_ctrl);
- } else {
- gpio_clear(fan_ctrl);
- }
-}
-
-
-/* Communication with slave modules */
-void comm_rx(uint8_t c)
-{
-}
-
-
-
-/***************************************************************************** */
-/* System communication over UART1 */
-void cmd_rx(uint8_t c)
-{
-}
-
-
-/***************************************************************************** */
-/* Decrementer for heating timers */
-void handle_dec_request(uint32_t curent_tick) {
- if (manual_activation_request > 0) {
- manual_activation_request--;
- }
- if (forced_heater_mode == 1) {
- if (forced_heater_delay > 0) {
- forced_heater_delay--;
- }
- if (forced_heater_time > 0) {
- forced_heater_time--;
- }
- }
-}
-
-
-/***************************************************************************** */
-/* Track power production and usage */
-
-/* Average value computed on last 10 ADC values */
-uint32_t moyenne_solar = 0;
-uint32_t moyenne_home = 0;
-/* Last ADC values (snapshot) */
-static uint16_t snapval_solar = 0;
-static uint16_t snapval_home = 0;
-
-#define NB_VAL 20
-static void track_isnail_values(void)
-{
- static uint16_t isnail_solar_values[NB_VAL];
- static uint16_t isnail_home_values[NB_VAL];
- static uint8_t idx = 0;
-
- /* Get new values */
- adc_get_value(&snapval_solar, LPC_ADC(1));
- adc_get_value(&snapval_home, LPC_ADC(0));
-
- /* Convert to mA value.
- * ADC range is 0 to 1024 - approx 3.2mV / ADC step
- * Coil convertion is 1000mA -> 50mV : multily mV value by 20 to get mA value
- * x * 3.2 * 20 == x * 32 * 2 == x << 6
- * Increment is 64mA / ADC step */
- snapval_solar = snapval_solar << 6;
- snapval_home = snapval_home << 6;
-
- /* Store value */
- isnail_solar_values[idx] = snapval_solar;
- isnail_home_values[idx++] = snapval_home;
- if (idx == NB_VAL) {
- idx = 0;
- }
-
- /* Compute average once we sampled enough values */
- if ((idx == 0) || (idx == (NB_VAL / 2))) {
- int i = 0;
- moyenne_solar = 0;
- moyenne_home = 0;
- for (i = 0; i < NB_VAL; i++) {
- moyenne_solar += isnail_solar_values[i];
- moyenne_home += isnail_home_values[i];
- }
- moyenne_solar = moyenne_solar / NB_VAL;
- moyenne_home = moyenne_home / NB_VAL;
- }
-}
-
-/***************************************************************************** */
-/* AC control */
-
-volatile uint8_t command_val = 0;
-volatile int act_cmd = 100; /* Start off */
-int8_t force_cmd = -1; /* Forced command value, for tests */
-static int fan_on = 0;
-int force_fan = 0; /* Request to force fan ON from test menu */
-
-
-void set_ctrl_duty_cycle(uint8_t value)
-{
- act_cmd = value;
- /* Below 3% and above 97% there are triggering problems which
- * lead to 50% instead of desired command */
- if (act_cmd >= 97) {
- act_cmd = 100;
- } else if (act_cmd <= 2) {
- act_cmd = 0;
- }
-}
-
-void ac_switch_on(uint32_t flags)
-{
- gpio_set(ac_ctrl);
-}
-
-static uint32_t clk_cycles_ac_zc = 0;
-volatile uint32_t zc_cnt = 0;
-void zero_cross(uint32_t gpio)
-{
- uint32_t delay = 0;
-
- zc_cnt++;
- if (act_cmd == 100) {
- gpio_set(ac_ctrl);
- return;
- }
- gpio_clear(ac_ctrl);
- if (act_cmd == 0) {
- return;
- }
- /* Set timer to trigger ac out ON at given delay */
- delay = clk_cycles_ac_zc * (100 - act_cmd);
- timer_set_match(LPC_TIMER_32B1, CHAN0, delay);
- timer_restart(LPC_TIMER_32B1);
-}
-
-/* This one is used to synchronize to the real zero-crossing detection, if any. */
-void zero_cross_detect(uint32_t gpio)
-{
- timer_restart(LPC_TIMER_32B0);
- zero_cross(0);
-}
-
-/* Handle the power command */
-#define CMD_UP_DELAY_RESET_VALUE 5
-void handle_cmd_update(uint32_t curent_tick)
-{
- static uint8_t cmd = 0;
- static uint8_t cmd_up_delay = CMD_UP_DELAY_RESET_VALUE;
-
- /* Unable to read internal temperature : turn off heating */
- if (error_shutdown == 1) {
- cmd = 0;
- goto cmd_update_end;
- }
-
- /* Water max temperature protection */
- if (water_centi_degrees > (MAX_WATER_TEMP * 10)) {
- cmd = 0;
- goto cmd_update_end;
- }
-
- /* Forced test mode */
- if (force_cmd != -1) {
- cmd = force_cmd;
- goto cmd_update_end;
- }
-
- /* Which is the current mode ? */
- if (forced_heater_mode == 1) {
- /* Forced heating mode */
- if ((forced_heater_delay == 0) && (forced_heater_time > 0)) {
- cmd = FORCED_MODE_VALUE;
- }
- /* Entering forced heating mode from temperature getting below threshold,
- * wait some delay before effective forced heating */
- if (forced_heater_time == 0) {
- forced_heater_delay = FORCED_HEATER_DELAY;
- forced_heater_time = FORCED_HEATER_DELAY + FORCED_HEATER_DURATION;
- }
- } else if (moyenne_solar < (moyenne_home - 64)) {
- /* Low production mode */
- if (cmd > 0) {
- cmd--;
- }
- } else if (moyenne_solar > (moyenne_home + 1280)) {
- if (cmd < 100) {
- cmd++;
- }
- } else if (moyenne_solar > (moyenne_home + 192)) {
- /* High production mode */
- if (cmd_up_delay > 0) {
- cmd_up_delay--;
- } else {
- cmd_up_delay = CMD_UP_DELAY_RESET_VALUE;
- if (cmd < 100) {
- cmd++;
- }
- }
- }
-
-cmd_update_end:
- command_val = cmd;
- /* Set Control Output duty cycle */
- set_ctrl_duty_cycle(100 - cmd);
-}
-
-
-/***************************************************************************** */
-void scialys_config(void)
-{
- /* We want 100 Hz (50 Hz but two zero crossings) with 1% granularity */
- clk_cycles_ac_zc = get_main_clock() / (100 * 100);
- /* And configure the fake zero-cross generation timer to 100Hz */
- timer_set_match(LPC_TIMER_32B0, CHAN0, (clk_cycles_ac_zc * 100));
-
- /* Read parameters from memory */
- if (1) {
- never_force = 0;
- sunny_days_prod_value_config = SUNNY_DAYS_PROD_VALUE;
- }
-
- /* Add a systick callback to handle command value update */
- add_systick_callback(handle_cmd_update, CMD_UPD_PERIOD);
-
- /* Add a systick callback to handle time counting */
- add_systick_callback(handle_dec_request, DEC_PERIOD);
-}
-
-/* Add a false zero-cross, but make sure to do it only once and only if we are not AC powered */
-void DC_switch_start(void)
-{
- static int already_done = 0;
- if ((already_done == 0) && (zc_cnt == 0)) {
- int ret = add_systick_callback(zero_cross, 10); /* 2 zero-crossing at 50Hz -> 100Hz -> 10ms */
- uprintf(UART0, "Entering forced DC mode on systick callback %d\n", ret);
- already_done = 1;
- } else {
- uprintf(UART0, "DC forced mode already ON: %d/%d\n", already_done, zc_cnt);
- }
-}
-
-/***************************************************************************** */
-int main(void)
-{
- uint32_t loop = 0;
- char mode = heat; /* Debug info */
-
- system_init();
- board_io_config();
- modules_config();
-
- external_config(UART0);
- scialys_config();
-
- msleep(1500);
- status_led(green_only);
-
- while (1) {
- uint16_t snapval_load = 0;
-
- /* Default mode : try to heat the water tank */
- mode = heat;
-
- /* Always track power consumption and production */
- track_isnail_values();
-
- /* Get load power usage */
- adc_get_value(&snapval_load, LPC_ADC(2));
-
- /* Feed the dog */
- if ((moyenne_solar != 0) && (moyenne_home != 0)) {
- watchdog_feed();
- }
-
- /* Get internal temperature */
- if (temp_read(UART0, &tmp101_deci_degrees) != 0) {
- /* Set internal temperature to 60°C if there's a temp error, as this will trigger the
- * internal fan */
- tmp101_deci_degrees = DEFAULT_INTERNAL_TEMP;
- }
-
- /* Internal temperature protection */
- if (tmp101_deci_degrees > (MAX_INTERNAL_TEMP)) {
- error_shutdown = 1;
- } else if (tmp101_deci_degrees < ((MAX_INTERNAL_TEMP) / 2)) {
- error_shutdown = 0;
- }
- /* If internal temperature is above 32°C, then turn on fan. Turn off when back to under 28°C */
- if ((tmp101_deci_degrees > FAN_ON_INTERNAL_TEMP) || (force_fan != 0)) {
- fan_on = 1;
- gpio_set(fan_ctrl);
- } else if (tmp101_deci_degrees < FAN_OFF_INTERNAL_TEMP) {
- fan_on = 0;
- gpio_clear(fan_ctrl);
- }
-
- /* Get thermocouple value */
- if (1) {
- int ret = 0, old_water_temp = 0;
- old_water_temp = water_centi_degrees;
- ret = max31855_sensor_read(&thermocouple, NULL, &water_centi_degrees);
- if (ret != 0) {
- uprintf(UART0, "Water Temp read error : %d\n", ret);
- /* Do not act upon iinvalid temp value */
- water_centi_degrees = old_water_temp;
- }
- }
-
- /* Need to enter Forced heating mode ? */
- if (water_centi_degrees < (FORCE_HEATER_TEMP * 10)) {
- if (forced_heater_mode == 0) {
- uprintf(UART0, "Water temp low, entering forced mode\n");
- forced_heater_mode = 1;
- }
- status_led(red_on);
- mode = forced;
- } else if ((water_centi_degrees > (TARGET_FORCED_HEATER_TEMP * 10)) && (forced_heater_mode == 1)) {
- status_led(red_off);
- uprintf(UART0, "Water temp OK, forced mode exit\n");
- forced_heater_mode = 0;
- mode = temp_OK;
- }
-
- /* Do not force if there is a lot of sun, it may be enough to heat again soon */
- if (moyenne_solar > sunny_days_prod_value_config) {
- mode = delayed_heat_prod;
- forced_heater_mode = 0;
- }
-
- /* Do not force heating if disabled by external command */
- external_disable = gpio_read(ext_disable_in_pin);
- if ((external_disable == EXTERNAL_DISABLE_FORCE) && (forced_heater_mode != 0)) {
- forced_heater_mode = 0;
- mode = ext_disable;
- }
-
- if (never_force == 1) {
- forced_heater_mode = 0;
- }
-
- /* Did the user request a forced heating ? */
- if (manual_activation_request != 0) {
- forced_heater_mode = 1;
- mode = manual;
- if (manual_activation_request == -1) {
- uprintf(UART0, "Entering manual forced mode for %d ticks\n", manual_activation_request);
- manual_activation_request = MANUAL_ACTIVATION_DURATION;
- forced_heater_time = FORCED_HEATER_DURATION;
- }
- if (manual_activation_request < 10) {
- uprintf(UART0, "Leaving manual forced mode\n");
- manual_activation_request = 0;
- }
- }
-
- /* Display */
- interface_update(mode);
-
- /* Debug */
- if (1) {
- uprintf(UART0, "%c:%d - Is: %d,%04d - Ih: %d,%04d\n", mode, loop++,
- (moyenne_solar / 1000), (moyenne_solar % 1000),
- (moyenne_home / 1000), (moyenne_home % 1000));
- uprintf(UART0, "Water Temp : %d\n", water_centi_degrees);
- uprintf(UART0, "Internal Temp : %d\n", tmp101_deci_degrees);
- uprintf(UART0, "ADC: Sol: %dmA, Home: %dmA, Load: %d\n",
- snapval_solar, snapval_home, snapval_load);
- uprintf(UART0, "Zc: %d\n", zc_cnt);
- uprintf(UART0, "CMD: %d/%d, Fan: %d/%d\n\n", command_val, (100 - act_cmd), fan_on, force_fan);
- }
-
- /* Wait approx 10ms between each loop */
- msleep(10);
- }
- return 0;
-}
-
-
-
+++ /dev/null
-/****************************************************************************
- * apps/blyes/inter_lights/time.c
- *
- * Copyright 2018 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 = 0x18,
- .month = 0x08,
- .day = 0x09,
- .hour = 0x13,
- .min = 0x37,
-};
-int time_valid = 0;
-int rtc_conf_ok = 0;
-
-int 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 time_update(uint32_t uart, struct rtc_time* now)
-{
- rtc_pcf85363_time_write(&rtc_conf, now);
- return 0;
-}
-
-
-/***************************************************************************** */
-/* RTC init */
-void time_config(uint32_t i2c_bus_num, uint8_t rtc_addr)
-{
- rtc_conf.bus_num = i2c_bus_num;
- rtc_conf.addr = rtc_addr;
-}
-
+++ /dev/null
-/****************************************************************************
- * apps/blyes/inter_lights/time.h
- *
- * Copyright 2018 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 time_init_check(uint32_t uart);
-void time_config(uint32_t i2c_bus_num, uint8_t rtc_addr);
-
-#endif /* RTC_TIME_H */
-
+++ /dev/null
-/****************************************************************************
- * apps/blyes/inter_lights/uSD.c
- *
- * Copyright 2018 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/ssp.h"
-
-#include "lib/stdio.h"
-
-#include "uSD.h"
-
-
-/***************************************************************************** */
-/* SD/MMC Card */
-struct sdmmc_card micro_sd = {
- .card_type = MMC_CARDTYPE_UNKNOWN,
- .block_size = 64,
-};
-
-static uint8_t got_uSD = 0;
-
-/* Buffer to read / write uSD card */
-uint8_t mmc_data[MMC_BUF_SIZE];
-
-/* Read up to 4 blocks of 16 bytes each */
-int uSD_read(uint32_t offset, uint8_t nb_blocks)
-{
- int i = 0, ret = 0;
- if (got_uSD == 0) {
- return -1;
- }
- if (nb_blocks > 4) {
- return -2;
- }
- memset(mmc_data, 0, MMC_BUF_SIZE);
- for (i = 0; i < nb_blocks; i++) {
- ret = sdmmc_read_block(µ_sd, (offset + i), (mmc_data + (i * 16)));
- /* FIXME : check return value */
- }
- return 0;
-}
-
-
-/* Write up to 4 blocks of 16 bytes each */
-int uSD_write(uint32_t offset, uint8_t nb_blocks)
-{
- int i = 0, ret = 0;
- if (got_uSD == 0) {
- return -1;
- }
- if (nb_blocks > 4) {
- return -1;
- }
- for (i = 0; i < nb_blocks; i++) {
- ret = sdmmc_write_block(µ_sd, (offset + i), (mmc_data + (i * 16)));
- /* FIXME : check return value */
- }
- return 0;
-}
-
-/* microSD card init */
-int uSD_detect(int uart)
-{
- int i = 0, ret = 0, step = 0;
- do {
- step = 0;
- ret = sdmmc_init(µ_sd);
- if (ret == 0) {
- step = 1;
- msleep(10);
- ret = sdmmc_init_wait_card_ready(µ_sd);
- if (ret <= 1) {
- step = 2;
- ret = sdmmc_init_end(µ_sd);
- }
- }
- uprintf(uart, "uSD init(%d): step:%d, ret: %d, type: %d, bs: %d\n",
- i, step, ret, micro_sd.card_type, micro_sd.block_size);
- i++;
- } while (((ret != 0) || (micro_sd.card_type == MMC_CARDTYPE_UNKNOWN)) && (i < 10));
-
- /* Got uSD ? */
- if (i >= 10) {
- uprintf(uart, "uSD init failed, no uSD card present.\n");
- got_uSD = 0;
- return -1;
- }
-
- /* uSD card detected */
- got_uSD = 1;
- /* got_uSD MUST be set to 1 from here on ! */
- uSD_read(0, 4); /* Read 4 blocks at start of card */
- /* FIXME : check that the card magic is present */
- uprintf(uart, "uSD read: %s\n", mmc_data);
-
- return 0;
-}
-
-
-int uSD_logs_init(int uart)
-{
- /* FIXME : read 4 blocks at offset FIXME to get the last block used info */
- return 0;
-}
-
-/***************************************************************************** */
-/* microSD card init */
-void uSD_config(struct pio* uSD_cs, uint32_t ssp_bus_num)
-{
- memcpy(&(micro_sd.chip_select), uSD_cs, sizeof(struct pio));
- micro_sd.ssp_bus_num = ssp_bus_num;
-}
-
+++ /dev/null
-/****************************************************************************
- * apps/blyes/inter_lights/usSD.h
- *
- * Copyright 2018 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 USD_H
-#define USD_H
-
-#include "extdrv/sdmmc.h"
-
-#define MMC_BUF_SIZE 64
-
-extern struct sdmmc_card micro_sd;
-extern uint8_t mmc_data[MMC_BUF_SIZE];
-
-int uSD_read(uint32_t offset, uint8_t nb_blocks);
-int uSD_write(uint32_t offset, uint8_t nb_blocks);
-
-/***************************************************************************** */
-/* microSD card init */
-int uSD_detect(int uart);
-void uSD_config(struct pio* uSD_cs, uint32_t ssp_bus_num);
-
-
-#endif /* USD_H */
-
+++ /dev/null
-# 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} $@
-
+++ /dev/null
-/****************************************************************************
- * apps/scialys/v07/config.c
- *
- * Copyright 2018 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 "drivers/adc.h"
-#include "drivers/ssp.h"
-#include "drivers/timers.h"
-
-
-#include "extdrv/status_led.h"
-#include "extdrv/max31855_thermocouple.h"
-
-#include "lib/stdio.h"
-
-#include "config.h"
-#include "interface.h"
-#include "time.h"
-#include "uSD.h"
-
-#define SELECTED_FREQ FREQ_SEL_48MHz
-
-
-uint8_t config_got_interface = 1;
-
-/***************************************************************************** */
-/* Pins configuration */
-/* pins blocks are passed to set_pins() for pins configuration.
- * Unused pin blocks can be removed safely with the corresponding set_pins() call
- * All pins blocks may be safelly merged in a single block for single set_pins() call..
- */
-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) },
- /* SPI */
- { LPC_SSP0_SCLK_PIO_0_14, LPC_IO_DIGITAL },
- { LPC_SSP0_MOSI_PIO_0_17, LPC_IO_DIGITAL },
- { LPC_SSP0_MISO_PIO_0_16, LPC_IO_DIGITAL },
- /* ADC */
- { LPC_ADC_AD0_PIO_0_30, LPC_IO_ANALOG }, /* Home */
- { LPC_ADC_AD1_PIO_0_31, LPC_IO_ANALOG }, /* Production */
- { LPC_ADC_AD2_PIO_1_0, LPC_IO_ANALOG }, /* Load */
- /* GPIO */
- { LPC_GPIO_0_3, LPC_IO_DIGITAL }, /* External switch input */
- { LPC_GPIO_0_4, LPC_IO_DIGITAL }, /* Zero crossing detection input */
- { LPC_GPIO_0_5, LPC_IO_DIGITAL }, /* Fan control */
- { LPC_GPIO_0_6, LPC_IO_DIGITAL }, /* Mosfet control */
- { LPC_GPIO_0_7, LPC_IO_DIGITAL }, /* Thermocouple chip select */
- { LPC_GPIO_0_12, LPC_IO_DIGITAL }, /* ISP / User button OK */
- { LPC_GPIO_0_18, LPC_IO_DIGITAL }, /* Uext Chip select / Module eeprom select */
- { LPC_GPIO_0_20, LPC_IO_DIGITAL }, /* RGB Leds */
- { LPC_GPIO_0_21, LPC_IO_DIGITAL }, /* Oled Reset */
- { LPC_GPIO_1_1, LPC_IO_DIGITAL }, /* uSD Card SPI Chip Select */
- { 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;
-
-/* Inputs */
-/* External signals */
-const struct pio zero_cross_in_pin = LPC_GPIO_0_4;
-const struct pio ext_disable_in_pin = LPC_GPIO_0_3;
-
-/* Outputs */
-/* AC output control (Mosfet) */
-const struct pio ac_ctrl = LPC_GPIO_0_6;
-const struct pio fan_ctrl = LPC_GPIO_0_5;
-/* Chip selects */
-const struct pio uSD_cs = LPC_GPIO_1_1; /* uSD card */
-const struct pio th_cs = LPC_GPIO_0_7; /* Thermocouple */
-const struct pio uext_cs = LPC_GPIO_0_18; /* UEXT module (optionnal) */
-
-
-
-/***************************************************************************** */
-/* 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);
-}
-
-
-/***************************************************************************** */
-/* Internal modules */
-const struct lpc_tc_config ac_timer_conf_zc = {
- .mode = LPC_TIMER_MODE_TIMER | LPC_TIMER_MODE_MATCH,
- .match_control = { LPC_TIMER_INT_RESET_ON_MATCH, 0, 0, 0, },
-};
-const struct lpc_tc_config ac_timer_conf_delay = {
- .mode = LPC_TIMER_MODE_TIMER | LPC_TIMER_MODE_MATCH,
- .match_control = { LPC_TIMER_INT_RESET_AND_STOP_ON_MATCH, 0, 0, 0, },
-};
-const struct lpc_tc_config ac_timer_conf_power_track = {
- .mode = LPC_TIMER_MODE_TIMER | LPC_TIMER_MODE_MATCH,
- .match_control = { LPC_TIMER_INT_RESET_ON_MATCH, 0, 0, 0, },
-};
-
-extern void config_rx(uint8_t c);
-extern void comm_rx(uint8_t c);
-extern void ac_switch_on(uint32_t flags);
-extern void zero_cross(uint32_t flags);
-extern void power_track(uint32_t flags);
-
-void modules_config(void)
-{
- uart_on(UART0, 115200, config_rx);
- uart_on(UART1, 115200, comm_rx);
- i2c_on(I2C0, I2C_CLK_100KHz, I2C_MASTER);
- ssp_master_on(SSP_BUS_0, LPC_SSP_FRAME_SPI, 8, 4*1000*1000);
- adc_on(NULL);
- adc_start_burst_conversion(ADC_MCH(0) | ADC_MCH(1) | ADC_MCH(2), LPC_ADC_SEQ(0));
- timer_on(LPC_TIMER_32B0, 0, zero_cross);
- timer_counter_config(LPC_TIMER_32B0, &ac_timer_conf_zc);
- timer_on(LPC_TIMER_32B1, 0, ac_switch_on);
- timer_counter_config(LPC_TIMER_32B1, &ac_timer_conf_delay);
- timer_on(LPC_TIMER_16B0, 0, power_track);
- timer_counter_config(LPC_TIMER_16B0, &ac_timer_conf_power_track);
-}
-
-
-/***************************************************************************** */
-extern void zero_cross_detect(uint32_t gpio);
-void board_io_config(void)
-{
- /* Immediatly turn off Mosfet / Triac */
- config_gpio(&ac_ctrl, 0, GPIO_DIR_OUT, 1);
- /* Start with FAN ON */
- config_gpio(&fan_ctrl, 0, GPIO_DIR_OUT, 1);
-
- /* Configure Input GPIO */
- config_gpio(&ext_disable_in_pin, 0, GPIO_DIR_IN, 0);
-
- /* Zero-crossing detection */
- set_gpio_callback(zero_cross_detect, &zero_cross_in_pin, EDGE_FALLING);
-}
-
-
-
-/***************************************************************************** */
-/* Configure external components */
-
-/* Thermocouple reading */
-const struct max31855_sensor_config thermocouple = {
- .ssp_bus_num = 0,
- .chip_select = LPC_GPIO_0_7,
-};
-
-/* RTC and time */
-#define RTC_ADDR 0xA2
-
-
-int external_config(uint32_t uart)
-{
- int ret = 0;
-
- /* Configure GPIO for specific board usage */
- board_io_config();
-
- /* uSD card */
- uSD_config((struct pio *)&uSD_cs, SSP_BUS_0);
- uSD_detect(uart);
-
- /* RTC */
- time_config(I2C0, RTC_ADDR);
- time_init_check(uart);
-
- /* Thermocouple configuration */
- max31855_sensor_config(&thermocouple);
-
- /* Configure interface board */
- ret = interface_config(uart);
- if (ret != 0) {
- return -1;
- }
- return 0;
-}
+++ /dev/null
-/****************************************************************************
- * apps/scialys/v07/config.h
- *
- * Copyright 2018 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 "drivers/adc.h"
-#include "drivers/ssp.h"
-#include "drivers/timers.h"
-
-#include "extdrv/status_led.h"
-#include "extdrv/max31855_thermocouple.h"
-#include "extdrv/ws2812.h"
-
-#include "lib/stdio.h"
-
-
-#define SELECTED_FREQ FREQ_SEL_48MHz
-
-/***************************************************************************** */
-/* Pins configuration */
-extern const struct pio status_led_green;
-extern const struct pio status_led_red;
-
-/* Inputs */
-/* External signals */
-extern const struct pio zero_cross_in_pin;
-extern const struct pio ext_disable_in_pin;
-
-/* Outputs */
-/* AC output control (Mosfet) */
-extern const struct pio ac_ctrl;
-extern const struct pio fan_ctrl;
-/* Chip selects */
-extern const struct pio uSD_cs; /* uSD card */
-extern const struct pio th_cs; /* Thermocouple */
-extern const struct pio uext_cs; /* UEXT module (optionnal) */
-
-
-
-/***************************************************************************** */
-extern const struct max31855_sensor_config thermocouple;
-
-
-/***************************************************************************** */
-/* 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 modules and main functions */
-void modules_config(void);
-
-/* Configure GPIO for specific board usage */
-void board_io_config(void);
-
-/* Configure external components */
-int external_config(uint32_t uart);
-
-
-#endif /* CONFIG_H */
-
+++ /dev/null
-/****************************************************************************
- * apps/scialys/v07/interface.c
- *
- * Copyright 2018 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 "drivers/adc.h"
-
-#include "extdrv/status_led.h"
-#include "extdrv/ws2812.h"
-#include "extdrv/tmp101_temp_sensor.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 "interface.h"
-#include "time.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;
-
-
-static uint8_t interface_board_present = 0;
-static uint8_t power_board_present = 0;
-
-/***************************************************************************** */
-/* TMP101 I2C temperature sensor on Power board */
-#define TMP101_ADDR1 0x94 /* Pin Addr0 (pin5 of tmp101) connected to VCC */
-struct tmp101_sensor_config tmp101_sensor_power = {
- .bus_num = I2C0,
- .addr = TMP101_ADDR1,
- .resolution = TMP_RES_ELEVEN_BITS,
-};
-
-/* TMP101 I2C temperature sensor on display board */
-#define TMP101_ADDR0 0x90 /* Pin Addr0 (pin5 of tmp101) connected to GND */
-struct tmp101_sensor_config tmp101_sensor_display = {
- .bus_num = I2C0,
- .addr = TMP101_ADDR0,
- .resolution = TMP_RES_ELEVEN_BITS,
-};
-
-/***************************************************************************** */
-/* 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;
-
- if (interface_board_present == 0) {
- return -1;
- }
-
- 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;
-
- if (interface_board_present == 0) {
- return -1;
- }
-
- 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);
-}
-
-
-int temp_read(uint32_t uart, int* deci_degrees_disp, int* deci_degrees_power)
-{
- int ret = 0;
- int conv = 0;
-
- if (interface_board_present != 0) {
- ret = tmp101_sensor_read(&tmp101_sensor_display, NULL, deci_degrees_disp);
- if (ret == 0) {
- conv |= CONV_DISPLAY_OK;
- } else {
- uprintf(uart, "TMP101 read error on display board : %d\n", ret);
- }
- }
- if (power_board_present != 0) {
- ret = tmp101_sensor_read(&tmp101_sensor_power, NULL, deci_degrees_power);
- if (ret == 0) {
- conv |= CONV_POWER_OK;
- } else {
- uprintf(uart, "TMP101 read error on power board : %d\n", ret);
- }
- }
- return conv;
-}
-
-
-/***************************************************************************** */
-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;
- }
-}
-
-int interface_config(uint32_t uart)
-{
- int ret = 0;
-
- /* TMP101 sensor config on power board */
- power_board_present = 1;
- ret = tmp101_sensor_config(&tmp101_sensor_power);
- if (ret != 0) {
- uprintf(uart, "Temp config error on power board: %d\n", ret);
- power_board_present = 0;
- } else {
- ret = tmp101_sensor_set_continuous_conversion(&tmp101_sensor_power);
- if (ret != 0) {
- uprintf(uart, "Temp config error on power board: %d\n", ret);
- power_board_present = 0;
- }
- }
-
- /* TMP101 sensor config on display board */
- ret = tmp101_sensor_config(&tmp101_sensor_display);
- if (ret != 0) {
- uprintf(uart, "Temp config error on display board: %d\n", ret);
- interface_board_present = 0;
- return -1;
- }
- ret = tmp101_sensor_set_continuous_conversion(&tmp101_sensor_display);
- if (ret != 0) {
- uprintf(uart, "Temp config error on display board: %d\n", ret);
- interface_board_present = 0;
- return -2;
- }
- interface_board_present = 1;
-
- /* Buttons inputs on front panel */
- /* Activate on Rising edge (button release) */
- set_gpio_callback(button_callback, &button_up, EDGE_RISING);
- set_gpio_callback(button_callback, &button_left, EDGE_RISING);
- set_gpio_callback(button_callback, &button_right, EDGE_RISING);
- set_gpio_callback(button_callback, &button_down, EDGE_RISING);
- set_gpio_callback(button_callback, &button_ok, EDGE_RISING);
-
- /* WS2812B Leds on display board */
- ws2812_config(&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(0, 0x05, 0x15, 0x08);
- ws2812_send_frame(0);
-
- uprintf(uart, "Config OK\n");
-
- return 0;
-}
-
-
-
-/***************************************************************************** */
- /* Menu part */
-
-enum interface_modes {
- MODE_RUN = 0,
- MODE_CONFIG,
- MODE_DISPLAY,
-};
-static int interface_mode = MODE_RUN;
-volatile int manual_activation_request = 0;
-
-enum menu_list {
- MAIN_MENU,
- MANUAL_MODE,
- DATE_CONFIG,
- LIMITS,
- FUNCTIONS,
- TEST_MODE,
- SAVE_CONFIG,
- NB_MENU, /* This one must be the last */
-};
-static const char* menu_titles[] = {
- [MAIN_MENU] = "Menu principal",
- [MANUAL_MODE] = "Marche Forcee",
- [DATE_CONFIG] = "Regl. Heure",
- [LIMITS] = "Limites",
- [FUNCTIONS] = "Fonctions",
- [TEST_MODE] = "Test mode",
- [SAVE_CONFIG] = "Save config",
-};
-static uint8_t current_menu = MAIN_MENU;
-static uint8_t current_entry = MANUAL_MODE;
-
-enum func_menu_list {
- FUNC_LOAD_TYPE,
- FUNC_CMD_TYPE,
- FUNC_NB_MENU, /* This one must be the last */
-};
-static const char* func_modes_titles[] = {
- [FUNC_LOAD_TYPE] = "Load Type",
- [FUNC_CMD_TYPE] = "CMD type",
-};
-static uint8_t func_cur_menu = FUNC_LOAD_TYPE;
-static uint8_t func_cur_entry = FUNC_LOAD_TYPE;
-
-enum limits_menu_list {
- TEMP_FORCED_IN,
- TEMP_FORCED_TARGET,
- FORCED_MODE_VAL,
- SUNNY_DAY_VALUE,
- LIM_NB_MENU, /* This one must be the last */
-};
-static const char* limits_modes_titles[] = {
- [TEMP_FORCED_IN] = "Water Min",
- [TEMP_FORCED_TARGET] = "Water Target",
- [FORCED_MODE_VAL] = "Forced cmd",
- [SUNNY_DAY_VALUE] = "Sun Prod",
-};
-static uint8_t limits_cur_menu = TEMP_FORCED_IN;
-static uint8_t limits_cur_entry = TEMP_FORCED_IN;
-
-enum test_menu_list {
- TEST_FAN,
- TEST_12V,
- TEST_CMDV,
- TEST_NB_MENU, /* This one must be the last */
-};
-static const char* test_modes_titles[] = {
- [TEST_FAN] = "Fan",
- [TEST_12V] = "DC 12V",
- [TEST_CMDV] = "CMD Val",
-};
-extern uint8_t force_fan;
-extern int8_t force_cmd;
-extern uint32_t zc_cnt;
-extern void zero_cross_detect(uint32_t gpio);
-static uint8_t test_cur_entry = TEST_FAN;
-
-
-/* Current sub-menu level. main menu is level 0 */
-int sub_menu_level = 0;
-
-
-void config_interface_handle(void)
-{
- char line[DISP_LLEN];
- 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;
- for (i = 1; i < NB_MENU; i++) {
- if (i != current_entry) {
- display_line(i + 1, 3, menu_titles[i]);
- } else {
- snprintf(line, DISP_LLEN, " ->%s", menu_titles[i]);
- display_line(i + 1, 0, line);
- }
- }
- 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) {
- current_entry = NB_MENU - 1;
- }
- if (button & (BUTTON_OK | BUTTON_RIGHT)) {
- current_menu = current_entry;
- sub_menu_level = 1;
- }
- if (button & BUTTON_LEFT) {
- interface_mode = MODE_RUN;
- }
- }
- break;
- case DATE_CONFIG: {
- static uint8_t date_idx = 0;
- struct rtc_time now;
- snprintf(line, DISP_LLEN, "^");
- display_line(3, 6 + (date_idx * 3), line);
- rtc_pcf85363_time_read(&rtc_conf, &now);
- snprintf(line, DISP_LLEN, "%02xh%02x:%02x", now.hour, now.min, now.sec);
- display_line(4, 6, line);
- snprintf(line, DISP_LLEN, "V");
- display_line(5, 6 + (date_idx * 3), line);
- if ((button & BUTTON_RIGHT) && (date_idx < 2)) {
- date_idx++;
- }
- if ((button & BUTTON_LEFT) && (date_idx > 0)) {
- date_idx--;
- }
- if (button & (BUTTON_UP | BUTTON_DOWN)) {
- uint32_t seconds = rtc_pcf85363_daytime_to_seconds(&now);
- uint32_t add[6] = {
- 3600, 60, 1,
- (23 * 3600), (23* 3600 + 59 * 60), (23 * 3600 + 59 * 60 + 59),
- };
- if (button & BUTTON_UP) {
- if (seconds < add[date_idx + 3]) {
- seconds += add[date_idx];
- } else {
- seconds -= add[date_idx + 3];
- }
- } else {
- if (seconds < add[date_idx]) {
- seconds += add[date_idx + 3];
- } else {
- seconds -= add[date_idx];
- }
- }
- rtc_pcf85363_seconds_to_daytime(&now, seconds);
- rtc_pcf85363_time_write(&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 {
- snprintf(line, DISP_LLEN, " ->%s", limits_modes_titles[i]);
- display_line(i + 2, 0, line);
- }
- }
- if (button & BUTTON_UP) {
- limits_cur_entry -= 1;
- }
- if (button & BUTTON_DOWN) {
- limits_cur_entry += 1;
- }
- if (limits_cur_entry >= LIM_NB_MENU) {
- limits_cur_entry = 0;
- } else if (limits_cur_entry == 0xFF) {
- limits_cur_entry = LIM_NB_MENU - 1;
- }
- 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 {
- }
- }
- break;
- case FUNCTIONS: {
- if (sub_menu_level == 1) {
- int i = 0;
- for (i = 0; i < FUNC_NB_MENU; i++) {
- if (i != func_cur_entry) {
- display_line(i + 2, 3, func_modes_titles[i]);
- } else {
- snprintf(line, DISP_LLEN, " ->%s", func_modes_titles[i]);
- display_line(i + 2, 0, line);
- }
- }
- if (button & BUTTON_UP) {
- func_cur_entry -= 1;
- }
- if (button & BUTTON_DOWN) {
- func_cur_entry += 1;
- }
- if (func_cur_entry >= FUNC_NB_MENU) {
- func_cur_entry = 0;
- } else if (func_cur_entry == 0xFF) {
- func_cur_entry = FUNC_NB_MENU - 1;
- }
- if (button & (BUTTON_OK | BUTTON_RIGHT)) {
- func_cur_menu = func_cur_entry;
- sub_menu_level = 2;
- }
- if (button & BUTTON_LEFT) {
- sub_menu_level = 0;
- current_menu = MAIN_MENU;
- }
- } else {
- }
- }
- break;
- case TEST_MODE: {
- int i = 0;
- uint8_t states[3];
-
- states[TEST_FAN] = force_fan;
- states[TEST_12V] = (zc_cnt != 0) ? 1 : 0;
- states[TEST_CMDV] = force_cmd;
-
- for (i = 0; i < TEST_NB_MENU; i++) {
- if (i != test_cur_entry) {
- display_line(i + 2, 3, test_modes_titles[i]);
- } else {
- snprintf(line, DISP_LLEN, " ->%s : %d", test_modes_titles[i], states[i]);
- display_line(i + 2, 0, line);
- }
- }
- if (button & BUTTON_UP) {
- test_cur_entry -= 1;
- }
- if (button & BUTTON_DOWN) {
- test_cur_entry += 1;
- }
- if (test_cur_entry >= TEST_NB_MENU) {
- test_cur_entry = 0;
- } else if (test_cur_entry == 0xFF) {
- test_cur_entry = TEST_NB_MENU - 1;
- }
- if (button & (BUTTON_LEFT | BUTTON_RIGHT)) {
- switch (test_cur_entry) {
- case TEST_FAN:
- force_fan = !force_fan;
- break;
- case TEST_12V:
- /* Generate a fake zero-cross detect by calling the handler directly */
- zero_cross_detect(0);
- break;
- case TEST_CMDV:
- if (force_cmd >= 0) {
- force_cmd = -1;
- } else {
- force_cmd = 0;
- }
- default:
- break;
- }
- }
- if (button & BUTTON_OK) {
- sub_menu_level = 0;
- current_menu = MAIN_MENU;
- }
- }
- break;
- case SAVE_CONFIG: {
- }
- break;
- case MANUAL_MODE:
- manual_activation_request = -1;
- /* Fallback */
- default:
- interface_mode = MODE_RUN;
- current_menu = MAIN_MENU;
- break;
- }
- /* Update Oled display */
- ssd130x_display_full_screen(&display);
-}
-
-
-/***************************************************************************** */
- /* usual menu and switch to config menu */
-
-extern int water_centi_degrees;
-extern uint32_t solar_prod_value;
-extern uint32_t home_conso_value;
-extern volatile uint8_t command_val;
-
-void interface_update(char heat_mode)
-{
- int abs_centi = water_centi_degrees;
-
- if (water_centi_degrees < 0) {
- abs_centi = -water_centi_degrees;
- }
-
- /* Debug */
- if (button_pressed != 0) {
- uprintf(UART0, "Button : 0x%02x\n", button_pressed);
- }
-
- if (interface_mode == MODE_RUN) {
- if (button_pressed != 0) {
- if (button_pressed & BUTTON_OK) {
- manual_activation_request = -1;
- }
- if (button_pressed & BUTTON_UP) {
- interface_mode = MODE_CONFIG;
- }
- if (force_cmd >= 0) {
- if ((button_pressed & BUTTON_RIGHT) && (force_cmd <= 95)) {
- force_cmd += 5;
- }
- if ((button_pressed & BUTTON_LEFT) && (force_cmd >= 5)) {
- force_cmd -= 5;
- }
- }
- button_pressed = 0;
- }
-
- if (interface_board_present != 0) {
- char line[DISP_LLEN];
-
- /* Start with a blank screen */
- erase_screen_content();
-
- /* Update time and time display on internal memory */
- rtc_pcf85363_time_read(&rtc_conf, &now);
- snprintf(line, DISP_LLEN, "%02xh%02x:%02x", now.hour, now.min, now.sec);
- display_line(0, 0, line);
-
- /* Display info */
- snprintf(line, DISP_LLEN, "Water:% 2d.%03d %cC", (water_centi_degrees / 100), (abs_centi % 100), 0x1F);
- display_line(2, 0, line);
- snprintf(line, DISP_LLEN, "Prod :% 2d,%03dA", (solar_prod_value / 1000), ((solar_prod_value % 1000) / 10));
- display_line(3, 0, line);
- snprintf(line, DISP_LLEN, "Conso:% 2d,%03dA", (home_conso_value / 1000), ((home_conso_value % 1000) / 10));
- display_line(4, 0, line);
- snprintf(line, DISP_LLEN, "Command: %d%%", command_val);
- display_line(5, 0, line);
- snprintf(line, DISP_LLEN, "Mode: %c", heat_mode);
- display_line(6, 0, line);
- snprintf(line, DISP_LLEN, "v0.8 - 0.3.3");
- display_line(7, 0, line);
- /* Update Oled display */
- ssd130x_display_full_screen(&display);
-
- /* Update RGB leds */
- /* FIXME : use for error signal */
- ws2812_set_pixel(0, (home_conso_value / 2000), (solar_prod_value / 2000), 0);
- ws2812_set_pixel(1, 0, 0, command_val);
- ws2812_send_frame(0);
- }
- } else {
- /* Config mode. Mode entered by button action, which implies that interface board is present. */
- config_interface_handle();
- }
-}
-
-
+++ /dev/null
-/****************************************************************************
- * apps/blyes/relay_lights/interface.h
- *
- * Copyright 2018 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;
-
-extern volatile int manual_activation_request;
-
-/* 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);
-
-
-/* Internal Temperature sensors */
-#define CONV_DISPLAY_OK (0x01 << 0)
-#define CONV_POWER_OK (0x01 << 1)
-#define CONV_OK (CONV_POWER_OK | CONV_DISPLAY_OK)
-int temp_read(uint32_t uart, int* deci_degrees_disp, int* deci_degrees_power);
-
-/* Interface content update */
-void interface_update(char mode);
-
-#endif /* INTERFACE_H */
-
+++ /dev/null
-/****************************************************************************
- * apps/scialys/v07/main.c
- *
- * Scialys system for solar-panel power generation tracking and fair use.
- *
- * Copyright 2016 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 "uSD.h"
-
-
-#define MODULE_VERSION 0x08
-#define MODULE_NAME "Scialys uC"
-
-
-
-/***************************************************************************** */
-/* System configuration */
-
-/* Period (in ms) of the decrementer handler from the systick interrupt */
-#define DEC_PERIOD 100
-/* Period (in ms) of the handler for the command value update */
-#define CMD_UPD_PERIOD 100
-
-/* NOTE : All temperatures are stored as decidegrees */
-/* Max water temperature. Internal protection which cannot be overriden by configuration */
-#define MAX_WATER_TEMP 900
-/* Internal system max temperature : turn off heating when reached */
-#define MAX_INTERNAL_TEMP 800 /* FIXME */
-
-#define DEFAULT_INTERNAL_TEMP 600
-#define FAN_ON_INTERNAL_TEMP_POWER 400
-#define FAN_OFF_INTERNAL_TEMP_POWER 350
-#define FAN_ON_INTERNAL_TEMP 380
-#define FAN_OFF_INTERNAL_TEMP 320
-
-/* Most of the defines in here should go to configuration setting in user flash */
-/* If temperature falls bellow FORCE_HEATER_TEMP value, we enter forced heater mode, until
- * TARGET_FORCED_HEATER_TEMP is reached.
- * When in forced heater mode, the heater is controlled to heat at FORCED_MODE_VALUE which
- * is between 0 and 100.
- */
-#define FORCE_HEATER_TEMP 300
-#define TARGET_FORCED_HEATER_TEMP 500
-#define FORCED_MODE_VALUE 75 /* A fraction of 100 */
-/* mA prod value above which the system will not enter forced mode, waiting for home
- * to stop using power to start automatic heating */
-#define SUNNY_DAYS_PROD_VALUE 3000
-
-/* Delay before automatic forced heating */
-#define FORCED_HEATER_DELAY (2 * 3600 * 1000 / DEC_PERIOD)
-/* Duration of automatic forced heating */
-#define FORCED_HEATER_DURATION (3 * 3600 * 1000 / DEC_PERIOD)
-
-/* Duration of manual forced heating */
-#define MANUAL_ACTIVATION_DURATION (3 * 3600 * 1000 / DEC_PERIOD) /* Three hours */
-
-
-uint32_t forced_heater_mode = 0;
-uint32_t forced_heater_delay = 0;
-uint32_t forced_heater_time = 0;
-uint8_t manual_forced_heater = 0;
-
-uint8_t error_shutdown = 0;
-
-uint8_t never_force = 0;
-uint32_t sunny_days_prod_value_config = 0;
-
-#define EXTERNAL_DISABLE_FORCE 0 /* Input is pulled low when external disable is ON */
-int external_disable = 0;
-
-enum modes {
- heat = 'C', /* Normal heating */
- ext_disable = 'E', /* Forced heating disabled by external input */
- forced = 'F', /* Forced heating */
- manual = 'M',
- delayed_heat_prod = 'P', /* Pause */
- overprod = 'S', /* Over production, try to start other loads */
- temp_OK = 'T', /* Max temperature reached */
-};
-
-/* Water and internaltemperature */
-int water_centi_degrees = 0;
-int deci_degrees_power = 0;
-int deci_degrees_disp = 0;
-
-/* RTC and time */
-struct rtc_time now;
-
-
-/***************************************************************************** */
-/* Rx interrupt handler for system configuration over USB */
-void config_rx(uint8_t c)
-{
- /* FAN control */
- if (c == 'f') {
- gpio_set(fan_ctrl);
- } else {
- gpio_clear(fan_ctrl);
- }
-}
-
-
-/* Communication with slave modules */
-void comm_rx(uint8_t c)
-{
-}
-
-
-
-/***************************************************************************** */
-/* System communication over UART1 */
-void cmd_rx(uint8_t c)
-{
-}
-
-
-/***************************************************************************** */
-/* Decrementer for heating timers */
-void handle_dec_request(uint32_t curent_tick) {
- if (manual_activation_request > 0) {
- manual_activation_request--;
- }
- if (forced_heater_mode == 1) {
- if (forced_heater_delay > 0) {
- forced_heater_delay--;
- }
- if (forced_heater_time > 0) {
- forced_heater_time--;
- }
- }
-}
-
-
-/***************************************************************************** */
-/* Track power production and usage */
-
-/* Average value computed on last 10 ADC values */
-uint32_t solar_prod_value = 0;
-uint32_t home_conso_value = 0;
-
-static void track_isnail_values(void)
-{
- uint16_t snapval_solar = 0;
- uint16_t snapval_home = 0;
- /* Get new values */
- adc_get_value(&snapval_solar, LPC_ADC(1));
- adc_get_value(&snapval_home, LPC_ADC(0));
-
- /* Convert to mA value.
- * ADC range is 0 to 1024 - approx 3.2mV / ADC step
- * Coil convertion is 1000mA -> 50mV : multily mV value by 20 to get mA value
- * x * 3.2 * 20 == x * 32 * 2 == x << 6
- * Increment is 64mA / ADC step */
- solar_prod_value = snapval_solar << 6;
- home_conso_value = snapval_home << 6;
-}
-
-/***************************************************************************** */
-/* AC control */
-
-volatile uint8_t command_val = 0;
-volatile int act_cmd = 100; /* Start off */
-int8_t force_cmd = -1; /* Forced command value, for tests */
-static int fan_on = 0;
-int force_fan = 0; /* Request to force fan ON from test menu */
-
-
-void set_ctrl_duty_cycle(uint8_t value)
-{
- act_cmd = value;
- /* Below 3% and above 97% there are triggering problems which
- * lead to 50% instead of desired command */
- if (act_cmd >= 97) {
- act_cmd = 100;
- } else if (act_cmd <= 2) {
- act_cmd = 0;
- }
-}
-
-void ac_switch_on(uint32_t flags)
-{
- gpio_set(ac_ctrl);
-}
-
-static uint32_t clk_cycles_ac_zc = 0;
-volatile uint32_t zc_cnt = 0;
-void zero_cross(uint32_t gpio)
-{
- uint32_t delay = 0;
-
- zc_cnt++;
- if (act_cmd == 100) {
- gpio_set(ac_ctrl);
- return;
- }
- gpio_clear(ac_ctrl);
- if (act_cmd == 0) {
- return;
- }
- /* Set timer to trigger ac out ON at given delay */
- delay = clk_cycles_ac_zc * (100 - act_cmd);
- timer_set_match(LPC_TIMER_32B1, CHAN0, delay);
- timer_restart(LPC_TIMER_32B1);
-}
-
-/* This one is used to synchronize to the real zero-crossing detection, if any. */
-void zero_cross_detect(uint32_t gpio)
-{
- timer_restart(LPC_TIMER_32B0);
- zero_cross(0);
-}
-
-/* Handle the power command */
-#define CMD_UP_DELAY_RESET_VALUE 3
-void handle_cmd_update(uint32_t curent_tick)
-{
- static uint8_t cmd = 0;
- static uint8_t cmd_up_delay = CMD_UP_DELAY_RESET_VALUE;
-
- /* Unable to read internal temperature : turn off heating */
- if (error_shutdown == 1) {
- cmd = 0;
- goto cmd_update_end;
- }
-
- /* Water max temperature protection */
- if (water_centi_degrees > (MAX_WATER_TEMP * 10)) {
- cmd = 0;
- goto cmd_update_end;
- }
-
- /* Forced test mode */
- if (force_cmd != -1) {
- cmd = force_cmd;
- goto cmd_update_end;
- }
-
- /* Which is the current mode ? */
- if (forced_heater_mode == 1) {
- /* Forced heating mode */
- if ((forced_heater_delay == 0) && (forced_heater_time > 0)) {
- cmd = FORCED_MODE_VALUE;
- goto cmd_update_end;
- }
- if (manual_forced_heater > 0) {
- cmd = FORCED_MODE_VALUE;
- goto cmd_update_end;
- }
- /* Entering forced heating mode from temperature getting below threshold,
- * wait some delay before effective forced heating */
- if (forced_heater_time == 0) {
- forced_heater_delay = FORCED_HEATER_DELAY;
- forced_heater_time = FORCED_HEATER_DELAY + FORCED_HEATER_DURATION;
- }
- }
- if (solar_prod_value < (home_conso_value - 64)) {
- /* Low production mode */
- if (cmd > 0) {
- cmd--;
- }
- } else if (solar_prod_value > (home_conso_value + 1280)) {
- if (cmd < 100) {
- cmd++;
- }
- } else if (solar_prod_value > (home_conso_value + 256)) {
- /* High production mode */
- if (cmd_up_delay > 0) {
- cmd_up_delay--;
- } else {
- cmd_up_delay = CMD_UP_DELAY_RESET_VALUE;
- if (cmd < 100) {
- cmd++;
- }
- }
- }
-
-cmd_update_end:
- if ((cmd > 0) || (force_fan == 1)) {
- fan_on = 1;
- gpio_set(fan_ctrl);
- } else {
- fan_on = 0;
- gpio_clear(fan_ctrl);
- }
- command_val = cmd;
- /* Set Control Output duty cycle */
- set_ctrl_duty_cycle(100 - cmd);
-}
-
-
-/***************************************************************************** */
-/* Power tracking - load part */
-
-/* ADC values are between 0 and 1023, no load value is at about 520 */
-static volatile uint16_t load_power_highest = 520;
-static volatile uint16_t load_power_lowest = 520;
-void power_track(uint32_t flags)
-{
- /* Start tracking the higest value */
- static int tracking = 1;
- static uint16_t last_value = 0;
- uint16_t val = 0;
-
- /* Get load power usage */
- adc_get_value(&val, LPC_ADC(2));
-
- /* Four possibilities :
- * tracking high and value increasing : nothing to do
- * tracking high and value decreasing : we found the max, store it and change tracking
- * tracking low and value decreasing : nothing to do
- * tracking low and value increasing : we found the min, store it and change tracking
- */
- if (tracking == 1) {
- if (last_value > val) {
- /* Found max */
- load_power_highest = last_value;
- tracking = 0;
- }
- } else {
- if (last_value < val) {
- /* Found min */
- load_power_lowest = last_value;
- tracking = 1;
- }
- }
- last_value = val;
-}
-
-/***************************************************************************** */
-void scialys_config(void)
-{
- /* We want 100 Hz (50 Hz but two zero crossings) with 1% granularity */
- clk_cycles_ac_zc = get_main_clock() / (100 * 100);
-
- /* Configure the fake zero-cross generation timer to 100Hz.
- * Do not start it now, it will be started by the first real zero-cross detected in order to
- * generate the other half zc (not seen by system due to hardware design)
- * Also sarted by config when DC mode is set */
- timer_set_match(LPC_TIMER_32B0, CHAN0, (clk_cycles_ac_zc * 100));
-
- /* Configure the power tracking timer.
- * 20 points per half sin wave should be enough to get the min and max */
- timer_set_match(LPC_TIMER_16B0, CHAN0, (clk_cycles_ac_zc * 5));
- timer_start(LPC_TIMER_16B0);
-
- /* Read parameters from memory */
- if (1) {
- never_force = 0;
- sunny_days_prod_value_config = SUNNY_DAYS_PROD_VALUE;
- }
-
- /* Add a systick callback to handle command value update */
- add_systick_callback(handle_cmd_update, CMD_UPD_PERIOD);
-
- /* Add a systick callback to handle time counting */
- add_systick_callback(handle_dec_request, DEC_PERIOD);
-}
-
-/* Add a false zero-cross, but make sure to do it only once and only if we are not AC powered */
-void DC_switch_start(void)
-{
- static int already_done = 0;
- if ((already_done == 0) && (zc_cnt == 0)) {
- int ret = add_systick_callback(zero_cross, 10); /* 2 zero-crossing at 50Hz -> 100Hz -> 10ms */
- uprintf(UART0, "Entering forced DC mode on systick callback %d\n", ret);
- already_done = 1;
- } else {
- uprintf(UART0, "DC forced mode already ON: %d/%d\n", already_done, zc_cnt);
- }
-}
-
-/***************************************************************************** */
-int main(void)
-{
- uint32_t loop = 0;
- char mode = heat; /* Debug info */
- int ret = 0;
-
- system_init();
- board_io_config();
- modules_config();
-
- external_config(UART0);
- scialys_config();
-
- msleep(500);
- status_led(green_only);
-
- while (1) {
-
- /* Default mode : try to heat the water tank */
- mode = heat;
-
- /* Always track power consumption and production */
- track_isnail_values();
-
- /* Feed the dog */
- if ((solar_prod_value != 0) && (home_conso_value != 0)) {
- watchdog_feed();
- }
-
- /* Get internal temperature */
- ret = temp_read(UART0, &deci_degrees_disp, &deci_degrees_power);
- if (ret != CONV_OK) {
- /* Set internal temperature to 60°C if there's a temp error, as this will trigger the
- * internal fan */
- if ((ret & CONV_POWER_OK) == 0) {
- deci_degrees_power = DEFAULT_INTERNAL_TEMP;
- }
- if ((ret & CONV_DISPLAY_OK) == 0) {
- deci_degrees_disp = DEFAULT_INTERNAL_TEMP;
- }
- }
-
- /* Internal temperature protection */
- if ((deci_degrees_power > MAX_INTERNAL_TEMP) || (deci_degrees_disp > MAX_INTERNAL_TEMP)) {
- error_shutdown = 1;
- } else if ((deci_degrees_power < ((MAX_INTERNAL_TEMP) / 2)) &&
- (deci_degrees_disp < ((MAX_INTERNAL_TEMP) / 2))) {
- error_shutdown = 0;
- }
-
- /* Get thermocouple value */
- if (1) {
- int ret = 0, old_water_temp = 0;
- old_water_temp = water_centi_degrees;
- ret = max31855_sensor_read(&thermocouple, NULL, &water_centi_degrees);
- if (ret != 0) {
- uprintf(UART0, "Water Temp read error : %d\n", ret);
- /* Do not act upon iinvalid temp value */
- water_centi_degrees = old_water_temp;
- }
- }
-
- /* Need to enter Forced heating mode ? */
- if (water_centi_degrees < (FORCE_HEATER_TEMP * 10)) {
- if (forced_heater_mode == 0) {
- uprintf(UART0, "Water temp low, entering forced mode\n");
- forced_heater_mode = 1;
- }
- status_led(red_on);
- mode = forced;
- } else if ((water_centi_degrees > (TARGET_FORCED_HEATER_TEMP * 10)) && (forced_heater_mode == 1)) {
- status_led(red_off);
- uprintf(UART0, "Water temp OK, forced mode exit\n");
- forced_heater_mode = 0;
- mode = temp_OK;
- }
-
- /* Do not force if there is a lot of sun, it may be enough to heat again soon */
- if ((solar_prod_value > sunny_days_prod_value_config) && (forced_heater_mode == 1)) {
- mode = delayed_heat_prod;
- forced_heater_mode = 0;
- }
-
- /* Do not force heating if disabled by external command */
- external_disable = gpio_read(ext_disable_in_pin);
- if ((external_disable == EXTERNAL_DISABLE_FORCE) && (forced_heater_mode != 0)) {
- forced_heater_mode = 0;
- mode = ext_disable;
- uprintf(UART0, "Forced mode disabled by external input\n");
- }
-
- if (never_force == 1) {
- forced_heater_mode = 0;
- }
-
- /* Did the user request a forced heating ? */
- if (manual_activation_request != 0) {
- forced_heater_mode = 1;
- mode = manual;
- if (manual_activation_request == -1) {
- uprintf(UART0, "Entering manual forced mode for %d ticks\n", manual_activation_request);
- manual_activation_request = MANUAL_ACTIVATION_DURATION;
- manual_forced_heater = 1;
- }
- if (manual_activation_request < 10) {
- uprintf(UART0, "Leaving manual forced mode\n");
- manual_activation_request = 0;
- }
- }
-
- /* Command at 100% and still more production than energy used ? */
- if ((command_val == 100) && (solar_prod_value > home_conso_value)) {
- mode = overprod;
- }
-
- /* Display */
- interface_update(mode);
-
- /* Debug */
- if (1) {
- uprintf(UART0, "%c:%d - Is: %d,%04d - Ih: %d,%04d\n", mode, loop++,
- (solar_prod_value / 1000), (solar_prod_value % 1000),
- (home_conso_value / 1000), (home_conso_value % 1000));
- uprintf(UART0, "Water Temp : %d\n", water_centi_degrees);
- uprintf(UART0, "Internal Temp power : %d\n", deci_degrees_power);
- uprintf(UART0, "Internal Temp display : %d\n", deci_degrees_disp);
- uprintf(UART0, "ADC: Sol: %dmA, Home: %dmA\n", solar_prod_value, home_conso_value);
- uprintf(UART0, "Load: %d - %d\n", load_power_lowest, load_power_highest);
- uprintf(UART0, "Zc: %d\n", zc_cnt);
- uprintf(UART0, "CMD: %d/%d, Fan: %d/%d\n\n", command_val, (100 - act_cmd), fan_on, force_fan);
- }
-
- /* Wait approx 10ms between each loop */
- //msleep(10);
- }
- return 0;
-}
-
-
-
+++ /dev/null
-/****************************************************************************
- * apps/blyes/inter_lights/time.c
- *
- * Copyright 2018 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 = 0x18,
- .month = 0x08,
- .day = 0x09,
- .hour = 0x13,
- .min = 0x37,
-};
-int time_valid = 0;
-int rtc_conf_ok = 0;
-
-int 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 time_update(uint32_t uart, struct rtc_time* now)
-{
- rtc_pcf85363_time_write(&rtc_conf, now);
- return 0;
-}
-
-
-/***************************************************************************** */
-/* RTC init */
-void time_config(uint32_t i2c_bus_num, uint8_t rtc_addr)
-{
- rtc_conf.bus_num = i2c_bus_num;
- rtc_conf.addr = rtc_addr;
-}
-
+++ /dev/null
-/****************************************************************************
- * apps/blyes/inter_lights/time.h
- *
- * Copyright 2018 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 time_init_check(uint32_t uart);
-void time_config(uint32_t i2c_bus_num, uint8_t rtc_addr);
-
-#endif /* RTC_TIME_H */
-
+++ /dev/null
-/****************************************************************************
- * apps/blyes/inter_lights/uSD.c
- *
- * Copyright 2018 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/ssp.h"
-
-#include "lib/stdio.h"
-
-#include "uSD.h"
-
-
-/***************************************************************************** */
-/* SD/MMC Card */
-struct sdmmc_card micro_sd = {
- .card_type = MMC_CARDTYPE_UNKNOWN,
- .block_size = 64,
-};
-
-static uint8_t got_uSD = 0;
-
-/* Buffer to read / write uSD card */
-uint8_t mmc_data[MMC_BUF_SIZE];
-
-/* Read up to 4 blocks of 16 bytes each */
-int uSD_read(uint32_t offset, uint8_t nb_blocks)
-{
- int i = 0, ret = 0;
- if (got_uSD == 0) {
- return -1;
- }
- if (nb_blocks > 4) {
- return -2;
- }
- memset(mmc_data, 0, MMC_BUF_SIZE);
- for (i = 0; i < nb_blocks; i++) {
- ret = sdmmc_read_block(µ_sd, (offset + i), (mmc_data + (i * 16)));
- /* FIXME : check return value */
- }
- return 0;
-}
-
-
-/* Write up to 4 blocks of 16 bytes each */
-int uSD_write(uint32_t offset, uint8_t nb_blocks)
-{
- int i = 0, ret = 0;
- if (got_uSD == 0) {
- return -1;
- }
- if (nb_blocks > 4) {
- return -1;
- }
- for (i = 0; i < nb_blocks; i++) {
- ret = sdmmc_write_block(µ_sd, (offset + i), (mmc_data + (i * 16)));
- /* FIXME : check return value */
- }
- return 0;
-}
-
-/* microSD card init */
-int uSD_detect(int uart)
-{
- int i = 0, ret = 0, step = 0;
- do {
- step = 0;
- ret = sdmmc_init(µ_sd);
- if (ret == 0) {
- step = 1;
- msleep(10);
- ret = sdmmc_init_wait_card_ready(µ_sd);
- if (ret <= 1) {
- step = 2;
- ret = sdmmc_init_end(µ_sd);
- }
- }
- uprintf(uart, "uSD init(%d): step:%d, ret: %d, type: %d, bs: %d\n",
- i, step, ret, micro_sd.card_type, micro_sd.block_size);
- i++;
- } while (((ret != 0) || (micro_sd.card_type == MMC_CARDTYPE_UNKNOWN)) && (i < 10));
-
- /* Got uSD ? */
- if (i >= 10) {
- uprintf(uart, "uSD init failed, no uSD card present.\n");
- got_uSD = 0;
- return -1;
- }
-
- /* uSD card detected */
- got_uSD = 1;
- /* got_uSD MUST be set to 1 from here on ! */
- uSD_read(0, 4); /* Read 4 blocks at start of card */
- /* FIXME : check that the card magic is present */
- uprintf(uart, "uSD read: %s\n", mmc_data);
-
- return 0;
-}
-
-
-int uSD_logs_init(int uart)
-{
- /* FIXME : read 4 blocks at offset FIXME to get the last block used info */
- return 0;
-}
-
-/***************************************************************************** */
-/* microSD card init */
-void uSD_config(struct pio* uSD_cs, uint32_t ssp_bus_num)
-{
- memcpy(&(micro_sd.chip_select), uSD_cs, sizeof(struct pio));
- micro_sd.ssp_bus_num = ssp_bus_num;
-}
-
+++ /dev/null
-/****************************************************************************
- * apps/blyes/inter_lights/usSD.h
- *
- * Copyright 2018 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 USD_H
-#define USD_H
-
-#include "extdrv/sdmmc.h"
-
-#define MMC_BUF_SIZE 64
-
-extern struct sdmmc_card micro_sd;
-extern uint8_t mmc_data[MMC_BUF_SIZE];
-
-int uSD_read(uint32_t offset, uint8_t nb_blocks);
-int uSD_write(uint32_t offset, uint8_t nb_blocks);
-
-/***************************************************************************** */
-/* microSD card init */
-int uSD_detect(int uart);
-void uSD_config(struct pio* uSD_cs, uint32_t ssp_bus_num);
-
-
-#endif /* USD_H */
-
+++ /dev/null
-# 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} $@
-
+++ /dev/null
-/****************************************************************************
- * apps/scialys/v07/config.c
- *
- * Copyright 2018 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 "drivers/adc.h"
-#include "drivers/ssp.h"
-#include "drivers/timers.h"
-
-
-#include "extdrv/status_led.h"
-#include "extdrv/max31855_thermocouple.h"
-
-#include "lib/stdio.h"
-
-#include "config.h"
-#include "interface.h"
-#include "time.h"
-#include "uSD.h"
-
-#define SELECTED_FREQ FREQ_SEL_48MHz
-
-
-uint8_t config_got_interface = 1;
-
-/***************************************************************************** */
-/* Pins configuration */
-/* pins blocks are passed to set_pins() for pins configuration.
- * Unused pin blocks can be removed safely with the corresponding set_pins() call
- * All pins blocks may be safelly merged in a single block for single set_pins() call..
- */
-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) },
- /* SPI */
- { LPC_SSP0_SCLK_PIO_0_14, LPC_IO_DIGITAL },
- { LPC_SSP0_MOSI_PIO_0_17, LPC_IO_DIGITAL },
- { LPC_SSP0_MISO_PIO_0_16, LPC_IO_DIGITAL },
- /* ADC */
- { LPC_ADC_AD0_PIO_0_30, LPC_IO_ANALOG }, /* Home */
- { LPC_ADC_AD1_PIO_0_31, LPC_IO_ANALOG }, /* Production */
- { LPC_ADC_AD2_PIO_1_0, LPC_IO_ANALOG }, /* Load */
- /* GPIO */
- { LPC_GPIO_0_0, LPC_IO_DIGITAL }, /* Over Voltage protection input */
- { LPC_GPIO_0_3, LPC_IO_DIGITAL }, /* External switch input */
- { LPC_GPIO_0_4, LPC_IO_DIGITAL }, /* Zero crossing detection input */
- { LPC_GPIO_0_5, LPC_IO_DIGITAL }, /* Fan control */
- { LPC_GPIO_0_6, LPC_IO_DIGITAL }, /* Mosfet control */
- { LPC_GPIO_0_7, LPC_IO_DIGITAL }, /* Thermocouple chip select */
- { LPC_GPIO_0_12, LPC_IO_DIGITAL }, /* ISP / User button OK */
- { LPC_GPIO_0_18, LPC_IO_DIGITAL }, /* Uext Chip select / Module eeprom select */
- { 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 }, /* Over Temperature protection input */
- { LPC_GPIO_1_1, LPC_IO_DIGITAL }, /* uSD Card SPI Chip Select */
- { 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;
-
-/* Inputs */
-/* External signals */
-const struct pio zero_cross_in_pin = LPC_GPIO_0_4;
-const struct pio ext_disable_in_pin = LPC_GPIO_0_3;
-const struct pio overvoltage_pin = LPC_GPIO_0_0;
-const struct pio overtemperature_pin = LPC_GPIO_0_28;
-
-/* Outputs */
-/* AC output control (Mosfet) */
-const struct pio ac_ctrl = LPC_GPIO_0_6;
-const struct pio fan_ctrl = LPC_GPIO_0_5;
-/* Chip selects */
-const struct pio uSD_cs = LPC_GPIO_1_1; /* uSD card */
-const struct pio th_cs = LPC_GPIO_0_7; /* Thermocouple */
-const struct pio uext_cs = LPC_GPIO_0_18; /* UEXT module (optionnal) */
-
-
-
-/***************************************************************************** */
-/* 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);
-}
-
-
-/***************************************************************************** */
-/* Internal modules */
-const struct lpc_tc_config ac_timer_conf_zc = {
- .mode = LPC_TIMER_MODE_TIMER | LPC_TIMER_MODE_MATCH,
- .match_control = { LPC_TIMER_INT_RESET_ON_MATCH, 0, 0, 0, },
-};
-const struct lpc_tc_config ac_timer_conf_delay = {
- .mode = LPC_TIMER_MODE_TIMER | LPC_TIMER_MODE_MATCH,
- .match_control = { LPC_TIMER_INT_RESET_AND_STOP_ON_MATCH, 0, 0, 0, },
-};
-const struct lpc_tc_config ac_timer_conf_power_track = {
- .mode = LPC_TIMER_MODE_TIMER | LPC_TIMER_MODE_MATCH,
- .match_control = { LPC_TIMER_INT_RESET_ON_MATCH, 0, 0, 0, },
-};
-
-extern void config_rx(uint8_t c);
-extern void comm_rx(uint8_t c);
-extern void ac_switch_on(uint32_t flags);
-extern void zero_cross(uint32_t flags);
-extern void power_track(uint32_t flags);
-
-void modules_config(void)
-{
- uart_on(UART0, 115200, config_rx);
- uart_on(UART1, 115200, comm_rx);
- i2c_on(I2C0, I2C_CLK_100KHz, I2C_MASTER);
- ssp_master_on(SSP_BUS_0, LPC_SSP_FRAME_SPI, 8, 4*1000*1000);
- adc_on(NULL);
- adc_start_burst_conversion(ADC_MCH(0) | ADC_MCH(1) | ADC_MCH(2), LPC_ADC_SEQ(0));
- timer_on(LPC_TIMER_32B0, 0, zero_cross); /* This one is used to generate the second zero-crossing */
- timer_counter_config(LPC_TIMER_32B0, &ac_timer_conf_zc);
- timer_on(LPC_TIMER_32B1, 0, ac_switch_on); /* This one is used to generate the power PMW */
- timer_counter_config(LPC_TIMER_32B1, &ac_timer_conf_delay);
- timer_on(LPC_TIMER_16B0, 0, power_track); /* This one is used to call the load power tracking ADC sampler */
- timer_counter_config(LPC_TIMER_16B0, &ac_timer_conf_power_track);
-}
-
-
-/***************************************************************************** */
-extern void zero_cross_detect(uint32_t gpio);
-extern void overvoltage_protect(uint32_t gpio);
-void board_io_config(void)
-{
- /* Immediatly turn off Mosfet / Triac */
- config_gpio(&ac_ctrl, 0, GPIO_DIR_OUT, 1);
- /* Start with FAN ON */
- config_gpio(&fan_ctrl, 0, GPIO_DIR_OUT, 1);
-
- /* Configure Input GPIO */
- config_gpio(&ext_disable_in_pin, 0, GPIO_DIR_IN, 0);
- config_gpio(&overtemperature_pin, 0, GPIO_DIR_IN, 0);
-
- /* Over-voltage protection */
- set_gpio_callback(overvoltage_protect, &overvoltage_pin, EDGE_RISING);
-
- /* Zero-crossing detection */
- set_gpio_callback(zero_cross_detect, &zero_cross_in_pin, EDGE_FALLING);
-}
-
-
-
-/***************************************************************************** */
-/* Configure external components */
-
-/* Thermocouple reading */
-const struct max31855_sensor_config thermocouple = {
- .ssp_bus_num = 0,
- .chip_select = LPC_GPIO_0_7,
-};
-
-/* RTC and time */
-#define RTC_ADDR 0xA2
-
-
-int external_config(uint32_t uart)
-{
- int ret = 0;
-
- /* Configure GPIO for specific board usage */
- board_io_config();
-
- /* uSD card */
- uSD_config((struct pio *)&uSD_cs, SSP_BUS_0);
- uSD_detect(uart);
-
- /* RTC */
- time_config(I2C0, RTC_ADDR);
- time_init_check(uart);
-
- /* Thermocouple configuration */
- max31855_sensor_config(&thermocouple);
-
- /* Configure interface board */
- ret = interface_config(uart);
- if (ret != 0) {
- return -1;
- }
- return 0;
-}
+++ /dev/null
-/****************************************************************************
- * apps/scialys/v07/config.h
- *
- * Copyright 2018 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 "drivers/adc.h"
-#include "drivers/ssp.h"
-#include "drivers/timers.h"
-
-#include "extdrv/status_led.h"
-#include "extdrv/max31855_thermocouple.h"
-#include "extdrv/ws2812.h"
-
-#include "lib/stdio.h"
-
-
-#define SELECTED_FREQ FREQ_SEL_48MHz
-
-/***************************************************************************** */
-/* Pins configuration */
-extern const struct pio status_led_green;
-extern const struct pio status_led_red;
-
-/* Inputs */
-/* External signals */
-extern const struct pio zero_cross_in_pin;
-extern const struct pio ext_disable_in_pin;
-extern const struct pio overvoltage_pin;
-extern const struct pio overtemperature_pin;
-
-
-/* Outputs */
-/* AC output control (Mosfet) */
-extern const struct pio ac_ctrl;
-extern const struct pio fan_ctrl;
-/* Chip selects */
-extern const struct pio uSD_cs; /* uSD card */
-extern const struct pio th_cs; /* Thermocouple */
-extern const struct pio uext_cs; /* UEXT module (optionnal) */
-
-
-
-/***************************************************************************** */
-extern const struct max31855_sensor_config thermocouple;
-
-
-/***************************************************************************** */
-/* 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 modules and main functions */
-void modules_config(void);
-
-/* Configure GPIO for specific board usage */
-void board_io_config(void);
-
-/* Configure external components */
-int external_config(uint32_t uart);
-
-
-#endif /* CONFIG_H */
-
+++ /dev/null
-/****************************************************************************
- * apps/scialys/v07/interface.c
- *
- * Copyright 2018 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 "drivers/adc.h"
-
-#include "extdrv/status_led.h"
-#include "extdrv/ws2812.h"
-#include "extdrv/tmp101_temp_sensor.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 "interface.h"
-#include "time.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;
-
-
-static uint8_t interface_board_present = 0;
-static uint8_t power_board_present = 0;
-
-/***************************************************************************** */
-/* TMP101 I2C temperature sensor on Power board */
-#define TMP101_ADDR1 0x94 /* Pin Addr0 (pin5 of tmp101) connected to VCC */
-struct tmp101_sensor_config tmp101_sensor_power = {
- .bus_num = I2C0,
- .addr = TMP101_ADDR1,
- .resolution = TMP_RES_ELEVEN_BITS,
-};
-
-/* TMP101 I2C temperature sensor on display board */
-#define TMP101_ADDR0 0x90 /* Pin Addr0 (pin5 of tmp101) connected to GND */
-struct tmp101_sensor_config tmp101_sensor_display = {
- .bus_num = I2C0,
- .addr = TMP101_ADDR0,
- .resolution = TMP_RES_ELEVEN_BITS,
-};
-
-/***************************************************************************** */
-/* 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;
-
- if (interface_board_present == 0) {
- return -1;
- }
-
- 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;
-
- if (interface_board_present == 0) {
- return -1;
- }
-
- 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);
-}
-
-
-int temp_read(uint32_t uart, int* deci_degrees_disp, int* deci_degrees_power)
-{
- int ret = 0;
- int conv = 0;
-
- if (interface_board_present != 0) {
- ret = tmp101_sensor_read(&tmp101_sensor_display, NULL, deci_degrees_disp);
- if (ret == 0) {
- conv |= CONV_DISPLAY_OK;
- } else {
- uprintf(uart, "TMP101 read error on display board : %d\n", ret);
- }
- }
- if (power_board_present != 0) {
- ret = tmp101_sensor_read(&tmp101_sensor_power, NULL, deci_degrees_power);
- if (ret == 0) {
- conv |= CONV_POWER_OK;
- } else {
- uprintf(uart, "TMP101 read error on power board : %d\n", ret);
- }
- }
- return conv;
-}
-
-
-/***************************************************************************** */
-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;
- }
-}
-
-int interface_config(uint32_t uart)
-{
- int ret = 0;
-
- /* TMP101 sensor config on power board */
- power_board_present = 1;
- ret = tmp101_sensor_config(&tmp101_sensor_power);
- if (ret != 0) {
- uprintf(uart, "Temp config error on power board: %d\n", ret);
- power_board_present = 0;
- } else {
- ret = tmp101_sensor_set_continuous_conversion(&tmp101_sensor_power);
- if (ret != 0) {
- uprintf(uart, "Temp config error on power board: %d\n", ret);
- power_board_present = 0;
- }
- }
-
- /* TMP101 sensor config on display board */
- ret = tmp101_sensor_config(&tmp101_sensor_display);
- if (ret != 0) {
- uprintf(uart, "Temp config error on display board: %d\n", ret);
- interface_board_present = 0;
- return -1;
- }
- ret = tmp101_sensor_set_continuous_conversion(&tmp101_sensor_display);
- if (ret != 0) {
- uprintf(uart, "Temp config error on display board: %d\n", ret);
- interface_board_present = 0;
- return -2;
- }
- interface_board_present = 1;
-
- /* Buttons inputs on front panel */
- /* Activate on Rising edge (button release) */
- set_gpio_callback(button_callback, &button_up, EDGE_RISING);
- set_gpio_callback(button_callback, &button_left, EDGE_RISING);
- set_gpio_callback(button_callback, &button_right, EDGE_RISING);
- set_gpio_callback(button_callback, &button_down, EDGE_RISING);
- set_gpio_callback(button_callback, &button_ok, EDGE_RISING);
-
- /* WS2812B Leds on display board */
- ws2812_config(&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(0, 0x05, 0x15, 0x08);
- ws2812_send_frame(0);
-
- uprintf(uart, "Config OK\n");
-
- return 0;
-}
-
-
-
-/***************************************************************************** */
- /* Menu part */
-
-enum interface_modes {
- MODE_RUN = 0,
- MODE_CONFIG,
- MODE_DISPLAY,
-};
-static int interface_mode = MODE_RUN;
-volatile int manual_activation_request = 0;
-
-enum menu_list {
- MAIN_MENU,
- MANUAL_MODE,
- DATE_CONFIG,
- FUNCTIONS,
- LIMITS,
- SAVE_CONFIG,
- NB_MENU, /* This one must be the last */
-};
-static const char* menu_titles[] = {
- [MAIN_MENU] = "Menu principal",
- [MANUAL_MODE] = "Marche Forcee",
- [DATE_CONFIG] = "Regl. Heure",
- [FUNCTIONS] = "Reglages",
- [LIMITS] = "Limites",
- [SAVE_CONFIG] = "Save config",
-};
-static uint8_t current_menu = MAIN_MENU;
-static uint8_t current_entry = MANUAL_MODE;
-
-enum func_menu_list {
- FUNC_CMD_TYPE,
- FUNC_TENSION,
- INST_POWER,
- LOAD_POWER,
- FORCED_MODE_VAL,
- FUNC_NB_MENU, /* This one must be the last */
-};
-static const char* func_modes_titles[] = {
- [FUNC_CMD_TYPE] = "Type Cmd", /* ToR / PWM */
- [FUNC_TENSION] = "Tension", /* AC / DC */
- [INST_POWER] = "P Prod", /* Production power (kW) */
- [LOAD_POWER] = "P Cahrge", /* Load power (kW) */
- [FORCED_MODE_VAL] = "Force Val", /* Default is 75% */
-};
-static uint8_t func_cur_menu = FUNC_CMD_TYPE;
-static uint8_t func_cur_entry = FUNC_CMD_TYPE;
-
-enum limits_menu_list {
- TEMP_FORCED_START,
- TEMP_FORCED_TARGET,
- SUNNY_DAY_VALUE,
- LIM_NB_MENU, /* This one must be the last */
-};
-static const char* limits_modes_titles[] = {
- [TEMP_FORCED_START] = "Water Min",
- [TEMP_FORCED_TARGET] = "Water Target",
- [SUNNY_DAY_VALUE] = "Sun Prod",
-};
-static uint8_t limits_cur_menu = TEMP_FORCED_START;
-static uint8_t limits_cur_entry = TEMP_FORCED_START;
-
-
-
-/* Current sub-menu level. main menu is level 0 */
-int sub_menu_level = 0;
-
-
-void config_interface_handle(void)
-{
- char line[DISP_LLEN];
- 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;
- for (i = 1; i < NB_MENU; i++) {
- if (i != current_entry) {
- display_line(i + 1, 3, menu_titles[i]);
- } else {
- snprintf(line, DISP_LLEN, " ->%s", menu_titles[i]);
- display_line(i + 1, 0, line);
- }
- }
- 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) {
- current_entry = NB_MENU - 1;
- }
- if (button & BUTTON_RIGHT) {
- current_menu = current_entry;
- sub_menu_level = 1;
- }
- if (button & (BUTTON_LEFT | BUTTON_OK)) {
- interface_mode = MODE_RUN;
- }
- }
- break;
- case DATE_CONFIG: {
- static uint8_t date_idx = 0;
- struct rtc_time now;
- snprintf(line, DISP_LLEN, "^");
- display_line(3, 6 + (date_idx * 3), line);
- rtc_pcf85363_time_read(&rtc_conf, &now);
- snprintf(line, DISP_LLEN, "%02xh%02x:%02x", now.hour, now.min, now.sec);
- display_line(4, 6, line);
- snprintf(line, DISP_LLEN, "V");
- display_line(5, 6 + (date_idx * 3), line);
- if ((button & BUTTON_RIGHT) && (date_idx < 2)) {
- date_idx++;
- }
- if ((button & BUTTON_LEFT) && (date_idx > 0)) {
- date_idx--;
- }
- if (button & (BUTTON_UP | BUTTON_DOWN)) {
- uint32_t seconds = rtc_pcf85363_daytime_to_seconds(&now);
- uint32_t add[6] = {
- 3600, 60, 1,
- (23 * 3600), (23* 3600 + 59 * 60), (23 * 3600 + 59 * 60 + 59),
- };
- if (button & BUTTON_UP) {
- if (seconds < add[date_idx + 3]) {
- seconds += add[date_idx];
- } else {
- seconds -= add[date_idx + 3];
- }
- } else {
- if (seconds < add[date_idx]) {
- seconds += add[date_idx + 3];
- } else {
- seconds -= add[date_idx];
- }
- }
- rtc_pcf85363_seconds_to_daytime(&now, seconds);
- rtc_pcf85363_time_write(&rtc_conf, &now);
- }
- if (button & BUTTON_OK) {
- sub_menu_level = 0;
- current_menu = MAIN_MENU;
- }
- }
- break;
- case FUNCTIONS: {
- if (sub_menu_level == 1) {
- int i = 0;
- for (i = 0; i < FUNC_NB_MENU; i++) {
- if (i != func_cur_entry) {
- display_line(i + 2, 3, func_modes_titles[i]);
- } else {
- snprintf(line, DISP_LLEN, " ->%s", func_modes_titles[i]);
- display_line(i + 2, 0, line);
- }
- }
- if (button & BUTTON_UP) {
- func_cur_entry -= 1;
- }
- if (button & BUTTON_DOWN) {
- func_cur_entry += 1;
- }
- if (func_cur_entry >= FUNC_NB_MENU) {
- func_cur_entry = 0;
- } else if (func_cur_entry == 0xFF) {
- func_cur_entry = FUNC_NB_MENU - 1;
- }
- if (button & (BUTTON_OK | BUTTON_RIGHT)) {
- func_cur_menu = func_cur_entry;
- sub_menu_level = 2;
- }
- if (button & BUTTON_LEFT) {
- sub_menu_level = 0;
- current_menu = MAIN_MENU;
- }
- } else {
- }
- }
- 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 {
- snprintf(line, DISP_LLEN, " ->%s", limits_modes_titles[i]);
- display_line(i + 2, 0, line);
- }
- }
- if (button & BUTTON_UP) {
- limits_cur_entry -= 1;
- }
- if (button & BUTTON_DOWN) {
- limits_cur_entry += 1;
- }
- if (limits_cur_entry >= LIM_NB_MENU) {
- limits_cur_entry = 0;
- } else if (limits_cur_entry == 0xFF) {
- limits_cur_entry = LIM_NB_MENU - 1;
- }
- 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 {
- }
- }
- break;
- case SAVE_CONFIG: {
- }
- break;
- case MANUAL_MODE:
- manual_activation_request = -1;
- /* Fallback */
- default:
- interface_mode = MODE_RUN;
- current_menu = MAIN_MENU;
- break;
- }
- /* Update Oled display */
- ssd130x_display_full_screen(&display);
-}
-
-
-/***************************************************************************** */
- /* usual menu and switch to config menu */
-
-extern int water_centi_degrees;
-extern uint32_t solar_prod_value;
-extern uint32_t home_conso_value;
-extern volatile uint8_t command_val;
-
-void interface_update(char heat_mode)
-{
- int abs_centi = water_centi_degrees;
-
- if (water_centi_degrees < 0) {
- abs_centi = -water_centi_degrees;
- }
-
- /* Debug */
- if (button_pressed != 0) {
- uprintf(UART0, "Button : 0x%02x\n", button_pressed);
- }
-
- if (interface_mode == MODE_RUN) {
- if (button_pressed != 0) {
- if (button_pressed & BUTTON_OK) {
- manual_activation_request = -1;
- }
- if (button_pressed & BUTTON_UP) {
- interface_mode = MODE_CONFIG;
- }
- button_pressed = 0;
- }
-
- if (interface_board_present != 0) {
- char line[DISP_LLEN];
-
- /* Start with a blank screen */
- erase_screen_content();
-
- /* Update time and time display on internal memory */
- rtc_pcf85363_time_read(&rtc_conf, &now);
- snprintf(line, DISP_LLEN, "%02xh%02x:%02x", now.hour, now.min, now.sec);
- display_line(0, 0, line);
-
- /* Display info */
- snprintf(line, DISP_LLEN, "Water:% 2d.%03d %cC", (water_centi_degrees / 100), (abs_centi % 100), 0x1F);
- display_line(2, 0, line);
- snprintf(line, DISP_LLEN, "Prod :% 2d,%03dA", (solar_prod_value / 1000), ((solar_prod_value % 1000) / 10));
- display_line(3, 0, line);
- snprintf(line, DISP_LLEN, "Conso:% 2d,%03dA", (home_conso_value / 1000), ((home_conso_value % 1000) / 10));
- display_line(4, 0, line);
- snprintf(line, DISP_LLEN, "Command: %d%%", command_val);
- display_line(5, 0, line);
- snprintf(line, DISP_LLEN, "Mode: %c", heat_mode);
- display_line(6, 0, line);
- snprintf(line, DISP_LLEN, "v0.9 - 0.3.5");
- display_line(7, 0, line);
- /* Update Oled display */
- ssd130x_display_full_screen(&display);
-
- /* Update RGB leds */
- /* FIXME : use for error signal */
- ws2812_set_pixel(0, (home_conso_value / 2000), (solar_prod_value / 2000), 0);
- ws2812_set_pixel(1, 0, 0, command_val);
- ws2812_send_frame(0);
- }
- } else {
- /* Config mode. Mode entered by button action, which implies that interface board is present. */
- config_interface_handle();
- }
-}
-
-
+++ /dev/null
-/****************************************************************************
- * apps/blyes/relay_lights/interface.h
- *
- * Copyright 2018 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;
-
-extern volatile int manual_activation_request;
-
-/* 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);
-
-
-/* Internal Temperature sensors */
-#define CONV_DISPLAY_OK (0x01 << 0)
-#define CONV_POWER_OK (0x01 << 1)
-#define CONV_OK (CONV_POWER_OK | CONV_DISPLAY_OK)
-int temp_read(uint32_t uart, int* deci_degrees_disp, int* deci_degrees_power);
-
-/* Interface content update */
-void interface_update(char mode);
-
-#endif /* INTERFACE_H */
-
+++ /dev/null
-/****************************************************************************
- * apps/scialys/testv09/main.c
- *
- * Scialys system for solar-panel power generation tracking and fair use.
- *
- * Copyright 2016 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 "uSD.h"
-
-
-#define MODULE_VERSION 0x09
-#define MODULE_NAME "Scialys uC"
-
-
-
-/***************************************************************************** */
-/* System configuration */
-
-/* Period (in ms) of the decrementer handler from the systick interrupt */
-#define DEC_PERIOD 100
-/* Period (in ms) of the handler for the command value update */
-#define CMD_UPD_PERIOD 50
-#define ADC_UPD_PERIOD 17
-
-/* Max water temperature. Internal protection which cannot be overriden by configuration */
-#define MAX_WATER_TEMP (90 * 100) /* Hundredth of degrees C : 90°C */
-/* Internal system max temperature : turn off heating when reached */
-#define MAX_INTERNAL_TEMP (80 * 10) /* Tenth of degrees C : 80°C */
-
-/* Number of main loops during which we keep the mosfets ON in order to keep them safe */
-#define OVERVOLTAGE_PROTECTION_CYCLES 100
-
-/* Most of the defines in here should go to configuration setting in user flash */
-/* If temperature falls bellow FORCE_HEATER_TEMP value, we enter forced heater mode, until
- * TARGET_FORCED_HEATER_TEMP is reached.
- * When in forced heater mode, the heater is controlled to heat at FORCED_MODE_VALUE which
- * is between 0 and 100.
- */
-#define FORCE_HEATER_TEMP (30 * 100)
-#define TARGET_FORCED_HEATER_TEMP (50 * 100)
-#define FORCED_MODE_VALUE 75 /* A fraction of 100 */
-/* mA prod value above which the system will not enter forced mode, waiting for home
- * to stop using power to start automatic heating */
-#define SUNNY_DAYS_PROD_VALUE 3000
-
-/* Delay before automatic forced heating */
-#define FORCED_HEATER_DELAY (2 * 3600 * 1000 / DEC_PERIOD)
-/* Duration of automatic forced heating */
-#define FORCED_HEATER_DURATION (3 * 3600 * 1000 / DEC_PERIOD)
-
-/* Duration of manual forced heating */
-#define MANUAL_ACTIVATION_DURATION (3 * 3600 * 1000 / DEC_PERIOD) /* Three hours */
-
-
-uint32_t forced_heater_mode = 0;
-uint32_t forced_heater_delay = 0;
-uint32_t forced_heater_time = 0;
-uint8_t manual_forced_heater = 0;
-
-uint8_t error_shutdown = 0;
-
-uint8_t never_force = 0;
-uint32_t sunny_days_prod_value_config = 0;
-
-#define EXTERNAL_DISABLE_FORCE 0 /* Input is pulled low when external disable is ON */
-int external_disable = 0;
-int temp_shutdown = 0;
-int overvoltage = 0;
-
-enum modes {
- heat = 'C', /* Normal heating */
- ext_disable = 'E', /* Forced heating disabled by external input */
- forced = 'F', /* Forced heating */
- manual = 'M',
- delayed_heat_prod = 'P', /* Pause */
- overprod = 'S', /* Over production, try to start other loads */
- temp_OK = 'T', /* Max temperature reached */
-};
-
-/* Water and internaltemperature */
-int water_centi_degrees = 0;
-int deci_degrees_power = 0;
-int deci_degrees_disp = 0;
-
-/* RTC and time */
-struct rtc_time now;
-
-
-/***************************************************************************** */
-/* Rx interrupt handler for system configuration over USB */
-void config_rx(uint8_t c)
-{
- /* FAN control */
- if (c == 'f') {
- gpio_set(fan_ctrl);
- } else {
- gpio_clear(fan_ctrl);
- }
-}
-
-
-/* Communication with slave modules */
-void comm_rx(uint8_t c)
-{
-}
-
-
-
-/***************************************************************************** */
-/* System communication over UART1 */
-void cmd_rx(uint8_t c)
-{
-}
-
-
-/***************************************************************************** */
-/* Decrementer for heating timers */
-void handle_dec_request(uint32_t curent_tick) {
- if (manual_activation_request > 0) {
- manual_activation_request--;
- }
- if (forced_heater_mode == 1) {
- if (forced_heater_delay > 0) {
- forced_heater_delay--;
- }
- if (forced_heater_time > 0) {
- forced_heater_time--;
- }
- }
-}
-
-
-/***************************************************************************** */
-/* Track power production and usage */
-
-/* Average value computed on last 10 ADC values */
-uint32_t solar_prod_value = 0;
-uint32_t home_conso_value = 0;
-#define NB_VAL 4 /* MUST be a power of 2 */
-#define NB_SHIFT 2
-
-static void track_isnail_values(uint32_t flags)
-{
- static uint8_t idx = 0;
- static uint16_t solar[NB_VAL];
- static uint16_t home[NB_VAL];
- uint16_t snapval_solar = 0;
- uint16_t snapval_home = 0;
- int cnt, sum_solar = 0, sum_home = 0;
-
- /* Get new values */
- adc_get_value(&snapval_solar, LPC_ADC(1));
- adc_get_value(&snapval_home, LPC_ADC(0));
-
- /* Store values */
- solar[idx] = snapval_solar;
- home[idx] = snapval_home;
- idx++;
- if (idx >= NB_VAL) {
- idx = 0;
- }
-
- /* Compute average value */
- for (cnt = 0; cnt < NB_VAL; cnt++) {
- sum_solar += solar[cnt];
- sum_home += home[cnt];
- }
-
- /* Convert to mA value.
- * ADC range is 0 to 1024 - approx 3.2mV / ADC step
- * Coil convertion is 1000mA -> 50mV : multily mV value by 20 to get mA value
- * x * 3.2 * 20 == x * 32 * 2 == x << 6
- * Increment is 64mA / ADC step */
- solar_prod_value = sum_solar << (6 - NB_SHIFT);
- home_conso_value = sum_home << (6 - NB_SHIFT);
-}
-
-/***************************************************************************** */
-/* AC control */
-
-volatile uint8_t command_val = 0;
-volatile int act_cmd = 0; /* Start off */
-int8_t force_cmd = -1; /* Forced command value, for tests */
-static int fan_on = 0;
-int force_fan = 0; /* Request to force fan ON from test menu */
-
-
-void set_ctrl_duty_cycle(uint8_t value)
-{
- act_cmd = value;
- /* Below 3% and above 97% there are triggering problems which
- * lead to 50% instead of desired command */
- if (act_cmd >= 97) {
- act_cmd = 100;
- } else if (act_cmd <= 3) {
- act_cmd = 0;
- }
-}
-
-void ac_switch_on(uint32_t flags)
-{
- gpio_clear(ac_ctrl);
-}
-
-static uint32_t clk_cycles_ac_zc = 0;
-extern uint32_t power_delay[101];
-volatile uint32_t zc_cnt = 0;
-
-void zero_cross(uint32_t unused)
-{
- uint32_t delay = 0;
-
- zc_cnt++;
- gpio_set(ac_ctrl);
- if (act_cmd == 0) {
- return;
- }
- if (act_cmd == 100) {
- gpio_clear(ac_ctrl);
- return;
- }
- /* Set timer to trigger ac out ON at given delay */
- if (0) {
- /* Time based delay */
- delay = clk_cycles_ac_zc * (100 - act_cmd);
- } else {
- /* Power based delay */
- delay = power_delay[100 - act_cmd];
- }
- timer_set_match(LPC_TIMER_32B1, CHAN0, delay);
- timer_restart(LPC_TIMER_32B1);
-}
-
-/* This one is used to synchronize to the real zero-crossing detection, if any. */
-void zero_cross_detect(uint32_t unused)
-{
- timer_restart(LPC_TIMER_32B0);
- zero_cross(0);
-}
-
-/* Handle the power command */
-#define CMD_UP_DELAY_RESET_VALUE 3
-void handle_cmd_update(uint32_t curent_tick)
-{
- static uint8_t cmd = 0;
- static uint8_t cmd_up_delay = CMD_UP_DELAY_RESET_VALUE;
-
- /* Unable to read internal temperature : turn off heating */
- if (error_shutdown == 1) {
- cmd = 0;
- force_fan = 1;
- goto cmd_update_end;
- }
-
- /* Water max temperature protection */
- if (water_centi_degrees > MAX_WATER_TEMP) {
- cmd = 0;
- goto cmd_update_end;
- }
-
- /* Forced test mode */
- if (force_cmd != -1) {
- cmd = force_cmd;
- goto cmd_update_end;
- }
-
- /* Which is the current mode ? */
- if (forced_heater_mode == 1) {
- /* Forced heating mode */
- if ((forced_heater_delay == 0) && (forced_heater_time > 0)) {
- cmd = FORCED_MODE_VALUE;
- goto cmd_update_end;
- }
- if (manual_forced_heater > 0) {
- cmd = FORCED_MODE_VALUE;
- goto cmd_update_end;
- }
- /* Entering forced heating mode from temperature getting below threshold,
- * wait some delay before effective forced heating */
- if (forced_heater_time == 0) {
- forced_heater_delay = FORCED_HEATER_DELAY;
- forced_heater_time = FORCED_HEATER_DELAY + FORCED_HEATER_DURATION;
- }
- }
- if ((home_conso_value > 1280) && (solar_prod_value < (home_conso_value - 1280))) {
- /* Very low production mode */
- if (cmd >= 10) {
- cmd -= 10;
- } else {
- cmd = 0;
- }
- } else if ((home_conso_value > 64) && (solar_prod_value < (home_conso_value - 64))) {
- /* Low production mode */
- if (cmd > 0) {
- cmd--;
- }
- } else if (solar_prod_value > (home_conso_value + 1280)) {
- if (cmd <= 98) {
- cmd += 2;
- } else {
- cmd = 100;
- }
- } else if (solar_prod_value > (home_conso_value + 256)) {
- /* High production mode */
- if (cmd_up_delay > 0) {
- cmd_up_delay--;
- } else {
- cmd_up_delay = CMD_UP_DELAY_RESET_VALUE;
- if (cmd < 100) {
- cmd++;
- }
- }
- }
-
-cmd_update_end:
- if ((cmd > 0) || (force_fan == 1)) {
- fan_on = 1;
- gpio_set(fan_ctrl);
- } else {
- fan_on = 0;
- gpio_clear(fan_ctrl);
- }
- command_val = cmd;
- /* Set Control Output duty cycle */
- set_ctrl_duty_cycle(cmd);
-}
-
-/* Over-voltage protection */
-void overvoltage_protect(uint32_t gpio)
-{
- gpio_clear(ac_ctrl);
- timer_stop(LPC_TIMER_32B1);
- set_ctrl_duty_cycle(100);
- force_cmd = 100;
- overvoltage = OVERVOLTAGE_PROTECTION_CYCLES;
- gpio_set(fan_ctrl);
-}
-
-
-/* Update current working mode depending on environnement */
-volatile char mode = heat; /* Debug info */
-volatile char* msg = NULL;
-void mode_update(uint32_t unused)
-{
- /* Default mode : try to heat the water tank */
- mode = heat;
-
- /* Need to enter Forced heating mode ? */
- if (water_centi_degrees < FORCE_HEATER_TEMP) {
- if (forced_heater_mode == 0) {
- msg = "Water temp low, entering forced mode\n";
- forced_heater_mode = 1;
- }
- status_led(red_on);
- mode = forced;
- } else if ((water_centi_degrees > TARGET_FORCED_HEATER_TEMP) && (forced_heater_mode == 1)) {
- status_led(red_off);
- msg = "Water temp OK, forced mode exit\n";
- forced_heater_mode = 0;
- mode = temp_OK;
- }
-
- /* Do not force if there is a lot of sun, it may be enough to heat again soon */
- if ((solar_prod_value > sunny_days_prod_value_config) && (forced_heater_mode == 1)) {
- mode = delayed_heat_prod;
- forced_heater_mode = 0;
- }
-
- /* Do not force heating if disabled by external command */
- external_disable = gpio_read(ext_disable_in_pin);
- if ((external_disable == EXTERNAL_DISABLE_FORCE) && (forced_heater_mode != 0)) {
- forced_heater_mode = 0;
- mode = ext_disable;
- msg = "Forced mode disabled by external input\n";
- }
- /* Get Over-Voltage or Over-Temperature information */
- temp_shutdown = gpio_read(overtemperature_pin);
- if (overvoltage > 0) {
- uint32_t ov_tmp = gpio_read(overvoltage_pin);
- if (ov_tmp == 0) {
- overvoltage--;
- if (overvoltage == 0) {
- /* Get back to normal */
- force_cmd = 0;
- }
- }
- }
-
- if (never_force == 1) {
- forced_heater_mode = 0;
- }
-
- /* Did the user request a forced heating ? */
- if (manual_activation_request != 0) {
- forced_heater_mode = 1;
- mode = manual;
- if (manual_activation_request == -1) {
- msg = "Entering manual forced mode\n";
- manual_activation_request = MANUAL_ACTIVATION_DURATION;
- manual_forced_heater = 1;
- }
- if (manual_activation_request < 10) {
- msg = "Leaving manual forced mode\n";
- manual_activation_request = 0;
- }
- }
-
- /* Command at 100% and still more production than energy used ? */
- if ((command_val == 100) && (solar_prod_value > home_conso_value)) {
- mode = overprod;
- }
-}
-
-/***************************************************************************** */
-/* Power tracking - load part */
-
-/* ADC values are between 0 and 1023, no load value is at about 520 */
-static volatile uint16_t load_power_highest = 520;
-static volatile uint16_t load_power_lowest = 520;
-void power_track(uint32_t flags)
-{
- /* Start tracking the higest value */
- static int tracking = 1;
- static uint16_t last_value = 0;
- uint16_t val = 0;
-
- /* Get load power usage */
- adc_get_value(&val, LPC_ADC(2));
-
- /* Four possibilities :
- * tracking high and value increasing : nothing to do
- * tracking high and value decreasing : we found the max, store it and change tracking
- * tracking low and value decreasing : nothing to do
- * tracking low and value increasing : we found the min, store it and change tracking
- */
- if (tracking == 1) {
- if (last_value > val) {
- /* Found max */
- load_power_highest = last_value;
- tracking = 0;
- }
- } else {
- if (last_value < val) {
- /* Found min */
- load_power_lowest = last_value;
- tracking = 1;
- }
- }
- last_value = val;
-}
-
-
-/***************************************************************************** */
-void scialys_config(void)
-{
- /* We want 100 Hz (50 Hz but two zero crossings) with 1% granularity */
- clk_cycles_ac_zc = get_main_clock() / (100 * 100);
- uprintf(UART0, "clk_cycles_ac_zc: %d\n", clk_cycles_ac_zc);
-
- /* Configure the fake zero-cross generation timer to 100Hz.
- * Do not start it now, it will be started by the first real zero-cross detected in order to
- * generate the other half zc (not seen by system due to hardware design)
- * Also sarted by config when DC mode is set */
- timer_set_match(LPC_TIMER_32B0, CHAN0, (clk_cycles_ac_zc * 100));
-
- /* Configure the power tracking timer.
- * 20 points per half sin wave should be enough to get the min and max */
- timer_set_match(LPC_TIMER_16B0, CHAN0, (clk_cycles_ac_zc * 5));
- timer_start(LPC_TIMER_16B0);
-
- /* Read parameters from memory */
- if (1) {
- never_force = 0;
- sunny_days_prod_value_config = SUNNY_DAYS_PROD_VALUE;
- }
-
- /* Add a systick callback to handle command value update */
- add_systick_callback(handle_cmd_update, CMD_UPD_PERIOD);
-
- /* Add a systick callback to track ismail values (power consumption and production) */
- add_systick_callback(track_isnail_values, ADC_UPD_PERIOD);
-
- /* Add a systick callback to handle time counting */
- add_systick_callback(handle_dec_request, DEC_PERIOD);
-}
-
-/* Add a false zero-cross, but make sure to do it only once and only if we are not AC powered */
-void DC_switch_start(void)
-{
- static int already_done = 0;
- if ((already_done == 0) && (zc_cnt == 0)) {
- int ret = add_systick_callback(zero_cross, 10); /* 2 zero-crossing at 50Hz -> 100Hz -> 10ms */
- uprintf(UART0, "Entering forced DC mode on systick callback %d\n", ret);
- already_done = 1;
- } else {
- uprintf(UART0, "DC forced mode already ON: %d/%d\n", already_done, zc_cnt);
- }
-}
-
-/***************************************************************************** */
-int main(void)
-{
- uint32_t loop = 0;
- int ret = 0;
-
- system_init();
- board_io_config();
- modules_config();
-
- external_config(UART0);
- scialys_config();
-
- msleep(500);
- status_led(green_only);
-
- while (1) {
- /* Feed the dog */
- if ((solar_prod_value != 0) && (home_conso_value != 0)) {
- watchdog_feed();
- }
-
- /* Update internal temperatures */
- if (1) {
- /* Get internal temperatures */
- ret = temp_read(UART0, &deci_degrees_disp, &deci_degrees_power);
- if (ret != CONV_OK) {
- if ((ret & CONV_POWER_OK) == 0) {
- force_fan = 1;
- }
- if ((ret & CONV_DISPLAY_OK) == 0) {
- force_fan = 1;
- }
- }
- /* Internal temperature protection */
- if ((deci_degrees_power > MAX_INTERNAL_TEMP) || (deci_degrees_disp > MAX_INTERNAL_TEMP)) {
- error_shutdown = 1;
- uprintf(UART0, "Error shutdown !\n");
- } else if ((deci_degrees_power < ((MAX_INTERNAL_TEMP) / 2)) &&
- (deci_degrees_disp < ((MAX_INTERNAL_TEMP) / 2))) {
- error_shutdown = 0;
- }
- }
-
- /* Update water tank temperature */
- if (1) {
- int ret = 0, old_water_temp = 0;
- old_water_temp = water_centi_degrees;
- /* Get thermocouple value */
- ret = max31855_sensor_read(&thermocouple, NULL, &water_centi_degrees);
- if (ret != 0) {
- uprintf(UART0, "Water Temp read error : %d\n", ret);
- /* Do not act upon iinvalid temp value */
- water_centi_degrees = old_water_temp;
- }
- }
-
- /* Update current mode */
- mode_update(0);
-
- /* Display */
- if (1) {
- interface_update(mode);
- }
-
- /* Debug */
- if (1) {
- uprintf(UART0, "#%c:%d:%d:%d:%d:%d:%d:",
- mode, loop++, solar_prod_value, home_conso_value,
- water_centi_degrees, deci_degrees_power, deci_degrees_disp);
- uprintf(UART0, "%d:%d:%d:%d:%d:%d:%d:%d:%d:%d:%d:%d:%d$\n",
- load_power_lowest, load_power_highest,
- zc_cnt, command_val, act_cmd, fan_on, force_fan,
- error_shutdown, temp_shutdown, overvoltage,
- external_disable, forced_heater_mode, manual_activation_request);
- if (msg != NULL) {
- uprintf(UART0, (char*)msg);
- msg = NULL;
- }
- }
- }
- return 0;
-}
-
-
-
+++ /dev/null
-#include "lib/stdint.h"
-
-uint32_t power_delay[101] = {
- [0] = 0,
- [1] = 30608,
- [2] = 43360,
- [3] = 53195,
- [4] = 61530,
- [5] = 68911,
- [6] = 75620,
- [7] = 81822,
- [8] = 87626,
- [9] = 93107,
- [10] = 98319,
- [11] = 103305,
- [12] = 108095,
- [13] = 112716,
- [14] = 117188,
- [15] = 121527,
- [16] = 125750,
- [17] = 129867,
- [18] = 133888,
- [19] = 137823,
- [20] = 141680,
- [21] = 145465,
- [22] = 149184,
- [23] = 152843,
- [24] = 156447,
- [25] = 159999,
- [26] = 163505,
- [27] = 166967,
- [28] = 170389,
- [29] = 173774,
- [30] = 177124,
- [31] = 180443,
- [32] = 183732,
- [33] = 186995,
- [34] = 190232,
- [35] = 193446,
- [36] = 196639,
- [37] = 199813,
- [38] = 202969,
- [39] = 206109,
- [40] = 209234,
- [41] = 212347,
- [42] = 215448,
- [43] = 218539,
- [44] = 221621,
- [45] = 224695,
- [46] = 227763,
- [47] = 230827,
- [48] = 233886,
- [49] = 236944,
- [50] = 240000,
- [51] = 243055,
- [52] = 246113,
- [53] = 249172,
- [54] = 252236,
- [55] = 255304,
- [56] = 258378,
- [57] = 261460,
- [58] = 264551,
- [59] = 267652,
- [60] = 270765,
- [61] = 273890,
- [62] = 277030,
- [63] = 280186,
- [64] = 283360,
- [65] = 286553,
- [66] = 289767,
- [67] = 293004,
- [68] = 296267,
- [69] = 299556,
- [70] = 302875,
- [71] = 306225,
- [72] = 309610,
- [73] = 313032,
- [74] = 316494,
- [75] = 320000,
- [76] = 323552,
- [77] = 327156,
- [78] = 330815,
- [79] = 334534,
- [80] = 338319,
- [81] = 342176,
- [82] = 346111,
- [83] = 350132,
- [84] = 354249,
- [85] = 358472,
- [86] = 362811,
- [87] = 367283,
- [88] = 371904,
- [89] = 376694,
- [90] = 381680,
- [91] = 386892,
- [92] = 392373,
- [93] = 398177,
- [94] = 404379,
- [95] = 411088,
- [96] = 418469,
- [97] = 426804,
- [98] = 436639,
- [99] = 449391,
- [100] = 480000,
-};
+++ /dev/null
-#! /usr/bin/perl -w
-
-use strict;
-use warnings;
-
-use Math::Trig;
-
-my ($clocks, $outfile) = @ARGV;
-my ($fh, $angle, $v, $w, $idx);
-
-if (defined $outfile) {
- open($fh, '>', $outfile);
- print $fh "#include \"lib/stdint.h\"\n\n";
- print $fh "uint32_t power_delay[101] = {\n";
-}
-
-for (my $i = 0; $i <= 100; $i++) {
- $angle = $i * 0.02;
- $v = (acos(1 - $angle) / pi * 100);
- $w = int($v * $clocks);
- if (not defined $outfile) {
- print "$v - [$i] = $w,\n";
- } else {
- print $fh "\t[$i] = $w,\n";
- }
-}
-
-if (defined $outfile) {
- print $fh "};\n";
- close($fh);
-}
-print "Done\n";
-
-
+++ /dev/null
-/****************************************************************************
- * apps/blyes/inter_lights/time.c
- *
- * Copyright 2018 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 = 0x18,
- .month = 0x08,
- .day = 0x09,
- .hour = 0x13,
- .min = 0x37,
-};
-int time_valid = 0;
-int rtc_conf_ok = 0;
-
-int 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 time_update(uint32_t uart, struct rtc_time* now)
-{
- rtc_pcf85363_time_write(&rtc_conf, now);
- return 0;
-}
-
-
-/***************************************************************************** */
-/* RTC init */
-void time_config(uint32_t i2c_bus_num, uint8_t rtc_addr)
-{
- rtc_conf.bus_num = i2c_bus_num;
- rtc_conf.addr = rtc_addr;
-}
-
+++ /dev/null
-/****************************************************************************
- * apps/blyes/inter_lights/time.h
- *
- * Copyright 2018 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 time_init_check(uint32_t uart);
-void time_config(uint32_t i2c_bus_num, uint8_t rtc_addr);
-
-#endif /* RTC_TIME_H */
-
+++ /dev/null
-/****************************************************************************
- * apps/blyes/inter_lights/uSD.c
- *
- * Copyright 2018 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/ssp.h"
-
-#include "lib/stdio.h"
-
-#include "uSD.h"
-
-
-/***************************************************************************** */
-/* SD/MMC Card */
-struct sdmmc_card micro_sd = {
- .card_type = MMC_CARDTYPE_UNKNOWN,
- .block_size = 64,
-};
-
-static uint8_t got_uSD = 0;
-
-/* Buffer to read / write uSD card */
-uint8_t mmc_data[MMC_BUF_SIZE];
-
-/* Read up to 4 blocks of 16 bytes each */
-int uSD_read(uint32_t offset, uint8_t nb_blocks)
-{
- int i = 0, ret = 0;
- if (got_uSD == 0) {
- return -1;
- }
- if (nb_blocks > 4) {
- return -2;
- }
- memset(mmc_data, 0, MMC_BUF_SIZE);
- for (i = 0; i < nb_blocks; i++) {
- ret = sdmmc_read_block(µ_sd, (offset + i), (mmc_data + (i * 16)));
- /* FIXME : check return value */
- }
- return 0;
-}
-
-
-/* Write up to 4 blocks of 16 bytes each */
-int uSD_write(uint32_t offset, uint8_t nb_blocks)
-{
- int i = 0, ret = 0;
- if (got_uSD == 0) {
- return -1;
- }
- if (nb_blocks > 4) {
- return -1;
- }
- for (i = 0; i < nb_blocks; i++) {
- ret = sdmmc_write_block(µ_sd, (offset + i), (mmc_data + (i * 16)));
- /* FIXME : check return value */
- }
- return 0;
-}
-
-/* microSD card init */
-int uSD_detect(int uart)
-{
- int i = 0, ret = 0, step = 0;
- do {
- step = 0;
- ret = sdmmc_init(µ_sd);
- if (ret == 0) {
- step = 1;
- msleep(10);
- ret = sdmmc_init_wait_card_ready(µ_sd);
- if (ret <= 1) {
- step = 2;
- ret = sdmmc_init_end(µ_sd);
- }
- }
- uprintf(uart, "uSD init(%d): step:%d, ret: %d, type: %d, bs: %d\n",
- i, step, ret, micro_sd.card_type, micro_sd.block_size);
- i++;
- } while (((ret != 0) || (micro_sd.card_type == MMC_CARDTYPE_UNKNOWN)) && (i < 10));
-
- /* Got uSD ? */
- if (i >= 10) {
- uprintf(uart, "uSD init failed, no uSD card present.\n");
- got_uSD = 0;
- return -1;
- }
-
- /* uSD card detected */
- got_uSD = 1;
- /* got_uSD MUST be set to 1 from here on ! */
- uSD_read(0, 4); /* Read 4 blocks at start of card */
- /* FIXME : check that the card magic is present */
- uprintf(uart, "uSD read: %s\n", mmc_data);
-
- return 0;
-}
-
-
-int uSD_logs_init(int uart)
-{
- /* FIXME : read 4 blocks at offset FIXME to get the last block used info */
- return 0;
-}
-
-/***************************************************************************** */
-/* microSD card init */
-void uSD_config(struct pio* uSD_cs, uint32_t ssp_bus_num)
-{
- memcpy(&(micro_sd.chip_select), uSD_cs, sizeof(struct pio));
- micro_sd.ssp_bus_num = ssp_bus_num;
-}
-
+++ /dev/null
-/****************************************************************************
- * apps/blyes/inter_lights/usSD.h
- *
- * Copyright 2018 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 USD_H
-#define USD_H
-
-#include "extdrv/sdmmc.h"
-
-#define MMC_BUF_SIZE 64
-
-extern struct sdmmc_card micro_sd;
-extern uint8_t mmc_data[MMC_BUF_SIZE];
-
-int uSD_read(uint32_t offset, uint8_t nb_blocks);
-int uSD_write(uint32_t offset, uint8_t nb_blocks);
-
-/***************************************************************************** */
-/* microSD card init */
-int uSD_detect(int uart);
-void uSD_config(struct pio* uSD_cs, uint32_t ssp_bus_num);
-
-
-#endif /* USD_H */
-