diff --git a/data/config.html b/data/config.html
index cf69e25..714aed4 100644
--- a/data/config.html
+++ b/data/config.html
@@ -166,9 +166,10 @@
YaSolR Configuration
relay2_type: ["Relay 2 Type", "select", "NO,NC"],
"PID Calibration": "TITLE",
+ pid_view_enable: ["View PID Data", "switch"],
pid_pmode: ["Proportional Mode (1: on Error, 2: on Input, 3: Both)", "select", "1,2,3"],
pid_dmode: ["Derivative Mode (1: on Error, 2: on Input)", "select", "1,2"],
- pid_icmode: ["Integral Correction Mode (0: Off, 1: Clamp, 2: Anti-windup)", "select", "0,1,2"],
+ pid_icmode: ["Integral Correction (0: Off, 1: Clamp, 2: Anti-windup)", "select", "0,1,2"],
pid_setpoint: ["Setpoint (Target Grid Power)", "float"],
pid_kp: ["Kp", "float"],
pid_ki: ["Ki", "float"],
diff --git a/include/YaSolRDefines.h b/include/YaSolRDefines.h
index 516ff4c..e91af92 100644
--- a/include/YaSolRDefines.h
+++ b/include/YaSolRDefines.h
@@ -55,6 +55,14 @@
#define YASOLR_SERIAL_BAUDRATE 115200
#define YASOLR_WEEK_DAYS "sun,mon,tue,wed,thu,fri,sat"
#define YASOLR_GRAPH_POINTS 120
+#define YASOLR_PID_P_MODE_1 "1: on Error"
+#define YASOLR_PID_P_MODE_2 "2: on Input"
+#define YASOLR_PID_P_MODE_3 "3: Both"
+#define YASOLR_PID_D_MODE_1 YASOLR_PID_P_MODE_1
+#define YASOLR_PID_D_MODE_2 YASOLR_PID_P_MODE_2
+#define YASOLR_PID_IC_MODE_0 "0: Off"
+#define YASOLR_PID_IC_MODE_1 "1: Clamp"
+#define YASOLR_PID_IC_MODE_2 "2: Anti-windup"
// UDP communication
@@ -87,6 +95,7 @@
#define KEY_ENABLE_OUTPUT2_DS18 "o2_ds18_enable"
#define KEY_ENABLE_OUTPUT2_PZEM "o2_pzem_enable"
#define KEY_ENABLE_OUTPUT2_RELAY "o2_relay_enable"
+#define KEY_ENABLE_PID_VIEW "pid_view_enable"
#define KEY_ENABLE_RELAY1 "relay1_enable"
#define KEY_ENABLE_RELAY2 "relay2_enable"
#define KEY_ENABLE_ZCD "zcd_enable"
@@ -130,15 +139,15 @@
#define KEY_OUTPUT2_TIME_START "o2_time_start"
#define KEY_OUTPUT2_TIME_STOP "o2_time_stop"
#define KEY_OUTPUT2_TIME_STOP "o2_time_stop"
+#define KEY_PID_P_MODE "pid_pmode"
#define KEY_PID_D_MODE "pid_dmode"
#define KEY_PID_IC_MODE "pid_icmode"
+#define KEY_PID_SETPOINT "pid_setpoint"
#define KEY_PID_KD "pid_kd"
#define KEY_PID_KI "pid_ki"
#define KEY_PID_KP "pid_kp"
#define KEY_PID_OUT_MAX "pid_out_max"
#define KEY_PID_OUT_MIN "pid_out_min"
-#define KEY_PID_P_MODE "pid_pmode"
-#define KEY_PID_SETPOINT "pid_setpoint"
#define KEY_RELAY1_LOAD "relay1_load"
#define KEY_RELAY1_TYPE "relay1_type"
#define KEY_RELAY2_LOAD "relay2_load"
diff --git a/include/YaSolRWebsite.h b/include/YaSolRWebsite.h
index 858a889..778267f 100644
--- a/include/YaSolRWebsite.h
+++ b/include/YaSolRWebsite.h
@@ -25,8 +25,12 @@ namespace YaSolR {
void initCards();
void updateCards();
void updateCharts();
+ void updatePID();
+ void resetPID();
private:
+ int _historyX[YASOLR_GRAPH_POINTS] = {0};
+
// statistics
Statistic _appName = Statistic(&dashboard, YASOLR_LBL_001);
Statistic _appModel = Statistic(&dashboard, YASOLR_LBL_002);
@@ -87,7 +91,6 @@ namespace YaSolR {
Card _gridPower = Card(&dashboard, ENERGY_CARD, YASOLR_LBL_044, "W");
Card _routerDS18State = Card(&dashboard, TEMPERATURE_CARD, YASOLR_LBL_045, "°C");
- int _historyX[YASOLR_GRAPH_POINTS] = {0};
int _gridPowerHistoryY[YASOLR_GRAPH_POINTS] = {0};
int _routedPowerHistoryY[YASOLR_GRAPH_POINTS] = {0};
int _routerTHDiHistoryY[YASOLR_GRAPH_POINTS] = {0};
@@ -245,11 +248,41 @@ namespace YaSolR {
Card _output2RelayType = Card(&dashboard, DROPDOWN_CARD, YASOLR_LBL_150);
Card _relay1Type = Card(&dashboard, DROPDOWN_CARD, YASOLR_LBL_151);
Card _relay2Type = Card(&dashboard, DROPDOWN_CARD, YASOLR_LBL_152);
+
+ Tab _pidTab = Tab(&dashboard, "\u2699 " YASOLR_LBL_159);
+ Card _pidView = Card(&dashboard, BUTTON_CARD, YASOLR_LBL_169);
+ Card _pidPMode = Card(&dashboard, DROPDOWN_CARD, YASOLR_LBL_160);
+ Card _pidDMode = Card(&dashboard, DROPDOWN_CARD, YASOLR_LBL_161);
+ Card _pidICMode = Card(&dashboard, DROPDOWN_CARD, YASOLR_LBL_162);
+ Card _pidSetpoint = Card(&dashboard, TEXT_INPUT_CARD, YASOLR_LBL_163);
+ Card _pidKp = Card(&dashboard, TEXT_INPUT_CARD, YASOLR_LBL_166);
+ Card _pidKi = Card(&dashboard, TEXT_INPUT_CARD, YASOLR_LBL_167);
+ Card _pidKd = Card(&dashboard, TEXT_INPUT_CARD, YASOLR_LBL_168);
+ Card _pidOutMin = Card(&dashboard, TEXT_INPUT_CARD, YASOLR_LBL_164);
+ Card _pidOutMax = Card(&dashboard, TEXT_INPUT_CARD, YASOLR_LBL_165);
+ Card _pidReset = Card(&dashboard, PUSH_BUTTON_CARD, YASOLR_LBL_177);
+
+ // input,output,error,pTerm,iTerm,dTerm,sum
+ int _pidInputHistoryY[YASOLR_GRAPH_POINTS] = {0};
+ int _pidOutputHistoryY[YASOLR_GRAPH_POINTS] = {0};
+ int _pidErrorHistoryY[YASOLR_GRAPH_POINTS] = {0};
+ int _pidPTermHistoryY[YASOLR_GRAPH_POINTS] = {0};
+ int _pidITermHistoryY[YASOLR_GRAPH_POINTS] = {0};
+ int _pidDTermHistoryY[YASOLR_GRAPH_POINTS] = {0};
+ int _pidSumHistoryY[YASOLR_GRAPH_POINTS] = {0};
+ Chart _pidInputHistory = Chart(&dashboard, LINE_CHART, YASOLR_LBL_170);
+ Chart _pidOutputHistory = Chart(&dashboard, LINE_CHART, YASOLR_LBL_171);
+ Chart _pidErrorHistory = Chart(&dashboard, LINE_CHART, YASOLR_LBL_172);
+ Chart _pidSumHistory = Chart(&dashboard, BAR_CHART, YASOLR_LBL_173);
+ Chart _pidPTermHistory = Chart(&dashboard, LINE_CHART, YASOLR_LBL_174);
+ Chart _pidITermHistory = Chart(&dashboard, LINE_CHART, YASOLR_LBL_175);
+ Chart _pidDTermHistory = Chart(&dashboard, LINE_CHART, YASOLR_LBL_176);
#endif
private:
void _boolConfig(Card& card, const char* key);
void _daysConfig(Card& card, const char* key);
+ void _floatConfig(Card& card, const char* key);
void _numConfig(Card& card, const char* key);
void _pinConfig(Card& card, const char* key);
void _passwordConfig(Card& card, const char* key);
diff --git a/include/i18n/en.h b/include/i18n/en.h
index 9f08d2c..40d0f33 100644
--- a/include/i18n/en.h
+++ b/include/i18n/en.h
@@ -162,25 +162,25 @@
#define YASOLR_LBL_156 "I/O"
#define YASOLR_LBL_157 "JSY Remote UDP: Message Rate"
#define YASOLR_LBL_158 "Grid Excess Remaining"
-#define YASOLR_LBL_159
-#define YASOLR_LBL_160
-#define YASOLR_LBL_161
-#define YASOLR_LBL_162
-#define YASOLR_LBL_163
-#define YASOLR_LBL_164
-#define YASOLR_LBL_165
-#define YASOLR_LBL_166
-#define YASOLR_LBL_167
-#define YASOLR_LBL_168
-#define YASOLR_LBL_169
-#define YASOLR_LBL_170
-#define YASOLR_LBL_171
-#define YASOLR_LBL_172
-#define YASOLR_LBL_173
-#define YASOLR_LBL_174
-#define YASOLR_LBL_175
-#define YASOLR_LBL_176
-#define YASOLR_LBL_177
+#define YASOLR_LBL_159 "PID Controller"
+#define YASOLR_LBL_160 "Proportional Mode"
+#define YASOLR_LBL_161 "Derivative Mode"
+#define YASOLR_LBL_162 "Integral Correction"
+#define YASOLR_LBL_164 "Output Min (W)"
+#define YASOLR_LBL_165 "Output Max (W)"
+#define YASOLR_LBL_163 "Setpoint (Target Grid Power in W)"
+#define YASOLR_LBL_166 "Kp"
+#define YASOLR_LBL_167 "Ki"
+#define YASOLR_LBL_168 "Kd"
+#define YASOLR_LBL_169 "Real-time PID Data"
+#define YASOLR_LBL_170 "Input (Grid Power, W)"
+#define YASOLR_LBL_171 "Output (Power to route, W)"
+#define YASOLR_LBL_172 "Error (W)"
+#define YASOLR_LBL_173 "Sum (W)"
+#define YASOLR_LBL_174 "P Term (W)"
+#define YASOLR_LBL_175 "I Term (W)"
+#define YASOLR_LBL_176 "D Term (W)"
+#define YASOLR_LBL_177 "Chart Reset"
#define YASOLR_LBL_178
#define YASOLR_LBL_179
#define YASOLR_LBL_180
diff --git a/include/i18n/fr.h b/include/i18n/fr.h
index 21ee8cd..e966a68 100644
--- a/include/i18n/fr.h
+++ b/include/i18n/fr.h
@@ -162,25 +162,25 @@
#define YASOLR_LBL_156 "E/S"
#define YASOLR_LBL_157 "JSY Remote UDP: Débit"
#define YASOLR_LBL_158 "Surplus réseau restant"
-#define YASOLR_LBL_159
-#define YASOLR_LBL_160
-#define YASOLR_LBL_161
-#define YASOLR_LBL_162
-#define YASOLR_LBL_163
-#define YASOLR_LBL_164
-#define YASOLR_LBL_165
-#define YASOLR_LBL_166
-#define YASOLR_LBL_167
-#define YASOLR_LBL_168
-#define YASOLR_LBL_169
-#define YASOLR_LBL_170
-#define YASOLR_LBL_171
-#define YASOLR_LBL_172
-#define YASOLR_LBL_173
-#define YASOLR_LBL_174
-#define YASOLR_LBL_175
-#define YASOLR_LBL_176
-#define YASOLR_LBL_177
+#define YASOLR_LBL_159 "Contrôleur PID"
+#define YASOLR_LBL_160 "Mode proportionnel"
+#define YASOLR_LBL_161 "Mode dérivé"
+#define YASOLR_LBL_162 "Correction d'Intégrale"
+#define YASOLR_LBL_164 "Sortie Min (W)"
+#define YASOLR_LBL_165 "Sortie Max (W)"
+#define YASOLR_LBL_163 "Consigne (puissance cible du réseau en W)"
+#define YASOLR_LBL_166 "Kp"
+#define YASOLR_LBL_167 "Ki"
+#define YASOLR_LBL_168 "Kd"
+#define YASOLR_LBL_169 "Données PID temps-réel"
+#define YASOLR_LBL_170 "Entrée (Puissance réseau, W)"
+#define YASOLR_LBL_171 "Sortie (Puissance à router, W)"
+#define YASOLR_LBL_172 "Erreur (W)"
+#define YASOLR_LBL_173 "Somme (W)"
+#define YASOLR_LBL_174 "Terme P (W)"
+#define YASOLR_LBL_175 "Terme I (W)"
+#define YASOLR_LBL_176 "Terme D (W)"
+#define YASOLR_LBL_177 "Réinitialisation graphique"
#define YASOLR_LBL_178
#define YASOLR_LBL_179
#define YASOLR_LBL_180
diff --git a/src/Website.cpp b/src/Website.cpp
index 73e7d6e..a613c09 100644
--- a/src/Website.cpp
+++ b/src/Website.cpp
@@ -16,18 +16,37 @@ void YaSolR::WebsiteClass::initLayout() {
for (int i = 0; i < YASOLR_GRAPH_POINTS; i++)
_historyX[i] = i - YASOLR_GRAPH_POINTS;
+ // overview
_gridPowerHistory.updateX(_historyX, YASOLR_GRAPH_POINTS);
_routedPowerHistory.updateX(_historyX, YASOLR_GRAPH_POINTS);
_routerTHDiHistory.updateX(_historyX, YASOLR_GRAPH_POINTS);
+ // PID
+ _pidInputHistory.updateX(_historyX, YASOLR_GRAPH_POINTS);
+ _pidOutputHistory.updateX(_historyX, YASOLR_GRAPH_POINTS);
+ _pidErrorHistory.updateX(_historyX, YASOLR_GRAPH_POINTS);
+ _pidPTermHistory.updateX(_historyX, YASOLR_GRAPH_POINTS);
+ _pidITermHistory.updateX(_historyX, YASOLR_GRAPH_POINTS);
+ _pidDTermHistory.updateX(_historyX, YASOLR_GRAPH_POINTS);
+ _pidSumHistory.updateX(_historyX, YASOLR_GRAPH_POINTS);
+
#ifdef APP_MODEL_PRO
dashboard.setChartAnimations(false);
- // graphs
+ // overview graphs
_gridPowerHistory.setSize(chartSize);
_routedPowerHistory.setSize(chartSize);
_routerTHDiHistory.setSize(chartSize);
+ // pid graphs
+ _pidInputHistory.setSize(chartSize);
+ _pidOutputHistory.setSize(chartSize);
+ _pidPTermHistory.setSize(chartSize);
+ _pidITermHistory.setSize(chartSize);
+ _pidDTermHistory.setSize(chartSize);
+ _pidErrorHistory.setSize(chartSize);
+ _pidSumHistory.setSize(chartSize);
+
// output 1 (status)
_output1State.setTab(&_output1Tab);
_output1DS18State.setTab(&_output1Tab);
@@ -145,6 +164,55 @@ void YaSolR::WebsiteClass::initLayout() {
_reset.attachCallback([] PUSH_BUTTON_CARD_CB { resetTask.resume(); });
_restart.attachCallback([] PUSH_BUTTON_CARD_CB { restartTask.resume(); });
+ // network (config)
+ _adminPwd.setTab(&_networkConfigTab);
+ _apMode.setTab(&_networkConfigTab);
+ _ntpServer.setTab(&_networkConfigTab);
+ _ntpSync.setTab(&_networkConfigTab);
+ _ntpTimezone.setTab(&_networkConfigTab);
+ _wifiPwd.setTab(&_networkConfigTab);
+ _wifiSSID.setTab(&_networkConfigTab);
+
+ _boolConfig(_apMode, KEY_ENABLE_AP_MODE);
+ _passwordConfig(_adminPwd, KEY_ADMIN_PASSWORD);
+ _passwordConfig(_wifiPwd, KEY_WIFI_PASSWORD);
+ _textConfig(_ntpServer, KEY_NTP_SERVER);
+ _textConfig(_ntpTimezone, KEY_NTP_TIMEZONE);
+ _textConfig(_wifiSSID, KEY_WIFI_SSID);
+
+ _ntpSync.attachCallback([](const char* value) {
+ const String str = String(value);
+ const size_t len = str.length();
+ const timeval tv = {str.substring(0, len - 3).toInt(), str.substring(len - 3).toInt()};
+ Mycila::NTP.sync(tv);
+ });
+
+ // mqtt (config)
+ _haDiscovery.setTab(&_mqttConfigTab);
+ _haDiscoveryTopic.setTab(&_mqttConfigTab);
+ _mqttGridPower.setTab(&_mqttConfigTab);
+ _mqttGridVoltage.setTab(&_mqttConfigTab);
+ _mqttPort.setTab(&_mqttConfigTab);
+ _mqttPublishInterval.setTab(&_mqttConfigTab);
+ _mqttPwd.setTab(&_mqttConfigTab);
+ _mqttSecured.setTab(&_mqttConfigTab);
+ _mqttServer.setTab(&_mqttConfigTab);
+ _mqttServerCert.setTab(&_mqttConfigTab);
+ _mqttTopic.setTab(&_mqttConfigTab);
+ _mqttUser.setTab(&_mqttConfigTab);
+
+ _boolConfig(_haDiscovery, KEY_ENABLE_HA_DISCOVERY);
+ _boolConfig(_mqttSecured, KEY_MQTT_SECURED);
+ _numConfig(_mqttPort, KEY_MQTT_PORT);
+ _passwordConfig(_mqttPwd, KEY_MQTT_PASSWORD);
+ _sliderConfig(_mqttPublishInterval, KEY_MQTT_PUBLISH_INTERVAL);
+ _textConfig(_haDiscoveryTopic, KEY_HA_DISCOVERY_TOPIC);
+ _textConfig(_mqttGridPower, KEY_GRID_POWER_MQTT_TOPIC);
+ _textConfig(_mqttGridVoltage, KEY_GRID_VOLTAGE_MQTT_TOPIC);
+ _textConfig(_mqttServer, KEY_MQTT_SERVER);
+ _textConfig(_mqttTopic, KEY_MQTT_TOPIC);
+ _textConfig(_mqttUser, KEY_MQTT_USERNAME);
+
// GPIO (configuration)
_pinDimmerO1.setTab(&_pinConfigTab);
_pinDimmerO2.setTab(&_pinConfigTab);
@@ -196,8 +264,6 @@ void YaSolR::WebsiteClass::initLayout() {
_output1Relay.setTab(&_hardwareEnableTab);
_output1DS18.setTab(&_hardwareEnableTab);
_output2Dimmer.setTab(&_hardwareEnableTab);
- _output1ResistanceInput.setTab(&_hardwareConfigTab);
- _output2ResistanceInput.setTab(&_hardwareConfigTab);
_output2PZEM.setTab(&_hardwareEnableTab);
_output2Relay.setTab(&_hardwareEnableTab);
_output2DS18.setTab(&_hardwareEnableTab);
@@ -234,6 +300,8 @@ void YaSolR::WebsiteClass::initLayout() {
_output2RelayType.setTab(&_hardwareConfigTab);
_relay1Type.setTab(&_hardwareConfigTab);
_relay2Type.setTab(&_hardwareConfigTab);
+ _output1ResistanceInput.setTab(&_hardwareConfigTab);
+ _output2ResistanceInput.setTab(&_hardwareConfigTab);
_numConfig(_gridFreq, KEY_GRID_FREQUENCY);
_numConfig(_displayRotation, KEY_DISPLAY_ROTATION);
@@ -251,54 +319,42 @@ void YaSolR::WebsiteClass::initLayout() {
_output1PZEMSync.attachCallback([] PUSH_BUTTON_CARD_CB { pzemO1PairingTask.resume(); });
_output2PZEMSync.attachCallback([] PUSH_BUTTON_CARD_CB { pzemO2PairingTask.resume(); });
- // mqtt (config)
- _haDiscovery.setTab(&_mqttConfigTab);
- _haDiscoveryTopic.setTab(&_mqttConfigTab);
- _mqttGridPower.setTab(&_mqttConfigTab);
- _mqttGridVoltage.setTab(&_mqttConfigTab);
- _mqttPort.setTab(&_mqttConfigTab);
- _mqttPublishInterval.setTab(&_mqttConfigTab);
- _mqttPwd.setTab(&_mqttConfigTab);
- _mqttSecured.setTab(&_mqttConfigTab);
- _mqttServer.setTab(&_mqttConfigTab);
- _mqttServerCert.setTab(&_mqttConfigTab);
- _mqttTopic.setTab(&_mqttConfigTab);
- _mqttUser.setTab(&_mqttConfigTab);
-
- _boolConfig(_haDiscovery, KEY_ENABLE_HA_DISCOVERY);
- _boolConfig(_mqttSecured, KEY_MQTT_SECURED);
- _numConfig(_mqttPort, KEY_MQTT_PORT);
- _passwordConfig(_mqttPwd, KEY_MQTT_PASSWORD);
- _sliderConfig(_mqttPublishInterval, KEY_MQTT_PUBLISH_INTERVAL);
- _textConfig(_haDiscoveryTopic, KEY_HA_DISCOVERY_TOPIC);
- _textConfig(_mqttGridPower, KEY_GRID_POWER_MQTT_TOPIC);
- _textConfig(_mqttGridVoltage, KEY_GRID_VOLTAGE_MQTT_TOPIC);
- _textConfig(_mqttServer, KEY_MQTT_SERVER);
- _textConfig(_mqttTopic, KEY_MQTT_TOPIC);
- _textConfig(_mqttUser, KEY_MQTT_USERNAME);
-
- // network (config)
- _adminPwd.setTab(&_networkConfigTab);
- _apMode.setTab(&_networkConfigTab);
- _ntpServer.setTab(&_networkConfigTab);
- _ntpSync.setTab(&_networkConfigTab);
- _ntpTimezone.setTab(&_networkConfigTab);
- _wifiPwd.setTab(&_networkConfigTab);
- _wifiSSID.setTab(&_networkConfigTab);
-
- _boolConfig(_apMode, KEY_ENABLE_AP_MODE);
- _passwordConfig(_adminPwd, KEY_ADMIN_PASSWORD);
- _passwordConfig(_wifiPwd, KEY_WIFI_PASSWORD);
- _textConfig(_ntpServer, KEY_NTP_SERVER);
- _textConfig(_ntpTimezone, KEY_NTP_TIMEZONE);
- _textConfig(_wifiSSID, KEY_WIFI_SSID);
-
- _ntpSync.attachCallback([](const char* value) {
- const String str = String(value);
- const size_t len = str.length();
- const timeval tv = {str.substring(0, len - 3).toInt(), str.substring(len - 3).toInt()};
- Mycila::NTP.sync(tv);
+ // PID
+ _pidView.setTab(&_pidTab);
+ _pidPMode.setTab(&_pidTab);
+ _pidDMode.setTab(&_pidTab);
+ _pidICMode.setTab(&_pidTab);
+ _pidSetpoint.setTab(&_pidTab);
+ _pidKp.setTab(&_pidTab);
+ _pidKi.setTab(&_pidTab);
+ _pidKd.setTab(&_pidTab);
+ _pidOutMin.setTab(&_pidTab);
+ _pidOutMax.setTab(&_pidTab);
+
+ _pidInputHistory.setTab(&_pidTab);
+ _pidOutputHistory.setTab(&_pidTab);
+ _pidErrorHistory.setTab(&_pidTab);
+ _pidSumHistory.setTab(&_pidTab);
+ _pidPTermHistory.setTab(&_pidTab);
+ _pidITermHistory.setTab(&_pidTab);
+ _pidDTermHistory.setTab(&_pidTab);
+ _pidReset.setTab(&_pidTab);
+
+ _pidReset.attachCallback([this] PUSH_BUTTON_CARD_CB {
+ resetPID();
+ updatePID();
});
+
+ _boolConfig(_pidView, KEY_ENABLE_PID_VIEW);
+ _numConfig(_pidPMode, KEY_PID_P_MODE);
+ _numConfig(_pidDMode, KEY_PID_D_MODE);
+ _numConfig(_pidICMode, KEY_PID_IC_MODE);
+ _numConfig(_pidSetpoint, KEY_PID_SETPOINT);
+ _floatConfig(_pidKp, KEY_PID_KP);
+ _floatConfig(_pidKi, KEY_PID_KI);
+ _floatConfig(_pidKd, KEY_PID_KD);
+ _numConfig(_pidOutMin, KEY_PID_OUT_MIN);
+ _numConfig(_pidOutMax, KEY_PID_OUT_MAX);
#endif
}
@@ -326,12 +382,12 @@ void YaSolR::WebsiteClass::initCards() {
#ifdef APP_MODEL_PRO
// output 1 (control)
- bool dimmer1Enabled = config.getBool(KEY_ENABLE_OUTPUT1_DIMMER);
- bool output1RelayEnabled = config.getBool(KEY_ENABLE_OUTPUT1_RELAY);
- bool bypass1Possible = dimmer1Enabled || output1RelayEnabled;
- bool autoDimmerO1Activated = config.getBool(KEY_ENABLE_OUTPUT1_AUTO_DIMMER);
- bool autoBypassActivated = config.getBool(KEY_ENABLE_OUTPUT1_AUTO_BYPASS);
- bool output1DS18Enabled = config.getBool(KEY_ENABLE_OUTPUT1_DS18);
+ const bool dimmer1Enabled = config.getBool(KEY_ENABLE_OUTPUT1_DIMMER);
+ const bool output1RelayEnabled = config.getBool(KEY_ENABLE_OUTPUT1_RELAY);
+ const bool bypass1Possible = dimmer1Enabled || output1RelayEnabled;
+ const bool autoDimmerO1Activated = config.getBool(KEY_ENABLE_OUTPUT1_AUTO_DIMMER);
+ const bool autoBypassActivated = config.getBool(KEY_ENABLE_OUTPUT1_AUTO_BYPASS);
+ const bool output1DS18Enabled = config.getBool(KEY_ENABLE_OUTPUT1_DS18);
_output1DimmerAuto.update(autoDimmerO1Activated);
_output1DimmerRatio.update(static_cast(config.get(KEY_OUTPUT1_RESERVED_EXCESS).toInt()));
@@ -369,12 +425,12 @@ void YaSolR::WebsiteClass::initCards() {
_output1AutoStoptTemp.setDisplay(bypass1Possible && autoBypassActivated && output1DS18Enabled);
// output 2 (control)
- bool dimmer2Enabled = config.getBool(KEY_ENABLE_OUTPUT2_DIMMER);
- bool output2RelayEnabled = config.getBool(KEY_ENABLE_OUTPUT2_RELAY);
- bool bypass2Possible = dimmer2Enabled || output2RelayEnabled;
- bool autoDimmerO2Activated = config.getBool(KEY_ENABLE_OUTPUT2_AUTO_DIMMER);
- bool autoBypassO2Activated = config.getBool(KEY_ENABLE_OUTPUT2_AUTO_BYPASS);
- bool output2DS18Enabled = config.getBool(KEY_ENABLE_OUTPUT2_DS18);
+ const bool dimmer2Enabled = config.getBool(KEY_ENABLE_OUTPUT2_DIMMER);
+ const bool output2RelayEnabled = config.getBool(KEY_ENABLE_OUTPUT2_RELAY);
+ const bool bypass2Possible = dimmer2Enabled || output2RelayEnabled;
+ const bool autoDimmerO2Activated = config.getBool(KEY_ENABLE_OUTPUT2_AUTO_DIMMER);
+ const bool autoBypassO2Activated = config.getBool(KEY_ENABLE_OUTPUT2_AUTO_BYPASS);
+ const bool output2DS18Enabled = config.getBool(KEY_ENABLE_OUTPUT2_DS18);
_output2DimmerAuto.update(autoDimmerO2Activated);
_output2DimmerRatio.update(static_cast(config.get(KEY_OUTPUT2_RESERVED_EXCESS).toInt()));
@@ -432,6 +488,29 @@ void YaSolR::WebsiteClass::initCards() {
_otaLink.update("/update");
_energyReset.setDisplay(config.getBool(KEY_ENABLE_JSY) || config.getBool(KEY_ENABLE_OUTPUT1_PZEM) || config.getBool(KEY_ENABLE_OUTPUT2_PZEM));
+ // network (config)
+ _adminPwd.update(config.get(KEY_ADMIN_PASSWORD).isEmpty() ? "" : HIDDEN_PWD);
+ _apMode.update(config.getBool(KEY_ENABLE_AP_MODE));
+ _ntpServer.update(config.get(KEY_NTP_SERVER));
+ _ntpTimezone.update(config.get(KEY_NTP_TIMEZONE), "/timezones");
+ _wifiPwd.update(config.get(KEY_WIFI_PASSWORD).isEmpty() ? "" : HIDDEN_PWD);
+ _wifiSSID.update(config.get(KEY_WIFI_SSID));
+
+ // mqtt (config)
+ _haDiscovery.update(config.getBool(KEY_ENABLE_HA_DISCOVERY));
+ _haDiscoveryTopic.update(config.get(KEY_HA_DISCOVERY_TOPIC));
+ _mqttGridPower.update(config.get(KEY_GRID_POWER_MQTT_TOPIC));
+ _mqttGridVoltage.update(config.get(KEY_GRID_VOLTAGE_MQTT_TOPIC));
+ _mqttPort.update(config.get(KEY_MQTT_PORT));
+ _mqttPublishInterval.update(config.get(KEY_MQTT_PUBLISH_INTERVAL));
+ _mqttPwd.update(config.get(KEY_MQTT_PASSWORD).isEmpty() ? "" : HIDDEN_PWD);
+ _mqttSecured.update(config.getBool(KEY_MQTT_SECURED));
+ _mqttServer.update(config.get(KEY_MQTT_SERVER));
+ _mqttServerCert.update("/api/config/mqttServerCertificate");
+ _mqttTopic.update(config.get(KEY_MQTT_TOPIC));
+ _mqttUser.update(config.get(KEY_MQTT_USERNAME));
+ _mqttConfigTab.setDisplay(config.getBool(KEY_ENABLE_MQTT));
+
// GPIO
std::map pinout = {};
@@ -488,28 +567,62 @@ void YaSolR::WebsiteClass::initCards() {
_relay1Type.setDisplay(config.getBool(KEY_ENABLE_RELAY1));
_relay2Type.setDisplay(config.getBool(KEY_ENABLE_RELAY2));
- // mqtt (config)
- _haDiscovery.update(config.getBool(KEY_ENABLE_HA_DISCOVERY));
- _haDiscoveryTopic.update(config.get(KEY_HA_DISCOVERY_TOPIC));
- _mqttGridPower.update(config.get(KEY_GRID_POWER_MQTT_TOPIC));
- _mqttGridVoltage.update(config.get(KEY_GRID_VOLTAGE_MQTT_TOPIC));
- _mqttPort.update(config.get(KEY_MQTT_PORT));
- _mqttPublishInterval.update(config.get(KEY_MQTT_PUBLISH_INTERVAL));
- _mqttPwd.update(config.get(KEY_MQTT_PASSWORD).isEmpty() ? "" : HIDDEN_PWD);
- _mqttSecured.update(config.getBool(KEY_MQTT_SECURED));
- _mqttServer.update(config.get(KEY_MQTT_SERVER));
- _mqttServerCert.update("/api/config/mqttServerCertificate");
- _mqttTopic.update(config.get(KEY_MQTT_TOPIC));
- _mqttUser.update(config.get(KEY_MQTT_USERNAME));
- _mqttConfigTab.setDisplay(config.getBool(KEY_ENABLE_MQTT));
-
- // network (config)
- _adminPwd.update(config.get(KEY_ADMIN_PASSWORD).isEmpty() ? "" : HIDDEN_PWD);
- _apMode.update(config.getBool(KEY_ENABLE_AP_MODE));
- _ntpServer.update(config.get(KEY_NTP_SERVER));
- _ntpTimezone.update(config.get(KEY_NTP_TIMEZONE), "/timezones");
- _wifiPwd.update(config.get(KEY_WIFI_PASSWORD).isEmpty() ? "" : HIDDEN_PWD);
- _wifiSSID.update(config.get(KEY_WIFI_SSID));
+ // PID
+ const bool pidViewEnabled = config.getBool(KEY_ENABLE_PID_VIEW);
+ _pidView.update(pidViewEnabled);
+ switch (config.get(KEY_PID_P_MODE).toInt()) {
+ case 1:
+ _pidPMode.update(YASOLR_PID_P_MODE_1, YASOLR_PID_P_MODE_1 "," YASOLR_PID_P_MODE_2 "," YASOLR_PID_P_MODE_3);
+ break;
+ case 2:
+ _pidPMode.update(YASOLR_PID_P_MODE_2, YASOLR_PID_P_MODE_1 "," YASOLR_PID_P_MODE_2 "," YASOLR_PID_P_MODE_3);
+ break;
+ case 3:
+ _pidPMode.update(YASOLR_PID_P_MODE_3, YASOLR_PID_P_MODE_1 "," YASOLR_PID_P_MODE_2 "," YASOLR_PID_P_MODE_3);
+ break;
+ default:
+ _pidPMode.update("", YASOLR_PID_P_MODE_1 "," YASOLR_PID_P_MODE_2 "," YASOLR_PID_P_MODE_3);
+ break;
+ }
+ switch (config.get(KEY_PID_D_MODE).toInt()) {
+ case 1:
+ _pidDMode.update(YASOLR_PID_D_MODE_1, YASOLR_PID_D_MODE_1 "," YASOLR_PID_D_MODE_2);
+ break;
+ case 2:
+ _pidDMode.update(YASOLR_PID_D_MODE_2, YASOLR_PID_D_MODE_1 "," YASOLR_PID_D_MODE_2);
+ break;
+ default:
+ _pidDMode.update("", YASOLR_PID_D_MODE_1 "," YASOLR_PID_D_MODE_2);
+ break;
+ }
+ switch (config.get(KEY_PID_IC_MODE).toInt()) {
+ case 0:
+ _pidICMode.update(YASOLR_PID_IC_MODE_0, YASOLR_PID_IC_MODE_0 "," YASOLR_PID_IC_MODE_1 "," YASOLR_PID_IC_MODE_2);
+ break;
+ case 1:
+ _pidICMode.update(YASOLR_PID_IC_MODE_1, YASOLR_PID_IC_MODE_0 "," YASOLR_PID_IC_MODE_1 "," YASOLR_PID_IC_MODE_2);
+ break;
+ case 2:
+ _pidICMode.update(YASOLR_PID_IC_MODE_2, YASOLR_PID_IC_MODE_0 "," YASOLR_PID_IC_MODE_1 "," YASOLR_PID_IC_MODE_2);
+ break;
+ default:
+ _pidICMode.update("", YASOLR_PID_IC_MODE_0 "," YASOLR_PID_IC_MODE_1 "," YASOLR_PID_IC_MODE_2);
+ break;
+ }
+ _pidSetpoint.update(config.get(KEY_PID_SETPOINT));
+ _pidKp.update(config.get(KEY_PID_KP));
+ _pidKi.update(config.get(KEY_PID_KI));
+ _pidKd.update(config.get(KEY_PID_KD));
+ _pidOutMin.update(config.get(KEY_PID_OUT_MIN));
+ _pidOutMax.update(config.get(KEY_PID_OUT_MAX));
+
+ _pidInputHistory.setDisplay(pidViewEnabled);
+ _pidOutputHistory.setDisplay(pidViewEnabled);
+ _pidErrorHistory.setDisplay(pidViewEnabled);
+ _pidSumHistory.setDisplay(pidViewEnabled);
+ _pidPTermHistory.setDisplay(pidViewEnabled);
+ _pidITermHistory.setDisplay(pidViewEnabled);
+ _pidDTermHistory.setDisplay(pidViewEnabled);
#endif
}
@@ -656,11 +769,10 @@ void YaSolR::WebsiteClass::updateCharts() {
router.getMeasurements(routerMetrics);
// shift array
- for (size_t i = 0; i < YASOLR_GRAPH_POINTS - 1; i++) {
- _gridPowerHistoryY[i] = _gridPowerHistoryY[i + 1];
- _routedPowerHistoryY[i] = _routedPowerHistoryY[i + 1];
- _routerTHDiHistoryY[i] = _routerTHDiHistoryY[i + 1];
- }
+ constexpr size_t shift = sizeof(_gridPowerHistoryY) - sizeof(*_gridPowerHistoryY);
+ memmove(&_gridPowerHistoryY[0], &_gridPowerHistoryY[1], shift);
+ memmove(&_routedPowerHistoryY[0], &_routedPowerHistoryY[1], shift);
+ memmove(&_routerTHDiHistoryY[0], &_routerTHDiHistoryY[1], shift);
// set new value
_gridPowerHistoryY[YASOLR_GRAPH_POINTS - 1] = round(gridMetrics.power);
@@ -673,6 +785,46 @@ void YaSolR::WebsiteClass::updateCharts() {
_routerTHDiHistory.updateY(_routerTHDiHistoryY, YASOLR_GRAPH_POINTS);
}
+void YaSolR::WebsiteClass::updatePID() {
+ // shift array
+ constexpr size_t shift = sizeof(_pidInputHistoryY) - sizeof(*_pidInputHistoryY);
+ memmove(&_pidInputHistoryY[0], &_pidInputHistoryY[1], shift);
+ memmove(&_pidOutputHistoryY[0], &_pidOutputHistoryY[1], shift);
+ memmove(&_pidErrorHistoryY[0], &_pidErrorHistoryY[1], shift);
+ memmove(&_pidSumHistoryY[0], &_pidSumHistoryY[1], shift);
+ memmove(&_pidPTermHistoryY[0], &_pidPTermHistoryY[1], shift);
+ memmove(&_pidITermHistoryY[0], &_pidITermHistoryY[1], shift);
+ memmove(&_pidDTermHistoryY[0], &_pidDTermHistoryY[1], shift);
+
+ // set new values
+ _pidInputHistoryY[YASOLR_GRAPH_POINTS - 1] = round(pidController.getInput());
+ _pidOutputHistoryY[YASOLR_GRAPH_POINTS - 1] = round(pidController.getOutput());
+ _pidErrorHistoryY[YASOLR_GRAPH_POINTS - 1] = round(pidController.getError());
+ _pidSumHistoryY[YASOLR_GRAPH_POINTS - 1] = round(pidController.getSum());
+ _pidPTermHistoryY[YASOLR_GRAPH_POINTS - 1] = round(pidController.getPTerm());
+ _pidITermHistoryY[YASOLR_GRAPH_POINTS - 1] = round(pidController.getITerm());
+ _pidDTermHistoryY[YASOLR_GRAPH_POINTS - 1] = round(pidController.getDTerm());
+
+ // update charts
+ _pidInputHistory.updateY(_pidInputHistoryY, YASOLR_GRAPH_POINTS);
+ _pidOutputHistory.updateY(_pidOutputHistoryY, YASOLR_GRAPH_POINTS);
+ _pidErrorHistory.updateY(_pidErrorHistoryY, YASOLR_GRAPH_POINTS);
+ _pidSumHistory.updateY(_pidSumHistoryY, YASOLR_GRAPH_POINTS);
+ _pidPTermHistory.updateY(_pidPTermHistoryY, YASOLR_GRAPH_POINTS);
+ _pidITermHistory.updateY(_pidITermHistoryY, YASOLR_GRAPH_POINTS);
+ _pidDTermHistory.updateY(_pidDTermHistoryY, YASOLR_GRAPH_POINTS);
+}
+
+void YaSolR::WebsiteClass::resetPID() {
+ memset(_pidOutputHistoryY, 0, sizeof(_pidOutputHistoryY));
+ memset(_pidInputHistoryY, 0, sizeof(_pidInputHistoryY));
+ memset(_pidErrorHistoryY, 0, sizeof(_pidErrorHistoryY));
+ memset(_pidSumHistoryY, 0, sizeof(_pidSumHistoryY));
+ memset(_pidPTermHistoryY, 0, sizeof(_pidPTermHistoryY));
+ memset(_pidITermHistoryY, 0, sizeof(_pidITermHistoryY));
+ memset(_pidDTermHistoryY, 0, sizeof(_pidDTermHistoryY));
+}
+
void YaSolR::WebsiteClass::_sliderConfig(Card& card, const char* key) {
card.attachCallback([key, &card](int value) {
config.set(key, String(value));
@@ -681,6 +833,20 @@ void YaSolR::WebsiteClass::_sliderConfig(Card& card, const char* key) {
});
}
+void YaSolR::WebsiteClass::_floatConfig(Card& card, const char* key) {
+#ifdef APP_MODEL_PRO
+ card.attachCallback([key, &card](const char* value) {
+ if (strlen(value) == 0) {
+ config.unset(key);
+ } else {
+ config.set(key, value);
+ }
+ card.update(config.get(key));
+ dashboard.refreshCard(&card);
+ });
+#endif
+}
+
void YaSolR::WebsiteClass::_numConfig(Card& card, const char* key) {
#ifdef APP_MODEL_PRO
card.attachCallback([key, &card](const char* value) {
diff --git a/src/init/Boot.cpp b/src/init/Boot.cpp
index fa734bb..da18e2e 100644
--- a/src/init/Boot.cpp
+++ b/src/init/Boot.cpp
@@ -53,6 +53,7 @@ Mycila::Task bootTask("Boot", [](void* params) {
config.configure(KEY_ENABLE_OUTPUT2_DS18, YASOLR_FALSE);
config.configure(KEY_ENABLE_OUTPUT2_PZEM, YASOLR_FALSE);
config.configure(KEY_ENABLE_OUTPUT2_RELAY, YASOLR_FALSE);
+ config.configure(KEY_ENABLE_PID_VIEW, YASOLR_FALSE);
config.configure(KEY_ENABLE_RELAY1, YASOLR_FALSE);
config.configure(KEY_ENABLE_RELAY2, YASOLR_FALSE);
config.configure(KEY_ENABLE_ZCD, YASOLR_FALSE);
@@ -94,8 +95,8 @@ Mycila::Task bootTask("Boot", [](void* params) {
config.configure(KEY_PID_IC_MODE, "2");
config.configure(KEY_PID_SETPOINT, "0");
config.configure(KEY_PID_KP, "0.3");
- config.configure(KEY_PID_KI, "0.4");
- config.configure(KEY_PID_KD, "0.2");
+ config.configure(KEY_PID_KI, "0.3");
+ config.configure(KEY_PID_KD, "0.1");
config.configure(KEY_PID_OUT_MIN, "-10000");
config.configure(KEY_PID_OUT_MAX, "10000");
config.configure(KEY_PIN_DISPLAY_SCL, String(YASOLR_DISPLAY_CLOCK_PIN));
diff --git a/src/init/Debug.cpp b/src/init/Debug.cpp
index dff39f6..1892c22 100644
--- a/src/init/Debug.cpp
+++ b/src/init/Debug.cpp
@@ -5,7 +5,7 @@
#include
static const Mycila::TaskDoneCallback LOG_EXEC_TIME = [](const Mycila::Task& me, const uint32_t elapsed) {
- logger.debug(TAG, "%s in %" PRIu32 " us", me.getName(), elapsed);
+ logger.debug(TAG, "Task %s finished in %" PRIu32 " us", me.getName(), elapsed);
};
Mycila::Task initLoggingTask("Init Logging", [](void* params) {
diff --git a/src/init/Events.cpp b/src/init/Events.cpp
index 77ed9a4..8849bff 100644
--- a/src/init/Events.cpp
+++ b/src/init/Events.cpp
@@ -206,6 +206,7 @@ Mycila::Task initEventsTask("Init Events", [](void* params) {
pidController.setSetPoint(config.get(KEY_PID_SETPOINT).toFloat());
pidController.setTunings(config.get(KEY_PID_KP).toFloat(), config.get(KEY_PID_KI).toFloat(), config.get(KEY_PID_KD).toFloat());
pidController.setOutputLimits(config.get(KEY_PID_OUT_MIN).toFloat(), config.get(KEY_PID_OUT_MAX).toFloat());
+ logger.info(TAG, "PID Controller reconfigured! Resetting...");
pidController.reset();
}
diff --git a/src/init/HTTP.cpp b/src/init/HTTP.cpp
index 1a85e30..893f70f 100644
--- a/src/init/HTTP.cpp
+++ b/src/init/HTTP.cpp
@@ -66,7 +66,7 @@ Mycila::Task initWebTask("Init Web", [](void* params) {
if (type == WS_EVT_CONNECT) {
logger.info(TAG, "Websocket client connected to: /ws/debug/pid");
wsDebugPID.cleanupClients(4);
- client->text("pMode,dMode,icMode,rev,setpoint,kp,ki,kd,outMin,outMax,input,output,error,pTerm,iTerm,dTerm,sum");
+ client->text("pMode,dMode,icMode,rev,setpoint,kp,ki,kd,outMin,outMax,input,output,error,sum,pTerm,iTerm,dTerm");
}
});
webServer.addHandler(&wsDebugPID);
diff --git a/src/main.cpp b/src/main.cpp
index c2dfaed..2bf2502 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -65,7 +65,7 @@ void setup() {
assert( ioTaskManager.asyncStart(512 * 9, 1, 1, 100, false)); // NOLINT
assert( jsyTaskManager.asyncStart(512 * 4, 5, 0, 100, true)); // NOLINT
assert( pzemTaskManager.asyncStart(512 * 4, 5, 0, 100, true)); // NOLINT
- assert(routerTaskManager.asyncStart(512 * 3, 5, 1, 100, true)); // NOLINT
+ assert(routerTaskManager.asyncStart(512 * 6, 5, 1, 100, true)); // NOLINT
// STARTUP READY!
logger.info(TAG, "Started %s", Mycila::AppInfo.nameModelVersion.c_str());
diff --git a/src/tasks/Dashboard.cpp b/src/tasks/Dashboard.cpp
index 6db60c9..b70b202 100644
--- a/src/tasks/Dashboard.cpp
+++ b/src/tasks/Dashboard.cpp
@@ -8,5 +8,7 @@
Mycila::Task dashboardTask("Dashboard", [](void* params) {
YaSolR::Website.updateCards();
YaSolR::Website.updateCharts();
- dashboard.sendUpdates();
+ if (!routingTask.isEnabled() || !config.getBool(KEY_ENABLE_PID_VIEW)) {
+ dashboard.sendUpdates();
+ }
});
diff --git a/src/tasks/Router.cpp b/src/tasks/Router.cpp
index 088ba33..0366838 100644
--- a/src/tasks/Router.cpp
+++ b/src/tasks/Router.cpp
@@ -3,6 +3,7 @@
* Copyright (C) 2023-2024 Mathieu Carbou and others
*/
#include
+#include
Mycila::Task routerTask("Router", [](void* params) {
grid.applyExpiration();
@@ -37,42 +38,14 @@ Mycila::Task routingTask("Routing", Mycila::TaskType::ONCE, [](void* params) {
Mycila::GridMetrics gridMetrics;
grid.getMeasurements(gridMetrics);
router.divert(gridMetrics.voltage, gridMetrics.power);
- if (config.getBool(KEY_ENABLE_DEBUG)) {
- String message;
- message.reserve(256);
- message.concat(pidController.getProportionalMode());
- message.concat(",");
- message.concat(pidController.getDerivativeMode());
- message.concat(",");
- message.concat(pidController.getIntegralCorrectionMode());
- message.concat(",");
- message.concat(pidController.isReversed());
- message.concat(",");
- message.concat(pidController.getSetPoint());
- message.concat(",");
- message.concat(pidController.getKp());
- message.concat(",");
- message.concat(pidController.getKi());
- message.concat(",");
- message.concat(pidController.getKd());
- message.concat(",");
- message.concat(pidController.getOutputMin());
- message.concat(",");
- message.concat(pidController.getOutputMax());
- message.concat(",");
- message.concat(pidController.getInput());
- message.concat(",");
- message.concat(pidController.getOutput());
- message.concat(",");
- message.concat(pidController.getError());
- message.concat(",");
- message.concat(pidController.getPTerm());
- message.concat(",");
- message.concat(pidController.getITerm());
- message.concat(",");
- message.concat(pidController.getDTerm());
- message.concat(",");
- message.concat(pidController.getSum());
- wsDebugPID.textAll(message);
+ if (config.getBool(KEY_ENABLE_PID_VIEW)) {
+ // Dashboard
+ YaSolR::Website.updatePID();
+ dashboard.sendUpdates();
+
+ // WebSocket
+ AsyncWebSocketMessageBuffer* buffer = wsDebugPID.makeBuffer(256);
+ snprintf((char*)buffer->get(), 256, "%d,%d,%d,%d,%d,%.3f,%.3f,%.3f,%d,%d,%.3f,%.3f,%.3f,%.3f,%.3f,%.3f,%.3f", pidController.getProportionalMode(), pidController.getDerivativeMode(), pidController.getIntegralCorrectionMode(), pidController.isReversed(), static_cast(pidController.getSetPoint()), pidController.getKp(), pidController.getKi(), pidController.getKd(), static_cast(pidController.getOutputMin()), static_cast(pidController.getOutputMax()), pidController.getInput(), pidController.getOutput(), pidController.getError(), pidController.getSum(), pidController.getPTerm(), pidController.getITerm(), pidController.getDTerm()); // NOLINT
+ wsDebugPID.textAll(buffer);
}
});