#include "Ecran.h" #include "Ctrl.h" ////////////////////////////////////////// // Connections for e.g. LOLIN D32 //static const uint8_t EPD_BUSY = 4; // to EPD BUSY //static const uint8_t EPD_CS = 5; // to EPD CS //static const uint8_t EPD_RST = 16; // to EPD RST //static const uint8_t EPD_DC = 17; // to EPD DC //static const uint8_t EPD_SCK = 18; // to EPD CLK //static const uint8_t EPD_MISO = 19; // Master-In Slave-Out not used, as no data from display //static const uint8_t EPD_MOSI = 23; // to EPD DIN static const uint8_t EPD_BUSY = 25; static const uint8_t EPD_CS = 15; static const uint8_t EPD_RST = 26; static const uint8_t EPD_DC = 27; static const uint8_t EPD_SCK = 13; static const uint8_t EPD_MISO = 12; // Master-In Slave-Out not used, as no data from display static const uint8_t EPD_MOSI = 14; //use GxEPD_BLACK or GxEPD_WHITE or GxEPD_RED or GxEPD_YELLOW depending on display type //GxEPD2_3C display(GxEPD2_750c(/*CS=5*/ SS, /*DC=*/ 17, /*RST=*/ 16, /*BUSY=*/ 4)); // GDEW075Z09 640x384, UC8179 (IL0371) GxEPD2_3C display(GxEPD2_750c_Z08(EPD_CS, EPD_DC, EPD_RST, EPD_BUSY)); // GDEW075Z08 800x480, GD7965 //GxEPD2_3C display(GxEPD2_750c_Z90(/*CS=5*/ SS, /*DC=*/ 17, /*RST=*/ 16, /*BUSY=*/ 4)); // GDEH075Z90 880x528, SSD1677 Ecran::Ecran() { printf("--------------------------------------"); printf("Init Ecran"); _display = &display; } void Ecran::clear(void) { if (debug) Serial.println("Font set"); _display->fillScreen(GxEPD_WHITE); if (debug) Serial.println("fill screen"); _display->setFullWindow(); if (debug) Serial.println("Full window"); } void Ecran::initialise(void) { if (debug) Serial.println("screen init"); fonts.begin(display); if (debug) Serial.println("font init"); _display->init(115200, true, 2, false); // init(uint32_t serial_diag_bitrate, bool initial, uint16_t reset_duration, bool pulldown_rst_mode) // display.init(); for older Waveshare HAT's if (debug) Serial.println("display init"); SPI.end(); SPI.begin(EPD_SCK, EPD_MISO, EPD_MOSI, EPD_CS); if (debug) Serial.println("SPI init"); //fonts.begin(_display); // connect u8g2 procedures to Adafruit GFX fonts.setFontMode(1); // use u8g2 transparent mode (this is default) fonts.setFontDirection(0); // left to right (this is default) fonts.setForegroundColor(GxEPD_BLACK); // apply Adafruit GFX color fonts.setBackgroundColor(GxEPD_WHITE); // apply Adafruit GFX color fonts.setFont(FONT_10); // select u8g2 font from here: https://github.com/olikraus/u8g2/wiki/fntlistall } //######################################################################################### void Ecran::drawString(int x, int y, String text, alignment align, uint16_t color) { int16_t x1, y1; //the bounds of x,y and w and h of the variable 'text' in pixels. uint16_t w, h; fonts.setForegroundColor(color); _display->setTextColor(color); //, GxEPD_RED); _display->setTextWrap(false); _display->getTextBounds(text, x, y, &x1, &y1, &w, &h); if (align == RIGHT) x = x - w; if (align == CENTER) x = x - w / 2; fonts.setCursor(x, y + h); fonts.print(text); } //######################################################################################### void Ecran::arrow(int x, int y, int asize, float aangle, int pwidth, int plength) { float dx = (asize + 28) * cos((aangle - 90) * PI / 180) + x; // calculate X position float dy = (asize + 28) * sin((aangle - 90) * PI / 180) + y; // calculate Y position float x1 = 0; float y1 = plength; float x2 = pwidth / 2; float y2 = pwidth / 2; float x3 = -pwidth / 2; float y3 = pwidth / 2; float angle = aangle * PI / 180; float xx1 = x1 * cos(angle) - y1 * sin(angle) + dx; float yy1 = y1 * cos(angle) + x1 * sin(angle) + dy; float xx2 = x2 * cos(angle) - y2 * sin(angle) + dx; float yy2 = y2 * cos(angle) + x2 * sin(angle) + dy; float xx3 = x3 * cos(angle) - y3 * sin(angle) + dx; float yy3 = y3 * cos(angle) + x3 * sin(angle) + dy; _display->fillTriangle(xx1, yy1, xx3, yy3, xx2, yy2, GxEPD_RED); } //######################################################################################### void Ecran::drawStringMaxWidth(int x, int y, unsigned int text_width, String text, alignment align) { int16_t x1, y1; //the bounds of x,y and w and h of the variable 'text' in pixels. uint16_t w, h; _display->getTextBounds(text, x, y, &x1, &y1, &w, &h); if (align == RIGHT) x = x - w; if (align == CENTER) x = x - w / 2; fonts.setCursor(x, y); if (text.length() > text_width * 2) { fonts.setFont(FONT_10); text_width = 42; y = y - 3; } fonts.println(text.substring(0, text_width)); if (text.length() > text_width) { fonts.setCursor(x, y + h + 15); String secondLine = text.substring(text_width); secondLine.trim(); // Remove any leading spaces fonts.println(secondLine); } } void Ecran::drawAngledLine(int x, int y, int x1, int y1, int size, int color) { float dx = (size / 2.0) * (y - y1) / sqrt(sq(x - x1) + sq(y - y1)); float dy = (size / 2.0) * (x - x1) / sqrt(sq(x - x1) + sq(y - y1)); _display->fillTriangle(x + dx, y - dy, x - dx, y + dy, x1 + dx, y1 - dy, color); _display->fillTriangle(x - dx, y + dy, x1 - dx, y1 + dy, x1 + dx, y1 - dy, color); } void Ecran::drawBitmap(int16_t x, int16_t y, const uint8_t *bitmap, int16_t w, int16_t h, uint16_t color, uint8_t scale, bool inverted) { // uint8_t new_bitmap[512]; // replaceFullBlack(bitmap, new_bitmap, w, h, 4); if (inverted) { if (scale != 1) { drawScaledInvertedBitmap(x, y, bitmap, w, h, color, scale); } else { _display->drawInvertedBitmap(x, y, bitmap, w, h, color); } } else { if (scale != 1) { drawScaledBitmap(x, y, bitmap, w, h, color, scale); } else { _display->drawBitmap(x, y, bitmap, w, h, color); } } } void Ecran::drawScaledBitmap(int16_t x, int16_t y, const uint8_t *bitmap, int16_t w, int16_t h, uint16_t color, uint8_t scale) { int16_t i, j, byteWidth = (w + 7) / 8; for(j = 0; j < h; j++) { for(i = 0; i < w; i++) { if(pgm_read_byte(bitmap + j * byteWidth + i / 8) & (128 >> (i & 7))) { // Interpolate pixel values according to the scale factor for (uint8_t sy = 0; sy < scale; sy++) { for (uint8_t sx = 0; sx < scale; sx++) { int16_t px = x + i * scale + sx; int16_t py = y + j * scale + sy; // Calculate bilinear interpolation coefficients float fx = static_cast(i * scale + sx) / static_cast(w * scale); float fy = static_cast(j * scale + sy) / static_cast(h * scale); // Perform bilinear interpolation uint16_t interpolatedColor = bilinearInterpolation(x, y, w, h, color, fx, fy); // Draw the interpolated pixel _display->drawPixel(px, py, interpolatedColor); } } } } } } void Ecran::drawScaledInvertedBitmap(int16_t x, int16_t y, const uint8_t bitmap[], int16_t w, int16_t h, uint16_t color, uint8_t scale) { int16_t byteWidth = (w + 7) / 8; // Largeur d'une ligne en octets = ligne entière uint8_t byte = 0; for (int16_t j = 0; j < h; j++) { for (int16_t i = 0; i < w; i++) { if (i & 7) byte <<= 1; else { #if defined(__AVR) || defined(ESP8266) || defined(ESP32) byte = pgm_read_byte(&bitmap[j * byteWidth + i / 8]); #else byte = bitmap[j * byteWidth + i / 8]; #endif } if (!(byte & 0x80)) { // Interpoler les valeurs de pixels en fonction du facteur de redimensionnement for (uint8_t sy = 0; sy < scale; sy++) { for (uint8_t sx = 0; sx < scale; sx++) { int16_t px = x + i * scale + sx; int16_t py = y + j * scale + sy; // Calculer les coefficients d'interpolation bilinéaire float fx = static_cast(i * scale + sx) / static_cast(w * scale); float fy = static_cast(j * scale + sy) / static_cast(h * scale); // Effectuer l'interpolation bilinéaire uint16_t interpolatedColor = bilinearInterpolation(x, y, w, h, color, fx, fy); // Dessiner le pixel interpolé _display->drawPixel(px, py, interpolatedColor); } } } } } } // Bilinear interpolation function uint16_t Ecran::bilinearInterpolation(int16_t x, int16_t y, int16_t width, int16_t height, uint16_t color, float fx, float fy) { int16_t x0 = static_cast(floor(fx * width)); int16_t y0 = static_cast(floor(fy * height)); int16_t x1 = x0 + 1; int16_t y1 = y0 + 1; float wx = fx * width - x0; float wy = fy * height - y0; // Perform bilinear interpolation uint16_t result = static_cast( (1.0 - wx) * (1.0 - wy) * color + wx * (1.0 - wy) * color + (1.0 - wx) * wy * color + wx * wy * color ); return result; } void Ecran::replaceFullBlack(const uint8_t *bitmap, uint8_t * new_bitmap, int largeur, int hauteur, int tailleCarre) { for (int y = 0; y < hauteur; y += tailleCarre) { for (int x = 0; x < largeur; x += tailleCarre) { // Remplacer chaque carré par un maillage alternant blanc et noir for (int dy = 0; dy < tailleCarre; dy++) { for (int dx = 0; dx < tailleCarre; dx++) { int pixelX = x + dx; int pixelY = y + dy; if (pixelX < largeur && pixelY < hauteur) { if ((pixelX + pixelY) % 2 == 0) { // Pixel blanc new_bitmap[pixelY * largeur + pixelX] = 0xff; } else { // Pixel noir new_bitmap[pixelY * largeur + pixelX] = 0x00; } } } } } } } // Fonction pour dessiner un arc de cercle void Ecran::drawArc(int16_t x, int16_t y, int16_t radius, int16_t start_angle, int16_t end_angle, uint16_t color) { float deg_to_rad = 3.14159265358979323846 / 180.0; float angle_step = 1.0; // Ajustez cela pour changer la résolution de l'arc for (float angle = start_angle; angle <= end_angle; angle += angle_step) { float radian_angle = angle * deg_to_rad; int16_t x_pos = x + radius * cos(radian_angle); int16_t y_pos = y + radius * sin(radian_angle); _display->drawPixel(x_pos, y_pos, color); } }