Getting number of characters int

Hello,

It's the first time i'm using this & i thought this one would be easy.....no.
I need to count the number of characters in a int (first conversion to string and then use lenght()?) and ad this number to the number of characters in a String.
I want to store the result as a int, with everything in the previous in one line.
Both the int and the string are stored in two separate array's.
Everything else works in my sketch/project but this is extra to always get a part of my displayed text to the far right of the 16x2 lcd display. That why i need that count so i can set the cursor on the right spot every time i display the text.

int menuVariables[5] = { 180, 30, 170, 150, 60 };                                                 //last int is 120 minutes and needs to be converted to ms
String menuUnits[] = { "cm  ", "cm  ", "cm  ", "cm  ", "min " };

and someting like this:

int startCursor = 16 - (String((menuVariables[lcdMenu-1]).lenght() + (menuUnits[lcdMenu-1]).lenght());

Don't mind the lcdMenu-1. It's got to do with re-indexing the info in combo with switch/case method.
I can't get specific info about this.

thx!

It is not very efficient but this will give you the length of n

int len = int(log10(n) + 1);

Personally I would probably put the ints and their lengths in an array of structs and use lcdMenu - 1 as the index to the values and I would not use Strings either

There may well be a more efficient way to do what you want so expect further comments

First thing I did was to "decode" your expression:

int startCursor = 16 - (String((menuVariables[lcdMenu-1]).lenght() 
                     + (menuUnits[lcdMenu-1]).lenght());

Making it a bit more readable to me, let's say you have in "VarLen" the length of the variable, and in "MenuLen" the length of the menu string.

int startCursor = 16 - VarLen + MenuLen

So, if considering "menuVariables[0]" or 180, VarLen is 3, and "menuunits[0]" or "cm ", so MenuLen is 4. I don't get why you need to add the "menuUnits" length (you'd have done better to show us the wanted result, letting us also understand if that units string should be displayed at the left or right of the value...), but this expression gives us the value of 17, when the LCD display cursor valid position is from 0 to 15, and id doesn't make sense to me.

Anyway, let's ignore all this useless bunch of calculations and lengths, you just need to have kinda right-justified value, right?
If you just need to right-justify (with spaces) a value and a measure unit you can simply use sprintf() and print the resulting string (I usually do it with a "buffer" variable) at a fixed display position. And possibly celaring the area first.
Example:

  lcd.setCursor(6,0);
  lcd.print("         "); // change it, based on the total length
  char buf[10]; // Change this value accordingly (max string length + 1)
  sprintf(buf, "%5d%s", menuVariables[lcdMenu-1], menuUnits[lcdMenu-1]);
  lcd.setCursor(6,0);
  lcd.print(buf);

Is this what you meant to do?

similar to above but uses c-strings not String

          1 cm  
         30 cm  
        170 cm  
        150 cm  
         60 min 
1234567890123456
int         menuVals  [] = { 1, 30, 170, 150, 60 };                                                 //last int is 120 minutes and needs to be converted to ms
const char *menuUnits [] = { "cm  ", "cm  ", "cm  ", "cm  ", "min " };

char s [90];

// -----------------------------------------------------------------------------
void
setup (void)
{
    Serial.begin (9600);
    Serial.println ();

    for (int n = 0; n < 5; n++)  {
        sprintf (s, " %10d %-4s", menuVals [n], menuUnits [n]);
        Serial.println (s);
    }
    Serial.println ("1234567890123456");
}

void loop (void) { }

Thx for reminding me sprintf. Although i'm using a other methode to print my int and strings in one line:
lcd.print(menuText[lcdMenu - 1] + menuVariables[lcdMenu - 1] + menuUnits[lcdMenu - 1]);
I have not yet used sprintf (only 3 months at it) so my question about your example (because i can't seem to find a answer). The '5' in %5d%s . What is the purpose?

your right from 0 to 15 so my ...16- is probably wrong. I want to display the int and string next to each other as far at the right side of the lcd display as possible. The actual problem to solve is that the int varies from a single digit to a three digit number and i don't want any space left at the far right side of the lcd if the int is a for example '9' (that means two spaces at the right without the correction i want to achieve)

Thanks for the answer!

see pg 154 in The C Programming Language.

Ok, so if the value is from 1 to 3 chars long and also the unit, the total size/space is up to 6 chars starting from the right, so column 15-6 is 9. And get rid of "String" variables, use plain "C-strings".

See if this example code could help you for this job:

#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x27,16,2);

int menuVariables[5] = { 180, 30, 170, 150, 60 };
char menuUnits[][5] = { "cm", "cm", "cm", "cm", "min" };

char buf[10]; // Change this value accordingly (max string length + 1)

void setup()
{
  Serial.begin(115200);	// Debugging only

  lcd.init();
  lcd.backlight();
  lcd.clear();

  lcd.setCursor(0, 0);
  lcd.print(" - TEST CODE - ");
  
}

void printValue(int lcdMenu) {
  sprintf(buf, "%3d%s", menuVariables[lcdMenu-1], menuUnits[lcdMenu-1]);
  lcd.setCursor(16 - strlen(buf), 1);
  lcd.print(buf);
}

void loop() 
{
  for(int i=1; i<=5; ++i) {
  	printValue(i);
  	delay(1000);
  }
  delay(2000);
}

'works' like a charm! thx!
I read about c strings and sprintf but if i don't use it in a project it gets forgotten verry fast.
Now i have to try to incorporate this c string/sprintf solution in my project. It is not essential but i'm eager to perfect the result. My waterlevelmeter with refill valve command and double refill valve alarm needs to be very intuitive and simple to operate (one button for everything!). The last coding hurdle wil be the use of EEPROM to store settings in case of a power loss.

You really don't need a log function just to know the number of digits. This is one of those cases where the apparent dumb way can be good enough.

if( input >= 10000 ) return 5;
if( input >= 1000 ) return 4;
if( input >= 100 ) return 3;
if( input >= 10 ) return 2;
return 1;

Only handles positive ints, but that'll probably be loads faster than computing a log function and rounding the resulting float. Wouldn't be that hard to modify it to handle signed numbers, and there's probably a way to recursively define it based on the maximum size of the specific variable you use. But that's a good base.

question
const char *menuUnits [] = { "cm ", "cm ", "cm ", "cm ", "min " };
If i'm correct this is a array of pointers.
But { "cm ", "cm ", "cm ", "cm ", "min " } ar these the names of the pointers? How do i have to look at this? And and the content where the pointers 'point' to can only be occupied by type char. Right? Or am i rambling?

After looking some stuff up this seems to be the most logical and simplest solution for my problem (i don't need to make a buffer with sprintf).
And i'm not shure if this realy takes up that mutch computing power.
I'll wait with appointing a solution. Still trying to soak it all up but everybody has been very helpfull!

yes, the array holds the addresses of the strings which are stored elsewhere.

pointers don't have names like what you're suggesting, they are variables which are named.

accessing an array element requires using an index into the array.

a c string is an array of chars with a NUL following the last char in the string. A pointer to a string will be to an addresses containing a char. the next address and subsequent addresses will also contain a chars.

look this over

output

 0x100 names

 0x146 tom 
 0x14a dick
 0x14f harry
 0x155 harry bellefonte

dump: tom
    0146: 74 6f 6d 00
dump: dick
    014a: 64 69 63 6b 00
dump: harry
    014f: 68 61 72 72 79 00
dump: harry bellefonte
    0155: 68 61 72 72 79 20 62 65
    015d: 6c 6c 65 66 6f 6e 74 65
    0165: 00
const char *names [] = { "tom", "dick", "harry", "harry bellefonte" };
const int   Nnames   = sizeof(names) / sizeof(char*);

char s [90];

// -----------------------------------------------------------------------------
void
dumpChar (
    char       *p,
    int         nByte,
    const char *label )
{
    sprintf (s, "dump: %s",  label);
    Serial.print (s);
    
    for (int n = 0; n < nByte; n++)  {
        if (! (n % 8))  {
            sprintf (s, "\n    %04x:",  & p [n]);
            Serial.print (s);
        }
        sprintf (s, " %02x",  p [n]);
        Serial.print (s);
    }
    Serial.println ();
}

// -----------------------------------------------------------------------------
void
setup (void)
{
    Serial.begin (9600);
    Serial.println ();

    sprintf (s, " %p %-4s",  names, "names");
    Serial.println (s);
    Serial.println ();

    for (int n = 0; n < Nnames; n++)  {
        sprintf (s, " %p %-4s",  names [n], names [n]);
        Serial.println (s);
    }
    Serial.println ();

    for (int n = 0; n < Nnames; n++)
        dumpChar (names [n], 1+strlen(names [n]), names [n]);
}

void loop (void) { }

just test your function.
In the German section we had a similar discussion 5 years ago. In the end I have compared 11 variants (well 12, but one is not correct) with different compiler settings.

I have aded a extra char array because that is like in my project but is doens't seem to work. And i have defined the lenght for all the elements in the buffer. (your example works fine)
What am i missing here? I took the time to get info but i feel it has been a waste of time:

#include <LiquidCrystal.h>

LiquidCrystal lcd(7, 8, 9, 10, 11, 12);

char menuText[][5] = { "MinLevel", "MaxLevel", "FillStart", "FillStop", "Alarmtime" };
int menuVariables[5] = { 180, 30, 170, 150, 60 };
char menuUnits[][5]= { "cm", "cm", "cm", "cm", "min" };

char buf[16]; // Change this value accordingly (max string length + 1)

void setup() {
  //Serial.begin(115200);	// Debugging only

  //lcd.init();
  //lcd.backlight();
  //lcd.clear();

  lcd.setCursor(0, 0);
  lcd.print(" - TEST CODE - ");
  }

void printValue(int lcdMenu) {
  sprintf(buf, "%10s%3d%-3s", menuText[lcdMenu-1],  menuVariables[lcdMenu-1], menuUnits[lcdMenu-1]);
  //lcd.setCursor(16 - strlen(buf), 1);
  lcd.setCursor(0, 0);
  lcd.print(buf);
}

void loop() 
{
  for(int i=1; i<=5; ++i) {
  	printValue(i);
  	delay(1000);
  }
  delay(2000);
}

That is a very inadequate description of the problem

What doesn't work ?

You are filling in all 16 bytes of buf so there is no room for the trailing nul '\0' which a c-string needs. If you display is 16 chars wide, buf needs to be 17 chars in size.

your right: lcd display keeps showing - TESTCODE - (with a slight flicker)

i'v made it
char buf[17];
but it still doesn't work. It still keeps showing - TESTCODE - but the flickering is gone.
It's damn hard, that coding stuff to learn. The moment i think i figured something out i'm completely wrong.

Your menuText variable is not correct. You have declared an array of pointers to char with each array being 5 chars in length which is not the case. You really want an array of char pointers

char *menuText[] = { "MinLevel", "MaxLevel", "FillStart", "FillStop", "Alarmtime" };
int menuVariables[] = { 180, 30, 170, 150, 60 };
char *menuUnits[]= { "cm", "cm", "cm", "cm", "min" };

Your wise guidence of using the array of char pointer just happened to spring to mind while i was on my way to work (yes these days i'm starting & going to sleep with it. Even my wife is complaining!) This is the last posibilty i didn't yet tested.
question: Am i correct that a array of char pointers actualy holds different arrays of char in the background? for example "Minlevel" is actualy a char array that holds char 'M' at offset 0, char 'i' at offset 1, char 'n' at offset 2, etc...? Right? This realy sucks in C because this is not actualy visible when coding this stuff. Also the use of "" or , or ; is still confusing.
@gcjr @blh64 thx for the help with your solution. I'll test it asap. btw i have found a not so elegant working solution that completly does wat i want it to do but i want to solve it like you guy's.
Less elegant:

String menuText[] = { "MinLevel     ", "MaxLevel     ", "FillStart    ", "FillStop     ", "Alarmtime    " };
int menuVariables[5] = { 180, 30, 170, 150, 60 };                                                 
String menuUnits[] = { "cm  ", "cm  ", "cm  ", "cm  ", "min" };
    int startCursor = 14 - menuUnits[lcdMenu-1].length();//prep calculation to set the second part of the lcd text on the first line to the right
    lcd.setCursor(0, 0); //cursor set line 0, character 0
    lcd.print(menuText[lcdMenu-1]);
    lcd.setCursor(startCursor, 0);
    lcd.print(menuVariables[lcdMenu - 1] + menuUnits[lcdMenu - 1]);