Projects

Portable Power

A variable power supply unit using a power bank

Peter Stewart

Issue 75, October 2023

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

Log in

A Power Bank can be used as a portable Variable Output Voltage Power Supply Unit to power your projects or charge your batteries - by Peter Stewart

In Part 2 of the “DC Electronic Load” (DIYODE magazine Issue 71), I indicated that I used a Power Bank Spoofer to power the DC Electronic Load for several hours making it a very portable piece of test equipment. This involves putting a small electronic device (what I call a spoofer) between a QC 3.0 compliant Power Bank and the DC Electronic Load. The Spoofer basically sends signals over the USB D- and D+ connections to the Power Bank, which in turn, responds with the output voltage requested. The Spoofer makes the Power Bank behave as though a QC 3.0 compliant device is connected.

Figure 1 - QC 3.0 D+/D- Mode Settings

QC 3.0 (or Quick Charge Version 3.0) is a proprietary protocol developed by Qualcomm. My Spoofer is based on the work by “Vince” (see his article “Turning a Quick Charge 3.0 Charger into a variable voltage power supply” published on 9th August 2017). “Vince” (Vincent Deconinck) has also developed an Arduino Library “QC3Control”. I have used the most recent version, 1.4.1. The QC 3.0 protocol is explained fairly well in “Vince’s” article (especially the EOSMEM link). Figure 1 shows a diagram that indicates the voltage ranges required to be placed on the USB D+ and D- connection to a QC 3.0 compliant Power Bank to achieve the desired voltages. You will see that a QC 3.0 compliant Power Bank can provide output voltages of 5, 9 and 12 Volts. 20 Volts can be supplied by some QC 3.0 compliant Power Banks. These are Class B compliant. I have only used Class A compliant Power Banks and Chargers. As such, my Spoofer circuit is limited to a Maximum of 12 Volts output. But wait, you say, the subtitle of this article says “Variable Power Supply Unit”. Yes, the output is variable. I have made use of the “Continuous Mode”. Once the Power Bank is placed into Continuous Mode, you can adjust the output voltage in 0.2 Volt steps between around 3.6 Volts to 12 Volts. But, as “Vince” indicates in his article, he found some of the cheap QC 3.0 Power Banks would not go below 4 volts. My Power Banks do not go below 4.6 Volts (my Power Banks must be very cheap!).

From Figure 1, you will also see some dotted lines at 0.6 Volts and 3.3 Volts on both the D+ and D- axes. These are the voltages “Vince” found worked best for configuring the desired voltage output from the Power Banks. I found these voltages worked quite well for me also. So, how do you set up the output to just 5 Volts. This is normally the default output from a Power Bank. However, using the QC 3.0 protocol, you first have to wakeup the Power Bank. This is handled by the “begin()” function in the “QC3Control” library. It involves placing 0.6 Volts on both the D+ and D- signals, waiting 1.5 seconds then putting 0 Volts on the D- signal. You can then configure all the output voltages you desire.

“Vince” initially created his design using 5V Arduino Nano type microcontrollers. The Arduino Nano, while smaller than an Arduino Uno, is still a relatively large device and I wanted something that would fit into a Jiffy UB5 case. I also wanted my microcontroller to operate from 3.3 Volts. This would give me many options as far as selecting a microcontroller was concerned. “Vince” in his article, initially, had a resistor network which, combined with input from an Arduino Nano, created the voltages required to meet the QC 3.0 protocol.

After a little research, I came up with the resistor network as shown in Figure 2. This is a very simple resistor network and relatively easy to understand.

Let’s examine the D- input to the USB Power Bank. When the pin connected on a microcontroller operating at 3.3 Volts is first set as an input, the voltage at the D- pin is determined by just the 10k/2.2k resistive divider. If you have 3.3 Volts on the 10kΩ resistor, you will measure 3.3 x 2.2k /(2.2k +10k) volts or 0.595 Volts (let’s say 0.6 Volts). This gives us one of the voltages required as determined by “Vince”. Now, if you change the microcontroller pin to an output, it is the output voltage that determines what is placed on the D- input to the Power Bank. For a 3.3 Volt microcontroller, with the output “HIGH”, that is close to 3.3 Volts. With the output “LOW”, that gives close to 0 Volts. What applies on the D- pin also applies on the D+ pin. We can now easily control the QC 3.0 compliant Power Bank.

Figure 2 - Microcontroller Connections

THE DESIGN

I have made many variants of my Power Bank Spoofer (PBS). Originally, all I wanted was something to allow me to charge an 8.4 Volt Battery (2 x Li-ion batteries in series). This I did using the resistor network shown in Figure 2 and an ATtiny85 microcontroller. While simple, this did not have any feedback to make sure the output voltage was correct, nor did I know how much current was flowing, nor did I have any visual indication on what was going on and finally, I could not adjust the output voltage for other devices I would like to power.

I decided I needed a small I2C Display, for example, the SSD1306 126x84 dot OLED display. These displays are cheap to purchase and readily available. An ATtiny85 could still interface to the SSD1306 and the D+/D-. I now have one I/O pin available. I needed to measure the output voltage to provide some feedback to the Power Bank to ensure enough voltage was being provided.

By using a voltage divider and an Analog Input on the ATtiny85, I could measure the voltage to the output device. The voltage divider is required to make sure that the input voltage to the ATtiny85 does not exceed the supply voltage of 3.3 Volts. Noting the output voltage of the Power Bank could be up to 12 Volts.

Note on Charging Li-ion Batteries

When charging Li-Ion batteries, you initially use a constant current mode, which should not exceed 1C charge rate (or for a 3000 mAh battery, 3000 mA). There is some debate over the actual charge rate. Some recommend up to 3C as a charge rate. However, using a lower charge rate will extend the life of the battery.

The downside is that it takes longer to charge the battery. For my 3000 mAhr batteries, I used a charge rate of 500 mA. Then, when the Li-Ion battery voltage reaches 4.2 Volts (1 cell), charging is then completed using a constant voltage mode, i.e. maintain the voltage at 4.2 Volts (per cell) with the current decreasing. It is advised to terminate charging when the charge current reaches, for a 3000 mAhr battery, approximately 10mA. I have not included this feature in any of my PBS’s and, to date, have not had any issues with my batteries. Although, once the current does reach a low value, I manually disconnect the PBS.

So, I need to measure current. I now have run out of pins on an ATtiny85. So I moved up to an ATtiny84, which has 11 I/O pins. To measure the current, I initially selected the MAX471 module. This particular module could measure up to 3 Amps and provided an analogue output. I could use the 10-bit ADC function of the ATtiny84 to determine the actual current being drawn from the Power Bank. I also needed some way of setting the voltage output for other types of battery or something to act as a Power Supply for any of my projects. I decided to use an EC11 Rotary Encoder. This required a further three I/O pins. The ATtiny84 had more than enough pins. Also, one of the reasons for originally selecting the ATtiny85 was that I could program it via the programmer I presented in my article “Keeping Time” published in December 2021 issue of DIYODE Magazine. It just so happens, the same programmer would also program the ATtiny84.

Now, it was a couple of years ago since I built and programmed my PBS based on the ATtiny84. As I was thinking of making some changes to my original program, I thought I had better re-compile my original program first, as there had been some updates to the Arduino Libraries and various Boards/Processors. Sure enough, my original program no longer compiled. The 8k Flash of the ATtiny84 was no longer large enough for the compiled program. I guess there has been some bloating in some of the Libraries. I thought about modifying the Libraries to make them smaller and more efficient. But this would only be useful to myself and not portable for other users.

Now I have recently been playing with a new processor, the ATtiny3224. It is an 8-bit processor with 11 usable I/O pins and would run reliably at 10 MHz when powered from 3.3 Volts. Most importantly, it has 32k of Flash memory. So I could use the standard Arduino Libraries, as program memory size was no longer a restriction.

I could also design with the ATtiny3224 to fit into a standard UB5 Jiffy case. For the Current Sensor, I again used the MAX471. However, as for my ATtiny84 design, I had to use a voltage divider to measure the voltage output to the connected device. This was not really a problem, as the ATtiny3224 has a 12-bit ADC available on any of the 11 usable I/O pins. The design basically worked with some modifications to the original ATtiny84 program. However, I thought, not everyone might want to use the ATtiny3224. The ATtiny3224 comes in a SOP14 package (read surface mount device - SMD). SOP14 to DIP adapters are readily available, but you still have to solder the SMD to the adapter and maybe, not everyone is happy to do that. Also, a new programmer is required to program the ATtiny3224.

I thought, there must be another way. Now, I recently developed a design for the “DC Electronic Load Part 3” (See DIYODE Magazine September 2023) of an I2C Rotary Encoder. This I2C Rotary Encoder was based on an ATtiny85. Would it be possible to use this ATtiny I2C Rotary Encoder with the I2C SSD1306 OLED Display, all being controlled by another ATtiny85. But what about measuring the output current and voltage to the attached device. Why not also use the I2C INA219 Current Sensor! I used this in the “DC_Electronic Load” and also in my “Auto-Ranging USB Current Meter (DIYODE Magazine November 2022).

This, I thought, was a brilliant idea. Using the ATtiny85 Processor for the Main Processor (easy to program and readily available), the INA219 Current Sensor to measure both current and voltage, the I2C ATtiny Rotary Encoder for input and the I2C SSD1306 display for output seemed like a good combination. I did build this, but encountered several problems. Firstly, the hardware would not fit into a standard UB5 Jiffy case. So I designed and printed a new case on my 3D Printer.

Then of course the software when compiled required more than 8k Flash memory. I then resigned myself to the fact that I would have to use custom libraries. So I used the SSD1306 and INA219 Libraries I developed for my “Auto-Ranging USB Current Meter”. This then compiled and sort of worked. I found that the Rotary Encoder function did not work reliably. This was strange, I thought, as it works well with my “DC Electronic Load”. The only thing I could put it down to was that the ATtiny85 clock speed was 8MHz and the ESP8266 in the “DC Electronic Load” was 80MHz. I might need a faster processor. Back to the drawing board!

I decided to revisit the ATtiny3224 design. It had enough pins to handle the Rotary Encoder directly. This time though, I decided to use the INA219 Current Sensor.

The main reason for this was that the design using the MAX471 Current Sensor, mentioned previously, was a very tight fit into the UB5 Jiffy case. If I used one of the spare INA219 chips, I had from my “Auto-Ranging USB Current Meter” project, soldered onto a SOP8 to DIP adapter, this would be smaller and a better fit into a UB5 Jiffy case. Another reason for using the ATtiny3224, even though it requires a different programmer, was that it could be programmed easily whilst in-circuit.

I have also included my design for the ATtiny3224 programmer at the end of this article. Note this is a High Voltage (HV) UPDI Programmer, which can program any of the Microchip (ATtiny) Series 0, 1 and 2 processors, that support UPDI. Also being a High Voltage programmer, it can be used to unbrick processor chips (more on this later).

The ATtiny3224 design is now the one I have settled on, but I did not give up on the ATtiny85 design. I found some improvements on my original code for the ATtiny85 I2C Rotary Encoder and also in the code for the main ATtiny85 processor. For those who really want to use the ATtiny85, I will include the Schematics, Veroboard layouts and code,

which can be downloaded from the associated resources for this project from the DIYODE Website.

The complete Schematic for the ATtiny3224 based Power Bank Spoofer is shown in Figure 3. You can see the Resistor Network as per Figure 2 in R3, R4, R5 and R6. U1 is the ATtiny3224 Processor. U2 is the I2C INA219 Current Sensor and the OLED Display Module is the SSD1306 I2C Display. I have used an EC11 Rotary Encoder, but directly coupled to the ATtiny3224.

This was done to save some space over using the ATtiny85 based I2C Rotary Encoder. You will also notice I have not included physical pullup resistors for the Rotary Encoder. This is because I have used the internal resistor pullup capability of the ATtiny3224. R1 and R2 are the required pullup resistors for the I2C Bus.

Now the input voltage to the PBS from the QC3.0 (Class A!) power bank can vary from 4 Volts to 12 Volts. So I needed a Low Drop Out (LDO) Voltage Regulator to provide the 3.3 Volts for the ATtiny3224, the USB Resistor Network, the SSD1306 and the INA219 Current Sensor.

I chose the HT7333A as it has an input voltage range of 3.4 to 12 Volts for a 3.3 Volt output with a current capability of 250 mAmps. I have quite a few HT7333A Regulators in my Parts Box, but I understand that these are now not easily obtained. An equivalent Regulator is the MCP1702-3302E/TO, which is readily available from Element14.

Now, just to protect the whole circuit, I also included a 12 Volt Transient Voltage Suppressor (TVS), D1. This ensures that no voltages above 12 Volts can be input to the PBS. The TVS (P6KE12A) will clamp the voltage to around 12 Volts ensuring no damage will occur to the PBS nor to the device attached to the PBS, due to overvoltage.

There are four connectors associated with the PBS, the two obvious ones – the USB Input connector and the 5.5 x 2.1 mm DC Output connector. The other two connectors, which is why the ATtiny3224 has become one of my favourite 8-Bit Processors, is the connector for a Unified Program and Debug Interface (UPDI) Programmer and the Debug connector.

One of the great features of the ATtiny3224 Processor is the ability of programming it in circuit via a single pin. Another great feature of the ATtiny3224 is the Serial Port capability so that you can use “Serial.print()” statements natively in your code for debugging, as this is already built in the Arduino IDE.

Diode D2, a 1N5817 Schottky Diode, which you may deem not to be necessary, is provided to prevent a reverse current back into the Power Bank. A Schottky type Diode is used to minimise the voltage drop between the Power Bank and the connected load on the output.

Finally, there is R7, R8, R9 and Q1. Basically, the ATtiny3224 sends a pulse signal to Q1 via the base resistor R9. This causes Q1 to conduct current through R7 and R8 from the Power Bank for the duration of the pulse. Why would you want to do this?

Some QC 3.0 Power Banks will automatically turn off if the current being drawn is less than a certain value over a certain amount of time. As the ATtiny3224 is monitoring the current drawn via the INA219 Current Sensor, it knows when the current has fallen below a defined amount (defined in the ATtiny3224 program) and sends a pulse to Q1 to draw current through R7 and R8 (the pulse width is also defined in the ATtiny3224 program).

When I determined the amount of current required and calculated the resistance and power dissipation, I found I needed a 50Ω 1 Watt resistor or two 100Ω 0.5 Watt resistors in parallel. This works well for both of my Power Banks. You may need to tune the minimum current and pulse width in the ATtiny3224 program for you own Power Banks.

The Build:

Parts RequiredJaycarAltronicsPakronics
1 x ATtiny3224 Microcontroller--Element 14: 3872793
1 x INA219 I2C Current Sensor--Element 14: 3118165
1 x SSD1306 0.96 in OLED Display (Yellow/Blue)(1)ebay--
1 x HT7333A LDO Voltage Regulatorebay--
1 x MCP1702-3302E/TO (alternative to HT7333A)--Element 14: 1331485
1 x 2N4401 NPN Transistor*--Element 14: 1574373
1 x P6KE12A Transient Voltage Suppressor--Element 14: 3788300
1 x 1N5817 Schottky Diode*ZR1020Z0040-
1 x EC11 Rotary EncoderSR1230--
1 x 0.01Ω 0.25 Watt 1% SMD 1206 Current Sense Resistor--Element 14: 4070202
2 x 100Ω 0.5 Watt Resistor*RR0548R7734-
1 x 1kΩ 0.25 Watt Resistor*RR0572R7046-
2 x 2.2kΩ 0.25 Watt 1% Metal Oxide Resistor*RR0580R7566-
4 x 10kΩ 0.25 Watt 1% Metal Oxide Resistor*RR0596R7582-
2 x 100nF Ceramic Capacitor*RC5490R2930A-
2 x 10µF Tantalum Capacitor*RZ6648R2638A-
1 x Knob (For EC11 Rotary Encoder)(HK7734 2)H6109-
1 x VeroboardHP9542H0712-
1 x SOP8 to DIL Adapter for INA219--Element 14: FIT0290
1 x SOP14 to DIL Adapter for ATtiny3224--Element 14: 2476036
1 x 2-Pin Male Header*(HM34223)(P55123)-
1 x 3-Pin Male Header*(HM34243)(P55143)-
2 x 4-Pin Male Header*(HM32123)(P54403)-
2 x 7-Pin Male Header*(HM32123)(P54403)-
2 x 7-Pin Female Header Socket*-(P53754)-
1 x USB 2.0 Female Breakoutebay--
1 x 5.5x2.1 mm DC SocketPS0522P0622-
1 x UB5 Jiffy CaseHB6015H0205-
2 x M2x12mm Bolts--Element 14: 1420387
2 x M2 Nuts--Element 14: 1419445
2 x M2 Nylon Washers--Element 14: 3804764
26 AWG Stranded White Hook Up WireWH3007W2257Element 14: 118398701

Parts Required

* Quantity shown, may be sold in packs. You’ll also need a breadboard and prototyping hardware.
Notes:
1. With the SSD1306 OLED Display, make sure pin connections are in the order GND,VCC, SCL, SDA. Some displays have GND and VCC swapped.
2. Other colours available.
3. Can also be made from HM3212/P5440.
4. Cut off last pin

The Power Bank Spoofer Veroboard layout is shown in Figure 4A if you use a 0.5in pitch SOP14 (SOIC14) to DIL Adapter for the ATtiny3224. If you use a 0.6 in pitch SOP14 (SOIC14) to DIL Adapter, the Veroboard Layout is as per Figure 4B. The Parts List is found at Table 1.

Cut the Veroboard to size 35 mm (14 holes) x 64 mm (25 holes). Cut the tracks indicated by the “X” using a “spot face cutter” or 3/8” drill bit. You will also notice the small cutouts in the right corners of the veroboard. This is to clear the screw mounts inside the UB5 Jiffy case. Have a close look at the photos. Solder the hook up wires as shown. Note the black lines are wires on top of the veroboard and the red dashed lines are wires soldered on the copper side of the veroboard. Because of the density of this design, wires have to be soldered on both sides of the veroboard. Note also, R10 (the INA219 Current Sense Resistor) is soldered to the copper side of the veroboard (see the resistor circled on adjacent photo).

I soldered the INA219 to an SOP8 to DIL Adapter. I then soldered the adapter direct to the veroboard. I did this to reduce the resistance to the current sense resistor (R10). You could solder in 4-pin female headers and then plug in the INA219 Adapter, to make it easy to remove and really, the current measurement accuracy is not crucial. Only an approximation, say ±10 %, is all that is necessary. I did mount the ATtiny3224 on a DIL Adapter and used 7-pin female headers into which the ATtiny3224 Adapter was plugged. Those with keen eyes may have noticed that I actually used a SOP16 Adapter for the ATtiny3224 and I cut off the end two pins. I did this mainly because I did not, at the time, have any SOP14 Adapters. I also used a 4-pin female header for the SSD1306 OLED Display.

The Rotary Encoder was also a little tricky. I needed to raise the height a little as I wanted to use the supplied nut to mount the veroboard to the lid of the UB5 Jiffy case. I used a couple of small pieces of veroboard and a couple of male header pins in a type of sandwich and soldered the EC11 Rotary Encoder to the two sandwiches, then soldered the whole assembly to the PBS Veroboard. The above photo shows how I did this. This allowed the top of the rotary encoder to align with the ATtiny3224 and the SSD1306 OLED Display.

Case Installation

Figure 5 - Power Bank Spoofer UB5 Cutout Template

To install the PBS into the UB5 Jiffy Case, I carefully made a Template which I taped onto the lid of the Jiffy Case (See Figure 5). This allowed me to get the holes for the SSD1306 OLED display and the EC11 Rotary Encoder in the right place. You will notice in a previous photo that I used only two screws to mount the OLED Display to the Case lid. Those two screws (M2 x 12mm), with nuts, nylon washers and the nut for the EC11 Rotary Encoder make for a secure mounting for the PBS.

You will need some spacers between the OLED Display and the Case lid. The same applies to the EC11 Rotary Encoder. The spacers need to be about 2mm thick. Or you could do what I did, as I mangled my UB5 Case lid when cutting the hole for the OLED Display (see photos of some of my previous version efforts), and 3D print one (the STL file will be included in the project resources). I drilled a 7mm hole near the bottom of the UB5 case at one end, for the DC Socket. It needs to be as close to the bottom of the case as possible, but allow some clearance as the DC Socket diameter is greater than the threaded part.

At the other end of the case, cut a rectangular hole for the USB female connector. Make sure the wires connecting the DC Socket to the verobard are long enough so that you can jiggle the veroboard, when bolted to the lid, into place. I put a little PVC tape underneath the veroboard to insulate it from the DC Socket, just in case!

THE SOFTWARE

The software consists of seven files:

  1. PB_Spoof_ATtiny3224_INA219_V4.ino (the main module);
  2. PBS_ina219.h (Header file for INA219 Current Sensor);
  3. PBS_ina219.cpp (File with functions for INA219 Current Sensor);
  4. PBS_ssd1306.h (Header file for SSD1306 OLED Display);
  5. PBS_ssd1306.cpp (File with functions for SSD1306 OLED Display);
  6. font.h (Header file containing data for normal sized OLED Display font);
  7. font_2x.h (Header file containing data for double sized OLED Display font).

Setting up the Arduino IDE

All seven files have to be contained in the same folder “PB_Spoof_ATtiny3224_INA219_V4” for the Arduino IDE to compile. Before you can compile a program for an ATtiny3224, you need to configure the Arduino IDE. Note, the following is for the Microsoft Windows environment.

Firstly open the Arduino IDE. Select File/Preferences and add http://drazzy.com/package_drazzy.com_index.json to the “Additional Boards Manager URLs”.

Second open the Boards Manager at ‘Tools/Board: “….”/Board Manager’. Then install “megaTinyCore” by Spence Konde. Also check out the link “https://github.com/SpenceKonde/megaTinyCore”, as there is a lot of useful information.

Once installed, select the settings shown in the following under “Tools”. Note the clock speed is set to 10 MHz. Any higher clock rate may not be reliable, when powering the ATtiny3224 from 3.3 Volts. Don’t forget to “Burn Bootloader” before uploading the program to ensure the ATtiny3224 fuses are set correctly. To program the ATtiny3224, see the section on the “HV UPDI Programmer” near the end of this article.

Program Description

As usual for Arduino programs, the Libraries used are declared at the beginning of the program. The Library has to be downloaded and installed. This can be done in the usual way by using the Arduino IDE Library Manager. This library is used to control the communications between the PBS and the QC 3.0 compliant Powerbank (or a QC 3.0 compliant USB Charger adapter). The Library is a built in library installed when the “megaATtinyCore” is installed via the Board Manager. The “PBS_ssd1306.h” and “PBS_ina219.h” header files are my custom header files for the SSD1306 OLED Display and INA219 Current Sensor respectively and are contained within the same directory as the main program.

#include <QC3Control.h>
#include <Wire.h>
#include "PBS_ssd1306.h"  // SSD1306 functions
#include "PBS_ina219.h"   // INA219 functions

This is followed by all the #defines, constructors and variables used (See the code in the resources).

Next is the Interrupt Service Routine (ISR), which processes Pin Change Interrupts for the Rotary Encoder, when it is rotated or the pushbutton is pressed. This ISR is unusual in that a lot is done within the ISR. Normally, the advice is to do the absolute minimum in an ISR. However, I found it more reliable to do it the way I have. Doing incrementing/decrementing in the main “Loop()” function resulted values not being increment/decremented correctly due to interrupts occurring during the calculations. Doing everything within the ISR and disabling/enabling interrupts also, made everything more reliable. You will note I have also used some direct port addressing when handling interrupts. I did this as I found using the normal Arduino functions inefficient and not always reliable.

// Interrupt routine sets a flag when rotation is detected and adjusts
// Current or Voltage Set values dependent on which Menu is selected
ISR (PORTA_PORT_vect)
{
  // interrupt caused by Rotary Encoder 
  uint8_t flags = VPORTA.INTFLAGS;  // Read Interrupt Flags Register
  // Disable interrupts to prevent further interrupts during this ISR
  PORTA.PIN1CTRL = PORT_PULLUPEN_bm | PORT_ISC_INTDISABLE_gc; // Encode A Pin
  PORTA.PIN3CTRL = PORT_PULLUPEN_bm | PORT_ISC_INTDISABLE_gc; // Encoder Pushbutton
  if(!Enc_A_Flag)
  {
    uint8_t a= digitalRead(PIN_ENC_A);
    uint8_t b= digitalRead(PIN_ENC_B);
    if(Menu_Flag)
    {
      // In Menu select mode
      if(!a && b)
      {
        // Clockwise Rotation
        Menu_Count++;
        if(Menu_Count >= MENU_MAX) Menu_Count = MENU_MAX -1;
        Enc_A_Flag = true;
      }
      if(!a && !b)
      {
        // Counter Clockwise Rotation
        Menu_Count--;
        if(Menu_Count < 0) Menu_Count = 0;
        Enc_A_Flag = true;
      }
    }
    else
    {
      // Not in Menu Select Mode
      if(!a && b)
      {
        // Clockwise Rotation
        if((Menu_Count == LI_ION_MENU) || (Menu_Count == PSU_MENU))        {
          Set_V +=V_CHNG; // increment voltage
          if(Set_V > SV_MAX) Set_V = SV_MAX;
        }
        if(Menu_Count == NICD_MH_MENU)
        {
          Set_I +=I_CHNG; //increment current
          if(Set_I > SI_MAX) Set_I = SI_MAX;
        }
        Enc_A_Flag = true;
      }
      if(!a && !b)
      {
        // Counter Clockwise Rotation
        if((Menu_Count == LI_ION_MENU) || (Menu_Count == PSU_MENU))
        {
          Set_V -=V_CHNG; // increment voltage
          if(Set_V < SV_MIN) Set_V = SV_MIN;
        }
        if(Menu_Count == NICD_MH_MENU)
        {
          Set_I -=I_CHNG; //increment current
          if(Set_I < SI_MIN) Set_I = SI_MIN;
        }
        Enc_A_Flag = true;
      }
    }
    if(Enc_A_Flag)
    {
      // If a rotation was detected, 
      // set the debounce timer
      Debounce_Timer = millis();
    }
    else
    {
      // Interrupts not valid or not caused 
      // by rotating Rotary Encoder
      // so re-enable interrupt
      // Set PA1 (PIN_ENC_A) to have interrupts 
      // enabled on both edges
      PORTA.PIN1CTRL = PORT_PULLUPEN_bm | PORT_ISC_BOTHEDGES_gc;
    }
  }
  if(!Enc_PB_Flag)
  {
    if(!digitalRead(PIN_ENC_SW))
    {
      // Pushbutton pressed
      Enc_PB_Flag = true;
      // Disable interrupt
      // Set PA3 (PIN_ENC_SW) to have 
      // interrupts disabled
      PORTA.PIN3CTRL = PORT_PULLUPEN_bm | PORT_ISC_INTDISABLE_gc;
      Debounce_Timer = millis();
    }
    else
    {
      // Interrupt not caused by Pushbutton 
      // so re-enable interrupt
      // Set PA3 (PIN_ENC_SW) to have 
      // interrupts enabled
      PORTA.PIN3CTRL = PORT_PULLUPEN_bm | PORT_ISC_FALLING_gc;
    }
  }
  VPORTA.INTFLAGS = flags; // Clear interrupts
}

Next is the function for displaying the initial titles on the OLED Display, “ Disp_Titles()”. This is only called during the “Setup()” function and is only correct if the “Charge Li-Ion” Menu is initially selected, which it is during “Setup()”. If you want a different Menu at startup, you will need to modify this function.

The next function is the “Display_New_Menu()” function. This is called from the “Loop()” function after the Rotary Encoder pushbutton has been pressed to get into Menu Select Mode. Once in Menu Select Mode, you rotate the Rotary Encoder knob to select one of “Charge Li-Ion”, “Charge NiCd/NiMH” or “PSU Mode”.

I use “Charge Li-Ion” mode to charge my 8.4 Volt Li-Ion battery (2x 18650 in series). If you want to charge a single Li-Ion battery, change the voltage after exiting Select Menu Mode (by pressing the Rotary Encoder pushbutton again) and before connecting the PBS to the battery. I often use the TP4056 module when powering a circuit from a single Li-Ion battery. In this case, I set the voltage to 5.0 Volts and let the TP4056 sort out the correct voltage for charging the battery.

The PBS is set to allow a maximum current of 500mA. To ensure the maximum current is not exceeded, the PBS will automatically adjust the Power Bank output voltage down to try and not exceed the maximum current (a sort of constant current mode) see “CheckOutputVoltage()” function. It then automatically adjusts the Power Bank voltage back up to try and maintain the maximum current until the set voltage “Set_V” has been reached (a sort of constant voltage mode). The current then reduces and ultimately full charge is reached.

// Routine to Check Charging Voltage and adjust up 
// by 200mV if Less than Set Voltage or adjust down 
// by 200 mV if Greater than Set Voltage, however 
// charging current must not exceed 500mA
void CheckOutputVoltage (int M_C)
{
  switch (M_C)
  {
    case LI_ION_MENU:
      if( (BatteryVoltage > (Set_V +200)) || (ChargeCurrent > 500)) quickCharge.decrementVoltage();
      if( (BatteryVoltage < (Set_V -200)) && (ChargeCurrent < 500)) quickCharge.incrementVoltage();
      break;
    case PSU_MENU:
      if( BatteryVoltage > (Set_V +200)) quickCharge.decrementVoltage();
      if( BatteryVoltage < (Set_V -200)) quickCharge.incrementVoltage();
      break;
    default:
      break;
  }
}

I use the “Charge NiCd/NiMH” mode to charge the 7.2 Volt battery in my Radio Controlled (RC) car. Here the PBS will attempt to charge the NiCD or NiMH battery at 500 mAmps, which you can vary between 200 to 1000 mAmps (“Set_I”). The PBS will try and maintain the Set Current while allowing the output voltage to go up to 9.4 Volts maximum (see “CheckOutputCurrent()” function).

// Routine to Check Charging Voltage and adjust up 
// by 200mV if less than Max allowed voltage
// and charge current is less than Set current 
// minus the current tolerance or
// adjust down by 200 mV if Greater than Min 
// allowed voltage and charge current is greater 
// that Set current plus the current tolerance
void CheckOutputCurrent (void)
{
  if((BatteryVoltage > V_NICD_MH_MIN) && (ChargeCurrent > (Set_I + I_CHRG_TOL))) quickCharge.decrementVoltage();
  if((BatteryVoltage < V_NICD_MH_MAX) && (ChargeCurrent < (Set_I - I_CHRG_TOL))) quickCharge.incrementVoltage();
}

The “PSU Menu” mode is used to vary the output voltage between 4.0 and 12.0 Volts. 12.0 Volts cannot actually be reached due to the Schottky Diode (D2) between the Power Bank and the INA219 Current Sensor. The output voltage will be dependent on the current flowing through D2, but around 11.4 volts should be achievable. As the PBS can measure the output voltage via the INA219 Current Sensor, it will try and maintain the Set Voltage (“Set_V”) regardless of the output current, within the limits of the Power Bank (again see “CheckOutputVoltage()” function).

The “Disp_Upd()” function is called every 200 msec (“Disp_Upd_TO”) from the “Loop()” function. Depending on the Menu Mode, the display is updated accordingly.

// Routine to Display the Measured Output Voltage, 
// Measured Charging Current and the Set Voltage
// on the SSD1306 OLED display
void Disp_Upd (int M_C)
{
  // Print Output Voltage on OLED Display
  oled.setCursor(88,0);
  if(BatteryVoltage < 10000) oled.print(' ');  
  // Add space for voltage below 10.0 Volts
  // Printing floats around 10.0 can cause errors, 
  // so only deal with integers
  oled.print(BatteryVoltage/1000);
  oled.print('.');
  oled.print((BatteryVoltage%1000)/100);
  oled.print('V');
  // Print Charging current on OLED Display
  oled.setCursor(80,2);
  if(ChargeCurrent < 1000) oled.print(' ');
  if(ChargeCurrent < 100) oled.print(' ');
  if(ChargeCurrent < 10) oled.print(' ');
  oled.print(ChargeCurrent);
  oled.print("mA");
  // Print Set Voltage/Current on OLED Display
  oled.setCursor(80,4);
  switch (M_C)
  {
    case LI_ION_MENU:
    case PSU_MENU:
      // Display Set Voltage
      oled.print(' ');
      if(Set_V < 10000) oled.print(' ');
      oled.print(Set_V/1000);
      oled.print('.');
      oled.print((Set_V%1000)/100);
      oled.print('V');
      break;
    case NICD_MH_MENU:
      // Display Set Current
      if(Set_I < 1000) oled.print(' ');
      if(Set_I < 100) oled.print(' ');
      oled.print(Set_I);
      oled.print("mA");
      break;
    default:
      break;
  }
  // On bottom line indicate charging or 
  // charged if not changing menu
  oled.setCursor(0,6);
  // Set Display Update Timer
  Disp_Upd_Timer = millis(); 
  
  //Initialise Menu Counter to Li-Ion Menu
  Menu_Count = LI_ION_MENU;
  
  // Set Encoder and pushbutton interrupt flags 
  // and Menu Flag to off
  Enc_A_Flag = false;
  Enc_PB_Flag = false;
  Menu_Flag = false;
// We need to monitor only A (PA1) pin of Rotary 
// Encoder, rising and falling edges for all states 
// and just the falling edge for the pushbutton, 
// ie. when it is pressed, not released
// Set PA1 (PIN_ENC_A) to have interrupts enabled 
// on both edges
  PORTA.PIN1CTRL = PORT_PULLUPEN_bm | PORT_ISC_BOTHEDGES_gc;
// Set PA2 (PIN_ENC_B) to have interrupts disabled
  PORTA.PIN2CTRL = PORT_PULLUPEN_bm | PORT_ISC_INTDISABLE_gc;
// Set PA3 (PIN_ENC_SW) to have interrupts enabled 
// falling edge only
  PORTA.PIN3CTRL = PORT_PULLUPEN_bm | PORT_ISC_FALLING_gc;
  
}

As mentioned earlier, many Power Banks will shut down if the current reduces below a set level for a set period. The set level and set period can vary from Brand to Brand. The “Pulse_PB_Load()” function is called to place a sufficient temporary load on the Power Bank to reset the timeout period.

// This function briefly turns on the Powerbank 
// Load transistor to
// make the Powerbank think there is a load 
// (charging) connected so that
// it does not turn off - Some Powerbanks 
// automatically turn off if the
// load current is less than ~50 mAmps
void Pulse_PB_Load( void)
{
//  digitalWrite(PBLoadPin, HIGH);    
// Enable load
  VPORTA.OUT |= PIN5_bm;
  delay(PB_LOAD_PULSE);
//  digitalWrite(PBLoadPin, LOW);     
// Disable Load
  VPORTA.OUT &= ~PIN5_bm;
}

At power up of the PBS, the “Setup()” function is called. I have used direct port addressing as this uses less code space when setting up ports for input, output and setting up interrupts. I have left the normal equivalent Arduino function calls commented out. After inputs and outputs are set up and the Serial port initialised for debugging purposes, the communication with the QC 3.0 Power Bank is initialised, “quickCharge.begin()”. I then set the output voltage to 9 Volts before going into Continuous Mode and setting the voltage to 8.4 Volts (Charge Li-Ion battery, default mode). The reason for this, which may be just peculiar to my Power Banks, was that I could not go directly into Continuous Mode until a normal mode was set up. So I selected the mode closest to the output voltage I initially desired.

// Pullup resistors connected to encoder
//  pinMode(PIN_ENC_A, INPUT_PULLUP);
//  pinMode(PIN_ENC_B, INPUT_PULLUP);
//  pinMode(PIN_ENC_SW, INPUT_PULLUP);
// Set PA1, PA2, PA3 as Inputs 
// (Pullup resistors added later)
  VPORTA.DIR &= ~(PIN1_bm | PIN2_bm | PIN3_bm);
  Serial.begin(115200);   
// only really required for debugging
  delay(100);
  // Set the Load Driver to an Output to 
  // drive the Load Transistor
//  pinMode(PBLoadPin, OUTPUT);
  VPORTA.DIR |= PIN5_bm;
  // Pulse Powerbank to keep it active
    Pulse_PB_Load();
  
// Initialise QC Powerbank
  quickCharge.begin();
  delay(200);  // Time to settle
  quickCharge.set9V();  // Initially set to 9 volts
  delay(200);  // Time to Settle
  // Actually want 8.4 Volts
  quickCharge.setMilliVoltage(SV_START); 
  Set_V = SV_START;      // mV initial value
  Set_I = SI_START;      // mA initial value
  // Wire begin for OLED Display and INA219
  Wire.begin();
  // Start Current Sensors
  INA219_4A.begin();
  
  // Setup OLED Display
  oled.begin();
  oled.fill(0);    // Clear display
  // Put up Initial Screen on OLE Display
  Disp_Titles();
  // Set Display Update Timer
  Disp_Upd_Timer = millis(); 
  
  //Initialise Menu Counter to Li-Ion Menu
  Menu_Count = LI_ION_MENU;
  
  // Set Encoder and pushbutton interrupt flags 
  // and Menu Flag to off
  Enc_A_Flag = false;
  Enc_PB_Flag = false;
  Menu_Flag = false;
// We need to monitor only A (PA1) pin of Rotary 
// Encoder, rising and falling edges for all states 
// and just the falling edge for the pushbutton, 
// ie. when it is pressed, not released
// Set PA1 (PIN_ENC_A) to have interrupts enabled 
// on both edges
  PORTA.PIN1CTRL = 
PORT_PULLUPEN_bm | PORT_ISC_BOTHEDGES_gc;
// Set PA2 (PIN_ENC_B) to have interrupts disabled
  PORTA.PIN2CTRL = 
PORT_PULLUPEN_bm | PORT_ISC_INTDISABLE_gc;
// Set PA3 (PIN_ENC_SW) to have interrupts enabled 
// falling edge only
  PORTA.PIN3CTRL = 
PORT_PULLUPEN_bm | PORT_ISC_FALLING_gc;
  
}

After the Power Bank is set up, the I2C function “Wire.begin()” is started to allow both the INA219 Current Sensor and the SSD1306 OLED Display to be initialised. After clearing the display, the initial Titles are set up on the OLED Display. Various counters and flags are then initialised. The last part of the “Setup()” function is to enable interrupts and enable the internal pullup resistors for the Rotary Encoder. You will notice that only interrupts for the “A” pin and the push button are enabled.

The “Loop()” function, which runs continuously after “Setup()”, first looks to see if the Rotary Encoder has been rotated (“Enc_A_Flag” set in the ISR). If this has occurred and the debounce timer has expired then, if in Menu Mode, the New Menu is displayed.

Then the Encoder Flag (“Enc_A_Flag”) is reset and the associated interrupt, that was disabled in the ISR, is re-enabled. All this happens to try and avoid multiple actions due to Rotary Encoder switch bounce. Similar things happen if the Rotary Encoder push button has been pressed, with the exception that Menu Mode is enabled or disabled.

void loop() 
{
  // If Input from Rotary Encoder, 
  // update menu if changed
  if(Enc_A_Flag)
  {
    if(millis() > (Debounce_Timer+DEBOUNCE_TO))
    {
      if(Menu_Flag)
      {
        Disp_New_Menu(Menu_Count);
      }
      Enc_A_Flag = false;
      // Re-enable Interrupt
      // Set PA1 (PIN_ENC_A) to have interrupts 
      // enabled on both edges
      PORTA.PIN1CTRL = PORT_PULLUPEN_bm | PORT_ISC_BOTHEDGES_gc;
    }
  }
 // Check to see if the Rotary Encoder push button 
 // has been pressed
  if(Enc_PB_Flag)
  {
    if(millis() > (Debounce_Timer+DEBOUNCE_TO))
    {
      if(Menu_Flag)
      {
        Menu_Flag = false;
        // Menu change now disabled, 
        // so set output voltage
        quickCharge.setMilliVoltage(Set_V); 
      }
      else
      {
        Menu_Flag = true;
        Disp_New_Menu(Menu_Count);
      }
      Enc_PB_Flag = false;
      // Re-enable Interrupt
      // Set PA3 (PIN_ENC_SW) to have 
      // interrupts enabled
      PORTA.PIN3CTRL = PORT_PULLUPEN_bm | PORT_ISC_FALLING_gc;
    }
  }
  // Update display checking current drawn and 
  // set load control
   if(millis() > (Disp_Upd_Timer + Disp_Upd_TO))
  {
    ChargeCurrent = INA219_4A.read_current_mA(CURRENT_LSB_4A);
    BatteryVoltage = INA219_4A.read_bus_voltage_mV();
// If charging current less than the Powerbank 
// Minimum stay on current Pulse the Powerbank load 
// transistor to fool the Powerbank into staying on
    if( ChargeCurrent < PB_MIN_CURRENT)
    {
        Pulse_PB_Load();
    }
    // Only change Power Bank output voltage when 
    // not in Menu change Mode
    if(!Menu_Flag)
    {
       switch (Menu_Count)
       {
         case LI_ION_MENU:
         case PSU_MENU:
           CheckOutputVoltage(Menu_Count);  
// Check output voltage is same as Set Voltage
           break;
         case NICD_MH_MENU:
           CheckOutputCurrent();     
// Check output current is same as Set Current
           break;
         default:
           break;
       }
     }
     Disp_Upd(Menu_Count);       
     // Update display data
     Disp_Upd_Timer = millis();  
     // Reset Display update timer
  }
  delay(1);
}

Lastly, if there has been a Display Update Timeout (“Disp_Upd_Timer” expired), then the output current and output voltage are read from the INA219 Current Sensor. If the current has fallen below the minimum setting (“PB_MIN_CURRENT”), the Power Bank is load pulsed to ensure the Power Bank remains active. Then, depending on the Menu Mode, the Output Voltage or Output Current are checked and the Power Bank output voltage is adjusted accordingly. The OLED Display is then updated with the Display Update Timer being re-initialised.

One last thing to mention is to do with the font size of the display text and numbers. In the Header file “PBS_ssd1306.h”, you will notice a #define statement “#define FONT_2X”. If this is commented out, the font is standard (small). If this #define is not commented out, the larger font is used – your choice.

CONCLUSION

The Power Bank Spoofer (PBS) has been a very useful tool for me. I can charge up the Li-Ion batteries (one cell and two cell batteries) that I use in many of my projects. A good example is my recent article on the “DC Electronic Load”. By plugging in a Power Bank with the PBS, my DC Electronic Load becomes very portable with many hours of operation.

Similarly, if I take my RC Car out and about, I can recharge its battery many times over. Okay, it does take a little time and I could have a second battery! However, I have modified my RC Car to allow me to plug the PBS in directly without having to remove the battery and I can have a cup of tea/coffee/something while I wait. A bit like charging up your Electric Vehicle (EV). Only it doesn’t take so long!

Also, for those who want to operate their projects, even temporarily, at voltages between 4 and 12 Volts, you can use “PSU Mode” and you do not have to buy an expensive Laboratory Power Supply Unit.

Now for those eager to program their ATtiny3224 or other Series 0/1/2 UPDI Microchip compatible microprocessors, the following is my take on a High Voltage (HV) Unified Program and Debug Interface (UPDI) Programmer.

My HV UPDI Programmer

I have looked at many High Voltage (HV) Unified Program and Debug Interface (UPDI) Programmers. Basically, there are two types of HV UPDI Programmers. There is the Serial Type, which basically uses an USB interface chip (eg. CH340) and some switching components, or the Processor Type, which uses a processor of some type and some switching components.

I tried the Serial Type as it seemed the most simple. However, I could not get it to work. I am not exactly sure why, as there are many on the market. If you do not want to use my design, you could always purchase one of the many Serial Type UPDI programmers. However, it is more fun to make your own – maybe?

Well, I opted for the Processor Type, based on the Arduino Nano using the “jtag2updi” Arduino program. But why did I opt to build a HV Programmer? Well not only can you program, for example, an ATtiny3224 through the UPDI designated pin, but you can use the same pin as a “Reset” input or a “General Purpose Input/Output” (GPIO) pin.

However, to reprogram the chip, once you have set the UPDI pin to a Reset or GPIO pin, you need a HV Programmer, which will apply a high voltage (12 Volts) to set the pin back to a UPDI pin, so that reprogramming can occur. It may also help, if during the programming process, you manage to brick your chip. The HV UPDI Programmer may help to unbrick your chip.

THE BUILD

Parts Required:JaycarAltronics
1 x Arduino Comptible NanoXC4414Z6372
2 x EL817 (FOD817) OptocouplersElement14 2985527-
1 x HT7333A LDO Voltage Regulatorebay-
1 x MCP1702-3302E/TO (alternative to HT7333A)Element14 1331485-
1 x MT3608 Boost Convertorebay-
1 x 1N5817 Schottky Diode*ZR1020Z0040
2 x 470Ω 0.5 Watt Resistor*RR0564R7750
3 x 1kΩ 0.5 Watt 1% Metal Oxide Resistor*RR0572R7758
1 x 4.7kΩ 0.25 Watt Resistor*RR0588R7054
1 x 5.1kΩ 0.25 Watt 1% Metal Oxide Resistor*RR0589R7575
1 x 10kΩ 0.25 Watt 1% Metal Oxide Resistor*RR0596R7582
2 x 10µF Tantalum Capacitor*RZ6648R2638A
1 x VeroboardHP9542H0712
2 x 3-Pin Male Header*(HM34241)(P55141)
1 x 8-Pin DIL Socket for 2x OptocouplersPI6500P0550
2 x 16-Pin Female Header Socket for Arduino NanoElement14 2827905-
1 x SPDT Slide Switch PCB MountSS0834-
26 AWG Stranded White Hook Up WireWH3007W2257

Parts Required:

* Quantity shown, may be sold in packs. You’ll also need a breadboard and prototyping hardware.

The Schematic for my HV UPDI Programmer is shown in Figure 6. It is a derivation of several designs I have found on the internet. Basically, the “jtag2updi” program is downloaded to the Arduino Nano using the Arduino IDE. Now depending on whether your ATtiny, that you want to program, is operating off 3.3 or 5 Volts, SW1 is used to select the appropriate Target voltage. This Target voltage is switched on or off by the Nano via pins A0-A6. Now the original design from which I derived my design, used pins A0-A6 all connected together to provide enough current and to provide the Target supply voltage. With my design, I have used the A0-A6 pins to drive an Optocoupler (U2), which in turn, outputs the Target voltage (TVCC) to the ATtiny supply pin via the UPDI Connector. I could have just used A0 and it would work just as well. Note the 5 Volts is driven from an output from the Nano, which is supplied directly via the USB connector on the Nano. The 3.3 Volts, however, does not use the Nano 3.3 Volts output, but is derived from the

5 Volt Nano output via the HT7333A voltage regulator (note the MCP1702-3302E/TO could also be used). The reason I have done this is because the 3.3 Volt Nano output is provided by the USB Transceiver chip, which can only supply a limited amount of current, around 50 mA max. For the 12 Volts, I have used the MT3608 Boost convertor, which derives its output from the Nano 5 Volts output.

The Nano then, when required, connects the 12 Volts via an Optocoupler (U1) to the UPDI pin on the UPDI connector. Diode D1 is a Schottky Diode, which protects the Nano from the 12 Volt pulse, when it is output on the UPDI connector. (Warning: Do not connect the TVCC pin if your ATtiny is powered separately. This can result in damage to the Programmer or your ATtiny. Do set SW1 correctly though.)

The Veroboard Layout is shown in Figure 7 and the Parts List in Table 2. You will notice from Figure 7, that there is a Mode Header, so that the HV UPDI Programmer can operate in one of three modes: 1. UPDI; 2. HV; and 3. PCHV. To change the mode, put a jumper across the pins as indicated in Figure 7. If the Nano has been powered up, a reset is required for the new mode to take effect.

UPDI Mode:

This mode would be used when the UPDI pin is configured as UPDI and is not being used as a Reset pin nor a GPIO pin. Also, use this mode for any connected device that is not HV tolerant.

HV Mode:

This mode applies a HV pulse (12 Volt UPDI enable sequence) at the start of the programming sequence. This temporarily reconfigures the UPDI pin to UPDI mode and will remain in this mode until the next Power On Reset (POR). This allows programming to occur when the pin is configured as a Reset pin. A POR needs to occur for any fuse setting changes to take effect.

PCHV Mode:

Power Cycle High Voltage (PCHV) mode will initiate a HV pulse at the start of the programming sequence. At the end of the sequence, a second power cycle will occur which causes any new fuse setting to take effect. The power cycle OFF duration has been set to 10 ms. This mode would be used when the UPDI pin has been configured as Reset or as GPIO.

THE SOFTWARE

Once you have assembled the Veroboard, you are ready to program the Arduino Nano. This can be done before plugging it into the sockets on the Veroboard. First, you need to download the “jtag2updi” software. This can be found at https://github.com/Dlloydev/jtag2updi. Download the “zip” file from the “Code” drop down. In the “zip” file you will find a folder “jtag2updi-master”. Copy it and its contents into your “Arduino” directory or to wherever you can remember. Rename the “jtag2updi-master” folder to “jtag2updi”. Open the “jtag2updi.ino” file in the Arduino IDE. Then compile and upload the program to your Arduino Nano.

You are now almost ready to program your ATtiny3224 as per the settings as shown in the “Setting up the Arduino IDE” section of the PBS description. However, if you want to use the HV function of this HV UPDI Programmer, there are a few more steps to be taken. If you have the Arduino IDE open, close it down. Then find the “boards.txt” file. It should be in a directory that looks something like “/Users/username/AppData/Local/Arduino15/packages/megaTinyCore/hardware/megaavr/2.6.8”.

Open the “boards.txt” file in a text editor, for example, Notepad. Find the section headed “UPDI/RESET PIN CONFIGURATION”. Under this, you will find the following four commented out lines. Remove the “#” from the beginning of each line.

#atxy7.menu.resetpin.reset=Reset (DANGER - Bricks  chip w/out HV UPDI programmer!)
#atxy7.menu.resetpin.reset.bootloader.resetpinbits=10
#atxy7.menu.resetpin.gpio=GPIO (DANGER - Bricks chip w/out HV # UPDI programmer - Use PCHV)
#atxy7.menu.resetpin.gpio.bootloader.resetpinbits=00

Then, in the next section headed “UPDI/RESET PIN CONFIGURATION”, remove the “#” from the following four commented out lines.

#atxy6.menu.resetpin.reset=Reset (DANGER - Bricks chip w/out  HV UPDI programmer!)
#atxy6.menu.resetpin.reset.bootloader.resetpinbits=10
#atxy6.menu.resetpin.gpio=GPIO (DANGER - Bricks chip w/out HV  UPDI programmer - Use PCHV)
#atxy6.menu.resetpin.gpio.bootloader.resetpinbits=00

Then, in the next section headed “UPDI/RESET PIN CONFIGURATION”, remove the “#” from the following six commented out lines.

#atxy4.menu.resetpin.UPDI=UPDI (no reset pin)
#atxy4.menu.resetpin.UPDI.bootloader.resetpinbits=01
#atxy4.menu.resetpin.reset=Reset (DANGER - Bricks chip w/out # HV UPDI programmer!)
#atxy4.menu.resetpin.reset.bootloader.resetpinbits=10
#atxy4.menu.resetpin.gpio=GPIO (DANGER - Bricks chip w/out HV UPDI programmer - Use PCHV)
#atxy4.menu.resetpin.gpio.bootloader.resetpinbits=00

And lastly, in the next section headed “UPDI/RESET PIN CONFIGURATION”, remove the “#” from the following six commented out lines.

#atxy2.menu.resetpin.UPDI=UPDI (no reset pin)
#atxy2.menu.resetpin.UPDI.bootloader.resetpinbits=01 
#atxy2.menu.resetpin.reset=Reset (DANGER - Bricks chip w/out # HV UPDI programmer!)
#atxy2.menu.resetpin.reset.bootloader.resetpinbits=10
#atxy2.menu.resetpin.gpio=GPIO (DANGER - Bricks chip w/out HV UPDI programmer - Use PCHV)
#atxy2.menu.resetpin.gpio.bootloader.resetpinbits=00

Save the file.

Now open the Arduino IDE and under the “Tools” menu, you
will see a new selection “UPDI/Reset Pin Function” has been
added. Whatever you select, remember to “Burn Bootloader”
before uploading your program. Also make sure under
“Programmer”, “jtag2updi” has been selected.

CONCLUSION

If you make the above described “HV UPDI Programmer”, you should find that a huge number of UPDI microchips can now be programmed, see the Compatible Microchips List.