brewco/brewco.c

changeset 487
d5bc44183aa4
parent 486
5a237a99a793
child 488
bee1f70fb42b
equal deleted inserted replaced
486:5a237a99a793 487:d5bc44183aa4
1 /*****************************************************************************
2 * Copyright (C) 2015
3 *
4 * Michiel Broek <mbroek at mbse dot eu>
5 *
6 * This file is part of the mbsePi-apps
7 *
8 * This is free software; you can redistribute it and/or modify it
9 * under the terms of the GNU General Public License as published by the
10 * Free Software Foundation; either version 2, or (at your option) any
11 * later version.
12 *
13 * mbsePi-apps is distributed in the hope that it will be useful, but
14 * WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with ThermFerm; see the file COPYING. If not, write to the Free
20 * Software Foundation, 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
21 *****************************************************************************/
22
23 #include "brewco.h"
24 #include "rdconfig.h"
25 #include "rdsession.h"
26 #include "rdrecipes.h"
27 #include "util.h"
28 #include "xutil.h"
29 #include "lcd-pcf8574.h"
30 #include "slcd.h"
31 #include "lock.h"
32 #include "devices.h"
33 #include "keyboard.h"
34 #include "simulator.h"
35 #include "prompt.h"
36 #include "setup.h"
37 #include "logger.h"
38
39
40
41 int my_shutdown = FALSE;
42 int mlt_pump_state = 0;
43
44 double hltInput; /* HLT PID variables */
45 double hltOutput;
46 double hltSetpoint;
47 double mltInput; /* MLT PID variables */
48 double mltOutput;
49 double mltSetpoint;
50
51 extern int debug;
52 extern sys_config Config;
53 extern a_recipe *recipes;
54 extern int lcdHandle;
55 extern int slcdHandle;
56 extern int sock;
57
58 #ifdef USE_SIMULATOR
59 extern int SIM_cooler;
60 #endif
61 char *etcpath = NULL;
62 char *varpath = NULL;
63
64
65
66 #ifndef HAVE_WIRINGPI_H
67 pthread_t threads[5];
68 #endif
69
70
71 #define MANUAL_NONE 0
72 #define MANUAL_SELHLT 1
73 #define MANUAL_SELMLT 2
74 #define MANUAL_HLT 11
75 #define MANUAL_MLT 12
76
77 #define PERC_INIT 0
78 #define PERC_MLT 1
79 #define PERC_HLT 2
80 #define PERC_REST 3
81
82
83 int manual = MANUAL_NONE;
84
85
86
87 /*
88 * CGRAM characters
89 */
90 unsigned char degC[8] = { 0b01000, 0b10100, 0b01000, 0b00111, 0b01000, 0b01000, 0b01000, 0b00111 };
91 unsigned char degF[8] = { 0b01000, 0b10100, 0b01000, 0b00111, 0b00100, 0b00110, 0b00100, 0b00100 };
92 unsigned char SP_Symbol[8] = { 0b11100, 0b10000, 0b11100, 0b00111, 0b11101, 0b00111, 0b00100, 0b00100 };
93 unsigned char PumpONOFF[8] = { 0b00000, 0b01110, 0b01010, 0b01110, 0b01000, 0b01000, 0b01000, 0b00000 };
94 unsigned char RevPumpONOFF[8] = { 0b11111, 0b10001, 0b10101, 0b10001, 0b10111, 0b10111, 0b10111, 0b11111 };
95 unsigned char HeatONOFF[8] = { 0b00000, 0b01010, 0b01010, 0b01110, 0b01110, 0b01010, 0b01010, 0b00000 };
96 unsigned char RevHeatONOFF[8] = { 0b11111, 0b10101, 0b10101, 0b10001, 0b10001, 0b10101, 0b10101, 0b11111 };
97 unsigned char Language[8] = { 0b11111, 0b00010, 0b01000, 0b11111, 0b00000, 0b10001, 0b10101, 0b11111 };
98
99
100 void help(void);
101 void die(int);
102
103
104
105 void help(void)
106 {
107 fprintf(stdout, "mbsePi-apps brewco v%s starting\n\n", VERSION);
108 fprintf(stdout, "Usage: brewco [-d] [-h]\n");
109 fprintf(stdout, " -d --debug Debug and run in foreground\n");
110 fprintf(stdout, " -h --help Display this help\n");
111 }
112
113
114
115 void die(int onsig)
116 {
117 switch (onsig) {
118 #ifdef USE_SIMULATOR
119 case SIGUSR1: syslog(LOG_NOTICE, "Got SIGUSR1, start cooler");
120 SIM_cooler = TRUE;
121 return;
122 case SIGUSR2: syslog(LOG_NOTICE, "Got SIGUSR2, stop cooler");
123 SIM_cooler = FALSE;
124 return;
125 #endif
126 case SIGHUP: syslog(LOG_NOTICE, "Got SIGHUP, shutting down");
127 break;
128 case SIGINT: syslog(LOG_NOTICE, "Keyboard interrupt, shutting down");
129 break;
130 case SIGTERM: syslog(LOG_NOTICE, "Got SIGTERM, shutting down");
131 break;
132 case SIGSEGV: syslog(LOG_NOTICE, "Got SIGSEGV, shutting down");
133 my_shutdown = TRUE;
134 exit(SIGSEGV);
135 break;
136 default: syslog(LOG_NOTICE, "die() on signal %d", onsig);
137 }
138
139 my_shutdown = TRUE;
140 }
141
142
143
144 void tempstatus(void)
145 {
146 char text[81];
147
148 snprintf(text, 8, "%6.2f\001", hltInput);
149 #ifdef HAVE_WIRINGPI_H
150 piLock(LOCK_LCD);
151 lcdPosition(lcdHandle, 1, 1);
152 lcdPuts(lcdHandle, text);
153 #endif
154 slcdPosition(slcdHandle, 1, 1);
155 slcdPuts(slcdHandle, text);
156
157 snprintf(text, 8, "%6.2f\001", mltInput);
158 #ifdef HAVE_WIRINGPI_H
159 piLock(LOCK_LCD);
160 lcdPosition(lcdHandle, 10, 1);
161 lcdPuts(lcdHandle, text);
162 #endif
163 slcdPosition(slcdHandle, 10, 1);
164 slcdPuts(slcdHandle, text);
165 }
166
167
168
169 /*
170 * Third line, show setPoints or heater percentage.
171 */
172 void percstatus(int which)
173 {
174 char text[21];
175
176 if (which)
177 snprintf(text, 8, "%6.2f\002", hltSetpoint);
178 else
179 snprintf(text, 8, "HLT%3d%%", (int)hltOutput);
180 #ifdef HAVE_WIRINGPI_H
181 piLock(LOCK_LCD);
182 lcdPosition(lcdHandle, 1, 2);
183 lcdPuts(lcdHandle, text);
184 #endif
185 slcdPosition(slcdHandle, 1, 2);
186 slcdPuts(slcdHandle, text);
187
188 if (which)
189 snprintf(text, 8, "%6.2f\002", mltSetpoint);
190 else
191 snprintf(text, 8, "MLT%3d%%", (int)mltOutput);
192 #ifdef HAVE_WIRINGPI_H
193 piLock(LOCK_LCD);
194 lcdPosition(lcdHandle, 10, 2);
195 lcdPuts(lcdHandle, text);
196 #endif
197 slcdPosition(slcdHandle, 10, 2);
198 slcdPuts(slcdHandle, text);
199 }
200
201
202
203 /*
204 * Third line during boil, only MLT status
205 */
206 void mltstatus(void)
207 {
208 char text[21];
209
210 snprintf(text, 20, "MLT %3d%% %6.2f\002 ", (int)mltOutput, mltSetpoint);
211 #ifdef HAVE_WIRINGPI_H
212 piLock(LOCK_LCD);
213 lcdPosition(lcdHandle, 0, 2);
214 lcdPuts(lcdHandle, text);
215 #endif
216 slcdPosition(slcdHandle, 0, 2);
217 slcdPuts(slcdHandle, text);
218 }
219
220
221
222 void timestatus(int row, int timeval)
223 {
224 char text[21];
225 int hours, mins, val = timeval;
226
227 hours = val / 3600;
228 val -= (hours * 3600);
229 mins = val / 60;
230 val -= (mins * 60);
231
232 snprintf(text, 20, " %02d:%02d:%02d ", hours, mins, val);
233 #ifdef HAVE_WIRINGPI_H
234 piLock(LOCK_LCD);
235 lcdPosition(lcdHandle, 0, row);
236 lcdPuts(lcdHandle, text);
237 #endif
238 slcdPosition(slcdHandle, 0, row);
239 slcdPuts(slcdHandle, text);
240 }
241
242
243
244 int set_HLT_heater(units_list *unit, int state, double val)
245 {
246 if (strcmp(unit->hlt_heater.uuid, (char *)"00000000-0000-0000-0000-000000000000") == 0)
247 return 0;
248
249 hltSetpoint = val;
250
251 if (state && (PID_getMode(unit->PID_hlt) == P_MANUAL)) {
252 hlt_status(1);
253 PID_setMode(unit->PID_hlt, P_AUTOMATIC);
254 return 1;
255 }
256 if (! state && (PID_getMode(unit->PID_hlt) == P_AUTOMATIC)) {
257 hlt_status(0);
258 PID_setMode(unit->PID_hlt, P_MANUAL);
259 return 1;
260 }
261
262 return 0;
263 }
264
265
266
267 int set_MLT_heater(units_list *unit, int state, double val)
268 {
269 if (strcmp(unit->mlt_heater.uuid, (char *)"00000000-0000-0000-0000-000000000000") == 0)
270 return 0;
271
272 mltSetpoint = val;
273
274 if (state && (PID_getMode(unit->PID_mlt) == P_MANUAL)) {
275 mlt_status(1);
276 PID_setMode(unit->PID_mlt, P_AUTOMATIC);
277 return 1;
278 }
279 if (! state && (PID_getMode(unit->PID_mlt) == P_AUTOMATIC)) {
280 mlt_status(0);
281 PID_setMode(unit->PID_mlt, P_MANUAL);
282 return 1;
283 }
284
285 return 0;
286 }
287
288
289
290 int set_MLT_pump(units_list *unit, int state)
291 {
292 if (strcmp(unit->mlt_pump.uuid, (char *)"00000000-0000-0000-0000-000000000000") == 0)
293 return 0;
294
295 if (state && ! mlt_pump_state) {
296 device_out(unit->mlt_pump.uuid, 1);
297 mlt_pump_state = 1;
298 return 1;
299 }
300 if (! state && mlt_pump_state) {
301 device_out(unit->mlt_pump.uuid, 0);
302 mlt_pump_state = 0;
303 return 1;
304 }
305
306 return 0;
307 }
308
309
310
311 void automatic_brew(units_list *, brew_session *, a_recipe *, int, int);
312 void automatic_brew(units_list *unit, brew_session *brew, a_recipe *recipe, int dosave, int seconds)
313 {
314 int key, save = dosave, i;
315 char data[128];
316 static int mash_fase = MASH_NA, hopstand = 0, last_step = STEP_NA, last_fase = -1, oldsec = 75, startdelay = 0;
317
318 if (brew->brewstep != last_step) {
319 snprintf(data, 40, brewstep_name(last_step));
320 syslog(LOG_NOTICE, "AUTO: brewstep %s to %s", data, brewstep_name(brew->brewstep));
321 }
322
323 switch (brew->brewstep) {
324 case STEP_NA: if (debug)
325 fprintf(stdout, "auto: init recipe: %s-%s unit: %s\n", recipe->code, recipe->name, unit->name);
326 syslog(LOG_NOTICE, "AUTO: starting new brew, recipe: %s-%s unit: %s", recipe->code, recipe->name, unit->name);
327 brew->brewstep = STEP_BREWINIT;
328 break;
329
330 case STEP_BREWINIT: prompt(103, NULL); /* " AUTOMATIC MODE " */
331 prompt(207, NULL); /* " Delay start? " */
332 prompt(300, NULL); /* " " */
333 prompt(407, NULL); /* "--- --- No Yes " */
334 if (debug)
335 fprintf(stdout, "step brewinit\n");
336 key = keywait();
337 if (key == KEY_RETURN) {
338 brew->brewstep = STEP_WATERCHECK;
339 startdelay = 0;
340 syslog(LOG_NOTICE, "AUTO: brew initialize, direct start selected");
341 break;
342 }
343 if (key == KEY_ENTER) {
344 startdelay = 30;
345 editInteger(&startdelay, 10, 960, 10, (char *)"Start delay", (char *)"mins");
346 }
347 syslog(LOG_NOTICE, "AUTO: brew initialize");
348 brew->brewstep = STEP_WATERCHECK;
349 break;
350
351 case STEP_WATERCHECK: if (brew->brewstep != last_step) {
352 prompt(111, NULL); /* "AUTO --> Mash In " */
353 prompt(209, NULL); /* " Water Added? " */
354 prompt(300, NULL);
355 prompt(407, NULL); /* "--- --- No Yes " */
356 last_step = brew->brewstep;
357 }
358 slcdDummy(slcdHandle);
359 key = keycheck();
360 if (key == KEY_ENTER) {
361 brew->brewstep = STEP_PUMPPRIME;
362 syslog(LOG_NOTICE, "AUTO: confirmed water added");
363 }
364 if (key == KEY_RETURN) {
365 syslog(LOG_NOTICE, "AUTO: aborted water added");
366 brew->brewstep = STEP_CLEANUP;
367 }
368 break;
369
370 case STEP_PUMPPRIME: if (brew->brewstep != last_step) {
371 prompt(100, NULL); /* " " */
372 prompt(210, NULL); /* " Pump Prime " */
373 prompt(300, NULL); /* " " */
374 prompt(400, NULL); /* " " */
375 hlt_status(0);
376 mlt_status(0);
377 last_step = brew->brewstep;
378 }
379 for (i = 1; i < 6; i++) {
380 if (set_MLT_pump(unit, 1))
381 syslog(LOG_NOTICE, "AUTO: pump prime %d turn %s MLT pump", i, mlt_pump_state ? "on":"off");
382 usleep(750000 + (i * 250000)); /* 250 + i * 250 mSec */
383 if (set_MLT_pump(unit, 0))
384 syslog(LOG_NOTICE, "AUTO: pump prime %d turn %s MLT pump", i, mlt_pump_state ? "on":"off");
385 usleep(350000); /* 350 mSec */
386 }
387 brew->brewstep = STEP_WAITSTART;
388 break;
389
390 case STEP_WAITSTART: if (startdelay == 0) {
391 brew->brewstep = STEP_PREMASH;
392 break;
393 }
394 if (brew->brewstep != last_step) {
395 brew->timeout = startdelay * 60;
396 prompt(111, NULL); /* "AUTO --> Mash In " */
397 prompt(212, NULL); /* " To be started in " */
398 prompt(410, NULL); /* " Continue: Yes No " */
399 last_step = brew->brewstep;
400 }
401 if (oldsec != seconds) {
402 timestatus(2, brew->timeout);
403 brew->timeout--;
404 if (brew->timeout <= 0) {
405 syslog(LOG_NOTICE, "AUTO: delayed start time reached");
406 brew->brewstep = STEP_PREMASH;
407 }
408 oldsec = seconds;
409 }
410 slcdDummy(slcdHandle);
411 key = keycheck();
412 if (key == KEY_RETURN) {
413 syslog(LOG_NOTICE, "AUTO: delayed start skipped by user");
414 brew->brewstep = STEP_PREMASH;
415 }
416 break;
417
418 case STEP_PREMASH: if (brew->brewstep != last_step) {
419 prompt(111, NULL); /* "AUTO --> Prepare " */
420 prompt(300, NULL); /* " " */
421 prompt(418, NULL); /* "--- --- Pause --- " */
422 tempstatus();
423 hlt_status(1);
424 mlt_status(1);
425 pump_status(unit->pump_premash);
426 last_step = brew->brewstep;
427 initlog(brew->name);
428 }
429 if (oldsec != seconds) {
430 tempstatus();
431 percstatus((seconds / 2) % 4);
432 oldsec = seconds;
433 }
434 if (set_HLT_heater(unit, 1, 85.0))
435 syslog(LOG_NOTICE, "AUTO: premash turn on HLT at %6.2f", hltSetpoint);
436 if (set_MLT_heater(unit, 1, 10.0))
437 syslog(LOG_NOTICE, "AUTO: premash turn on MLT at %6.2f", mltSetpoint);
438 if (set_MLT_pump(unit, unit->pump_premash))
439 syslog(LOG_NOTICE, "AUTO: premash turn %s MLT pump", mlt_pump_state ? "on":"off");
440 if (hltInput >= 85.0) {
441 brew->brewstep = STEP_MASHING;
442 brew->mashstep = 0;
443 mash_fase = MASH_NA;
444 save = TRUE;
445 }
446 break;
447
448 case STEP_MASHING: if (brew->brewstep != last_step) {
449 prompt(111 + brew->mashstep, NULL); /* "AUTO --> [mashname] " */
450 prompt(300, NULL); /* " " */
451 prompt(418, NULL); /* "--- --- Pause --- " */
452 tempstatus();
453 hlt_status(1);
454 mlt_status(1);
455 pump_status(unit->pump_onmash);
456 last_step = brew->brewstep;
457 }
458 if (set_HLT_heater(unit, 1, 85.0))
459 syslog(LOG_NOTICE, "AUTO: mash turn on HLT at %6.2f", hltSetpoint);
460 if (set_MLT_heater(unit, 1, recipe->mash[brew->mashstep].setpoint))
461 syslog(LOG_NOTICE, "AUTO: mash turn on MLT at %6.2f", mltSetpoint);
462
463 switch (mash_fase) {
464 case MASH_NA: if (recipe->mash[brew->mashstep].skip) {
465 syslog(LOG_NOTICE, "AUTO: skipping mash step %d", brew->mashstep);
466 brew->mashstep++;
467 } else {
468 mltSetpoint = recipe->mash[brew->mashstep].setpoint;
469 brew->timeout = recipe->mash[brew->mashstep].duration * 60;
470 mash_fase = MASH_HEATING;
471 syslog(LOG_NOTICE, "AUTO: mash step %d fase NA, setpoint %6.2f, duration %d",
472 brew->mashstep, mltSetpoint, brew->timeout);
473 }
474 if (brew->mashstep == 0) {
475 brew->starttime = time(NULL);
476 save = TRUE;
477 }
478 break;
479
480 case MASH_PROMPT: if (last_fase != mash_fase) {
481 prompt(111 + brew->mashstep, NULL); /* "AUTO --> [mashname] " */
482 prompt(219, NULL); /* " Mash added? " */
483 prompt(300, NULL);
484 prompt(407, NULL); /* "--- --- No Yes " */
485 last_fase = mash_fase;
486 }
487 if (set_MLT_pump(unit, 0)) /* Off during mash add */
488 syslog(LOG_NOTICE, "AUTO: mash turn %s MLT pump", mlt_pump_state ? "on":"off");
489 slcdDummy(slcdHandle);
490 key = keycheck();
491 if (key == KEY_ENTER) {
492 mash_fase = MASH_NA;
493 brew->mashstep++;
494 syslog(LOG_NOTICE, "AUTO: confirmed mash added");
495 }
496 if (key == KEY_RETURN) {
497 syslog(LOG_NOTICE, "AUTO: aborted mash added");
498 brew->brewstep = STEP_CLEANUP;
499 brew->mashstep = 0;
500 }
501 break;
502
503 case MASH_IODINE: if (last_fase != mash_fase) {
504 prompt(118, NULL); /* "AUTO --> Mash Out " */
505 prompt(213, NULL); /* " Iodine test " */
506 timestatus(2, brew->timeout);
507 prompt(410, NULL); /* " Continue: Yes No " */
508 last_fase = mash_fase;
509 }
510 if (set_MLT_pump(unit, unit->pump_onmash))
511 syslog(LOG_NOTICE, "AUTO: mash turn %s MLT pump", mlt_pump_state ? "on":"off");
512 if (oldsec != seconds) {
513 brew->timeout--;
514 timestatus(2, brew->timeout);
515 if (brew->timeout <= 0) {
516 syslog(LOG_NOTICE, "AUTO: mash IODINE test timeout");
517 mash_fase = MASH_NA;
518 brew->mashstep++;
519 }
520 oldsec = seconds;
521 }
522 slcdDummy(slcdHandle);
523 key = keycheck();
524 if (key == KEY_RETURN) {
525 syslog(LOG_NOTICE, "AUTO: mash IODINE test confirmed");
526 mash_fase = MASH_NA;
527 brew->mashstep++;
528 }
529 if (key == KEY_ENTER) {
530 syslog(LOG_NOTICE, "AUTO: mash IODINE test declined");
531 mash_fase = MASH_REST;
532 brew->timeout = 600; /* Add 10 more minutes */
533 }
534 break;
535
536 case MASH_HEATING: if (last_fase != mash_fase) {
537 prompt(111 + brew->mashstep, NULL); /* "AUTO --> [mashname] " */
538 prompt(200, NULL);
539 prompt(300, NULL);
540 prompt(418, NULL); /* "--- --- Pause --- " */
541 hlt_status(1);
542 mlt_status(1);
543 pump_status(unit->pump_onmash);
544 last_fase = mash_fase;
545 syslog(LOG_NOTICE, "AUTO: mash step %d fase HEATING started", brew->mashstep);
546 }
547 if (set_MLT_pump(unit, unit->pump_onmash))
548 syslog(LOG_NOTICE, "AUTO: mash turn %s MLT pump", mlt_pump_state ? "on":"off");
549 if (oldsec != seconds) {
550 tempstatus();
551 percstatus((seconds / 2) % 4);
552 oldsec = seconds;
553 }
554 if (mltInput > mltSetpoint) {
555 syslog(LOG_NOTICE, "AUTO: mash step %d fase HEATING reached %6.2f", brew->mashstep, mltSetpoint);
556 mash_fase = MASH_REST;
557 }
558 break;
559
560 case MASH_REST: if (last_fase != mash_fase) {
561 prompt(111 + brew->mashstep, NULL); /* "AUTO --> [mashname] " */
562 prompt(200, NULL);
563 prompt(300, NULL);
564 prompt(418, NULL); /* "--- --- Pause --- " */
565 hlt_status(1);
566 mlt_status(1);
567 pump_status(unit->pump_onmash);
568 last_fase = mash_fase;
569 }
570 if (oldsec != seconds) {
571 tempstatus();
572 timestatus(2, brew->timeout);
573 if (brew->mashstep == 7) {
574 /*
575 * During mash-out rest, allow the grain to sink
576 */
577 if (set_MLT_pump(unit, unit->pump_mashout))
578 syslog(LOG_NOTICE, "AUTO: mash-out turn %s MLT pump", mlt_pump_state ? "on":"off");
579 } else {
580 if (set_MLT_pump(unit, unit->pump_onmash))
581 syslog(LOG_NOTICE, "AUTO: mash turn %s MLT pump", mlt_pump_state ? "on":"off");
582 }
583 brew->timeout--;
584 if (brew->timeout <= 0) {
585 syslog(LOG_NOTICE, "AUTO: mash step %d fase REST done", brew->mashstep);
586 if ((brew->mashstep == 0) && ! unit->skip_add) {
587 mash_fase = MASH_PROMPT;
588 } else if ((brew->mashstep == 6) && ! unit->skip_iodine) {
589 mash_fase = MASH_IODINE;
590 brew->timeout = unit->iodine_time * 60;
591 } else if (brew->mashstep == 7) {
592 mash_fase = MASH_DONE;
593 } else {
594 mash_fase = MASH_NA;
595 brew->mashstep++;
596 }
597 }
598 oldsec = seconds;
599 }
600 break;
601
602 case MASH_DONE: syslog(LOG_NOTICE, "AUTO: mash step %d fase DONE", brew->mashstep);
603 if (set_MLT_heater(unit, 1, recipe->mash[7].setpoint))
604 syslog(LOG_NOTICE, "AUTO: mash done turn MLT off");
605 if (set_MLT_pump(unit, 0))
606 syslog(LOG_NOTICE, "AUTO: mash done turn %s MLT pump", mlt_pump_state ? "on":"off");
607 brew->mashstep = 0;
608 brew->brewstep = STEP_MASHREMOVE;
609 break;
610 }
611 break;
612
613 case STEP_MASHREMOVE: if (unit->skip_remove) {
614 syslog(LOG_NOTICE, "AUTO: skipping Mash remove");
615 brew->brewstep = STEP_PREBOIL;
616 } else {
617 if (brew->brewstep != last_step) {
618 prompt(118, NULL); /* "AUTO --> Mash Out " */
619 prompt(220, NULL); /* " Mash Removed? " */
620 prompt(300, NULL);
621 prompt(407, NULL); /* "--- --- No Yes " */
622 last_step = brew->brewstep;
623 }
624 slcdDummy(slcdHandle);
625 key = keycheck();
626 if (key == KEY_ENTER) {
627 syslog(LOG_NOTICE, "AUTO: Confirmed Mash removed");
628 brew->brewstep = STEP_PREBOIL;
629 }
630 }
631 break;
632
633 case STEP_PREBOIL: if (brew->brewstep != last_step) {
634 prompt(119, NULL); /* "AUTO --> Boil " */
635 prompt(200, NULL);
636 prompt(300, NULL);
637 tempstatus();
638 mltstatus();
639 prompt(418, NULL); /* "--- --- Pause --- " */
640 hlt_status(0);
641 mlt_status(1);
642 pump_status(unit->pump_onboil && (mltInput < unit->pump_stop));
643 last_step = brew->brewstep;
644 }
645 if (oldsec != seconds) {
646 tempstatus();
647 mltstatus();
648 oldsec = seconds;
649 }
650 if (set_HLT_heater(unit, 0, 10.0))
651 syslog(LOG_NOTICE, "AUTO: preboil turn off HLT");
652 if (set_MLT_heater(unit, 1, 100.1))
653 syslog(LOG_NOTICE, "AUTO: preboil turn on MLT to boil");
654 if (set_MLT_pump(unit, unit->pump_onboil && (mltInput < unit->pump_stop)))
655 syslog(LOG_NOTICE, "AUTO: preboil turn %s MLT pump", mlt_pump_state ? "on":"off");
656 if (mltInput > 99.2) {
657 syslog(LOG_NOTICE, "AUTO: reached boil temperature %.2f, start %d minutes boil", mltInput, recipe->boiltime);
658 brew->brewstep = STEP_BOILING;
659 brew->boiltimer = recipe->boiltime * 60;
660 }
661 break;
662
663 case STEP_BOILING: if (set_HLT_heater(unit, 0, 10.0))
664 syslog(LOG_NOTICE, "AUTO: boil turn off HLT");
665 if (set_MLT_heater(unit, 1, 100.1))
666 syslog(LOG_NOTICE, "AUTO: boil turn on MLT to boil");
667 if (set_MLT_pump(unit, unit->pump_onboil && (mltInput < unit->pump_stop)))
668 syslog(LOG_NOTICE, "AUTO: boil turn %s MLT pump", mlt_pump_state ? "on":"off");
669 if (brew->brewstep != last_step) {
670 prompt(119, NULL); /* "AUTO --> Boil " */
671 prompt(200, NULL);
672 prompt(300, NULL);
673 tempstatus();
674 mltstatus();
675 prompt(418, NULL); /* "--- --- Pause --- " */
676 hlt_status(0);
677 mlt_status(1);
678 pump_status(unit->pump_onboil && (mltInput < unit->pump_stop));
679 last_step = brew->brewstep;
680 }
681 if (oldsec != seconds) {
682 tempstatus();
683 if ((seconds / 2) % 4) {
684 timestatus(2, brew->boiltimer);
685 } else {
686 mltstatus();
687 }
688 if (brew->boiltimer >= 0) {
689 brew->boiltimer--;
690 } else {
691 brew->brewstep = STEP_BOILDONE;
692 syslog(LOG_NOTICE, "AUTO: boil is done");
693 }
694 oldsec = seconds;
695 }
696 break;
697
698 case STEP_BOILDONE: if (set_MLT_heater(unit, 0, 10.0))
699 syslog(LOG_NOTICE, "AUTO: after boil turn off MLT heater");
700 if (set_MLT_pump(unit, 0))
701 syslog(LOG_NOTICE, "AUTO: after boil turn %s MLT pump", mlt_pump_state ? "on":"off");
702 if (brew->brewstep != last_step) {
703 prompt(120, NULL); /* "AUTO --> Cooling " */
704 prompt(214, NULL); /* " START COOLING " */
705 prompt(300, NULL);
706 prompt(410, NULL); /* " Continue: Yes No " */
707 last_step = brew->brewstep;
708 }
709 slcdDummy(slcdHandle);
710 key = keycheck();
711 if (key == KEY_ENTER) {
712 brew->brewstep = STEP_CLEANUP;
713 syslog(LOG_NOTICE, "AUTO: user skipped cooling");
714 }
715 if (key == KEY_RETURN) {
716 brew->brewstep = STEP_COOLING;
717 syslog(LOG_NOTICE, "AUTO: user started cooling");
718 }
719 break;
720
721 case STEP_COOLING: for (i = 0; i < 3; i++) {
722 if ((recipe->hopstand[i].skip == 0) && (mltInput <= recipe->hopstand[i].max) && (mltInput >= recipe->hopstand[i].min)) {
723 brew->brewstep = STEP_HOPSTAND;
724 hopstand = i;
725 syslog(LOG_NOTICE, "AUTO: starting hopstand %d", i+1);
726 }
727 }
728 if (brew->brewstep == STEP_HOPSTAND)
729 break;
730 // hot whirlpool start at 85 degrees
731 // cold whirlpool start at 30 degrees
732
733 if (brew->brewstep != last_step) {
734 prompt(120, NULL); /* "AUTO --> Cooling " */
735 prompt(200, NULL);
736 tempstatus();
737 prompt(300, NULL);
738 prompt(418, NULL); /* "--- --- Pause --- " */
739 hlt_status(0);
740 mlt_status(0);
741 pump_status(0);
742 last_step = brew->brewstep;
743 }
744 if (oldsec != seconds) {
745 tempstatus();
746 oldsec = seconds;
747 }
748 if (mltInput <= recipe->coolto) {
749 syslog(LOG_NOTICE, "AUTO: cool temperture %.2f reached", recipe->coolto);
750 brew->brewstep = STEP_CLEANUP;
751 }
752 break;
753
754 case STEP_HOPSTAND: if (brew->brewstep != last_step) {
755 prompt(122 + hopstand, NULL); /* "AUTO --> Hopstand n " */
756 tempstatus();
757 timestatus(2, brew->timeout);
758 prompt(418, NULL); /* "--- --- Pause --- " */
759 brew->boiltimer = recipe->hopstand[hopstand].duration * 60;
760 last_step = brew->brewstep;
761 }
762
763 if (recipe->hopstand[hopstand].hold) {
764 if (set_MLT_heater(unit, 1, recipe->hopstand[hopstand].setpoint))
765 syslog(LOG_NOTICE, "AUTO: hopstand 1 turn on MLT at %6.2f", mltSetpoint);
766 }
767 if (set_MLT_pump(unit, 1))
768 syslog(LOG_NOTICE, "AUTO: hopstand 1 turn %s MLT pump", mlt_pump_state ? "on":"off");
769 if (oldsec != seconds) {
770 tempstatus();
771 timestatus(2, brew->timeout);
772 brew->boiltimer--;
773 if (brew->boiltimer <= 0) {
774 syslog(LOG_NOTICE, "AUTO: hopstand %d done", hopstand+1);
775 if (set_MLT_heater(unit, 0, 10.0))
776 syslog(LOG_NOTICE, "AUTO: hopstand 1 turn off MLT at %6.2f", mltSetpoint);
777 brew->brewstep = STEP_COOLING;
778 }
779 oldsec = seconds;
780 }
781 break;
782
783 case STEP_WHIRLPOOL: prompt(121, NULL); /* "AUTO --> Whirlpool " */
784 break;
785
786 case STEP_CLEANUP: if (brew->brewstep != last_step) {
787 prompt(101, NULL); /* " Brewco x.x.x " */
788 prompt(200, NULL); /* " " */
789 prompt(300, NULL); /* " " */
790 prompt(400, NULL); /* " " */
791 last_step = brew->brewstep;
792 }
793 syslog(LOG_NOTICE, "AUTO: cleanup");
794 if (set_HLT_heater(unit, 0, 10.0))
795 syslog(LOG_NOTICE, "AUTO: cleanup turn on HLT at %6.2f", hltSetpoint);
796 if (set_MLT_heater(unit, 0, 10.0))
797 syslog(LOG_NOTICE, "AUTO: cleanup turn on MLT at %6.2f", mltSetpoint);
798 if (set_MLT_pump(unit, 0))
799 syslog(LOG_NOTICE, "AUTO: cleanup turn %s MLT pump", mlt_pump_state ? "on":"off");
800 brew->brewstep = STEP_BREWDONE;
801 break;
802
803 case STEP_BREWDONE: if (brew->brewstep != last_step) {
804 prompt(101, NULL); /* " Brewco x.x.x " */
805 prompt(200, NULL); /* " " */
806 prompt(301, NULL); /* " Finished " */
807 prompt(408, NULL); /* "--- --- Ok --- " */
808 last_step = brew->brewstep;
809 }
810 syslog(LOG_NOTICE, "AUTO: brew done");
811 brew->brewstep = -1;
812 brew->endtime = time(NULL);
813 save = TRUE;
814 do {
815 key = keywait();
816 } while (key != KEY_RETURN);
817 /*
818 * Rewrite the display
819 */
820 prompt(101, NULL); /* " Brewco x.x.x " */
821 tempstatus();
822 prompt(300, NULL); /* " " */
823 prompt(401, NULL); /* "--- MAN AUTO SETUP" */
824 break;
825 }
826
827 if (save) {
828 snprintf(data, 127, "%d,%.2f,%.2f,%.2f,%.2f,%.2f,%.2f", brew->brewstep,
829 hltInput, hltOutput, hltSetpoint, mltInput, mltOutput, mltSetpoint);
830 logger(brew->name, data);
831 wrsession(brew);
832 }
833 }
834
835
836
837 void manual_prompt(void)
838 {
839 switch (manual) {
840 case MANUAL_SELHLT: prompt(104, NULL); /* " MANUAL MODE " */
841 prompt(303, NULL); /* " Manual HLT " */
842 prompt(402, NULL); /* "--- dwn quit ok " */
843 break;
844 case MANUAL_SELMLT: prompt(104, NULL); /* " MANUAL MODE " */
845 prompt(304, NULL); /* " Manual MLT " */
846 prompt(404, NULL); /* " up --- quit ok " */
847 break;
848 case MANUAL_HLT: prompt(104, NULL); /* " MANUAL MODE " */
849 prompt(300, NULL); /* " " */
850 prompt(413, NULL); /* "UP* *DWN heat --- " */
851 break;
852 case MANUAL_MLT: prompt(104, NULL); /* " MANUAL MODE " */
853 prompt(300, NULL); /* " " */
854 prompt(406, NULL); /* "UP* *DWN heat pmp " */
855 break;
856 }
857 }
858
859
860
861 /*
862 * Manual menu for testing your equipment.
863 */
864 int manual_menu(units_list *, int);
865 int manual_menu(units_list *unit, int seconds)
866 {
867 int key;
868
869 switch (manual) {
870 case MANUAL_SELHLT: slcdDummy(slcdHandle);
871 key = keycheck();
872 if (key == KEY_DOWN) {
873 manual = MANUAL_SELMLT;
874 manual_prompt();
875 }
876 if (key == KEY_RETURN) {
877 manual = MANUAL_NONE;
878 mlt_pump_state = 0;
879 PID_setMode(unit->PID_mlt, P_MANUAL);
880 PID_setMode(unit->PID_hlt, P_MANUAL);
881 hlt_status(0);
882 mlt_status(0);
883 device_out(unit->mlt_pump.uuid, mlt_pump_state);
884 }
885 if (key == KEY_ENTER) {
886 // TODO: prompt for water
887 manual = MANUAL_HLT;
888 manual_prompt();
889 }
890 break;
891 case MANUAL_SELMLT: slcdDummy(slcdHandle);
892 key = keycheck();
893 if (key == KEY_UP) {
894 manual = MANUAL_SELHLT;
895 manual_prompt();
896 }
897 if (key == KEY_RETURN) {
898 manual = MANUAL_NONE;
899 mlt_pump_state = 0;
900 PID_setMode(unit->PID_mlt, P_MANUAL);
901 PID_setMode(unit->PID_hlt, P_MANUAL);
902 hlt_status(0);
903 mlt_status(0);
904 device_out(unit->mlt_pump.uuid, mlt_pump_state);
905 }
906 if (key == KEY_ENTER) {
907 // TODO: prompt for water
908 manual = MANUAL_MLT;
909 manual_prompt();
910 }
911 break;
912 case MANUAL_HLT: tempstatus();
913 percstatus((seconds / 2) % 4);
914
915 slcdDummy(slcdHandle);
916 key = keycheck();
917 if (key == KEY_RETURN) {
918 if (PID_getMode(unit->PID_hlt) == P_MANUAL) {
919 PID_setMode(unit->PID_hlt, P_AUTOMATIC);
920 hlt_status(1);
921 } else {
922 PID_setMode(unit->PID_hlt, P_MANUAL);
923 hlt_status(0);
924 }
925 }
926 if ((key == KEY_DOWN) && (hltSetpoint > 10))
927 hltSetpoint -= 1.0;
928 if ((key == KEY_UP) && (hltSetpoint < 100))
929 hltSetpoint += 1.0;
930 if (key == KEY_ESCAPE) {
931 manual = MANUAL_SELHLT;
932 manual_prompt();
933 }
934 break;
935 case MANUAL_MLT: tempstatus();
936 percstatus((seconds / 2) % 4);
937
938 slcdDummy(slcdHandle);
939 key = keycheck();
940 if (key == KEY_RETURN) {
941 if (PID_getMode(unit->PID_mlt) == P_MANUAL) {
942 PID_setMode(unit->PID_mlt, P_AUTOMATIC);
943 mlt_status(1);
944 } else {
945 PID_setMode(unit->PID_mlt, P_MANUAL);
946 mlt_status(0);
947 }
948 }
949 if ((key == KEY_DOWN) && (mltSetpoint > 10))
950 mltSetpoint -= 1.0;
951 if ((key == KEY_UP) && (mltSetpoint < 100))
952 mltSetpoint += 1.0;
953 if (key == KEY_ENTER) {
954 if (mlt_pump_state)
955 set_MLT_pump(unit, 0);
956 else
957 set_MLT_pump(unit, 1);
958 }
959 if (key == KEY_ESCAPE) {
960 manual = MANUAL_SELMLT;
961 manual_prompt();
962 }
963 device_out(unit->mlt_pump.uuid, mlt_pump_state);
964 break;
965 }
966
967 return 0;
968 }
969
970
971
972 char *choose_recipe(void);
973 char *choose_recipe(void)
974 {
975 int total, i, key, choice = 1;
976 static char uuid[37];
977 a_recipe *recipe;
978 char pmpt[81];
979
980 strcpy(uuid, (char *)"00000000-0000-0000-0000-000000000000");
981 for (;;) {
982 total = 0;
983 for (recipe = recipes; recipe; recipe = recipe->next)
984 total++;
985
986 if (total == 0)
987 return uuid;
988
989 i = 0;
990 for (recipe = recipes; recipe; recipe = recipe->next) {
991 i++;
992 if (i == choice)
993 break;
994 }
995
996 prompt(102, NULL); /* " SETUP MENU " */
997 prompt(221, NULL); /* " Select Recipe " */
998 if (total) {
999 snprintf(pmpt, Config.lcd_cols + 1, "%s %s ", recipe->code, recipe->name);
1000 prompt(300, pmpt);
1001 }
1002 if (total == 1)
1003 prompt(405, NULL); /* "--- --- quit ok " */
1004 else if (choice == 1)
1005 prompt(402, NULL); /* "--- dwn quit ok " */
1006 else if (choice == total)
1007 prompt(404, NULL); /* " up --- quit ok " */
1008 else
1009 prompt(403, NULL); /* " up dwn quit ok " */
1010
1011 key = keywait();
1012 if ((key == KEY_RETURN) || my_shutdown)
1013 return uuid;
1014 if (key == KEY_ENTER) {
1015 strcpy(uuid, recipe->uuid);
1016 return uuid;
1017 }
1018 if ((key == KEY_UP) && (total > 1) && (choice > 1)) {
1019 choice--;
1020 }
1021 if ((key == KEY_DOWN) && (total > 1) && (choice < total))
1022 choice++;
1023 }
1024 }
1025
1026
1027
1028 int server(void);
1029 int server(void)
1030 {
1031 int rc = 0, run = 1, dosave, key, temp, MLTp, HLTp, percslot, percfase = PERC_INIT;
1032 int do_init = TRUE, seconds = 0, minutes = 0;
1033 units_list *unit;
1034 a_recipe *recipe = NULL;
1035 brew_session *brew = NULL;
1036 #ifndef HAVE_WIRINGPI_H
1037 long t = 0;
1038 #endif
1039 time_t now, last = (time_t)0;
1040 long nowmillis, perctimer;
1041 struct tm *tm;
1042
1043 prompt(101, NULL);
1044
1045 /*
1046 * Define special characters in the display CGRAM
1047 */
1048 if (Config.tempFormat == 'C')
1049 slcdCharDef(slcdHandle, 1, degC);
1050 else
1051 slcdCharDef(slcdHandle, 1, degF);
1052 slcdCharDef(slcdHandle, 2, SP_Symbol);
1053 slcdCharDef(slcdHandle, 3, PumpONOFF);
1054 slcdCharDef(slcdHandle, 4, RevPumpONOFF);
1055 slcdCharDef(slcdHandle, 5, HeatONOFF);
1056 slcdCharDef(slcdHandle, 6, RevHeatONOFF);
1057 slcdCharDef(slcdHandle, 7, Language);
1058
1059 if (lockprog((char *)"brewco")) {
1060 syslog(LOG_NOTICE, "Can't lock");
1061 return 1;
1062 }
1063
1064 if (debug)
1065 fprintf(stdout, "Begin server()\n");
1066
1067 if ((rc = devices_detect())) {
1068 syslog(LOG_NOTICE, "Detected %d new devices", rc);
1069 if (debug)
1070 fprintf(stdout, "Detected %d new devices\n", rc);
1071 wrconfig();
1072 }
1073
1074 #ifdef HAVE_WIRINGPI_H
1075 rc = piThreadCreate(my_devices_loop);
1076 #else
1077 rc = pthread_create(&threads[t], NULL, my_devices_loop, (void *)t );
1078 #endif
1079 if (rc) {
1080 fprintf(stderr, "my_devices_loop thread didn't start rc=%d\n", rc);
1081 syslog(LOG_NOTICE, "my_devices_loop thread didn't start rc=%d", rc);
1082 #ifndef HAVE_WIRINGPI_H
1083 } else {
1084 t++;
1085 #endif
1086 }
1087
1088 #ifdef HAVE_WIRINGPI_H
1089 rc = piThreadCreate(my_keyboard_loop);
1090 #else
1091 rc = pthread_create(&threads[t], NULL, my_keyboard_loop, (void *)t );
1092 #endif
1093 if (rc) {
1094 fprintf(stderr, "my_keyboard_loop thread didn't start rc=%d\n", rc);
1095 syslog(LOG_NOTICE, "my_keyboard_loop thread didn't start rc=%d", rc);
1096 #ifndef HAVE_WIRINGPI_H
1097 } else {
1098 t++;
1099 #endif
1100 }
1101
1102 #ifdef USE_SIMULATOR
1103 #ifdef HAVE_WIRINGPI_H
1104 rc = piThreadCreate(my_simulator_loop);
1105 #else
1106 rc = pthread_create(&threads[t], NULL, my_simulator_loop, (void *)t );
1107 #endif
1108 if (rc) {
1109 fprintf(stderr, "my_simulator_loop thread didn't start rc=%d\n", rc);
1110 syslog(LOG_NOTICE, "my_simulator_loop thread didn't start rc=%d", rc);
1111 #ifndef HAVE_WIRINGPI_H
1112 } else {
1113 t++;
1114 #endif
1115 }
1116 #endif
1117
1118 if (! Config.units) {
1119 /*
1120 * No brewsystems defined, add the first
1121 */
1122 prompt(218, NULL); /* Add Brewsystem? */
1123 prompt(407, NULL); /* --- --- Ok --- */
1124
1125 do {
1126 key = keywait();
1127 } while (key != KEY_RETURN);
1128
1129 if (key == KEY_RETURN) {
1130 addUnit(1);
1131 }
1132 }
1133
1134 /*
1135 * Initialize units for processing
1136 */
1137 for (unit = Config.units; unit; unit = unit->next) {
1138 if (unit->active)
1139 break;
1140 }
1141
1142 if (! unit->active) {
1143 fprintf(stdout, "No active units found\n");
1144 }
1145
1146 /*
1147 * Safety, turn everything off
1148 */
1149 if (unit->active) {
1150 if (debug)
1151 fprintf(stdout, "Starting brewsystem %d `%s'\n", unit->number, unit->name);
1152 syslog(LOG_NOTICE, "Starting brewsystem %d `%s'", unit->number, unit->name);
1153 }
1154
1155 /*
1156 * During automation there will be a state file:
1157 * ~/.brewco/var/brewing.xml
1158 * If this file is present, there has been a crash.
1159 */
1160 brew = (brew_session *)malloc(sizeof(brew_session));
1161 rc = rdsession(brew);
1162 syslog(LOG_NOTICE, "rdsession: rc=%d", rc);
1163 if (debug)
1164 fprintf(stdout, "rdsession: rc=%d\n", rc);
1165
1166 char *mypath = xstrcpy(etcpath);
1167 mypath = xstrcat(mypath, (char *)"brewing.xml");
1168 if (rc == 0) {
1169 if (debug)
1170 fprintf(stdout, "Active brew session found\n");
1171 } else if (rc == -1) {
1172 free(brew);
1173 brew = NULL;
1174 if (debug)
1175 fprintf(stdout, "No active brew session found\n");
1176 } else {
1177 unlink(mypath);
1178 free(brew);
1179 brew = NULL;
1180 if (debug)
1181 fprintf(stdout, "Error brew session found\n");
1182 }
1183 free(mypath);
1184 mypath = NULL;
1185
1186 do {
1187 if (my_shutdown) {
1188 run = 0;
1189 unit->hlt_heater.value = 0;
1190 unit->mlt_heater.value = 0;
1191 unit->mlt_pump.value = 0;
1192 device_out(unit->hlt_heater.uuid, 0);
1193 device_out(unit->mlt_heater.uuid, 0);
1194 device_out(unit->mlt_pump.uuid, 0);
1195 hlt_status(0);
1196 mlt_status(0);
1197 break;
1198 }
1199
1200 /*
1201 * Do we need to initialize this unit?
1202 */
1203 if (do_init) {
1204 if (debug)
1205 fprintf(stdout, "Initialize brewsystem %d `%s'\n", unit->number, unit->name);
1206 syslog(LOG_NOTICE, "Initialize brewsystem %d `%s'", unit->number, unit->name);
1207
1208 prompt(0, NULL);
1209 prompt(101, NULL); /* " Brewco x.x.x " */
1210 prompt(401, NULL); /* "--- MAN AUTO SETUP" */
1211
1212 /*
1213 * Turn everything off
1214 */
1215 unit->hlt_heater.value = 0;
1216 unit->mlt_heater.value = 0;
1217 unit->mlt_pump.value = 0;
1218 device_out(unit->hlt_heater.uuid, 0);
1219 device_out(unit->mlt_heater.uuid, 0);
1220 device_out(unit->mlt_pump.uuid, 0);
1221
1222 /*
1223 * Initialize PID's
1224 */
1225 hltInput = hltSetpoint = mltInput = mltSetpoint = 20.0;
1226 hltOutput = mltOutput = 0;
1227 PID_init(unit->PID_hlt, &hltInput, &hltOutput, &hltSetpoint, unit->PID_hlt->dispKp, unit->PID_hlt->dispKi, unit->PID_hlt->dispKd, unit->PID_hlt->Direction);
1228 PID_setOutputLimits(unit->PID_hlt, 0, 100);
1229 PID_setSampleTime(unit->PID_hlt, unit->PID_hlt->SampleTime);
1230 PID_init(unit->PID_mlt, &mltInput, &mltOutput, &mltSetpoint, unit->PID_mlt->dispKp, unit->PID_mlt->dispKi, unit->PID_mlt->dispKd, unit->PID_mlt->Direction);
1231 PID_setOutputLimits(unit->PID_mlt, 0, 100);
1232 PID_setSampleTime(unit->PID_mlt, unit->PID_mlt->SampleTime);
1233 hlt_status(0);
1234 mlt_status(0);
1235
1236 manual = MANUAL_NONE;
1237
1238 do_init = FALSE;
1239 nowmillis = perctimer = millis();
1240 percslot = 0;
1241 percfase = PERC_INIT;
1242 dosave = 0;
1243
1244 if (brew) {
1245 /*
1246 * Restore session
1247 */
1248 if (debug)
1249 fprintf(stdout, "loop_init: restoring brew session\n");
1250 for (recipe = recipes; recipe; recipe = recipe->next) {
1251 if (strcmp(recipe->uuid, brew->uuid_recipe) == 0) {
1252 break;
1253 }
1254 }
1255 if (debug)
1256 fprintf(stdout, "loop_init: brewstep=%d mashstep=%d recipe=%s\n", brew->brewstep, brew->mashstep, recipe->code);
1257 }
1258 }
1259
1260 /* run_pause code here */
1261
1262 /*
1263 * Update PID's, even if they are off. Both PID's will do
1264 * the scheduling by themselves.
1265 */
1266 rc = PID_compute(unit->PID_hlt);
1267 rc = PID_compute(unit->PID_mlt);
1268
1269 /*
1270 * This is the serial heaters schedule loop. The total
1271 * loop time is 5 seconds, heating is 0..100% so we
1272 * have 5 mSeconds timeslots. The MLT has priority over
1273 * the HLT heater, so the HLT heater can get too little
1274 * power. TODO: simultaneous use must be implemented.
1275 * In sequentiel mode, the total drawn power is the same
1276 * as the power used by the largest heater element.
1277 */
1278 nowmillis = millis();
1279 if (nowmillis > (perctimer + 50)) {
1280 percslot++;
1281 if (percfase == PERC_INIT) {
1282 HLTp = (int)hltOutput;
1283 MLTp = (int)mltOutput;
1284 if ((MLTp + HLTp) > 100)
1285 HLTp = 100 - MLTp; /* The HLT has lower priority */
1286 if (MLTp) {
1287 percfase = PERC_MLT;
1288 device_out(unit->hlt_heater.uuid, 0);
1289 device_out(unit->mlt_heater.uuid, 1);
1290 } else if (HLTp) {
1291 percfase = PERC_HLT;
1292 device_out(unit->hlt_heater.uuid, 1);
1293 device_out(unit->mlt_heater.uuid, 0);
1294 } else {
1295 percfase = PERC_REST;
1296 device_out(unit->hlt_heater.uuid, 0);
1297 device_out(unit->mlt_heater.uuid, 0);
1298 }
1299 // if (debug)
1300 // fprintf(stdout, " perslot=%d MLT=%d%% HLT=%d%% fase=%d\n", percslot, MLTp, HLTp, percfase);
1301 } else if (percfase == PERC_MLT) {
1302 if (percslot > MLTp) {
1303 device_out(unit->mlt_heater.uuid, 0);
1304 if (HLTp) {
1305 device_out(unit->hlt_heater.uuid, 1);
1306 percfase = PERC_HLT;
1307 } else {
1308 percfase = PERC_REST;
1309 }
1310 }
1311 } else if (percfase == PERC_HLT) {
1312 if (percslot > (MLTp + HLTp)) {
1313 device_out(unit->hlt_heater.uuid, 0);
1314 percfase = PERC_REST;
1315 }
1316 }
1317 if (percslot == 100) { /* End of the loop, start over */
1318 percslot = 0;
1319 percfase = PERC_INIT;
1320 }
1321 perctimer = nowmillis;
1322 }
1323
1324 now = time(NULL);
1325 if (now != last) {
1326 /*
1327 * Each second
1328 */
1329 last = now;
1330 seconds++;
1331
1332 if (seconds > 59) {
1333 seconds = 0;
1334 minutes++;
1335 dosave = 1;
1336 }
1337
1338 rc = device_in(unit->hlt_sensor.uuid, &temp);
1339 if (rc == DEVPRESENT_YES) {
1340 hltInput = temp / 1000.0;
1341 unit->hlt_sensor.state = 0;
1342 } else if (rc == DEVPRESENT_ERROR) {
1343 unit->hlt_sensor.state = 1;
1344 } else {
1345 unit->hlt_sensor.state = 2;
1346 }
1347 rc = device_in(unit->mlt_sensor.uuid, &temp);
1348 if (rc == DEVPRESENT_YES) {
1349 mltInput = temp / 1000.0;
1350 unit->mlt_sensor.state = 0;
1351 } else if (rc == DEVPRESENT_ERROR) {
1352 unit->mlt_sensor.state = 1;
1353 } else {
1354 unit->mlt_sensor.state = 2;
1355 }
1356
1357 if (debug && ((seconds % 10) == 1)) {
1358 fprintf(stdout, "MLT: In=%.2lf Out=%.2lf Set=%.2lf Stat=%d HLT: In=%.2lf Out=%.2lf Set=%.2lf Stat=%d\n",
1359 mltInput, mltOutput, mltSetpoint, PID_getMode(unit->PID_mlt),
1360 hltInput, hltOutput, hltSetpoint, PID_getMode(unit->PID_hlt));
1361 }
1362
1363 }
1364
1365 if (brew) {
1366 /*
1367 * Automate mode
1368 */
1369 automatic_brew(unit, brew, recipe, dosave, seconds);
1370 dosave = 0;
1371 if (brew->brewstep == -1) {
1372 /*
1373 * Save session and move it
1374 */
1375 wrsession(brew);
1376 char *fpath, *tpath;
1377 fpath = xstrcpy(etcpath);
1378 fpath = xstrcat(fpath, (char *)"brewing.xml");
1379 tpath = xstrcpy(varpath);
1380 tpath = xstrcat(tpath, (char *)"old/brewing.xml.");
1381 mkdirs(tpath, 0755);
1382 tpath = xstrcat(tpath, brew->name);
1383 if (debug)
1384 fprintf(stdout, "auto: saving as %s\n", tpath);
1385 file_cp(fpath, tpath);
1386 unlink(fpath);
1387 free(fpath);
1388 free(tpath);
1389
1390 /*
1391 * Free memory, session is over.
1392 */
1393 if (brew->name)
1394 free(brew->name);
1395 if (brew->uuid_recipe)
1396 free(brew->uuid_recipe);
1397 if (brew->uuid_unit)
1398 free(brew->uuid_unit);
1399 free(brew);
1400 brew = NULL;
1401 }
1402
1403 } else if (manual != MANUAL_NONE) {
1404 /*
1405 * Manual mode
1406 */
1407 manual_menu(unit, seconds);
1408 if (manual == MANUAL_NONE) {
1409 /*
1410 * Rewrite the display
1411 */
1412 prompt(101, NULL); /* " Brewco x.x.x " */
1413 prompt(300, NULL); /* " " */
1414 prompt(401, NULL); /* "--- MAN AUTO SETUP" */
1415 }
1416 } else {
1417 /*
1418 * Not running.
1419 */
1420 tempstatus();
1421 key = keycheck();
1422 if (key == KEY_ENTER) {
1423 setup();
1424 prompt(101, NULL); /* " Brewco x.x.x " */
1425 prompt(200, NULL);
1426 prompt(300, NULL);
1427 hlt_status(0);
1428 mlt_status(0);
1429 pump_status(0);
1430 prompt(401, NULL); /* "--- MAN AUTO SETUP" */
1431 } else if (key == KEY_RETURN && ! brew) {
1432 int i, isOk = TRUE;
1433 char message[41];
1434
1435 tm = localtime(&now);
1436 snprintf(message, 40, "%04d%02d%02d-%02d%02d", tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min);
1437 brew = (brew_session *)malloc(sizeof(brew_session));
1438 brew->uuid_recipe = xstrcpy(choose_recipe());
1439 brew->uuid_unit = xstrcpy(unit->uuid);
1440 brew->name = xstrcpy(message);
1441 brew->brewstep = STEP_NA;
1442 brew->mashstep = MASH_NA;
1443 brew->timeout = brew->boiltimer = 0;
1444 brew->starttime = brew->endtime = (time_t)0;
1445 /*
1446 * Now check if everything is sane
1447 */
1448 if (strcmp(brew->uuid_recipe, (char *)"00000000-0000-0000-0000-000000000000") == 0) {
1449 isOk = FALSE;
1450 snprintf(message, Config.lcd_cols + 1, " No recipe selected ");
1451 if (debug)
1452 fprintf(stdout, "brew init: No recipe selected\n");
1453 }
1454 if (isOk) {
1455 isOk = FALSE;
1456 for (recipe = recipes; recipe; recipe = recipe->next) {
1457 if (strcmp(recipe->uuid, brew->uuid_recipe) == 0) {
1458 isOk = TRUE;
1459 if (! recipe->boiltime)
1460 isOk = FALSE;
1461 for (i = 0; i < 8; i++) {
1462 if (! recipe->mash[i].skip && ! recipe->mash[i].setpoint)
1463 isOk = FALSE;
1464 }
1465 for (i = 0; i < 3; i++) {
1466 if (! recipe->hopstand[i].skip && recipe->hopstand[i].hold && (recipe->hopstand[i].setpoint == 0))
1467 isOk = FALSE;
1468 }
1469 break;
1470 }
1471 }
1472 if (isOk == FALSE) {
1473 snprintf(message, Config.lcd_cols + 1, " Recipe error ");
1474 if (debug)
1475 fprintf(stdout, "brew init: recipe error\n");
1476 }
1477 }
1478
1479 if (debug)
1480 fprintf(stdout, "init brew: %s\n", isOk ? (char *)"Ok":(char *)"Error");
1481
1482 if (isOk) {
1483 wrsession(brew);
1484 } else {
1485 prompt(300, message);
1486 prompt(408, NULL); /* "--- --- Ok --- " */
1487 key = keywait();
1488 if (brew->name)
1489 free(brew->name);
1490 if (brew->uuid_recipe)
1491 free(brew->uuid_recipe);
1492 if (brew->uuid_unit)
1493 free(brew->uuid_unit);
1494 free(brew);
1495 brew = NULL;
1496 /*
1497 * Rewrite the display
1498 */
1499 prompt(101, NULL); /* " Brewco x.x.x " */
1500 prompt(300, NULL); /* " " */
1501 prompt(401, NULL); /* "--- MAN AUTO SETUP" */
1502 }
1503
1504 } else if (key == KEY_DOWN) {
1505 manual = MANUAL_SELHLT;
1506 manual_prompt();
1507 }
1508 }
1509
1510 usleep(5000); /* 5 mSec */
1511
1512 } while (run);
1513
1514 syslog(LOG_NOTICE, "Out of loop");
1515 if (debug)
1516 fprintf(stdout, (char *)"Out of loop\n");
1517
1518 prompt(0, NULL);
1519 prompt(101, NULL); /* " Brewco x.x.x " */
1520 prompt(302, NULL); /* " Shutting down " */
1521
1522 /*
1523 * Give threads time to cleanup
1524 */
1525 usleep(1500000);
1526 prompt(0, NULL);
1527 setBacklight(0);
1528
1529 if (sock != -1) {
1530 if (shutdown(sock, SHUT_RDWR)) {
1531 syslog(LOG_NOTICE, "Can't shutdown socket: %s", strerror(errno));
1532 }
1533 sock = -1;
1534 }
1535
1536 wrrecipes();
1537 wrconfig();
1538 ulockprog((char *)"brewco");
1539 return 0;
1540 }
1541
1542
1543
1544 int main(int argc, char *argv[])
1545 {
1546 int rc = 0, c, i;
1547 char *homepath = NULL;
1548
1549 while (1) {
1550 int option_index = 0;
1551 static struct option long_options[] = {
1552 {"debug", 0, 0, 'c'},
1553 {"help", 0, 0, 'h'},
1554 {0, 0, 0, 0}
1555 };
1556
1557 c = getopt_long(argc, argv, "dh", long_options, &option_index);
1558 if (c == -1)
1559 break;
1560
1561 switch (c) {
1562 case 'd': debug = TRUE;
1563 break;
1564 case 'h': help();
1565 return 1;
1566 }
1567 }
1568
1569 openlog("brewco", LOG_PID|LOG_CONS|LOG_NOWAIT, LOG_USER);
1570 syslog(LOG_NOTICE, "mbsePi-apps brewco v%s starting", VERSION);
1571 if (debug)
1572 fprintf(stdout, "mbsePi-apps brewco v%s starting\n", VERSION);
1573
1574 /*
1575 * Set home directory path
1576 */
1577 if (getenv((char *)"USER") == NULL) {
1578 homepath = xstrcpy((char *)"/root");
1579 } else {
1580 homepath = xstrcpy(getenv((char *)"HOME"));
1581 }
1582 varpath = xstrcpy(homepath);
1583 varpath = xstrcat(varpath, (char *)"/.brewco/var/");
1584 etcpath = xstrcpy(homepath);
1585 etcpath = xstrcat(etcpath, (char *)"/.brewco/etc/");
1586 mkdirs(varpath, 0755);
1587 mkdirs(etcpath, 0755);
1588
1589 if (rdconfig()) {
1590 fprintf(stderr, "Error reading configuration\n");
1591 syslog(LOG_NOTICE, "Error reading configuration: halted");
1592 return 1;
1593 }
1594 if (debug)
1595 fprintf(stdout, "configuration loaded\n");
1596
1597 if (rdrecipes()) {
1598 fprintf(stderr, "Error reading recipes\n");
1599 syslog(LOG_NOTICE, "Error reading recipes: halted");
1600 return 1;
1601 }
1602 if (debug)
1603 fprintf(stdout, "recipes loaded\n");
1604
1605 /*
1606 * Catch all the signals we can, and ignore the rest. Note that SIGKILL can't be ignored
1607 * but that's live. This daemon should only be stopped by SIGTERM.
1608 * Don't catch SIGCHLD.
1609 */
1610 for (i = 0; i < NSIG; i++) {
1611 if ((i != SIGCHLD) && (i != SIGKILL) && (i != SIGSTOP))
1612 signal(i, (void (*))die);
1613 }
1614
1615 #ifdef HAVE_WIRINGPI_H
1616 if (wiringPiSetup () )
1617 return 1;
1618 #endif
1619
1620 if ((rc = initLCD (Config.lcd_cols, Config.lcd_rows))) {
1621 fprintf(stderr, "Cannot initialize LCD display, rc=%d\n", rc);
1622 return 1;
1623 }
1624
1625 rc = server();
1626
1627 syslog(LOG_NOTICE, "Finished, rc=%d", rc);
1628 if (debug)
1629 fprintf(stdout, "Finished, rc=%d\n", rc);
1630 return rc;
1631 }
1632
1633

mercurial