# HG changeset patch # User Michiel Broek # Date 1624295050 -7200 # Node ID 96e30a3a39808256f09b07171ff50c59671f038a # Parent 1bc6e9263adaed00a769b26582138d7b8ff96fa6 Finished experimental code to drive the German HendiControl board. Added BoilPower and RampPower buttons during the while boil process. RampPower (going to boil power) is now adjustable. Added PWM driver code to the driver task. diff -r 1bc6e9263ada -r 96e30a3a3980 main/automation.c --- a/main/automation.c Sat Jun 19 20:46:42 2021 +0200 +++ b/main/automation.c Mon Jun 21 19:04:10 2021 +0200 @@ -6,6 +6,7 @@ #include "config.h" int BoilPower = 100; ///< Boil power 0..100% +int RampPower = 100; ///< Boil ramp power 0..100% int LastMashStep = 0; ///< Last valid mash step char temp_buf[64]; ///< Temporary buffer char logline[128]; ///< Log line buffer @@ -269,11 +270,14 @@ runtime.StageResume = Main_Screen; updateRuntime = true; TempReached = false; + RampPower = equipment.RampPower; TopMessage((char *)"Naar koken"); MLT_info(71, 26, false); - Buttons_Add( 5, 30, 60, 40, (char *)"+sp", 0); - Buttons_Add(255, 30, 60, 40, (char *)"-sp", 1); + Buttons_Add( 5, 30, 60, 40, (char *)"+sp", 0); + Buttons_Add(255, 30, 60, 40, (char *)"-sp", 1); + Buttons_Add( 3, 110, 60, 40, (char *)"+%", 2); + Buttons_Add(257, 110, 60, 40, (char *)"-%", 3); Buttons_Show(); log_msg(TAG, "Mash done, going to boil."); Sub_Screen = 0; @@ -300,10 +304,12 @@ updateRuntime = true; TopMessage((char *)"Koken"); MLT_info(71, 26, false); - Buttons_Add( 3, 30, 60, 40, (char *)"+sp", 0); - Buttons_Add(257, 30, 60, 40, (char *)"-sp", 1); - Buttons_Add( 3, 190, 60, 40, (char *)"+1m", 2); - Buttons_Add(257, 190, 60, 40, (char *)"-1m", 3); + Buttons_Add( 3, 30, 60, 40, (char *)"+sp", 0); + Buttons_Add(257, 30, 60, 40, (char *)"-sp", 1); + Buttons_Add( 3, 110, 60, 40, (char *)"+%", 2); + Buttons_Add(257, 110, 60, 40, (char *)"-%", 3); + Buttons_Add( 3, 190, 60, 40, (char *)"+1m", 4); + Buttons_Add(257, 190, 60, 40, (char *)"-1m", 5); Buttons_Show(); log_msg(TAG, "Boil temperature reached, boil %d minutes", (TimeLeft / 60) -1); log_annotation(ANNOTATION_STAGE, (char *)"Koken"); @@ -1003,7 +1009,7 @@ break; case MAIN_AUTO_TOBOIL: - Output = 255; + Output = (int)((RampPower * 255.0) / 100.0); /* * Go to the boil temperature and wait until it is steady. */ @@ -1032,6 +1038,16 @@ } break; + case 2: if (RampPower < 100) + RampPower++; + log_msg(TAG, "Increase ramppower to %d%%", RampPower); + break; + + case 3: if (RampPower > 0) + RampPower--; + log_msg(TAG, "Decrease ramppower to %d%%", RampPower); + break; + default: break; } if (Resume) @@ -1063,19 +1079,8 @@ Output = 0; } else if (driver_state->mlt_pv >= stageTemp) { Output = (int)((BoilPower * 255.0) / 100.0); - if (Buttons[4].x == -1) { - Buttons_Add( 3,110, 60, 40, (char *)"+%", 4); - Buttons_Add(257,110, 60, 40, (char *)"-%", 5); - Buttons_Show(); - } } else { - Output = 255; - if (Buttons[4].x != -1) { - Buttons[4].x = Buttons[5].x = -1; - Buttons_Show(); - TFT_fillRect( 3,110, 60, 40, TFT_BLACK); - TFT_fillRect(257,110, 60, 40, TFT_BLACK); - } + Output = (int)((RampPower * 255.0) / 100.0);; } MLT_info(71, 26, true); @@ -1092,20 +1097,32 @@ } break; - case 2: change_tl(21600); + case 4: change_tl(21600); break; - case 3: change_tl(0); + case 5: change_tl(0); break; - case 4: if (BoilPower < 100) - BoilPower++; - log_msg(TAG, "Increase boilpower to %d%%", BoilPower); + case 2: if (driver_state->mlt_pv >= stageTemp) { + if (BoilPower < 100) + BoilPower++; + log_msg(TAG, "Increase boilpower to %d%%", BoilPower); + } else { + if (RampPower < 100) + RampPower++; + log_msg(TAG, "Increase ramppower to %d%%", RampPower); + } break; - case 5: if (BoilPower > 0) - BoilPower--; - log_msg(TAG, "Decrease boilpower to %d%%", BoilPower); + case 3: if (driver_state->mlt_pv >= stageTemp) { + if (BoilPower > 0) + BoilPower--; + log_msg(TAG, "Decrease boilpower to %d%%", BoilPower); + } else { + if (RampPower > 0) + RampPower--; + log_msg(TAG, "Decrease ramppower to %d%%", RampPower); + } break; default: break; diff -r 1bc6e9263ada -r 96e30a3a3980 main/config.h --- a/main/config.h Sat Jun 19 20:46:42 2021 +0200 +++ b/main/config.h Mon Jun 21 19:04:10 2021 +0200 @@ -25,6 +25,7 @@ #include "freertos/event_groups.h" #include "freertos/queue.h" #include "driver/i2c.h" +#include "driver/ledc.h" #include "esp_log.h" #include "esp_spiffs.h" #include "esp_event.h" diff -r 1bc6e9263ada -r 96e30a3a3980 main/task_driver.c --- a/main/task_driver.c Sat Jun 19 20:46:42 2021 +0200 +++ b/main/task_driver.c Mon Jun 21 19:04:10 2021 +0200 @@ -37,7 +37,6 @@ int HLT_Mode = HLT_MODE_NONE; ///< HLT mode flag TickType_t MLT_time, HLT_time; - static const char *MLTTypes[] = { "None", "Bang", "PID", "Off", "Ext" }; static const char *HLTTypes[] = { "None", "Bang", "Off", "On" }; @@ -63,6 +62,12 @@ */ void Pump(int onoff); +/** + * @brief Calculate and set PWM value. + * @param percent Then power percentage, 0..100 + */ +void MLT_PWM(int percent); + void MLT(int onoff) { @@ -111,6 +116,32 @@ } +//int oldval = 200; +void MLT_PWM(int percent) { + int val; + static int oldval = -1; + + if (outEnable) { + if (percent < 0) { + val = 0; + } else if (percent > 100) { + val = 1024; + } else { + val = (percent * 1024) / 100; + } + } else { + val = 0; + } + + if (val != oldval) { + ESP_LOGI(TAG, "MLT_PWM(%d) val=%d %.0f watt", percent, val, (percent / 100.0) * equipment.MLT_watt); + ledc_set_duty(LEDC_LOW_SPEED_MODE, LEDC_CHANNEL_0, 1024 - val); + ledc_update_duty(LEDC_LOW_SPEED_MODE, LEDC_CHANNEL_0); + } + oldval = val; +} + + /** * @brief Load PID settings from equipment record. @@ -129,6 +160,26 @@ +void AllowHLT(void) { + 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); + } +} + + + void task_driver(void *pvParameter) { TickType_t wait_ticks, last_tick, now_tick; @@ -147,6 +198,27 @@ gpio_pad_select_gpio(SSR_PUMP); gpio_set_direction(SSR_PUMP, GPIO_MODE_OUTPUT); + // Prepare and then apply the LEDC PWM timer configuration + ledc_timer_config_t ledc_timer = { + .speed_mode = LEDC_LOW_SPEED_MODE, ///< Use low speed + .timer_num = LEDC_TIMER_1, + .duty_resolution = LEDC_TIMER_10_BIT, ///< 10 bits resolution + .freq_hz = 100, ///< 100 Hz + .clk_cfg = LEDC_AUTO_CLK ///< Auto select PWM clock + }; + ledc_timer_config(&ledc_timer); + + ledc_channel_config_t pwm_channel = { + .channel = LEDC_CHANNEL_0, + .duty = 1024, ///< Default 0% (inverted value) + .gpio_num = PWM_MLT, ///< MLT pin + .speed_mode = LEDC_LOW_SPEED_MODE, + .hpoint = 0, + .intr_type = LEDC_INTR_DISABLE, + .timer_sel = LEDC_TIMER_1 + }; + ledc_channel_config(&pwm_channel); + /* * Initialize state */ @@ -168,7 +240,7 @@ driver_state->pwm_nohlt = 10; /* Conservative safety value. */ PID(&Input, &Output, &Setpoint, 200, 2.0, 1.5, PID_DIRECT); - + /* * One loop must complete in 20 mSecs, that is one mains * frequency period cycle in 50 Hz countries. @@ -241,7 +313,38 @@ w_StartTime = now; } - if ((int)((Output / 255.0) * RealTime) > (now - w_StartTime)) { + if (equipment.Hendi) { + + int PWMout = (int)((Output * 100) / 255.0); + + if ((PID_GetMode() == PID_AUTOMATIC) && (MLT_Mode == MLT_MODE_PID)) { + if (PWMout > equipment.MashPower) + PWMout = equipment.MashPower; + } + + /* + * Hendi minimum power is 500 Watt, this is 14%. + * So, we turn the cooker on around 10% power. + */ + if (PWMout >= 10) { // Hendi minimum power is 500 Watt, this is 10% + if ((((PWMout / 100.0) * equipment.MLT_watt) + equipment.HLT_watt) > equipment.Max_watt) { + if (HLT_pin) { + ESP_LOGI(TAG, "Power %f %d", ((PWMout / 100.0) * equipment.MLT_watt) + equipment.HLT_watt, equipment.Max_watt); + ESP_LOGI(TAG, "Immediate HLT panic shutdown"); + HLT_Output = 0; + HLT(0); // As soon as possible before the Hendi increases power. + } + } else { + AllowHLT(); // TODO: delay this one loop. + } + MLT_PWM(PWMout); + MLT(1); + } else { + MLT_PWM(0); + MLT(0); + AllowHLT(); + } + } else if ((int)((Output / 255.0) * RealTime) > (now - w_StartTime)) { MLT(1); if ((equipment.SSR2 == SSR2_HLT_SHARE) || (equipment.SSR2 == SSR2_ON_IDLE)) { HLT(0); @@ -249,21 +352,7 @@ } } 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); - } + AllowHLT(); } /* @@ -309,8 +398,7 @@ } #endif - // Not reliable, so do it manually. - //vTaskDelayUntil(&last_wake_time, (1000 / 50) / portTICK_PERIOD_MS); + // Do not use vTaskDelayUntil(), it is not reliable here. now_tick = xTaskGetTickCount(); if ((now_tick - last_tick) > (1000 / 50)) { // This happens one or two times during a brew.