]> git.itanic.dy.fi Git - BME280_driver/blob - tempd.c
Add simple tempd daemon
[BME280_driver] / tempd.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <unistd.h>
5 #include <fcntl.h>
6 #include <math.h>
7 #include <time.h>
8 #include <errno.h>
9 #include <sys/types.h>
10 #include <sys/socket.h>
11 #include <netinet/in.h>
12 #include <arpa/inet.h>
13 #include <sys/ioctl.h>
14
15 #define min(a, b) ((a) < (b) ? (a) : (b))
16 #define max(a, b) ((a) > (b) ? (a) : (b))
17
18 static int read_1wire_temp(const char *path, double *temp)
19 {
20         char buf[16];
21         int fd;
22         int ret;
23
24         fd = open(path, O_RDONLY);
25         if (fd < 0) {
26                 printf("%s: Failed to open file %s: %m\n", __func__, path);
27                 return -1;
28         }
29
30         ret = read(fd, buf, sizeof(buf) - 1);
31         if (ret < 0) {
32                 printf("%s: Failed read temperature from file %s: %m\n",
33                         __func__, path);
34                 goto out_close;
35         }
36         buf[ret] = '\0';
37
38         *temp = atof(buf);
39
40         ret = 0;
41
42 out_close:
43         close(fd);
44
45         return ret;
46 }
47
48 struct data_entry {
49         time_t time;
50         double temperature;
51         double humidity;
52         double pressure;
53         double dew_point;
54 };
55
56 static char *addrstr(struct sockaddr_in *addr)
57 {
58         static char str[32];
59
60         snprintf(str, sizeof(str), "%s:%d",
61                 inet_ntoa(addr->sin_addr), ntohs(addr->sin_port));
62         str[sizeof(str) - 1] = '\0';
63
64         return str;
65 }
66
67 static int fetch_bmed_data(struct sockaddr_in *addr, struct data_entry *data)
68 {
69         char buf[64];
70         int fd;
71         int ret;
72
73         fd = socket(AF_INET, SOCK_STREAM, 0);
74         if (fd < 0) {
75                 printf("%s: Failed to create socket: %m\n", __func__);
76                 return -1;
77         }
78
79         ret = connect(fd, (struct sockaddr *)addr, sizeof(*addr));
80         if (ret < 0) {
81                 printf("%s: Failed to connect at %s: %m\n", __func__, addrstr(addr));
82                 goto out_close;
83         }
84
85         /*
86          * Send timestamp of the first entry we want. Data is
87          * generated every 60 seconds, so this should give us the last
88          * entry only
89          */
90         ret = dprintf(fd, "%ld\n", time(NULL) - 60);
91         if (ret < 0) {
92                 printf("%s: Failed to send timestamp to bmed at %s: %m\n",
93                         __func__, addrstr(addr));
94                 goto out_close;
95         }
96
97         ret = read(fd, buf, sizeof(buf) - 1);
98         if (ret < 0) {
99                 printf("%s: Failed read data from bmed at %s: %m\n",
100                         __func__, addrstr(addr));
101                 goto out_close;
102         }
103         buf[ret] = 0;
104
105         ret = sscanf(buf, "%ld:%lf:%lf:%lf:%lf",
106                 &data->time,
107                 &data->temperature,
108                 &data->pressure,
109                 &data->humidity,
110                 &data->dew_point);
111         if (ret != 5) {
112                 printf("%s: Read only %d datapoints out of 5 from string: %s\n", __func__,
113                         ret, buf);
114                 ret = -1;
115         } else {
116                 ret = 0;
117         }
118
119 out_close:
120         close(fd);
121
122         return ret;
123 }
124
125 static int parse_ip_port(const char *str, struct sockaddr_in *addr)
126 {
127         char ip[16];
128         char *s;
129         long int len;
130         int ret;
131
132         s = strstr(str, ":");
133         len = s - str;
134         if (len >= (long)sizeof(ip) || len < 0 || !s) {
135                 printf("%s: Unable to parse ip:port from string %s, %ld\n",
136                         __func__, str, len);
137                 return -1;
138         }
139
140         strncpy(ip, str, len);
141         ip[len] = '\0';
142
143         ret = inet_aton(ip, &addr->sin_addr);
144         if (!ret) {
145                 printf("Invalid address: %s\n", ip);
146                 return -1;
147         }
148
149         s++;
150         addr->sin_port = htons(atoi(s));
151
152         printf("%s:Parsed %s from %s: \n", __func__, addrstr(addr), str);
153
154         addr->sin_family = AF_INET;
155         return 0;
156 }
157
158 static int daemon_loop(const char *temp_path, struct sockaddr_in *bme_addr)
159 {
160         struct sockaddr_in addr_in;
161         struct data_entry data;
162         int listen_fd, fd, ret;
163         double temp = 0;
164
165         bzero(&addr_in, sizeof(addr_in));
166
167         listen_fd = socket(AF_INET, SOCK_STREAM, 0);
168         if (listen_fd < 0) {
169
170         }
171
172         addr_in.sin_family = AF_INET;
173         addr_in.sin_port = htons(6000);
174         addr_in.sin_addr.s_addr = INADDR_ANY;
175
176         ret = bind(listen_fd, (struct sockaddr *)&addr_in, sizeof(addr_in));
177         if (ret < 0) {
178                 printf("%s: Failed to bind to addr %s: %m\n",
179                         __func__, addrstr(&addr_in));
180                 goto close_socket;
181         }
182
183         ret = listen(listen_fd, 5);
184         if (ret < 0) {
185                 printf("Failed to listen(): %m\n");
186                 goto close_socket;
187         }
188
189         while (1) {
190                 char buf[128];
191                 socklen_t peerlen = 0;
192                 int onewire_ok, bme_ok;
193                 int i, ret, len;
194
195                 fd = accept(listen_fd, (struct sockaddr *)&addr_in, &peerlen);
196                 if (fd < 0) {
197                         printf("%s: Error while accept(): %m\n", __func__);
198                         continue;
199                 }
200
201                 ret = read_1wire_temp(temp_path, &temp);
202                 onewire_ok = !ret;
203                 if (onewire_ok)
204                         printf("1wire data: %.2lf\n", temp);
205
206
207                 ret = fetch_bmed_data(bme_addr, &data);
208                 bme_ok = !ret;
209                 if (bme_ok)
210                         printf("bmed data: %ld %.4lf %.4lf %.4lf %.4lf\n",
211                                 data.time, data.temperature, data.humidity,
212                                 data.pressure, data.dew_point);
213
214                 if (!onewire_ok)
215                         temp = data.temperature;
216
217                 len = snprintf(buf, sizeof(buf), "%.1lf %.1lf %.1lf %.1lf\n\r",
218                         min(temp, data.temperature),
219                         data.humidity,
220                         data.pressure,
221                         data.dew_point);
222
223                 for (i = 0; i < (int)sizeof(buf) && buf[i]; i++)
224                         if (buf[i] == '.')
225                                 buf[i] = ',';
226
227                 ret = write(fd, buf, len);
228                 if (ret < 0)
229                         printf("%s: Failed to write to %s: %m\n", __func__, addrstr(&addr_in));
230
231                 close(fd);
232         }
233
234 close_socket:
235         close(listen_fd);
236         return ret;
237 }
238
239 int main(int argc, char *argv[])
240 {
241         char *path;
242         struct sockaddr_in addr;
243         int ret;
244
245         if (argc < 3) {
246                 printf("Usage: %s [1wire path] [bmed ip:addr]\n", argv[0]);
247                 return 1;
248         }
249
250         bzero(&addr, sizeof(addr));
251         ret = parse_ip_port(argv[2], &addr);
252         if (ret < 0)
253                 return 1;
254
255         path = argv[1];
256
257         daemon_loop(path, &addr);
258         return 0;
259 }