The String issue....

I am using a code as below to read from a ADC and save the values a SD card file ... it uses the String object.

Needless to mention this is code example from the net and almost all of them use the String variable to keep it simple.

The alternate for this would a char array and formatted using sprintf(). Any other option ?

The question is when there is lot of spare memory and its not a blazing fast application why not just live with the String with the capital S?

void readAnalogValues()
{
  if ( millis() - readAdcMs > readAdcInterval ) {

    readAdcMs = millis();

    adc_value1 = adc->adc0->analogRead(readPin1);
    voltage1 = adc_value1 * resolution;
    delay(2);

    adc_value2 = adc->adc0->analogRead(readPin2);
    voltage2 = adc_value2 * resolution;
    delay(2);

    adc_value3 = adc->adc0->analogRead(readPin3);
    voltage3 = adc_value3 * resolution;
    delay(2);

    adc_value4 = adc->adc0->analogRead(readPin4);
    voltage4 = adc_value4 * resolution;
    delay(2);

    adc_value5 = adc->adc0->analogRead(readPin5);
    voltage5 = adc_value5 * resolution;
    delay(2);

    adc_value6 = adc->adc0->analogRead(readPin6);
    voltage6 = adc_value6 * resolution;
    delay(2);

    adc_value7 = adc->adc0->analogRead(readPin7);
    voltage7 = adc_value7 * resolution;
    delay(2);

    adc_value8 = adc->adc0->analogRead(readPin8);
    voltage8 = adc_value8 * resolution;
    delay(2);

    sdFile.println(String(now()) + "," + String(voltage1, 3) + "," + String(voltage2, 3) + "," + String(voltage3, 3) + "," + String(voltage4, 3) + "," + String(voltage5, 3) + "," + String(voltage6, 3) + "," + String(voltage7, 3) + "," + String(voltage8, 3));
  }
}

Because String allocates memory from the heap when it it needed. In your case, when it it building up the line to log to the SD card.

It grabs some memory for each invocation plus it has to allocate more memory for each concatenation. And then, when the println() call is done, none of it is needed anymore. The problem arises from the fact that there is no garbage collection on these systems so some of that memory may never be reclaimed. Call this function a lot and eventually you consume all of memory.

There is no need to do a single println() call. Multiple print() statements will work just fine.

Note that there isn't any floating point support for sprintf() in avr arduinos so you will need to use dtostr() to convert your floating point numbers to strings before logging them.

blh64:
The problem arises from the fact that there is no garbage collection on these systems so some of that memory may never be reclaimed.

When the String goes out of scope, its destructor will be called, which will free the memory. When the memory is freed, is will be combined with adjacent free blocks. In the code sample provided by the OP, this will leave the heap in EXACTLY the same state it was in before the first Sting was created.

Regards,
Ray L.

blh64:
Note that there isn't any floating point support for sprintf()

...but that's OK, because you can use the print method to convert your floats to ASCII.

If Strings work for you on an Arduino, be happy. Much has been written about the issues so you can inform yourself of the risk and return.

Just be aware that when you post your code here looking for help, that whatever your problem actually is, folks may stop looking for it once they see a String object. Use of goto may have a similar effect.

this will leave the heap in EXACTLY the same state it was in before the first Sting was created.

Totally true ! It does gobble up a bit of extra memory along the way if i'm not mistaken.

There is no need to do a single println() call. Multiple print() statements will work just fine.

So there is no big issue if you would rather use char-arrays

Just be aware that when you post your code here looking for help, that whatever your problem actually is, folks may stop looking for it once they see a String objec

So true, unfortunately.

Use of goto may have a similar effect.

And so it should... 'continue' is also one to be frowned upon.

You could use the traditional Arduino method:

  ssdFile.print(now());
  sdFile.print(',');
  sdFile.print(voltage1, 3);
  sdFile.print(',');
  sdFile.print(voltage2, 3);
  sdFile.print(',');
  sdFile.print(voltage3, 3);
  sdFile.print(',');
  sdFile.print(voltage4, 3);
  sdFile.print(',');
  sdFile.print(voltage5, 3);
  sdFile.print(',');
  sdFile.print(voltage6, 3);
  sdFile.print(',');
  sdFile.print(voltage7, 3);
  sdFile.print(',');
  sdFile.println(voltage8, 3);

If you aren't keeping those values around for some other reason you can just print the values directly:

  sdFile.print(now());
  sdFile.print(',');

  sdFile.print(adc->adc0->analogRead(readPin1) * resolution, 3);
  delay(2);
  sdFile.print(',');

  sdFile.print(adc->adc0->analogRead(readPin2) * resolution, 3);
  delay(2);
  sdFile.print(',');

  sdFile.print(adc->adc0->analogRead(readPin3) * resolution, 3);
  delay(2);
  sdFile.print(',');

  sdFile.print(adc->adc0->analogRead(readPin4) * resolution, 3);
  delay(2);
  sdFile.print(',');

  sdFile.print(adc->adc0->analogRead(readPin5) * resolution, 3);
  delay(2);
  sdFile.print(',');

  sdFile.print(adc->adc0->analogRead(readPin6) * resolution, 3);
  delay(2);
  sdFile.print(',');

  sdFile.print(adc->adc0->analogRead(readPin7) * resolution, 3);
  delay(2);
  sdFile.print(',');

  sdFile.println(adc->adc0->analogRead(readPin8) * resolution, 3);
  delay(2);

And you can use an array of pin numbers to reduce the repeated code:

  sdFile.print(now());
  sdFile.print(',');
  for (i = 0 ; i < 8; i++)
  {
    sdFile.print(adc->adc0->analogRead(readPins[i]) * resolution, 3);
    delay(2);
    if (i < 7)
      sdFile.print(',');
  }
  sdFile.println();

Thanks to all those who responded. It was a big learning for me.

@johnwasser .. while it now looks obvious to use a for loop to cut down repetition, it did not occur to me in the first place. As every other skill coding also is an art that gets perfected with practice ! Thanks for your time to actually send the optimizing code snippets :slight_smile:

I have one more point to clarify... I guess its OK instead of opening another thread ?

It's on the actual writing to the SD card.

I am collecting data and writing to the SD card in specific infrequent intervals. I do understand that this has the inherent risk of the "last chunk data loss" , in case something goes wrong just before the write.

Assuming I am willing to take that risk, is there any upper limit to the size of the data that i can write in one go ?? Or is it only limited by the size of the concerned SD card ??

Thanks

I am collecting data and writing to the SD card in specific infrequent intervals. I do understand that this has the inherent risk of the "last chunk data loss" , in case something goes wrong just before the write.

Your biggest risk is actually not closing the file properly. You should consider opening the file to 'append'

Assuming I am willing to take that risk, is there any upper limit to the size of the data that i can write in one go ?? Or is it only limited by the size of the concerned SD card ??

The space you buffer takes up in memory i suppose.

There definitely used to be a bug in the String class that didn't do reference counts correctly. (IIRC it was something in copy constructors or some such).

This got fixed.

People never noticed it was fixed and kept claiming String was broken! [ People never seem to read
changelogs for new releases, and perhaps understandably need strong evidence that a broken feature
has been fully fixed! ]

People never seem to read
changelogs for new releases, and perhaps understandably need strong evidence that a broken feature
has been fully fixed!

Sometimes i read them, but eh.. any idea if the c-string strtok() has been fixed in the ESP-core ?