Hackster is hosting Hackster Holidays, Ep. 6: Livestream & Giveaway Drawing. Watch previous episodes or stream live on Monday!Stream Hackster Holidays, Ep. 6 on Monday!
Waner Silva
Published © GPL3+

Smart Electrical Power Analyzer

The Smart Analyzer is an extended version of Oscillogram for Utility Grid with advanced resources: fft analysis on voltage and current.

AdvancedFull instructions provided8 hours11,282
Smart Electrical Power Analyzer

Things used in this project

Hardware components

SeeedStudio BeagleBone Green
BeagleBoard.org SeeedStudio BeagleBone Green
Can be BeagleBone, Raspberry and others... (to run the program Energy_QoS and Node-Red)
×1
EK-TM4C129EXL TM4C Tiva Crypto Ethernet LaunchPad
Texas Instruments EK-TM4C129EXL TM4C Tiva Crypto Ethernet LaunchPad
×1

Software apps and online services

Code Composer Studio
Texas Instruments Code Composer Studio
To program the microcontroller
Node-RED
Node-RED
To development the user interface

Story

Read more

Schematics

Voltage sensor

Code

Energy_QoS

C/C++
This is the program that runs on beaglebone or raspberry, is responsible for receiving data (via uart) from Tiva and store in .csv files.
//gcc Energy_QoS.c -o Energy_QoS -lm -lpthread
#include <unistd.h>
#include <stdio.h>
#include <time.h>
#include <errno.h>
#include <fcntl.h> 
#include <stdlib.h>
#include <string.h>
#include <termios.h>
#include <signal.h>
#include <limits.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <pthread.h>
#include <net/if.h>
#include <pthread.h>
#include <math.h>
#define SIZE_BUFFER 8

char buffer_tx[] = {0,0,0,0,0,0,0,0};
char buffer_rx[] = {0,0,0,0,0,0,0,0};

time_t timer;
struct tm *horarioLocal;
int dia, mes, ano, hora, min, sec;

volatile unsigned short int msg_receive = 0, nbytes = 0;
volatile unsigned short int running = 1;
unsigned short int nwrite;
unsigned short int nread;
pthread_t thread_id;
char *portname = "/dev/ttyACM0";
int handle;	
int n = SIZE_BUFFER;
unsigned short int anomalias = 0;
void signal_Handler(int sign){
	int o_exit = 0;
	switch (sign){
		case SIGINT:{
			o_exit = 1;
			break;
		}
		case SIGTERM:{
			o_exit = 1;
			break;
		}
		default:{
			break;
		}
	}

	if (o_exit == 1)
	running = 0;
}

void *uart_msg_receive(void *t){
	while(running){
		for(n = 0; n < SIZE_BUFFER; n++){
			nread = read(handle,&buffer_rx[n],1);
			/*if(nread < 0){
				printf("Read error\n");
			}else{
				printf("Data (%d) read = %d \n",n,buffer_rx[n]);
			}*/		
		}		
        msg_receive = 1;
        usleep(1000);
        while(msg_receive);
    }
}

int set_interface_attribs(int fd, int speed){
    struct termios tty;
    if (tcgetattr(fd, &tty) < 0) {
        printf("Error from tcgetattr: %s\n", strerror(errno));
        return -1;
    }
    cfsetospeed(&tty, (speed_t)speed);
    cfsetispeed(&tty, (speed_t)speed);
    tty.c_cflag |= (CLOCAL | CREAD);	/* ignore modem controls */
    tty.c_cflag &= ~CSIZE;
    tty.c_cflag |= CS8;					/* 8-bit characters */
    tty.c_cflag &= ~PARENB;				/* no parity bit */
    tty.c_cflag &= ~CSTOPB;				/* only need 1 stop bit */
    tty.c_cflag &= ~CRTSCTS;			/* no hardware flowcontrol */
    /* setup for non-canonical mode */
    tty.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON);
    tty.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
    tty.c_oflag &= ~OPOST;
    /* fetch bytes as they become available */
    tty.c_cc[VMIN] = 1;
    tty.c_cc[VTIME] = 1;
    if (tcsetattr(fd, TCSANOW, &tty) != 0) {
        printf("Error from tcsetattr: %s\n", strerror(errno));
        return -1;
    }
    return 0;
}

void set_mincount(int fd, int mcount){
    struct termios tty;
    if (tcgetattr(fd, &tty) < 0) {
        printf("Error tcgetattr: %s\n", strerror(errno));
        return;
    }
    tty.c_cc[VMIN] = mcount ? 1 : 0;
    tty.c_cc[VTIME] = 5;        /* half second timer */
    if (tcsetattr(fd, TCSANOW, &tty) < 0)
        printf("Error tcsetattr: %s\n", strerror(errno));
}

void update_time(void){
    time(&timer);
	horarioLocal = localtime(&timer);
	dia = horarioLocal->tm_mday;
	mes = horarioLocal->tm_mon + 1;
	ano = horarioLocal->tm_year + 1900;
	hora = horarioLocal->tm_hour;
	min  = horarioLocal->tm_min;
	sec  = horarioLocal->tm_sec;
}


int main(void){	

	/////
	unsigned int i = 0, count = 0;
	unsigned char c = 0, wave_complete = 0, harm_ord = 1;
	float v_vector[2048], f = 0, v = 0, p = 0, q =0;
	float Mag_vg[53], Mag_ig[53]; 
	/////
	char query[512], str[30];
	FILE *dataloger;
	FILE *temperatureFile;

	handle = open(portname, O_RDWR | O_NOCTTY | O_SYNC);
    if (handle < 0) {
		printf("Error opening %s: %s\n", portname, strerror(errno));
        return -1;
    }    
    set_interface_attribs(handle, B115200);// baudrate 115200, 8 bits, no parity, 1 stop bit
   	
	if( pthread_create(&thread_id, NULL, &uart_msg_receive, NULL) ){
		printf("\nErro thread\n");
    }
    
	for(i = 0; i < 2048; i++){
		v_vector[i] = 0.0;
	}
	
	signal(SIGINT,  signal_Handler);
    signal(SIGTERM, signal_Handler);
	time(NULL);

	sleep(1);
	
	buffer_tx[0] = 0xF0;
	buffer_tx[1] = 0x01;
	nwrite = write(handle,buffer_tx,8);
	tcdrain(handle);

	while (running){
		if(msg_receive){
			if( (buffer_rx[0] >> 4) == 1 ){
				
				i = ((buffer_rx[0] & 0x07) << 8) + (buffer_rx[1] & 0xFF);
				for(c = 2; c < 8; c++){
					if(i < 2048){
						if( (buffer_rx[c] & 0x80) == 0x80)
							v_vector[i] = -0.01*( (float)((0xFF ^ buffer_rx[c]) + 1) );
						else
							v_vector[i] = 0.01*((float)buffer_rx[c]);
						i++;						
					}				
				}
				
				if(i == 2048){
					anomalias++;
					wave_complete = 1;
				}
			}else if( (buffer_rx[0] >> 4) == 2 ){
				if( (buffer_rx[2] & 0x80) == 0x80)
					v = -0.1*( (float)((( 0xFF ^ buffer_rx[2])<<8) + ( 0xFF ^ buffer_rx[3]) + 1 ) );
				else
					v = 0.1*( (float)((buffer_rx[2]<<8) + (buffer_rx[3]) ) );

				if( (buffer_rx[4] & 0x80) == 0x80)
					f = -0.01*( (float)((( 0xFF ^ buffer_rx[4])<<8) + ( 0xFF ^ buffer_rx[5]) + 1 ) );
				else
					f = 0.01*( (float)((buffer_rx[4]<<8) + (buffer_rx[5]) ) );

				if( (buffer_rx[6] & 0x80) == 0x80)
					p = -0.01*( (float)( (0xFF ^ buffer_rx[6]) + 1) );
				else
					p = 0.01*( (float)((int)buffer_rx[6]));

				if( (buffer_rx[7] & 0x80) == 0x80)
					q = -0.01*( (float)( (0xFF ^ buffer_rx[7]) + 1) );
				else
					q = 0.01*( (float)buffer_rx[7]);
				
				update_time();

				dataloger = fopen("dataloger_1.csv","a");
				if(dataloger == NULL){
					printf("\nErro dataloger\n!");
					exit(1);
				}			
				sprintf(query,"%d,%d,%d,%d,%d,%d,%.2f,%.2f,%.2f,%.2f\n",dia,mes,ano,hora,min,sec,v,f,p,q);
				fprintf(dataloger,"%s",query);			
				
				fclose(dataloger);				
			}else if( (buffer_rx[0] >> 4) == 3 ){
				
				for(c = 1; c < 8; c++){					
					Mag_vg[harm_ord] = 0.1*((float)((unsigned)buffer_rx[c]));
					harm_ord = harm_ord+2;
					if(harm_ord == 53){
						sprintf(query,"v,%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,%.2f\n",Mag_vg[1],Mag_vg[3],Mag_vg[5],Mag_vg[7],Mag_vg[9],Mag_vg[11],Mag_vg[13],Mag_vg[15],Mag_vg[17],Mag_vg[19],Mag_vg[21],Mag_vg[23],Mag_vg[25],Mag_vg[27],Mag_vg[29],Mag_vg[31],Mag_vg[33],Mag_vg[35],Mag_vg[37],Mag_vg[39],Mag_vg[41],Mag_vg[43],Mag_vg[45],Mag_vg[47],Mag_vg[49],Mag_vg[51]);
						dataloger = fopen("dataloger_2.csv","a");
						if(dataloger == NULL){
							printf("\nErro fft dataloger\n!");
							exit(1);
						}else{		
							fprintf(dataloger,"%s",query);				
						}
						harm_ord = 1;
						fclose(dataloger);
						break;
					}					
				}
				
			}else if( (buffer_rx[0] >> 4) == 4 ){
				
				for(c = 1; c < 8; c++){					
					Mag_ig[harm_ord] = 0.1*((float) ((unsigned)buffer_rx[c]) );
					harm_ord = harm_ord+2;
					if(harm_ord == 53){
						sprintf(query,"i,%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,%.2f\n",Mag_ig[1],Mag_ig[3],Mag_ig[5],Mag_ig[7],Mag_ig[9],Mag_ig[11],Mag_ig[13],Mag_ig[15],Mag_ig[17],Mag_ig[19],Mag_ig[21],Mag_ig[23],Mag_ig[25],Mag_ig[27],Mag_ig[29],Mag_ig[31],Mag_ig[33],Mag_ig[35],Mag_ig[37],Mag_ig[39],Mag_ig[41],Mag_ig[43],Mag_ig[45],Mag_ig[47],Mag_ig[49],Mag_ig[51]);
						dataloger = fopen("dataloger_2.csv","a");
						if(dataloger == NULL){
							printf("\nErro fft dataloger\n!");
							exit(1);
						}else{		
							fprintf(dataloger,"%s",query);				
						}
						harm_ord = 1;
						fclose(dataloger);
						break;
					}					
				}			
			}

			memset(buffer_rx,0,sizeof(buffer_rx));
			msg_receive = 0;
		}
		
		if(wave_complete){
			
			update_time();
			
			dataloger = fopen("dataloger_0.csv","a");
			if(dataloger == NULL){
				printf("\nErro dataloger\n!");
                exit(1);
			}			
			for(i = 0; i < 2048; i++){				
				sprintf(query,"%d,%d,%d,%d,%d,%d,%d,%.3f\n",dia,mes,ano,hora,min,sec,i,v_vector[i]);
				fprintf(dataloger,"%s",query);			
			}
			fclose(dataloger);
			memset(buffer_tx,0,sizeof(buffer_rx));
			buffer_tx[0] = 0xF1;
			nwrite = write(handle,buffer_tx,8);
			tcdrain(handle);
			for(i = 0; i < 2048; i++){
				v_vector[i] = 0.0;
			}
			wave_complete = 0;
		}
		
		usleep(2000);
		count++;
		if(count == 200){
			count = 0;
			update_time();
		}
	}
	buffer_tx[0] = 0xF0;
	buffer_tx[1] = 0x00;
	nwrite = write(handle,buffer_tx,8);
	tcdrain(handle);

	close(handle);
	printf("\nStop program\n");
    return 0;	
}

Tiva 1294 source code

C/C++
Microcontroller code. Is necessary the library TivaWare™ for C Series TI, for LaunchPad.
http://www.ti.com/tool/SW-TM4C. This code samples the voltage and current at analog inputs an0 and an1. Performs calculations and sends data via serial (115200 kbs, 8bit, no parity) to beaglebone or raspberry
No preview (download only).

Node-red interface

JavaScript
Node-red application code, where .csv files are read and graphics are displayed
[{"id":"cb6abf33.b5419","type":"tab","label":"Flow 2","disabled":false,"info":""},{"id":"dd5470ee.89e2a","type":"exec","z":"cb6abf33.b5419","command":"tail -n 1 /home/debian/dataloger_1.csv","addpay":false,"append":"","useSpawn":"false","timer":"","oldrc":false,"name":"","x":330,"y":40,"wires":[["7c80c147.67333"],[],[]]},{"id":"e9ce267c.34c068","type":"inject","z":"cb6abf33.b5419","name":"","topic":"","payload":"1","payloadType":"num","repeat":"5","crontab":"","once":false,"onceDelay":0.1,"x":70,"y":40,"wires":[["dd5470ee.89e2a","c9f781d5.9d3a7","c1d3672c.7c3358"]]},{"id":"7c80c147.67333","type":"function","z":"cb6abf33.b5419","name":"","func":"var dados = msg.payload.split(\",\");\n\nvoltage = parseFloat(dados[6]);\nfrequency = parseFloat(dados[7]);\np_watt = parseFloat(dados[8]);\nq_var = parseFloat(dados[9]);\n\nvar msg1 = {payload: parseFloat(voltage).toFixed(2).toString()};\nvar msg2 = {payload: parseFloat(frequency).toFixed(2).toString()};\nvar msg3 = {payload: parseFloat(p_watt).toFixed(2).toString()};\nvar msg4 = {payload: parseFloat(q_var).toFixed(2).toString()};\n\nreturn [msg1, msg2, msg3, msg4];\n","outputs":4,"noerr":0,"x":570,"y":40,"wires":[["aa339d.4c91ec6"],["7c80a8fe.b6f658"],["bd3d8d6d.1781b"],["7dc336d0.6a6a88"]]},{"id":"aa339d.4c91ec6","type":"ui_gauge","z":"cb6abf33.b5419","name":"","group":"24980630.fd640a","order":1,"width":"4","height":"4","gtype":"gage","title":"Voltage","label":"V","format":"{{value}}","min":"63","max":"190","colors":["#ff0000","#00ff00","#ff0000"],"seg1":"114","seg2":"139","x":840,"y":20,"wires":[]},{"id":"7c80a8fe.b6f658","type":"ui_gauge","z":"cb6abf33.b5419","name":"","group":"24980630.fd640a","order":1,"width":"4","height":"4","gtype":"gage","title":"Frequency","label":"Hz","format":"{{value}}","min":"59","max":"61","colors":["#ff0000","#00ff00","#ff0000"],"seg1":"59.5","seg2":"60.5","x":850,"y":60,"wires":[]},{"id":"bd3d8d6d.1781b","type":"ui_gauge","z":"cb6abf33.b5419","name":"","group":"24980630.fd640a","order":1,"width":"4","height":"4","gtype":"gage","title":"Power (W)","label":"p.u.","format":"{{value}}","min":"0","max":"1.3","colors":["#00ff00","#00ff00","#00ff00"],"seg1":"0.5","seg2":"0.5","x":850,"y":100,"wires":[]},{"id":"7dc336d0.6a6a88","type":"ui_gauge","z":"cb6abf33.b5419","name":"","group":"24980630.fd640a","order":1,"width":"4","height":"4","gtype":"gage","title":"Reactive (Var)","label":"p.u.","format":"{{value}}","min":"0","max":"1.3","colors":["#ffff00","#ffff00","#ffff00"],"seg1":"0.5","seg2":"0.5","x":860,"y":140,"wires":[]},{"id":"c9f781d5.9d3a7","type":"exec","z":"cb6abf33.b5419","command":"tail -n 2 /home/debian/dataloger_2.csv","addpay":false,"append":"","useSpawn":"false","timer":"","oldrc":false,"name":"","x":330,"y":200,"wires":[["b20f93cb.353eb"],[],[]]},{"id":"b20f93cb.353eb","type":"function","z":"cb6abf33.b5419","name":"","func":"var lines = msg.payload.split('\\n');\ndados = lines[0].split(\",\");\n\nv_h1 = parseFloat(dados[1]);\nv_h3 = parseFloat(dados[2]);\nv_h5 = parseFloat(dados[3]);\nv_h7 = parseFloat(dados[4]);\nv_h9 = parseFloat(dados[5]);\nv_h11 = parseFloat(dados[6]);\n\ndados = lines[1].split(\",\");\n\ni_h1 = parseFloat(dados[1]);\ni_h3 = parseFloat(dados[2]);\ni_h5 = parseFloat(dados[3]);\ni_h7 = parseFloat(dados[4]);\ni_h9 = parseFloat(dados[5]);\ni_h11 = parseFloat(dados[6]);\n\nvar msg1 = {payload: parseFloat(v_h1).toFixed(1).toString(), \"topic\": \"1h\"};\nvar msg2 = {payload: parseFloat(v_h3).toFixed(1).toString(), \"topic\": \"3h\"};\nvar msg3 = {payload: parseFloat(v_h5).toFixed(1).toString(), \"topic\": \"5h\"};\nvar msg4 = {payload: parseFloat(v_h7).toFixed(1).toString(), \"topic\": \"7h\"};\nvar msg5 = {payload: parseFloat(v_h9).toFixed(1).toString(), \"topic\": \"9h\"};\nvar msg6 = {payload: parseFloat(v_h11).toFixed(1).toString(), \"topic\": \"11h\"};\n\nvar msgi1 = {payload: parseFloat(i_h1).toFixed(1).toString(), \"topic\": \"1h\"};\nvar msgi2 = {payload: parseFloat(i_h3).toFixed(1).toString(), \"topic\": \"3h\"};\nvar msgi3 = {payload: parseFloat(i_h5).toFixed(1).toString(), \"topic\": \"5h\"};\nvar msgi4 = {payload: parseFloat(i_h7).toFixed(1).toString(), \"topic\": \"7h\"};\nvar msgi5 = {payload: parseFloat(i_h9).toFixed(1).toString(), \"topic\": \"9h\"};\nvar msgi6 = {payload: parseFloat(i_h11).toFixed(1).toString(), \"topic\": \"11h\"};\n\nreturn [msg1, msg2, msg3, msg4, msg5, msg6, msgi1, msgi2, msgi3, msgi4, msgi5, msgi6];\n","outputs":12,"noerr":0,"x":570,"y":200,"wires":[["5fd4110f.f016b"],["5fd4110f.f016b"],["5fd4110f.f016b"],["5fd4110f.f016b"],["5fd4110f.f016b"],["5fd4110f.f016b"],["94a14b40.2b4a18"],["94a14b40.2b4a18"],["94a14b40.2b4a18"],["94a14b40.2b4a18"],["94a14b40.2b4a18"],["94a14b40.2b4a18"]]},{"id":"5fd4110f.f016b","type":"ui_chart","z":"cb6abf33.b5419","name":"","group":"24980630.fd640a","order":0,"width":"8","height":"4","label":"FFT Voltage (dB)","chartType":"bar","legend":"false","xformat":"HH:mm:ss","interpolate":"linear","nodata":"","dot":false,"ymin":"0","ymax":"20","removeOlder":1,"removeOlderPoints":"","removeOlderUnit":"3600","cutout":0,"useOneColor":false,"colors":["#1f77b4","#1f77b4","#1f77b4","#1f77b4","#1f77b4","#1f77b4","#ff9896","#9467bd","#c5b0d5"],"useOldStyle":false,"x":830,"y":200,"wires":[[],[]]},{"id":"94a14b40.2b4a18","type":"ui_chart","z":"cb6abf33.b5419","name":"","group":"24980630.fd640a","order":0,"width":"8","height":"4","label":"FFT Current (dB)","chartType":"bar","legend":"false","xformat":"HH:mm:ss","interpolate":"linear","nodata":"","dot":false,"ymin":"0","ymax":"20","removeOlder":1,"removeOlderPoints":"","removeOlderUnit":"3600","cutout":0,"useOneColor":false,"colors":["#ff8040","#ff8040","#ff8040","#ff8040","#ff8040","#ff8040","#ff9896","#9467bd","#c5b0d5"],"useOldStyle":false,"x":830,"y":240,"wires":[[],[]]},{"id":"c1d3672c.7c3358","type":"exec","z":"cb6abf33.b5419","command":"tail -n 2048 /home/debian/dataloger_0.csv","addpay":false,"append":"","useSpawn":"false","timer":"","oldrc":false,"name":"","x":340,"y":380,"wires":[["4d9ca60e.d7bbf8"],[],[]]},{"id":"4d9ca60e.d7bbf8","type":"function","z":"cb6abf33.b5419","name":"","func":"var lines = msg.payload.split('\\n');\nvar dados = 0;\nvar chart = [{\n\"series\":[\"\"],\n\"data\":[[{\"x\":0,\"y\":0},{\"x\":0,\"y\":0}]],\n\"labels\":[\"\"]\n}];\n\nfor(i=0; i<2047; i++){\n\tdados = lines[i].split(\",\");\n    chart[0].data[0][i]= { \"x\": i, \"y\": dados[7]};\n}\nmsg.payload = chart;\nreturn msg;","outputs":1,"noerr":0,"x":590,"y":360,"wires":[["8664a034.befd9"]]},{"id":"8664a034.befd9","type":"ui_chart","z":"cb6abf33.b5419","name":"","group":"24980630.fd640a","order":0,"width":"16","height":"4","label":"Disturbance on Voltage (8 cycles)","chartType":"line","legend":"false","xformat":"HH:mm:ss","interpolate":"linear","nodata":"","dot":false,"ymin":"-1.2","ymax":"1.2","removeOlder":1,"removeOlderPoints":"2048","removeOlderUnit":"3600","cutout":0,"useOneColor":false,"colors":["#ff8040","#ff8040","#ff8040","#ff8040","#ff8040","#ff8040","#ff9896","#9467bd","#c5b0d5"],"useOldStyle":false,"x":820,"y":360,"wires":[[],[]]},{"id":"24980630.fd640a","type":"ui_group","z":"","name":"Measures","tab":"31c625fa.fdc9ea","disp":false,"width":"16","collapse":false},{"id":"31c625fa.fdc9ea","type":"ui_tab","z":"","name":"Home","icon":"dashboard"}]

Credits

Waner Silva

Waner Silva

6 projects • 14 followers
I am electronic enginer

Comments