Projects

Auto-Ranging USB Current Meter

ATtiny84-based

Peter Stewart

Issue 64, November 2022

This article includes additional downloadable resources.
Please log in to access.

Log in

Have you ever wanted to measure the current consumed by your project when it is in Deep Sleep or when it wakes up? The current range can be from a microAmp (µA) to hundreds of milliAmps (mA). It is important to know how much current your project consumes, especially when operating from batteries.

The most common method of measuring current is by employing a shunt resistor of known resistance, passing the current through it and measuring the voltage across it. Then using Ohms Law, determine the current, i.e. I = V/R. The main problem with this method is that the voltage drop across the resistor reduces the voltage to your project. So, if you use a very low value resistor, then the voltage drop will be low. However, only being able to measure a small voltage is fine for large currents, i.e. Amps. But when measuring µA, the voltage will also be µV or even less. The answer, of course, is to use some form of voltage amplification. This is the most common way to measure current with a reasonable degree of accuracy.

Figure 1 Simple current measurement

A simple example is shown here in Figure 1. The voltage across RSHUNT is amplified by a Differential Amplifier. The Output Voltage becomes:

Vo = (V+-V-) x G

where G is the gain of the Differential Amplifier. Then, to determine the current through RSHUNT and also RLOAD :

ILOAD = (V+-V-)/RSHUNT

or

ILOAD = (Vo/G)/ RSHUNT

The broad overview

This auto-ranging USB Current Meter allows the measurement of currents consumed by your project, from less than µA up to 4A. Just insert the Current Meter between your power source and your project.

It uses three current sensors to cover the entire range, which are controlled by an ATtiny84 using software developed through the Arduino IDE.

As well as current, the voltage and the power consumed are shown on an OLED display. Also on the OLED display, a graphical representation of the current, as it is consumed, is also presented.

Current Sensor Selection

To be able to represent the current on some form of digital display, we need to read the analog voltage output from the Differential Amplifier and convert it to digital format. We can do this using a microcontroller through an analog input which is connected to an internal Analog to Digital Converter (ADC). Most microcontroller ADCs are only 10-bits and not very accurate. To get higher resolution, 12-bits would be better.

We did consider using Current Sensors similar to the ACS712, which provides the differential Amplifier and relies on an external ADC similar to the ADS1015.

The ADS1015 is a 4-channel 12-bit ADC, so it should be capable of performing the function required. While ADS1015 modules are available (although not cheap), the ADS1015 chips are not, due to the current chip shortage. Also, I thought it would be more efficient if the ADC function could be performed by the Current Sensor. This would reduce noise being induced in the analog signal from the current sensor.

We therefore settled on the INA219 current sensor. Modules were available and so were the chips. The other advantage of using the INA219 is that it also provides measurement of the Voltage going to the load. It also provides current conversion and power calculations if required. The inbuilt ADC is 12-bits and can perform conversions and all calculations in 532 µsec or almost 1880 samples per second (SPS).

Switch Selection

The INA219 current sensor provides four programmable gain ranges for its internal differential amplifier, ±40mV, ±80mV, ±160mV and ±320mV. We chose the ±40mV range as it gives the minimum voltage drop across the sensor and thus the least voltage loss to the powered project. As you may see from the photograph of the INA219 module, the shunt resistor is 0.1Ω (R100). This means for a 12-bit ADC, the maximum current measurement is 400mA and the minimum is approximately 100µA. This does not allow us to measure Amps to µA. What we need is multiple INA219s with different shunt resistors. We decided on using three INA219s with shunt resistor values of 0.01Ω, 1Ω and 100Ω. Table 1 shows the current measurement ranges. There is some overlap to allow selection of the best range while maintaining a usable resolution.

Shunt (Ω)

Max Current

Min Current

0.01

4 Amps

~1 mAmp

1

40 mAmps

~10µAmps

100

400µAmps

~0.1µAmps

The problem with using three INA219s is that depending on how much current is being drawn, you want to switch to the INA219 with the most appropriate range. Figure 2 shows, in principle, how the INA219s are configured.

Figure 2. Multi-range Current Measurement

Each INA219 has a switch associated with it. Each switch has a digital control, from the microcontroller to close or open the switch. The current, voltage and power readings are read from each INA219 by the microcontroller via the Inter-Integrated Circuit (I2C) bus.

Each switch had to be of a very low resistance so as to have a minimum voltage drop across it. This is most important on the high current (4A) range. There are really only two types of components that meet this criteria: A relay or a MOSFET. A relay is eliminated very quickly as it is too slow with response time around 50 milli-seconds. A MOSFET can switch in less than a microsecond. The arrangement we used for this switch is shown in Figure 3.

Figure 3. P/N MOSFET Switch

We have used a P-MOSFET as the actual switch and an N-MOSFET to drive the P-MOSFET and interface to the microcontroller. When the output from the microcontroller is low (0 Volts), the N-MOSFET (2N7000) is off. The P-MOSFET is also off as its Gate is pulled up to the same voltage as the Drain. When the output from the microcontroller is High (3.3V), the N-MOSFET is turned on and it pulls the Gate of the P-MOSFET to 0 Volts. This causes the P-MOSFET to turn on completely and conduct current. The selection of the N-MOSFET is not critical. It must be able to turn on when a Gate voltage of 3.3V is applied, i.e. VGS(TH) should be less than 3.3V. Also the Drain Source Voltage (VDS) should be higher than 20V. This is because we intended to use the USB Current Meter with devices (e.g. power banks) that can supply up to 20V output as per the QC3.0 protocol.

The 2N7000 meets the requirements easily (VGS(TH) – 3VDC max and VDS – 60VDC). The 2N7000 is also cheap and readily available. The choice of the SI4143DY as the P-MOSFET was much more difficult. There were many characteristics I had to consider. The most important was a very low on resistance (RDS(on)). As with the choice of using the ±40 mV range of the INA219 to ensure the absolute minimum voltage drop between the Project (the Load) and the Power Source (e.g. a battery), the RDS(on) of the P-MOSFET must cause minimum voltage drop also. The next characteristic was that the P-MOSFET must be able to take more than the 20V between the Source and Gate (VGS), for the same reasons as VDS on the 2N7000. Also, VGS(TH) must be low for when the Power Source is only supplying 5VDC.

Lastly, the P-MOSFET must be capable of passing a minimum of 4A (ID). After much searching and looking for devices that are readily available and reasonably priced, we found the SI4143DY manufactured by Vishay Siliconix. The SI4143DY has a RDS(on) of 0.0092Ω when VGS is -4.5VDC with ID at -20.8A. Also, VGS can be up to ±25VDC, VGS(TH) is -2.5VDC max. The only initial potential downside was that the SI4143DY was packaged in a SO8 package. But, as you will see later in the Build section, this turned out to be very useful.

Microcontroller Selection

We needed a microcontroller that was reasonably fast in order to read data from the INA219 with a sample rate of at least 1880 SPS. The microcontroller must also have enough I/O pins to drive three MOSFET switches, two I/O pins for the I2C bus to communicate with the INA219s, plus also two I/O pins for pushbuttons. The pushbuttons are required for Reset and Hold functions (which we thought would be useful). Without the pushbuttons, we could have used an ATtiny85, 8-pin microcontroller. However, we want the utility of the pushbuttons more than a small footprint. Also, during prototyping, it is always useful to have a serial output for debugging purposes. So the next readily available microcontroller with more pins and at a good price is the ATtiny84. It is a 14-pin device, and comes in a Dual Inline Package (DIP), great for prototyping. It also has 11 usable I/O pins and we have a few in our parts box. It has 8kBytes of Flash memory for our program, which was almost not enough especially with the debug serial print statements. In the later section on the Program, you will see we had to be very efficient with coding.

Display Selection

The selection of the Display was fairly easy. We wanted to display the output voltage to the powered project, the current and the power consumed. We also wanted a graphical representation of the current consumption in realtime.

Our project is based on the “ATtinyPowerMeter” by Chen Liang (see https://www.instructables.com/ATtinyPowerMeter/), with several modifications to hardware and software. As Chen had developed some simple libraries, one bring for the SSD1306 OLED Display (and we had a SSD1306 in our parts box), we decided to use this display.

The Build:

Parts Required:JaycarAltronicsElement14
1 x ATtiny84A-PU Microcontroller--1972172
3 x INA219 Current Sensor--3118165
1 x NCP1117IST33T3G 3.3V Voltage Regulator--1652366
1 x Si4143DY-T1-GE3 P-MOSFET--3470713
3 x 2N7000 N-MOSFET*ZT2400Z15552775063
1 x MCP6002-E/P Dual Op-Amp--1332117
1 x 0.01Ω 1% SMD Current Sense Resistor*--2503044
1 x 1Ω 1% SMD Current Sense Resistor*--2447479
1 x 100Ω 1% SMD Current Sense Resistor*--2447454
2 x 1kΩ 1% Metal Oxide (MO) Resistor*RR0572R7558-
2 x 4.7kΩ 1% Metal Oxide (MO) Resistor*RR0588R7574-
3 x 10kΩ 1% Metal Oxide (MO) Resistor*RR0596R7582-
5 x 100kΩ 1% Metal Oxide (MO) Resistor*RR0620R7606-
4 x 100nF Ceramic Capacitor*RC5490R2931-
2 x 10µF Tantalum Capacitor*RZ6655R2638A-
2 x USB Type A Female Breakout-ebay-
1 x Red Banana SocketPS0406P9261-
1 x Black Banana SocketPS0408P9262-
1 x SSD1306 OLED Display-Z6525-
1 x VeroboardHP952H0712-
3 x SOP16(SOIC16) to 16-pin DIL Adapter board--2476039
1 x 2-pin, 1 x 3-pin, 2 x 4-pin Male Right Angle Headers^-P5440-
6x8-pin Male Headers*^HM3212P5430-
6x8-pin Female Headers*-P5375-
1 x 14-pin DIL Socket (for ATtiny84A-PU)-P0560-
28 AWG Stranded White PTFE Hook Up Wire--118398701

Parts Required:

The schematic diagram for the auto-ranging USB Current Meter is shown here in Figure 4. As with most of our prototypes, it has been built on Veroboard with the layout shown.

Figure 5A. Veroboard Layout A
Figure 5B. Veroboard Layout B

From the schematic diagram, you will notice that there are apparently three components missing, which are C6, R14 and R15. We initially added these components to the 400µA range as per the datasheet (see resources link at the end of the article), Section 8.4.1 for noise reduction. This gave very inaccurate results, which we believe is because the shunt resistor of 100Ω was much greater than the two filter resistors of 10Ω each, resulting in some sort of voltage divider effect. Removing the filter components improved the results greatly.

We have used female USB A connectors for both input and output. The original concept was to insert the Current Meter in between a USB power supply (Powerbank or charger) and measure the current, voltage and power. The USB D+ and D- connections pass straight through allowing the device connected to control, for example, a QC3.0 Powerbank, and set up the required charging voltage. You will also notice we added a differential amplifier and buffer, U1a and U1b respectively. This is for connecting to an oscilloscope to view the current waveform. The differential amplifier has a gain of 100.

All the ICs are powered from a 3.3V regulator, NCP1117. As we mentioned earlier, the input voltage could range up to 20V. As the NCP1117 takes its input from the USB Input side, the NCP117 must be able to take a 20V input. Most 3.3V regulators cannot take such a high voltage input – check your datasheets! Note, for the NCP1117, 20V is the absolute maximum, so it must not be exceeded, otherwise damage will occur to not only the NCP1117, but the rest of the integrated circuits. Also note, that by having the voltage regulator and circuitry for the Current Meter connected to the input, the current measured is only that of the load.

One final note on the NCP1117 is that it comes in a surface mount package. But, as it comes in a SOT-223 package, it can be soldered onto a small piece of veroboard and mounted onto the main veroboard using a 3-pin right angle header.

Make sure you cut the tracks on the veroboard, as shown, to ensure no shorts between the centre pin and the outer pins of the NCP1117. Note the top tab is connected to the centre pin. The NCP1117 is then soldered to the veroboard on the copper track side. The 3-pin right angle header is then soldered on being mounted from the non-track side.

The main veroboard is constructed by firstly cutting it to size. It is a minimum of 27 holes wide and 48 holes long. You may be able to fit everything on a smaller sized board by being a little more clever with the layout. Once we cut the veroboard to size, we cut all the required tracks as per Figure 5A.

We then soldered all the wires on as per Figure 5A.

Note: We use PTFE hook-up wire as the insulation has a high melting point and usually does not melt when soldering, unlike PVC insulated wire.

Install all the resistors, capacitors, N-MOSFET transistors, DIL socket for ATtiny84, male headers for INA219/SI4143DY modules, right-angle headers for SSD1306 and debugging port, the USB A breakouts and the NCP117 voltage regulator adapter.

Now, you may have noticed on Figure 5A a module containing both the INA219 and the SI4143DY. You may also remember we mentioned that the SI4143DY P-MOSFET comes in a SMD SO-8 package. The INA219 also comes in a SMD SO-8 (SOIC-8) package. So we have used a SOP-16 to 16-pin DIL Adapter.

We have soldered the INA219 at the top of the adapter, using the top eight positions and soldered the SI4143DY at the bottom eight positions. See the following photos. Note pin 1 (the one with the dot next to it), is located on the adapter at pin 1 for the INA219 and pin 5 for the SI4143DY. The SOP-16 to 16-pin DIL adapter that we have used has a spacing of 0.5” between the two DIL rows. Many other adapters use a spacing of 0.6”. For these adapters, see Figure 5B for the placement of components and wiring.

Note: The Veroboard in Figure 5B is the same size as in Figure 5A and the track cuts are also in the same places.

In both Figures 5A and 5B, you will see that the current shunt resistors R11 (0.01Ω), R12 (1Ω) and R13 (100 Ω) are SMD in a 1206 package (0.12in x 0.06in). These fit nicely on the copper side of the Veroboard, very close to the sense pins on the INA219 to ensure measurement errors are minimised.

The next two photographs show the component side and the solder side of the USB Current Meter.

A third photograph shows a close up of two of the Current Shunt resistors (R13 and R12). The OLED Display is connected via a cable to the four pin connector on the right of the component side photograph. The two pin connector below the OLED Display connector can be connected to an FTDI module for debugging purposes.

CALIBRATION

Current Shunt Calibration

As you will see from Datasheet (Section 8.5.1) of the INA219, there are some calibration calculations required to convert the Shunt Voltage (the voltage measured by the INA219 across the Shunt Resistor) to provide current and power readings directly from the INA219. These calculations are shown in the header file “USB_PM_ina219.h” of our program (see resources link at the end of this article).

Current Offset Calibration

This is not the only calibration required. We noticed under no load, that we were getting a current reading when the 400µA and 40mA ranges were selected. This was not too surprising as can be seen from Section 7.5 of the INA219 datasheet, there is a Voltage Offset of up to 100µV on the input measuring the Shunt Voltage. While this is only 0.25% potential error, I found on the 400µA range, we were reading 130µA under no load. As we were not entirely sure where this rather large current reading was coming from, we decided to compensate for this in software.

We wrote a simple calibration program for the ATtiny84, which displays the current and voltage measurements on the OLED display and also outputs the data to the Arduino IDE Serial Monitor via the Debug port through an FTDI Module. (See “INA219_Current_Calibration.ino”).

We adjusted the input voltage to the Current Meter from 4V to 20V. The results are shown in Figures 7 and 8.


As it can be seen, the current offset is voltage dependent. The impact is most noticeable on the 400µA range. The impact is small on the 40mA range and insignificant on the 4A range, which is why it is not shown. Looking at both Figures 7 and 8, you can see these are simple algebraic graphs of the type “y = mx + c”. Where m is the slope of the graph, x is the voltage, c is the intercept on the y-axis and y is the offset current.

Offset Values

m

c

40mAmp Range

0.0667

0.0108

400µAmp Range

9.58

80.21

From the graphs, we derived values for m and c for both the 40mA and 400µA ranges as shown in Table 3. The offset current is then subtracted from the measured current.

To confirm our calculations, we built a USB calibration module. As shown in Figure 9, the USB calibration module consists of a male Type A USB connector (to plug directly into the Current Meter output USB connector), six resistors (10Ω, 100Ω, 1kΩ, 10kΩ, 100kΩ and 1MΩ) all 0.25 Watt rating with the exception of the 10Ω, which is 5 Watt, and a DIP Switch to switch in or out the individual resistors.

With the USB calibration module plugged into the Current Meter, we first measured each resistor from ground (GND) on the Current Meter to the voltage output (VBUS). All the resistors we chose were 1% metal oxide (with the exception of the 10Ω 5 Watt 5% resistor), as we wanted to reduce errors as much as possible.

We then connected a power supply to the Current Meter input, adjusting it to give approximately 5VDC across the USB calibration module. We then calculated the current and compared it to that indicated on the Current Meter display. Table 4 shows the results.

While the results for the Current Meter look consistent, except on the 100kΩ range, the error % is a little higher than hoped. However, the Current Meter can still be used for relative measurements.

If you look at the photograph of our main build, you will see from the graph on the OLED Display, that the signal was a bit noisy, which could also contribute to reading errors. Even so, we are trying to measure a current consumption of around 5µA, if we are out by less than half a µA, we are not going to be too concerned.

USB Current Meter Schematic Diagram.

Range

Actual Ω

Volts

Calc I (mA)

Meter I (mA)

Error (%)

1MΩ

999k

5.02

0.00503

0.0052

3.4

100kΩ

99.6k

5.01

0.0503

0.0503

0

10kΩ

10.02k

5.02

0.501

0.529

5.6

1kΩ

993

5.02

5.055

5.29

4.6

100Ω

99.7

5.01

50.25

52.9

5.3

10Ω

9.9

5.01

506.06

526

3.9

Internal Oscillator Calibration

On the OLED display, we show the time, in seconds, for how long the measurements have been running. This is directly related to the power shown in mWh. We found over a long period of time that the measurement time was a little inaccurate. This is because the internal 8MHz oscillator of the ATtiny84 is factory calibrated. However, it is possible to improve the internal 8MHz oscillator calibration (See section 6.5.1 of the ATtiny84 datasheet).

We have written a program to help with this calibration, ”ATtiny84_OSC_Calibration.ino”. This program is loaded into the Current Meter ATtiny84. Using the Debug output via a FTDI module and the Serial Monitor running on the Arduino IDE with “Show timestamp” checked, you can determine what value for the ATtiny84 OSCCAL register gives the best timing accuracy.

SOFTWARE

The original software for the “ATtinyPowerMeter” was developed by Chen Liang. We have modified his software using the Arduino IDE for our development. The project consists of six files “INA219_USB_Current_Meter_V1_0.ino”, is the main module, which includes the “Setup()” and the “Loop()” functions. “USB_CM_ina219.cpp” and “USB_CM_ina219.h” contain all the functions and definitions for the INA219 current sensor. “USB_CM_SSD1306.cpp”, “USB_CM_SSD1306.h” and “fonts.h” contain all the functions and definitions for the SSD1306 OLED Display. Make sure you put all these files in a directory called “INA219_USB_Current_Meter_V1_0”. The Arduino IDE will compile all the files.

We used the ATtiny programmer as presented in the DIYODE December 2021 issue for the “Keeping Time – ATtiny General Purpose Timer” project, to program the ATtiny84. Just use Socket 1, the 14 pin socket.

#include <TinyWireM.h>.     // For I2C
#include "USB_CM_ina219.h"   // INA219 functions
#include "USB_CM_ssd1306.h"  // SSD1306 OLED 
                             // display functions

In the main module, “INA219_USB_Current_Meter_V1_0.ino”, I start with the Header File #includes:

You may have to use the Library Manager to download the “TinyWireM.h” library. This is a more efficient library than the standard I2C “Wire.h” library and it uses less memory, which is critical for the program as an ATtiny84 only has 8kB of Flash memory. We have also included some defines for debugging. Only if the line “//#define D_BUG” is uncommented, will any of the debugging lines of code get compiled. Be careful, the debugging code can use a lot of Flash memory.

Next, we instantiate the three current sensors.

//Declare Current Sensors
USB_CM_INA219 INA219_4A(INA219_I2C_ADDR_4A);
USB_CM_INA219 INA219_40mA(INA219_I2C_ADDR_40mA);
USB_CM_INA219 INA219_400uA(INA219_I2C_ADDR_400uA);

Then there are defines and declarations for the different variables.

Note: The define for the new OSCCAL register to change the internal calibration for the 8MHz oscillator.

// After Oscillator Calibration, the new OSCCAL 
// value is (the old was 0x7A)
#define OSCCAL_NEW 0x74

Next is the function called to display data on the OLED display, “void Display_OLED_Data ( void)”. This updates the data for output voltage, current, power and time. If debug is enabled, the data is also sent to the Serial Monitor via the Debug interface.

The next two functions are associated with interrupts for the two buttons I have on the veroboard module. One is the Reset Button (Right) and the other is the Hold Button (Left). The Reset Button sets the maximum current measured so far back to zero and the maximum for the current scale of the graph back to 0.001mA. It also resets the measurement time back to zero as well as the power consumption back to zero. The Hold Button on first press enables the Hold Function. Pressing the Hold button again disables the Hold Function.

// Function to enable pin change interrupts for the 
// Reset and Hold buttons
void Set_But_interrupts( bool But1, bool But2)
{
  // Enable Pin Change Interrupts in the General 
  // Interupt Mask Register (GIMSK)
  GIMSK |= _BV(PCIE0);   
  // If B1 true, enable interrupts for Button 1 
  // (PA1) in the Pin Change Mask Register (PCMSK0)
  if(But1) PCMSK0 |= bit(PCINT1);    
  // If B2 true, enable interrupts for Button 2 
  // (PA2) in the Pin Change Mask Register (PCMSK0)
  if(But2) PCMSK0 |= bit(PCINT2);    
}
// Interrupt Service Routine (ISR) for pin change 
// interrupts
ISR(PCINT0_vect) 
{
//  if(!digitalRead(R_BUT))
  if(!(PINA&bit(R_BUT)))
  {
    // Reset button pressed
    PCMSK0 &= ~bit(PCINT1);   
    // Turn off PA1 as interrupt pin
    R_But_PF = true;
  }
//  if(!digitalRead(H_BUT))
  if(!(PINA&bit(H_BUT)))
  {
    // Hold button pressed
    PCMSK0 &= ~bit(PCINT2); // Turn off PA2 as interrupt pin
    H_But_PF = true;
  }
}

The “Set_But_interrupts” function enables the pin change interrupts for either or both buttons. While this means the interrupt can occur for pressing and releasing the button, we only allow it for when the button is pressed. In the Interrupt Service Routine (ISR) for these interrupts “ISR(PCINT0_vect)”, we disable the associated button interrupt. The interrupts are re-enabled in the “Loop()” function only after the button has been released. This is basically a debounce function. Note also, we have addressed the interrupt registers directly (GIMSK and PCMSK0) as it saves memory over using Arduino C code. We have used similar code saving techniques in the “Setup()” function.

// Setup control pins for current range selects
//  pinMode(AMP_Pin, OUTPUT);     // 4.0 Amp Range
//  pinMode(mAMP_Pin, OUTPUT);    // 40 mAmp Range
//  pinMode(uAMP_Pin, OUTPUT);    // 400 uAmp Range
  DDRB |= bit(AMP_Pin)|bit(mAMP_Pin)|bit(uAMP_Pin);
  // Setup Reset and Hold buttons inputs
//  pinMode(R_BUT, INPUT_PULLUP);  // Reset Button
//  pinMode(H_BUT, INPUT_PULLUP);  // Hold Button
  DDRA &=~bit(R_BUT)&~bit(H_BUT);
  PORTA |= bit(R_BUT)|bit(H_BUT);
  // Turn off all Current measuring
//  digitalWrite(AMP_Pin,LOW);
//  digitalWrite(mAMP_Pin,LOW);
//  digitalWrite(uAMP_Pin,LOW);
PORTB &= ~bit(AMP_Pin)&~bit(mAMP_Pin)&~bit(uAMP_Pin);

Here, we have left the Arduino C Code commented out so that you can see what the direct register addressing we have used is doing. We did a compilation comparison between using the Arduino C code and the direct register addressing and found we save a couple of hundred bytes of flash memory.

In the “Loop()” function, which is executed continuously, we initially start with the 4A sensor selected (Sensor_Count = 1). We then make a call to the overflow function “INA219_4A.get_overflow()” to determine if the current being measured is greater than that allowed for the selected sensor. This is done for the 40mA and 400µA sensors also, when they are selected. This overflow check is performed with the selected INA219 ADC set to 9-bits.

This is because the conversion only takes 84µs as opposed to 532µs when the ADC is set to the maximum 12-bits. This allows us to switch up to a higher range sensor as quickly as possible to ensure minimum disruption to the connected test module. If the current is too high for a selected sensor, the output voltage to the test module is reduced and could cause the test module to mis-function. (See the following example for the 40mA sensor being selected).

case 2:
      // 40 mAmp Sensor active
      // Do an Overflow check first to make sure the
      // current will be in range
      if(INA219_40mA.get_overflow())
      {
        Sensor_Count = 1;
//        digitalWrite(AMP_Pin,HIGH);  
          // Enable Amp Sensor
        PORTB |= bit(AMP_Pin);
        delay(10); 
        // Leave mAmp Sensor enabled briefly
        // digitalWrite(mAMP_Pin,LOW);
        PORTB &= ~bit(mAMP_Pin);
        D_BUG_PRINTLN("40mA Out of range");
        break;
      }
      INA219_40mA.start_conversion();   
      // Start sensor readings
      Current_Measurement = INA219_40mA.read_current(CURRENT_LSB_40mA);
      Bus_Voltage_Measurement = INA219_40mA.read_bus_voltage();
      Current_Offset = INA219_40mA.current_offset(Bus_Voltage_Measurement, COFF_40mA, MOFF_40mA);
      // Adjust Current Measurement allowing 
      // for no load offset current
      Current_Measurement = fabs(Current_Measurement - Current_Offset);
      Power_Measurement = INA219_40mA.read_power(CURRENT_LSB_40mA);
      // Adjust Power Measurement allowing 
      // for no load offset current
      Power_Measurement = fabs(Power_Measurement - Bus_Voltage_Measurement * Current_Offset);
      curr_time = millis();
      // note to convert mS to S /1000
      mWHr_Total +=(Power_Measurement*((float)(curr_time-last_time)/1000.0))/3600.0;
      last_time = curr_time;
      #ifdef D_BUG
      Monitor_Print(INA219_I2C_ADDR_40mA);
      #endif
      // auto adjust graph ceiling
      if ((Current_Measurement) > Max_i) Max_i = Current_Measurement;
      if ((Current_Measurement) >= max_ma) // already mA
      {
        max_ma = Current_Measurement;
      }
      Display_OLED_Data();
      // plot graph
      oled.plot_graph(i, 2, 6, Current_Measurement, Last_Current, 0.0F, max_ma);
      Last_Current = Current_Measurement;
      // If current is less than 1% of maximum, 
      // switch to lower range
      if((Current_Measurement < (40.0 * 0.01))&& (!H_But_HF))
      {
        Sensor_Count = 3;
//        digitalWrite(uAMP_Pin,HIGH);   
          // Enable uAmp Sensor
        PORTB |= bit(uAMP_Pin);
        delay(10);
// Leave mAmp Sensor enabled briefly
//        digitalWrite(mAMP_Pin,LOW);
        PORTB &= ~bit(mAMP_Pin);
      }
      break;

You will also notice that if the current measurement is less than 1% of the range of the current sensor selected, a lower range, is selected. This is to ensure the maximum resolution of the current being measured. While this determination is being made, it is compared with the HOLD function being enabled. If it has been enabled, the lower range sensor is not selected. However, this does not apply in reverse. If the current measurement is above the range of the selected

current sensor range, the next range up is immediately selected for the next measurement.

CONCLUSION

While the Current Meter accuracy is not perfect, it is certainly a great tool to analyse your project’s current and power consumption. For us, we could see the difference in Deep Sleep consumption as opposed to active power consumption on one of our projects. The results allowed us to make a quite reasonable assessment of power consumption from the battery.

We have shown how using some simple direct register manipulations can save several hundred bytes of flash program storage, which could be essential where flash size is limited, as in the ATtiny84.

Overall, we believe you will find the design techniques we explained, useful to incorporate into many of your own projects.

Peter Stewart

Retired Engineer