toInt() function not working on a string

Folks,

I'm into my second day trying to resolve this issue after much searching. I am testing a method of taking input from a numeric keypad (includes * and #) and converting it to an integer to use as an array subscript. The code works in taking the input and adding each character to a string. Input is terminated with a '#'. What doesn't work is converting that string to an integer. Here is the relevant code (currently just being tested in setup() ):

char key;
String tapRelayStr;
int tapRelayInt = 7; 

// Enter tap relay number and press #
  while (key != '#') {  
    Serial.println("Now in while loop");
    tapRelayStr.concat(key);    
    key = keypad.getKey();
  }

  tapRelayStr.trim();
  Serial.print("tapRelayStr is: "); Serial.println(tapRelayStr);
  Serial.print("Existing value of tapRelayInt: "); Serial.println(tapRelayInt);
  tapRelayInt = tapRelayStr.toInt();         // ***** THIS DOESN'T SEEM TO WORK AS EXPECTED
  Serial.print("tapRelayInt after .toInt is: "); Serial.println(tapRelayInt);
  //digitalWrite(taps[tapRelayInt], LOW);
}

Here's the output when a '5' is entered followed by '#':

.
.
.
Now in while loop
tapRelayStr is: 5
Existing value of tapRelayInt: 7
tapRelayInt after .toInt is: 0

I declared tapRelayInt = 7 to see if tapRelayStr.toInt() changes the value in any way. It does. It sets tapRelayInt to 0. This is what the .toInt function is supposed to do when it's not given a numeric string. As you see, I've trimmed tapRelayStr just in case, but that's not the issue here. Does anyone know what is the problem? It's driving me nuts. Thanks! --Todd

Hello

Show full code

This is a small test sketch. This is the full code. Is there something that's ambiguous about the code that's shown?

It may be caused by another problem somewhere in the code that you didn't show, it's not a full code because it can't be compiled

Show a minimal but compilable code, just enough to reproduce the problem

Okay. Here it is:

#include <Keypad.h>

// Arduino pin assignments for tap relays (from highest to lowest inductance).
// taps[0] element not used.
int taps[] = {0,16,A0,14,A2,12,A4,10,A6,8,A8,6,A10,4,A12,2,A14};

const byte ROWS = 4;
const byte COLS = 3;

char keys[ROWS][COLS] = {
  {'1','2','3'},
  {'4','5','6'},
  {'7','8','9'},
  {'*','0','#'}
  };
byte rowPins[ROWS] = {19, 21, 23, 25};
byte colPins[COLS] = {27, 29, 31};

Keypad keypad = Keypad(makeKeymap(keys), rowPins, colPins, ROWS, COLS );

void setup() {

  Serial.begin(115200);

  // Set tap relay pins to output.
  for (byte i = 1; i <= 16; i++) {
    pinMode(taps[i], OUTPUT);
    digitalWrite(taps[i], HIGH);
  }

  for (byte i = 1; i <= 16; i++) {
   digitalWrite(taps[i], LOW);
   delay(250);
   digitalWrite(taps[i], HIGH);
  }

char key;
String tapRelayStr;
int tapRelayInt = 7; 

// Enter tap relay number and press #
  while (key != '#') {  // 
    Serial.println("Now in while loop");
    tapRelayStr.concat(key);
    Serial.print("tapRelayStr is now: "); Serial.println(tapRelayStr);
    delay(1000);    
    key = keypad.getKey();
  }

  tapRelayStr.trim();
  Serial.print("tapRelayStr is: "); Serial.println(tapRelayStr);
  Serial.print("Existing value of tapRelayInt: "); Serial.println(tapRelayInt);
  tapRelayInt = tapRelayStr.toInt();
  Serial.print("tapRelayInt after .toInt is: "); Serial.println(tapRelayInt);
  digitalWrite(taps[tapRelayStr.toInt()], LOW);
}

void loop() {
  
}

This tutorial shows you how to input character strings (C-strings) from a keypad without using String objects, which cause program malfunctions and even crashes on AVR-based Arduinos.

To convert a character string to a number, use the C-string function atoi().

Okay! Thanks, I'll take a look. --Todd

Try this

  while ( key != '#' ) {  // 
    if ( key >= '0' && key <= '9' )
    {
      tapRelayStr.concat(key);
      Serial.print("tapRelayStr is now: "); Serial.println(tapRelayStr);
    }  
    key = keypad.getKey();
  }

And you should always initialize local variable to a known value (unless you don't care about the garbage that it may contain), for example char key = NO_KEY;. If you don't initialize this variable, it may very well contain the garbage value # and your code will not work as expected

@guix suggestion should help, getKey() returns a value of 0 (the actual value of 0, not a '0' character) when no key is pressed, that is getting added onto your String, and toInt will terminate when it sees a character that is not part of a number. You can print the length of the String to see that it exceeds the actual number you are entering.

1 Like

guix--

Well, that works! Keypad input works just as I intended. Many thanks!

I'm at a loss, though, as to the reason why adding the if from '0' to '9' clause made the tapRelayStr.toInt statement play nice. As far as I could tell, there already were no characters outside that range, and I trimmed off any leading or trailing blanks or nulls. What am I missing here?

David--

Okay, I didn't know getKey() did anything at all unless there was a pending keypad event. I guess tapRelayStr.trim() doesn't strip off the 0 values? If not, I guess I can test for them if needed with the .length() function? Many thanks!

--Todd

guix--

I think david_2018's point just sank in. The if clause you suggested kept any zero characters returned from keypad.getKey() from getting added to tapRelayStr (and therefore causing tapRelayStr.toInt() to return 0 from a string that doesn't begin with a number character). Is that right?

Thanks again!

Todd

Yes but the real solution is to follow @jremington's advice to not use Strings.. :slight_smile:

I had no idea Strings could contain multiple NULL characters, it's a strange behavior..

jremington--

Regarding atoi(), is it better to use than toInt() because atoi() doesn't care about leading whitespace (including zero characters)? And also because trim() doesn't remove leading zero characters?

Thanks!

--Todd

Don't confuse zero character and NULL character

If you didn't press any key for 4 seconds then you pressed key 7, your String contained this : \0\0\0\07, so trim and toInt stopped right at the beginning of the String, because those functions saw a \0 and that is (normally) the end of String, as well as the end of a C-string (null-terminated char array), hence why toInt returned 0 and not 7.

The fact that you can print the String and it shows ____7 ( _ being blank spaces that can't be displayed here ) is strange to me, I would expect the String to be printed as empty.

And atoi works with null-terminated char arrays, not with Strings

How would those even get into your keypad output buffer?

If you use some common sense in writing the code for inputting numbers or text strings from the keypad, this is not a problem.

I'm told that when getKey() (from the keypad.h library) is called it returns a NULL if there's no keypad input, so each time through my while loop with no input a NULL is added to the string I built using concat(). It shouldn't do that, but there you have it. @guix's solution was to use an if statement to keep all characters except 0-9 from being appended to the string. I will be looking into using c-strings instead of Arduino's String class for future projects, but in this particular case that would not have helped.

As for common sense in writing code, if there isn't a library header for that I can include (i.e., CommonSense.h) then I'm screwed. Anyway, common sense won't help me unless it's also employed by the writers of the libraries I use.

If a null is input, there is no reason to add it to the C-string, unless you intend to terminate the C-string at that point and move on to some other action, like printing the string.

If you don't know how to avoid adding the null, post the code as it now stands and forum members can show you how.

Thanks. As you can see earlier in the thread, @guix already suggested using a conditional to keep everything except 0-9 out of the string. Had I know about the silly NULLs, I would have figured that out myself.

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.