PString

A Lightweight String Class for Formatting Text

Note: PString 3 is now Arduino 1.0 compatible.

Since Print was introduced with Arduino 0012, several classes, including HardwareSerial, LiquidCrystal, Ethernet Client/Server, and my own NewSoftSerial, have been written to leverage its text rendering engine.  But getting formatted text to output devices not in this short list still requires either writing custom code or turning to expensive alternative solutions like sprintf().

PString (“Print-to-String”) is a new lightweight Print-derivative string class that renders text into a character buffer. With PStrings, you can use the Print renderer for any device, even those that do not directly support Print-style text formatting, by first “printing” to a string.

In its simplest use case, you deploy an “on-the-fly” constructor to format text:

char buffer[30];
#define pi 3.14159
PString(buffer, sizeof(buffer), pi);

This code uses Print’s float rendering functions to generate the string equivalent of pi into buffer.

Since PString inherits from Print, PString objects can do everything that other Print-derived classes do:

<code>char buffer[50];
PString mystring(buffer, sizeof(buffer));
char name[] = "Joe";
int age = 45;

mystring.print("Hi, my name is ");
mystring.print(name);
mystring.print(" and I am ");
mystring.print(age);
mystring.println(" years old.");

This generates the expected sentence in buffer the same as if you had printed to the Serial port.

Other member functions

PString is a fairly minimal string class. It can report its length and capacity and give const access to its internal string buffer:

Serial.print(str.length());
Serial.print(str.capacity());
Serial.print(str);

You can reuse a string by calling its begin() function. This effectively resets the position in the buffer where the next printed text will go:

str.print("Hello");
str.begin();
str.print("World");
// str contains "World" here

Operators

PString provides three operators for assignment, concatenation, and equivalency test:

char buffer[20];
PString str(buffer, sizeof(buffer));
str = "Yin"; // assignment
str += " Yang"; // concatenation
if (str == "Yin Yang") // comparison
{
  Serial.println("They are equal!");
}

Runtime safety

PStrings do not “own” their own buffers. Instead, they rely on preallocated static buffers that are passed in at the point of construction. PStrings never allocate memory dynamically, even when the result of a print(), assignment, or concatenation operation would seem to exceed the current buffer’s size. In these cases, the excess data is simply discarded and the string correctly terminated.

Because of these constraints, PStrings can make three key guarantees:

  • they will never cause a buffer overflow
  • a string’s buffer will always be valid memory, i.e. the original buffer
  • buffers will always contain valid (i.e. NULL-terminated) C string data.

Download

The latest version of PString is PString3.zip.

Revision History

Version 1 – initial release
Version 2 – include support for inline renderings with modifiers HEX, OCT, etc. (and eventually float precision)
Version 3 – Arduino 1.0 compatibility

Resource Consumption

PString objects consume 8 bytes of memory during their lifetimes. Depending on what features are used, #including the PString library usually adds only 100-600 bytes to a program’s size.

All input is appreciated.

Mikal Hart

Page last updated on July 3, 2013 at 7:42 pm
76 Responses → “PString”

  1. thomas

    11 years ago

    what do you mean Pstring does not name a type


  2. Mikal

    11 years ago

    @thomas,

    That error message usually means that you didn’t #include the library header file PString.h at the top of your code.


  3. Peter

    11 years ago

    Thanks for the PString lib; a massive improvement over messing about with the bloated & dodgy dynamic memory eating String class in the tiny memory space on the Uno! Also saved nearly 3k of program space when replacing the 3 String variables I was using to pipe text and numbers to a TFT screen with PStrings. Quite how formatting an integer as a string and concatenating it with a handful of characters of text could result in 3k of binary code is a total mystery :)


  4. Mikal

    11 years ago

    Thanks Peter. I agree with your assessment about the String class. I never use that.


  5. gus smith

    10 years ago

    I’m new to Arduino, probably a noob question. I would like to use PString in a custom class like this:

    class MyClass
    {

    public:
    MyClass();

    private:
    char _buffer[100];
    PString buffer(_buffer, sizeof(_buffer));

    };

    This gives the error: “C++ requires a type specifier for all declarations”

    How can I solve this or is this not doable?

    cheers,
    Gus


  6. Mikal

    10 years ago

    @Gus, make sure you #include , and if this custom class is in a secondary file, put #include in the main sketch as well. Let me know if that solves your problem.


  7. Yuzee

    10 years ago

    Hello!!

    Could it support Arduino 1.5.3 for Galileo?

    Error compiling show this message:

    Arduino: 1.5.3 (Windows NT (unknown)), Board: “Intel® Galileo”

    In file included from PString.cpp:20:0:
    PString.h:34:16: error: conflicting return type specified for ‘virtual void PString::write(uint8_t)’
    In file included from PString.h:23:0,
    from PString.cpp:20:
    C:\Users\stone\Desktop\arduino-1.5.3\hardware\arduino\x86\cores\arduino/Print.h:48:20: error: overriding ‘virtual size_t Print::write(uint8_t)’

    This report would have more information with
    “Show verbose output during compilation”
    enabled in File > Preferences.

    How could I solve this problem.

    thanks


  8. Mikal

    10 years ago

    Hi Yuzee–

    Thanks. I’ll fix this soon.

    M


  9. tv12345

    10 years ago

    how do i clear the string .. actually on a loop there is some additional junk value in the buffer from the previous run


  10. Alberto

    10 years ago

    I think I have a similar problem here using PString in a custom class:

    #include “Arduino.h”
    #include “PString.h”

    #define LOG_MESSAGE_SIZE 60

    class Logger {
    public:
    Logger();
    private:
    char _message_buffer[LOG_MESSAGE_SIZE];
    PString _message(_message_buffer, sizeof(message_buffer));

    };

    I have included PString both in cpp and in my sketch, and it gives me this:
    error: ‘_message_buffer’ is not a type
    error: expected identifier before ‘sizeof’
    error: expected ‘,’ or ‘…’ before ‘sizeof’

    maybe I am missing something, but I can’t figure this out.. can you help me?


  11. Mikal

    10 years ago

    @tv12345,

    Good question. Try str.begin();


  12. Mikal

    10 years ago

    @Alberto,

    Yeah, you can’t initialize _message that way. Try this:

    class Logger {
    public:
    Logger() : _message(_message_buffer, sizeof(_message_buffer))
    {}
    private:
    char _message_buffer[44];
    PString _message;
    };


  13. tv12345

    10 years ago

    Hello Mikal ,

    i really appreciate you taking time out to reply to my post actually if u can add a function like str.clear; so that everything inside the buffer is clear and we are ready to go it would be awesome.

    cause str.begin does’nt seem to clear it out during the program running.

    also i would like to mention that i am working on a similar project that uve worked on using balloon to measure data and send using iridiumSBD.

    but i plan to send a 4 mintues of data at a time,instead of sending ar short interval.

    so i need to clear the buffer after 4 minutes.
    thanks again
    tv


  14. Mikal

    10 years ago

    @tv12345,

    Hi…! I use .begin all the time to clear the buffer. It doesn’t actually clear the buffer, but it does zero out the beginning of the buffer, and since the buffer is interpreted as a C string, that should suffice, right? Put it another way, can you show some code that this fails on?

    (If you really need the entire buffer to be zeroed out for some reason, you could call str.begin, and then memset the buffer to 0.)


  15. tv12345

    10 years ago

    Hey Mikal,

    thanks for your response,i already did figure out how to clear the buffer this is how i do it everytime:-

    leng =0;
    PString ptr(inBuffer + leng, sizeof(inBuffer) – leng);
    ptr.begin();

    Right now i am having some different problems like a clash between your iridium and tinyGPS library maybe because of softwareserial and memory issues

    i have two pstring buffer ptr and str of 200 bytes each

    TV


  16. Bruce

    10 years ago

    Hi,
    I have a programme that has a lot of sequential client.print (…) and dataFile.print (…) type statements to build up strings and output numbers to web pages and files Eg

    client.print(F(“: “));
    client.print(entry.name());
    client.print(F(“
    “));

    this is reiterated inside a standard ardunio routine for listing files on the SD card insded of going to serial they go to a web page so that one can be chosen to download.

    I’m wondering if using PString would make my code more compact

    Firstly- I’m allowed to use the following repeated concatination:-

    char buffer[50];
    PString str(buffer, sizeof(buffer));
    str = (F(“: “))+entry.name()+(F(““));
    client.print(str);

    Secondly- is it possible to plant a substring in the middle of ‘str’ as I have a similar problem sending numeric data from 3 arrays to a file that is a mix of integers and floats that all need formatting to one decimal place. At the moment I have 3 for(..){…} loops printing each array in turn.

    Another possibility would be:-

    srt = datetime;
    for (byte i =0; i <6 ;i++){
    str = str+","+((readings[0]),1)
    }
    dataFile.print(str);

    but I'm not sure if it would take up less space than the existing code
    which is:-
    dataFile.print(dateTime);
    for (byte i =0; i < 6; i++){
    dataFile.print (",");
    dataFile.print(convert2T(readings[i]),1);
    }//end for!!


  17. Mikal

    10 years ago

    @Bruce,

    1. No, that’s not how PString was designed. You might try the Streaming library, though, which would allow

    client << ": " << entry.name() << "";
    

    2. Here you could use

    str += ", " + readings[i];
    

    in your loop.


  18. Jorge

    9 years ago

    Hi, I have a question for my Project.

    I receive a UDP package into a Buffer which is in char[30] my question is how can i do to convert this Buffer into a String, because i want to do a two substrings to compare with mode(auto,manual) and consult(some sensors and activate outputs).


  19. Mikal

    9 years ago

    @Jorge, is the buffer 0 terminated? If so,

    string s = buf;
    

  20. Gus Smith

    8 years ago

    Sorry for the late answer. Just got back to using Arduino and this usefull class.

    Your reply to @Alberto about how to initialize a PString in a custom class works!

    [quote]
    class Logger {
    public:
    Logger() : _message(_message_buffer, sizeof(_message_buffer))
    {}
    private:
    char _message_buffer[44];
    PString _message;
    };
    [/quote]

6 Trackbacks For This Post
  1. Buffering sensor data to an I2C eeprom. | An Arduino Based Underwater Sensor Pod

    […] days of heavy lifting later…I had cobbled together this script, using PSTRING to to dramatically simplify the concatenation of the sensor data into a 28 byte long char buffer […]

  2. Using a cheap $3 DS3231 RTC & AT24C32 EEprom from Ebay | An Arduino based underwater sensor project

    […] me be really lazy on the coding and just block write ANY numbers or characters that I PSTRING’d together before I send that buffer variable to the eeprom page writing code.  This flexibility […]

  3. Project Update: Power Tests & Battery issues | An Arduino based underwater sensor project

    […] the SD card. With 42 eeprom buffer writes per cycle (and inefficient Ascii character encoding via pString) the 4k AT24C32 on the $2 RTC board was filled almost 9300 times in this test.  This is […]

  4. The 2014 Cave Pearl Project ‘Year in Review’ | An Arduino based underwater sensor project

    […] a report card of sorts, and I owe thanks to all the Arduino makers out there who shared their code & experience, because they really made it possible for me to pick it up as I went along.  But […]

  5. Testing Power Use with a Complex Duty Cycle | Arduino based underwater sensors

    […] leaving barely 500 bytes free.  I know real coders out there probably laugh at my use of Pstring & Ascii but it makes adding support for new sensors very easy, and adaptability one of my […]

  6. Arduino Tutorial: Adding Sensors to Your Data Logger | Underwater Arduino Data Loggers

    […] alternative way to address string memory problems is to use the Pstring library by Mikal Hart.   “Print-to-String” is a lightweight Print-derivative string class that renders text into a […]

Leave a Reply