Sporttafel-old/src/display/Display.h

251 lines
6.3 KiB
C++

#ifndef DISPLAY_H
#define DISPLAY_H
// #include <Adafruit_NeoPixel.h>
#include <AsyncWebSocket.h>
#include <cstdint>
#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