thermferm/devices.c

Sun, 05 May 2024 17:24:54 +0200

author
Michiel Broek <mbroek@mbse.eu>
date
Sun, 05 May 2024 17:24:54 +0200
changeset 730
6eba006ed8f5
parent 729
f5d94730db18
permissions
-rw-r--r--

Much faster shutdown of the websocket service.

/*****************************************************************************
 * Copyright (C) 2014..2024
 *   
 * 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 "delay.h"
#include "devices.h"
#include "rc-switch.h"
#include "panel.h"
#include "xutil.h"
#include "websocket.h"


int			my_devices_shutdown = 0;
int			my_devices_state = 0;

extern sys_config	Config;
extern pthread_mutex_t  mutexes[5];
extern w1_list		*w1_devices;

extern const char	DEVTYPE[8][6];
extern const char	DEVPRESENT[4][6];
extern const char	DEVDIR[7][11];

#ifdef USE_SIMULATOR
extern int		my_simulator_state;
#endif


/*
 * Since kernel version 4 there is a module, and a dtoverlay so that the
 * temperature and humidity can simply read from the /sys filesystem.
 */
int             dht11_temperature = -1;
int             dht11_humidity = -1;
int		dht11_state = DEVPRESENT_UNDEF;
time_t		dht11_next;



/*
 * DHT11 sensor read.
 * Called at 30 seconds interval if all is well.
 * If last read was an error, the interval is 2 seconds.
 */
void dht11Read(char *address)
{
    int        	temp, hum;
    int		fd, rc, err, oldstate;
    char	buffer[25], *dhtpath = NULL;

    dht11_temperature = -1;
    dht11_humidity = -1;
    oldstate = dht11_state;
    dhtpath = xstrcpy((char *)"/sys/bus/iio/devices/");
    dhtpath = xstrcat(dhtpath, address);
    dhtpath = xstrcat(dhtpath, (char *)"/in_temp_input");

    fd = open(dhtpath, O_RDONLY);
    if (fd < 0) {
	/*
	 * The sensor is gone.
	 */
	err = errno;
	syslog(LOG_NOTICE, "DHT11 open temperature: %d %s", err, strerror(err));
	free(dhtpath);
	dht11_state = DEVPRESENT_NO;
	return;
    }

    rc = read(fd, buffer, 25);
    if (rc == -1) {
	err = errno;
	if (err == 110) {
		dht11_state = DEVPRESENT_NO;	/* Device is gone */
	} else {
		dht11_state = DEVPRESENT_ERROR;
	}
	syslog(LOG_NOTICE, "DHT11 read temperature: %s", strerror(err));
    } else {
	sscanf(buffer, "%d", &temp);
	dht11_temperature = temp;
	dht11_state = DEVPRESENT_YES;
    }
    close(fd);
    free(dhtpath);
    dhtpath = NULL;

    /*
     * Only read humidity if state is DEVPRESENT_YES
     */
    if (dht11_state == DEVPRESENT_YES) {
	dhtpath = xstrcpy((char *)"/sys/bus/iio/devices/");
	dhtpath = xstrcat(dhtpath, address);
	dhtpath = xstrcat(dhtpath, (char *)"/in_humidityrelative_input");

	fd = open(dhtpath, O_RDONLY);
	rc = read(fd, buffer, 25);
	if (rc == -1) {
	    err = errno;
	    if (err == 110) {
		    dht11_state = DEVPRESENT_NO;
	    } else {
		    dht11_state = DEVPRESENT_ERROR;
	    }
	    syslog(LOG_NOTICE, "DHT11 read humidity: %s", strerror(err));
	    dht11_temperature = -1;	/* Make invalid	*/
	} else {
	    sscanf(buffer, "%d", &hum);
	    dht11_humidity = hum;
	    dht11_state = DEVPRESENT_YES;
	}
	close(fd);
	free(dhtpath);
	dhtpath = NULL;
    }

    if (oldstate != dht11_state)
	syslog(LOG_NOTICE, "dht11 t:%d h:%d state:%d", dht11_temperature, dht11_humidity, dht11_state);
}



/*
 * 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;
}



int device_out(char *uuid, int value)
{
    devices_list	*device;
    time_t		now, my_timestamp;
    int			my_value, test_value;
#ifdef HAVE_WIRINGPI_H
    int			rc, i;
    char		buf[40];
#endif
#ifdef USE_SIMULATOR
    simulator_list      *simulator;
#endif

    if (uuid == NULL)
	return 0;

    now = time(NULL);
//    pthread_mutex_lock(&mutexes[LOCK_DEVICES]);

    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_RC433) && (device->gpiopin != -1) && (device->present == DEVPRESENT_YES)) {
		    snprintf(buf, 39, "%s,%d", device->address, value ? 1:0);
		    for (i = 0; i < strlen(buf); i++)
			if (buf[i] == '-')
			    buf[i] = ',';
//		    pthread_mutex_unlock(&mutexes[LOCK_DEVICES]);
	    	    enableTransmit(device->gpiopin);	    
		    rc = toggleSwitch(buf);
		    disableTransmit();
//		    pthread_mutex_lock(&mutexes[LOCK_DEVICES]);
		    syslog(LOG_NOTICE, "RC433 command %s rc=%d", buf, rc);
                    device->value = value;
		    device->timestamp = time(NULL);
		    devices_ws();
//		    pthread_mutex_unlock(&mutexes[LOCK_DEVICES]);
		    return rc;
	        }

		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) {
//		    	syslog(LOG_NOTICE, "DS2413 PIO%c value=%d (%s)", (device->subdevice == 0) ? 'A' : 'B', (value == 0) ? 0 : 1, device->comment);
//			pthread_mutex_lock(&mutexes[LOCK_DEVICES]);
			if (device->value != (value == 0) ? 0 : 1) {
			    syslog(LOG_NOTICE, "DS2413 PIO%c value=%d (%s)", (device->subdevice == 0) ? 'A' : 'B', (value == 0) ? 0 : 1, device->comment);
			    device->value = (value == 0) ? 0 : 1;
			    device->timestamp = time(NULL);
			    devices_ws();
			}
//			pthread_mutex_unlock(&mutexes[LOCK_DEVICES]);
		    }
		}

#ifdef USE_SIMULATOR
		if ((device->type == DEVTYPE_SIM) && (device->direction == DEVDIR_OUT_BIN)) {
		    while (my_simulator_state == THREAD_PAUSE) { mDelay(20); };
		    for (simulator = Config.simulators; simulator; simulator = simulator->next) {
			if ((strcmp(device->address, simulator->cooler_address) == 0) ||
			    (strcmp(device->address, simulator->heater_address) == 0) ||
			    (strcmp(device->address, simulator->fan_address) == 0) ||
			    (strcmp(device->address, simulator->light_address) == 0)) {
			    if (value != device->value) {
				int rc = DEVPRESENT_UNDEF;
			    	device->value = value;
                            	device->timestamp = time(NULL);
			    	if (strcmp(device->address, simulator->cooler_address) == 0) {
                            	    simulator->cooler_power = value;
				    rc = simulator->cooler_present;
			    	} else if (strcmp(device->address, simulator->heater_address) == 0) {
				    simulator->heater_power = value;
				    rc = simulator->heater_present;
			    	} else if (strcmp(device->address, simulator->fan_address) == 0) {
				    simulator->fan_power = value;
				    rc = simulator->fan_present;
			    	} else if (strcmp(device->address, simulator->light_address) == 0) {
				    simulator->light_power = value;
				    rc = simulator->light_present;
			    	}
				syslog(LOG_NOTICE, "SIM %s value=%d, present=%s", device->address, value, DEVPRESENT[rc]);
				device->present = rc;
				devices_ws();
			    }
			}
		    }
		}
#endif
	    } else {
//		pthread_mutex_unlock(&mutexes[LOCK_DEVICES]);
		return 0;
	    } // if ((test_value != my_value) || (((int)now - (int)my_timestamp) >= 120))
	} // if (! strcmp(uuid, device->uuid))
    } // for (device = Config.devices; device; device = device->next)

//    pthread_mutex_unlock(&mutexes[LOCK_DEVICES]);
    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 DEVPRESENT_UNDEF;

    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;
	    }
	    *value = tmp;
	    return present;
	}
    }

    return DEVPRESENT_NO;
}


int device_present(char *address, int present)
{
    devices_list        *device;

    if (address == NULL || present < DEVPRESENT_UNDEF || present > DEVPRESENT_ERROR) {
	return -1;
    }

    for (device = Config.devices; device; device = device->next) {
	if (! strcmp(address, device->address)) {
	    if (device->present != present) {
	    	device->present = present;
		device->timestamp = time(NULL);
		return 1;
	    }
	    return 0;
	}
    }
    return -1;
}


/*
 * Return json data for one device.
 */
char *device_json(devices_list *device)
{
    char	*payload;
    char	vbuf[64];

    payload = xstrcpy((char *)"{\"uuid\":\"");
    payload = xstrcat(payload, device->uuid);
    payload = xstrcat(payload, (char *)"\",\"type\":\"");
    payload = xstrcat(payload, (char *)DEVTYPE[device->type]);
    payload = xstrcat(payload, (char *)"\",\"direction\":\"");
    payload = xstrcat(payload, (char *)DEVDIR[device->direction]);
    payload = xstrcat(payload, (char *)"\",\"address\":\"");
    payload = xstrcat(payload, device->address);
    payload = xstrcat(payload, (char *)"\",\"subdevice\":");
    snprintf(vbuf, 63, "%d", device->subdevice);
    payload = xstrcat(payload, vbuf);
    payload = xstrcat(payload, (char *)",\"value\":");
    snprintf(vbuf, 63, "%d", device->value);
    payload = xstrcat(payload, vbuf);
    payload = xstrcat(payload, (char *)",\"offset\":");
    snprintf(vbuf, 63, "%d", device->offset);
    payload = xstrcat(payload, vbuf);
    payload = xstrcat(payload, (char *)",\"present\":\"");
    payload = xstrcat(payload, (char *)DEVPRESENT[device->present]);
    payload = xstrcat(payload, (char *)"\"");
    payload = xstrcat(payload, (char *)",\"gpiopin\":");
    snprintf(vbuf, 63, "%d", device->gpiopin);
    payload = xstrcat(payload, vbuf);
    payload = xstrcat(payload, (char *)",\"inuse\":");
    snprintf(vbuf, 63, "%d", device->inuse);
    payload = xstrcat(payload, vbuf);
    payload = xstrcat(payload, (char *)",\"description\":\"");
    payload = xstrcat(payload, device->description);
    payload = xstrcat(payload, (char *)"\",\"comment\":\"");
    payload = xstrcat(payload, device->comment);
    payload = xstrcat(payload, (char *)"\",\"timestamp\":");
    snprintf(vbuf, 63, "%ld", (long)device->timestamp);
    payload = xstrcat(payload, vbuf);
    payload = xstrcat(payload, (char *)"}");

    return payload;
}



void devices_ws(void)
{
    bool		comma = false;
    char		*payload = NULL, *payloadu = NULL;
    devices_list	*device;

    payload = xstrcpy((char *)"{\"type\":\"device\",\"metric\":[");
    for (device = Config.devices; device; device = device->next) {
	if (comma)
	    payload = xstrcat(payload, (char *)",");
	payloadu = device_json(device);
	payload = xstrcat(payload, payloadu);
	comma = true;
	free(payloadu);
	payloadu = NULL;
    }
    payload = xstrcat(payload, (char *)"]}");
    ws_broadcast(payload);
    free(payload);
    payload = NULL;
}



/*
 * Auto detect hotplugged or known to be present devices
 */
int devices_detect(void)
{
    struct dirent	*de;
    DIR			*fd;
    w1_list		*dev_w1;
    devices_list	*device, *ndev;
    int			found, subdevices, i, rc = 0;
    uuid_t		uu;
#ifdef HAVE_WIRINGPI_H
    int			pin;
    char		buf[40];
#endif
#ifdef USE_SIMULATOR
    simulator_list	*simulator;
#endif

    /*
     * Scan for 1-wire devices. These are already detected by the
     * one-wire thread that is already running. So we just check
     * these detected devices.
     */
    for (dev_w1 = w1_devices; dev_w1; dev_w1 = dev_w1->next) {
	found = FALSE;
	for (device = Config.devices; device; device = device->next) {
	    if (strcmp(device->address, dev_w1->address) == 0) {
		found = TRUE;
		break;
	    }
	}

	if (found == FALSE) {
	    subdevices = (strcmp(dev_w1->family, (char *)"3a") == 0) ? 2:1;
	    for (i = 0; i < subdevices; i++) {
		ndev = (devices_list *)malloc(sizeof(devices_list));
		ndev->next = NULL;
		ndev->uuid = malloc(37);
		uuid_generate(uu);
		uuid_unparse(uu, ndev->uuid);
		ndev->type = DEVTYPE_W1;
		ndev->direction = DEVDIR_IN_ANALOG;
		if (strcmp(dev_w1->family, (char *)"10") == 0) {
		    ndev->description = xstrcpy((char *)"DS18S20 Digital thermometer");
		} else if (strcmp(dev_w1->family, (char *)"22") == 0) {
		    ndev->description = xstrcpy((char *)"DS1822 Digital thermometer");
		} else if (strcmp(dev_w1->family, (char *)"28") == 0) {
		    ndev->description = xstrcpy((char *)"DS18B20 Digital thermometer");
		} else if (strcmp(dev_w1->family, (char *)"3a") == 0) {
		    ndev->description = xstrcpy((char *)"DS2413 Dual channel addressable switch");
		    ndev->direction = DEVDIR_IN_BIN;
		} else if (strcmp(dev_w1->family, (char *)"3b") == 0) {
		    ndev->description = xstrcpy((char *)"DS1825 Digital thermometer");
		} else if (strcmp(dev_w1->family, (char *)"42") == 0) {
		    ndev->description = xstrcpy((char *)"DS28EA00 Digital thermometer");
		} else {
		    ndev->description = xstrcpy((char *)"Unknown device family ");
		    ndev->direction = DEVDIR_UNDEF;
		}
		ndev->value = ndev->offset = ndev->inuse = 0;
		ndev->present = DEVPRESENT_YES;
		ndev->address = xstrcpy(dev_w1->address);
		ndev->subdevice = i;
		ndev->gpiopin = -1;
		ndev->comment = xstrcpy((char *)"Auto detected device");
		ndev->timestamp = time(NULL);

		syslog(LOG_NOTICE, "New W1 device %s, subdevice %d, %s", ndev->address, ndev->subdevice, ndev->description);

		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++;
	    }
	}
    }

    /*
     * DHT11 as kernel module. The number after the @ is the hexadecimal
     * number of the BCM gpio pin. Since we use pin 22 at connector pin 15
     * the number is 16.
     */
    char *dhtaddr = NULL;
    if ((fd = opendir((char *)"/sys/devices/platform/dht11@16"))) {
	while ((de = readdir(fd))) {
	    if (de->d_name[0] != '.') {
		if (strncmp(de->d_name, (char *)"iio", 3) == 0) {
		    dhtaddr = xstrcpy(de->d_name);
		}
	    }
	}
	closedir(fd);
    }

    if (dhtaddr) {

	found = FALSE;
	for (device = Config.devices; device; device = device->next) {
	    if (strcmp(device->address, dhtaddr) == 0) {
		found = TRUE;
		break;
	    }
	}

	if (found == FALSE) {
	    for (i = 0; i < 2; i++) {
		ndev = (devices_list *)malloc(sizeof(devices_list));
		ndev->next = NULL;
		ndev->uuid = malloc(37);
		uuid_generate(uu);
		uuid_unparse(uu, ndev->uuid);
		ndev->type = DEVTYPE_DHT;
		ndev->direction = DEVDIR_IN_ANALOG;
		if (i == 0)
		    ndev->description = xstrcpy((char *)"DHT11 temperature sensor");
		else
		    ndev->description = xstrcpy((char *)"DHT11 humidity sensor");
		ndev->value = ndev->offset = ndev->inuse = 0;
	        ndev->present = DEVPRESENT_YES;
		ndev->address = xstrcpy(dhtaddr);
		ndev->subdevice = i;
		ndev->gpiopin = -1;
		ndev->comment = xstrcpy((char *)"Auto detected device");
		ndev->timestamp = time(NULL);

		syslog(LOG_NOTICE, "New DHT11 device %s, subdevice %d, %s", ndev->address, ndev->subdevice, ndev->description);

		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++;
	    }
	}
    }

#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->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);
		if (i == 0) {
		    ndev->direction = DEVDIR_INTERN;
		    ndev->inuse = 1;
		    ndev->comment = xstrcpy((char *)"433 Mhz transmitter");
		} else if (i == 3) {
		    ndev->direction = DEVDIR_INTERN;
		    ndev->inuse = 1;
		    ndev->comment = xstrcpy((char *)"DHT11 sensor");
		} else if (i == PANEL_LED) {
		    ndev->direction = DEVDIR_OUT_BIN;
		    ndev->inuse = 1;
		    ndev->comment = xstrcpy((char *)"Frontpanel LED");
		} else if (i == PANEL_ENTER) {
		    ndev->direction = DEVDIR_IN_BIN;
		    ndev->inuse = 1;
		    ndev->comment = xstrcpy((char *)"Frontpanel Enter key");
		} else if (i == PANEL_DOWN) {
		    ndev->direction = DEVDIR_IN_BIN;
		    ndev->inuse = 1;
		    ndev->comment = xstrcpy((char *)"Frontpanel Down key");
		} else if (i == PANEL_UP) {
		    ndev->direction = DEVDIR_IN_BIN;
		    ndev->inuse = 1;
		    ndev->comment = xstrcpy((char *)"Frontpanel Up key");
		} else if (i == 7) {
		    ndev->direction = DEVDIR_INTERN;
		    ndev->inuse = 1;
		    ndev->comment = xstrcpy((char *)"1-Wire bus");
	    	} else {
		    ndev->direction = DEVDIR_IN_BIN;
		    ndev->inuse = 0;
		    ndev->comment = xstrcpy((char *)"Raspberry GPIO");
	    	}
	    	pin++;

		syslog(LOG_NOTICE, "New GPIO device %s, subdevice %d, %s", ndev->address, ndev->subdevice, ndev->description);

	    	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

    for (simulator = Config.simulators; simulator; simulator = simulator->next) {
	for (i = 0; i < 12; i++) {

	    found = FALSE;
            for (device = Config.devices; device; device = device->next) {
            	if ((i == 0  && strcmp(device->address, simulator->room_tempaddress) == 0) ||
		    (i == 1  && strcmp(device->address, simulator->air_address) == 0) ||
		    (i == 2  && strcmp(device->address, simulator->beer_address) == 0) ||
		    (i == 3  && strcmp(device->address, simulator->heater_address) == 0) ||
		    (i == 4  && strcmp(device->address, simulator->cooler_address) == 0) ||
		    (i == 5  && strcmp(device->address, simulator->room_humaddress) == 0) ||
		    (i == 6  && strcmp(device->address, simulator->chiller_address) == 0) ||
		    (i == 7  && strcmp(device->address, simulator->fan_address) == 0) ||
		    (i == 8  && strcmp(device->address, simulator->light_address) == 0) ||
		    (i == 9  && strcmp(device->address, simulator->beer_address2) == 0) ||
		    (i == 10 && strcmp(device->address, simulator->door_address) == 0) ||
		    (i == 11 && strcmp(device->address, simulator->psu_address) == 0)) {
                    found = TRUE;
                    break;
            	}
            }

	    if (found == FALSE) {
		ndev = (devices_list *)malloc(sizeof(devices_list));
		ndev->next = NULL;
		ndev->uuid = malloc(37);
                uuid_generate(uu);
                uuid_unparse(uu, ndev->uuid);
		ndev->type = DEVTYPE_SIM;
        	ndev->value = ndev->offset = 0;
        	ndev->present = DEVPRESENT_NO;
        	ndev->gpiopin = -1;
		ndev->subdevice = i;
        	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(simulator->room_tempaddress);
				ndev->description = xstrcpy((char *)"Simulated room temperature");
				ndev->present = DEVPRESENT_YES;
				break;
		    case 1:	ndev->direction = DEVDIR_IN_ANALOG;
				ndev->address = xstrcpy(simulator->air_address);
				ndev->description = xstrcpy((char *)"Simulated air temperature");
				ndev->present = simulator->air_present;
				break;
		    case 2:	ndev->direction = DEVDIR_IN_ANALOG;
				ndev->address = xstrcpy(simulator->beer_address);
				ndev->description = xstrcpy((char *)"Simulated beer temperature");
				ndev->present = simulator->beer_present;
				break;
		    case 3:	ndev->direction = DEVDIR_OUT_BIN;
				ndev->address = xstrcpy(simulator->heater_address);
				ndev->description = xstrcpy((char *)"Simulated heater");
				ndev->present = simulator->heater_present;
				break;
		    case 4:	ndev->direction = DEVDIR_OUT_BIN;
				ndev->address = xstrcpy(simulator->cooler_address);
				ndev->description = xstrcpy((char *)"Simulated cooler");
				ndev->present = simulator->cooler_present;
				break;
		    case 5:	ndev->direction = DEVDIR_IN_ANALOG;
				ndev->address = xstrcpy(simulator->room_humaddress);
				ndev->description = xstrcpy((char *)"Simulated room humidity");
				ndev->present = DEVPRESENT_YES;
				break;
		    case 6:	ndev->direction = DEVDIR_IN_ANALOG;
				ndev->address = xstrcpy(simulator->chiller_address);
				ndev->description = xstrcpy((char *)"Simulated Chiller temperature");
				ndev->present = simulator->chiller_present;
				break;
		    case 7:	ndev->direction = DEVDIR_OUT_BIN;
				ndev->address = xstrcpy(simulator->fan_address);
				ndev->description = xstrcpy((char *)"Simulated fan");
				ndev->present = simulator->fan_present;
				break;
		    case 8:	ndev->direction = DEVDIR_OUT_BIN;
				ndev->address = xstrcpy(simulator->light_address);
				ndev->description = xstrcpy((char *)"Simulated light");
				ndev->present = simulator->light_present;
				break;
		    case 9:	ndev->direction = DEVDIR_IN_ANALOG;
				ndev->address = xstrcpy(simulator->beer_address2);
				ndev->description = xstrcpy((char *)"Simulated beer temperature (alt)");
				ndev->present = simulator->beer_present2;
				break;
		    case 10:	ndev->direction = DEVDIR_IN_BIN;
				ndev->address = xstrcpy(simulator->door_address);
				ndev->description = xstrcpy((char *)"Simulated fridge door");
				ndev->present = simulator->door_present;
				break;
		    case 11:	ndev->direction = DEVDIR_IN_BIN;
				ndev->address = xstrcpy(simulator->psu_address);
				ndev->description = xstrcpy((char *)"Simulated PSU status");
				ndev->present = simulator->psu_present;
				break;
		}

		syslog(LOG_NOTICE, "New Simulator device %s, subdevice %d", ndev->address, ndev->subdevice);

		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;
}



void *my_devices_loop(void *threadid)
{
    devices_list	*device;
    w1_list		*dev_w1;
#ifdef USE_SIMULATOR
    simulator_list	*simulator;
#endif
    int			found;
    time_t		now;
    bool		changed;
    pid_t		pid = gettid();

    my_devices_state = 1;
    syslog(LOG_NOTICE, "Thread my_devices_loop started, pid=%d", pid);
    dht11_next = time(NULL);

    /*
     * Loop forever until the external shutdown variable is set.
     */
    for (;;) {

	changed = false;
    	/*
    	 * Process all devices.
    	 */
    	for (device = Config.devices; device; device = device->next) {

	    if (my_devices_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)) {
			    found = FALSE;
			    for (dev_w1 = w1_devices; dev_w1; dev_w1 = dev_w1->next) {
				if (strcmp(device->address, dev_w1->address) == 0) {
				    found = TRUE;
				    if ((dev_w1->value == -1) || (dev_w1->value < -55000)) {
					if (device->present != DEVPRESENT_ERROR)
					    syslog(LOG_NOTICE, "sensor %s value error %d, keep %d", device->address, dev_w1->value, device->value);
					device->present = DEVPRESENT_ERROR;
					changed = true;
				    } else {
					if (device->present != DEVPRESENT_YES) {
					    syslog(LOG_NOTICE, "sensor %s value ok %d", device->address, dev_w1->value);
					    changed = true;
					    device->present = DEVPRESENT_YES;
					}
					if (device->value != dev_w1->value) {
					    changed = true;
					    device->value = dev_w1->value;
					    device->timestamp = time(NULL);
					}
				    }
				}
			    }

			    if (found == FALSE) {
				if (device->present != DEVPRESENT_NO) {
				    syslog(LOG_NOTICE, "sensor %s is missing", device->address);
//				    pthread_mutex_lock(&mutexes[LOCK_DEVICES]);
				    device->present = DEVPRESENT_NO;
				    changed = true;
//				    pthread_mutex_unlock(&mutexes[LOCK_DEVICES]);
				}
			    }

			} /* if temperature sensor */

			break;

		case DEVTYPE_DHT:
			/*
			 * Don't read these to often, 2 seconds minimum delay.
			 * Delay 30 seconds if last update was successfull,
			 * else delay 2 seconds.
			 */
			now = time(NULL);
			if ((int)(now >= dht11_next)) {
			    if (device->subdevice == 0) {
				/* Read once during subdevice 0 */
			    	dht11Read(device->address);
//				pthread_mutex_lock(&mutexes[LOCK_DEVICES]);
				device->present = dht11_state;
			    	if (dht11_state == DEVPRESENT_YES) {
				    if (device->value != dht11_temperature) {
					changed = true;
				    	device->value = dht11_temperature;
				    	device->timestamp = time(NULL);
				    }
			    	}
//				pthread_mutex_unlock(&mutexes[LOCK_DEVICES]);
			    } else if (device->subdevice == 1) {
				/* Data already present, valid or not. */
//				pthread_mutex_lock(&mutexes[LOCK_DEVICES]);
				device->present = dht11_state;
			    	if (dht11_state == DEVPRESENT_YES) {
				    if (device->value != dht11_humidity) {
					changed = true;
				    	device->value = dht11_humidity;
				    	device->timestamp = time(NULL);
				    }
				    dht11_next = now + 29;
			    	} else {
				    dht11_next = now + 1;
				}
//				pthread_mutex_unlock(&mutexes[LOCK_DEVICES]);
			    }
			}
			break;

#ifdef HAVE_WIRINGPI_H
		case DEVTYPE_GPIO:
			if (device->direction == DEVDIR_IN_BIN) {
//			    pthread_mutex_lock(&mutexes[LOCK_DEVICES]);
			    if (device->value != digitalRead(device->gpiopin)) {
				changed = true;
			    	device->value = digitalRead(device->gpiopin);
			    	device->offset = 0;
			    	device->timestamp = time(NULL);
			    }
//			    pthread_mutex_unlock(&mutexes[LOCK_DEVICES]);
			}
			break;

#endif
#ifdef USE_SIMULATOR
		case DEVTYPE_SIM:
			while (my_simulator_state == THREAD_PAUSE) { mDelay(20); };
//			pthread_mutex_lock(&mutexes[LOCK_DEVICES]);
			for (simulator = Config.simulators; simulator; simulator = simulator->next) {
			    int		val;

			    if (strcmp(simulator->room_tempaddress, device->address) == 0) {
				val = (int)((int)(simulator->room_temperature * 1000) / 500) * 500;
				if (device->value != val) {
			    	    device->value = val;
			    	    device->timestamp = time(NULL);
				    changed = true;
				}
			    } else if (strcmp(simulator->air_address, device->address) == 0) {
				val = (int)((int)((simulator->air_temperature * 1000) + 31.25) / 62.5) * 62.5;
				if (device->value != val) {
			    	    device->value = val;
			    	    device->timestamp = time(NULL);
				    changed = true;
				}
			    } else if (strcmp(simulator->beer_address, device->address) == 0) {
				val = (int)((int)((simulator->beer_temperature * 1000) + 31.25) / 62.5) * 62.5;
				if (device->value != val) {
			    	    device->value = val;
			    	    device->timestamp = time(NULL);
				    changed = true;
				}
			    } else if (strcmp(simulator->room_humaddress, device->address) == 0) {
				val = (int)((int)(simulator->room_humidity * 1000) / 500) * 500;
				if (device->value != val) {
				    device->value = val;
				    device->timestamp = time(NULL);
				    changed = true;
				}
			    } else if (strcmp(simulator->chiller_address, device->address) == 0) {
				val = (int)((int)(simulator->chiller_temperature * 1000) / 62.5) * 62.5;
				if (device->value != val) {
				    device->value = val;
				    device->timestamp = time(NULL);
				    changed = true;
				}
			    } else if (strcmp(simulator->door_address, device->address) == 0) {
				val = simulator->door_value;
				if (device->value != val) {
				    device->value = val;
				    device->timestamp = time(NULL);
				    changed = true;
				}
			    } else if (strcmp(simulator->psu_address, device->address) == 0) {
				val = simulator->psu_value;
				if (device->value != val) {
                                    device->value = val;
                                    device->timestamp = time(NULL);
                                    changed = true;
                                }
			    }
			}
//			pthread_mutex_unlock(&mutexes[LOCK_DEVICES]);
			break;
#endif
		default:
			break;
	    }

	    if (my_devices_shutdown)
		break;
	    /*
	     * Delay a bit after procesing a device.
	     */
	    mDelay(15);
	} /* for device = ... */
	if (my_devices_shutdown)
	    break;
	/*
	 * Delay a bit after all devices
	 */
	mDelay(50);
	if (changed) {
	    devices_ws();
	}
	mDelay(50);
    }

    syslog(LOG_NOTICE, "Thread my_devices_loop stopped");
    my_devices_state = 0;
    return 0;
}


mercurial