converting code from String to char arrays - alternative for String(integer)?

as suggested by someone in a previous question, i'm trying to convert my library, using char arrays instead of Strings.

I bumped into the following problem:
to convert integers to the char array, i found the following code:

	String a = String(hours); // string containing hours as a string
	char uren[2];
	a.toCharArray(uren, 2);

which does the job, but it doesn't seem ok.
is there a better alternative to convert the integers to char arrays?

What do you mean by "better"? A Chrysler is a better car than a Ferrari if you have 4 kids. It's impossible to propose a solution until we know what your real problem is.

  • Have you run out of memory on the UNO and you need to get rid of the String object that is taking up lots of program space?
  • Do you want to control the formatting, such as leading zeros
  • ...

but it doesn't seem ok.

Define "OK"

as i’m quoting PaulS:


(http://forum.arduino.cc/index.php?topic=284397.0)

So by not using the String class, i’m supposed to get around the utilities of this class too, right?

however, i’m not sure if there are ‘better’ alternatives for the code i’m working with.

for instance, the method getTimeString() without strings:

char* Menu::getTimeString(){
	DateTime now = this->rtc->now();
	char uren[] = now.hour();
	char minuten[];
	if (now.minute() < 10) {
		strcpy(minuten, "0");
	}
	else	strcpy(minuten, "");
	strcat(minuten, (now.minute()));

	char seconden[] = (now.second());
	char[] tijd;
	strcat(tijd, uren);
	strcat(tijd, "u");
	strcat(tijd, minuten);
	return tijd;
}

Versus using Strings:

String Menu::getTimeString(){
	DateTime now = this->rtc->now();
	String uren = String(now.hour(), DEC);
	String minuten;
	if (now.minute() < 10) {
		minuten = "0";
	}
	else minuten = "";
	minuten.concat(String(now.minute(), DEC));

	String seconden = String(now.second(), DEC);
	String tijd = uren + "u" + minuten; //+ ":" + seconden;
	return tijd;
}

The goal is just to make the library as small as possible, yet not having to give in on speed too much. currently my project is running on arduino nano, with around 75% of memory used.

This seems like an example of "premature optimization." If it's working now then STOP. There's no reason to make it smaller. It doesn't cost anything if your program fills 75% or 70% of the Arduino.

Now if you need to add some features and you fill 101% of the Arduino's memory then you can start looking at things to optimize. If your program uses a lot of String functions then it's going to be a lot of work to replace them all. It might be easier/faster/cheaper (more specific definitions of 'better') to just buy a bigger Arduino. If it's only used in one place, then it might be the right thing to replace the String object to get your memory usage below 100%.

it's working, so I guess i'll stop until it's not working anymore :slight_smile:

Thank you for your time tho.

Ward123:
as suggested by someone in a previous question, i'm trying to convert my library, using char arrays instead of Strings.

I bumped into the following problem:
to convert integers to the char array, i found the following code:

 String a = String(hours); // string containing hours as a string

char uren[2];
a.toCharArray(uren, 2);




which does the job, but it doesn't seem ok.
is there a better alternative to convert the integers to char arrays?

It seems to me that you are not completely embracing the use of strings instead of Strings. Why is hours a String in the first place ?

is there a better alternative to convert the integers to char arrays?

itoa() or sprintf().

If you're trying to avoid the String class, why does your code still use String? The class will bloat your memory requirements by about 2K. Also, you said you're writing it for a library. If that's the case, even though your memory available makes it workable doesn't make it viable for everyone who may wish to use the library.

If you do not want to use the more efficient char arrays and the functions to manipulate them, you can do what you want a little better anyway. Avoiding the String class is more work on your part, however the String library just wraps the same functionality you would otherwise use, except it throws in a little dynamic memory usage too.

String a = String(hours); // string containing hours as a string
char uren[2];
a.toCharArray(uren, 2);

This method is useful when you need to copy the data, if just want to read the data there are easier ways.

You can remove the temporary String object and just call the constructor.
String str = String(hours);

Simply be written as:
String str(hours);

The string can be used like an array to read each character:

* *for( int i = 0 ; i < str.length() ; ++i ){  Serial.print( str[i] ); }* *

Accessing the elements like this allows you to overwrite them too: str[0] = ‘!’;.

If all you want to do is read the contents, you can also use the function String::c_str(), however it returns a const pointer and cannot be used to write data:

  const char *ptr = str.c_str();
  
  ptr[0]='a'; //Will not work.
  
  Serial.print( ptr ); //Works fine!
  Serial.print( str.c_str() ); //Works fine!

I managed to convert everything, and get rid of the entire String class.

Sharing the results:

**using Char Arrays**
Compiling 'Leds30' for 'Arduino Nano w/ ATmega328'
Binary sketch size: 5 698 bytes (used 19% of a 30 720 byte maximum) (0,34 secs)
Minimum Memory Usage: 577 bytes (28% of a 2048 byte maximum)

**Using String class**
Compiling 'Leds30' for 'Arduino Nano w/ ATmega328'
Binary sketch size: 8 024 bytes (used 26% of a 30 720 byte maximum) (0,44 secs)
Minimum Memory Usage: 538 bytes (26% of a 2048 byte maximum)

Overall, the sketch size looks to be quite smaller.
Should I worry about the little increase of memory usage?

Should I worry about the little increase of memory usage?

On the whole, no. If you post your program as it is now there may be ways of reducing memory usage.

I guess it will be easier to view the code on github then here so please find it Here

Btw, I've coverted the code using sprintf(...) but it made the library's size much larger.

I guess it will be easier to view the code on github then here

What makes you think that ?

the size of the code:
header:

#ifndef _Menu_h
#define _Menu_h

#include <Wire.h>

#include <RTClib.h>
#include <LiquidCrystal\LiquidCrystal.h>
#include <LiquidCrystal_I2C.h>

#include "Arduino.h"


class Menu
{
private:

#ifndef I2C


	LiquidCrystal *lcd;

#endif
#ifdef I2C

	LiquidCrystal_I2C *lcd;





#endif
	RTC_DS1307 *rtc;

	char lO[17];
	char lB[17];
	char rO[17];
	char rB[17];
	char cB[17];
	char cO[17];

	int _lcdLength;
	int _lcdHeight;

	bool _useRTC;

public:
#ifdef I2C
	void initI2C(){
		this->lcd->init();
		this->lcd->backlight();
		if (_useRTC){
			Wire.begin();
			this->rtc->begin();
		}
	}
	Menu(LiquidCrystal_I2C *scherm, RTC_DS1307 *realTimeClock, int lcdLength, int lcdHeight){
		_lcdLength = lcdLength;
		_lcdHeight = lcdHeight;
		this->lcd = scherm;
		this->rtc = realTimeClock;
		_useRTC = true;
	};

	Menu(LiquidCrystal_I2C *lcd, int lcdLength, int lcdHeight){
		_lcdLength = lcdLength;
		_lcdHeight = lcdHeight;
		initI2C();
	};


#else
	Menu(LiquidCrystal *lcd, int lcdLength, int lcdHeight){
		this->lcd = lcd;
		_lcdLength = lcdLength;
		_lcdHeight = lcdHeight;
		this->lcd->begin(_lcdLength, _lcdHeight);
	};

	Menu(LiquidCrystal *lcd, RTC_DS1307 *rtc, int lcdLength, int lcdHeight){
		this->lcd = lcd;
		this->rtc = rtc;
		Wire.begin();
		this->rtc->begin();
		_lcdLength = lcdLength;
		_lcdHeight = lcdHeight;
		this->lcd->begin(_lcdLength, _lcdHeight);
	}
#endif

	void off();

	void all(char lB[], char rB[], char lO[], char rO[]){
		linksBoven(lB);
		rechtsBoven(rB);
		linksOnder(lO);
		rechtsOnder(rO);
	};

	void linksBoven(char s[]){
		strcpy(lB, s);
		if (strlen(lB) > 0)	{
			this->lcd->setCursor(0, 0);
			this->lcd->print(lB);
		}

	}
	void linksOnder(char s[]){
		strcpy(lO, s);

		if (strlen(lO) > 0)	{
			this->lcd->setCursor(0, 1);
			this->lcd->print(lO);
		}

	}
	void rechtsBoven(char s[]){

		strcpy(rB, s);
		int rBlength = strlen(rB);


		this->lcd->setCursor(16 - rBlength - 1, 0);

		if (rBlength > 0)	{
			this->lcd->print(" ");
			this->lcd->print(rB);
		}
	}

	void rechtsOnder(char s[]){

		strcpy(rO, s);
		int rOlength = strlen(rO);

		this->lcd->setCursor(16 - rOlength-1, 1);

		if (rOlength > 0)	{
			this->lcd->print(" ");
			this->lcd->print(rO);
		}

	}
	void centerBoven(char s[]){
		strcpy(cB, s);
		int cbLength = strlen(cB);
		int col = 0; int row = 0;
		int positie = floor((_lcdLength - cbLength) / 2);
		this->lcd->setCursor(col, row);
		for (int i = 0; i < positie; i++)	{
			this->lcd->print(" ");
		}
		this->lcd->setCursor(positie, 0);
		this->lcd->print(s);
		for (int i = positie + cbLength; i <= 16; i++) this->lcd->print(" ");
	}


	void centerOnder(char s[]){

		int lBLength = strlen(lB);
		int lOLength = strlen(lO);
		int rBLength = strlen(rB);
		int rOLength = strlen(rO);

			this->lcd->clear();

			if (lBLength > 0)	{
				this->lcd->setCursor(0, 0);
				this->lcd->print(lB);
			}

			if (rBLength > 0)	{
				int pos = _lcdLength - rBLength - 1;
				this->lcd->setCursor(pos, 0);
				this->lcd->print(" ");
				this->lcd->print(rB);
			}

			if (lOLength > 0)	{
				this->lcd->setCursor(0, 1);
				this->lcd->print(lO);
			}
			if (rOLength > 0)	{
				int pos = _lcdLength - rOLength - 1;
				this->lcd->setCursor(pos, 1);
				this->lcd->print(" ");
				this->lcd->print(rO);
			}
		
	}


#pragma region huidige tijd printen
	void timeLinksBoven(){
		linksBoven(getTimeString());
	}
	void timeRechtsBoven(){
		rechtsBoven(getTimeString());
	}
	void timeLinksOnder(){
		linksOnder(getTimeString());
	}
	void timeRechtsOnder(){
		rechtsOnder(getTimeString());
	}
#pragma endregion


	//NOK
#pragma region custom tijd printen
	void timeLinksBoven(DateTime time){
		linksBoven(getTimeString(time));
	}


	void timeRechtsBoven(DateTime time){
		rechtsBoven(getTimeString(time));
	}


	void timeLinksOnder(DateTime time){
		linksOnder(getTimeString(time));
	}


	void timeRechtsOnder(DateTime time){
		rechtsOnder(getTimeString(time));
	}
#pragma endregion


	//NOK
#pragma region huidige datum printen

	void dateLinksBoven(){
		linksBoven(getDateString());
	}

	void dateRechtsBoven(){
		rechtsBoven(getDateString());
	}


	void dateLinksOnder(){
		linksOnder(getDateString());
	}


	void dateRechtsOnder(){
		rechtsOnder(getDateString());
	}
#pragma endregion


	//NOK
#pragma region custom datum printen
	void dateLinksBoven(DateTime date){
		linksBoven(getDateString(date));
	}

	void dateRechtsBoven(DateTime date){
		rechtsBoven(getDateString(date));
	}


	void dateLinksOnder(DateTime date){
		linksOnder(getDateString(date));
	}


	void dateRechtsOnder(DateTime date){

		rechtsOnder(getDateString(date));
	}
#pragma endregion


	void printAll();


	char* timeToString(int hours, int minutes, int seconds);
	char* getTimeString();
	char* getTimeString(DateTime time);
	char* getDateString();
	char* getDateString(DateTime date);

private:
	void clear(){
		this->lcd->clear();

	}


};
#endif

.cpp:

char* Menu::timeToString(int hours, int minutes, int seconds){ //positie: lB,lO,rB,rO
	char tijd[9];
	char h[3];
	char m[3];
	char s[3];

	itoa(hours, h, 10);
	itoa(minutes, m, 10);
	itoa(seconds, s, 10);

	if (hours < 10) strcpy(tijd, "0");

	strcat(tijd, h);
	strcat(tijd, ":");

	if (minutes < 10) strcat(tijd, "0");
	strcat(tijd, m);
	strcat(tijd, ":");
	if (seconds < 10) strcat(tijd, "0");
	strcat(tijd, s);

	return tijd;
}

char* Menu::getTimeString(){
	DateTime now = this->rtc->now();
	int hours = now.hour();
	int minutes = now.minute();
	char tijd[6];
	char h[3];
	char m[3];

	itoa(hours, h, 10);
	itoa(minutes, m, 10);

	if (hours < 10) strcpy(tijd, "0");

	strcat(tijd, h);
	strcat(tijd, "u");

	if (minutes < 10) strcat(tijd, "0");
	strcat(tijd, m);
	return tijd;
}
char*  Menu::getTimeString(DateTime time){
	int hours = time.hour();
	int minutes = time.minute();
	char tijd[6];
	char h[3];
	char m[3];

	itoa(hours, h, 10);
	itoa(minutes, m, 10);

	if (hours < 10) strcpy(tijd, "0");

	strcat(tijd, h);
	strcat(tijd, "u");

	if (minutes < 10) strcat(tijd, "0");
	strcat(tijd, m);
	return tijd;
}


char*  Menu::getDateString(){
	DateTime now = this->rtc->now();
	char dag[3];
	char maand[3];
	char jaar[3];
	char datum[9];

	int d = now.day();
	int m = now.month();
	int y = now.year() - 2000;

	itoa(d, dag, 10);
	itoa(m, maand, 10);
	itoa(y, jaar, 10);

	strcat(datum, dag);
	strcat(datum, "/");
	strcat(datum, maand);
	strcat(datum, "/");
	strcat(datum, jaar);
	return datum;
}
char*  Menu::getDateString(DateTime date){
	char dag[3];
	char maand[3];
	char jaar[3];
	char datum[9];

	int d = date.day();
	int m = date.month();
	int y = date.year() - 2000;

	itoa(d, dag, 10);
	itoa(m, maand, 10);
	itoa(y, jaar, 10);

	strcat(datum, dag);
	strcat(datum, "/");
	strcat(datum, maand);
	strcat(datum, "/");
	strcat(datum, jaar);
	return datum;
}

but there you go, if you prefer it like this :slight_smile:

char* Menu::timeToString(int hours, int minutes, int seconds){ //positie: lB,lO,rB,rO
	char tijd[9];
// Snip happens
	return tijd;
}

tijd is valid ONLY while the timeToString() method is running. As soon as that function ends, the space used by that array is up for grabs. Returning a pointer to that space is useless (and wrong).

That method needs to take an argument that defines where to write the data. It does NOT define the space. It returns no pointer. It might return an error code, or the return type might be void. But, the return type is NOT char *.