thermferm/one-wire.c

Mon, 15 Apr 2024 17:04:57 +0200

author
Michiel Broek <mbroek@mbse.eu>
date
Mon, 15 Apr 2024 17:04:57 +0200
changeset 678
cc49115e769e
parent 674
6cabc02f4c8d
child 690
3a5b019e9acc
permissions
-rw-r--r--

Better websocket broadcast messages. Added GLOBAL JSON command to the server. Better logic to trigger websocket and mqtt data updates for the fermenter units. Websocket receive added fermenter mode, stage, setpoints, switches. Added more css styles for the fermenter screen. Added the fermenter screen php and javascript.

/**
 * @brief One-wire devices
 *
 * Copyright (C) 2024
 *
 * 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 MBSE BBS; see the file COPYING.  If not, write to the Free
 * Software Foundation, 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
 */

extern int		debug;

#include "thermferm.h"
#include "statetbl.h"
#include "one-wire.h"
#include "devices.h"
#include "delay.h"
#include "futil.h"
#include "xutil.h"

#define	W1_TEMP_RESOLUTION	12


extern sys_config	Config;
extern pthread_mutex_t	mutexes[5];

int			my_one_wire_state = 0;
int			my_one_wire_shutdown = 0;
w1_list			*w1_devices = NULL;


static int one_wire(void);

void *my_one_wire_loop(void *threadid)
{
    my_one_wire_state = 1;
    syslog(LOG_NOTICE, "Thread my_one_wire_loop started");

    /*
     * Run the state machine
     */
    one_wire();

    /*
     * Remove the dynamic tables.
     */

    syslog(LOG_NOTICE, "Thread my_one_wire_loop stopped");
    my_one_wire_state = 0;
    return 0;
}




SM_DECL(one_wire,(char *)"one-wire")
SM_STATES
    ScanNew,
    ScanDel,
    Read2413,
    ReadTemp,
    Missing
SM_NAMES
    (char *)"ScanNew",
    (char *)"ScanDel",
    (char *)"Read2413",
    (char *)"ReadTemp",
    (char *)"Missing"
SM_EDECL

    int			found, i, rc, value, conv_time;
    FILE		*fp;
    devices_list	*device;
    w1_list		*dev_w1, *n_w1, *cur_w1 = NULL;
    char		buffer[25], w1type[10], *devfile = NULL;
    uint8_t		state, output, newval;

SM_START(ScanNew)

SM_STATE(ScanNew)

    if (my_one_wire_shutdown) {
	SM_SUCCESS;
    }

    /*
     * Scan for current one-wire devices.
     */
    fp = fopen("/sys/devices/w1_bus_master1/w1_master_slaves", "r");
    if (fp == NULL) {
	syslog(LOG_NOTICE, "No w1_bus_master: %s", strerror(errno));
	SM_ERROR;
    }
    while ((fgets(buffer, 25, fp))) {
	buffer[strlen(buffer)-1] = '\0';
	strncpy(w1type, buffer, 2);
	w1type[2] = '\0';

	/*
	 * Check if device is known and already detected.
	 */
	if ((strcmp(w1type, (char *)"3a") == 0) || (strcmp(w1type, (char *)"10") == 0) ||
	    (strcmp(w1type, (char *)"22") == 0) || (strcmp(w1type, (char *)"28") == 0) ||
	    (strcmp(w1type, (char *)"3b") == 0) || (strcmp(w1type, (char *)"42") == 0)) {
	    found = FALSE;
	    for (dev_w1 = w1_devices; dev_w1; dev_w1 = dev_w1->next) {
		if (strcmp(dev_w1->address, buffer) == 0) {
		    found = TRUE;
		    dev_w1->timestamp = time(NULL);
		    if (dev_w1->present != DEVPRESENT_YES) {
			syslog(LOG_NOTICE, "One-wire device %s is back", buffer);
			pthread_mutex_lock(&mutexes[LOCK_ONE_WIRE]);
			dev_w1->present = DEVPRESENT_YES;
			pthread_mutex_unlock(&mutexes[LOCK_ONE_WIRE]);
			for (device = Config.devices; device; device = device->next) {
			    if (strcmp(dev_w1->address, device->address) == 0) {
//				pthread_mutex_lock(&mutexes[LOCK_DEVICES]);
				device->present = DEVPRESENT_YES;
//				pthread_mutex_unlock(&mutexes[LOCK_DEVICES]);
			    }
			}
		    }
		    break;
		}
	    }
	    if (found == FALSE) {
		syslog(LOG_NOTICE, "One-wire device %s add new", buffer);
		n_w1 = (w1_list *)malloc(sizeof(w1_list));
		n_w1->next = NULL;
		n_w1->address = xstrcpy(buffer);
		strncpy(n_w1->family, buffer, 2);
		n_w1->family[2] = '\0';
		n_w1->present = DEVPRESENT_YES;
		n_w1->value = (strcmp(w1type, (char *)"3a") == 0) ? 3:-1;
		n_w1->timestamp = time(NULL);

		pthread_mutex_lock(&mutexes[LOCK_ONE_WIRE]);
		if (w1_devices == NULL) {
		    w1_devices = n_w1;
		    cur_w1 = w1_devices;	/* Point to first device	*/
		} else {
		    for (dev_w1 = w1_devices; dev_w1; dev_w1 = dev_w1->next) {
			if (dev_w1->next == NULL) {
			    dev_w1->next = n_w1;
			    break;
			}
		    }
		}
		pthread_mutex_unlock(&mutexes[LOCK_ONE_WIRE]);
	    }
	} else if (strcmp(w1type, (char *)"00")) {
	    syslog(LOG_NOTICE, "One-wire device %ld %s unknown", strlen(buffer), buffer);
	}
    }
    fclose(fp);
    SM_PROCEED(ScanDel);

SM_STATE(ScanDel)

    if (my_one_wire_shutdown) {
	SM_SUCCESS;
    }

    /*
     * Scan from the linked list if all devices are still present.
     */
    for (dev_w1 = w1_devices; dev_w1; dev_w1 = dev_w1->next) {
	devfile = xstrcpy((char *)"/sys/bus/w1/devices/");
	devfile = xstrcat(devfile, dev_w1->address);
	devfile = xstrcat(devfile, (char *)"/uevent");
	if (file_exist(devfile, R_OK) && (dev_w1->present == DEVPRESENT_YES)) {
	    /*
	     * Gone missing
	     */
	    syslog(LOG_NOTICE, "One-wire device %s is missing", dev_w1->address);
	    pthread_mutex_lock(&mutexes[LOCK_ONE_WIRE]);
	    dev_w1->present = DEVPRESENT_NO;
	    pthread_mutex_unlock(&mutexes[LOCK_ONE_WIRE]);
	    for (device = Config.devices; device; device = device->next) {
		if (strcmp(dev_w1->address, device->address) == 0) {
//		    pthread_mutex_lock(&mutexes[LOCK_DEVICES]);
		    device->present = DEVPRESENT_NO;
//		    pthread_mutex_unlock(&mutexes[LOCK_DEVICES]);
		}
	    }
	}
	free(devfile);
	devfile = NULL;
    }

    SM_PROCEED(Read2413);

SM_STATE(Read2413)

    if (my_one_wire_shutdown) {
	SM_SUCCESS;
    }

    for (dev_w1 = w1_devices; dev_w1; dev_w1 = dev_w1->next) {
	if (strcmp(dev_w1->family, "3a") == 0) {
	    for (i = 0; i < 2; i++) {
		for (device = Config.devices; device; device = device->next) {
		    if ((strcmp(dev_w1->address, device->address) == 0) && (device->subdevice == i) && (device->direction == DEVDIR_IN_BIN)) {
			/*
			 * First make sure that this device is configured as input
			 * to drive the output high. Fix if programmed as 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 ((i == 0) && ((state & 0x02) == 0)) {				/* Fix A side		*/
				syslog(LOG_NOTICE, "One-wire device %s-%d out %02x -> %02x", dev_w1->address, i, output, output | 0x01);
				output |= 0x01;
				write_w1(device->address, (char *)"output", output);
				mDelay(10);
				if ((rc = read_w1(device->address, (char *)"state")) >= 0)	/* Read PIO again	*/
				    state = (unsigned int)rc;
			    }
			    if ((i == 1) && ((state & 0x08) == 0)) {				/* Fix B side		*/
				syslog(LOG_NOTICE, "One-wire device %s-%d out %02x -> %02x", dev_w1->address, i, output, output | 0x02);
				output |= 0x02;
				write_w1(device->address, (char *)"output", output);
				mDelay(10);
				if ((rc = read_w1(device->address, (char *)"state")) >= 0)      /* Read PIO again       */
				    state = (unsigned int)rc;
			    }

			    newval = ((state & 0x04) >> 1) + (state & 0x01);
			    if (newval != dev_w1->value) {
			    	syslog(LOG_NOTICE, "One-wire device %s-%d in %02x value %d => %d", dev_w1->address, i, state, dev_w1->value, newval);
			    	dev_w1->value = newval;
			    }

//			    pthread_mutex_lock(&mutexes[LOCK_DEVICES]);
			    /*
			     * Read PIOA or PIOB pin state bits
			     */
			    if (device->subdevice == 0)
				device->value = (state & 0x01) ? 0 : 1;
			    else if (device->subdevice == 1)
				device->value = (state & 0x04) ? 0 : 1;
			    device->timestamp = time(NULL);
//			    pthread_mutex_unlock(&mutexes[LOCK_DEVICES]);
			}
			mDelay(20);
		    } else if ((strcmp(dev_w1->address, device->address) == 0) && (device->subdevice == i) && (device->direction == DEVDIR_OUT_BIN)) {
			/*
			 * Sync output state
			 */
			if ((rc = read_w1(device->address, (char *)"state")) >= 0) {
                            state = (unsigned int)rc;
			    newval = output = (state & 0x01) + ((state & 0x04) >> 1);

			    if (device->subdevice == 0) {
				newval = (newval & 0xfe);
				newval |= (device->value) ? 0x00 : 0x01;
			    } else if (device->subdevice == 1) {
				newval = (newval & 0xfd);
				newval |= (device->value) ? 0x00 : 0x02;
			    }

			    if (output != newval) {
				if ((write_w1(dev_w1->address, (char *)"output", newval)) == 0) {
				    syslog(LOG_NOTICE, "One-wire device %s-%d out %02x -> %02x", dev_w1->address, i, output, newval);
				    dev_w1->value = newval;
				}
			    }
			}
		    }
		} /* for (device = Config.devices; ... */
	    }
	}
    }

    SM_PROCEED(ReadTemp);

SM_STATE(ReadTemp)

    if (my_one_wire_shutdown) {
	SM_SUCCESS;
    }

    /*
     * cur_w1 points to the next not handled device.
     */
    if (cur_w1 != NULL) {

	if ((strcmp(cur_w1->family, (char *)"10") == 0) || (strcmp(cur_w1->family, (char *)"22") == 0) || (strcmp(cur_w1->family, (char *)"28") == 0) ||
	    (strcmp(cur_w1->family, (char *)"3b") == 0) || (strcmp(cur_w1->family, (char *)"42") == 0)) {
	    devfile = xstrcpy((char *)"/sys/bus/w1/devices/");
	    devfile = xstrcat(devfile, cur_w1->address);
	    devfile = xstrcat(devfile, (char *)"/resolution");
	    if ((fp = fopen(devfile, "r+"))) {
		if ((fgets(buffer, 25, fp))) {
		     sscanf(buffer, "%d", &value);
		     if (value != W1_TEMP_RESOLUTION) {
			syslog(LOG_NOTICE, "One-wire device %s set resolution from %d to %d", cur_w1->address, value, W1_TEMP_RESOLUTION);
			fseek(fp, 0L, SEEK_SET);
			sprintf(buffer, "%d", W1_TEMP_RESOLUTION);
			fputs(buffer, fp);
		     }
		}
		fclose(fp);
		free(devfile);

		conv_time = 760;
		devfile = xstrcpy((char *)"/sys/bus/w1/devices/");
		devfile = xstrcat(devfile, cur_w1->address);
		devfile = xstrcat(devfile, (char *)"/conv_time");
		if ((fp = fopen(devfile, "r"))) {
		    if ((fgets(buffer, 25, fp))) {
			sscanf(buffer, "%d", &conv_time);
		    }
		    fclose(fp);
		}
		free(devfile);

		devfile = xstrcpy((char *)"/sys/bus/w1/devices/");
		devfile = xstrcat(devfile, cur_w1->address);
		devfile = xstrcat(devfile, (char *)"/temperature");
		if ((fp = fopen(devfile, "r"))) {
//		    syslog(LOG_NOTICE, "One-wire device %s temperature is open, delay %d", cur_w1->address, conv_time);
		    mDelay(conv_time);
		    if ((fgets(buffer, 25, fp))) {
			sscanf(buffer, "%d", &value);
//			if (cur_w1->value != value)
//			    syslog(LOG_NOTICE, "One-wire device %s temperature read %d => %d", cur_w1->address, cur_w1->value, value);
			cur_w1->value = value;		/* devices.c will pick this up */
		    } else {
			syslog(LOG_NOTICE, "One-wire device %s temperature read error", cur_w1->address);
		    }
		    fclose(fp);
		}

	    } else {
		syslog(LOG_NOTICE, "One-wire device %s open: %s", cur_w1->address, strerror(errno));
	    }
	    free(devfile);
	    devfile = NULL;
	}

	for (;;) {
	    if (cur_w1->next != NULL) {
	    	cur_w1 = cur_w1->next;
	    } else {
	    	cur_w1 = w1_devices;
	    }
	    if ((strcmp(cur_w1->family, (char *)"10") == 0) || (strcmp(cur_w1->family, (char *)"22") == 0) || (strcmp(cur_w1->family, (char *)"28") == 0) ||
		(strcmp(cur_w1->family, (char *)"3b") == 0) || (strcmp(cur_w1->family, (char *)"42") == 0))
		break;
	}

    } else {
    	mDelay(750);
    }

    SM_PROCEED(Missing);

SM_STATE(Missing)

    if (my_one_wire_shutdown) {
	SM_SUCCESS;
    }

    sleep(1);
    SM_PROCEED(ScanNew);

SM_END
SM_RETURN

mercurial