Become a subscriber (Free)

Join 29,000 other subscribers to receive subscriber sale discounts and other free resources.
:
:
Don't worry -- youre-mail address is totally secure. I promise to use it only to send you MicroZine.

Arduino String

You use the Arduino String Class to manage lines of text stored in SRAM and you can control strings in different ways:

  • Initialize them - easily setup text messages.
  • Concatenate them (add characters or append another string).
  • Search through them (identify what is within a string).
  • and more.

They provide the human interface to and from the outside world. So for instance you will use Strings to store, and later send messages to an LCD or to receive and send data through serial link etc.

Note: In this page the word 'String' (in this page) with capital 1st letter refers to the String class (C++) whereas non-capitalized 'string' refers to C style strings. You can use either in the Arduino Compiler (avr compiler).

Learn Arduino String by Example

To learn how to use Arduino Strings lets solve a simple problem, receiving and transmitting data through the serial port.

One problem with compiling C/C++ code is that to change a constant in a program requires re-compilation and this takes time. Using the serial port allows you to update internal variables in real time without recompiling the code.

The code below will accept data from the serial port in the form of text commands that control simple on/off variables. A further section below shows you how to get integer text values into an integer variable.

It s quite an interesting problem as data from the RS232 interface is "serial" so you have to build up a commands from the serial port byte by byte. You also need to identify when a command is complete and then take action on the command.

The first thing to do, as you do for all serial communications, is initialize the Serial class in the setup() function with the baud rate (a startup message is also useful to show the Code has begun):

void setup (void) {
   Serial.begin(9600);
Serial.println("Command Interpreter");
 }

For this tutorial the operational code will be in the loop function but as you program grows, you will want to create a separate function to contain the serial decode operation. For now lets leave it in loop().

First off you need to declare a variable of type String that you will use to hold the data from the serial port; Here this will be sdata. So sdata is a String class object of type String.

String sdata="";

As each byte of data comes in you also need a temporary storage area to process it:

byte ch = ' ';
The function needed to decide if a byte has been received is
Serial.available()

Which returns true if anything has been received at the serial port.

Next you need to read the data from the serial port into a variable using the String class member function read():

Serial.read();

This returns a byte of data.

Arduino String Serial Command Decode Structure

With these four bits of code you can make up the serial receiver as follows:

String sdata="";  // Initialised to nothing.

void setup (void) {
   Serial.begin(9600);
Serial.println("Command Interpreter");
 }
void loop(void ) { byte ch; if (Serial.available()) { ch = Serial.read(); sdata += (char)ch; if (ch=='\r') { // Command recevied and ready. sdata.trim(); // Process command in sdata.

sdata = ""; // Clear the string ready for the next command.
 
} } }

The code in setup() initializes the serial port to 9600 Baud and prints out the first string message.

In loop() the serial port is continuously monitored and if a byte is received it is placed into variable ch and appended to the string sdata. If the byte was a Carriage Return (CR - when you hit the return key), then the trim function is used to remove all white space at the end of sdata. sdata is now ready to be decoded.

Notice how you can concatenate strings, that is add one to the end of another by using the overloaded '+' operator, here using the shortened form '+='.

Just before leaving the if condition, sdata is set to nothing ready for the next command decode.

Arduino String Serial Command Control

Now lets look at the section that you will write after the comment:

// Process command in sdata.
This is where you can decode the serial data which ended in \r (now removed by trim().

The easiest way to do it is to examine the 1st character and use that in a switch statement. To access a character at a specific position se the charAt member function of String.

sdata.charAt(0)

Here's the switch statement that decodes each command

         // Process command in sdata.
         switch( sdata.charAt(0) ) {
         case 's':
            start = 1;
            Serial.println("Start Process");
            break;
         case 't':
            test = 1;
            Serial.println("Test");
            break;
         default: Serial.println(sdata);
         } // switch

Notice how a Serial.println is used to send information back to the user. This is not really necessary but allows you to see that a command has been actioned - making the program easier to use. The variables start and test are defined earlier in the program.

The above code gives you simple control of variables within a microcontroller.

Arduino String Serial Command Value Entry

To enter a value into a variable, under serial control, you can use the following as part of the switch statement.

         case 'v':
            if (sdata.length()>1){
               valStr = sdata.substring(1);
               val = valStr.toInt();
            }
            Serial.print("Val ");
            Serial.println(val);
            break;

This simply uses the functions substring() and toInt() to retrieve a number string from the serial port and convert it to internal number format.

If the serial data input begins with a v then this section of the switch statement is activated. if there is more data after the 'v' i.e. you typed a number, then valStr is set to a new string that starts from index 1 of sdata to the end i.e. avoiding the 'v' character.

valStr is then decoded from string to int using the toInt() function and the value placed in variable 'val'. 'val' is then printed back to the serial port to show what happened.

If you only typed 'v' then only the last two print statements are executed since the string's length is now one. In this way you can either change or just query the value of 'val'.

Arduino String Sketch Examples

Full Command Decoder using Arduino String

The following Arduino String Sketch example pulls all of the above code snippets together showing you how to decode serial data for entering values into variables in any Arduino Sketch.

String sdata="";  // Initialised to nothing.
byte test,start;

void setup (void) {
   Serial.begin(9600);
   Serial.println("Command Interpreter");
}

void loop(void ) {
byte ch;
String valStr;
int val;

   if (Serial.available()) {
      ch = Serial.read();

      sdata += (char)ch;

      if (ch=='\r') {  // Command received and ready.
         sdata.trim();

         // Process command in sdata.
         switch( sdata.charAt(0) ) {
         case 's':
            start = 1;
            Serial.println("Start Process");
            break;
         case 't':
            test = 1;
            Serial.println("Test");
            break;
         case 'v':
            if (sdata.length()>1){
               valStr = sdata.substring(1);
               val = valStr.toInt();
            }
            Serial.print("Val ");
            Serial.println(val);
            break;
         default: Serial.println(sdata);
         } // switch

         sdata = ""; // Clear the string ready for the next command.
      } // if \r
   }  // available
}

[file:string.ino]

Statistics for Above String based command decoder (IDE ver 1.8.8):

Sketch uses 3912 bytes (12%) of program storage space. Maximum is 30720 bytes.
Global variables use 248 bytes (12%) of dynamic memory, leaving 1800 bytes for local variables. Maximum is 2048 bytes.

Serial Monitor Results for Command Decoder

Here's what happens when you type 's', 't', 'v198' - after each command hit the return Key (Enter key).

Command Interpreter
Start Process
Test
Val 198

This shows that each section of the switch statement was activated and that the variable val has the decimal value 198. This is true since the code...

Serial.println(val);

...converts the internal binary representation of 'val' into text readable form and sends the data out to the serial port.

Full Command Decoder using c string

Now lets look at the c string version of the same operation (you'll see why in a while!)

byte test,start;

#define BUF_LEN 20

void setup (void) {
   Serial.begin(9600);
   Serial.println("Command Interpreter");
}

void loop(void ) {
static char sdata[BUF_LEN], *pSdata=sdata;
byte ch;
int val;

   if (Serial.available()) {
      ch = Serial.read();

      // -1 for null terminator space
      if ((pSdata - sdata)>=BUF_LEN-1) {
pSdata--;
Serial.print("BUFFER OVERRUN\n");
}
*pSdata++ = (char)ch; if (ch=='\r') { // Command received and ready. pSdata--; // Don't add \r to string. *pSdata = '\0'; // Null terminate the string. // Process command in sdata. switch( sdata[0] ) { case 's': start = 1; Serial.println("Start Process"); break; case 't': test = 1; Serial.println("Test"); break; case 'v': if (strlen(sdata)>1) val = atoi(&sdata[1]); Serial.print("Val "); Serial.println(val); break; default: Serial.println(sdata); } // switch pSdata = sdata; // Reset pointer to start of string. } // if \r } // available }

Statistics for above Arduino string (c string based) command decoder (IDE ver 1.8.8):

Sketch uses 2076 bytes (6%) of program storage space. Maximum is 30720 bytes.
Global variables use 270 bytes (13%) of dynamic memory, leaving 1778 bytes for local variables. Maximum is 2048 bytes.

Note: The output and ultimate operation of the above program is exactly the same as for the 'String-class' based one.

Arduino String References

Arduino String Objects

For C object Strings a good place to look is the Arduino String Reference. This shows you the available member functions (scroll down for links).

Arduino string (c style strings)

For C object Strings an ok place to look is the Arduino c string Reference.

However the above link does not detail the functions available for c-style strings. For that look to a standard reference. This link is good as it orders the functions in the order of most used.

P.S. Have a look at strtok() as this can allow you to process a command line with multiple commands separated with a delimiter e.g. a semi-colon - Useful for a more complex serial command decoder.

Comparison of String and c string

The following table shows the difference in memory usage:

Type
Flash
SRAM
String
3912 248
c string
2076
270
Difference String cf c string
+1836
-22

Note: It appears that the String class uses less SRAM but it, in fact uses the heap and uses more than c string uses (it is just not easy to measure because it can change all the time as strings are created and destroyed).

Using Class String to control strings is undoubtedly the easiest way and is very similar to higher level languages such as python but it comes at a cost - that cost is Flash program memory.

Flash memory

Although this c-string program performs exactly the same operation as the String based one, the String based one adds a further 1836 bytes of Flash. This is the String class performing magic in the background.

This amount of memory may not matter to a device with large Flash memory but it is fairly significant when using an Arduino Uno (32k Flash).

Arduino String SRAM Use

You can see that the SRAM use for c strings is higher than for String - this is because the buffer is declared as a static array of bytes before the program runs:

static char sdata[BUF_LEN],

This means the SRAM for this string is reserved before run time. When using the heap, (in the String Class) memory is allocated at run time. That really means the compiler does not known how much memory is (or will be) used by the program when using String objects (and it will be larger as there is an overhead in managing blocks of SRAM using malloc and free which are the memory allocation and deallocation functions used in C and C++).

SRAM Memory Size

The other thing to notice is:

  • In the String program there is no memory declaration.

There is no specification of the size of memory used to store the string - in fact the strings are stored in SRAM but they use the heap. The heap is unused memory that sits between the stack and the variable space.

Should You Use String?

If you search on the Web, you will find that there can be problems in using heap based systems and that is due to memory fragmentation. Fragmentation in the heap is caused when strings of larger length can not be allocated to a 'freed' memory block (since the block that had been previously 'freed' up is too small); they have to be allocated in new memory. This leaves lots of small blocks of unused memory.

In the extreme case memory fragmentation, caused when you have low SRAM! and lots of string manipulations, can cause your program to hang - the solution is to press the reset button! For small programs with low numbers of Strings, its fine (there won't be #enough memory fragmentation to cause the program to hang.

The following statement is from a consortium of car manufacturers MISRA (Motor Industry Software Reliability Association). They specifically forbid using heap based memory management because safety is crucial:

    MISRA C++ rule 18-4-1, dynamic heap memory allocation cannot be used.

This document describes static, dynamic memory fragmentation etc. but its final conclusion is this:

Exhaustion is still our major impediment to using dynamic memory in real-time embedded systems. A good failure policy based around std::bad_alloc can address many of the issues, but in high integrity systems dynamic memory usage will remain unacceptable.

For programs that do not use many strings the Arduino string class is fine. However if you have low levels of SRAM and use lots of string manipulations you could run into fragmentation problems (random failure).

What exactly is a String?

As noted at the start, a string is a line of text stored in SRAM.

Note: This section applies to both Strings and c-strings.

Both Strings and 'strings' are dynamic entities meaning they can be changed whenever you want since they exist in SRAM. For an embedded system that typically has very low SRAM available (Arduino Uno 2k Byte) it means the SRAM will disappear fast unless you put all your constant strings into Flash.

In fact strings are doubly wasteful in C or C++, because they use both Flash and SRAM.

The first reason is initialisation. You probably want to start off a string with some information in it e.g. "Initialising I2C" etc.; Sometimes you don't e.g. a receive buffer for a serial input. To do the initialisation you need somewhere to store the string when the power is off, and that store is Flash memory.

The second reason is that strings are defined as existing in RAM so that they can be changed as your program runs.

In some cases you don't want an updatable string e.g for a text message that never changes. The problem is that the compiler won't know that the string is never going to change.

Putting Strings into Flash memory

To place strings into Flash, in Arduino code, enclose a fixed string with the F macro e.g. Serial.print(F("My fixed string")); That leaves the string in Flash memory.

Using the F() macro stops the transfer of initialization data from flash memory to SRAM and only uses the data from the Flash memory, so you save SRAM.

TIP: There are other functions specially made for using Flash memory e.g. string copy from Flash to 'a normal string in SRAM'. You can find these by searching for "Arduino PROGMEM strcpy_P". These are specialized functions that you won't need often but it is good to know they are available.


Arduino Strings and c strings compared

Advantages of String class

  • Does not allow buffer overrun.
  • Easy to use and intuitive e.g. to append strings use the '+ operator

Disadvantages of the String class

  • Uses more Flash memory.
  • Uses the heap leading to heap fragmentation.
  • Uses the heap that uses an undefined amount of SRAM at run time.

Advantages of c strings

  • Uses minimum Flash memory.
  • Does not use the heap.
  • You have complete control of how memory is used.

Disadvantages of c strings

  • Not intuitive.
  • Can get buffer overrun if not careful.





New! Comments

Have your say about what you just read! Leave me a comment in the box below.




Privacy Policy | Contact | About Me

Site Map | Terms of Use



Visit our Facebook Page:
To Visit Click Here


Recent Articles

  1. Arduino EEPROM - how it works and how to use it - with examples.

    Arduino EEPROM: How to use it and How to presrve the life of EEPROM. Two examples sketches to save multiple values to EEPROM.

    Read more

  2. How to use the ADS1115

    A tutorial on using the ADS1115 precision 16 bit ADC for low power use.

    Read more

  3. The TP4056: Lithium Ion/polymer Battery Charger IC

    Learn how to use the TP4056 properly. There's a right, and a wrong way, to use it to safely charge Lithium Ion batteries.

    Read more

  4. DW01A Battery Protector IC

    The DW01A chip is a Lithium Ion battery protector commonly used on TP4056 boards.  Find out Exactly how it works and how to use it the correct way.

    Read more

  5. Arduino String: How to read commands from the serial port.

    For Arduino string operations you can use Object Class Strings or C style strings but which should you use? Also find out how to decode commands and control variables in your programs using strings.

    Read more

  6. A Real Time Clock design (DS1307) with a PIC microcontroller

    Real Time Clock Design (FREE): A Free and Complete RTC design using the DS1307 and a PIC micro (16F88) also re-targetable. This PIC project uses an I2C Clock chip and 7-segment display to create a fou…

    Read more

Readers Comments

"I wanted to thank
you so so so much
for all the information
you have provided in
your site it's

SUPERB and FANTASTIC."

- Ranish Pottath

"This site really is
the best and my favorite.
I find here many useful
projects and tips."

- Milan

bursach<at>gmail.com<

"Awesome site,
very, very easy and nice
to navigate!"


- Matt
matt_tr<at>
wolf359.cjb.net


Learn Microcontrollers

"Interested in
Microcontrollers?"

Sign up for The
Free 7 day guide:

FREE GUIDE : CLICK HERE


"I am a newbie to PIC
and I wanted to say
 how great your
site has been for me."


- Dave

de_scott<at>bellsouth.net

"Your site is a great
and perfect work.
congratulations."


- Suresh

integratredinfosys<at>
yahoo.com

"I couldn't find the correct
words to define
yourweb site.

Very useful, uncovered,
honest and clear.

Thanks so much for
your time and works.
Regards."


- Anon

Back to Top