35 double HLT_Setpoint = 0; ///< HLT setpoint values |
35 double HLT_Setpoint = 0; ///< HLT setpoint values |
36 int HLT_Output = 0; ///< HLT output value |
36 int HLT_Output = 0; ///< HLT output value |
37 int HLT_Mode = HLT_MODE_NONE; ///< HLT mode flag |
37 int HLT_Mode = HLT_MODE_NONE; ///< HLT mode flag |
38 TickType_t MLT_time, HLT_time; |
38 TickType_t MLT_time, HLT_time; |
39 |
39 |
40 |
|
41 static const char *MLTTypes[] = { "None", "Bang", "PID", "Off", "Ext" }; |
40 static const char *MLTTypes[] = { "None", "Bang", "PID", "Off", "Ext" }; |
42 static const char *HLTTypes[] = { "None", "Bang", "Off", "On" }; |
41 static const char *HLTTypes[] = { "None", "Bang", "Off", "On" }; |
43 |
42 |
44 static const char *TAG = "task_driver"; |
43 static const char *TAG = "task_driver"; |
45 |
44 |
109 Pump_pin = 0; |
114 Pump_pin = 0; |
110 } |
115 } |
111 } |
116 } |
112 |
117 |
113 |
118 |
|
119 //int oldval = 200; |
|
120 void MLT_PWM(int percent) { |
|
121 int val; |
|
122 static int oldval = -1; |
|
123 |
|
124 if (outEnable) { |
|
125 if (percent < 0) { |
|
126 val = 0; |
|
127 } else if (percent > 100) { |
|
128 val = 1024; |
|
129 } else { |
|
130 val = (percent * 1024) / 100; |
|
131 } |
|
132 } else { |
|
133 val = 0; |
|
134 } |
|
135 |
|
136 if (val != oldval) { |
|
137 ESP_LOGI(TAG, "MLT_PWM(%d) val=%d %.0f watt", percent, val, (percent / 100.0) * equipment.MLT_watt); |
|
138 ledc_set_duty(LEDC_LOW_SPEED_MODE, LEDC_CHANNEL_0, 1024 - val); |
|
139 ledc_update_duty(LEDC_LOW_SPEED_MODE, LEDC_CHANNEL_0); |
|
140 } |
|
141 oldval = val; |
|
142 } |
|
143 |
|
144 |
114 |
145 |
115 /** |
146 /** |
116 * @brief Load PID settings from equipment record. |
147 * @brief Load PID settings from equipment record. |
117 */ |
148 */ |
118 void LoadPIDsettings() { |
149 void LoadPIDsettings() { |
123 * Initialize the PID |
154 * Initialize the PID |
124 */ |
155 */ |
125 Output = 0.0; // Reset internal Iterm. |
156 Output = 0.0; // Reset internal Iterm. |
126 PID_SetMode(PID_MANUAL); |
157 PID_SetMode(PID_MANUAL); |
127 PID_SetMode(PID_AUTOMATIC); |
158 PID_SetMode(PID_AUTOMATIC); |
|
159 } |
|
160 |
|
161 |
|
162 |
|
163 void AllowHLT(void) { |
|
164 if (equipment.SSR2 == SSR2_HLT_SHARE) { |
|
165 if (xSemaphoreTake(xSemaphoreDriver, 10) == pdTRUE) { |
|
166 HLT_Output = 0; |
|
167 if (driver_state->hlt_mode == HLT_MODE_BANG) { |
|
168 HLT_Output = (HLT_Input < HLT_Setpoint) ? 1:0; |
|
169 } else if (driver_state->hlt_mode == HLT_MODE_ON) { |
|
170 HLT_Output = 1; |
|
171 } |
|
172 xSemaphoreGive(xSemaphoreDriver); |
|
173 } |
|
174 HLT(HLT_Output); |
|
175 } else if (equipment.SSR2 == SSR2_ON_IDLE) { |
|
176 HLT_Output = 1; |
|
177 HLT(1); |
|
178 } |
128 } |
179 } |
129 |
180 |
130 |
181 |
131 |
182 |
132 void task_driver(void *pvParameter) |
183 void task_driver(void *pvParameter) |
144 gpio_set_direction(SSR_MLT, GPIO_MODE_OUTPUT); |
195 gpio_set_direction(SSR_MLT, GPIO_MODE_OUTPUT); |
145 gpio_pad_select_gpio(SSR_HLT); |
196 gpio_pad_select_gpio(SSR_HLT); |
146 gpio_set_direction(SSR_HLT, GPIO_MODE_OUTPUT); |
197 gpio_set_direction(SSR_HLT, GPIO_MODE_OUTPUT); |
147 gpio_pad_select_gpio(SSR_PUMP); |
198 gpio_pad_select_gpio(SSR_PUMP); |
148 gpio_set_direction(SSR_PUMP, GPIO_MODE_OUTPUT); |
199 gpio_set_direction(SSR_PUMP, GPIO_MODE_OUTPUT); |
|
200 |
|
201 // Prepare and then apply the LEDC PWM timer configuration |
|
202 ledc_timer_config_t ledc_timer = { |
|
203 .speed_mode = LEDC_LOW_SPEED_MODE, ///< Use low speed |
|
204 .timer_num = LEDC_TIMER_1, |
|
205 .duty_resolution = LEDC_TIMER_10_BIT, ///< 10 bits resolution |
|
206 .freq_hz = 100, ///< 100 Hz |
|
207 .clk_cfg = LEDC_AUTO_CLK ///< Auto select PWM clock |
|
208 }; |
|
209 ledc_timer_config(&ledc_timer); |
|
210 |
|
211 ledc_channel_config_t pwm_channel = { |
|
212 .channel = LEDC_CHANNEL_0, |
|
213 .duty = 1024, ///< Default 0% (inverted value) |
|
214 .gpio_num = PWM_MLT, ///< MLT pin |
|
215 .speed_mode = LEDC_LOW_SPEED_MODE, |
|
216 .hpoint = 0, |
|
217 .intr_type = LEDC_INTR_DISABLE, |
|
218 .timer_sel = LEDC_TIMER_1 |
|
219 }; |
|
220 ledc_channel_config(&pwm_channel); |
149 |
221 |
150 /* |
222 /* |
151 * Initialize state |
223 * Initialize state |
152 */ |
224 */ |
153 driver_state = malloc(sizeof(DRIVER_State)); |
225 driver_state = malloc(sizeof(DRIVER_State)); |
166 driver_state->pwm_gpio = PWM_MLT; |
238 driver_state->pwm_gpio = PWM_MLT; |
167 driver_state->pwm_mlt = false; |
239 driver_state->pwm_mlt = false; |
168 driver_state->pwm_nohlt = 10; /* Conservative safety value. */ |
240 driver_state->pwm_nohlt = 10; /* Conservative safety value. */ |
169 |
241 |
170 PID(&Input, &Output, &Setpoint, 200, 2.0, 1.5, PID_DIRECT); |
242 PID(&Input, &Output, &Setpoint, 200, 2.0, 1.5, PID_DIRECT); |
171 |
243 |
172 /* |
244 /* |
173 * One loop must complete in 20 mSecs, that is one mains |
245 * One loop must complete in 20 mSecs, that is one mains |
174 * frequency period cycle in 50 Hz countries. |
246 * frequency period cycle in 50 Hz countries. |
175 */ |
247 */ |
176 while (1) { |
248 while (1) { |
239 |
311 |
240 if (rc) { |
312 if (rc) { |
241 w_StartTime = now; |
313 w_StartTime = now; |
242 } |
314 } |
243 |
315 |
244 if ((int)((Output / 255.0) * RealTime) > (now - w_StartTime)) { |
316 if (equipment.Hendi) { |
|
317 |
|
318 int PWMout = (int)((Output * 100) / 255.0); |
|
319 |
|
320 if ((PID_GetMode() == PID_AUTOMATIC) && (MLT_Mode == MLT_MODE_PID)) { |
|
321 if (PWMout > equipment.MashPower) |
|
322 PWMout = equipment.MashPower; |
|
323 } |
|
324 |
|
325 /* |
|
326 * Hendi minimum power is 500 Watt, this is 14%. |
|
327 * So, we turn the cooker on around 10% power. |
|
328 */ |
|
329 if (PWMout >= 10) { // Hendi minimum power is 500 Watt, this is 10% |
|
330 if ((((PWMout / 100.0) * equipment.MLT_watt) + equipment.HLT_watt) > equipment.Max_watt) { |
|
331 if (HLT_pin) { |
|
332 ESP_LOGI(TAG, "Power %f %d", ((PWMout / 100.0) * equipment.MLT_watt) + equipment.HLT_watt, equipment.Max_watt); |
|
333 ESP_LOGI(TAG, "Immediate HLT panic shutdown"); |
|
334 HLT_Output = 0; |
|
335 HLT(0); // As soon as possible before the Hendi increases power. |
|
336 } |
|
337 } else { |
|
338 AllowHLT(); // TODO: delay this one loop. |
|
339 } |
|
340 MLT_PWM(PWMout); |
|
341 MLT(1); |
|
342 } else { |
|
343 MLT_PWM(0); |
|
344 MLT(0); |
|
345 AllowHLT(); |
|
346 } |
|
347 } else if ((int)((Output / 255.0) * RealTime) > (now - w_StartTime)) { |
245 MLT(1); |
348 MLT(1); |
246 if ((equipment.SSR2 == SSR2_HLT_SHARE) || (equipment.SSR2 == SSR2_ON_IDLE)) { |
349 if ((equipment.SSR2 == SSR2_HLT_SHARE) || (equipment.SSR2 == SSR2_ON_IDLE)) { |
247 HLT(0); |
350 HLT(0); |
248 HLT_Output = 0; |
351 HLT_Output = 0; |
249 } |
352 } |
250 } else { |
353 } else { |
251 MLT(0); |
354 MLT(0); |
252 if (equipment.SSR2 == SSR2_HLT_SHARE) { |
355 AllowHLT(); |
253 if (xSemaphoreTake(xSemaphoreDriver, 10) == pdTRUE) { |
|
254 HLT_Output = 0; |
|
255 if (driver_state->hlt_mode == HLT_MODE_BANG) { |
|
256 HLT_Output = (HLT_Input < HLT_Setpoint) ? 1:0; |
|
257 } else if (driver_state->hlt_mode == HLT_MODE_ON) { |
|
258 HLT_Output = 1; |
|
259 } |
|
260 xSemaphoreGive(xSemaphoreDriver); |
|
261 } |
|
262 HLT(HLT_Output); |
|
263 } else if (equipment.SSR2 == SSR2_ON_IDLE) { |
|
264 HLT_Output = 1; |
|
265 HLT(1); |
|
266 } |
|
267 } |
356 } |
268 |
357 |
269 /* |
358 /* |
270 * Independant HLT temperature control |
359 * Independant HLT temperature control |
271 */ |
360 */ |
307 Input, Output, Setpoint, PID_GetMode() ? "AUTOMATIC" : "MANUAL ", RealTime, |
396 Input, Output, Setpoint, PID_GetMode() ? "AUTOMATIC" : "MANUAL ", RealTime, |
308 HLT_Input, HLT_Output, HLT_Setpoint); |
397 HLT_Input, HLT_Output, HLT_Setpoint); |
309 } |
398 } |
310 #endif |
399 #endif |
311 |
400 |
312 // Not reliable, so do it manually. |
401 // Do not use vTaskDelayUntil(), it is not reliable here. |
313 //vTaskDelayUntil(&last_wake_time, (1000 / 50) / portTICK_PERIOD_MS); |
|
314 now_tick = xTaskGetTickCount(); |
402 now_tick = xTaskGetTickCount(); |
315 if ((now_tick - last_tick) > (1000 / 50)) { |
403 if ((now_tick - last_tick) > (1000 / 50)) { |
316 // This happens one or two times during a brew. |
404 // This happens one or two times during a brew. |
317 wait_ticks = (1000 / 50); |
405 wait_ticks = (1000 / 50); |
318 } else { |
406 } else { |