diff --git a/data/http/index.css b/data/http/index.css new file mode 100644 index 0000000..d9c8b61 --- /dev/null +++ b/data/http/index.css @@ -0,0 +1,29 @@ +body { + font-family: sans-serif; + font-size: 8vw; + margin: 0; +} + +button { + width: 33vmin; + font-size: 9vw; +} + +button.cancel { + width: 15vmin; + height: 15vmin; +} + +table { + border-collapse: collapse; +} + +td { + text-align: center; +} + +svg { + border: 1px solid black; + width: 100%; + height: calc(11 / 27 * 100%); +} \ No newline at end of file diff --git a/data/http/index.html b/data/http/index.html index eba3657..84caa05 100644 --- a/data/http/index.html +++ b/data/http/index.html @@ -5,41 +5,11 @@ Sporttafel - + + - - + - - - + + + + + + + + + +
@@ -68,130 +38,24 @@  
+ +   + +
+ +   + +
\ No newline at end of file diff --git a/data/http/index.js b/data/http/index.js new file mode 100644 index 0000000..4396bde --- /dev/null +++ b/data/http/index.js @@ -0,0 +1,118 @@ +const DEV_HOST = "10.0.0.119"; + +const S = 100 / 27; + +const display = document.getElementById("display"); + +const segments = []; + +function url(protocol, path) { + const hostPart = location.host.substring(0, location.host.indexOf(":")) + const isLocal = hostPart === "" || hostPart === "localhost" || hostPart === "0"; + const host = isLocal ? DEV_HOST : hostPart; + const port = isLocal ? "80" : location.port; + return `${protocol}://${host}:${port}${path}`; +} + +function get(path) { + const request = new XMLHttpRequest(); + request.open("GET", url("http", path), true); + request.send(); +} + +function drawDigit(x, y) { + drawPixel(x, y, 0, 3); + drawPixel(x, y, 0, 2); + drawPixel(x, y, 0, 1); + + drawPixel(x, y, 1, 0); + drawPixel(x, y, 2, 0); + drawPixel(x, y, 3, 0); + + drawPixel(x, y, 4, 1); + drawPixel(x, y, 4, 2); + drawPixel(x, y, 4, 3); + + drawPixel(x, y, 4, 5); + drawPixel(x, y, 4, 6); + drawPixel(x, y, 4, 7); + + drawPixel(x, y, 3, 8); + drawPixel(x, y, 2, 8); + drawPixel(x, y, 1, 8); + + drawPixel(x, y, 0, 7); + drawPixel(x, y, 0, 6); + drawPixel(x, y, 0, 5); + + drawPixel(x, y, 1, 4); + drawPixel(x, y, 2, 4); + drawPixel(x, y, 3, 4); +} + +function drawPixel(offsetRasterX, offsetRasterY, innerRasterX, innerRasterY) { + const segment = document.createElementNS("http://www.w3.org/2000/svg", "rect"); + const x = (offsetRasterX + innerRasterX) * S; + const y = (offsetRasterY + innerRasterY) * S; + segment.setAttribute("x", x + "vw"); + segment.setAttribute("y", y + "vw"); + segment.setAttribute("width", S + "vw"); + segment.setAttribute("height", S + "vw"); + segment.setAttribute("stroke", "white"); + segment.setAttribute("fill", "none"); + segment.setAttribute("id", "segment" + segments.length); + display.appendChild(segment); + segments.push(segment); +} + +function drawDisplay(rasterX, rasterY) { + drawDigit(rasterX, rasterY); + rasterX += 6; + drawDigit(rasterX, rasterY); + rasterX += 6; + drawPixel(rasterX, rasterY, 0, 0); + drawPixel(rasterX, rasterY + 2, 0, 0); + drawPixel(rasterX, rasterY + 6, 0, 0); + drawPixel(rasterX, rasterY + 8, 0, 0); + rasterX += 2; + drawDigit(rasterX, rasterY); + rasterX += 6; + drawDigit(rasterX, rasterY); +} + +function connect() { + console.log("connecting websocket..."); + const socket = new WebSocket(url("ws", "/ws")); + socket.timeout = 1; + socket.addEventListener('open', _ => { + console.log('websocket connected'); + segments.forEach(segment => { + segment.setAttribute("fill", "none"); + segment.setAttribute("stroke", "none"); + }); + }); + socket.addEventListener('message', event => { + const len = event.data.length; + let index = 0; + let start = 0; + while (start < len) { + const end = start + 3; + const color = event.data.substring(start, end); + const segment = segments[index]; + segment.setAttribute("fill", "#" + color) + index++; + start = end; + } + }); + socket.addEventListener('close', _ => { + console.log('websocket disconnected'); + segments.forEach(segment => { + segment.setAttribute("fill", "none"); + segment.setAttribute("stroke", "white"); + }); + setTimeout(connect, 1000); + }); +} + +drawDisplay(1, 1); +setTimeout(connect, 1000); diff --git a/src/app/App.h b/src/app/App.h index 1483833..54893e5 100644 --- a/src/app/App.h +++ b/src/app/App.h @@ -4,7 +4,7 @@ #include #include #include -#include +#include #define CONFIG_WRITE_DELAY_MILLIS (30 * 1000) @@ -87,6 +87,22 @@ public: // } + virtual void leftUp() { + // + } + + virtual void leftDown() { + // + } + + virtual void rightUp() { + // + } + + virtual void rightDown() { + // + } + virtual bool setConfig(const String& key, const String& valueStr) { return false; } diff --git a/src/app/AppDemo.h b/src/app/AppDemo.h index 2ac462c..52ca39b 100644 --- a/src/app/AppDemo.h +++ b/src/app/AppDemo.h @@ -71,7 +71,7 @@ protected: void draw() override { display.clear(); - display.strokeRect(step, step, DISPLAY_VIRTUAL_WIDTH - 1 - 2 * step, DISPLAY_VIRTUAL_HEIGHT - 1 - 2 * step); + display.main.strokeRect(step, step, DISPLAY_VIRTUAL_WIDTH - 1 - 2 * step, DISPLAY_VIRTUAL_HEIGHT - 1 - 2 * step); } }; diff --git a/src/app/AppMatch.h b/src/app/AppMatch.h index 0cad465..ce0454d 100644 --- a/src/app/AppMatch.h +++ b/src/app/AppMatch.h @@ -52,10 +52,14 @@ class AppMatch final : public App { State state = INITIAL; + int pointsLeft = 0; + + int pointsRight = 0; + public: explicit AppMatch() - : App(APP_MATCH_NAME) { + : App(APP_MATCH_NAME) { // } @@ -118,12 +122,35 @@ public: } } + void leftUp() override { + pointsLeft++; + markDirty(true); + } + + void leftDown() override { + pointsLeft = max(0, pointsLeft - 1); + markDirty(true); + } + + void rightUp() override { + pointsRight++; + markDirty(true); + } + + void rightDown() override { + pointsRight = max(0, pointsRight - 1); + markDirty(true); + } + protected: void _start() override { configMillis = configGet(CONFIG_SECONDS_KEY, CONFIG_SECONDS_DEFAULT) * MS_PER_SEC; configCentis = configGet(CONFIG_CENTIS_KEY, CONFIG_CENTIS_DEFAULT); + pointsLeft = 0; + pointsRight = 0; + info("config:"); info(" seconds = %ld", configMillis / MS_PER_SEC); info(" centis = %s", configCentis ? "true" : "false"); @@ -177,17 +204,21 @@ protected: } if (blinkIntervalMillis == 0 || blinkState) { if (totalMinutes > 0) { - display.printf("%2d:%02d", totalMinutes, partSeconds); + display.main.printf("%2d:%02d", totalMinutes, partSeconds); } else if (totalMillis > 0) { if (configCentis) { - display.printf("%2d.%02d", partSeconds, partCentis); + display.main.printf("%2d.%02d", partSeconds, partCentis); } else { - display.printf(" %2d", partSeconds); + display.main.printf(" %2d", partSeconds); } } else { - display.printf("00:00"); + display.main.printf("00:00"); } } + // ReSharper disable once CppExpressionWithoutSideEffects + display.left.printf("%2d", pointsLeft); + // ReSharper disable once CppExpressionWithoutSideEffects + display.right.printf("%2d", pointsRight); } private: diff --git a/src/core/BASICS.h b/src/core/BASICS.h new file mode 100644 index 0000000..73092ca --- /dev/null +++ b/src/core/BASICS.h @@ -0,0 +1,12 @@ +#ifndef BASICS_H +#define BASICS_H + +#include +#include "log.h" + +#define X true +#define _ false + +#define countof(a) (sizeof(a) / sizeof((a)[0])) + +#endif diff --git a/src/core/http.cpp b/src/core/http.cpp index b5ea73b..82d0076 100644 --- a/src/core/http.cpp +++ b/src/core/http.cpp @@ -53,6 +53,34 @@ void httpActionConfirm(AsyncWebServerRequest *request) { request->send(200); } +void httpActionLeftUp(AsyncWebServerRequest *request) { + if (app != nullptr) { + app->leftUp(); + } + request->send(200); +} + +void httpActionLeftDown(AsyncWebServerRequest *request) { + if (app != nullptr) { + app->leftDown(); + } + request->send(200); +} + +void httpActionRightUp(AsyncWebServerRequest *request) { + if (app != nullptr) { + app->rightUp(); + } + request->send(200); +} + +void httpActionRightDown(AsyncWebServerRequest *request) { + if (app != nullptr) { + app->rightDown(); + } + request->send(200); +} + void httpAppConfig(AsyncWebServerRequest *request) { if (!request->hasArg("key")) { error("missing parameter 'key'"); @@ -97,6 +125,10 @@ void httpSetup() { server.on("/action/right", HTTP_GET, httpActionRight); server.on("/action/cancel", HTTP_GET, httpActionCancel); server.on("/action/confirm", HTTP_GET, httpActionConfirm); + server.on("/action/left/up", HTTP_GET, httpActionLeftUp); + server.on("/action/left/down", HTTP_GET, httpActionLeftDown); + server.on("/action/right/up", HTTP_GET, httpActionRightUp); + server.on("/action/right/down", HTTP_GET, httpActionRightDown); server.on("/app/config", HTTP_GET, httpAppConfig); server.serveStatic("/", LittleFS, "/http/", "max-age=86400").setDefaultFile("index.html"); server.onNotFound(httpNotFound); diff --git a/src/display/Display.cpp b/src/display/Display.cpp deleted file mode 100644 index 44bed1d..0000000 --- a/src/display/Display.cpp +++ /dev/null @@ -1,3 +0,0 @@ -#include "Display.h" - -Display display; diff --git a/src/display/Display.h b/src/display/Display.h index c68fb01..917832e 100644 --- a/src/display/Display.h +++ b/src/display/Display.h @@ -5,53 +5,69 @@ #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] = {}; + size_t totalPixelCount; + + size_t pixelByteCount; + + Color *pixels; + + char *hexBuffer; Color color = WHITE; - char hexBuffer[HEX_BUFFER_SIZE] = ""; +protected: - struct Mapping { - uint8_t x; - uint8_t y; - }; - -public: - - Display() /* : leds(PIXEL_COUNT, GPIO_NUM_13) */ { + explicit Display(const int totalPixelCount) :/* leds(totalPixelCount, GPIO_NUM_13) ,*/ totalPixelCount(totalPixelCount), pixelByteCount(totalPixelCount * sizeof(Color)) { // leds.begin(); + + pixels = static_cast(malloc(pixelByteCount)); + if (pixels == nullptr) { + error("Failed to allocate memory for Display.pixels"); + } + + hexBuffer = static_cast(malloc(pixelByteCount + 1)); + if (hexBuffer == nullptr) { + error("Failed to allocate memory for Display.hexBuffer"); + } else { + *hexBuffer = 0; + } + setBrightness(6); clear(); flush(); } + ~Display() { + if (pixels != nullptr) { + free(pixels); + pixels = nullptr; + } + if (hexBuffer != nullptr) { + free(hexBuffer); + hexBuffer = nullptr; + } + } + +public: + + void setPixel(const int index) const { + if (pixels == nullptr) { + return; + } + pixels[index] = color; + } + void setColor(const Color newColor) { this->color = newColor; } @@ -60,12 +76,15 @@ public: // leds.setBrightness(brightness); } - void clear() { - memset(pixels, 0, TOTAL_PIXEL_BYTE_COUNT); + void clear() const { + if (pixels == nullptr) { + return; + } + memset(pixels, 0, pixelByteCount); } - void flush(const bool forceNextHexBuffer = false) { - // memcpy(leds.getPixels(), buffer, PIXEL_BYTE_COUNT); + void flush(const bool forceNextHexBuffer = false) const { + // memcpy(leds.getPixels(), buffer, pixelByteCount); // leds.show(); const auto now = millis() - HEX_BUFFER_MIN_WAIT_MS; @@ -73,178 +92,43 @@ public: if (now - last >= HEX_BUFFER_MIN_WAIT_MS || forceNextHexBuffer) { last = now; fillHexBuffer(); - websocketSendAll(hexBuffer); + sendHexBufferAll(); } } - 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 sendHexBufferAll() const { + if (hexBuffer == nullptr) { + return; + } + if (*hexBuffer == 0) { + return; + } + websocketSendAll(hexBuffer); } - 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 sendHexBuffer(AsyncWebSocketClient *client) const { + if (hexBuffer == nullptr) { + return; } - } - - 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; + if (*hexBuffer == 0) { + return; } - 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); - } +private +: + + void fillHexBuffer() const { + if (pixels == nullptr || hexBuffer == nullptr) { + return; } - } - - 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); + for (auto pixel = pixels; pixel < pixels + totalPixelCount; ++pixel) { + b += snprintf(b, sizeof hexBuffer - (b - hexBuffer), "%X%X%X", pixel->r / 16, pixel->g / 16, pixel->b / 16); } } + }; -extern Display display; - #endif diff --git a/src/display/DisplayDigits4.h b/src/display/DisplayDigits4.h new file mode 100644 index 0000000..d7b137b --- /dev/null +++ b/src/display/DisplayDigits4.h @@ -0,0 +1,200 @@ +#ifndef DISPLAY_DIGITS4_H +#define DISPLAY_DIGITS4_H + +#include "DisplayPart.h" +#include "font.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_POINTS_PIXEL_COUNT (4 * 3 * 5) +#define TOTAL_PIXEL_COUNT (TOTAL_SEGMENT_PIXEL_COUNT + TOTAL_DOT_PIXEL_COUNT + TOTAL_POINTS_PIXEL_COUNT) + +#define DISPLAY_VIRTUAL_WIDTH 25 +#define DISPLAY_VIRTUAL_HEIGHT 9 + +class DisplayDigits4 final : public DisplayPart { + + struct Mapping { + + uint8_t x; + + uint8_t y; + + }; + +public: + + explicit DisplayDigits4(Display *display, const int indexOffset) + : DisplayPart(display, indexOffset,TOTAL_PIXEL_COUNT) { + // + } + + void printf(const char *format, ...) const { + 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) const { + 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) const { + auto pixel = PIXELS_PER_DIGIT * 2; + if (dot0) { + setIndexPixel(pixel); + } + pixel++; + if (dot1) { + setIndexPixel(pixel); + } + pixel++; + if (dot2) { + setIndexPixel(pixel); + } + pixel++; + if (dot3) { + setIndexPixel(pixel); + } + } + + void printCharacter(const int digit, const char character) const { + auto pixel = digit * PIXELS_PER_DIGIT + (digit >= 2 ? TOTAL_DOT_PIXEL_COUNT : 0); + const auto symbol = getSegment7Character(character); + for (auto s = *symbol; s < *symbol + sizeof(Segment7Character); s++) { + if (*s) { + setIndexPixel(pixel++); + setIndexPixel(pixel++); + setIndexPixel(pixel++); + } else { + pixel += 3; + } + } + } + + void fillRect(const uint8_t x, const uint8_t y, const uint8_t w, const uint8_t h) const { + for (auto dx = 0; dx < w; ++dx) { + for (auto dy = 0; dy < h; ++dy) { + setRasterPixel(x + dx, y + dy); + } + } + } + + void strokeRect(const uint8_t x, const uint8_t y, const uint8_t w, const uint8_t h) const { + for (auto dx = 0; dx < w; ++dx) { + setRasterPixel(x + dx, y); + setRasterPixel(x + dx, y + h); + } + for (auto dy = 0; dy < h; ++dy) { + setRasterPixel(x, y + dy); + setRasterPixel(x + w, y + dy); + } + } + + void setRasterPixel(const uint8_t x, const uint8_t y) const override { + const auto index = findIndexForVirtualCoordinates(x, y); + if (index >= 0) { + setIndexPixel(index); + } + } + +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}; + + int findIndexForVirtualCoordinates(const uint8_t x, const uint8_t y) const { + if (x < 0 || x > 24 || y < 0 || y > 8) { + return -1; + } + if (x >= 20) { + return _findIndexForVirtualCoordinates_digitRelative(x - 20, y) + 3 * PIXELS_PER_DIGIT + TOTAL_DOT_PIXEL_COUNT; + } + if (x >= 14) { + return _findIndexForVirtualCoordinates_digitRelative(x - 14, y) + 2 * PIXELS_PER_DIGIT + TOTAL_DOT_PIXEL_COUNT; + } + if (x == 12) { + return _findIndexForVirtualCoordinates_dotRelative(y) + 2 * PIXELS_PER_DIGIT; + } + if (x >= 6) { + return _findIndexForVirtualCoordinates_digitRelative(x - 6, y) + 1 * PIXELS_PER_DIGIT; + } + return _findIndexForVirtualCoordinates_digitRelative(x, y) + 0 * PIXELS_PER_DIGIT; + } + + int _findIndexForVirtualCoordinates_digitRelative(const uint8_t x, const uint8_t y) const { + for (auto index = 0; index < PIXELS_PER_DIGIT; index++) { + const auto item = digitMapping[index]; + if (item.x == x && item.y == y) { + return index; + } + } + return -1; + } + + int _findIndexForVirtualCoordinates_dotRelative(const uint8_t y) const { + for (auto index = 0; index < DOT_COUNT; index++) { + const auto dotY = dotMapping[index]; + if (dotY == y) { + return index; + } + } + return -1; + } + +}; + +#endif diff --git a/src/display/DisplayMatrix.h b/src/display/DisplayMatrix.h new file mode 100644 index 0000000..455aa77 --- /dev/null +++ b/src/display/DisplayMatrix.h @@ -0,0 +1,66 @@ +#ifndef DISPLAY_MATRIX_H +#define DISPLAY_MATRIX_H + +#include "DisplayPart.h" +#include "DisplayRasterFont.h" + +class DisplayMatrix final : public DisplayPart { + +public: + + const int rows; + + DisplayMatrix(Display *display, const int indexOffset, const int cols, const int rows) : DisplayPart(display, indexOffset, cols * rows), rows(rows) { + // + } + + int printf(const char *format, ...) const { + va_list args; + va_start(args, format); + const auto x = printf(0, 0, format, args); + va_end(args); + return x; + } + + int printf(int x, const int y, const char *format, ...) const { + va_list args; + va_start(args, format); + x = printf(x, y, format, args); + va_end(args); + return x; + } + + int printf(const int x, const int y, const char *format, const va_list args) const { + char buffer[128]; + vsnprintf(buffer, sizeof buffer, format, args); + return print(x, y, buffer); + } + + int print(int x, const int y, const char *text) const { + for (auto c = text; *c != '\0'; ++c) { + x = print(x, y, *c) + 1; + } + return x; + } + + int print(const int x, const int y, const char c) const { + auto maxWidth = 0; + const auto character = getRasterCharacter(c); + for (auto cy = 0; cy < sizeof(RasterCharacter); cy++) { + for (auto cx = 0; cx < sizeof(RasterCharacter[0]); cx++) { + if (character[y][x]) { + setRasterPixel(x + cx, y + cy); + maxWidth = max(maxWidth, x + cx); + } + } + } + return x + maxWidth; + } + + void setRasterPixel(const uint8_t x, const uint8_t y) const override { + setIndexPixel(x * rows + y); + } + +}; + +#endif diff --git a/src/display/DisplayPart.h b/src/display/DisplayPart.h new file mode 100644 index 0000000..2225024 --- /dev/null +++ b/src/display/DisplayPart.h @@ -0,0 +1,37 @@ +#ifndef DISPLAYPART_H +#define DISPLAYPART_H + +#include "Display.h" + + +class DisplayPart { + + Display *display; + +public: + + const int indexOffset; + + const int totalPixelCount; + + const int nextPixelIndex; + +protected: + + DisplayPart(Display *display, const int indexOffset, const int totalPixelCount) : display(display), indexOffset(indexOffset), totalPixelCount(totalPixelCount), nextPixelIndex(indexOffset + totalPixelCount) { + // + } + + virtual ~DisplayPart() = default; + +public: + + virtual void setRasterPixel(uint8_t x, uint8_t y) const = 0; + + void setIndexPixel(const int index) const { + display->setPixel(indexOffset + index); + } + +}; + +#endif diff --git a/src/display/DisplayRasterFont.cpp b/src/display/DisplayRasterFont.cpp new file mode 100644 index 0000000..3840c44 --- /dev/null +++ b/src/display/DisplayRasterFont.cpp @@ -0,0 +1,325 @@ +#include "DisplayRasterFont.h" + +#include "../core/BASICS.h" + +#define RASTER_FONT_NUMBER 0 +#define RASTER_FONT_ALPHA (RASTER_FONT_NUMBER + 10) +#define RASTER_FONT_SPECIAL (RASTER_FONT_ALPHA + 26) + +#define RASTER_FONT_COLON (RASTER_FONT_SPECIAL + 1) +#define RASTER_FONT_DASH (RASTER_FONT_COLON + 1) +#define RASTER_FONT_PERCENT (RASTER_FONT_DASH + 1) +#define RASTER_FONT_DEGREE (RASTER_FONT_PERCENT + 1) +#define RASTER_FONT_FALLBACK (RASTER_FONT_DEGREE + 1) + +RasterCharacter *getRasterCharacter(const char character) { + if (character >= '0' && character <= '9') { + return &RASTER_FONT[character - '0' + RASTER_FONT_NUMBER]; + } + if (character >= 'a' && character <= 'z') { + return &RASTER_FONT[character - 'a' + RASTER_FONT_ALPHA]; + } + if (character >= 'A' && character <= 'Z') { + return &RASTER_FONT[character - 'A' + RASTER_FONT_ALPHA]; + } + switch (character) { + case ':': return &RASTER_FONT[RASTER_FONT_COLON]; + case '-': return &RASTER_FONT[RASTER_FONT_DASH]; + case '%': return &RASTER_FONT[RASTER_FONT_PERCENT]; + case '^': return &RASTER_FONT[RASTER_FONT_DEGREE]; + default: { + error("[ERROR] NO RASTER MAPPING FOR CHARACTER \"%c\" = #%d\n", character, character); + return &RASTER_FONT[RASTER_FONT_FALLBACK]; + } + } +} + +RasterCharacter RASTER_FONT[] = { + { + {X, X, X}, + {X, _, X}, + {X, _, X}, + {X, _, X}, + {X, X, X}, + }, // 0 + { + {_, _, X}, + {_, X, X}, + {X, _, X}, + {_, _, X}, + {_, _, X}, + }, // 1 + { + {X, X, X}, + {_, _, X}, + {X, X, X}, + {X, _, _}, + {X, X, X}, + }, // 2 + { + {X, X, X}, + {_, _, X}, + {_, X, X}, + {_, _, X}, + {X, X, X}, + }, // 3 + { + {X, _, X}, + {X, _, X}, + {X, X, X}, + {_, _, X}, + {_, _, X}, + }, // 4 + { + {X, X, X}, + {X, _, _}, + {X, X, X}, + {_, _, X}, + {X, X, X}, + }, // 5 + { + {X, X, X}, + {X, _, _}, + {X, X, X}, + {X, _, X}, + {X, X, X}, + }, // 6 + { + {X, X, X}, + {_, _, X}, + {_, X, _}, + {X, _, _}, + {X, _, _}, + }, // 7 + { + {X, X, X}, + {X, _, X}, + {X, X, X}, + {X, _, X}, + {X, X, X}, + }, // 8 + { + {X, X, X}, + {X, _, X}, + {X, X, X}, + {_, _, X}, + {X, X, X}, + }, // 9 + { + {_, X, _}, + {X, _, X}, + {X, X, X}, + {X, _, X}, + {X, _, X}, + }, // A + { + {X, X, _}, + {X, _, X}, + {X, X, _}, + {X, _, X}, + {X, X, _}, + }, // + { + {_, X, X}, + {X, _, _}, + {X, _, _}, + {X, _, _}, + {_, X, X}, + }, // + { + {X, X, _}, + {X, _, X}, + {X, _, X}, + {X, _, X}, + {X, X, _}, + }, // + { + {X, X, X}, + {X, _, _}, + {X, X, _}, + {X, _, _}, + {X, X, X}, + }, // + { + {X, X, X}, + {X, _, _}, + {X, X, _}, + {X, _, _}, + {X, _, _}, + }, // + { + {_, X, X}, + {X, _, _}, + {X, _, X}, + {X, _, X}, + {_, X, _}, + }, // + { + {X, _, X}, + {X, _, X}, + {X, X, X}, + {X, _, X}, + {X, _, X}, + }, // + { + {_, X, _}, + {_, X, _}, + {_, X, _}, + {_, X, _}, + {_, X, _}, + }, // + { + {X, X, X}, + {_, _, X}, + {_, _, X}, + {X, _, X}, + {_, X, _}, + }, // + { + {X, _, X}, + {X, X, _}, + {X, X, _}, + {X, _, _}, + {X, _, X}, + }, // + { + {X, _, _}, + {X, _, _}, + {X, _, _}, + {X, _, _}, + {X, X, X}, + }, // + { + {X, _, X}, + {X, X, X}, + {X, _, X}, + {X, _, X}, + {X, _, X}, + }, // + { + {_, _, _}, + {_, _, _}, + {_, X, _}, + {X, _, X}, + {X, _, X}, + }, // + { + {_, _, _}, + {_, _, _}, + {_, X, _}, + {X, _, X}, + {_, X, _}, + }, // + { + {X, X, _}, + {X, _, X}, + {X, X, _}, + {X, _, _}, + {X, _, _}, + }, // + { + {_, _, _}, + {_, X, X}, + {X, _, X}, + {_, X, X}, + {_, _, X}, + }, // + { + {X, X, _}, + {X, _, X}, + {X, X, _}, + {X, X, _}, + {X, _, X}, + }, // + { + {_, X, X}, + {X, _, _}, + {_, X, _}, + {_, _, X}, + {X, X, _}, + }, // + { + {X, X, X}, + {_, X, _}, + {_, X, _}, + {_, X, _}, + {_, X, _}, + }, // + { + {X, _, X}, + {X, _, X}, + {X, _, X}, + {X, _, X}, + {_, X, _}, + }, // : + { + {_, _, _}, + {_, _, _}, + {_, _, _}, + {X, _, X}, + {_, X, _}, + }, // : + { + {X, _, X}, + {X, _, X}, + {X, _, X}, + {X, X, X}, + {X, _, X}, + }, // : + { + {_, _, _}, + {_, _, _}, + {X, _, X}, + {_, X, _}, + {X, _, X}, + }, // : + { + {X, _, X}, + {X, _, X}, + {_, X, _}, + {_, X, _}, + {X, _, _}, + }, // : + { + {X, X, X}, + {_, X, X}, + {_, X, _}, + {X, _, _}, + {X, X, X}, + }, // : + { + {_, _, _}, + {_, X, _}, + {_, _, _}, + {_, X, _}, + {_, _, _}, + }, // : + { + {_, _, _}, + {_, _, _}, + {X, X, X}, + {_, _, _}, + {_, _, _}, + }, // - + { + {X, _, _}, + {_, _, X}, + {_, X, _}, + {X, _, _}, + {_, _, X}, + }, // % + { + {_, X, _}, + {X, _, X}, + {_, X, _}, + {_, _, _}, + {_, _, _}, + }, // % + { + {X, X, X}, + {X, X, X}, + {X, X, X}, + {X, X, X}, + {X, X, X}, + }, // FALLBACK +}; diff --git a/src/display/DisplayRasterFont.h b/src/display/DisplayRasterFont.h new file mode 100644 index 0000000..c99cb22 --- /dev/null +++ b/src/display/DisplayRasterFont.h @@ -0,0 +1,10 @@ +#ifndef RASTER_FONT_H +#define RASTER_FONT_H + +typedef bool RasterCharacter[5][4]; + +extern RasterCharacter RASTER_FONT[]; + +RasterCharacter *getRasterCharacter(char character); + +#endif diff --git a/src/display/MyDisplay.cpp b/src/display/MyDisplay.cpp new file mode 100644 index 0000000..b512f42 --- /dev/null +++ b/src/display/MyDisplay.cpp @@ -0,0 +1,3 @@ +#include "MyDisplay.h" + +MyDisplay display; diff --git a/src/display/MyDisplay.h b/src/display/MyDisplay.h new file mode 100644 index 0000000..fd94ffc --- /dev/null +++ b/src/display/MyDisplay.h @@ -0,0 +1,32 @@ +#ifndef MY_DISPLAY_H +#define MY_DISPLAY_H + +#include "DisplayDigits4.h" + +#include "DisplayMatrix.h" + +class MyDisplay : public Display { + +public: + + DisplayDigits4 main; + + DisplayMatrix left; + + DisplayMatrix right; + + explicit MyDisplay() + : Display(TOTAL_PIXEL_COUNT + 2 * 3 * 5), + main(this, 0), + left(this, main.nextPixelIndex, 3, 5), + right(this, left.nextPixelIndex, 3, 5) { + // + } + + ~MyDisplay() = default; + +}; + +extern MyDisplay display; + +#endif diff --git a/src/display/font.cpp b/src/display/font.cpp index 1e6ed1c..7ef9935 100644 --- a/src/display/font.cpp +++ b/src/display/font.cpp @@ -1,9 +1,30 @@ #include "font.h" -#include -#include "../core/log.h" +#include "../core/BASICS.h" -SYMBOL SYMBOLS[][SYMBOL_SIZE] = { +Segment7Character *getSegment7Character(const char character) { + if (character >= '0' && character <= '9') { + return &SEGMENT7_FONT[character - '0']; + } + if (character >= 'a' && character <= 'z') { + return &SEGMENT7_FONT[character - 'a' + 10]; + } + if (character >= 'A' && character <= 'Z') { + return &SEGMENT7_FONT[character - 'A' + 10]; + } + switch (character) { + case '-': return &SEGMENT7_FONT[36]; + case '_': return &SEGMENT7_FONT[37]; + case '^': return &SEGMENT7_FONT[38]; + case ' ': return &SEGMENT7_FONT[39]; + default: { + error("[ERROR] NO SEGMENT7 MAPPING FOR CHARACTER \"%c\" = #%d\n", character, character); + return &SEGMENT7_FONT[countof(SEGMENT7_FONT) - 1]; + } + } +} + +Segment7Character SEGMENT7_FONT[] = { {X,X,X,X,X,X,_}, // 0 {_,_,X,X,_,_,_}, // 1 {_,X,X,_,X,X,X}, // 2 @@ -45,25 +66,3 @@ SYMBOL SYMBOLS[][SYMBOL_SIZE] = { {X,X,X,_,_,_,X}, // ° {_,_,_,_,_,_,_}, // }; - -SYMBOL *getSymbol(const char character) { - if (character >= '0' && character <= '9') { - return SYMBOLS[character - '0']; - } - if (character >= 'a' && character <= 'z') { - return SYMBOLS[character - 'a' + 10]; - } - if (character >= 'A' && character <= 'Z') { - return SYMBOLS[character - 'A' + 10]; - } - switch (character) { - case '-': return SYMBOLS[36]; - case '_': return SYMBOLS[37]; - case '^': return SYMBOLS[38]; - case ' ': return SYMBOLS[39]; - default: { - error("[ERROR] NO SYMBOL MAPPING FOR CHARACTER \"%c\" = #%d\n", character, character); - return SYMBOLS[SYMBOL_SIZE - 1]; - } - } -} diff --git a/src/display/font.h b/src/display/font.h index d806d1a..a9ea567 100644 --- a/src/display/font.h +++ b/src/display/font.h @@ -1,17 +1,10 @@ #ifndef FONT_H #define FONT_H -#define X true -#define _ false +typedef const bool Segment7Character[7]; -constexpr auto SYMBOL_COUNT = 39; +extern Segment7Character SEGMENT7_FONT[40]; -constexpr auto SYMBOL_SIZE = 7; - -typedef const bool SYMBOL[SYMBOL_SIZE]; - -extern SYMBOL SYMBOLS[][SYMBOL_SIZE]; - -SYMBOL *getSymbol(char character); +Segment7Character *getSegment7Character(char character); #endif diff --git a/src/main.cpp b/src/main.cpp index f012896..eab15ab 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -12,7 +12,7 @@ void setup() { bootDelay(); fsMount(); httpSetup(); - appStart(APP_DEMO_NAME); + appStart(APP_MATCH_NAME); } void loop() {