Numeric input from monitor

I’d like to get numbers from the serial monitor to use in a sketch. For now the object is to take a series of numbers (1-99), put them in a FIFO and compute a running average.

As I understand it, the keyboard input will arrive as char. I then need to place the digits into an array and then convert them into an int and finally move the int s into (and out of) the FIFO. I’m not concerned with filtering out non-numeric characters at this point.

I found this description of the stoi function and the IDE accepts an #include <string.h> command. This page does include examples but they’re using the string data type, not char.

a. Is there a place where the Arduino syntax for this function is described?

b. Can I even use a char array or am I required to use the string type?

So many questions. :confused:

easiest...

parseInt()

The difference between a character array and a string is that the string is a charcter array that is terminated with a NULL (\0).

The serial input basics thread may have useful information for you.

Keep in mind that String, as discussed here, refers to the String class (note the capital 'S'). A C string like that discussed by groundFungus, is built up from a char array and uses a lowercase 's' in discussions. A String object instantiated from the String class does provide an easy way to manipulate string data. However, most of us here prefer to use the char array for strings because the string library generally uses less memory. You can see most of the string library functions here.

The only possible problem with parseInt is that it uses a timeout which might delay the program.

sterretje:
The only possible problem with parseInt is that it uses a timeout which might delay the program.

if and only if you send data with no non-numeric end marker

i.e. Send like this and wait for the timeout (which can be modified using the aptly-named setTimeout() function):

-123

or send like this and experience no delay apart from the time it takes for the numeric string to arrive:

-123;

Thanks for that; I suspected that to be the case but didn't have an Arduino to test.

Solved it! The function I was looking for was atoi since it deals with small ‘s’ strings. I modified the second program (receive with an end marker) in the thread suggested by BulldogLowell. :slight_smile: As a bonus, I discovered that string.h isn’t needed to be able to use atoi.

The delay in replying was due to investigating all your links and experimenting/debugging - plus power was off 5 1/2 hours last night. :sob:

The modified code is included for reference and critique. I did not include a bunch of validation logic since the main object was to prove the fifo and averaging code.

// Example 2 - Receive with an end-marker
// http://forum.arduino.cc/index.php?topic=396450

// code modified for FIFO moving average test

const byte numChars = 6;
char receivedChars[numChars];   // an array to store the received data

boolean newData = false;
boolean newFifo = false;
int fifoData[10];//  rcv'd chars converted to ints and stored here

void setup() {
  Serial.begin(9600);
  Serial.println("<Arduino is ready>");
}

void loop() {
  recvWithEndMarker();
  showNewData();
  if (newFifo) {
    fifoInject();  // if entry rcv'd push it into FIFO
  }
  if (newFifo) {
    avgFifo();  // call to compute average of array contents
  }
  newFifo = false;
}

void recvWithEndMarker() {
  static byte ndx = 0;
  char endMarker = '\n';
  char rc;

  while (Serial.available() > 0 && newData == false) {
    rc = Serial.read();

    if (rc != endMarker) {
      receivedChars[ndx] = rc;
      ndx++;
      if (ndx >= numChars) {
        ndx = numChars - 1;
      }
    }
    else {
      receivedChars[ndx] = '\0'; // terminate the string
      ndx = 0;
      newData = true;
      newFifo = true;// needed because newData is reset in previous instruction
    }
  }
}

void showNewData() {  // guts removed to reduce clutter
  if (newData == true) {
    newData = false;
  }
}

void fifoInject() {  // function definition
  int i; // for loop index counter
  for (i = 9; i > 0; i--) {   // shift all data up one position
    fifoData[i] = fifoData[i - 1];    // data at highest array position [9] is lost
  }
  fifoData[0] =  atoi(receivedChars); //  chars converted to int and enter fifo at bottom

  Serial.println("array contents");
  for (i = 0; i < 10 ; i++) {
    Serial.print(fifoData[i]);
    Serial.print("  ");

  }
  Serial.println();


}
void avgFifo() {
  int fifoSum = 0;
  int fifoAvg = 0;
  int divisor = 10;
  for (int i = 0; i < 10; i++) {
    fifoSum += fifoData[i];  // add up all the numbers in the array

    if (fifoData[i] == 0) {  // if a position is zero, remove it from the divisor
      divisor = divisor - 1;
    }
  }

  fifoAvg = (fifoSum / divisor);
  Serial.println();
  Serial.print("Average of array contents is ");
  Serial.println(fifoAvg);
  Serial.println();
  newFifo = false;  // reset flag to ready for next entry.

}

Thanks for the help!

dougp:
The delay in replying was due to investigating all your links and experimenting/debugging - plus power was off 5 1/2 hours last night. :sob:

Oh, OK. I thought maybe the dog ate your homework!
:smiling_imp:

good job on he code but you should be looking at a more functional approach relying less on blocking code.

Here is an example of a functional solution to your problem that does not block and doesn't rely on global variables (except that I did use a variable for the message length):

#define MAX_MESSAGE_LENGTH 6

void setup() 
{
  Serial.begin(9600);
  Serial.println("let's go!");
}

void loop() 
{
  if(char* newMessage = checkForNewMessage(Serial, '\n'))
  {
    Serial.println(atoi(newMessage));
  }
}

char* checkForNewMessage(Stream& stream, const char endMarker)
{
  static char incomingMessage[MAX_MESSAGE_LENGTH] = "";
  static byte idx = 0;
  if(stream.available())
  {
    incomingMessage[idx] = stream.read();
    if(incomingMessage[idx] == endMarker)
    {
      incomingMessage[idx] = '\0';
      idx = 0;
      return incomingMessage;
    }
    else
    {
      idx++;
      if(idx > MAX_MESSAGE_LENGTH - 1)
      {
        stream.println(F("{\"error\":\"message too long\"}"));  //you can send an error to sender here
        idx = 0;
        incomingMessage[idx] = '\0';
      }
    }
  }
  return NULL;
}

BulldogLowell:
Here is an example of a functional solution to your problem that does not block and doesn't rely on global variables (except that I did use a variable for the message length):

Thanks for the feedback. I don't think I'm ready for that yet. This is like going to Niagara Falls for a glass of water!

  if (newFifo) {
    fifoInject();  // if entry rcv'd push it into FIFO
  }
  if (newFifo) {
    avgFifo();  // call to compute average of array contents
  }

Good thing you don't have a bunch of things to do every time newFifo is true. The world would run out of curly braces.

By the way, how does newFifo differ from newData?

PaulS:
Good thing you don't have a bunch of things to do every time newFifo is true. The world would run out of curly braces.

I'm just using the ones discarded by more experienced coders! ;D

PaulS:
By the way, how does newFifo differ from newData?

It is not reset within showNewData.

dougp:
I then need to place the digits into an array and then convert them into an int and finally move the int s into (and out of) the FIFO.

No need to put the chars into an array, just compute the int on the fly - for instance reading '123'
would first set the int to 1, then 12, then 123 (*), and if the next character isn't a digit, you know the int
is ready to place in the FIFO - you then reset the int to 0. To handle sign character you need a flag
variable to record if the number had a '-' at the front and negate the int when its complete.

You might need a long rather than an int if the values are large.

(*)

  intvalue = intvalue * 10 + charval - '0' ;