#ifndef DISPLAY_H #define DISPLAY_H // #include #include #include #include "Color.h" #include "font.h" #include "core/http.h" #define PIXELS_PER_SEGMENT 3 #define SEGMENTS_PER_DIGIT 7 #define PIXELS_PER_DOT 1 #define DOT_COUNT 4 #define DIGIT_COUNT 4 #define PIXELS_PER_DIGIT (PIXELS_PER_SEGMENT * SEGMENTS_PER_DIGIT) #define TOTAL_DOT_PIXEL_COUNT (PIXELS_PER_DOT * DOT_COUNT) #define TOTAL_SEGMENT_PIXEL_COUNT (DIGIT_COUNT * PIXELS_PER_DIGIT) #define TOTAL_PIXEL_COUNT (TOTAL_SEGMENT_PIXEL_COUNT + TOTAL_DOT_PIXEL_COUNT) #define TOTAL_PIXEL_BYTE_COUNT (TOTAL_PIXEL_COUNT * sizeof(Color)) #define HEX_BUFFER_SIZE (TOTAL_PIXEL_BYTE_COUNT + 1) #define DISPLAY_VIRTUAL_WIDTH 25 #define DISPLAY_VIRTUAL_HEIGHT 9 #define HEX_BUFFER_MIN_WAIT_MS 500 class Display { // Adafruit_NeoPixel leds; Color pixels[TOTAL_PIXEL_COUNT] = {}; Color color = WHITE; char hexBuffer[HEX_BUFFER_SIZE] = ""; struct Mapping { uint8_t x; uint8_t y; }; public: Display() /* : leds(PIXEL_COUNT, GPIO_NUM_13) */ { // leds.begin(); setBrightness(6); clear(); flush(); } void setColor(const Color newColor) { this->color = newColor; } void setBrightness(const int brightness) { // leds.setBrightness(brightness); } void clear() { memset(pixels, 0, TOTAL_PIXEL_BYTE_COUNT); } void flush(const bool forceNextHexBuffer = false) { // memcpy(leds.getPixels(), buffer, PIXEL_BYTE_COUNT); // leds.show(); const auto now = millis() - HEX_BUFFER_MIN_WAIT_MS; static auto last = now; if (now - last >= HEX_BUFFER_MIN_WAIT_MS || forceNextHexBuffer) { last = now; fillHexBuffer(); websocketSendAll(hexBuffer); } } void printf(const char *format, ...) { char buffer[64]; va_list args; va_start(args, format); vsnprintf(buffer, sizeof buffer, format, args); va_end(args); print(buffer); } void print(const char *str) { auto digit = 0; for (auto c = str; *c != 0 && digit < 4; c++) { switch (*c) { case '\'': case '"': case '`': case '.': printDots(false, false, false, true); digit = 2; break; case ',': printDots(false, false, true, true); digit = 2; break; case ':': printDots(false, true, true, false); digit = 2; break; case ';': printDots(false, true, true, true); digit = 2; break; case '|': printDots(true, true, true, true); digit = 2; break; default: printCharacter(digit++, *c); break; } } } void printDots(const bool dot0, const bool dot1, const bool dot2, const bool dot3) { auto pixel = PIXELS_PER_DIGIT * 2; if (dot0) { pixels[pixel] = color; } pixel++; if (dot1) { pixels[pixel] = color; } pixel++; if (dot2) { pixels[pixel] = color; } pixel++; if (dot3) { pixels[pixel] = color; } } void printCharacter(int digit, const char character) { auto pixel = digit * PIXELS_PER_DIGIT + (digit >= 2 ? TOTAL_DOT_PIXEL_COUNT : 0); const auto symbol = getSymbol(character); for (auto s = *symbol; s < *symbol + SYMBOL_SIZE; s++) { if (*s) { pixels[pixel++] = color; pixels[pixel++] = color; pixels[pixel++] = color; } else { pixel += 3; } } } void sendHexBuffer(AsyncWebSocketClient *client) { client->text(hexBuffer); } void fillRect(const uint8_t x, uint8_t y, uint8_t w, uint8_t h) { for (int dx = 0; dx < w; ++dx) { for (int dy = 0; dy < h; ++dy) { drawVirtualPixel(x + dx, y + dy); } } } void strokeRect(const uint8_t x, uint8_t y, uint8_t w, uint8_t h) { for (int dx = 0; dx < w; ++dx) { drawVirtualPixel(x + dx, y); drawVirtualPixel(x + dx, y + h); } for (int dy = 0; dy < h; ++dy) { drawVirtualPixel(x, y + dy); drawVirtualPixel(x + w, y + dy); } } void drawVirtualPixel(const uint8_t x, const uint8_t y) { const auto index = findIndexForVirtualCoordinates(x, y); if (index >= 0) { pixels[index] = color; } } private: Mapping digitMapping[32] = { // @formatter:off {0, 3}, {0, 2}, {0, 1}, // top left {1, 0}, {2, 0}, {3, 0}, // top {4, 1}, {4, 2}, {4, 3}, // top right {4, 5}, {4, 6}, {4, 7}, // bottom right {3, 8}, {2, 8}, {1, 8}, // bottom {0, 7}, {0, 6}, {0, 5}, // bottom left {1, 4}, {2, 4}, {3, 4}, // middle // @formatter:on }; uint8_t dotMapping[4] = {0, 2, 6, 8}; uint8_t findIndexForVirtualCoordinates(const uint8_t x, const uint8_t y) { if (x < 0 || x > 24 || y < 0 || y > 8) { return -1; } else if (x >= 20) { return _findIndexForVirtualCoordinates_digitRelative(x - 20, y) + 3 * PIXELS_PER_DIGIT + TOTAL_DOT_PIXEL_COUNT; } else if (x >= 14) { return _findIndexForVirtualCoordinates_digitRelative(x - 14, y) + 2 * PIXELS_PER_DIGIT + TOTAL_DOT_PIXEL_COUNT; } else if (x == 12) { return _findIndexForVirtualCoordinates_dotRelative(y) + 2 * PIXELS_PER_DIGIT; } else if (x >= 6) { return _findIndexForVirtualCoordinates_digitRelative(x - 6, y) + 1 * PIXELS_PER_DIGIT; } else { return _findIndexForVirtualCoordinates_digitRelative(x, y) + 0 * PIXELS_PER_DIGIT;; } } uint8_t _findIndexForVirtualCoordinates_digitRelative(const uint8_t x, const uint8_t y) { for (auto index = 0; index < PIXELS_PER_DIGIT; index++) { auto item = digitMapping[index]; if (item.x == x && item.y == y) { return index; } } return -1; } uint8_t _findIndexForVirtualCoordinates_dotRelative(const uint8_t y) { for (auto index = 0; index < DOT_COUNT; index++) { auto dotY = dotMapping[index]; if (dotY == y) { return index; } } return -1; } void fillHexBuffer() { auto b = hexBuffer; for (const auto& pixel: pixels) { b += snprintf(b, sizeof hexBuffer - (b - hexBuffer), "%X%X%X", pixel.r / 16, pixel.g / 16, pixel.b / 16); } } }; extern Display display; #endif