#include "badge/display.h" #include "badge/log.h" #include #include // #include #include #include #include "badge/config.h" #include "badge/util.h" #include #include #include #include #if UVOK_EPAP_DISPLAY == DISPLAY_WAVESHARE_219_YBW // HINT: Update the library code, set budy timeout to 30 or 60 seconds! // Small color GxEPD2_3C display(GxEPD2_213_Z19c(22, 21, 17, 16)); #elif UVOK_EPAP_DISPLAY == DISPLAY_WAVESHARE_290_BW // larger b/w // or T5 // GxEPD2_BW display(GxEPD2_290_T5D(22, 21, 17, 16)); GxEPD2_BW display(GxEPD2_290_M06(22, 21, 17, 16)); #elif UVOK_EPAP_DISPLAY == DISPLAY_ELECROW_290_BW /* #define SCK 12 #define MOSI 11 #define RES 47 #define DC 46 #define CS 45 #define BUSY 48 */ GxEPD2_BW display(GxEPD2_290_T94(45, 46, 47, 48)); #else #error "define display" #endif QRCodeGFX qrc(display); static uint8_t displayed_image = 0; static std::string displayed_text; #include "cheebox.xbm" #include "chleepy.xbm" #include "hug.xbm" #include "hungry.xbm" #include "qr.xbm" #include "uvok.xbm" // #include "sneppump.xbm" #include "snep2.xbm" // #include "snep3.xbm" static bool is_initial = true; static constexpr uint8_t rotation = 1; #define IMAGE_DATA(name, text) \ (ImageInfo) \ { \ name##_bits, name##_width, name##_height, text \ } struct ImageInfo { // I always thought making the struct const makes all members const, yet I still get warnings... const char *bits; const int width; const int height; const char *text; }; constexpr std::array imgs{ #include "./images.cfg" }; const int de::uvok::badge::image_count = imgs.size(); typedef enum { DISPLAY_PREVIEW, DISPLAY_FULL, DISPLAY_QRCODE } display_mode_t; void de::uvok::badge::display_init() { LOG_F("Init display...\n"); #if UVOK_EPAP_DISPLAY == DISPLAY_ELECROW_290_BW // Turn on once, let controller handle the rest, lest I want to do re-init... pinMode(7, OUTPUT); // Set pin 7 as output mode digitalWrite(7, HIGH); // Set pin 7 to high level to turn on the screen power display.init(115200, true); #else display.init(115200, true, 2, false); // USE THIS for Waveshare boards with "clever" reset circuit, 2ms reset pulse #endif display.hibernate(); LOG_F("Display done.\n"); qrc.setScale(2); qrc.getGenerator().setErrorCorrectionLevel(QRCodeECCLevel::Medium); const auto uvok_pos = std::find_if( // imgs.begin(), // imgs.end(), // [](const ImageInfo &info) { std::string_view s(info.text); return s.find("uvok") != std::string::npos; }); if (uvok_pos < imgs.end()) { display_direct(uvok_pos - imgs.begin()); } } static void displayQRCode() { do { display.setFullWindow(); qrc.draw(displayed_text.c_str(), 10, 10); } while (display.nextPage()); } static void displayDo(display_mode_t mode) { if (mode == DISPLAY_QRCODE) { displayQRCode(); return; } if (is_initial) { mode = DISPLAY_FULL; is_initial = false; } LOG_F("Print image %d in full mode? %d\n", displayed_image, mode); display.setRotation(rotation); display.setTextColor(GxEPD_BLACK); display.setFont(&FreeSansBold12pt7b); const char *display_text = imgs[displayed_image].text; int16_t tbx, tby; uint16_t tbw, tbh; display.getTextBounds(display_text, 0, 0, &tbx, &tby, &tbw, &tbh); constexpr int TEXT_BORDER = 10; const uint16_t x = display.width() - tbw - TEXT_BORDER - tbx; const uint16_t y = ((display.height() - tbh) / 2) - tby; // re-calculate right-centered display.getTextBounds(display_text, x, y, &tbx, &tby, &tbw, &tbh); display.firstPage(); do { if (mode == DISPLAY_FULL) { display.setFullWindow(); display.drawXBitmap(0, 0, (const unsigned char *)imgs[displayed_image].bits, imgs[displayed_image].width, imgs[displayed_image].height, GxEPD_BLACK); } else { display.setPartialWindow(tbx - TEXT_BORDER, tby - TEXT_BORDER, tbw + 2 * TEXT_BORDER, tbh + 2 * TEXT_BORDER); } display.fillRect(tbx - TEXT_BORDER, tby - TEXT_BORDER, tbw + 2 * TEXT_BORDER, tbh + 2 * TEXT_BORDER, GxEPD_WHITE); display.setCursor(x, y); display.print(display_text); } while (display.nextPage()); display.hibernate(); } uint8_t de::uvok::badge::display_prev() { displayed_image--; if (displayed_image >= image_count) displayed_image = image_count - 1; displayDo(DISPLAY_PREVIEW); return displayed_image; } uint8_t de::uvok::badge::display_next() { displayed_image = (displayed_image + 1) % image_count; displayDo(DISPLAY_PREVIEW); return displayed_image; } uint8_t de::uvok::badge::display_refresh() { displayDo(DISPLAY_FULL); return displayed_image; } void de::uvok::badge::display_direct(uint8_t num) { LOG_F("Display direct\n"); if (num >= image_count) return; displayed_image = num; displayDo(DISPLAY_FULL); } void de::uvok::badge::display_text(const std::string &text) { displayed_text = text; displayDo(DISPLAY_QRCODE); is_initial = true; } void de::uvok::badge::display_indicator(DisplayIndicator indicator) { LOG_F("Set indicator to %d\n", indicator); // no idea what do about initial state. display.setRotation(rotation); display.setTextColor(GxEPD_BLACK); display.setFont(&FreeMono9pt7b); const char *display_text = ""; switch (indicator) { case DisplayIndicator::Advertising: display_text = "Adv"; break; case DisplayIndicator::Connected: display_text = "Conn"; break; } int16_t tbx, tby; uint16_t tbw, tbh; constexpr int TEXT_BORDER_X = 3; constexpr int TEXT_POS_Y = 15; // For Y, cursor means the "bottom" line? display.getTextBounds(display_text, TEXT_BORDER_X, TEXT_POS_Y, &tbx, &tby, &tbw, &tbh); LOG_F("GTB for %s@%d,%d returned %d, %d, %d, %d\n", display_text, (int16_t)TEXT_BORDER_X, (int16_t)TEXT_POS_Y, tbx, tby, tbw, tbh); // Determined by debugging constexpr uint16_t max_width = 50; constexpr uint16_t max_height = 18; // right-align stuff const uint16_t rect_x = display.width() - max_width; display.firstPage(); do { if (is_initial) { display.setFullWindow(); display.clearScreen(); // display.drawXBitmap(0, 0, (unsigned char *)imgs[displayed].bits, imgs[displayed].width, // imgs[displayed].height, GxEPD_BLACK); } else { display.setPartialWindow(rect_x, 0, max_width, max_height); } display.fillRect(rect_x, 0, max_width, max_height, GxEPD_WHITE); display.setCursor(TEXT_BORDER_X + rect_x, TEXT_POS_Y); display.print(display_text); } while (display.nextPage()); display.hibernate(); } /* // -> #include "output_h4x4a.xbm" #include "output_o8x8.xbm" */