Arduino: Sending and Receiving Multi-Digit Integers

Arduino's Basic Serial Monitor

When Serial data is transmitted to an Arduino, it is sent one byte at a time.  Even though you might type “123” in the Serial Monitor, that’s not quite what is sent.  Instead the bytes “1” then “2” then “3” are sent.  Once received into a buffer on the Arduino, these individual bytes need to be reassembled into something useful. The Arduino IDE provides Serial.readBytes() as one option.  However, if you aren’t sure how many digits you are going to receive, this may not work.  So another option is to use Serial.readBytesUntil() like so:

void setup() {
 Serial.begin(9600);
}

void loop() {
 char buffer[] = {' ',' ',' ',' ',' ',' ',' '}; // Receive up to 7 bytes
 while (!Serial.available()); // Wait for characters
 Serial.readBytesUntil('n', buffer, 7);
 int incomingValue = atoi(buffer);
 Serial.println(incomingValue);
}

There are some limitations to using that method.  For example, what if you want to keep your program active while waiting for Serial input to finish?  Or if you want to ignore certain characters (like a comma or decimal). The following code gives you much more control over receiving multiple digit integers like 99 or 100 or 1000.  There is also an explanation of how this code works, so you can modify it for your sketch.

Code Example

void setup() {
  Serial.begin(9600);
}

unsigned int integerValue=0;  // Max value is 65535
char incomingByte;

void loop() {
  if (Serial.available() > 0) {   // something came across serial
    integerValue = 0;		  // throw away previous integerValue
    while(1) {			  // force into a loop until 'n' is received
      incomingByte = Serial.read();
      if (incomingByte == '\n') break;   // exit the while(1), we're done receiving
      if (incomingByte == -1) continue;  // if no characters are in the buffer read() returns -1
      integerValue *= 10;  // shift left 1 decimal place
      // convert ASCII to integer, add, and shift left 1 decimal place
      integerValue = ((incomingByte - 48) + integerValue);
    }
    Serial.println(integerValue);   // Do something with the value
  }
}

Caution

Enabling “Newline”

Two points of caution when using this code:

  1. You absolutely must enable “Newline” (only) termination in the Serial Monitor (or your favorite serial terminal program).  Otherwise, the while(1) loop will never exit.  See below for a screen shot on how to do this in the Arduino IDE.
  2. The maximum value you can send is 65535. There is no checking to verify if this will overflow.

If you have short routines, like a toggling LED / Heartbeat indicator, you can call them within the while(1) loop to keep your program running while waiting for input to finish.

Why does this work?

There are 4 lines of code that make this work.  The first block are:

while(1) {
//...
if (incomingByte == '\n') break;
if (incomingByte == -1) continue;

The while(1) will cause the Arduino to wait until the character ‘n’ is recevied.  This is “Newline”, set by the Serial Monitor.  The second is the important fact that Serial.read() will return -1 if no characters are available.  This keeps the program from having to constantly check Serial.available(). Math can be fun and may make the next couple of lines look like magic, but let’s break them down.

integerValue *= 10;
integerValue = ((incomingByte - 48) + integerValue)

When a decimal number (base 10) is multiplied by 10, all of the digits shift left once. So each time a digit is entered, the value of integerValue is shifted to the left and the new digit is added. Cool, eh? Why do the subtraction? Because… the variable ‘incomingByte’ has an ASCII value in it. The character “0” has an ASCII value of 48. (See this ASCII Chart for more information.) So by subtracting 48 from the incomingByte, we get the integer value of that ASCII character. [Note: This is why you are limited to “65535”. This is the maximum value that can be held by an unsigned int.]

Conclusion

Either the Serial.readUntil() method or the code above can be used to receive big integers.  They both have advantages and potential disadvantages.  It just depends on what you need to do in your code.  Keep in mind, both methods require having the IDE set the line termination to ‘n’ (newline).

Question: How else do you want to capture serial data? You can leave a comment by clicking here.

Long comments, URLs, and code tend to get flagged for spam moderation. No need to resubmit.

Leave a comment

40 thoughts on “Arduino: Sending and Receiving Multi-Digit Integers

  1. Randomly stumbled on your website after doing a bit of Google searching for some Arduino-related information. This was pretty helpful. Thanks for this! I’ll definitely check out some of the website.

  2. hello dears,
    i have a small bugg with getting a value from serial, it s an ascii code ( ex: 0x05 0x00 0x00 0x00), all i need is the value 5 and the i want to get rid of the other three bytes, so i wrote an arduino code as following :

    void setup() {
      Serial.begin(9600);
      pinMode(53,OUTPUT);
    }
    
    void loop() { 
      while (Serial.available() == 0);
      byte n0 = Serial.read();
      String mystring = String (n0, DEC);
      if (n0 == 5 ) 
        digitalWrite(53,HIGH);
      else 
        digitalWrite(53,LOW); 
    
      { 
       Serial.println(n0);
      }
    }
    

    but the n0 returned is :

    Serial.print(n0); => 5000

    or

    Serial.println(n0); =>

    5
    0
    0
    0

    my question: what can we do to Keep only the value ( 5) and get rid of the other bytes,
    i.e : what what should we do to get a new variabl with the val 5 ??

    thank you very much.

    • (Now reformatted) lines 14-16 execute every time loop does, which is everytime there is a byte in the serial buffer. So it is going to print exactly what you receive. If the sequence is always 4 bytes, then just ignore the other three.

      There is no reason to use “String” in this code. The if-statement on line 9 could just be if (n0 == 0x05).

      • dear James,
        thanks for the claification, but the “0x05” i mentioned is just an exemple, it can be any other value, but the last three zeros are ruining the aplication, in another way is there a possibility to split the code and get a variable containing only the first byte/digit ??

  3. i want the work some think like that but due to weak programmer i am unable to do so.
    i am trying to call function from single push button and if i press button twice(suppose ) i want to be in configuartion setting where i can add some value manually from serial monitor like apn,username,password and url extra so please if can help in this

  4. Thank you!
    I have used your code to read csv values from an SD card, it worked perfectly after I figured out that I was getting a Carriage Return (\r) at the end of the line and adjusted the code by adding the following lines:
    if (incomingByte == ‘,’) break;
    if (incomingByte == ‘\r’) break;

    Regards,
    Peter

  5. I have project to revice data sensors in serial data from an other arduino as data logger, the data logger is sent packet data with start byte (*) and end byte (#)
    The data example like this:

    *4/10/2015,1:58:01,2,30,60,3000,1.2,360.0#
    *4/10/2015,1:58:02,4,40,160,3010,1.3,0.10#
    *4/10/2015,1:58:03,6,50,300,3520,1.4,1.00#

    How do i receive packet correctly?
    Thank You James

  6. Hey James
    This was really useful. I have an another question
    suppose I enter N-bit number in serial monitor then how can i spilt it into digits and use it??
    Thank you

      • Basically, I want to enter a 4 digit no then my system will check whether it is a 4 digit number it is. then it will assign each digit to a variable.

        if (Serial.available() >= 4)
        need_message = false;
        type = Serial.read();
        parameter = Serial.read()
        n = Serial.read();
        l = Serial.read();

        I tried this way, but I m getting some ASCII and some random values.

  7. It is a helpful tutorial, appreciated it.
    May I have another question about the serial message sending.
    I wanna use an analog input to measure DC voltage(not only 5V) and send the value to serial immediately.
    if it is a continue voltage, the value sending is correct.
    Currently, there is a chopping voltage like square wave, the frequency is around 500 Hz and duty cycle 50%, I want to measure it by analog input.
    I tried to print the value to serial in few seconds and drawing the value against time,
    it seems the sending rate(sample rate) is not fast enough, so the wave is not correct and not a period wave.
    Is there something wrong about the sending??

    • The amount of time it takes to transmit serial may be slowing down the sampling rate. Make sure you are using 115200 baud. If not fast enough, cut down the number of characters you are transmitting.

    • Working a little bit more, here’s the code. Bascailly, set a flag when the negative character is received. Then when the operation is done (newline or \n is received), invert the value.

      Don’t forget to change the type of variable to “int” for integerValue.

      void setup() {
        Serial.begin(9600);
      }
       
      int integerValue=0;
      bool negativeNumber=false; // track if number is negative
      char incomingByte;
       
      void loop() {
        if (Serial.available() > 0) {   // something came across serial
          integerValue = 0;         // throw away previous integerValue
          negativeNumber = false;  // reset for negative
          while(1) {            // force into a loop until 'n' is received
            incomingByte = Serial.read();
            if (incomingByte == '\n') break;   // exit the while(1), we're done receiving
            if (incomingByte == -1) continue;  // if no characters are in the buffer read() returns -1
            if (incomingByte == '-') {
              negativeNumber=true;
              continue;
            }
            integerValue *= 10;  // shift left 1 decimal place
            // convert ASCII to integer, add, and shift left 1 decimal place
            integerValue = ((incomingByte - 48) + integerValue);
          }
          if (negativeNumber)
            integerValue = -integerValue;
          Serial.println(integerValue);   // Do something with the value
        }
      }
      
  8. Thank you for this great article. I have a litlle issue: I tried to do specific actions when a specific string are typed in serial monitor. For exemple, I want a led to become HIGH if I type the word “on” in the serial monitor. The problem is that it turns HIGH whatever I type in serial monitor. I tried several codes but without sucess at all. For example, using your code I modified this line to:

    if (incomingByte == ‘on’ && incomingByte == ‘\n’) break;
    digitalWrite(led, HIGH);

    The led turns HIGH If I Type any string in serial monitor, not a specific string.

    Can you help me in this issue?

    Thank you in advance.

    Best regards

    • If you read your code outloud, you might understand your problem.

      if (incomingByte == ‘on’

      “on” is two characters, which means it is two bytes. You only receive one byte at a time. The easiest thing to do is make your “Commands” single characters.

  9. Hey

    I’m glad to run into your code. It’s much better to read a byte instead of trying to piece an integer number together from ascii digits like this guy does it here:

    http://combustory.com/wiki/index.php/Arduino_Communications

    Very strange that yesterday his code worked, today with the same settings it does not. I reseted, unplugged ardu all the settings are the same…

    One little correction for your article is that
    if (incomingByte == ‘n’) break;
    Should be
    if (incomingByte == ‘\n’) break; // exit the while(1), we’re done receiving

    I guess the engine of your blog removes the \ (ASCII 0134) character.

    • Thanks. I fixed the newline references. There’s some background process that runs which mangles code. I copy/paste/compile after posting, but at some point it gets mangled.

  10. Thanks a bunch for this article, it really helped me out. I didn’t know Serial.readBytesUntil() even existed.

  11. Hi there,

    I want to use something like Serial.readBytesUntil() for just 7 chars. but if the user inputs something larger than 7 chars I get something weird:

    first line: 1234567
    second line: 8934567

    How can I limit the function from re-writing the extra chars?

    • Either flush the input buffer before calling readBytesUntil() or don’t use it. I’m not a fan of the function because of its limited use such as cases like this. It is also probably a bad idea to rely on a human entering the correct number of characters. You are better off waiting for a delimiter like end of line.

  12. If you multiply integerValue by ten, after line 14 and before you add (incomingByte – 48) to it:

    ……

    13 if (incomingByte == ‘n’) break;
    14 if (incomingByte == -1) continue;
    15 integerValue *= 10;
    16 integerValue = ((incomingByte – 48) + integerValue);
    17 }
    18 Serial.println(integerValue);
    19 }
    20 }

    you don’t have to divide by 10 and you can use
    integers to 65535.This happens because every time you have a new digit you first shift the old integerValue, by multiplying by 10, and then you add the new integer which you may not need to shift because your could find ‘n’ and break the loop.

    Your code helped me a lot!Thank you!