/* * Display.cpp * * Wrapper class for Adafruit OLED display. * * Created on: 28.01.2022 * Author: FSmilari */ #include "Display.h" // Array of all bitmaps for convenience. (Total bytes used to store images in PROGMEM = 48) const int epd_rotate_bitmap_allArray_LEN = 8; const static unsigned char *epd_rotate_bitmap_allArray[epd_rotate_bitmap_allArray_LEN] = { epd_bitmap_Rotate_0, epd_bitmap_Rotate_45, epd_bitmap_Rotate_90, epd_bitmap_Rotate_135, epd_bitmap_Rotate_180, epd_bitmap_Rotate_225, epd_bitmap_Rotate_270, epd_bitmap_Rotate_315 }; /***************** ** Constructors. ****************/ Display::Display() : mode(FAST) { ssd1306 = Adafruit_SSD1306(SCREEN_WIDTH, SCREEN_HEIGHT); u8g2_gfx.begin(ssd1306); wlsConnected = false; angle = 0; starttime = millis(); refreshScreen = true; distanceValue = 0.0; distanceUnit = "mm"; maxDiveDistance = 0.0; levelHeightDiving = 0.0; } /****************** ** Public methods *****************/ void Display::init() { if (!ssd1306.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS, true, true)) { Serial.println(F("SSD1306 allocation failed")); for (;;) ; // Don't proceed, loop forever } } void Display::setRefreshScreen() { refreshScreen = true; } void Display::display() { if (refreshScreen) { refreshScreen = false; ssd1306.display(); } } void Display::clearDisplay() { ssd1306.clearDisplay(); } void Display::showBrand() { ssd1306.clearDisplay(); ssd1306.drawBitmap(0, 0, epd_bitmap_SFTools_Logo, imageWidth, imageHeight, SSD1306_WHITE); char *s = &String("Frästisch N172")[0]; int16_t w = 0, h = 0; u8g2_gfx.setFont(u8g2_font_helvB12_tf); w = u8g2_gfx.getUTF8Width(s); h = u8g2_gfx.getFontAscent() - u8g2_gfx.getFontDescent(); u8g2_gfx.setForegroundColor(SSD1306_WHITE); u8g2_gfx.setCursor((SCREEN_WIDTH - w) / 2, 63 - (30 - h) / 2); u8g2_gfx.println(F(s)); display(); } void Display::showInitialization() { ssd1306.clearDisplay(); char *s = &String("Initialisieren")[0]; int16_t w = 0; u8g2_gfx.setFont(u8g2_font_helvB12_tf); w = u8g2_gfx.getUTF8Width(s); u8g2_gfx.setForegroundColor(SSD1306_WHITE); u8g2_gfx.setCursor((SCREEN_WIDTH - w) / 2, 30); u8g2_gfx.println(F(s)); char *g; w = u8g2_gfx.getUTF8Width(". . . . ."); u8g2_gfx.setCursor((SCREEN_WIDTH - w) / 2, 50); String gauge = ""; for (int i = 0; i < 6; i++) { g = &gauge[0]; u8g2_gfx.print(g); setRefreshScreen(); display(); delay(500); if (i < 4) { gauge = ". "; g = &gauge[0]; } else { gauge = "."; g = &gauge[0]; } } } void Display::showFrame(Status status) { switch (status) { case MOVING_ELEVATOR: rotateAndDrawRotationBitmap(); break; case TOOL_CHANGE: redrawFrame(); drawStatusText(STATUS_TXT_TOOLCHG); drawBitmap(37 + (SCREEN_WIDTH - 37 - imageSide) / 2, (SCREEN_HEIGHT - imageSide) / 2, imageSide, imageSide, epd_bitmap_Tools); break; case CONFIGURATION: redrawFrame(); ssd1306.drawLine(37, SCREEN_HEIGHT / 2 - 1, SCREEN_WIDTH - 1, SCREEN_HEIGHT / 2 - 1, SSD1306_WHITE); drawStatusText(STATUS_TXT_CFG); drawConfigText(this->configText); drawConfigOption(this->configOption); break; case IDLE: redrawFrame(); ssd1306.drawLine(37, 43, SCREEN_WIDTH - 1, 43, SSD1306_WHITE); drawStatusText(STATUS_TXT_IDLE); drawDistanceValue(distanceValue); drawDistanceUnit(distanceUnit); drawMode(this->mode); break; case NULLING: case NULLING_TLS: redrawFrame(); drawStatusText(STATUS_TXT_NULLING); drawBitmap(37 + (SCREEN_WIDTH - 37 - imageSide) / 2, (SCREEN_HEIGHT - imageSide) / 2, imageSide, imageSide, epd_bitmap_Nulling); break; case DIVING: redrawFrame(); drawStatusText(STATUS_TXT_DIVING); drawBitmap((37 - imageSide_R) / 2, 47, imageSide_D, imageSide_D, epd_bitmap_Dive); drawDistanceValue(distanceValue); drawDistanceUnit(distanceUnit); drawActualDiveStep(maxDiveDistance, levelHeightDiving, distanceValue); drawMaxDiveDistance(maxDiveDistance); break; default: break; } display(); } void Display::drawConfigText(String txt) { char *s = &txt[0]; int16_t w = 0, h = 0; u8g2_gfx.setFont(u8g2_font_helvB12_tf); // select u8g2 font from here: https://github.com/olikraus/u8g2/wiki/fntlistall w = u8g2_gfx.getUTF8Width(s); h = u8g2_gfx.getFontAscent() - u8g2_gfx.getFontDescent(); u8g2_gfx.setCursor((SCREEN_WIDTH + 37 - w) / 2, SCREEN_HEIGHT / 4 + h / 2 - 1); // start writing at this position u8g2_gfx.println(F(s)); } void Display::drawConfigOption(String txt) { char *s = &txt[0]; int16_t w = 0, h = 0; u8g2_gfx.setFont(u8g2_font_helvB12_tf); w = u8g2_gfx.getUTF8Width(s); h = u8g2_gfx.getFontAscent() - u8g2_gfx.getFontDescent(); u8g2_gfx.setCursor((SCREEN_WIDTH + 37 - w) / 2, SCREEN_HEIGHT / 4 * 3 + h / 2 - 1); u8g2_gfx.println(F(s)); } const String& Display::getConfigOption() const { return configOption; } void Display::setConfigOption(const String &configOption) { if (!this->configOption.equals(configOption)) { this->configOption = configOption; setRefreshScreen(); } } const String& Display::getConfigText() const { return configText; } void Display::setConfigText(const String &configText) { if (!this->configText.equals(configText)) { this->configText = configText; setRefreshScreen(); } } void Display::setWlsConnected(bool wlsConnected) { if (this->wlsConnected != wlsConnected) { this->wlsConnected = wlsConnected; setRefreshScreen(); } } void Display::setLevelHeightDiving(float levelHeightDiving) { if (this->levelHeightDiving != levelHeightDiving) { this->levelHeightDiving = levelHeightDiving; setRefreshScreen(); } } void Display::setMaxDiveDistance(float maxDiveDistance) { if (this->maxDiveDistance != maxDiveDistance) { this->maxDiveDistance = maxDiveDistance; setRefreshScreen(); } } void Display::setDistanceUnit(const String &distanceUnit) { if (this->distanceUnit != distanceUnit) { this->distanceUnit = distanceUnit; setRefreshScreen(); } } void Display::setDistanceValue(const float &distanceValue) { if (this->distanceValue != distanceValue) { this->distanceValue = distanceValue; setRefreshScreen(); } } void Display::setMode(const ValueMode &mode) { if (!this->mode.equals(mode)) { this->mode = mode; setRefreshScreen(); } } void Display::drawWLSStatus() { String txt = wlsConnected ? "WLS" : ""; char *s = &txt[0]; int16_t w = 0; u8g2_gfx.setFont(u8g2_font_helvR08_tf); // select u8g2 font from here: https://github.com/olikraus/u8g2/wiki/fntlistall w = u8g2_gfx.getUTF8Width(s); u8g2_gfx.setCursor((37 - w) / 2, 27); u8g2_gfx.println(F(s)); } void Display::drawMode(ValueMode mode) { String txt = mode.toString(); char *s = &txt[0]; int16_t w = 0; u8g2_gfx.setFont(u8g2_font_helvB08_tf); w = u8g2_gfx.getUTF8Width(s); u8g2_gfx.setCursor((SCREEN_WIDTH - w - 6), 63 - 6); u8g2_gfx.println(F(s)); } void Display::drawDistanceValue(float distance) { String txt = String(distance, 2); if (txt.equals("-0.00")) { txt.replace("-", ""); } char *s = &txt[0]; int16_t w = 0, h = 0; u8g2_gfx.setFont(u8g2_font_logisoso22_tf); w = u8g2_gfx.getUTF8Width(s); h = u8g2_gfx.getFontAscent() - u8g2_gfx.getFontDescent(); u8g2_gfx.setCursor((SCREEN_WIDTH - w - 6), h); u8g2_gfx.println(F(s)); } void Display::drawDistanceUnit(String unit) { String txt = unit; char *s = &txt[0]; int16_t w = 0; u8g2_gfx.setFont(u8g2_font_helvB12_tf); w = u8g2_gfx.getUTF8Width(s); u8g2_gfx.setCursor((SCREEN_WIDTH - w - 6), 41); u8g2_gfx.println(F(s)); } void Display::drawMaxDiveDistance(float maxDiveDistance) { String txt = String(maxDiveDistance, 2) + "mm"; char *s = &txt[0]; int16_t w = 0; u8g2_gfx.setFont(u8g2_font_helvB10_tf); w = u8g2_gfx.getUTF8Width(s); u8g2_gfx.setCursor((SCREEN_WIDTH - w - 6), 59); u8g2_gfx.println(F(s)); } void Display::drawActualDiveStep(float maxDiveDistance, float levelHeightDiving, float distance) { int totalSteps = ceil(maxDiveDistance / levelHeightDiving); int actualStep = ceil(distance / levelHeightDiving); String txt = String(actualStep) + "/" + String(totalSteps); char *s = &txt[0]; int16_t w = 0; u8g2_gfx.setFont(u8g2_font_helvB10_tf); w = u8g2_gfx.getUTF8Width(s); u8g2_gfx.setCursor(42, 59); u8g2_gfx.println(F(s)); } /******************************** ** Private methods *******************************/ void Display::drawStatusText(String txt) { char *s = &txt[0]; int16_t w = 0; u8g2_gfx.setFont(u8g2_font_helvR08_tf); // select u8g2 font from here: https://github.com/olikraus/u8g2/wiki/fntlistall w = u8g2_gfx.getUTF8Width(s); u8g2_gfx.setCursor((37 - w) / 2, 12); u8g2_gfx.println(F(s)); } void Display::drawBitmap(int x, int y, int w, int h, const uint8_t bitmap[]) { ssd1306.drawBitmap(x, y, bitmap, w, h, SSD1306_WHITE); } void Display::redrawFrame() { ssd1306.clearDisplay(); ssd1306.drawRoundRect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, 3, SSD1306_WHITE); ssd1306.drawLine(37, 0, 37, SCREEN_HEIGHT - 1, SSD1306_WHITE); ssd1306.drawLine(0, 15, 37, 15, SSD1306_WHITE); drawWLSStatus(); } void Display::rotateAndDrawRotationBitmap() { if (millis() - starttime >= 125) { int xOffset = 0; int yOffset = 0; switch (angle) { case 0: xOffset = imageSide_R / 2; yOffset = 0; break; case 1: xOffset = imageSide_R / 2; yOffset = imageSide_R / 4; break; case 2: xOffset = imageSide_R / 2; yOffset = imageSide_R / 2; break; case 3: xOffset = imageSide_R / 4; yOffset = imageSide_R / 2; break; case 4: xOffset = 0; yOffset = imageSide_R / 2; break; case 5: xOffset = 0; yOffset = imageSide_R / 4; break; case 6: xOffset = 0; yOffset = 0; break; case 7: xOffset = imageSide_R / 4; yOffset = 0; break; default: break; } ssd1306.fillRect((37 - imageSide_R) / 2 + xOffset, 30 + yOffset, imageSide_R / 2 + 1, imageSide_R / 2 + 1, SSD1306_BLACK); ssd1306.display(); drawBitmap((37 - imageSide_R) / 2, 30, imageSide_R, imageSide_R, epd_rotate_bitmap_allArray[angle]); angle = (angle + 1) % 8; starttime = millis(); setRefreshScreen(); display(); } }