thermferm/one-wire.c

Wed, 01 May 2024 14:38:37 +0200

author
Michiel Broek <mbroek@mbse.eu>
date
Wed, 01 May 2024 14:38:37 +0200
changeset 715
f5d85af156ab
parent 712
93a87fe230cc
child 724
01e3936f62d4
permissions
-rw-r--r--

Added device_present() function to easy update device present from one-wire and simulator devices. When a simulator temperature sensor present is changed, the device table is changed too. Controlling simulator relays is now for each simulator. The simulator runs under the state machine. If something changed in the running simulator, all data is broadcasted over websocket. Completed the web editor.

/**
 * @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 "websocket.h"
#include "futil.h"
#include "xutil.h"

#define	W1_TEMP_RESOLUTION	12


extern sys_config	Config;
extern pthread_mutex_t	mutexes[5];
extern const char	DEVPRESENT[4][6];


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


static int one_wire(void);


/*
 * Return json data for one device.
 */
char *one_wire_json(w1_list *dev_w1)
{
    char        *payload;
    char        vbuf[64];

    payload = xstrcpy((char *)"{\"address\":\"");
    payload = xstrcat(payload, dev_w1->address);
    payload = xstrcat(payload, (char *)"\",\"family\":\"");
    payload = xstrcat(payload, dev_w1->family);
    payload = xstrcat(payload, (char *)"\",\"present\":\"");
    payload = xstrcat(payload, (char *)DEVPRESENT[dev_w1->present]);
    payload = xstrcat(payload, (char *)"\",\"value\":");
    snprintf(vbuf, 63, "%d", dev_w1->value);
    payload = xstrcat(payload, vbuf);
    payload = xstrcat(payload, (char *)",\"timestamp\":");
    snprintf(vbuf, 63, "%ld", (long)dev_w1->timestamp);
    payload = xstrcat(payload, vbuf);
    payload = xstrcat(payload, (char *)"}");

    return payload;
}


void one_wire_ws(void)
{
    bool	comma = false;
    char	*payload = NULL, *payloadu = NULL;
    w1_list	*dev_w1;

    payload = xstrcpy((char *)"{\"type\":\"onewire\",\"metric\":[");
    for (dev_w1 = w1_devices; dev_w1; dev_w1 = dev_w1->next) {
        if (comma)
            payload = xstrcat(payload, (char *)",");
        payloadu = one_wire_json(dev_w1);
        payload = xstrcat(payload, payloadu);
        comma = true;
        free(payloadu);
        payloadu = NULL;
    }
    payload = xstrcat(payload, (char *)"]}");
    ws_broadcast(payload);
    free(payload);
    payload = NULL;
}



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,
    Websocket
SM_NAMES
    (char *)"ScanNew",
    (char *)"ScanDel",
    (char *)"Read2413",
    (char *)"ReadTemp",
    (char *)"Missing",
    (char *)"Websocket"
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;
    bool		changed;

SM_START(ScanNew)

SM_STATE(ScanNew)

    if (my_one_wire_shutdown) {
	SM_SUCCESS;
    }

    changed = false;
    /*
     * 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;
		    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;
			dev_w1->timestamp = time(NULL);
			pthread_mutex_unlock(&mutexes[LOCK_ONE_WIRE]);
			changed = true;
			device_present(dev_w1->address, DEVPRESENT_YES);
		    }
		    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);
		changed = true;

		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", (long)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;
	    dev_w1->timestamp = time(NULL);
	    pthread_mutex_unlock(&mutexes[LOCK_ONE_WIRE]);
	    changed = true;
	    device_present(dev_w1->address, DEVPRESENT_NO);
	}
	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;
				dev_w1->timestamp = time(NULL);
				changed = true;
			    }

//			    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;
				    dev_w1->timestamp = time(NULL);
				    changed = true;
				}
			    }
			}
		    }
		} /* 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)) &&
	     (cur_w1->present == DEVPRESENT_YES)) {
	    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 device is removed, value is negative (errno?) */
		     if ((value > 0) && (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"))) {
		    mDelay(conv_time);
		    if ((fgets(buffer, 25, fp))) {
			sscanf(buffer, "%d", &value);
			if (cur_w1->value != value) {
			    cur_w1->timestamp = time(NULL);
			    changed = true;
			    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;
	} /* if temperature sensor and present */

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

    SM_PROCEED(Websocket);

SM_STATE(Websocket)

    for (i = 0; i < 10; i++) {
	if (my_one_wire_shutdown) {
	    SM_SUCCESS;
	}

	mDelay(100);
	if (changed && i == 5)
	    one_wire_ws();
    }
    SM_PROCEED(ScanNew);

SM_END
SM_RETURN

mercurial