Wed, 24 Apr 2024 20:36:06 +0200
Finished save fermenter uit edit.
/** * @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 *)"]}"); syslog(LOG_NOTICE, "%s", payload); 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; 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); 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; 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; 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"))) { // 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) { 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