Public Access
258 lines
5.3 KiB
C++
258 lines
5.3 KiB
C++
#include "command_parser.h"
|
|||
|
|
|
||
|
|
#include "settings.h"
|
||
|
|
|
||
|
|
// ---------------------------------------------------------------------------
|
||
|
|
// Hex helpers
|
||
|
|
// ---------------------------------------------------------------------------
|
||
|
|
|
||
|
|
static bool isHex(char c)
|
||
|
|
{
|
||
|
|
return (c >= '0' && c <= '9') || (c >= 'A' && c <= 'F') || (c >= 'a' && c <= 'f');
|
||
|
|
}
|
||
|
|
|
||
|
|
static uint8_t hex2dec(char c)
|
||
|
|
{
|
||
|
|
if (c >= '0' && c <= '9')
|
||
|
|
return c - '0';
|
||
|
|
if (c >= 'A' && c <= 'F')
|
||
|
|
return c - 'A' + 10;
|
||
|
|
if (c >= 'a' && c <= 'f')
|
||
|
|
return c - 'a' + 10;
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
static uint8_t hex2byte(const char *s)
|
||
|
|
{
|
||
|
|
return (hex2dec(s[0]) << 4) | hex2dec(s[1]);
|
||
|
|
}
|
||
|
|
|
||
|
|
static bool isBitmapCommand(char c)
|
||
|
|
{
|
||
|
|
return c == 'b' || c == 'B';
|
||
|
|
}
|
||
|
|
|
||
|
|
// Length of a bitmap header: BxxyywwhhC
|
||
|
|
static const uint8_t BITMAP_HEADER_LEN = 10;
|
||
|
|
|
||
|
|
// ---------------------------------------------------------------------------
|
||
|
|
// Input handling
|
||
|
|
// ---------------------------------------------------------------------------
|
||
|
|
|
||
|
|
CommandParser::CommandParser(BROSE9323 &display, Print &reply) : _display(display), _reply(reply)
|
||
|
|
{
|
||
|
|
}
|
||
|
|
|
||
|
|
void CommandParser::feed(char c)
|
||
|
|
{
|
||
|
|
if (_bitmap_active)
|
||
|
|
{
|
||
|
|
if (c == '\n')
|
||
|
|
_bitmap_active = false;
|
||
|
|
else
|
||
|
|
_bitmapData(c);
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (c == '\n')
|
||
|
|
{
|
||
|
|
if (!_discard_line)
|
||
|
|
_runLine();
|
||
|
|
_line_len = 0;
|
||
|
|
_discard_line = false;
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
if (c == '\r' || _discard_line)
|
||
|
|
return;
|
||
|
|
if (_line_len >= LINE_MAX)
|
||
|
|
{
|
||
|
|
_error(F("Line too long"));
|
||
|
|
_discard_line = true;
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
_line[_line_len++] = c;
|
||
|
|
_frame_ended = false;
|
||
|
|
|
||
|
|
// Bitmap header complete: draw the pixel data as it arrives
|
||
|
|
if (_line_len == BITMAP_HEADER_LEN && isBitmapCommand(_line[0]))
|
||
|
|
{
|
||
|
|
_line_len = 0;
|
||
|
|
if (!_bitmapStart())
|
||
|
|
_discard_line = true;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
bool CommandParser::frameEnded() const
|
||
|
|
{
|
||
|
|
return _frame_ended;
|
||
|
|
}
|
||
|
|
|
||
|
|
// Run one complete command line (without the '\n')
|
||
|
|
void CommandParser::_runLine()
|
||
|
|
{
|
||
|
|
if (_line_len == 0)
|
||
|
|
return;
|
||
|
|
_line[_line_len] = '\0';
|
||
|
|
|
||
|
|
char cmd = _line[0];
|
||
|
|
switch (cmd)
|
||
|
|
{
|
||
|
|
case 'b':
|
||
|
|
case 'B':
|
||
|
|
// Complete bitmap headers are handled in feed()
|
||
|
|
_error(F("Incomplete bitmap header"));
|
||
|
|
break;
|
||
|
|
case 'd':
|
||
|
|
case 'D':
|
||
|
|
_display.display();
|
||
|
|
_frame_ended = true;
|
||
|
|
break;
|
||
|
|
case 'f':
|
||
|
|
case 'F':
|
||
|
|
_fill();
|
||
|
|
break;
|
||
|
|
case 'p':
|
||
|
|
case 'P':
|
||
|
|
_print();
|
||
|
|
break;
|
||
|
|
case 'r':
|
||
|
|
case 'R':
|
||
|
|
_display.printBuffer();
|
||
|
|
_reply.flush();
|
||
|
|
break;
|
||
|
|
case 's':
|
||
|
|
case 'S':
|
||
|
|
_pixel(cmd == 'S');
|
||
|
|
break;
|
||
|
|
case 't':
|
||
|
|
case 'T':
|
||
|
|
_timing();
|
||
|
|
break;
|
||
|
|
default:
|
||
|
|
#ifdef DEBUG
|
||
|
|
_reply.print(F("0x"));
|
||
|
|
_reply.print(cmd >> 4, HEX);
|
||
|
|
_reply.print(cmd & 0xF, HEX);
|
||
|
|
_reply.println(F(" - command not known."));
|
||
|
|
#endif
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
void CommandParser::_error(const __FlashStringHelper *msg)
|
||
|
|
{
|
||
|
|
_reply.print(F("ERR: "));
|
||
|
|
_reply.println(msg);
|
||
|
|
}
|
||
|
|
|
||
|
|
// ---------------------------------------------------------------------------
|
||
|
|
// Commands
|
||
|
|
// ---------------------------------------------------------------------------
|
||
|
|
|
||
|
|
// BxxyywwhhcBITMAP (xx/ww = 8 bit hex x position/width; yy/hh = 8 bit hex y position/height;
|
||
|
|
// c = 0/1 color; BITMAP = one hex digit per 4 pixels, row by row, width must be a multiple of 4)
|
||
|
|
bool CommandParser::_bitmapStart()
|
||
|
|
{
|
||
|
|
_bitmap_x = hex2byte(_line + 1);
|
||
|
|
_bitmap_y = hex2byte(_line + 3);
|
||
|
|
_bitmap_w = hex2byte(_line + 5);
|
||
|
|
_bitmap_h = hex2byte(_line + 7);
|
||
|
|
_bitmap_color = _line[9] != '0';
|
||
|
|
if (_bitmap_w == 0 || _bitmap_w % 4)
|
||
|
|
{
|
||
|
|
_error(F("Invalid bitmap width (not dividable by 4)"));
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
if (_bitmap_h == 0)
|
||
|
|
{
|
||
|
|
_error(F("Invalid bitmap height (0)"));
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
_bitmap_nibble = 0;
|
||
|
|
_bitmap_active = true;
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
|
||
|
|
void CommandParser::_bitmapData(char c)
|
||
|
|
{
|
||
|
|
if (!isHex(c))
|
||
|
|
return;
|
||
|
|
uint16_t pixel = _bitmap_nibble * 4;
|
||
|
|
if (pixel >= (uint16_t)_bitmap_w * _bitmap_h)
|
||
|
|
return; // more data than the bitmap size, ignore
|
||
|
|
uint8_t b = hex2dec(c);
|
||
|
|
for (uint8_t i = 0; i < 4; i++)
|
||
|
|
{
|
||
|
|
if (b & (0x8 >> i))
|
||
|
|
{
|
||
|
|
uint16_t p = pixel + i;
|
||
|
|
_display.drawPixel(_bitmap_x + p % _bitmap_w, _bitmap_y + p / _bitmap_w, _bitmap_color);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
_bitmap_nibble++;
|
||
|
|
}
|
||
|
|
|
||
|
|
// Fc (c = 0/1 color)
|
||
|
|
void CommandParser::_fill()
|
||
|
|
{
|
||
|
|
if (_line_len < 2)
|
||
|
|
{
|
||
|
|
_error(F("Missing color"));
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
_display.fillScreen(_line[1] != '0');
|
||
|
|
}
|
||
|
|
|
||
|
|
// PxxyycsTEXT (xx = 8 bit hex x position; yy = 8 bit hex y position; c = 0/1 color;
|
||
|
|
// s = 1-9 size; TEXT = string, newline = escaped \n)
|
||
|
|
void CommandParser::_print()
|
||
|
|
{
|
||
|
|
if (_line_len < 7)
|
||
|
|
{
|
||
|
|
_error(F("Incomplete print command"));
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
bool color = _line[5] != '0';
|
||
|
|
_display.setCursor(hex2byte(_line + 1), hex2byte(_line + 3));
|
||
|
|
_display.setTextColor(color, !color);
|
||
|
|
_display.setTextSize(_line[6] - '0');
|
||
|
|
for (uint8_t i = 7; i < _line_len; i++)
|
||
|
|
{
|
||
|
|
if (_line[i] == '\\' && _line[i + 1] == 'n')
|
||
|
|
{
|
||
|
|
_display.println();
|
||
|
|
i++;
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
_display.write(_line[i]);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// sxxyyc / Sxxyyc (xx = 8 bit hex x position; yy = 8 bit hex y position; c = 0/1 color)
|
||
|
|
// s only changes the buffer, S flips the dot immediately
|
||
|
|
void CommandParser::_pixel(bool direct)
|
||
|
|
{
|
||
|
|
if (_line_len < 6)
|
||
|
|
{
|
||
|
|
_error(F("Incomplete pixel command"));
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
_display.setDirect(direct);
|
||
|
|
_display.drawPixel(hex2byte(_line + 1), hex2byte(_line + 3), hex2dec(_line[5]));
|
||
|
|
_display.setDirect(false);
|
||
|
|
}
|
||
|
|
|
||
|
|
// Tttt (ttt = decimal flip time in us), stored in EEPROM
|
||
|
|
void CommandParser::_timing()
|
||
|
|
{
|
||
|
|
long t = atol(_line + 1);
|
||
|
|
if (t < FLIP_TIME_MIN || t > FLIP_TIME_MAX)
|
||
|
|
{
|
||
|
|
_error(F("Flip time out of range (50-2000 us)"));
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
_display.setTiming(t);
|
||
|
|
saveFlipTime(t);
|
||
|
|
}
|