diff --git a/sonoff/_changelog.ino b/sonoff/_changelog.ino index ebf1b83cb..abf4572f6 100644 --- a/sonoff/_changelog.ino +++ b/sonoff/_changelog.ino @@ -1,4 +1,7 @@ -/* 6.2.1.8 20180926 +/* 6.2.1.9 20180928 + * Add Apparent Power and Reactive Power to Energy Monitoring devices (#251) + * + * 6.2.1.8 20180926 * Change status JSON message providing more switch and retain information * Change pinmode for no-pullup defined switches to pullup when configured as switchmode PUSHBUTTON (=3 and up) (#3896) * Add delay after restart before processing rule sensor data (#3811) diff --git a/sonoff/sonoff_version.h b/sonoff/sonoff_version.h index 915ce52d2..4f2b5c0ec 100644 --- a/sonoff/sonoff_version.h +++ b/sonoff/sonoff_version.h @@ -20,7 +20,7 @@ #ifndef _SONOFF_VERSION_H_ #define _SONOFF_VERSION_H_ -#define VERSION 0x06020108 +#define VERSION 0x06020109 #define D_PROGRAMNAME "Sonoff-Tasmota" #define D_AUTHOR "Theo Arends" diff --git a/sonoff/support.ino b/sonoff/support.ino index fb2b5eae3..fc73e726e 100644 --- a/sonoff/support.ino +++ b/sonoff/support.ino @@ -494,6 +494,32 @@ double FastPrecisePow(double a, double b) return r * u.d; } +uint32_t SqrtInt(uint32_t num) +{ + if (num <= 1) { + return num; + } + + uint32_t x = num / 2; + uint32_t y; + do { + y = (x + num / x) / 2; + if (y >= x) { + return x; + } + x = y; + } while (true); +} + +uint32_t RoundSqrtInt(uint32_t num) +{ + uint32_t s = SqrtInt(4 * num); + if (s & 1) { + s++; + } + return s / 2; +} + char* GetTextIndexed(char* destination, size_t destination_size, uint16_t index, const char* haystack) { // Returns empty string if not found diff --git a/sonoff/xdrv_03_energy.ino b/sonoff/xdrv_03_energy.ino index 3ccad7df4..13ab1cb62 100644 --- a/sonoff/xdrv_03_energy.ino +++ b/sonoff/xdrv_03_energy.ino @@ -41,23 +41,25 @@ const char kEnergyCommands[] PROGMEM = D_CMND_MAXPOWER "|" D_CMND_MAXPOWERHOLD "|" D_CMND_MAXPOWERWINDOW "|" D_CMND_SAFEPOWER "|" D_CMND_SAFEPOWERHOLD "|" D_CMND_SAFEPOWERWINDOW ; -float energy_voltage = 0; // 123.1 V -float energy_current = 0; // 123.123 A -float energy_power = 0; // 123.1 W -float energy_power_factor = NAN; // 0.12 -int energy_calc_power_factor = 0; // Do not calculate power factor from data -float energy_frequency = NAN; // 123.1 Hz -float energy_start = 0; // 12345.12345 kWh total previous +float energy_voltage = 0; // 123.1 V +float energy_current = 0; // 123.123 A +float energy_active_power = 0; // 123.1 W +float energy_apparent_power = NAN; // 123.1 VA +float energy_reactive_power = NAN; // 123.1 VAr +float energy_power_factor = NAN; // 0.12 +float energy_frequency = NAN; // 123.1 Hz +float energy_start = 0; // 12345.12345 kWh total previous -float energy_daily = 0; // 123.123 kWh -float energy_total = 0; // 12345.12345 kWh +float energy_daily = 0; // 123.123 kWh +float energy_total = 0; // 12345.12345 kWh unsigned long energy_kWhtoday_delta = 0; // 1212312345 Wh 10^-5 (deca micro Watt hours) - Overflows to energy_kWhtoday (HLW and CSE only) -unsigned long energy_kWhtoday; // 12312312 Wh * 10^-2 (deca milli Watt hours) - 5764 = 0.05764 kWh = 0.058 kWh = energy_daily -unsigned long energy_period = 0; // 12312312 Wh * 10^-2 (deca milli Watt hours) - 5764 = 0.05764 kWh = 0.058 kWh = energy_daily +unsigned long energy_kWhtoday; // 12312312 Wh * 10^-2 (deca milli Watt hours) - 5764 = 0.05764 kWh = 0.058 kWh = energy_daily +unsigned long energy_period = 0; // 12312312 Wh * 10^-2 (deca milli Watt hours) - 5764 = 0.05764 kWh = 0.058 kWh = energy_daily float energy_power_last[3] = { 0 }; uint8_t energy_power_delta = 0; +bool energy_type_dc = false; bool energy_power_on = true; byte energy_min_power_flag = 0; @@ -124,15 +126,6 @@ void Energy200ms() } XnrgCall(FUNC_EVERY_200_MSECOND); - - if (energy_calc_power_factor) { - float power_factor = 0; - if (energy_voltage && energy_current && energy_power) { - power_factor = energy_power / (energy_voltage * energy_current); - if (power_factor > 1) power_factor = 1; - } - energy_power_factor = power_factor; - } } void EnergySaveState() @@ -178,21 +171,21 @@ void EnergyMarginCheck() } if (Settings.energy_power_delta) { - float delta = abs(energy_power_last[0] - energy_power); + float delta = abs(energy_power_last[0] - energy_active_power); // Any delta compared to minimal delta - float min_power = (energy_power_last[0] > energy_power) ? energy_power : energy_power_last[0]; + float min_power = (energy_power_last[0] > energy_active_power) ? energy_active_power : energy_power_last[0]; if (((delta / min_power) * 100) > Settings.energy_power_delta) { energy_power_delta = 1; - energy_power_last[1] = energy_power; // We only want one report so reset history - energy_power_last[2] = energy_power; + energy_power_last[1] = energy_active_power; // We only want one report so reset history + energy_power_last[2] = energy_active_power; } } energy_power_last[0] = energy_power_last[1]; // Shift in history every second allowing power changes to settle for up to three seconds energy_power_last[1] = energy_power_last[2]; - energy_power_last[2] = energy_power; + energy_power_last[2] = energy_active_power; if (energy_power_on && (Settings.energy_min_power || Settings.energy_max_power || Settings.energy_min_voltage || Settings.energy_max_voltage || Settings.energy_min_current || Settings.energy_max_current)) { - energy_power_u = (uint16_t)(energy_power); + energy_power_u = (uint16_t)(energy_active_power); energy_voltage_u = (uint16_t)(energy_voltage); energy_current_u = (uint16_t)(energy_current * 1000); @@ -235,7 +228,7 @@ void EnergyMarginCheck() #if FEATURE_POWER_LIMIT // Max Power if (Settings.energy_max_power_limit) { - if (energy_power > Settings.energy_max_power_limit) { + if (energy_active_power > Settings.energy_max_power_limit) { if (!energy_mplh_counter) { energy_mplh_counter = Settings.energy_max_power_limit_hold; } else { @@ -535,6 +528,8 @@ const char HTTP_ENERGY_SNS1[] PROGMEM = "%s" "{s}" D_POWERUSAGE "{m}%s " D_UNIT_WATT "{e}"; const char HTTP_ENERGY_SNS2[] PROGMEM = "%s" + "{s}" D_POWERUSAGE_APPARENT "{m}%s " D_UNIT_VA "{e}" + "{s}" D_POWERUSAGE_REACTIVE "{m}%s " D_UNIT_VAR "{e}" "{s}" D_POWER_FACTOR "{m}%s{e}"; const char HTTP_ENERGY_SNS3[] PROGMEM = "%s" @@ -548,27 +543,64 @@ const char HTTP_ENERGY_SNS4[] PROGMEM = "%s" void EnergyShow(boolean json) { - char energy_total_chr[10]; + char voltage_chr[10]; + char current_chr[10]; + char active_power_chr[10]; + char apparent_power_chr[10]; + char reactive_power_chr[10]; + char power_factor_chr[10]; + char frequency_chr[10]; char energy_daily_chr[10]; char energy_period_chr[10]; - char energy_power_chr[10]; - char energy_voltage_chr[10]; - char energy_current_chr[10]; - char energy_frequency_chr[10]; - char energy_power_factor_chr[10]; char energy_yesterday_chr[10]; + char energy_total_chr[10]; + char speriod[20]; - char spfactor[20]; char sfrequency[20]; bool show_energy_period = (0 == tele_period); - dtostrfd(energy_power, Settings.flag2.wattage_resolution, energy_power_chr); - dtostrfd(energy_voltage, Settings.flag2.voltage_resolution, energy_voltage_chr); - dtostrfd(energy_current, Settings.flag2.current_resolution, energy_current_chr); - dtostrfd(energy_total, Settings.flag2.energy_resolution, energy_total_chr); + if (!energy_type_dc) { + float apparent_power = energy_apparent_power; + if (isnan(apparent_power)) { + apparent_power = energy_voltage * energy_current; + } + if (apparent_power < energy_active_power) { // Should be impossible + energy_active_power = apparent_power; + } + + float power_factor = energy_power_factor; + if (isnan(power_factor)) { + power_factor = (energy_active_power && apparent_power) ? energy_active_power / apparent_power : 0; + if (power_factor > 1) power_factor = 1; + } + + float reactive_power = energy_reactive_power; + if (isnan(reactive_power)) { + reactive_power = 0; + uint32_t difference = ((uint32_t)(apparent_power * 100) - (uint32_t)(energy_active_power * 100)) / 10; + if ((energy_current > 0.005) && ((difference > 15) || (difference > (uint32_t)(apparent_power * 100 / 1000)))) { + // calculating reactive power only if current is greater than 0.005A and + // difference between active and apparent power is greater than 1.5W or 1% + reactive_power = (float)(RoundSqrtInt((uint32_t)(apparent_power * apparent_power * 100) - (uint32_t)(energy_active_power * energy_active_power * 100))) / 10; + } + } + + dtostrfd(apparent_power, Settings.flag2.wattage_resolution, apparent_power_chr); + dtostrfd(reactive_power, Settings.flag2.wattage_resolution, reactive_power_chr); + dtostrfd(power_factor, 2, power_factor_chr); + if (!isnan(energy_frequency)) { + dtostrfd(energy_frequency, Settings.flag2.frequency_resolution, frequency_chr); + snprintf_P(sfrequency, sizeof(sfrequency), PSTR(",\"" D_JSON_FREQUENCY "\":%s"), frequency_chr); + } + } + + dtostrfd(energy_voltage, Settings.flag2.voltage_resolution, voltage_chr); + dtostrfd(energy_current, Settings.flag2.current_resolution, current_chr); + dtostrfd(energy_active_power, Settings.flag2.wattage_resolution, active_power_chr); dtostrfd(energy_daily, Settings.flag2.energy_resolution, energy_daily_chr); dtostrfd((float)Settings.energy_kWhyesterday / 100000, Settings.flag2.energy_resolution, energy_yesterday_chr); + dtostrfd(energy_total, Settings.flag2.energy_resolution, energy_total_chr); float energy = 0; if (show_energy_period) { @@ -577,34 +609,30 @@ void EnergyShow(boolean json) dtostrfd(energy, Settings.flag2.wattage_resolution, energy_period_chr); snprintf_P(speriod, sizeof(speriod), PSTR(",\"" D_JSON_PERIOD "\":%s"), energy_period_chr); } - if (!isnan(energy_frequency)) { - dtostrfd(energy_frequency, Settings.flag2.frequency_resolution, energy_frequency_chr); - snprintf_P(sfrequency, sizeof(sfrequency), PSTR(",\"" D_JSON_FREQUENCY "\":%s"), energy_frequency_chr); - } - if (!isnan(energy_power_factor)) { - dtostrfd(energy_power_factor, 2, energy_power_factor_chr); - snprintf_P(spfactor, sizeof(spfactor), PSTR(",\"" D_JSON_POWERFACTOR "\":%s"), energy_power_factor_chr); - } if (json) { - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s,\"" D_RSLT_ENERGY "\":{\"" D_JSON_TOTAL "\":%s,\"" D_JSON_YESTERDAY "\":%s,\"" D_JSON_TODAY "\":%s%s,\"" - D_JSON_POWERUSAGE "\":%s%s,\"" D_JSON_VOLTAGE "\":%s,\"" D_JSON_CURRENT "\":%s%s}"), - mqtt_data, energy_total_chr, energy_yesterday_chr, energy_daily_chr, (show_energy_period) ? speriod : "", - energy_power_chr, (!isnan(energy_power_factor)) ? spfactor : "", energy_voltage_chr, energy_current_chr, (!isnan(energy_frequency)) ? sfrequency : ""); + snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s,\"" D_RSLT_ENERGY "\":{\"" D_JSON_TOTAL "\":%s,\"" D_JSON_YESTERDAY "\":%s,\"" D_JSON_TODAY "\":%s%s,\"" D_JSON_POWERUSAGE "\":%s"), + mqtt_data, energy_total_chr, energy_yesterday_chr, energy_daily_chr, (show_energy_period) ? speriod : "", active_power_chr); + if (!energy_type_dc) { + snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s,\"" D_JSON_APPARENT_POWERUSAGE "\":%s,\"" D_JSON_REACTIVE_POWERUSAGE "\":%s,\"" D_JSON_POWERFACTOR "\":%s%s"), + mqtt_data, apparent_power_chr, reactive_power_chr, power_factor_chr, (!isnan(energy_frequency)) ? sfrequency : ""); + } + snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s,\"" D_JSON_VOLTAGE "\":%s,\"" D_JSON_CURRENT "\":%s}"), mqtt_data, voltage_chr, current_chr); + #ifdef USE_DOMOTICZ if (show_energy_period) { // Only send if telemetry dtostrfd(energy_total * 1000, 1, energy_total_chr); - DomoticzSensorPowerEnergy((int)energy_power, energy_total_chr); // PowerUsage, EnergyToday - DomoticzSensor(DZ_VOLTAGE, energy_voltage_chr); // Voltage - DomoticzSensor(DZ_CURRENT, energy_current_chr); // Current + DomoticzSensorPowerEnergy((int)energy_active_power, energy_total_chr); // PowerUsage, EnergyToday + DomoticzSensor(DZ_VOLTAGE, voltage_chr); // Voltage + DomoticzSensor(DZ_CURRENT, current_chr); // Current } #endif // USE_DOMOTICZ #ifdef USE_KNX if (show_energy_period) { KnxSensor(KNX_ENERGY_VOLTAGE, energy_voltage); KnxSensor(KNX_ENERGY_CURRENT, energy_current); - KnxSensor(KNX_ENERGY_POWER, energy_power); - if (!isnan(energy_power_factor)) { KnxSensor(KNX_ENERGY_POWERFACTOR, energy_power_factor); } + KnxSensor(KNX_ENERGY_POWER, energy_active_power); + if (!energy_type_dc) { KnxSensor(KNX_ENERGY_POWERFACTOR, power_factor); } KnxSensor(KNX_ENERGY_DAILY, energy_daily); KnxSensor(KNX_ENERGY_TOTAL, energy_total); KnxSensor(KNX_ENERGY_START, energy_start); @@ -612,9 +640,11 @@ void EnergyShow(boolean json) #endif // USE_KNX #ifdef USE_WEBSERVER } else { - snprintf_P(mqtt_data, sizeof(mqtt_data), HTTP_ENERGY_SNS1, mqtt_data, energy_voltage_chr, energy_current_chr, energy_power_chr); - if (!isnan(energy_power_factor)) { snprintf_P(mqtt_data, sizeof(mqtt_data), HTTP_ENERGY_SNS2, mqtt_data, energy_power_factor_chr); } - if (!isnan(energy_frequency)) { snprintf_P(mqtt_data, sizeof(mqtt_data), HTTP_ENERGY_SNS3, mqtt_data, energy_frequency_chr); } + snprintf_P(mqtt_data, sizeof(mqtt_data), HTTP_ENERGY_SNS1, mqtt_data, voltage_chr, current_chr, active_power_chr); + if (!energy_type_dc) { + snprintf_P(mqtt_data, sizeof(mqtt_data), HTTP_ENERGY_SNS2, mqtt_data, apparent_power_chr, reactive_power_chr, power_factor_chr); + if (!isnan(energy_frequency)) { snprintf_P(mqtt_data, sizeof(mqtt_data), HTTP_ENERGY_SNS3, mqtt_data, frequency_chr); } + } snprintf_P(mqtt_data, sizeof(mqtt_data), HTTP_ENERGY_SNS4, mqtt_data, energy_daily_chr, energy_yesterday_chr, energy_total_chr); #endif // USE_WEBSERVER } diff --git a/sonoff/xnrg_01_hlw8012.ino b/sonoff/xnrg_01_hlw8012.ino index 5140d6596..025ccff44 100644 --- a/sonoff/xnrg_01_hlw8012.ino +++ b/sonoff/xnrg_01_hlw8012.ino @@ -111,9 +111,9 @@ void HlwEvery200ms() if (hlw_cf_pulse_length && energy_power_on && !hlw_load_off) { hlw_w = (hlw_power_ratio * Settings.energy_power_calibration) / hlw_cf_pulse_length; - energy_power = (float)hlw_w / 10; + energy_active_power = (float)hlw_w / 10; } else { - energy_power = 0; + energy_active_power = 0; } hlw_cf1_timer++; @@ -142,7 +142,7 @@ void HlwEvery200ms() hlw_cf1_current_pulse_length = hlw_cf1_pulse_length; hlw_cf1_current_max_pulse_counter = hlw_cf1_pulse_counter; - if (hlw_cf1_current_pulse_length && energy_power) { // No current if no power being consumed + if (hlw_cf1_current_pulse_length && energy_active_power) { // No current if no power being consumed hlw_i = (hlw_current_ratio * Settings.energy_current_calibration) / hlw_cf1_current_pulse_length; energy_current = (float)hlw_i / 1000; } else { @@ -217,7 +217,6 @@ void HlwDrvInit() { if (!energy_flg) { if ((pin[GPIO_HLW_SEL] < 99) && (pin[GPIO_HLW_CF1] < 99) && (pin[GPIO_HLW_CF] < 99)) { // Sonoff Pow or any HLW8012 based device - energy_calc_power_factor = 1; // Calculate power factor from data energy_flg = XNRG_01; } } diff --git a/sonoff/xnrg_02_cse7766.ino b/sonoff/xnrg_02_cse7766.ino index 8ba1b146e..1be231192 100644 --- a/sonoff/xnrg_02_cse7766.ino +++ b/sonoff/xnrg_02_cse7766.ino @@ -95,14 +95,14 @@ void CseReceived() if (adjustement & 0x10) { // Power valid cse_power_invalid = 0; if ((header & 0xF2) == 0xF2) { // Power cycle exceeds range - energy_power = 0; + energy_active_power = 0; } else { if (0 == power_cycle_first) { power_cycle_first = power_cycle; } // Skip first incomplete power_cycle if (power_cycle_first != power_cycle) { power_cycle_first = -1; - energy_power = (float)(Settings.energy_power_calibration * CSE_PREF) / (float)power_cycle; + energy_active_power = (float)(Settings.energy_power_calibration * CSE_PREF) / (float)power_cycle; } else { - energy_power = 0; + energy_active_power = 0; } } } else { @@ -110,11 +110,11 @@ void CseReceived() cse_power_invalid++; } else { power_cycle_first = 0; - energy_power = 0; // Powered on but no load + energy_active_power = 0; // Powered on but no load } } if (adjustement & 0x20) { // Current valid - if (0 == energy_power) { + if (0 == energy_active_power) { energy_current = 0; } else { energy_current = (float)Settings.energy_current_calibration / (float)current_cycle; @@ -123,7 +123,7 @@ void CseReceived() } else { // Powered off power_cycle_first = 0; energy_voltage = 0; - energy_power = 0; + energy_active_power = 0; energy_current = 0; } } @@ -180,7 +180,7 @@ void CseEverySecond() } else { cf_frequency = cf_pulses - cf_pulses_last_time; } - if (cf_frequency && energy_power) { + if (cf_frequency && energy_active_power) { cf_pulses_last_time = cf_pulses; energy_kWhtoday_delta += (cf_frequency * Settings.energy_power_calibration) / 36; EnergyUpdateToday(); @@ -194,7 +194,6 @@ void CseDrvInit() if ((SONOFF_S31 == Settings.module) || (SONOFF_POW_R2 == Settings.module)) { // Sonoff S31 or Sonoff Pow R2 baudrate = 4800; serial_config = SERIAL_8E1; - energy_calc_power_factor = 1; // Calculate power factor from data energy_flg = XNRG_02; } } diff --git a/sonoff/xnrg_03_pzem004t.ino b/sonoff/xnrg_03_pzem004t.ino index 03581862c..3dfbc3879 100644 --- a/sonoff/xnrg_03_pzem004t.ino +++ b/sonoff/xnrg_03_pzem004t.ino @@ -177,7 +177,7 @@ void PzemEvery200ms() energy_current = value; break; case 3: // Power as 20W - energy_power = value; + energy_active_power = value; break; case 4: // Total energy as 99999Wh if (!energy_start || (value < energy_start)) energy_start = value; // Init after restart and hanlde roll-over if any @@ -215,7 +215,6 @@ void PzemDrvInit() { if (!energy_flg) { if ((pin[GPIO_PZEM_RX] < 99) && (pin[GPIO_PZEM_TX] < 99)) { // Any device with a Pzem004T - energy_calc_power_factor = 1; // Calculate power factor from data energy_flg = XNRG_03; } } diff --git a/sonoff/xnrg_04_mcp39f501.ino b/sonoff/xnrg_04_mcp39f501.ino index f2ce321bc..d962ef204 100644 --- a/sonoff/xnrg_04_mcp39f501.ino +++ b/sonoff/xnrg_04_mcp39f501.ino @@ -448,8 +448,8 @@ void McpParseData(void) if (energy_power_on) { // Powered on energy_frequency = (float)mcp_line_frequency / 1000; energy_voltage = (float)mcp_voltage_rms / 10; - energy_power = (float)mcp_active_power / 100; - if (0 == energy_power) { + energy_active_power = (float)mcp_active_power / 100; + if (0 == energy_active_power) { energy_current = 0; } else { energy_current = (float)mcp_current_rms / 10000; @@ -457,7 +457,7 @@ void McpParseData(void) } else { // Powered off energy_frequency = 0; energy_voltage = 0; - energy_power = 0; + energy_active_power = 0; energy_current = 0; } } @@ -557,7 +557,6 @@ void McpDrvInit(void) mcp_calibrate = 0; mcp_timeout = 2; // Initial wait mcp_init = 2; // Initial setup steps - energy_calc_power_factor = 1; // Calculate power factor from data energy_flg = XNRG_04; } } diff --git a/sonoff/xnrg_05_pzem2.ino b/sonoff/xnrg_05_pzem2.ino index b7e1eab42..bb0f4864d 100644 --- a/sonoff/xnrg_05_pzem2.ino +++ b/sonoff/xnrg_05_pzem2.ino @@ -141,12 +141,13 @@ void Pzem2Every200ms() float energy = 0; if (PZEM2_TYPES_003_017 == pzem2_type) { + energy_type_dc = true; // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 // FE 04 10 27 10 00 64 03 E8 00 00 00 00 00 00 00 00 00 00 HH LL = PZEM-017 // Id Cc Sz Volt- Curre Power------ Energy----- HiAlm LoAlm Crc-- energy_voltage = (float)((buffer[3] << 8) + buffer[4]) / 100.0; // 655.00 V energy_current = (float)((buffer[5] << 8) + buffer[6]) / 100.0; // 655.00 A - energy_power = (float)((uint32_t)buffer[9] << 24 + (uint32_t)buffer[10] << 16 + (uint32_t)buffer[7] << 8 + buffer[8]) / 10.0; // 429496729.0 W + energy_active_power = (float)((uint32_t)buffer[9] << 24 + (uint32_t)buffer[10] << 16 + (uint32_t)buffer[7] << 8 + buffer[8]) / 10.0; // 429496729.0 W energy = (float)((uint32_t)buffer[13] << 24 + (uint32_t)buffer[14] << 16 + (uint32_t)buffer[11] << 8 + buffer[12]); // 4294967295 Wh if (!energy_start || (energy < energy_start)) { energy_start = energy; } // Init after restart and hanlde roll-over if any energy_kWhtoday += (energy - energy_start) * 100; @@ -154,12 +155,13 @@ void Pzem2Every200ms() EnergyUpdateToday(); } else if (PZEM2_TYPES_014_016 == pzem2_type) { // PZEM-014,016 + energy_type_dc = false; // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 // FE 04 14 08 98 03 E8 00 00 08 98 00 00 00 00 00 00 01 F4 00 64 00 00 HH LL = PZEM-014 // Id Cc Sz Volt- Current---- Power------ Energy----- Frequ PFact Alarm Crc-- energy_voltage = (float)((buffer[3] << 8) + buffer[4]) / 10.0; // 6553.0 V energy_current = (float)((uint32_t)buffer[7] << 24 + (uint32_t)buffer[8] << 16 + (uint32_t)buffer[5] << 8 + buffer[6]) / 1000.0; // 4294967.000 A - energy_power = (float)((uint32_t)buffer[11] << 24 + (uint32_t)buffer[12] << 16 + (uint32_t)buffer[9] << 8 + buffer[10]) / 10.0; // 429496729.0 W + energy_active_power = (float)((uint32_t)buffer[11] << 24 + (uint32_t)buffer[12] << 16 + (uint32_t)buffer[9] << 8 + buffer[10]) / 10.0; // 429496729.0 W energy_frequency = (float)((buffer[17] << 8) + buffer[18]) / 10.0; // 50.0 Hz energy_power_factor = (float)((buffer[19] << 8) + buffer[20]) / 100.0; // 1.00 energy = (float)((uint32_t)buffer[15] << 24 + (uint32_t)buffer[16] << 16 + (uint32_t)buffer[13] << 8 + buffer[14]); // 4294967295 Wh