thermferm/devices.c

Mon, 25 Mar 2024 11:44:45 +0100

author
Michiel Broek <mbroek@mbse.eu>
date
Mon, 25 Mar 2024 11:44:45 +0100
changeset 649
64cfc01ec024
parent 648
62c5ed1b9cfd
child 650
0b215e4b814e
permissions
-rw-r--r--

Version 0.9.16. Final changes DHT11 reading via sysfs.

/*****************************************************************************
 * 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 "devices.h"
#include "rc-switch.h"
#include "panel.h"
#include "xutil.h"


int			my_devices_state = 0;

extern sys_config	Config;
extern int		my_shutdown;
extern pthread_mutex_t  mutexes[5];

#ifdef USE_SIMULATOR

extern int	SIMcooling;
extern int	SIMheating;
extern int	SIMfan;
extern int	SIMlight;

#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_last = (time_t)0;



/*
 * DHT11 sensor read.
 */
void dht11Read(char *address)
{
    int         	tries = 5, temp, hum;
    int			fd, rc, err;
    char		buffer[25], *dhtpath = NULL;

    while (tries) {

	dht11_temperature = -1;
	dht11_humidity = -1;
	dht11_state = DEVPRESENT_UNDEF;
	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;
	    goto retry;
//	    break;
	}
	rc = read(fd, buffer, 25);
	if (rc == -1) {
	    err = errno;
	    if (err == 110) {
		dht11_state = DEVPRESENT_NO;	/* Device is gone */
		break;
	    } else {
		dht11_state = DEVPRESENT_ERROR;
	    }
	    syslog(LOG_NOTICE, "DHT11 read temperature: %d %s", err, strerror(err));
	    dht11_temperature = -1;
	} 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;
		    break;
		} else {
		    dht11_state = DEVPRESENT_ERROR;
		}
	    	syslog(LOG_NOTICE, "DHT11 read temperature: %d %s", err, strerror(err));
	    	dht11_humidity = -1;
	    } else {
	    	sscanf(buffer, "%d", &hum);
	    	dht11_humidity = hum;
		dht11_state = DEVPRESENT_YES;
	    }
	    close(fd);
	    free(dhtpath);
	    dhtpath = NULL;
	}

	if (dht11_state == DEVPRESENT_YES) {
	    break;
	}
retry:
	if (tries > 0) {
	    tries--;
	    usleep(400000);
	}
    }

    syslog(LOG_NOTICE, "dht11 t:%d h:%d tries:%d state:%d", dht11_temperature, dht11_humidity, 6-tries, 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			rc, my_value, test_value;
#ifdef HAVE_WIRINGPI_H
    int			i;
    char		buf[40];
#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);
		    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) {
			/*
			 * 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);
				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 *)"SimCooler", device->address) == 0) || (strcmp((char *)"SimHeater", device->address) == 0) ||
			(strcmp((char *)"SimFan"   , device->address) == 0) || (strcmp((char *)"SimLight" , device->address) == 0)) {
			if (value != device->value) {
			    syslog(LOG_NOTICE, "SIM %s value=%d", device->address, value);
			}
			device->value = value;
			device->timestamp = time(NULL);
			if (strcmp((char *)"SimCooler", device->address) == 0)
			    SIMcooling = value;
			if (strcmp((char *)"SimHeater", device->address) == 0)
			    SIMheating = value;
			if (strcmp((char *)"SimFan", device->address) == 0)
			    SIMfan = value;
			if (strcmp((char *)"SimLight", device->address) == 0)
			    SIMlight = value;
		    }
		}
#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 0;

    pthread_mutex_lock(&mutexes[LOCK_DEVICES]);

    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;
	    }
	    pthread_mutex_unlock(&mutexes[LOCK_DEVICES]);
	    *value = tmp;
	    return present;
	}
    }

    pthread_mutex_unlock(&mutexes[LOCK_DEVICES]);
    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);
		    subdevices = 0;

		    /*
		     * ival is zero when a ghost sensor is detected.
		     */
		    if (ival > 0)
		    	subdevices = 1;
		    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->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 *)"DS1822 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->direction = DEVDIR_IN_BIN;
			    ndev->description = xstrcpy((char *)"DS2413 Dual channel addressable switch");
			} 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);

			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++;
		    }
		}
	    }
	}
	closedir(fd);
    }

    /*
     * 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);
    }
    syslog(LOG_NOTICE, "%s", dhtaddr);

    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
    found = 0;
    for (device = Config.devices; device; device = device->next) {
	if (device->type == DEVTYPE_SIM) {
	    found++;
	}
    }

    /*
     * Create simulated devices, or upgrade with new devices.
     */
    subdevices = 9;
    for (i = found; 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_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_BIN;
			ndev->address = xstrcpy((char *)"SimHeater");
			ndev->description = xstrcpy((char *)"Simulated heater");
			break;
	    case 4:	ndev->direction = DEVDIR_OUT_BIN;
			ndev->address = xstrcpy((char *)"SimCooler");
			ndev->description = xstrcpy((char *)"Simulated cooler");
			break;
	    case 5:	ndev->direction = DEVDIR_IN_ANALOG;
			ndev->address = xstrcpy((char *)"SimRoomHum");
			ndev->description = xstrcpy((char *)"Simulated room humidity");
			break;
	    case 6:	ndev->direction = DEVDIR_IN_ANALOG;
			ndev->address = xstrcpy((char *)"SimChillerTemp");
			ndev->description = xstrcpy((char *)"Simulated Chiller temperature");
			break;
	    case 7:	ndev->direction = DEVDIR_OUT_BIN;
			ndev->address = xstrcpy((char *)"SimFan");
			ndev->description = xstrcpy((char *)"Simulated fan");
			break;
	    case 8:	ndev->direction = DEVDIR_OUT_BIN;
			ndev->address = xstrcpy((char *)"SimLight");
			ndev->description = xstrcpy((char *)"Simulated light");
			break;
	}

	syslog(LOG_NOTICE, "New Simulator 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

    return rc;
}



void *my_devices_loop(void *threadid)
{
    devices_list	*device;
#ifdef USE_SIMULATOR
    simulator_list	*simulator;
#endif
    char		*addr = NULL, line1[60], line2[60], *p = NULL;
    FILE		*fp;
    int			temp, rc;
    time_t		now;

    my_devices_state = 1;
    syslog(LOG_NOTICE, "Thread my_devices_loop started");

#ifdef HAVE_WIRINGPI_H
    if ((rc = piHiPri(50)))
    	syslog(LOG_NOTICE, "my_devices_loop: piHiPri(50) rc=%d", rc);
#endif

    /*
     * Set the temperature sensors to 12 bits resolution and write it in EEPROM
     */
    for (device = Config.devices; device; device = device->next) {
	if ((device->type == DEVTYPE_W1) &&
	    ((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, "w"))) {
		rc = fprintf(fp, "12\n0\n");	// According to the kernel documentation. Seems to work.
		fclose(fp);
		if (rc != 5) {
		    syslog(LOG_NOTICE, "Program 12 bits resolution error rc=%d for %s", rc, addr);
                }
	    }
	    free(addr);
	    addr = NULL;
	}
    }

    /*
     * 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"))) {
				fgets(line1, 50, fp);	// Read 2 lines
				fgets(line2, 50, fp);
				fclose(fp);
				/*
				 * 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
				 */
				line1[strlen(line1)-1] = '\0';
				if ((line1[36] == 'Y') && (line1[37] == 'E')) {
				    /* CRC is Ok, continue */
				    if (device->present != DEVPRESENT_YES) {
					syslog(LOG_NOTICE, "sensor %s is Ok", device->address);
					pthread_mutex_lock(&mutexes[LOCK_DEVICES]);
					device->present = DEVPRESENT_YES;
					pthread_mutex_unlock(&mutexes[LOCK_DEVICES]);
				    }

				    line2[strlen(line2)-1] = '\0';
				    strtok(line2, (char *)"=");
				    p = strtok(NULL, (char *)"=");
				    rc = sscanf(p, "%d", &temp);
				    if ((rc == 1) && (device->value != temp)) {
					pthread_mutex_lock(&mutexes[LOCK_DEVICES]);
					if (temp < -55000) {
					    syslog(LOG_NOTICE, "sensor %s value error '%d` '%s`", device->address, temp, line1);
					    device->present = DEVPRESENT_ERROR;
					} else {
					    device->value = temp;
				    	    device->timestamp = time(NULL);
					    device->present = DEVPRESENT_YES;
					}
					pthread_mutex_unlock(&mutexes[LOCK_DEVICES]);
				    }
				} else {
				    syslog(LOG_NOTICE, "sensor %s CRC error '%s`", device->address, line1);
				    pthread_mutex_lock(&mutexes[LOCK_DEVICES]);
				    device->present = DEVPRESENT_ERROR;
				    pthread_mutex_unlock(&mutexes[LOCK_DEVICES]);
				}
			    } else {
				if (device->present != DEVPRESENT_NO) {
				    syslog(LOG_NOTICE, "sensor %s is missing", device->address);
				    pthread_mutex_lock(&mutexes[LOCK_DEVICES]);
				    device->present = DEVPRESENT_NO;
				    pthread_mutex_unlock(&mutexes[LOCK_DEVICES]);
				}
			    }
			    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);
				    pthread_mutex_lock(&mutexes[LOCK_DEVICES]);
				    device->present = DEVPRESENT_YES;
				    pthread_mutex_unlock(&mutexes[LOCK_DEVICES]);
				}
				/*
				 * 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) {
				    pthread_mutex_lock(&mutexes[LOCK_DEVICES]);
				    /*
				     * 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);
				    pthread_mutex_unlock(&mutexes[LOCK_DEVICES]);
				}
			    } else {
				if (device->present != DEVPRESENT_NO) {
				    syslog(LOG_NOTICE, "DS2413 %s is missing", device->address);
				    pthread_mutex_lock(&mutexes[LOCK_DEVICES]);
				    device->present = DEVPRESENT_NO;
				    pthread_mutex_unlock(&mutexes[LOCK_DEVICES]);
				}
			    }
			    free(addr);
			    addr = NULL;
			}

			break;

		case DEVTYPE_DHT:
			/*
			 * Don't read these to often, 2 seconds minimum delay.
			 * But we use 30 seconds interval.
			 */
			now = time(NULL);
			if ((int)(now - dht11_last) > 30) {
			    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) {
				    device->value = dht11_temperature;
				    device->timestamp = time(NULL);
			    	}
				pthread_mutex_unlock(&mutexes[LOCK_DEVICES]);
			    } else if (device->subdevice == 1) {
				/* Data already present */
				pthread_mutex_lock(&mutexes[LOCK_DEVICES]);
				device->present = dht11_state;
			    	if (dht11_state == DEVPRESENT_YES) {
				    device->value = dht11_humidity;
				    device->timestamp = time(NULL);
			    	}
				pthread_mutex_unlock(&mutexes[LOCK_DEVICES]);
				dht11_last = now;	/* Okay for a new reading. */
			    }
			}
			break;

#ifdef HAVE_WIRINGPI_H
		case DEVTYPE_GPIO:
			if (device->direction == DEVDIR_IN_BIN) {
			    pthread_mutex_lock(&mutexes[LOCK_DEVICES]);
			    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:
			pthread_mutex_lock(&mutexes[LOCK_DEVICES]);
			if (Config.simulators) {
			    simulator = Config.simulators;
			    if (device->subdevice == 0) {
			    	device->value = (int)((int)(simulator->room_temperature * 1000) / 500) * 500;
			    	device->timestamp = time(NULL);
			    } else if (device->subdevice == 1) {
			    	device->value = (int)((int)(simulator->air_temperature * 1000) / 62.5) * 62.5;
			    	device->timestamp = time(NULL);
			    } else if (device->subdevice == 2) {
			    	device->value = (int)((int)(simulator->beer_temperature * 1000) / 62.5) * 62.5;
			    	device->timestamp = time(NULL);
			    } else if (device->subdevice == 5) {
				device->value = (int)((int)(simulator->room_humidity * 1000) / 500) * 500;
				device->timestamp = time(NULL);
			    } else if (device->subdevice == 6) {
				device->value = (int)((int)(simulator->chiller_temperature * 1000) / 62.5) * 62.5;
				device->timestamp = time(NULL);
			    }
			}
			pthread_mutex_unlock(&mutexes[LOCK_DEVICES]);
			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");
    my_devices_state = 0;
    return 0;
}


mercurial