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