Pages: [1]   Go Down
Author Topic: Whats the best way to send serial commands?  (Read 800 times)
0 Members and 1 Guest are viewing this topic.
0
Offline Offline
Newbie
*
Karma: 0
Posts: 27
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

I have two functions I want to be able to call via serial and have some values passed to them.  Whats the best method of doing this?

function one needs to be passed a long, two booleans, and a byte
function two just needs one boolean

The functions are not going to be called one after another, so it needs to figure out which one is being called itself.
Logged

0
Offline Offline
Newbie
*
Karma: 0
Posts: 27
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Since no one is replying, let me clarify a bit.  I'm trying to come up with a way to distinguish between data and syntax.  Like how do I tell it to call function A, and then how do I tell it where the data is starting and stopping for each variable I need to pass.  I thought about using special characters, but what if one gets used in the data of a variable?  The only solution I could come up with was have a sequence of special characters, but that would take a half dozen nested if statements to pull off and just dosent seem like the right way to do it.  Help?
Logged

0
Offline Offline
Faraday Member
**
Karma: 8
Posts: 2526
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Sounds like you mean you are sending the data via serial to trigger one function or the other?  Your problem description seems kinda vague to me.

I'd use entire lines of text.  Build up a character buffer until the line was read (lines are delimited by newline and/or carriage return characters), parse the buffer to see how many variables (or do more extensive input validation), convert the text to the needed data type(s), then call the appropriate function.

-j

Logged

0
Offline Offline
Newbie
*
Karma: 0
Posts: 27
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
Sounds like you mean you are sending the data via serial to trigger one function or the other?  Your problem description seems kinda vague to me.
Yes!  That is exactly what I mean, sorry for being vague.

I will start putting what you suggested into code and see how far I get, but I'm sure it will work.
Logged

0
Offline Offline
Newbie
*
Karma: 0
Posts: 27
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

ok i'm having a little trouble with when exactly to "parse the buffer" to get how may vars I have.  One function will only have 1 var, the other will have 4, if I check it frequently, how would I know that if it reads 1 if its really 1 or just waiting for more?

Right now I have a 13 byte long char array to hold all the variables + their terminators, but like I said, how do I determine when to stop receiving and start parsing?

This is what I got so far:
Code:
void loop(void){
boolean tempselect;
boolean dir;
boolean overide;
byte srate;
long steps;

byte ticker;
char b[13];


if (Serial.available() > 0){
      b[ticker] += char(Serial.read());
      ticker ++;}
}
Logged

0
Offline Offline
Newbie
*
Karma: 0
Posts: 27
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

I think I'm digging myself into a big hole here.  I decided I would use 10 as the variable terminator and 13 as the end of transmission terminator, that solves the when to parse problem.  But now that I'm in the parsing section, its getting very muddy and I'm not sure I'm going in the right direction.  Here is what I got:

Code:
void loop(void){
boolean tempselect;
boolean dir;
boolean overide;
byte srate;
long steps;

byte ticker;
byte varcount;
char b[13];


if (ticker == 13){
  varcount = 0;
  for ( ticker = 0; ticker < 13; ticker++){
    if (ticker-1 == 0){
      tempselect,dir = b[ticker-1];}
    if (ticker-1 == 2){
      overide = b[ticker-1];}
    }
}
      

if (Serial.available() > 0){
      b[ticker] += char(Serial.read());
      if (b[ticker] == 13){    // Detected end of transmission char
        ticker = 12;}
      ticker ++;
}
}


Edit:  Still trekking in the mud, this code is getting really long and nasty, but almost done I think.  Latest problem, how do I convert a series of bytes of unknown length into a long?  Here is latest code:

Code:
void loop(void){
boolean tempselect;
boolean dir;
boolean overide;
byte srate;
long steps;

byte ticker;
byte varcount;
char b[13];


if (ticker == 13){
  varcount = 0;
  for ( ticker = 0; ticker < 13; ticker++){
      if (b[ticker-1] = 10){
        varcount++;
      }
      if (b[ticker-1] = 13) break;
    }
  ticker = 7;
}
if (ticker == 7){
    if (varcount == 1){
      tempselect = b[0];
      gtemp(tempselect);
    }
    if (varcount > 1){
      dir = boolean(b[0]);
      overide = boolean(b[2]);
      srate = int(b[4]);
      while (ticker = 5; ticker <= 13; ticker++){
        convert ASCII bytes b[6] -> b[13] while not 10 or 13 into the long var steps....;}
      move(dir,overide,srate,steps);  
    }
}      

if (Serial.available() > 0){
      b[ticker] += char(Serial.read());
      if (b[ticker] == 13){    // Detected end of transmission char
        b[ticker+1] = 0;      // Null terminate end of array
        ticker = 12;}
      ticker ++;
}
      
}


Edit 2:  I think I came up with a solution for the last problem, but now I go to run it and instantly shooting me garbage back when I haven't even sent a single byte to it, I dont know how it got through the maze of if and for statements.  In any case here is the latest Rev, I am giving up for the night.  I am struggling to follow my own code at this point.  Is it really this hard to call a function from serial?

Code:
void loop(void){
boolean tempselect;
boolean dir;
boolean overide;
byte srate;
long steps;

byte ticker;
byte varcount;
byte scount;
char b[13];


if (ticker == 13){
  varcount = 0;
  for ( ticker = 0; ticker < 13; ticker++){
      if (b[ticker-1] = 10){
        varcount++;
      }
      if (b[ticker-1] = 13) break;
    }
  ticker = 7;
}
if (ticker == 7){
    if (varcount == 1){
      tempselect = b[0];
      Serial.print(tempselect);
    }
    if (varcount > 1){
      dir = boolean(b[0]);
      overide = boolean(b[2]);
      srate = int(b[4]);
      for (ticker = 5; ticker <= 13; ticker++){
        if (b[ticker] == 10) break;
        else {
          scount ++;}
      }    
      for (ticker = 0; ticker < scount; ticker++){
        steps += b[5+ticker]*(scount*(10^(scount-1)));
      Serial.print(steps);  
      }      
    }
}
if (Serial.available() > 0){
      b[ticker] += char(Serial.read());
      if (b[ticker] == 13){    // Detected end of transmission char
        b[ticker+1] = 0;      // Null terminate end of array
        ticker = 12;}
      ticker ++;
}
      
}
« Last Edit: August 31, 2008, 10:09:49 pm by Nonyaz » Logged

Austin, TX USA
Offline Offline
God Member
*****
Karma: 5
Posts: 998
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Nonyaz--

This is the kind of problem that software engineers in PC land invented XML to solve.  However, this isn't PC land, so I don't recommend writing an XML parser.  But you might try using some of the useful principles of XML.  First of all, for example, unless there is an important performance reason to use binary, I would suggest using text for all the data you transmit.  The main reason for this is that you can then use the Arduino serial monitor to easily debug the output from the "calling" program to make sure it's completely correct before trying to parse it by the "called" program.

What if your calling program generated some serial output that looked something like this:

Function=2
Arg1=true
Function=2
Arg1=false
Function=1
Arg1=12345678
Arg2=44
Arg3=50
Arg4=true
Function=1
Arg1=87654321
Arg2=40
Arg3=101
Arg4=false
...


As kg4wsv suggested, if you read these one line at a time it shouldn't be too hard to parse.  You can convert a string like "87654321" to a long using the atol function.  If that seems too hard to parse, you could simplify the stream by removing all the "name=" bits and replacing "true" and "false" with "1" and "0".  The following, for example, is a simplified version of the above.  Simpler to transmit and parse, but harder for humans to read.

F2
1
F2
0
F1
12345678
44
50
1
F1
87654321
40
101
0
...


Once you are perfectly sure that you "calling" side is sending out the correct data, then you can parse it.  Shout if this is confusing. smiley

Mikal
Logged

0
Offline Offline
Newbie
*
Karma: 0
Posts: 27
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

I am not using binary, I am (or at least I thought I was) using text.

My output from the host I imagined to look like this: (note the numbers are ASCII chars not the char values 1 and 0)

1 0xA
0 0xA
50 0xA
144000 0xA 0xD

In the above attempts at coding, I was using the 0xA to find end of lines, and 0xD as end of transmission.  I am writing the calling software after this (In Python), so what ever is easiest to parse is the way I want to go.  I can't stand to work in C, so the less work the AVR has to do the better.  I could really benefit from some examples.  As you can see my interpretation of kg4wsv suggestion is not so good...

As a reminder my functions need the following (no signing needed):
Function 1: boolean
Function 2: boolean, boolean, byte, long
« Last Edit: August 31, 2008, 10:56:46 pm by Nonyaz » Logged

Austin, TX USA
Offline Offline
God Member
*****
Karma: 5
Posts: 998
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Ok, I think I understand now.  Does your Arduino "called" program need to run forever, responding to the caller program?  If so, how about something like this, then?

This assumes that every "line" contains an ASCII number terminated by a hex A.

void setup(){}

// retrieves an ASCII number up to and including the terminal 0xA
long parsenumber()
{
  int c;
  byte ret = 0;
  
  while (true)
  {
    while (!Serial.available())
      ;
    c = Serial.read();
    if (c == 0xA)
      break;
    ret = 10 * ret + c - '0';
  }
  
  return ret;
}

void loop()
{
  // first number xmitted is function number - should be 0 or 1
  long function = parsenumber();
  if (function == 0)
  {
    bool arg = parsenumber() == 1 ? true : false;
    // Here you can call function1(arg);
  }
  else
  {
    bool arg1, arg2;
    byte arg3;
    long arg4;
    
    arg1 = parsenumber() == 1 ? true : false;
    arg2 = parsenumber() == 1 ? true : false;
    arg3 = (byte)parsenumber();
    arg4 = parsenumber();
    // Here you can call function2(arg1, arg2, arg3, arg4);
  }
}


This algorithm will choke on the 0xD end of transmission you propose.  You'll have to either remove that or change the algorithm.

Just as an example, if you wanted to call function1 with argument "true" and function2 with arguments "false", "true", 66, 142850, you would send a stream like this:

'0', 0xA // the next function being called is #1 (0)
'1', 0xA // the argument is 1 (true)
'1', 0xA // the next function being called is #2 (1)
'0', 0xA // first argument is 0 (false)
'1', 0xA // second argument is 1 (true)
'6', '6', 0xA // third argument is 66 (the byte)
'1', '4', '2', '8', '5', '0', 0xA // last argument is 142850 (long)
etc.


Regards,

Mikal
« Last Edit: September 01, 2008, 01:04:14 am by mikalhart » Logged

0
Offline Offline
Newbie
*
Karma: 0
Posts: 27
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Ah thank you Mikal, beautiful code, so easy to read and follow, very refreshing especially after killing my self to writing the garbage I did.  I will put this in use first thing tomorrow.  

If you would; could you help me understand the math behind this magic piece of code:
ret = 10 * ret + c - '0';
I'm not quite sure how it works, wouldn't you have to change the 10 multiplier for the place, like 100 for the hundreds place ect?  And what is the - '0' there do?
« Last Edit: September 01, 2008, 03:31:40 am by Nonyaz » Logged

Austin, TX USA
Offline Offline
God Member
*****
Karma: 5
Posts: 998
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Hey, thanks, Nonyaz!  I enjoyed writing it. smiley  There's actually a bug in that code now that I look closely.  It should be
long ret = 0; instead of byte.

So, if you are getting an ASCII stream of byte like '1', '4', '4', '0', '0', '0' followed by 0xA, how would you construct a long out of that?  Well, starting out with
long ret = 0;

you convert each digit from ASCII to binary by simply subtracting '0' which is the ASCII code 48 (0x30).  So the expression c - '0' simply means "subtract 48 from c" or convert c from ASCII to binary.

The rest of the expression simply creates the long one digit at a time

ret = 0;
ret = 10 * ret + 1; // ret is 1
ret = 10 * ret + 4; // ret is 14
ret = 10 * ret + 4; // ret is 144
ret = 10 * ret + 0; // ret is 1440
ret = 10 * ret + 0; // ret is 14400
ret = 10 * ret + 0; // ret is 144000

Make sense?  Don't forget to make ret a long instead of byte though, or it won't work for larger values.  Sorry!

Mikal
Logged

Pages: [1]   Go Up
Jump to: