A simple serial shell running on its own thread (stack is statically allocated).

Dependents:   FRDM_K64F_IOT lpc1768_blinky

A simple serial shell running on its own thread. The thread is not started until a call to start(). A start() call creates a thread either using static allocation (pre-defined stack size).

Sample Usage

#include "mbed.h"
#include "rtos.h"
#include "Shell.h"

Serial pc(p9,p10);

#define SHELL_STACK_SIZ 1024
// Pre-allocate the shell's stack (on global mem)
unsigned char shellStack[SHELL_STACK_SIZ];
Shell shell(&pc);

// Shell Commands
/**
 *  \brief Gets the amount of free memory
 *  \param none
 *  \return none
 **/
static void cmd_mem(Stream * strm, int argc, char * argv[])
{
   // In order to get free mem within RTOS
   // we need to get the main thread's stack pointer
   // and subtract it with the top of the heap
   // ------+-------------------+   Last Address of RAM (INITIAL_SP)
   //       | Scheduler Stack   |
   //       +-------------------+
   //       | Main Thread Stack |
   //       |         |         |
   //       |         v         |
   //       +-------------------+ <- bottom_of_stack/__get_MSP()
   // RAM   |                   | 
   //       |  Available RAM    |  
   //       |                   |  
   //       +-------------------+ <- top_of_heap
   //       |         ^         |
   //       |         |         |
   //       |       Heap        |
   //       +-------------------+ <- __end__ / HEAP_START (linker defined var)
   //       | ZI                |
   //       +-------------------+
   //       | ZI: Shell Stack   |
   //       +-------------------+
   //       | ZI: Idle Stack    |
   //       +-------------------+
   //       | ZI: Timer Stack   |
   //       +-------------------+
   //       | RW                |  
   // ------+===================+  First Address of RAM
   //       |                   |
   // Flash |                   |
   //

   uint32_t bottom_of_stack = __get_MSP();
   char     * top_of_heap =  (char *) malloc(sizeof(char));
   uint32_t diff = bottom_of_stack - (uint32_t) top_of_heap;

   free((void *) top_of_heap);

   strm->printf("Available Memory : %d bytes\r\n",
        diff);
}

int main() {

    // Start the shell
    pc.printf("Starting debug shell ...\r\n");
    shell.addCommand("mem", cmd_mem);
    shell.start(osPriorityNormal, SHELL_STACK_SIZ, shellStack);

    while(1) {
        wait(0.2);
    }      
}


Files at this revision

API Documentation at this revision

Comitter:
vpcola
Date:
Tue Apr 28 16:56:21 2015 +0000
Child:
1:25fa46a375dd
Commit message:
Simple Serial shell running on its own thread

Changed in this revision

Shell.cpp Show annotated file Show diff for this revision Revisions of this file
Shell.h Show annotated file Show diff for this revision Revisions of this file
Utility.h Show annotated file Show diff for this revision Revisions of this file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Shell.cpp	Tue Apr 28 16:56:21 2015 +0000
@@ -0,0 +1,181 @@
+#include "mbed.h"
+#include "cmsis_os.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+// cpp headers
+#include <string>
+#include <cctype>
+
+
+
+#include "Shell.h"
+#include "Utility.h"
+
+static char *_strtok(char *str, const char *delim, char **saveptr) 
+{
+  char *token;
+  if (str)
+    *saveptr = str;
+  token = *saveptr;
+
+  if (!token)
+    return NULL;
+
+  token += strspn(token, delim);
+  *saveptr = strpbrk(token, delim);
+  if (*saveptr)
+    *(*saveptr)++ = '\0';
+
+  return *token ? token : NULL;
+}
+
+
+Shell::Shell(Stream * channel)
+    :_chp(channel)
+{
+    _commands.clear();
+}
+
+void Shell::addCommand(std::string name, shellcmd_t func)
+{
+    _commands.insert(std::pair<std::string, shellcmd_t>(name, func));
+}
+
+void Shell::start(osPriority priority,
+                int stackSize,
+                unsigned char *stack_pointer)
+{
+    _thread = new Thread(Shell::threadHelper, this, priority, stackSize, stack_pointer);
+}
+
+void Shell::threadHelper(const void * arg)
+{
+    Shell * pinstance = static_cast<Shell *>(const_cast<void *>(arg));
+
+    pinstance->shellMain();
+}
+
+void Shell::shellUsage(const char *p) 
+{
+     _chp->printf("Usage: %s\r\n", p);
+}
+
+void Shell::listCommands()
+{
+    std::map<std::string, shellcmd_t>::iterator it;
+    for (it = _commands.begin(); it != _commands.end(); it++)
+        _chp->printf("%s ", (*it).first.c_str());
+}
+
+bool Shell::cmdExec(char * name, int argc, char *argv[])
+{
+    std::map<std::string, shellcmd_t>::iterator it;
+    it = _commands.find(std::string(name));
+    if (it != _commands.end())
+    {
+        it->second(_chp, argc, argv);
+        return true;
+    }
+
+    return false;
+}
+
+
+void Shell::shellMain() 
+{
+  int n;
+  char *lp, *cmd, *tokp;
+
+  _chp->printf("\r\nEmbed/RX Shell\r\n");
+  while (true) {
+    _chp->printf(">> ");
+    if (shellGetLine(line, sizeof(line))) {
+      _chp->printf("\r\nlogout");
+      break;
+    }
+    // Get the command
+    lp = _strtok(line, " \t", &tokp);
+    cmd = lp;
+
+    // Get the arguments
+    n = 0;
+    while ((lp = _strtok(NULL, " \t", &tokp)) != NULL) {
+      if (n >= SHELL_MAX_ARGUMENTS) {
+        _chp->printf("too many arguments\r\n");
+        cmd = NULL;
+        break;
+      }
+      args[n++] = lp;
+    }
+    args[n] = NULL;
+
+    // Determine the command
+    if (cmd != NULL) 
+    {
+      if (strcasecmp(cmd, "exit") == 0)     // If "exit"
+      {
+        if (n > 0) {
+          shellUsage("exit");
+          continue;
+        }
+                // Break here breaks the outer loop
+                // hence, we exit the shell.
+        break;
+      }
+      else if (strcasecmp(cmd, "help") == 0) // If "help"
+      {
+        if (n > 0) {
+          shellUsage("help");
+          continue;
+        }
+        _chp->printf("Commands: help exit ");
+        listCommands();
+        _chp->printf("\r\n");
+      }
+      else if (!cmdExec(cmd, n, args))       // Finally call exec on the command
+      {
+        // If the command is unknown
+        _chp->printf("%s", cmd);
+        _chp->printf(" ?\r\n");
+      }
+    } // cmd != NULL
+  }
+}
+
+bool Shell::shellGetLine(char *line, unsigned size) 
+{
+  char *p = line;
+
+  while (true) {
+    char c;
+
+    if ((c = _chp->getc()) == 0)
+      return true;
+    if (c == 4) {
+      _chp->printf("^D");
+      return true;
+    }
+    if (c == 8) {
+      if (p != line) {
+        _chp->putc(c);
+        _chp->putc(0x20);
+        _chp->putc(c);
+        p--;
+      }
+      continue;
+    }
+    if (c == '\r') {
+      _chp->printf("\r\n");
+      *p = 0;
+      return false;
+    }
+    if (c < 0x20)
+      continue;
+    if (p < line + size - 1) {
+      _chp->putc(c);
+      *p++ = (char)c;
+    }
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Shell.h	Tue Apr 28 16:56:21 2015 +0000
@@ -0,0 +1,46 @@
+#ifndef _SERIAL_SHELL_H_
+#define _SERIAL_SHELL_H_
+
+#include "mbed.h"
+#include "rtos.h"
+
+#include <string>
+#include <map>
+
+#define SHELL_MAX_LINE_LENGTH       64
+#define SHELL_MAX_ARGUMENTS         4
+
+typedef void (*shellcmd_t) (Stream *, int , char **);
+
+class Shell {
+    public:
+        Shell(Stream * channel);
+        virtual ~Shell() {}
+
+        void addCommand(std::string name, shellcmd_t func);
+        void start(osPriority priority = osPriorityNormal,
+                int stackSize = 1024, 
+                unsigned char *stack_pointer=NULL);
+
+    private:
+        static void threadHelper(const void * arg);
+
+        void shellMain();
+        void shellUsage(const char *p); 
+        bool shellGetLine(char *line, unsigned size);
+        void listCommands();
+        bool cmdExec(char * name, int argc, char *argv[]);
+
+        Stream * _chp;
+        Thread * _thread;
+
+        // UART/Serial buffers for
+        // parsing command line
+        char line[SHELL_MAX_LINE_LENGTH];
+        char *args[SHELL_MAX_ARGUMENTS + 1];
+
+        // commands
+        std::map<std::string, shellcmd_t> _commands; 
+};
+
+#endif