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