]> git.itanic.dy.fi Git - BME280_driver/commitdiff
Add simple tempd daemon
authorTimo Kokkonen <timo.t.kokkonen@iki.fi>
Tue, 9 Mar 2021 18:38:57 +0000 (20:38 +0200)
committerTimo Kokkonen <timo.t.kokkonen@iki.fi>
Tue, 9 Mar 2021 19:45:00 +0000 (21:45 +0200)
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 <timo.t.kokkonen@iki.fi>
Makefile
tempd.c [new file with mode: 0644]
tempd.service [new file with mode: 0644]

index 8c125c5f015c482fb5d614919dfa228fa6aafcff..bda0b8110b8bd33cafead92bbbb697b8d54ab9a5 100644 (file)
--- 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 (file)
index 0000000..51b9ded
--- /dev/null
+++ b/tempd.c
@@ -0,0 +1,259 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <math.h>
+#include <time.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <sys/ioctl.h>
+
+#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 (file)
index 0000000..a83f35c
--- /dev/null
@@ -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