Compare commits
15 Commits
f2ce84a002
...
2b80b1d671
| Author | SHA1 | Date | |
|---|---|---|---|
| 2b80b1d671 | |||
| d498cd12b4 | |||
| 76120d4607 | |||
| 1a75387aea | |||
| 80df3ec9cc | |||
| 2b61f7ae98 | |||
| 03d6899817 | |||
| 7fa66fb4d5 | |||
| 9a6ca32976 | |||
| 56dda350b4 | |||
| 8e5a4e2501 | |||
| 705e8c7d77 | |||
| d57ef636f2 | |||
| 8bbddc3bb8 | |||
| d7ed172fca |
@ -7,7 +7,7 @@
|
|||||||
|
|
||||||
Config config("/test.json");
|
Config config("/test.json");
|
||||||
|
|
||||||
DisplayMatrix<32, 8> display(27);
|
DisplayMatrix<32, 8> display(13);
|
||||||
|
|
||||||
class NodeTest final : public PatrixNode {
|
class NodeTest final : public PatrixNode {
|
||||||
|
|
||||||
@ -19,18 +19,10 @@ public:
|
|||||||
|
|
||||||
void setup() override {
|
void setup() override {
|
||||||
config.read();
|
config.read();
|
||||||
display.setup();
|
display.setup(10);
|
||||||
display.setBrightness(6);
|
|
||||||
|
|
||||||
display.clear();
|
display.clear();
|
||||||
|
display.printf(1, 1, LEFT, Blue, "Test");
|
||||||
display.foreground = Blue;
|
display.drawLine(0, 7, 32, 0, 1, Red);
|
||||||
display.printf("Test");
|
|
||||||
|
|
||||||
display.foreground = Red;
|
|
||||||
display.cursorX = 0;
|
|
||||||
display.cursorY = 7;
|
|
||||||
display.drawLine(32, 0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void loop() override {
|
void loop() override {
|
||||||
|
|||||||
@ -4,7 +4,6 @@ void setup() {
|
|||||||
logSetup();
|
logSetup();
|
||||||
bootDelay();
|
bootDelay();
|
||||||
patrixNode.setup();
|
patrixNode.setup();
|
||||||
httpSetup();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void loop() {
|
void loop() {
|
||||||
|
|||||||
@ -15,14 +15,14 @@ class Config final {
|
|||||||
|
|
||||||
const String path;
|
const String path;
|
||||||
|
|
||||||
JsonDocument config;
|
|
||||||
|
|
||||||
unsigned long dirty = 0;
|
unsigned long dirty = 0;
|
||||||
|
|
||||||
bool doForceNextHexBuffer = true;
|
bool doForceNextHexBuffer = true;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
|
JsonDocument json;
|
||||||
|
|
||||||
explicit Config(String path) : path(std::move(path)) {
|
explicit Config(String path) : path(std::move(path)) {
|
||||||
//
|
//
|
||||||
}
|
}
|
||||||
@ -33,12 +33,12 @@ public:
|
|||||||
fsMount();
|
fsMount();
|
||||||
auto file = LittleFS.open(path, "r");
|
auto file = LittleFS.open(path, "r");
|
||||||
if (!file) {
|
if (!file) {
|
||||||
error("failed to open file for config: %s", path.c_str());
|
error("Failed to open file for config: %s", path.c_str());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!deserialize(file)) {
|
if (!deserialize(file)) {
|
||||||
config.clear();
|
json.clear();
|
||||||
config = config.to<JsonObject>();
|
json = json.to<JsonObject>();
|
||||||
}
|
}
|
||||||
file.close();
|
file.close();
|
||||||
}
|
}
|
||||||
@ -47,16 +47,16 @@ public:
|
|||||||
fsMount();
|
fsMount();
|
||||||
auto write = LittleFS.open(path, "w");
|
auto write = LittleFS.open(path, "w");
|
||||||
if (!write) {
|
if (!write) {
|
||||||
error("failed to open file for config write: %s", path.c_str());
|
error("Failed to open file for config write: %s", path.c_str());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const auto size = measureJson(config);
|
const auto size = measureJson(json);
|
||||||
if (serializeJson(config, write) == size) {
|
if (serializeJson(json, write) == size) {
|
||||||
char buffer[256];
|
char buffer[256];
|
||||||
serializeJson(config, buffer, sizeof buffer);
|
serializeJson(json, buffer, sizeof buffer);
|
||||||
info("config written: %s => %s", path.c_str(), buffer);
|
info("Config written: %s => %s", path.c_str(), buffer);
|
||||||
} else {
|
} else {
|
||||||
error("failed to write config: %s", path.c_str());
|
error("Failed to write config: %s", path.c_str());
|
||||||
}
|
}
|
||||||
write.close();
|
write.close();
|
||||||
}
|
}
|
||||||
@ -69,35 +69,82 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
T get(const char *key, T fallback) {
|
bool has(const char *key) {
|
||||||
if (config[key].is<T>()) {
|
return json[key].is<T>();
|
||||||
return config[key].as<T>();
|
|
||||||
}
|
|
||||||
warn("config key \"%s\" not found!", key);
|
|
||||||
return fallback;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
void set(const char *key, T value) {
|
T get(const char *key, T fallback) {
|
||||||
config[key] = value;
|
if (json[key].is<T>()) {
|
||||||
dirty = max(1UL, millis()); // avoid special value zero (=> not dirty)
|
return json[key].as<T>();
|
||||||
|
}
|
||||||
|
warn("Config key \"%s\" not found!", key);
|
||||||
|
return fallback;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool setIfNot(const char *key, const int value) {
|
||||||
|
auto changed = !json[key].is<int>();
|
||||||
|
if (!changed) {
|
||||||
|
changed = json[key].as<int>() != value;
|
||||||
|
}
|
||||||
|
if (changed) {
|
||||||
|
set(key, value);
|
||||||
|
info("Changed key \"%s\" to: %d", key, value);
|
||||||
|
}
|
||||||
|
return changed;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool setIfNot(const char *key, const double value) {
|
||||||
|
auto changed = !json[key].is<double>();
|
||||||
|
if (!changed) {
|
||||||
|
changed = json[key].as<double>() != value;
|
||||||
|
}
|
||||||
|
if (changed) {
|
||||||
|
set(key, value);
|
||||||
|
info("Changed key \"%s\" to: %d", key, value);
|
||||||
|
}
|
||||||
|
return changed;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
void set(const String& key, T value) {
|
||||||
|
json[key] = value;
|
||||||
|
dirty = millis();
|
||||||
|
if (dirty == 0) {
|
||||||
|
// avoid special value zero (=> not dirty)
|
||||||
|
dirty--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned long getAutoWriteInMillis() const {
|
||||||
|
if (dirty == 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return CONFIG_WRITE_DELAY_MILLIS - (millis() - dirty);
|
||||||
|
}
|
||||||
|
|
||||||
|
time_t getAutoWriteAtEpoch() const {
|
||||||
|
if (dirty == 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return time(nullptr) + static_cast<long>(getAutoWriteInMillis() / 1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
bool deserialize(File file) {
|
bool deserialize(File file) {
|
||||||
if (deserializeJson(config, file) != DeserializationError::Ok) {
|
if (deserializeJson(json, file) != DeserializationError::Ok) {
|
||||||
error("failed to deserialize config: %s", file.path());
|
error("Failed to deserialize config: %s", file.path());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!config.is<JsonObject>()) {
|
if (!json.is<JsonObject>()) {
|
||||||
error("config not a json-object: %s", file.path());
|
error("Config not a json-object: %s", file.path());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
char buffer[256];
|
char buffer[256];
|
||||||
serializeJson(config, buffer, sizeof buffer);
|
serializeJson(json, buffer, sizeof buffer);
|
||||||
info("config loaded: %s => %s", path.c_str(), buffer);
|
info("Config loaded: %s => %s", path.c_str(), buffer);
|
||||||
config = config.as<JsonObject>();
|
json = json.as<JsonObject>();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -32,16 +32,28 @@ const char *getResetReason(const RESET_REASON reason) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool isFailureReset(const RESET_REASON reason) {
|
||||||
|
switch (reason) {
|
||||||
|
case POWERON_RESET:
|
||||||
|
case SW_RESET:
|
||||||
|
case DEEPSLEEP_RESET:
|
||||||
|
case SDIO_RESET:
|
||||||
|
case SW_CPU_RESET:
|
||||||
|
case EXT_CPU_RESET: return false;
|
||||||
|
default: return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void bootReasonLoad() {
|
void bootReasonLoad() {
|
||||||
const auto r0 = rtc_get_reset_reason(0);
|
const auto r0 = rtc_get_reset_reason(0);
|
||||||
const auto r1 = rtc_get_reset_reason(1);
|
const auto r1 = rtc_get_reset_reason(1);
|
||||||
failureReset = (r0 != POWERON_RESET && r0 != DEEPSLEEP_RESET) || (r1 != POWERON_RESET && r1 != DEEPSLEEP_RESET);
|
failureReset = isFailureReset(r0) || isFailureReset(r1);
|
||||||
if (failureReset) {
|
if (failureReset) {
|
||||||
warn("Forcing OTA delay because of failure-reset: r0=%s, r1=%s", getResetReason(r0), getResetReason(r1));
|
warn("Forcing OTA delay because of failure-reset: r0=%s, r1=%s", getResetReason(r0), getResetReason(r1));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void boot_waitForWifi() {
|
void boot_waitForWiFi() {
|
||||||
info("Waiting for WiFi...");
|
info("Waiting for WiFi...");
|
||||||
while (!isWiFiConnected()) {
|
while (!isWiFiConnected()) {
|
||||||
wifiLoop();
|
wifiLoop();
|
||||||
@ -77,7 +89,7 @@ void bootDelay() {
|
|||||||
|
|
||||||
info("Boot delay active...");
|
info("Boot delay active...");
|
||||||
|
|
||||||
boot_waitForWifi();
|
boot_waitForWiFi();
|
||||||
|
|
||||||
if (failureReset || patrixNode.waitForOTA) {
|
if (failureReset || patrixNode.waitForOTA) {
|
||||||
boot_waitForOTA();
|
boot_waitForOTA();
|
||||||
|
|||||||
@ -27,17 +27,16 @@ void clockLoop() {
|
|||||||
const auto now = time(nullptr);
|
const auto now = time(nullptr);
|
||||||
if (isCorrectTime(now)) {
|
if (isCorrectTime(now)) {
|
||||||
startupTime = now - clockOffset;
|
startupTime = now - clockOffset;
|
||||||
info("clock set after %ld seconds! So startup was at %s", clockOffset, getStartupStr());
|
info("Clock set after %ld seconds! So startup was at %s", clockOffset, getStartupStr());
|
||||||
} else {
|
} else {
|
||||||
clockOffset = now;
|
clockOffset = now;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isClockSet() {
|
bool isClockSet() {
|
||||||
return startupTime != 0;
|
return isCorrectTime(time(nullptr));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
char *getClockStr(const time_t epoch) {
|
char *getClockStr(const time_t epoch) {
|
||||||
auto now = epoch;
|
auto now = epoch;
|
||||||
if (now == 0) {
|
if (now == 0) {
|
||||||
|
|||||||
@ -7,13 +7,14 @@
|
|||||||
auto fsMounted = false;
|
auto fsMounted = false;
|
||||||
|
|
||||||
bool fsMount() {
|
bool fsMount() {
|
||||||
if (!fsMounted) {
|
|
||||||
fsMounted = LittleFS.begin(true);
|
|
||||||
}
|
|
||||||
if (fsMounted) {
|
if (fsMounted) {
|
||||||
info("filesystem mounted: %3d%% used (%d bytes)", static_cast<int>(round(100.0 * LittleFS.usedBytes() / LittleFS.totalBytes())), LittleFS.usedBytes());
|
return true;
|
||||||
|
}
|
||||||
|
fsMounted = LittleFS.begin(true);
|
||||||
|
if (fsMounted) {
|
||||||
|
info("Filesystem mounted: %3d%% used (%d bytes)", static_cast<int>(round(100.0 * LittleFS.usedBytes() / LittleFS.totalBytes())), LittleFS.usedBytes());
|
||||||
} else {
|
} else {
|
||||||
error("failed to mount filesystem");
|
error("Failed to mount filesystem");
|
||||||
}
|
}
|
||||||
return fsMounted;
|
return fsMounted;
|
||||||
}
|
}
|
||||||
@ -25,11 +26,11 @@ bool fsIsMounted() {
|
|||||||
void fsList(FS& fs, const char *path, const String& indent) { // NOLINT(*-no-recursion)
|
void fsList(FS& fs, const char *path, const String& indent) { // NOLINT(*-no-recursion)
|
||||||
auto dir = fs.open(path);
|
auto dir = fs.open(path);
|
||||||
if (!dir) {
|
if (!dir) {
|
||||||
error("not found: %s", path);
|
error("Not found: %s", path);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!dir.isDirectory()) {
|
if (!dir.isDirectory()) {
|
||||||
error("not a directory: %s", path);
|
error("Not a directory: %s", path);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,12 +1,18 @@
|
|||||||
#include <ESPAsyncWebServer.h>
|
#include <ESPAsyncWebServer.h>
|
||||||
|
#include <LittleFS.h>
|
||||||
#include <patrix/core/log.h>
|
#include <patrix/core/log.h>
|
||||||
#include <patrix/core/system.h>
|
#include <patrix/core/system.h>
|
||||||
#include <patrix/node/PatrixNode.h>
|
#include <patrix/node/PatrixNode.h>
|
||||||
|
|
||||||
|
#include "filesystem.h"
|
||||||
|
#include "wifi.h"
|
||||||
|
|
||||||
AsyncWebServer server(80);
|
AsyncWebServer server(80);
|
||||||
|
|
||||||
AsyncWebSocket ws("/ws");
|
AsyncWebSocket ws("/ws");
|
||||||
|
|
||||||
|
auto httpSetUp = false;
|
||||||
|
|
||||||
void httpReboot(AsyncWebServerRequest *request) {
|
void httpReboot(AsyncWebServerRequest *request) {
|
||||||
info("Rebooting...");
|
info("Rebooting...");
|
||||||
request->redirect("/");
|
request->redirect("/");
|
||||||
@ -18,12 +24,13 @@ void httpReboot(AsyncWebServerRequest *request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void httpSetup() {
|
void httpSetup() {
|
||||||
|
fsMount();
|
||||||
ws.onEvent([](AsyncWebSocket *socket, AsyncWebSocketClient *client, AwsEventType type, void *arg, unsigned char *message, unsigned length) {
|
ws.onEvent([](AsyncWebSocket *socket, AsyncWebSocketClient *client, AwsEventType type, void *arg, unsigned char *message, unsigned length) {
|
||||||
const char *t;
|
const char *t;
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case WS_EVT_CONNECT:
|
case WS_EVT_CONNECT:
|
||||||
t = "CONNECT";
|
t = "CONNECT";
|
||||||
patrixNode.websocketEvent(socket, client, type, arg, message, length);
|
patrixNode.websocketEvent(socket, client, type, arg, message, length);
|
||||||
break;
|
break;
|
||||||
case WS_EVT_DISCONNECT:
|
case WS_EVT_DISCONNECT:
|
||||||
t = "DISCONNECT";
|
t = "DISCONNECT";
|
||||||
@ -45,14 +52,29 @@ void httpSetup() {
|
|||||||
});
|
});
|
||||||
server.addHandler(&ws);
|
server.addHandler(&ws);
|
||||||
|
|
||||||
|
server.serveStatic("/", LittleFS, "/http/");
|
||||||
server.on("/reboot", HTTP_GET, httpReboot);
|
server.on("/reboot", HTTP_GET, httpReboot);
|
||||||
server.begin();
|
server.begin();
|
||||||
|
|
||||||
|
DefaultHeaders::Instance().addHeader("Access-Control-Allow-Origin", "*");
|
||||||
|
DefaultHeaders::Instance().addHeader("Access-Control-Allow-Methods", "GET, POST, PUT");
|
||||||
|
DefaultHeaders::Instance().addHeader("Access-Control-Allow-Headers", "Content-Type");
|
||||||
|
|
||||||
|
httpSetUp = true;
|
||||||
|
info("Webserver started.");
|
||||||
}
|
}
|
||||||
|
|
||||||
void httpLoop() {
|
void httpLoop() {
|
||||||
ws.cleanupClients();
|
if (!httpSetUp && isWiFiConnected()) {
|
||||||
|
httpSetup();
|
||||||
|
}
|
||||||
|
if (httpSetUp) {
|
||||||
|
ws.cleanupClients();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void websocketSendAll(const String& payload) {
|
void websocketSendAll(const String& payload) {
|
||||||
ws.textAll(payload);
|
if (httpSetUp) {
|
||||||
|
ws.textAll(payload);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,9 +1,9 @@
|
|||||||
#ifndef PATRIX_HTTP_H
|
#ifndef PATRIX_HTTP_H
|
||||||
#define PATRIX_HTTP_H
|
#define PATRIX_HTTP_H
|
||||||
|
|
||||||
#include <Arduino.h>
|
#include <ESPAsyncWebServer.h>
|
||||||
|
|
||||||
void httpSetup();
|
extern AsyncWebServer server;
|
||||||
|
|
||||||
void httpLoop();
|
void httpLoop();
|
||||||
|
|
||||||
|
|||||||
@ -3,6 +3,7 @@
|
|||||||
#include <patrix/core/log.h>
|
#include <patrix/core/log.h>
|
||||||
#include <patrix/core/mqtt.h>
|
#include <patrix/core/mqtt.h>
|
||||||
#include <patrix/core/wifi.h>
|
#include <patrix/core/wifi.h>
|
||||||
|
#include <patrix/node/PatrixNode.h>
|
||||||
|
|
||||||
#define MQTT_RETRY_DELAY_MILLIS 3000
|
#define MQTT_RETRY_DELAY_MILLIS 3000
|
||||||
|
|
||||||
@ -32,14 +33,25 @@ void mqttLoop() {
|
|||||||
mqtt.setKeepAlive(10);
|
mqtt.setKeepAlive(10);
|
||||||
mqtt.setBufferSize(512);
|
mqtt.setBufferSize(512);
|
||||||
mqttLastConnectMillis = millis();
|
mqttLastConnectMillis = millis();
|
||||||
info("mqtt connecting: host=%s, port=%d", mqttHost, mqttPort);
|
info("MQTT connecting: host=%s, port=%d", mqttHost, mqttPort);
|
||||||
if (mqtt.connect(WiFiClass::getHostname(), mqttWillTopic, 1, true, "false")) {
|
if (mqtt.connect(WiFiClass::getHostname(), mqttWillTopic, 1, true, "false")) {
|
||||||
info("mqtt connected");
|
info("MQTT connected");
|
||||||
yield();
|
yield();
|
||||||
mqtt.publish(mqttWillTopic, "true", true);
|
mqtt.publish(mqttWillTopic, "true", true);
|
||||||
|
mqtt.setCallback([](char *topic, uint8_t *payload, unsigned int length) {
|
||||||
|
char message[128];
|
||||||
|
if (length > sizeof message - 1) {
|
||||||
|
Serial.printf("MQTT: received too long message: topic=%s, length=%d", topic, length);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
memcpy(message, payload, length);
|
||||||
|
message[length] = 0;
|
||||||
|
patrixNode.mqttMessage(topic, message);
|
||||||
|
yield();
|
||||||
|
});
|
||||||
yield();
|
yield();
|
||||||
} else {
|
} else {
|
||||||
error("mqtt failed to connect");
|
error("MQTT failed to connect");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -47,3 +59,11 @@ void mqttLoop() {
|
|||||||
bool mqttPublish(const String& topic, const String& payload, const Retain retain) {
|
bool mqttPublish(const String& topic, const String& payload, const Retain retain) {
|
||||||
return mqtt.publish(topic.c_str(), payload.c_str(), retain == RETAIN);
|
return mqtt.publish(topic.c_str(), payload.c_str(), retain == RETAIN);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void mqttSubscribe(const String& topic) {
|
||||||
|
mqtt.subscribe(topic.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
void mqttUnsubscribe(const String& topic) {
|
||||||
|
mqtt.unsubscribe(topic.c_str());
|
||||||
|
}
|
||||||
|
|||||||
@ -11,4 +11,8 @@ void mqttLoop();
|
|||||||
|
|
||||||
bool mqttPublish(const String& topic, const String& payload, Retain retain);
|
bool mqttPublish(const String& topic, const String& payload, Retain retain);
|
||||||
|
|
||||||
|
void mqttSubscribe(const String& topic);
|
||||||
|
|
||||||
|
void mqttUnsubscribe(const String& topic);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@ -18,7 +18,7 @@ auto wifiHost = WIFI_HOST;
|
|||||||
|
|
||||||
void wifiSetupOTA() {
|
void wifiSetupOTA() {
|
||||||
ArduinoOTA.onStart([] {
|
ArduinoOTA.onStart([] {
|
||||||
info("beginning ota update...");
|
info("Beginning ota update...");
|
||||||
Serial.print("OTA-UPDATE: 0%");
|
Serial.print("OTA-UPDATE: 0%");
|
||||||
});
|
});
|
||||||
ArduinoOTA.onProgress([](const unsigned done, const unsigned total) {
|
ArduinoOTA.onProgress([](const unsigned done, const unsigned total) {
|
||||||
@ -54,7 +54,7 @@ void wifiSetupOTA() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void wifiOff() {
|
void wifiOff() {
|
||||||
info("wifi disabled");
|
info("WiFi disabled");
|
||||||
wifiEnabled = false;
|
wifiEnabled = false;
|
||||||
WiFi.disconnect();
|
WiFi.disconnect();
|
||||||
}
|
}
|
||||||
@ -64,10 +64,10 @@ void wifiLoop() {
|
|||||||
if (wifiConnected != currentState) {
|
if (wifiConnected != currentState) {
|
||||||
wifiConnected = currentState;
|
wifiConnected = currentState;
|
||||||
if (wifiConnected) {
|
if (wifiConnected) {
|
||||||
info("wifi connected as %s", WiFi.localIP().toString().c_str());
|
info("WiFi connected as %s", WiFi.localIP().toString().c_str());
|
||||||
wifiSetupOTA();
|
wifiSetupOTA();
|
||||||
} else {
|
} else {
|
||||||
warn("wifi disconnected");
|
warn("WiFi disconnected");
|
||||||
ArduinoOTA.end();
|
ArduinoOTA.end();
|
||||||
WiFi.disconnect();
|
WiFi.disconnect();
|
||||||
}
|
}
|
||||||
|
|||||||
314
src/patrix/display/Display.h
Normal file
314
src/patrix/display/Display.h
Normal file
@ -0,0 +1,314 @@
|
|||||||
|
#ifndef PATRIX_DISPLAY_height
|
||||||
|
#define PATRIX_DISPLAY_height
|
||||||
|
|
||||||
|
#include <Adafruit_NeoPixel.h>
|
||||||
|
|
||||||
|
#include "DisplayMatrix_FontCommon.h"
|
||||||
|
#include "DisplayMatrix_FontLower.h"
|
||||||
|
#include "DisplayMatrix_FontNumberSegments.h"
|
||||||
|
#include "DisplayMatrix_FontSpecial.h"
|
||||||
|
#include "DisplayMatrix_FontUpper.h"
|
||||||
|
|
||||||
|
#include <patrix/core/log.h>
|
||||||
|
|
||||||
|
inline void fixRect(int& x0, int& y0, int& w, int& h) {
|
||||||
|
if (w < 0) {
|
||||||
|
x0 += w;
|
||||||
|
w = -w;
|
||||||
|
}
|
||||||
|
if (h < 0) {
|
||||||
|
y0 += h;
|
||||||
|
h = -h;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum Align {
|
||||||
|
LEFT, CENTER, RIGHT
|
||||||
|
};
|
||||||
|
|
||||||
|
class Display {
|
||||||
|
|
||||||
|
Adafruit_NeoPixel leds;
|
||||||
|
|
||||||
|
bool dirty = true;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
|
||||||
|
virtual RGB _read_pixel_(int x, int y) = 0;
|
||||||
|
|
||||||
|
virtual void _write_pixel_(int x, int y, RGBA color) = 0;
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
const uint8_t width;
|
||||||
|
|
||||||
|
const uint8_t height;
|
||||||
|
|
||||||
|
const uint16_t pixelCount;
|
||||||
|
|
||||||
|
Display(const int16_t pin, const uint8_t width, const uint8_t height)
|
||||||
|
: leds(width * height, pin),
|
||||||
|
width(width),
|
||||||
|
height(height),
|
||||||
|
pixelCount(width * height) {
|
||||||
|
//
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual ~Display() = default;
|
||||||
|
|
||||||
|
// basic ----------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
void setup(const int brightness) {
|
||||||
|
leds.begin();
|
||||||
|
setBrightness(brightness);
|
||||||
|
clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void loop() {
|
||||||
|
if (dirty) {
|
||||||
|
for (auto y = 0; y < height; y++) {
|
||||||
|
for (auto x = 0; x < width; x++) {
|
||||||
|
const auto rgb = _read_pixel_(x, y);
|
||||||
|
const auto index = y * width + (y % 2 == 0 ? x : width - x - 1);
|
||||||
|
leds.setPixelColor(index, rgb.r, rgb.g, rgb.b);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
leds.show();
|
||||||
|
dirty = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t setBrightness(int brightness) {
|
||||||
|
brightness = max(0, min(brightness, 255));
|
||||||
|
if (leds.getBrightness() != brightness) {
|
||||||
|
leds.setBrightness(brightness);
|
||||||
|
dirty = true;
|
||||||
|
}
|
||||||
|
return brightness;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t getBrightness() const {
|
||||||
|
return leds.getBrightness();
|
||||||
|
}
|
||||||
|
|
||||||
|
// draw -----------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
void clear() {
|
||||||
|
fillRect(0, 0, width, height, Black);
|
||||||
|
}
|
||||||
|
|
||||||
|
void setPixel(const double x, const double y, const RGBA color) {
|
||||||
|
setPixel(static_cast<int>(round(x)), static_cast<int>(round(y)), color);
|
||||||
|
}
|
||||||
|
|
||||||
|
void setPixel(const int x, const int y, const RGBA color) {
|
||||||
|
if (x < 0 || x >= width || y < 0 || y >= height) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_write_pixel_(x, y, color);
|
||||||
|
dirty = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void fillRect(int x0, int y0, int w, int h, const RGBA color) {
|
||||||
|
fixRect(x0, y0, w, h);
|
||||||
|
for (auto y = y0; y < y0 + h; ++y) {
|
||||||
|
for (auto x = x0; x < x0 + w; ++x) {
|
||||||
|
setPixel(x, y, color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void drawLine(int x0, int y0, int w, int h, const int thickness, const RGBA color) {
|
||||||
|
fixRect(x0, y0, w, h);
|
||||||
|
for (auto t = 0; t < thickness; ++t) {
|
||||||
|
const auto offset = t % 2 == 0 ? t / 2 : -t / 2;
|
||||||
|
if (h == 0) {
|
||||||
|
for (auto x = x0; x <= x0 + w; ++x) {
|
||||||
|
setPixel(x, y0, color);
|
||||||
|
}
|
||||||
|
} else if (w == 0) {
|
||||||
|
for (auto y = y0; y <= y0 + h; ++y) {
|
||||||
|
setPixel(x0, y, color);
|
||||||
|
}
|
||||||
|
} else if (w >= h) {
|
||||||
|
const auto m = static_cast<double>(h) / w;
|
||||||
|
for (auto x = x0; x <= x0 + w; ++x) {
|
||||||
|
const auto y = static_cast<int>(round(offset + x * m));
|
||||||
|
setPixel(x, y, color);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const auto m = static_cast<double>(w) / h;
|
||||||
|
for (auto y = y0; y <= y0 + h; ++y) {
|
||||||
|
const auto x = static_cast<int>(round(offset + y * m));
|
||||||
|
setPixel(x, y, color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// print ----------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
void printf(const int x, const int y, const Align align, const RGBA color, const char *format, ...) {
|
||||||
|
va_list args;
|
||||||
|
va_start(args, format);
|
||||||
|
printf(x, y, align, color, format, args);
|
||||||
|
va_end(args);
|
||||||
|
}
|
||||||
|
|
||||||
|
void printf(const int x, const int y, const Align align, const RGBA color, const char *format, const va_list args) {
|
||||||
|
char buffer[64];
|
||||||
|
vsnprintf(buffer, sizeof buffer, format, args);
|
||||||
|
print(x, y, align, color, buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
void print(const int x0, const int y0, const Align align, const RGBA color, const char *message) {
|
||||||
|
auto x = x0;
|
||||||
|
auto y = y0;
|
||||||
|
doAlign(x, align, message);
|
||||||
|
print(x, y, color, message, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void doAlign(int& x, const Align align, const char *message) {
|
||||||
|
if (align != LEFT) {
|
||||||
|
auto w = 0;
|
||||||
|
auto h = 0;
|
||||||
|
measure(message, w, h);
|
||||||
|
if (align == RIGHT) {
|
||||||
|
x = x - w;
|
||||||
|
} else if (align == CENTER) {
|
||||||
|
x = x - w / 2;
|
||||||
|
}
|
||||||
|
} else {}
|
||||||
|
}
|
||||||
|
|
||||||
|
void measure(const char *message, int& w, int& h) {
|
||||||
|
w = 0;
|
||||||
|
h = 0;
|
||||||
|
print(w, h, White, message, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void print(int& x, int& y, const RGBA color, const char *message, const bool doDraw) {
|
||||||
|
auto character = message;
|
||||||
|
while (*character != '\0') {
|
||||||
|
print(x, x, y, color, &character, doDraw);
|
||||||
|
x++; // separate characters
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void print(const int x0, int& x, int& y, const RGBA color, const char **cPP, const bool doDraw) {
|
||||||
|
const auto character = *(*cPP)++;
|
||||||
|
|
||||||
|
if ('0' <= character && character <= '9') {
|
||||||
|
return print(x, y, reinterpret_cast<bool *>(FONT_NUMBER_SEGMENTS[character - '0']), countof(FONT_NUMBER_SEGMENTS[character - '0'][0]), countof(FONT_NUMBER_SEGMENTS[character - '0']), color, doDraw);
|
||||||
|
}
|
||||||
|
if ('A' <= character && character <= 'Z') {
|
||||||
|
return print(x, y, reinterpret_cast<bool *>(FONT_UPPER[character - 'A']), countof(FONT_UPPER[character - 'A'][0]), countof(FONT_UPPER[character - 'A']), color, doDraw);
|
||||||
|
}
|
||||||
|
if ('a' <= character && character <= 'z') {
|
||||||
|
return print(x, y, reinterpret_cast<bool *>(FONT_LOWER[character - 'a']), countof(FONT_LOWER[character - 'a'][0]), countof(FONT_LOWER[character - 'a']), color, doDraw);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (character == 0xC2) {
|
||||||
|
const auto secondCharacter = *(*cPP)++;
|
||||||
|
if (secondCharacter == 0xB0) {
|
||||||
|
return print(x, y, reinterpret_cast<bool *>(FONT_CHAR_DEGREE), countof(FONT_CHAR_DEGREE[0]), countof(FONT_CHAR_DEGREE), color, doDraw);
|
||||||
|
}
|
||||||
|
return printERROR(x, y, color, doDraw);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (character == 0xE2) {
|
||||||
|
const auto secondCharacter = *(*cPP)++;
|
||||||
|
if (secondCharacter == 0x82) {
|
||||||
|
const auto thirdCharacter = *(*cPP)++;
|
||||||
|
if (thirdCharacter == 0xAC) {
|
||||||
|
return print(x, y, reinterpret_cast<bool *>(FONT_CHAR_EURO), countof(FONT_CHAR_EURO[0]), countof(FONT_CHAR_EURO), color, doDraw);
|
||||||
|
}
|
||||||
|
return printERROR(x, y, color, doDraw);
|
||||||
|
}
|
||||||
|
return printERROR(x, y, color, doDraw);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (character == ' ') {
|
||||||
|
x += 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (character == '\n') {
|
||||||
|
x = x0;
|
||||||
|
y += 6;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (character) {
|
||||||
|
case '-': return print(x, y, reinterpret_cast<bool *>(FONT_CHAR_MINUS), countof(FONT_CHAR_MINUS[0]), countof(FONT_CHAR_MINUS), color, doDraw);
|
||||||
|
case '+': return print(x, y, reinterpret_cast<bool *>(FONT_CHAR_PLUS), countof(FONT_CHAR_PLUS[0]), countof(FONT_CHAR_PLUS), color, doDraw);
|
||||||
|
case '_': return print(x, y, reinterpret_cast<bool *>(FONT_CHAR_UNDERLINE), countof(FONT_CHAR_UNDERLINE[0]), countof(FONT_CHAR_UNDERLINE), color, doDraw);
|
||||||
|
case '%': return print(x, y, reinterpret_cast<bool *>(FONT_CHAR_PERCENT), countof(FONT_CHAR_PERCENT[0]), countof(FONT_CHAR_PERCENT), color, doDraw);
|
||||||
|
case '?': return print(x, y, reinterpret_cast<bool *>(FONT_CHAR_QUESTION), countof(FONT_CHAR_QUESTION[0]), countof(FONT_CHAR_QUESTION), color, doDraw);
|
||||||
|
case '!': return print(x, y, reinterpret_cast<bool *>(FONT_CHAR_EXCLAMATION), countof(FONT_CHAR_EXCLAMATION[0]), countof(FONT_CHAR_EXCLAMATION), color, doDraw);
|
||||||
|
case '.': return print(x, y, reinterpret_cast<bool *>(FONT_CHAR_POINT), countof(FONT_CHAR_POINT[0]), countof(FONT_CHAR_POINT), color, doDraw);
|
||||||
|
case ',': return print(x, y, reinterpret_cast<bool *>(FONT_CHAR_COMMA), countof(FONT_CHAR_COMMA[0]), countof(FONT_CHAR_COMMA), color, doDraw);
|
||||||
|
case ';': return print(x, y, reinterpret_cast<bool *>(FONT_CHAR_SEMICOLON), countof(FONT_CHAR_SEMICOLON[0]), countof(FONT_CHAR_SEMICOLON), color, doDraw);
|
||||||
|
case ':': return print(x, y, reinterpret_cast<bool *>(FONT_CHAR_COLON), countof(FONT_CHAR_COLON[0]), countof(FONT_CHAR_COLON), color, doDraw);
|
||||||
|
case '#': return print(x, y, reinterpret_cast<bool *>(FONT_CHAR_SHARP), countof(FONT_CHAR_SHARP[0]), countof(FONT_CHAR_SHARP), color, doDraw);
|
||||||
|
case '~': return print(x, y, reinterpret_cast<bool *>(FONT_CHAR_TILDE), countof(FONT_CHAR_TILDE[0]), countof(FONT_CHAR_TILDE), color, doDraw);
|
||||||
|
case '*': return print(x, y, reinterpret_cast<bool *>(FONT_CHAR_ASTERISK), countof(FONT_CHAR_ASTERISK[0]), countof(FONT_CHAR_ASTERISK), color, doDraw);
|
||||||
|
case '"': return print(x, y, reinterpret_cast<bool *>(FONT_CHAR_QUOTE_DOUBLE), countof(FONT_CHAR_QUOTE_DOUBLE[0]), countof(FONT_CHAR_QUOTE_DOUBLE), color, doDraw);
|
||||||
|
case '\'': return print(x, y, reinterpret_cast<bool *>(FONT_CHAR_QUOTE_SINGLE), countof(FONT_CHAR_QUOTE_SINGLE[0]), countof(FONT_CHAR_QUOTE_SINGLE), color, doDraw);
|
||||||
|
case '=': return print(x, y, reinterpret_cast<bool *>(FONT_CHAR_EQUALS), countof(FONT_CHAR_EQUALS[0]), countof(FONT_CHAR_EQUALS), color, doDraw);
|
||||||
|
case '(': return print(x, y, reinterpret_cast<bool *>(FONT_CHAR_PAR_L), countof(FONT_CHAR_PAR_L[0]), countof(FONT_CHAR_PAR_L), color, doDraw);
|
||||||
|
case ')': return print(x, y, reinterpret_cast<bool *>(FONT_CHAR_PAR_R), countof(FONT_CHAR_PAR_R[0]), countof(FONT_CHAR_PAR_R), color, doDraw);
|
||||||
|
case '[': return print(x, y, reinterpret_cast<bool *>(FONT_CHAR_BRACKET_L), countof(FONT_CHAR_BRACKET_L[0]), countof(FONT_CHAR_BRACKET_L), color, doDraw);
|
||||||
|
case ']': return print(x, y, reinterpret_cast<bool *>(FONT_CHAR_BRACKET_R), countof(FONT_CHAR_BRACKET_R[0]), countof(FONT_CHAR_BRACKET_R), color, doDraw);
|
||||||
|
case '{': return print(x, y, reinterpret_cast<bool *>(FONT_CHAR_CURL_L), countof(FONT_CHAR_CURL_L[0]), countof(FONT_CHAR_CURL_L), color, doDraw);
|
||||||
|
case '}': return print(x, y, reinterpret_cast<bool *>(FONT_CHAR_CURL_R), countof(FONT_CHAR_CURL_R[0]), countof(FONT_CHAR_CURL_R), color, doDraw);
|
||||||
|
case '/': return print(x, y, reinterpret_cast<bool *>(FONT_CHAR_SLASH), countof(FONT_CHAR_SLASH[0]), countof(FONT_CHAR_SLASH), color, doDraw);
|
||||||
|
case '\\': return print(x, y, reinterpret_cast<bool *>(FONT_CHAR_BACKSLASH), countof(FONT_CHAR_BACKSLASH[0]), countof(FONT_CHAR_BACKSLASH), color, doDraw);
|
||||||
|
case '&': return print(x, y, reinterpret_cast<bool *>(FONT_CHAR_AND), countof(FONT_CHAR_AND[0]), countof(FONT_CHAR_AND), color, doDraw);
|
||||||
|
case '|': return print(x, y, reinterpret_cast<bool *>(FONT_CHAR_PIPE), countof(FONT_CHAR_PIPE[0]), countof(FONT_CHAR_PIPE), color, doDraw);
|
||||||
|
case '$': return print(x, y, reinterpret_cast<bool *>(FONT_CHAR_DOLLAR), countof(FONT_CHAR_DOLLAR[0]), countof(FONT_CHAR_DOLLAR), color, doDraw);
|
||||||
|
case '@': return print(x, y, reinterpret_cast<bool *>(FONT_CHAR_AT), countof(FONT_CHAR_AT[0]), countof(FONT_CHAR_AT), color, doDraw);
|
||||||
|
case '<': return print(x, y, reinterpret_cast<bool *>(FONT_CHAR_LT), countof(FONT_CHAR_LT[0]), countof(FONT_CHAR_LT), color, doDraw);
|
||||||
|
case '>': return print(x, y, reinterpret_cast<bool *>(FONT_CHAR_GT), countof(FONT_CHAR_GT[0]), countof(FONT_CHAR_GT), color, doDraw);
|
||||||
|
default: return printERROR(x, y, color, doDraw);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void print(int& x0, const int& y0, const bool *s, const int w, const int h, const RGBA color, const bool doDraw) {
|
||||||
|
auto maxWidth = 0;
|
||||||
|
for (auto y = 0; y < h; y++) {
|
||||||
|
for (auto x = 0; x < w; x++) {
|
||||||
|
const auto symbolIndex = y * w + x;
|
||||||
|
const auto active = *(s + symbolIndex);
|
||||||
|
if (active) {
|
||||||
|
maxWidth = max(x + 1, maxWidth);
|
||||||
|
const auto pixelX = x0 + x;
|
||||||
|
const auto pixelY = y0 + y;
|
||||||
|
if (doDraw) {
|
||||||
|
setPixel(pixelX, pixelY, color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
x0 += maxWidth;
|
||||||
|
}
|
||||||
|
|
||||||
|
void print(int& x0, const int& y0, const RGBA *s, const int w, const int h) {
|
||||||
|
for (auto y = 0; y < h; y++) {
|
||||||
|
for (auto x = 0; x < w; x++) {
|
||||||
|
const auto symbolIndex = y * w + x;
|
||||||
|
const auto color = *(s + symbolIndex);
|
||||||
|
const auto pixelX = x0 + x;
|
||||||
|
const auto pixelY = y0 + y;
|
||||||
|
setPixel(pixelX, pixelY, color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
x0 += w;
|
||||||
|
}
|
||||||
|
|
||||||
|
void printERROR(int& x, const int& y, const RGBA color, const bool doDraw) {
|
||||||
|
return print(x, y, reinterpret_cast<bool *>(FONT_ERROR_), countof(FONT_ERROR_[0]), countof(FONT_ERROR_), color, doDraw);
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
@ -1,103 +1,32 @@
|
|||||||
#ifndef DISPLAY_MATRIX_H
|
#ifndef DISPLAY_MATRIX_H
|
||||||
#define DISPLAY_MATRIX_H
|
#define DISPLAY_MATRIX_H
|
||||||
|
|
||||||
#include <Adafruit_NeoPixel.h>
|
#include "Display.h"
|
||||||
|
|
||||||
#include "DisplayMatrix_FontCommon.h"
|
|
||||||
|
|
||||||
template<int W, int H>
|
template<int W, int H>
|
||||||
class DisplayMatrix {
|
class DisplayMatrix final : public Display {
|
||||||
|
|
||||||
Adafruit_NeoPixel leds;
|
|
||||||
|
|
||||||
RGB matrix[H][W] = {};
|
RGB matrix[H][W] = {};
|
||||||
|
|
||||||
bool dirty = true;
|
protected:
|
||||||
|
|
||||||
|
RGB _read_pixel_(int x, int y) override {
|
||||||
|
return matrix[y][x];
|
||||||
|
}
|
||||||
|
|
||||||
|
void _write_pixel_(const int x, const int y, const RGBA color) override {
|
||||||
|
matrix[y][x] = color;
|
||||||
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
RGBA foreground = White;
|
explicit DisplayMatrix(const int16_t pin)
|
||||||
|
: Display(pin, W, H) {
|
||||||
RGBA background = Transparent;
|
|
||||||
|
|
||||||
uint8_t brightness = 10;
|
|
||||||
|
|
||||||
uint8_t alpha = 255;
|
|
||||||
|
|
||||||
int cursorX = 0;
|
|
||||||
|
|
||||||
int cursorY = 0;
|
|
||||||
|
|
||||||
// basic ----------------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
explicit DisplayMatrix(const int pin): leds(W * H, pin) {
|
|
||||||
//
|
//
|
||||||
}
|
}
|
||||||
|
|
||||||
void setup() {
|
~DisplayMatrix() override = default;
|
||||||
leds.begin();
|
|
||||||
leds.setBrightness(brightness);
|
|
||||||
clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
void loop() {
|
|
||||||
if (dirty) {
|
|
||||||
for (auto y = 0; y < H; y++) {
|
|
||||||
for (auto x = 0; x < W; x++) {
|
|
||||||
auto rgb = matrix[y][x];
|
|
||||||
leds.setPixelColor(y * W + x, rgb.r, rgb.g, rgb.b);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
leds.show();
|
|
||||||
dirty = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void setBrightness(const uint8_t brightness) {
|
|
||||||
if (leds.getBrightness() != brightness) {
|
|
||||||
leds.setBrightness(brightness);
|
|
||||||
dirty = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// draw -----------------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
void clear();
|
|
||||||
|
|
||||||
void fillRect(int w = W, int h = H);
|
|
||||||
|
|
||||||
void drawLine(int w, int h, int thickness = 1);
|
|
||||||
|
|
||||||
// print ----------------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
void printf(const char *format, ...);
|
|
||||||
|
|
||||||
void print(const char *str);
|
|
||||||
|
|
||||||
void print(const char **cPP);
|
|
||||||
|
|
||||||
void print(Symbol1 s);
|
|
||||||
|
|
||||||
void print(Symbol2 s);
|
|
||||||
|
|
||||||
void print(Symbol3 s);
|
|
||||||
|
|
||||||
void print(Symbol4 s);
|
|
||||||
|
|
||||||
void print(Symbol5 s);
|
|
||||||
|
|
||||||
void print(bool **s, size_t w, size_t h);
|
|
||||||
|
|
||||||
void print(SymbolRGBA8x8 s);
|
|
||||||
|
|
||||||
void print(RGBA **s, size_t w, size_t h);
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// ReSharper disable once CppUnusedIncludeDirective
|
|
||||||
#include <patrix/display/DisplayMatrix_draw.h>
|
|
||||||
|
|
||||||
// ReSharper disable once CppUnusedIncludeDirective
|
|
||||||
#include <patrix/display/DisplayMatrix_print.h>
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@ -83,7 +83,7 @@ Symbol5 FONT_LOWER[] = {
|
|||||||
{X,_,_,_,_},
|
{X,_,_,_,_},
|
||||||
{X,_,_,_,_},
|
{X,_,_,_,_},
|
||||||
{X,_,_,_,_},
|
{X,_,_,_,_},
|
||||||
{_,X,_,_,_},
|
{X,X,_,_,_},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
{_,_,_,_,_},
|
{_,_,_,_,_},
|
||||||
|
|||||||
@ -1,74 +0,0 @@
|
|||||||
#include "DisplayMatrix_FontNumber.h"
|
|
||||||
|
|
||||||
Symbol4 FONT_NUMBER[] = {
|
|
||||||
{
|
|
||||||
{_,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,_},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
@ -1,8 +0,0 @@
|
|||||||
#ifndef DISPLAY_MATRIX_FONT_NUMBER_H
|
|
||||||
#define DISPLAY_MATRIX_FONT_NUMBER_H
|
|
||||||
|
|
||||||
#include "DisplayMatrix_FontCommon.h"
|
|
||||||
|
|
||||||
extern Symbol4 FONT_NUMBER[];
|
|
||||||
|
|
||||||
#endif
|
|
||||||
74
src/patrix/display/DisplayMatrix_FontNumberSegments.cpp
Normal file
74
src/patrix/display/DisplayMatrix_FontNumberSegments.cpp
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
#include "DisplayMatrix_FontCommon.h"
|
||||||
|
|
||||||
|
Symbol4 FONT_NUMBER_SEGMENTS[] = {
|
||||||
|
{
|
||||||
|
{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},
|
||||||
|
},
|
||||||
|
};
|
||||||
8
src/patrix/display/DisplayMatrix_FontNumberSegments.h
Normal file
8
src/patrix/display/DisplayMatrix_FontNumberSegments.h
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
#ifndef DISPLAY_MATRIX_FONT_NUMBER_SEGMENTS_H
|
||||||
|
#define DISPLAY_MATRIX_FONT_NUMBER_SEGMENTS_H
|
||||||
|
|
||||||
|
#include "DisplayMatrix_FontCommon.h"
|
||||||
|
|
||||||
|
extern Symbol4 FONT_NUMBER_SEGMENTS[];
|
||||||
|
|
||||||
|
#endif
|
||||||
@ -1,13 +1,5 @@
|
|||||||
#include "DisplayMatrix_FontSpecial.h"
|
#include "DisplayMatrix_FontSpecial.h"
|
||||||
|
|
||||||
Symbol3 FONT_CHAR_SPACE = {
|
|
||||||
{_,_,_},
|
|
||||||
{_,_,_},
|
|
||||||
{_,_,_},
|
|
||||||
{_,_,_},
|
|
||||||
{_,_,_},
|
|
||||||
};
|
|
||||||
|
|
||||||
Symbol3 FONT_CHAR_MINUS = {
|
Symbol3 FONT_CHAR_MINUS = {
|
||||||
{_,_,_},
|
{_,_,_},
|
||||||
{_,_,_},
|
{_,_,_},
|
||||||
@ -237,11 +229,11 @@ Symbol4 FONT_ERROR_ = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
Symbol5 FONT_CHAR_PERCENT = {
|
Symbol5 FONT_CHAR_PERCENT = {
|
||||||
{X,_,_,_,X},
|
{X,_,_},
|
||||||
{_,_,_,X,_},
|
{_,_,X},
|
||||||
{_,_,X,_,_},
|
{_,X,_},
|
||||||
{_,X,_,_,_},
|
{X,_,_},
|
||||||
{X,_,_,_,X},
|
{_,_,X},
|
||||||
};
|
};
|
||||||
|
|
||||||
//@formatter:off
|
//@formatter:off
|
||||||
|
|||||||
@ -3,7 +3,6 @@
|
|||||||
|
|
||||||
#include "DisplayMatrix_FontCommon.h"
|
#include "DisplayMatrix_FontCommon.h"
|
||||||
|
|
||||||
extern Symbol3 FONT_CHAR_SPACE;
|
|
||||||
extern Symbol3 FONT_CHAR_MINUS;
|
extern Symbol3 FONT_CHAR_MINUS;
|
||||||
extern Symbol3 FONT_CHAR_PLUS;
|
extern Symbol3 FONT_CHAR_PLUS;
|
||||||
extern Symbol3 FONT_CHAR_UNDERLINE;
|
extern Symbol3 FONT_CHAR_UNDERLINE;
|
||||||
|
|||||||
@ -2,11 +2,11 @@
|
|||||||
|
|
||||||
Symbol5 FONT_UPPER[] = {
|
Symbol5 FONT_UPPER[] = {
|
||||||
{
|
{
|
||||||
{_,_,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,_,_},
|
||||||
@ -44,11 +44,11 @@ Symbol5 FONT_UPPER[] = {
|
|||||||
{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,_},
|
||||||
@ -100,11 +100,11 @@ Symbol5 FONT_UPPER[] = {
|
|||||||
{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,_,_},
|
||||||
@ -114,11 +114,11 @@ Symbol5 FONT_UPPER[] = {
|
|||||||
{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,_,_},
|
||||||
@ -135,23 +135,23 @@ Symbol5 FONT_UPPER[] = {
|
|||||||
{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,_,_},
|
||||||
},
|
},
|
||||||
|
|||||||
@ -1,14 +1,18 @@
|
|||||||
#include "DisplayMatrix_common.h"
|
#include "DisplayMatrix_common.h"
|
||||||
|
|
||||||
//@formatter:off
|
// ReSharper disable CppVariableCanBeMadeConstexpr
|
||||||
|
// @formatter:off
|
||||||
const RGBA Transparent { 0, 0, 0, 0};
|
const RGBA Transparent { 0, 0, 0, 0};
|
||||||
const RGBA Black { 0, 0, 0, 255};
|
const RGBA Black { 0, 0, 0, 255};
|
||||||
const RGBA Red {255, 0, 0, 255};
|
const RGBA Red {255, 0, 0, 255};
|
||||||
const RGBA Yellow {255, 255, 0, 255};
|
const RGBA Yellow {255, 255, 0, 255};
|
||||||
|
const RGBA Orange {255, 127, 0, 255};
|
||||||
const RGBA Green { 0, 255, 0, 255};
|
const RGBA Green { 0, 255, 0, 255};
|
||||||
const RGBA Cyan { 0, 255, 255, 255};
|
const RGBA Cyan { 0, 255, 255, 255};
|
||||||
const RGBA Blue { 0, 0, 255, 255};
|
const RGBA Blue { 0, 0, 255, 255};
|
||||||
const RGBA Magenta {255, 0, 255, 255};
|
const RGBA Magenta {255, 0, 255, 255};
|
||||||
|
const RGBA Violet {192, 0, 255, 255};
|
||||||
const RGBA Gray {127, 127, 127, 255};
|
const RGBA Gray {127, 127, 127, 255};
|
||||||
const RGBA White {255, 255, 255, 255};
|
const RGBA White {255, 255, 255, 255};
|
||||||
//@formatter:on
|
// @formatter:on
|
||||||
|
// ReSharper restore CppVariableCanBeMadeConstexpr
|
||||||
|
|||||||
@ -13,6 +13,38 @@ struct RGBA {
|
|||||||
uint8_t g;
|
uint8_t g;
|
||||||
uint8_t b;
|
uint8_t b;
|
||||||
uint8_t a;
|
uint8_t a;
|
||||||
|
|
||||||
|
static RGBA gray(const uint8_t brightness) {
|
||||||
|
return gray(brightness, 255);
|
||||||
|
}
|
||||||
|
|
||||||
|
static RGBA gray(const uint8_t brightness, const uint8_t alpha) {
|
||||||
|
return {brightness, brightness, brightness, alpha};
|
||||||
|
}
|
||||||
|
|
||||||
|
static RGBA rnd() {
|
||||||
|
return rnd(255);
|
||||||
|
}
|
||||||
|
|
||||||
|
static RGBA rnd(const uint8_t alpha) {
|
||||||
|
return {static_cast<uint8_t>(random(255)), static_cast<uint8_t>(random(255)), static_cast<uint8_t>(random(255)), alpha};
|
||||||
|
}
|
||||||
|
|
||||||
|
RGBA factor(const double factor) const {
|
||||||
|
return {
|
||||||
|
limit(r * factor),
|
||||||
|
limit(g * factor),
|
||||||
|
limit(b * factor),
|
||||||
|
a
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
|
||||||
|
static uint8_t limit(const double newValue) {
|
||||||
|
return static_cast<uint8_t>(round(max(0.0, min(255.0, newValue))));
|
||||||
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct RGB {
|
struct RGB {
|
||||||
@ -20,18 +52,26 @@ struct RGB {
|
|||||||
uint8_t g;
|
uint8_t g;
|
||||||
uint8_t b;
|
uint8_t b;
|
||||||
|
|
||||||
void operator =(const RGBA& color) {
|
RGB &operator =(const RGBA& color) {
|
||||||
blend(&this->r, color.r, color.a);
|
r = color.r;
|
||||||
blend(&this->g, color.g, color.a);
|
g = color.g;
|
||||||
blend(&this->b, color.b, color.a);
|
b = color.b;
|
||||||
|
// blend(&this->r, color.r, color.a);
|
||||||
|
// blend(&this->g, color.g, color.a);
|
||||||
|
// blend(&this->b, color.b, color.a);
|
||||||
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
protected:
|
||||||
|
|
||||||
|
static uint8_t limit(const double newValue) {
|
||||||
|
return static_cast<uint8_t>(round(max(0.0, min(255.0, newValue))));
|
||||||
|
}
|
||||||
|
|
||||||
static void blend(uint8_t *target, const uint8_t color, const uint8_t alpha) {
|
static void blend(uint8_t *target, const uint8_t color, const uint8_t alpha) {
|
||||||
const auto factor = alpha / 255.0;
|
const auto factor = alpha / 255.0;
|
||||||
const auto newValue = (*target * (1 - factor) + color * factor) / 2.0;
|
const auto newValue = (*target * (1 - factor) + color * factor) / 2.0;
|
||||||
*target = static_cast<uint8_t>(round(max(0.0, min(255.0, newValue))));
|
*target = limit(newValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
@ -40,10 +80,12 @@ extern const RGBA Transparent;
|
|||||||
extern const RGBA Black;
|
extern const RGBA Black;
|
||||||
extern const RGBA Red;
|
extern const RGBA Red;
|
||||||
extern const RGBA Yellow;
|
extern const RGBA Yellow;
|
||||||
|
extern const RGBA Orange;
|
||||||
extern const RGBA Green;
|
extern const RGBA Green;
|
||||||
extern const RGBA Cyan;
|
extern const RGBA Cyan;
|
||||||
extern const RGBA Blue;
|
extern const RGBA Blue;
|
||||||
extern const RGBA Magenta;
|
extern const RGBA Magenta;
|
||||||
|
extern const RGBA Violet;
|
||||||
extern const RGBA Gray;
|
extern const RGBA Gray;
|
||||||
extern const RGBA White;
|
extern const RGBA White;
|
||||||
|
|
||||||
|
|||||||
@ -1,52 +0,0 @@
|
|||||||
#ifndef DISPLAY_MATRIX_DRAW_H
|
|
||||||
#define DISPLAY_MATRIX_DRAW_H
|
|
||||||
|
|
||||||
template<int W, int H>
|
|
||||||
void DisplayMatrix<W, H>::clear() {
|
|
||||||
const auto backup = foreground;
|
|
||||||
foreground = Black;
|
|
||||||
fillRect();
|
|
||||||
foreground = backup;
|
|
||||||
}
|
|
||||||
|
|
||||||
template<int W, int H>
|
|
||||||
void DisplayMatrix<W, H>::fillRect(const int w, const int h) {
|
|
||||||
if (w < 1 || h < 1) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
for (auto y = cursorY; y < cursorY + h; ++y) {
|
|
||||||
for (auto x = cursorX; x < cursorX + w; ++x) {
|
|
||||||
matrix[y][x] = foreground;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
dirty = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
template<int W, int H>
|
|
||||||
void DisplayMatrix<W, H>::drawLine(const int w, const int h, const int thickness) {
|
|
||||||
if (w < 1 && h < 1) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (w >= h) {
|
|
||||||
const auto m = static_cast<double>(h) / w;
|
|
||||||
for (auto t = 0; t < thickness; ++t) {
|
|
||||||
const auto offset = t % 2 == 0 ? t / 2 : -t / 2;
|
|
||||||
for (auto x = 0; x < w; ++x) {
|
|
||||||
const auto y = static_cast<int>(round(offset + x * m));
|
|
||||||
matrix[cursorY + y][cursorX + x] = foreground;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
const auto m = static_cast<double>(w) / h;
|
|
||||||
for (auto t = 0; t < thickness; ++t) {
|
|
||||||
const auto offset = t % 2 == 0 ? t / 2 : -t / 2;
|
|
||||||
for (auto y = 0; y < w; ++y) {
|
|
||||||
const auto x = static_cast<int>(round(offset + y * m));
|
|
||||||
matrix[cursorY + y][cursorX + x] = foreground;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
dirty = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@ -1,154 +0,0 @@
|
|||||||
#ifndef DISPLAYMATRIX_PRINT_H
|
|
||||||
#define DISPLAYMATRIX_PRINT_H
|
|
||||||
|
|
||||||
#include "DisplayMatrix_FontLower.h"
|
|
||||||
#include "DisplayMatrix_FontNumber.h"
|
|
||||||
#include "DisplayMatrix_FontSpecial.h"
|
|
||||||
#include "DisplayMatrix_FontUpper.h"
|
|
||||||
|
|
||||||
template<int W, int H>
|
|
||||||
void DisplayMatrix<W, H>::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);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<int W, int H>
|
|
||||||
void DisplayMatrix<W, H>::print(const char *str) {
|
|
||||||
while (*str != '\0') {
|
|
||||||
print(&str);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
template<int W, int H>
|
|
||||||
void DisplayMatrix<W, H>::print(const char **cPP) {
|
|
||||||
const auto a = *(*cPP)++;
|
|
||||||
auto b = '\0';
|
|
||||||
auto c = '\0';
|
|
||||||
if ('a' <= a && a <= 'z') {
|
|
||||||
return print(FONT_LOWER[a - 'a']);
|
|
||||||
}
|
|
||||||
if ('A' <= a && a <= 'Z') {
|
|
||||||
return print(FONT_UPPER[a - 'A']);
|
|
||||||
}
|
|
||||||
if ('0' <= a && a <= '9') {
|
|
||||||
return print(FONT_NUMBER[a - '0']);
|
|
||||||
}
|
|
||||||
if (a == 0xC2) {
|
|
||||||
b = *(*cPP)++;
|
|
||||||
switch (b) {
|
|
||||||
case 0xB0: return print(FONT_CHAR_DEGREE);
|
|
||||||
default: return print(FONT_ERROR_);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (a == 0xE2) {
|
|
||||||
b = *(*cPP)++;
|
|
||||||
switch (b) {
|
|
||||||
case 0x82:
|
|
||||||
c = *(*cPP)++;
|
|
||||||
switch (c) {
|
|
||||||
case 0xAC: return print(FONT_CHAR_EURO);
|
|
||||||
default: return print(FONT_ERROR_);
|
|
||||||
}
|
|
||||||
default: return print(FONT_ERROR_);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
switch (a) {
|
|
||||||
case ' ': return print(FONT_CHAR_SPACE);
|
|
||||||
case '-': return print(FONT_CHAR_MINUS);
|
|
||||||
case '+': return print(FONT_CHAR_PLUS);
|
|
||||||
case '_': return print(FONT_CHAR_UNDERLINE);
|
|
||||||
case '%': return print(FONT_CHAR_PERCENT);
|
|
||||||
case '?': return print(FONT_CHAR_QUESTION);
|
|
||||||
case '!': return print(FONT_CHAR_EXCLAMATION);
|
|
||||||
case '.': return print(FONT_CHAR_POINT);
|
|
||||||
case ',': return print(FONT_CHAR_COMMA);
|
|
||||||
case ';': return print(FONT_CHAR_SEMICOLON);
|
|
||||||
case ':': return print(FONT_CHAR_COLON);
|
|
||||||
case '#': return print(FONT_CHAR_SHARP);
|
|
||||||
case '~': return print(FONT_CHAR_TILDE);
|
|
||||||
case '*': return print(FONT_CHAR_ASTERISK);
|
|
||||||
case '"': return print(FONT_CHAR_QUOTE_DOUBLE);
|
|
||||||
case '\'': return print(FONT_CHAR_QUOTE_SINGLE);
|
|
||||||
case '=': return print(FONT_CHAR_EQUALS);
|
|
||||||
case '(': return print(FONT_CHAR_PAR_L);
|
|
||||||
case ')': return print(FONT_CHAR_PAR_R);
|
|
||||||
case '[': return print(FONT_CHAR_BRACKET_L);
|
|
||||||
case ']': return print(FONT_CHAR_BRACKET_R);
|
|
||||||
case '{': return print(FONT_CHAR_CURL_L);
|
|
||||||
case '}': return print(FONT_CHAR_CURL_R);
|
|
||||||
case '/': return print(FONT_CHAR_SLASH);
|
|
||||||
case '\\': return print(FONT_CHAR_BACKSLASH);
|
|
||||||
case '&': return print(FONT_CHAR_AND);
|
|
||||||
case '|': return print(FONT_CHAR_PIPE);
|
|
||||||
case '$': return print(FONT_CHAR_DOLLAR);
|
|
||||||
case '@': return print(FONT_CHAR_AT);
|
|
||||||
case '<': return print(FONT_CHAR_LT);
|
|
||||||
case '>': return print(FONT_CHAR_GT);
|
|
||||||
default: return print(FONT_ERROR_);
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
template<int W, int H>
|
|
||||||
void DisplayMatrix<W, H>::print(Symbol1 s) {
|
|
||||||
print(reinterpret_cast<bool **>(s),countof(Symbol1),countof(Symbol1[0]));
|
|
||||||
}
|
|
||||||
|
|
||||||
template<int W, int H>
|
|
||||||
void DisplayMatrix<W, H>::print(Symbol2 s) {
|
|
||||||
print(reinterpret_cast<bool **>(s),countof(Symbol2),countof(Symbol2[0]));
|
|
||||||
}
|
|
||||||
|
|
||||||
template<int W, int H>
|
|
||||||
void DisplayMatrix<W, H>::print(Symbol3 s) {
|
|
||||||
print(reinterpret_cast<bool **>(s),countof(Symbol3),countof(Symbol3[0]));
|
|
||||||
}
|
|
||||||
|
|
||||||
template<int W, int H>
|
|
||||||
void DisplayMatrix<W, H>::print(Symbol4 s) {
|
|
||||||
print(reinterpret_cast<bool **>(s),countof(Symbol4),countof(Symbol4[0]));
|
|
||||||
}
|
|
||||||
|
|
||||||
template<int W, int H>
|
|
||||||
void DisplayMatrix<W, H>::print(Symbol5 s) {
|
|
||||||
print(reinterpret_cast<bool **>(s),countof(Symbol5),countof(Symbol5[0]));
|
|
||||||
}
|
|
||||||
|
|
||||||
template<int W, int H>
|
|
||||||
void DisplayMatrix<W, H>::print(bool **s, const size_t w, const size_t h) {
|
|
||||||
auto xMax = 0;
|
|
||||||
for (auto y = 0; y < h; y++) {
|
|
||||||
for (auto x = 0; x < w; x++) {
|
|
||||||
const auto c = s[y][x];
|
|
||||||
if (c) {
|
|
||||||
matrix[y][x] = foreground;
|
|
||||||
xMax = x > xMax ? x : xMax;
|
|
||||||
} else {
|
|
||||||
matrix[y][x] = background;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
cursorX += xMax;
|
|
||||||
}
|
|
||||||
|
|
||||||
template<int W, int H>
|
|
||||||
void DisplayMatrix<W, H>::print(SymbolRGBA8x8 s) {
|
|
||||||
print(reinterpret_cast<RGBA **>(s),countof(SymbolRGBA8x8),countof(SymbolRGBA8x8[0]));
|
|
||||||
}
|
|
||||||
|
|
||||||
template<int W, int H>
|
|
||||||
void DisplayMatrix<W, H>::print(RGBA **s, const size_t w, const size_t h) {
|
|
||||||
for (auto y = 0; y < h; y++) {
|
|
||||||
for (auto x = 0; x < w; x++) {
|
|
||||||
const auto color = s[y][x];
|
|
||||||
matrix[y][x] = color;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
cursorX += w;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@ -1,4 +1,4 @@
|
|||||||
#include <patrix/node/PatrixNode.h>
|
#include <patrix/node/PatrixNode.h>
|
||||||
|
|
||||||
// ReSharper disable once CppUseAuto
|
// ReSharper disable once CppUseAuto
|
||||||
PatrixNode patrixNode = patrixGetNode();
|
PatrixNode &patrixNode = patrixGetNode();
|
||||||
|
|||||||
@ -22,16 +22,18 @@ public:
|
|||||||
|
|
||||||
virtual ~PatrixNode() = default;
|
virtual ~PatrixNode() = default;
|
||||||
|
|
||||||
virtual void setup() {}
|
virtual void setup() = 0;
|
||||||
|
|
||||||
virtual void loop() {}
|
virtual void loop() = 0;
|
||||||
|
|
||||||
|
virtual void mqttMessage(char *topic, char *message) {}
|
||||||
|
|
||||||
virtual void websocketEvent(AsyncWebSocket *socket, AsyncWebSocketClient *client, AwsEventType type, void *arg, unsigned char *message, unsigned length) {}
|
virtual void websocketEvent(AsyncWebSocket *socket, AsyncWebSocketClient *client, AwsEventType type, void *arg, unsigned char *message, unsigned length) {}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
extern PatrixNode patrixNode;
|
extern PatrixNode& patrixNode;
|
||||||
|
|
||||||
PatrixNode& patrixGetNode();
|
PatrixNode &patrixGetNode();
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user