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) 2014..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 "devices.h" #include "util.h" #include "xutil.h" #include "keyboard.h" #include "slcd.h" extern int debug; extern sys_config Config; extern int my_shutdown; extern uint16_t leds; extern int slcdHandle; #ifdef USE_SIMULATOR extern int SIM_hlt_value; extern int SIM_mlt_value; #endif /* * Read one byte from a 1-wire device like a DS2413 */ int read_w1(char *address, char *file) { char *addr = NULL; int fn = -1, rc = -1, retries = 5; uint8_t val; addr = xstrcpy((char *)"/sys/bus/w1/devices/"); addr = xstrcat(addr, address); addr = xstrcat(addr, (char *)"/"); addr = xstrcat(addr, file); if ((fn = open(addr, O_RDONLY)) >= 0) { if ((lseek(fn, 0L, SEEK_SET)) == 0) { while (retries--) { if ((read(fn, &val, 1)) == 1) { rc = (int)val; goto leave; } } syslog(LOG_NOTICE, "read_w1() read %s fatal: %s", addr, strerror(errno)); } else { syslog(LOG_NOTICE, "read_w1() lseek %s: %s", addr, strerror(errno)); } } else { syslog(LOG_NOTICE, "read_w1() open %s: %s", addr, strerror(errno)); } leave: if (fn != -1) { if ((close(fn)) == -1) { syslog(LOG_NOTICE, "read_w1() close %s: %s", addr, strerror(errno)); } } free(addr); return rc; } /* * Write a byte to a 1-wire device like a DS2413 */ int write_w1(char *address, char *file, uint8_t val) { char *addr = NULL; int fn = -1, rc = -1, retries = 5; addr = xstrcpy((char *)"/sys/bus/w1/devices/"); addr = xstrcat(addr, address); addr = xstrcat(addr, (char *)"/"); addr = xstrcat(addr, file); if ((fn = open(addr, O_WRONLY)) >= 0) { if ((lseek(fn, 0L, SEEK_SET)) == 0) { while (retries--) { if ((write(fn, &val, 1)) == 1) { rc = 0; goto leave; } } syslog(LOG_NOTICE, "write_w1() write %s fatal: %s", addr, strerror(errno)); } else { syslog(LOG_NOTICE, "write_w1() lseek %s: %s", addr, strerror(errno)); } } else { syslog(LOG_NOTICE, "write_w1() open %s: %s", addr, strerror(errno)); } leave: if (fn != -1) { if ((close(fn)) == -1) { syslog(LOG_NOTICE, "write_w1() close %s: %s", addr, strerror(errno)); } } free(addr); return rc; } void hlt_heater(int value) { if (value) { leds |= SLED_HLTH; } else { leds &= ~SLED_HLTH; } slcdLEDs(slcdHandle); } void mlt_heater(int value) { if (value) { leds |= SLED_MLTH; } else { leds &= ~SLED_MLTH; } slcdLEDs(slcdHandle); } void mlt_pump(int value) { pump_status(value); if (value) { leds |= SLED_MLTP; } else { leds &= ~SLED_MLTP; } slcdLEDs(slcdHandle); } int device_out(char *uuid, int value) { devices_list *device; time_t now, my_timestamp; int rc, my_value, test_value; if (uuid == NULL) return 0; now = time(NULL); #ifdef HAVE_WIRINGPI_H piLock(LOCK_DEVICES); #endif for (device = Config.devices; device; device = device->next) { if (! strcmp(uuid, device->uuid)) { /* * Execute command if different then the old value. But also * every 2 minutes because commands can have temporary * disconnects, or have radio problems. */ my_timestamp = device->timestamp; my_value = device->value; if ((device->type == DEVTYPE_W1) && (device->direction == DEVDIR_OUT_BIN)) { test_value = (value == 0) ? 0 : 1; } else { test_value = value; } if ((test_value != my_value) || (((int)now - (int)my_timestamp) >= 120)) { #ifdef HAVE_WIRINGPI_H rc = 0; if ((device->type == DEVTYPE_GPIO) && (device->gpiopin != -1) && (device->present == DEVPRESENT_YES)) { } #endif if ((device->type == DEVTYPE_W1) && (device->direction == DEVDIR_OUT_BIN) && (device->present == DEVPRESENT_YES)) { if (strncmp(device->address, (char *)"3a", 2) == 0) { /* * DS2413. First read state so that we can preserve the state of * the "other" PIO channel. To make things a bit more complicated * the bits in the state register differ from the output register. */ uint8_t state, output; if ((rc = read_w1(device->address, (char *)"state")) >= 0) { state = (unsigned int)rc; output = (state & 0x01) + ((state & 0x04) >> 1); if (device->subdevice == 0) { output = (output & 0xfe); output |= (value == 0) ? 0x01 : 0x00; } else if (device->subdevice == 1) { output = (output & 0xfd); output |= (value == 0) ? 0x02 : 0x00; } else { output = 0xff; } if ((write_w1(device->address, (char *)"output", output)) == 0) { syslog(LOG_NOTICE, "DS2413 PIO%c value=%d (%s)", (device->subdevice == 0) ? 'A' : 'B', (value == 0) ? 0 : 1, device->comment); if (debug) fprintf(stdout, "DS2413 PIO%c value=%d (%s)\n", (device->subdevice == 0) ? 'A' : 'B', (value == 0) ? 0 : 1, device->comment); device->value = (value == 0) ? 0 : 1; device->timestamp = time(NULL); } } } } #ifdef USE_SIMULATOR if ((device->type == DEVTYPE_SIM) && (device->direction == DEVDIR_OUT_BIN) && (device->present == DEVPRESENT_YES)) { if ((strcmp((char *)"SimHLTheater", device->address) == 0) || (strcmp((char *)"SimMLTheater", device->address) == 0) || (strcmp((char *)"SimMLTpump", device->address) == 0)) { device->value = value; if (strcmp((char *)"SimHLTheater", device->address) == 0) { SIM_hlt_value = value; hlt_heater(value); } if (strcmp((char *)"SimMLTheater", device->address) == 0) { SIM_mlt_value = value; mlt_heater(value); } if (strcmp((char *)"SimMLTpump", device->address) == 0) { mlt_pump(value); } } } #endif } else { #ifdef HAVE_WIRINGPI_H piUnlock(LOCK_DEVICES); #endif return 0; } } } #ifdef HAVE_WIRINGPI_H piUnlock(LOCK_DEVICES); #endif return 0; } /* * Returns DEVPRESENT_NO if failed. * Returns DEVPRESENT_YES if success, value contains new value. */ int device_in(char *uuid, int *value) { devices_list *device; int tmp, present; if (uuid == NULL) return 0; #ifdef HAVE_WIRINGPI_H piLock(LOCK_DEVICES); #endif for (device = Config.devices; device; device = device->next) { if (! strcmp(uuid, device->uuid)) { present = device->present; if (present == DEVPRESENT_YES) { tmp = device->value + device->offset; } else { tmp = 0; } #ifdef HAVE_WIRINGPI_H piUnlock(LOCK_DEVICES); #endif *value = tmp; return present; } } #ifdef HAVE_WIRINGPI_H piUnlock(LOCK_DEVICES); #endif return DEVPRESENT_NO; } /* * Auto detect hotplugged or known to be present devices */ int devices_detect(void) { struct dirent *de; DIR *fd; devices_list *device, *ndev; int found, subdevices, ival, i, rc = 0; char buf[40]; uuid_t uu; #ifdef HAVE_WIRINGPI_H int pin; #endif /* * Scan for 1-wire devices */ if ((fd = opendir((char *)"/sys/bus/w1/devices"))) { while ((de = readdir(fd))) { if (de->d_name[0] != '.') { found = FALSE; for (device = Config.devices; device; device = device->next) { if (strcmp(device->address,de->d_name) == 0) { found = TRUE; break; } } if (found == FALSE) { strncpy(buf, de->d_name, 2); buf[2] = '\0'; sscanf(buf, "%02x", &ival); syslog(LOG_NOTICE, "Scan 1-wire %02x %d", ival, ival); subdevices = 1; if (strcmp(buf, (char *)"29") == 0) subdevices = 8; if (strcmp(buf, (char *)"3a") == 0) subdevices = 2; 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_W1; ndev->direction = DEVDIR_UNDEF; if (strcmp(buf, (char *)"10") == 0) { ndev->direction = DEVDIR_IN_ANALOG; ndev->description = xstrcpy((char *)"DS18S20 Digital thermometer"); } else if (strcmp(buf, (char *)"22") == 0) { ndev->direction = DEVDIR_IN_ANALOG; ndev->description = xstrcpy((char *)"DS1820 Digital thermometer"); } else if (strcmp(buf, (char *)"28") == 0) { ndev->direction = DEVDIR_IN_ANALOG; ndev->description = xstrcpy((char *)"DS18B20 Digital thermometer"); } else if (strcmp(buf, (char *)"3a") == 0) { ndev->description = xstrcpy((char *)"DS2413 Dual channel addressable switch"); ndev->direction = DEVDIR_IN_BIN; } else if (strcmp(buf, (char *)"3b") == 0) { ndev->direction = DEVDIR_IN_ANALOG; ndev->description = xstrcpy((char *)"DS1825 Digital thermometer"); } else if (strcmp(buf, (char *)"42") == 0) { ndev->direction = DEVDIR_IN_ANALOG; ndev->description = xstrcpy((char *)"DS28EA00 Digital thermometer"); } else if (strcmp(buf, (char *)"w1") == 0) { ndev->description = xstrcpy((char *)"Master System device"); } else { ndev->description = xstrcpy((char *)"Unknown device family "); ndev->description = xstrcat(ndev->description, buf); } ndev->value = ndev->offset = ndev->inuse = 0; ndev->present = DEVPRESENT_YES; ndev->address = xstrcpy(de->d_name); ndev->subdevice = i; ndev->gpiopin = -1; ndev->comment = xstrcpy((char *)"Auto detected device"); ndev->timestamp = time(NULL); 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++; } } } } closedir(fd); } #ifdef HAVE_WIRINGPI_H if (piBoardRev() == 2) { /* * Support rev B and newer boards only */ found = FALSE; for (device = Config.devices; device; device = device->next) { if (device->type == DEVTYPE_GPIO) { found = TRUE; break; } } if (found == FALSE) { /* * There were no GPIO devices found. */ subdevices = 12; pin = 0; for (i = 0; i < subdevices; i++) { if (i == 8) pin = 17; 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_GPIO; ndev->value = digitalRead(pin); ndev->offset = 0; ndev->present = DEVPRESENT_YES; ndev->address = xstrcpy((char *)"GPIO"); snprintf(buf, 39, "Raspberry GPIO %d", i); ndev->description = xstrcpy(buf); ndev->subdevice = i; ndev->gpiopin = pin; ndev->timestamp = time(NULL); switch (i) { case 0: ndev->direction = DEVDIR_OUT_ANALOG; ndev->inuse = 1; ndev->comment = xstrcpy((char *)"MLT heater SSR"); break; case 1: ndev->direction = DEVDIR_OUT_ANALOG; ndev->inuse = 1; ndev->comment = xstrcpy((char *)"MLT heater PWM"); break; case 2: ndev->direction = DEVDIR_OUT_ANALOG; ndev->inuse = 1; ndev->comment = xstrcpy((char *)"MLT pump relay"); break; case PANEL_RETURN: ndev->direction = DEVDIR_IN_BIN; ndev->inuse = 1; ndev->comment = xstrcpy((char *)"Frontpanel Return"); break; case PANEL_ENTER: ndev->direction = DEVDIR_IN_BIN; ndev->inuse = 1; ndev->comment = xstrcpy((char *)"Frontpanel Enter key"); break; case PANEL_DOWN: ndev->direction = DEVDIR_IN_BIN; ndev->inuse = 1; ndev->comment = xstrcpy((char *)"Frontpanel Down key"); break; case PANEL_UP: ndev->direction = DEVDIR_IN_BIN; ndev->inuse = 1; ndev->comment = xstrcpy((char *)"Frontpanel Up key"); break; case 7: ndev->direction = DEVDIR_INTERN; ndev->inuse = 1; ndev->comment = xstrcpy((char *)"1-Wire bus"); break; case 8: ndev->direction = DEVDIR_OUT_BIN; ndev->inuse = 1; ndev->comment = xstrcpy((char *)"Buzzer"); break; default: ndev->direction = DEVDIR_IN_BIN; ndev->inuse = 0; ndev->comment = xstrcpy((char *)"Raspberry GPIO"); } pin++; 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 #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 = 6; 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 *)"SimHLTtemp"); ndev->description = xstrcpy((char *)"Simulated HLT temperature"); break; case 2: ndev->direction = DEVDIR_IN_ANALOG; ndev->address = xstrcpy((char *)"SimMLTtemp"); ndev->description = xstrcpy((char *)"Simulated MLT temperature"); break; case 3: ndev->direction = DEVDIR_OUT_BIN; ndev->address = xstrcpy((char *)"SimHLTheater"); ndev->description = xstrcpy((char *)"Simulated HLT heater"); break; case 4: ndev->direction = DEVDIR_OUT_BIN; ndev->address = xstrcpy((char *)"SimMLTheater"); ndev->description = xstrcpy((char *)"Simulated MLT heater"); break; case 5: ndev->direction = DEVDIR_OUT_BIN; ndev->address = xstrcpy((char *)"SimMLTpump"); ndev->description = xstrcpy((char *)"Simulated MLT pump"); 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; } #ifdef HAVE_WIRINGPI_H PI_THREAD (my_devices_loop) #else void *my_devices_loop(void *threadid) #endif { devices_list *device; char *addr = NULL, line[60], *p = NULL; FILE *fp; int temp, rc; syslog(LOG_NOTICE, "Thread my_devices_loop started"); if (debug) fprintf(stdout, "Thread my_devices_loop started\n"); #ifdef HAVE_WIRINGPI_H if ((rc = piHiPri(10))) syslog(LOG_NOTICE, "my_devices_loop: piHiPri(10) rc=%d", rc); #endif /* * Loop forever until the external shutdown variable is set. */ for (;;) { /* * Process all devices. */ for (device = Config.devices; device; device = device->next) { if (my_shutdown) break; switch (device->type) { case DEVTYPE_W1: /* * Only tested with DS18B20 but from the kernel source this * should work with all 1-wire thermometer sensors. */ if ((strncmp(device->address, (char *)"10", 2) == 0) || (strncmp(device->address, (char *)"22", 2) == 0) || (strncmp(device->address, (char *)"28", 2) == 0) || (strncmp(device->address, (char *)"3b", 2) == 0) || (strncmp(device->address, (char *)"42", 2) == 0)) { addr = xstrcpy((char *)"/sys/bus/w1/devices/"); addr = xstrcat(addr, device->address); addr = xstrcat(addr, (char *)"/w1_slave"); if ((fp = fopen(addr, "r"))) { if (device->present != DEVPRESENT_YES) { syslog(LOG_NOTICE, "sensor %s is back", device->address); #ifdef HAVE_WIRINGPI_H piLock(LOCK_DEVICES); #endif device->present = DEVPRESENT_YES; #ifdef HAVE_WIRINGPI_H piUnlock(LOCK_DEVICES); #endif } /* * The output looks like: * 72 01 4b 46 7f ff 0e 10 57 : crc=57 YES * 72 01 4b 46 7f ff 0e 10 57 t=23125 */ fgets(line, 50, fp); line[strlen(line)-1] = '\0'; if ((line[36] == 'Y') && (line[37] == 'E')) { /* CRC is Ok, continue */ fgets(line, 50, fp); line[strlen(line)-1] = '\0'; strtok(line, (char *)"="); p = strtok(NULL, (char *)"="); rc = sscanf(p, "%d", &temp); #ifdef HAVE_WIRINGPI_H piLock(LOCK_DEVICES); #endif if ((rc == 1) && (device->value != temp)) { device->value = temp; device->timestamp = time(NULL); } #ifdef HAVE_WIRINGPI_H piUnlock(LOCK_DEVICES); #endif } else { syslog(LOG_NOTICE, "sensor %s CRC error", device->address); #ifdef HAVE_WIRINGPI_H piLock(LOCK_DEVICES); #endif device->present = DEVPRESENT_ERROR; #ifdef HAVE_WIRINGPI_H piUnlock(LOCK_DEVICES); #endif } fclose(fp); } else { if (device->present != DEVPRESENT_NO) { syslog(LOG_NOTICE, "sensor %s is missing", device->address); #ifdef HAVE_WIRINGPI_H piLock(LOCK_DEVICES); #endif device->present = DEVPRESENT_NO; #ifdef HAVE_WIRINGPI_H piUnlock(LOCK_DEVICES); #endif } } free(addr); addr = NULL; } /* if temperature sensor */ /* * DS2413 Dual channel addressable switch */ if (strncmp(device->address, (char *)"3a", 2) == 0) { addr = xstrcpy((char *)"/sys/bus/w1/devices/"); addr = xstrcat(addr, device->address); addr = xstrcat(addr, (char *)"/state"); if ((access(addr, R_OK)) == 0) { if (device->present != DEVPRESENT_YES) { syslog(LOG_NOTICE, "DS2413 %s is back", device->address); #ifdef HAVE_WIRINGPI_H piLock(LOCK_DEVICES); #endif device->present = DEVPRESENT_YES; #ifdef HAVE_WIRINGPI_H piUnlock(LOCK_DEVICES); #endif } /* * First make sure that if this device is configured as input * to drive the output high. */ if (device->direction == DEVDIR_IN_BIN) { uint8_t state, output; if ((rc = read_w1(device->address, (char *)"state")) >= 0) { state = (unsigned int)rc; output = ((state & 0x02) >> 1) + ((state & 0x08) >> 2); /* Both latch states */ if (device->subdevice == 0) { output = (output & 0xfe); output |= 0x01; } else if (device->subdevice == 1) { output = (output & 0xfd); output |= 0x02; } else { output = 0xff; } write_w1(device->address, (char *)"output", output); } } if ((rc = read_w1(device->address, (char *)"state")) >= 0) { #ifdef HAVE_WIRINGPI_H piLock(LOCK_DEVICES); #endif /* * Read PIOA or PIOB pin state bits */ if (device->subdevice == 0) device->value = (rc & 0x01) ? 0 : 1; else if (device->subdevice == 1) device->value = (rc & 0x04) ? 0 : 1; device->timestamp = time(NULL); #ifdef HAVE_WIRINGPI_H piUnlock(LOCK_DEVICES); #endif } } else { if (device->present != DEVPRESENT_NO) { syslog(LOG_NOTICE, "DS2413 %s is missing", device->address); #ifdef HAVE_WIRINGPI_H piLock(LOCK_DEVICES); #endif device->present = DEVPRESENT_NO; #ifdef HAVE_WIRINGPI_H piUnlock(LOCK_DEVICES); #endif } } free(addr); addr = NULL; } break; #ifdef HAVE_WIRINGPI_H case DEVTYPE_GPIO: if (device->direction == DEVDIR_IN_BIN) { piLock(LOCK_DEVICES); device->value = digitalRead(device->gpiopin); device->offset = 0; device->timestamp = time(NULL); piUnlock(LOCK_DEVICES); } break; #endif #ifdef USE_SIMULATOR case DEVTYPE_SIM: #ifdef HAVE_WIRINGPI_H piLock(LOCK_DEVICES); #endif if (Config.simulator) { if (device->subdevice == 0) { device->value = (int)(Config.simulator->room_temperature * 1000); device->timestamp = time(NULL); } else if (device->subdevice == 1) { device->value = (int)(Config.simulator->hlt_temperature * 1000); device->timestamp = time(NULL); } else if (device->subdevice == 2) { device->value = (int)(Config.simulator->mlt_temperature * 1000); device->timestamp = time(NULL); } } #ifdef HAVE_WIRINGPI_H piUnlock(LOCK_DEVICES); #endif break; #endif default: break; } /* * Delay a bit after procesing a device. */ usleep(10000); } if (my_shutdown) break; /* * Delay a bit after all devices */ usleep(100000); } syslog(LOG_NOTICE, "Thread my_devices_loop stopped"); if (debug) fprintf(stdout, "Thread my_devices_loop stopped\n"); return 0; }