diff -r 000000000000 -r b74b0e4902c3 main/task_driver.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/main/task_driver.c Sat Oct 20 13:23:15 2018 +0200 @@ -0,0 +1,307 @@ +/** + * @file task_driver.c + * @brief BrewBoard relays driver. Control the hardware outputs with the + * Solid State relays for the Mash/Boil kettle (MLT, the Hot + * Liquer Tank (HLT) and the pump. The MLT has a PID controller + * during mashing, and a simple bang on/off control during the + * boil. + * The HLT output can be off, bang on/off, or just on if the MLT + * is off, depending on the configuration. + * Use SSR modules that switch during zero crossing of the mains + * power, so that when one is turned on and on the same time the + * other is turned off, they won't be active at the same time. + */ + +#include "config.h" + + +#define SSR_MLT CONFIG_SSR_MLT_GPIO +#define SSR_HLT CONFIG_SSR_HLT_GPIO +#define SSR_PUMP CONFIG_SSR_PUMP_GPIO + + +bool outEnable = false; +DRIVER_State * driver_state; +SemaphoreHandle_t xSemaphoreDriver = NULL; +int MLT_pin = 0; +int HLT_pin = 0; +int Pump_pin = 0; +double Input = 0, Output = 0, Setpoint = 0; +int MLT_Mode = MLT_MODE_NONE; +double HLT_Input = 0, HLT_Setpoint = 0; +int HLT_Output = 0; +int HLT_Mode = HLT_MODE_NONE; + +static const char *TAG = "task_driver"; + +extern SemaphoreHandle_t xSemaphoreDS18B20; +extern DS18B20_State * ds18b20_state; +extern unsigned long lastTime; + + +/** + * @brief Turn the MLT SSR on or off. + */ +void MLT(int onoff); + +/** + * @brief Turn the HLT SSR on or off. + */ +void HLT(int onoff); + +/** + * @brief Turn the Pump on or off. + */ +void Pump(int onoff); + + + +void MLT(int onoff) { + + if (onoff && outEnable) { + gpio_set_level(SSR_MLT, 1); + MLT_pin = 1; + } else { + gpio_set_level(SSR_MLT, 0); + MLT_pin = 0; + } +} + + + +void HLT(int onoff) { + + if (onoff && outEnable) { + gpio_set_level(SSR_HLT, 1); + HLT_pin = 1; + } else { + gpio_set_level(SSR_HLT, 0); + HLT_pin = 0; + } +} + + + +void Pump(int onoff) { + + if (onoff && outEnable) { + gpio_set_level(SSR_PUMP, 1); + Pump_pin = 1; + } else { + gpio_set_level(SSR_PUMP, 0); + Pump_pin = 0; + } +} + + + +void LoadPIDsettings() { + PID_SetTunings(equipment.PID_kP, equipment.PID_kI, equipment.PID_kD, equipment.PID_POn); + PID_SetSampleTime(equipment.SampleTime); + + /* + * Initialize the PID + */ + Output = 0.0; // Reset internal Iterm. + PID_SetMode(PID_MANUAL); + PID_SetMode(PID_AUTOMATIC); +} + + + +void task_driver(void *pvParameter) +{ + TickType_t wait_ticks, last_tick, now_tick; + bool rc; + unsigned long now, RealTime, w_StartTime = 0; + + ESP_LOGI(TAG, "Starting output drivers"); + + /* + * Configure IOMUX register. + */ + gpio_pad_select_gpio(SSR_MLT); + gpio_set_direction(SSR_MLT, GPIO_MODE_OUTPUT); + gpio_pad_select_gpio(SSR_HLT); + gpio_set_direction(SSR_HLT, GPIO_MODE_OUTPUT); + gpio_pad_select_gpio(SSR_PUMP); + gpio_set_direction(SSR_PUMP, GPIO_MODE_OUTPUT); + + /* + * Initialize state + */ + driver_state = malloc(sizeof(DRIVER_State)); + driver_state->enable = outEnable = false; + driver_state->mlt_gpio = SSR_MLT; + driver_state->mlt_mode = MLT_MODE_NONE; + driver_state->mlt_sp = driver_state->mlt_pv = 0.0; + driver_state->mlt_power = 0; + driver_state->hlt_gpio = SSR_HLT; + driver_state->hlt_mode = HLT_MODE_NONE; + driver_state->hlt_sp = driver_state->hlt_pv = 0.0; + driver_state->hlt_power = 0; + driver_state->hlt_and_mlt = false; + driver_state->pump_gpio = SSR_PUMP; + driver_state->pump_run = 0; + + PID(&Input, &Output, &Setpoint, 150, 1.5, 15000, PID_P_ON_E, PID_DIRECT); + + /* + * One loop must complete in 20 mSecs, that is one mains + * frequency period cycle in 50 Hz countries. + */ + while (1) { + + last_tick = xTaskGetTickCount(); + + if (xSemaphoreTake(xSemaphoreDriver, 10) == pdTRUE) { + /* + * Get the current temperature readings + */ + if (xSemaphoreTake(xSemaphoreDS18B20, 10) == pdTRUE) { + if (ds18b20_state->mlt_valid) + driver_state->mlt_pv = ds18b20_state->mlt_temperature; + if (ds18b20_state->hlt_valid) + driver_state->hlt_pv = ds18b20_state->hlt_temperature; + xSemaphoreGive(xSemaphoreDS18B20); + } + + /* + * Other values that we need + */ + Input = driver_state->mlt_pv; + Setpoint = driver_state->mlt_sp; + if (driver_state->mlt_mode != MLT_Mode) { + if (driver_state->mlt_mode == MLT_MODE_BANG) { + PID_SetMode(PID_MANUAL); + } else if (driver_state->mlt_mode == MLT_MODE_PID) { + LoadPIDsettings(); + } + MLT_Mode = driver_state->mlt_mode; + ESP_LOGI(TAG, "MLT mode set to %d", MLT_Mode); + } + if (driver_state->hlt_mode != HLT_Mode) { + HLT_Mode = driver_state->hlt_mode; + ESP_LOGI(TAG, "HLT mode set to %d", HLT_Mode); + } + outEnable = driver_state->enable; + HLT_Input = driver_state->hlt_pv; + HLT_Setpoint = driver_state->hlt_sp; + xSemaphoreGive(xSemaphoreDriver); + } + + rc = false; + now = xTaskGetTickCount() * portTICK_PERIOD_MS; + + if ((PID_GetMode() == PID_AUTOMATIC) && (MLT_Mode == MLT_MODE_PID)) { + rc = PID_Compute(); + RealTime = (equipment.SampleTime * equipment.MashPower) / 100; + } else { + /* + * Schedule the loop ourself. + */ + unsigned long timeChange = (now - lastTime); + if (timeChange >= equipment.SampleTime) { + lastTime = now; + rc = true; + } + RealTime = equipment.SampleTime; + if (driver_state->mlt_mode == MLT_MODE_BANG) { + Output = (Input < Setpoint) ? 255:0; + } + if (driver_state->mlt_mode == MLT_MODE_NONE || driver_state->mlt_mode == MLT_MODE_OFF) { + Output = 0; + } + } + + if (rc) { + w_StartTime = now; + } + + if ((int)((Output / 255.0) * RealTime) > (now - w_StartTime)) { + MLT(1); + if ((equipment.SSR2 == SSR2_HLT_SHARE) || (equipment.SSR2 == SSR2_ON_IDLE)) { + HLT(0); + HLT_Output = 0; + } + } else { + MLT(0); + if (equipment.SSR2 == SSR2_HLT_SHARE) { + if (xSemaphoreTake(xSemaphoreDriver, 10) == pdTRUE) { + HLT_Output = 0; + if (driver_state->hlt_mode == HLT_MODE_BANG) { + HLT_Output = (HLT_Input < HLT_Setpoint) ? 1:0; + } else if (driver_state->hlt_mode == HLT_MODE_ON) { + HLT_Output = 1; + } + xSemaphoreGive(xSemaphoreDriver); + } + HLT(HLT_Output); + } else if (equipment.SSR2 == SSR2_ON_IDLE) { + HLT_Output = 1; + HLT(1); + } + } + + /* + * Independant HLT temperature control + */ + if (equipment.SSR2 == SSR2_HLT_IND) { + if (xSemaphoreTake(xSemaphoreDriver, 10) == pdTRUE) { + HLT_Output = 0; + if (driver_state->hlt_mode == HLT_MODE_BANG) { + HLT_Output = (HLT_Input < HLT_Setpoint) ? 1:0; + } else if (driver_state->hlt_mode == HLT_MODE_ON) { + HLT_Output = 1; + } + xSemaphoreGive(xSemaphoreDriver); + } + HLT(HLT_Output); + } + + /* + * Update the driver results. + */ + if (xSemaphoreTake(xSemaphoreDriver, 10) == pdTRUE) { + driver_state->mlt_power = (int)((Output * 100) / 255.0); + if (HLT_Output) { + if (equipment.SSR2 == SSR2_HLT_SHARE) { + driver_state->hlt_power = 100 - driver_state->mlt_power; + } else if (equipment.SSR2 == SSR2_HLT_IND) { + driver_state->hlt_power = 100; + } + } else { + driver_state->hlt_power = 0; + } + if (driver_state->pump_run != Pump_pin) + Pump(driver_state->pump_run); + xSemaphoreGive(xSemaphoreDriver); + } + +#if 0 + if (rc) { + printf("ST: %s MLT[In: %7.3f Out: %3.0f Sp: %6.2f %s RT: %lu] HLT[In: %7.3f Out: %d Sp: %5.1f]\n", outEnable ? "E":"D", + Input, Output, Setpoint, PID_GetMode() ? "AUTOMATIC" : "MANUAL ", RealTime, + HLT_Input, HLT_Output, HLT_Setpoint); + } +#endif + + // Not reliable, so do it manually. + //vTaskDelayUntil(&last_wake_time, (1000 / 50) / portTICK_PERIOD_MS); + now_tick = xTaskGetTickCount(); + if ((now_tick - last_tick) > (1000 / 50)) { + // This happens one or two times during a brew. + wait_ticks = (1000 / 50); + } else { + wait_ticks = (1000 / 50) - (now_tick - last_tick); + } + if (wait_ticks == 0) { + // This is rare, but it happens. + wait_ticks = 1; + } + + vTaskDelay(wait_ticks); + } +} + +