Trouble with consistency using Serial.parseInt() with array

I am setting up a wireless gamepad to control my trench-bot (big picture). I am having trouble with consistency reading the button/joystick states into an array on the receiving end (little picture).

Hardware:
Wireless Gamepad Wirless_GamePad_V2.0_(SKU_DFR0182)-DFRobot
two Series 1 xbees
Arduino Mega with xbee shield SparkFun XBee Shield - WRL-12847 - SparkFun Electronics
an xbee Explorer for debugging SparkFun XBee Explorer USB - WRL-11812 - SparkFun Electronics

The gamepad is sending the state of the buttons and joysticks (read into an array) as a series of space-delimited integers, ending with a newline. Using the XBee explorer to read these values, all seems to work fine on the sending side. Here is the code uploaded to the gamepad:

int buttonState[17];
int joystick[4];
int AnalogButton[2];

void setup()
{
  Serial1.begin(9600); 
  InitIO();        
}

void InitIO(){ 
  for(int i = 0; i < 17; i++) pinMode(i, INPUT); 
}

unsigned long timer = 0;

void loop(){
  if(millis() - timer > 100){  // manage the updating freq of all the controlling information
    DataUpdate();  //read the button and joystick data
    printData();   //print the states
    timer = millis(); 
  }
}

void DataUpdate(){

  for(int i = 3; i < 17; i++)  buttonState[i] = digitalRead(i);
  buttonState[0] = analogRead(0);
  buttonState[1] = analogRead(1);
  buttonState[2] = 0;
  for(int i = 0; i < 4; i++)  joystick[i] = analogRead(i);
  for(int i = 4; i < 6; i++)  AnalogButton[i-4] = analogRead(i);

}

void printData(){

  for(int i = 0; i < 17; i++)  Serial1.print(buttonState[i]), Serial1.print(" ");
  for(int i = 2; i < 4; i++)  Serial1.print(joystick[i]), Serial1.print(" ");
  for(int i = 0; i < 2; i++)  Serial1.print(AnalogButton[i]), Serial1.print(" ");
  Serial1.println("");

}

Here is the code I have uploaded to the Arduino Mega (receiving end):

int Joystick[21];

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

void loop()
{  
  if(Serial.available() > 60) {

   for(int i = 0; i < 21; i++) {
      Joystick[i] = Serial.parseInt();
    }
    Serial.println(Joystick[0]);
    Serial.println(Joystick[20]);

    if (Joystick[1] == 0) {
      Serial.print("JoystickButton");  
      delay (10);   
    }
    Serial.flush();
  }
}

What I would like to do is to read the appropriate values into the Joystick[] array for use in the main program. The Serial.parseInt() function seems like a great option for this. The problem I am having is that the values do not always populate the appropriate array position. Sometimes it does, usually on initial start-up, but if I turn off the Joystick and turn it back on, then the "starting" value read into the array can be different.

The things I have tried include experimenting with the use of If(Serial.available()... and While(Serial.available()... and the placement of the Serial.flush() commands. Nick Gammon has provided some great tips/tricks for reading incoming serial data Gammon Forum : Electronics : Microprocessors : How to process incoming serial data without blocking, but none use the Serial.parseInt() funtion. It seems that the newline ( '/n' ) sent at the end of the package could be useful, but I haven't been able to figure out how. Serial.parseInt() ignores non-integer values, and mixing Serial.parseInt() with Serial.read() is where my brain starts locking up.

If you could look at the code and let me know if there are any nifty ways to consistently read the values into the appropriate array positions that would be great. I am not bent on using Serial.parseInt() if it is not the best option.

Thanks for any help.

Geologist

read into an array

You don't read into an array. You write into, or read from, an array.

  InitIO();        
}

void InitIO(){ 
  for(int i = 0; i < 17; i++) pinMode(i, INPUT); 
}

Was this function really necessary? Do you REALLY want to be fucking with the hardware serial port pins? I didn't think so.

    timer = millis();

timer is a crappy name. lastTime would be better. millis() is not a timer.

  if(Serial.available() > 60) {

You have no idea how many characters will be in the string sent. Waiting for the serial buffer to be almost full is not a good idea. The sender sends an end of packet marker, and delimiters between items. Read the data as it arrives, storing it in an array, until the end of packet marker arrives. Then, parse and use the data.

    Serial.flush();

Why? 99.9% of all calls to this function are done so in error. Convince me that you are a 0.01%er.

Ouch. I figured you would beat me up a little. In fact, deep down, i think i was hoping for it. :.

PaulS:

  InitIO();        

}

void InitIO(){
  for(int i = 0; i < 17; i++) pinMode(i, INPUT);
}



Was this function really necessary? Do you REALLY want to be fucking with the hardware serial port pins? I didn't think so.

The Init IO () function is part of the code that came with the Gamepad. The pin #s assigned are unique to the gamepad, so no fucking should be occuring.

PaulS:

  if(Serial.available() > 60) {

You have no idea how many characters will be in the string sent. Waiting for the serial buffer to be almost full is not a good idea.

There are up to 60 bytes coming through in the data string, which is why I waited until there was at least that much inthe buffer. I didn't realize how small the buffer was though (64 bytes).

The sender sends an end of packet marker, and delimiters between items. Read the data as it arrives, storing it in an array, until the end of packet marker arrives. Then, parse and use the data.

Ahh I see, I'll work on that. Back to Nick Gammon's examples. So the Serial.parseInt() is not useful in what I am trying to do?

Thanks

PaulS:

    Serial.flush();

Why? 99.9% of all calls to this function are done so in error. Convince me that you are a 0.01%er.

Oh to be a 0.01%er

The pin #s assigned are unique to the gamepad, so no fucking should be occuring.

The pin number are unique to the Arduino, NOT the game pad, so you ARE f**king with the hardware serial pins.

There are up to 60 bytes coming through in the data string

So, if you send 55 one time, you don't read them until you send the next set, which could result in buffer overflow. As I said, not a good idea.

Hi.
Have you read the last bit of code on the page you linked?

// Example of sending numbers by Serial
// Author: Nick Gammon
// Date: 31 March 2012

const char startOfNumberDelimiter = '<';
const char endOfNumberDelimiter   = '>';

void setup ()
  { 
  srand (42);
  Serial.begin (115200);
  } // end of setup
  
void loop ()
  {
  Serial.println ("Starting ...");
  for (int i = 0; i < 10; i++)
    {
    Serial.print (startOfNumberDelimiter);    
    Serial.print (rand ());    // send the number
    Serial.print (endOfNumberDelimiter);  
    Serial.println ();
    }  // end of for

  delay (5000);
  }  // end of loop

If you were to use a start delimiter and end delimiter for each of the types of data you'll receive, you can adjust the code on the receiving end accordingly (last piece of code on the page you linked)

EG instead of inserting a space between the 3 kinds of data, you could put parentheses around them {} [] and <> for example.
The code on the receiving end can be done with just if(serial.available()) and checking whether the received code is:

  • Beginning parentheses? -> Set a random variable (typeOfData) to 0, 1 or 2 (indicating which type of data it is) and set the variable Data to 0.

  • A number? -> multiply the variable Data with 10 and add the received number to it.

  • End parentheses? -> Write the number Data to an array in the TypeOfData position. If TypeOfData is 2, you should have a complete array with all 3 kinds of data you want, and you could do the stuff you want with that data.

  • Saw your post on freelancer. :wink: Just trying to help.

  • EDIT: I checked your code more thoroughly, and I see now that you are getting multiple input values. Oops. No problem, you could still do it the same way, but with checking for spaces and counting the amount of spaces checked. I don't think you'll need ParseInt if you just read one character out at a time.

Have you tried using serialEvent() ? It's like a built-in interrupt function for serial input. It has worked really well for me. See here:

You can convert a char to an int like so:

char character = 'x';
int number = character - '0';

This is guaranteed to work by the C standard.

pearldragoness:
'{}' might not be a good choice because of an Arduino IDE bug:
http://forum.arduino.cc/index.php?topic=218292.msg1594502#msg1594502

swmcdonnell:
Have you tried using serialEvent() ? It's like a built-in interrupt function for serial input.[/url]

I have not tried serialEvent(), though I will give it a try tonight. I'm still playing with the Serial.read() and Serial.parseInt() combination with no luck yet. Later, I'll post what I was trying last night. Learning slowly. The interrupt nature of the serialEvent() function seems a little tricky in that I will need to make sure it doesn't "interrupt" other functions. Sounds like appropriate placement of interrupts() and nointerrupts() would be the ticket.

thanks for all the help so far.

The interrupt nature of the serialEvent() function seems a little tricky

serialEvent() does NOT interrupt anything. The function is called at the end of loop() IF there is any serial data to deal with. It adds NOTHING to an application that is already dealing with serial data properly. OP, don't bother with serialEvent().

Your code looks rather strange to me.

I don't understand how you expect to receive 60+ serial characters and then get 21 integers out of it.

What stops your sequence of integers and blanks from being more than 60 characters long ?

My suggestions:

Send some kind of synchronising character at the start and end of your sequence.

Lose the irrelevant flush()

Lose the bogus pinmode setting.

Read all the serial characters, whenever they are available, into a separate char array, which can be longer than the 64-byte buffer which the serial port has.

When you get the end-of-sequence synchronising character, then try parsing your 21 ints.

When you get the start-of-sequence synchronising character, throw away any data you have, and start filling the input char buffer again.

PaulS:
OP, don't bother with serialEvent().

Thanks for heading that off for me PaulS

michinyon:
I don't understand how you expect to receive 60+ serial characters and then get 21 integers out of it.

My understanding was that Serial.available() returns the # of bytes in the buffer. The 60 refers to the max # of bytes that would make up my 21 integers (along with the spaces). Below is the output of my gamepad as read from the IDE:

1023 1023 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 519 513 501 507

55 bytes and 21 integers. yes?

What stops your sequence of integers and blanks from being more than 60 characters long ?

If all my analog integers were maxed at 1028 then it would equal 60.

Send some kind of synchronising character at the start and end of your sequence.

I'm working with that concept. Curious if it is necessary to have one at the start AND end, considering that they are essentially the same position in the string. (?)

Lose the bogus pinmode setting.

It came with the controller. PaulS indicated it was a problem, too. Is this better? I changed the pin #s to leave the serial pins alone. Or am I missing something else?

void InitIO(){ 
  for(int i = 3; i < 17; i++) pinMode(i, INPUT); 
}

Read all the serial characters, whenever they are available, into a separate char array, which can be longer than the 64-byte buffer which the serial port has.

When you get the end-of-sequence synchronising character, then try parsing your 21 ints.

When you get the start-of-sequence synchronising character, throw away any data you have, and start filling the input char buffer again.

This is what I am working on now. I need to learn more about parsing.

Here is the code I put together the other night. I'm sure it's probably garbage, but it seemed like a good idea at the time. Interestingly, my computer would come to a stand still if I let this code run too long with the IDE open. Not sure what that was all about. Before bogging my computer this code would sometimes work, but isn't consistent. For my own learning, can someone comment on what I'm not understanding here?

int Joystick[21];

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

void loop()
{  
  if(Serial.available()) {

    char incomingByte = Serial.read();

    if(incomingByte == '<'){
      for(int i = 0; i < 21; i++) {
        Joystick[i] = Serial.parseInt();
      }
      Serial.println(Joystick[0]);
      Serial.println(Joystick[20]);
      delay(300);
    }
  }


  if (Joystick[1] == 0) {
    Serial.print("JoystickButton");  
    delay (100);   
  }
}

I'll keep plugging away. I'm learning a lot and feel like I'm getting closer. Parsing.....

Sweeet! I believe I have bridged a gap on this. This code seems to be working fairly well based on what I'm seeing on the IDE. I need to test it still, and I'm reading a lot of references to the importance of error handling, which I have not looked into yet.

Thank you Nick Gammon for the code examples that I bastardized. Let me know if you see any obvious improvements or fixes I should make. Thanks all.

int Joystick[21];

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

void print_array ()
{
  Serial.print('J');
  for(int i = 0; i < 21; i++) {
    Serial.print(Joystick[i]);
    Serial.print(' ');
  } 
  Serial.println();
}

void processIncomingChar (const char inChar)
{
  static int value;
  static int index;

  switch (inChar)
  {
  case '<':  
    index = 0;
    value = 0;
    print_array ();
    break;

  case '0' ... '9':
    value = ((10*value) + (inChar - '0')); 
    break;

  case ' ': 
    Joystick[index] = value;
    value = 0;
    index++;
    break;

  default:
    break;
  } 
} 

void loop()
{
  if (Serial.available () > 0)
    processIncomingChar (Serial.read ());
}