Support library for the ESP8266 Wireless Terminal. Can also be used for communicating with any VT100-compatible terminal.
espterm.cpp
- Committer:
- MightyPork
- Date:
- 2017-03-19
- Revision:
- 4:294e8f53ebcd
- Parent:
- 3:1114012184bf
- Child:
- 5:7379bd37f3e2
File content as of revision 4:294e8f53ebcd:
#include "mbed.h" #include "espterm.hpp" #include <cstdarg> void ESPTerm::init(Serial *s) { cs = -1; device_ok = false; on_mouse_click = NULL; on_button = NULL; on_esp_reset = NULL; on_char_rx = NULL; on_key_press = NULL; on_osc_rx = NULL; ser = s; ser->attach(callback(this, &ESPTerm::ser_rx_char), Serial::RxIrq); } ESPTerm::ESPTerm(Serial *s) { init(s); } ESPTerm::ESPTerm(void) { // Defaults init(new Serial(PA_2, PA_3, 115200)); } ESPTerm::ESPTerm(PinName txPin, PinName rxPin, int baud) { init(new Serial(txPin, rxPin, baud)); } // ----- Printing ----- int ESPTerm::printf(const char *format, ...) { std::va_list arg; va_start(arg, format); int r = ser->vprintf(format, arg); va_end(arg); return r; } // alias of printf int ESPTerm::print(const char *format, ...) { std::va_list arg; va_start(arg, format); int r = ser->vprintf(format, arg); va_end(arg); return r; } int ESPTerm::println(const char *format, ...) { std::va_list arg; va_start(arg, format); int r = ser->vprintf(format, arg); va_end(arg); r += ser->puts("\r\n"); return r; } // ----- Colors ----- void ESPTerm::fg(Color c) { int ci = c; if(ci > 7) { ci += (90-8); } else { ci += 30; } ser->printf("\033[%dm", ci); } void ESPTerm::bg(Color c) { int ci = c; if(ci > 7) { ci += (100-8); } else { ci += 40; } ser->printf("\033[%dm", ci); } void ESPTerm::reset_attribs(void) { ser->puts("\033[0m"); } // ----- Cursor control & erasing ----- void ESPTerm::go_to(int y, int x) { ser->printf("\033[%d;%dH", y, x); } void ESPTerm::clear_screen(ClearMode mode) { ser->printf("\033[%dJ", mode); } void ESPTerm::clear_line(ClearMode mode) { ser->printf("\033[%dK", mode); } void ESPTerm::screen_reset(void) { ser->puts("\033c"); } void ESPTerm::show_cursor(bool yes) { if (yes) ser->puts("\033[?25h"); else ser->puts("\033[?25l"); } // ----- System commands ----- void ESPTerm::factory_reset(void) { ser->puts("\033]FR\a"); } void ESPTerm::set_screen_size(int rows, int cols) { ser->printf("\033]W%d;%d\a", rows, cols); } bool ESPTerm::query_status(void) { if (device_ok) return true; ser->puts("\033[5n"); return false; } // ----- Rx command parsing ----- void ESPTerm::ser_rx_char(void) { while (ser->readable()) { char c = ser->getc(); ansi_parser(&c, 1); // pretend as if it was a string, this is safe } } void ESPTerm::apars_handle_plainchar(char c) { if (c == 24) { if (on_esp_reset) on_esp_reset(); } else if (c >= 1 && c <= 5) { if (on_button) on_button((int)c); } else { if (on_char_rx) on_char_rx(c); } } void ESPTerm::apars_handle_csi(char lead, const int* nums, char keychar) { // Keyboard events if (on_key_press) { if (keychar == 'A') on_key_press(KEY_UP); else if (keychar == 'B') on_key_press(KEY_DOWN); else if (keychar == 'C') on_key_press(KEY_RIGHT); else if (keychar == 'D') on_key_press(KEY_LEFT); } // Screen tapped / clicked if (on_mouse_click && keychar == 'M') { on_mouse_click(nums[0], nums[1]); } // "Device OK" response to "Device Status Query" if (keychar == 'n' && nums[0] == 0) { device_ok = true; } // ... other commands when added } void ESPTerm::apars_handle_osc(const char *str) { // ... additional parsing when needed in later esp term versions if (on_osc_rx) on_osc_rx(str); } void ESPTerm::apars_handle_badseq(void) { // Do nothing } /* ----- Ragel constants block ------ */ /* #line 38 "ansi_parser_cpp.c" */ static const int ansi_start = 1; //static const int ansi_first_final = 7; //static const int ansi_error = 0; //static const int ansi_en_CSI_body = 3; //static const int ansi_en_OSC_body = 5; //static const int ansi_en_main = 1; /* Ragel generated parser */ /** * \brief Linear ANSI chars stream parser (Rage generated) * * Parses a stream of bytes using a Ragel parser. The defined * grammar does not use 'unget', so the entire buffer is * always processed in a linear manner. * * \attention -> but always check the Ragel output for 'p--' * or 'p -=', that means trouble. * * \param newdata - array of new chars to process * \param len - length of the newdata buffer */ void ESPTerm::ansi_parser(const char *newdata, size_t len) { if (len == 0) len = strlen(newdata); // Load new data to Ragel vars const char *p = newdata; const char *eof = NULL; const char *pe = newdata + len; // Init Ragel on the first run if (cs == -1) { /* #line 77 "ansi_parser_cpp.c" */ { cs = ansi_start; } /* #line 65 "ansi_parser_cpp.rl" */ } // The parser /* #line 87 "ansi_parser_cpp.c" */ { if ( p == pe ) goto _test_eof; switch ( cs ) { tr0: /* #line 75 "ansi_parser_cpp.rl" */ { apars_handle_plainchar((*p)); } goto st1; st1: if ( ++p == pe ) goto _test_eof1; case 1: /* #line 103 "ansi_parser_cpp.c" */ if ( (*p) == 27 ) goto st2; goto tr0; st2: if ( ++p == pe ) goto _test_eof2; case 2: switch( (*p) ) { case 91: goto tr3; case 93: goto tr4; } goto tr2; tr2: /* #line 116 "ansi_parser_cpp.rl" */ { apars_handle_badseq(); {goto st1;} } // goto st0; /* #line 123 "ansi_parser_cpp.c" */ //st0: //cs = 0; // goto _out; tr3: /* #line 82 "ansi_parser_cpp.rl" */ { /* Reset the CSI builder */ csi_leading = csi_char = 0; csi_ni = 0; /* Zero out digits */ for(int i = 0; i < CSI_N_MAX; i++) { csi_n[i] = 0; } {goto st3;} } //goto st7; tr4: /* #line 129 "ansi_parser_cpp.rl" */ { /* Reset the OSC buffer */ osc_i = 0; {goto st5;} } //goto st7; //st7: // if ( ++p == pe ) // goto _test_eof7; case 7: /* #line 154 "ansi_parser_cpp.c" */ if ( (*p) == 27 ) goto st2; goto tr0; st3: if ( ++p == pe ) goto _test_eof3; case 3: if ( (*p) == 59 ) goto tr7; if ( (*p) < 60 ) { if ( (*p) > 47 ) { if ( 48 <= (*p) && (*p) <= 57 ) goto tr6; } else if ( (*p) >= 32 ) goto tr5; } else if ( (*p) > 64 ) { if ( (*p) > 90 ) { if ( 97 <= (*p) && (*p) <= 122 ) goto tr8; } else if ( (*p) >= 65 ) goto tr8; } else goto tr5; goto tr2; tr5: /* #line 95 "ansi_parser_cpp.rl" */ { csi_leading = (*p); } goto st4; tr6: /* #line 99 "ansi_parser_cpp.rl" */ { /* x10 + digit */ if (csi_ni < CSI_N_MAX) { csi_n[csi_ni] = csi_n[csi_ni]*10 + ((*p) - '0'); } } goto st4; tr7: /* #line 106 "ansi_parser_cpp.rl" */ { csi_ni++; } goto st4; st4: if ( ++p == pe ) goto _test_eof4; case 4: /* #line 204 "ansi_parser_cpp.c" */ if ( (*p) == 59 ) goto tr7; if ( (*p) < 65 ) { if ( 48 <= (*p) && (*p) <= 57 ) goto tr6; } else if ( (*p) > 90 ) { if ( 97 <= (*p) && (*p) <= 122 ) goto tr8; } else goto tr8; goto tr2; tr8: /* #line 110 "ansi_parser_cpp.rl" */ { csi_char = (*p); apars_handle_csi(csi_leading, csi_n, csi_char); {goto st1;} } // goto st8; //st8: // if ( ++p == pe ) // goto _test_eof8; case 8: /* #line 228 "ansi_parser_cpp.c" */ goto tr2; tr9: /* #line 135 "ansi_parser_cpp.rl" */ { if (osc_i < OSC_BUF_LEN-1) { osc_buff[osc_i++] = (*p); } } goto st5; st5: if ( ++p == pe ) goto _test_eof5; case 5: /* #line 242 "ansi_parser_cpp.c" */ switch( (*p) ) { case 7: goto tr10; case 27: goto st6; } goto tr9; tr10: /* #line 141 "ansi_parser_cpp.rl" */ { /** Terminate the buffer */ osc_buff[osc_i] = '\0'; apars_handle_osc(osc_buff); {goto st1;} } // goto st9; //st9: // if ( ++p == pe ) // goto _test_eof9; case 9: /* #line 262 "ansi_parser_cpp.c" */ goto tr2; st6: if ( ++p == pe ) goto _test_eof6; case 6: if ( (*p) == 92 ) goto tr10; goto tr2; } _test_eof1: cs = 1; goto _test_eof; _test_eof2: cs = 2; goto _test_eof; //_test_eof7: cs = 7; goto _test_eof; _test_eof3: cs = 3; goto _test_eof; _test_eof4: cs = 4; goto _test_eof; //_test_eof8: cs = 8; goto _test_eof; _test_eof5: cs = 5; goto _test_eof; //_test_eof9: cs = 9; goto _test_eof; _test_eof6: cs = 6; goto _test_eof; _test_eof: {} if ( p == eof ) { switch ( cs ) { case 1: case 2: case 3: case 4: case 5: case 6: /* #line 116 "ansi_parser_cpp.rl" */ { apars_handle_badseq(); {goto st1;} } // break; /* #line 298 "ansi_parser_cpp.c" */ } } //_out: {} } /* #line 162 "ansi_parser_cpp.rl" */ }