Switching from String to std::string is way harder than I thought !

GeorgeFlorian1
Posts: 160
Joined: Thu Jan 31, 2019 2:32 pm

Switching from String to std::string is way harder than I thought !

Postby GeorgeFlorian1 » Tue Mar 26, 2019 11:16 am

Hello !

I am currently trying to switch from using String to std::string, because even if it's WAY easier to use String, I understand that it plays with realloc() and can cause "heap fragmentation".

I would like some help with moving from String to std::string.

I have a void function that takes as argument a std::string string1 and puts it in a ring buffer but also prints it on the Serial.

Code: Select all

void logOutput(std::string string1) {
	delay(500);
	circle.push(string1);	
	Serial.println(string1.c_str());	
}
And it doesn't work when I try and use the following:

Code: Select all

char* name = "name";
logOutput("Something something" + name);
logOutput("Something" + "Something"); //also doesn't work
Not even a simple for works properly:

Code: Select all

for(int i = 1; i<=10;i++){
	logOutput("Linia " + i);
}
It outputs: inia nia ia a. What is wrong with std::string ?
String is a champion and easy to use. I have to write a lot of additional lines for a std::string to work properly.

The following while:

Code: Select all

	while(WiFi.status() != WL_CONNECTED && k<20) {
		delay(1000);
		k++;
		logOutput("Connecting to WiFi");
	}
Outputs:

Connecting to WiFi
onnecting to WiFi
nnecting to WiFi
necting to WiFi

So how am I supposed to send one beautiful string to logOutput so that it can add it to the ring buffer ? I can’t send pieces of it, because they’ll be put in a ring buffer that, later on, will print them on different lines.

If I can’t use + operator to concatenate char to a string how should I do it ?

I am beginning to miss the ability to concatenate String as easily as using + operator.

For reference:

Circular Buffer:

Code: Select all

std::string strlog; // global variable

struct ring_buffer
{
    ring_buffer(size_t cap) : buffer(cap) {}

    bool empty() const { return sz == 0 ; }
    bool full() const { return sz == buffer.size() ; }

    void push( std::string str )
    {
        if(last >= buffer.size()) last = 0 ;
        buffer[last] = str ;
        ++last ;
        if(full()) 
			first = (first+1) %  buffer.size() ;
        else ++sz ;
    }
    void print() const {
		strlog = ""; // empty its previous content
		if( first < last )
			for( size_t i = first ; i < last ; ++i ) {
				strlog.append(buffer[i]);
				strlog.append("<br>");
			}	
		else
		{
			for( size_t i = first ; i < buffer.size() ; ++i ) {
				strlog.append(buffer[i]);
				strlog.append("<br>");
			}
			for( size_t i = 0 ; i < last ; ++i ) {
				strlog.append(buffer[i]);
				strlog.append("<br>");
			}
		}
	}

    private:
        std::vector<std::string> buffer ;
        size_t first = 0 ;
        size_t last = 0 ;
        size_t sz = 0 ;
};
I use this circular buffer to send what ever string I would output with Serial.println(); to a webpage using ESPAsyncWebServer library’s processor(); function:

Code: Select all

String processor(const String& var) { 
	circle.print();
	if  (var == "PLACEHOLDER_1")
		return strlog.c_str();
	return String();
}

idahowalker
Posts: 166
Joined: Wed Aug 01, 2018 12:06 pm

Re: Switching from String to std::string is way harder than I thought !

Postby idahowalker » Tue Mar 26, 2019 11:32 am

Have you considered using sString.reserve(x) where x is the String buffer size to use, sString.concate( "xxx") to add to the string, sString ="" to clear the string buffer, and a few other functions to work on/with the string buffer? .reserve() assigns an allocated memory space that does not get/cause fragmenting.

I have a STM32 bluepill running a sensor that transmits strings to an ESP32. There is a set sString.reserve(250) that is used as the working location for String work. The STM32 Bluepill has 20K RAM and has been running for over a year without need to be reset.

markkuk
Posts: 38
Joined: Wed Mar 27, 2019 11:50 am

Re: Switching from String to std::string is way harder than I thought !

Postby markkuk » Wed Mar 27, 2019 12:11 pm

GeorgeFlorian1 wrote:
Tue Mar 26, 2019 11:16 am

And it doesn't work when I try and use the following:

Code: Select all

char* name = "name";
logOutput("Something something" + name);
logOutput("Something" + "Something"); //also doesn't work
Not even a simple for works properly:

Code: Select all

for(int i = 1; i<=10;i++){
	logOutput("Linia " + i);
}
It outputs: inia nia ia a. What is wrong with std::string ?
The type of a quoted literal string like "Linia" isn't std::string, it's a pointer to a character array. You can't use the + operator to concatenate char pointers, and adding a number to a pointer addresses different members of the character array.
You need to actually use std::strings in your code instead of character arrays.

Code: Select all

const std::string name("name");
logOutput(std::string("Something something") + name);

for(int i = 1; i <= 10; ++i) {
    std::ostringstream os;
    os << "Linia"  << i;
    logOutput(os.str());
}

GeorgeFlorian1
Posts: 160
Joined: Thu Jan 31, 2019 2:32 pm

Re: Switching from String to std::string is way harder than I thought !

Postby GeorgeFlorian1 » Fri Mar 29, 2019 9:27 am

idahowalker wrote:
Tue Mar 26, 2019 11:32 am
Have you considered using sString.reserve(x) where x is the String buffer size to use, sString.concate( "xxx") to add to the string, sString ="" to clear the string buffer, and a few other functions to work on/with the string buffer? .reserve() assigns an allocated memory space that does not get/cause fragmenting.

I have a STM32 bluepill running a sensor that transmits strings to an ESP32. There is a set sString.reserve(250) that is used as the working location for String work. The STM32 Bluepill has 20K RAM and has been running for over a year without need to be reset.
That sounds like a proper solution. I had no idea that there existed String.reserve(x). This is what you're talking about, right ?

If it isn't too much of a bother, can you take a look over this code and help me modify it accordingly ?
I am using a Circular Buffer and a global String to store some "logs" and then I output them on /logs web-page in a FIFO manner.

Code: Select all

#include <Arduino.h>
#include <SPIFFS.h>
#include <ESPAsyncWebServer.h>

String strlog;

void logOutput(String string1);

//---------- Circular Buffer
struct ring_buffer
{
    ring_buffer(size_t cap) : buffer(cap) {}

    bool empty() const { return sz == 0 ; }
    bool full() const { return sz == buffer.size() ; }

    void push( String str )
    {
        if(last >= buffer.size()) last = 0 ;
        buffer[last] = str ;
        ++last ;
        if(full()) 
			first = (first+1) %  buffer.size() ;
        else ++sz ;
    }
    void print() const {
		strlog= "";
		if( first < last )
			for( size_t i = first ; i < last ; ++i ) {
				strlog += (buffer[i] + "<br>");
			}	
		else {
			for( size_t i = first ; i < buffer.size() ; ++i ) {
				strlog += (buffer[i] + "<br>");
			}
			for( size_t i = 0 ; i < last ; ++i ) {
				strlog += (buffer[i] + "<br>");
			}
		}
	}

    private:
        std::vector<String> buffer ;
        size_t first = 0 ;
        size_t last = 0 ;
        size_t sz = 0 ;
};
//---------- Circular Buffer

ring_buffer circle(10);

void logOutput(String string1) {
	delay(500);
	circle.push(string1);	
	Serial.println(string1);	
}

AsyncWebServer server(80);

const char* ssid = "ssid";
const char* password = "password";

String processor(const String& var) { 
	circle.print();
	if  (var == "PLACEHOLDER_1")
		return strlog;
	return String();
}

void setup() {
	Serial.begin(115200);
	delay(2000);

	if(!SPIFFS.begin(true)) {
		logOutput("ERROR ! SPIFFS file system was not mounted. Reformatting !");
	}
	WiFi.begin(ssid, password);
	delay(1000);
	int k = 0;
	while(WiFi.status() != WL_CONNECTED && k<20) {
		delay(1000);
		k++;
		logOutput((String)"Connecting to WiFi");
	}
	if(WiFi.status() == WL_CONNECTED) {
		logOutput((String)"Connected to: " + ssid + " with IP: " + WiFi.localIP().toString());
	} else {
		logOutput("Couldn't connect to WiFi ! Restarting in 5 seconds");
		delay(5000);
		ESP.restart();
	}

	server.on("/logs", HTTP_GET, [](AsyncWebServerRequest* request){
		request->send(SPIFFS, "/events.html", "text/html");
	});
	server.on("/events_log.html", HTTP_GET, [](AsyncWebServerRequest* request){
		request->send(SPIFFS, "/events_log.html", "text/html", false, processor);
	});
	server.on("/jquery-1.12.4.min.js", HTTP_GET, [](AsyncWebServerRequest* request){
		request->send(SPIFFS, "/jquery-1.12.4.min.js", "text/javascript");
	});	
	server.on("/master.css", HTTP_GET, [](AsyncWebServerRequest *request) {
		request->send(SPIFFS, "/master.css", "text/css");
	});	
	server.on("/back-image.jpg", HTTP_GET, [](AsyncWebServerRequest *request) {
		request->send(SPIFFS, "/back-image.jpg", "image/jpeg");
	});
	server.on("/logo.png", HTTP_GET, [](AsyncWebServerRequest *request) {
		request->send(SPIFFS, "/logo.png", "image/png");
	});

	server.begin();
	delay(5000);

	logOutput("After server.begin()");
	for(int i = 1; i<=10;i++){
		logOutput((String)"Linia " + i);
	}
	delay(1000);
}

void loop() {
	logOutput("Beginning the loop()");
	Serial.print("Size of string: ");
	Serial.println(sizeof(strlog));
	delay(5000);	
}

idahowalker
Posts: 166
Joined: Wed Aug 01, 2018 12:06 pm

Re: Switching from String to std::string is way harder than I thought !

Postby idahowalker » Fri Mar 29, 2019 10:01 am

Yes, to that link being what I am 'talking' about. If I remember correctly, the string reserve buffer is created on the heap space instead of the stack space.

In regards to the buffer, ring. Over all, freeRTOS is built in to the ESP32. One aspect of freeRTOS is the ability to use stream buffers ( https://www.freertos.org/RTOS-stream-buffer-API.html and https://www.freertos.org/RTOS-stream-me ... ffers.html.

Also, by using freeRTOS, you can eliminate those delays, which stop program execution but do not allow others tasks from running, with the use of vTaskDelay.

Who is online

Users browsing this forum: No registered users and 63 guests