ads1115 analog-to-digital 4-channel added
[i2c-telemetry.git] / ina219.c
1 /*
2 * INA219 util - part of PowerCape by AndiceLabs
3 *
4 * Copyright (C) 2014  AndiceLabs admin@andicelabs.com  http://andicelabs.com
5 * Copyright (C) 2014  Zig Fisher flyrouter@gmail.com   http://zftlab.org
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 * GNU General Public License for more details.
16 *
17 *
18 * Example run and output:
19 *
20 * OpenWRT:~# ina219 -b 0 -i 60
21 *
22 * 22:49 12168mV  134.2mA
23 * 22:50 12168mV  239.9mA
24 * 22:51 12168mV  134.7mA
25 *
26 */
27
28 #include <unistd.h>
29 #include <stdlib.h>
30 #include <stdio.h>
31 #include <errno.h>
32 #include <endian.h>
33 #include <string.h>
34 #include <time.h>
35 #include <getopt.h>
36 #include <sys/types.h>
37 #include <sys/stat.h>
38 #include <sys/ioctl.h>
39 #include <fcntl.h>
40 #include <linux/i2c-dev.h>
41
42 #define CONFIG_REG          0
43 #define SHUNT_REG           1
44 #define BUS_REG             2
45 #define POWER_REG           3
46 #define CURRENT_REG         4
47 #define CALIBRATION_REG     5
48
49 #define AVR_ADDRESS         0x21
50 #define INA_ADDRESS         0x40
51
52 typedef enum {
53     OP_DUMP,
54     OP_VOLTAGE,
55     OP_CURRENT,
56     OP_MONITOR,
57     OP_NONE
58 } op_type;
59
60 op_type operation = OP_DUMP;
61
62 int interval = 60;
63 int i2c_bus = 0;
64 int i2c_address = INA_ADDRESS;
65 int handle;
66 int whole_numbers = 0;
67
68
69 void msleep( int msecs )
70 {
71     usleep( msecs * 1000 );
72 }
73
74
75 int i2c_read( void *buf, int len )
76 {
77     int rc = 0;
78
79     if ( read( handle, buf, len ) != len )
80     {
81         printf( "I2C read failed: %s\n", strerror( errno ) );
82         rc = -1;
83     }
84
85     return rc;
86 }
87
88
89 int i2c_write( void *buf, int len )
90 {
91     int rc = 0;
92
93     if ( write( handle, buf, len ) != len ) 
94     {
95         printf( "I2C write failed: %s\n", strerror( errno ) );
96         rc = -1;
97     }
98
99     return rc;
100 }
101
102
103 int register_read( unsigned char reg, unsigned short *data )
104 {
105     int rc = -1;
106     unsigned char bite[ 4 ];
107
108     bite[ 0 ] = reg;
109     if ( i2c_write( bite, 1 ) == 0 )
110     {
111         if ( i2c_read( bite, 2 ) == 0 )
112         {
113             *data = ( bite[ 0 ] << 8 ) | bite[ 1 ];
114             rc = 0;
115         }
116     }
117
118     return rc;
119 }
120
121
122 int register_write( unsigned char reg, unsigned short data )
123 {
124     int rc = -1;
125     unsigned char bite[ 4 ];
126
127     bite[ 0 ] = reg;
128     bite[ 1 ] = ( data >> 8 ) & 0xFF;
129     bite[ 2 ] = ( data & 0xFF );
130
131     if ( i2c_write( bite, 3 ) == 0 )
132     {
133         rc = 0;
134     }
135
136     return rc;
137 }
138
139
140 void show_usage( char *progname )
141 {
142     fprintf( stderr, "Usage: %s <mode> \n", progname );
143     fprintf( stderr, "   Mode (required):\n" );
144     fprintf( stderr, "      -h --help           Show usage.\n" );
145     fprintf( stderr, "      -i --interval       Set interval for monitor mode.\n" );
146     fprintf( stderr, "      -w --whole          Show whole numbers only. Useful for scripts.\n" );
147     fprintf( stderr, "      -v --voltage        Show battery voltage in mV.\n" );
148     fprintf( stderr, "      -c --current        Show battery current in mA.\n" );
149     fprintf( stderr, "      -a --address <addr> Override I2C address of INA219 from default of 0x%02X.\n", i2c_address );
150     fprintf( stderr, "      -b --bus <i2c bus>  Override I2C bus from default of %d.\n", i2c_bus );
151     exit( 1 );
152 }
153
154
155 void parse( int argc, char *argv[] )
156 {
157     while( 1 )
158     {
159         static const struct option lopts[] =
160         {
161             { "address",    0, 0, 'a' },
162             { "bus",        0, 0, 'b' },
163             { "current",    0, 0, 'c' },
164             { "help",       0, 0, 'h' },
165             { "interval",   0, 0, 'i' },
166             { "voltage",    0, 0, 'v' },
167             { "whole",      0, 0, 'w' },
168             { NULL,         0, 0, 0 },
169         };
170         int c;
171
172         c = getopt_long( argc, argv, "a:b:chi:vw", lopts, NULL );
173
174         if( c == -1 )
175             break;
176
177         switch( c )
178         {
179             case 'a':
180             {
181                 errno = 0;
182                 i2c_address = (int)strtol( optarg, NULL, 0 );
183                 if ( errno != 0 )
184                 {
185                     fprintf( stderr, "Unknown address parameter %s.\n", optarg );
186                     exit( 1 );
187                 }
188                 break;
189             }
190
191             case 'b':
192             {
193                 errno = 0;
194                 i2c_bus = (int)strtol( optarg, NULL, 0 );
195                 if ( errno != 0 )
196                 {
197                     fprintf( stderr, "Unknown bus parameter %s.\n", optarg );
198                     exit( 1 );
199                 }
200                 break;
201             }
202
203             case 'c':
204             {
205                 operation = OP_CURRENT;
206                 break;
207             }
208
209             default:
210             case 'h':
211             {
212                 operation = OP_NONE;
213                 show_usage( argv[ 0 ] );
214                 break;
215             }
216
217             case 'i':
218             {
219                 operation = OP_MONITOR;
220                 interval = atoi( optarg );
221                 if ( ( interval == 0 ) && ( errno != 0 ) )
222                 {
223                     fprintf( stderr, "Invalid interval value\n" );
224                     exit( 1 );
225                 }
226                 break;
227             }
228
229             case 'v':
230             {
231                 operation = OP_VOLTAGE;
232                 break;
233             }
234
235             case 'w':
236             {
237                 whole_numbers = 1;
238                 break;
239             }
240         }
241     }
242 }
243
244
245 int get_voltage( float *mv )
246 {
247     short bus;
248
249     if ( register_read( BUS_REG, (unsigned short*)&bus ) != 0 )
250     {
251         return -1;
252     }
253
254     *mv = ( float )( ( bus & 0xFFF8 ) >> 1 );
255     return 0;
256 }
257
258
259 int get_current( float *ma )
260 {
261     short shunt;
262
263     if ( register_read( SHUNT_REG, &shunt ) != 0 )
264     {
265         return -1;
266     }
267
268     *ma = (float)shunt / 10;
269     return 0;
270 }
271
272
273 void show_current( void )
274 {
275     float ma;
276
277     if ( get_current( &ma ) )
278     {
279         fprintf( stderr, "Error reading current\n" );
280         return;
281     }
282
283     if ( whole_numbers )
284     {
285         printf( "%4.0f\n", ma );
286     }
287     else
288     {
289         printf( "%04.1f\n", ma );
290     }
291 }
292
293
294 void show_voltage( void )
295 {
296     float mv;
297
298     if ( get_voltage( &mv ) )
299     {
300         fprintf( stderr, "Error reading voltage\n" );
301         return;
302     }
303     printf( "%4.0f\n", mv );
304 }
305
306
307 void show_voltage_current( void )
308 {
309     float mv, ma;
310
311     if ( get_current( &ma ) || get_voltage( &mv ) )
312     {
313         fprintf( stderr, "Error reading voltage/current\n" );
314         return;
315     }
316
317     if ( whole_numbers )
318     {
319         printf( "%04.0fmV  %4.0fmA\n", mv, ma );
320     }
321     else
322     {
323         printf( "%04.0fmV  %04.1fmA\n", mv, ma );
324     }
325 }
326
327
328 void monitor( void )
329 {
330     struct tm *tmptr;
331     time_t seconds;
332
333     while ( 1 )
334     {
335         seconds = time( NULL );
336         tmptr = localtime( &seconds );
337         printf( "%02d:%02d ", tmptr->tm_hour, tmptr->tm_min );
338         show_voltage_current();
339         sleep( interval );
340     }
341 }
342
343
344 int main( int argc, char *argv[] )
345 {
346     char filename[ 20 ];
347
348     parse( argc, argv );
349
350     snprintf( filename, 19, "/dev/i2c-%d", i2c_bus );
351     handle = open( filename, O_RDWR );
352     if ( handle < 0 ) 
353     {
354         fprintf( stderr, "Error opening bus %d: %s\n", i2c_bus, strerror( errno ) );
355         exit( 1 );
356     }
357
358     if ( ioctl( handle, I2C_SLAVE, i2c_address ) < 0 ) 
359     {
360         fprintf( stderr, "Error setting address %02X: %s\n", i2c_address, strerror( errno ) );
361         exit( 1 );
362     }
363
364     switch ( operation )
365     {
366         case OP_DUMP:
367         {
368             show_voltage_current();
369             break;
370         }
371
372         case OP_VOLTAGE:
373         {
374             show_voltage();
375             break;
376         }
377
378         case OP_CURRENT:
379         {
380             show_current();
381             break;
382         }
383
384         case OP_MONITOR:
385         {
386             monitor();
387             break;
388         }
389
390         default:
391         case OP_NONE:
392         {
393             break;
394         }
395     }
396
397     close( handle );
398     return 0;
399 }
400