Home | pfodApps/pfodDevices | WebStringTemplates | Java/J2EE | Unix | Torches | Superannuation | | About Us
 

Forward Logo (image)      

How to use strings in Arduino for Beginners
Safe, Robust, Debuggable String class for Arduino

by Matthew Ford 25th June 2020 (original 1st June 2020)
© Forward Computing and Control Pty. Ltd. NSW Australia
All rights reserved.

How to do Safe, Robust and Debuggable String Processing in Arduino
using Printable and Writeable non-dynamic SafeStrings.

Update 1st July 2020: SafeString V1.0.3 available added fix for Nano 33 BLE
Update 25
th June 2020: SafeString V1.0.2 available fixed pgmspace.h for Nano 33 IoT
Update 15
th June 2020: SafeString V1.0.1 available
Update 14th June 2020: SafeString now installable via Arduino Library Manager

Introduction

This SafeString library is designed for beginners to be a safe, robust and debuggable replacement for string processing in Arduino. This tutorial shows you how to use SafeStrings for your Arduino string processing. As a concrete example, it includes a simple sketch to check for user's commands, separated by spaces or commas, without blocking the program waiting for input. See the User Commands Example below.

Quick Start
Passing SafeStrings to methods as references and returning SafeString results
A String Tokenising and Number Conversion Example
User Commands Example
Converting between String and SafeString
Differences between Arduino Strings (WString.cpp) and SafeStrings
Controlling SafeString Error messages and Debugging
What is wrong with Arduino String processing
Conclusion

Also see Arduino For Beginners – Next Steps
How to write Timers and Delays in Arduino
Safe Arduino String Processing for Beginners
Simple Arduino Libraries for Beginners
Simple Multi-tasking in Arduino

Why should you use SafeString for your Arduino string processing.

The previous options for string processing in Arduino were C character arrays (using strcat, srtcpy etc), which are a major source of program crashes, and the Arduino standard String class (contained in WString.cpp/.h files) which leads to program failure due to memory fragmentation, excessive memory usage, and is slower and contains bugs. See What is wrong with Arduino String processing below for the details.

SafeStrings are easy to debug. SafeStrings provide detailed error messages, including the name of SafeString in question, to help you get your program running correctly.
SafeStrings are safe and robust. SafeStrings never cause reboots and are always in a valid usable state, even if your code passes null pointers or '\0' arguments or exceeds the available capacity of the SafeString.
SafeString programs run forever. SafeStrings completely avoid memory fragmentation which will eventually cause your program to fail and never makes extra copies when passed as arguments.
SafeStrings are faster. SafeStrings do not create multiple copies or short lived objects nor do they do unnecessary copying of the data.

Both Sparkfun and Adafruit advise against using the Arduino String class.
Sparkfun's comment on the String class is “The String method (capital 'S') in Arduino uses a large amount of memory and tends to cause problems in larger scale projects. ‘S’trings should be strongly avoided in libraries.
Adafruit's comment on the String class is “In most usages, lots of other little String objects are used temporarily as you perform these (String) operations, forcing the new string allocation to a new area of the heap and leaving a big hole where the previous one was (memory fragmentation). “

Unlike Arduino Strings and C character arrays, SafeStrings will NEVER crash your program due to out of memory, null pointers, buffer overrun or invalid objects.

Quick Start

Installation of SafeString library:

SafeString is now available via the Arduino Library Manager, thanks to Va_Tech_EE
Start the Arduino IDE, open the library manager via the menu
Tools->Manage Libraries..
Type
SafeString into the Filter your search search bar and mouse over the SafeString entry and click on Install.

You can also install the library manually if you wish, but the Library Manager is easier.
For manual installation, download the SafeString.zip files to your computer, move it to your desktop or some other folder you can easily find. This zip file is a duplicate of what the library manager provides.
Then open the Arduino IDE and use the menu
SketchImport LibraryAdd Library to install it. Stop and restart the Arduino IDE.

Under File->Examples you should now see SafeString listed (under Examples from Custom Libraries) and under that a large number of examples sketches.
There are numerous examples provided with the SafeString library. Start with the SafeString_ConstructorAndDebugging example.

A First Example Sketch

As a first example try the sketch below, SafeString_Example1.ino,

Some of the important statements in this sketch are:-

 createSafeString(msgStr, 5);
creates an empty SafeString large enough to hold 5 char (plus a terminating '\0'). Here a global SafeString, msgStr, is created, but you can also create temporary local SafeStrings inside your functions. In all cases there is never any heap fragmentation.

 SafeString::setOutput(Serial);
set the output Stream to send the error messages and debug() to

 msgStr = F("A0");
 msgStr += " = ";
 msgStr += analogRead(A0);

builds up the string to be output.

 msgStr.debug(F(" After adding analogRead "));
will print the title followed by the details and contents msgStr (but only if
setOutput( ) has been called).

Running this sketch, SafeString_Example1.ino, produces the following output

 10 9 8 7 6 5 4 3 2 1
Error: msgStr.concat() needs capacity of 8 for the first 3 chars of the input.
        Input arg was '598'
        msgStr cap:5 len:5 'A0 = '
 After adding analogRead  msgStr cap:5 len:5 'A0 = '
A0 = 

The error message,

Error: msgStr.concat() needs capacity of 8 for the first 3 chars of the input.
        Input arg was '598'
        msgStr cap:5 len:5 'A0 = '
 After adding analogRead  msgStr cap:5 len:5 'A0 = '

indicates that the msgStr is not large enough to hold the whole message. It needs to be increased to at least a capacity of 8. Note that the msgStr is still valid. If an operation cannot be performed the SafeString is just left unchanged.

Edit the createSafeString(msgStr, 5); to read increase it size to 10, i.e. SafeString_Example1a.ino
 
createSafeString(msgStr, 10);

Re-running the sketch (SafeString_Example1a.ino) after this change gives the following complete output

 10 9 8 7 6 5 4 3 2 1
 After adding analogRead  msgStr cap:10 len:8 'A0 = 604'
A0 = 604

Finally comment out or remove the setOutput statement, i.e. SafeString_Example1b.ino
 
// SafeString::setOutput(Serial);

Re-running the sketch (SafeString_Example1b.ino) after this change gives the following output

 10 9 8 7 6 5 4 3 2 1
A0 = 487

All the debug() output has been suppressed. Without the setOutput( ) statement the error messages are also suppressed, so you should include a setOutput( ); statement while testing.

How to add text to a SafeString

There are a number of ways to add text to a SafeString. As shown above you can use = and += to add text, numbers and other SafeStrings. You can also pass an initial string value to createSafeString( ) , e.g.
 createSafeString(msgStr, 10, "A0");
If the SafeString is not large enough to hold the initial value, the result will just be an empty SafeString. If SafeString::setOutput(Serial); has been called then an error message will also be output.

You can also use the concat() methods to do the same. e.g. SafeString_Example1c.ino
The
clear() method clears out any existing text in the SafeString. The concat() and clear() methods return a reference to the SafeString so you can chain calls as shown in SafeString_Example1c.ino

 Serial.println(msgStr.concat(" = ").concat(analogRead(A0)));

For more format control you can use the familiar print() methods to print to a SafeString. e.g. SafeString_Example1d.ino To add the analog reading in HEX form you use
 
msgStr.print(analogRead(A0),HEX);
All the print/write methods are available for adding to SafeStrings.

The output from SafeString_Example1d.ino is

SafeString also has prefix() methods and the -= operator to add text to the front of the SafeString, e.g. SafeString_Example1e.ino

 msgStr.clear(); // remove the initial value
 msgStr.print(analogRead(A0),HEX);
 msgStr -= " = 0x";
 msgStr.prefix(F("A0"));
Serial.println(msgStr);

Other SafeString methods()

SafeString has a range of other methods to work on text :- compareTo, equals, == , != , < , <= , > , >= , equalsIgnoreCase, startsWith, endsWith, endsWithCharFrom, startsWithIgnoreCase, setCharAt, charAt, [ ] (access only), indexOf, lastIndexOf, indexOfCharFrom, substring, remove, removeLast, keepLast, replace, toLowerCase, toUpperCase, trim, toLong, toFloat, toDouble, stoken, read, readUntil.

There are two 'unsafe' methods in this library:- readBuffer and writeBuffer. These two methods read and write characters to a C character array (char*) and can cause the program to crash if size of the array or is its index arguments are not correct. A null pointer for the char* argument will not crash the program.

Passing SafeStrings to methods as references and returning SafeString results

The Arduino compiler will issue an error or warning if you try and write an incorrect method using SafeString arguments or returns

Enabling Arduino Complier Warnings

To see these error and warning easily, make the following settings in the Arduino preference dialog.

Open Arduino File->Preferences and untick Show verbose output during compilation and just below that turn on Compiler Warnings: That is set Compiler Warning to something other than None, for example Default. These settings will make it easy to see the warnings/errors referred to below.

How to pass a SafeString as an argument to a method

When you define a method that has a SafeString argument you declare it as a reference, SafeString &str, e.g.
 void test(SafeString& strIn) {
 . . .
 }

If you forget to use the & when defining the function, you will get an error message like the the following

 . . . /SafeString.h: In function 'void setup()':
 . . . /SafeString.h:109:5: error: 'SafeString::SafeString(const SafeString&)' is private
     SafeString(const SafeString& other ); // You must declare SafeStrings function arguments as a reference, SafeString&,  e.g. void test(SafeString& strIn)
     ^
TestCode:30:11: error: within this context
   test(str);
           ^

The first line of the errors tells you where the function with the error was called from, e.g. test( ) was called in setup()
The third line tells you what the error was
   // You must declare SafeStrings function arguments as a reference, SafeString&, e.g. void test(SafeString& strIn)
and a little further down you will see which function call caused the error

  TestCode:30:11: error: within this context
   test(str);

That tells you that it is the test( ) method that is missing the SafeString & in its definition. Go the where you wrote the test( ) method and add a & to the end of SafeString. That is change test(SafeString .. ) to test(SafeString& … )

Declaring the method SafeString& argument as a reference means that any changes you make to it in the method will immediately update the SafeString that you passed to the method when you called it. There is only ever one copy of each SafeString and it is that one copy which gets all the changes applied to it. You could also declare the argument as a SafeString pointer, e.g. SafeString *str, but it is easier to code the method using a SafeString & reference.

How to pass a SafeString result back from a method

Unlike the String class, SafeString does not support returning a SafeString from a method. To return results in a SafeString, you must update one of the SafeString & arguments you passed to the method. For example write a method that takes the number of loops and returns a SafeString for printing you would use SafeStringMethods.ino

void loopCountStr(int num, SafeString& str) {
  str = "loop count = ";
  str += num; // update it
  // str returned via SafeString& arg
}

The caller of this method has to provide the SafeString, as an argument, for the result to be returned in. The SafeString provided as an argument can be a global SafeString OR a local SafeString created in the code calling the loopCountStr method.

Arduino Strings let you return a String or String & as the return type from the method. It does this by coping the result and passing that copy back. SafeString only ever has one instance of each SafeString and never creates another copy. If you try to pass a SafeString back as the method return you will get either a complier error or warning as shown below.

Here are two examples that do not work and the errors and warning they produce.
SafeStringMethod_Error_1.ino uses
SafeString loopCountStr(int num) and the compiler gives the error message
 
SafeString(const SafeString& other ); // You must declare SafeStrings function arguments as a reference, SafeString&, e.g. void test(SafeString& strIn)
That is to return a SafeString you need to pass it into the method as a reference and then update it as in SafeStringMethods.ino

SafeStringMethod_Error_2.ino uses SafeString& loopCountStr(int num) and the compiler give the warning message
 
warning: reference to local variable 'str' returned
That is the method loopCountStr is trying to return a reference (a pointer) to the local SafeString that was created on the local method stack. The method stack is completely discarded when the method returns so the data referenced is no longer valid.
You MUST heed this warning and change the code to remove it, because sometimes a sketch with this warning will appear to work, but it will ultimately fail and cause a reboot.

A String Tokenising and Number Conversion Example

The SafeString_stoken_Example.ino sketch illustrates extracting numbers from a CSV (comma separated values) input and converting the valid fields to numbers. Also see the SafeString_stoken example included with the SafeString library
Commenting out the
 
// SafeString::setOutput(Serial);
to remove all the SafeString::Output.print( ) and debug( ) output gives the following

 10 9 8 7 6 5 4 3 2 1
 The input is 23.5, 44a ,, , -5. , +.5, 7a, 33,fred5, 6.5.3, a.5,b.3

Fields with numbers are:-
Field 1 : 23.50
Field 5 : -5.00
Field 6 : 0.50
Field 8 : 33.00
 

Note that after finding and processing one field the rest of the loop() is run, so the rest of you program remains 'alive'. If you where using the Arduino String class there would be two problems, i) there is no tokenize method and ii) fields like 44a would be interpreted as valid numeric fields.

User Commands Example

You often need a way to enter commands to control a sketch, while it is running. SafeString_ReadCmdsTimed.ino provides that functionality. The standard Arduino Stream methods, readString(), readStringUntil(), etc, do not support this because they pause the whole program while waiting for the user input. They are completely unsuitable when the rest of the sketch is controlling something in 'real time', e.g. a stepper motor.

The basic sketch outline is:-

void loop() {
  input.read(Serial);

  if (input.nextToken(token, delimiters)) { // process at most one token per loop    
    if (token == commandStr) {
      // command action here
    } else if (token == anotherCmd) {
        . . .   test for other cmds
       . . .
    }// else  // not a valid cmd ignore
  }
  // rest of your sketch code here is executed without delay every loop()
}

The process is, read some input, check if there is a complete token (terminated by a delimiter), if there is check it against known commands then execute rest of the loop code, and repeat. There are no delays waiting for user input in this process, so the loop() runs at maximum speed.

The SafeString_ReadCmds.ino example sketch implements this process. Although this sketch will accept an unlimited number of characters and commands in one line, the size of the input and tokens need only be 1 more then the longest command (to allow for the delimiter), so the sketch uses very little RAM memory.

SafeString_ReadCmds.ino works well as long as the Arduino monitor is set to send Newline or Carrage Return or Both NL & CR line endings. However if the Arduino monitor is set to No line ending, or the input is being sent character by character from a terminal program, then the last command seems to be ignored. This because the nextToken( ) is waiting for a delimiter to terminate the command. nextToken() can not tell if the characters stop are a valid command or just part of the input text stopping. It needs to the next character OR it needs to decide no more characters are coming.

To decide no more characters are coming a timer is used. A 0.1sec timer is started whenever some input is received. If the timer times out, then there has been not any input for 0.1secs and the sketch assumes the input has ended and adds a dummy delimiter so that last characters received are terminated and a token is returned for processing. SafeString_ReadCmdsTimed.ino includes that timer.

The revised, timed, sketch outline is:-

void loop() {
  if (input.read(Serial)) {
    timeout.start(100); // start/restart the 0.1sec timer every time something is read
  }

  if (input.nextToken(token, delimiters)) { // process at most one token per loop    
    if (token == commandStr) {
      // command action here
    } else if (token == anotherCmd) {
        . . .   test for other cmds
       . . .
    }// else  // not a valid cmd ignore
  }
  if (timeout.justFinished()) {
    input += delimiters[0]; // nothing received for 0.1secs, terminated last chars so token will be processed.
  }
  // rest of your sketch code here is executed without delay every loop()
}

Looking at SafeString_ReadCmdsTimed.ino in more detail, the top of the sketch initializes the start, stop and reset commands as SafeStrings and defines two SafeStrings to handle the input and the tokens.

The loop() code is

The loopCounter counts very quickly because the loop() always runs as fast at it can. So the loopCounter value is only printed every few secs. (i.e. every multiple of 10,000)

Here is some sample output for the command line
stop,reset start

Using SafeString_ReadCmdsTimed.ino you can now enter commands in the Arduino Monitor with the No line ending setting and they will be executed. If you are typing commands, a character at a time, from a terminal connection, then you will want to increase the TIMEOUT_MS setting to say 500 to 1000 (0.5 to 1.0 secs) depending on how fast you type. Otherwise if you are a bit slow typing in the whole command, the sketch will insert a terminator in the middle of your command and it will not be recognized.

Converting between String and SafeString

To convert a String to a SafeString you can use safely use the readBuffer() method. readBuffer() appends to the SafeString as much of the Arduino String as it can.

  createSafeString(str,8);
  String arduinoStr;
  arduinoStr = "this is a string";
  size_t charsRead = str.readBuffer(arduinoStr.c_str());
  if (charsRead < arduinoStr.length()) {
    Serial.println(" Only part of arduinoStr copied.");
  }
  str.debug();


This gives the output

 Only part of arduinoStr copied.
SafeString str cap:8 len:8 'this is '


To convert from a SafeString back to a String just use

  arduinoStr = str.c_str();

Differences between Arduino Strings (WString.cpp) and SafeStrings

The major difference is that the capacity of a SafeString is fixed at creation and does not change, where as Arduino Strings can resize dynamically using more memory then you expected and fragmenting the heap-space. SafeStrings are also more robust and provide detailed error messages when an operation cannot be preformed.

SafeStrings are Always Valid

SafeString is always left unchanged and in a valid state even if the operation cannot be preformed. On the other hand, Arduino Strings loose all the existing data if the dynamic resize fails and some operations result in invalid String states. e.g.
 String str = "abc";
 str += '\0';
or
 str[2] = '\0';
result in an invalid Arduino String.

In SafeString, s_str += '\0'; and s_str.concat('\0'); errors are caught and ignored and generate an error message. While str[ .. ] = is not supported because the error cannot be caught. In SafeStrings use setCharAt( ) instead.
 str.setCharAt(2,'a');

SafeStrings provide consistent operator syntax

SafeStrings do not support the + operator, because it creates temporary Strings objects and is problematic in its application. For example
 String testStr1;
 testStr1 = "a" + 10; // this compiles and runs
 testStr1 = "a" + 10 + "b"; // this does NOT compile

With SafeStrings use the += operator or the concat() method, i.e.
 createSafeString(testStr, 20);
 testStr = "a";
 testStr += 10;
 testStr += "b";

SafeStrings have Non-blocking Read methods

SafeStrings has two non-blocking read methods, read() and readUntil(), which allow you to look for user input without halting the rest of your program. See the User Commands Example above and the SafeString_readUntil example sketch provided with the library.

SafeStrings have stoken( ) and nextToken() methods

SafeString has two tokenising methods. stoken() is the low level method which can be used to split a string into tokens separated by specified delimiters or extract tokens that only contain characters from a specified valid set of characters. nextToken() is a high level method that will remove a delimited token from the SafeString and and return it for processing. See the User Commands Example above and the SafeString_stoken example sketch provided with the library.

SafeStrings implement the Print interface

As shown in the previous examples, SafeStrings implement the Print interface so you can print( ) to a SafeString to add text to it and use all the familiar formatting features available with print( ).

Differences in string to number conversions

There are noticeable differences between SafeStrings and Arduino Strings for string to number conversions. In Arduino Strings, if the conversion fails the method returns 0 or 0.0 , so you cannot tell if the conversion failed or the number was actually 0 Also the Arduino String number conversions do not look at the whole string but just stop and return a result as soon as the next character is invalid. For example for Arduino String toInt() on “5a” returns 5 as the answer, and for toFloat(), “5.3.5” returns 5.3.

The SafeStrings conversion methods, on the other hand, return true or false to indicate if a valid number was found and if one is found then the argument variable is updated. For example (also see the SafeStringToNum example sketch provided with the library.)

 createSafeString(str, 7, " 5.3.6 ");
 
float f = 0.0;
 bool rtn = str.toFloat(f);
will return false and leave f unchanged

 createSafeString(str, 7, " 5 ");
 long ans = 0;
 bool rtn = str.toLong(ans);
will return true and set ans to 5.
SafeString does not have a
toInt() method. SafeString uses the more accurately named method, toLong() Arduino String's toInt() method actually converts the string to a long

Differences in returns type/values

SafeStrings returns bool (true or false) for logical returns, where as Arduino's Strings return unsigned char . In almost all cases there is no functional difference.

SafeStrings returns size_t from indexOf type methods and you need to test for < str.length() to determine if the search succeeded. size_t is the type C/C++ uses for array indices and so it is the 'correct' type to return for an index. size_t is an unsigned type an so is always >= 0. Arduino Strings returns an int for index searches and uses -1 to indicate the search failed. When you move from using Arduino Strings to SafeStrings you will need to change your tests for the return of methods that return a index to test of < str.length() to see if the search succeeded. See the SafeStringIndexOf example sketch provided with the library for code examples.

Controlling SafeString Error messages and Debugging

SafeString has extensive error checking build in. To enable the error messages, you need to tell SafeString where to send them to using the setOutput( ) method
 SafeString::setOutput(Serial);

SafeString Error messages

Then if your code tries to exceed the capacity of a SafeString, you will get an error message sent to Serial e.g.
 stringOne = "abc";
 stringOne += "123456789";

Error: stringOne.concat() needs capacity of 12 for the first 9 chars of the input.
      Input arg was '123456789'
      stringOne cap:8 len:3 'abc'


The error message tells you which operation failed, why it failed and a summary of the current state of the SafeString object and it current contents. This is enough for you to find and fix the problem. In this case by increasing the size of stringOne to 12 in the createSafeString( ) macro.

You can use the setVerbose( ) method to turn off/on the printing of the current string contents in error messages at various points in your sketch.
 SafeString::setVerbose(false);
 stringOne = "abc";
 stringOne += "123456789";

now outputs a more compact error message.

Error: stringOne.concat() needs capacity of 12 for the first 9 chars of the input. --- stringOne cap:8 len:3

SafeString Debug statements

You often want to debug your program by outputting the value of a SafeString at various points in the program. The debug() method does that for you. Once you have told SafeString where to send error/debugging messages, you can use the debug( ) method to output the current state of a SafeString. e.g.
 stringOne.debug();

SafeString stringOne cap:8 len:3 'abc'

To get a compact output without the string's contents use
 stringOne.debug(false);

SafeString stringOne cap:8 len:3

You can also add a title, either a " ... " or an F( ) string or another SafeString, to the debug output e.g.
 stringOne.debug(F(" After stringOne = \"abc\";") );

 After stringOne = "abc"; stringOne cap:8 len:3 'abc'

Turning off SafeString Error messages and debug output

You can use the statement
 SafeString::turnOutputOff();
to turn off all error messages and debug() output from then on. Use
 SafeString::setOutput(Serial);
to turn it back on again.

Using SafeString::Output for debugging messages

You can print debugging messages using SafeString's Output. e.g.
 SafeString::Output.println(" string debug msg.. ");

If SafeString::setOutput( ) has been called then SafeString::Output.print( will print to that Stream. Otherwise the output is discarded. This lets you add string debugging messages that will disappear when you turn off the other SafeString error messages and debugging output.

Removing All Error Messages Code

The SafeString Error messages add a noticeable amount of code to your program. If you are running out of program space, you can quickly remove all the error messages code by commenting out
// #define SSTRING_DEBUG
at the top of the SafeString.h file and recompiling your sketch. This will remove all the error message handling but will keep all the error checks so that your code is still safe and robust.

The debug() method and SafeString::Output.print( methods are still available with //#define SSTRING_DEBUG commented out, but the name of the variable is not longer included in the debug() output. i.e.
 stringOne.debug(F(" After stringOne = \"abc\";") );

 After stringOne = "abc"; cap:8 len:3 'abc'

Debug and Error Message Print Delays

Sending output to Serial can delay the rest of you program from running. At 9600baud it takes about 1mS per char to send the output. Once the outgoing Serial buffer fills up, your program stops and waits for some chars to be sent to free up space in the buffer so it can send the rest of the output. See Simple Multitasking Arduino for the details and for how to add larger output buffer. You can reduce the amount of SafeString output by using setVerbose(false); This gives compact error messages which omit the printing the contents of the SafeString. debug(..,false); does the same for debug() statements

What is wrong with Arduino String processing.

Neither C character arrays nor Arduino Strings are recommended for string processing. C character arrays are a major source of programming bugs in C/C++ programs. With C character arrays there are no checks against to over filling them, thus writing over the memory being used by another variable. While both Both Sparkfun and Adafruit advise against using the Arduino String class.
Sparkfun's comment on the String class is “The String method (capital 'S') in Arduino uses a large amount of memory and tends to cause problems in larger scale projects. ‘S’trings should be strongly avoided in libraries.
Adafruit's comment on the String class is “In most usages, lots of other little String objects are used temporarily as you perform these (String) operations, forcing the new string allocation to a new area of the heap and leaving a big hole where the previous one was (memory fragmentation). “

Adafruit supplies a very neat image of this problem.

Adafruit suggests using String.reserve(), but this does prevent heap fragmentation, only reduces it, and does nothing about the excess memory usage.

As well as heap fragmentation, the lots of little String objects created mean lots of extra time spent creating creating those objects and then copying the values to the next little object. For example string.replace(“abcd”,”efgh”); creates two new String objects, one each for “abcd” and “efgh”, before passing the String objects to the replace() method. Creating these extra String objects makes the program run slower due to the String object creation and the copying of the char arrays, “abcd” and “efgh”, into the new String objects. SafeStrings avoid all these problems

Adafruit also has this advice
Local Variables – Every function call creates a stack frame that makes the stack grow toward the heap. Each stack frame will contain:

This data is usable within the function, but the space is 100% reclaimed when the function exits!
Adafruit advises

Some of the Errors in Arduino's WString code

The current WString code supplied with Arduino contains a number of bugs which cause programs using it to either fail or give un-expected results. For example

  String a;
  a = "12345";
  Serial.println(a);
  a += a;
  Serial.println(a);

causes an Arduino UNO to continually reboot.
Another example is

  String aStr;
  aStr = "";
  aStr.concat('\0');
  Serial.print("aStr .length():");Serial.println(aStr .length());
  Serial.print("strlen(aStr.c_str()):");Serial.println(strlen(aStr .c_str()));

which outputs

aStr.length():1
strlen(aStr.c_str()):0

That is the String object length() is no longer the same as the strlen() of the underlying char buffer. The Arduino String class has a number of these types of errors.

In contrast using SafeStrings gives outputs of
For the first example

12345
1234512345

And for the second example gives the error message

Error: aStr.concat() of '\0'

Using SafeStrings, the first example works as expected and the second example gives a detailed error message contain the name of the SafeString, aStr , involved, and leaves the original value of aStr unchanged.
Most, but not all of, WString's bugs could be fixed but that would not fix the basic problems with Arduino Strings dynamic memory allocation and lots of object creations and data copying.

Conclusion

String processing using C character arrays (using strcat, srtcpy etc) is a major source of program crashes. While the Arduino standard String class (contained in WString.cpp/.h files) leads to program failure due to memory fragmentation and is slower and contains bugs. The SafeString library presented here solves these problems by providing a safe, robust and debuggable means for doing string processing in Arduino.

SafeStrings are easy to debug. SafeStrings provide detailed error messages, including the name of SafeString in question, to help you get your program running correctly.
SafeStrings are safe and robust.
SafeStrings never cause reboots and are always in a valid usable state, even if your code passes null pointers or '\0' arguments or exceeds the available capacity of the SafeString.
SafeString programs run forever.
SafeStrings completely avoid memory fragmentation which will eventually cause your program to fail and never makes extra copies when passed as arguments.
SafeStrings are faster.
SafeStrings do not create multiple copies or short lived objects nor do they do unnecessary copying of the data.

The SafeString library provide numerous examples and as well as simple practical sketch which process user commands while keeping running the sketch at maximum speed, SafeString_ReadCmdsTimed.ino




The General Purpose Android/Arduino Control App.
pfodDevice™ and pfodApp™ are trade marks of Forward Computing and Control Pty. Ltd.


Forward home page link (image)

Contact Forward Computing and Control by
©Copyright 1996-2020 Forward Computing and Control Pty. Ltd. ACN 003 669 994