131 } |
130 } |
132 } else { |
131 } else { |
133 val = 0; |
132 val = 0; |
134 } |
133 } |
135 |
134 |
|
135 /* |
|
136 * If the Hendi is on, the lowest setting is 500 Watt. So, if we need less then |
|
137 * 10% power, turn it off, just like the manual knob. |
|
138 */ |
|
139 MLT((val >= 10) ? 1:0); |
|
140 |
136 if (val != oldval) { |
141 if (val != oldval) { |
137 ESP_LOGI(TAG, "MLT_PWM(%d) val=%d %.0f watt", percent, val, (percent / 100.0) * equipment.MLT_watt); |
142 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); |
143 ledc_set_duty(LEDC_HIGH_SPEED_MODE, LEDC_CHANNEL_0, 1024 - val); |
139 ledc_update_duty(LEDC_LOW_SPEED_MODE, LEDC_CHANNEL_0); |
144 ledc_update_duty(LEDC_HIGH_SPEED_MODE, LEDC_CHANNEL_0); |
140 } |
145 } |
141 oldval = val; |
146 oldval = val; |
142 } |
147 } |
143 |
148 |
144 |
149 |
182 |
187 |
183 void task_driver(void *pvParameter) |
188 void task_driver(void *pvParameter) |
184 { |
189 { |
185 TickType_t wait_ticks, last_tick, now_tick; |
190 TickType_t wait_ticks, last_tick, now_tick; |
186 bool rc; |
191 bool rc; |
|
192 int SafeCount = 0; |
187 unsigned long now, RealTime, w_StartTime = 0; |
193 unsigned long now, RealTime, w_StartTime = 0; |
188 |
194 |
189 ESP_LOGI(TAG, "Start drivers"); |
195 ESP_LOGI(TAG, "Start drivers"); |
190 |
196 |
191 /* |
197 /* |
198 gpio_pad_select_gpio(SSR_PUMP); |
204 gpio_pad_select_gpio(SSR_PUMP); |
199 gpio_set_direction(SSR_PUMP, GPIO_MODE_OUTPUT); |
205 gpio_set_direction(SSR_PUMP, GPIO_MODE_OUTPUT); |
200 |
206 |
201 // Prepare and then apply the LEDC PWM timer configuration |
207 // Prepare and then apply the LEDC PWM timer configuration |
202 ledc_timer_config_t ledc_timer = { |
208 ledc_timer_config_t ledc_timer = { |
203 .speed_mode = LEDC_LOW_SPEED_MODE, ///< Use low speed |
209 .speed_mode = LEDC_HIGH_SPEED_MODE, ///< Use high speed timer |
204 .timer_num = LEDC_TIMER_1, |
210 .timer_num = LEDC_TIMER_0, ///< Timer 0 |
205 .duty_resolution = LEDC_TIMER_10_BIT, ///< 10 bits resolution |
211 .duty_resolution = LEDC_TIMER_10_BIT, ///< 10 bits resolution |
206 .freq_hz = 100, ///< 100 Hz |
212 .freq_hz = 100, ///< 100 Hz |
207 .clk_cfg = LEDC_AUTO_CLK ///< Auto select PWM clock |
213 .clk_cfg = LEDC_AUTO_CLK ///< Auto select PWM clock |
208 }; |
214 }; |
209 ledc_timer_config(&ledc_timer); |
215 ledc_timer_config(&ledc_timer); |
210 |
216 |
211 ledc_channel_config_t pwm_channel = { |
217 ledc_channel_config_t pwm_channel = { |
212 .channel = LEDC_CHANNEL_0, |
218 .channel = LEDC_CHANNEL_0, |
213 .duty = 1024, ///< Default 0% (inverted value) |
219 .duty = 1024, ///< Default 0% (inverted value) |
214 .gpio_num = PWM_MLT, ///< MLT pin |
220 .gpio_num = PWM_MLT, ///< MLT pin |
215 .speed_mode = LEDC_LOW_SPEED_MODE, |
221 .speed_mode = LEDC_HIGH_SPEED_MODE, |
216 .hpoint = 0, |
222 .hpoint = 0, |
217 .intr_type = LEDC_INTR_DISABLE, |
223 .intr_type = LEDC_INTR_DISABLE, |
218 .timer_sel = LEDC_TIMER_1 |
224 .timer_sel = LEDC_TIMER_0 ///< Timer 0 |
219 }; |
225 }; |
220 ledc_channel_config(&pwm_channel); |
226 ledc_channel_config(&pwm_channel); |
221 |
227 |
222 /* |
228 /* |
223 * Initialize state |
229 * Initialize state |
233 driver_state->hlt_sp = driver_state->hlt_pv = 0.0; |
239 driver_state->hlt_sp = driver_state->hlt_pv = 0.0; |
234 driver_state->hlt_power = 0; |
240 driver_state->hlt_power = 0; |
235 driver_state->hlt_and_mlt = false; |
241 driver_state->hlt_and_mlt = false; |
236 driver_state->pump_gpio = SSR_PUMP; |
242 driver_state->pump_gpio = SSR_PUMP; |
237 driver_state->pump_run = 0; |
243 driver_state->pump_run = 0; |
238 driver_state->pwm_gpio = PWM_MLT; |
|
239 driver_state->pwm_mlt = false; |
|
240 driver_state->pwm_nohlt = 10; /* Conservative safety value. */ |
|
241 |
244 |
242 PID(&Input, &Output, &Setpoint, 200, 2.0, 1.5, PID_DIRECT); |
245 PID(&Input, &Output, &Setpoint, 200, 2.0, 1.5, PID_DIRECT); |
243 |
246 |
244 /* |
247 /* |
245 * One loop must complete in 20 mSecs, that is one mains |
248 * One loop must complete in 20 mSecs, that is one mains |
316 if (equipment.Hendi) { |
319 if (equipment.Hendi) { |
317 |
320 |
318 int PWMout = (int)((Output * 100) / 255.0); |
321 int PWMout = (int)((Output * 100) / 255.0); |
319 |
322 |
320 if ((PID_GetMode() == PID_AUTOMATIC) && (MLT_Mode == MLT_MODE_PID)) { |
323 if ((PID_GetMode() == PID_AUTOMATIC) && (MLT_Mode == MLT_MODE_PID)) { |
|
324 /* Mash power limited */ |
321 if (PWMout > equipment.MashPower) |
325 if (PWMout > equipment.MashPower) |
322 PWMout = equipment.MashPower; |
326 PWMout = equipment.MashPower; |
323 } |
327 } |
324 |
328 |
325 /* |
329 /* |
326 * Hendi minimum power is 500 Watt, this is 14%. |
330 * Do not send power values < 10%. |
327 * So, we turn the cooker on around 10% power. |
|
328 */ |
331 */ |
329 if (PWMout >= 10) { // Hendi minimum power is 500 Watt, this is 10% |
332 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) { |
333 if ((((PWMout / 100.0) * equipment.MLT_watt) + equipment.HLT_watt) > equipment.Max_watt) { |
331 if (HLT_pin) { |
334 if (HLT_pin) { |
332 ESP_LOGI(TAG, "Power %f %d", ((PWMout / 100.0) * equipment.MLT_watt) + equipment.HLT_watt, equipment.Max_watt); |
335 ESP_LOGI(TAG, "Current %.0f Watt above %d limit, shutdown HLT", |
333 ESP_LOGI(TAG, "Immediate HLT panic shutdown"); |
336 ((PWMout / 100.0) * equipment.MLT_watt) + equipment.HLT_watt, equipment.Max_watt); |
334 HLT_Output = 0; |
337 HLT_Output = 0; |
335 HLT(0); // As soon as possible before the Hendi increases power. |
338 HLT(0); // As soon as possible before the Hendi increases power. |
336 } |
339 } |
337 } else { |
340 SafeCount = (int)(15000 / equipment.SampleTime) + 1; // About 15 seconds release time |
338 AllowHLT(); // TODO: delay this one loop. |
341 } else if (rc) { |
|
342 if (SafeCount > 0) { |
|
343 SafeCount--; |
|
344 } else { |
|
345 AllowHLT(); |
|
346 } |
339 } |
347 } |
340 MLT_PWM(PWMout); |
348 MLT_PWM(PWMout); |
341 MLT(1); |
|
342 } else { |
349 } else { |
343 MLT_PWM(0); |
350 MLT_PWM(0); |
344 MLT(0); |
351 if (rc) { |
345 AllowHLT(); |
352 if (SafeCount > 0) { |
|
353 SafeCount--; |
|
354 } else { |
|
355 AllowHLT(); |
|
356 } |
|
357 } |
346 } |
358 } |
347 } else if ((int)((Output / 255.0) * RealTime) > (now - w_StartTime)) { |
359 } else if ((int)((Output / 255.0) * RealTime) > (now - w_StartTime)) { |
|
360 /* |
|
361 * Use On/Off kettles using time slices. |
|
362 */ |
348 MLT(1); |
363 MLT(1); |
349 if ((equipment.SSR2 == SSR2_HLT_SHARE) || (equipment.SSR2 == SSR2_ON_IDLE)) { |
364 if ((equipment.SSR2 == SSR2_HLT_SHARE) || (equipment.SSR2 == SSR2_ON_IDLE)) { |
350 HLT(0); |
365 HLT(0); |
351 HLT_Output = 0; |
366 HLT_Output = 0; |
352 } |
367 } |