From aeefd48ce5facaa25c0debd94ec3a3ba18149087 Mon Sep 17 00:00:00 2001 From: Timo Kokkonen Date: Tue, 9 Mar 2021 20:38:57 +0200 Subject: [PATCH] Add simple tempd daemon This simple one takes temperature readings from both bmed server port and a 1wire sensor path found behind owfs. Only one temperature reading is give back, the one with lower temperature. The rest of the data is taken from bme280 sensor. The data is fetched via tcp port 6000 off the daemon. Signed-off-by: Timo Kokkonen --- Makefile | 10 +- tempd.c | 259 ++++++++++++++++++++++++++++++++++++++++++++++++++ tempd.service | 12 +++ 3 files changed, 279 insertions(+), 2 deletions(-) create mode 100644 tempd.c create mode 100644 tempd.service diff --git a/Makefile b/Makefile index 8c125c5..bda0b81 100644 --- a/Makefile +++ b/Makefile @@ -9,11 +9,14 @@ else QUIET_LINK = @echo " LINK " $@; endif -all: bmed +all: bmed tempd bmed: bmed.o bme280.o $(QUIET_LINK)gcc -Wall -Wextra -g -lm -lpthread $^ -o $@ +tempd: tempd.o + $(QUIET_LINK)gcc -Wall -Wextra -g $^ -o $@ + %.o: %.c $(QUIET_CC)$(CC) -MMD -MF .$@.d $(CFLAGS) -c $< -o $@ $(Q)cp .$@.d .$@.P; \ @@ -26,6 +29,9 @@ FORCE: TAGS: FORCE etags *.[ch] -install: bmed +install: bmed tempd install bmed $(DEST)/usr/bin install -m 644 bmed.service $(DEST)/lib/systemd/system + + install tempd $(DEST)/usr/bin + install -m 644 tempd.service $(DEST)/lib/systemd/system diff --git a/tempd.c b/tempd.c new file mode 100644 index 0000000..51b9ded --- /dev/null +++ b/tempd.c @@ -0,0 +1,259 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define min(a, b) ((a) < (b) ? (a) : (b)) +#define max(a, b) ((a) > (b) ? (a) : (b)) + +static int read_1wire_temp(const char *path, double *temp) +{ + char buf[16]; + int fd; + int ret; + + fd = open(path, O_RDONLY); + if (fd < 0) { + printf("%s: Failed to open file %s: %m\n", __func__, path); + return -1; + } + + ret = read(fd, buf, sizeof(buf) - 1); + if (ret < 0) { + printf("%s: Failed read temperature from file %s: %m\n", + __func__, path); + goto out_close; + } + buf[ret] = '\0'; + + *temp = atof(buf); + + ret = 0; + +out_close: + close(fd); + + return ret; +} + +struct data_entry { + time_t time; + double temperature; + double humidity; + double pressure; + double dew_point; +}; + +static char *addrstr(struct sockaddr_in *addr) +{ + static char str[32]; + + snprintf(str, sizeof(str), "%s:%d", + inet_ntoa(addr->sin_addr), ntohs(addr->sin_port)); + str[sizeof(str) - 1] = '\0'; + + return str; +} + +static int fetch_bmed_data(struct sockaddr_in *addr, struct data_entry *data) +{ + char buf[64]; + int fd; + int ret; + + fd = socket(AF_INET, SOCK_STREAM, 0); + if (fd < 0) { + printf("%s: Failed to create socket: %m\n", __func__); + return -1; + } + + ret = connect(fd, (struct sockaddr *)addr, sizeof(*addr)); + if (ret < 0) { + printf("%s: Failed to connect at %s: %m\n", __func__, addrstr(addr)); + goto out_close; + } + + /* + * Send timestamp of the first entry we want. Data is + * generated every 60 seconds, so this should give us the last + * entry only + */ + ret = dprintf(fd, "%ld\n", time(NULL) - 60); + if (ret < 0) { + printf("%s: Failed to send timestamp to bmed at %s: %m\n", + __func__, addrstr(addr)); + goto out_close; + } + + ret = read(fd, buf, sizeof(buf) - 1); + if (ret < 0) { + printf("%s: Failed read data from bmed at %s: %m\n", + __func__, addrstr(addr)); + goto out_close; + } + buf[ret] = 0; + + ret = sscanf(buf, "%ld:%lf:%lf:%lf:%lf", + &data->time, + &data->temperature, + &data->pressure, + &data->humidity, + &data->dew_point); + if (ret != 5) { + printf("%s: Read only %d datapoints out of 5 from string: %s\n", __func__, + ret, buf); + ret = -1; + } else { + ret = 0; + } + +out_close: + close(fd); + + return ret; +} + +static int parse_ip_port(const char *str, struct sockaddr_in *addr) +{ + char ip[16]; + char *s; + long int len; + int ret; + + s = strstr(str, ":"); + len = s - str; + if (len >= (long)sizeof(ip) || len < 0 || !s) { + printf("%s: Unable to parse ip:port from string %s, %ld\n", + __func__, str, len); + return -1; + } + + strncpy(ip, str, len); + ip[len] = '\0'; + + ret = inet_aton(ip, &addr->sin_addr); + if (!ret) { + printf("Invalid address: %s\n", ip); + return -1; + } + + s++; + addr->sin_port = htons(atoi(s)); + + printf("%s:Parsed %s from %s: \n", __func__, addrstr(addr), str); + + addr->sin_family = AF_INET; + return 0; +} + +static int daemon_loop(const char *temp_path, struct sockaddr_in *bme_addr) +{ + struct sockaddr_in addr_in; + struct data_entry data; + int listen_fd, fd, ret; + double temp = 0; + + bzero(&addr_in, sizeof(addr_in)); + + listen_fd = socket(AF_INET, SOCK_STREAM, 0); + if (listen_fd < 0) { + + } + + addr_in.sin_family = AF_INET; + addr_in.sin_port = htons(6000); + addr_in.sin_addr.s_addr = INADDR_ANY; + + ret = bind(listen_fd, (struct sockaddr *)&addr_in, sizeof(addr_in)); + if (ret < 0) { + printf("%s: Failed to bind to addr %s: %m\n", + __func__, addrstr(&addr_in)); + goto close_socket; + } + + ret = listen(listen_fd, 5); + if (ret < 0) { + printf("Failed to listen(): %m\n"); + goto close_socket; + } + + while (1) { + char buf[128]; + socklen_t peerlen = 0; + int onewire_ok, bme_ok; + int i, ret, len; + + fd = accept(listen_fd, (struct sockaddr *)&addr_in, &peerlen); + if (fd < 0) { + printf("%s: Error while accept(): %m\n", __func__); + continue; + } + + ret = read_1wire_temp(temp_path, &temp); + onewire_ok = !ret; + if (onewire_ok) + printf("1wire data: %.2lf\n", temp); + + + ret = fetch_bmed_data(bme_addr, &data); + bme_ok = !ret; + if (bme_ok) + printf("bmed data: %ld %.4lf %.4lf %.4lf %.4lf\n", + data.time, data.temperature, data.humidity, + data.pressure, data.dew_point); + + if (!onewire_ok) + temp = data.temperature; + + len = snprintf(buf, sizeof(buf), "%.1lf %.1lf %.1lf %.1lf\n\r", + min(temp, data.temperature), + data.humidity, + data.pressure, + data.dew_point); + + for (i = 0; i < (int)sizeof(buf) && buf[i]; i++) + if (buf[i] == '.') + buf[i] = ','; + + ret = write(fd, buf, len); + if (ret < 0) + printf("%s: Failed to write to %s: %m\n", __func__, addrstr(&addr_in)); + + close(fd); + } + +close_socket: + close(listen_fd); + return ret; +} + +int main(int argc, char *argv[]) +{ + char *path; + struct sockaddr_in addr; + int ret; + + if (argc < 3) { + printf("Usage: %s [1wire path] [bmed ip:addr]\n", argv[0]); + return 1; + } + + bzero(&addr, sizeof(addr)); + ret = parse_ip_port(argv[2], &addr); + if (ret < 0) + return 1; + + path = argv[1]; + + daemon_loop(path, &addr); + return 0; +} diff --git a/tempd.service b/tempd.service new file mode 100644 index 0000000..a83f35c --- /dev/null +++ b/tempd.service @@ -0,0 +1,12 @@ +[Unit] +Description=BME280 and 1wire data daemon + +[Service] +EnvironmentFile=/etc/tempd.conf +ExecStart=/usr/bin/tempd ${OWIRE_PATH} ${BMED_IPORT} + +Restart=always +RestartSec=15 + +[Install] +WantedBy=multi-user.target -- 2.44.0