#include #include // 4.2" b/w/r //#include // 2.9" b/w/r //#include //#include //#include //#include #include GxEPD_BitmapExamples // FreeFonts from Adafruit_GFX #include #include #include #include #include #include #include #include #include #include #include //#if defined(ESP8266) //GxIO_Class io(SPI, /*CS=D8*/ SS, /*DC=D3*/ 0, /*RST=D4*/ 2); // arbitrary selection of D3(=0), D4(=2), selected for default of GxEPD_Class //GxEPD_Class display(io, /*RST=D4*/ 2, /*BUSY=D2*/ 4); // default selection of D4(=2), D2(=4) //#elif defined(ESP32) GxIO_Class io(SPI, /*CS=5*/ SS, /*DC=*/ 17, /*RST=*/ 16); // arbitrary selection of 17, 16 GxEPD_Class display(io, /*RST=*/ 16, /*BUSY=*/ 4); // arbitrary selection of (16), 4 //GxIO_Class io(SPI, SS, 22, 21); //GxEPD_Class display(io, 16, 4); //#elif defined(ARDUINO_ARCH_SAMD) //GxIO_Class io(SPI, /*CS=*/ 4, /*DC=*/ 7, /*RST=*/ 6); //GxEPD_Class display(io, /*RST=*/ 6, /*BUSY=*/ 5); //#elif defined(ARDUINO_GENERIC_STM32F103C) && defined(MCU_STM32F103C8) //GxIO_Class io(SPI, /*CS=*/ SS, /*DC=*/ 3, /*RST=*/ 2); //GxEPD_Class display(io, /*RST=*/ 2, /*BUSY=*/ 1); //#elif defined(ARDUINO_GENERIC_STM32F103V) && defined(MCU_STM32F103VB) //GxIO_Class io(SPI, /*CS=*/ SS, /*DC=*/ PE15, /*RST=*/ PE14); // DC, RST as wired by DESPI-M01 //GxEPD_Class display(io, /*RST=*/ PE14, /*BUSY=*/ PE13); // RST, BUSY as wired by DESPI-M01 //#elif defined(ARDUINO_AVR_MEGA2560) // select one, depending on your CS connection //GxIO_Class io(SPI, /*CS=*/ SS, /*DC=*/ 8, /*RST=*/ 9); // arbitrary selection of 8, 9 selected for default of GxEPD_Class //GxIO_Class io(SPI, /*CS=*/ 10, /*DC=*/ 8, /*RST=*/ 9); // arbitrary selection of 8, 9, CS on 10 (for CS same as on UNO, for SPI on ICSP use) // //GxEPD_Class display(io, /*RST=*/ 9, /*BUSY=*/ 7); // default selection of (9), 7 //#else // //GxIO_Class io(SPI, /*CS=*/ SS, /*DC=*/ 8, /*RST=*/ 9); // arbitrary selection of 8, 9 selected for default of GxEPD_Class // //GxEPD_Class display(io, /*RST=*/ 9, /*BUSY=*/ 7); // default selection of (9), 7 //#endif class Dashboard { private: static const uint16_t input_buffer_pixels = 800; // may affect performance static const uint16_t max_row_width = 1448; // for up to 6" display 1448x1072 static const uint16_t max_palette_pixels = 256; // for depth <= 8 uint16_t read16(File& f); uint32_t read32(File& f); uint8_t input_buffer[3 * input_buffer_pixels]; // up to depth 24 uint8_t output_row_mono_buffer[max_row_width / 8]; // buffer for at least one row of b/w bits uint8_t output_row_color_buffer[max_row_width / 8]; // buffer for at least one row of color bits uint8_t mono_palette_buffer[max_palette_pixels / 8]; // palette buffer for depth <= 8 b/w uint8_t color_palette_buffer[max_palette_pixels / 8]; // palette buffer for depth <= 8 c/w uint16_t rgb_palette_buffer[max_palette_pixels]; // palette buffer for depth <= 8 for buffered graphics, needed for 7-color display void drawBitmapFromSD(File file, int16_t x, int16_t y, bool with_color); public: Dashboard(); void drawBitmapFile(File file); void drawLogo(File f); }; uint16_t read16(File& f) { // BMP data is stored little-endian, same as Arduino. uint16_t result; ((uint8_t *)&result)[0] = f.read(); // LSB ((uint8_t *)&result)[1] = f.read(); // MSB return result; } uint32_t read32(File& f) { // BMP data is stored little-endian, same as Arduino. uint32_t result; ((uint8_t *)&result)[0] = f.read(); // LSB ((uint8_t *)&result)[1] = f.read(); ((uint8_t *)&result)[2] = f.read(); ((uint8_t *)&result)[3] = f.read(); // MSB return result; } static const uint16_t input_buffer_pixels = 800; // may affect performance static const uint16_t max_row_width = 1448; // for up to 6" display 1448x1072 static const uint16_t max_palette_pixels = 256; // for depth <= 8 uint8_t input_buffer[3 * input_buffer_pixels]; // up to depth 24 uint8_t output_row_mono_buffer[max_row_width / 8]; // buffer for at least one row of b/w bits uint8_t output_row_color_buffer[max_row_width / 8]; // buffer for at least one row of color bits uint8_t mono_palette_buffer[max_palette_pixels / 8]; // palette buffer for depth <= 8 b/w uint8_t color_palette_buffer[max_palette_pixels / 8]; // palette buffer for depth <= 8 c/w uint16_t rgb_palette_buffer[max_palette_pixels]; // palette buffer for depth <= 8 for buffered graphics, needed for 7-color display void drawBitmapFromSD(File file, int16_t x, int16_t y, bool with_color) //void drawBitmapFromSD(const char *filename, int16_t x, int16_t y, bool with_color) { bool valid = false; // valid format to be handled bool flip = true; // bitmap is stored bottom-to-top bool has_multicolors = false;//display.epd2.panel == GxEPD2::ACeP565; uint32_t startTime = millis(); if ((x >= display.width()) || (y >= display.height())) return; Serial.println(); Serial.print("Loading image '"); /*Serial.print(filename); Serial.println('\''); #if defined(ESP32) file = SD.open(String("/") + filename, FILE_READ); if (!file) { Serial.print("File not found"); return; } #else file = SD.open(filename); if (!file) { Serial.print("File not found"); return; } #endif*/ // Parse BMP header if (read16(file) == 0x4D42) // BMP signature { uint32_t fileSize = read32(file); uint32_t creatorBytes = read32(file); //(void)creatorBytes; //unused uint32_t imageOffset = read32(file); // Start of image data uint32_t headerSize = read32(file); uint32_t width = read32(file); int32_t height = (int32_t) read32(file); uint16_t planes = read16(file); uint16_t depth = read16(file); // bits per pixel uint32_t format = read32(file); if ((planes == 1) && ((format == 0) || (format == 3))) // uncompressed is handled, 565 also { Serial.print("File size: "); Serial.println(fileSize); Serial.print("Image Offset: "); Serial.println(imageOffset); Serial.print("Header size: "); Serial.println(headerSize); Serial.print("Bit Depth: "); Serial.println(depth); Serial.print("Image size: "); Serial.print(width); Serial.print('x'); Serial.println(height); // BMP rows are padded (if needed) to 4-byte boundary uint32_t rowSize = (width * depth / 8 + 3) & ~3; if (depth < 8) rowSize = ((width * depth + 8 - depth) / 8 + 3) & ~3; if (height < 0) { height = -height; flip = false; } uint16_t w = width; uint16_t h = height; if ((x + w - 1) >= display.width()) w = display.width() - x; if ((y + h - 1) >= display.height()) h = display.height() - y; //if (w <= max_row_width) // handle with direct drawing { valid = true; uint8_t bitmask = 0xFF; uint8_t bitshift = 8 - depth; uint16_t red, green, blue; bool whitish = false; bool colored = false; if (depth == 1) with_color = false; if (depth <= 8) { if (depth < 8) bitmask >>= depth; //file.seek(54); //palette is always @ 54 file.seek(imageOffset - (4 << depth)); //54 for regular, diff for colorsimportant for (uint16_t pn = 0; pn < (1 << depth); pn++) { blue = file.read(); green = file.read(); red = file.read(); file.read(); whitish = with_color ? ((red > 0x80) && (green > 0x80) && (blue > 0x80)) : ((red + green + blue) > 3 * 0x80); // whitish colored = (red > 0xF0) || ((green > 0xF0) && (blue > 0xF0)); // reddish or yellowish? if (0 == pn % 8) mono_palette_buffer[pn / 8] = 0; mono_palette_buffer[pn / 8] |= whitish << pn % 8; if (0 == pn % 8) color_palette_buffer[pn / 8] = 0; color_palette_buffer[pn / 8] |= colored << pn % 8; rgb_palette_buffer[pn] = ((red & 0xF8) << 8) | ((green & 0xFC) << 3) | ((blue & 0xF8) >> 3); } } uint32_t rowPosition = flip ? imageOffset + (height - h) * rowSize : imageOffset; for (uint16_t row = 0; row < h; row++, rowPosition += rowSize) // for each line { uint32_t in_remain = rowSize; uint32_t in_idx = 0; uint32_t in_bytes = 0; uint8_t in_byte = 0; // for depth <= 8 uint8_t in_bits = 0; // for depth <= 8 uint16_t color = GxEPD_WHITE; file.seek(rowPosition); for (uint16_t col = 0; col < w; col++) // for each pixel { // Time to read more pixel data? if (in_idx >= in_bytes) // ok, exact match for 24bit also (size IS multiple of 3) { in_bytes = file.read(input_buffer, in_remain > sizeof(input_buffer) ? sizeof(input_buffer) : in_remain); in_remain -= in_bytes; in_idx = 0; } switch (depth) { case 24: blue = input_buffer[in_idx++]; green = input_buffer[in_idx++]; red = input_buffer[in_idx++]; whitish = with_color ? ((red > 0x80) && (green > 0x80) && (blue > 0x80)) : ((red + green + blue) > 3 * 0x80); // whitish colored = (red > 0xF0) || ((green > 0xF0) && (blue > 0xF0)); // reddish or yellowish? color = ((red & 0xF8) << 8) | ((green & 0xFC) << 3) | ((blue & 0xF8) >> 3); break; case 16: { uint8_t lsb = input_buffer[in_idx++]; uint8_t msb = input_buffer[in_idx++]; if (format == 0) // 555 { blue = (lsb & 0x1F) << 3; green = ((msb & 0x03) << 6) | ((lsb & 0xE0) >> 2); red = (msb & 0x7C) << 1; color = ((red & 0xF8) << 8) | ((green & 0xFC) << 3) | ((blue & 0xF8) >> 3); } else // 565 { blue = (lsb & 0x1F) << 3; green = ((msb & 0x07) << 5) | ((lsb & 0xE0) >> 3); red = (msb & 0xF8); color = (msb << 8) | lsb; } whitish = with_color ? ((red > 0x80) && (green > 0x80) && (blue > 0x80)) : ((red + green + blue) > 3 * 0x80); // whitish colored = (red > 0xF0) || ((green > 0xF0) && (blue > 0xF0)); // reddish or yellowish? } break; case 1: case 4: case 8: { if (0 == in_bits) { in_byte = input_buffer[in_idx++]; in_bits = 8; } uint16_t pn = (in_byte >> bitshift) & bitmask; whitish = mono_palette_buffer[pn / 8] & (0x1 << pn % 8); colored = color_palette_buffer[pn / 8] & (0x1 << pn % 8); in_byte <<= depth; in_bits -= depth; color = rgb_palette_buffer[pn]; } break; } if (with_color && has_multicolors) { // keep color } else if (whitish) { color = GxEPD_WHITE; } /*else if (colored && with_color) { color = GxEPD_COLORED; }*/ else { color = GxEPD_BLACK; } uint16_t yrow = y + (flip ? h - row - 1 : row); display.drawPixel(x + col, yrow, color); } // end pixel } // end line Serial.print("page loaded in "); Serial.print(millis() - startTime); Serial.println(" ms"); // } //while (display.nextPage()); Serial.print("loaded in "); Serial.print(millis() - startTime); Serial.println(" ms"); } } } file.close(); if (!valid) { Serial.println("bitmap format not handled."); } } void drawLogo(File f) { // WaveShare 4.2" display int16_t w2 = display.width() / 2; // total width 400 pixels int16_t h2 = display.height() / 2; // total height 300 pixels Serial.print("Display width: "); Serial.println(display.width()); Serial.print("Display height: "); Serial.println(display.height()); drawBitmapFromSD(f, w2-200, h2-150, false); // Set initial (x, y) coordinates to (0, 0) } /* void drawLogo(File f) { int16_t w2 = display.width() / 2; int16_t h2 = display.height() / 2; drawBitmapFromSD(f, w2 - 64, h2 - 80, false); } void drawLogo(File f) { int16_t w2 = 400 / 2; int16_t h2 = 300 / 2; drawBitmapFromSD(f, 400, 300, false); }*/