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;
}
}
}
2 Answers 2
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
fornumber
- 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.
-
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– chux2020年04月11日 20:57:33 +00:00Commented 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– chux2020年04月11日 21:43:38 +00:00Commented 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 technicallyloop
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– Reinderien2020年04月12日 15:00:55 +00:00Commented 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– Reinderien2020年04月12日 15:01:19 +00:00Commented 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– Reinderien2020年04月12日 15:05:34 +00:00Commented Apr 12, 2020 at 15:05
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;
}
}
}
Explore related questions
See similar questions with these tags.