/* Patch class for 32x16 RGB Matrix panels reimplement all functions which use x,y coordinates */ #ifndef ESP_HUB75_32x16MatrixPanel #define ESP_HUB75_32x16MatrixPanel #include "ESP32-HUB75-MatrixPanel-I2S-DMA.h" #include #include "glcdfont.c" struct VirtualCoords { int16_t x; int16_t y; }; #define VP_WIDTH 32 #define VP_HEIGHT 16 #define DEFAULT_FONT_W 5 #define DEFAULT_FONT_H 7 #define PIXEL_SPACE 1 // space between chars in a string /* --- PRINTF_BYTE_TO_BINARY macro's --- */ #define PRINTF_BINARY_PATTERN_INT8 "%c%c%c%c%c%c%c%c" #define PRINTF_BYTE_TO_BINARY_INT8(i) \ (((i) & 0x80ll) ? '1' : '0'), \ (((i) & 0x40ll) ? '1' : '0'), \ (((i) & 0x20ll) ? '1' : '0'), \ (((i) & 0x10ll) ? '1' : '0'), \ (((i) & 0x08ll) ? '1' : '0'), \ (((i) & 0x04ll) ? '1' : '0'), \ (((i) & 0x02ll) ? '1' : '0'), \ (((i) & 0x01ll) ? '1' : '0') #define PRINTF_BINARY_PATTERN_INT16 \ PRINTF_BINARY_PATTERN_INT8 PRINTF_BINARY_PATTERN_INT8 #define PRINTF_BYTE_TO_BINARY_INT16(i) \ PRINTF_BYTE_TO_BINARY_INT8((i) >> 8), PRINTF_BYTE_TO_BINARY_INT8(i) /* --- end macros --- */ class QuarterScanMatrixPanel : public Adafruit_GFX { public: MatrixPanel_I2S_DMA *display; QuarterScanMatrixPanel(MatrixPanel_I2S_DMA &disp) : Adafruit_GFX(64, 32) { this->display = &disp; size_x = size_y = 1 ; wrap = false; cursor_x = cursor_y = 0; dir = 1; loop = true; } VirtualCoords getCoords(int16_t x, int16_t y); int16_t getVirtualX(int16_t x) { VirtualCoords coords = getCoords(x, 0); return coords.x; } int16_t getVirtualY(int16_t y) { VirtualCoords coords = getCoords(0,y); return coords.y; } // int16_t getVirtualY(int16_t y) {return getCoords(0,y).y;} /** extende function to draw lines/rects/... **/ virtual uint8_t width() {return VP_WIDTH;}; virtual uint8_t height() {return VP_HEIGHT;}; virtual void drawLine(int16_t x0, int16_t y0, int16_t x1, int16_t y1, uint16_t color); virtual void drawHLine(int16_t x0, int16_t y0, int16_t w, uint16_t color); virtual void drawVLine(int16_t x0, int16_t y0, int16_t h, uint16_t color); virtual void fillRect(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t color); virtual void drawChar(int16_t x, int16_t y, unsigned char c, uint16_t color, uint16_t bg, uint8_t size); virtual void drawChar(int16_t x, int16_t y, unsigned char c, uint16_t color, uint16_t bg, uint8_t size_x, uint8_t size_y); virtual void scrollChar(int16_t x, int16_t y, unsigned char c, uint16_t color, uint16_t bg, uint16_t dir, uint16_t speed); virtual void drawString(int16_t x, int16_t y, unsigned char* c, uint16_t color, uint16_t bg); virtual size_t write(unsigned char c); // write a character on current cursor postion virtual size_t write(const char *str); // write a character array (string) on curreont cursor postion virtual void setTextWrap(bool w); virtual void setCursor (int16_t x, int16_t y); void setTextFGColor(uint16_t color) {textFGColor = color;}; void setTextBGColor(uint16_t color) {textBGColor = color;}; void setTextSize(uint8_t x, uint8_t y) {size_x = x; size_y = y;}; // magnification, default = 1 void setScrollDir(uint8_t d = 1) { dir = (d != 1) ? 0 : 1;}; // set scroll dir default = 1 void setScroolLoop (bool b = true) { loop = b;} ; // scroll text in a loop, default true void scrollText(const char *str, uint16_t speed, uint16_t pixels); /**------------------------------------------**/ // equivalent methods of the matrix library so it can be just swapped out. virtual void drawPixel(int16_t x, int16_t y, uint16_t color); virtual void fillScreen(uint16_t color); // overwrite adafruit implementation void clearScreen() { fillScreen(0); } void drawPixelRGB565(int16_t x, int16_t y, uint16_t color); void drawPixelRGB888(int16_t x, int16_t y, uint8_t r, uint8_t g, uint8_t b); void drawPixelRGB24(int16_t x, int16_t y, RGB24 color); void drawIcon (int *ico, int16_t x, int16_t y, int16_t module_cols, int16_t module_rows); uint16_t color444(uint8_t r, uint8_t g, uint8_t b) { return display->color444(r, g, b); } uint16_t color565(uint8_t r, uint8_t g, uint8_t b) { return display->color565(r, g, b); } uint16_t color333(uint8_t r, uint8_t g, uint8_t b) { return display->color333(r, g, b); } void flipDMABuffer() { display->flipDMABuffer(); } void showDMABuffer() { display->showDMABuffer(); } void drawDisplayTest(); protected: int16_t cursor_x, cursor_y; // Cursor position uint8_t size_x, size_y; // Font size Multiplikator default = 1 => 5x7 Font (5widht,7Height) uint16_t textFGColor, textBGColor; bool wrap ; // < If set, 'wrap' text at right edge of display uint8_t dir ; // used for scrolling text direction bool loop ; // used for scrolling text in a loop private: VirtualCoords coords; }; // end Class header /*************************************************************************************** @brief scroll text from right to left or vice versa on current cursor position please note, this function is not interruptable. @param *c pointer to \0 terminated string @param pixels number of pixels to scroll, if 0, than scroll complete text @param speed velocity of scrolling in ms ***************************************************************************************/ void QuarterScanMatrixPanel::scrollText(const char *str,uint16_t speed, uint16_t pixels = 0) { // first we put all columns of every char inside str into a big array of lines // than we move through this arry and draw line per line and move this line // one position to dir const uint8_t xSize = 6; uint16_t len = strlen(str); uint8_t array[len * xSize]; // size of array number of chars * width of char //uint16_t lenArray = sizeof(array)/sizeof(array[0]); uint16_t aPtr = 0; // // generate array char c = *str; // Serial.printf("size *str (%d), size array: (%d) \n", len, lenArray); while (c) { // Serial.printf("** %c ** \n", c); // read font line per line. A line is a column inside a char for (int8_t i = 0; i < 5; i++) { uint8_t line = pgm_read_byte(&font[c * 5 + i]); array[aPtr++] = line; // Serial.printf("%d - Line " PRINTF_BINARY_PATTERN_INT8 "\n", i, PRINTF_BYTE_TO_BINARY_INT8(line) ); } str++; c = *str; array[aPtr++] = 0x00; // line with 0 (space between chars) } array[aPtr++] = 0x00; // line with 0 (space between chars) /* Serial.printf("---------------------------- \n"); for (aPtr=0; aPtr < (len*xSize); aPtr++) { Serial.printf("%d - Line " PRINTF_BINARY_PATTERN_INT8 "\n", aPtr, PRINTF_BYTE_TO_BINARY_INT8(array[aPtr]) ); } */ int16_t x,y,lastX, p; lastX = (dir) ? VP_WIDTH : 0; x = cursor_x; y = cursor_y; Serial.printf("X: %d, Y: %d \n", x,y); p=0; pixels = (pixels) ? pixels : len * xSize; while (p <= pixels) { // remove last pixel positions fillRect(x,y,5,7,textBGColor); // set new pixel position x = (dir) ? lastX - p : lastX + p - pixels; // iterator through our array for (uint8_t i=0; i < (len*xSize); i++) { uint8_t line = array[i]; //Serial.printf("%d:%d : " PRINTF_BINARY_PATTERN_INT8 "\n", x, i, PRINTF_BYTE_TO_BINARY_INT8(line) ); // read line and shift from right to left // start with bit 0 (top of char) to 7(bottom) for (uint8_t j=0; j < 8; j++, line>>=1) { if (line & 1) { // got 1, if x + i outside panel ignore pixel if (x + i >= 0 && x + i < VP_WIDTH) { drawPixel(x + i, y + j, textFGColor); } } else { // got 0 if (x + i >= 0 && x + i < VP_WIDTH) { drawPixel(x + i, y + j, textBGColor); } } // if } // for j } // for i p++; delay(speed); } // while } inline size_t QuarterScanMatrixPanel::write(const char *str) { uint8_t x, y; x=cursor_x; y=cursor_y; char c = *str; while (c) { //Serial.printf("%c ", c); write(c); str++; c = *str; x = x + ((DEFAULT_FONT_W + PIXEL_SPACE) * size_x); setCursor(x,y); } Serial.printf("\n"); return 1; } inline size_t QuarterScanMatrixPanel::write(unsigned char c) { Serial.printf("\twrite(%d, %d, %c)\n", cursor_x, cursor_y, c); drawChar(cursor_x, cursor_y, c, textFGColor, textBGColor, size_x, size_y); return 1; } void QuarterScanMatrixPanel::setTextWrap(bool w) {this->display->setTextWrap(w);} void QuarterScanMatrixPanel::setCursor(int16_t x, int16_t y) { cursor_x = x; cursor_y = y; } /* - new for 16x32 panels - */ inline void QuarterScanMatrixPanel::drawLine(int16_t x, int16_t y, int16_t x1, int16_t y1, uint16_t color) { int16_t a,b; for (a=x; a <= x1; a++) { for (b=y; b <= y1; b++) { drawPixel(a,b,color); } } } inline void QuarterScanMatrixPanel::drawHLine(int16_t x, int16_t y, int16_t w, uint16_t color) { drawLine(x,y,x+w,y, color); } inline void QuarterScanMatrixPanel::drawVLine(int16_t x, int16_t y, int16_t h, uint16_t color) { drawLine(x,y,x,y+h, color); } inline void QuarterScanMatrixPanel::fillRect(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t color) { for (int16_t i = x; i < x + w; i++) { drawVLine(i, y, h, color); } } void QuarterScanMatrixPanel::drawChar(int16_t x, int16_t y, unsigned char c, uint16_t color, uint16_t bg, uint8_t size) { drawChar(x,y,c,color, bg, size, size); } inline void QuarterScanMatrixPanel::scrollChar(int16_t x, int16_t y, unsigned char c, uint16_t color, uint16_t bg, uint16_t dir, uint16_t speed){ if ((x >= VP_WIDTH) || (y >= VP_HEIGHT) || ((x + 6 * size_x-1) < 0) || ((y + 8 * size_y-1) <0)) return; setTextWrap(true); // text wrap is only for the right end of the panel, to scroll soft out of the left of panel // algorithm should wrap the character from left to right // loop s = scroll-loop, scrolls char 5 pixels into dir uint8_t lastX = x; for (int8_t s = 0; s < 6; s++) { // loop i : width of a character Serial.printf("X:%d ", x); // clear current position fillRect(x,y,5,7,0); x = lastX - s; for (int8_t i = 0; i < 5; i++) { // first line is the firste vertical part of a character and 8bits long // last bit is everytime 0 // we read 5 lines with 8 bit (5x7 char + 8bit with zeros) // Example : char A (90deg cw) // 01111100 // 00010010 // 00010001 // 00010010 // 01111100 uint8_t line = pgm_read_byte(&font[c * 5 + i]); // shift from right to left bit per bit // loop j = height of a character // loop through a colunm of currenc character Serial.printf("i:%d ", i); // ignore all pixels outside panel if (x+i >= VP_WIDTH) continue; for (int8_t j=0; j < 8; j++, line >>= 1) { if (line & 1) { Serial.printf (" ON %d", x+i); // we read 1 if (x >= 0) { drawPixel(x+i, y+j, color); } else if (x+i >= 0) { drawPixel(x+i, y+j, color); } } else if (bg != color) { // we read 0 Serial.printf(" OFF %d", x+i); if (x >= 0) { drawPixel(x+i, y+j, bg); } else if (x+i >= 0) { drawPixel(x+i, y+j, bg); } } } } Serial.printf("\n"); delay(speed); } } inline void QuarterScanMatrixPanel::drawChar(int16_t x, int16_t y, unsigned char c, uint16_t color, uint16_t bg, uint8_t size_x, uint8_t size_y) { //Serial.printf("unmapped : drawChar(%d, %d, %c) \n",x, y, c); // note: remapping to 16x32 coordinats is done inside drawPixel() or fillRect if ((x >= VP_WIDTH) || (y >= VP_HEIGHT) || ((x + 6 * size_x-1) < 0) || ((y + 8 * size_y-1) <0)) return; //Serial.printf("Font-Array : %d \n", sizeof(font)); for (int8_t i = 0; i < 5; i++) { uint8_t line = pgm_read_byte(&font[c * 5 + i]); //Serial.printf("%d - Line " PRINTF_BINARY_PATTERN_INT8 "\n", i, PRINTF_BYTE_TO_BINARY_INT8(line) ); for (int8_t j = 0; j < 8; j++, line >>= 1) { if (line & 1) { if (size_x == 1 && size_y == 1) //Serial.printf(""); drawPixel(x + i, y + j, color); else // remark: it's important to call function with orgininal coordinates for x/y fillRect(x + i * size_x, y + j * size_y, size_x, size_y, color); } else if (bg != color) { if (size_x == 1 && size_y == 1) drawPixel(x + i, y + j, bg); else // remark: it's important to call function with orgininal coordinates for x/y fillRect(x + i * size_x, y + j * size_y, size_x, size_y, bg); } } } } inline void QuarterScanMatrixPanel::drawString(int16_t x, int16_t y, unsigned char* c, uint16_t color, uint16_t bg) { } inline VirtualCoords QuarterScanMatrixPanel::getCoords(int16_t x, int16_t y) { const int y_remap[] = { 0,1,8,9,4,5,12,13,16,17,24,25,22,23,30,31 }; if (y > VP_HEIGHT) y = VP_HEIGHT; if (x > VP_WIDTH) x = VP_WIDTH; coords.x = x + VP_WIDTH; coords.y = y_remap[y]; return coords; } /* -------------------------*/ inline void QuarterScanMatrixPanel::drawPixel(int16_t x, int16_t y, uint16_t color) { VirtualCoords coords = getCoords(x, y); this->display->drawPixel(coords.x, coords.y, color); } inline void QuarterScanMatrixPanel::fillScreen(uint16_t color) // adafruit virtual void override { // No need to map this. this->display->fillScreen(color); } inline void QuarterScanMatrixPanel::drawPixelRGB565(int16_t x, int16_t y, uint16_t color) { VirtualCoords coords = getCoords(x, y); this->display->drawPixelRGB565( coords.x, coords.y, color); } inline void QuarterScanMatrixPanel::drawPixelRGB888(int16_t x, int16_t y, uint8_t r, uint8_t g, uint8_t b) { VirtualCoords coords = getCoords(x, y); this->display->drawPixelRGB888( coords.x, coords.y, r, g, b); } inline void QuarterScanMatrixPanel::drawPixelRGB24(int16_t x, int16_t y, RGB24 color) { VirtualCoords coords = getCoords(x, y); this->display->drawPixelRGB24(coords.x, coords.y, color); } // need to recreate this one, as it wouldnt work to just map where it starts. inline void QuarterScanMatrixPanel::drawIcon (int *ico, int16_t x, int16_t y, int16_t module_cols, int16_t module_rows) { } #endif