Add ESP32 support for eigth energy phases/channels

- ESP32 support for eigth energy phases/channels
- ESP32 command ``EnergyCols 1..8`` to change number of GUI columns
- ESP32 command ``EnergyDisplay 1..3`` to change GUI column presentation
- support for SEN5X gas and air quality sensor by Tyeth Gundry (#17736)
This commit is contained in:
Theo Arends 2023-01-29 16:09:13 +01:00
parent 664b60332a
commit db3fdc5118
5 changed files with 208 additions and 71 deletions

View File

@ -5,7 +5,10 @@ All notable changes to this project will be documented in this file.
## [12.3.1.5] ## [12.3.1.5]
### Added ### Added
- ESP32 Prep support for eigth energy phases/channels - ESP32 support for eigth energy phases/channels
- ESP32 command ``EnergyCols 1..8`` to change number of GUI columns
- ESP32 command ``EnergyDisplay 1..3`` to change GUI column presentation
- support for SEN5X gas and air quality sensor by Tyeth Gundry (#17736)
### Breaking Changed ### Breaking Changed
- Berry energy_ctypes changed with new energy driver - Berry energy_ctypes changed with new energy driver

View File

@ -117,11 +117,15 @@ The latter links can be used for OTA upgrades too like ``OtaUrl https://ota.tasm
- Support for IPv6 only networks on Ethernet (not yet Wifi) - Support for IPv6 only networks on Ethernet (not yet Wifi)
- Support for TM1650 display as used in some clocks by Stefan Oskamp [#17594](https://github.com/arendst/Tasmota/issues/17594) - Support for TM1650 display as used in some clocks by Stefan Oskamp [#17594](https://github.com/arendst/Tasmota/issues/17594)
- Support for PCA9632 4-channel 8-bit PWM driver as light driver by Pascal Heinrich [#17557](https://github.com/arendst/Tasmota/issues/17557) - Support for PCA9632 4-channel 8-bit PWM driver as light driver by Pascal Heinrich [#17557](https://github.com/arendst/Tasmota/issues/17557)
- support for SEN5X gas and air quality sensor by Tyeth Gundry [#17736](https://github.com/arendst/Tasmota/issues/17736)
- Berry support for ``crypto.SHA256`` [#17430](https://github.com/arendst/Tasmota/issues/17430) - Berry support for ``crypto.SHA256`` [#17430](https://github.com/arendst/Tasmota/issues/17430)
- Berry crypto add ``EC_P256`` and ``PBKDF2_HMAC_SHA256`` algorithms required by Matter protocol [#17473](https://github.com/arendst/Tasmota/issues/17473) - Berry crypto add ``EC_P256`` and ``PBKDF2_HMAC_SHA256`` algorithms required by Matter protocol [#17473](https://github.com/arendst/Tasmota/issues/17473)
- Berry crypto add ``random`` to generate series of random bytes - Berry crypto add ``random`` to generate series of random bytes
- Berry crypto add ``HKDF_HMAC_SHA256`` - Berry crypto add ``HKDF_HMAC_SHA256``
- Berry crypto add ``SPAKE2P_Matter`` for Matter support - Berry crypto add ``SPAKE2P_Matter`` for Matter support
- ESP32 command ``EnergyCols 1..8`` to change number of GUI columns
- ESP32 command ``EnergyDisplay 1..3`` to change GUI column presentation
- ESP32 support for eigth energy phases/channels
- ESP32 support for BMPxxx sensors on two I2C busses [#17643](https://github.com/arendst/Tasmota/issues/17643) - ESP32 support for BMPxxx sensors on two I2C busses [#17643](https://github.com/arendst/Tasmota/issues/17643)
### Breaking Changed ### Breaking Changed

View File

@ -454,6 +454,8 @@
#define D_CMND_VOLTAGEHIGH "VoltageHigh" #define D_CMND_VOLTAGEHIGH "VoltageHigh"
#define D_CMND_CURRENTLOW "CurrentLow" #define D_CMND_CURRENTLOW "CurrentLow"
#define D_CMND_CURRENTHIGH "CurrentHigh" #define D_CMND_CURRENTHIGH "CurrentHigh"
#define D_CMND_ENERGYDISPLAY "EnergyDisplay"
#define D_CMND_ENERGYCOLS "EnergyCols"
#define D_CMND_ENERGYTODAY "EnergyToday" #define D_CMND_ENERGYTODAY "EnergyToday"
#define D_CMND_ENERGYYESTERDAY "EnergyYesterday" #define D_CMND_ENERGYYESTERDAY "EnergyYesterday"
#define D_CMND_ENERGYTOTAL "EnergyTotal" #define D_CMND_ENERGYTOTAL "EnergyTotal"

View File

@ -38,6 +38,9 @@
#undef ENERGY_MAX_PHASES #undef ENERGY_MAX_PHASES
#define ENERGY_MAX_PHASES 8 // Support max eight phases/channels #define ENERGY_MAX_PHASES 8 // Support max eight phases/channels
#define ENERGY_GUI_MAX_COLS 4 // [EnergyCols] Number of GUI data columns - Preffered 4
#define ENERGY_GUI_DISPLAY_MODE 3 // [EnergyDisplay] 1 = Rotate if over EnergyCols, 2 = Rotate only powered on if over EnergyCols, 3 = Use tabs if over EnergyCols
#include <Ticker.h> #include <Ticker.h>
#define D_CMND_POWERCAL "PowerCal" #define D_CMND_POWERCAL "PowerCal"
@ -47,6 +50,11 @@
#define D_CMND_TARIFF "Tariff" #define D_CMND_TARIFF "Tariff"
#define D_CMND_MODULEADDRESS "ModuleAddress" #define D_CMND_MODULEADDRESS "ModuleAddress"
enum EnergyDisplayModes {
ENERGY_DISPLAY_MIN_OPTION,
ENERGY_DISPLAY_ROTATE, ENERGY_DISPLAY_ROTATE_POWERED_ON, ENERGY_DISPLAY_TABS,
ENERGY_DISPLAY_MAX_OPTION };
enum EnergyCalibration { enum EnergyCalibration {
ENERGY_POWER_CALIBRATION, ENERGY_VOLTAGE_CALIBRATION, ENERGY_CURRENT_CALIBRATION, ENERGY_FREQUENCY_CALIBRATION }; ENERGY_POWER_CALIBRATION, ENERGY_VOLTAGE_CALIBRATION, ENERGY_CURRENT_CALIBRATION, ENERGY_FREQUENCY_CALIBRATION };
@ -61,7 +69,8 @@ const char kEnergyCommands[] PROGMEM = "|" // No prefix
D_CMND_MAXENERGY "|" D_CMND_MAXENERGYSTART "|" D_CMND_MAXENERGY "|" D_CMND_MAXENERGYSTART "|"
D_CMND_MAXPOWER "|" D_CMND_MAXPOWERHOLD "|" D_CMND_MAXPOWERWINDOW "|" D_CMND_MAXPOWER "|" D_CMND_MAXPOWERHOLD "|" D_CMND_MAXPOWERWINDOW "|"
D_CMND_SAFEPOWER "|" D_CMND_SAFEPOWERHOLD "|" D_CMND_SAFEPOWERWINDOW "|" D_CMND_SAFEPOWER "|" D_CMND_SAFEPOWERHOLD "|" D_CMND_SAFEPOWERWINDOW "|"
D_CMND_ENERGYTODAY "|" D_CMND_ENERGYYESTERDAY "|" D_CMND_ENERGYTOTAL "|" D_CMND_ENERGYEXPORTACTIVE "|" D_CMND_ENERGYUSAGE "|" D_CMND_ENERGYEXPORT "|" D_CMND_TARIFF; D_CMND_ENERGYTODAY "|" D_CMND_ENERGYYESTERDAY "|" D_CMND_ENERGYTOTAL "|" D_CMND_ENERGYEXPORTACTIVE "|" D_CMND_ENERGYUSAGE "|" D_CMND_ENERGYEXPORT "|"
D_CMND_TARIFF "|" D_CMND_ENERGYDISPLAY "|" D_CMND_ENERGYCOLS ;
void (* const EnergyCommand[])(void) PROGMEM = { void (* const EnergyCommand[])(void) PROGMEM = {
&CmndPowerCal, &CmndVoltageCal, &CmndCurrentCal, &CmndFrequencyCal, &CmndPowerCal, &CmndVoltageCal, &CmndCurrentCal, &CmndFrequencyCal,
@ -70,7 +79,8 @@ void (* const EnergyCommand[])(void) PROGMEM = {
&CmndMaxEnergy, &CmndMaxEnergyStart, &CmndMaxEnergy, &CmndMaxEnergyStart,
&CmndMaxPower, &CmndMaxPowerHold, &CmndMaxPowerWindow, &CmndMaxPower, &CmndMaxPowerHold, &CmndMaxPowerWindow,
&CmndSafePower, &CmndSafePowerHold, &CmndSafePowerWindow, &CmndSafePower, &CmndSafePowerHold, &CmndSafePowerWindow,
&CmndEnergyToday, &CmndEnergyYesterday, &CmndEnergyTotal, &CmndEnergyExportActive, &CmndEnergyUsage, &CmndEnergyExport, &CmndTariff}; &CmndEnergyToday, &CmndEnergyYesterday, &CmndEnergyTotal, &CmndEnergyExportActive, &CmndEnergyUsage, &CmndEnergyExport,
&CmndTariff, &CmndEnergyDisplay, &CmndEnergyCols };
/********************************************************************************************/ /********************************************************************************************/
@ -81,16 +91,40 @@ typedef struct {
float last_usage_total_kWh; float last_usage_total_kWh;
} tEnergyUsage; } tEnergyUsage;
typedef union {
uint16_t data;
struct {
uint16_t spare00 : 1; // bit 0
uint16_t spare01 : 1; // bit 1
uint16_t spare02 : 1; // bit 2
uint16_t spare03 : 1; // bit 3
uint16_t spare04 : 1; // bit 4
uint16_t spare05 : 1; // bit 5
uint16_t spare06 : 1; // bit 6
uint16_t spare07 : 1; // bit 7
uint16_t spare08 : 1; // bit 8
uint16_t spare09 : 1; // bit 9
uint16_t spare10 : 1; // bit 10
uint16_t spare11 : 1; // bit 11
uint16_t spare12 : 1; // bit 12
uint16_t spare13 : 1; // bit 13
uint16_t spare14 : 1; // bit 14
uint16_t spare15 : 1; // bit 15
};
} tEnergyBitfield;
typedef struct { typedef struct {
uint32_t crc32; // To detect file changes uint32_t crc32; // To detect file changes
uint16_t version; // To detect driver function changes uint16_t version; // To detect driver function changes
uint16_t energy_kWhdoy; uint16_t energy_kWhdoy;
uint32_t energy_kWhtotal_time; uint32_t energy_kWhtotal_time;
uint32_t spare1; tEnergyBitfield flag;
uint32_t spare2; uint8_t gui_display; // EnergyDisplay - GUI display all relays (0), only powered on relays (1) or user selected relays (2)
uint32_t spare3; uint8_t gui_cols; // EnergyCols
uint32_t spare4; uint32_t spare32_1;
uint32_t spare5; uint32_t spare32_2;
uint32_t spare32_3;
uint32_t spare32_4;
uint32_t power_calibration[ENERGY_MAX_PHASES]; uint32_t power_calibration[ENERGY_MAX_PHASES];
uint32_t voltage_calibration[ENERGY_MAX_PHASES]; uint32_t voltage_calibration[ENERGY_MAX_PHASES];
@ -137,36 +171,39 @@ typedef struct {
float daily_sum_import_balanced; // 123.123 kWh float daily_sum_import_balanced; // 123.123 kWh
float daily_sum_export_balanced; // 123.123 kWh float daily_sum_export_balanced; // 123.123 kWh
uint16_t power_history[ENERGY_MAX_PHASES][3];
uint16_t mplh_counter;
uint16_t mplw_counter;
uint8_t data_valid[ENERGY_MAX_PHASES];
uint8_t phase_count; // Number of phases active
uint8_t fifth_second; uint8_t fifth_second;
uint8_t command_code; uint8_t command_code;
uint8_t data_valid[ENERGY_MAX_PHASES]; uint8_t power_steady_counter; // Allow for power on stabilization
uint8_t mplr_counter;
uint8_t max_energy_state;
uint8_t gui_indirect[ENERGY_MAX_PHASES];
uint8_t gui_rotate;
uint8_t gui_count;
uint8_t gui_offset;
uint8_t phase_count; // Number of phases active
bool voltage_common; // Use common voltage bool voltage_common; // Use common voltage
bool frequency_common; // Use common frequency bool frequency_common; // Use common frequency
bool use_overtemp; // Use global temperature as overtemp trigger on internal energy monitor hardware bool use_overtemp; // Use global temperature as overtemp trigger on internal energy monitor hardware
bool kWhtoday_offset_init; bool kWhtoday_offset_init;
bool voltage_available; // Enable if voltage is measured bool voltage_available; // Enable if voltage is measured
bool current_available; // Enable if current is measured bool current_available; // Enable if current is measured
bool local_energy_active_export; // Enable if support for storing energy_active bool local_energy_active_export; // Enable if support for storing energy_active
bool type_dc; bool type_dc;
bool power_on; bool power_on;
uint16_t power_history[ENERGY_MAX_PHASES][3];
uint8_t power_steady_counter; // Allow for power on stabilization
bool min_power_flag; bool min_power_flag;
bool max_power_flag; bool max_power_flag;
bool min_voltage_flag; bool min_voltage_flag;
bool max_voltage_flag; bool max_voltage_flag;
bool min_current_flag; bool min_current_flag;
bool max_current_flag; bool max_current_flag;
uint16_t mplh_counter;
uint16_t mplw_counter;
uint8_t mplr_counter;
uint8_t max_energy_state;
} tEnergy; } tEnergy;
tEnergy *Energy = nullptr; tEnergy *Energy = nullptr;
@ -240,7 +277,7 @@ bool EnergyRtcSettingsValid(void) {
* Driver Settings load and save using filesystem * Driver Settings load and save using filesystem
\*********************************************************************************************/ \*********************************************************************************************/
const uint32_t XDRV_03_VERSION = 0x0101; // Latest driver version (See settings deltas below) const uint32_t XDRV_03_VERSION = 0x0102; // Latest driver version (See settings deltas below)
void EnergySettingsLoad(void) { void EnergySettingsLoad(void) {
// *** Start init default values in case file is not found *** // *** Start init default values in case file is not found ***
@ -270,6 +307,11 @@ void EnergySettingsLoad(void) {
Energy->Settings.power_delta[i] = (float)(Settings->energy_power_delta[i]); Energy->Settings.power_delta[i] = (float)(Settings->energy_power_delta[i]);
} }
// v0102 additions
Energy->Settings.gui_display = ENERGY_GUI_DISPLAY_MODE;
Energy->Settings.gui_cols = ENERGY_GUI_MAX_COLS;
// *** End Init default values *** // *** End Init default values ***
#ifndef USE_UFILESYS #ifndef USE_UFILESYS
@ -283,6 +325,10 @@ void EnergySettingsLoad(void) {
if (Energy->Settings.version != XDRV_03_VERSION) { // Fix version dependent changes if (Energy->Settings.version != XDRV_03_VERSION) { // Fix version dependent changes
// *** Start fix possible setting deltas *** // *** Start fix possible setting deltas ***
if (Energy->Settings.version < 0x0102) {
Energy->Settings.gui_display = ENERGY_GUI_DISPLAY_MODE;
Energy->Settings.gui_cols = ENERGY_GUI_MAX_COLS;
}
// *** End setting deltas *** // *** End setting deltas ***
@ -346,7 +392,7 @@ char* EnergyFormat(char* result, float* input, uint32_t resolution, uint32_t sin
} }
result[0] = '\0'; result[0] = '\0';
for (uint32_t i = 0; i < index; i++) { for (uint32_t i = 0; i < index; i++) {
ext_snprintf_P(result, TOPSZ, PSTR("%s%s%*_f%s"), result, (0==i)?(1==index)?"":"[":",", resolution, &input[i], (index-1==i)?(1==index)?"":"]":""); ext_snprintf_P(result, GUISZ, PSTR("%s%s%*_f%s"), result, (0==i)?(1==index)?"":"[":",", resolution, &input[i], (index-1==i)?(1==index)?"":"]":"");
} }
return result; return result;
} }
@ -372,20 +418,20 @@ char* WebEnergyFormat(char* result, float* input, uint32_t resolution, uint32_t
} }
#ifdef USE_ENERGY_COLUMN_GUI #ifdef USE_ENERGY_COLUMN_GUI
ext_snprintf_P(result, GUISZ, PSTR("</td>")); // Skip first column ext_snprintf_P(result, GUISZ, PSTR("</td>")); // Skip first column
if ((Energy->phase_count > 1) && single) { // Need to set colspan so need new columns if ((Energy->gui_count > 1) && single) { // Need to set colspan so need new columns
// </td><td colspan='3' style='text-align:right'>1.23</td><td>&nbsp;</td><td> // </td><td colspan='3' style='text-align:right'>1.23</td><td>&nbsp;</td><td>
// </td><td colspan='5' style='text-align:right'>1.23</td><td>&nbsp;</td><td> // </td><td colspan='5' style='text-align:right'>1.23</td><td>&nbsp;</td><td>
// </td><td colspan='7' style='text-align:right'>1.23</td><td>&nbsp;</td><td> // </td><td colspan='7' style='text-align:right'>1.23</td><td>&nbsp;</td><td>
ext_snprintf_P(result, GUISZ, PSTR("%s<td colspan='%d' style='text-align:%s'>%*_f</td><td>&nbsp;</td>"), ext_snprintf_P(result, GUISZ, PSTR("%s<td colspan='%d' style='text-align:%s'>%*_f</td><td>&nbsp;</td>"),
result, (Energy->phase_count *2) -1, (Settings->flag5.gui_table_align)?PSTR("right"):PSTR("center"), resolution, &input[0]); result, (Energy->gui_count *2) -1, (Settings->flag5.gui_table_align)?PSTR("right"):PSTR("center"), resolution, &input[Energy->gui_indirect[0]]);
} else { } else {
// </td><td style='text-align:right'>1.23</td><td>&nbsp;</td><td> // </td><td style='text-align:right'>1.23</td><td>&nbsp;</td><td>
// </td><td style='text-align:right'>1.23</td><td>&nbsp;</td><td style='text-align:right'>1.23</td><td>&nbsp;</td><td> // </td><td style='text-align:right'>1.23</td><td>&nbsp;</td><td style='text-align:right'>1.23</td><td>&nbsp;</td><td>
// </td><td style='text-align:right'>1.23</td><td>&nbsp;</td><td style='text-align:right'>1.23</td><td>&nbsp;</td><td style='text-align:right'>1.23</td><td>&nbsp;</td><td> // </td><td style='text-align:right'>1.23</td><td>&nbsp;</td><td style='text-align:right'>1.23</td><td>&nbsp;</td><td style='text-align:right'>1.23</td><td>&nbsp;</td><td>
// </td><td style='text-align:right'>1.23</td><td>&nbsp;</td><td style='text-align:right'>1.23</td><td>&nbsp;</td><td style='text-align:right'>1.23</td><td>&nbsp;</td><td style='text-align:right'>1.23</td><td>&nbsp;</td><td> // </td><td style='text-align:right'>1.23</td><td>&nbsp;</td><td style='text-align:right'>1.23</td><td>&nbsp;</td><td style='text-align:right'>1.23</td><td>&nbsp;</td><td style='text-align:right'>1.23</td><td>&nbsp;</td><td>
for (uint32_t i = 0; i < Energy->phase_count; i++) { for (uint32_t i = 0; i < Energy->gui_count; i++) {
ext_snprintf_P(result, GUISZ, PSTR("%s<td style='text-align:%s'>%*_f</td><td>&nbsp;</td>"), ext_snprintf_P(result, GUISZ, PSTR("%s<td style='text-align:%s'>%*_f</td><td>&nbsp;</td>"),
result, (Settings->flag5.gui_table_align)?PSTR("right"):PSTR("left"), resolution, &input[i]); result, (Settings->flag5.gui_table_align)?PSTR("right"):PSTR("left"), resolution, &input[Energy->gui_indirect[Energy->gui_offset +i]]);
} }
} }
ext_snprintf_P(result, GUISZ, PSTR("%s<td>"), result); ext_snprintf_P(result, GUISZ, PSTR("%s<td>"), result);
@ -665,7 +711,7 @@ void EnergyMarginCheck(void) {
for (uint32_t phase = 0; phase < Energy->phase_count; phase++) { for (uint32_t phase = 0; phase < Energy->phase_count; phase++) {
power_diff_f[phase] = power_diff[phase]; power_diff_f[phase] = power_diff[phase];
} }
char value_chr[TOPSZ]; char value_chr[GUISZ];
ResponseAppend_P(PSTR("\"" D_CMND_POWERDELTA "\":%s"), EnergyFormat(value_chr, power_diff_f, 0)); ResponseAppend_P(PSTR("\"" D_CMND_POWERDELTA "\":%s"), EnergyFormat(value_chr, power_diff_f, 0));
} }
@ -833,9 +879,9 @@ void EnergyEverySecond(void) {
\*********************************************************************************************/ \*********************************************************************************************/
void ResponseCmndEnergyTotalYesterdayToday(void) { void ResponseCmndEnergyTotalYesterdayToday(void) {
char value_chr[TOPSZ]; // Used by EnergyFormatIndex char value_chr[GUISZ]; // Used by EnergyFormatIndex
char value2_chr[TOPSZ]; char value2_chr[GUISZ];
char value3_chr[TOPSZ]; char value3_chr[GUISZ];
float energy_yesterday_kWh[3]; float energy_yesterday_kWh[3];
for (uint32_t i = 0; i < Energy->phase_count; i++) { for (uint32_t i = 0; i < Energy->phase_count; i++) {
@ -858,6 +904,26 @@ void ResponseCmndEnergyTotalYesterdayToday(void) {
ResponseJsonEndEnd(); ResponseJsonEndEnd();
} }
void CmndEnergyDisplay(void) {
// Select either all relays, only powered on relays or user selected relay group
// EnergyDisplay 1, EnergyDisplay 2 or EnergyDisplay 3
if ((XdrvMailbox.payload > ENERGY_DISPLAY_MIN_OPTION) && (XdrvMailbox.payload < ENERGY_DISPLAY_MAX_OPTION)) {
Energy->Settings.gui_display = XdrvMailbox.payload;
Energy->gui_rotate = 0;
}
ResponseCmndNumber(Energy->Settings.gui_display);
}
void CmndEnergyCols(void) {
// Select number of columns
// EnergyCols 1..8
if ((XdrvMailbox.payload > 0) && (XdrvMailbox.payload <= ENERGY_MAX_PHASES)) {
Energy->Settings.gui_cols = XdrvMailbox.payload;
Energy->gui_rotate = 0;
}
ResponseCmndNumber(Energy->Settings.gui_cols);
}
void CmndEnergyTotal(void) { void CmndEnergyTotal(void) {
uint32_t values[2] = { 0 }; uint32_t values[2] = { 0 };
uint32_t params = ParseParameters(2, values); uint32_t params = ParseParameters(2, values);
@ -1536,55 +1602,114 @@ void EnergyShow(bool json) {
#ifdef USE_WEBSERVER #ifdef USE_WEBSERVER
} else { } else {
#ifdef USE_ENERGY_COLUMN_GUI #ifdef USE_ENERGY_COLUMN_GUI
// Need a new table supporting more columns using empty columns (with &nbsp; in data rows) as easy column spacing uint8_t relays[ENERGY_MAX_PHASES];
// {s}</th><th></th><th>Head1</th><th></th><td>{e} uint32_t relay_show = 0;
// {s}</th><th></th><th>Head1</th><th></th><th>Head2</th><th></th><td>{e} power_t power = TasmotaGlobal.power;
// {s}</th><th></th><th>Head1</th><th></th><th>Head2</th><th></th><th>Head3</th><th></th><td>{e} for (uint32_t i = 0; i < Energy->phase_count; i++) { // Init relays and gui_indirect tables based on EnergyDisplay
// {s}</th><th></th><th>Head1</th><th></th><th>Head2</th><th></th><th>Head3</th><th></th><th>Head4</th><th></th><td>{e} if ((ENERGY_DISPLAY_ROTATE == Energy->Settings.gui_display) ||
WSContentSend_P(PSTR("</table><hr/>{t}{s}</th><th></th>")); // First column is empty ({t} = <table style='width:100%'>, {s} = <tr><th>) ((ENERGY_DISPLAY_ROTATE_POWERED_ON == Energy->Settings.gui_display) && (power >> i) &1) ||
bool label_o = Energy->voltage_common; (ENERGY_DISPLAY_TABS == Energy->Settings.gui_display)) {
bool no_label = (1 == Energy->phase_count); relays[relay_show] = i +1;
for (uint32_t i = 0; i < Energy->phase_count; i++) { Energy->gui_indirect[relay_show] = i;
WSContentSend_P(PSTR("<th style='text-align:center'>%s%s<th></th>"), (no_label)?"":(label_o)?"O":"L", (no_label)?"":itoa(i +1, value_chr, 10)); relay_show++;
}
} }
WSContentSend_P(PSTR("<td>{e}")); // Last column is units ({e} = </td></tr>)
if (relay_show) {
if (Energy->Settings.gui_display != ENERGY_DISPLAY_TABS) {
if (relay_show > Energy->Settings.gui_cols) {
Energy->gui_rotate++;
} else {
Energy->gui_rotate = 0;
}
}
if (Energy->gui_rotate >= relay_show) {
Energy->gui_rotate = 0;
}
Energy->gui_offset = (Energy->gui_rotate / Energy->Settings.gui_cols) * Energy->Settings.gui_cols;
Energy->gui_count = relay_show - Energy->gui_offset;
if (Energy->gui_count > Energy->Settings.gui_cols) { Energy->gui_count = Energy->Settings.gui_cols; }
WSContentSend_P(PSTR("</table><hr/>")); // Close current table as we will use different column count
bool label_o = Energy->voltage_common;
if (ENERGY_DISPLAY_TABS == Energy->Settings.gui_display) {
uint32_t tabs = (relay_show -1 + Energy->Settings.gui_cols) / Energy->Settings.gui_cols;
if (tabs > 1) {
WSContentSend_P(PSTR("{t}<tr>")); // {t} = <table style='width:100%'>
uint32_t cols_width = 100 / tabs;
uint32_t current_tab = Energy->gui_rotate / Energy->Settings.gui_cols;
for (uint32_t idx = 0; idx < tabs; idx++) {
WSContentSend_P(PSTR("<td style='width:%d%%'><button style='border-radius:0;background:#%06X;font-weight:%s' onclick='la(\"&k03=%d\");'>%s%d</button></td>"), // &k03 is related to WebGetArg("k", tmp, sizeof(tmp));
cols_width,
(current_tab == idx) ? WebColor(COL_BACKGROUND) : WebColor(COL_FORM),
(current_tab == idx) ? "bold" : "normal",
idx,
(label_o) ? "O" : "L", (idx *Energy->Settings.gui_cols) +1);
}
WSContentSend_P(PSTR("</tr></table>")); // Close current table as we will use different column count
}
}
// {s}</th><th></th><th>Head1</th><th></th><td>{e}
// {s}</th><th></th><th>Head1</th><th></th><th>Head2</th><th></th><td>{e}
// {s}</th><th></th><th>Head1</th><th></th><th>Head2</th><th></th><th>Head3</th><th></th><td>{e}
// {s}</th><th></th><th>Head1</th><th></th><th>Head2</th><th></th><th>Head3</th><th></th><th>Head4</th><th></th><td>{e}
WSContentSend_P(PSTR("{t}{s}</th><th></th>")); // First column is empty ({t} = <table style='width:100%'>, {s} = <tr><th>)
bool no_label = (1 == Energy->phase_count);
for (uint32_t i = 0; i < Energy->gui_count; i++) {
WSContentSend_P(PSTR("<th style='text-align:center'>%s%s<th></th>"),
(no_label) ? "" : (label_o) ? "O" : "L",
(no_label) ? "" : itoa(relays[Energy->gui_offset +i], value_chr, 10));
}
WSContentSend_P(PSTR("<td>{e}")); // Last column is units ({e} = </td></tr>)
#endif // USE_ENERGY_COLUMN_GUI #endif // USE_ENERGY_COLUMN_GUI
if (Energy->voltage_available) { if (Energy->voltage_available) {
WSContentSend_PD(HTTP_SNS_VOLTAGE, WebEnergyFormat(value_chr, Energy->voltage, Settings->flag2.voltage_resolution, Energy->voltage_common)); WSContentSend_PD(HTTP_SNS_VOLTAGE, WebEnergyFormat(value_chr, Energy->voltage, Settings->flag2.voltage_resolution, Energy->voltage_common));
}
if (!Energy->type_dc) {
if (!isnan(Energy->frequency[0])) {
WSContentSend_PD(PSTR("{s}" D_FREQUENCY "{m}%s " D_UNIT_HERTZ "{e}"),
WebEnergyFormat(value_chr, Energy->frequency, Settings->flag2.frequency_resolution, Energy->frequency_common));
} }
} if (!Energy->type_dc) {
if (Energy->current_available) { if (!isnan(Energy->frequency[0])) {
WSContentSend_PD(HTTP_SNS_CURRENT, WebEnergyFormat(value_chr, Energy->current, Settings->flag2.current_resolution)); WSContentSend_PD(PSTR("{s}" D_FREQUENCY "{m}%s " D_UNIT_HERTZ "{e}"),
} WebEnergyFormat(value_chr, Energy->frequency, Settings->flag2.frequency_resolution, Energy->frequency_common));
WSContentSend_PD(HTTP_SNS_POWER, WebEnergyFormat(value_chr, Energy->active_power, Settings->flag2.wattage_resolution)); }
if (!Energy->type_dc) {
if (Energy->current_available && Energy->voltage_available) {
WSContentSend_PD(HTTP_ENERGY_SNS1, WebEnergyFormat(value_chr, apparent_power, Settings->flag2.wattage_resolution),
WebEnergyFormat(value2_chr, reactive_power, Settings->flag2.wattage_resolution),
WebEnergyFormat(value3_chr, power_factor, 2));
} }
} if (Energy->current_available) {
WSContentSend_PD(HTTP_ENERGY_SNS2, WebEnergyFormat(value_chr, Energy->daily_kWh, Settings->flag2.energy_resolution, 2), WSContentSend_PD(HTTP_SNS_CURRENT, WebEnergyFormat(value_chr, Energy->current, Settings->flag2.current_resolution));
WebEnergyFormat(value2_chr, energy_yesterday_kWh, Settings->flag2.energy_resolution, 2), }
WebEnergyFormat(value3_chr, Energy->total, Settings->flag2.energy_resolution, 2)); WSContentSend_PD(HTTP_SNS_POWER, WebEnergyFormat(value_chr, Energy->active_power, Settings->flag2.wattage_resolution));
if (!isnan(Energy->export_active[0])) { if (!Energy->type_dc) {
uint32_t single = (!isnan(Energy->export_active[1]) && !isnan(Energy->export_active[2])) ? 2 : 1; if (Energy->current_available && Energy->voltage_available) {
WSContentSend_PD(HTTP_ENERGY_SNS3, WebEnergyFormat(value_chr, Energy->export_active, Settings->flag2.energy_resolution, single)); WSContentSend_PD(HTTP_ENERGY_SNS1, WebEnergyFormat(value_chr, apparent_power, Settings->flag2.wattage_resolution),
} WebEnergyFormat(value2_chr, reactive_power, Settings->flag2.wattage_resolution),
WebEnergyFormat(value3_chr, power_factor, 2));
}
}
WSContentSend_PD(HTTP_ENERGY_SNS2, WebEnergyFormat(value_chr, Energy->daily_kWh, Settings->flag2.energy_resolution, 2),
WebEnergyFormat(value2_chr, energy_yesterday_kWh, Settings->flag2.energy_resolution, 2),
WebEnergyFormat(value3_chr, Energy->total, Settings->flag2.energy_resolution, 2));
if (!isnan(Energy->export_active[0])) {
uint32_t single = (!isnan(Energy->export_active[1]) && !isnan(Energy->export_active[2])) ? 2 : 1;
WSContentSend_PD(HTTP_ENERGY_SNS3, WebEnergyFormat(value_chr, Energy->export_active, Settings->flag2.energy_resolution, single));
}
#ifdef USE_ENERGY_COLUMN_GUI #ifdef USE_ENERGY_COLUMN_GUI
XnrgCall(FUNC_WEB_COL_SENSOR); XnrgCall(FUNC_WEB_COL_SENSOR);
WSContentSend_P(PSTR("</table><hr/>{t}")); // {t} = <table style='width:100%'> - Define for next FUNC_WEB_SENSOR WSContentSend_P(PSTR("</table><hr/>{t}")); // {t} = <table style='width:100%'> - Define for next FUNC_WEB_SENSOR
#endif // USE_ENERGY_COLUMN_GUI #endif // USE_ENERGY_COLUMN_GUI
XnrgCall(FUNC_WEB_SENSOR); XnrgCall(FUNC_WEB_SENSOR);
#endif // USE_WEBSERVER #endif // USE_WEBSERVER
}
} }
} }
#ifdef USE_WEBSERVER
void EnergyWebGetArg(void) {
char tmp[8]; // WebGetArg numbers only
WebGetArg(PSTR("k03"), tmp, sizeof(tmp)); // relay gtoups
if (strlen(tmp)) { Energy->gui_rotate = atoi(tmp) * Energy->Settings.gui_cols; }
}
#endif // USE_WEBSERVER
/*********************************************************************************************\ /*********************************************************************************************\
* Interface * Interface
\*********************************************************************************************/ \*********************************************************************************************/
@ -1650,6 +1775,9 @@ bool Xsns03(uint32_t function)
case FUNC_WEB_SENSOR: case FUNC_WEB_SENSOR:
EnergyShow(false); EnergyShow(false);
break; break;
case FUNC_WEB_GET_ARG:
EnergyWebGetArg();
break;
#endif // USE_WEBSERVER #endif // USE_WEBSERVER
case FUNC_SAVE_BEFORE_RESTART: case FUNC_SAVE_BEFORE_RESTART:
EnergySaveState(); EnergySaveState();

View File

@ -660,7 +660,7 @@ void Ade7953DrvInit(void) {
if (PinUsed(GPIO_ADE7953_IRQ, GPIO_ANY)) { // Irq is not supported... if (PinUsed(GPIO_ADE7953_IRQ, GPIO_ANY)) { // Irq is not supported...
uint32_t pin_irq = Pin(GPIO_ADE7953_IRQ, GPIO_ANY); uint32_t pin_irq = Pin(GPIO_ADE7953_IRQ, GPIO_ANY);
pinMode(pin_irq, INPUT); // Related to resetPins() - Must be set to input pinMode(pin_irq, INPUT); // Related to resetPins() - Must be set to input
// 0 (1 = Shelly 2.5), 1 (2 = Shelly EM), 2 (3 = Shelly Plus 2PM), 3 (4 = Shelly Pro 1PM), 4 (5 = Shelly Pro 2PM) // 0 (1 = Shelly 2.5), 1 (2 = Shelly EM), 2 (3 = Shelly Plus 2PM), 3 (4 = Shelly Pro 1PM), 4 (5 = Shelly Pro 2PM), 5 (6 = Shelly Pro 4PM)
Ade7953.model = GetPin(pin_irq) - AGPIO(GPIO_ADE7953_IRQ); Ade7953.model = GetPin(pin_irq) - AGPIO(GPIO_ADE7953_IRQ);
int pin_reset = Pin(GPIO_ADE7953_RST); // -1 if not defined int pin_reset = Pin(GPIO_ADE7953_RST); // -1 if not defined