Strings fressen meinen Speicher auf

Ich habe folgende funktionen, mit denen ich einen HTML QueryString erstelle:

bool save_data(PubSubClient* mqttClient, WiFiClient* wifiClient, const char* data) {
  if (WiFi.status() != WL_CONNECTED && wasConnected) {
    delay(200);
    ESP.restart();
  }

  digitalWrite(WIFI_LED, LOW);
  
  const char* PimaticHost = HandleEeprom(pimhost_Address, EEPROM_READ).c_str();
  uint16_t PimaticPort = EepromReadInt16(pimport_Address);

  if (PimaticType == 1) {
#ifdef DEBUG
    char query[33];
    snprintf(query, sizeof(query), "%s", HandleEeprom(pimquery_Address, EEPROM_READ).c_str());
    Serial.print(F("Sending data: http://"));
    Serial.print(PimaticHost);
    Serial.println((PimaticPort != 80 ? ":" + PimaticPort : "") + query + data);
#endif
    // Use WiFiClient class to create TCP connections
    if (!wifiClient->connect(PimaticHost, PimaticPort)) {
#ifdef DEBUG
      Serial.println(F("Connection Error"));
#endif
      digitalWrite(WIFI_LED, HIGH);
      return false;
    }
    // This will send the request to the server
    wifiClient->println("GET /" + HandleEeprom(pimquery_Address, EEPROM_READ, "", "/") + data + "\r\nHTTP/1.1");
    wifiClient->print("Host: ");
    wifiClient->println(PimaticHost);
    wifiClient->println("Connection: close\r\n");

#ifdef DEBUG
    delay(200);
    // Read all the lines of the reply from server and print them to Serial
    String response = "";
    while (wifiClient->available()) {
      response = wifiClient->readStringUntil('\r');
// only for debug
//      Serial.print(response);
    }
#endif
    wifiClient->flush();
    wifiClient->stop();
  }
  else if (PimaticType == 2) {
    if (!mqttClient->connected()) {
      MqttReconnect(mqttClient);
    }
#ifdef DEBUG
    Serial.print(F("Publish message: "));
    Serial.println(data);
#endif
    char topic[50] = {'\0'};
    snprintf(topic, sizeof(topic), "%s/%s", HandleEeprom(pimquery_Address, EEPROM_READ).c_str(), DeviceName);
    mqttClient->publish(topic, data);
  }
  delay(200);
  digitalWrite(WIFI_LED, HIGH);
  return true;
}

void addValue(String& queryString, const char* var, char value[], bool firstValue = false) {
  queryString += (firstValue ? "\"" : ",\"");
  queryString += var;
  queryString += "\":";
  queryString += value;
}

und im loop:

void loop() {
  .
  .
  .
  if (millis() - pimatic_lastInterval > pimatic_sendInterval && PimaticEnabled) {
    String msg = "";
    switch (PimaticType) {
      case 1:     //HTTP
        msg = "?data={\"sensor\":\"" + String(DeviceName) + "\",";
        break;
      case 2:     //MQTT
        msg = "{";
        break;
      default:    //Pimatic
        msg = "";
        break;
    }
    bool firstValue = true;
    addValue(msg, "bv", LastBatteryVoltage, firstValue);
    firstValue = false;

    if (ADCEnabled) {
      addValue(msg, HandleEeprom(adcvar_Address, EEPROM_READ, "" "value1").c_str(), LastAnalogValue, firstValue);
      firstValue = false;
    }
    if (BME280Enabled) {
      addValue(msg, HandleEeprom(bme280tempvar_Address, EEPROM_READ, "", "value2").c_str(), LastBmeTemperature, firstValue);
      addValue(msg, HandleEeprom(bme280humvar_Address, EEPROM_READ, "", "value3").c_str(), LastBmeHumidity);
      addValue(msg, HandleEeprom(bme280pressvar_Address, EEPROM_READ, "", "value4").c_str(), LastBmePressure);
      firstValue = false;
    }
    if (BH1750Enabled) {
      addValue(msg, HandleEeprom(bh1750var_Address, EEPROM_READ, "", "value5").c_str(), LastBrightness, firstValue);
      firstValue = false;
    }
    if (MoistureEnabled) {
      addValue(msg, HandleEeprom(moisturevar_Address, EEPROM_READ, "", "value6").c_str(), LastSoilMoisture, firstValue);
      char buff[24];
      snprintf(buff, sizeof(buff), "%lu", last_watering_time);
      addValue(msg, "lw", buff);
      firstValue = false;
    }
    if (RainGaugeEnabled) {
      addValue(msg, "hr", HourlyRain, firstValue);
      addValue(msg, "dr", DaylyRain);
      firstValue = false;
    }
    if (PimaticType < 3 {
      msg += "}";
    }
    save_data(&mqttClient, &wifiClient, msg.c_str());
    pimatic_lastInterval = millis();
  }
}

Jeder Aufbau der msg fragmentiert den Speicher und nach 8-12 Tagen bootet der ESP32 neu.
Nach send_data(...) gibt ESP.getFreeHeap(); bis zu 2kB weniger zurück.

Die gleiche Hardware, ohne die Daten an den Webserver zu senden, läuft schon seit über 30 Tagen stabil.

Wie kann ich die msg anders konstruieren, ohne immer bis zu 2 KB Speicher zu verlieren.

Gruß
Fred

Nutze Char-Arrays anstelle von Strings. Infos. Auch das F-Macro kann Dir an einigen Stellen helfen.

Gruß Tommy

snprintf() kannst du doch auch für msg verwenden. Oder str(n)cat()

Ansonsten sehr gerne ignoriert:

Serenifly:
Ansonsten sehr gerne ignoriert:

Gbt es mittlerweile auch in deutsch.

Alles was in der refence steht, kann durch Austausch von /en/ in der url durch /de/ für die weniger englishkenntnishabende Userschaft auch in Deutsch gelesen werden.

...
Wenn ich das Ankündigungspost finde, trag ichs hier noch ein.
War garnicht schwer:
Die Ankündigung und Hinweis für Berichtigungen:Die deutschsprachige Befehlsreferenz ist fertig :) - Deutsch - Arduino Forum

snprintf und reserve...
ich weiß doch im Voraus nicht, wie lang der Text letztendlich wird.
zu viel Speicher reserviert -> Verschwendung
zu wenig Speicher reserviert-> BOOM

Gesamtspeicher ist auf einem ESP eigentlich kein Problem. Da muss man nicht so knausrig sein

Und reserve() ist nicht der maximale Speicher. Das kann immer noch darüber hinaus wachsen. Aber man verhindert dass ständig der Speicher umorganisiert wird nur weil man mal ein Zeichen mehr braucht

freddy64:
ich weiß doch im Voraus nicht, wie lang der Text letztendlich wird.

Du brauchst Dir doch nur immer, wenn msg fertig ist, deren Länge auf Serial ausgeben zu lassen.
Dann bekommst Du doch eine Übersicht über die benötigte Länge.

Gruß Tommy