Reading serial data into an array and then parsing it.

Hello all. Just getting into Arduino's and I have dabbled in PIC microcontroller in the past, but never anything too complex, mostly PIC Basic. Well I have a bunch of project I want to do, and my first is using XBees to remotely monitor a solar panel.

Right now I have a pololu A-Star and an Uno talking to an XBee, both TX and RX to the XBee. Where I am having trouble is on the RX side collecting the data from the serial port correctly.

Here is my read routine:

    while(Serial.available() > 0)
  {
    char aChar = Serial.read();
    inData[index] = aChar;
    index++;
    inData[index] = '\0'; // Keep array NULL terminated
  }

For now I am printing the collected data right to an OLED display with no parsing. What I am noticing is that the I sometime miss the first couple of digits of information.

To start I am sending two floats on the TX XBee via the Serial.print statement both serial ports and XBees are running a 9600. Here is the TX Loop:

void loop(void) {
  int voltIn = analogRead(10);
  float outputVoltIn = map(voltIn,0,1023,0,5);
  float currentIn=1.50;
  float outputCurrentIn=currentIn;
  
  // rebuild the picture after some delay
  

  if (counter1==1000)
  { 
    Serial1.print(outputVoltIn);
    Serial1.print(" , "); 
    Serial1.print(outputCurrentIn);
    Serial1.print("\0");
    counter1=0;
    delay(1000);
  }
  counter1++;
}

Probably not the cleanest code, but it mostly works. I am taking baby steps here by trying to get this to work with two floats.

A valid read right now will show 0.00 , 0.00

Now my questions:

  1. How can I improve the repeatability of the serial read? Right now 1-2 of every 10 is invalid. (When it is invalid it will display only the latter half of the TX data. )

  2. Assuming I can get the data reliable what is the best way to parse the data assuming is is comma separated?

  3. Am I using the best method to send the data via serial (i.e. Serial.print)?

Thanks!

The information you have given us is too sparse.

We have no way of knowing what the reciever is getting up to before it gets to that snippet of code you've given. Nor do we know what it gets upto once it breaks out of the loop.

Chances are, it's one of these areas that has the problem.

What is this achieving?

    Serial1.print("\0");

You need to show all your receiver code, not just a snippet.

http://snippets-r-us.com/

There really is not much else in the code at this point, but here it is

TX Code (A-Star)

int counter1=0;

void setup(void) {

  //Start Serial Port (9600 baud)
  Serial1.begin(9600);

}

void loop(void) {
  int voltIn = analogRead(10);   // READ ADC 
  float outputVoltIn = map(voltIn,0,1023,0,5);  // Temporary MAPING 0 - 5
  float currentIn=1.50;  // Temp hold for second data to be sent via serial
  float outputCurrentIn=currentIn;  //  Temp variable to match final variable name

  if (counter1==1000)
  { 
    Serial1.print(outputVoltIn);
    Serial1.print(" , "); 
    Serial1.print(outputCurrentIn);
    Serial1.print("\0");
    counter1=0;
    delay(1000);
  }
  counter1++;
}

RX Code (Uno)

#include "U8glib.h"

U8GLIB_SSD1306_128X32 u8g(U8G_I2C_OPT_NONE);	// I2C / TWI 
int counter1=0;

void setup(void) {

  //Start Serial Ports
  Serial.begin(9600);   // Start Port 0 (XBee)

}

char inData[10]; // Allocate input Array
char inChar; // Character Read
byte index = 0; // Array Index


void loop() {

    while(Serial.available() > 0)
  {
    char aChar = Serial.read();  // Read serial input
    inData[index] = aChar;  // store in array at index
    index++;   // increment index
    inData[index] = '\0'; // NULL terminate
  }

  
  u8g.firstPage();   
  do {
    printScreen(inData);   // print received array to screen
    index=0;  // reset index
  }while(u8g.nextPage());

}

///////////  MY FUNCTIONS //////////////////////

void printScreen(char* voltage) {  
  u8g.setFont(u8g_font_5x8);
  u8g.setPrintPos(0, 6);
  u8g.print("Status");  
  u8g.setFont(u8g_font_profont12);
  u8g.setPrintPos(0,17);
  u8g.print(voltage);
}

The last "\0" in the TX code is because I was playing with padding the final TX data with nulls, totally not necessary, and removed it makes no difference with my RX problem.

Nick, I browsed your website, and tried your code for buffering serial data, that works much better than my code, put it appears there is a limit as to how fast I can send the data before I start to get errors. My guess is the cause of that is the writing to the display

RX Code (Uno)

#include "U8glib.h"

U8GLIB_SSD1306_128X32 u8g(U8G_I2C_OPT_NONE);	// I2C / TWI 
int counter1=0;
const unsigned int MAX_INPUT = 15;

void setup(void) {

  //Start Serial Ports
  Serial.begin(9600);   // Start Port 0 (XBee)
  u8g.firstPage();
  do {
    // empty to clear screen
  } 
  while( u8g.nextPage() );
}

void loop() {

  if (Serial.available () > 0)
    processIncomingByte (Serial.read());

}

///////////  MY FUNCTIONS //////////////////////

void process_data (char* data){
  u8g.firstPage();   
  do {
    u8g.setFont(u8g_font_5x8);
    u8g.setPrintPos(0, 6);
    u8g.print("Status:");  
    u8g.setFont(u8g_font_profont12);
    u8g.setPrintPos(0,17);
    u8g.print(data);
  }
  while(u8g.nextPage());
}
void processIncomingByte (const byte inByte)
{
  static char input_line [MAX_INPUT];
  static unsigned int input_pos = 0;

  switch (inByte)
  {

  case '\n':   // end of text
    input_line [input_pos] = 0;  // terminating null byte
    
    // terminator reached! process input_line here ...
    process_data (input_line);

    // reset buffer for next time
    input_pos = 0;  
    break;

  case '\r':   // discard carriage return
    break;

  default:
    // keep adding if not full ... allow for terminating null byte
    if (input_pos < (MAX_INPUT - 1))
      input_line [input_pos++] = inByte;
    break;

  }  // end of switch

} // end of processIncomingByte

TX Code (A-Star)... This is about as fast as I can set the repeatedly send the two floats (much faster than I anticipate)

int counter1=0;

void setup(void) {

  //Start Serial Port (9600 baud)
  Serial1.begin(9600);

}

void loop(void) {
  int voltIn = analogRead(10);   // READ ADC 
  float outputVoltIn = map(voltIn,0,1023,0,5);  // Temporary MAPING 0 - 5
  float currentIn=1.50;  // Temp hold for second data to be sent via serial
  float outputCurrentIn=currentIn;  //  Temp variable to match final variable name

  if (counter1==250)
  { 
    Serial1.print(outputVoltIn);
    Serial1.print(" , "); 
    Serial1.print(outputCurrentIn);
    Serial1.print("\n");
    counter1=0;
    delay(25);
  }
  counter1++;
}

Frozen001:
My guess is the cause of that is the writing to the display ...

Quite possibly.

Not part of your problem, or a solution to it, but do you really need to do this

  int voltIn = analogRead(10);   // READ ADC 
  float outputVoltIn = map(voltIn,0,1023,0,5);  // Temporary MAPING 0 - 5
  float currentIn=1.50;  // Temp hold for second data to be sent via serial
  float outputCurrentIn=currentIn;  //  Temp variable to match final variable name

every time through loop() on the transmitter ? Why not collect the data only when you need it and while you are at it use a more controlled method to determine the frequency of the readings ?

  float outputCurrentIn=currentIn;  //  Temp variable to match final variable nameThe receiver will not know the name of the variable on the transmitter so why the need to copy the variable to another name ?

float outputVoltIn = map(voltIn,0,1023,0,5);  // Temporary MAPING 0 - 5The map() function uses integer maths so will not generate a float.

Not part of your problem, or a solution to it, but do you really need to do this

Right now I am just starting, and I want to take a lot of readings and TX/RX them to see what how much and how fast I can send a receive. So all of this is just temporary, hence the 'Temp' in the comments. As I move forward I will most likely use an ISR inked to a timer to do the reading of the analog data. My likely interval will be 1 minute. I just needed to verify that I can accurately transmit and receive the data.

Now on to the parsing of the data. I am thinking maybe I can do a nested Switch/Case that will increment a flag every time a ',' is read to separate the data into the different data bins. Thoughts? I hate using for/next and do/while loops...

Frozen001:
I hate using for/next and do/while loops...

Well, they are part of the language, and should be used when appropriate.

I want to take a lot of readings and TX/RX them to see what how much and how fast I can send a receive.

Then you are going about it the wrong way. Start with a slow transmission speed and frequency, get it working, speed things up until it fails, then back off a little to get it working reliably.

I hate using for/next and do/while loops...

What about while loops rather than do/while ? I find them easier to read, understand and debug because the loop condition if up front rather than at the end of the loop as it is with do/while.

UKHeliBob:
Then you are going about it the wrong way. Start with a slow transmission speed and frequency, get it working, speed things up until it fails, then back off a little to get it working reliably.

Well I thought I was, in my original code, I was only sending the two Serial.print statements once every few seconds, hardly fast (9600baud) or frequent in my book. at this point I think I have my data TX/RX working good enough.

if (counter1==250)Once every 250 times through the loop() function.
Or
  if (counter1==1000)Once every 1000 times through the loop() function.

Neither of them are "once every few seconds". The analogRead() and messing with floats and the map() function each time through loop() will slow it down a little, but not much.

UKHeliBob:
if (counter1==250)Once every 250 times through the loop() function.
Or
 if (counter1==1000)Once every 1000 times through the loop() function.

Neither of them are "once every few seconds". The analogRead() and messing with floats and the map() function each time through loop() will slow it down a little, but not much.

Read through all the code... delay(1000); at the end to slow TX rate which I varied during my testing.

delay(1000); Point taken, but not in the Tx code in reply #4 though.

If you are going to use a 1 second delay() then you may as well not bother incrementing a counter 250 or 1000 times as the delay() will far outweigh the 250 or 1000 count. You may as well stick the delay() directly in the loop() function and forget the counter.

UKHeliBob:
Point taken, but not in the Tx code in reply #4 though.

If you are going to use a 1 second delay() then you may as well not bother incrementing a counter 250 or 1000 times as the delay() will far outweigh the 250 or 1000 count. You may as well stick the delay() directly in the loop() function and forget the counter.

By reply #4 I had it working at that speed reliably. My reason for the count function is so that I expand the code, I can do other things between the sample and transmit of the data, which is why I think at some point the sample of the analog data will be handled by an ISR tied to a timer.