Power Analyzer based on a COTS Power Monitoring IC

The idea of this project is the development of a power analyzer based on the ACS71020 power monitoring IC.

Similar projects worth following
This project was developed during a seminar and bachelors' thesis of mine.
It is designed to connect to 230V and can measure currents up to 15A.

The main features are the measurement of
- Power consumption
- Power factor
- Mains frequency
- RMS of voltage and current
- Phase angle

Additionally, the voltage and current input-waveforms are captured and analyzed for distortion (e.g. total harmonic distortion (THD))

Why develop another power analyzer?

First of all, commercial power analyzers (PA) are expensive and mostly designed for use on-site, for example for testing motors etc.

The goal of this project and the bachelors thesis was, to develop a low-cost power analyzer with the most important features when integrated into devices or used as a small adaptor - which was achieved.

How to use

The PA is designed to plug into a common socket here in Germany.
After plugging it in, the socket on the other side can be used for appliances with a power draw according to the fuse built in, but 15A at max.

The display then starts to show the measured values

  • RMS Voltage and Current
  • Power consumed
  • Current time (if synced with a PC)
  • Harmonic distortion factors (THD) of current and voltage
  • Power factor of the device
  • Phase angle


Additionally, a software GUI is provided to configure the chip. 

Settings Page
Settings Page to configure the ACS71020

The chip's EEPROM can be configures via the settings page of the GUI. First, the settings have to be initialized, to not accidentally overwrite a setting.

Live Page
Live Page for reading the measured values

The measured values can be read with the live page view.

Scope Page
Scope Page

The scope page shows the current voltage and current. On the bottom is a live view of the voltage and current harmonics, respectively.

Further development

Following things are planned for the future:

  • Do some software refactoring
  • Eliminate bugs
  • Additional configuration of measured data
  • Adding a calibration "wizard"

  • 1 × ACS71020 Power Monitoring IC by Allegro MicroSystems
  • 1 × Teensy 4.0 Microcontroller by PJRC
  • 4 × 1Meg 1% Resistor 1/4W Resistors for the input voltage divider
  • 1 × Waveshare 128x128 Display 1.5" SPI/I2C Display
  • 1 × 1.5k 1% 0805 Resistor Sense-resistor of voltage divider

View all 16 components

  • Calibration wizard - first thoughts

    Sebastian2 days ago 0 comments

    The calibration wizard will be implemented similar to the waveform view, in a separate window.
    It will guide through the calibration process, by calculating recommended settings, which can be accepted or ignored. The following calibrations have to be done:

    1. Voltage 
      1. RMS with a calibration factor
    2. Current
      1. RMS with a calibration factor
      2. Raw Values in terms of
        1. Coarse Gain
        2. Offset
        3. Fine trim

    I will try to present the first results until the end of the next week.

  • PCB-changes and calibration wizard update

    Sebastian2 days ago 0 comments

    While thinking about the calibration wizard, the idea occurred to me, that empty EEPROM-space may be used to store user data. For example the calibration date. So I added read and write functions for whole addresses, to see if it works. 

    It does work with unused EEPROM-Space, for example register 0x0F bits 20-23. Other unused bits may be used as well. They can be accessed by using the writeEEPROMValue function, with a 32-bit mask, where the corresponding bit-positions are 0.


    For long-term datalogging, an SD card can be used. Because the space is somewhat limited, for such a bulky SD card, a 6-pin header was added. The SD-card can be mounted anywhere in the housing and connected to them, just like the display. And I added pin-descriptions, because I they were missing.

  • Future Developments

    Sebastian07/23/2021 at 15:11 0 comments

    The goal for the next few weeks, is to develop a "calibration wizard", to guide through the calibration process.

    Additionally, I will try to write build-instructions at least for the housing, in case someone wants to build it similar to this prototype.

  • Adding a WD Timer to the Teensy

    Sebastian07/22/2021 at 11:22 0 comments

    When hitting "read" of the button in rapid succession, the Teensy somehow stopped working and had to be reset manually. To fix this problem, I used a Watchdog-Timer which is fed periodically and resets the teensy after 30s, if not "fed". The library used for this is the WDT_T4 library by tonton81.

    The reason for the bug under load is yet to be determined.

  • BugFix: Changing Voltage/Current view in Scope-Mode

    Sebastian07/15/2021 at 14:41 0 comments

    Changing between "voltage", "current" oder "both"-mode does not always work.

    I fixed it, by disabling the Radiobuttons while streaming. In addition to that, the bus-flush was improved, by clearing out the input buffer with the .reset_input_buffer() function.

  • Bug-fix: wrong-port-connection

    Sebastian07/15/2021 at 11:13 0 comments

    The GUI connects with any port that is found, even if it's not the correct teensy port. For debugging, the teensy is used in dual-serial mode. Only one can be used for bidirectional communication. A handshake with the teensy should fix this issue.

    The handshake implementation was a bit difficult, due to confusion decoding. 

    The handling is done in the GUI.
    Readings from the serial bus are represented as bytearrays, which have to be decoded with 

        ans = answer.decode('utf-8') .

    This returns a string, which is shown by print(ans). The teensy is configured to send the string "tconn" upon request, which I double checked is the case.
    But the comparison of ans == "tconn" didn't work, even if print(ans) returned the correct string. 

    After some troubleshooting, I found the solution. The repr(ans) function returns the exact representation of the string. And apparently, the string contained a newline character, which should've been removed after reading. Naturally "tconn\n" == "tconn" returned false every time.

    The issue was fixed in the readline function of the special linereader class. It returns the first value in a buffer which is separated by a newline character. The linereader looks for the next index i where "\n" is located in the buffer and returns all characters by accessing 

    r = buffer[:i+1]  (representing "tconn\n") .

    removing the "+1" solved the problem.

    This fix is stored in a separate branch "BugFix_Handshake"

  • Code cleaning and documentation

    Sebastian07/09/2021 at 10:02 0 comments

    One of the most important things in any software program is readability. This does not only enable others to understand the program, but also decreases error-proneness.

    As a first step, I reviewed how the code is structured.
    Basically, all files are in one directory. The current structure is shown in the following diagram.

    It is a simple structure. It consists of one page per general function. Because all of the pages look and function a bit different, it is not necessary to create an abstract base-class for pages. Utils can be used by all functions, while the serial_device ist instantiated by the backend.

    One thing, that would make the GUI more intuitive would be to change the names of the pages. Something like "Values" seems to be more accurate instead of LivePage.

    The names of variables should give a hint to what they do. While I'm at it, it is a good idea to make the page classes more consistent, too. They should all contain 

    • an init_GUI-function, which contains style-definitions and all GUI-elements for the page
    • A convention to function names. An underscore ('_') is used for internal use in classes
    • Correct documentation

    It is useful to add new pages later, in case the functionality should be extended.


    There are various documentation methods for python available. PyDoc uses docstrings (in triple quotes at the start of a method) and annotations in method signatures. More information can be found here.

    All of that should increase the readability and extendability.

    Apparently, I saved this as a draft...
    The documentation of the code is finally done and a docs-folder now exists with the documentation to each python module, created with pydoc. It was interesting to learn about different documentation techniques, as there are many good solutions.

    I used docstrings, because the project is kind of small and they were the easiest to do. For a bigger project, sphinx does look a lot more capable.

View all 7 project logs

Enjoy this project?



Similar Projects

Does this project spark your interest?

Become a member to follow this project and never miss any updates