1 /**
2 * collectd - src/multimeter.c
3 * Copyright (C) 2005,2006 Peter Holik
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the
7 * Free Software Foundation; either version 2 of the License, or (at your
8 * option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18 *
19 * Authors:
20 * Peter Holik <peter at holik.at>
21 *
22 * Used multimeter: Metex M-4650CR
23 **/
24
25 #include "collectd.h"
26
27 #include "plugin.h"
28 #include "utils/common/common.h"
29
30 #if HAVE_TERMIOS_H && HAVE_SYS_IOCTL_H
31 #include <sys/ioctl.h>
32 #include <termios.h>
33 #else
34 #error "No applicable input method."
35 #endif
36
37 static int fd = -1;
38
39 #define LINE_LENGTH 14
multimeter_read_value(double * value)40 static int multimeter_read_value(double *value) {
41 int retry = 3; /* sometimes we receive garbadge */
42
43 do {
44 struct timeval time_end;
45
46 tcflush(fd, TCIFLUSH);
47
48 if (gettimeofday(&time_end, NULL) < 0) {
49 ERROR("multimeter plugin: gettimeofday failed: %s", STRERRNO);
50 return -1;
51 }
52 time_end.tv_sec++;
53
54 while (1) {
55 char buf[LINE_LENGTH];
56 char *range;
57 int status;
58 fd_set rfds;
59 struct timeval timeout;
60 struct timeval time_now;
61
62 status = swrite(fd, "D", 1);
63 if (status != 0) {
64 ERROR("multimeter plugin: swrite failed.");
65 return -1;
66 }
67
68 FD_ZERO(&rfds);
69 FD_SET(fd, &rfds);
70
71 if (gettimeofday(&time_now, NULL) < 0) {
72 ERROR("multimeter plugin: "
73 "gettimeofday failed: %s",
74 STRERRNO);
75 return -1;
76 }
77 if (timeval_cmp(time_end, time_now, &timeout) < 0)
78 break;
79
80 status = select(fd + 1, &rfds, NULL, NULL, &timeout);
81
82 if (status > 0) /* usually we succeed */
83 {
84 status = read(fd, buf, LINE_LENGTH);
85
86 if ((status < 0) && ((errno == EAGAIN) || (errno == EINTR)))
87 continue;
88
89 /* Format: "DC 00.000mV \r" */
90 if (status > 0 && status == LINE_LENGTH) {
91 *value = strtod(buf + 2, &range);
92
93 if (range > (buf + 6)) {
94 range = buf + 9;
95
96 switch (*range) {
97 case 'p':
98 *value *= 1.0E-12;
99 break;
100 case 'n':
101 *value *= 1.0E-9;
102 break;
103 case 'u':
104 *value *= 1.0E-6;
105 break;
106 case 'm':
107 *value *= 1.0E-3;
108 break;
109 case 'k':
110 *value *= 1.0E3;
111 break;
112 case 'M':
113 *value *= 1.0E6;
114 break;
115 case 'G':
116 *value *= 1.0E9;
117 break;
118 }
119 } else
120 return -1; /* Overflow */
121
122 return 0; /* value received */
123 } else
124 break;
125 } else if (!status) /* Timeout */
126 {
127 break;
128 } else if ((status == -1) && ((errno == EAGAIN) || (errno == EINTR))) {
129 continue;
130 } else /* status == -1 */
131 {
132 ERROR("multimeter plugin: "
133 "select failed: %s",
134 STRERRNO);
135 break;
136 }
137 }
138 } while (--retry);
139
140 return -2; /* no value received */
141 } /* int multimeter_read_value */
142
multimeter_init(void)143 static int multimeter_init(void) {
144 char device[] = "/dev/ttyS ";
145
146 for (int i = 0; i < 10; i++) {
147 device[strlen(device) - 1] = i + '0';
148
149 if ((fd = open(device, O_RDWR | O_NOCTTY)) != -1) {
150 struct termios tios = {0};
151 int rts = TIOCM_RTS;
152 double value;
153
154 tios.c_cflag = B1200 | CS7 | CSTOPB | CREAD | CLOCAL;
155 tios.c_iflag = IGNBRK | IGNPAR;
156 tios.c_oflag = 0;
157 tios.c_lflag = 0;
158 tios.c_cc[VTIME] = 3;
159 tios.c_cc[VMIN] = LINE_LENGTH;
160
161 tcflush(fd, TCIFLUSH);
162 tcsetattr(fd, TCSANOW, &tios);
163 ioctl(fd, TIOCMBIC, &rts);
164
165 if (multimeter_read_value(&value) < -1) {
166 close(fd);
167 fd = -1;
168 } else {
169 INFO("multimeter plugin: Device "
170 "found at %s",
171 device);
172 return 0;
173 }
174 }
175 }
176
177 ERROR("multimeter plugin: No device found");
178 return -1;
179 }
180 #undef LINE_LENGTH
181
multimeter_submit(double value)182 static void multimeter_submit(double value) {
183 value_list_t vl = VALUE_LIST_INIT;
184
185 vl.values = &(value_t){.gauge = value};
186 vl.values_len = 1;
187 sstrncpy(vl.plugin, "multimeter", sizeof(vl.plugin));
188 sstrncpy(vl.type, "multimeter", sizeof(vl.type));
189
190 plugin_dispatch_values(&vl);
191 }
192
multimeter_read(void)193 static int multimeter_read(void) {
194 double value;
195
196 if (fd < 0)
197 return -1;
198
199 if (multimeter_read_value(&value) != 0)
200 return -1;
201
202 multimeter_submit(value);
203 return 0;
204 } /* int multimeter_read */
205
multimeter_shutdown(void)206 static int multimeter_shutdown(void) {
207 if (fd >= 0) {
208 close(fd);
209 fd = -1;
210 }
211
212 return 0;
213 }
214
module_register(void)215 void module_register(void) {
216 plugin_register_init("multimeter", multimeter_init);
217 plugin_register_read("multimeter", multimeter_read);
218 plugin_register_shutdown("multimeter", multimeter_shutdown);
219 } /* void module_register */
220