Wed, 08 May 2019 15:46:50 +0200
Merged with default version 0.3.0
0 | 1 | /** |
2 | * @file task_driver.c | |
3 | * @brief BrewBoard relays driver. Control the hardware outputs with the | |
4 | * Solid State relays for the Mash/Boil kettle (MLT, the Hot | |
5 | * Liquer Tank (HLT) and the pump. The MLT has a PID controller | |
6 | * during mashing, and a simple bang on/off control during the | |
7 | * boil. | |
8 | * The HLT output can be off, bang on/off, or just on if the MLT | |
9 | * is off, depending on the configuration. | |
10 | * Use SSR modules that switch during zero crossing of the mains | |
11 | * power, so that when one is turned on and on the same time the | |
12 | * other is turned off, they won't be active at the same time. | |
13 | */ | |
14 | ||
15 | #include "config.h" | |
16 | ||
17 | ||
1
ad2c8b13eb88
Updated lots of doxygen comments
Michiel Broek <mbroek@mbse.eu>
parents:
0
diff
changeset
|
18 | #define SSR_MLT CONFIG_SSR_MLT_GPIO ///< GPIO SSR MLT pin |
ad2c8b13eb88
Updated lots of doxygen comments
Michiel Broek <mbroek@mbse.eu>
parents:
0
diff
changeset
|
19 | #define SSR_HLT CONFIG_SSR_HLT_GPIO ///< GPIO SSR HLT pin |
ad2c8b13eb88
Updated lots of doxygen comments
Michiel Broek <mbroek@mbse.eu>
parents:
0
diff
changeset
|
20 | #define SSR_PUMP CONFIG_SSR_PUMP_GPIO ///< GPIO Pump relay pin |
0 | 21 | |
22 | ||
1
ad2c8b13eb88
Updated lots of doxygen comments
Michiel Broek <mbroek@mbse.eu>
parents:
0
diff
changeset
|
23 | bool outEnable = false; ///< Enable outputs flag |
ad2c8b13eb88
Updated lots of doxygen comments
Michiel Broek <mbroek@mbse.eu>
parents:
0
diff
changeset
|
24 | DRIVER_State * driver_state; ///< Driver state |
ad2c8b13eb88
Updated lots of doxygen comments
Michiel Broek <mbroek@mbse.eu>
parents:
0
diff
changeset
|
25 | SemaphoreHandle_t xSemaphoreDriver = NULL; ///< Driver state lock |
ad2c8b13eb88
Updated lots of doxygen comments
Michiel Broek <mbroek@mbse.eu>
parents:
0
diff
changeset
|
26 | int MLT_pin = 0; ///< MLT state |
ad2c8b13eb88
Updated lots of doxygen comments
Michiel Broek <mbroek@mbse.eu>
parents:
0
diff
changeset
|
27 | int HLT_pin = 0; ///< HLT state |
ad2c8b13eb88
Updated lots of doxygen comments
Michiel Broek <mbroek@mbse.eu>
parents:
0
diff
changeset
|
28 | int Pump_pin = 0; ///< Pump state |
ad2c8b13eb88
Updated lots of doxygen comments
Michiel Broek <mbroek@mbse.eu>
parents:
0
diff
changeset
|
29 | double Input = 0; ///< PID input value |
ad2c8b13eb88
Updated lots of doxygen comments
Michiel Broek <mbroek@mbse.eu>
parents:
0
diff
changeset
|
30 | double Output = 0; ///< PID output value |
ad2c8b13eb88
Updated lots of doxygen comments
Michiel Broek <mbroek@mbse.eu>
parents:
0
diff
changeset
|
31 | double Setpoint = 0; ///< PID setpoint value |
ad2c8b13eb88
Updated lots of doxygen comments
Michiel Broek <mbroek@mbse.eu>
parents:
0
diff
changeset
|
32 | int MLT_Mode = MLT_MODE_NONE; ///< MLT mode flag |
ad2c8b13eb88
Updated lots of doxygen comments
Michiel Broek <mbroek@mbse.eu>
parents:
0
diff
changeset
|
33 | double HLT_Input = 0; ///< HLT input value |
ad2c8b13eb88
Updated lots of doxygen comments
Michiel Broek <mbroek@mbse.eu>
parents:
0
diff
changeset
|
34 | double HLT_Setpoint = 0; ///< HLT setpoint values |
ad2c8b13eb88
Updated lots of doxygen comments
Michiel Broek <mbroek@mbse.eu>
parents:
0
diff
changeset
|
35 | int HLT_Output = 0; ///< HLT output value |
ad2c8b13eb88
Updated lots of doxygen comments
Michiel Broek <mbroek@mbse.eu>
parents:
0
diff
changeset
|
36 | int HLT_Mode = HLT_MODE_NONE; ///< HLT mode flag |
0 | 37 | |
38 | static const char *TAG = "task_driver"; | |
39 | ||
40 | extern SemaphoreHandle_t xSemaphoreDS18B20; | |
41 | extern DS18B20_State * ds18b20_state; | |
42 | extern unsigned long lastTime; | |
43 | ||
44 | ||
45 | /** | |
46 | * @brief Turn the MLT SSR on or off. | |
47 | */ | |
48 | void MLT(int onoff); | |
49 | ||
50 | /** | |
51 | * @brief Turn the HLT SSR on or off. | |
52 | */ | |
53 | void HLT(int onoff); | |
54 | ||
55 | /** | |
56 | * @brief Turn the Pump on or off. | |
57 | */ | |
58 | void Pump(int onoff); | |
59 | ||
60 | ||
61 | ||
62 | void MLT(int onoff) { | |
63 | ||
64 | if (onoff && outEnable) { | |
65 | gpio_set_level(SSR_MLT, 1); | |
66 | MLT_pin = 1; | |
67 | } else { | |
68 | gpio_set_level(SSR_MLT, 0); | |
69 | MLT_pin = 0; | |
70 | } | |
71 | } | |
72 | ||
73 | ||
74 | ||
75 | void HLT(int onoff) { | |
76 | ||
77 | if (onoff && outEnable) { | |
78 | gpio_set_level(SSR_HLT, 1); | |
79 | HLT_pin = 1; | |
80 | } else { | |
81 | gpio_set_level(SSR_HLT, 0); | |
82 | HLT_pin = 0; | |
83 | } | |
84 | } | |
85 | ||
86 | ||
87 | ||
88 | void Pump(int onoff) { | |
89 | ||
90 | if (onoff && outEnable) { | |
91 | gpio_set_level(SSR_PUMP, 1); | |
92 | Pump_pin = 1; | |
93 | } else { | |
94 | gpio_set_level(SSR_PUMP, 0); | |
95 | Pump_pin = 0; | |
96 | } | |
97 | } | |
98 | ||
99 | ||
100 | ||
1
ad2c8b13eb88
Updated lots of doxygen comments
Michiel Broek <mbroek@mbse.eu>
parents:
0
diff
changeset
|
101 | /** |
ad2c8b13eb88
Updated lots of doxygen comments
Michiel Broek <mbroek@mbse.eu>
parents:
0
diff
changeset
|
102 | * @brief Load PID settings from equipment record. |
ad2c8b13eb88
Updated lots of doxygen comments
Michiel Broek <mbroek@mbse.eu>
parents:
0
diff
changeset
|
103 | */ |
0 | 104 | void LoadPIDsettings() { |
105 | PID_SetTunings(equipment.PID_kP, equipment.PID_kI, equipment.PID_kD, equipment.PID_POn); | |
106 | PID_SetSampleTime(equipment.SampleTime); | |
107 | ||
108 | /* | |
109 | * Initialize the PID | |
110 | */ | |
111 | Output = 0.0; // Reset internal Iterm. | |
112 | PID_SetMode(PID_MANUAL); | |
113 | PID_SetMode(PID_AUTOMATIC); | |
114 | } | |
115 | ||
116 | ||
117 | ||
118 | void task_driver(void *pvParameter) | |
119 | { | |
120 | TickType_t wait_ticks, last_tick, now_tick; | |
121 | bool rc; | |
122 | unsigned long now, RealTime, w_StartTime = 0; | |
123 | ||
124 | ESP_LOGI(TAG, "Starting output drivers"); | |
125 | ||
126 | /* | |
127 | * Configure IOMUX register. | |
128 | */ | |
129 | gpio_pad_select_gpio(SSR_MLT); | |
130 | gpio_set_direction(SSR_MLT, GPIO_MODE_OUTPUT); | |
131 | gpio_pad_select_gpio(SSR_HLT); | |
132 | gpio_set_direction(SSR_HLT, GPIO_MODE_OUTPUT); | |
133 | gpio_pad_select_gpio(SSR_PUMP); | |
134 | gpio_set_direction(SSR_PUMP, GPIO_MODE_OUTPUT); | |
135 | ||
136 | /* | |
137 | * Initialize state | |
138 | */ | |
139 | driver_state = malloc(sizeof(DRIVER_State)); | |
140 | driver_state->enable = outEnable = false; | |
141 | driver_state->mlt_gpio = SSR_MLT; | |
142 | driver_state->mlt_mode = MLT_MODE_NONE; | |
143 | driver_state->mlt_sp = driver_state->mlt_pv = 0.0; | |
144 | driver_state->mlt_power = 0; | |
145 | driver_state->hlt_gpio = SSR_HLT; | |
146 | driver_state->hlt_mode = HLT_MODE_NONE; | |
147 | driver_state->hlt_sp = driver_state->hlt_pv = 0.0; | |
148 | driver_state->hlt_power = 0; | |
149 | driver_state->hlt_and_mlt = false; | |
150 | driver_state->pump_gpio = SSR_PUMP; | |
151 | driver_state->pump_run = 0; | |
152 | ||
153 | PID(&Input, &Output, &Setpoint, 150, 1.5, 15000, PID_P_ON_E, PID_DIRECT); | |
154 | ||
155 | /* | |
156 | * One loop must complete in 20 mSecs, that is one mains | |
157 | * frequency period cycle in 50 Hz countries. | |
158 | */ | |
159 | while (1) { | |
160 | ||
161 | last_tick = xTaskGetTickCount(); | |
162 | ||
163 | if (xSemaphoreTake(xSemaphoreDriver, 10) == pdTRUE) { | |
164 | /* | |
165 | * Get the current temperature readings | |
166 | */ | |
167 | if (xSemaphoreTake(xSemaphoreDS18B20, 10) == pdTRUE) { | |
168 | if (ds18b20_state->mlt_valid) | |
169 | driver_state->mlt_pv = ds18b20_state->mlt_temperature; | |
170 | if (ds18b20_state->hlt_valid) | |
171 | driver_state->hlt_pv = ds18b20_state->hlt_temperature; | |
172 | xSemaphoreGive(xSemaphoreDS18B20); | |
173 | } | |
174 | ||
175 | /* | |
176 | * Other values that we need | |
177 | */ | |
178 | Input = driver_state->mlt_pv; | |
179 | Setpoint = driver_state->mlt_sp; | |
180 | if (driver_state->mlt_mode != MLT_Mode) { | |
181 | if (driver_state->mlt_mode == MLT_MODE_BANG) { | |
182 | PID_SetMode(PID_MANUAL); | |
183 | } else if (driver_state->mlt_mode == MLT_MODE_PID) { | |
184 | LoadPIDsettings(); | |
185 | } | |
186 | MLT_Mode = driver_state->mlt_mode; | |
187 | ESP_LOGI(TAG, "MLT mode set to %d", MLT_Mode); | |
188 | } | |
189 | if (driver_state->hlt_mode != HLT_Mode) { | |
190 | HLT_Mode = driver_state->hlt_mode; | |
191 | ESP_LOGI(TAG, "HLT mode set to %d", HLT_Mode); | |
192 | } | |
193 | outEnable = driver_state->enable; | |
194 | HLT_Input = driver_state->hlt_pv; | |
195 | HLT_Setpoint = driver_state->hlt_sp; | |
196 | xSemaphoreGive(xSemaphoreDriver); | |
197 | } | |
198 | ||
199 | rc = false; | |
200 | now = xTaskGetTickCount() * portTICK_PERIOD_MS; | |
201 | ||
202 | if ((PID_GetMode() == PID_AUTOMATIC) && (MLT_Mode == MLT_MODE_PID)) { | |
203 | rc = PID_Compute(); | |
204 | RealTime = (equipment.SampleTime * equipment.MashPower) / 100; | |
205 | } else { | |
206 | /* | |
207 | * Schedule the loop ourself. | |
208 | */ | |
209 | unsigned long timeChange = (now - lastTime); | |
210 | if (timeChange >= equipment.SampleTime) { | |
211 | lastTime = now; | |
212 | rc = true; | |
213 | } | |
214 | RealTime = equipment.SampleTime; | |
215 | if (driver_state->mlt_mode == MLT_MODE_BANG) { | |
216 | Output = (Input < Setpoint) ? 255:0; | |
217 | } | |
218 | if (driver_state->mlt_mode == MLT_MODE_NONE || driver_state->mlt_mode == MLT_MODE_OFF) { | |
219 | Output = 0; | |
220 | } | |
221 | } | |
222 | ||
223 | if (rc) { | |
224 | w_StartTime = now; | |
225 | } | |
226 | ||
227 | if ((int)((Output / 255.0) * RealTime) > (now - w_StartTime)) { | |
228 | MLT(1); | |
229 | if ((equipment.SSR2 == SSR2_HLT_SHARE) || (equipment.SSR2 == SSR2_ON_IDLE)) { | |
230 | HLT(0); | |
231 | HLT_Output = 0; | |
232 | } | |
233 | } else { | |
234 | MLT(0); | |
235 | if (equipment.SSR2 == SSR2_HLT_SHARE) { | |
236 | if (xSemaphoreTake(xSemaphoreDriver, 10) == pdTRUE) { | |
237 | HLT_Output = 0; | |
238 | if (driver_state->hlt_mode == HLT_MODE_BANG) { | |
239 | HLT_Output = (HLT_Input < HLT_Setpoint) ? 1:0; | |
240 | } else if (driver_state->hlt_mode == HLT_MODE_ON) { | |
241 | HLT_Output = 1; | |
242 | } | |
243 | xSemaphoreGive(xSemaphoreDriver); | |
244 | } | |
245 | HLT(HLT_Output); | |
246 | } else if (equipment.SSR2 == SSR2_ON_IDLE) { | |
247 | HLT_Output = 1; | |
248 | HLT(1); | |
249 | } | |
250 | } | |
251 | ||
252 | /* | |
253 | * Independant HLT temperature control | |
254 | */ | |
255 | if (equipment.SSR2 == SSR2_HLT_IND) { | |
256 | if (xSemaphoreTake(xSemaphoreDriver, 10) == pdTRUE) { | |
257 | HLT_Output = 0; | |
258 | if (driver_state->hlt_mode == HLT_MODE_BANG) { | |
259 | HLT_Output = (HLT_Input < HLT_Setpoint) ? 1:0; | |
260 | } else if (driver_state->hlt_mode == HLT_MODE_ON) { | |
261 | HLT_Output = 1; | |
262 | } | |
263 | xSemaphoreGive(xSemaphoreDriver); | |
264 | } | |
265 | HLT(HLT_Output); | |
266 | } | |
267 | ||
268 | /* | |
269 | * Update the driver results. | |
270 | */ | |
271 | if (xSemaphoreTake(xSemaphoreDriver, 10) == pdTRUE) { | |
272 | driver_state->mlt_power = (int)((Output * 100) / 255.0); | |
273 | if (HLT_Output) { | |
274 | if (equipment.SSR2 == SSR2_HLT_SHARE) { | |
275 | driver_state->hlt_power = 100 - driver_state->mlt_power; | |
276 | } else if (equipment.SSR2 == SSR2_HLT_IND) { | |
277 | driver_state->hlt_power = 100; | |
278 | } | |
279 | } else { | |
280 | driver_state->hlt_power = 0; | |
281 | } | |
282 | if (driver_state->pump_run != Pump_pin) | |
283 | Pump(driver_state->pump_run); | |
284 | xSemaphoreGive(xSemaphoreDriver); | |
285 | } | |
286 | ||
287 | #if 0 | |
288 | if (rc) { | |
289 | 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", | |
290 | Input, Output, Setpoint, PID_GetMode() ? "AUTOMATIC" : "MANUAL ", RealTime, | |
291 | HLT_Input, HLT_Output, HLT_Setpoint); | |
292 | } | |
293 | #endif | |
294 | ||
295 | // Not reliable, so do it manually. | |
296 | //vTaskDelayUntil(&last_wake_time, (1000 / 50) / portTICK_PERIOD_MS); | |
297 | now_tick = xTaskGetTickCount(); | |
298 | if ((now_tick - last_tick) > (1000 / 50)) { | |
299 | // This happens one or two times during a brew. | |
300 | wait_ticks = (1000 / 50); | |
301 | } else { | |
302 | wait_ticks = (1000 / 50) - (now_tick - last_tick); | |
303 | } | |
304 | if (wait_ticks == 0) { | |
305 | // This is rare, but it happens. | |
306 | wait_ticks = 1; | |
307 | } | |
308 | ||
309 | vTaskDelay(wait_ticks); | |
310 | } | |
311 | } | |
312 | ||
313 |