Added framework for a simulation of a fridge with heater to use as controlled fermentor

Fri, 22 Aug 2014 17:12:42 +0200

author
Michiel Broek <mbroek@mbse.eu>
date
Fri, 22 Aug 2014 17:12:42 +0200
changeset 259
b7c967359771
parent 258
e02393b29733
child 260
ef1469dd92e7

Added framework for a simulation of a fridge with heater to use as controlled fermentor

config.h.in file | annotate | diff | comparison | revisions
configure file | annotate | diff | comparison | revisions
configure.ac file | annotate | diff | comparison | revisions
thermferm/Makefile file | annotate | diff | comparison | revisions
thermferm/devices.c file | annotate | diff | comparison | revisions
thermferm/rdconfig.c file | annotate | diff | comparison | revisions
thermferm/server.c file | annotate | diff | comparison | revisions
thermferm/simulator.c file | annotate | diff | comparison | revisions
thermferm/simulator.h file | annotate | diff | comparison | revisions
thermferm/thermferm.c file | annotate | diff | comparison | revisions
thermferm/thermferm.h file | annotate | diff | comparison | revisions
--- a/config.h.in	Wed Aug 20 22:15:12 2014 +0200
+++ b/config.h.in	Fri Aug 22 17:12:42 2014 +0200
@@ -6,6 +6,9 @@
 /* Compile experimental code (may not be present) */
 #undef USE_EXPERIMENT
 
+/* Compile simulator code */
+#undef USE_SIMULATOR
+
 /* According to Sun we MUST define this in the source */
 #define _REENTRANT 1
 
--- a/configure	Wed Aug 20 22:15:12 2014 +0200
+++ b/configure	Fri Aug 22 17:12:42 2014 +0200
@@ -683,6 +683,7 @@
 ac_user_opts='
 enable_option_checking
 enable_experiment
+enable_simulator
 enable_debugging
 '
       ac_precious_vars='build_alias
@@ -1302,6 +1303,7 @@
   --disable-FEATURE       do not include FEATURE (same as --enable-FEATURE=no)
   --enable-FEATURE[=ARG]  include FEATURE [ARG=yes]
   --enable-experiment     Compile experimental code
+  --enable-simulator      Compile simulator code
   --enable-debugging      Compile for debugging
 
 Some influential environment variables:
@@ -2032,7 +2034,7 @@
 
 
 PACKAGE="mbsePi-apps"
-VERSION="0.2.0"
+VERSION="0.2.1"
 COPYRIGHT="Copyright (C) 2014 Michiel Broek, All Rights Reserved"
 CYEARS="2014"
 
@@ -3521,6 +3523,18 @@
 
 fi
 
+# Check whether --enable-simulator was given.
+if test "${enable_simulator+set}" = set; then :
+  enableval=$enable_simulator;  simulator=$enableval
+else
+   simulator=no
+fi
+
+if test "$simulator" = "yes"; then
+  $as_echo "#define USE_SIMULATOR 1" >>confdefs.h
+
+fi
+
 # Check whether --enable-debugging was given.
 if test "${enable_debugging+set}" = set; then :
   enableval=$enable_debugging;  debugging=$enableval
--- a/configure.ac	Wed Aug 20 22:15:12 2014 +0200
+++ b/configure.ac	Fri Aug 22 17:12:42 2014 +0200
@@ -8,7 +8,7 @@
 dnl General settings
 dnl After changeing the version number, run autoconf!
 PACKAGE="mbsePi-apps"
-VERSION="0.2.0"
+VERSION="0.2.1"
 COPYRIGHT="Copyright (C) 2014 Michiel Broek, All Rights Reserved"
 CYEARS="2014"
 AC_SUBST(PACKAGE)
@@ -47,6 +47,11 @@
   AC_DEFINE(USE_EXPERIMENT)
 fi
 
+AC_ARG_ENABLE(simulator,  [  --enable-simulator      Compile simulator code], [ simulator=$enableval ], [ simulator=no ])
+if test "$simulator" = "yes"; then
+  AC_DEFINE(USE_SIMULATOR)
+fi
+
 AC_ARG_ENABLE(debugging,   [  --enable-debugging      Compile for debugging], [ debugging=$enableval ], [ debugging=no ])
 if test "$debugging" = "yes"; then
   CFLAGS="-O -g -Wall -Wshadow -Wwrite-strings -Wstrict-prototypes -Winline"    
--- a/thermferm/Makefile	Wed Aug 20 22:15:12 2014 +0200
+++ b/thermferm/Makefile	Fri Aug 22 17:12:42 2014 +0200
@@ -62,8 +62,9 @@
 lock.o: lock.h thermferm.h
 logger.o: logger.h thermferm.h futil.h xutil.h
 lcd-pcf8574.o: thermferm.h lcd-pcf8574.h
-thermferm.o: lock.h logger.h rdconfig.h devices.h server.h thermferm.h lcd-pcf8574.h lcd-buffer.h panel.h futil.h xutil.h
+thermferm.o: lock.h logger.h rdconfig.h devices.h server.h thermferm.h simulator.h lcd-pcf8574.h lcd-buffer.h panel.h futil.h xutil.h
 xutil.o: thermferm.h xutil.h
 server.o: rdconfig.h thermferm.h logger.h devices.h server.h lcd-buffer.h xutil.h
+simulator.o: thermferm.h simulator.h
 rdconfig.o: rdconfig.h thermferm.h futil.h xutil.h
 # End of generated dependencies
--- a/thermferm/devices.c	Wed Aug 20 22:15:12 2014 +0200
+++ b/thermferm/devices.c	Fri Aug 22 17:12:42 2014 +0200
@@ -213,6 +213,11 @@
 		if ((device->type == DEVTYPE_W1) && (device->direction == DEVDIR_OUT_BIN) && (device->present == DEVPRESENT_YES)) {
 
 		}
+#ifdef USE_SIMULATOR
+		if ((device->type == DEVTYPE_SIM) && (device->direction == DEVDIR_OUT_BIN) && (device->present == DEVPRESENT_YES)) {
+
+		}
+#endif
 	    } else {
 		return 0;
 	    }
@@ -393,6 +398,70 @@
     }
 #endif
 
+#ifdef USE_SIMULATOR
+    found = FALSE;
+    for (device = Config.devices; device; device = device->next) {
+	if (device->type == DEVTYPE_SIM) {
+	    found = TRUE;
+	    break;
+	}
+    }
+
+    if (found == FALSE) {
+	subdevices = 5;
+	for (i = 0; i < subdevices; i++) {
+	    ndev = (devices_list *)malloc(sizeof(devices_list));
+	    ndev->next = NULL;
+	    ndev->version = 1;
+	    ndev->uuid = malloc(37);
+	    uuid_generate(uu);
+	    uuid_unparse(uu, ndev->uuid);
+	    ndev->type = DEVTYPE_SIM;
+	    ndev->value = ndev->offset = 0;
+	    ndev->present = DEVPRESENT_YES;
+	    ndev->subdevice = i;
+	    ndev->gpiopin = -1;
+	    ndev->comment = xstrcpy((char *)"Auto detected device");
+	    ndev->timestamp = time(NULL);
+	    ndev->inuse = 0;
+	    switch (i) {
+		case 0:	ndev->direction = DEVDIR_IN_ANALOG;
+			ndev->address = xstrcpy((char *)"SimRoomTemp");
+			ndev->description = xstrcpy((char *)"Simulated room temperature");
+			break;
+		case 1:	ndev->direction = DEVDIR_IN_ANALOG;
+			ndev->address = xstrcpy((char *)"SimAirTemp");
+			ndev->description = xstrcpy((char *)"Simulated air temperature");
+			break;
+		case 2:	ndev->direction = DEVDIR_IN_ANALOG;
+			ndev->address = xstrcpy((char *)"SimBeerTemp");
+			ndev->description = xstrcpy((char *)"Simulated beer temperature");
+			break;
+		case 3:	ndev->direction = DEVDIR_OUT_ANALOG;
+			ndev->address = xstrcpy((char *)"SimHeater");
+			ndev->description = xstrcpy((char *)"Simulated heater");
+			break;
+		case 4:	ndev->direction = DEVDIR_OUT_ANALOG;
+			ndev->address = xstrcpy((char *)"SimCooler");
+			ndev->description = xstrcpy((char *)"Simulated cooler");
+			break;
+	    }
+
+	    if (Config.devices == NULL) {
+		Config.devices = ndev;
+	    } else {
+		for (device = Config.devices; device; device = device->next) {
+		    if (device->next == NULL) {
+			device->next = ndev;
+			break;
+		    }
+		}
+	    }
+	    rc++;
+	}
+    }
+#endif
+
     return rc;
 }
 
@@ -536,7 +605,26 @@
 			}
 			break;
 #endif
-
+#ifdef USE_SIMULATOR
+		case DEVTYPE_SIM:
+#ifdef HAVE_WIRINGPI_H
+			piLock(LOCK_DEVICES);
+#endif
+			if (device->subdevice == 0) {
+			    device->value = 20000;
+			    device->timestamp = time(NULL);
+			} else if (device->subdevice == 1) {
+			    device->value = 20125;
+			    device->timestamp = time(NULL);
+			} else if (device->subdevice == 2) {
+			    device->value = 20250;
+			    device->timestamp = time(NULL);
+			}
+#ifdef HAVE_WIRINGPI_H
+			piUnlock(LOCK_DEVICES);
+#endif
+			break;
+#endif
 		default:
 			break;
 	    }
--- a/thermferm/rdconfig.c	Wed Aug 20 22:15:12 2014 +0200
+++ b/thermferm/rdconfig.c	Fri Aug 22 17:12:42 2014 +0200
@@ -34,7 +34,7 @@
 const char	TEMPSTATE[3][8] = { "OK", "MISSING", "ERROR" };
 const char      UNITMODE[5][8]	= { "OFF", "NONE", "FRIDGE", "BEER", "PROFILE" };
 const char	PROFSTATE[5][6]	= { "OFF", "PAUSE", "RUN", "DONE", "ABORT" };
-const char	DEVTYPE[7][6] = { "NA", "W1", "GPIO", "RC433", "DHT", "I2C", "SPI" };
+const char	DEVTYPE[8][6] = { "NA", "W1", "GPIO", "RC433", "DHT", "I2C", "SPI", "SIM" };
 const char	DEVPRESENT[4][6] = { "UNDEF", "NO", "YES", "ERROR" };
 const char	DEVDIR[7][11] = { "UNDEF", "IN_BIN", "OUT_BIN", "IN_ANALOG", "OUT_ANALOG", "OUT_PWM", "INTERN" };
 
@@ -46,6 +46,9 @@
     profiles_list	*tmp3;
     prof_step		*tmp4;
     devices_list	*device;
+#ifdef USE_SIMULATOR
+    simulator_list	*simulator;
+#endif
 
     if (Config.name)
 	free(Config.name);
@@ -112,6 +115,17 @@
     }
     Config.devices = NULL;
 
+#ifdef USE_SIMULATOR
+    for (simulator = Config.simulators; simulator; simulator = simulator->next) {
+	if (simulator->uuid)
+	    free(simulator->uuid);
+	if (simulator->name)
+	    free(simulator->name);
+	free(simulator);
+    }
+    Config.simulators = NULL;
+#endif
+
 #ifdef HAVE_WIRINGPI_H
     Config.lcd_cols = 16;
     Config.lcd_rows = 2;
@@ -131,6 +145,9 @@
     profiles_list	*tmp4;
     prof_step		*tmp5;
     devices_list	*device;
+#ifdef USE_SIMULATOR
+    simulator_list	*simulator;
+#endif
 
     /* 
      * Create a new XML buffer, to which the XML document will be written
@@ -585,6 +602,121 @@
 	}
     }
 
+#ifdef USE_SIMULATOR
+    if (Config.simulators) {
+	if ((rc = xmlTextWriterStartElement(writer, BAD_CAST "SIMULATORS")) < 0) {
+	    syslog(LOG_WARNING, "wrconfig: error at xmlTextWriterStartElement");
+	    return 1;
+	}
+	for (simulator = Config.simulators; simulator; simulator = simulator->next) {
+    	    if ((rc = xmlTextWriterStartElement(writer, BAD_CAST "SIMULATOR")) < 0) {
+	    	syslog(LOG_WARNING, "wrconfig: error at xmlTextWriterStartElement");
+	    	return 1;
+    	    }
+    	    if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "VERSION", "%d", simulator->version)) < 0) {
+	    	syslog(LOG_WARNING, "wrconfig: error at xmlTextWriterWriteElement");
+	    	return 1;
+    	    }
+	    if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "UUID", "%s", simulator->uuid)) < 0) {
+		syslog(LOG_WARNING, "wrconfig: error at xmlTextWriterWriteFormatElement");
+		return 1;
+	    }
+	    if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "NAME", "%s", simulator->name)) < 0) {
+		syslog(LOG_WARNING, "wrconfig: error at xmlTextWriterWriteFormatElement");
+		return 1;
+	    }
+	    if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "VOLUME_AIR", "%d", simulator->volume_air)) < 0) {
+	    	syslog(LOG_WARNING, "wrconfig: error at xmlTextWriterWriteElement");
+	    	return 1;
+	    }
+	    if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "VOLUME_BEER", "%d", simulator->volume_beer)) < 0) {
+	    	syslog(LOG_WARNING, "wrconfig: error at xmlTextWriterWriteElement");
+	    	return 1;
+	    }
+	    if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "ROOM_TEMPERATURE", "%.1f", simulator->room_temperature)) < 0) {
+	   	syslog(LOG_WARNING, "wrconfig: error at xmlTextWriterWriteElement");
+	   	return 1;
+	    }
+	    if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "AIR_TEMPERATURE", "%.1f", simulator->air_temperature)) < 0) {
+	    	syslog(LOG_WARNING, "wrconfig: error at xmlTextWriterWriteElement");
+	    	return 1;
+	    }
+	    if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "BEER_TEMPERATURE", "%.1f", simulator->beer_temperature)) < 0) {
+	    	syslog(LOG_WARNING, "wrconfig: error at xmlTextWriterWriteElement");
+	    	return 1;
+	    }
+	    if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "COOLER_TEMP", "%.1f", simulator->cooler_temp)) < 0) {
+	    	syslog(LOG_WARNING, "wrconfig: error at xmlTextWriterWriteElement");
+	    	return 1;
+	    }
+	    if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "COOLER_TIME", "%d", simulator->cooler_time)) < 0) {
+	    	syslog(LOG_WARNING, "wrconfig: error at xmlTextWriterWriteElement");
+	    	return 1;
+	    }
+	    if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "COOLER_SIZE", "%.3f", simulator->cooler_size)) < 0) {
+	    	syslog(LOG_WARNING, "wrconfig: error at xmlTextWriterWriteElement");
+	    	return 1;
+	    }
+	    if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "HEATER_TEMP", "%.1f", simulator->heater_temp)) < 0) {
+	    	syslog(LOG_WARNING, "wrconfig: error at xmlTextWriterWriteElement");
+	    	return 1;
+	    }
+	    if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "HEATER_TIME", "%d", simulator->heater_time)) < 0) {
+	    	syslog(LOG_WARNING, "wrconfig: error at xmlTextWriterWriteElement");
+	    	return 1;
+	    }
+	    if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "HEATER_SIZE", "%.3f", simulator->heater_size)) < 0) {
+	    	syslog(LOG_WARNING, "wrconfig: error at xmlTextWriterWriteElement");
+	    	return 1;
+	    }
+	    if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "HEATER_STATE", "%d", simulator->heater_state)) < 0) {
+	    	syslog(LOG_WARNING, "wrconfig: error at xmlTextWriterWriteElement");
+	    	return 1;                           
+	    }
+	    if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "COOLER_STATE", "%d", simulator->cooler_state)) < 0) {
+	    	syslog(LOG_WARNING, "wrconfig: error at xmlTextWriterWriteElement");
+	    	return 1;                       
+	    }
+	    if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "FRIGO_ISOLATION", "%.3f", simulator->frigo_isolation)) < 0) {
+	    	syslog(LOG_WARNING, "wrconfig: error at xmlTextWriterWriteElement");
+	    	return 1;
+	    }
+	    if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "S_YEAST_HEAT", "%.1f", simulator->s_yeast_heat)) < 0) {
+	    	syslog(LOG_WARNING, "wrconfig: error at xmlTextWriterWriteElement");
+	    	return 1;
+	    }
+	    if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "S_YEAST_STARTED", "%d", (int)simulator->s_yeast_started)) < 0) {
+	    	syslog(LOG_WARNING, "wrconfig: error at xmlTextWriterWriteElement");
+	    	return 1;
+	    }
+	    if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "S_COOL_TEMP", "%.1f", simulator->s_cool_temp)) < 0) {
+	    	syslog(LOG_WARNING, "wrconfig: error at xmlTextWriterWriteElement");
+	    	return 1;
+	    }
+	    if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "S_HEAT_TEMP", "%.1f", simulator->s_heat_temp)) < 0) {
+	    	syslog(LOG_WARNING, "wrconfig: error at xmlTextWriterWriteElement");
+	    	return 1;
+	    }
+	    if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "S_COOL_CHANGED", "%d", (int)simulator->s_cool_changed)) < 0) {
+	    	syslog(LOG_WARNING, "wrconfig: error at xmlTextWriterWriteElement");
+	    	return 1;
+	    }
+	    if ((rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "S_HEAT_CHANGED", "%d", (int)simulator->s_heat_changed)) < 0) {
+	    	syslog(LOG_WARNING, "wrconfig: error at xmlTextWriterWriteElement");
+	    	return 1;
+	    }
+    	    if ((rc = xmlTextWriterEndElement(writer)) < 0) {
+	    	syslog(LOG_WARNING, "wrconfig: error at xmlTextWriterEndElement");
+	    	return 1;
+    	    }
+	}
+	if ((rc = xmlTextWriterEndElement(writer)) < 0) {
+	    syslog(LOG_WARNING, "wrconfig: error at xmlTextWriterEndElement");
+	    return 1;
+	}
+    }
+#endif
+
     /*
      * All done, close any open elements
      */
@@ -1065,7 +1197,7 @@
 	}
 	if ((!xmlStrcmp(cur->name, (const xmlChar *)"TYPE"))) {
 	    key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
-	    for (i = 0; i < 7; i++) {
+	    for (i = 0; i < 8; i++) {
 		if (! xmlStrcmp(key, (const xmlChar *)DEVTYPE[i])) {
 		    device->type = i;
 		    break;
@@ -1172,6 +1304,198 @@
 
 
 
+#ifdef USE_SIMULATOR
+int parseSimulator(xmlDocPtr doc, xmlNodePtr cur)
+{
+    xmlChar             *key;
+    simulator_list      *simulator, *tmp;
+    int                 ival;
+    float		fval;
+
+    simulator = (simulator_list *)malloc(sizeof(simulator_list));
+    simulator->next = NULL;
+    simulator->version = 1;
+    simulator->uuid = simulator->name = NULL;
+    simulator->volume_air = simulator->volume_beer = 0;
+    simulator->room_temperature = simulator->air_temperature = simulator->beer_temperature = simulator->s_cool_temp = simulator->s_heat_temp = 20.0;
+    simulator->cooler_temp = simulator->cooler_size = simulator->heater_temp = simulator->heater_size = simulator->frigo_isolation = 0.0;
+    simulator->cooler_time = simulator->heater_time = simulator->cooler_state = simulator->heater_state = 0;
+    simulator->s_yeast_started = simulator->s_cool_changed = simulator->s_heat_changed = (time_t)0;
+    simulator->s_yeast_heat = simulator->s_cool_temp = simulator->s_heat_temp = 0.0;
+
+    cur = cur->xmlChildrenNode;
+    while (cur != NULL) {
+	if ((!xmlStrcmp(cur->name, (const xmlChar *)"VERSION"))) {
+	    key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
+	    if (xmlStrcmp(key, (const xmlChar *)"1")) {
+		xmlFree(key);
+		return 1;
+	    }
+	    simulator->version = 1;
+	    xmlFree(key);
+	}
+	if ((!xmlStrcmp(cur->name, (const xmlChar *)"UUID"))) {
+	    simulator->uuid = (char *)xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
+	}
+	if ((!xmlStrcmp(cur->name, (const xmlChar *)"NAME"))) {
+	    simulator->name = (char *)xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
+	}
+	if ((!xmlStrcmp(cur->name, (const xmlChar *)"VOLUME_AIR"))) {
+	    key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
+	    if (sscanf((const char *)key, "%d", &ival) == 1)
+		simulator->volume_air = ival;
+	    xmlFree(key);
+	}
+	if ((!xmlStrcmp(cur->name, (const xmlChar *)"VOLUME_BEER"))) {
+	    key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
+	    if (sscanf((const char *)key, "%d", &ival) == 1)
+		simulator->volume_beer = ival;
+	    xmlFree(key);
+	}
+	if ((!xmlStrcmp(cur->name, (const xmlChar *)"ROOM_TEMPERATURE"))) {
+	    key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
+	    if (sscanf((const char *)key, "%f", &fval) == 1)
+		simulator->room_temperature = fval;
+	    xmlFree(key);
+	}
+	if ((!xmlStrcmp(cur->name, (const xmlChar *)"AIR_TEMPERATURE"))) {
+	    key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
+	    if (sscanf((const char *)key, "%f", &fval) == 1)
+		simulator->air_temperature = fval;
+	    xmlFree(key);
+	}
+	if ((!xmlStrcmp(cur->name, (const xmlChar *)"BEER_TEMPERATURE"))) {
+	    key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
+	    if (sscanf((const char *)key, "%f", &fval) == 1)
+		simulator->beer_temperature = fval;
+	    xmlFree(key);
+	}
+	if ((!xmlStrcmp(cur->name, (const xmlChar *)"COOLER_TEMP"))) {
+	    key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
+	    if (sscanf((const char *)key, "%f", &fval) == 1)
+		simulator->cooler_temp = fval;
+	    xmlFree(key);
+	}
+	if ((!xmlStrcmp(cur->name, (const xmlChar *)"COOLER_TIME"))) {
+	    key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
+	    if (sscanf((const char *)key, "%d", &ival) == 1)
+		simulator->cooler_time = ival;
+	    xmlFree(key);
+	}
+	if ((!xmlStrcmp(cur->name, (const xmlChar *)"COOLER_SIZE"))) {
+	    key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
+	    if (sscanf((const char *)key, "%f", &fval) == 1)
+		simulator->cooler_size = fval;
+	    xmlFree(key);
+	}
+	if ((!xmlStrcmp(cur->name, (const xmlChar *)"HEATER_TEMP"))) {
+	    key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
+	    if (sscanf((const char *)key, "%f", &fval) == 1)
+		simulator->heater_temp = fval;
+	    xmlFree(key);
+	}
+	if ((!xmlStrcmp(cur->name, (const xmlChar *)"HEATER_TIME"))) {
+	    key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
+	    if (sscanf((const char *)key, "%d", &ival) == 1)
+		simulator->heater_time = ival;
+	    xmlFree(key);
+	}
+	if ((!xmlStrcmp(cur->name, (const xmlChar *)"HEATER_SIZE"))) {
+	    key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
+	    if (sscanf((const char *)key, "%f", &fval) == 1)
+		simulator->heater_size = fval;
+	    xmlFree(key);
+	}
+	if ((!xmlStrcmp(cur->name, (const xmlChar *)"HEATER_STATE"))) {
+	    key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
+	    if (sscanf((const char *)key, "%d", &ival) == 1)
+		simulator->heater_state = ival;
+	    xmlFree(key);
+	}
+	if ((!xmlStrcmp(cur->name, (const xmlChar *)"COOLER_STATE"))) {
+	    key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
+	    if (sscanf((const char *)key, "%d", &ival) == 1)
+		simulator->cooler_state = ival;
+	    xmlFree(key);
+	}
+	if ((!xmlStrcmp(cur->name, (const xmlChar *)"FRIGO_ISOLATION"))) {
+	    key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
+	    if (sscanf((const char *)key, "%f", &fval) == 1)
+		simulator->frigo_isolation = fval;
+	    xmlFree(key);
+	}
+
+	if ((!xmlStrcmp(cur->name, (const xmlChar *)"S_YEAST_HEAT"))) {
+	    key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
+	    if (sscanf((const char *)key, "%f", &fval) == 1)
+		simulator->s_yeast_heat = fval;
+	    xmlFree(key);
+	}
+	if ((!xmlStrcmp(cur->name, (const xmlChar *)"S_YEAST_STARTED"))) {
+	    key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
+	    if (sscanf((const char *)key, "%d", &ival) == 1)
+		simulator->s_yeast_started = (time_t)ival;
+	    xmlFree(key);
+	}
+	if ((!xmlStrcmp(cur->name, (const xmlChar *)"S_COOL_TEMP"))) {
+	    key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
+	    if (sscanf((const char *)key, "%f", &fval) == 1)
+		simulator->s_cool_temp = fval;
+	    xmlFree(key);
+	}
+	if ((!xmlStrcmp(cur->name, (const xmlChar *)"S_HEAT_TEMP"))) {
+	    key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
+	    if (sscanf((const char *)key, "%f", &fval) == 1)
+		simulator->s_heat_temp = fval;
+	    xmlFree(key);
+	}
+	if ((!xmlStrcmp(cur->name, (const xmlChar *)"S_COOL_CHANGED"))) {
+	    key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
+	    if (sscanf((const char *)key, "%d", &ival) == 1)
+		simulator->s_cool_changed = (time_t)ival;
+	    xmlFree(key);
+	}
+	if ((!xmlStrcmp(cur->name, (const xmlChar *)"S_HEAT_CHANGED"))) {
+	    key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
+	    if (sscanf((const char *)key, "%d", &ival) == 1)
+		simulator->s_heat_changed = (time_t)ival;
+	    xmlFree(key);
+	}
+
+	cur = cur->next;
+    }
+
+    if (Config.simulators == NULL) {
+	Config.simulators = simulator;
+    } else {
+	for (tmp = Config.simulators; tmp; tmp = tmp->next) {
+	    if (tmp->next == NULL) {
+		tmp->next = simulator;
+		break;
+	    }
+	}
+    }
+
+    return 0;
+}
+
+
+
+int parseSimulators(xmlDocPtr doc, xmlNodePtr cur)
+{
+    cur = cur->xmlChildrenNode;
+    while (cur != NULL) {
+	if ((!xmlStrcmp(cur->name, (const xmlChar *)"SIMULATOR"))) {
+	    parseSimulator(doc, cur);
+	}
+	cur = cur->next;
+    }
+    return 0;
+}
+#endif
+
+
+
 int rdconfig(void) 
 {
     int		rc = 0, ival;
@@ -1269,6 +1593,11 @@
 	if ((!xmlStrcmp(cur->name, (const xmlChar *)"DEVICES"))) {
 	    parseDevices(doc, cur);
 	}
+#ifdef USE_SIMULATOR
+	if ((!xmlStrcmp(cur->name, (const xmlChar *)"SIMULATORS"))) {
+	    parseSimulators(doc, cur);
+	}
+#endif
 	cur = cur->next;
     }
     xmlFreeDoc(doc);
--- a/thermferm/server.c	Wed Aug 20 22:15:12 2014 +0200
+++ b/thermferm/server.c	Fri Aug 22 17:12:42 2014 +0200
@@ -36,7 +36,7 @@
 extern sys_config       Config;
 extern const char	UNITMODE[5][8];
 extern const char	TEMPSTATE[3][8];
-extern const char	DEVTYPE[7][6];
+extern const char	DEVTYPE[8][6];
 extern const char	DEVPRESENT[4][6];
 extern const char	DEVDIR[7][11];
 extern const char	PROFSTATE[5][6];
@@ -241,7 +241,7 @@
 	    srv_send((char *)"%s,%s,%d,%d,%s,%s", device->uuid, device->address, device->subdevice, device->inuse, device->comment, DEVDIR[device->direction]);
 	}
 	srv_send((char *)".");
-	return 0;
+	return 1;
     }
 
     if (param == NULL) {
@@ -259,7 +259,7 @@
 	    device->uuid = malloc(37);
 	    uuid_generate(uu);
 	    uuid_unparse(uu, device->uuid);
-	    for (i = 0; i < 7; i++) {
+	    for (i = 0; i < 8; i++) {
 		if (strcmp(param, DEVTYPE[i]) == 0) {
 		    device->type = i;
 		    break;
@@ -379,7 +379,7 @@
 			val = strtok(NULL, "\0");
 			if (kwd && val) {
 			    if (strcmp(kwd, (char *)"TYPE") == 0) {
-				for (i = 0; i < 7; i++) {
+				for (i = 0; i < 8; i++) {
 				    if (strcmp(val, DEVTYPE[i]) == 0) {
 					device->type = i;
 					break;
@@ -601,7 +601,7 @@
 	    srv_send((char *)"%s,%s,%s", unit->uuid, unit->name, UNITMODE[unit->mode]);
 	}
 	srv_send((char *)".");
-	return 0;
+	return 1;
 
     } else if (strcmp(opt, (char *)"LOG") == 0) {
 
@@ -642,7 +642,7 @@
 	free(filename);
 	filename = NULL;
 	srv_send((char *)".");
-	return 0;
+	return 1;
     }
 
     srv_send((char *)"504 Subcommand error");
@@ -905,6 +905,282 @@
 
 
 
+#ifdef USE_SIMULATOR
+int delete_Simulator(char *uuid)
+{
+    simulator_list	*current = Config.simulators;
+    simulator_list	*previous = NULL;
+
+    while (current) {
+	if (strcmp(current->uuid, uuid) == 0) {
+	    if (previous == NULL) {
+		Config.simulators = current->next;
+	    	free(current->uuid);
+	    	current->uuid = NULL;
+	    	free(current->name);
+	    	current->name = NULL;
+	    	free(current);
+	    	return 1;
+	    } else {
+		free(current->uuid);
+		current->uuid = NULL;
+		free(current->name);
+		current->name = NULL;
+		previous->next = current->next;
+		free(current);
+		current = previous->next;
+		return 1;
+	    }
+	} else {
+	    previous = current;
+	    current = current->next;
+	}
+    }
+    return 0;
+}
+
+
+
+/*
+ * SIMULATOR ADD name
+ * SIMULATOR DEL uuid
+ * SIMULATOR LIST
+ * SIMULATOR GET uuid
+ * SIMULATOR PUT uuid
+ */
+int cmd_simulator(char *buf)
+{
+    char		*opt, *param, *kwd, *val, ibuf[SS_BUFSIZE];
+    simulator_list	*simulator, *tmps;
+    socklen_t		fromlen;
+    int			i, rc, rlen, ival;
+    float		fval;
+    uuid_t		uu;
+
+    opt = strtok(buf, " \0");
+    opt = strtok(NULL, " \0");
+
+    if (opt == NULL) {
+	srv_send((char *)"501 Subcommand missing");
+	return 1;
+    }
+    param = strtok(NULL, "\0");
+
+    if (strcmp(opt, (char *)"LIST") == 0) {
+	srv_send((char *)"212 Simulators list follows:");
+	for (simulator = Config.simulators; simulator; simulator = simulator->next) {
+	    srv_send((char *)"%s,%s", simulator->uuid, simulator->name);
+	}
+	srv_send((char *)".");
+	return 1;
+    }
+
+    if (param == NULL) {
+	srv_send((char *)"502 Parameter missing");
+	return 1;
+    }
+
+    if (strcmp(opt, (char *)"ADD") == 0) {
+
+	/*
+	 * For now, only one simulator is allowed.
+	 */
+	if (Config.simulators) {
+	    srv_send((char *)"441 Maximum simulators reached");
+	    return 1;
+	}
+
+	simulator = (simulator_list *)malloc(sizeof(simulator_list));
+	simulator->next = NULL;
+	simulator->version = 1;
+	simulator->uuid = malloc(37);
+	uuid_generate(uu);
+	uuid_unparse(uu, simulator->uuid);
+	simulator->name = xstrcpy(param);
+	simulator->volume_air = 150;
+	simulator->volume_beer = 50;
+	simulator->room_temperature = simulator->air_temperature = simulator->beer_temperature = simulator->s_cool_temp = simulator->s_heat_temp = 20.0;
+	simulator->cooler_temp = -3.0;	/* Cooling temperature */
+	simulator->cooler_time = 720;	/* About 12 minutes for the cooler plate */
+	simulator->cooler_size = 0.8;	/* 0.8 square meter cooler plate */
+	simulator->heater_temp = 150.0;	/* Heating temperature */
+	simulator->heater_time = 3;	/* 3 seconds to heat-up	*/
+	simulator->heater_size = 0.01;	/* 0.01 square meter heater plate */
+	simulator->heater_state = simulator->cooler_state = 0;
+	simulator->frigo_isolation = 0.002;
+	simulator->s_yeast_heat = 0.0;
+	simulator->s_yeast_started = simulator->s_cool_changed = simulator->s_heat_changed = (int)0;
+
+	if (Config.simulators == NULL) {
+	    Config.simulators = simulator;
+	} else {
+	    for (tmps = Config.simulators; tmps; tmps = tmps->next) {
+		if (tmps->next == NULL) {
+		    tmps->next = simulator;
+		    break;
+		}
+	    }
+	}
+
+	syslog(LOG_NOTICE, "Simulator %s added", simulator->uuid);
+	srv_send((char *)"211 Simulator %s added", simulator->uuid);
+	return 0;
+    }
+
+    if (strcmp(opt, (char *)"DEL") == 0) {
+	rc = delete_Simulator(param);
+	if (rc) {
+	    syslog(LOG_NOTICE, "Simulator %s deleted", param);
+	    srv_send((char *)"211 Simulator %s deleted", param);
+	    return 0;
+	} else {
+	    srv_send((char *)"440 No such simulator");
+	    return 1;
+	}
+    }
+
+    if (strcmp(opt, (char *)"GET") == 0) {
+	for (simulator = Config.simulators; simulator; simulator = simulator->next) {
+	    if (strcmp(simulator->uuid, param) == 0) {
+		srv_send((char *)"213 Simulator record follows:");
+		srv_send((char *)"NAME,%s", simulator->name);
+		srv_send((char *)"VOLUME_AIR,%d", simulator->volume_air);
+		srv_send((char *)"VOLUME_BEER,%d", simulator->volume_beer);
+		srv_send((char *)"ROOM_TEMPERATURE,%.1f", simulator->room_temperature);
+		srv_send((char *)"AIR_TEMPERATURE,%.3f", simulator->air_temperature);
+		srv_send((char *)"BEER_TEMPERATURE,%.3f", simulator->beer_temperature);
+		srv_send((char *)"COOLER_TEMP,%.1f", simulator->cooler_temp);
+		srv_send((char *)"COOLER_TIME,%d", simulator->cooler_time);
+		srv_send((char *)"COOLER_SIZE,%.3d", simulator->cooler_size);
+		srv_send((char *)"HEATER_TEMP,%.1f", simulator->heater_temp);
+		srv_send((char *)"HEATER_TIME,%d", simulator->heater_time);
+		srv_send((char *)"HEATER_SIZE,%.3f", simulator->heater_size);
+		srv_send((char *)"HEATER_STATE,%d", simulator->heater_state);
+		srv_send((char *)"COOLER_STATE,%d", simulator->cooler_state);
+		srv_send((char *)"FRIGO_ISOLATION,%.6f", simulator->frigo_isolation);
+		srv_send((char *)".");
+		return 1;
+	    }
+	}
+	srv_send((char *)"440 No such simulator");
+	return 1;
+    }
+
+    if (strcmp(opt, (char *)"PUT") == 0) {
+	for (simulator = Config.simulators; simulator; simulator = simulator->next) {
+	    if (strcmp(simulator->uuid, param) == 0) {
+		while (1) {
+		    memset((char *)&ibuf, 0, SS_BUFSIZE);
+		    fromlen = sizeof(peeraddr_in);
+		    rlen = recvfrom(s, ibuf, sizeof(ibuf) -1, 0, (struct sockaddr *)&peeraddr_in, &fromlen);
+		    if (rlen == -1) {
+			syslog(LOG_WARNING, "recvfrom(): %s", strerror(errno));
+			srv_send((char *)"518 recfrom(): %s", strerror(errno));
+			return 1;
+		    }
+		    for (i = 0; i < strlen(ibuf); i++) {
+			if (ibuf[i] == '\n')
+			    ibuf[i] = '\0';
+			if (ibuf[i] == '\r')
+			    ibuf[i] = '\0';
+		    }
+		    for (i = strlen(ibuf) -1; i > 0; i--) {
+			if (ibuf[i] == ' ')
+			    ibuf[i] = '\0';
+			else
+			    break;
+		    }
+		    if (strlen(ibuf)) {
+			if (debug) {
+			    syslog(LOG_NOTICE, "recv: \"%s\"", ibuf);
+			    fprintf(stdout, "recv: \"%s\"\n", ibuf);
+			}
+			if (strcmp(ibuf, (char *)".") == 0) {
+			    srv_send((char *)"219 Accepted Simulator record");
+			    return 0;
+			}
+			kwd = strtok(ibuf, ",\0");
+			val = strtok(NULL, "\0");
+			if (kwd && val) {
+
+			    if (strcmp(kwd, (char *)"NAME") == 0) {
+				if (simulator->name)
+				    free(simulator->name);
+				simulator->name = xstrcpy(val);
+
+			    } else if (strcmp(kwd, (char *)"VOLUME_AIR") == 0) {
+				if (sscanf(val, "%d", &ival) == 1)
+				    simulator->volume_air = ival;
+
+			    } else if (strcmp(kwd, (char *)"VOLUME_BEER") == 0) {
+				if (sscanf(val, "%d", &ival) == 1)
+				    simulator->volume_beer = ival;
+
+			    } else if (strcmp(kwd, (char *)"ROOM_TEMPERATURE") == 0) {
+				if (sscanf(val, "%f", &fval) == 1)
+				    simulator->room_temperature = fval;
+
+			    } else if (strcmp(kwd, (char *)"AIR_TEMPERATURE") == 0) {
+				if (sscanf(val, "%f", &fval) == 1)
+				    simulator->air_temperature = fval;
+
+			    } else if (strcmp(kwd, (char *)"BEER_TEMPERATURE") == 0) {
+				if (sscanf(val, "%f", &fval) == 1)
+				    simulator->beer_temperature = fval;
+
+			    } else if (strcmp(kwd, (char *)"COOLER_TEMP") == 0) {
+				if (sscanf(val, "%f", &fval) == 1)
+				    simulator->cooler_temp = fval;
+
+			    } else if (strcmp(kwd, (char *)"COOLER_TIME") == 0) {
+				if (sscanf(val, "%d", &ival) == 1)
+				    simulator->cooler_time = ival;
+	
+			    } else if (strcmp(kwd, (char *)"COOLER_SIZE") == 0) {
+				if (sscanf(val, "%f", &fval) == 1)
+				    simulator->cooler_size = fval;
+
+			    } else if (strcmp(kwd, (char *)"HEATER_TEMP") == 0) {
+				if (sscanf(val, "%f", &fval) == 1)
+				    simulator->heater_temp = fval;
+
+			    } else if (strcmp(kwd, (char *)"HEATER_TIME") == 0) {
+				if (sscanf(val, "%d", &ival) == 1)
+				    simulator->heater_time = ival;
+
+			    } else if (strcmp(kwd, (char *)"HEATER_SIZE") == 0) {
+				if (sscanf(val, "%f", &fval) == 1)
+				    simulator->heater_size = fval;
+
+			    } else if (strcmp(kwd, (char *)"HEATER_STATE") == 0) {
+				if (sscanf(val, "%d", &ival) == 1)
+				    simulator->heater_state = ival;
+
+			    } else if (strcmp(kwd, (char *)"COOLER_STATE") == 0) {
+				if (sscanf(val, "%d", &ival) == 1)
+				    simulator->cooler_state = ival;
+
+			    } else if (strcmp(kwd, (char *)"FRIGO_ISOLATION") == 0) {
+				if (sscanf(val, "%f", &fval) == 1)
+				    simulator->frigo_isolation = fval;
+
+			    }
+			}
+		    }
+		}
+	    }
+	}
+	srv_send((char *)"440 No such simulator");
+	return 1;
+    }
+
+    srv_send((char *)"504 Subcommand error");
+    return 1;
+}
+#endif
+
+
+
 int delete_Unit(char *uuid)
 {
     units_list  *current = Config.units;
@@ -1381,7 +1657,6 @@
 	return 1;
     }
 
-
     srv_send((char *)"504 Subcommand error");
     return 1;
 }
@@ -1442,14 +1717,21 @@
 		srv_send((char *)"LIST                          List all fermenter units");
 		srv_send((char *)"LIST LOG uuid                 List logfile data in 1 hour lines");
 		srv_send((char *)"PROFILE uuid,name             Profile rename");
-		srv_send((char *)"PROFILE ADD name              Add new profile with name");
-		srv_send((char *)"PROFILE DEL uuid              Delete profile by uuid");
+		srv_send((char *)"PROFILE ADD name              Add new Profile with name");
+		srv_send((char *)"PROFILE DEL uuid              Delete Profile by uuid");
 		srv_send((char *)"PROFILE LIST                  List available profiles");
 		srv_send((char *)"PROFILE GET uuid              Get Profile record by uuid");
 		srv_send((char *)"PROFILE PUT uuid              Put Profile record by uuid");
 		srv_send((char *)"PROFILE GETS uuid             Profile get steps list");
 		srv_send((char *)"PROFILE PUTS uuid             Profile put steps list");
-		srv_send((char *)"UNIT ADD name                 Add a new unit with name");
+#ifdef USE_SIMULATOR
+		srv_send((char *)"SIMULATOR ADD name            Add a new Simulator with name");
+		srv_send((char *)"SIMULATOR DEL uuid            Delete Simulator by uuid");
+		srv_send((char *)"SIMULATOR LIST                List all Simulators");
+		srv_send((char *)"SIMULATOR GET uuid            Get Simulator record by uuid");
+		srv_send((char *)"SIMULATOR PUT uuid            Put Simulator record by uuid");
+#endif
+		srv_send((char *)"UNIT ADD name                 Add a new Unit with name");
 		srv_send((char *)"UNIT DEL uuid                 Delete Unit by uuid");
 		srv_send((char *)"UNIT LIST                     List all Units");
 		srv_send((char *)"UNIT GET uuid                 Get Unit record by uuid");
@@ -1460,6 +1742,11 @@
 	    } else if (strncmp(buf, "PROFILE", 7) == 0) {
 		if (cmd_profile(buf) == 0)
 		    wrconfig();
+#ifdef USE_SIMULATOR
+	    } else if (strncmp(buf, "SIMULATOR", 9) == 0) {
+		if (cmd_simulator(buf) == 0)
+		    wrconfig();
+#endif
 	    } else if (strncmp(buf, "UNIT", 4) == 0) {
 		if (cmd_unit(buf) == 0)
 		    wrconfig();
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/thermferm/simulator.c	Fri Aug 22 17:12:42 2014 +0200
@@ -0,0 +1,59 @@
+/*****************************************************************************
+ * Copyright (C) 2008-2014
+ *   
+ * 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 "thermferm.h"
+#include "simulator.h"
+
+#ifdef USE_SIMULATOR
+
+extern int		my_shutdown;
+extern int		debug;
+
+
+
+#ifdef HAVE_WIRINGPI_H
+PI_THREAD (my_simulator_loop)
+#else
+void *my_simulator_loop(void *threadid)
+#endif
+{
+
+    syslog(LOG_NOTICE, "Thread my_simulator_loop started");
+    if (debug)
+	fprintf(stdout, "Thread my_simulator_loop started\n");
+
+    for (;;) {
+
+	if (my_shutdown)
+	    break;
+
+	usleep(100000);
+    }
+
+    syslog(LOG_NOTICE, "Thread my_simulator_loop stopped");
+    if (debug)
+	fprintf(stdout, "Thread my_simulator_loop stopped\n");
+    return 0;
+}
+
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/thermferm/simulator.h	Fri Aug 22 17:12:42 2014 +0200
@@ -0,0 +1,15 @@
+#ifndef SIMULATOR_H
+#define	SIMULATOR_H
+
+#ifdef USE_SIMULATOR
+
+
+#ifdef HAVE_WIRINGPI_H
+PI_THREAD (my_simulator_loop);
+#else
+void *my_simulator_loop(void *);
+#endif
+
+#endif
+
+#endif
--- a/thermferm/thermferm.c	Wed Aug 20 22:15:12 2014 +0200
+++ b/thermferm/thermferm.c	Fri Aug 22 17:12:42 2014 +0200
@@ -26,6 +26,7 @@
 #include "devices.h"
 #include "server.h"
 #include "thermferm.h"
+#include "simulator.h"
 #include "lcd-pcf8574.h"
 #include "lcd-buffer.h"
 #include "panel.h"
@@ -49,7 +50,7 @@
 #endif
 
 #ifndef HAVE_WIRINGPI_H
-pthread_t		threads[4];
+pthread_t		threads[5];
 #endif
 extern const char       UNITMODE[5][8];
 extern const char	PROFSTATE[4][6];
@@ -889,6 +890,23 @@
     }
 #endif
 
+
+#ifdef USE_SIMULATOR
+#ifdef HAVE_WIRINGPI_H
+    rc = piThreadCreate(my_simulator_loop);
+#else
+    rc = pthread_create(&threads[t], NULL, my_simulator_loop, (void *)t );
+#endif
+    if (rc) {
+	fprintf(stderr, "my_simulator_loop thread didn't start rc=%d\n", rc);
+	syslog(LOG_WARNING, "my_simulator_loop thread didn't start rc=%d", rc);
+#ifndef HAVE_WIRINGPI_H
+    } else {
+	t++;
+#endif
+    }
+#endif
+
     /*
      * Initialize units for processing
      */
--- a/thermferm/thermferm.h	Wed Aug 20 22:15:12 2014 +0200
+++ b/thermferm/thermferm.h	Fri Aug 22 17:12:42 2014 +0200
@@ -201,6 +201,9 @@
 #define	DEVTYPE_DHT		4		/* DHT type device on GPIO	*/
 #define	DEVTYPE_I2C		5		/* I2C bus device		*/
 #define	DEVTYPE_SPI		6		/* SPI bus device		*/
+#ifdef USE_SIMULATOR
+#define DEVTYPE_SIM		7		/* Simulated device		*/
+#endif
 
 #define	DEVPRESENT_UNDEF	0		/* Precence not testable	*/
 #define	DEVPRESENT_NO		1		/* Device is missing		*/
@@ -215,6 +218,46 @@
 #define	DEVDIR_OUT_PWM		5		/* PWM outout			*/
 #define	DEVDIR_INTERN		6		/* Internal function		*/
 
+#ifdef USE_SIMULATOR
+
+/*
+ * The frigo is a simulation of a fridge with a heating device. 
+ * It has a volume air, a volume of your beer. There is a simulated
+ * thermal sensor that measures the air and one that measures the beer.
+ * It looks like a normal live setup.
+ */
+typedef struct _simulator {
+    struct _simulator	*next;
+    int			version;		/* Version of this record	*/
+    char		*uuid;			/* Simulator uuid		*/
+    char		*name;			/* Simulator name		*/
+    int			volume_air;		/* Volume air of the frigo	*/
+    int			volume_beer;		/* Volume beer inside frigo	*/
+    float		room_temperature;	/* Temp outside frigo		*/
+    float		air_temperature;	/* Simulated air temperature	*/
+    float		beer_temperature;	/* Simulated beer temperature	*/
+    float		cooler_temp;		/* Lowest cooler temperature	*/
+    int			cooler_time;		/* Time to reach temperature	*/
+    float		cooler_size;		/* Size of cooler in square mtr	*/
+    float		heater_temp;		/* Highest heater temperature	*/
+    int			heater_time;		/* Time to reach temperature	*/
+    float		heater_size;		/* Size of heater in square mtr	*/
+    int			heater_state;		/* Heater status		*/
+    int			cooler_state;		/* Cooler status		*/
+    float		frigo_isolation;	/* Frigo isolation value	*/
+    /*
+     * Status values, maintained by the simulator but stored
+     * here so they don't get lost over program restarts.
+     */
+    float		s_yeast_heat;		/* Heat generated by yeast	*/
+    time_t		s_yeast_started;	/* Start date/time fermentation	*/
+    float		s_cool_temp;		/* Temp cooler			*/
+    float		s_heat_temp;		/* Temp heater			*/
+    time_t		s_cool_changed;		/* Start date/time cooler	*/
+    time_t		s_heat_changed;		/* Start date/time heater	*/
+} simulator_list;
+
+#endif
 
 typedef struct _sys_config {
     char		*name;			/* Configuration name		*/
@@ -234,6 +277,9 @@
     units_list		*units;			/* Fermenter units		*/
     profiles_list	*profiles;		/* Ferment profiles		*/
     devices_list	*devices;		/* Sensors and switches		*/
+#ifdef USE_SIMULATOR
+    simulator_list	*simulators;		/* Simulators			*/
+#endif
 } sys_config;
 
 

mercurial