Help with reading string from serial

Geez, goforsmoke and zoomkat, that was the longest "he said" "she said" argument I have seen on this forum. I don't really understand what you two are talking about, but I know if you want to get anything done, you need to make a clear objective post that shouldn't contain unnecessary quotes that escalate the situation. As for my original question, what is the cause of the jumbled serial monitor? Nick Gammon suggested that it is because of the delay()s. Is this the only problem? I also agree with zooomkat that you should put example code instead of just ideas. It makes for a much for clear post. Thanks.

I have found a solution to my problem. After reading what techcone said about 2 programs using the serial, I closed down the serial monitor and made processing print the incoming serial. Thanks.

Processing and Arduino code if anyone wants:
The important part of the arduino program:

String readString(char terminator) {
  byte index=0;
  char data[18] = "";
  while(index<18) {
    if(Serial.available() > 0) {
      char c = Serial.read();
      if(c == terminator) break;
      data[index] = c;
      index++;
    }
  }
  return data;
}

And processing:

import processing.serial.*;

Serial myPort;

char TerminateChar = '*';

void setup() {
  size(100,100);
  myPort = new Serial(this,Serial.list()[0],9600);
}

void draw() {
  background(50);
  //arduinoPort.write(composePitch(0.1));
  /*
  char data[] = {'a','b','c','d','\n'};
  for(char c:data) {
    arduinoPort.write(c);
  }
  */
  myPort.write('a');
  myPort.write('b');
  myPort.write('c');
  myPort.write('d');
  myPort.write(',');
  if(myPort.available() > 0) {
    delay(5);
    String input = myPort.readStringUntil(',');
    print(input);
  }
  delay(200);
}

Your Arduino code returns a char array for a function that is supposed to return a String.
It isn't even a C string, because it has no termination.

zoomkat:
... the people that always say remove the delay generally never post improved working code that removes the delay. Goes back to those that can vs. those that can't.

Apart from this?

Anyway, in the interests of explaining, here is a version of the original post that eliminates delays, and uses millis instead.

void setup() {
  Serial.begin(9600);
}  // end of setup


// read into buffer until terminator received
// timeout if not received within timeout_period mS
// returns true if received within timeout period
// returns false if not received, or no terminator received but buffer full
bool readStringUntil (char * data, 
                     const unsigned int length, 
                     const unsigned long timeout_period, 
                     const char terminator) 
  {
  unsigned int index = 0;
  unsigned long start_time = millis ();
  
  while (index < length) 
    {
      
    // check if time is up
    if (millis () - start_time >= timeout_period)
       return false;  // no data in timeout period

    // if data, add to buffer       
    if (Serial.available () > 0) 
      {
      char r = Serial.read();
      if (r == terminator)
        {
        data [index] = 0;  // terminating null byte
        return true;
        }  // end if terminator reached
        
      data [index++] = r;
      }  // end if available

  }  // end while loop

  return false;  // filled up without terminator
  
}  // end of readStringUntil

void loop() {
  
  
  // see if there's incoming serial data:
  if (Serial.available() > 0) {
    char foo [20];
    
    if (readStringUntil (foo, sizeof foo, 500, '\n'))
      {
      Serial.print ("Got: ");
      Serial.println (foo);
      }
    else
      Serial.println ("timeout");
  }    
}  // end of loop

This also eliminates the use of the String class, which I think you will thank me for later.

Rather than using a "counted" timeout the code above uses a (supplied) timeout in mS. By checking millis () we can see if this is up.

It also fixes the problem of the original not putting a 0x00 at the end of the string, and also the (possible) problem of checking for data [ index ] being equal to terminator due to the fact that you are testing a memory location that has not been set yet.

You didn't even post the link, so Bzzzzzzt! :slight_smile:

I prefer to not use a separate function which "blocks" like readStringUntil does, because while you are inside readStringUntil you can't process other things (like button presses).

The example below shows how you can collect data in "loop" without blocking at all (without delay, hehe) and also lets you do other things in loop (eg. test switches). This is adapated a bit from my RFID reader code. In this case I am using newline as a terminator and discarding carriage-return. The newline is not stored in the buffer, but the buffer is null-terminated so you can subsequently use strcmp on it if you want to.

/*
Example of processing incoming serial data without blocking.

Author: Nick Gammon
Date:   13 November 2011

Released for public use.
*/

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


const unsigned int MAX_INPUT = 20;
char input_line [MAX_INPUT];
unsigned int input_pos = 0;

void loop()
{

  if (Serial.available () > 0) 
    {
    char inByte = Serial.read ();

    switch (inByte)
      {

      case '\n':   // end of text
        input_line [input_pos] = 0;  // terminating null byte
        
        // terminator reached! process input_line here ...
        Serial.println (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 incoming data

  // do other stuff here like testing digital input (button presses) ...

}  // end of loop

In the case of the RFID reader there were an extra couple of lines inside the switch statement:

    case 2:    // start of text
      input_pos = 0;
      break;

That was because it sent 0x02 (STX) at the start of scanning a new card. Thus you would set the input position back to zero to get rid of any "noise characters" which you might have received.

AWOL, how can I turn a char array into a String object? I need the string methods for later on. Also by adding '\0' to the char[] and then casting, will it make it a legit String? I don't quite understand why everyone says char arrays are better than Strings.

I don't quite understand why everyone says char arrays are better than Strings.

Because char arrays are usually statically defined, while Strings use dynamically allocated memory.

On a system with 4G of memory, dynamically allocating space is fine. On a system with 2K of memory, dynamically allocating memory, especially in the small chunks that String does, causes problems.

I need the string methods for later on.

The String class simply uses the string functions internally. There is no reason why you can't do the same. The string functions are as easy to use as the stuff in the String class.

'Better' is a tricky concept.
Given a processor with large amounts of memory and a well written String class, String objects would be much simpler for most people to use.
We don't have the luxury of large amounts of memory on the AVR, so Strings can be problematical.
IMHO, it is better to get used to C strings and their methods.

But that's just my opinion.

Nate711:
Also by adding '\0' to the char[] and then casting, will it make it a legit String?

No. That simply makes a valid null-terminated C string. You might be able to pass that to a String class constructor however.

I need the string methods for later on.

Can you demonstrate how exactly?

I don't quite understand why everyone says char arrays are better than Strings.

Let's say you stick with Strings (not C strings). Then we can pretty confidently predict that in a week or two you will make a post along the lines of "my program hangs after it has been running for a few days". This may not necessarily be due to an underlying bug in the system, but due to memory fragmentation.

How can I return a char array in a method? For example:

loop() {
   char[50] foo;
   foo = aMethod();
}

char[] aMethod() {
   char[50] rar = {'a','b','c','d'};
   return rar;
}

string.h for C strings, as opposed to String.h for C++ strings, has a nice set of string-manipulation routines.

http://www.nongnu.org/avr-libc/user-manual/group__avr__string.html

All a C string is is a byte array with a 0 (byte=0, not character '0') on the end. Only 1 byte overhead.
It's very simple and direct, easy to manipulate with or without a set of special functions. Very hands-on.

How can I return a char array in a method? For example:

You can't. In that example, rar is a local variable, allocated on the heap. When the function ends, the heap can be overwritten at any time. Having a pointer to that memory is not a useful thing to have. It is what you would have, though, if you returned rar from the function.

What you need to do is pass a pointer to an array to the function, and have the function populate the pointed to array.

void aMethod(char *arrayToPopulate)
{
  arrayToPopulate[0] = 'a';
  arrayToPopulate[1] = 'b';
  arrayToPopulate[2] = 'c';
  arrayToPopulate[3] = 'd';
}

Then call it:

   char[50] foo;
   aMethod(foo);

Once again, beat to the draw!

Nate, do you know about scope and about pointers?

Here's another version of what Paul wrote:
You're going to have a problem with that. aMethod() is allocating an array that will go out of scope when aMethod() ends. Even if you pass a pointer back it will be worthless.

What you can do:
It would be better if you set up your buffer as a global. That way it's allocated once, stays where you put it and visible to the rest of your sketch.

char foo[50]; // here is the array, foo is the pointer

Thanks. I have limited knowledge of pointers, but I can understand this. In another part of my program, I use the String method indexOf. What is the equivalent for c strings? On the link goforsmoke posted, it has a method strchr which returns a pointer to that character or null if it isn't found. How can use the pointer to find the index? I can always make a for loop, but as long as the method's there I might as well use it. Thanks!

You know the start of the string, you know where it was found, the difference is the index.

How can use the pointer to find the index?

Why do you need the index? What will you do with it, if you have it?

There are a number of ways to get it. You have a pointer to the character at some location. You have a pointer to the start of the string. Pointer arithmetic is possible, resulting in the index.

If you are trying to parse the string, skip the indexOf mess, and use strtok() to parse it.

Hey what a guess! Looking at the source for the String class:

int String::indexOf( char ch, unsigned int fromIndex ) const
{
  if ( fromIndex >= _length )
    return -1;

  const char* temp = strchr( &_buffer[fromIndex], ch );
  if ( temp == NULL )
    return -1;

  return temp - _buffer;
}

Nate711:
Thanks. I have limited knowledge of pointers, but I can understand this. In another part of my program, I use the String method indexOf. What is the equivalent for c strings? On the link goforsmoke posted, it has a method strchr which returns a pointer to that character or null if it isn't found. How can use the pointer to find the index? I can always make a for loop, but as long as the method's there I might as well use it. Thanks!

Hi Nate.

With an array the indexes don't need a function to get at.

char foo[] = "bar";

foo[0] is 'b', foo[1] is 'a', foo[2] is 'r' ---- the 0,1 and 2 are indexes.

Notice I can get to the data directly. I know where foo[0] is, foo points to it. foo + 1 points to foo[1]
you can simply add to a pointer to offset but be aware you add bytes, with ints you need to add by 2's.

Ah, that makes sense. Goes to show how much I know about pointers :wink: Thanks!