Pages: [1] 2   Go Down
Author Topic: Reading input from serial communication.  (Read 1488 times)
0 Members and 1 Guest are viewing this topic.
Offline Offline
Newbie
*
Karma: 0
Posts: 1
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Hey guys,

this is my first post and I'm hoping someone can help me. I'm quite new to arduino and programming in general and am having trouble when trying to make use of serial communication. What i would like to do is have a user input a string through the serial monitor and then store it for use later in the program. Where i get stuck is the fact that the length of the string is unknown until entered so i can't declare a variable to store it in. Any help on how to approach this would be greatly appreciated.

Cheers,
bresciano23
Logged

UK
Offline Offline
Faraday Member
**
Karma: 99
Posts: 4153
Where is your SSCCE?!?!
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Decide upon a maximum length that the user is allowed to enter and allocate a variable that big.  Anything over the decided length, discard.  Maybe warn the user through the serial connection that it was truncated.
Logged

Get 10% off all 4D Systems TFT screens this month: use discount code MAJENKO10

Offline Offline
Sr. Member
****
Karma: 1
Posts: 462
I am a amateur.
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Download Arduino 1.0.1. You can use the new Serial.readString() method to store the input into the String class. For example...
Code:
String inString = "";
void setup(){
  Serial.begin(9600);
}
void loop()
{
  if(Serial.available() > 0){
    inString = Serial.readString();
  }
  if(inString != ""){
    //do something with inString here
    inString = "";
  }
}


Or if you perfer char arrays...
Code:
size_t len;
char buf[51];
void setup(){
  Serial.begin(9600);
}
void loop()
{
  if(Serial.available() > 0){
    len = Serial.readBytes(buf, 50);
    buf [len] = '\0';
  }
  if(strlen(buf) > 0){
    //do something with buf here
    *buf = '\0';
  }
}
In this example, the user can enter a string up to 50 characters long.
« Last Edit: June 10, 2012, 10:58:39 am by dkl65 » Logged


Global Moderator
UK
Offline Offline
Brattain Member
*****
Karma: 285
Posts: 25633
I don't think you connected the grounds, Dave.
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
In this example, the user can enter a string up to 50 characters long.
...though the under-generous timeout may well truncate your string early, and may not work at all with a terminal emulator.
Logged

"Pete, it's a fool looks for logic in the chambers of the human heart." Ulysses Everett McGill.
Do not send technical questions via personal messaging - they will be ignored.

Offline Offline
Sr. Member
****
Karma: 1
Posts: 462
I am a amateur.
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

You need 52.35 ms to send 50 characters at 9600 baud. So, just forget about the timeout, and use the 1 second default. I edited my post.
Logged


0
Offline Offline
Tesla Member
***
Karma: 141
Posts: 9470
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Very simple test code that uses a delay to allow the input buffer to fill while captuting character into a string variable.

Code:
// zoomkat 7-30-11 serial I/O string test
// type a string in serial monitor. then send or enter
// for IDE 0019 and later

String readString;

void setup() {
  Serial.begin(9600);
  Serial.println("serial test 0021"); // so I can keep track of what is loaded
}

void loop() {

  while (Serial.available()) {
    delay(2);  //delay to allow byte to arrive in input buffer
    char c = Serial.read();
    readString += c;
  }

  if (readString.length() >0) {
    Serial.println(readString);

    readString="";
  }
}

Logged

Consider the daffodil. And while you're doing that, I'll be over here, looking through your stuff.   smiley-cool

Pittsburgh, PA, USA
Offline Offline
Faraday Member
**
Karma: 91
Posts: 4683
I learn a bit every time I visit the forum.
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
Decide upon a maximum length that the user is allowed to enter and allocate a variable that big.

That big + 1 for the terminating 0. And don't just truncate overlong input, actually make an error handler to report the error and get the user to re-do the entry.

If it's just a word then a 16-byte buffer should be plenty.

bresciano23, for your own good stay away from using C++ Strings in the tiny Arduino ram space. Sure, you -can- but then it gets to be a habit and when you try to do something "big" you will find your program is crashing/resetting mysteriously for no obvious reason. Part of the fix for that will be to unlearn the whifto C++ String way and then learn C string arrays that you should have from the start.

----------------------------------------------------------------

Serial comes in 1 character at a time with many cycles in between. You could write in a delay and collect your bytes and -then- check for good data or you can check your data as it comes in. Set your Serial Monitor to add a newline or carriage return (lower right on the monitor window) and watch for that (newline character is '\n', value is 10, carriage return is '\r', value is 13) and you will know the end of line is reached.
Watching characters is even more effective when it's a terminal or terminal emulator at the other end that sends characters as they are typed. In that case you can interrupt errors as they are being typed, also evaluate numbers and react to codes (A100, SW001, etc) without buffering and wanking around with strcmp() and like -at all-.
Logged

I find it harder to express logic in English than in Code.
Sometimes an example says more than many times as many words.

Offline Offline
Sr. Member
****
Karma: 1
Posts: 462
I am a amateur.
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote from: GoForSmoke
That big + 1 for the terminating 0.
You mean terminating '\0'. '0' and '\0' is not the same; '\0' = 0 and '0' = 48.

My second example works very well without needing for a special character to signal the end of the string.

Quote from: GoForSmoke
In that case you can interrupt errors as they are being typed, also evaluate numbers and react to codes (A100, SW001, etc) without buffering and wanking around with strcmp() and like -at all-.
That's cool! How do you make an interrupt that triggers when some random condition evaluates to true (e.g. (foo < 90 && bar == 78))?
« Last Edit: June 11, 2012, 09:05:31 am by dkl65 » Logged


Pittsburgh, PA, USA
Offline Offline
Faraday Member
**
Karma: 91
Posts: 4683
I learn a bit every time I visit the forum.
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote from: GoForSmoke
That big + 1 for the terminating 0.
You mean terminating '\0'.

'A', '0', '  ', '\n', '\r', '\0' are all -ASCII characters- which is what the single quotes are for.
The bytes; 'A' = 65 and '\0' = 0. So terminating 0, you check the byte and it is value = zero.
It's not like I made the term up.

Quote
My second example works very well without needing for a special character to signal the end of the string.

And EOL mark is definite and serial communication is not guaranteed. Do you bother with error handling?

Quote
Quote from: GoForSmoke
In that case you can interrupt errors as they are being typed, also evaluate numbers and react to codes (A100, SW001, etc) without buffering and wanking around with strcmp() and like -at all-.
That's cool! How do you make an interrupt that triggers when some random condition evaluates to true (e.g. (foo < 90 && bar == 78))?

I don't need interrupts. I keep my loop() fast like 100x per millisec if I can. I get each character as it comes in and check it for what I want right then. Then I have many times through loop() before the next serial is available. The same loop checks other conditions and reacts as needed.
I use a state engine to keep track of progress in processes that require multiple loops, like evaluating serial input but what the states are depends on what I want to do.

Here is a sketch that I wrote to show someone one method of checksum and non-buffered user i/o. It could be better but it does suffice. The top comment block is from a post he sent to me.

Code:
/*
Checksum Calculation:
(FF) - (17 01 00 13 A2 00 40 63 D9 BB FF FE 02 44 31 04)
= (FF) - (57C)=(255 - 1404)
= (FFFFFFFFFFFFFB83)=(-1149)
Checksum = 83 (last two bytes of the HEX value)
*/

byte  B, message[50] = "17 01 00 13 A2 00 40 63 D9 BB FF FE 02 44 31 04";
byte  csum = 0xFF;
byte  userval = 0;
byte  count = 0;
byte  error = 0;

void  setup(void)
{
  Serial.begin(9600);
  Serial.println( "Checksum calculator." );
  Serial.println( "Enter hex numbers to check with a space between each." );
  Serial.println( "Be sure that your serial monitor sends a newline at line ending." );
  Serial.println( "Test data: " );
  Serial.println( (char *) message );
  Serial.println( " checksum = 83 HEX\n" );
}

void  loop(void)
{
  if ( Serial.available())
  {
    B = Serial.read();
    if ( B >= 'a' && B <= 'f' )
    {
      B &= 0xDF;  // turns lower case alpha into upper case
    }
    Serial.print( B );
   
    if ( B == '\n' ) {
      if ( !error ) {
        if ( count == 2 )  {
          csum -= userval;
        }
        else if ( count == 1 )
        {
          Serial.print( "Error " );
        }
       
        if ( !error )
        {
          Serial.print( " : Checksum = " );
          Serial.println( csum, HEX );
        }
       
        count = userval = 0;
        csum = 0xFF; 
        return;
      }
      else
      {
        error = count = userval = 0;
        csum = 0xFF; 
        return;
      }
    }

    if ( error )  return;
   
    if (( count > 1 ) && ( B != ' ' ))
    {
      Serial.print( " Error " );
      error = 1;
    }
   
    if ( B >= '0' && B <= '9' )
    {
      B -= '0';
    }
    else if ( B >= 'A' && B <= 'F' )
    {
      B -= 55;
    }
    else if ( B == ' ' )
    {
      if ( count < 2 )  {
        Serial.print( " Error " );
        error = 1;
      }
      else  count = 2;
    }
    else
    {
      Serial.print( " Error " );
      error = 1;
      return;
    }
   
    switch ( count ) {
     
      case  0 :
        userval = B;
        count = 1;
        break;
     
      case  1 :
        userval *= 0x10;
        userval += B;
        count = 2;
        break;
       
      case  2 :
        csum -= userval;
        userval = count = 0;
    }
  }
}
Logged

I find it harder to express logic in English than in Code.
Sometimes an example says more than many times as many words.

Offline Offline
Sr. Member
****
Karma: 0
Posts: 322
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Download Arduino 1.0.1. You can use the new Serial.readString() method to store the input into the String class. For example...
Code:
String inString = "";
void setup(){
  Serial.begin(9600);
}
void loop()
{
  if(Serial.available() > 0){
    inString = Serial.readString();
  }
  if(inString != ""){
    //do something with inString here
    inString = "";
  }
}


Or if you perfer char arrays...
Code:
size_t len;
char buf[51];
void setup(){
  Serial.begin(9600);
}
void loop()
{
  if(Serial.available() > 0){
    len = Serial.readBytes(buf, 50);
    buf [len] = '\0';
  }
  if(strlen(buf) > 0){
    //do something with buf here
    *buf = '\0';
  }
}
In this example, the user can enter a string up to 50 characters long.

I am using some already written code to do something, it is printing out a character to an lcd one at a time using serial read, each time it runs the loop it gets a new character and prints it,

i have coded it to move the cursor after each character thus making it look like it is slowly wirting a word, but
i want to build some string recognition into it, is there a way to take each caracter out to a string then compare that string after the whole message is sent ?
Logged

Seattle, WA USA
Offline Offline
Brattain Member
*****
Karma: 601
Posts: 48543
Seattle, WA USA
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
is there a way to take each caracter out to a string
Out from where?

Of course, you can store each character in (the next position in) an array, keeping the array NULL terminated (which defines a string).

Quote
then compare that string
That's what the strcmp() method is for.

Quote
after the whole message is sent ?
Ah, there's the tricky part. How do you know when that has happened?
Logged

Pittsburgh, PA, USA
Offline Offline
Faraday Member
**
Karma: 91
Posts: 4683
I learn a bit every time I visit the forum.
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

I am using some already written code to do something, it is printing out a character to an lcd one at a time using serial read, each time it runs the loop it gets a new character and prints it,

i have coded it to move the cursor after each character thus making it look like it is slowly wirting a word, but
i want to build some string recognition into it, is there a way to take each caracter out to a string then compare that string after the whole message is sent ?


Why wait for the whole message to be sent to make compares?

You get 1 character at a time through serial and your compare word(s) should be in character arrays as single characters all lined up nicely.
As the letters arrive you want to compare the those with each in the compare word in turn. The "big" work is keeping track of what letter in the compare word to check against the next serial character that comes in. I say "big" because it is only as big as your scheme to organize multiple compare words which doesn't have to be "big" at all.
Still, it can be confusing if you've not done much with arrays before. If you don't know pointers the cleanest way (using pointers) will seem like voodoo but I swear that pointers are a fundamental and actually basic/simple part of C.

How many compare words (or even phrases/sentences -- let's just say strings) are you needing?

Logged

I find it harder to express logic in English than in Code.
Sometimes an example says more than many times as many words.

Offline Offline
Sr. Member
****
Karma: 0
Posts: 322
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

im going to send a code, which will then print out a relevant word to the screen,
the code will begin with "Q" so as to avoid receipt of unwated codes from other sources,

somthing like this,
Q1 , Q2, Q3 etc for a station identifier,
Q1b - for low battery
Q1t for trigger,
Q1h for heartbeat
QCA change channel all,
Q1CC change channel confirmation

etc. just simple codes for functions i plan to build in,
Logged

Pittsburgh, PA, USA
Offline Offline
Faraday Member
**
Karma: 91
Posts: 4683
I learn a bit every time I visit the forum.
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

So characters come in serial and you look for one that == 'Q'.
Then next one should be a number.

Do you know how ASCII works? Character '0' is value 48 decimal, for example. There is a whole standard table of 128 characters, 0 to 127, and extended table for 0 to 255.

Code:
char B = '7'; // I have set B to be the same value as a serial character 7 would be

// this code will print My character is the digit 7
if (( B >= '0' ) && ( B <= '9' ))
{
  Serial.print( "My character is the digit " );
  Serial.println( B );
}

// this code will set int N = 7
int N = B - '0';

Logged

I find it harder to express logic in English than in Code.
Sometimes an example says more than many times as many words.

Offline Offline
Sr. Member
****
Karma: 0
Posts: 322
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

its not that im looking for one that begins with Q really, all mesages that are not recognised will be ignored, if too many are received that are not recognised then i am going to make the channel number change on the Tx Rx as they will be considered noise,

Q is just a convenient way of stoppin noise being interpreted as a mesage as in english you dont see words that contain QN for example
Logged

Pages: [1] 2   Go Up
Jump to: