[Memory] Make memory allocation on ESP8266 2nd heap more stable

This commit is contained in:
TD-er
2025-02-20 17:07:00 +01:00
parent b0a3a29b3c
commit d7274af9ce
10 changed files with 231 additions and 165 deletions
+8
View File
@@ -107,6 +107,14 @@ build_flags = ${beta_platform.build_flags}
lib_ignore = ${beta_platform.lib_ignore}
${no_ir.lib_ignore}
[normal_beta_2ndheap]
platform = ${beta_platform_2ndheap.platform}
platform_packages = ${beta_platform_2ndheap.platform_packages}
build_flags = ${beta_platform_2ndheap.build_flags}
-DBUILD_NO_DEBUG
lib_ignore = ${beta_platform.lib_ignore}
${no_ir.lib_ignore}
;;; COLLECTION ********************************************************
; additional plugins (and dependend code) that is marked as COLLECTION ;
; Includes "normal" + "collection" plugins ;
+20
View File
@@ -300,6 +300,26 @@ build_flags = ${normal.build_flags}
-D NO_LIMIT_BUILD_SIZE
lib_ignore = ${normal.lib_ignore}
[env:normal_beta_2ndheap_ESP8266_4M1M]
extends = esp8266_4M1M
platform = ${normal_beta_2ndheap.platform}
platform_packages = ${normal_beta_2ndheap.platform_packages}
build_flags = ${normal_beta_2ndheap.build_flags}
${esp8266_4M1M.build_flags}
-D NO_LIMIT_BUILD_SIZE
lib_ignore = ${normal_beta_2ndheap.lib_ignore}
[env:normal_beta_2ndheap_ESP8266_4M1M_VCC]
extends = esp8266_4M1M
platform = ${normal_beta_2ndheap.platform}
platform_packages = ${normal_beta_2ndheap.platform_packages}
build_flags = ${normal_beta_2ndheap.build_flags}
${esp8266_4M1M.build_flags}
-D FEATURE_ADC_VCC=1
-D NO_LIMIT_BUILD_SIZE
lib_ignore = ${normal_beta_2ndheap.lib_ignore}
; NORMAL: 16M version --- LittleFS --------------
; LittleFS is determined by using "LittleFS" in the pio env name
+11 -8
View File
@@ -56,9 +56,9 @@ bool NPlugin_001(NPlugin::Function function, struct EventStruct *event, String&
// if (command == F("email"))
// {
// MakeNotificationSettings(NotificationSettings);
// LoadNotificationSettings(event->NotificationIndex, (uint8_t*)&NotificationSettings, sizeof(NotificationSettingsStruct));
// NPlugin_001_send(NotificationSettings.Domain, NotificationSettings.Receiver, NotificationSettings.Sender,
// NotificationSettings.Subject, NotificationSettings.Body, NotificationSettings.Server, NotificationSettings.Port);
// LoadNotificationSettings(event->NotificationIndex, (uint8_t *)NotificationSettings.get(), sizeof(NotificationSettingsStruct));
// NPlugin_001_send(NotificationSettings->Domain, NotificationSettings->Receiver, NotificationSettings->Sender,
// NotificationSettings->Subject, NotificationSettings->Body, NotificationSettings->Server, NotificationSettings->Port);
// success = true;
// }
// break;
@@ -67,11 +67,14 @@ bool NPlugin_001(NPlugin::Function function, struct EventStruct *event, String&
case NPlugin::Function::NPLUGIN_NOTIFY:
{
MakeNotificationSettings(NotificationSettings);
LoadNotificationSettings(event->NotificationIndex, (uint8_t *)&NotificationSettings, sizeof(NotificationSettingsStruct));
NotificationSettings.validate();
if (!AllocatedNotificationSettings()) {
break;
}
LoadNotificationSettings(event->NotificationIndex, (uint8_t *)NotificationSettings.get(), sizeof(NotificationSettingsStruct));
NotificationSettings->validate();
String subject = NotificationSettings.Subject;
String body = NotificationSettings.Body;
String subject = NotificationSettings->Subject;
String body = NotificationSettings->Body;
if (!event->String1.isEmpty()) {
body = event->String1;
@@ -80,7 +83,7 @@ bool NPlugin_001(NPlugin::Function function, struct EventStruct *event, String&
if (!event->String2.isEmpty()) {
subject = event->String2;
}
NPlugin_001_send(NotificationSettings, subject, body);
NPlugin_001_send(*NotificationSettings, subject, body);
success = true;
break;
}
+8 -4
View File
@@ -46,7 +46,7 @@ bool NPlugin_002(NPlugin::Function function, struct EventStruct *event, String&
// if (command == F("buzzer"))
// {
// MakeNotificationSettings(NotificationSettings);
// LoadNotificationSettings(event->NotificationIndex, (uint8_t*)&NotificationSettings, sizeof(NotificationSettingsStruct));
// LoadNotificationSettings(event->NotificationIndex, (uint8_t *)NotificationSettings.get(), sizeof(NotificationSettingsStruct));
// success = true;
// }
// break;
@@ -55,10 +55,14 @@ bool NPlugin_002(NPlugin::Function function, struct EventStruct *event, String&
case NPlugin::Function::NPLUGIN_NOTIFY:
{
MakeNotificationSettings(NotificationSettings);
LoadNotificationSettings(event->NotificationIndex, (uint8_t*)&NotificationSettings, sizeof(NotificationSettingsStruct));
NotificationSettings.validate();
if (!AllocatedNotificationSettings()) {
break;
}
LoadNotificationSettings(event->NotificationIndex, (uint8_t *)NotificationSettings.get(), sizeof(NotificationSettingsStruct));
NotificationSettings->validate();
//this reserves IRAM and uninitialized RAM
tone_espEasy(NotificationSettings.Pin1, 440, 500);
tone_espEasy(NotificationSettings->Pin1, 440, 500);
success = true;
}
+8 -1
View File
@@ -57,7 +57,14 @@ boolean Plugin_002(uint8_t function, struct EventStruct *event, String& string)
P002_data->webformLoad(event);
success = true;
} else {
P002_data = new (std::nothrow) P002_data_struct();
constexpr unsigned size = sizeof(P002_data_struct);
void *ptr = special_calloc(1, size);
if (ptr == nullptr) {
break;
}
P002_data = new (ptr) P002_data_struct();
if (nullptr != P002_data) {
P002_data->init(event);
+6 -5
View File
@@ -711,11 +711,12 @@ boolean Plugin_036(uint8_t function, struct EventStruct *event, String& string)
P036_CheckHeap(F("_INIT: Entering"));
# endif // P036_CHECK_HEAP
{
# ifdef USE_SECOND_HEAP
HeapSelectIram ephemeral;
# endif // ifdef USE_SECOND_HEAP
initPluginTaskData(event->TaskIndex, new (std::nothrow) P036_data_struct());
constexpr unsigned size = sizeof(P036_data_struct);
void *ptr = special_calloc(1, size);
if (ptr != nullptr) {
initPluginTaskData(event->TaskIndex, new (ptr) P036_data_struct());
}
}
# ifdef P036_CHECK_HEAP
P036_CheckHeap(F("_INIT: Before (*P036_data = static_cast<P036_data_struct *>)"));
@@ -36,8 +36,12 @@ struct NotificationSettingsStruct
};
typedef std::shared_ptr<NotificationSettingsStruct> NotificationSettingsStruct_ptr_type;
#define MakeNotificationSettings(T) NotificationSettingsStruct_ptr_type NotificationSettingsStruct_ptr(new (std::nothrow) NotificationSettingsStruct());\
NotificationSettingsStruct& T = *NotificationSettingsStruct_ptr;
#define MakeNotificationSettings(T) void * calloc_ptr = special_calloc(1,sizeof(NotificationSettingsStruct)); NotificationSettingsStruct_ptr_type T(new (calloc_ptr) NotificationSettingsStruct());
// Check to see if MakeNotificationSettings was successful
#define AllocatedNotificationSettings() (NotificationSettings.get() != nullptr)
// Need to make sure every byte between the members is also zero
// Otherwise the checksum will fail and settings will be saved too often.
+3 -3
View File
@@ -176,14 +176,14 @@ void* special_calloc(size_t num, size_t size) {
res = heap_caps_calloc(num, size, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
}
#else // ifdef ESP32
if (size > 64) {
# ifdef USE_SECOND_HEAP
if (size > 64 || FreeMem() < 5000) {
// Try allocating on ESP8266 2nd heap, only when sufficiently large data is needed
HeapSelectIram ephemeral;
# endif // ifdef USE_SECOND_HEAP
res = calloc(num, size);
}
# endif // ifdef USE_SECOND_HEAP
#endif // ifdef ESP32
if (res == nullptr) {
@@ -212,7 +212,7 @@ bool String_reserve_special(String& str, size_t size) {
return true;
}
#ifdef USE_SECOND_HEAP
if (size >= 48) {
if (size >= 48 || FreeMem() < 5000) {
// Only try to store larger strings here as those tend to be kept for a longer period.
HeapSelectIram ephemeral;
// String does round up to nearest multiple of 16 bytes, so no need to round up to multiples of 32 bit here
@@ -19,6 +19,9 @@
# include <OLED_SSD1306_SH1106_images.h>
void P036_LineContent::loadDisplayLines(taskIndex_t taskIndex, uint8_t LoadVersion) {
#ifdef USE_SECOND_HEAP
HeapSelectIram ephemeral;
#endif
if (LoadVersion == 0) {
// read data of version 0 (up to 22.11.2019)
String DisplayLinesV0[P36_Nlines]; // used to load the CustomTaskSettings for V0
+158 -142
View File
@@ -34,7 +34,6 @@
# include "../Globals/NPlugins.h"
void handle_notifications() {
# ifndef BUILD_NO_RAM_TRACKER
checkRAM(F("handle_notifications"));
@@ -67,43 +66,51 @@ void handle_notifications() {
} else {
MakeNotificationSettings(NotificationSettings);
if (Settings.Notification[notificationindex] != notification.value)
{
Settings.Notification[notificationindex] = notification.value;
}
else
{
if (Settings.Notification[notificationindex] != INVALID_N_PLUGIN_ID.value)
if (!AllocatedNotificationSettings()) {
addHtmlError(F("Out of memory!"));
} else {
if (Settings.Notification[notificationindex] != notification.value)
{
nprotocolIndex_t NotificationProtocolIndex = getNProtocolIndex_from_NotifierIndex(notificationindex);
Settings.Notification[notificationindex] = notification.value;
}
else
{
if (Settings.Notification[notificationindex] != INVALID_N_PLUGIN_ID.value)
{
nprotocolIndex_t NotificationProtocolIndex = getNProtocolIndex_from_NotifierIndex(notificationindex);
if (validNProtocolIndex(NotificationProtocolIndex)) {
String dummyString;
NPlugin_ptr[NotificationProtocolIndex](NPlugin::Function::NPLUGIN_WEBFORM_SAVE, 0, dummyString);
if (validNProtocolIndex(NotificationProtocolIndex)) {
String dummyString;
NPlugin_ptr[NotificationProtocolIndex](NPlugin::Function::NPLUGIN_WEBFORM_SAVE, 0, dummyString);
}
// Reload & overwrite
LoadNotificationSettings(notificationindex, reinterpret_cast<uint8_t *>(NotificationSettings.get()),
sizeof(NotificationSettingsStruct));
NotificationSettings->validate();
NotificationSettings->Port = getFormItemInt(F("port"), 0);
NotificationSettings->Timeout_ms = getFormItemInt(F("timeout"), NPLUGIN_001_DEF_TM);
NotificationSettings->Pin1 = getFormItemInt(F("pin1"), -1);
NotificationSettings->Pin2 = getFormItemInt(F("pin2"), -1);
Settings.NotificationEnabled[notificationindex] = isFormItemChecked(F("notificationenabled"));
strncpy_webserver_arg(NotificationSettings->Domain, F("domain"));
strncpy_webserver_arg(NotificationSettings->Server, F("server"));
strncpy_webserver_arg(NotificationSettings->Sender, F("sender"));
strncpy_webserver_arg(NotificationSettings->Receiver, F("receiver"));
strncpy_webserver_arg(NotificationSettings->Subject, F("subject"));
strncpy_webserver_arg(NotificationSettings->User, F("username"));
strncpy_webserver_arg(NotificationSettings->Body, F("body"));
copyFormPassword(F("password"), NotificationSettings->Pass, sizeof(NotificationSettings->Pass));
}
// Reload & overwrite
LoadNotificationSettings(notificationindex, reinterpret_cast<uint8_t *>(&NotificationSettings), sizeof(NotificationSettingsStruct));
NotificationSettings.validate();
NotificationSettings.Port = getFormItemInt(F("port"), 0);
NotificationSettings.Timeout_ms = getFormItemInt(F("timeout"), NPLUGIN_001_DEF_TM);
NotificationSettings.Pin1 = getFormItemInt(F("pin1"), -1);
NotificationSettings.Pin2 = getFormItemInt(F("pin2"), -1);
Settings.NotificationEnabled[notificationindex] = isFormItemChecked(F("notificationenabled"));
strncpy_webserver_arg(NotificationSettings.Domain, F("domain"));
strncpy_webserver_arg(NotificationSettings.Server, F("server"));
strncpy_webserver_arg(NotificationSettings.Sender, F("sender"));
strncpy_webserver_arg(NotificationSettings.Receiver, F("receiver"));
strncpy_webserver_arg(NotificationSettings.Subject, F("subject"));
strncpy_webserver_arg(NotificationSettings.User, F("username"));
strncpy_webserver_arg(NotificationSettings.Body, F("body"));
copyFormPassword(F("password"), NotificationSettings.Pass, sizeof(NotificationSettings.Pass));
}
}
addHtmlError(SaveNotificationSettings(notificationindex, reinterpret_cast<const uint8_t *>(&NotificationSettings),
sizeof(NotificationSettingsStruct)));
addHtmlError(SaveNotificationSettings(
notificationindex,
reinterpret_cast<const uint8_t *>(NotificationSettings.get()),
sizeof(NotificationSettingsStruct)));
}
// Save the settings.
@@ -138,51 +145,55 @@ void handle_notifications() {
MakeNotificationSettings(NotificationSettings);
for (uint8_t x = 0; x < NOTIFICATION_MAX; x++)
{
LoadNotificationSettings(x, reinterpret_cast<uint8_t *>(&NotificationSettings), sizeof(NotificationSettingsStruct));
NotificationSettings.validate();
html_TR_TD();
html_add_button_prefix();
addHtml(F("notifications?index="));
addHtmlInt(x + 1);
addHtml(F("'>Edit</a>"));
html_TD();
addHtmlInt(x + 1);
html_TD();
if (Settings.Notification[x] != INVALID_N_PLUGIN_ID.value)
if (!AllocatedNotificationSettings()) {
addHtmlError(F("Out of memory!"));
} else {
for (uint8_t x = 0; x < NOTIFICATION_MAX; x++)
{
addEnabled(Settings.NotificationEnabled[x]);
LoadNotificationSettings(x, reinterpret_cast<uint8_t *>(NotificationSettings.get()), sizeof(NotificationSettingsStruct));
NotificationSettings->validate();
html_TR_TD();
html_add_button_prefix();
addHtml(F("notifications?index="));
addHtmlInt(x + 1);
addHtml(F("'>Edit</a>"));
html_TD();
addHtmlInt(x + 1);
html_TD();
uint8_t NotificationProtocolIndex = getNProtocolIndex(npluginID_t::toPluginID(Settings.Notification[x]));
String NotificationName = F("(plugin not found?)");
if (validNProtocolIndex(NotificationProtocolIndex))
if (Settings.Notification[x] != INVALID_N_PLUGIN_ID.value)
{
NPlugin_ptr[NotificationProtocolIndex](NPlugin::Function::NPLUGIN_GET_DEVICENAME, 0, NotificationName);
}
addHtml(NotificationName);
html_TD();
addHtml(NotificationSettings.Server);
html_TD();
addEnabled(Settings.NotificationEnabled[x]);
if (NotificationSettings.Port) {
addHtmlInt(NotificationSettings.Port);
} else {
// MFD: we display the GPIO
addGpioHtml(NotificationSettings.Pin1);
html_TD();
uint8_t NotificationProtocolIndex = getNProtocolIndex(npluginID_t::toPluginID(Settings.Notification[x]));
String NotificationName = F("(plugin not found?)");
if (NotificationSettings.Pin2 >= 0)
if (validNProtocolIndex(NotificationProtocolIndex))
{
html_BR();
addGpioHtml(NotificationSettings.Pin2);
NPlugin_ptr[NotificationProtocolIndex](NPlugin::Function::NPLUGIN_GET_DEVICENAME, 0, NotificationName);
}
addHtml(NotificationName);
html_TD();
addHtml(NotificationSettings->Server);
html_TD();
if (NotificationSettings->Port) {
addHtmlInt(NotificationSettings->Port);
} else {
// MFD: we display the GPIO
addGpioHtml(NotificationSettings->Pin1);
if (NotificationSettings->Pin2 >= 0)
{
html_BR();
addGpioHtml(NotificationSettings->Pin2);
}
}
}
}
else {
html_TD(3);
else {
html_TD(3);
}
}
}
html_end_table();
@@ -213,88 +224,93 @@ void handle_notifications() {
if (Settings.Notification[notificationindex] != INVALID_N_PLUGIN_ID.value)
{
MakeNotificationSettings(NotificationSettings);
LoadNotificationSettings(notificationindex, reinterpret_cast<uint8_t *>(&NotificationSettings), sizeof(NotificationSettingsStruct));
NotificationSettings.validate();
nprotocolIndex_t NotificationProtocolIndex = getNProtocolIndex_from_NotifierIndex(notificationindex);
if (!AllocatedNotificationSettings()) {
addHtmlError(F("Out of memory!"));
} else {
LoadNotificationSettings(notificationindex, reinterpret_cast<uint8_t *>(NotificationSettings.get()), sizeof(NotificationSettingsStruct));
NotificationSettings->validate();
if (validNProtocolIndex(NotificationProtocolIndex))
{
if (Notification[NotificationProtocolIndex].usesMessaging)
nprotocolIndex_t NotificationProtocolIndex = getNProtocolIndex_from_NotifierIndex(notificationindex);
if (validNProtocolIndex(NotificationProtocolIndex))
{
if (NotificationSettings.Port == 0) {
# if FEATURE_EMAIL_TLS
NotificationSettings.Port = 465;
# else // if FEATURE_EMAIL_TLS
NotificationSettings.Port = 25;
# endif // if FEATURE_EMAIL_TLS
}
addFormSubHeader(F("SMTP Server Settings"));
addFormTextBox(F("Domain"), F("domain"), NotificationSettings.Domain, sizeof(NotificationSettings.Domain) - 1);
addFormTextBox(F("Server"), F("server"), NotificationSettings.Server, sizeof(NotificationSettings.Server) - 1);
addFormNumericBox(
F("Port"), F("port"),
NotificationSettings.Port,
1,
65535);
# if FEATURE_EMAIL_TLS
addFormNote(F("default port SSL: 465"));
# else // if FEATURE_EMAIL_TLS
addFormNote(F("default port: 25, SSL/TLS servers NOT supported!"));
# endif // if FEATURE_EMAIL_TLS
if ((NotificationSettings.Timeout_ms < NPLUGIN_001_MIN_TM) ||
(NotificationSettings.Timeout_ms > NPLUGIN_001_MAX_TM))
if (Notification[NotificationProtocolIndex].usesMessaging)
{
NotificationSettings.Timeout_ms = NPLUGIN_001_DEF_TM;
if (NotificationSettings->Port == 0) {
# if FEATURE_EMAIL_TLS
NotificationSettings->Port = 465;
# else // if FEATURE_EMAIL_TLS
NotificationSettings->Port = 25;
# endif // if FEATURE_EMAIL_TLS
}
addFormSubHeader(F("SMTP Server Settings"));
addFormTextBox(F("Domain"), F("domain"), NotificationSettings->Domain, sizeof(NotificationSettings->Domain) - 1);
addFormTextBox(F("Server"), F("server"), NotificationSettings->Server, sizeof(NotificationSettings->Server) - 1);
addFormNumericBox(
F("Port"), F("port"),
NotificationSettings->Port,
1,
65535);
# if FEATURE_EMAIL_TLS
addFormNote(F("default port SSL: 465"));
# else // if FEATURE_EMAIL_TLS
addFormNote(F("default port: 25, SSL/TLS servers NOT supported!"));
# endif // if FEATURE_EMAIL_TLS
if ((NotificationSettings->Timeout_ms < NPLUGIN_001_MIN_TM) ||
(NotificationSettings->Timeout_ms > NPLUGIN_001_MAX_TM))
{
NotificationSettings->Timeout_ms = NPLUGIN_001_DEF_TM;
}
addFormNumericBox(
F("Timeout"), F("timeout"),
NotificationSettings->Timeout_ms,
NPLUGIN_001_MIN_TM,
NPLUGIN_001_MAX_TM
# if FEATURE_TOOLTIPS
, F("Maximum Server Response Time")
# endif // if FEATURE_TOOLTIPS
);
addUnit(F("ms"));
ZERO_TERMINATE(NotificationSettings->Pass);
addFormSubHeader(F("Credentials"));
addFormTextBox(F("Username"), F("username"), NotificationSettings->User, sizeof(NotificationSettings->User) - 1);
addFormPasswordBox(F("Password"), F("password"), NotificationSettings->Pass, sizeof(NotificationSettings->Pass) - 1);
addFormSubHeader(F("Email Attributes"));
addFormTextBox(F("Sender"), F("sender"), NotificationSettings->Sender, sizeof(NotificationSettings->Sender) - 1);
addFormTextBox(F("Receiver"), F("receiver"), NotificationSettings->Receiver, sizeof(NotificationSettings->Receiver) - 1);
addFormTextBox(F("Subject"), F("subject"), NotificationSettings->Subject, sizeof(NotificationSettings->Subject) - 1);
addRowLabel(F("Body"));
addHtml(F("<textarea name='body' rows='20' size=512 wrap='off'>"));
addHtml(NotificationSettings->Body);
addHtml(F("</textarea>"));
}
addFormNumericBox(
F("Timeout"), F("timeout"),
NotificationSettings.Timeout_ms,
NPLUGIN_001_MIN_TM,
NPLUGIN_001_MAX_TM
# if FEATURE_TOOLTIPS
, F("Maximum Server Response Time")
# endif // if FEATURE_TOOLTIPS
);
if (Notification[NotificationProtocolIndex].usesGPIO > 0)
{
addRowLabel(F("1st GPIO"));
addPinSelect(PinSelectPurpose::Generic, F("pin1"), NotificationSettings->Pin1);
}
addUnit(F("ms"));
addRowLabel(F("Enabled"));
addCheckBox(F("notificationenabled"), Settings.NotificationEnabled[notificationindex]);
ZERO_TERMINATE(NotificationSettings.Pass);
addFormSubHeader(F("Credentials"));
TempEvent.NotificationIndex = notificationindex;
String webformLoadString;
NPlugin_ptr[NotificationProtocolIndex](NPlugin::Function::NPLUGIN_WEBFORM_LOAD, &TempEvent, webformLoadString);
addFormTextBox(F("Username"), F("username"), NotificationSettings.User, sizeof(NotificationSettings.User) - 1);
addFormPasswordBox(F("Password"), F("password"), NotificationSettings.Pass, sizeof(NotificationSettings.Pass) - 1);
addFormSubHeader(F("Email Attributes"));
addFormTextBox(F("Sender"), F("sender"), NotificationSettings.Sender, sizeof(NotificationSettings.Sender) - 1);
addFormTextBox(F("Receiver"), F("receiver"), NotificationSettings.Receiver, sizeof(NotificationSettings.Receiver) - 1);
addFormTextBox(F("Subject"), F("subject"), NotificationSettings.Subject, sizeof(NotificationSettings.Subject) - 1);
addRowLabel(F("Body"));
addHtml(F("<textarea name='body' rows='20' size=512 wrap='off'>"));
addHtml(NotificationSettings.Body);
addHtml(F("</textarea>"));
}
if (Notification[NotificationProtocolIndex].usesGPIO > 0)
{
addRowLabel(F("1st GPIO"));
addPinSelect(PinSelectPurpose::Generic, F("pin1"), NotificationSettings.Pin1);
}
addRowLabel(F("Enabled"));
addCheckBox(F("notificationenabled"), Settings.NotificationEnabled[notificationindex]);
TempEvent.NotificationIndex = notificationindex;
String webformLoadString;
NPlugin_ptr[NotificationProtocolIndex](NPlugin::Function::NPLUGIN_WEBFORM_LOAD, &TempEvent, webformLoadString);
if (webformLoadString.length() > 0) {
addHtmlError(F("Bug in NPlugin::Function::NPLUGIN_WEBFORM_LOAD, should not append to string, use addHtml() instead"));
if (webformLoadString.length() > 0) {
addHtmlError(F("Bug in NPlugin::Function::NPLUGIN_WEBFORM_LOAD, should not append to string, use addHtml() instead"));
}
}
}
}