PVI-xxx Inverter Logic Deep Dive

hardware

PVI-xxx Inverter Logic Deep Dive

August 9, 2025

Intro

This is Part 2 of my inverter adventure. In Part 1, we did a high-level teardown of a grid-tie inverter - the “big picture” view of where the power flows and what the major building blocks look like.

Now we’re looking into the multi-CPU controller board. This is where timing-critical control loops, user-facing UI, and inter-processor chatter all come together.

We’ll repeat the safety warning from Part 1, and then go through:

  • Identifying and mapping the components on the CPU board.
  • Some hands-on hardware reverse engineering.
  • Powering it up safely on the bench, far away from dangerous voltages.
  • Extracting firmware and live signals.
  • A dash of protocol analysis.
  • And finally some firmware emulation.

If Part 1 was “lifting the hood,” Part 2 is crawling inside the dashboard with a logic analyser, a multimeter, and a healthy dose of optimism that it’ll all go back together.

Safety Warning (IMPORTANT)

Grid-tie inverters contain lethal voltages in many areas. Both high-voltage AC and DC are present on multiple internal rails, and disconnecting the unit does not automatically make it safe.

Large internal capacitors can retain 300 - 400 VDC for minutes after shutdown. There’s more than enough energy in those to kill you. These voltages are found on the MPPT input, the bulk DC bus, and the inverter H-bridge, and they’re spread across multiple PCBs.

If you haven’t worked with power electronics before, or you haven’t developed the instinct to spot something dangerous, this is not the project to start on.

This post is not how to repair your own unit. Read at your own risk!

Overview

The CPU board is where all the brains of the inverter live. It manages everything from the high-speed control loops that keep the output in phase with the grid, to the user interface that lets you see what’s going on.

Three processors share the workload:

  • The ATmega128, a general-purpose 8-bit micro runs the user interface; the front-panel LCD, some LEDs, and the RS-485 Modbus link for talking to the outside world.
  • DSP1 - Running the MPPT control loop to maximise the power extraction from the attached solar panels.
  • DSP2 - Running the inverter control loop feeding that power to the grid.

CPU block diagram

This article focuses on the ATmega micro. We’ll tackle the other two in future posts.

Identifying the components

The first step in any new board exploration is identifying the principal components. This was made more difficult than usual by the presnece of a thick layer of conformal coating which is presumably how the inverter gets its outdoor rating. The coating made the part numbers very difficult to read but it was easily scraped off the top with a guitar pick. (Any soft plastic would be suitable - the key is to avoid scraping off the part numbers with it!).

To make things a little more challenging the silk-screen appears to be confined mainly to test-points with very few component references or pin identifiers.

CPU Component depictions

In the above diagram, the small white squares, for example the one labelled “L2 primary relay” denote the location of the transistor drivers for the associated component.

Powering the CPU and display boards outside of the inverter

The first power up attempt looked like this with 100 V from Makita tool batteries wired in series and the boost connected to the SMPS input directly.

Initial power up setup

As you can see it’s not very practical! The observant will notice the missing IGBT that was the cause of the initial fault - I snipped it out.

CPU board 50-way connector
CPU board 50-way connector

As I don’t feel like doing live probing with a high-current 100V supply, to make analysis more comfortable I decided to find out how to power the board without needing the whole inverter attached.

To do this, I first had to identify the pinout of the main header. This involved carefully tracing each connection using a multi-meter and photographs of both the CPU PCB and the Power Handling PCB to which it connects.

As there are 50 connections that took a little time! The result is shown in the figure to the right.

To power the board on the bench, you will need a power supply with the following voltages:

  • 12 V (I current limited that to 250 mA)
  • -12 V (negative, this also works with -9 V)

My power supply doesn’t do negative voltages so I used 9 V PP3 battery with the positive terminal connected to ground to produce a negative voltage.

I measured the current drawn by the negative supply to be only 16 mA so it should last a reasonably long time if you don’t forget to disconnect it after testing (ask me how I know)!

The negative supply is used for both display contrast adjustment and elsewhere on the analogue side of the board. If it is not present, the display will not show properly, the Red LED illuminates and an error message is shown (faintly!).

The distinct 12 V IN points must both be connected.

If you power the board like that, you’ll get a promising boot followed quickly by:

Error 33

The user manual for the inverter helpfully contains a list of the error codes and their meanings. For error 33, it lists it as “UnderTemperature.” There are two temperature sensors (look like NTCs) next to the IGBTs and boost MOSFETS so I reverse engineered the connector to the inverter board as follows:

Inverter Logic Connector

I then put the whole thing back into the 100 V rig to take some measurements at those points; just over 4 V. For a quick test, I connected those pins (RT1 & RT2) to +3.3V and we have standalone boot success:

Boot success

Firmware part 1: The ATMega CPU

After some hands-on reverse engineering with a multimeter and a few datasheets for the inverting and tri-state buffers, I traced the relevant connections for the ATmega128’s programming port. Unlike the other CPUs, this one has a soldered header, a strong indication that it’s the factory programming port.

ATMega CPU programming port

The programming pins are dual-purpose in that they also handle other board functions. To let them share duties, the designers routed them through tri-state drivers whose state is controlled by the nPDI_EN pin on the programming header. By grounding that pin, the drivers switch over these duties allowing us to program the chip.

I connected a cheap USBasp AVR ISP programmer, grounded nPDI_EN, and dumped the firmware. To my surprise, the read blocking fuses weren’t set so full flash and EEPROM came down without a fight.

Firmware emulation

I decided it would be fun to try to emulate the hardware and run the firmware on a PC. Using simavr (a fantastic AVR simulator), I loaded the extracted firmware and EEPROM images and was happy to discover it didn’t immediately crash!

Using simavr’s API, I wrote a simple C harness that initially just dumped traces so I could see what the firmware was poking. My main goal was to emulate the front-panel LCD.

Conveniently, simavr includes an HD44780 LCD peripheral in its virtual part’s library, so with a bit of glue code I had the firmware’s LCD output in my terminal. That worked, so I added an ANSI layer to keep the LCD in one spot and show the LEDs blinking:

ATMega CPU programming port

But it didn’t work for long until error 26 appeared. Listed in the manual as a Vref error, this suggested that the ATmega was reading the Vref as part of its pre-flight checks. I hooked the ADC trigger interrupt in the emulator and found it was sampling ADC0, ADC1, and ADC2. Measuring those pins on the real board gave me target voltages, so I coded the emulator to feed those values back. The result:

ATMega CPU programming port

It’s working, or so I thought. After a longer while error 5 pops up. This one’s listed in the manual as a “Communications Error” so almost certainly it’s missing talking to its friends the DSP brothers.

Protocol analysis: just enough to boot, part 1

Breaking out the oscilloscope, I saw that the ATmega’s SPI bus was alive with two distinct chip-select (CS) lines. Using a logic-analyser, I captured the CS lines, along with the SPI bus signals.

SPI Logic analyser

I exported the data from PulseView as binary and loaded it into Python for parsing. Here’s a snippet of the output, left is MOSI (master → slave), right is MISO (slave → master):

SPI frame dumped

The pattern is a consistent 10-byte frame for both request and response.

Master packet

  • Bytes 0 to 4: A possible write vector; i.e. a way to send data to the DSPS.
  • Byte 5: register number
  • Bytes 6 to 9: dummy bytes to allow space for the slave to respond.

The first 5 bytes of the master packet look like a possible write vector, i.e. a way to send data to the DSPs. The sixth byte looks like a register number. Finally the final four are dummy bytes to allow the slave device to respond.

Slave packet

  • First byte: dummy (not echoed)
  • Next 5 bytes: echo of master payload
  • Sixth byte: constant 0xFF
  • Final 3 bytes: actual register data (possibly custom 24-bit float)

The register map looks pretty big. I didn’t fully decode the format, but it could easily be proprietary fixed-point or floating.

Simulating just enough SPI.

Back in the emulator, I stubbed out an SPI peripheral that returned correctly-framed packets with zeroed data for every register. That was enough to keep the firmware happy.

With that in place, the ATmega firmware booted cleanly without further errors. I even mapped the firmware’s front-panel buttons to keyboard keys, giving me a fully interactive UI in the emulator:

ATMega CPU programming port

Next steps

We now have:

  • A working map of the ATmega128’s role in the system
  • A repeatable method to power and dump it
  • A full emulator with LCD, LEDs, button inputs, and just-enough SPI to fool the firmware

Next steps:

  • the DSPs. Will they give up their secrets so easily? - Find out in part 3
  • the takeover… converting it to an offline, non-grid tied inverter with fully custom firmware.

Comments