Function running when it shouldn't

My program seems to work properly with one (or maybe two) temperature probes.
But when I have more (five probes in this case) connected, one function that is called during an alarm event runs too often. Let me explain:
My program should work as follows:

  1. Every 15 seconds, it queries the five temperature probes, the readings are sent to my local MQTT broker. This section works as it should. (Results display on an Android app)
  2. Every minute, it queries the five temperature probes, the readings are sent to the ThingSpeak.com MQTT broker. This section also works as it should.
  3. Every six minutes, it queries the five temperature probes and checks to see if the temperature is too high, too low or within range.
    If the temperature is too high or too low, it checks if a "flag" is set, if it is not, it crafts an email (subject and body) with the relevant information, jumps to another function that fires off the email.
    When it returns from sending the email, it sets the "flag" that the email has been sent.
    The next time it runs, if the "flag" is set, it skips sending a duplicate email.
    Once the temperature returns to the "normal" range, it once again crafts an email, jumps to the function to send the email, upon the return, it "unsets" the flag. This also all works as it should.

The problem I am running into is:
if a temperature probe is right on the "balance point" of being "normal" and being too high (or too low), I will get bombarded with emails. Sometimes, three or four (or more) within a minute or two.

In my design, I should ONLY be running the "Check Alarm" function every six minutes.
I understand that I may get multiple emails if multiple probes are too high or too low, this is normal and expected.

But, I may get several emails (for example) with one probe going between 80 (normal) and 81 (too high) several time in a row, until it either gets a bit warmer or cooler so it stop flapping between the set point.

I have written and rewritten this code about four or five different ways with no luck in any of my attempts or ideas on how to correct this issue.

Am I missing something easy here?
Maybe a new set of eyes on the code will see the error in my ways.

NOTE: The code listed below is just a small subset of the bigger program.
What I have below will compile and run, unfortunately, I only have one probe in my test setup and can't get it to fail.
My main circuit is a bit remote and I have to wait basically a full 24 hours to get any good test results.
I'm sure I have left out some details that may be revelant, or if you need further clarification, please don't hesitate to ask and I will try to better explain.

#include <ESP8266WiFi.h>
#include <OneWire.h>
#include <DallasTemperature.h>
#include <ESP_Mail_Client.h>

const char *wifiSSID          = "SSID";
const char *wifiPassword      = "Password";
const char *wifiHostName      = "TempPublisher-0";
const char *smtpHost          = "smtp.gmail.com";
const char *senderEmail       = "EMailSenderAddress";
const char *senderPassword    = "EMailSenderPassword";
const char *receiverEmail1    = "whoGetsTheEMail";
const int smtpPort            = 587;
const int oneWireBuss         = 4;
int alarmCheckCounter         = 0;
int oneSecond                 = 1000;
int oneMinute                 = 60000;
int tempSensorCount           = 0;
int temperatureF              = 0;
int tempSetPoints             = 0;
unsigned long alarmCheckDelay = 0;

WiFiClient wLANClient0;
OneWire oneWire(oneWireBuss);
DallasTemperature sensors(&oneWire);
SMTPSession smtp;
Session_Config config;
SMTP_Message message;

void wifiConnect() {
  WiFi.mode(WIFI_STA);
  WiFi.hostname(wifiHostName);
  WiFi.begin(wifiSSID, wifiPassword);
  delay(oneSecond);
  Serial.print("\nAttempting connection to WiFi Network... ");
  while(WiFi.status() != WL_CONNECTED) {
    Serial.print("/");
    delay(oneSecond / 8);
    Serial.print("\\");
    delay(oneSecond / 4);
  }
  WiFi.setAutoReconnect(true);
  WiFi.persistent(true);
  Serial.print("\nConnected to WiFi Network: ");
  Serial.println(WiFi.SSID());
  Serial.print("On IP Address: ");
  Serial.println(WiFi.localIP());
  Serial.print("With Hostname: ");
  Serial.println(WiFi.hostname());
  Serial.print("Power Level: ");
  Serial.print(WiFi.RSSI());
  Serial.println("dB");
}

void eMailConnect() {
  MailClient.networkReconnect(true);
  smtp.debug(0);
  config.server.host_name = smtpHost;
  config.server.port = smtpPort;
  config.login.email = senderEmail;
  config.login.password = senderPassword;
  config.login.user_domain = "";
  message.sender.name = F("Temp Probes");
  message.sender.email = senderEmail;
  message.addRecipient(F("SomeBody"), receiverEmail1);
}

void sendEmail() {
  smtp.connect(&config);
  MailClient.sendMail(&smtp, &message);
  Serial.println("Message has been sent!");
}

String tempProbeInfo[6][6] {
  {"NotSent", "NotSent", "Probe-1", "80", "65"},
  {"NotSent", "NotSent", "Probe-2", "80", "65"},
  {"NotSent", "NotSent", "Probe-3", "80", "65"},
  {"NotSent", "NotSent", "Probe-4", "80", "65"},
  {"NotSent", "NotSent", "Probe-5", "85", "50"},
  {"NotSent", "NotSent", "Probe-6", "80", "65"}
};

void alarmCheck() {
  sensors.requestTemperatures();
  for(alarmCheckCounter = 0; alarmCheckCounter <= tempSensorCount; alarmCheckCounter++) {
    temperatureF = roundf(sensors.getTempFByIndex(alarmCheckCounter));
    if(temperatureF > (tempSetPoints = tempProbeInfo[alarmCheckCounter][3].toInt())) {
      if(tempProbeInfo[alarmCheckCounter][0] == "NotSent") {
        String htmlMsg = "<div><h2>" + tempProbeInfo[alarmCheckCounter][2] + "</h2></br><h1><em>The temperature is too high!<em></h1></br><h1>" + temperatureF + "</h1></div>";
        message.html.content = htmlMsg.c_str();
        message.subject = tempProbeInfo[alarmCheckCounter][2];
        sendEmail();
        tempProbeInfo[alarmCheckCounter][0] = "Sent";
      }
    }
    else if(temperatureF < (tempSetPoints = tempProbeInfo[alarmCheckCounter][4].toInt())) {
      if(tempProbeInfo[alarmCheckCounter][1] == "NotSent") {
        String htmlMsg = "<div><h2>" + tempProbeInfo[alarmCheckCounter][2] + "</h2></br><h1><em>The temperature is too low!<em></h1></br><h1>" + temperatureF + "</h1></div>";
        message.html.content = htmlMsg.c_str();
        message.subject = tempProbeInfo[alarmCheckCounter][2];
        sendEmail();
        tempProbeInfo[alarmCheckCounter][1] = "Sent";
      }
    }
    else {
      if(tempProbeInfo[alarmCheckCounter][0] == "Sent") {
        String htmlMsg = "<div><h2>" + tempProbeInfo[alarmCheckCounter][2] + "</h2></br><h1><em>The temperature is normal<em></h1></br><h1>" + temperatureF + "</h1></div>";
        message.html.content = htmlMsg.c_str();
        message.subject = tempProbeInfo[alarmCheckCounter][2];
        sendEmail();
        tempProbeInfo[alarmCheckCounter][0] = "NotSent";
      }
      if(tempProbeInfo[alarmCheckCounter][1] == "Sent") {
        String htmlMsg = "<div><h2>" + tempProbeInfo[alarmCheckCounter][2] + "</h2></br><h1><em>The temperature is normal<em></h1></br><h1>" + temperatureF + "</h1></div>";
        message.html.content = htmlMsg.c_str();
        message.subject = tempProbeInfo[alarmCheckCounter][2];
        sendEmail();
        tempProbeInfo[alarmCheckCounter][1] = "NotSent";
      }
    }
  }
  alarmCheckDelay = millis();
}

void setup() {
  Serial.begin(115200);
  wifiConnect();
  sensors.begin();
  tempSensorCount = sensors.getDeviceCount() - 1;
  eMailConnect();
}

void loop() {
  if(millis() >= (alarmCheckDelay + (oneMinute * 6))) {
    alarmCheck();
  }
}

@Delta_G , thanks for the input.

I thought about hysteresis, and grasp the basic idea of it, I think...
But I don't know how to implement it. Do you know of any code examples that you can point me towards?

Also RE: the rollover bug for millis, thanks for the info, I will look at the example and figure out how to work in it my code properly.

@Delta_G ,
Hmmm, well, the temperature probes are not controlling anything (heaters/coolers, etc.) but rather reporting the temperatures in a seed starting rack.
The probes are in cells with soil (no pepper/tomato seed) to keep tabs on the soil temperature to ensure good germination.
Maybe I just need to move the high and low set points a bit "wider" than what the temperature seems to be stabilizing at during a 24 hour cycle.
Still, it seems very odd that the loop cycles many (many) times in a row when it should still only run through getting the temperature readings every six minutes.
Additional note:
If any of the probes are in "alarm" (too high or too low) when I power up the NodeMCU controller, I get a full six minute wait (as it should) before I get the first round of emails.
This is also the expected result.
But later in the day/evening, if a temperature drops (for example) and get to that "balance" point, I will start getting the emails fast and furious when I should still be getting them every six minutes.

Hysteresis, one of my favorites

Easiest way to see it

    if it is too hot enough, turn off the heat

    if it is too cold enough, turn on the heat

more code-like

   if (temperature > hotEnough) digitalWrite(heaterPin, LOW);

   if (temperature < coldEnough) digitalWrite(heaterPin, HIGH);

where in you home hotEnough might be a degree warmer than the setpoint and coldEnough a degree cooler than the setpoint on the thermostat.

When I am not moving I will look to see where that might go in your sketch.

a7

1 Like

@Delta_G ,
I understand what you are saying about two IF statements and two setpoints.
My code has all that, what I think I may not be explaingin well enough is the fact that the "FOR" loop in the "alarmCheck" function is running more times than then number of temperature sensors detected.
Like it's almost in an endless loop until the temperature reading settles down more outside of the High or low setpoint for a given probe.
Like it never exits the "For" loop properly.

@Delta_G ,
I will have to look at it more in-depth tomorrow.

RE: Strings for the "flags".
IDK, it's how I set it up. There is a different part of the program that needs to have some of the values as a "String". So, I reasoned that to save space and make it easier for me, I would just put all the little bits into one array. I don't know that it's "wrong" (or right), I just know that it works for me.
It's really hard to know all these "rules" and best practices when you are not a programmer!

Is this a new problem or the same one?

This

  for(alarmCheckCounter = 0; alarmCheckCounter <= tempSensorCount; alarmCheckCounter++) {

goes one too far and tries to handle tempSensorCount + 1 number of <whatever>s.

a7

I just noticed that. To be fair, it doesn't break, it finally gets around to doing what might have been intended for every loop iteration - a pause of alarmCheckDelay + (oneMinute * 6) milliseconds.

Now alarmCheck() gets called a bit more frequently.

a7

@scottvinzant - you are sending one of four messages. If you made them controlled by the number, you could condition the email to be suppressed if it would be the same message as last time.

Start with having sent a fake impossible message guaranteed not to match the first real message, then just don't send "it's too hot" again, e.g.

Your logic may be sound, but I got lost trying to see where things returned to norbal.

I fed the requirements for the logic of this to the AI I trained on 50000 lines of my own Arduino code. This is that plus a bit of inevitable (so far) adjustmenting.

Try it here. The slide fader controls the input variable from very too cold to vary too hot.


Wokwi_badge UA Hysteresis Message Demo


And the code

Hysteresis Messages Code
// https://forum.arduino.cc/t/function-running-when-it-shouldnt/1237583
// https://wokwi.com/projects/392878392548727809

void setup() {
  Serial.begin(115200);
  Serial.println("Jello Whirled!\n");
}

bool sentHot;
bool sentCold;
bool sentNormal;

# define TOO_HOT    768
# define TOO_COLD   256

void loop() {
  int temperature = analogRead(A0);

  if (temperature > TOO_HOT) {
    if (!sentHot) {
      Serial.println("Too hot.");
      sentHot = true;
      sentCold = false;
      sentNormal = false;
    }
  }
  else if (temperature < TOO_COLD) {
    if (!sentCold) {
      Serial.println("Too cold.");
      sentCold = true;
      sentHot = false;
      sentNormal = false;
    }
  }
  else {
    if (!sentNormal) {
      Serial.println("Temperature normal.");
      sentNormal = true;
    }
  }
//  delay(777);  // until it works
}

I tried to utnagle @scottvinzant's logic for awhile. Easier to see this when it is stripped away from all the drama in the code doing the real work.

The use of Strings for internal communication is clever, but that too seemed to place a block in front of me.

It makes the code readable in a certain way, but using other common habits would do the same. In particular well-named variables, manifest constants and enum types can be very useful.

As can the use of functions (lotta duplicated code) and a strict separation of logic from pesky steps of action.

What was needed was just a bit more than a "sent/not sent" concept. Look in the code to see how flags get set to suppress dupliacte messages. Look in the code to see where certain message flags get reset so the message the protect will go out again.

HTH

a7

@alto777 ,
Thank you for taking the time to look deeper into my issue, and thank you for working up that cool simulation.
Ultimately, I don't think it's a hysteresis problem, as my code works as I built it, except for this "alarm" part. Sometimes...
What I think may be happening is every once in a while, you will get a bad reading from a sensor.
I have to wonder if that is tripping things up in the "FOR" loop. Maybe I can find a way to trap those bad readings out and through them away.
Also, I probably failed to explain in the best terms what this section of code does.
Again, the rest of the program (sending data to two different MQTT brokers works perfectly.
Let me try to detail it out again... (If you don't care, stop reading here. This is really long....)
Every six minutes run this "AlarmCheck" function.
The "FOR" loop will iterate through however many temperature probes were detected at boot time.
For this group, it's five probes.
Get the temperature reading for the first probe and check to see if:
The temperature is above the high setpoint.
If not, check to see if is below the low setpoint.
if not, it must be within the "normal" range.
If it's within the "normal" range, check to see the status of the "Sent/NotSent" flag.
For the very first run from a clean boot, it will be set to "NotSent" for both the high and low set points.
If no probes are in alarm, it will finish the first iteration without doing anything. This is normal/expected behavior and works as described.
BUT, If it finds that (for example) that the temperature reading is too high, check to see what the "Sent/NotSent" flag is set to.
If it is "NotSent", then craft together an email with the probe number and current temperature.
Jump out the the "sendEmail" function, log into the server, send the email, print a console message that the email has been sent, return from where it came from and set the "Sent/NotSent" flag to "Sent". This all works as expected, too.
Since that "IF' block of code was "True" (the temperature being too high) the next "Else If" block is skipped as is the following and final "Else" block of code.

That's it for the first iteration, then it does the next iteration(s) checking for too high or too low or and checking the "Sent/NotSent" flag along the way.
After the "AlarmCheck" function has iterated through the full list of temperature probes, it finely exits that code block and updates the "alarmCheckDelay" value with the current millis() count.
This also works as expected. (I've had console print statements sprinkled throughout my code to watch what was going on with counters, etc.)

Once again, back in the main loop as the millis() count exceeds the newly update "alarmCheckDelay" value (six minutes has elapsed), the "alarmCheck" function should once again run, but this time, if a temperature probe is still too high (again, for example), it will see that the "Sent/NotSent" flag is set to "Sent" and fail the test, then exit that section of the code block (NOT craft an email), also, because the first part of the test WAS true (the temperature was too high) it passed that test and will skip the next "Else if" and final "Else" blocks of code.

Again, a LOT of this works, it ALWAYS works correctly the first time after booting up if one (or more) of the probes are above or below the setpoints.
It always seems to be after the first run that things get weird.

The other wrinkle to all this is, as our weather gets better, and the sun comes up earlier, the temperature probes will not spend as much "time" flapping around on the "balance" point of "normal" and too high (or too low).

As an example, i had three reading for probe #2 this morning, yesterday, I had seven or eight.
It's still odd to me that it appears that the code gets sent around and around in the "FOR" loop when it should only go through based on the count of probes.
Even if a probe gave a bogus reading (typically -197F) it should report that as "too low" and move on to the next probe in the iteration loop. That is another reason to look into tossing out bad, improbable readings so as to not muddy up the inbox with junk readings.

And again, it seems that the more probes I have to iterate through, the bigger the problem is.
Another setup with only two probes, has only had the problem once or twice, I believe.
The other problem is, it's really hard to test for the fault, I basically just have to wait a full 24 hours and see what I get in my inbox.

So, there you have it, my detailed explanation of the inner workings of how my brain came to understand and write that code.

Thanks again for taking an interest in the problem and offering some solutions.
Scott

@Delta_G ,
The navigation of my program makes great sense to me, LOL!
Do you know of any (free / on-line) resources that are like: "Do it this way, BECAUSE! And, here's an example or three, try them out"
Or, "DON'T do it this way, because it is bad and people will have a hard time with it"
I don't have a problem learning new things, but I need to know WHY I should be doing something a particular way.
Thanks!
Scott

We like to help.

What I do not think you said is whether my demo produces the correct messages in the correct number in response to changes in the temperature.

Please say if it does not, and say how it should work.

Your logic, striPped of drama and actuall activity around the changes

    temperatureF = roundf(sensors.getTempFByIndex(alarmCheckCounter));

    if (temperatureF > something[3]) {

// temperature too high
    
      if (something[0] == "NotSent") {
        SEND: The temperature is too high!
        something[0] = "Sent";
      }
   
    }
    else if (temperatureF < something[4]) {

// temperature too low
    
      if (something[1] == "NotSent") {
        SEND: The temperature is too low!
        something[1] = "Sent";
      }

    }
    
    else {

// the temperature is neither too high or too low
// Resetting both flags here means the instant you read
//  a temperature that is too high or too low, you'll get another message
    
      if (something[0] == "Sent") {
        SEND: The temperature is normal
        something[0] = "NotSent";
      }
      
      if (something[1] == "Sent") {
        SEND: The temperature is normal
        something[1] = "NotSent";
      }

    }

  }

behaves differently, I think @Delta_G pointed out how the middle band neither too hot nor too cool improperly resets the flags, so you are not enjoying any benefits that hysteresis is desgined to afford.

So you will get spammed if the temperature just happens to wiggle about either the lower setpoitn or the upper setpoint.

My demo needed three flags, dunno if I could have spent more time and figured out how to dispense with any of them. I'd like to think not, but it wasn't terribly important. A third flag is not expensive, and it might make things that were clever more abundantly obvsly correct.

I repeat - if the code I wrote does what you want message-wise, please look to see where the three flags are set, and why, and where they are reset. This is the key to getting but one message when you are now getting too many redundant or otherwise unneeded.

When I am in the lab, I will place your logic into my demo, and you can see for yourself by sliding the temperature back and forth.

a7

Well, I would need to play with it a bit more, and look at your code example in the morning.
I think it basically works as you describe, BUT....
(You knew that was coming... :wink: )

I have to disagree on several other points. I know I typed a book about how I designed it and it works, but I think something keeps getting lost here.
Let me try again.... (Sorry more rambling drivel)

Ultimately, this "alarmCheck" function block should ONLY run every six minutes.
When the millis() value exceeds the last time it ran it will jump to the "alarmCheck" function.
Once there, it requests the sensor readings, sets up a "for" loop.
The first iteration through that loop it looks to see if the temperature is too high or too low, if nether case is true, it MUST be in the "normal" range. For sake of discussion, let say this is the first time it has ran, so in that final "else" section, the flags are set to "NotSent", hence the check of the too high and too low flags fails, so nothing happens. Perfect, this is how it should work.
Now, back to the top of the "for" loop for the next iteration and to check the next probe.
(Remember, this particular setup has five probes.)
Continue looping through the "for" statement for probes three, four and five.
(Also note that the probe "addresses" (by index) are zero referenced, so zero through four for the probe index number.
In the last iteration, as it passes through the final "else" section, and once again (assuming all the probes are in the normal range) it will exit that code block section and on it's way out, update the "alarmCheckDelay" value with the current millis() value.
Here it will ultimately exit the "alarmCheck" function block.

Back in the main loop, millis will have been incrementing while the probes have been gathering readings and getting compared if the temperature is too high or too low.
NOTE: it takes about one or two seconds to do that full run in the "alarmCheck" function.
So, really fast!

Now, the ONLY way for an email to be sent is from within that "alarmCheck" function.
How, if it is not "time" to run "alarmCheck" again, because millis has not exceeded the new "alarmCheckDelay" time (plus the six minutes added to it in the main loop) can it be possibly running to kick out an email?

So, every six minutes, the "alarmCheck" function runs through this scenario, as long as the temperature reading are within the high and low setpoints, noting happens. All is well.
But lets say during that six minute wait period, one of the probes gets to warm, once the six minute period times out, the "alarmCheck" function runs, and of course, the first "if" section evaluates as "True", perfect, the next "if" statement (checking the Sent/NotSent" flag) in that same section also evaluates as "True". As it should.
The email strings are "built" with the pertinent information and the "sendEmail" function is called.
The email gets sent and as the program return from the "sendEmail" function, the flag for that probe being to hot gets set to "Sent". Again, all this works as I have had console print statements watching all this happen in realtime. So far, so good.
Then the "for" loop continues to iterate through the rest of the temperature probes.
After the final probe is checked, once again the "alarmCheckDelay" value gets updated with the current millis value.
NOTE: if an email gets kicked out, it adds a couple of extra seconds to the whole "for" loop time.

But, somewhere along the line, and this is where the whole rub is, somehow (so it seems to me, anyhow) it runs through a complete iteration of all five temperature probes and sends an email where appropriate, but something is not in a correct state and it's like it runs through again because the temperature on the probe that was in alarm is no longer in alarm.
But, it should only loop through the "alarmCheck" function every six minutes. So, how is it getting "re-triggered" to run again?
In my thought process, each temperature probe is only checked once during each pass through the "for" loop.
I would not have a good way to see if the probe temperature came back down below the setpoint while it was still iterating on that same probe, if that makes sense.
Even so, if it did, it should not be evaluated by the other "else if" and "else" sections in the "alarmCheck" function because it was already evaluated as "True" in the first "if" statement.
At this point, I should probably stop and approach it again tomorrow morning.
Maybe a simple "delay(1000)" statement somewhere in the "alarmCheck" function to "slow things down" might help.
Or maybe I am not understanding how/when the evaluation and exit point(s) of the "If/else if/else" part of the function works.

As a side note, I have never type this much information in my life. I'm not one of those people that is "in love with the sound of their voice" (I know plenty) and have a constant desire to "be heard above all others", I am a bit out of my element here. Sorry for all the rambling. This would be a lot easier to understand in a face-to-face conversation/discussion.

Thanks again!
Scott

Do you get more than one message from one sensor sooner than the next time it should look at all sensors?

The logic should work for the long period, you should get at most N sensors number of email messages, all with the sensor name unique every six minutes.

Is there anything outside the control of this program that would cause a message to be produced?

Reading your code and looking at your prose it should work, but has logic flaws that make it send a message at every opportunity by luck.

I think your splitting the timing instead of having either the caller or the function decide itself about the timing is distracting.

For now or later or never:

The A common pattern would be

unsigned long now;
unsigned long lastTime;

void loop() {
  now = millis();

  if (now - lastTime > (oneMinute * 6)) {
    alarmCheck();
    lastTime = now;
  }
}

Generic now and last time, the function says what's being timed. alarmCheck() needs no alarmCheckDelay.

I look again. The stripped down logic does indeed send hot/normal if the temperature is messing with the threhsold sometimes slightly above and well, you know. Cold same flaw.

a7

I have a question with this kind of String definition

String tempProbeInfo[6][6] {
  {"NotSent", "NotSent", "Probe-1", "80", "65"},
  {"NotSent", "NotSent", "Probe-2", "80", "65"},
  {"NotSent", "NotSent", "Probe-3", "80", "65"},
  {"NotSent", "NotSent", "Probe-4", "80", "65"},
  {"NotSent", "NotSent", "Probe-5", "85", "50"},
  {"NotSent", "NotSent", "Probe-6", "80", "65"}
};

this defines a two-dimesional array both dimensions with 6 elements

[6][6] 

then the following up initialisation has five elements inside one pair of curly braces

     1         2          3        4     5
{"NotSent", "NotSent", "Probe-1", "80", "65"},

and there are 6 pairs of curly braces.
This is of variable-type String (not char) !
How does this array behave if new values were assigned to the elements?
example of the code

tempProbeInfo[alarmCheckCounter][0] = "Sent";

I did never dive deep into using Strings. After reading that Strings can (not must!) eat up all RAM memory I decided to personally use SafeStrings.

Now I have two questions:
why does this work to define six elements

[6][6] 

and then have a initialisation with only 5 Strings

     1         2          3        4     5
{"NotSent", "NotSent", "Probe-1", "80", "65"},

Does it mean the sixth one just stays empty?

What does the code do if new values are assigned?
Does it occupy a new part of RAM each time a new string is assigned?

best regards Stefan

A String object (like all class objects) is has a fixed size. It's 6 bytes on a regular Uno. Looking at the class declaration in Wstring.h, near the bottom you'll see:

protected:
	char *buffer;	        // the actual char array
	unsigned int capacity;  // the array length minus one (for the '\0')
	unsigned int len;       // the String length (not counting the '\0')

The only data members are a pointer, and two unsigned int variables. That's 6 bytes on an AVR. Everything else is declarations of the class member functions.
Another way to tell is:

void setup() {
  Serial.begin(115200);
  delay(2000);
  Serial.println(sizeof(String));
}

void loop() {}

That will print "6"
So, it doesn't matter what character string is held in the memory being pointed to, the String object only occupies 6 bytes. So, when you have an array of String objects, changing the character string has no effect on how the array is laid out.

Yes, its character string is empty. But it still has it's 6 bytes allocated in the array.

1 Like

Thank you very much @gfvalvo for explaining it.

So does this mean that the problem of eating up memory over time caused through a lot of repeated assigning new values to the Strings applies too in this case?

That's the standard Dogma here on the Arduino forum.

Might be. I have read quite a lot of postings about this subject and some of them said

It is not always a problem. So it seems that there are some cases where re-assigning new values to Strings again and again and again does not eat up all memory over time.

As a very specific question:

Can you describe this specific case?
(Easiest answer would be "no i can't")