Go Down

Topic: UART command shell server (Read 2 times) previous topic - next topic

Jiashu

I'm not sure if anyone has done this before, but I think the command shell is pretty useful so I wrote the command shell server, basic IO handling and servo control commands built in, and customized command can be easily integrated.
Oops, seems cannot post an attachment here?

Anachrocomputer

You'll be able to post your code on your second posting (or after that).  It's an anti-spam feature.

Jiashu

Well, it seems cannot hold the 600 line code in one post, the first part is the shell command server without the servo handling.
Code: [Select]
/*
* Arduino uart shell command server
*     -- a simple uart shell command server on Arduino
*
* Jiashu Lin, jiashu.lin@gmail.com
*
*/

/* Macro define */

/* include the servo control commands */
#undef UART_CMD_SERVO

/* include the basic io commands */
#define UART_CMD_BASIC_IO

/* UART command buffer size */
#define UART_BUF_SIZE   32

/* UART command max length */
#define UART_CMD_LENGH_MAX (UART_BUF_SIZE - 1)

/* UART command max parameter count */
#define UART_PARA_CNT_MAX 2

/* UART command shell prompt */
#define UART_PROMPT ">>"

/* type defines */
/* UART command function prototype */
typedef int (*uart_cmd_func)(char * args[], char num_args);

/* UART command structure */
typedef struct uart_cmd_struct
{
 char *name;
 char *help;
 uart_cmd_func do_cmd;
} uart_cmd_struct_t;

/* local variables */
/* UART command buffer */
static unsigned char uart_buf[UART_BUF_SIZE];
/* UART command buffer write pointer */
static unsigned char uart_wptr = 0;
/* UART command parameter pointer */
static char * uart_para_ptr[UART_PARA_CNT_MAX];

/* UART command set array, customized commands may add here */
const uart_cmd_struct_t uart_cmd_set[] =
{
   {"help", "\thelp", uart_cmd_svr_help},
#ifdef UART_CMD_BASIC_IO
   {"pinMode", "\tpinMode [pin_num] [output|input]",uart_cmd_pinMode},
   {"digiWrite", "digiWrite [pin_num] [high|low]",uart_cmd_digiWrite},
   {"digiRead", "digiRead [pin_num]",uart_cmd_digiRead},
   {"analWrite", "analWrite [pin_num] [val]",uart_cmd_analWrite},
   {"analRead", "analRead [pin_num]",uart_cmd_analRead},
#endif //UART_CMD_BASIC_IO
#ifdef UART_CMD_SERVO
   {"servoEnable", "servoEnable [servo_index] [0|1]", uart_cmd_servoEnable},
   {"servoAttach", "servoAttach [servo_index] [pin_num]", uart_cmd_servoAttach},
   {"servoPos", "servoPos [servo_index] [pos]", uart_cmd_servoPos},
   {"servoStat", "servoStat [servo_index]", uart_cmd_servoStat},
#endif //UART_CMD_SERVO
   {0,0,0}
};

/* local function define */
/* flush the UART command buffer */
void uart_cmd_svr_flush_buf(void)
{
   uart_wptr = 0;
   memset(uart_buf,0,sizeof(uart_buf));
   memset(uart_para_ptr,0,sizeof(uart_para_ptr));
}

/* init the UART command server, should call in setup() */
void uart_cmd_svr_init(void)
{
   Serial.begin(9600);
   Serial.flush();
   uart_cmd_svr_flush_buf();
   Serial.println("\n\n   ---ARDUINO UART COMMAND SHELL---");
   Serial.println("[Jiashu Lin - jiashu.lin@gmail.com]\n\n");
   uart_cmd_prompt();
}

/* Execute the command in the command buffer */
void uart_cmd_execute(void)
{
   unsigned char i = 0, para_cnt = 0, err = 0;

   while((para_cnt < UART_PARA_CNT_MAX) && \
         (uart_para_ptr[para_cnt]) && \
         (*uart_para_ptr[para_cnt]))
   {
       para_cnt++;
   }
   
   while(0 != (uart_cmd_set[i].name))
   {
       if(!strcmp((char*)uart_buf,uart_cmd_set[i].name))
       {
         Serial.println();
         err = uart_cmd_set[i].do_cmd(uart_para_ptr, para_cnt);
         Serial.println('\n');
         Serial.println(err,HEX);
         uart_cmd_svr_flush_buf();
         uart_cmd_prompt();
         return;
       }
       i++;
   }

   Serial.println("\nUnknown Command");
   uart_cmd_svr_flush_buf();
   uart_cmd_prompt();
}

/* print the command shell prompt */
void uart_cmd_prompt()
{
 Serial.print('\n');
 Serial.print(UART_PROMPT);
}

/* uart command server service routine, should call in loop() */
void uart_cmd_service()
{
   char c = 0;
   char i = 0;
   while(Serial.available())
   {
     // read one byte from serial port
     c = Serial.read();

     // if the first byte is ' ' or '\n', ignore it
     if((0 == uart_wptr)&&(' ' == c || '\r' == c))
     {
       uart_cmd_prompt();
       continue;
     }

     // if '\n' is read, execute the command
     if('\r' == c)
     {
       uart_cmd_execute();
     }
     // if ' ' is read, record the parameter ptr
     else if(' ' == c)
     {
       // damping the space
       if(uart_buf[uart_wptr-1])
       {
           // replace it with NULL
           uart_buf[uart_wptr] = 0;

           uart_wptr++;

           // record the parameter address
           for(i = 0; i < UART_PARA_CNT_MAX; i++)
           {
               if(!uart_para_ptr[i])
               {
                 uart_para_ptr[i] = (char*)(&uart_buf[uart_wptr]);
                 break;
               }
           }
             
           if(UART_PARA_CNT_MAX == i)
           {
               uart_cmd_execute();
               break;
           }
       }
     }
     // other characters, just record it
     else
     {
         uart_buf[uart_wptr] = c;
         uart_wptr++;
         if(uart_wptr == UART_CMD_LENGH_MAX)
         {
           uart_cmd_execute();
         }
     }
   }
}

/* help command implementation */
int uart_cmd_svr_help(char * args[], char num_args)
{
   char i = 0;

 Serial.println("\nARDUINO COMMAND SHELL HELP\n\n\rCommand list");

   while (uart_cmd_set[i].name)
   {
      Serial.print(uart_cmd_set[i].name);
      Serial.print("\t");
      Serial.println(uart_cmd_set[i].help);
      i++;
   }
   Serial.println("\nCommand parameter uses hex format without '0x' prefix");
   
   return 0;
}

/* convert a hex number string into int */
int str2int(char * str,int * val_p)
{
 int i, len;
 char c;
 
 *val_p = 0;
 len = strlen(str);
 for(i = 0; i < len; i++)
 {
   if(str[i]>='0'&&str[i]<='9')
   {
     (*val_p)+=((str[i]-'0')<<(4*(len-i-1)));
   }
   else if(str[i]>='a'&&str[i]<='f')
   {
     (*val_p)+=((str[i]-'a'+0xa)<<(4*(len-i-1)));
   }
   else
   {
     return 1;
   }
 }
 return 0;
}

#ifdef UART_CMD_BASIC_IO
/* pinMode uart command implementation */
int uart_cmd_pinMode(char * args[],char num_args)
{
 int pin = 0;
 
 if(2 != num_args)
 {
   return 1;
 }

 if(0 != str2int(args[0],&pin))
 {
   return 2;
 }
 
 if(0 == strcmp(args[1],"input"))
 {
   pinMode(pin,INPUT);
 }
 else if(0 == strcmp(args[1],"output"))
 {
   pinMode(pin,OUTPUT);
 }
 else
 {
   return 3;
 }
 
 return 0;
}

/* digiWrite uart command implementation */
int uart_cmd_digiWrite(char * args[],char num_args)
{
 int pin = 0;
 
 if(2 != num_args)
 {
   return 1;
 }

 if(0 != str2int(args[0],&pin))
 {
   return 2;
 }
 
 if(0 == strcmp(args[1],"high"))
 {
   digitalWrite(pin,HIGH);
 }
 else if(0 == strcmp(args[1],"low"))
 {
   digitalWrite(pin,LOW);
 }
 else
 {
   return 3;
 }
 
 return 0;
}

/* digiRead UART command implementation */
int uart_cmd_digiRead(char * args[],char num_args)
{
 int pin = 0;
 
 if(1 != num_args)
 {
   return 1;
 }

 if(0 != str2int(args[0],&pin))
 {
   return 2;
 }

 if(HIGH == digitalRead(pin))
 {
   Serial.println("HIGH");
 }
 else
 {
   Serial.println("LOW");
 }
 
 return 0;
}

/* analRead uart command implementation */
int uart_cmd_analRead(char * args[], char num_args)
{
 int pin = 0;
 int val = 0;

 if(1 != num_args)
 {
   return 1;
 }

 if(0 != str2int(args[0],&pin))
 {
   return 2;
 }
 
 val = analogRead(pin);
 Serial.println(val,HEX);
 
 return 0;
}

/* analWrite uart command implementation */
int uart_cmd_analWrite(char * args[], char num_args)
{
 int pin = 0;
 int val = 0;  

 if(2 != num_args)
 {
   return 1;
 }

 if(0 != str2int(args[0],&pin))
 {
   return 2;
 }

 if(0 != str2int(args[1],&val))
 {
   return 3;
 }

 analogWrite(pin, val);

 return 0;
}
#endif //UART_CMD_BASIC_IO

void setup()
{
 /* initiate the uart command server */
 uart_cmd_svr_init();
   
 /* initiate the servo object */
#ifdef UART_CMD_SERVO
   servo_init();
#endif //UART_CMD_SERVO
}

void loop()
{
 /* service the uart command */
 uart_cmd_service();
 
 /* service the servo*/
#ifdef UART_CMD_SERVO
 servo_service();
#endif //UART_CMD_SERVO
}

Jiashu

The second part: servo handling, just past the following code to the first part and add"#define UART_CMD_SERVO"
Code: [Select]
/* servo control uart command implementations */
#ifdef UART_CMD_SERVO
/* include the servo library */
#include <Servo.h>

/* control structure type define */
typedef struct
{
 char pin;
 char enable;
 char pos;
 Servo servo_obj;
}servo_ctrl_t;

/* macro define */
/* max servo count */
#define SERVO_COUNT 2
/* pulse delay time in ms*/
#define REFRESH_TIME 20
/* default servo position on start up */
#define DEFAULT_SERVO_POS 90

/* variable */
/* servo control object */
servo_ctrl_t servo_ctrl[SERVO_COUNT];

/* servoAttach uart command implementation */
int uart_cmd_servoAttach(char* args[], char num_args)
{
 int pin = 0;
 int servo_index = 0;

 if(2 != num_args)
 {
   return 1;
 }

 if(0 != str2int(args[0],&servo_index))
 {
   return 2;
 }

 if(0 != str2int(args[1],&pin))
 {
   return 3;
 }
 
 servo_attach(servo_index, pin);
 
 return 0;
}

/* servoPos uart command implementation */
int uart_cmd_servoPos(char* args[], char num_args)
{
 int pos = 0;
 int servo_index = 0;

 if(2 != num_args)
 {
   return 1;
 }

 if(0 != str2int(args[0],&servo_index))
 {
   return 2;
 }

 if(0 != str2int(args[1],&pos))
 {
   return 3;
 }
 
 servo_pos(servo_index, pos);
 
 return 0;
}

/* servoStat uart command implementation */
int uart_cmd_servoStat(char* args[], char num_args)
{
 int servo_index = 0;

 if((1 != num_args) && \
    (0 != num_args))
 {
   return 1;
 }

 if((1 == num_args) && \
     (0 != str2int(args[0],&servo_index)))
 {
     return 2;
 }
 
 if(0 == num_args)
 {
   for(servo_index = 0; servo_index < SERVO_COUNT; servo_index++)
   {
     servo_stat(servo_index);
   }
 }
 else
 {
   servo_stat(servo_index);
 }
 return 0;
}

/* servoEnable uart command implementation */
int uart_cmd_servoEnable(char* args[], char num_args)
{
 int enable = 0;
 int servo_index = 0;

 if(2 != num_args)
 {
   return 1;
 }

 if(0 != str2int(args[0],&servo_index))
 {
   return 2;
 }

 if(0 != str2int(args[1],&enable))
 {
   return 3;
 }

 servo_enable(servo_index,enable);
 return 0;
}

/* attach a servo to a digital pin,
* should call before enable the servo
*/
void servo_attach(int servo_index, int pin)
{
 if(servo_index > SERVO_COUNT)
 {
   return;
 }

 if(pin > 13 || pin < 0)
 {
   return;
 }

 servo_ctrl[servo_index].servo_obj.attach(pin);
 servo_ctrl[servo_index].pin = pin;
}

/* set servo position */
void servo_pos(int servo_index, int pos)
{
 if(servo_index > SERVO_COUNT)
 {
   return;
 }

 if(pos > 179 || pos < 0)
 {
   return;
 }
 
 if(servo_ctrl[servo_index].enable)
 {
   servo_ctrl[servo_index].pos = pos;
 }
}

/* print the servo status */
void servo_stat(int servo_index)
{
 
 Serial.print("servo");
 Serial.print(servo_index,HEX);
 if(servo_index >= SERVO_COUNT)
 {
   Serial.print("\tinvalid");
   return;
 }
 
 if(servo_ctrl[servo_index].enable)
 {
   Serial.print("\tenabled");
 }
 else
 {
    Serial.print("\tdisabled");
 }

 Serial.print("\tpin ");
 Serial.print(servo_ctrl[servo_index].pin, HEX);
 Serial.print("\tpos ");
 Serial.println(servo_ctrl[servo_index].pos,HEX);
}

/* enable a servo object, should attach first */
void servo_enable(int servo_index, char enable)
{
 if(enable)
 {
   if(servo_ctrl[servo_index].pin!=-1)
   {
     servo_ctrl[servo_index].enable = 1;
   }
 }
 else
 {
   servo_ctrl[servo_index].enable = 0;
 }
}

/* init the servo objects, call in setup() */
void servo_init(void)
{
 int i;
 for(i = 0; i < SERVO_COUNT; i++)
 {
   servo_ctrl[i].enable = 0;
   servo_ctrl[i].pos = DEFAULT_SERVO_POS;
   servo_ctrl[i].pin = -1;
 }
}

/* servie the servo, call in loop() */
void servo_service(void)
{
 static long last_update = 0;
 int i;
 
 if (millis() - last_update >= REFRESH_TIME)
 {
   for(i = 0; i < SERVO_COUNT; i++)
   {
     if(servo_ctrl[i].enable)
    {
       servo_ctrl[i].servo_obj.write(servo_ctrl[i].pos);
    }
   }
   last_update = millis();
 }
}
#endif //UART_CMD_SERVO

Tze-Chien Chu


rf_

#5
Nov 16, 2010, 02:38 pm Last Edit: Nov 16, 2010, 05:27 pm by rf_ Reason: 1
Hey Jiashu,
I found a adoption in http://thomascannon.net/projects/hacking-challenge/
(featured on Hack A Day http://hackaday.com/2010/11/15/playing-hacker-with-a-toy-vault/ (Just for your information ;))

Thanks for your work!


rf_

edit:

Hint: Remember the hex-notation of numbers. eg to set pin 13 high use:
Code: [Select]
>>digiWrite d high ;)

For a more general purpose Arduino shell see http://bitlash.net/wiki/

For a AVR shell see: http://www.instructables.com/id/AVRSH-A-Command-Interpreter-Shell-for-ArduinoAVR/
[/list]

Go Up