diff --git a/custom_16Mb.csv b/custom_16Mb.csv new file mode 100644 index 00000000..cff52140 --- /dev/null +++ b/custom_16Mb.csv @@ -0,0 +1,7 @@ +# https://docs.espressif.com/projects/esp-idf/en/v4.4.1/esp32s3/api-guides/partition-tables.html +# Name, Type, SubType, Offset, Size, Flags +nvs, data, nvs, 0x9000, 0x6000, +phy_init, data, phy, 0xf000, 0x1000, +factory, app, factory, 0x10000, 0x4E0000, +spiffs, data, spiffs, 0x4F0000,0x1100000, +coredump, data, coredump,0x15F0000,0x10000, diff --git a/src/core/VectorDisplay.h b/src/core/VectorDisplay.h new file mode 100755 index 00000000..2fa5332d --- /dev/null +++ b/src/core/VectorDisplay.h @@ -0,0 +1,1320 @@ +#ifndef _VECTOR_DISPLAY_H +#define _VECTOR_DISPLAY_H + +#ifndef ARDUINO +#define NO_SERIAL +#include +#define pgm_read_byte_near(a) (*(uint8_t*)(a)) + +#include +#include + +typedef std::string String; + +uint32_t millis() { + struct timeb t; + ftime(&t); + return t.millitm + t.time * 1000; +} + +class Print { +public: + virtual size_t write(uint8_t c) = 0; + virtual size_t write(const uint8_t* s, size_t length) { + size_t wrote = 0; + while (length > 0) { + size_t b = write(*s++); + if (b <= 0) + break; + b++; + length--; + } + return wrote; + } + + virtual size_t write(const char* s) { + return write((uint8_t*)s, strlen(s)); + } +}; + +class Stream : public Print { +public: + virtual int read() = 0; + virtual int available() = 0; +}; + +#else +#include +#endif + +#ifdef ESP8266 +# include +#endif + +#define VECTOR_DISPLAY_MESSAGE_SIZE 8 +#define VECTOR_DISPLAY_MAX_STRING 256 + +#define VECTOR_DISPLAY_DEFAULT_WIDTH 240 +#define VECTOR_DISPLAY_DEFAULT_HEIGHT 320 + +#define ALIGN_LEFT 'l' +#define ALIGN_RIGHT 'r' +#define ALIGN_CENTER 'c' +#define ALIGN_TOP 't' +#define ALIGN_BOTTOM 'b' +#define ALIGN_BASELINE 'l' + +#ifndef VECTOR_DISPLAY_SEND_DELAY +#define VECTOR_DISPLAY_SEND_DELAY 0 +#endif + +#define TFT_BLACK 0x0000 /* 0, 0, 0 */ +#define TFT_NAVY 0x000F /* 0, 0, 128 */ +#define TFT_DARKGREEN 0x03E0 /* 0, 128, 0 */ +#define TFT_DARKCYAN 0x03EF /* 0, 128, 128 */ +#define TFT_MAROON 0x7800 /* 128, 0, 0 */ +#define TFT_PURPLE 0x780F /* 128, 0, 128 */ +#define TFT_OLIVE 0x7BE0 /* 128, 128, 0 */ +#define TFT_LIGHTGREY 0xC618 /* 192, 192, 192 */ +#define TFT_DARKGREY 0x7BEF /* 128, 128, 128 */ +#define TFT_BLUE 0x001F /* 0, 0, 255 */ +#define TFT_GREEN 0x07E0 /* 0, 255, 0 */ +#define TFT_CYAN 0x07FF /* 0, 255, 255 */ +#define TFT_RED 0xF800 /* 255, 0, 0 */ +#define TFT_MAGENTA 0xF81F /* 255, 0, 255 */ +#define TFT_YELLOW 0xFFE0 /* 255, 255, 0 */ +#define TFT_WHITE 0xFFFF /* 255, 255, 255 */ +#define TFT_ORANGE 0xFD20 /* 255, 165, 0 */ +#define TFT_GREENYELLOW 0xAFE5 /* 173, 255, 47 */ +#define TFT_PINK 0xF81F + +// Color definitions for backwards compatibility +#define ILI9341_BLACK 0x0000 /* 0, 0, 0 */ +#define ILI9341_NAVY 0x000F /* 0, 0, 128 */ +#define ILI9341_DARKGREEN 0x03E0 /* 0, 128, 0 */ +#define ILI9341_DARKCYAN 0x03EF /* 0, 128, 128 */ +#define ILI9341_MAROON 0x7800 /* 128, 0, 0 */ +#define ILI9341_PURPLE 0x780F /* 128, 0, 128 */ +#define ILI9341_OLIVE 0x7BE0 /* 128, 128, 0 */ +#define ILI9341_LIGHTGREY 0xC618 /* 192, 192, 192 */ +#define ILI9341_DARKGREY 0x7BEF /* 128, 128, 128 */ +#define ILI9341_BLUE 0x001F /* 0, 0, 255 */ +#define ILI9341_GREEN 0x07E0 /* 0, 255, 0 */ +#define ILI9341_CYAN 0x07FF /* 0, 255, 255 */ +#define ILI9341_RED 0xF800 /* 255, 0, 0 */ +#define ILI9341_MAGENTA 0xF81F /* 255, 0, 255 */ +#define ILI9341_YELLOW 0xFFE0 /* 255, 255, 0 */ +#define ILI9341_WHITE 0xFFFF /* 255, 255, 255 */ +#define ILI9341_ORANGE 0xFD20 /* 255, 165, 0 */ +#define ILI9341_GREENYELLOW 0xAFE5 /* 173, 255, 47 */ +#define ILI9341_PINK 0xF81F + +#define MESSAGE_DOWN 'D' +#define MESSAGE_UP 'U' +#define MESSAGE_MOVE 'M' +#define MESSAGE_BUTTON 'B' +#define MESSAGE_ACK 'A' + +typedef uint32_t FixedPoint32; +#define TO_FP32(f) ((uint32_t)((f)*65536. + 0.5)) + +struct VectorDisplayMessage { + char what; + char what2; + union { + uint8_t button; + struct { + int16_t x; + int16_t y; + } xy; + } data; +} __attribute__((packed)); + +class VectorDisplayClass : public Print { +private: + static const uint32_t MAX_BUFFER = (uint32_t)1024*256; + static const uint32_t MESSAGE_TIMEOUT = 3000; + static const uint8_t FLAG_LOW_ENDIAN_BITS = 1; + static const uint8_t FLAG_HAVE_MASK = 2; + static const uint8_t FLAG_PAD_BYTE = 4; + static const uint8_t FLAG_LOW_ENDIAN_BYTES = 8; + + bool waitForAck = true; + int gfxFontSize = 1; + int curx = 0; + int cury = 0; + int readPos = 0; + int32_t curForeColor565 = -1; + uint32_t lastMessageStart = 0; + int pointerX; + int pointerY; + int curWidth = VECTOR_DISPLAY_DEFAULT_WIDTH; + int curHeight = VECTOR_DISPLAY_DEFAULT_HEIGHT; + uint8_t curRotation = 0; + bool pointerDown = false; + bool wrap = 1; + bool fixCP437 = true; + uint16_t polyLineCount; + uint8_t polyLineSum; + uint32_t delayTime = 0; + + uint8_t readBuf[VECTOR_DISPLAY_MESSAGE_SIZE]; + union { + uint32_t color; + uint16_t twoByte[9]; + struct { + uint16_t x; + uint16_t y; + char text[VECTOR_DISPLAY_MAX_STRING+1]; + } __attribute__((packed)) xyText; + struct { + uint16_t endianness; + uint16_t width; + uint16_t height; + FixedPoint32 aspectRatio; + uint16_t reserved[3]; + } __attribute__((packed)) initialize; + struct { + uint8_t c; + char text[VECTOR_DISPLAY_MAX_STRING+1]; + } __attribute__((packed)) charText; + struct { + uint16_t width; + uint16_t height; + } __attribute__((packed)) coords; + struct { + char attr; + uint8_t value; + } __attribute__((packed)) attribute8; + struct { + char attr; + uint16_t value; + } __attribute__((packed)) attribute16; + struct { + char attr; + uint32_t value; + } __attribute__((packed)) attribute32; + struct { + uint32_t length; + uint8_t depth; + uint8_t flags; + uint16_t x; + uint16_t y; + uint16_t w; + uint16_t h; + uint32_t foreColor; // only if depth==1 + uint32_t backColor; // only if depth==1 + } __attribute__((packed)) bitmap; + struct { + uint16_t x1; + uint16_t y1; + uint16_t x2; + uint16_t y2; + uint16_t r; + uint8_t filled; + } __attribute__((packed)) roundedRectangle; + struct { + uint16_t x; + uint16_t y; + uint16_t r; + FixedPoint32 angle1; + FixedPoint32 sweep; + uint8_t filled; + } __attribute__((packed)) arc; + struct { + char attr; + uint16_t values[2]; + } __attribute__((packed)) attribute16x2; + uint8_t bytes[VECTOR_DISPLAY_MAX_STRING+1]; + char text[VECTOR_DISPLAY_MAX_STRING+1]; + } args; + uint32_t lastSend = 0; + +private: + inline void sendDelay() { + if (delayTime>0) { + while(millis()-lastSend < delayTime) ; + lastSend = millis(); + } + } + +public: + int textsize = 1; + uint32_t textcolor = TFT_WHITE; + uint32_t textbgcolor = TFT_BLACK; + + void setWaitForAck(bool wait) { + waitForAck = wait; + } + + void setDelay(uint32_t delayMillis) { + delayTime = delayMillis; + lastSend = millis(); + } + + virtual void remoteFlush() { + while(remoteAvailable()) + remoteRead(); + } + virtual int remoteRead() = 0; // must be non-blocking + virtual void remoteWrite(uint8_t c) = 0; + virtual void remoteWrite(const void* data, size_t n) = 0; + virtual size_t remoteAvailable() = 0; + + void attribute8(char a, uint8_t value) { + args.attribute8.attr = a; + args.attribute8.value = value; + sendCommand('Y', &args, 2); + } + + void attribute8(char a, bool value) { + args.attribute8.attr = a; + args.attribute8.value = value ? 1 : 0; + sendCommand('Y', &args, 2); + } + + void attribute16(char a, uint16_t value) { + args.attribute16.attr = a; + args.attribute16.value = value; + sendCommand('A', &args, 3); + } + + void attribute32(char a, uint32_t value) { + args.attribute32.attr = a; + args.attribute32.value = value; + sendCommand('B', &args, 5); + } + + void sendCommand(char c, const void* arguments, int argumentsLength) { + sendDelay(); + remoteWrite(c); + remoteWrite(c^0xFF); + if (argumentsLength > 0) + remoteWrite((uint8_t*)arguments, argumentsLength); + uint8_t sum = 0; + for (int i = 0; i 0) + s += *p++; + return s; + } + + void startPoly(char c, uint16_t n) { + polyLineCount = n; + remoteWrite(c); + remoteWrite(c^0xFF); + args.twoByte[0] = n; + remoteWrite((uint8_t*)&args, 2); + polyLineSum = args.bytes[0] + args.bytes[1]; + } + + void startFillPoly(uint16_t n) { + startPoly('N', n); + } + + void startPolyLine(uint16_t n) { + startPoly('O', n); + } + + void addPolyLine(int16_t x, int16_t y) { + if (polyLineCount>0) { + args.twoByte[0] = x; + args.twoByte[1] = y; + remoteWrite((uint8_t*)&args, 4); + polyLineSum += args.bytes[0] + args.bytes[1] + args.bytes[2] + args.bytes[3]; + polyLineCount--; + if (polyLineCount == 0) { + remoteWrite(0xFF^polyLineSum); + } + } + } + + void line(int x1, int y1, int x2, int y2) { + args.twoByte[0] = x1; + args.twoByte[1] = y1; + args.twoByte[2] = x2; + args.twoByte[3] = y2; + sendCommand('L', &args, 8); + } + + void fillRectangle(int x1, int y1, int x2, int y2) { + args.twoByte[0] = x1; + args.twoByte[1] = y1; + args.twoByte[2] = x2; + args.twoByte[3] = y2; + sendCommand('R', &args, 8); + } + + void rectangle(int x1, int y1, int x2, int y2, bool fill=false) { + if (fill) + fillRectangle(x1,y1,x2,y2); + else { + startPolyLine(4); + addPolyLine(x1,y1); + addPolyLine(x2,y1); + addPolyLine(x2,y2); + addPolyLine(x1,y2); + } + } + + void roundedRectangle(int x1, int y1, int x2, int y2, int r, bool fill) { + args.roundedRectangle.filled = fill ? 1 : 0; + args.roundedRectangle.x1 = x1; + args.roundedRectangle.x2 = x2; + args.roundedRectangle.y1 = y1; + args.roundedRectangle.y2 = y2; + args.roundedRectangle.r = r; + sendCommand('Q', &args, 11); + } + + void roundedRectangle(int x1, int y1, int x2, int y2, int r) { + roundedRectangle(x1,y1,x2,y2,r,false); + } + + void fillRoundedRectangle(int x1, int y1, int x2, int y2, int r) { + roundedRectangle(x1,y1,x2,y2,r,true); + } + + void fillTriangle(int x1, int y1, int x2, int y2, int x3, int y3) { + args.twoByte[0] = x1; + args.twoByte[1] = y1; + args.twoByte[2] = x2; + args.twoByte[3] = y2; + args.twoByte[4] = x3; + args.twoByte[5] = y3; + sendCommand('G', &args, 12); + } + +/* void initialize() { + args.twoByte[0] = 0x1234; // endianness detector + args.twoByte[1] = 0; + sendCommandWithAck('H', &args, 4); + } */ + + void initialize(int w=VECTOR_DISPLAY_DEFAULT_WIDTH, int h=VECTOR_DISPLAY_DEFAULT_HEIGHT) { + args.initialize.endianness = 0x1234; // endianness detector + args.initialize.width = w; + args.initialize.height = h; + args.initialize.aspectRatio = TO_FP32(1.); + args.initialize.reserved[0] = 0; + args.initialize.reserved[1] = 0; + args.initialize.reserved[2] = 0; + curWidth = w; + curHeight = h; + + sendCommandWithAck('Z', &args, 16); + } + + void fillCircle(int x, int y, int r) { + args.twoByte[0] = x; + args.twoByte[1] = y; + args.twoByte[2] = r; + sendCommand('J', &args, 6); + } + + void circle(int x, int y, int r) { + args.twoByte[0] = x; + args.twoByte[1] = y; + args.twoByte[2] = r; + sendCommand('I', &args, 6); + } + + void point(int x, int y) { + args.twoByte[0] = x; + args.twoByte[1] = y; + sendCommand('P', &args, 4); + } + + void arc(int x, int y, int r, FixedPoint32 angle1, FixedPoint32 sweep, bool fill=false) { + args.arc.x = x; + args.arc.y = y; + args.arc.r = r; + args.arc.angle1 = angle1; + args.arc.sweep = sweep; + args.arc.filled = fill ? 1 : 0; + sendCommand('S', &args, 15); + } + + void arc(int x, int y, int r, float angle1, float sweep, bool fill=false) { + arc(x,y,r,TO_FP32(angle1),TO_FP32(sweep),fill); + } + + // 32-bit fixed point + void textSize(FixedPoint32 s) { + args.attribute32.attr = 's'; + args.attribute32.value = s; + sendCommand('B', &args, 5); + } + + void text(int x, int y, const char* str, int n) { + args.xyText.x = x; + args.xyText.y = y; + if (n>VECTOR_DISPLAY_MAX_STRING) + n = VECTOR_DISPLAY_MAX_STRING; + strncpy(args.xyText.text, str, n); + + if (fixCP437) { + for (int i=0;i=176) + args.xyText.text[i]++; + } + } + args.xyText.text[n] = 0; + sendCommand('T', &args, 4+strlen(args.xyText.text)+1); + } + + void text(int x, int y, const char* str) { + text(x, y, str, strlen(str)); + } + + void text(int x, int y, String str) { + text(x,y,str.c_str(), str.length()); + } + + void deleteButton(uint8_t command) { + sendCommand('D', &command, 1); + } + + void addButton(uint8_t command, const char* str) { + args.charText.c = command; + strncpy(args.charText.text, str, VECTOR_DISPLAY_MAX_STRING); + args.charText.text[VECTOR_DISPLAY_MAX_STRING] = 0; + sendCommand('U', &args, 1+strlen(args.charText.text)+1); + } + + void addButton(uint8_t command, String str) { + addButton(command, str.c_str()); + } + + void toast(const char* str, unsigned n) { + if (VECTOR_DISPLAY_MAX_STRING < n) + n = VECTOR_DISPLAY_MAX_STRING; + strncpy(args.text, str, n); + args.text[n] = 0; + sendCommand('M', &args, n+1); + } + + void toast(const char* str) { + toast(str, strlen(str)); + } + + void toast(String text) { + toast(text.c_str(), text.length()); + } + + void foreColor(uint32_t color) { + args.attribute32.attr = 'f'; + args.attribute32.value = color; + sendCommand('B', &args, 5); + curForeColor565 = -1; + } + + void backColor(uint32_t color) { + args.attribute32.attr = 'b'; + args.attribute32.value = color; + sendCommand('B', &args, 5); + } + + void textBackColor(uint32_t color) { + args.attribute32.attr = 'k'; + args.attribute32.value = color; + sendCommand('B', &args, 5); + } + + void textForeColor(uint32_t color) { + args.attribute32.attr = 'F'; + args.attribute32.value = color; + sendCommand('B', &args, 5); + } + + void foreColor565(uint16_t color) { + args.attribute16.attr = 'f'; + args.attribute16.value = color; + sendCommand('A', &args, 3); + curForeColor565 = color; + } + + void backColor565(uint16_t color) { + args.attribute16.attr = 'b'; + args.attribute16.value = color; + sendCommand('A', &args, 3); + } + + void textBackColor565(uint16_t color) { + args.attribute16.attr = 'k'; + args.attribute16.value = color; + sendCommand('A', &args, 3); + } + + void textForeColor565(uint16_t color) { + args.attribute16.attr = 'F'; + args.attribute16.value = color; + sendCommand('A', &args, 3); + } + + void rounded(uint8_t value) { + args.attribute8.attr = 'n'; + args.attribute8.value = value ? 1 : 0; + sendCommand('Y', &args, 2); + } + + void thickness(FixedPoint32 t) { + args.attribute32.attr = 't'; + args.attribute32.value = t; + sendCommand('B', &args, 5); + } + + void pixelAspectRatio(FixedPoint32 a) { + args.attribute32.attr = 'a'; + args.attribute32.value = a; + sendCommand('B', &args, 5); + } + +#ifdef SUPPORT_FLOATING_POINT + inline void setThickness(double thickness) { + setThickness(TO_FP32(thickness)); + } + + inline void setPixelAspectRatio(double aspect) { + setThickness(TO_FP32(aspect)); + } +#endif + + void clear() { + sendCommand('C', NULL, 0); + } + + void update() { + sendCommand('F', NULL, 0); + } + +/* void reset() { + sendCommandWithAck('E', NULL, 0); + } */ + + void coordinates(int width, int height) { + args.attribute16x2.attr = 'c'; + curWidth = width; + curHeight = height; + args.attribute16x2.values[0] = width; + args.attribute16x2.values[1] = height; + sendCommandWithAck('B', &args, 5); + } + + void continuousUpdate(bool value) { + args.attribute8.attr = 'c'; + args.attribute8.value = value ? 1 : 0; + sendCommand('Y', &args, 2); + } + + void textHorizontalAlign(char hAlign) { + args.attribute8.attr = 'h'; + args.attribute8.value = hAlign; + sendCommand('Y', &args, 2); + } + + void textVerticalAlign(char hAlign) { + args.attribute8.attr = 'v'; + args.attribute8.value = hAlign; + sendCommand('Y', &args, 2); + } + + void textOpaqueBackground(bool opaque) { + args.attribute8.attr = 'o'; + args.attribute8.value = opaque ? 1 : 0; + sendCommand('Y', &args, 2); + } + + void textBold(bool bold) { + args.attribute8.attr = 'b'; + args.attribute8.value = bold ? 1 : 0; + sendCommand('Y', &args, 2); + } + + bool isTouchDown() { + return pointerDown; + } + + int getTouchX() { + return pointerX; + } + + int getTouchY() { + return pointerY; + } + + bool readMessage(VectorDisplayMessage* msg) { + while (remoteAvailable()) { + uint8_t c = remoteRead(); + + if (0 < readPos && millis()-lastMessageStart > MESSAGE_TIMEOUT) + readPos = 0; + + if (2 <= readPos) { + readBuf[readPos++] = c; + if (readPos >= VECTOR_DISPLAY_MESSAGE_SIZE) { + readPos = 0; + if (msg != NULL) + memcpy(msg, readBuf, sizeof(VectorDisplayMessage)); + else + msg = (VectorDisplayMessage*)readBuf; + + if (msg->what == MESSAGE_DOWN || msg->what == MESSAGE_UP || msg->what == MESSAGE_MOVE) { + pointerDown = msg->what != MESSAGE_UP; + pointerX = msg->data.xy.x; + pointerY = msg->data.xy.y; + } + return true; + } + continue; + } + + if (1 <= readPos) { + if ( (*readBuf == 'U' && c == 'P') || + (*readBuf == 'D' && c == 'N') || + (*readBuf == 'M' && c == 'V') || + (*readBuf == 'B' && c == 'T') || + (*readBuf == 'A' && c == 'c') + ) { + readBuf[readPos++] = c; + continue; + } + readPos = 0; + } + if (readPos == 0 && (c == 'U' || c == 'D' || c == 'M' || c == 'B' || c == 'A')) { + readBuf[readPos++] = c; + lastMessageStart = millis(); + } + } + return false; + } + + uint32_t color565To8888(uint16_t c) { + return 0xFF000000 | ((((c>>11) & 0x1F) * 255 / 0x1F) << 16) | ((((c>>5) & 0x3F) * 255 / 0x3F) << 8) | ((c & 0x1F) * 255 / 0x1F); + } + + uint16_t color565(uint8_t r, uint8_t g, uint8_t b) { + return ((r & 0xF8) << 8) | ((g & 0xFC) << 3) | (b >> 3); + } + + uint32_t getBitmap1Size(int16_t w, int16_t h, uint8_t flags=0) { + return (flags & FLAG_PAD_BYTE) ? ((uint32_t)w+7)/8*h : ((uint32_t)w*h+7)/8; + } + + uint32_t getBitmapSize(int16_t w, int16_t h, uint8_t depth=1, uint8_t flags=0) { + if (depth==1) { + return getBitmap1Size(w,h,flags); + } + else { + return w*h*(depth/8); + } + } + + /*TODO: stubs*/ + void* createSprite(int16_t width, int16_t height, uint8_t frames = 1) { return NULL; } + void pushSprite(int32_t x, int32_t y) {} + void deleteSprite(void) {} + void fillSprite(uint32_t color) {} + + void bitmap_progmem(int16_t x, int16_t y, const uint8_t* bmp, + int16_t w, int16_t h, uint8_t depth=1, uint8_t flags=0, const uint8_t* mask=NULL, + uint32_t foreColor=0xFFFFFFFF, + uint32_t backColor=0x00FFFFFF) /* PROGMEM */ { + if (mask != NULL) + flags |= FLAG_HAVE_MASK; + uint32_t bitmapSize = getBitmapSize(w,h,depth,flags); + int headerSize = depth==1 ? 22 : 14; + uint32_t maskSize = mask == NULL ? 0 : getBitmap1Size(w,h,flags); + uint32_t fullSize = bitmapSize + headerSize + maskSize; + + if (fullSize + 1 > MAX_BUFFER) + return; + + sendDelay(); + remoteWrite('K'); + remoteWrite('K'^0xFF); + args.bitmap.length = fullSize; + args.bitmap.depth = 1; + args.bitmap.flags = flags; + args.bitmap.x = x; + args.bitmap.y = y; + args.bitmap.w = w; + args.bitmap.h = h; + if (depth == 1) { + args.bitmap.foreColor = foreColor; + args.bitmap.backColor = backColor; + } + + uint8_t sum = sumBytes(&args, headerSize); + remoteWrite(&args,headerSize); + for (uint32_t i=0; i MAX_BUFFER) + return; + + sendDelay(); + remoteWrite('K'); + remoteWrite('K'^0xFF); + args.bitmap.length = fullSize; + args.bitmap.depth = depth; + args.bitmap.flags = flags; + args.bitmap.x = x; + args.bitmap.y = y; + args.bitmap.w = w; + args.bitmap.h = h; + if (depth == 1) { + args.bitmap.foreColor = foreColor; + args.bitmap.backColor = backColor; + } + remoteWrite(&args,headerSize); + remoteWrite(bmp,bitmapSize); + uint8_t sum = sumBytes(&args, headerSize) + sumBytes((void*)bmp, bitmapSize); + if (maskSize > 0) { + remoteWrite(mask,maskSize); + sum += sumBytes((void*)mask, maskSize); + } + remoteWrite(sum^0xFF); + } + + void utf8() { + fixCP437 = false; + args.attribute8.attr = 'i'; + args.attribute8.value = 0; + sendCommand('Y', &args, 2); + } + + /* The following are meant to be compatible with Adafruit GFX */ + void cp437(bool s) { + // if true, activates real cp437 mode; if false, activates buggy Arduino compatible cp437 mode + fixCP437 = !s; + args.attribute8.attr = 'i'; + args.attribute8.value = 1; + sendCommand('Y', &args, 2); + } + + void setRotation(uint8_t r) { + args.attribute8.attr = 'r'; + args.attribute8.value = r; + curRotation = r & 3; + sendCommand('Y', &args, 2); + } + + void setTextSize(uint8_t size) { + gfxFontSize = size; + textsize = size; + textSize((FixedPoint32)size * 8 * 65536); + } + + void setTextColor(uint16_t f, uint16_t b) { + textBackColor565(b); + textForeColor565(f); + textcolor = f; + textbgcolor = b; + textOpaqueBackground(true); + } + + void setTextColor(uint16_t f) { + textForeColor565(f); + textcolor = f; + textOpaqueBackground(false); + } + + void setCursor(int16_t x, int16_t y) { + curx = x; + cury = y; + } + + + int16_t getCursorX(void) { + return curx; + } + + int16_t getCursorY(void) { + return cury; + } + + void setTextWrap(bool w) { + wrap = w; + } + + int16_t drawRightString(const char *string, int32_t x, int32_t y, uint8_t font) { + // TODO: add spaces + return drawString(string, x, y); + } + + int16_t drawRightString(const String& string, int32_t x, int32_t y, uint8_t font) { + return drawRightString(string.c_str(), x, y, font); + } + + int16_t drawCentreString(const char *string, int32_t x, int32_t y, uint8_t font) { + // TODO: add spaces + return drawString(string, x, y); + } + + + int16_t drawCentreString(const String& string, int32_t x, int32_t y, uint8_t font) { + return drawCentreString(string.c_str(), x, y, font); + } + + int16_t drawString(const String& string, int32_t x, int32_t y) { + return drawString(string.c_str(), x, y); + } + + int16_t drawString(const char *string, int32_t x, int32_t y) { + setCursor(x, y); + return write(string); + }; + + int16_t drawChar(uint16_t uniCode, int32_t x, int32_t y) { + setCursor(x, y); + return write(uniCode); + } + + // TODO: fix back color handling + size_t write(uint8_t c) override { + if (wrap && curx + 5*gfxFontSize>width()) { + curx = 0; + cury += 8*gfxFontSize; + } + text(curx, cury, (char*)&c, 1); + curx += 5*gfxFontSize; + return 0; + } + + // TODO: fix back color handling + size_t write(const char* s) /*override*/ { //ESP8266 core doesn't supply write(const char*) + int l = strlen(s); + int w = width(); + if (!wrap || curx + 5*gfxFontSize*l <= w) { + text(curx, cury, s); + curx += 5*gfxFontSize*l; + } + else { + while(l>0) { + int end = ((int)w-curx)/(5*gfxFontSize); + if (end <= 0) { + curx = 0; + cury += 8*gfxFontSize; + end = w/(5*gfxFontSize); + } + if (end > l) + end = l; + text(curx, cury, s, end); + l-=end; + s += end; + curx = 5*gfxFontSize*end; + } + } + return 0; + } + + void drawPixel(int16_t x, int16_t y, uint16_t color) { + if (color != curForeColor565) { + foreColor565(color); + } + point(x, y); + } + + void drawRect(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t color) { + if (color != curForeColor565) { + foreColor565(color); + } + rectangle(x,y,x+w-1,y+h-1); + } + + void fillRect(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t color) { + if (color != curForeColor565) { + foreColor565(color); + } + fillRectangle(x,y,x+w-1,y+h-1); + } + + void drawFastHLine(int16_t x, int16_t y, int16_t w, uint16_t color) { + if (color != curForeColor565) { + foreColor565(color); + } + line(x,y,x+w,y); + } + + void drawLine(int16_t x, int16_t y, int16_t x2, int16_t y2, uint16_t color) { + if (color != curForeColor565) { + foreColor565(color); + } + line(x,y,x2,y2); + } + + + // Draw an anti-aliased wide line from ax,ay to bx,by width wd with radiused ends (radius is wd/2) + // If bg_color is not included the background pixel colour will be read from TFT or sprite + void drawWideLine(float ax, float ay, float bx, float by, float wd, uint32_t fg_color, uint32_t bg_color = 0x00FFFFFF) { + drawRect(ax, ay, wd, abs(ay-by), fg_color); + } + + // As per "drawSmoothArc" except the ends of the arc are NOT anti-aliased, this facilitates dynamic arc length changes with + // arc segments and ensures clean segment joints. + // The sides of the arc are anti-aliased by default. If smoothArc is false sides will NOT be anti-aliased + void drawArc(int32_t x, int32_t y, int32_t r, int32_t ir, uint32_t startAngle, uint32_t endAngle, uint32_t fg_color, uint32_t bg_color, bool smoothArc = true){ + // TODO + drawRect(x, y, r*2, ir*2, fg_color); + } + + void drawSmoothArc(int32_t x, int32_t y, int32_t r, int32_t ir, uint32_t startAngle, uint32_t endAngle, uint32_t fg_color, uint32_t bg_color, bool roundEnds = false) { + drawArc(x, y, r, ir, startAngle, endAngle, fg_color, bg_color); + } + + // Draw an anti-aliased filled circle at x, y with radius r + // If bg_color is not included the background pixel colour will be read from TFT or sprite + void fillSmoothCircle(int32_t x, int32_t y, int32_t r, uint32_t color, uint32_t bg_color = 0x00FFFFFF) { + fillCircle(x, y, r, color) ; + } + + void drawSmoothRoundRect(int32_t x, int32_t y, int32_t r, int32_t ir, int32_t w, int32_t h, uint32_t fg_color, uint32_t bg_color = 0x00FFFFFF, uint8_t quadrants = 0xF) { + drawRoundRect(x, y, w, h, r, fg_color); + } + + // Draw a filled rounded rectangle , corner radius r and bounding box defined by x,y and w,h + void fillSmoothRoundRect(int32_t x, int32_t y, int32_t w, int32_t h, int32_t radius, uint32_t color, uint32_t bg_color = 0x00FFFFFF) { + fillRoundRect(x, y, w, h, radius, color); + } + + + void drawFastVLine(int16_t x, int16_t y, int16_t h, uint16_t color) { + if (color != curForeColor565) { + foreColor565(color); + } + line(x,y,x,y+h); + } + + void fillScreen(uint16_t color) { + backColor565(color); + clear(); + backColor(0xFF000000); + } + + void drawCircle(int16_t x, int16_t y, int16_t r, uint16_t color) { + if (color != curForeColor565) { + foreColor565(color); + } + circle(x,y,r); + } + + void fillCircle(int16_t x, int16_t y, int16_t r, uint16_t color) { + if (color != curForeColor565) { + foreColor565(color); + } + fillCircle(x,y,r); + } + + void fillEllipse(int16_t x, int16_t y, int32_t rx, int32_t ry, uint16_t color) { + // TODO + if(rx0) { + rectangle(cx-r,cy,cx,cy+delta,false); + rectangle(cx-r,cy,cx,cy+delta,true); + } + } + if (corners & 1) { + arc(cx,cy,r,TO_FP32(270),TO_FP32(180),false); // drawing edges separately makes things fit better + arc(cx,cy,r,TO_FP32(270),TO_FP32(180),true); + arc(cx,cy+delta,r,TO_FP32(270),TO_FP32(180),false); + arc(cx,cy+delta,r,TO_FP32(270),TO_FP32(180),true); + if (delta>0) { + rectangle(cx,cy,cx+r,cy+delta,false); + rectangle(cx,cy,cx+r,cy+delta,true); + } + } + } + + /* the following Adafruit GFX APIs are not implemented at present */ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" + void drawChar(int16_t x, int16_t y, unsigned char c, uint16_t color, + uint16_t bg, uint8_t size) {} + void setFont(const void /*GFXfont*/ *f = NULL) {} + void getTextBounds(const char *string, int16_t x, int16_t y, + int16_t *x1, int16_t *y1, uint16_t *w, uint16_t *h) {} + void getTextBounds(const void /*__FlashStringHelper*/ *s, int16_t x, int16_t y, + int16_t *x1, int16_t *y1, uint16_t *w, uint16_t *h) {} +#pragma GCC diagnostic pop +}; + +class SerialDisplayClass : public VectorDisplayClass { + private: + Stream& s; + const bool doSerialBegin; + + public: + virtual int remoteRead() override { + return s.read(); + } + + virtual void remoteWrite(uint8_t c) override { + s.write(c); + } + + virtual void remoteWrite(const void* data, size_t n) override { + s.write((uint8_t*)data, n); + } + + /* only works with the Serial object; do not call externally without it */ + void begin(uint32_t speed, int width=VECTOR_DISPLAY_DEFAULT_WIDTH, int height=VECTOR_DISPLAY_DEFAULT_HEIGHT) { +#ifndef NO_SERIAL + if (doSerialBegin) { + Serial.begin(speed); + while(!Serial) ; + } +#endif + VectorDisplayClass::begin(width, height); + } + + virtual void begin(int width=VECTOR_DISPLAY_DEFAULT_WIDTH, int height=VECTOR_DISPLAY_DEFAULT_HEIGHT) override { + begin(115200, width, height); + } + + virtual size_t remoteAvailable() override { + return s.available(); + } + +#ifndef NO_SERIAL + SerialDisplayClass() : s(Serial), doSerialBegin(true) {} +#endif + + SerialDisplayClass(Stream& _s) : s(_s), doSerialBegin(false) {} +}; + +#ifdef ESP8266 +class WiFiDisplayClass : public SerialDisplayClass { + private: + WiFiClient client; + public: + bool begin(const char* host, int width=VECTOR_DISPLAY_DEFAULT_WIDTH, int height=VECTOR_DISPLAY_DEFAULT_HEIGHT) { + VectorDisplayClass::begin(width, height); + return client.connect(host, 7788); + } + + virtual void end() override { + VectorDisplayClass::end(); + client.stop(); + } + + WiFiDisplayClass() : SerialDisplayClass(client) { + } +}; +#endif + +#endif diff --git a/src/core/display.cpp b/src/core/display.cpp index 9d0687f8..e7bb720a 100644 --- a/src/core/display.cpp +++ b/src/core/display.cpp @@ -272,6 +272,9 @@ int getBattery() { float b = axp192.GetBatVoltage(); percent = ((b - 3.0) / 1.2) * 100; + #elif defined (ESP32S3DEVKITC1) + // do nothing + #else #if defined(CARDPUTER) diff --git a/src/core/globals.h b/src/core/globals.h index 9e5b0fa0..e782cc55 100644 --- a/src/core/globals.h +++ b/src/core/globals.h @@ -6,7 +6,6 @@ extern char16_t FGCOLOR; #define BGCOLOR TFT_BLACK #include -#include #include #include //#include @@ -28,9 +27,24 @@ extern char16_t FGCOLOR; #endif // Declaração dos objetos TFT -extern TFT_eSPI tft; -extern TFT_eSprite sprite; -extern TFT_eSprite draw; +#if defined(ESP32S3DEVKITC1) + #if defined(USE_DUMB_DISPLAY) + #include "esp32dumbdisplay.h" + extern GraphicalDDLayer tft; + #else + //#include + #include "VectorDisplay.h" + extern SerialDisplayClass tft; + extern SerialDisplayClass& sprite; + extern SerialDisplayClass& draw; + #endif +#else + #include + extern TFT_eSPI tft; + extern TFT_eSprite sprite; + extern TFT_eSprite draw; +#endif + extern char timeStr[10]; diff --git a/src/core/mykeyboard.cpp b/src/core/mykeyboard.cpp index f49411b0..ea157b25 100644 --- a/src/core/mykeyboard.cpp +++ b/src/core/mykeyboard.cpp @@ -12,6 +12,8 @@ bool checkNextPress(){ #if defined (CARDPUTER) Keyboard.update(); if(Keyboard.isKeyPressed('/') || Keyboard.isKeyPressed('.')) + #elif defined (ESP32S3DEVKITC1) + if(touchRead(DW_BTN) // for PRIu64 -#ifndef STICK_C_PLUS +//#ifndef STICK_C_PLUS +#ifdef CARDPUTER #include #include #endif @@ -202,7 +203,7 @@ void handleSerialCommands() { } } // endof rf - #ifndef STICK_C_PLUS + #if defined(CARDPUTER) if(cmd_str.startsWith("music_player " ) || cmd_str.startsWith("tts" ) || cmd_str.startsWith("say" ) ) { // TODO: move in audio.cpp module AudioOutputI2S *audioout = new AudioOutputI2S(); // https://github.com/earlephilhower/ESP8266Audio/blob/master/src/AudioOutputI2S.cpp#L32 diff --git a/src/main.cpp b/src/main.cpp index 5b873d55..a2e5af38 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -40,14 +40,29 @@ std::vector>> options; const int bufSize = 4096; uint8_t buff[4096] = {0}; // Protected global variables -TFT_eSPI tft = TFT_eSPI(); // Invoke custom library -TFT_eSprite sprite = TFT_eSprite(&tft); -TFT_eSprite draw = TFT_eSprite(&tft); +#if defined(ESP32S3DEVKITC1) + #if defined(USE_DUMB_DISPLAY) + //TFT_eSPI tft = TFT_eSPI(); https://github.com/trevorwslee/Arduino-DumbDisplay/issues/169 + GraphicalDDLayer tft; + #else + SerialDisplayClass tft; + SerialDisplayClass& sprite = tft; + SerialDisplayClass& draw = tft; + #endif +#else + TFT_eSPI tft = TFT_eSPI(); // Invoke custom library + TFT_eSprite sprite = TFT_eSprite(&tft); + TFT_eSprite draw = TFT_eSprite(&tft); +#endif + #if defined(CARDPUTER) Keyboard_Class Keyboard = Keyboard_Class(); #elif defined (STICK_C_PLUS) AXP192 axp192; +//#elif defined (ESP32S3DEVKITC1) +//TODO: onscreen keyboard +// #endif #include "Wire.h" @@ -92,7 +107,18 @@ void setup_gpio() { ** Config tft *********************************************************************/ void begin_tft(){ - tft.init(); + #if defined(ESP32S3DEVKITC1) + #if defined(USE_DUMB_DISPLAY) + DumbDisplay dumbdisplay(new DDInputOutput(115200)); + GraphicalDDLayer *graphical = dumbdisplay.createGraphicalLayer(WIDTH, HEIGHT); + tft = *graphical; + #else + tft.begin(); //115200, 240,320); + tft.clear(); + #endif + #else + tft.init(); + #endif rotation = gsetRotation(); tft.setRotation(rotation); resetTftDisplay();