Created
December 12, 2012 21:41
-
-
Save BlockoS/4271886 to your computer and use it in GitHub Desktop.
A simple serial server for Arduino. It handles some special keys.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const char g_welcomeMsg[] = "Welcome!"; | |
#define CH_NUL 0x00 | |
#define CH_BACKSPACE 0x08 | |
#define CH_CANCEL 0x18 | |
#define CH_SUBSTITUTE 0x1a | |
#define CH_ESCAPE 0x1b | |
#define CH_SPACE 0x20 | |
#define CH_CSI 0x9b | |
#define CH_ST 0x9c | |
static const uint8_t ESC_UP = 0x41; | |
static const uint8_t ESC_DOWN = 0x42; | |
static const uint8_t ESC_RIGHT = 0x43; | |
static const uint8_t ESC_LEFT = 0x44; | |
static const uint8_t ESC_PGUP = 0x35; | |
static const uint8_t ESC_PGDOWN = 0x36; | |
enum ANSI_INPUT_STATE | |
{ | |
ANSI_NORMAL, | |
ANSI_ESCAPE, | |
ANSI_ESCAPE_SEQUENCE, | |
ANSI_CSI, | |
ANSI_STRING, | |
ANSI_TERMINATOR | |
}; | |
ANSI_INPUT_STATE g_inputState = ANSI_NORMAL; | |
#define MAX_STRING_BUFFER_SIZE 256 | |
char g_stringBuffer[MAX_STRING_BUFFER_SIZE]; | |
int g_stringBufferLen = 0; | |
volatile bool g_stringBufferReady = false; | |
#define MAX_ESCAPE_BUFFER_SIZE 64 | |
char g_escapeBuffer[MAX_ESCAPE_BUFFER_SIZE]; | |
int g_escapeBufferLast = 0; | |
#define MAX_ANSI_BUFFER_SIZE 128 | |
char g_ANSIStringBuffer[MAX_ANSI_BUFFER_SIZE]; | |
int g_ANSIStringBufferLen = 0; | |
bool g_ANSIStringReceived = false; | |
enum SERIAL_READ_STATE | |
{ | |
SERIAL_READ_NONE = 0, | |
SERIAL_READ_STANDARD, | |
SERIAL_READ_ESCAPE | |
}; | |
enum COMMAND | |
{ | |
CMD_UP, | |
CMD_DOWN, | |
CMD_RIGHT, | |
CMD_LEFT, | |
CMD_PGUP, | |
CMD_PGDOWN | |
}; | |
#define MAX_COMMAND_STACK_SIZE 32 | |
volatile uint8_t g_commandStackTop = 0; | |
uint8_t g_commandStack[MAX_COMMAND_STACK_SIZE]; | |
void processEscape() | |
{ | |
if(g_commandStackTop >= MAX_COMMAND_STACK_SIZE) | |
{ | |
return; | |
} | |
if(g_escapeBufferLast >= 2) | |
{ | |
if((g_escapeBuffer[0] == 0x1B) && (g_escapeBuffer[1] == 0x5B)) | |
{ | |
if(g_escapeBufferLast == 2) | |
{ | |
switch(g_escapeBuffer[2]) | |
{ | |
case ESC_UP: | |
g_commandStack[g_commandStackTop++] = CMD_UP; | |
break; | |
case ESC_DOWN: | |
g_commandStack[g_commandStackTop++] = CMD_DOWN; | |
break; | |
case ESC_RIGHT: | |
g_commandStack[g_commandStackTop++] = CMD_RIGHT; | |
break; | |
case ESC_LEFT: | |
g_commandStack[g_commandStackTop++] = CMD_LEFT; | |
break; | |
} | |
} | |
else if((g_escapeBufferLast == 3) && (g_escapeBuffer[3] == 0x7E)) | |
{ | |
switch(g_escapeBuffer[2]) | |
{ | |
case ESC_PGUP: | |
g_commandStack[g_commandStackTop++] = CMD_PGUP; | |
break; | |
case ESC_PGDOWN: | |
g_commandStack[g_commandStackTop++] = CMD_PGDOWN; | |
break; | |
} | |
} | |
} | |
} | |
g_escapeBufferLast = 0; | |
} | |
// Shameless rip of http://www.mpp.mpg.de/~huber/vmssig/src/C/RMESCSEQ.C | |
char ANSIDecode(char data) | |
{ | |
if(g_inputState == ANSI_NORMAL) | |
{ | |
if(data == CH_ESCAPE) | |
{ | |
// A new escape sequence is starting. | |
g_inputState = ANSI_ESCAPE; | |
g_escapeBuffer[0] = data; | |
g_escapeBufferLast = 0; | |
} | |
else if(data == CH_CSI) | |
{ | |
// The escape sequence was restarted. | |
g_inputState = ANSI_CSI; | |
// Save sequence in case it has to be replayed. | |
g_escapeBuffer[0] = data; | |
g_escapeBuffer[1] = '['; | |
g_escapeBufferLast = 1; | |
} | |
else | |
{ | |
// This is a "standard" input. | |
return data; | |
} | |
return -1; | |
} | |
/* Escape sequence. */ | |
if((data < CH_SPACE) || (data == CH_CSI)) | |
{ | |
switch(data) | |
{ | |
case CH_CANCEL: | |
case CH_SUBSTITUTE: | |
// Cancel escape sequence and go back to normal. | |
g_inputState = ANSI_NORMAL; | |
g_ANSIStringReceived = false; | |
g_ANSIStringBuffer[0] = 0; | |
g_ANSIStringBufferLen = 0; | |
break; | |
case CH_BACKSPACE: | |
// Erase previous characters. | |
if((g_inputState == ANSI_CSI) && (g_escapeBufferLast == 1)) | |
{ | |
g_inputState = ANSI_ESCAPE; | |
g_escapeBufferLast = 0; | |
} | |
else if((g_inputState == ANSI_ESCAPE_SEQUENCE) && (g_escapeBufferLast == 1)) | |
{ | |
g_inputState = ANSI_ESCAPE; | |
g_escapeBufferLast = 0; | |
} | |
else if((g_inputState == ANSI_ESCAPE) && (g_escapeBufferLast == 0)) | |
{ | |
g_inputState = ANSI_NORMAL; | |
} | |
else if(g_inputState == ANSI_TERMINATOR) | |
{ | |
g_inputState = ANSI_STRING; | |
} | |
else if(g_inputState == ANSI_STRING) | |
{ | |
if(g_ANSIStringBufferLen > 0) | |
{ | |
--g_ANSIStringBufferLen; | |
} | |
else | |
{ | |
g_inputState = ANSI_ESCAPE; | |
} | |
} | |
else if(g_escapeBufferLast > 0) | |
{ | |
--g_escapeBufferLast; | |
} | |
break; | |
case CH_ESCAPE: | |
if(g_inputState == ANSI_STRING) | |
{ | |
g_inputState = ANSI_TERMINATOR; | |
} | |
else | |
{ | |
g_inputState = ANSI_ESCAPE; | |
g_escapeBuffer[0] = CH_ESCAPE; | |
g_escapeBufferLast = 0; | |
} | |
break; | |
case CH_CSI: | |
g_inputState = ANSI_CSI; | |
g_escapeBuffer[0] = CH_CSI; | |
g_escapeBuffer[1] = '['; | |
g_escapeBufferLast = 1; | |
break; | |
default: | |
if(data != CH_NUL) | |
{ | |
return data; | |
} | |
break; | |
} | |
return -1; | |
} | |
// Put bytes into the escape sequence buffer | |
if((g_inputState != ANSI_STRING) && (g_inputState != ANSI_TERMINATOR)) | |
{ | |
if(g_escapeBufferLast < MAX_ESCAPE_BUFFER_SIZE) | |
{ | |
g_escapeBuffer[++g_escapeBufferLast] = data; | |
} | |
} | |
switch(g_inputState) | |
{ | |
case ANSI_ESCAPE: | |
switch(data) | |
{ | |
case '[': | |
g_inputState = ANSI_CSI; | |
break; | |
case '_': | |
case 'P': | |
case 'Q': | |
case 'R': | |
case 'X': | |
case '^': | |
case ']': | |
g_inputState = ANSI_STRING; | |
g_ANSIStringReceived = true; | |
g_ANSIStringBufferLen = 0; | |
break; | |
default: | |
if((data > 57) && (data < 177)) | |
{ | |
g_inputState = ANSI_NORMAL; | |
return -2; | |
} | |
else | |
{ | |
g_inputState = ANSI_ESCAPE_SEQUENCE; | |
} | |
}; | |
break; | |
case ANSI_ESCAPE_SEQUENCE: | |
if((data > 57) && (data < 177)) | |
{ | |
g_inputState = ANSI_NORMAL; | |
return -2; | |
} | |
case ANSI_CSI: | |
if((data > 64) && (data < 177)) | |
{ | |
g_inputState = ANSI_NORMAL; | |
return -2; | |
} | |
case ANSI_STRING: | |
if(data == CH_ESCAPE) | |
{ | |
g_inputState = ANSI_TERMINATOR; | |
} | |
else if(data == CH_ST) | |
{ | |
g_inputState = ANSI_NORMAL; | |
g_ANSIStringReceived = false; | |
} | |
else if(g_ANSIStringReceived) | |
{ | |
if(g_ANSIStringBufferLen < MAX_ANSI_BUFFER_SIZE) | |
{ | |
g_ANSIStringBuffer[g_ANSIStringBufferLen++] = data; | |
} | |
else | |
{ | |
g_ANSIStringReceived = false; | |
g_ANSIStringBufferLen = 0; | |
g_inputState = ANSI_NORMAL; | |
} | |
} | |
break; | |
case ANSI_TERMINATOR: | |
if(data == '\\') | |
{ | |
g_inputState = ANSI_NORMAL; | |
g_ANSIStringReceived = false; | |
} | |
else | |
{ | |
if(data >= CH_SPACE) | |
{ | |
g_inputState = ANSI_STRING; | |
} | |
if(g_ANSIStringReceived && ((g_ANSIStringBufferLen+1) < MAX_ANSI_BUFFER_SIZE)) | |
{ | |
g_ANSIStringBuffer[g_ANSIStringBufferLen++] = CH_ESCAPE; | |
g_ANSIStringBuffer[g_ANSIStringBufferLen++] = data; | |
} | |
} | |
} | |
return -1; | |
} | |
void processInput(char data) | |
{ | |
if(data == 127) | |
{ | |
if(g_stringBufferLen > 0) | |
{ | |
Serial.print(data); | |
g_stringBuffer[--g_stringBufferLen] = '\0'; | |
} | |
} | |
else | |
{ | |
if((data == '\r') || (data == '\n')) | |
{ | |
data = '\0'; | |
Serial.println(); | |
g_stringBufferReady = true; | |
} | |
else | |
{ | |
Serial.print(data); | |
} | |
if(g_stringBufferLen < MAX_STRING_BUFFER_SIZE) | |
{ | |
g_stringBuffer[g_stringBufferLen++] = data; | |
} | |
} | |
} | |
void setup() | |
{ | |
g_stringBufferReady = false; | |
g_stringBufferLen = 0; | |
Serial.begin(115200); | |
Serial.println(g_welcomeMsg); | |
Serial.print('>'); | |
} | |
void serialEvent() | |
{ | |
while(Serial.available()) | |
{ | |
char data = Serial.read(); | |
char ret; | |
if((data > 127) && (data < 160) && (data != CH_CSI)) | |
{ | |
ret = ANSIDecode(CH_ESCAPE); | |
if(ret >= 0) | |
{ | |
data = ret; | |
processInput(data); | |
} | |
else if(ret == -2) | |
{ | |
processEscape(); | |
} | |
data = (data & 0x7f) | 0x40; | |
} | |
ret = ANSIDecode(data); | |
if(ret >= 0) | |
{ | |
processInput(data); | |
} | |
else if(ret == -2) | |
{ | |
processEscape(); | |
} | |
} | |
} | |
void loop() | |
{ | |
for(uint8_t i=0; i<g_commandStackTop; ++i) | |
{ | |
switch(g_commandStack[i]) | |
{ | |
case CMD_UP: | |
// todo | |
break; | |
case CMD_DOWN: | |
// todo | |
break; | |
case CMD_RIGHT: | |
// todo | |
break; | |
case CMD_LEFT: | |
// todo | |
break; | |
case CMD_PGUP: | |
// todo | |
break; | |
case CMD_PGDOWN: | |
// todo | |
break; | |
} | |
} | |
g_commandStackTop = 0; | |
if(g_stringBufferReady) | |
{ | |
uint8_t j=0; | |
for(uint8_t i=0; i<g_stringBufferLen; ++i) | |
{ | |
if(g_stringBuffer[i] == '\0') | |
{ | |
if((i-j) >= 1) | |
{ | |
Serial.print("String:"); | |
Serial.println(g_stringBuffer+j); | |
// todo | |
} | |
j = i+1; | |
} | |
} | |
g_stringBufferLen = 0; | |
Serial.print('>'); | |
g_stringBufferReady = false; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment