Introduction
For detecting & identifying an electrical load using machine learning we will use power pattern as our dataset. Electrical instantaneous power can be calculated from instantaneous voltage and instantaneous current. From both resistive and inductive load voltages, current, and power fluctuates 50/60 times a second as shown in figure 1 and figure 2 below, for us humans we can't keep up with change at this speed and so we have a more useful value for power: the average of the instantaneous power which we call real power! (or active power).
Real power is often defined as the power used by a device to produce useful work. Looking at the graph above the positive bits are power going to the load from the supply and the negative bits are power going back from the load to the supply, the power that was actually used by the load is the power going to minus the power going back is the real power.
Reactive power (or imaginary power) is a measure of the power going back and forth between the load and the supply that does no useful work.
Another useful measure of power is Apparent Power which is the product of the Root-Mean-Squared (RMS) average of the Voltage and the RMS average of the Current. For purely resistive loads real power is equal to apparent power. But for all other loads, real power is less than apparent power. Apparent power is a measure of the real and reactive power but it is not an algebraic sum of the two, as the sum of the two does not take into account phase differences.
Relationship between real, reactive, and apparent power for IDEAL sinusoidal loads:
- Real Power = Apparent Power x cosΦ
- Reactive Power = Apparent Power x sinΦ
The cosΦ is also known as the power factor. In AC circuits, the power factor is the ratio of the real power that is used to do work and the apparent power that is supplied to the circuit. The power factor can get values in the range from 0 to 1. When all the power is reactive power with no real power (usually inductive load) - the power factor is 0. We can calculate power factor from the following equation:
- Power Factor = Real Power / Apparent Power
Calculation of Power
AC Voltage and current continually alternate, as the name suggests, if we draw a picture of the voltage and current waveform over time, it will look something like the image below (depending on what's using power - the current waveform - blue in the diagram below - is what you get if you look at a typical laptop power supply. There's an incandescent light bulb in there as well).
The image was made by sampling the mains voltage and current at high frequency, which is exactly what we do on our project for calculating power. We will sample instantaneous voltage and current (50 sample per 20ms). Though a higher sampling rate increases the accuracy - we're limited by the speed of the analog conversion).
Calculating real power
Real power is the average of instantaneous power. The calculation is relatively straightforward on the microcontroller program as shown in the following code snippet. First, we calculate the instantaneous power by multiplying the instantaneous voltage measurement with the instantaneous current measurement. We sum this instantaneous power measurement over a given number of samples and divide by that number of samples:
for (n=0; n<number_of_sample; n++){ // inst_voltage and inst_current calculation from raw ADC goes here inst_power = inst_voltage * inst_current; sum_inst_power += inst_power; } real_power = sum_inst_power / number_of_samples;
Root-Mean-Square (RMS) Voltage
The root-mean-square is calculated in the way the name suggests first we square the quantity, then we calculate the mean, and finally the square-root of the mean-square, this is how it's done on the program:
for (n=0; n<number_of_sample; n++){ // inst_voltage calculation from raw ADC input goes here. squared_voltage = inst_voltage * inst_voltage; sum_squared_voltage += squared_voltage; } mean_square_voltage = sum_squared_voltage / number_of_samples; root_mean_square_voltage = sqrt(mean_square_voltage);
Root-Mean-Square (RMS) Current Same as the RMS voltage calculation:
for (n=0; n<number_of_sample; n++) { // inst_current calculation from raw ADC input goes here. squared_current = inst_current * inst_current; sum_squared_current += squared_current; } mean_square_current = sum_squared_current / number_of_samples; root_mean_square_current = sqrt(mean_square_current);
Apparent power
apparent_power = root_mean_square_voltage * root_mean_square_current;
As RMS voltage is generally a fixed value such as: 220V (+10% -6% in the BD) its possible to approximate apparent power without having to make a voltage measurement by setting the RMS voltage to 230V. This is a common practice used by domestic energy monitors.
Voltage & Current Sensor
We will use the ZMPT101B voltage sensor module for measuring the AC voltage and the ZMCT103C current sensor module for measuring the load current. Both sensors provide analog data. For interfacing the sensors with PSoC 6 MCU board we will use the analog channel for data collection. We should and will use the same type of sensors for real-time detection for accurate results.
Figure: Voltage Sensor
Figure: Current Sensor
Sample Program for Sensor reading and Power Calculation
As we already know both current and voltage sensors we are using in our project provide analog data. We need to write program for PSoC 6 microcontroller to read the analog data and calculate the power. We will use P10_0 and P10_1 (Arduino A0, A1) pins respectively for reading voltage and current. This is a sample program for testing the sensors:
#include "cy_pdl.h" #include "cyhal.h" #include "cybsp.h" #include "cy_retarget_io.h" /* ADC input pin */ #define VOLTAGE_CHANNEL (P10_0) #define CURRENT_CHANNEL (P10_1) /* Number of scans every time ADC read is initiated */ #define NUM_SCAN (1) /* ADC Channel constants*/ enum ADC_CHANNELS { CHANNEL_0 = 0, CHANNEL_1, NUM_CHANNELS } adc_channel; /* Multi-channel initialization function */ void adc_multi_channel_init(void); /* Function to read input voltage from multiple channels */ void adc_multi_channel_process(void); /* ADC Event Handler */ static void adc_event_handler(void* arg, cyhal_adc_event_t event); /******************************************************************************* * Global Variables *******************************************************************************/ /* ADC Object */ cyhal_adc_t adc_obj; /* ADC Channel 0 Object */ cyhal_adc_channel_t adc_chan_0_obj; /* ADC Channel 1 Object */ cyhal_adc_channel_t adc_chan_1_obj; /* Default ADC configuration */ const cyhal_adc_config_t adc_config = { .continuous_scanning=false, // Continuous Scanning is disabled .average_count=1, // Average count disabled .vref=CYHAL_ADC_REF_VDDA, // VREF for Single ended channel set to VDDA .vneg=CYHAL_ADC_VNEG_VSSA, // VNEG for Single ended channel set to VSSA .resolution = 12u, // 12-bit resolution .ext_vref = NC, // No connection .bypass_pin = NC }; // No connection /* Asynchronous read complete flag, used in Event Handler */ static bool async_read_complete = false; /* Variable to store results from multiple channels during asynchronous read*/ int32_t result_arr[NUM_CHANNELS * NUM_SCAN] = {0}; int main(void) { /* Variable to capture return value of functions */ cy_rslt_t result; /* Initialize the device and board peripherals */ result = cybsp_init(); /* Board init failed. Stop program execution */ if (result != CY_RSLT_SUCCESS) { CY_ASSERT(0); } /* Enable global interrupts */ __enable_irq(); /* Initialize retarget-io to use the debug UART port */ result = cy_retarget_io_init(CYBSP_DEBUG_UART_TX, CYBSP_DEBUG_UART_RX, CY_RETARGET_IO_BAUDRATE); /* retarget-io init failed. Stop program execution */ if (result != CY_RSLT_SUCCESS) { CY_ASSERT(0); } /* Print message */ /* \x1b[2J\x1b[;H - ANSI ESC sequence for clear screen */ printf("\x1b[2J\x1b[;H"); printf("-----------------------------------------------------------\r\n"); printf("PSoC 6 MCU: ADC using HAL\r\n"); printf("-----------------------------------------------------------\r\n\n"); /* Initialize Channel 0 and Channel 1 */ adc_multi_channel_init(); /* Update ADC configuration */ result = cyhal_adc_configure(&adc_obj, &adc_config); if(result != CY_RSLT_SUCCESS) { printf("ADC configuration update failed. Error: %ld\n", (long unsigned int)result); CY_ASSERT(0); } for (;;) { /* Sample input voltage at channel 0 and channel 1*/ adc_multi_channel_process(); /* 200ms delay between scans */ cyhal_system_delay_ms(200); } } /******************************************************************************* * Function Name: adc_multi_channel_init ******************************************************************************* * * Summary: * ADC Multichannel initilization. This function initializes and configures * channel 0 and channel 1 of ADC. * * Parameters: * void * * Return: * void * *******************************************************************************/ void adc_multi_channel_init(void) { /* Variable to capture return value of functions */ cy_rslt_t result; /* Initialize ADC. The ADC block which can connect to pin 10[0] is selected */ result = cyhal_adc_init(&adc_obj, VOLTAGE_CHANNEL, NULL); if(result != CY_RSLT_SUCCESS) { printf("ADC initialization failed. Error: %ld\n", (long unsigned int)result); CY_ASSERT(0); } /* ADC channel configuration */ const cyhal_adc_channel_config_t channel_config = { .enable_averaging = false, // Disable averaging for channel .min_acquisition_ns = 1000, // Minimum acquisition time set to 1us .enabled = true }; // Sample this channel when ADC performs a scan /* Initialize a channel 0 and configure it to scan P10_0 in single ended mode. */ result = cyhal_adc_channel_init_diff(&adc_chan_0_obj, &adc_obj, VOLTAGE_CHANNEL, CYHAL_ADC_VNEG, &channel_config); if(result != CY_RSLT_SUCCESS) { printf("ADC voltage channel initialization failed. Error: %ld\n", (long unsigned int)result); CY_ASSERT(0); } /* Initialize a channel 1 and configure it to scan P10_0 in single ended mode. */ result = cyhal_adc_channel_init_diff(&adc_chan_1_obj, &adc_obj, CURRENT_CHANNEL, CYHAL_ADC_VNEG, &channel_config); if(result != CY_RSLT_SUCCESS) { printf("ADC current channel initialization failed. Error: %ld\n", (long unsigned int)result); CY_ASSERT(0); } /* Register a callback to handle asynchronous read completion */ cyhal_adc_register_callback(&adc_obj, &adc_event_handler, result_arr); /* Subscribe to the async read complete event to process the results */ cyhal_adc_enable_event(&adc_obj, CYHAL_ADC_ASYNC_READ_COMPLETE, CYHAL_ISR_PRIORITY_DEFAULT, true); printf("ADC is configured in multichannel configuration.\r\n\n"); printf("Voltage channel is configured in single ended mode, connected to pin \r\n"); printf("P10_0. Provide input voltage at P10_0\r\n"); printf("Current channel is configured in single ended mode, connected to pin \r\n"); printf("P10_1. Provide input voltage at P10_1\r\n"); } /******************************************************************************* * Function Name: adc_multi_channel_process ******************************************************************************* * * Summary: * ADC single channel process function. This function reads the input voltage * from channel 0 and channel 1. Prints the input voltage on UART. * * Parameters: * void * * Return: * void * *******************************************************************************/ void adc_multi_channel_process(void) { /* Variable to capture return value of functions */ cy_rslt_t result; /* Variable to store ADC conversion result from channel 0 */ int32_t adc_result_0 = 0; /* Variable to store ADC conversion result from channel 1 */ int32_t adc_result_1 = 0; /* Initiate an asynchronous read operation. The event handler will be called * when it is complete. */ result = cyhal_adc_read_async_uv(&adc_obj, NUM_SCAN, result_arr); if(result != CY_RSLT_SUCCESS) { printf("ADC async read failed. Error: %ld\n", (long unsigned int)result); CY_ASSERT(0); } /* * Read data from result list, input voltage in the result list is in * microvolts. Convert it millivolts and print input voltage * */ adc_result_0 = result_arr[CHANNEL_0]/1000; adc_result_1 = result_arr[CHANNEL_1]/1000; printf("Voltage channel input: %4ldmV \t Current channel input: %4ldmV\r\n", (long int)adc_result_0, (long int)adc_result_1); /* Clear async read complete flag */ async_read_complete = false; } /******************************************************************************* * Function Name: adc_event_handler ******************************************************************************* * * Summary: * ADC event handler. This function handles the asynchronous read complete event * and sets the async_read_complete flag to true. * * Parameters: * void *arg : pointer to result list * cyhal_adc_event_t event : ADC event type * * Return: * void * *******************************************************************************/ static void adc_event_handler(void* arg, cyhal_adc_event_t event) { if(0u != (event & CYHAL_ADC_ASYNC_READ_COMPLETE)) { /* Set async read complete flag to true */ async_read_complete = true; } } void calculate_real_power(int16_t no_of_sample){ cy_rslt_t adc_result; int32_t ins_voltage = 0; int32_t ins_current = 0; int32_t actual_ins_voltage = 0; int32_t actual_ins_current = 0; int32_t ins_power = 0; int16_t real_power = 0; for(int16_t i=0; i<no_of_sample; i++) { adc_result = cyhal_adc_read_async_uv(&adc_obj, NUM_SCAN, result_arr); if(adc_result != CY_RSLT_SUCCESS) { printf("ADC async read failed. Error: %ld\n", (long unsigned int)adc_result); CY_ASSERT(0); } ins_voltage = result_arr[CHANNEL_0]/1000; ins_current = result_arr[CHANNEL_1]/1000; actual_ins_voltage = ins_voltage * 400; //multiplied by scale factor of sensor actual_ins_current = ins_current * 100; ins_power = ins_power + actual_ins_voltage * actual_ins_current; async_read_complete = false; } real_power = ins_power/no_of_sample; ins_power = 0; //int16_t power = real_power; printf("Power input: %4ldW\r\n", (long int)real_power); }