The Multi-Core Three-Phase Hexabitz Power Analyzer is a new upgrade of the " Single-Phase True RMS Analyzer " with H2AR3 modules to perform ten qualitative and quantitative measurements in each module (A, V, kW, kVAR, KVA, Hz, Cos, kWh, kVARh, Sec) shared through the BOS (Bitz Operating System) messaging protocol to monitor the electrical grid.
We'll list the tools used in this project:
1) Hexabitz AC Current and Voltage Sensor Module (H2AR3):
H2AR3x is an AC current and voltage sensor module based on bidirectional current sense amplifiers INA199B2DCKT, INA199B1DCKT, Low-Drift, Low-Power, Dual-Output Vref and Vref/2 Voltage Reference REF2033AIDDCR and STM32F0 MCU [1].
2) User Tools Kit:
This kit includes the tools that are essential for programming, debugging, and powering Hexabitz modules.
- FTDI USB to UART Serial Cable:
The 4-pin USB 2.0 to UART serial cable is an indispensable tool for Hexabitz development! It incorporates FTDI’s USB to UART interface [2].
- Hexabitz BitzClamp:
Hexabitz BitzClamp is a modified Kelvin current clamp soldered to two 2.56 mm-pitch male jumper wires [3].
- STLINK-V3MODS Programmer (H40Rx):
H40Rx is a programmer module which contains STLINK-V3MODS stand-alone debugging and programming mini probe for STM32 microcontrollers (Hexabitz modules and other MCUs) [4].
- E-Z-Hook Programming Kit:
Instead of soldering SMD connectors there, you could use a nice off-the-shelf E-Z-Hook kit that we assembled for you [5].
3) CR8400 CurrentTransformer:
CR Magnetics CR8450-1000 is a wire lead current transformer [6].
Table(1): General purpose for current transformer used in this project
4) Variable Speed Drive:
The Altivar Machine ATV320 IP20 product is a variable speed drive for three-phase asynchronous and synchronous motors with a power of 1.5 kW [7].
We'll mention step-by-step instructions from designing to implementing this project:
1. Writing codes with STM32CubeIDE software:
If you are programming with STM32CubeIDE software for the first time, take advantage of Hexabitz WIKI from this step-by-step article.
1-a) The multi-core three phase Hexabitz power analyzer firmware:
In this application, three code files and two header files should be written as follows:
- The typology.h files for each module are the same and should be edited according to the hardware network configuration:
topology.h file:
#define __N 3 // Number of array modules
// Array modules
#define _mod1 1<<3
#define _mod2 2<<3
#define _mod3 3<<3
// Topology
static uint16_t array[__N ][7] ={
{_H2AR3, _mod3 | P3,_mod2 | P2, 0, 0, 0, 0}, // Module 1
{_H2AR3,0,_mod1 | P2,0,0,0,0}, // Module 2
{_H2AR3, 0,0, _mod1 | P1, 0,0, 0}, // Module 3
};
// Configurations for duplex serial ports
#if ( _module == 1 )
#define H2AR3 1
#define _P1pol_normal 1
#define _P2pol_normal 1
#define _P3pol_normal 1
#define _P4pol_normal 1
#define _P5pol_normal 1
#define _P6pol_normal 1
#endif
#if ( _module == 2 )
#define H2AR3 1
#define _P1pol_normal 1
#define _P2pol_reversed 1
#define _P3pol_normal 1
#define _P4pol_normal 1
#define _P5pol_normal 1
#define _P6pol_normal 1
#endif
#if ( _module == 3 )
#define H2AR3 1
#define _P1pol_normal 1
#define _P2pol_normal 1
#define _P3pol_reversed 1
#define _P4pol_normal 1
#define _P5pol_normal 1
#define _P6pol_normal 1
#endif
- The project.h file for the first module should be edited to meet the requirements of the application:
Project.h file:
/* Emulated EEPROM Virtual addresses for user parameters */
#define _EE_MyVar 607
This is necessary to use the built-in permanent memory (emulated EEPROM) to save the power readings.
1-b) H2AR3x firmware main.c code:
Clone the latest firmware of the module and start coding!
Taking the advantage of Hexabitz Single Phase Power Analyzer project [8], first, we define our variables:
#include "BOS.h"
int R_p_s,R_p_h,q_s,q_h; // Active & reactive Energy
float volt,curr, sum,sum_v,Ibuff[100]={0}, Vbuff[100]={0}, Pbuff[100]={0}, buff[100]={0}, buf[6]={0};
float MaxV,MaxI,avi,avv,sumv,sumi;
volatile float V,I,A_p,P_F,R_p,q;
int i,theta;
The common part involved in the three modules and responsible for the measurement is:
HAL_Delay(1000);IND_toggle();// update measurements every second
sumi=0;avi=0;avv=0;I=0;V=0;A_p=0;P_F=0;R_p=0;
MaxV=0;MaxI=0;sumv=0,sumi=0;
// get values sequentially not simultaneously ...!
for(i=0;i<100;i++)
{
SampleV(&volt);SampleA(&curr);
Vbuff[i]=volt;Ibuff[i]=curr;
StartMicroDelay(135);//135+65=200u, 200u*100= 20 ms.
}
for(i=0;i<100;i++) // calc average error for current
{avi=avi+Ibuff[i];} avi=avi/100;
for(i=0;i<100;i++) // Current online calibration
{Ibuff[i]=Ibuff[i]-avi;}
for(i=0;i<100;i++) // calc average error for volt
{avv=avv+Vbuff[i];} avv=avv/100;
for(i=0;i<100;i++) // Current online calibration
{Vbuff[i]=Vbuff[i]-avv;}
// Real Power discrete time calculation
for(i=0;i<100;i++)
{Pbuff[i]=Ibuff[i]*Vbuff[i];// the acquired samples were sequential not simultaneous
sumi+=Pbuff[i];}R_p=sumi/100;// watt
if(R_p<0) R_p=-R_p;sumi=0;
// RMS discrete time calculation
for(i=0;i<100;i++)
{buff[i]=pow(Vbuff[i],2);sumv+=buff[i];}
V=sqrt(sumv/100);
if(V<MaxV) V=MaxV;
for(i=0;i<100;i++)
{buff[i]=pow(Ibuff[i],2);sumi+=buff[i];}
I=sqrt(sumi/100);
// Apparent power = RMS Voltage x RMS current
// Power Factor = Real Power / Apparent Power
A_p=V2*I2; if(A_p<=0) A_p=0;
P_F=R_p/A_p; if (P_F< 0 | P_F>1) P_F=0;
theta=acos(P_F)*180/3.14159;
q=sqrt(pow(A_p,2)-pow(R_p,2));
In order to share the measurement data with the first module (which is responsible for formatting and display in the terminal), the corresponding variables must be defined on BOS, which in turn makes them available to the other modules:
in the first module:
/* User Task */
void UserTask(void *argument){
BOSMessaging.trace=TRACE_NONE;
AddBOSvar(FMT_FLOAT, (uint32_t)&V2);
AddBOSvar(FMT_FLOAT, (uint32_t)&I2);
AddBOSvar(FMT_FLOAT, (uint32_t)&A_p2);
AddBOSvar(FMT_FLOAT, (uint32_t)&P_F2);
AddBOSvar(FMT_FLOAT, (uint32_t)&R_p2);
AddBOSvar(FMT_FLOAT, (uint32_t)&q2);
AddBOSvar(FMT_FLOAT, (uint32_t)&theta2);
AddBOSvar(FMT_FLOAT, (uint32_t)&V3);
AddBOSvar(FMT_FLOAT, (uint32_t)&I3);
AddBOSvar(FMT_FLOAT, (uint32_t)&A_p3);
AddBOSvar(FMT_FLOAT, (uint32_t)&P_F3);
AddBOSvar(FMT_FLOAT, (uint32_t)&R_p3);
AddBOSvar(FMT_FLOAT, (uint32_t)&q3);
AddBOSvar(FMT_FLOAT, (uint32_t)&theta3); ....
in the second module:
/* User Task */
void UserTask(void *argument){
BOSMessaging.trace=TRACE_NONE;
AddBOSvar(FMT_FLOAT, (uint32_t)&V2);
AddBOSvar(FMT_FLOAT, (uint32_t)&I2);
AddBOSvar(FMT_FLOAT, (uint32_t)&A_p2);
AddBOSvar(FMT_FLOAT, (uint32_t)&P_F2);
AddBOSvar(FMT_FLOAT, (uint32_t)&R_p2);
AddBOSvar(FMT_FLOAT, (uint32_t)&q2);
AddBOSvar(FMT_FLOAT, (uint32_t)&theta2);
while(1){ ....
in the third module:
/* User Task */
void UserTask(void *argument){
BOSMessaging.trace=TRACE_NONE;
AddBOSvar(FMT_FLOAT, (uint32_t)&V3);
AddBOSvar(FMT_FLOAT, (uint32_t)&I3);
AddBOSvar(FMT_FLOAT, (uint32_t)&A_p3);
AddBOSvar(FMT_FLOAT, (uint32_t)&P_F3);
AddBOSvar(FMT_FLOAT, (uint32_t)&R_p3);
AddBOSvar(FMT_FLOAT, (uint32_t)&q3);
AddBOSvar(FMT_FLOAT, (uint32_t)&theta3);
while(1){ ...
At the end of the user task in the second and third module, these variables must be available on the bus so that the measurement task runs in the background and delivers the data to the first module:
at the end of user task (2nd module):
WriteRemote(1,(uint32_t)&V2,1,FMT_FLOAT,0);
WriteRemote(1,(uint32_t)&I2,2,FMT_FLOAT,0);
WriteRemote(1,(uint32_t)&A_p2,3,FMT_FLOAT,0);
WriteRemote(1,(uint32_t)&P_F2,4,FMT_FLOAT,0);
WriteRemote(1,(uint32_t)&R_p2,5,FMT_FLOAT,0);
WriteRemote(1,(uint32_t)&q2,6,FMT_FLOAT,0);
WriteRemote(1,(uint32_t)&theta2,7,FMT_FLOAT,0);
at the end of user task (3rd module):
WriteRemote(1,(uint32_t)&V3,8,FMT_FLOAT,0);
WriteRemote(1,(uint32_t)&I3,9,FMT_FLOAT,0);
WriteRemote(1,(uint32_t)&A_p3,10,FMT_FLOAT,0);
WriteRemote(1,(uint32_t)&P_F3,11,FMT_FLOAT,0);
WriteRemote(1,(uint32_t)&R_p3,12,FMT_FLOAT,0);
WriteRemote(1,(uint32_t)&q3,13,FMT_FLOAT,0);
WriteRemote(1,(uint32_t)&theta3,14,FMT_FLOAT,0);
In turn, the first module must save the data in the emulated EEPROM at each iteration of the user task and read it out at startup (just in time):
status = EE_ReadVariable(_EE_MyVar, &p); // read EEprom where the energy has been saved last time
status = EE_ReadVariable(_EE_MyVar, &p2);
status = EE_ReadVariable(_EE_MyVar, &p3);
R_p_h=p/3600;R_p_s=p%3600; // decomposition of total energy
R_p_h2=p2/3600;R_p_s2=p2%3600;R_p_h3=p3/3600;R_p_s3=p3%3600;
while(1){ .....
......
GetTimeDate();elapsed=BOS.time.seconds-temp; if(elapsed<0) elapsed+=60;
R_p_s+=R_p*elapsed;R_p_s2+=R_p2*elapsed;R_p_s3+=R_p3*elapsed; // watt.s (cumulative sum)
if(R_p_s>3600) {R_p_h+=R_p_s/3600;R_p_s%=3600;}// watt.h (cumulative conversion)
if(R_p_s2>3600) {R_p_h2+=R_p_s2/3600;R_p_s2%=3600;}
if(R_p_s3>3600) {R_p_h3+=R_p_s3/3600;R_p_s3%=3600;}
q_s+=q*elapsed;q_s2+=q2*elapsed;q_s3+=q3*elapsed;
if(q_s>3600) {q_h+=q_s/3600;q_s%=3600;}// VAR.h (cumulative conversion)
if(q_s2>3600) {q_h2+=q_s2/3600;q_s2%=3600;}
if(q_s3>3600) {q_h3+=q_s3/3600;q_s3%=3600;}
p=R_p_s+(R_p_h*3600);p2=R_p_s2+(R_p_h2*3600);p3=R_p_s3+(R_p_h3*3600);
if (Rmsv>30) {status = EE_WriteVariable(_EE_MyVar,p);// no need to store energy in case of phase absence/failure
status = EE_WriteVariable(_EE_MyVar,p2);
status = EE_WriteVariable(_EE_MyVar,p3);
freq+= 1250 /(inda-indv);if(freq<0)freq*=-1;if(freq<30) freq*=2;}
By the way, it is clear that we used the function GetTimeDate (available in BOS) to read the elapsed time and perform the power calculations.
Finally, the data should be formatted and displayed in the terminal:
/* Send the analysis Package to Realterm, MobaXterm, ... */
sprintf(buffer,"\n\n\rIrms : %.1f,%.1f,%.1f A\n\rVrms : %.1f, %.1f, %.1f V\n\rS : %.1f, %.1f, %.1f VA\n\rP : %.1f, %.1f, %.1f W\n\rQ : %.1f, %.1f, %.1f VAR",Rmsi,I2,I3,Rmsv,V2,V3,A_p,A_p2,A_p3,R_p,R_p2,R_p3,q,q2,q3);
writePxMutex(P3,buffer, bufferLength, 0xffff, 0xffff);
for(i=0;i<135;i++) {buffer[i]=0;} // clean
sprintf(buffer,"\n\rCOS : %.2f, %.2f, %.2f\n\rTheta: %d, %d, %d deg\n\rfreq : %d Hz",P_F,P_F2,P_F3,theta,theta2,theta3,freq);
writePxMutex(P3,buffer, bufferLength, 0xffff, 0xffff);
for(i=0;i<135;i++) {buffer[i]=0;} // clean
sprintf(buffer,"\n\rActive Energy cumulative:\n\r W.sec / W.hour\n\r %d, %d\n\r %d, %d\n\r %d, %d",R_p_s,R_p_h,R_p_s2,R_p_h2,R_p_s3,R_p_h3);
writePxMutex(P3,buffer, bufferLength, 0xffff, 0xffff);
for(i=0;i<135;i++) {buffer[i]=0;} // clean
sprintf(buffer,"\n\rReactive Energy cumulative:\n\r VAR.sec / VAR.hour\n\r %d, %d\n\r %d, %d\n\r %d, %d\n\r",q_s,q_h,q_s2,q_h2,q_s3,q_h3);
writePxMutex(P3,buffer, bufferLength, 0xffff, 0xffff);
for(i=0;i<135;i++) {buffer[i]=0;} // clean
2. Updating firmware:
Once we have written our codes, we update the modules firmware with the programmer. If you are updating the module's firmware for the first time, please read this article.
3. Starting the project:
Start wiring tools as shown in figure (10).
■ Connect the CTs to the CT connectors on modules.
■ Connect the module's voltage connector to the electric network.
■ Connect the programming pin marked with * to the serial monitor via the serial UART cable (FTDI USB):
□ Yellow (RXD) >> MCU TXD (the top communication pad)
□ Orange (TXD) >> MCU RXD (the bottom communication pad)
■ Power the module in one of the ways mentioned in this article.
■ Open any serial monitor and follow the instructions described in this article, to start monitoring values.
At the beginning of the process, the modules read the last value stored in the EEPROM, then it continues with the display of the measured values.
Here are some live experiments of our project. Have fun looking at them! More interesting projects will be presented in Hexabitz Channel.
► References[1]: AC Current and Voltage Sensor (H2AR3x) – Hexabitz
[2]: 4-Pin USB-Serial Prototype Cable – Hexabitz
[3]: Hexabitz BitzClamp – Hexabitz
[4]: STLINK-V3MODS Programmer (H40Rx) – Hexabitz
[5]: E-Z-Hook Programming Kit – Hexabitz
[6]: Wire Lead (crmagnetics.com)
[8]: Hexabitz Single Phase Power Analyser - Hackster.io
[9]: Wiki – Hexabitz
Comments
Please log in or sign up to comment.