It is currently Sat Jul 26, 2014 2:07 am


Post a new topicPost a reply Page 1 of 1   [ 3 posts ]
Author Message
 Post subject: Arduino project: serial port remote command template
PostPosted: Mon Dec 06, 2010 12:04 pm 

Joined: Sun Oct 31, 2010 7:52 am
Posts: 11
My last Project post (Arduino remote control panel) focused on receiving primitive commands from a remote computer. Once I had the serial communication and the primitive command parsing working, I wanted to generalize the Arduino side.

I think it will be a useful "building block", as I work on Arduino projects, to have the ability to receive text-string commands, parse them and execute them. The actual commands and what they execute is arbitrary; the goal is to have a "template" program (er, "sketch" in Arduino-speak) that can be used for any application that needs remote commands.

In my previous post, I mentioned two classes of applications where this might be useful:

1) to make an "operator's console" to control a complex Arduino project (permanently connected)

2) to make a "maintenance console" to tweak/calibrate/configure even a simple Arduino project (occasionally connected)

Another type of project came to mind this week:

3) A 3- or 4-axis robot arm, one Arduino controls each axis and its stepper-motor, while one Arduino Mega (with 4 serial ports) coordinates the sequence of movements by sending commands via serial port to the axis controllers.

So it seemed like I'd want to do something like this often enough that it was worth a good, clean generalized implementation that I could use as a template. And once I got it working, I went back over it with comments to make it (hopefully) clear to others how to use the template themselves...

So the program design is this:

Arduino's sit in an infinite loop, so we do the following forever:

Code:
1) See if any input has started arriving
    If so, gather it all up until it stops (or we fill the buffer)
2) If we got something, then
      parse it into its component pieces (a command and some arguments)
3)    execute the command
4) Check for any timer-based events needing attention
5) Do any other work that needs doing
Wait a millisecond


The design allows commands to set up a timer, to blink an LED for a variable time period, say. The loop has a 1 ms delay at the end, so fairly precise timing is available.

The step numbers at the left above are keyed to comments in the source code that follows. The code as written implements a trivial case of turning an LED on/off or blinking it for N milliseconds. There are print commands that allow you to enter command strings in the Arduino serial monitor and see the results. Comment these out in a real application (or not). Define new commands by adding new else clauses in Step 3.

Code:

/*

 Program to receive and exceute commands via serial port
 
 Reads lines of text containing "commands" and optional arguments (pararmeters).
 This instance turns an LED on/off, or blinks it every N milliseconds.
 Modify Steps 3, 4 & 5 as desired to implement new commands...
 
 Larry Walker      Walker Energy Systems LLC      Dec. 5, 2010
 
 */

#include <Stdio.h>                    // need this so we can use sscanf(), atoi(), strcmp()

// adjust the number and length of args and the input line length
// as needed per application, to minimize space consumed...
const int MAXARGNUM = 10;
const int MAXARGLEN = 16;
const int MAXBUFLEN = 64;

char *argv[MAXARGNUM];                // the arg vector (ptrs to the char strings)
char   arg[MAXARGNUM][MAXARGLEN+1];   // the args themselves
int argc;                             // the arg count

int delayVal = 0;       // duration of blink, in millis (0 means no blinking in effect)
int delayCnt = 0;       // counter to step through the delayVal
int blinkState = LOW;   // state variable for blinking LED

const int LED1 = 13;    // define pin for LED1

char ascii[64];         // output buffer for debug prints

void setup()
{
  int i;

  Serial.begin(9600);

  // initialize the array of argv pointers
  // to point to an array of char variables

  // init the argv pointers
  for (i=0; i<MAXARGNUM; i++) {
    argv[i] = &arg[i][0];
  }
}

void loop()
{
  int i, inPtr, n;                // indexes used in processing the args

  char inputLine[MAXBUFLEN+1];    // buffer for incoming lines

  /*
   Step 1: each time around, check to see if a line of input has begun
   */

  // suck up characters until they stop (or buffer fills)
  // this should (nominally, theoretically) be one "line" of input
  i = 0;
  while ((Serial.available() > 0) && (i<MAXBUFLEN)) {
    inputLine[i] = Serial.read();
    i++;
    // need a brief delay to give any chars remaining in the line time to arrive
    delay(1);
  }

  /*
   Step 2: if/when a line has arrived, parse it
   */

  // check if we got anything this time around
  if (i > 0) {
    // terminate the incoming line
    inputLine[i] = '\0';
    sprintf(ascii, "inputLine = %s", inputLine);
    Serial.println(ascii);

    // use sscanf() to pull the args apart and count them

    // arguments are  strings of consecutive alphanumeric chars,
    //  separated by spaces or tabs. They must be explicitly converted if numeric
    argc = 0;
    inPtr = 0;
    // loop through the input, pulling out one arg each time and noting its length
    // [ cryptic incantation follows: see any good C book for details on sscanf ]
    while (sscanf(&inputLine[inPtr], "%s %n", argv[argc], &n) > 0) {

      // confirmation/debugging output
      sprintf(ascii, "argv[%d] => %s    ", argc, argv[argc]);
      Serial.println(ascii);

      // found another arg, so bump the pointer past it for next sscanf call
      inPtr = inPtr + n;
      argc++;
      // enforce the max args limit!
      if (argc >= MAXARGNUM) {
        break;
      }
    }  // end of while
    sprintf(ascii, "argc = %d", argc);
    Serial.println(ascii);

    /*
     Step 3: the incoming line has been parsed into argv[], so perform argv[0] as a command
     */

    // command parsed, now do it
    if (strcmp(argv[0], "ON") == 0) {
      // do the ON command
      digitalWrite(LED1, HIGH);
      Serial.println("     turning LED on...");
      delayVal = 0;     // suppress any pending blinking
    }
    else if (strcmp(argv[0], "OFF") == 0) {
      // do the OFF command
      digitalWrite(LED1, LOW);
      Serial.println("     turning LED off...");
      delayVal = 0;     // suppress any pending blinking
    }
    else if (strcmp(argv[0], "BLINK") == 0) {
      // do the BLINK command
      delayVal = atoi(argv[1]);      // the first/only arg is the blink-time in ms
      delayCnt = 0;
      Serial.print("     blinking LED every ");
      Serial.print(delayVal, DEC);
      Serial.println(" ms...");
      delayCnt = delayVal-1;     // terminate any pending blink
    }
    else {
      // if nothing else matches, do the default
      Serial.println("     Invalid command!");
    }
    Serial.println();
  }  // end of if (got a line)

  /*
   Step 4: tend to any timer-like things that command(s) may have started
   */

  // see if we need to do any timer-/counter-based things

  // just one, for now, the blink timer
  if (delayVal != 0) {
    // we have a delayVal, so we're blinking
    delayCnt++;
    // time to toggle the LED?
    if (delayCnt >= delayVal) {
      blinkState = !blinkState;
      digitalWrite(LED1, blinkState);
      delayCnt = 0;      // restart the delay period
    }  // end of if (delay expired)
  }  //end of if (delay in effect)

  // add additional timer-related checks here

  /*
   Step 5: (Optional) do any additional application-specific tasks here
   */

  // this code will get executed every ms
  //   (except during brief bursts of char-reading during incoming lines)

  // do any other things that your application needs to do here...

  /*
   end of Steps: wait briefly and go around again
   */

  delay(1);

  /*
   This whole thing is a 1-ms clockwork mechanism,
   getting interrupted by a line of input now
   and then...
   */

}


Larry Walker


Top
 Profile  
 
 Post subject: Re: Arduino project: serial port remote command template
PostPosted: Sat Dec 11, 2010 6:39 pm 

Joined: Tue Jul 27, 2010 10:38 pm
Posts: 13
Great idea. I had often wondered if someone had come up with this sort of "IOS" (if you are familiar with Cisco devices) for Arduino, but never looked to find one. I could see this sort of basic serial console/command framework being extended with some project-agnostic commands ("reset", dump flash memory to the console, etc.), the ability to enable/disable certain Arduino diagnostics, a standardized help mechanism and a command shorthand/auto completion facility. I imagine those would be useful to nearly every project making use of something like this.

Taking the IOS analogy perhaps a bit too far, you could develop a standardized but extensible flash memory configuration data structure, and then have commands to save/dump that.

Nice work,

Scott


Top
 Profile  
 
 Post subject: Re: Arduino project: serial port remote command template
PostPosted: Fri Feb 04, 2011 11:45 am 

Joined: Fri Feb 04, 2011 11:35 am
Posts: 1
My environment (Arduino IDE 0022, Uno board) required the following line added to setup():

pinMode(LED1, OUTPUT);

Without that line, the onboard LED would work, but very very dimly; with the line the LED returned to its full orangy goodness.

Cheers,
Mike


Top
 Profile  
 
Display posts from previous:  Sort by  
Post a new topicPost a reply Page 1 of 1   [ 3 posts ]


Who is online

Users browsing this forum: No registered users and 0 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to:  


Powered by phpBB