245 lines
5.0 KiB
C++
245 lines
5.0 KiB
C++
#ifndef DISPLAY_H
|
|
#define DISPLAY_H
|
|
|
|
#include <Adafruit_NeoPixel.h>
|
|
|
|
enum Align {
|
|
ALIGN_LEFT, ALIGN_CENTER, ALIGN_RIGHT
|
|
};
|
|
|
|
enum Font {
|
|
FONT5, FONT7
|
|
};
|
|
|
|
struct Color {
|
|
uint8_t r, g, b;
|
|
};
|
|
|
|
extern Color WHITE;
|
|
|
|
extern Color BLACK;
|
|
|
|
extern Color RED;
|
|
|
|
extern Color GREEN;
|
|
|
|
extern Color BLUE;
|
|
|
|
extern Color YELLOW;
|
|
|
|
extern Color CYAN;
|
|
|
|
extern Color MAGENTA;
|
|
|
|
extern Color ORANGE;
|
|
|
|
extern Color GREY;
|
|
|
|
extern const bool DOT7[][7][1];
|
|
|
|
extern const bool SPECIAL7[][7][3];
|
|
|
|
extern const bool NUM7[][7][4];
|
|
|
|
extern const bool ALPHA7[][7][4];
|
|
|
|
class Display {
|
|
|
|
Adafruit_NeoPixel leds;
|
|
|
|
bool pixels[8 * 32]{};
|
|
|
|
static int mapPixel(const int x, const int y) {
|
|
return y * 32 + (y % 2 == 0 ? x : 31 - x);
|
|
}
|
|
|
|
public:
|
|
|
|
explicit Display(const uint8_t pin): leds(8 * 32, pin,NEO_GRB + NEO_KHZ800) {
|
|
//
|
|
}
|
|
|
|
void setBrightness(int i) {
|
|
#if LEDS_ENABLED
|
|
leds.setBrightness(max(0, min(i, 255)));
|
|
#endif
|
|
}
|
|
|
|
void setup() {
|
|
#if LEDS_ENABLED
|
|
leds.begin();
|
|
#endif
|
|
for (int y = 0; y < 8; y++) {
|
|
for (int x = 0; x < 32; x++) {
|
|
pixels[mapPixel(x, y)] = false;
|
|
}
|
|
}
|
|
setBrightness(32);
|
|
clear();
|
|
show();
|
|
}
|
|
|
|
void show() {
|
|
#if LEDS_ENABLED
|
|
leds.show();
|
|
#endif
|
|
}
|
|
|
|
void setPixel(const int x, const int y, const Color &color) {
|
|
const auto index = mapPixel(x, y);
|
|
if (index >= 8 * 32) {
|
|
Serial.printf("[ERROR] No pixel at (%d, %d) = %d >= %d\n", x, y, index, 8 * 32);
|
|
return;
|
|
}
|
|
#if LEDS_ENABLED
|
|
leds.setPixelColor(index, color.r, color.g, color.b);
|
|
#endif
|
|
pixels[index] = color.r > 0 || color.g > 0 || color.b > 0;
|
|
}
|
|
|
|
void rect(const int x0, const int y0, const int w, const int h, const Color &color) {
|
|
for (int y = y0; y < y0 + h; y++) {
|
|
for (int x = x0; x < x0 + w; x++) {
|
|
setPixel(x, y, color);
|
|
}
|
|
}
|
|
}
|
|
|
|
void print(int x, const int y, const Align align, const Font font, const Color &color, const char *format, ...) {
|
|
char text[32];
|
|
va_list args;
|
|
va_start(args, format);
|
|
vsnprintf(text, sizeof text, format, args);
|
|
va_end(args);
|
|
const auto width = measure(text, font);
|
|
x = align == ALIGN_LEFT ? x : align == ALIGN_RIGHT ? x - width : x - width / 2;
|
|
for (const char *c = text; *c; c++) {
|
|
x += printChar(x, y, *c, font, color) + 1;
|
|
}
|
|
}
|
|
|
|
int printChar(const int x, const int y, const char c, const Font font, const Color &color) {
|
|
if (isDot(c)) {
|
|
if (c == '.') {
|
|
return printSymbol(x, y, 0, color, DOT7);
|
|
}
|
|
if (c == ',') {
|
|
return printSymbol(x, y, 1, color, DOT7);
|
|
}
|
|
if (c == ':') {
|
|
return printSymbol(x, y, 2, color, DOT7);
|
|
}
|
|
if (c == ';') {
|
|
return printSymbol(x, y, 3, color, DOT7);
|
|
}
|
|
}
|
|
if (isSpecial(c)) {
|
|
if (c == ' ') {
|
|
return printSymbol(x, y, 0, color, SPECIAL7);
|
|
}
|
|
if (c == '-') {
|
|
return printSymbol(x, y, 1, color, SPECIAL7);
|
|
}
|
|
}
|
|
if (isNumber(c)) {
|
|
return printSymbol(x, y, c - '0', color, NUM7);
|
|
}
|
|
if (isLower(c)) {
|
|
return printSymbol(x, y, c - 'a', color, ALPHA7);
|
|
}
|
|
if (isUpper(c)) {
|
|
return printSymbol(x, y, c - 'A', color, ALPHA7);
|
|
}
|
|
Serial.printf("UNKNOWN SYMBOL '%c'\n", c);
|
|
return 0;
|
|
}
|
|
|
|
template<int symbolHeight, int symbolWidth>
|
|
int printSymbol(const int x, const int y, const int index, const Color &color, const bool symbols[][symbolHeight][symbolWidth]) {
|
|
for (int innerY = 0; innerY < symbolHeight; innerY++) {
|
|
for (int innerX = 0; innerX < symbolWidth; innerX++) {
|
|
if (symbols[index][innerY][innerX]) {
|
|
setPixel(x + innerX, y + innerY, color);
|
|
}
|
|
}
|
|
}
|
|
return symbolWidth;
|
|
}
|
|
|
|
void clear() {
|
|
rect(0, 0, 32, 8, BLACK);
|
|
}
|
|
|
|
void log() const {
|
|
Serial.print("+");
|
|
for (int x = 0; x < 32; x++) {
|
|
Serial.print("--");
|
|
}
|
|
Serial.print("+\n");
|
|
|
|
for (int y = 0; y < 8; y++) {
|
|
Serial.print("|");
|
|
for (int x = 0; x < 32; x++) {
|
|
if (pixels[mapPixel(x, y)]) {
|
|
Serial.print("##");
|
|
} else {
|
|
Serial.print(" ");
|
|
}
|
|
}
|
|
Serial.print("|\n");
|
|
}
|
|
|
|
Serial.print("+");
|
|
for (int x = 0; x < 32; x++) {
|
|
Serial.print("--");
|
|
}
|
|
Serial.print("+\n");
|
|
|
|
Serial.print('\n');
|
|
}
|
|
|
|
static uint8_t measure(const char *text, const Font font) {
|
|
uint8_t width = 0;
|
|
for (const char *c = text; *c; c++) {
|
|
if (c != text) {
|
|
width++; // space between chars
|
|
}
|
|
width += getWidth(*c, font);
|
|
}
|
|
return width;
|
|
}
|
|
|
|
static uint8_t getWidth(const char c, const Font font) {
|
|
if (isDot(c)) {
|
|
return 1;
|
|
}
|
|
if (isSpecial(c)) {
|
|
return 3;
|
|
}
|
|
return font == FONT5 ? 3 : 4;
|
|
}
|
|
|
|
static bool isDot(const char c) {
|
|
return c == ':' || c == ';' || c == '.' || c == ',';
|
|
}
|
|
|
|
static bool isSpecial(const char c) {
|
|
return c == ' ' || c == '-';
|
|
}
|
|
|
|
static bool isNumber(const char c) {
|
|
return c >= '0' && c <= '9';
|
|
}
|
|
|
|
static bool isLower(const char c) {
|
|
return c >= 'a' && c <= 'z';
|
|
}
|
|
|
|
static bool isUpper(const char c) {
|
|
return c >= 'A' && c <= 'Z';
|
|
}
|
|
|
|
};
|
|
|
|
#endif
|