This is an invitation for comments by the those who feel experienced enough (you know who you are )
Sometime(often) people ask here how to use the Serial input to give simple commands, possibly with some numeric argument to their sketch. The thread then revolves around ASCII <-> Integer conversion and what a "terminator" is. For my own use I have evolved the simple syntax: first the number is entered (ASCIIwise) in the Serial input and then terminated by a command character. The code accumulates so it does not block. Sending the ASCII sequence "67x3yp" would be interpreted as three commands: Command X with argument 67, command Y with argument 3 and Command P with argument 0.
Until recently I usually cut-n-paste this codesnippet in my various sketches, I have now rewritten it as a very small library for the community.
The nnnC.h module
/*--------------------------------------------------------------------------------*\
| nnnC - Number Command input |
| |
| Read serial input of simple argument values in format "nnnC" |
| nnn is a series of (ASCII) digits, with optional sign (& decimalpoint for nnnF)|
| C is a single character that is the command or argument name terminates string |
| C is converted to uppercase. |
| Call "fffC" instead of "nnnC" for float value (See prototype declaration below) |
| |
| The call is non-blocking, so command interpretation is done in its own "thread", |
| meaning : You can call it as often as you want in the loop() |
| Only when it returns true will the the arguments C and V contain valid values. |
| |
| Example use: |
| static char C ; static int V ; |
| if ( nnnC( &C, &V) ) { |
| Serial.print("Command ");Serial.print(C); |
| Serial.print(" : ");Serial.print(V,DEC); |
| } |
\*--------------------------------------------------------------------------------*/
/*-------------------------------------------------------------------------*\
| Not-a-bug; known limitations: |
| Illegal numeric syntax, how the "-", "+" and "." are placed, are accepted |
| If you use both nnnC and fffC only "switch" between them after a command |
| Note the parameters must be global or static |
| the nnnC only takes int - maximum is +/-32767 as usual |
| (But it is easy - just change "int" to "long" and you can do longs :-) ) |
\*-------------------------------------------------------------------------*/
#ifndef _nnnC
#define _nnnC
#if ARDUINO < 100
#include <WProgram.h>
#else
#include <Arduino.h>
#endif
boolean nnnC( char *, int * ) ;
boolean fffC( char *, float * ) ;
#endif
The nnnC.cpp module
/*--------------------------------------------------------------------------------*\
| nnnC - Number Command input |
| |
| Read serial input of simple argument values in format "nnnC" |
| nnn is a series of (ASCII) digits, with optional sign |
| C is a single character that is the command or argument name terminates string |
| C is converted to uppercase. |
| |
| The call is non-blocking, so command interpretation is done in its own "thread", |
| meaning : You can call it as often as you want in the loop() |
| Only when it returns true will the the arguments C and V contain valid values. |
| |
| Example use: |
| static char C ; static int V ; |
| if ( nnnC( &C, &V) ) { |
| Serial.print("Command ");Serial.print(C); |
| Serial.print(" : ");Serial.print(V,DEC); |
| } |
| Use "fffC" instead of "nnnC" for float value |
\*--------------------------------------------------------------------------------*/
#include "nnnC.h"
// Integer version
// NB: The "." acts a command character, not part of a number.
boolean nnnC (char * Cmd, int * Val) {
static boolean Done = true ;
static boolean Negative = false ;
if ( Done ) { // if a valid value has been returned before
Negative = Done = false ; // reset for new command
*Val = 0 ;
}
while ( Serial.available()>0 ) { // interpret all serial bytes ...
*Cmd = Serial.read() ;
if (*Cmd == '-') // negative value
Negative = true ; // set flag
else if ('0' <= *Cmd && *Cmd <= '9') // valid digit
*Val = (*Val) * 10 + (*Cmd) - '0' ; // accumulate input
else if (*Cmd == '+') // A "+" sign
; // ignore
else { // It is a non-numeric character, i.e. the command.
if ('a' <= *Cmd && *Cmd <= 'z') *Cmd &= B01011111 ; // forces uppercase (for 7bit ASCII)
if ( Negative ) *Val = -*Val ; // Adjust for negative value
Done = true ; // note we are done
return true ; // and exit
}
}
return false ;
}
// Float version
boolean fffC (char * Cmd, float * Val) {
static boolean Done = true ;
static boolean Negative ;
static byte Fraction ;
if ( Done ) { // reset for new interpretation.
Negative = Done = false ;
*Val = 0 ; Fraction = 0 ;
}
while ( Serial.available()>0 ) { // interpret all serial bytes ...
*Cmd = Serial.read() ;
if (*Cmd == '-') { // negative value
Negative = true ; // set flag
return false ; // and exit
}
if (*Cmd == '.') { // decimal point
Fraction = 1 ; // start counting, and thus flag decimal seen.
return false ; // and exit
}
if ('0' <= *Cmd && *Cmd <= '9') { // valid digit
*Val = *Val * 10 + *Cmd - '0' ; // accumulate input
if ( Fraction > 0 ) Fraction++ ; // adjusting for after decimal
return false ; // and exit
}
if (*Cmd == '+') // Accept and ignore a "+" sign
return false ;
// It is a non-numeric character - treated as command character
if ('a' <= *Cmd && *Cmd <= 'z') *Cmd &= B01011111 ; // forces uppercase (for 7bit ASCII)
if ( Negative ) *Val = -*Val ; //adjust for negive value
for( ; Fraction > 0 ; Fraction-- ) *Val /= 10.0 ; // divide down decimal
Done = true ; // note we are done
return true ;
}
}
Note: This is intended as a very lightweight routine, when a simple input commandparser is needed.
(Edit: fixed a bug on float fractions, cleaned up some comments, added some brackets for clarity)