From 40ef33b2f2ca38c6fbcecbb369053ff4c21ff5f6 Mon Sep 17 00:00:00 2001 From: Florian Loeffler Date: Wed, 13 Jan 2021 17:49:13 +0100 Subject: [PATCH] initial commit including pre-compiled binary for armv6 --- Makefile | 10 ++ README.md | 0 ina219 | Bin 0 -> 13900 bytes ina219.c | 373 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 383 insertions(+) create mode 100644 Makefile create mode 100644 README.md create mode 100755 ina219 create mode 100644 ina219.c diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..25c9d2b --- /dev/null +++ b/Makefile @@ -0,0 +1,10 @@ +default: ina219 + +ina219: ina219.c + gcc -o ina219 ina219.c + +clean: + rm ina219 + +install: + cp ina219 /usr/bin/ina219 diff --git a/README.md b/README.md new file mode 100644 index 0000000..e69de29 diff --git a/ina219 b/ina219 new file mode 100755 index 0000000000000000000000000000000000000000..291ce0e5e1a2f1babd9847635af0337848e48424 GIT binary patch literal 13900 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define CONFIG_REG 0 +#define SHUNT_REG 1 +#define BUS_REG 2 +#define POWER_REG 3 +#define CURRENT_REG 4 +#define CALIBRATION_REG 5 + +#define AVR_ADDRESS 0x21 +#define INA_ADDRESS 0x40 + +typedef enum { + OP_DUMP, + OP_VOLTAGE, + OP_CURRENT, + OP_MONITOR, + OP_NONE +} op_type; + +op_type operation = OP_DUMP; + +int interval = 60; +int i2c_bus = 1; +int i2c_address = INA_ADDRESS; +int handle; +int whole_numbers = 0; + + +void msleep( int msecs ) +{ + usleep( msecs * 1000 ); +} + + +int i2c_read( void *buf, int len ) +{ + int rc = 0; + + if ( read( handle, buf, len ) != len ) + { + printf( "I2C read failed: %s\n", strerror( errno ) ); + rc = -1; + } + + return rc; +} + + +int i2c_write( void *buf, int len ) +{ + int rc = 0; + + if ( write( handle, buf, len ) != len ) + { + printf( "I2C write failed: %s\n", strerror( errno ) ); + rc = -1; + } + + return rc; +} + + +int register_read( unsigned char reg, unsigned short *data ) +{ + int rc = -1; + unsigned char bite[ 4 ]; + + bite[ 0 ] = reg; + if ( i2c_write( bite, 1 ) == 0 ) + { + if ( i2c_read( bite, 2 ) == 0 ) + { + *data = ( bite[ 0 ] << 8 ) | bite[ 1 ]; + rc = 0; + } + } + + return rc; +} + + +int register_write( unsigned char reg, unsigned short data ) +{ + int rc = -1; + unsigned char bite[ 4 ]; + + bite[ 0 ] = reg; + bite[ 1 ] = ( data >> 8 ) & 0xFF; + bite[ 2 ] = ( data & 0xFF ); + + if ( i2c_write( bite, 3 ) == 0 ) + { + rc = 0; + } + + return rc; +} + + +void show_usage( char *progname ) +{ + fprintf( stderr, "Usage: %s \n", progname ); + fprintf( stderr, " Mode (required):\n" ); + fprintf( stderr, " -h --help Show usage.\n" ); + fprintf( stderr, " -i --interval Set interval for monitor mode.\n" ); + fprintf( stderr, " -w --whole Show whole numbers only. Useful for scripts.\n" ); + fprintf( stderr, " -v --voltage Show battery voltage in mV.\n" ); + fprintf( stderr, " -c --current Show battery current in mA.\n" ); + fprintf( stderr, " -a --address Override I2C address of INA219 from default of 0x%02X.\n", i2c_address ); + fprintf( stderr, " -b --bus Override I2C bus from default of %d.\n", i2c_bus ); + exit( 1 ); +} + + +void parse( int argc, char *argv[] ) +{ + while( 1 ) + { + static const struct option lopts[] = + { + { "address", 0, 0, 'a' }, + { "bus", 0, 0, 'b' }, + { "current", 0, 0, 'c' }, + { "help", 0, 0, 'h' }, + { "interval", 0, 0, 'i' }, + { "voltage", 0, 0, 'v' }, + { "whole", 0, 0, 'w' }, + { NULL, 0, 0, 0 }, + }; + int c; + + c = getopt_long( argc, argv, "a:b:chi:vw", lopts, NULL ); + + if( c == -1 ) + break; + + switch( c ) + { + case 'a': + { + errno = 0; + i2c_address = (int)strtol( optarg, NULL, 0 ); + if ( errno != 0 ) + { + fprintf( stderr, "Unknown address parameter %s.\n", optarg ); + exit( 1 ); + } + break; + } + + case 'b': + { + errno = 0; + i2c_bus = (int)strtol( optarg, NULL, 0 ); + if ( errno != 0 ) + { + fprintf( stderr, "Unknown bus parameter %s.\n", optarg ); + exit( 1 ); + } + break; + } + + case 'c': + { + operation = OP_CURRENT; + break; + } + + default: + case 'h': + { + operation = OP_NONE; + show_usage( argv[ 0 ] ); + break; + } + + case 'i': + { + operation = OP_MONITOR; + interval = atoi( optarg ); + if ( ( interval == 0 ) && ( errno != 0 ) ) + { + fprintf( stderr, "Invalid interval value\n" ); + exit( 1 ); + } + break; + } + + case 'v': + { + operation = OP_VOLTAGE; + break; + } + + case 'w': + { + whole_numbers = 1; + break; + } + } + } +} + + +int get_voltage( float *mv ) +{ + short bus; + + if ( register_read( BUS_REG, (unsigned short*)&bus ) != 0 ) + { + return -1; + } + + *mv = ( float )( ( bus & 0xFFF8 ) >> 1 ); + return 0; +} + + +int get_current( float *ma ) +{ + short shunt; + + if ( register_read( SHUNT_REG, &shunt ) != 0 ) + { + return -1; + } + + *ma = (float)shunt / 10; + return 0; +} + + +void show_current( void ) +{ + float ma; + + if ( get_current( &ma ) ) + { + fprintf( stderr, "Error reading current\n" ); + return; + } + + if ( whole_numbers ) + { + printf( "%4.0f\n", ma ); + } + else + { + printf( "%04.1f\n", ma ); + } +} + + +void show_voltage( void ) +{ + float mv; + + if ( get_voltage( &mv ) ) + { + fprintf( stderr, "Error reading voltage\n" ); + return; + } + printf( "%4.0f\n", mv ); +} + + +void show_voltage_current( void ) +{ + float mv, ma; + + if ( get_current( &ma ) || get_voltage( &mv ) ) + { + fprintf( stderr, "Error reading voltage/current\n" ); + return; + } + + if ( whole_numbers ) + { + printf( "%4.0fmV %4.0fmA\n", mv, ma ); + } + else + { + printf( "%4.0fmV %4.1fmA\n", mv, ma ); + } +} + + +void monitor( void ) +{ + struct tm *tmptr; + time_t seconds; + + while ( 1 ) + { + seconds = time( NULL ); + tmptr = localtime( &seconds ); + printf( "%2d:%02d:%02d ", tmptr->tm_hour, tmptr->tm_min, tmptr->tm_sec ); + show_voltage_current(); + sleep( interval ); + } +} + + +int main( int argc, char *argv[] ) +{ + char filename[ 20 ]; + + parse( argc, argv ); + + snprintf( filename, 19, "/dev/i2c-%d", i2c_bus ); + handle = open( filename, O_RDWR ); + if ( handle < 0 ) + { + fprintf( stderr, "Error opening bus %d: %s\n", i2c_bus, strerror( errno ) ); + exit( 1 ); + } + + if ( ioctl( handle, I2C_SLAVE, i2c_address ) < 0 ) + { + fprintf( stderr, "Error setting address %02X: %s\n", i2c_address, strerror( errno ) ); + exit( 1 ); + } + + switch ( operation ) + { + case OP_DUMP: + { + show_voltage_current(); + break; + } + + case OP_VOLTAGE: + { + show_voltage(); + break; + } + + case OP_CURRENT: + { + show_current(); + break; + } + + case OP_MONITOR: + { + monitor(); + break; + } + + default: + case OP_NONE: + { + break; + } + } + + close( handle ); + return 0; +} +