ted plugin: Updated ted.c based on Florian's input.
[collectd.git] / src / ted.c
1 /**
2  * collectd - src/ted.c
3  * Copyright (C) 2009  Eric Reed
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  *   Eric Reed <ericr at reedhome.net>
21  *
22  *  This is a collectd module for The Energy Detective: A low-cost whole
23  * house energy monitoring system. For more DEBUGrmation on TED, see
24  * http://theenergydetective.com
25  *
26  * This module was not created by Energy, Inc. nor is it supported by
27  * them in any way. It was created using DEBUGrmation from two sources:
28  * David Satterfield's TED module for Misterhouse, and Micah Dowty's TED
29  * Python Module.
30  * 
31  * This has only tested with the model 1001 RDU, with
32  * firmware version 9.01U. The USB port is uses the very common FTDI
33  * USB-to-serial chip, so the RDU will show up as a serial device on
34  * Windows, Mac OS, or Linux.
35  **/
36
37 #include "collectd.h"
38 #include "common.h"
39 #include "plugin.h"
40 #include "configfile.h"
41
42 #if HAVE_TERMIOS_H && HAVE_SYS_IOCTL_H && HAVE_MATH_H
43 # include <termios.h>
44 # include <sys/ioctl.h>
45 # include <math.h>
46 #else
47 # error "No applicable input method."
48 #endif
49
50
51
52 #define PKT_LENGTH 278
53 #define MAX_PKT 300
54 #define ESCAPE       0x10
55 #define PKT_BEGIN    0x04
56 #define PKT_END      0x03
57
58 #define DEFAULT_DEVICE "/dev/ttyUSB "
59
60 static char *device = NULL;
61 static int fd = -1;
62
63 static const char *config_keys[] = { "Device" };
64 static int config_keys_num = STATIC_ARRAY_SIZE (config_keys);
65
66 static int ted_read_value(double *kw, double *voltage)
67 {
68     unsigned char sResult[MAX_PKT];
69     unsigned char package_buffer[MAX_PKT];
70     char sResultByte;
71     int status;
72     int byte;
73     int package_length;
74     int escape_flag;
75     int end_flag;
76     int sResultnum;
77     struct timeval timeout;
78     char pkt_request[1] = {0xAA};
79     fd_set input;
80
81     int retry = 3; /* sometimes we receive garbadge */
82
83 /*     Initialize the input set*/
84     FD_ZERO(&input);
85     FD_SET(fd, &input);
86 /*     Initialize timout structure, set to 1 second    */
87
88  
89     do
90     {
91         timeout.tv_sec = 2;
92         timeout.tv_usec = 0;
93         escape_flag = 0;
94         end_flag = 0;
95         package_length = 0;
96         // clear out anything in the buffer
97         tcflush(fd, TCIFLUSH);
98         
99         status = write (fd, pkt_request,sizeof(pkt_request));
100         DEBUG ("status of write %d",status);
101         if (status < 0)
102         {
103                 ERROR ("ted plugin: swrite failed.");
104                 return (-1);
105         }
106
107     
108 /*       Loop until we find the end of the package */
109         while (end_flag == 0)
110         { 
111 /*          check for timeout or input error*/
112             status = select(fd+1, &input, NULL, NULL, &timeout);
113             if (status == 0) /* Timeout */
114             {
115                 INFO ("Timeout");
116                 break;
117             }
118             else if ((status == -1) && ((errno == EAGAIN) || (errno == EINTR)))
119             {
120                 DEBUG ("Not Ready");
121                 continue;
122             }
123             else if (status == -1)
124             {
125                 char errbuf[1024];
126                 ERROR ("ted plugin: select failed: %s",
127                                 sstrerror (errno, errbuf, sizeof (errbuf)));
128                 break;
129             }
130             
131             else
132             {
133 /*              find out how may bytes are in the input buffer*/
134                 ioctl(fd, FIONREAD, &byte);
135                 DEBUG  ("bytes in buffer %d",byte);
136                 if (byte <= 0) 
137                 {
138                     continue;
139                 } 
140                 // Read input buffer
141                 sResultnum = read(fd, sResult, MAX_PKT);
142                 DEBUG  ("bytes read %d",sResultnum);
143
144                 //
145                 // packet filter loop
146                 // 
147                 for (byte=0; byte< sResultnum; byte++) 
148                 {
149                     sResultByte = sResult[byte];
150                     // was byte before escape 
151                     if (escape_flag == 1) 
152                     {
153                         escape_flag = 0;
154                         // escape escape = single escape
155                         if ((sResultByte==ESCAPE) & (package_length > 0))
156                         {
157                             package_buffer[package_length] = ESCAPE;
158                             package_length++;  
159                         } 
160                         else if (sResultByte==PKT_BEGIN)
161                         {
162                             package_length=0;
163                         }
164                         else if  (sResultByte==PKT_END)
165                         {
166                             end_flag = 1;
167                             break;
168                         }
169                     }
170                     else if (sResultByte == ESCAPE)
171                     {
172                         escape_flag = 1;
173                     }
174                     // if we are in a package add byte to buffer
175                     // otherwise throw away
176                     else if (package_length >= 0)
177                     {
178                         package_buffer[package_length] = sResultByte;
179                         package_length++;  
180                     }
181                 }
182             }
183         }
184         DEBUG ("read package_length %d",package_length);
185                                 
186         if (package_length != PKT_LENGTH)
187         {
188             INFO ("Not the correct package");
189             // delay until next package is loaded into TED
190             // TED is updated once per second
191             usleep (1000000);
192             continue;
193         }       
194         //part of the DEBUG in the package
195         //get KiloWatts at char 247 and 248
196         //get Voltage at char 251 and 252 
197         *kw = ((package_buffer[248] * 256) + package_buffer[247])*10.0;
198         DEBUG ("kw %f",*kw);
199         *voltage = ((package_buffer[252] * 256) + package_buffer[251])/10.0;
200         DEBUG ("voltage %f",*voltage);
201         return (0); /* value received */
202
203
204     } while (--retry);
205
206     return (-2);  /* no value received */
207 } /* int ted_read_value */
208
209 static int ted_init (void)
210 {
211         int i;
212         double kw;
213         double voltage;
214         
215         if (device == NULL)
216         {
217             device = DEFAULT_DEVICE;
218         }
219         
220         for (i = 0; i < 10; i++)
221         {
222             device[strlen(device)-1] = i + '0'; 
223
224             if ((fd = open(device, O_RDWR | O_NOCTTY | O_NDELAY | O_NONBLOCK)) <= 0)
225             {
226                 DEBUG ("No device at fd %d", fd);
227                 close (fd);
228                 continue;
229             }
230             struct termios options;
231             // Get the current options for the port...
232             tcgetattr(fd, &options);                        
233             options.c_cflag = B19200 | CS8 | CSTOPB | CREAD | CLOCAL;
234             options.c_iflag = IGNBRK | IGNPAR;
235             options.c_oflag = 0;
236             options.c_lflag = 0;
237             options.c_cc[VTIME] = 20;
238             options.c_cc[VMIN]  = 250;
239                                 
240             // Set the new options for the port...
241             tcflush(fd, TCIFLUSH);
242             tcsetattr(fd, TCSANOW, &options);
243             
244             if (ted_read_value (&kw,&voltage) != 0) 
245             {
246                 DEBUG ("No device at fd %d", fd);
247                 close (fd);
248                 continue;
249             }
250             
251             INFO ("ted plugin: Device found at %s", device);
252             return (0);
253         }
254
255         ERROR ("ted plugin: No device found");
256         return (-1);
257 }
258 #undef LINE_LENGTH
259
260 static void ted_submit (char *type_instance, double value)
261 {
262         value_t values[1];
263         value_list_t vl = VALUE_LIST_INIT;
264
265         values[0].gauge = value;
266
267         vl.values = values;
268         vl.values_len = 1;
269         sstrncpy (vl.host, hostname_g, sizeof (vl.host));
270         sstrncpy (vl.plugin, "ted", sizeof (vl.plugin));
271         sstrncpy (vl.type_instance, type_instance, sizeof (vl.type_instance));
272         sstrncpy (vl.type, "ted", sizeof (vl.type));
273
274         plugin_dispatch_values (&vl);
275 }
276
277 static int ted_config (const char *key, const char *value)
278 {
279         if (strcasecmp ("Device", key) != 0)
280         {
281                 return (-1);
282         }       
283         
284         sfree (device);
285         device = sstrdup (value);
286         return (0);
287         
288 } /* int ted_config */
289
290
291 static int ted_read (void)
292 {
293         double kw;
294         double voltage;
295
296         if (fd < 0)
297                 return (-1);
298
299         if (ted_read_value (&kw,&voltage) != 0)
300                 return (-1);
301
302         ted_submit ("ted_kw", kw);      
303         ted_submit ("ted_voltage", voltage);
304         return (0);
305 } /* int ted_read */
306
307 static int ted_shutdown (void)
308 {
309         if (fd >= 0)
310         {
311                 close (fd);
312                 fd = -1;
313         }
314
315         return (0);
316 }
317
318 void module_register (void)
319 {
320         plugin_register_config ("ted", ted_config,
321                                 config_keys, config_keys_num);
322         plugin_register_init ("ted", ted_init);
323         plugin_register_read ("ted", ted_read);
324         plugin_register_shutdown ("ted", ted_shutdown);
325 } /* void module_register */