This CLI (Command Line Interface) is based mbed-os. Both NNN50 and NQ620 are supported.

Fork of NNN40_CLI by Delta

BLE CLI Document can be downloaded here .

Note that when evaluate using Windows PC as the host, the Serial driver need to be installed in advance. The instruction is explained in the link below

https://developer.mbed.org/handbook/Windows-serial-configuration

Once installed, a device called 'mbed Serial Port (COM#)' should be recognized in Device Manager, as shown below

/media/uploads/tsungta/mbed_serial_port_1.png

Please open the com port at 115200 8n1 as default

CLI_Source/command-interpreter.cpp

Committer:
silviaChen
Date:
2016-11-11
Revision:
24:838a0b25934b
Parent:
21:72d7a6e85d7f

File content as of revision 24:838a0b25934b:

/**
 * File: command-interpreter.c
 * Description: processes commands incoming over the serial port.
 *
 * Copyright 2014 by CYNTEC Corporation.  All rights reserved.
 */

#include <stdint.h>
#include <string.h>
#include "mbed.h"
#include "command-interpreter.h"

extern Serial console;

// Command parsing state
typedef struct {

  // Finite-state machine's current state. 
  uint8_t state;

  // The command line is stored in this buffer.
  uint8_t buffer[CYNTEC_COMMAND_BUFFER_LENGTH];

  // Indices of the tokens (command(s) and arguments) in the above buffer.
  uint8_t tokenIndices[MAX_TOKEN_COUNT];

  // The number of tokens read in, including the command(s). 
  uint8_t tokenCount;

  // Used while reading in the command line.
  uint8_t index;

  // First error found in this command. 
  uint8_t error;

  // The token number of the first true argument after possible nested commands.
  uint8_t argOffset;
  
  //gill
  uint8_t totalBuffer[CYNTEC_COMMAND_BUFFER_LENGTH];
  uint8_t totalIndex;

} CyntecCommandState;

static CyntecCommandState commandState;

// Remember the previous character seen by emberProcessCommandString() to ignore
// an LF following a CR.
static uint8_t previousCharacter = 0;
CyntecCommandEntry *cyntecCurrentCommand;

enum {
  CMD_AWAITING_ARGUMENT,
  CMD_READING_ARGUMENT,
  CMD_READING_TO_EOL                   // clean up after error
};



const char* cyntecCommandErrorNames[] =
  {
    "",
    "No such command;",
    "Wrong number of arguments;",
    "Argument out of range;",
    "Argument syntax error;",		
    "No matched argument;",
	"Wrong command order;",
	"Invalid state to perform operation;",
    "Function call fail;"
  };

	/**
*   @brief Converts a character representation of a hex to real value.
*   @param c is the hex value in char format
*   @return the value of the hex otherwise INVALID_HEX_CHARACTER
*/

uint8_t cyntecAtoi(uint8_t *str, uint8_t len)
{
	uint8_t result = 0;
	uint8_t i = 0;
	
	while( *str != '\0' && i < len)
	{
		result *= 10;
		result = result + ( *str - '0' );
		str++;
		i++;
	}
	
	return result;
}
int cyntecAtoInt(uint8_t *str)
{
	int result = 0;
	uint8_t i = 0;
	bool is_negative = false;
	
	if (*str == '-') {
		is_negative = true;
		str++;
	}
	while( *str != '\0')
	{
		result *= 10;
		result = result + ( *str - '0' );
		str++;
		i++;
	}
	
	if (is_negative)
		return -result;
	else
		return result;
}
uint8_t cyntecArgToUint8(uint8_t *str, uint8_t len)
{
	uint8_t result = 0;
	uint8_t num[2];
	uint8_t i;
	
	if ( len != 2 )
	{
		return 0;
	}
	
	for ( i = 0 ; i < 2 ; i++ )
	{
		if ('0' <= str[i] && str[i] <= '9')
			num[i] = str[i] - '0';
		else if ('a' <= str[i] && str[i] <= 'f')
			num[i] = str[i] - 'a' + 10;
		else if ('A' <= str[i] && str[i] <= 'F')
			num[i] = str[i] - 'A' + 10;
		else
			return 0;
	}
	
	result |= num[0] << 4;
	result |= num[1] << 0;
	
	return result;
}

uint16_t cyntecAtoiUint16(uint8_t *str, uint8_t len)
{
	uint16_t result = 0;
	uint16_t i = 0;
	
	while( *str != '\0' && i < len)
	{
		result *= 10;
		result = result + ( *str - '0' );
		str++;
		i++;
	}
	
	return result;
}

uint16_t cyntecArgToUint16(uint8_t *str, uint8_t len)
{
	uint16_t result = 0;
	uint8_t num[4];
	uint8_t i;
	
	if ( len != 4 )
	{
		return 0;
	}
	
	for ( i = 0 ; i < 4 ; i++ )
	{
		if ('0' <= str[i] && str[i] <= '9')
			num[i] = str[i] - '0';
		else if ('a' <= str[i] && str[i] <= 'f')
			num[i] = str[i] - 'a' + 10;
		else if ('A' <= str[i] && str[i] <= 'F')
			num[i] = str[i] - 'A' + 10;
		else
			return 0;
	}
	
	result |= num[0] << 12;
	result |= num[1] << 8;
	result |= num[2] << 4;
	result |= num[3] << 0;
	
	return result;
}

//gill 20150918
uint32_t cyntecHexToUint32(uint8_t *str, uint8_t len)
{
	if (len > 8)
		return 0;
	uint32_t result = 0;
	uint16_t i = 0;
	
	while( *str != '\0' && i < len)
	{
		result *= 16;
		result = result + ( *str - '0' );
		str++;
		i++;
	}
	
	return result;
}




uint8_t cyntecStrCmp(uint8_t *src, uint8_t *dst, uint8_t len)
{
	uint8_t i = 0;
	
	while ( *src != '\0' && *dst != '\0' && i < len ) 
	{
		if ( *src != *dst )
			return 0;
		i++;
		src++;
		dst++;
	}
	
	return 1;
}
	
// Initialize the state machine.
void cyntecCommandReaderInit(void)
{
  commandState.state = CMD_AWAITING_ARGUMENT;
  commandState.index = 0;
  commandState.tokenIndices[0] = 0;
  commandState.tokenCount = 0;
  commandState.error = CYNTEC_CMD_SUCCESS;
  commandState.argOffset = 0;
	cyntecCurrentCommand = NULL;
	commandState.totalIndex = 0; //gill
}

static uint8_t tokenLength(uint8_t num)
{
  return (commandState.tokenIndices[num + 1] 
          - commandState.tokenIndices[num]);
}

static uint8_t *tokenPointer(uint8_t tokenNum)
{
  return (commandState.buffer + commandState.tokenIndices[tokenNum]);
}

void cyntecCommandActionHandler(const CommandAction action)
{
  (*action)();
	clearBuffer();
}

static bool getNestedCommand(CyntecCommandEntry *entry,
                                CyntecCommandEntry **nestedCommand)
{
  if ( entry -> action == NULL ) {
    *nestedCommand = (CyntecCommandEntry*)entry->subMenu;
    return true;
  } else {
    return false;
  }
}

static void cyntecPrintCommandUsage(CyntecCommandEntry *entry) 
{
  CyntecCommandEntry *commandFinger;

	if (entry == NULL) {
		entry = commandFinger = cyntecCommandTable;
	} else {
		getNestedCommand(entry, &commandFinger);
		
		console.printf("%s-%s\r\n",entry->name,entry->description);
	}
	
  if ( commandFinger != NULL ) {
    for (; commandFinger->name != NULL; commandFinger++) {
			console.printf("%s - %s\r\n",commandFinger->name,commandFinger->description);
    }   
  }
	
}

void cyntecCommandErrorHandler(uint8_t status)
{
		//Silvia 20161111 modify
		console.printf("\r\nERROR;%s\r\n\r\n", cyntecCommandErrorNames[status]);
		cyntecPrintCommandUsage(cyntecCurrentCommand);
}

static CyntecCommandEntry *commandLookup(CyntecCommandEntry *commandFinger, 
                                        uint8_t tokenNum)
{
  uint8_t *inputCommand = tokenPointer(tokenNum);
  uint8_t inputLength = tokenLength(tokenNum);

  for (; commandFinger->name != NULL; commandFinger++) {
    const char *entryFinger = commandFinger->name;
    uint8_t *inputFinger = inputCommand;
    for (;; entryFinger++, inputFinger++) {
      bool endInput = (inputFinger - inputCommand == inputLength);
      bool endEntry = (*entryFinger == 0);
      if (endInput && endEntry) {
        return commandFinger;  // Exact match.
      } else if ((*inputFinger) != (*entryFinger)) {
        break;
      }
    }
  }
  return NULL;
}

void callCommandAction(void)
{
	CyntecCommandEntry *commandFinger = cyntecCommandTable;
	uint8_t tokenNum = 0;
	
	if (commandState.tokenCount == 0) {
    cyntecCommandReaderInit();
		return;
  }
	
	// Lookup the command.
  while (true) {
		commandFinger = commandLookup(commandFinger, tokenNum);
		if (commandFinger == NULL) {
      commandState.error = CYNTEC_CMD_ERR_NO_SUCH_COMMAND;
			break;
    } else {
			cyntecCurrentCommand = commandFinger;
      tokenNum += 1;
      commandState.argOffset += 1;

      if ( getNestedCommand(commandFinger, &commandFinger) ) {
        if (tokenNum >= commandState.tokenCount) {
          commandState.error = CYNTEC_CMD_ERR_WRONG_NUMBER_OF_ARGUMENTS;
					break;
        }
      } else {
        break;
      }
    }
	}

  if (commandState.error == CYNTEC_CMD_SUCCESS) {
    cyntecCommandActionHandler(commandFinger->action);
  } else {
		cyntecCommandErrorHandler(commandState.error);;
  }	
	
	cyntecCommandReaderInit();
}

/*
 * 
 */
void cyntecEndArgument(uint8_t input)
{
  if (commandState.tokenCount == MAX_TOKEN_COUNT) {
		commandState.error = CYNTEC_CMD_ERR_WRONG_NUMBER_OF_ARGUMENTS;
    commandState.state = CMD_READING_TO_EOL;
	}
	
	commandState.tokenCount += 1;
	commandState.tokenIndices[commandState.tokenCount] = commandState.index;
	commandState.state = CMD_AWAITING_ARGUMENT;
	
  if (input == '\r' || input == '\n') {
		callCommandAction();
  }
}

/*
 * 
 */
void cyntecWriteToBuffer(uint8_t input)
{
	if (commandState.index == CYNTEC_COMMAND_BUFFER_LENGTH) {
		commandState.error = CYNTEC_CMD_ERR_WRONG_NUMBER_OF_ARGUMENTS;
		commandState.state = CMD_READING_TO_EOL;
	} else {
		commandState.buffer[commandState.index] = input;
		commandState.index += 1;
	}
}


/*
 * Process the given char as a command.
 */
void cyntecProcessCommandInput(uint8_t input) {
	
	bool isEol = false;
  bool isSpace = false;

	if (previousCharacter == '\r' && input == '\n') {
		previousCharacter = input;
    return;
  }
	
  previousCharacter = input;	
  isEol = ((input == '\r') || (input == '\n'));
  isSpace = (input == ' ');
	
	switch (commandState.state) {
    case CMD_AWAITING_ARGUMENT:
      if (!isEol)
        cyntecWriteToTotalBuffer(input); // total buffer including space
      if (isEol) {
        callCommandAction();
      } else if (! isSpace) {
        commandState.state = CMD_READING_ARGUMENT;
        cyntecWriteToBuffer(input);
      }
      break;
	case CMD_READING_ARGUMENT:
	  if (!isEol)
        cyntecWriteToTotalBuffer(input);
      if (isEol || isSpace) {
        cyntecEndArgument(input);
      } else {
        cyntecWriteToBuffer(input);
      }			
			break;
    case CMD_READING_TO_EOL:
      if (isEol) {
        if (commandState.error != CYNTEC_CMD_SUCCESS) {
          cyntecCommandErrorHandler(commandState.error);
        }
        cyntecCommandReaderInit();
      }
      break;			
	}
}


/** Retrieves unsigned integer arguments. */
uint8_t *cyntecGetCommandArgument(uint8_t argNum, uint8_t *length)
{
  uint8_t tokenNum = argNum + commandState.argOffset;

  if (length != NULL) {
    *length = tokenLength(tokenNum);
  }
  return tokenPointer(tokenNum);
}

void clearBuffer(void)
{
	uint16_t i;
	for (i=0;i<CYNTEC_COMMAND_BUFFER_LENGTH;i++)
	{
		commandState.buffer[i] = NULL;
	}
}

/** Retrieves the token count. */
uint8_t cyntecGetCommandTokenCnt()
{
	return commandState.tokenCount;
}

/*
gill add for accept blank in name 20150904
uint8_t *cyntecGetCommandBuffer()
void cyntecWriteToTotalBuffer(uint8_t input)
uint8_t *cyntecGetCommandTotalBuffer(void)
uint8_t cyntecGetTotalIndex(void)
*/

	
void cyntecWriteToTotalBuffer(uint8_t input)
{
	if (commandState.totalIndex == CYNTEC_COMMAND_BUFFER_LENGTH) {
		commandState.error = CYNTEC_CMD_ERR_WRONG_NUMBER_OF_ARGUMENTS;
		commandState.state = CMD_READING_TO_EOL;
	} else {
		commandState.totalBuffer[commandState.totalIndex] = input;
		commandState.totalIndex += 1;
	}
}

uint8_t *cyntecGetCommandTotalBuffer(void)
{
	return commandState.totalBuffer;
}

uint8_t cyntecGetTotalIndex(void)
{
	return commandState.totalIndex;
}