Phasor Measurement Unit Using Microcontroller

 

PMU Test Setup in the Lab

Phasor Measurement Unit Using Microcontroller

 

PMU Test Setup in the Lab

Intro

A PMU measures the instantaneous magnitudes and phases of voltages, currents, power and other parameters of an Electric Grid which effectively represents a snapshot of the health of the system, which is used by the operators in the control room to manage the Grid. The basic building blocks of a PMU are shown in the following diagram.

A three phase voltage sensor was designed with LEM LV-25P voltage sensors which outputs three unipolar sine waves, i.e. their voltage level lies between 0v to 3.3 volt. A three phase sine to square wave converter circuit is used to provide 3.3 v pulses for each half cycle of the AC supply. A GPS Disciplined Oscillator generates 3200 pulses per second synchronized to the PPS signal from a GPS module, which time the sampling of the signals by the ADC of the microcontroller. The Phasor micro-controller was connected to these signals, which after calculating the phasors and time stamping them reported to the local display, as well as to the computer. The computer runs a Graphical User Interface created with Python which displays the data from the PMU in real time


Block Diagram of the developed Prototype PMU

 

 

Demo

 

Signal Generator

Programmable Three Phase Test Signal Generator

A programmable signal generator which can be used to generate three signals, as desired by just altering the programming of the micro-controller, i.e it can be used to create three phase sine waves with user defined phase shifts or sag-swell.

The basic Building Blocks of the signal generator

The Microcontroller uses look-up tables to find the pre-calculated 8-bit integer values and writes them to the 8-bit output ports. The R-2R ladder DACs generate a voltage signal based on the digital values at the 8 nodes. This voltage signal is boosted, i.e. its current sourcing capability, which is limited by the microcontroller’s I/O ports is increased with the help of an OP-AMP based buffer circuit. The desired signals are then collected at the output of the buffer circuits.

 

Schematic of the Signal Generator

This particular board was used to generate three phase AC signals, with 120 Degree phase shifts to be fed to the PMU in order to further process it.

 

 

Arduino Code:
  1.  
  2.  
  3. #include "JeeLib.h"
  4. #include "avr/pgmspace.h"
  5. #include "TimerOne.h"
  6.  
  7. //byte index;
  8. int i = 0;
  9.  
  10. byte sineR256[] PROGMEM = {
  11. 128, 131, 134, 137, 140, 143, 146, 149,
  12. 152, 155, 158, 162, 165, 167, 170, 173,
  13. 176, 179, 182, 185, 188, 190, 193, 196,
  14. 198, 201, 203, 206, 208, 211, 213, 215,
  15. 218, 220, 222, 224, 226, 228, 230, 232,
  16. 234, 235, 237, 238, 240, 241, 243, 244,
  17. 245, 246, 248, 249, 250, 250, 251, 252,
  18. 253, 253, 254, 254, 254, 255, 255, 255,
  19. 255, 255, 255, 255, 254, 254, 254, 253,
  20. 253, 252, 251, 250, 250, 249, 248, 246,
  21. 245, 244, 243, 241, 240, 238, 237, 235,
  22. 234, 232, 230, 228, 226, 224, 222, 220,
  23. 218, 215, 213, 211, 208, 206, 203, 201,
  24. 198, 196, 193, 190, 188, 185, 182, 179,
  25. 176, 173, 170, 167, 165, 162, 158, 155,
  26. 152, 149, 146, 143, 140, 137, 134, 131,
  27. 128, 124, 121, 118, 115, 112, 109, 106,
  28. 103, 100, 97, 93, 90, 88, 85, 82,
  29. 79, 76, 73, 70, 67, 65, 62, 59,
  30. 57, 54, 52, 49, 47, 44, 42, 40,
  31. 37, 35, 33, 31, 29, 27, 25, 23,
  32. 21, 20, 18, 17, 15, 14, 12, 11,
  33. 10, 9, 7, 6, 5, 5, 4, 3,
  34. 2, 2, 1, 1, 1, 0, 0, 0,
  35. 0, 0, 0, 0, 1, 1, 1, 2,
  36. 2, 3, 4, 5, 5, 6, 7, 9,
  37. 10, 11, 12, 14, 15, 17, 18, 20,
  38. 21, 23, 25, 27, 29, 31, 33, 35,
  39. 37, 40, 42, 44, 47, 49, 52, 54,
  40. 57, 59, 62, 65, 67, 70, 73, 76,
  41. 79, 82, 85, 88, 90, 93, 97, 100,
  42. 103, 106, 109, 112, 115, 118, 121, 124
  43. };
  44.  
  45. byte sineY256[] PROGMEM = {
  46. 238, 237, 235,
  47. 234, 232, 230, 228, 226, 224, 222, 220,
  48. 218, 215, 213, 211, 208, 206, 203, 201,
  49. 198, 196, 193, 190, 188, 185, 182, 179,
  50. 176, 173, 170, 167, 165, 162, 158, 155,
  51. 152, 149, 146, 143, 140, 137, 134, 131,
  52. 128, 124, 121, 118, 115, 112, 109, 106,
  53. 103, 100, 97, 93, 90, 88, 85, 82,
  54. 79, 76, 73, 70, 67, 65, 62, 59,
  55. 57, 54, 52, 49, 47, 44, 42, 40,
  56. 37, 35, 33, 31, 29, 27, 25, 23,
  57. 21, 20, 18, 17, 15, 14, 12, 11,
  58. 10, 9, 7, 6, 5, 5, 4, 3,
  59. 2, 2, 1, 1, 1, 0, 0, 0,
  60. 0, 0, 0, 0, 1, 1, 1, 2,
  61. 2, 3, 4, 5, 5, 6, 7, 9,
  62. 10, 11, 12, 14, 15, 17, 18, 20,
  63. 21, 23, 25, 27, 29, 31, 33, 35,
  64. 37, 40, 42, 44, 47, 49, 52, 54,
  65. 57, 59, 62, 65, 67, 70, 73, 76,
  66. 79, 82, 85, 88, 90, 93, 97, 100,
  67. 103, 106, 109, 112, 115, 118, 121, 124,
  68. 128, 131, 134, 137, 140, 143, 146, 149,
  69. 152, 155, 158, 162, 165, 167, 170, 173,
  70. 176, 179, 182, 185, 188, 190, 193, 196,
  71. 198, 201, 203, 206, 208, 211, 213, 215,
  72. 218, 220, 222, 224, 226, 228, 230, 232,
  73. 234, 235, 237, 238, 240, 241, 243, 244,
  74. 245, 246, 248, 249, 250, 250, 251, 252,
  75. 253, 253, 254, 254, 254, 255, 255, 255,
  76. 255, 255, 255, 255, 254, 254, 254, 253,
  77. 253, 252, 251, 250, 250, 249, 248, 246,
  78. 245, 244, 243, 241, 240
  79. };
  80.  
  81. byte sineB256[] PROGMEM = {
  82. 18, 17, 15, 14, 12, 11,
  83. 10, 9, 7, 6, 5, 5, 4, 3,
  84. 2, 2, 1, 1, 1, 0, 0, 0,
  85. 0, 0, 0, 0, 1, 1, 1, 2,
  86. 2, 3, 4, 5, 5, 6, 7, 9,
  87. 10, 11, 12, 14, 15, 17, 18, 20,
  88. 21, 23, 25, 27, 29, 31, 33, 35,
  89. 37, 40, 42, 44, 47, 49, 52, 54,
  90. 57, 59, 62, 65, 67, 70, 73, 76,
  91. 79, 82, 85, 88, 90, 93, 97, 100,
  92. 103, 106, 109, 112, 115, 118, 121, 124,
  93. 128, 131, 134, 137, 140, 143, 146, 149,
  94. 152, 155, 158, 162, 165, 167, 170, 173,
  95. 176, 179, 182, 185, 188, 190, 193, 196,
  96. 198, 201, 203, 206, 208, 211, 213, 215,
  97. 218, 220, 222, 224, 226, 228, 230, 232,
  98. 234, 235, 237, 238, 240, 241, 243, 244,
  99. 245, 246, 248, 249, 250, 250, 251, 252,
  100. 253, 253, 254, 254, 254, 255, 255, 255,
  101. 255, 255, 255, 255, 254, 254, 254, 253,
  102. 253, 252, 251, 250, 250, 249, 248, 246,
  103. 245, 244, 243, 241, 240, 238, 237, 235,
  104. 234, 232, 230, 228, 226, 224, 222, 220,
  105. 218, 215, 213, 211, 208, 206, 203, 201,
  106. 198, 196, 193, 190, 188, 185, 182, 179,
  107. 176, 173, 170, 167, 165, 162, 158, 155,
  108. 152, 149, 146, 143, 140, 137, 134, 131,
  109. 128, 124, 121, 118, 115, 112, 109, 106,
  110. 103, 100, 97, 93, 90, 88, 85, 82,
  111. 79, 76, 73, 70, 67, 65, 62, 59,
  112. 57, 54, 52, 49, 47, 44, 42, 40,
  113. 37, 35, 33, 31, 29, 27, 25, 23,
  114. 21, 20
  115. };
  116.  
  117. void setup () {
  118. for (int i = 22; i < 50; i++) //Define Port A,C and L as Output
  119. {
  120. pinMode(i, OUTPUT);
  121. }
  122. //Timer1.initialize(87); //45 Hz
  123. //Timer1.initialize(86); //45.6 Hz
  124. //Timer1.initialize(85); //46.1 Hz
  125. //Timer1.initialize(84); //46.6 Hz
  126. //Timer1.initialize(83); //47.26 Hz
  127. //Timer1.initialize(82); //47.8 Hz
  128. //Timer1.initialize(81); //48.4 Hz
  129. //Timer1.initialize(80); //49.02 Hz
  130. //Timer1.initialize(79); //49.65 Hz
  131. Timer1.initialize(78); //50.3 Hz
  132. //Timer1.initialize(77); //51 Hz
  133. //Timer1.initialize(76); //51.6 Hz
  134. //Timer1.initialize(75); //52.3 Hz
  135. //Timer1.initialize(74); //52.97 Hz
  136. //Timer1.initialize(73); //53.7 Hz
  137. //Timer1.initialize(72); //54.4 Hz
  138. //Timer1.initialize(71);//55.23hz
  139.  
  140. Timer1.attachInterrupt(write_data);
  141. }
  142.  
  143. void loop () {
  144.  
  145. }
  146. void write_data() {
  147. PORTA = pgm_read_byte(sineR256 + i);//Red Phase
  148. PORTC = pgm_read_byte(sineY256 + i);//Blue Phase
  149. PORTL = pgm_read_byte(sineB256 + i);//Yellow Phase
  150. i++;
  151. if (i == 255)
  152. {
  153. i = 0;
  154. }
  155. }
  156.  
  157.  

 

Voltage Sensor

Three Phase Voltage Sensor Module

 

To calculate phasor, the voltage signal is acquired from a transmission line using current transformer (CT) and potential transformer (PT). The signal from CT and PT is provided to the microcontroller unit using Hall Effect voltage sensors (LEM-LV25P).

The schematic of using the voltage sensor for measuring the AC Supply

 

The output of the Hall Effect voltage sensor is bipolar in nature, i.e. it has both positive and negative voltage , which cannot be given to the ADC of the microcontroller, which only takes positive analog signals. Hence a level shifter is used to increase the signal level to all positive.

Schematic of Voltage Offset Adder circuit

 

 

 

Frequency

Frequency Measurement


A sine to square wave converter circuit is used to convert the three phase sine waves into three square waves which are identical in phases and frequency as the sine waves. These square waves are given as interrupts to the microcontroller which calculates the frequencies.

Schematic of the sine to square wave converter

PSU

DC Power Supply Module

The Step Down transformers reduce the voltage level of the AC supply from 230 Volt to in around 17 volt AC. This AC is rectified using diodes or bridge rectifiers. This signal is smoothed out by some Bulk capacitors. Afterwards the voltage regulators generate the regulated DC voltages suitable for the electronics and microcontrollers.

Schematic of Positive DC Power Supply

Schematic of Dual Power Supply

 


 

Arduino code for Temperature Controlled Fan to be attached with the Power Supply Unit

Arduino Code:
  1. #include "OneWire.h"
  2. #include "LiquidCrystal.h"
  3. #include "FreqMeasure.h"
  4.  
  5. OneWire ds(3); // on pin 10 (a 4.7K resistor is necessary)
  6. LiquidCrystal lcd(A5, A4, A3, A2, A1, A0);
  7. int pwm_fan = 6;
  8. void setup(void) {
  9. lcd.begin(16, 2);
  10. Serial.begin(9600);
  11. FreqMeasure.begin();
  12. pinMode(pwm_fan, OUTPUT);
  13. }
  14.  
  15. double sum = 0;
  16. int count = 0;
  17. int rpm = 0;
  18. void loop(void) {
  19. byte i;
  20. byte present = 0;
  21. byte type_s;
  22. byte data[12];
  23. byte addr[8];
  24. float celsius, fahrenheit;
  25.  
  26. if ( !ds.search(addr)) {
  27. Serial.println("No more addresses.");
  28. Serial.println();
  29. ds.reset_search();
  30. delay(250);
  31. return;
  32. }
  33.  
  34. Serial.print("ROM =");
  35. for ( i = 0; i < 8; i++) {
  36. Serial.write(' ');
  37. Serial.print(addr[i], HEX);
  38. }
  39.  
  40. if (OneWire::crc8(addr, 7) != addr[7]) {
  41. Serial.println("CRC is not valid!");
  42. return;
  43. }
  44. Serial.println();
  45.  
  46. // the first ROM byte indicates which chip
  47. switch (addr[0]) {
  48. case 0x10:
  49. Serial.println(" Chip = DS18S20"); // or old DS1820
  50. type_s = 1;
  51. break;
  52. case 0x28:
  53. Serial.println(" Chip = DS18B20");
  54. type_s = 0;
  55. break;
  56. case 0x22:
  57. Serial.println(" Chip = DS1822");
  58. type_s = 0;
  59. break;
  60. default:
  61. Serial.println("Device is not a DS18x20 family device.");
  62. return;
  63. }
  64.  
  65. ds.reset();
  66. ds.select(addr);
  67. ds.write(0x44, 1); // start conversion, with parasite power on at the end
  68.  
  69. delay(1000); // maybe 750ms is enough, maybe not
  70. // we might do a ds.depower() here, but the reset will take care of it.
  71.  
  72. present = ds.reset();
  73. ds.select(addr);
  74. ds.write(0xBE); // Read Scratchpad
  75.  
  76. Serial.print(" Data = ");
  77. Serial.print(present, HEX);
  78. Serial.print(" ");
  79. for ( i = 0; i < 9; i++) { // we need 9 bytes
  80. data[i] = ds.read();
  81. Serial.print(data[i], HEX);
  82. Serial.print(" ");
  83. }
  84. Serial.print(" CRC=");
  85. Serial.print(OneWire::crc8(data, 8), HEX);
  86. Serial.println();
  87.  
  88. // Convert the data to actual temperature
  89. // because the result is a 16 bit signed integer, it should
  90. // be stored to an "int16_t" type, which is always 16 bits
  91. // even when compiled on a 32 bit processor.
  92. int16_t raw = (data[1] << 8) | data[0];
  93. if (type_s) {
  94. raw = raw << 3; // 9 bit resolution default
  95. if (data[7] == 0x10) {
  96. // "count remain" gives full 12 bit resolution
  97. raw = (raw & 0xFFF0) + 12 - data[6];
  98. }
  99. } else {
  100. byte cfg = (data[4] & 0x60);
  101. // at lower res, the low bits are undefined, so let's zero them
  102. if (cfg == 0x00) raw = raw & ~7; // 9 bit resolution, 93.75 ms
  103. else if (cfg == 0x20) raw = raw & ~3; // 10 bit res, 187.5 ms
  104. else if (cfg == 0x40) raw = raw & ~1; // 11 bit res, 375 ms
  105. //// default is 12 bit resolution, 750 ms conversion time
  106. }
  107. celsius = (float)raw / 16.0;
  108. fahrenheit = celsius * 1.8 + 32.0;
  109. Serial.print(" Temperature = ");
  110. Serial.print(celsius);
  111. Serial.print(" Celsius, ");
  112. Serial.print(fahrenheit);
  113. Serial.println(" Fahrenheit");
  114.  
  115. lcd.setCursor(0, 0);
  116. lcd.print("Temp: ");
  117. lcd.print(celsius);
  118. lcd.print(" *C");
  119.  
  120. if (FreqMeasure.available()) {
  121. // average several reading together
  122. sum = sum + FreqMeasure.read();
  123. count = count + 1;
  124. if (count > 15) {
  125. float frequency = FreqMeasure.countToFrequency(sum / count);
  126. rpm = frequency * (60 / 2);
  127. sum = 0;
  128. count = 0;
  129. Serial.print("Fan Speed");
  130. Serial.print(rpm);
  131. Serial.println("RPM");
  132. //rpm = 0;
  133. }
  134. }
  135.  
  136. int fan_speed = map(celsius, 20, 45, 0, 255);
  137. if (fan_speed < 0)
  138. {
  139. fan_speed = 0;
  140. }
  141. else if (fan_speed > 255)
  142. {
  143. fan_speed = 255;
  144. }
  145. else
  146. {}
  147. analogWrite(pwm_fan, fan_speed);
  148.  
  149. int desired_rpm = map(fan_speed, 0, 255, 800, 2200);
  150. lcd.setCursor(0, 1);
  151. lcd.print("Speed: ");
  152. lcd.print(rpm);
  153. lcd.print(" RPM");
  154.  
  155. }

GPSDO

This program is used to generate 3200 pulses per second from the 1 PPS signal from the GPS Module after a successful satellite Fix

Arduino Code:
  1. #include <TimerOne.h>
  2. // use this library to handle the Timer functionality
  3. const int sampling_clock_out_pin = 9;
  4. // the sampling pulses will be generated at this pin
  5. void setup()
  6. {
  7. pinMode(2, INPUT);
  8. // This is the pin where the 1 PPS pulse from GPS module is connected
  9. attachInterrupt(0, pulsePPS, RISING);//watch out for interrupt (1PPS) on pin 2
  10. //Timer1.initialize(400);//for 2500 pulses per sec
  11. Timer1.initialize(312.5);//for 3200 pulses per sec
  12. Timer1.pwm(sampling_clock_out_pin, 100);//duty cycle of the pulse, i.e. about 100uS
  13. }
  14. void loop()
  15. { /* Since the microcontrollers timer operate independently without invoking the CPU, and the Interrupt handlers
  16.   takes care of the ISR, there is nothing to do in the loop*/
  17. }
  18. void pulsePPS() //interrupt routine upon receiving PPS
  19. {
  20. Timer1.restart();
  21. /*Just restart the timer, to keep it in sync with the GPS module's PPS pulses*/
  22. }

Phasors

The following code is used by the Phasor Processor (Arduino Due) to calculate the phasors and transmit them to the Communication Module

Arduino Code:
  1. #include <Time.h> // Time Library
  2. #include <TinyGPS++.h> // GPS Library
  3. #include <math.h> // Math functions library
  4.  
  5. static const uint32_t GPSBaud = 38400;
  6. boolean Calculate_A_Phasor = false;
  7. boolean get_time_on_pps = false;
  8. // The TinyGPS++ object
  9. TinyGPSPlus gps;
  10.  
  11. // Serial connection to the GPS device
  12. #define Serial_GPS Serial3
  13. #define SerialTx Serial2
  14. //#define SerialTx Serial
  15.  
  16. time_t prevDisplay = 0; // Count for when time last displayed
  17. int Year;
  18. byte Month;
  19. byte Day;
  20. byte Hour;
  21. byte Minute;
  22. byte Second;
  23.  
  24. //Phasor Estimation Variable Declaration
  25. #define WindowSize 64 //i.e. 64 samples per second
  26. int N = WindowSize; //Sampling frequency 3200 Hz
  27. long double pi = 3.143;
  28.  
  29. long double adc_out_1[WindowSize], values_1[WindowSize];
  30. long double adc_out_2[WindowSize], values_2[WindowSize];
  31. long double adc_out_3[WindowSize], values_3[WindowSize];
  32.  
  33. long double Xi_1, Xr_1, Phasor_Magnitude_1, Phasor_Angle_1, Phasor_Angle_Degree_1;
  34. long double Xi_2, Xr_2, Phasor_Magnitude_2, Phasor_Angle_2, Phasor_Angle_Degree_2;
  35. long double Xi_3, Xr_3, Phasor_Magnitude_3, Phasor_Angle_3, Phasor_Angle_Degree_3;
  36.  
  37. unsigned long int calculation_start_millis;
  38. unsigned long int calculation_finish_millis;
  39.  
  40. unsigned long int pps_time_millis;
  41. unsigned long int phasor_stamp_millis;
  42.  
  43. //variables for frequency calculation
  44. volatile long double P1_start_micros = 0, last_P1_start_micros = 0, P1_period = 0;
  45. volatile long double P2_start_micros = 0, last_P2_start_micros = 0, P2_period = 0;
  46. volatile long double P3_start_micros = 0, last_P3_start_micros = 0, P3_period = 0;
  47.  
  48. int P1_freq, P2_freq, P3_freq;
  49. long double P1_freqf, P2_freqf, P3_freqf;
  50. int P1_lf, P2_lf, P3_lf;// last frequencies
  51. int P1_rocof, P2_rocof, P3_rocof;// rate of change of frequency df/dt
  52. long double P1_rocoff, P2_rocoff, P3_rocoff;
  53.  
  54. void setup()
  55. { adc_setup();
  56. SerialTx.begin(921600);//for transmitting Phasors
  57. Serial_GPS.begin(GPSBaud); // Start GPS Serial Connection
  58. smartDelay(1000);
  59. delay(2000);
  60. analogReadResolution(12);
  61. attachInterrupt(22, aquire, RISING);// aquire voltage samples
  62. attachInterrupt(23, attach_pps_time, RISING);// get pulse per second time
  63. attachInterrupt(31, capture_P1_start, RISING);// get starting time of P1 waveform
  64. attachInterrupt(33, capture_P2_start, RISING);// """""""""""""""""""" P2 waveform
  65. attachInterrupt(29, capture_P3_start, RISING);// """""""""""""""""""" P3 waveform
  66. }
  67. // Get start Time of waves for calculation of frequency
  68. void capture_P1_start() {
  69. P1_start_micros = micros();
  70. P1_period = P1_start_micros - last_P1_start_micros;
  71. last_P1_start_micros = P1_start_micros;
  72. }
  73. void capture_P2_start() {
  74. P2_start_micros = micros();
  75. P2_period = P2_start_micros - last_P2_start_micros;
  76. last_P2_start_micros = P2_start_micros;
  77. }
  78. void capture_P3_start() {
  79. P3_start_micros = micros();
  80. P3_period = P3_start_micros - last_P3_start_micros;
  81. last_P3_start_micros = P3_start_micros;
  82. }
  83. // Circular buffer, power of two.
  84. #define BUFSIZE 0x40 //64 samples buffer
  85. #define BUFMASK 0x3F
  86. volatile int R [BUFSIZE] ;
  87. volatile int Y [BUFSIZE] ;
  88. volatile int B [BUFSIZE] ;
  89. volatile int sptr = 0 ;
  90. volatile int isr_count = 0 ;
  91.  
  92. void aquire() {
  93. ADC->ADC_CR |= 0b10; //start conversion
  94. while (!(ADC->ADC_ISR & 0b11100000)); //wait for conversion to end
  95. int Rval = ADC->ADC_CDR[7];
  96. int Yval = ADC->ADC_CDR[6];
  97. int Bval = ADC->ADC_CDR[5];
  98.  
  99. R[sptr] = Rval;
  100. Y[sptr] = Yval;
  101. B[sptr] = Bval;
  102. sptr = (sptr + 1) & BUFMASK;
  103. isr_count ++ ;
  104. }
  105.  
  106. void adc_setup()
  107. {
  108. //ADC setup
  109. ADC->ADC_WPMR &= 0xFFFE; //disable write protect
  110. ADC->ADC_CHER = 0b11100000; //Enable AD7,AD6,AD5 or CH7,Ch6,Ch5 or PA16,PA24,PA23 or A0,A1 and A2 |
  111. ADC->ADC_MR &= 0b11111111000000000000011100000000;//Fast i.e. about 4mS for 2500 Conversions on three channels
  112. ADC->ADC_MR |= 0b00000000000100100000000000000000; //software trigger, hi res, no sleep, not free running
  113. ADC->ADC_IER = 0b11100000; //enable interrupt
  114. ADC->ADC_IMR = 0b11100000; //enable interrupt in mask
  115. ADC->ADC_CR |= 0b10; //start first conversion
  116. }
  117.  
  118. void loop()
  119. {
  120. if (get_time_on_pps == true)
  121. {
  122. pps_time_millis = millis();
  123. GPS_Timezone_Adjust(); // Call Time Adjust Function
  124. get_time_on_pps = false;
  125. }
  126. if (isr_count == 64)
  127. {
  128. Calculate_A_Phasor = true;
  129. isr_count = 0;
  130. }
  131. if (Calculate_A_Phasor == true)
  132. {
  133. calc_phasor();
  134. transmit_phasors_on_SerialTx();
  135. Calculate_A_Phasor = false;
  136. }
  137. smartDelay(0);
  138. }
  139.  
  140. void attach_pps_time()
  141. {
  142. get_time_on_pps = true;
  143. }
  144.  
  145. void GPS_Timezone_Adjust() {
  146.  
  147. Year = gps.date.year();
  148. Month = gps.date.month();
  149. Day = gps.date.day();
  150. Hour = gps.time.hour();
  151. Minute = gps.time.minute();
  152. Second = gps.time.second();
  153.  
  154. // Set Time from GPS data string
  155. setTime(Hour, Minute, Second, Day, Month, Year);
  156. // Calc current Time Zone time by offset value
  157.  
  158. if (timeStatus() != timeNotSet) {
  159. if (now() != prevDisplay) {
  160. prevDisplay = now();
  161. }
  162. }
  163. smartDelay(0);
  164. }
  165.  
  166. static void smartDelay(unsigned long ms)
  167. {
  168. unsigned long start = millis();
  169. do
  170. {
  171. while (Serial_GPS.available())
  172. gps.encode(Serial_GPS.read());
  173. } while (millis() - start < ms);
  174. }
  175.  
  176. //Phasor calculation function
  177. void calc_phasor() {
  178. //copy buffer to SampleWindow for calculation
  179. for (int i = 0; i < 64; i++)
  180. {
  181. adc_out_1[i] = R[i];
  182. adc_out_2[i] = Y[i];
  183. adc_out_3[i] = B[i];
  184. }
  185. calculation_start_millis = millis();
  186. for (int i = 0; i < N; i++)
  187. {
  188. values_1[i] = map_double(adc_out_1[i], 1433, 2812, -347.25, 347.25);// Phase A
  189. values_2[i] = map_double(adc_out_2[i], 1498, 2859, -336.78, 336.78);// Phase B
  190. values_3[i] = map_double(adc_out_3[i], 1408, 2851, -344.70, 344.70);// Phase C
  191. smartDelay(0);
  192. }
  193.  
  194. //Calculate 64-Point DFT
  195. Xr_1 = 0; Xr_2 = 0; Xr_3 = 0;
  196. Xi_1 = 0; Xi_2 = 0; Xi_3 = 0;
  197. Phasor_Magnitude_1 = 0; Phasor_Magnitude_2 = 0; Phasor_Magnitude_3 = 0;
  198. Phasor_Angle_1 = 0; Phasor_Angle_2 = 0; Phasor_Angle_3 = 0;
  199. for (int n = 0; n < N; n++)
  200. {
  201. Xr_1 = Xr_1 + values_1[n] * cos((2 * pi * n) / N);
  202. Xi_1 = Xi_1 + values_1[n] * sin((2 * pi * n) / N);
  203.  
  204. Xr_2 = Xr_2 + values_2[n] * cos((2 * pi * n) / N);
  205. Xi_2 = Xi_2 + values_2[n] * sin((2 * pi * n) / N);
  206.  
  207. Xr_3 = Xr_3 + values_3[n] * cos((2 * pi * n) / N);
  208. Xi_3 = Xi_3 + values_3[n] * sin((2 * pi * n) / N);
  209. smartDelay(0);
  210. }
  211.  
  212. Xr_1 = (sqrt(2) / N) * Xr_1;
  213. Xr_2 = (sqrt(2) / N) * Xr_2;
  214. Xr_3 = (sqrt(2) / N) * Xr_3;
  215.  
  216. Xi_1 = -(sqrt(2) / N) * Xi_1;
  217. Xi_2 = -(sqrt(2) / N) * Xi_2;
  218. Xi_3 = -(sqrt(2) / N) * Xi_3;
  219.  
  220. Phasor_Magnitude_1 = sqrt(Xr_1 * Xr_1 + Xi_1 * Xi_1);
  221. Phasor_Magnitude_2 = sqrt(Xr_2 * Xr_2 + Xi_2 * Xi_2);
  222. Phasor_Magnitude_3 = sqrt(Xr_3 * Xr_3 + Xi_3 * Xi_3);
  223.  
  224. Phasor_Angle_1 = atan2(Xi_1, Xr_1); //double atan2(double y, double x)
  225. Phasor_Angle_2 = atan2(Xi_2, Xr_2); //The atan2() function returns the arc tangent of y/x, in the range [-pi, +pi] radians.
  226. Phasor_Angle_3 = atan2(Xi_3, Xr_3);
  227.  
  228. //Calculate Phasor Angle in Degree
  229. Phasor_Angle_Degree_1 = (Phasor_Angle_1 * 4068) / 71;
  230. Phasor_Angle_Degree_2 = (Phasor_Angle_2 * 4068) / 71;
  231. Phasor_Angle_Degree_3 = (Phasor_Angle_3 * 4068) / 71;
  232. //
  233. calculation_finish_millis = millis();
  234. phasor_stamp_millis = calculation_start_millis - pps_time_millis;
  235.  
  236. // Calculate frequency
  237. //long int P1_period = P1_end_micros - P1_start_micros;
  238. P1_freqf = 1000000 / P1_period;
  239. P2_freqf = 1000000 / P2_period;
  240. P3_freqf = 1000000 / P3_period;
  241.  
  242. P1_rocoff = sqrt((P1_freqf - 50.00) * (P1_freqf - 50.00)) * 50;
  243. P2_rocoff = sqrt((P2_freqf - 50.00) * (P2_freqf - 50.00)) * 50;
  244. P3_rocoff = sqrt((P3_freqf - 50.00) * (P3_freqf - 50.00)) * 50;
  245.  
  246. smartDelay(0);
  247. }
  248.  
  249. float map_double(double x, double in_min, double in_max, double out_min, double out_max)
  250. {
  251. return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
  252. smartDelay(0);
  253. }
  254. void transmit_phasors_on_SerialTx() {
  255. SerialTx.write('!');
  256. SerialTx.print(int(Phasor_Magnitude_1 * 100));
  257. SerialTx.write('"');
  258. SerialTx.print(int(Phasor_Magnitude_2 * 100));
  259. SerialTx.write('#');
  260. SerialTx.print(int(Phasor_Magnitude_3 * 100));
  261. SerialTx.write('$');
  262.  
  263. //Calculate angle i.e. 2pi's complement to be sent
  264. if (Phasor_Angle_1 < 0)
  265. Phasor_Angle_1 = Phasor_Angle_1 + 6.286;
  266. if (Phasor_Angle_2 < 0)
  267. Phasor_Angle_2 = Phasor_Angle_2 + 6.286;
  268. if (Phasor_Angle_3 < 0)
  269. Phasor_Angle_3 = Phasor_Angle_3 + 6.286;
  270.  
  271. SerialTx.print(int(Phasor_Angle_1 * 1000));
  272. SerialTx.write('%');
  273. SerialTx.print(int(Phasor_Angle_2 * 1000));
  274. SerialTx.write('&');
  275. SerialTx.print(int(Phasor_Angle_3 * 1000));
  276. SerialTx.write('(');
  277.  
  278. SerialTx.print(day());
  279. SerialTx.write(')');
  280. SerialTx.print(month());
  281. SerialTx.write('*');
  282. SerialTx.print(year());
  283.  
  284. SerialTx.write('+');
  285. SerialTx.print(hour());
  286. SerialTx.write(',');
  287. SerialTx.print(minute());
  288. SerialTx.write('-');
  289. SerialTx.print(second());
  290. SerialTx.write('.');
  291. SerialTx.print(phasor_stamp_millis);
  292. SerialTx.write('/');
  293.  
  294. float Latitude = (gps.location.lat());
  295. float Longitude = (gps.location.lng());
  296. SerialTx.print(int(Latitude * 1000));
  297. SerialTx.write(':');
  298. SerialTx.print(int(Longitude * 1000));
  299. SerialTx.write(';');
  300.  
  301. //Transmit Frequencies
  302. SerialTx.print(int(P1_freqf * 100));
  303. SerialTx.write('@');
  304. SerialTx.print(int(P2_freqf * 100));
  305. SerialTx.write('^');
  306. SerialTx.print(int(P3_freqf * 100));
  307. SerialTx.write('?');
  308.  
  309. //Transmit ROCOF
  310. SerialTx.print(int(P1_rocoff * 100));
  311. SerialTx.write('[');
  312. SerialTx.print(int(P2_rocoff * 100));
  313. SerialTx.write(']');
  314. SerialTx.print(int(P3_rocoff * 100));
  315. SerialTx.write('|');
  316.  
  317. }

Commu

The Communication Module with Arduino Due which Transmits the Data received from the Phasor Processor, to the Display Module (LCD) and to the Computer(Which has the Real-time GUI Running)

Arduino Code:
  1. #define IDLE 0
  2. #define RECEIVING1 1
  3. #define RECEIVING2 2
  4. #define RECEIVING3 3
  5. #define RECEIVING4 4
  6. #define RECEIVING5 5
  7. #define RECEIVING6 6
  8. #define RECEIVING7 7
  9. #define RECEIVING8 8
  10. #define RECEIVING9 9
  11. #define RECEIVING10 10
  12. #define RECEIVING11 11
  13. #define RECEIVING12 12
  14. #define RECEIVING13 13
  15. #define RECEIVING14 14
  16. #define RECEIVING15 15
  17. #define RECEIVING16 16
  18. #define RECEIVING17 17
  19. #define RECEIVING18 18
  20. #define RECEIVING19 19
  21. #define RECEIVING20 20
  22. #define RECEIVING21 21
  23.  
  24. int Year, Month, Day;
  25. int Hour, Minute, Second, MilliSecond;
  26.  
  27. float Phasor_Magnitude_1, Phasor_Magnitude_2, Phasor_Magnitude_3;
  28.  
  29. float Phasor_Angle_1, Phasor_Angle_Degree_1;
  30. float Phasor_Angle_2, Phasor_Angle_Degree_2;
  31. float Phasor_Angle_3, Phasor_Angle_Degree_3;
  32.  
  33. float Latitude;
  34. float Longitude;
  35.  
  36. float P1_freq, P2_freq, P3_freq;
  37. float P1_rocof, P2_rocof, P3_rocof;
  38.  
  39. byte status = IDLE;
  40. #define SerialRx Serial2
  41. #define SerialLCD Serial3
  42. void setup() {
  43. SerialLCD.begin(921600);
  44. SerialRx.begin(921600);
  45. Serial.begin(460800);
  46. //Serial.println("Ready");
  47. }
  48. int count = 0;
  49. void loop() {
  50.  
  51. if (SerialRx.available()) {
  52. int c = SerialRx.read();
  53. if (status == RECEIVING1 && c >= '0' && c <= '9') {
  54. Phasor_Magnitude_1 = Phasor_Magnitude_1 * 10 + (c - '0');
  55. } else if (status == RECEIVING2 && c >= '0' && c <= '9') {
  56. Phasor_Magnitude_2 = Phasor_Magnitude_2 * 10 + (c - '0');
  57. } else if (status == RECEIVING3 && c >= '0' && c <= '9') {
  58. Phasor_Magnitude_3 = Phasor_Magnitude_3 * 10 + (c - '0');
  59. } else if (status == RECEIVING4 && c >= '0' && c <= '9') {
  60. Phasor_Angle_1 = Phasor_Angle_1 * 10 + (c - '0');
  61. } else if (status == RECEIVING5 && c >= '0' && c <= '9') {
  62. Phasor_Angle_2 = Phasor_Angle_2 * 10 + (c - '0');
  63. } else if (status == RECEIVING6 && c >= '0' && c <= '9') {
  64. Phasor_Angle_3 = Phasor_Angle_3 * 10 + (c - '0');
  65. } else if (status == RECEIVING7 && c >= '0' && c <= '9') {
  66. Day = Day * 10 + (c - '0');
  67. } else if (status == RECEIVING8 && c >= '0' && c <= '9') {
  68. Month = Month * 10 + (c - '0');
  69. } else if (status == RECEIVING9 && c >= '0' && c <= '9') {
  70. Year = Year * 10 + (c - '0');
  71. } else if (status == RECEIVING10 && c >= '0' && c <= '9') {
  72. Hour = Hour * 10 + (c - '0');
  73. } else if (status == RECEIVING11 && c >= '0' && c <= '9') {
  74. Minute = Minute * 10 + (c - '0');
  75. } else if (status == RECEIVING12 && c >= '0' && c <= '9') {
  76. Second = Second * 10 + (c - '0');
  77. } else if (status == RECEIVING13 && c >= '0' && c <= '9') {
  78. MilliSecond = MilliSecond * 10 + (c - '0');
  79. } else if (status == RECEIVING14 && c >= '0' && c <= '9') {
  80. Latitude = Latitude * 10 + (c - '0');
  81. } else if (status == RECEIVING15 && c >= '0' && c <= '9') {
  82. Longitude = Longitude * 10 + (c - '0');
  83. } else if (status == RECEIVING16 && c >= '0' && c <= '9') {
  84. P1_freq = P1_freq * 10 + (c - '0');
  85. } else if (status == RECEIVING17 && c >= '0' && c <= '9') {
  86. P2_freq = P2_freq * 10 + (c - '0');
  87. } else if (status == RECEIVING18 && c >= '0' && c <= '9') {
  88. P3_freq = P3_freq * 10 + (c - '0');
  89. } else if (status == RECEIVING19 && c >= '0' && c <= '9') {
  90. P1_rocof = P1_rocof * 10 + (c - '0');
  91. } else if (status == RECEIVING20 && c >= '0' && c <= '9') {
  92. P2_rocof = P2_rocof * 10 + (c - '0');
  93. } else if (status == RECEIVING21 && c >= '0' && c <= '9') {
  94. P3_rocof = P3_rocof * 10 + (c - '0');
  95. }
  96.  
  97. else if (status == RECEIVING1 && c == '"') {
  98. status = RECEIVING2;
  99. } else if (status == RECEIVING2 && c == '#') {
  100. status = RECEIVING3;
  101. } else if (status == RECEIVING3 && c == '$') {
  102. status = RECEIVING4;
  103. } else if (status == RECEIVING4 && c == '%') {
  104. status = RECEIVING5;
  105. } else if (status == RECEIVING5 && c == '&') {
  106. status = RECEIVING6;
  107. } else if (status == RECEIVING6 && c == '(') {
  108. status = RECEIVING7;
  109. } else if (status == RECEIVING7 && c == ')') {
  110. status = RECEIVING8;
  111. } else if (status == RECEIVING8 && c == '*') {
  112. status = RECEIVING9;
  113. } else if (status == RECEIVING9 && c == '+') {
  114. status = RECEIVING10;
  115. } else if (status == RECEIVING10 && c == ',') {
  116. status = RECEIVING11;
  117. } else if (status == RECEIVING11 && c == '-') {
  118. status = RECEIVING12;
  119. } else if (status == RECEIVING12 && c == '.') {
  120. status = RECEIVING13;
  121. } else if (status == RECEIVING13 && c == '/') {
  122. status = RECEIVING14;
  123. } else if (status == RECEIVING14 && c == ':') {
  124. status = RECEIVING15;
  125. } else if (status == RECEIVING15 && c == ';') {
  126. status = RECEIVING16;
  127. } else if (status == RECEIVING16 && c == '@') {
  128. status = RECEIVING17;
  129. } else if (status == RECEIVING17 && c == '^') {
  130. status = RECEIVING18;
  131. } else if (status == RECEIVING18 && c == '?') {
  132. status = RECEIVING19;
  133. } else if (status == RECEIVING19 && c == '[') {
  134. status = RECEIVING20;
  135. } else if (status == RECEIVING20 && c == ']') {
  136. status = RECEIVING21;
  137. } else if (c == '|') {
  138. status = IDLE;
  139.  
  140. //Remote value Received completely, Now display it
  141.  
  142. //Calculate Phasor Angles into Float
  143. Phasor_Angle_1 = Phasor_Angle_1 / 1000;
  144. Phasor_Angle_2 = Phasor_Angle_2 / 1000;
  145. Phasor_Angle_3 = Phasor_Angle_3 / 1000;
  146.  
  147. //Calculate Phasor Angle using reverse 2pi's complement
  148. if (Phasor_Angle_1 > 3.143)
  149. Phasor_Angle_1 = Phasor_Angle_1 - 6.286;
  150. if (Phasor_Angle_2 > 3.143)
  151. Phasor_Angle_2 = Phasor_Angle_2 - 6.286;
  152. if (Phasor_Angle_3 > 3.143)
  153. Phasor_Angle_3 = Phasor_Angle_3 - 6.286;
  154.  
  155. //Calculate Angles in Degrees
  156. Phasor_Angle_Degree_1 = (Phasor_Angle_1 * 4068) / 71;
  157. Phasor_Angle_Degree_2 = (Phasor_Angle_2 * 4068) / 71;
  158. Phasor_Angle_Degree_3 = (Phasor_Angle_3 * 4068) / 71;
  159.  
  160. Display_Phasors_on_Serial_Terminal();
  161. transmit_phasors_LCD();
  162.  
  163. } else if (c == '!') {
  164. status = RECEIVING1;
  165. //Reset the variables to Zero
  166. Year = 0;
  167. Month = 0;
  168. Day = 0;
  169. Hour = 0;
  170. Minute = 0;
  171. Second = 0;
  172. MilliSecond = 0;
  173. Phasor_Angle_1 = 0;
  174. Phasor_Angle_2 = 0;
  175. Phasor_Angle_3 = 0;
  176. Phasor_Magnitude_1 = 0;
  177. Phasor_Magnitude_2 = 0;
  178. Phasor_Magnitude_3 = 0;
  179. Phasor_Angle_Degree_1 = 0;
  180. Phasor_Angle_Degree_2 = 0;
  181. Phasor_Angle_Degree_3 = 0;
  182. Latitude = 0;
  183. Longitude = 0;
  184. P1_freq = 0;
  185. P2_freq = 0;
  186. P3_freq = 0;
  187. P1_rocof = 0;
  188. P2_rocof = 0;
  189. P3_rocof = 0;
  190. }
  191. }
  192. }
  193. // Transmit the phasors to local PDC, where it can be plotted in real-time
  194. void Display_Phasors_on_Serial_Terminal() {
  195. //Serial.print(millis());
  196. Serial.print(Day);
  197. Serial.print(" ");
  198. Serial.print(Month);
  199. Serial.print(" ");
  200. Serial.print(Year);
  201. Serial.print(" ");
  202. Serial.print(Hour);
  203. Serial.print(" ");
  204. Serial.print(Minute);
  205. Serial.print(" ");
  206. Serial.print(Second);
  207. Serial.print(" ");
  208. Serial.print(MilliSecond);
  209. Serial.print(" ");
  210. Serial.print(float(Phasor_Magnitude_1 / 100));
  211. Serial.print(" ");
  212. Serial.print(Phasor_Angle_Degree_1);
  213. Serial.print(" ");
  214. Serial.print(float(Phasor_Magnitude_2 / 100));
  215. Serial.print(" ");
  216. Serial.print(Phasor_Angle_Degree_2);
  217. Serial.print(" ");
  218. Serial.print(float(Phasor_Magnitude_3 / 100));
  219. Serial.print(" ");
  220. Serial.print(Phasor_Angle_Degree_3);
  221. Serial.print(" ");
  222. Serial.print(P1_freq / 100);
  223. Serial.print(" ");
  224. Serial.print(P2_freq / 100);
  225. Serial.print(" ");
  226. Serial.print(P3_freq / 100);
  227. Serial.print(" ");
  228. Serial.print(P1_rocof / 100);
  229. Serial.print(" ");
  230. Serial.print(P2_rocof / 100);
  231. Serial.print(" ");
  232. Serial.print(P3_rocof / 100);
  233. Serial.print("\n");
  234. }
  235.  
  236. // Transmit the Phasor parameters to LCD Module
  237. void transmit_phasors_LCD() {
  238. SerialLCD.write('!');
  239. SerialLCD.print(int(Phasor_Magnitude_1));
  240. SerialLCD.write('"');
  241. SerialLCD.print(int(Phasor_Magnitude_2));
  242. SerialLCD.write('#');
  243. SerialLCD.print(int(Phasor_Magnitude_3));
  244. SerialLCD.write('$');
  245.  
  246. //Calculate angle i.e. 2pi's complement to be sent
  247. if (Phasor_Angle_1 < 0)
  248. Phasor_Angle_1 = Phasor_Angle_1 + 6.286;
  249. if (Phasor_Angle_2 < 0)
  250. Phasor_Angle_2 = Phasor_Angle_2 + 6.286;
  251. if (Phasor_Angle_3 < 0)
  252. Phasor_Angle_3 = Phasor_Angle_3 + 6.286;
  253. SerialLCD.print(int(Phasor_Angle_1 * 100));
  254. SerialLCD.write('%');
  255. SerialLCD.print(int(Phasor_Angle_2 * 100));
  256. SerialLCD.write('&');
  257. SerialLCD.print(int(Phasor_Angle_3 * 100));
  258. SerialLCD.write('(');
  259.  
  260. SerialLCD.print(Day);
  261. SerialLCD.write(')');
  262. SerialLCD.print(Month);
  263. SerialLCD.write('*');
  264. SerialLCD.print(Year);
  265.  
  266. SerialLCD.write('+');
  267. SerialLCD.print(Hour);
  268. SerialLCD.write(',');
  269. SerialLCD.print(Minute);
  270. SerialLCD.write('-');
  271. SerialLCD.print(Second);
  272. SerialLCD.write('.');
  273. SerialLCD.print(MilliSecond);
  274. SerialLCD.write('/');
  275.  
  276. SerialLCD.print(int(Latitude));
  277. SerialLCD.write(':');
  278. SerialLCD.print(int(Longitude));
  279. SerialLCD.write(';');
  280.  
  281. //Transmit Frequencies
  282. SerialLCD.print(P1_freq);
  283. SerialLCD.write('@');
  284. SerialLCD.print(P2_freq);
  285. SerialLCD.write('^');
  286. SerialLCD.print(P3_freq);
  287. SerialLCD.write('?');
  288.  
  289. //Transmit ROCOF
  290. SerialLCD.print(P1_rocof);
  291. SerialLCD.write('[');
  292. SerialLCD.print(P2_rocof);
  293. SerialLCD.write(']');
  294. SerialLCD.print(P3_rocof);
  295. SerialLCD.write('|');
  296. }

LCD

The LCD Displays the phasor Information locally. An Arduino Due with a 7 inch UTFT LCD Module is used as the Display Unit.

Arduino Code:
  1. #include <UTFT.h>
  2. extern uint8_t Grotesk32x64[];// Declare which fonts we will be using
  3. extern uint8_t Ubuntubold[];
  4. extern uint8_t Ubuntu[];
  5. extern uint8_t franklingothic_normal[];
  6. extern uint8_t Inconsola[];
  7. extern uint8_t BigFont[];// Declare which fonts we will be using
  8.  
  9. UTFT PMU_LCD(CTE70, 25, 26, 27, 28);
  10. bool display_now_on_lcd = false;
  11.  
  12. #define IDLE 0
  13. #define RECEIVING1 1
  14. #define RECEIVING2 2
  15. #define RECEIVING3 3
  16. #define RECEIVING4 4
  17. #define RECEIVING5 5
  18. #define RECEIVING6 6
  19. #define RECEIVING7 7
  20. #define RECEIVING8 8
  21. #define RECEIVING9 9
  22. #define RECEIVING10 10
  23. #define RECEIVING11 11
  24. #define RECEIVING12 12
  25. #define RECEIVING13 13
  26. #define RECEIVING14 14
  27. #define RECEIVING15 15
  28. #define RECEIVING16 16
  29. #define RECEIVING17 17
  30. #define RECEIVING18 18
  31. #define RECEIVING19 19
  32. #define RECEIVING20 20
  33. #define RECEIVING21 21
  34.  
  35. int Year, Month, Day;
  36. int Hour, Minute, Second, MilliSecond;
  37.  
  38. float Phasor_Magnitude_1;
  39. float Phasor_Magnitude_2;
  40. float Phasor_Magnitude_3;
  41.  
  42. float Phasor_Angle_1, Phasor_Angle_Degree_1;
  43. float Phasor_Angle_2, Phasor_Angle_Degree_2;
  44. float Phasor_Angle_3, Phasor_Angle_Degree_3;
  45.  
  46. float Latitude;
  47. float Longitude;
  48.  
  49. float P1_freq, P2_freq, P3_freq;
  50. float P1_rocof, P2_rocof, P3_rocof;
  51.  
  52. byte status = IDLE;
  53.  
  54. void setup() {
  55. // Setup the LCD
  56. PMU_LCD.InitLCD();
  57.  
  58. init_LCD();
  59. Serial3.begin(921600);
  60. attachInterrupt(8, display_on_lcd, RISING);
  61.  
  62. }
  63. int count = 0;
  64. void loop() {
  65. // put your main code here, to run repeatedly:
  66. if (Serial3.available()) {
  67. int c = Serial3.read();
  68. if (status == RECEIVING1 && c >= '0' && c <= '9') {
  69. Phasor_Magnitude_1 = Phasor_Magnitude_1 * 10 + (c - '0');
  70. } else if (status == RECEIVING2 && c >= '0' && c <= '9') {
  71. Phasor_Magnitude_2 = Phasor_Magnitude_2 * 10 + (c - '0');
  72. } else if (status == RECEIVING3 && c >= '0' && c <= '9') {
  73. Phasor_Magnitude_3 = Phasor_Magnitude_3 * 10 + (c - '0');
  74. } else if (status == RECEIVING4 && c >= '0' && c <= '9') {
  75. Phasor_Angle_1 = Phasor_Angle_1 * 10 + (c - '0');
  76. } else if (status == RECEIVING5 && c >= '0' && c <= '9') {
  77. Phasor_Angle_2 = Phasor_Angle_2 * 10 + (c - '0');
  78. } else if (status == RECEIVING6 && c >= '0' && c <= '9') {
  79. Phasor_Angle_3 = Phasor_Angle_3 * 10 + (c - '0');
  80. } else if (status == RECEIVING7 && c >= '0' && c <= '9') {
  81. Day = Day * 10 + (c - '0');
  82. } else if (status == RECEIVING8 && c >= '0' && c <= '9') {
  83. Month = Month * 10 + (c - '0');
  84. } else if (status == RECEIVING9 && c >= '0' && c <= '9') {
  85. Year = Year * 10 + (c - '0');
  86. } else if (status == RECEIVING10 && c >= '0' && c <= '9') {
  87. Hour = Hour * 10 + (c - '0');
  88. } else if (status == RECEIVING11 && c >= '0' && c <= '9') {
  89. Minute = Minute * 10 + (c - '0');
  90. } else if (status == RECEIVING12 && c >= '0' && c <= '9') {
  91. Second = Second * 10 + (c - '0');
  92. } else if (status == RECEIVING13 && c >= '0' && c <= '9') {
  93. MilliSecond = MilliSecond * 10 + (c - '0');
  94. } else if (status == RECEIVING14 && c >= '0' && c <= '9') {
  95. Latitude = Latitude * 10 + (c - '0');
  96. } else if (status == RECEIVING15 && c >= '0' && c <= '9') {
  97. Longitude = Longitude * 10 + (c - '0');
  98. } else if (status == RECEIVING16 && c >= '0' && c <= '9') {
  99. P1_freq = P1_freq * 10 + (c - '0');
  100. } else if (status == RECEIVING17 && c >= '0' && c <= '9') {
  101. P2_freq = P2_freq * 10 + (c - '0');
  102. } else if (status == RECEIVING18 && c >= '0' && c <= '9') {
  103. P3_freq = P3_freq * 10 + (c - '0');
  104. } else if (status == RECEIVING19 && c >= '0' && c <= '9') {
  105. P1_rocof = P1_rocof * 10 + (c - '0');
  106. } else if (status == RECEIVING20 && c >= '0' && c <= '9') {
  107. P2_rocof = P2_rocof * 10 + (c - '0');
  108. } else if (status == RECEIVING21 && c >= '0' && c <= '9') {
  109. P3_rocof = P3_rocof * 10 + (c - '0');
  110. }
  111. else if (status == RECEIVING1 && c == '"') {
  112. status = RECEIVING2;
  113. } else if (status == RECEIVING2 && c == '#') {
  114. status = RECEIVING3;
  115. } else if (status == RECEIVING3 && c == '$') {
  116. status = RECEIVING4;
  117. } else if (status == RECEIVING4 && c == '%') {
  118. status = RECEIVING5;
  119. } else if (status == RECEIVING5 && c == '&') {
  120. status = RECEIVING6;
  121. } else if (status == RECEIVING6 && c == '(') {
  122. status = RECEIVING7;
  123. } else if (status == RECEIVING7 && c == ')') {
  124. status = RECEIVING8;
  125. } else if (status == RECEIVING8 && c == '*') {
  126. status = RECEIVING9;
  127. } else if (status == RECEIVING9 && c == '+') {
  128. status = RECEIVING10;
  129. } else if (status == RECEIVING10 && c == ',') {
  130. status = RECEIVING11;
  131. } else if (status == RECEIVING11 && c == '-') {
  132. status = RECEIVING12;
  133. } else if (status == RECEIVING12 && c == '.') {
  134. status = RECEIVING13;
  135. } else if (status == RECEIVING13 && c == '/') {
  136. status = RECEIVING14;
  137. } else if (status == RECEIVING14 && c == ':') {
  138. status = RECEIVING15;
  139. } else if (status == RECEIVING15 && c == ';') {
  140. status = RECEIVING16;
  141. } else if (status == RECEIVING16 && c == '@') {
  142. status = RECEIVING17;
  143. } else if (status == RECEIVING17 && c == '^') {
  144. status = RECEIVING18;
  145. } else if (status == RECEIVING18 && c == '?') {
  146. status = RECEIVING19;
  147. } else if (status == RECEIVING19 && c == '[') {
  148. status = RECEIVING20;
  149. } else if (status == RECEIVING20 && c == ']') {
  150. status = RECEIVING21;
  151. } else if (c == '|') {
  152. status = IDLE;
  153.  
  154. //Remote value Received completely, Now display it
  155.  
  156. //Calculate Phasor Angles into Float
  157. Phasor_Angle_1 = Phasor_Angle_1 / 100;
  158. Phasor_Angle_2 = Phasor_Angle_2 / 100;
  159. Phasor_Angle_3 = Phasor_Angle_3 / 100;
  160.  
  161. //Calculate Phasor Angle using reverse 2pi's complement
  162. if (Phasor_Angle_1 > 3.143)
  163. Phasor_Angle_1 = Phasor_Angle_1 - 6.286;
  164. if (Phasor_Angle_2 > 3.143)
  165. Phasor_Angle_2 = Phasor_Angle_2 - 6.286;
  166. if (Phasor_Angle_3 > 3.143)
  167. Phasor_Angle_3 = Phasor_Angle_3 - 6.286;
  168.  
  169. //Calculate Angles in Degrees
  170. Phasor_Angle_Degree_1 = (Phasor_Angle_1 * 4068) / 71;
  171. Phasor_Angle_Degree_2 = (Phasor_Angle_2 * 4068) / 71;
  172. Phasor_Angle_Degree_3 = (Phasor_Angle_3 * 4068) / 71;
  173.  
  174. //Interrupt Driven LCD Display
  175. if (display_now_on_lcd == true) {
  176. Display_on_LCD();
  177. display_now_on_lcd = false;
  178. }
  179.  
  180. } else if (c == '!') {
  181. status = RECEIVING1;
  182.  
  183. //Reset the variables to Zero
  184. Year = 0;
  185. Month = 0;
  186. Day = 0;
  187. Hour = 0;
  188. Minute = 0;
  189. Second = 0;
  190. MilliSecond = 0;
  191. Phasor_Angle_1 = 0;
  192. Phasor_Angle_2 = 0;
  193. Phasor_Angle_3 = 0;
  194. Phasor_Magnitude_1 = 0;
  195. Phasor_Magnitude_2 = 0;
  196. Phasor_Magnitude_3 = 0;
  197. Phasor_Angle_Degree_1 = 0;
  198. Phasor_Angle_Degree_2 = 0;
  199. Phasor_Angle_Degree_3 = 0;
  200. Latitude = 0;
  201. Longitude = 0;
  202. P1_freq = 0;
  203. P2_freq = 0;
  204. P3_freq = 0;
  205. P1_rocof = 0;
  206. P2_rocof = 0;
  207. P3_rocof = 0;
  208. }
  209. }
  210. }
  211.  
  212. void init_LCD() {
  213. //LCD Size 800:480
  214. //0,0 799,0
  215. //0,479 799,479
  216. PMU_LCD.setFont(BigFont);
  217. PMU_LCD.clrScr();
  218. PMU_LCD.setColor(0, 255, 0);
  219. PMU_LCD.print("* Phasor Measurement Unit Local Display *", CENTER, 1);
  220. PMU_LCD.setColor(255, 153, 51);
  221. PMU_LCD.print("!!! Developed by Debashish Mohapatra !!!", CENTER, 462);
  222.  
  223. // Print out Phase 1 phase 2 and Phase 3
  224. String Header1 = String("Phasor ") + String(" Phase1 ") + String(" Phase2") + String(" Phase3");
  225. PMU_LCD.setColor(255, 0, 255);
  226. PMU_LCD.setFont(Inconsola);
  227. PMU_LCD.print(Header1, LEFT, 30);
  228. PMU_LCD.setColor(255, 0, 255);
  229.  
  230. PMU_LCD.print("Magni:", LEFT, 80);
  231. PMU_LCD.print("Angle:", LEFT, 145);
  232. PMU_LCD.print("Frequ:", LEFT, 220);
  233. PMU_LCD.print("ROCOF:", LEFT, 295);
  234.  
  235. PMU_LCD.setColor(0, 255, 0);
  236. PMU_LCD.setFont(BigFont);
  237. PMU_LCD.print(" (VOLT)", LEFT, 115);
  238. PMU_LCD.print("(DEGREE)", LEFT, 180);
  239. PMU_LCD.print(" (HZ)", LEFT, 255);
  240. PMU_LCD.print("(HZ/SEC)", LEFT, 330);
  241. }
  242.  
  243. void Display_on_LCD() {
  244. // Print Phase1 Parameters
  245. PMU_LCD.setFont(Inconsola);
  246. PMU_LCD.setColor(255, 0, 0);// Red
  247. PMU_LCD.printNumF(Phasor_Magnitude_1 / 100, 2, 185, 85, 46, 5,48);
  248. PMU_LCD.print(" ", 185, 150);
  249. PMU_LCD.printNumF(Phasor_Angle_Degree_1, 2, 185, 150, 46, 6, 48);
  250. PMU_LCD.printNumF(P1_freq / 10000, 2, 185, 225, 46, 4,48);
  251. PMU_LCD.printNumF(P1_rocof / 10000, 2, 185, 300, 46, 4,48);
  252.  
  253. PMU_LCD.setColor(255, 255, 0);// Yellow
  254. PMU_LCD.printNumF(Phasor_Magnitude_2 / 100, 2, 380, 85, 46, 5,48);
  255. PMU_LCD.print(" ", 380, 150);
  256. PMU_LCD.printNumF(Phasor_Angle_Degree_2, 2, 380, 150, 46, 6, 48);
  257. PMU_LCD.printNumF(P2_freq / 10000, 2, 380, 225, 46, 4,48);
  258. PMU_LCD.printNumF(P2_rocof / 10000, 2, 380, 300, 46, 4,48);
  259.  
  260. PMU_LCD.setColor(127, 250, 250);// White-Blue
  261. PMU_LCD.printNumF(Phasor_Magnitude_3 / 100, 2, 600, 85, 46, 5,48);
  262. PMU_LCD.print(" ", 600, 150);
  263. PMU_LCD.printNumF(Phasor_Angle_Degree_3, 2, 600, 150, 46, 6, 48);
  264. PMU_LCD.printNumF(P3_freq / 10000, 2, 600, 225, 46, 4,48);
  265. PMU_LCD.printNumF(P3_rocof / 10000, 2, 600, 300, 46, 4,48);
  266.  
  267. // Print GPS Information
  268. String Time = String(" UTC Time: ") + String(Hour) + ":" + String(Minute) + ":" + String(Second) + " ";
  269. String Date = String(" Date: ") + String(Day) + "/" + String(Month) + "/" + String(Year);
  270. String Location = String(" Latitude: ") + String(Latitude / 1000) + String(" Longitude: ") + String(Longitude / 1000);
  271.  
  272. PMU_LCD.setFont(Ubuntu);
  273. PMU_LCD.setColor(255, 255, 255);
  274. PMU_LCD.print(Time, LEFT, 360);
  275. PMU_LCD.print(Date, LEFT, 395);
  276. PMU_LCD .print(Location, LEFT, 430);
  277. }
  278. void display_on_lcd() { //ISR
  279. display_now_on_lcd = true;
  280. }

Python GUI

The Graphical User Interface Developed for Plotting Real Time Data from PMU and logging data in a CSV file

Python Code:
  1. from pyqtgraph.Qt import QtGui, QtCore
  2. import pyqtgraph as pg
  3. ##import time
  4. import numpy as np
  5.  
  6. import serial
  7. ser = serial.Serial('com8', 460800, timeout=1)
  8. # Connect to serial port at COM8, at 460800 bauds
  9.  
  10.  
  11. pg.setConfigOptions(antialias=True)
  12. # Enable antialiasing for prettier plots
  13.  
  14. app = QtGui.QApplication([])
  15. win = pg.GraphicsWindow()
  16.  
  17. win.setWindowTitle('Realtime PMU Data Monitoring')
  18. # Set the window title
  19.  
  20. #Define first graph to show the phasor magnitudes
  21. p1 = win.addPlot(title="Phasor Magnitudes",colspan=2)
  22. p1.setRange(yRange=[215, 250],xRange=[0, 1000])
  23. p1.setLabel('left', "Phasor RMS Magnitude", units='Volts')
  24. p1.setLabel('bottom', "Time ( x20 milli Seconds)")
  25. p1.showGrid(x=1, y=1, alpha=.5)
  26. p1.addLegend(offset=[-10,-10])
  27.  
  28. win.nextRow()
  29. #Define second graph to show the phasor angles
  30. p2 = win.addPlot(title="Phasor Angles")
  31. p2.setRange(yRange=[-200, 200],xRange=[0, 1000])
  32. p2.setLabel('left', "Phasor angles", units='Degree')
  33. p2.setLabel('bottom', "Time ( x20 milli Seconds)")
  34. p2.showGrid(x=1, y=1, alpha=.5)
  35. p2.addLegend(offset=[-40,-10])
  36.  
  37. #Define third graph to show the phasor polar plot
  38. v = win.addViewBox()
  39. v.setAspectLocked()
  40. v.setFixedWidth(500)
  41. p3 = pg.PlotItem()
  42. p3.setRange(xRange=[-250,250],yRange=[-250, 250])
  43. curvePen = pg.mkPen(color=(255, 255, 255), style=QtCore.Qt.DotLine)
  44. c1 = p3.plot(x=218*np.cos(np.linspace(0, 2*np.pi, 360)), y=218*np.sin(np.linspace(0, 2*np.pi, 360)),pen=curvePen,name="218V",)
  45. c2 = p3.plot(x=50*np.cos(np.linspace(0, 2*np.pi, 360)), y=50*np.sin(np.linspace(0, 2*np.pi, 360)),pen=curvePen,name="50V")
  46. c4 = p3.plot(x=150*np.cos(np.linspace(0, 2*np.pi, 360)), y=150*np.sin(np.linspace(0, 2*np.pi, 360)),pen=curvePen,name="150V")
  47. c6 = p3.plot(x=250*np.cos(np.linspace(0, 2*np.pi, 360)), y=250*np.sin(np.linspace(0, 2*np.pi, 360)),pen=curvePen,name="250V")
  48. c7 = p3.plot(x=np.linspace(-177, 177, 500), y=np.linspace(-177, 177, 500),pen=curvePen)
  49. c8 = p3.plot(x=np.linspace(-177, 177, 500), y=np.linspace(177, -177, 500),pen=curvePen)
  50. c9 = p3.plot(x=np.linspace(-250, 250, 500), y=np.linspace(0, 0, 500),pen=curvePen)
  51. c10 = p3.plot(x=np.linspace(0, 0, 500), y=np.linspace(-250, 250, 500),pen=curvePen)
  52. p3.addLegend(offset=[-1,-1])
  53.  
  54. g = pg.GraphItem()
  55. v.addItem(g)
  56. v.addItem(c1)
  57. v.addItem(c2)
  58. v.addItem(c4)
  59. v.addItem(c6)
  60. v.addItem(c7)
  61. v.addItem(c8)
  62. v.addItem(c9)
  63. v.addItem(c10)
  64.  
  65. #plot the curves in the graph areas
  66. curve1 = p1.plot(pen=(255, 0, 0),name="Phase 1(RMS Magnitude)")
  67. curve2 = p1.plot(pen=(255, 255, 0),name="Phase 2(RMS Magnitude)")
  68. curve3 = p1.plot(pen=(0, 0, 255),name="Phase 3(RMS Magnitude)")
  69.  
  70. curve4 = p2.plot(pen=(255, 0, 0),name="Phase 1")
  71. curve5 = p2.plot(pen=(255, 255, 0),name="Phase 2")
  72. curve6 = p2.plot(pen=(0, 0, 255),name="Phase 3")
  73.  
  74. # Read the serial data string coming in from the PMU
  75. line1 = ser.readline()
  76. # split the string and extract the phasor parameters
  77. data1 = [float(val1) for val1 in line1.split()]
  78.  
  79. previous_minute = int(data1[4])
  80.  
  81. # define the log files, where the phasors will be stored
  82. path_txt = 'pmu_data.txt'
  83. path_txt_plot = 'pmu_data_plot.txt'
  84. path_excel = 'pmu_data.csv'
  85.  
  86. now_min = "%s-%s-%s_%s-%s" %(int(data1[0]), int(data1[1]), int(data1[2]), int(data1[3]), int(data1[4]))
  87. path_txt_n = '%s_%s' % (now_min, path_txt)
  88. path_txt_plot_n = '%s_%s' % (now_min, path_txt_plot)
  89. path_excel_n = '%s_%s' % (now_min, path_excel)
  90.  
  91. logfileExcel = open(path_excel_n, 'a')
  92. logfileText = open(path_txt_n, 'a')
  93. logfileTextPlot = open(path_txt_plot_n, 'a')
  94.  
  95. # define the read function to read the data stream and append the
  96. # parameters to sepatrate arrays
  97. def readfun():
  98. global data, current_minute, previous_minute, FORMAT, logfileText, logfileTextPlot, logfileExcel, path_txt, path_excel, path_txt_plot
  99. line = ser.readline()
  100. data = [float(val) for val in line.split()]
  101.  
  102. current_minute = int(data[4])
  103. if current_minute == (previous_minute+1):
  104. now_m = "%s-%s-%s_%s-%s" %(int(data[0]), int(data[1]), int(data[2]), int(data[3]), int(data[4]))
  105. new_path_txt = '%s_%s' % (now_m, path_txt)
  106. new_path_txt_plot = '%s_%s' % (now_m, path_txt_plot)
  107. new_path_excel = '%s_%s' % (now_m, path_excel)
  108.  
  109. logfileExcel.flush()
  110. logfileText.flush()
  111. logfileTextPlot.flush()
  112. logfileExcel.close()
  113. logfileText.close()
  114. logfileTextPlot.close()
  115. logfileExcel = open(new_path_excel, 'a')
  116. logfileText = open(new_path_txt, 'a')
  117. logfileTextPlot = open(new_path_txt_plot, 'a')
  118. previous_minute = current_minute
  119.  
  120. a = "%s-%s-%s, %s:%s:%s:%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s" % (int(data[0]), int(data[1]), int(data[2]), int(data[3]), int(data[4]), int(data[5]), int(data[6]), data[7], data[8], data[9], data[10], data[11], data[12], data[13], data[14], data[15], data[16], data[17], data[18],"\n")
  121. logfileExcel.write(a)
  122. logfileText.write(a)
  123.  
  124. bs = int(data[5])
  125. bms = int(data[6])
  126. bmS = (bs*1000)+bms
  127. b = "%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s %s" % (bmS, data[7], data[8], data[9], data[10], data[11], data[12], data[13], data[14], data[15],data[16], data[17], data[18], "\n")
  128. logfileTextPlot.write(b)
  129. return data[6],data[7],data[8],data[9],data[10],data[11],data[12]
  130.  
  131. readData = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]
  132.  
  133. y2=np.zeros(1000,dtype=float)
  134. y3=np.zeros(1000,dtype=float)
  135. y4=np.zeros(1000,dtype=float)
  136. y5=np.zeros(1000,dtype=float)
  137. y6=np.zeros(1000,dtype=float)
  138. y7=np.zeros(1000,dtype=float)
  139.  
  140. indx = 0
  141. # define the update function to update the plots with the parameter arrays
  142. def update():
  143. global curve1, curve2, curve3, indx, y2, y3, y4, y5, y6, y7 #y1
  144.  
  145. readData= readfun() #function that reads data from the sensor it returns a list of 6 elements as the y-coordinates for the updating plots
  146.  
  147. y2[indx]=readData[1]
  148. y3[indx]=readData[2]
  149. y4[indx]=readData[3]
  150. y5[indx]=readData[4]
  151. y6[indx]=readData[5]
  152. y7[indx]=readData[6]
  153.  
  154. Rx=y2[indx]*np.cos(np.deg2rad(y3[indx]))
  155. Ry=y2[indx]*np.sin(np.deg2rad(y3[indx]))
  156. Yx=y4[indx]*np.cos(np.deg2rad(y5[indx]))
  157. Yy=y4[indx]*np.sin(np.deg2rad(y5[indx]))
  158. Bx=y6[indx]*np.cos(np.deg2rad(y7[indx]))
  159. By=y6[indx]*np.sin(np.deg2rad(y7[indx]))
  160.  
  161. pos = np.array([[0,0],[Rx,Ry],[Yx,Yy],[Bx,By]])
  162. adj = np.array([[0,1],[0,2],[0,3]])
  163. symbols = ['o','t','t','t']
  164. lines = np.array([(255,0,0,255,3),(255,255,0,255,3),(0,0,255,255,3)], dtype=[('red',np.ubyte),
  165. ('green',np.ubyte),('blue',np.ubyte),('alpha',np.ubyte),('width',float)])
  166. g.setData(pos=pos, adj=adj,pen=lines,size=1,symbol=symbols )
  167.  
  168. if indx==999:
  169. y2=np.zeros(1000,dtype=float)
  170. y3=np.zeros(1000,dtype=float)
  171. y4=np.zeros(1000,dtype=float)
  172. y5=np.zeros(1000,dtype=float)
  173. y6=np.zeros(1000,dtype=float)
  174. y7=np.zeros(1000,dtype=float)
  175. indx = 0
  176. else:
  177. indx+=1
  178.  
  179. curve1.setData(y2)# update magnitudes
  180. curve2.setData(y4)
  181. curve3.setData(y6)
  182. curve4.setData(y3)# update angles
  183. curve5.setData(y5)
  184. curve6.setData(y7)
  185. app.processEvents()
  186.  
  187. timer = QtCore.QTimer()
  188. timer.timeout.connect(update)
  189. timer.start()
  190.  
  191. if __name__ == '__main__':
  192. import sys
  193. if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_'):
  194. QtGui.QApplication.instance().exec_()