Sun, 27 Dec 2015 17:52:26 +0100
Renamed Mash-in step to Prepare on the display. Don't run the pump when the mash is added. When preparing the mash, first heat the HLT, and then the MLT so that both have the chance to reach their target temperatures.
/***************************************************************************** * Copyright (C) 2015 * * Michiel Broek <mbroek at mbse dot eu> * * This file is part of the mbsePi-apps * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation; either version 2, or (at your option) any * later version. * * mbsePi-apps is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with ThermFerm; see the file COPYING. If not, write to the Free * Software Foundation, 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. *****************************************************************************/ #include "brewco.h" #include "slcd.h" #include "setup.h" #include "prompt.h" #include "xutil.h" #include "keyboard.h" #include "rdconfig.h" #include "rdrecipes.h" extern int my_shutdown; extern int debug; extern sys_config Config; extern int lcdHandle; extern int slcdHandle; extern a_recipe *recipes; void editFloat(float *value, float low, float high, char *text) { int key; float new = *value; char pmpt[81]; prompt(0, NULL); prompt(133, NULL); for (;;) { snprintf(pmpt, Config.lcd_cols + 1, "%s: %5.1f\001", text, new); prompt(200, pmpt); if (new == low) prompt(404, NULL); else if (new == high) prompt(402, NULL); else prompt(403, NULL); key = keywait(); if ((key == KEY_RETURN) || my_shutdown) return; if (key == KEY_UP) { new = new + 0.5; if (new > high) new = high; } if (key == KEY_DOWN) { new = new - 0.5; if (new < low) new = low; } if (key == KEY_ENTER) { *value = new; return; } } } void editDouble(double *value, double low, double high, char *text) { int key; double new = *value; char pmpt[81]; prompt(0, NULL); prompt(138, NULL); for (;;) { snprintf(pmpt, Config.lcd_cols + 1, "%s: %5.2lf", text, new); prompt(200, pmpt); if (new == low) prompt(404, NULL); else if (new == high) prompt(402, NULL); else prompt(403, NULL); key = keywait(); if ((key == KEY_RETURN) || my_shutdown) return; if (key == KEY_UP) { new = new + 0.5; if (new > high) new = high; } if (key == KEY_DOWN) { new = new - 0.5; if (new < low) new = low; } if (key == KEY_ENTER) { *value = new; return; } } } void editInteger(int *value, int low, int high, int step, char *text, char *text2) { int key, new = *value; char pmpt[81]; prompt(0, NULL); prompt(134, NULL); for (;;) { snprintf(pmpt, Config.lcd_cols + 1, "%s: %3d %s", text, new, text2); prompt(200, pmpt); if (new == low) prompt(404, NULL); else if (new == high) prompt(402, NULL); else prompt(403, NULL); key = keywait(); if ((key == KEY_RETURN) || my_shutdown) return; if (key == KEY_UP) { new += step; if (new > high) new = high; } if (key == KEY_DOWN) { new -= step; if (new < low) new = low; } if (key == KEY_ENTER) { *value = new; return; } } } void togglePmpts(int *value, char *text, int pno, char *pmpt1, char *pmpt2) { int key, new = *value; char pmpt[81]; prompt(0, NULL); prompt(pno, NULL); for (;;) { snprintf(pmpt, Config.lcd_cols + 1, "%s: %s", text, new ? pmpt1:pmpt2); prompt(200, pmpt); if (new) { prompt(404, NULL); } else { prompt(402, NULL); } key = keywait(); if ((key == KEY_RETURN) || my_shutdown) return; if ((key == KEY_UP) && new) new = 0; else if ((key == KEY_DOWN) && (new == 0)) new = 1; if (key == KEY_ENTER) { *value = new; return; } } } void toggleYesNo(int *value, char *text) { togglePmpts(value, text, 132, (char *)"Yes", (char *)"No "); } void toggleDirection(int *value, char *text) { togglePmpts(value, text, 137, (char *)"Reverse", (char *)"Direct "); } void editName(char *name, char *text) { char pmpt[81]; int i, x = 0, key, val; if (debug) fprintf(stdout, "editName(%s)\n", name); prompt(0, NULL); prompt(131, NULL); /* " Change Name " */ prompt(417, NULL); /* " up dwn next ok " */ for (;;) { snprintf(pmpt, Config.lcd_cols + 1, " "); prompt(200, pmpt); #ifdef HAVE_WIRINGPI_H lcdPosition(lcdHandle, x, 1); lcdPutchar(lcdHandle, '*'); #endif slcdPosition(slcdHandle, x, 1); slcdPutchar(slcdHandle, '*'); snprintf(pmpt, Config.lcd_cols + 1, "%s", name); prompt(300, pmpt); key = keywait(); if ((key == KEY_ENTER) || my_shutdown) { if (debug) fprintf(stdout, "End editName(%s)\n", name); for (i = strlen(name) -1; i > 1; i--) { if (name[i] != ' ') break; name[i] = '\0'; } if (debug) fprintf(stdout, "End editName(%s)\n", name); return; } if (key == KEY_RETURN) { if (x < 19) x++; else x = 0; if (debug) fprintf(stdout, "editName: strlen=%d x=%d\n", (int)strlen(name) - 1, x); if (x > ((int)strlen(name) - 1)) { name[x + 1] = '\0'; name[x] = ' '; } } if (key == KEY_UP || key == KEY_DOWN) { val = name[x]; if ((key == KEY_UP) && (val < 126)) val++; if ((key == KEY_DOWN) && (val > 32)) val--; name[x] = val; } } } void editPID(pid_var *pid) { int idx = 1, key, val; char pmpt[81]; double Kp, Ki, Kd; if (debug) fprintf(stdout, "editPID()\n"); for (;;) { prompt(0, NULL); prompt(192, NULL); if (idx == 1) prompt(402, NULL); else if (idx == 5) prompt(404, NULL); else prompt(403, NULL); switch (idx) { case 1: snprintf(pmpt, Config.lcd_cols + 1, "PID Kp: %5.2f", PID_getKp(pid)); break; case 2: snprintf(pmpt, Config.lcd_cols + 1, "PID Ki: %5.2f", PID_getKi(pid)); break; case 3: snprintf(pmpt, Config.lcd_cols + 1, "PID Kd: %5.2f", PID_getKd(pid)); break; case 4: snprintf(pmpt, Config.lcd_cols + 1, "SampleTime: %3d mSec", PID_getSampleTime(pid)); break; case 5: snprintf(pmpt, Config.lcd_cols + 1, "Direction: %s", PID_getDirection(pid) ? (char *)"Reverse" : (char *)"Direct"); break; } prompt(200, pmpt); key = keywait(); if ((key == KEY_RETURN) || my_shutdown) { if (debug) fprintf(stdout, "End editPID\n"); return; } if ((key == KEY_UP) && (idx > 1)) idx--; if ((key == KEY_DOWN) && (idx < 5)) idx++; if (key == KEY_ENTER) { Kp = PID_getKp(pid); Ki = PID_getKi(pid); Kd = PID_getKd(pid); switch(idx) { case 1: editDouble(&Kp, 0.5, 50, (char *)"PID Kp"); PID_setTunings(pid, Kp, Ki, Kd); break; case 2: editDouble(&Ki, 0.5, 50, (char *)"PID Ki"); PID_setTunings(pid, Kp, Ki, Kd); break; case 3: editDouble(&Kd, 0.5, 50, (char *)"PID Kd"); PID_setTunings(pid, Kp, Ki, Kd); break; case 4: val = PID_getSampleTime(pid); editInteger(&val, 50, 500, 10, (char *)"SampleTime", (char *)"mSec"); PID_setSampleTime(pid, val); break; case 5: val = PID_getDirection(pid); toggleDirection(&val, (char *)"Direction"); PID_setDirection(pid, val); break; } } } } void editMash(mash_step *mash) { int key, idx = 1, lo, hi; char pmpt[81]; for (;;) { prompt(0, NULL); prompt(194, NULL); if (mash->canskip) { lo = 1; if (mash->skip) hi = 1; else hi = 3; } else { lo = 2; hi = 3; } if (idx < lo) idx = lo; if (idx > hi) idx = hi; if (debug) fprintf(stdout, "editMash canskip=%d skip=%d lo=%d hi=%d idx=%d\n", mash->canskip, mash->skip, lo, hi, idx); switch (idx) { case 1: snprintf(pmpt, Config.lcd_cols + 1, " Mashstep skip: %s", mash->skip ? (char *)"Yes":(char *)"No"); break; case 2: snprintf(pmpt, Config.lcd_cols + 1, " Setpoint: %5.1f\001", mash->setpoint); break; case 3: snprintf(pmpt, Config.lcd_cols + 1, " Duration: %3d mins", mash->duration); break; } prompt(200, pmpt); if (lo == hi) prompt(405, NULL); /* "--- --- quit ok " */ else if (idx == lo) prompt(402, NULL); /* "--- dwn quit ok " */ else if (idx == hi) prompt(404, NULL); /* " up --- quit ok " */ else prompt(403, NULL); /* " up dwn quit ok " */ key = keywait(); if ((key == KEY_RETURN) || my_shutdown) return; if ((key == KEY_UP) && (idx > lo)) idx--; if ((key == KEY_DOWN) && (idx < hi)) idx++; if (key == KEY_ENTER) { switch(idx) { case 1: toggleYesNo(&mash->skip, (char *)" Mashstep skip"); break; case 2: editFloat(&mash->setpoint, mash->min, mash->max, (char *)" Setpoint"); break; case 3: editInteger(&mash->duration, 1, 120, 1, (char *)" Duration", (char *)"mins"); break; } } } } void editHopaddition(hop_addition *hops, int boiltime) { int key, idx = 1, hi; char pmpt[81]; for (;;) { prompt(0, NULL); prompt(195, NULL); if (hops->skip) hi = 1; else hi = 2; if (idx > hi) idx = hi; if (debug) fprintf(stdout, "editHopaddition skip=%d hi=%d idx=%d\n", hops->skip, hi, idx); switch (idx) { case 1: snprintf(pmpt, Config.lcd_cols + 1, "Addition skip: %s", hops->skip ? (char *)"Yes":(char *)"No"); break; case 2: snprintf(pmpt, Config.lcd_cols + 1, " Boil for: %3d mins", hops->boiltime); break; } prompt(200, pmpt); if (hi == 1) prompt(405, NULL); /* "--- --- quit ok " */ else if (idx == 1) prompt(402, NULL); /* "--- dwn quit ok " */ else if (idx == hi) prompt(404, NULL); /* " up --- quit ok " */ else prompt(403, NULL); /* " up dwn quit ok " */ key = keywait(); if ((key == KEY_RETURN) || my_shutdown) return; if ((key == KEY_UP) && (idx > 1)) idx--; if ((key == KEY_DOWN) && (idx < hi)) idx++; if (key == KEY_ENTER) { switch (idx) { case 1: toggleYesNo(&hops->skip, (char *)"Addition skip"); break; case 2: editInteger(&hops->boiltime, -1, boiltime, 1, (char *)" Boil for", (char *)"mins"); break; } } } } void editHopstand(hop_stand *hops) { int key, idx = 1, hi; char pmpt[81]; for (;;) { prompt(0, NULL); prompt(196, NULL); if (hops->skip) hi = 1; else hi = 4; if (idx < 1) idx = 1; if (idx > hi) idx = hi; if (debug) fprintf(stdout, "editHopstand skip=%d hi=%d idx=%d\n", hops->skip, hi, idx); switch (idx) { case 1: snprintf(pmpt, Config.lcd_cols + 1, "Hopstand skip: %s", hops->skip ? (char *)"Yes":(char *)"No"); break; case 2: snprintf(pmpt, Config.lcd_cols + 1, "Hold temperature: %s", hops->hold ? (char *)"Yes":(char *)"No"); break; case 3: snprintf(pmpt, Config.lcd_cols + 1, " Setpoint: %5.1f\001", hops->setpoint); break; case 4: snprintf(pmpt, Config.lcd_cols + 1, " Duration: %3d mins", hops->duration); break; } prompt(200, pmpt); if (hi == 1) prompt(405, NULL); /* "--- --- quit ok " */ else if (idx == 1) prompt(402, NULL); /* "--- dwn quit ok " */ else if (idx == hi) prompt(404, NULL); /* " up --- quit ok " */ else prompt(403, NULL); /* " up dwn quit ok " */ key = keywait(); if ((key == KEY_RETURN) || my_shutdown) return; if ((key == KEY_UP) && (idx > 1)) idx--; if ((key == KEY_DOWN) && (idx < hi)) idx++; if (key == KEY_ENTER) { switch(idx) { case 1: toggleYesNo(&hops->skip, (char *)"Hopstand skip"); break; case 2: toggleYesNo(&hops->hold, (char *)"Hold temperature"); break; case 3: editFloat(&hops->setpoint, hops->min, hops->max, (char *)" Setpoint"); break; case 4: editInteger(&hops->duration, 10, 240, 10, (char *)" Duration", (char *)"mins"); break; } } } } void editSensor(char *uuid, char *text) { char pmpt[81]; int i, old, choices, idx = 1, key; devices_list *device; if (debug) fprintf(stdout, "editSensor(%s, %s)\n", uuid, text); old = 1; // 1d0e5bb8-7408-48b9-abb4-e9041d7b99fe if ((i = strcmp((char *)"00000000-0000-0000-0000-000000000000", uuid))) { for (device = Config.devices; device; device = device->next) { if (device->direction == DEVDIR_IN_ANALOG) { old++; if (strcmp(device->uuid, uuid) == 0) break; } } } if (debug) fprintf(stdout, "editSensor(%s) old sensor index=%d\n", text, old); for (;;) { prompt(0, NULL); snprintf(pmpt, Config.lcd_cols + 1, "Edit %s", text); prompt(100, pmpt); /* * Count valid sensors */ choices = 1; if (old == 1) { snprintf(pmpt, Config.lcd_cols + 1, "N/A"); } for (device = Config.devices; device; device = device->next) { if (device->direction == DEVDIR_IN_ANALOG) { choices++; if (choices == old) { snprintf(pmpt, Config.lcd_cols + 1, "%s", device->description); } } } prompt(200, pmpt); /* Display current sensor */ i = 1; if (idx == 1) { snprintf(pmpt, Config.lcd_cols + 1, "N/A"); } else { for (device = Config.devices; device; device = device->next) { if (device->direction == DEVDIR_IN_ANALOG) { i++; if (i == idx) { snprintf(pmpt, Config.lcd_cols + 1, "%s", device->description); } } } } prompt(300, pmpt); /* Display possible new sensor */ if (choices == 1) prompt(405, NULL); else if (idx == 1) prompt(402, NULL); else if (idx == choices) prompt(404, NULL); else prompt(403, NULL); key = keywait(); if ((key == KEY_RETURN) || my_shutdown) { if (debug) fprintf(stdout, "End editSensor\n"); return; } if ((key == KEY_UP) && (idx > 1)) idx--; if ((key == KEY_DOWN) && (idx < choices)) idx++; if ((key == KEY_ENTER) && (idx != old)) { /* * Select new sensor. */ if (idx == 1) { /* * Disable the sensor */ strncpy(uuid, (char *)"00000000-0000-0000-0000-000000000000", 36); old = idx; } else { i = 1; for (device = Config.devices; device; device = device->next) { if (device->direction == DEVDIR_IN_ANALOG) { i++; if (i == idx) { strncpy(uuid, device->uuid, 36); break; } } } old = idx; } } } } void editRelay(char *uuid, char *text) { char pmpt[81]; int i, old, choices, idx = 1, key; devices_list *device; if (debug) fprintf(stdout, "editRelay(%s, %s)\n", uuid, text); old = 1; // 1d0e5bb8-7408-48b9-abb4-e9041d7b99fe if ((i = strcmp((char *)"00000000-0000-0000-0000-000000000000", uuid))) { for (device = Config.devices; device; device = device->next) { if (device->direction == DEVDIR_OUT_ANALOG) { old++; if (strcmp(device->uuid, uuid) == 0) break; } } } if (debug) fprintf(stdout, "editRelay(%s) old sensor index=%d\n", text, old); for (;;) { prompt(0, NULL); snprintf(pmpt, Config.lcd_cols + 1, "Edit %s", text); prompt(100, pmpt); /* * Count valid sensors */ choices = 1; if (old == 1) { snprintf(pmpt, Config.lcd_cols + 1, "N/A"); } for (device = Config.devices; device; device = device->next) { if (device->direction == DEVDIR_OUT_ANALOG) { choices++; if (choices == old) { snprintf(pmpt, Config.lcd_cols + 1, "%s", device->description); } } } prompt(200, pmpt); /* Display current relay */ i = 1; if (idx == 1) { snprintf(pmpt, Config.lcd_cols + 1, "N/A"); } else { for (device = Config.devices; device; device = device->next) { if (device->direction == DEVDIR_OUT_ANALOG) { i++; if (i == idx) { snprintf(pmpt, Config.lcd_cols + 1, "%s", device->description); } } } } prompt(300, pmpt); /* Display possible new relay */ if (choices == 1) prompt(405, NULL); else if (idx == 1) prompt(402, NULL); else if (idx == choices) prompt(404, NULL); else prompt(403, NULL); key = keywait(); if ((key == KEY_RETURN) || my_shutdown) { if (debug) fprintf(stdout, "End editRelay\n"); return; } if ((key == KEY_UP) && (idx > 1)) idx--; if ((key == KEY_DOWN) && (idx < choices)) idx++; if ((key == KEY_ENTER) && (idx != old)) { /* * Select new output. */ if (idx == 1) { /* * Disable the output */ strncpy(uuid, (char *)"00000000-0000-0000-0000-000000000000", 36); old = idx; } else { i = 1; for (device = Config.devices; device; device = device->next) { if (device->direction == DEVDIR_OUT_ANALOG) { i++; if (i == idx) { strncpy(uuid, device->uuid, 36); break; } } } old = idx; } } } } /* * Edit a single unit */ void editUnit(units_list *unit) { int idx = 1, key; char pmpt[81], *uuid, *name; uLong ocrc, ncrc; if (debug) fprintf(stdout, "Start edit brewsystem %d %s\n", unit->number, unit->uuid); prompt(0, NULL); ncrc = ocrc = crc32(0L, Z_NULL, 0); ocrc = crc32(ocrc, (const Bytef *) unit, sizeof(units_list)); for (;;) { prompt(0, NULL); prompt(193, NULL); if (idx == 1) prompt(402, NULL); else if (idx == 21) prompt(404, NULL); else prompt(403, NULL); switch (idx) { // 12345678901234567890 case 1: snprintf(pmpt, Config.lcd_cols + 1, "Unit name:"); prompt(200, pmpt); snprintf(pmpt, Config.lcd_cols + 1, "%s", unit->name); prompt(300, pmpt); break; case 2: snprintf(pmpt, Config.lcd_cols + 1, " HLT sensor setup"); prompt(200, pmpt); break; case 3: snprintf(pmpt, Config.lcd_cols + 1, " HLT heater setup"); prompt(200, pmpt); break; case 4: snprintf(pmpt, Config.lcd_cols + 1, " MLT sensor setup"); prompt(200, pmpt); break; case 5: snprintf(pmpt, Config.lcd_cols + 1, " MLT heater setup"); prompt(200, pmpt); break; case 6: snprintf(pmpt, Config.lcd_cols + 1, " MLT pump setup"); prompt(200, pmpt); break; case 7: snprintf(pmpt, Config.lcd_cols + 1, "MLT heat b4 HLT: %s", unit->hlt_heater_mltfirst ? (char *)"Yes":(char *)"No"); prompt(200, pmpt); break; case 8: snprintf(pmpt, Config.lcd_cols + 1, "Pump cycle: %3d mins", unit->pump_cycle); prompt(200, pmpt); break; case 9: snprintf(pmpt, Config.lcd_cols + 1, "Pump rest : %3d mins", unit->pump_rest); prompt(200, pmpt); break; case 10: snprintf(pmpt, Config.lcd_cols + 1, " Pump pre-mash: %s", unit->pump_premash ? (char *)"Yes":(char *)"No"); prompt(200, pmpt); break; case 11: snprintf(pmpt, Config.lcd_cols + 1, " Pump on-mash: %s", unit->pump_onmash ? (char *)"Yes":(char *)"No"); prompt(200, pmpt); break; case 12: snprintf(pmpt, Config.lcd_cols + 1, " Pump mashout: %s", unit->pump_mashout ? (char *)"Yes":(char *)"No"); prompt(200, pmpt); break; case 13: snprintf(pmpt, Config.lcd_cols + 1, " Pump on-boil: %s", unit->pump_onboil ? (char *)"Yes":(char *)"No"); prompt(200, pmpt); break; case 14: snprintf(pmpt, Config.lcd_cols + 1, " Pump stop: %5.1f\001", unit->pump_stop); prompt(200, pmpt); break; case 15: snprintf(pmpt, Config.lcd_cols + 1, " Skip Add: %s", unit->skip_add ? (char *)"Yes":(char *)"No"); prompt(200, pmpt); break; case 16: snprintf(pmpt, Config.lcd_cols + 1, " Skip Remove: %s", unit->skip_remove ? (char *)"Yes":(char *)"No"); prompt(200, pmpt); break; case 17: snprintf(pmpt, Config.lcd_cols + 1, " Skip Iodine: %s", unit->skip_iodine ? (char *)"Yes":(char *)"No"); prompt(200, pmpt); break; case 18: snprintf(pmpt, Config.lcd_cols + 1, "Iodine time: %3d min", unit->iodine_time); prompt(200, pmpt); break; case 19: snprintf(pmpt, Config.lcd_cols + 1, " Whirlpool: %s", unit->whirlpool ? (char *)"Yes":(char *)"No"); prompt(200, pmpt); break; case 20: snprintf(pmpt, Config.lcd_cols + 1, " HLT PID setup"); prompt(200, pmpt); break; case 21: snprintf(pmpt, Config.lcd_cols + 1, " MLT PID setup"); prompt(200, pmpt); break; } key = keywait(); if ((key == KEY_RETURN) || my_shutdown) { ncrc = crc32(ncrc, (const Bytef *)unit, sizeof(units_list)); if (ocrc != ncrc) wrconfig(); if (debug) fprintf(stdout, "End edit brewsystem %d %s\n", unit->number, unit->uuid); return; } if ((key == KEY_UP) && (idx > 1)) idx--; if ((key == KEY_DOWN) && (idx < 21)) idx++; if (key == KEY_ENTER) { switch(idx) { case 1: name = calloc(sizeof(char), 21); snprintf(name, 21, unit->name); editName(name, (char *)"Unit name"); if (unit->name) free(unit->name); unit->name = xstrcpy(name); free(name); break; case 2: uuid = xstrcpy(unit->hlt_sensor.uuid); editSensor(uuid, (char *)"HLT sensor"); strncpy(unit->hlt_sensor.uuid, uuid, 36); free(uuid); break; case 3: uuid = xstrcpy(unit->hlt_heater.uuid); editRelay(uuid, (char *)"HLT heater"); strncpy(unit->hlt_heater.uuid, uuid, 36); free(uuid); break; case 4: uuid = xstrcpy(unit->mlt_sensor.uuid); editSensor(uuid, (char *)"MLT sensor"); strncpy(unit->mlt_sensor.uuid, uuid, 36); free(uuid); break; case 5: uuid = xstrcpy(unit->mlt_heater.uuid); editRelay(uuid, (char *)"MLT heater"); strncpy(unit->mlt_heater.uuid, uuid, 36); free(uuid); break; case 6: uuid = xstrcpy(unit->mlt_pump.uuid); editRelay(uuid, (char *)"MLT pump"); strncpy(unit->mlt_pump.uuid, uuid, 36); free(uuid); break; case 7: toggleYesNo(&unit->hlt_heater_mltfirst, (char *)"MLT heat b4 HLT"); break; case 8: editInteger(&unit->pump_cycle, 5, 15, 1, (char *)"Pump cycle", (char *)"mins"); break; case 9: editInteger(&unit->pump_rest, 1, 5, 1, (char *)"Pump rest ", (char *)"mins"); break; case 10: toggleYesNo(&unit->pump_premash, (char *)" Pump pre-mash"); break; case 11: toggleYesNo(&unit->pump_onmash, (char *)" Pump on-mash"); break; case 12: toggleYesNo(&unit->pump_mashout, (char *)" Pump mashout"); break; case 13: toggleYesNo(&unit->pump_onboil, (char *)" Pump on-boil"); break; case 14: editFloat(&unit->pump_stop, 80.0, 110.0, (char *)" Pump stop"); break; case 15: toggleYesNo(&unit->skip_add, (char *)"Skip add water"); break; case 16: toggleYesNo(&unit->skip_remove, (char *)"Skip remove Mash"); break; case 17: toggleYesNo(&unit->skip_iodine, (char *)"Skip iodine test"); break; case 18: editInteger(&unit->iodine_time, 10, 240, 10, (char *)"Iodine time", (char *)"min"); break; case 19: toggleYesNo(&unit->whirlpool, (char *)"Do a whirlpool"); break; case 20: editPID(unit->PID_hlt); break; case 21: editPID(unit->PID_mlt); break; } } } } void addUnit(int number) { units_list *tmpu, *unit = (units_list *)malloc(sizeof(units_list)); char name[81]; uuid_t uu; double Input, Output, Setpoint; if (debug) fprintf(stdout, "Adding new brewsystem %d\n", number); unit->next = NULL; unit->version = 1; unit->uuid = malloc(37); uuid_generate(uu); uuid_unparse(uu, unit->uuid); snprintf(name, 20, "System %d", number); unit->name = xstrcpy(name); unit->number = number; if (number == 1) unit->active = 1; else unit->active = 0; unit->hlt_sensor.uuid = xstrcpy((char *)"00000000-0000-0000-0000-000000000000"); unit->hlt_sensor.state = DEVPRESENT_UNDEF; unit->hlt_sensor.value = 0; unit->mlt_sensor.uuid = xstrcpy((char *)"00000000-0000-0000-0000-000000000000"); unit->mlt_sensor.state = DEVPRESENT_UNDEF; unit->mlt_sensor.value = 0; unit->hlt_heater.uuid = xstrcpy((char *)"00000000-0000-0000-0000-000000000000"); unit->hlt_heater.value = 0; unit->hlt_heater.delay = 0; unit->mlt_heater.uuid = xstrcpy((char *)"00000000-0000-0000-0000-000000000000"); unit->mlt_heater.value = 0; unit->mlt_heater.delay = 0; unit->mlt_pump.uuid = xstrcpy((char *)"00000000-0000-0000-0000-000000000000"); unit->mlt_pump.value = 0; unit->mlt_pump.delay = 0; unit->hlt_heater_mltfirst = 1; unit->pump_cycle = 7; unit->pump_rest = 2; unit->pump_premash = 1; unit->pump_onmash = 1; unit->pump_mashout = 0; unit->pump_onboil = 0; unit->pump_stop = 90; unit->skip_add = 0; unit->skip_remove = 0; unit->skip_iodine = 0; unit->iodine_time = 90; unit->whirlpool = 1; unit->PID_hlt = (pid_var *)malloc(sizeof(pid_var)); unit->PID_mlt = (pid_var *)malloc(sizeof(pid_var)); Input = Setpoint = 20.0; Output = 0; PID_init(unit->PID_hlt, &Input, &Output, &Setpoint, 2, 5, 1, P_DIRECT); PID_init(unit->PID_mlt, &Input, &Output, &Setpoint, 2, 5, 1, P_DIRECT); editUnit(unit); if (Config.units == NULL) { Config.units = unit; } else { for (tmpu = Config.units; tmpu; tmpu = tmpu->next) { if (tmpu->next == NULL) { tmpu->next = unit; break; } } } syslog(LOG_NOTICE, "Brewsystem %d added to the configuration", number); if (debug) fprintf(stdout, "Brewsystem %d added to the configuration\n", number); } /* * Edit a single recipe */ void editRecipe(a_recipe *recipe) { int idx = 1, i, key; char pmpt[81], *name; uLong ocrc, ncrc; if (debug) fprintf(stdout, "Start edit recipe `%s' %s\n", recipe->name, recipe->uuid); prompt(0, NULL); ncrc = ocrc = crc32(0L, Z_NULL, 0); ocrc = crc32(ocrc, (const Bytef *) recipe, sizeof(a_recipe)); for (;;) { prompt(0, NULL); prompt(191, NULL); /* " Edit recipe " */ if (idx == 1) prompt(402, NULL); /* "--- dwn quit ok " */ else if (idx == 24) prompt(404, NULL); /* " up --- quit ok " */ else prompt(403, NULL); /* " up dwn quit ok " */ switch (idx) { // 12345678901234567890 case 1: snprintf(pmpt, Config.lcd_cols + 1, "Recipe name:"); prompt(200, pmpt); snprintf(pmpt, Config.lcd_cols + 1, "%s", recipe->name); prompt(300, pmpt); break; case 2: snprintf(pmpt, Config.lcd_cols + 1, "Recipe code:"); prompt(200, pmpt); snprintf(pmpt, Config.lcd_cols + 1, "%s", recipe->code); prompt(300, pmpt); break; case 3: snprintf(pmpt, Config.lcd_cols + 1, "Boil time: %3d mins", recipe->boiltime); prompt(200, pmpt); break; case 4: snprintf(pmpt, Config.lcd_cols + 1, " Cool to: %5.1f\001", recipe->coolto); prompt(200, pmpt); break; case 5: case 6: case 7: case 8: case 9: case 10: case 11: case 12: snprintf(pmpt, Config.lcd_cols + 1, "Mash: %s", recipe->mash[idx - 5].name); prompt(200, pmpt); if (recipe->mash[idx - 5].skip) snprintf(pmpt, Config.lcd_cols + 1, " Skipped"); else snprintf(pmpt, Config.lcd_cols + 1, " Sv %4.1f\001 %3d mins", recipe->mash[idx - 5].setpoint, recipe->mash[idx - 5].duration); prompt(300, pmpt); break; case 13: case 14: case 15: case 16: case 17: case 18: case 19: case 20: case 21: case 22: snprintf(pmpt, Config.lcd_cols + 1, "Add: %s", recipe->hops[idx - 13].name); prompt(200, pmpt); if (recipe->hops[idx - 13].skip) snprintf(pmpt, Config.lcd_cols + 1, " Skipped"); else { if (recipe->hops[idx - 13].boiltime == -1) snprintf(pmpt, Config.lcd_cols + 1, " First Wort Hop"); else snprintf(pmpt, Config.lcd_cols + 1, " Boil for %3d mins", recipe->hops[idx - 13].boiltime); } prompt(300, pmpt); break; case 23: case 24: case 25: snprintf(pmpt, Config.lcd_cols + 1, "Hopstand: %s", recipe->hopstand[idx - 23].name); prompt(200, pmpt); if (recipe->hopstand[idx - 23].skip) snprintf(pmpt, Config.lcd_cols + 1, " Skipped"); else if (recipe->hopstand[idx - 23].hold) snprintf(pmpt, Config.lcd_cols + 1, "Hold at %4.1f %3d mins", recipe->hopstand[idx - 23].setpoint, recipe->hopstand[idx - 23].duration); else snprintf(pmpt, Config.lcd_cols + 1, "Hold for %3d mins", recipe->hopstand[idx - 23].duration); prompt(300, pmpt); break; } key = keywait(); if ((key == KEY_RETURN) || my_shutdown) { ncrc = crc32(ncrc, (const Bytef *)recipe, sizeof(a_recipe)); if (ocrc != ncrc) { /* * Fix some user errors */ for (i = 0; i < 8; i++) if (recipe->hops[i].boiltime > recipe->boiltime) recipe->hops[i].boiltime = recipe->boiltime; wrrecipes(); } if (debug) fprintf(stdout, "End edit recipe `%s' %s\n", recipe->name, recipe->uuid); return; } if ((key == KEY_UP) && (idx > 1)) idx--; if ((key == KEY_DOWN) && (idx < 24)) idx++; if (key == KEY_ENTER) { switch(idx) { case 1: name = calloc(sizeof(char), 21); snprintf(name, 21, recipe->name); editName(name, (char *)"Recipe name"); if (recipe->name) free(recipe->name); recipe->name = xstrcpy(name); free(name); break; case 2: name = calloc(sizeof(char), 21); snprintf(name, 21, recipe->code); editName(name, (char *)"Recipe code"); if (recipe->code) free(recipe->code); recipe->code = xstrcpy(name); free(name); break; case 3: editInteger(&recipe->boiltime, 60, 240, 5, (char *)"Boil time:", (char *)"mins"); break; case 4: editFloat(&recipe->coolto, 10.0, 30.0, (char *)" Cool to"); case 5: case 6: case 7: case 8: case 9: case 10: case 11: case 12: editMash(&recipe->mash[idx-5]); break; case 13: case 14: case 15: case 16: case 17: case 18: case 19: case 20: case 21: case 22: editHopaddition(&recipe->hops[idx-13], recipe->boiltime); break; case 23: case 24: case 25: editHopstand(&recipe->hopstand[idx-23]); break; } } } } void addRecipe(int number) { a_recipe *tmpu, *recipe = (a_recipe *)malloc(sizeof(a_recipe)); char name[81]; uuid_t uu; if (debug) fprintf(stdout, "Adding new recipe %d\n", number); recipe->next = NULL; recipe->uuid = malloc(37); uuid_generate(uu); uuid_unparse(uu, recipe->uuid); snprintf(name, 21, "New recipe %d", number); recipe->name = xstrcpy(name); snprintf(name, 21, "%04d", number); recipe->code = xstrcpy(name); recipe->boiltime = 90; recipe->coolto = 20.0; recipe->starttime = recipe->endtime = (time_t)0; /* * Initial mash schedule, set a single-step 67 degr. mash */ recipe->mash[0].name = xstrcpy((char *)"Mash-in"); recipe->mash[0].min = 20; recipe->mash[0].max = 80; recipe->mash[0].canskip = 0; recipe->mash[0].setpoint = 68.0; recipe->mash[0].skip = 0; recipe->mash[0].duration = 1; recipe->mash[1].name = xstrcpy((char *)"Phytase"); recipe->mash[1].min = 25; recipe->mash[1].max = 55; recipe->mash[1].canskip = 1; recipe->mash[1].setpoint = 40.0; recipe->mash[1].skip = 1; recipe->mash[1].duration = 10; recipe->mash[2].name = xstrcpy((char *)"Glucanase"); recipe->mash[2].min = 35; recipe->mash[2].max = 50; recipe->mash[2].canskip = 1; recipe->mash[2].setpoint = 45.0; recipe->mash[2].skip = 1; recipe->mash[2].duration = 10; recipe->mash[3].name = xstrcpy((char *)"Protease"); recipe->mash[3].min = 45; recipe->mash[3].max = 60; recipe->mash[3].canskip = 1; recipe->mash[3].setpoint = 55.0; recipe->mash[3].skip = 1; recipe->mash[3].duration = 10; recipe->mash[4].name = xstrcpy((char *)"B-Amylase"); recipe->mash[4].min = 50; recipe->mash[4].max = 70; recipe->mash[4].canskip = 1; recipe->mash[4].setpoint = 62.0; recipe->mash[4].skip = 1; recipe->mash[4].duration = 30; recipe->mash[5].name = xstrcpy((char *)"A-Amylase 1"); recipe->mash[5].min = 60; recipe->mash[5].max = 76; recipe->mash[5].canskip = 1; recipe->mash[5].setpoint = 67.0; recipe->mash[5].skip = 1; recipe->mash[5].duration = 30; recipe->mash[6].name = xstrcpy((char *)"A-Amylase 2"); recipe->mash[6].min = 60; recipe->mash[6].max = 76; recipe->mash[6].canskip = 0; recipe->mash[6].setpoint = 67.0; recipe->mash[6].skip = 0; recipe->mash[6].duration = 60; recipe->mash[7].name = xstrcpy((char *)"Mash-out"); recipe->mash[7].min = 75; recipe->mash[7].max = 80; recipe->mash[7].canskip = 0; recipe->mash[7].setpoint = 78.0; recipe->mash[7].skip = 0; recipe->mash[7].duration = 10; /* * Add 2 hop additions, maximum 10 */ recipe->hops[0].name = xstrcpy((char *)"Hops 1"); recipe->hops[0].skip = 0; recipe->hops[0].boiltime = 90; recipe->hops[1].name = xstrcpy((char *)"Hops 2"); recipe->hops[1].skip = 0; recipe->hops[1].boiltime = 5; recipe->hops[2].name = xstrcpy((char *)"Hops 3"); recipe->hops[2].skip = 1; recipe->hops[2].boiltime = 0; recipe->hops[3].name = xstrcpy((char *)"Hops 4"); recipe->hops[3].skip = 1; recipe->hops[3].boiltime = 0; recipe->hops[4].name = xstrcpy((char *)"Hops 5"); recipe->hops[4].skip = 1; recipe->hops[4].boiltime = 0; recipe->hops[5].name = xstrcpy((char *)"Hops 6"); recipe->hops[5].skip = 1; recipe->hops[5].boiltime = 0; recipe->hops[6].name = xstrcpy((char *)"Hops 7"); recipe->hops[6].skip = 1; recipe->hops[6].boiltime = 0; recipe->hops[7].name = xstrcpy((char *)"Hops 8"); recipe->hops[7].skip = 1; recipe->hops[7].boiltime = 0; recipe->hops[8].name = xstrcpy((char *)"Hops 9"); recipe->hops[8].skip = 1; recipe->hops[8].boiltime = 0; recipe->hops[9].name = xstrcpy((char *)"Hops 10"); recipe->hops[9].skip = 1; recipe->hops[9].boiltime = 0; /* * Add 3 hopstands, disabled by default. */ recipe->hopstand[0].name = xstrcpy((char *)"Hopstand hot"); recipe->hopstand[0].min = 88; recipe->hopstand[0].max = 100; recipe->hopstand[0].hold = 0; recipe->hopstand[0].setpoint = 93.0; recipe->hopstand[0].skip = 1; recipe->hopstand[0].duration = 30; recipe->hopstand[1].name = xstrcpy((char *)"Hopstand default"); recipe->hopstand[1].min = 72; recipe->hopstand[1].max = 77; recipe->hopstand[1].hold = 0; recipe->hopstand[1].setpoint = 75.0; recipe->hopstand[1].skip = 1; recipe->hopstand[1].duration = 60; recipe->hopstand[2].name = xstrcpy((char *)"Hopstand cool"); recipe->hopstand[2].min = 60; recipe->hopstand[2].max = 66; recipe->hopstand[2].hold = 0; recipe->hopstand[2].setpoint = 63.0; recipe->hopstand[2].skip = 1; recipe->hopstand[2].duration = 60; editRecipe(recipe); if (recipes == NULL) { recipes = recipe; } else { for (tmpu = recipes; tmpu; tmpu = tmpu->next) { if (tmpu->next == NULL) { tmpu->next = recipe; break; } } } syslog(LOG_NOTICE, "Recipe %d added", number); if (debug) fprintf(stdout, "Recipe %d added\n", number); } void editRecipes(void) { int total, i, key, choice = 1;; a_recipe *recipe; char pmpt[81]; prompt(0, NULL); for (;;) { total = 0; for (recipe = recipes; recipe; recipe = recipe->next) { total++; } if (debug) fprintf(stdout, "editRecipes total=%d choice=%d\n", total, choice); i = 0; if (total) { for (recipe = recipes; recipe; recipe = recipe->next) { i++; if (i == choice) break; } } prompt(102, NULL); /* " SETUP MENU " */ prompt(221, NULL); /* " Select Recipe " */ if (total) { snprintf(pmpt, Config.lcd_cols + 1, "%s %s ", recipe->code, recipe->name); prompt(300, pmpt); } if (total == 0) prompt(416, NULL); /* "add --- quit --- " */ else if (total == 1) prompt(415, NULL); /* "add --- quit ok " */ else if (total && (choice == 1)) prompt(414, NULL); /* "add dwn quit ok " */ else if (total && (choice == total)) prompt(404, NULL); /* " up --- quit ok " */ else prompt(403, NULL); /* " up dwn quit ok " */ key = keywait(); if ((key == KEY_RETURN) || my_shutdown) return; if (total && (key == KEY_ENTER)) { editRecipe(recipe); prompt(0, NULL); } if (key == KEY_UP) { if ((total == 1) || (choice == 1)) addRecipe(total + 1); else if (choice > 1) choice--; } if ((key == KEY_DOWN) && (total > 1) && (choice < total)) choice++; } } void editUnits(void) { int total, i, key, choice = 1;; units_list *unit; char pmpt[81]; prompt(0, NULL); for (;;) { total = 0; for (unit = Config.units; unit; unit = unit->next) { total++; } if (debug) fprintf(stdout, "editUnits total=%d choice=%d\n", total, choice); if (total == 0) { /* * Impossible unless first setup was skipped */ addUnit(1); total = 1; } else { i = 0; for (unit = Config.units; unit; unit = unit->next) { i++; if (i == choice) break; } prompt(102, NULL); /* " SETUP MENU " */ prompt(222, NULL); /* " Select Brewsystem " */ snprintf(pmpt, Config.lcd_cols + 1, "%s ", unit->name); prompt(300, pmpt); if (total == 1) prompt(415, NULL); /* "add --- quit ok " */ else if (choice == 1) prompt(414, NULL); /* "add dwn quit ok " */ else if (choice == total) prompt(404, NULL); /* " up --- quit ok " */ else prompt(403, NULL); /* " up dwn quit ok " */ key = keywait(); if ((key == KEY_RETURN) || my_shutdown) return; if (key == KEY_ENTER) { editUnit(unit); prompt(0, NULL); } if (key == KEY_UP) { if ((total == 1) || (choice == 1)) addUnit(total + 1); else if (choice > 1) choice--; } if ((key == KEY_DOWN) && (total > 1) && (choice < total)) choice++; } } } void setup(void) { int key, option = 202; for (;;) { if (debug) fprintf(stdout, "setup() option=%d\n", option); prompt(0, NULL); prompt(102, NULL); /* " SETUP MENU " */ prompt(option, NULL); if (option == 202) prompt(402, NULL); /* "--- dwn quit ok " */ #ifdef USE_SIMULATOR else if (option == 205) #else else if (option == 204) #endif prompt(404, NULL); /* " up --- quit ok " */ else prompt(403, NULL); /* " up dwn quit ok " */ key = keywait(); if ((key == KEY_RETURN) || my_shutdown) return; if ((key == KEY_UP) && (option > 202)) { option--; } #ifdef USE_SIMULATOR if ((key == KEY_DOWN) && (option < 205)) { #else if ((key == KEY_DOWN) && (option < 204)) { #endif option++; } if (key == KEY_ENTER) { switch(option) { case 202: editRecipes(); break; case 203: editUnits(); break; case 204: // devices break; #ifdef USE_SIMULATOR case 205: // simulator break; #endif } if (my_shutdown) return; } } }