2
\$\begingroup\$

Description

I wanted to understand how integers can be written and stored inside variables without the use of Serial.parseInt(). I couldn't find any code examples about this. Perhaps this is the right way to do it, although it doesn't detect and block letters and special characters. Please leave some feedback on ways to improve it. Thanks in advance! :)

void setup()
{
  Serial.begin(9600); // Opens serial port, sets data rate to 9600 bps
  Serial.println(F("Serial port opened!"));
}

void loop()
{
  byte received;
  static byte counter = 0;
  static uint32_t number = 0; // Store value from 0 to 4294967295

  while (Serial.available()) // Read data in serial receive buffer
  {
    received = Serial.read(); // Store received byte
    if (received != 10) // Terminate if newline char detected
    {
      if (counter != 0) // Put number in its correct position (base-10 system)
      {
        number *= 10;
        number += (received - '0');
      }
      else
      {
        number = (received - '0');
      }
      counter++;
    }
    else
    {
      Serial.println(number); // Print value inside number and reset
      counter = 0;
    }
  }
}
\$\endgroup\$

2 Answers 2

3
\$\begingroup\$

Character literals

While this is technically correct:

if (received != 10) // Terminate if newline char detected

you're better off writing

if (received != '\n')

Much of embedded electronics assumes ASCII encoding, and this is no exception, but you're still better off using the symbol instead of the code.

IO management

You loop while serial I/O is available:

while (Serial.available())

But what if there is a pause in the availability of bytes in the middle of your integer? Your code will not do the right thing. Instead:

uint32_t number = 0;
while (true) {
  int received = Serial.read();
  if (received == -1) continue;
  if (received == '\n') break;
  // ...
}
Serial.println(number);
  • Do not use static for number
  • Don't need a counter
  • Keep looping if no data are available
  • Ensure that one execution of loop maps to one full output integer

Condition in loop

If you are using the ATmega328P, it has a dedicated MUL instruction that only takes two cycles. It's more complex and expensive to have your if (counter != 0) than it is to simply unconditionally multiply-and-add.

\$\endgroup\$
12
  • 1
    \$\begingroup\$ Re: "Do not use static for number Don't need a counter" ==> OP looks like loop() is a service function, not always called when data first arrives, but as it arrives. OP's approach is OK in that case. A non-global alternative would pass in the state data. \$\endgroup\$
    – chux
    Commented Apr 11, 2020 at 20:57
  • 1
    \$\begingroup\$ Minor: to be technically correct, '\n' is an integer constant, not a character literal. C does define string literals and compound literals, but no other literals. Perhaps you are thinking of another language? \$\endgroup\$
    – chux
    Commented Apr 11, 2020 at 21:43
  • 1
    \$\begingroup\$ Re. statics - it's a common micro-optimization in embedded code to make a function non-reentrant by switching local variables to static; and technically loop is a "service" function that's called by the Arduino firmware and does not need to be re-entrant. However, such an optimization is premature, and statics are a bad idea to invoke unless thoroughly justified. \$\endgroup\$
    – Reinderien
    Commented Apr 12, 2020 at 15:00
  • 1
    \$\begingroup\$ Also, there is no correlation between the arrival of data and the timing of a loop call. \$\endgroup\$
    – Reinderien
    Commented Apr 12, 2020 at 15:01
  • 1
    \$\begingroup\$ Also also, to be technically correct (the best type of correct), this is not C, nor is it even C++ - it's "Wiring", which is a dialect of C++. \$\endgroup\$
    – Reinderien
    Commented Apr 12, 2020 at 15:05
2
\$\begingroup\$

For more robust code to detect non-numeric input, overflow and start-up phasing, consider a state machine.

typedef struct {
  int counter;  // <0:indeterminate, 0:spacing, >0:digits; 
  uint32_t number;
} loop_state;

// Quietly drop data in 3 cases:
// 1) Overflow
// 2) Non-numeric
// 3) When state is indeterminate
void loop(loop_state *state) {
  while (Serial.available()) {
    byte received = Serial.read(); // Store received byte
    if (received >= '0' || received <= '9') { // or isdigit((unsigned char) received)
      if (state->counter >= 0) {
        state->counter++;
        unsigned digit = received - '0';
        if (state->number >= UINT32_MAX / 10
            && (state->number > UINT32_MAX / 10 || digit > UINT32_MAX % 10)) {
          // overflow
          state->counter = -1;
          continue;
        }
        state->number = state->number * 10 + digit;
      }
    } else if (isspace((unsigned char) received)) {
      if (state->counter == 1) {
        Serial.println(state->number);
      }
      state->number = 0;
      state->counter = 0;
    } else {
      state->number = 0;
      state->counter = -1;
    }
  }
}
\$\endgroup\$
0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.