/* XXX To do: get the parser "" working correctly */ /* * Sample parser: command implementations. * Each keyword in the file 'keywords' requires a function called # '_command'. This is a sample implementation. * Greg Lehey, 18 May 2004 * * Copyright (c) 2004 by Greg Lehey * * This software is distributed under the so-called ``Berkeley * License'': * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * This software is provided ``as is'', and any express or implied * warranties, including, but not limited to, the implied warranties * of merchantability and fitness for a particular purpose are * disclaimed. In no event shall Greg Lehey be liable for any direct, * indirect, incidental, special, exemplary, or consequential damages * (including, but not limited to, procurement of substitute goods or * services; loss of use, data, or profits; or business interruption) * however caused and on any theory of liability, whether in contract, * strict liability, or tort (including negligence or otherwise) * arising in any way out of the use of this software, even if advised * of the possibility of such damage. * * $Id: commands.c,v 1.17 2005/08/15 08:06:02 grog Exp $ */ #include #include #include "main.h" /* XXX may need change */ /* Configuration variables and default values. */ /* * The serial line from which to read temperature data. * For Linux, you might use something like /dev/ttyS0. */ char serialline [MAXPATHLEN] = "/dev/cuaa0"; /* The bit rate of the serial line (bps). */ int linespeed = 2400; /* Temperature probe use. Presently we measure only room temperature * (outside the fridge), ambient temperature (inside the fridge) and * fermenter temperature (typically intended to be the surface). This * program is currently set up for four probes, which leaves one over. * It will be monitored, but at present there's no particular use for * it. This may change. * The temperature probe with the room temperature. */ int roomtempprobe = 1; /* * The temperature probe with the ambient (in-fridge) temperature. * This doesn't have to exist; set to -1 if it isn't there. */ int ambientprobe = 2; /* * The temperature probe with the fermenter temperature. */ int fermenterprobe = 3; /* * The temperature probe with the second fermenter temperature. */ int fermenter2probe = 0; /* * Next we have a number of descriptive texts. They're purely * cosmetic: a list saying "fermenter 1" is less informative than * "brew 37" or "pale ale". For each fermenter there are three * labels: one for printing when there's enough space, and two others * for when there isn't, and it needs to be split over two lines. * This is the case with the status display. * * For silly reasons, the short versions for the first fermenter are * limited to 6 characters each, while the short versions for the * second fermenter are limited to 7 characters. Expect this to * change. */ char fermenter1label [21] = "Fermenter 1"; char fermenter1label1 [7] = "Ferm-"; char fermenter1label2 [7] = "enter"; char fermenter2label [21] = "Fermenter 2"; char fermenter2label1 [8] = "Ferm-"; char fermenter2label2 [8] = "ent 2"; /* * Due to the nature of the cooling hardware, the same cooling applies * to all fermenters in the fridge, but the temperatures of each * fermenter can be different according to the rate of fermentation * and the air flow around the fermenter. By default, the base * temperature (the one used to control the temperature) is the * temperature of the first fermenter. probe2factor is a value * between 0 and 1 which specifies to what extent the temperature of * the second probe should be used to calculate the base temperature. * * The formula is: * * basetemp = probe2temp * factor + probe1temp * (factor - 1) * * A factor of 0 will use the temperature of probe 1 as the base * temperature; a factor of will use the temperature of probe 2 as the * base temperature. A factor of 0.4 will set the base temperature at * .4 of the way from probe 1 temperature to probe 2 temperature. */ float probe2factor = 0; /* * The file in which to save temperature information. By default, it * is relative to the current working directory, but it doesn't have to * be. */ char logfile [MAXPATHLEN] = "tempinfo"; /* * The time interval with which to log, in seconds. 900 seconds is 15 * minutes. */ int loginterval = 900; /* * The number of lines on a "page" of the log file. After this * interval, a new heading appears. */ int logpagesize = 50; /* * The time to display idle status messages. These are typically the * reason that the system is currently idle, and they soon become * boring. On the other hand, if they're only displayed once, they're * liable to be missed. This parameter is the number of seconds to * display the message. */ int idledisplaytime = 10; /* * The file to which to display current information. This is intended * to be displayed on a screen, so it's written to every time. By * default the current information is not displayed; set this variable * to display it. To display on the current terminal, set it to * /dev/tty. */ char displayfile [MAXPATHLEN]; /* * The file in which to log information for graph plotting. * Default is not to log. */ char graphlogfile [MAXPATHLEN]; /* * The time interval with which to log, in seconds. 600 seconds is 10 * minutes. */ int graphloginterval = 600; /* * Set to 1 to log significant events to syslogd. */ int dosyslog = 1; /* * syslog priority. Currently not changeable. */ int syslog_prio = LOG_INFO | LOG_USER; /* * For debugging, log to this file. By default there is no file, * meaning that we don't do debug logging. * * To turn debugging off, specify the debugfile keyword without an * argument. */ char debugfile [MAXPATHLEN]; /* * Part 2: temperature control outputs. * relayline is the (parallel) port controlling the relays. */ char relayline [MAXPATHLEN] = "/dev/ppi0"; /* * We define two relays for controlling the temperature. These values * specify the output bits on relayline needed to set them. */ int coolrelay = 1; int heatrelay = 2; /* * We can't turn things on and off all the time. In particular, * refrigerators would die quickly. It also confuses the regulation. * The following two variables specify the minimum times, in seconds, * that the heater and cooler should be on and off. */ int heateronmin = 30; int heateroffmin = 300; int cooleronmin = 30; int cooleroffmin = 120; /* * To avoid oscillation, it's probably a good idea to wait longer * betweeen changing tack. These are the minimum times from cooler * off to heater on and heater off to cooler on. */ int coolertoheaterdelay = 600; int heatertocoolerdelay = 600; /* * Temperatures. All temperatures are in Celsius (or Kelvin where it * doesn't make any difference). If you really want to use Fahrenheit, * either change the source, or better, don't. Instead, convert to * Celsius with the formulae C = (F - 32) / 1.8 and F = C * 1.8 + 32. */ /* * Initial goal temperature. This is the temperature you want to aim * for when you start the program. Depending on what you're doing, * you can maintain this temperature throughout the fermentation or * gradually change to the end temperature at a fixed rate. * * This variable has a relatively sensible default, but you'll almost * certainly want to change it. */ float starttemp = 18; /* * Final goal temperature. This is the temperature you want to aim * for later in fermentation. The goal temperature changes uniformly * from starttemp to endtemp by the time specified by the second * parameter, which specifies the time and date by which to complete * the change. So, for example, you could complete the transition * from 20° to 17° by 15 August 2005, 20:15 with: * * starttemp 20 * endtemp 17 15 aug 2005 20:15 * * To disable this feature, remove the second parameter. Under these * circumstances the end temperature is ignored. Commenting out the * line won't work, since the program logic will then leave the old * value in place. Yes, this is not the most intuitive way to do * things, but I can't think of a better one. */ float endtemp = 18; time_t tempchangestart = 0; /* temperature change start */ time_t tempchangeend = 0; /* 0 is epoch, but also disables */ float goaltemp; /* set in monitor () */ /* * It's easy to overshoot the temperature we're aiming for. We need a * middle ground where we're neither heating nor cooling. These values * specify how far the temperature should drop below the goal * temperature before turning on the heating and how far the * temperature should rise above the goal temperature before turning on * the cooling. */ float coolerholdoff = 0.5; float heaterholdoff = 0.5; /* * On the other hand, if there's no or only minimal overrun, stopping * dead on the temperature can mean that the average is offset. * Counter this by not stopping until we have overrun a little. This * can use a lot of tuning. * * These values relate to coolerholdoff and heaterholdoff * respectively: they're the multiple of these values by which the * cooler or heater should overshoot. So a cooler holdoff of 0.5° and * a cooler overshoot of 0.9 means that the cooler will turn off when * the temperature has risen 0.45° above the goal temperature. */ float coolerovershoot = 0.9; float heaterovershoot = 0.9; /* * In addition to this, we have some absolute values for the difference * between ambient temperatures and desired wort temperature. They're * differences, not absolute temperatures, so they depend on the * desired wort temperature. I'm particularly concerned about * overheating worts, so this one is lower. */ float maxcooltempdiff = 15; float maxheattempdiff = 5; /* * Finally, for lagers, we need to ensure that we don't freeze the * thing. We also don't want to boil it. These are absolute * temperatures. */ float maxambienttemp = 30; float minambienttemp = 0; /* * We communicate with the outside world via a TCP port. We can * change it from the default if we want. */ int tcpport = BEER_PORT; /* * IPV4 Address of "local" network that doesn't get logged when * requesting status information. This address is stored in host byte * order. */ struct in_addr nologtcp = {0xc06dc500}; /* Mask for above address */ struct in_addr nologtcpmask = {0xffffff00}; /* function for ambient keyword */ void ambientprobe_command (int argc, char *argv [], char *arg0 []) { if (argc < 2) { fprintf (stderr, "Usage: %s temp\n", argv [0] ); return; } ambientprobe = atoi (argv [1]); } /* function for coolerholdoff keyword */ void coolerholdoff_command (int argc, char *argv [], char *arg0 []) { if (argc < 2) { fprintf (stderr, "Usage: %s temp\n", argv [0] ); return; } coolerholdoff = atof (argv [1]); } /* function for cooleroffmin keyword */ void cooleroffmin_command (int argc, char *argv [], char *arg0 []) { if (argc < 2) { fprintf (stderr, "Usage: %s temp\n", argv [0] ); return; } cooleroffmin = atoi (argv [1]); } /* function for cooleronmin keyword */ void cooleronmin_command (int argc, char *argv [], char *arg0 []) { if (argc < 2) { fprintf (stderr, "Usage: %s temp\n", argv [0] ); return; } cooleronmin = atoi (argv [1]); } /* function for coolerovershoot keyword */ void coolerovershoot_command (int argc, char *argv [], char *arg0 []) { if (argc < 2) { fprintf (stderr, "Usage: %s temp\n", argv [0] ); return; } coolerovershoot = atof (argv [1]); } /* Dummy function for coolertoheaterdelay keyword */ void coolertoheaterdelay_command (int argc, char *argv [], char *arg0 []) { if (argc < 2) { fprintf (stderr, "Usage: %s temp\n", argv [0] ); return; } coolertoheaterdelay = atof (argv [1]); } /* function for coolrelay keyword */ void coolrelay_command (int argc, char *argv [], char *arg0 []) { if (argc < 2) { fprintf (stderr, "Usage: %s temp\n", argv [0] ); return; } coolrelay = atoi (argv [1]); } /* function for debugfile keyword */ void debugfile_command (int argc, char *argv [], char *arg0 []) { if (argc < 2) debugfile [0] = '\0'; /* turn it off again */ strlcpy (debugfile, argv [1], MAXPATHLEN); } /* function for displayfile keyword */ void displayfile_command (int argc, char *argv [], char *arg0 []) { if (argc < 2) displayfile [0] = '\0'; else strlcpy (displayfile, argv [1], MAXPATHLEN); } /* function for serialline keyword */ void dosyslog_command (int argc, char *argv [], char *arg0 []) { if (argc < 2) { fprintf (stderr, "Usage: %s temp\n", argv [0] ); return; } if (strcmp (argv [1], "on") == 0) dosyslog = 1; else if (strcmp (argv [1], "off") == 0) dosyslog = 0; else fprintf (stderr, "dosyslog must be \"on\" or \"off\", not \"%s\"\n", argv [1]); } /* function for endtemp keyword */ void endtemp_command (int argc, char *argv [], char *arg0 []) { struct tm endtime; char *cp; int i; /* parameter counter */ char *months [] = { "january", "february", "march", "april", "may", "june", "july", "august", "september", "october", "november", "december" }; char *mon [] = { "jan", "feb", "mar", "apr", "may", "jun", "jul", "aug", "sep", "oct", "nov", "dec" }; if (argc < 3) /* no end time specified: disable */ { tempchangeend = 0; /* no end time */ return; } else if (argc < 6) { fprintf (stderr, "Usage: %s endtemp day month year time\n", argv [0] ); return; } endtemp = atof (argv [1]); memset ((void *) &endtime, 0, sizeof (endtime)); /* * Try to parse the date. We don't use strptime(3) because of the * pain: first we need to stick the parms back together again, and * secondly it craps out if you give it invalid data. * * Unfortunately this means that we can't put in user-specified * format strings. Our format is: * Parm Meaning * 2 day * 3 month * 4 year * 5 H:M[:S] */ endtime.tm_mday = atoi (argv [2]); if (isalpha (argv [3] [0])) { for (i = 0; i < 12; i++) if ((strcasecmp (argv [3], months [i])) == 0) break; if (i == 12) /* nothing found, */ for (i = 0; i < 12; i++) /* try short abbreviations */ if ((strcasecmp (argv [3], mon [i])) == 0) break; endtime.tm_mon = i; } else endtime.tm_mon = atoi (argv [3]) - 1; if ((endtime.tm_mon > 11) || (endtime.tm_mon < 0)) /* nothing valid */ { fprintf (stderr, "%s: invalid month: '%s'\n", argv [0], argv [3] ); return; } endtime.tm_year = atoi (argv [4]); if (endtime.tm_year < 100) endtime.tm_year += 100; else endtime.tm_year -= 1900; if ((endtime.tm_year > 137) || (endtime.tm_year < 0)) { fprintf (stderr, "%s: invalid year: '%s'\n", argv [0], argv [4] ); return; } /* HMS in a bundle */ cp = argv [5]; while (isdigit (*cp)) endtime.tm_hour = endtime.tm_hour * 10 + *cp++ - '0'; if (*cp = ':') /* minutes follow */ { cp++; while (isdigit (*cp)) endtime.tm_min = endtime.tm_min * 10 + *cp++ - '0'; if (*cp = ':') /* minutes follow */ { cp++; while (isdigit (*cp)) endtime.tm_sec = endtime.tm_sec * 10 + *cp - '0'; } } if ((endtime.tm_hour > 23) || (endtime.tm_min > 59) || (endtime.tm_sec > 59) || *cp ) { fprintf (stderr, "%s: invalid time spec: '%s'\n", argv [0], argv [5] ); return; } tempchangeend = mktime (&endtime); } /* function for fermenter1label keyword */ void fermenter1label_command (int argc, char *argv [], char *arg0 []) { if (argc < 2) { fprintf (stderr, "Usage: %s label\n", argv [0] ); return; } if (strlen (argv [1]) > 20) fprintf (stderr, "Warning: %s is too long, max 20 chars\n", argv [1]); strlcpy (fermenter1label, argv [1], 21); } /* function for fermenter1label1 keyword */ void fermenter1label1_command (int argc, char *argv [], char *arg0 []) { if (argc < 2) { fprintf (stderr, "Usage: %s label\n", argv [0] ); return; } if (strlen (argv [1]) > 6) fprintf (stderr, "Warning: %s is too long, max 6 chars\n", argv [1]); strlcpy (fermenter1label1, argv [1], 7); } /* function for fermenter1label2 keyword */ void fermenter1label2_command (int argc, char *argv [], char *arg0 []) { if (argc < 2) { fprintf (stderr, "Usage: %s label\n", argv [0] ); return; } if (strlen (argv [1]) > 6) fprintf (stderr, "Warning: %s is too long, max 6 chars\n", argv [1]); strlcpy (fermenter1label2, argv [1], 7); } /* function for fermenter2label keyword */ void fermenter2label_command (int argc, char *argv [], char *arg0 []) { if (argc < 2) { fprintf (stderr, "Usage: %s label\n", argv [0] ); return; } if (strlen (argv [1]) > 20) fprintf (stderr, "Warning: %s is too long, max 20 chars\n", argv [1]); strlcpy (fermenter2label, argv [1], 21); } /* function for fermenter2label1 keyword */ void fermenter2label1_command (int argc, char *argv [], char *arg0 []) { if (argc < 2) { fprintf (stderr, "Usage: %s label\n", argv [0] ); return; } if (strlen (argv [1]) > 7) fprintf (stderr, "Warning: %s is too long, max 7 chars\n", argv [1]); strlcpy (fermenter2label1, argv [1], 8); } /* function for fermenter2label2 keyword */ void fermenter2label2_command (int argc, char *argv [], char *arg0 []) { if (argc < 2) { fprintf (stderr, "Usage: %s label\n", argv [0] ); return; } if (strlen (argv [1]) > 7) fprintf (stderr, "Warning: %s is too long, max 7 chars\n", argv [1]); strlcpy (fermenter2label2, argv [1], 8); } /* function for fermenterprobe keyword */ void fermenterprobe_command (int argc, char *argv [], char *arg0 []) { if (argc < 2) { fprintf (stderr, "Usage: %s temp\n", argv [0] ); return; } fermenterprobe = atoi (argv [1]); } /* function for fermenter2probe keyword */ void fermenter2probe_command (int argc, char *argv [], char *arg0 []) { if (argc < 2) { fprintf (stderr, "Usage: %s temp\n", argv [0] ); return; } fermenter2probe = atoi (argv [1]); } /* function for graphlogfile keyword */ void graphlogfile_command (int argc, char *argv [], char *arg0 []) { if (argc < 2) graphlogfile [0] = '\0'; strlcpy (graphlogfile, argv [1], MAXPATHLEN); } /* Function for graphloginterval keyword */ void graphloginterval_command (int argc, char *argv [], char *arg0 []) { if (argc < 2) { fprintf (stderr, "Usage: %s temp\n", argv [0] ); return; } graphloginterval = atoi (argv [1]); } /* function for heateroffmin keyword */ void heateroffmin_command (int argc, char *argv [], char *arg0 []) { if (argc < 2) { fprintf (stderr, "Usage: %s temp\n", argv [0] ); return; } heateroffmin = atoi (argv [1]); } /* function for heaterholdoff keyword */ void heaterholdoff_command (int argc, char *argv [], char *arg0 []) { if (argc < 2) { fprintf (stderr, "Usage: %s temp\n", argv [0] ); return; } heaterholdoff = atof (argv [1]); } /* function for heaterovershoot keyword */ void heaterovershoot_command (int argc, char *argv [], char *arg0 []) { if (argc < 2) { fprintf (stderr, "Usage: %s temp\n", argv [0] ); return; } heaterovershoot = atof (argv [1]); } /* function for heateronmin keyword */ void heateronmin_command (int argc, char *argv [], char *arg0 []) { if (argc < 2) { fprintf (stderr, "Usage: %s temp\n", argv [0] ); return; } heateronmin = atoi (argv [1]); } /* Dummy function for heatertocoolerdelay keyword */ void heatertocoolerdelay_command (int argc, char *argv [], char *arg0 []) { if (argc < 2) { fprintf (stderr, "Usage: %s temp\n", argv [0] ); return; } heatertocoolerdelay = atof (argv [1]); } /* function for heatrelay keyword */ void heatrelay_command (int argc, char *argv [], char *arg0 []) { if (argc < 2) { fprintf (stderr, "Usage: %s temp\n", argv [0] ); return; } heatrelay = atoi (argv [1]); } /* function for idledisplaytime keyword */ void idledisplaytime_command (int argc, char *argv [], char *arg0 []) { if (argc < 2) { fprintf (stderr, "Usage: %s temp\n", argv [0] ); return; } idledisplaytime = atoi (argv [1]); } /* function for linespeed keyword */ void linespeed_command (int argc, char *argv [], char *arg0 []) { if (argc < 2) { fprintf (stderr, "Usage: %s temp\n", argv [0] ); return; } linespeed = atoi (argv [1]); } /* function for logfile keyword */ void logfile_command (int argc, char *argv [], char *arg0 []) { if (argc < 2) logfile [0] = '\0'; strlcpy (logfile, argv [1], MAXPATHLEN); } /* function for loginterval keyword */ void loginterval_command (int argc, char *argv [], char *arg0 []) { if (argc < 2) { fprintf (stderr, "Usage: %s temp\n", argv [0] ); return; } loginterval = atoi (argv [1]); } /* function for logpagesize keyword */ void logpagesize_command (int argc, char *argv [], char *arg0 []) { if (argc < 2) { fprintf (stderr, "Usage: %s temp\n", argv [0] ); return; } logpagesize = atoi (argv [1]); } /* function for maxcooltempdiff keyword */ void maxcooltempdiff_command (int argc, char *argv [], char *arg0 []) { if (argc < 2) { fprintf (stderr, "Usage: %s temp\n", argv [0] ); return; } maxcooltempdiff = atof (argv [1]); } /* function for maxheattempdiff keyword */ void maxheattempdiff_command (int argc, char *argv [], char *arg0 []) { if (argc < 2) { fprintf (stderr, "Usage: %s temp\n", argv [0] ); return; } maxheattempdiff = atof (argv [1]); } /* function for maxambienttemp keyword */ void maxambienttemp_command (int argc, char *argv [], char *arg0 []) { if (argc < 2) { fprintf (stderr, "Usage: %s temp\n", argv [0] ); return; } maxambienttemp = atof (argv [1]); } /* function for minambienttemp keyword */ void minambienttemp_command (int argc, char *argv [], char *arg0 []) { if (argc < 2) { fprintf (stderr, "Usage: %s temp\n", argv [0] ); return; } minambienttemp = atof (argv [1]); } /* function to select "no log" address range */ void nologtcp_command (int argc, char *argv [], char *arg0 []) { fprintf (stderr, "nologtcp command not implemented yet\n"); } /* function for probe2factor keyword */ void probe2factor_command (int argc, char *argv [], char *arg0 []) { float factor; if (argc < 2) { fprintf (stderr, "Usage: %s temp\n", argv [0] ); return; } factor = atof (argv [1]); if ((factor < 0) || (factor > 1)) fprintf (stderr, "Value must be between 0 and 1, not %g\n", factor); else probe2factor = factor; } /* function for relayline keyword */ void relayline_command (int argc, char *argv [], char *arg0 []) { if (argc < 2) { fprintf (stderr, "Usage: %s devicename\n", argv [0] ); return; } strlcpy (relayline, argv [1], MAXPATHLEN); } /* function for roomtempprobe keyword */ void roomtempprobe_command (int argc, char *argv [], char *arg0 []) { if (argc < 2) { fprintf (stderr, "Usage: %s temp\n", argv [0] ); return; } roomtempprobe = atoi (argv [1]); } /* function for serialline keyword */ void serialline_command (int argc, char *argv [], char *arg0 []) { if (argc < 2) { fprintf (stderr, "Usage: %s temp\n", argv [0] ); return; } strlcpy (serialline, argv [1], MAXPATHLEN); } /* function for starttemp keyword */ void starttemp_command (int argc, char *argv [], char *arg0 []) { if (argc < 2) { fprintf (stderr, "Usage: %s temp\n", argv [0] ); return; } starttemp = atof (argv [1]); } /* function for tcpport keyword */ void tcpport_command (int argc, char *argv [], char *arg0 []) { int tempport; if (argc < 2) { fprintf (stderr, "Usage: %s temp\n", argv [0] ); return; } tempport = atoi (argv [1]); if ((tempport > 1024) && (tempport < 65536)) /* valid tcp port */ tcpport = tempport; else fprintf (stderr, "Port number %d not in range 1024 to 65535\n", tempport); } /* XXX to do */ #if 0 void probetimeout_command (int argc, char *argv [], char *arg0 []) #endif