0

I have a buffer of random characters streaming into my Arduino from a XBee module. I want to extract the first integer that it sees (will be <= 3-digit int if that makes a difference), then move on with that number and stop looking at the rest of the incoming characters.

For reference, I'm trying to use this as part of a 2-way handshake with a node.js server that doesn't get messed up when other Arduinos are also attempting to handshake or are already connected and sending data.

I think I have a way that might work to extract an int, but it seems really ugly, so I'm thinking there must be a much shorter/cleaner way to go about this. Here's my very long code to do something that's probably pretty simple:

String intString = "";
int intStart = 0;
for (int i = 0; i < msg.length(); i++) {
    while (intStart != 2) {
        if (intStart == 0) {
            if ((msg[i] == '0') || (msg[i] == '1') || (msg[i] == '2') || 
                    (msg[i] == '3') || (msg[i] == '4') || (msg[i] == '5') || 
                    (msg[i] == '6') || (msg[i] == '7') || (msg[i] == '8') || 
                    (msg[i] == '9')) {
                        intString += msg[i];
                        intStart = 1;
                 }
        }
        // previous int, next is still int
        if (intStart == 1) {
            if ((msg[i] == '0') || (msg[i] == '1') || (msg[i] == '2') || 
                    (msg[i] == '3') || (msg[i] == '4') || (msg[i] == '5') || 
                    (msg[i] == '6') || (msg[i] == '7') || (msg[i] == '8') || 
                    (msg[i] == '9')) {
                        intString += msg[i];
                        intStart = 1;
                 }
        }
        // previous int, next is not int
        else if ((msg[i] != '0') && (msg[i] != '1') && (msg[i] != '2') && 
                         (msg[i] != '3') && (msg[i] != '4') && (msg[i] == '5') && 
                         (msg[i] != '6') && (msg[i] != '7') && (msg[i] == '8') && 
                         (msg[i] != '9')) {
                             intStart = 2;
        }
    } 
}
int number = intString.toInt();
Serial.println(number);

Any suggestions/advice is greatly appreciated.

Austin
  • 6,921
  • 12
  • 73
  • 138
  • atoi() works in arduino, have you tried it, perhaps you may have to transfer the string to a char buff before using it. – seccpur Sep 25 '16 at 05:26

4 Answers4

3

Rather than compare against every number from 0 to 9, use the standard C function isdigit().

John Zwinck
  • 239,568
  • 38
  • 324
  • 436
  • Thanks that definitely shortens it up quite a bit. I still can't help but think the thing I'm trying to do with `intStart` might already be built in functionality in some way as well? – Austin Sep 25 '16 at 04:43
  • 1
    @Jake would you be so kind to apply the suggested change to the text of your question? The way it is, it pounds on my sole working neuron so badly that I'm not able to distinguish what is the intended functionality of your code (vs the implemented one). Thanks – Adrian Colomitchi Sep 25 '16 at 05:16
2
String intString = "";
int intStart = 0;
for (int i = 0; i < msg.length(); i++) {
    while (intStart != 2) {
        if (intStart == 0) {
            if (isdigit(msg[i])){
                        intString += msg[i];
                        intStart = 1;
                 }
        }
        // previous int, next is still int
        if (intStart == 1) {
            if (isdigit(msg[i])) {
                        intString += msg[i];
                        intStart = 1;
                 }
        }
        // previous int, next is not int
        else if ( isdigit(msg[i]) ) {
                             intStart = 2;
        }
    } 
}

"Rubber duck debugging":

Let's assume the first char of the msg is a digit:

  1. set intStart to 0
  2. take the first char of the msg
  3. while intStart is not yet 2
  4. if intStart is 0 (it is, we haven't adjusted it) and the first char of the msg is digit (we assumed it is), then append the first char to intString and make intStart = 1
  5. if intStart == 1 (it is, we set it at the prev step) and the first char of the msg is digit (it is still the first, i didn't change), then append the first char to intString (great, now I have it twice) and set intStart=1 (hey, intStart didn't change). Else... well, we can ignore else, we are in the good conditions for then
  6. so back to the step 3, with the intStart==1 and i still 0 and the first char of the msg still a digit.

Should I continue or are you able to do it?

In essence, with the first char of the msg a digit, you'll never get out from while (intStart != 2) until you run out of heap-space due to intString growing by repeating the same fisrt char all over.

Is that what you want?

Is it so hard to explain this to your rubber duck before asking SO?
(yes, I understand, Arduino doesn't have a debugger, but you still can use Serial.print)

Adrian Colomitchi
  • 3,974
  • 1
  • 14
  • 23
1

[Update on the comments]

Sorry if I was unclear, but it doesn't necessarily start with an integer, the integer could be in the middle of the char buffer.

The first sequence of digits in the char buffer of any length (really doesn't have to be restricted to max 3-digit, only if it makes it easier)

So, before stating to collect, we just need to position ourselves on the first digit of the string buffer

int startScan=0;
// no body for the cycle, everything works just from 
// the exit condition and increment
for(
  ; 
  startScan < msg.length() && ! isdigit(msg[i]); // as long as it's not a digit
  startScan++
);

// from this position, start collecting for as long as we have digits
int intValue=0;
String intString;
for(; startScan < msg.length() && isdigit(msg[startScan]); startScan++) {
  intString += msg[startScan]; // take it inside the string

  // careful with this one, it may overflow if too many digits
  intValue = intValue*10 + (msg[startScan]-'0');
}
// if we reached here with an empty intString, we didn't find any digits

If you don't need the intString, just the intValue, don;t use the intString - at most a bool hasDigits to init to false and set to true in place of intString += msg[startScan]; (to act as a signal for the 'no digits encountered' case).

If you don't need the intValue, just wipe out from the code anithing that uses it.


So, if my understating is correct, you have the following problem:

I have a String message which starts by at most 3 decimal digits and ends possibly with other info I don't need. I want that 'at most 3 digits' prefix transformed in an integer for me to use further

If this is you problem, then try this:

int intValue=0;
String intString;
int maxLookInto=(msg.length() > 3 ? 3 : msg.length()); // at most 3 digits
for(int i=0; i<maxLookInto && isdigit(msg[i]); i++) {
  // if we got here, we know msg[i] is still a digit, otherwise
  // we get out of cycle ealier
  intString += msg[i]; // take it inside the string
  intValue = intValue*10 + (msg[i]-'0'); // transforming in base 10 in an int
}
// Do what you like with either intString (textual representation of the
//  at-most-3-digits or with the same value converted already to a number
//  in intValue

If Arduino doesn't have the isdigit function available, you can implement your own like

int isdigit(char c) {
  // we are using ASCII encoding for characters, aren't we?
  return (c>='0' && c <='9');
}
Community
  • 1
  • 1
Adrian Colomitchi
  • 3,974
  • 1
  • 14
  • 23
  • Arduino has an `isDigit()` method. https://www.arduino.cc/en/Reference/CharacterAnalysis – Salix alba Sep 25 '16 at 06:11
  • Sorry if I was unclear, but it doesn't necessarily start with an integer, the integer could be in the middle of the char buffer. – Austin Sep 25 '16 at 06:36
  • @Jake and you do want the first sequence of digits in the char buffer with at most 3 char len? Or do you want the first sequence that is exactly 3 char len? Which one? – Adrian Colomitchi Sep 25 '16 at 06:38
  • The first sequence of digits in the char buffer of any length (really doesn't have to be restricted to max 3-digit, only if it makes it easier) – Austin Sep 25 '16 at 06:49
0

One way is to use the String object. This has a toInt method.

BTW there is an Arduino specific stack exchange. arduino.stackexchange.com

Community
  • 1
  • 1
Salix alba
  • 7,536
  • 2
  • 32
  • 38