diff --git a/docs/api.md b/docs/api.md index 2878d27a..4b30b4a9 100644 --- a/docs/api.md +++ b/docs/api.md @@ -289,6 +289,11 @@ Each property is optional; you do not need to send all. | `HUM_COL` | string / array of ints| Sets the textcolor of the humidity app. Set 0 for global textcolor | an array of RGB values hexadecimal color value | N/A | | `BAT_COL` | string / array of ints| Sets the textcolor of the battery app. Set 0 for global textcolor | an array of RGB values hexadecimal color value | N/A | | `SSPEED` | integer | Modifies the scrollspeed | percentage value of the original scrollspeed | 100 | +| `TIM` | boolean | Enable or disable the native time app (needs a reboot) | true/false | true | +| `DAT` | boolean | Enable or disable the native date app (needs a reboot) | true/false | true | +| `HUM` | boolean | Enable or disable the native humidity app (needs a reboot) | true/false | true | +| `TEMP` | boolean | Enable or disable the native temperature app (needs a reboot) | true/false | true | +| `BAT` | boolean | Enable or disable the native battery app (needs a reboot) | true/false | true | **Transision effects:** ```bash @@ -342,3 +347,12 @@ You can start the firmware update with update button in HA or: | Topic | URL | Payload/Body | HTTP method | | --- | --- | --- | --- | | `[PREFIX]/reboot` | `http://[IP]/api/reboot` | - | POST | + + +## Erase Awtrix +WARNING: this will format the flashmemory and EEPROM but dont touches the Wifi Settings. +This is kind of a factoryreset! + +| Topic | URL | Payload/Body | HTTP method | +| --- | --- | --- | --- | +| `N/A` | `http://[IP]/api/erase` | - | POST | \ No newline at end of file diff --git a/docs/dev.md b/docs/dev.md index d60383b4..95edaefe 100644 --- a/docs/dev.md +++ b/docs/dev.md @@ -28,4 +28,6 @@ The JSON object has the following properties: | `min_brightness` | integer | Sets minimum brightness level for the Autobrightness control | `2` | | `max_brightness` | integer | Sets maximum brightness level for the Autobrightness control. On high levels, this could result in overheating! | `180` | | `ha_prefix` | string | Sets the prefix for Homassistant discovery | `homeassistant` | -| `background_effect` | string | Sets an [effect](https://blueforcer.github.io/awtrix-light/#/effects) as global background layer | | +| `background_effect` | string | Sets an [effect](https://blueforcer.github.io/awtrix-light/#/effects) as global background layer | - | +| `stats_interval` | integer | Sets the interval in milliseconds when awtrix should send its stats to HA and MQTT | 10000 | +| `debug_mode` | boolean | Enables serial debug outputs. | false | \ No newline at end of file diff --git a/src/Apps.h b/src/Apps.h index f2b5c06c..f78ed213 100644 --- a/src/Apps.h +++ b/src/Apps.h @@ -147,7 +147,14 @@ const char *getTimeFormat() } else { - return TIME_FORMAT[2] == ' ' ? "%H %M" : "%H:%M"; + if (TIME_FORMAT.length() > 5) + { + return TIME_FORMAT[2] == ' ' ? "%H %M" : "%H:%M"; + } + else + { + return TIME_FORMAT.c_str(); + } } } @@ -452,6 +459,8 @@ void ShowCustomApp(String name, FastLED_NeoMatrix *matrix, MatrixDisplayUiState } } + matrix->fillRect(x, y, 32, 8, ca->background); + if (ca->effect > -1) { callEffect(matrix, x, y, ca->effect); diff --git a/src/Dictionary.cpp b/src/Dictionary.cpp index 1121f824..f5d9ab47 100644 --- a/src/Dictionary.cpp +++ b/src/Dictionary.cpp @@ -42,7 +42,7 @@ const char HAbriOptions[] PROGMEM = {"Manual;Auto"}; const char HAeffectID[] PROGMEM = {"%s_eff"}; const char HAeffectIcon[] PROGMEM = {"mdi:auto-fix"}; const char HAeffectName[] PROGMEM = {"Transition effect"}; -const char HAeffectOptions[] PROGMEM = {"Slide;Dim;Zoom;Rotate;Pixelate;Curtain;Ripple;Blink;Reload;Fade"}; +const char HAeffectOptions[] PROGMEM = {"Random;Slide;Dim;Zoom;Rotate;Pixelate;Curtain;Ripple;Blink;Reload;Fade"}; const char HAbtnaID[] PROGMEM = {"%s_btna"}; const char HAbtnaIcon[] PROGMEM = {"mdi:bell-off"}; diff --git a/src/DisplayManager.cpp b/src/DisplayManager.cpp index dd47d28a..2f0dbaa7 100644 --- a/src/DisplayManager.cpp +++ b/src/DisplayManager.cpp @@ -246,7 +246,8 @@ void pushCustomApp(String name, int position) if (availableCallbackIndex == -1) { - DEBUG_PRINTLN("Error adding custom app -> Maximum number of custom apps reached"); + if (DEBUG_MODE) + DEBUG_PRINTLN("Error adding custom app -> Maximum number of custom apps reached"); return; } @@ -276,19 +277,22 @@ bool deleteCustomAppFile(const String &name) // Check if the file exists if (!LittleFS.exists(fileName)) { - DEBUG_PRINTLN("File does not exist: " + fileName); + if (DEBUG_MODE) + DEBUG_PRINTLN("File does not exist: " + fileName); return false; } // Delete the file if (LittleFS.remove(fileName)) { - DEBUG_PRINTLN("File removed successfully: " + fileName); + if (DEBUG_MODE) + DEBUG_PRINTLN("File removed successfully: " + fileName); return true; } else { - DEBUG_PRINTLN("Failed to remove file: " + fileName); + if (DEBUG_MODE) + DEBUG_PRINTLN("Failed to remove file: " + fileName); return false; } } @@ -381,7 +385,6 @@ bool DisplayManager_::parseCustomPage(const String &name, const char *json) if (doc.is()) { - DEBUG_PRINTLN("Single Page"); return generateCustomPage(name, json, false); } else if (doc.is()) @@ -390,7 +393,6 @@ bool DisplayManager_::parseCustomPage(const String &name, const char *json) int cpIndex = 0; for (JsonVariant customPage : customPagesArray) { - Serial.printf("Multiple Page: %i", cpIndex); JsonObject customPageObject = customPage.as(); String customPageJson; serializeJson(customPageObject, customPageJson); @@ -404,12 +406,14 @@ bool DisplayManager_::parseCustomPage(const String &name, const char *json) bool DisplayManager_::generateCustomPage(const String &name, const char *json, bool preventSave) { - DynamicJsonDocument doc(4096); + DynamicJsonDocument doc(8192); DeserializationError error = deserializeJson(doc, json); if (error) { - DEBUG_PRINTLN("PARSING FAILED"); - DEBUG_PRINTLN(error.c_str()); + if (DEBUG_MODE) + DEBUG_PRINTLN("PARSING FAILED"); + if (DEBUG_MODE) + DEBUG_PRINTLN(error.c_str()); doc.clear(); return false; } @@ -447,7 +451,8 @@ bool DisplayManager_::generateCustomPage(const String &name, const char *json, b File file = LittleFS.open(fileName, "w"); if (!file) { - DEBUG_PRINTLN("Failed to open file for writing: " + fileName); + if (DEBUG_MODE) + DEBUG_PRINTLN("Failed to open file for writing: " + fileName); return false; } @@ -653,14 +658,13 @@ bool DisplayManager_::generateCustomPage(const String &name, const char *json, b pushCustomApp(name, pos - 1); customApps[name] = customApp; doc.clear(); - DEBUG_PRINTLN("PARSING FINISHED"); return true; } bool DisplayManager_::generateNotification(uint8_t source, const char *json) { // source: 0=MQTT, 1=HTTP - StaticJsonDocument<4096> doc; + DynamicJsonDocument doc(8192); DeserializationError error = deserializeJson(doc, json); if (error) { @@ -914,12 +918,14 @@ void DisplayManager_::loadCustomApps() if (!root) { - DEBUG_PRINTLN("Failed to open directory"); + if (DEBUG_MODE) + DEBUG_PRINTLN("Failed to open directory"); return; } if (!root.isDirectory()) { - DEBUG_PRINTLN("/CUSTOMAPPS is not a directory"); + if (DEBUG_MODE) + DEBUG_PRINTLN("/CUSTOMAPPS is not a directory"); return; } @@ -1002,6 +1008,8 @@ void DisplayManager_::setup() FastLED.addLeds(leds, MATRIX_WIDTH * MATRIX_HEIGHT); setMatrixLayout(MATRIX_LAYOUT); matrix->setRotation(ROTATE_SCREEN ? 90 : 0); + // FastLED.setTemperature(16752790); + // FastLED.setCorrection(0xC8FFFF); if (COLOR_CORRECTION) { FastLED.setCorrection(COLOR_CORRECTION); @@ -1062,10 +1070,12 @@ void checkLifetime(uint8_t pos) CustomApp &app = appIt->second; if (app.lifetime > 0 && (millis() - app.lastUpdate) / 1000 >= app.lifetime) { - DEBUG_PRINTLN("Removing " + appName + " -> Lifetime over"); + if (DEBUG_MODE) + DEBUG_PRINTLN("Removing " + appName + " -> Lifetime over"); removeCustomAppFromApps(appName, false); - DEBUG_PRINTLN("Set new Apploop"); + if (DEBUG_MODE) + DEBUG_PRINTLN("Set new Apploop"); ui->setApps(Apps); } } @@ -1195,7 +1205,8 @@ void DisplayManager_::nextApp() { if (!MenuManager.inMenu) { - DEBUG_PRINTLN(F("Switching to next app")); + if (DEBUG_MODE) + DEBUG_PRINTLN(F("Switching to next app")); ui->nextApp(); } } @@ -1204,7 +1215,8 @@ void DisplayManager_::previousApp() { if (!MenuManager.inMenu) { - DEBUG_PRINTLN(F("Switching to previous app")); + if (DEBUG_MODE) + DEBUG_PRINTLN(F("Switching to previous app")); ui->previousApp(); } } @@ -1219,7 +1231,6 @@ void DisplayManager_::selectButton() { if (!MenuManager.inMenu) { - DisplayManager.getInstance().dismissNotify(); if (ALARM_ACTIVE && SNOOZE_TIME > 0) @@ -1228,7 +1239,6 @@ void DisplayManager_::selectButton() ALARM_ACTIVE = false; AlarmTicker.once(SNOOZE_TIME * 60, snozzeTimerCallback); } - if (TIMER_ACTIVE) { PeripheryManager.stopSound(); @@ -1248,16 +1258,13 @@ void DisplayManager_::selectButtonLong() void DisplayManager_::dismissNotify() { - bool wakeup; - if (!notifications.empty()) { wakeup = notifications[0].wakeup; notifications.erase(notifications.begin()); PeripheryManager.stopSound(); } - if (notifications.empty()) { if (wakeup && MATRIX_OFF) @@ -1265,7 +1272,6 @@ void DisplayManager_::dismissNotify() DisplayManager.setBrightness(0); } } - if (ALARM_ACTIVE) { PeripheryManager.stopSound(); @@ -1310,8 +1316,16 @@ bool DisplayManager_::switchToApp(const char *json) int index = findAppIndexByName(name); if (index > -1) { - ui->transitionToApp(index); - return true; + if (fast) + { + ui->switchToApp(index); + return true; + } + else + { + ui->transitionToApp(index); + return true; + } } else { @@ -1416,14 +1430,17 @@ std::pair getNativeAppByName(const String &appName) void DisplayManager_::updateAppVector(const char *json) { - DEBUG_PRINTLN(F("New apps vector received")); - DEBUG_PRINTLN(json); + if (DEBUG_MODE) + DEBUG_PRINTLN(F("New apps vector received")); + if (DEBUG_MODE) + DEBUG_PRINTLN(json); DynamicJsonDocument doc(2048); DeserializationError error = deserializeJson(doc, json); if (error) { doc.clear(); - DEBUG_PRINTLN(F("Failed to parse json")); + if (DEBUG_MODE) + DEBUG_PRINTLN(F("Failed to parse json")); return; } @@ -1493,38 +1510,39 @@ void DisplayManager_::updateAppVector(const char *json) String DisplayManager_::getStats() { - StaticJsonDocument<512> doc; + StaticJsonDocument<1024> doc; char buffer[20]; + #ifdef ULANZI doc[BatKey] = BATTERY_PERCENT; doc[BatRawKey] = BATTERY_RAW; #endif - snprintf(buffer, 5, "%.0f", CURRENT_LUX); - doc[LuxKey] = buffer; + + doc[LuxKey] = static_cast(CURRENT_LUX); doc[LDRRawKey] = LDR_RAW; - doc[RamKey] = ESP.getFreeHeap(); + + uint32_t freeHeap = ESP.getFreeHeap(); + doc[RamKey] = freeHeap; doc[BrightnessKey] = BRIGHTNESS; + if (SENSOR_READING) { - - snprintf(buffer, sizeof(buffer), "%.*f", TEMP_DECIMAL_PLACES, CURRENT_TEMP); - doc[TempKey] = buffer; - snprintf(buffer, 5, "%.0f", CURRENT_HUM); - doc[HumKey] = buffer; + doc[TempKey] = static_cast(CURRENT_TEMP); + doc[HumKey] = static_cast(CURRENT_HUM); } + doc[UpTimeKey] = PeripheryManager.readUptime(); doc[SignalStrengthKey] = WiFi.RSSI(); - doc[UpdateKey] = UPDATE_AVAILABLE; doc[MessagesKey] = RECEIVED_MESSAGES; doc[VersionKey] = VERSION; doc[F("indicator1")] = ui->indicator1State; doc[F("indicator2")] = ui->indicator2State; doc[F("indicator3")] = ui->indicator3State; doc[F("app")] = CURRENT_APP; - doc[F("freeFlash")] = LittleFS.totalBytes() - LittleFS.usedBytes(); - doc[F("heap")] = ESP.getFreeHeap(); + // doc[F("freeFlash")] = LittleFS.totalBytes() - LittleFS.usedBytes(); String jsonString; - return serializeJson(doc, jsonString), jsonString; + serializeJson(doc, jsonString); + return jsonString; } void DisplayManager_::setAppTime(long duration) @@ -1535,7 +1553,8 @@ void DisplayManager_::setAppTime(long duration) void DisplayManager_::setMatrixLayout(int layout) { delete matrix; // Free memory from the current matrix object - DEBUG_PRINTF("Set matrix layout to %i", layout); + if (DEBUG_MODE) + DEBUG_PRINTF("Set matrix layout to %i", layout); switch (layout) { case 0: @@ -1599,20 +1618,6 @@ void DisplayManager_::showSleepAnimation() } } -void DisplayManager_::showCurtainEffect() -{ - int width = 32; - int height = 8; - - for (int i = 0; i <= width / 2; i++) - { - // ui->update(); - matrix->fillRect(0, 0, i, 8, 0xFFFF); // Linker Vorhang - show(); - delay(80); - } -} - void DisplayManager_::setPower(bool state) { if (state) @@ -1789,7 +1794,7 @@ void DisplayManager_::sendAppLoop() String DisplayManager_::getSettings() { - StaticJsonDocument<512> doc; + StaticJsonDocument<1024> doc; doc["ABRI"] = AUTO_BRIGHTNESS; doc["BRI"] = BRIGHTNESS; doc["ATRANS"] = AUTO_TRANSITION; @@ -1798,6 +1803,8 @@ String DisplayManager_::getSettings() doc["TSPEED"] = TIME_PER_TRANSITION; doc["ATIME"] = TIME_PER_APP / 1000; doc["TMODE"] = TIME_MODE; + doc["CCOL"] = CALENDAR_COLOR; + doc["CTCOL"] = CALENDAR_TEXT_COLOR; doc["TFORMAT"] = TIME_FORMAT; doc["DFORMAT"] = DATE_FORMAT; doc["SOM"] = START_ON_MONDAY; @@ -1819,19 +1826,31 @@ String DisplayManager_::getSettings() doc["TEMP_COL"] = TEMP_COLOR; doc["BAT_COL"] = BAT_COLOR; doc["SSPEED"] = SCROLL_SPEED; + doc["TIM"] = SHOW_TIME; + doc["DAT"] = SHOW_DATE; + doc["HUM"] = SHOW_HUM; + doc["TEMP"] = SHOW_TEMP; + doc["BAT"] = SHOW_BAT; String jsonString; return serializeJson(doc, jsonString), jsonString; } void DisplayManager_::setNewSettings(const char *json) { - DEBUG_PRINTLN(F("Got new settings:")); - DEBUG_PRINTLN(json); - DynamicJsonDocument doc(512); + if (DEBUG_MODE) + DEBUG_PRINTLN(F("Got new settings:")); + if (DEBUG_MODE) + DEBUG_PRINTLN(json); + DynamicJsonDocument doc(2048); DeserializationError error = deserializeJson(doc, json); if (error) + { + if (DEBUG_MODE) + DEBUG_PRINTLN(F("Error while parsing json")); + if (DEBUG_MODE) + DEBUG_PRINTLN(error.c_str()); return; - + } if (doc.containsKey("ATIME")) { long atime = doc["ATIME"].as(); @@ -1856,6 +1875,11 @@ void DisplayManager_::setNewSettings(const char *json) UPPERCASE_LETTERS = doc.containsKey("UPPERCASE") ? doc["UPPERCASE"].as() : UPPERCASE_LETTERS; SHOW_WEEKDAY = doc.containsKey("WD") ? doc["WD"].as() : SHOW_WEEKDAY; BLOCK_NAVIGATION = doc.containsKey("BLOCKN") ? doc["BLOCKN"].as() : BLOCK_NAVIGATION; + SHOW_TIME = doc.containsKey("TIM") ? doc["TIM"].as() : SHOW_TIME; + SHOW_DATE = doc.containsKey("DAT") ? doc["DAT"].as() : SHOW_DATE; + SHOW_HUM = doc.containsKey("HUM") ? doc["HUM"].as() : SHOW_HUM; + SHOW_TEMP = doc.containsKey("TEMP") ? doc["TEMP"].as() : SHOW_TEMP; + SHOW_BAT = doc.containsKey("BAT") ? doc["BAT"].as() : SHOW_BAT; if (doc.containsKey("CCORRECTION")) { auto colorValue = doc["CCORRECTION"]; @@ -1929,42 +1953,48 @@ void DisplayManager_::setNewSettings(const char *json) if (doc.containsKey("TCOL")) { auto TCOL = doc["TCOL"]; - TEXTCOLOR_565 = getColorFromJsonVariant(TCOL, matrix->Color(255, 255, 255)); + uint16_t TempColor = getColorFromJsonVariant(TCOL, matrix->Color(255, 255, 255)); + for (auto it = customApps.begin(); it != customApps.end(); ++it) + { + CustomApp &app = it->second; + if (app.color == TEXTCOLOR_565) + { + app.color = TempColor; + } + } + TEXTCOLOR_565 = TempColor; } + if (doc.containsKey("TIME_COL")) { auto TIME_COL = doc["TIME_COL"]; - TIME_COLOR = getColorFromJsonVariant(TIME_COL, TEXTCOLOR_565); + TIME_COLOR = getColorFromJsonVariant(TIME_COL, 0); } - if (doc.containsKey("DATE_COL")) { auto DATE_COL = doc["DATE_COL"]; - DATE_COLOR = getColorFromJsonVariant(DATE_COL, TEXTCOLOR_565); + DATE_COLOR = getColorFromJsonVariant(DATE_COL, 0); } - if (doc.containsKey("TEMP_COL")) { auto TEMP_COL = doc["TEMP_COL"]; - TEMP_COLOR = getColorFromJsonVariant(TEMP_COL, TEXTCOLOR_565); + TEMP_COLOR = getColorFromJsonVariant(TEMP_COL, 0); } - if (doc.containsKey("HUM_COL")) { auto HUM_COL = doc["HUM_COL"]; - HUM_COLOR = getColorFromJsonVariant(HUM_COL, TEXTCOLOR_565); + HUM_COLOR = getColorFromJsonVariant(HUM_COL, 0); } - -#ifdef ULANZI if (doc.containsKey("BAT_COL")) { auto BAT_COL = doc["BAT_COL"]; - BAT_COLOR = getColorFromJsonVariant(BAT_COL, TEXTCOLOR_565); + BAT_COLOR = getColorFromJsonVariant(BAT_COL, 0); } -#endif doc.clear(); applyAllSettings(); saveSettings(); + if (DEBUG_MODE) + DEBUG_PRINTLN("Settings loaded"); } String DisplayManager_::ledsAsJson() @@ -2043,7 +2073,7 @@ void DisplayManager_::reorderApps(const String &jsonString) void DisplayManager_::processDrawInstructions(int16_t xOffset, int16_t yOffset, String &drawInstructions) { - StaticJsonDocument<4096> doc; + DynamicJsonDocument doc(8192); DeserializationError error = deserializeJson(doc, drawInstructions); if (error) @@ -2159,6 +2189,7 @@ void DisplayManager_::processDrawInstructions(int16_t xOffset, int16_t yOffset, } } } + doc.clear(); } bool DisplayManager_::moodlight(const char *json) diff --git a/src/Globals.cpp b/src/Globals.cpp index 6d391f43..e6991c55 100644 --- a/src/Globals.cpp +++ b/src/Globals.cpp @@ -13,13 +13,13 @@ char *getID() WiFi.macAddress(mac); char *macStr = new char[24]; snprintf(macStr, 24, "awtrix_%02x%02x%02x", mac[3], mac[4], mac[5]); - DEBUG_PRINTLN(F("Starting filesystem")); + if (DEBUG_MODE) DEBUG_PRINTLN(F("Starting filesystem")); return macStr; } void startLittleFS() { - DEBUG_PRINTLN(F("Starting filesystem")); + if (DEBUG_MODE) DEBUG_PRINTLN(F("Starting filesystem")); if (LittleFS.begin()) { #ifdef ULANZI @@ -28,11 +28,11 @@ void startLittleFS() LittleFS.mkdir("/ICONS"); LittleFS.mkdir("/PALETTES"); LittleFS.mkdir("/CUSTOMAPPS"); - DEBUG_PRINTLN(F("Filesystem started")); + if (DEBUG_MODE) DEBUG_PRINTLN(F("Filesystem started")); } else { - DEBUG_PRINTLN(F("Filesystem currupt. Formating...")); + if (DEBUG_MODE) DEBUG_PRINTLN(F("Filesystem currupt. Formating...")); LittleFS.format(); ESP.restart(); } @@ -40,7 +40,7 @@ void startLittleFS() void loadDevSettings() { - DEBUG_PRINTLN("Loading Devsettings"); + if (DEBUG_MODE) DEBUG_PRINTLN("Loading Devsettings"); if (LittleFS.exists("/dev.json")) { File file = LittleFS.open("/dev.json", "r"); @@ -48,11 +48,11 @@ void loadDevSettings() DeserializationError error = deserializeJson(doc, file); if (error) { - DEBUG_PRINTLN(F("Failed to read dev settings")); + if (DEBUG_MODE) DEBUG_PRINTLN(F("Failed to read dev settings")); return; } - DEBUG_PRINTF("%i dev settings found", doc.size()); + if (DEBUG_MODE) DEBUG_PRINTF("%i dev settings found", doc.size()); if (doc.containsKey("bootsound")) { @@ -99,6 +99,11 @@ void loadDevSettings() HA_PREFIX = doc["ha_prefix"].as(); } + if (doc.containsKey("stats_interval")) + { + STATS_INTERVAL = doc["stats_interval"].as(); + } + if (doc.containsKey("uppercase")) { UPPERCASE_LETTERS = doc["uppercase"].as(); @@ -119,6 +124,11 @@ void loadDevSettings() ROTATE_SCREEN = doc["rotate_screen"].as(); } + if (doc.containsKey("debug_mode")) + { + DEBUG_MODE = doc["debug_mode"].as(); + } + if (doc.containsKey("gamma")) { GAMMA = doc["gamma"].as(); @@ -152,21 +162,28 @@ void loadDevSettings() } else { - DEBUG_PRINTLN("Devsettings not found"); + if (DEBUG_MODE) DEBUG_PRINTLN("Devsettings not found"); } } +void formatSettings() +{ + Settings.begin("awtrix", false); + Settings.clear(); + Settings.end(); +} + void loadSettings() { startLittleFS(); - DEBUG_PRINTLN(F("Loading Usersettings")); + if (DEBUG_MODE) DEBUG_PRINTLN(F("Loading Usersettings")); Settings.begin("awtrix", false); BRIGHTNESS = Settings.getUInt("BRI", 120); AUTO_BRIGHTNESS = Settings.getBool("ABRI", false); TEXTCOLOR_565 = Settings.getUInt("TCOL", 0xFFFF); CALENDAR_COLOR = Settings.getUInt("CCOL", 0xF800); CALENDAR_TEXT_COLOR = Settings.getUInt("CTCOL", 0x0000); - TRANS_EFFECT = Settings.getUInt("TEFF", 0); + TRANS_EFFECT = Settings.getUInt("TEFF", 1); TIME_MODE = Settings.getUInt("TMODE", 1); TIME_COLOR = Settings.getUInt("TIME_COL", 0); DATE_COLOR = Settings.getUInt("DATE_COL", 0); @@ -184,6 +201,7 @@ void loadSettings() TIME_FORMAT = Settings.getString("TFORMAT", "%H:%M:%S"); DATE_FORMAT = Settings.getString("DFORMAT", "%d.%m.%y"); START_ON_MONDAY = Settings.getBool("SOM", true); + BLOCK_NAVIGATION = Settings.getBool("BLOCKN", false); IS_CELSIUS = Settings.getBool("CEL", true); SHOW_TIME = Settings.getBool("TIM", true); SHOW_DATE = Settings.getBool("DAT", false); @@ -207,7 +225,7 @@ void loadSettings() void saveSettings() { - DEBUG_PRINTLN(F("Saving usersettings")); + if (DEBUG_MODE) DEBUG_PRINTLN(F("Saving usersettings")); Settings.begin("awtrix", false); Settings.putUInt("CCOL", CALENDAR_COLOR); Settings.putUInt("CTCOL", CALENDAR_TEXT_COLOR); @@ -215,6 +233,7 @@ void saveSettings() Settings.putUInt("BRI", BRIGHTNESS); Settings.putBool("WD", SHOW_WEEKDAY); Settings.putBool("ABRI", AUTO_BRIGHTNESS); + Settings.putBool("BLOCKN", BLOCK_NAVIGATION); Settings.putBool("ATRANS", AUTO_TRANSITION); Settings.putUInt("TCOL", TEXTCOLOR_565); Settings.putUInt("TMODE", TIME_MODE); @@ -254,7 +273,7 @@ IPAddress gateway; IPAddress subnet; IPAddress primaryDNS; IPAddress secondaryDNS; -const char *VERSION = "0.72"; +const char *VERSION = "0.73"; String MQTT_HOST = ""; uint16_t MQTT_PORT = 1883; @@ -267,9 +286,7 @@ bool SHOW_TIME = true; bool SHOW_DATE = true; bool SHOW_WEATHER = true; -#ifdef ULANZI bool SHOW_BAT = true; -#endif bool SHOW_TEMP = true; bool SHOW_HUM = true; bool SHOW_SECONDS = true; @@ -280,7 +297,7 @@ String NET_SN = "255.255.255.0"; String NET_PDNS = "8.8.8.8"; String NET_SDNS = "1.1.1.1"; long TIME_PER_APP = 7000; -uint8_t MATRIX_FPS = 50; +uint8_t MATRIX_FPS = 40; int TIME_PER_TRANSITION = 400; String NTP_SERVER = "de.pool.ntp.org"; String NTP_TZ = "CET-1CEST,M3.5.0,M10.5.0/3"; @@ -357,7 +374,9 @@ uint16_t TEMP_COLOR = 0; uint16_t HUM_COLOR = 0; bool ARTNET_MODE; bool MOODLIGHT_MODE; +long STATS_INTERVAL = 10000; +bool DEBUG_MODE = false; uint8_t MIN_BRIGHTNESS = 2; uint8_t MAX_BRIGHTNESS = 180; -float movementFactor = 0.5; -int8_t TRANS_EFFECT = 0; \ No newline at end of file +double movementFactor = 0.5; +int8_t TRANS_EFFECT = 1; \ No newline at end of file diff --git a/src/Globals.h b/src/Globals.h index af8e1d02..3203757c 100644 --- a/src/Globals.h +++ b/src/Globals.h @@ -26,7 +26,7 @@ #define DEBUG_PRINTLN(x) #define DEBUG_PRINTF(format, ...) #endif - +void formatSettings(); extern const char *uniqueID; extern const char *VERSION; extern IPAddress local_IP; @@ -44,9 +44,7 @@ extern bool NET_STATIC; extern bool SHOW_TIME; extern bool SHOW_DATE; extern bool SHOW_WEATHER; -#ifdef ULANZI extern bool SHOW_BAT; -#endif extern String HA_PREFIX; extern bool SHOW_TEMP; extern bool SHOW_HUM; @@ -123,6 +121,7 @@ extern bool BLOCK_NAVIGATION; extern bool UPDATE_CHECK; extern bool SENSOR_READING; extern bool ROTATE_SCREEN; +extern long STATS_INTERVAL; extern uint8_t TIME_MODE; extern uint8_t SCROLL_SPEED; extern uint16_t CALENDAR_COLOR; @@ -134,7 +133,10 @@ extern uint16_t TEMP_COLOR; extern uint16_t HUM_COLOR; extern bool ARTNET_MODE; extern bool MOODLIGHT_MODE; -extern float movementFactor; +extern double movementFactor; extern uint8_t MIN_BRIGHTNESS; extern uint8_t MAX_BRIGHTNESS; +extern bool DEBUG_MODE; + + #endif // Globals_H diff --git a/src/MQTTManager.cpp b/src/MQTTManager.cpp index 6bde117e..6597312a 100644 --- a/src/MQTTManager.cpp +++ b/src/MQTTManager.cpp @@ -24,7 +24,7 @@ HASensor *temperature, *humidity, *illuminance, *uptime, *strength, *version, *r HABinarySensor *btnleft, *btnmid, *btnright = nullptr; bool connected; char matID[40], ind1ID[40], ind2ID[40], ind3ID[40], briID[40], btnAID[40], btnBID[40], btnCID[40], appID[40], tempID[40], humID[40], luxID[40], verID[40], ramID[40], upID[40], sigID[40], btnLID[40], btnMID[40], btnRID[40], transID[40], doUpdateID[40], batID[40], myID[40], sSpeed[40], effectID[40]; - +long previousMillis_Stats; // The getter for the instantiated singleton instance MQTTManager_ &MQTTManager_::getInstance() { @@ -120,7 +120,6 @@ void onStateCommand(bool state, HALight *sender) else if (sender == Indikator1) { DisplayManager.setIndicator1State(state); - Serial.println("I1"); } else if (sender == Indikator2) { @@ -128,7 +127,6 @@ void onStateCommand(bool state, HALight *sender) } else if (sender == Indikator3) { - Serial.println("I3"); DisplayManager.setIndicator3State(state); } sender->setState(state); @@ -161,12 +159,14 @@ void onNumberCommand(HANumeric number, HANumber *sender) void onMqttMessage(const char *topic, const uint8_t *payload, uint16_t length) { - DEBUG_PRINTF("MQTT message received at topic %s", topic); + if (DEBUG_MODE) + DEBUG_PRINTF("MQTT message received at topic %s", topic); String strTopic = String(topic); char *payloadCopy = new char[length + 1]; memcpy(payloadCopy, payload, length); payloadCopy[length] = '\0'; - DEBUG_PRINTF("Payload: %s", payloadCopy); + if (DEBUG_MODE) + DEBUG_PRINTF("Payload: %s", payloadCopy); ++RECEIVED_MESSAGES; if (strTopic.equals(MQTT_PREFIX + "/notify")) { @@ -208,9 +208,9 @@ void onMqttMessage(const char *topic, const uint8_t *payload, uint16_t length) return; } - if (strTopic.equals(MQTT_PREFIX + "/sendscreen")) + if (strTopic.equals(MQTT_PREFIX + "/sendscreen")) { - MQTTManager.getInstance().publish("screen",DisplayManager.ledsAsJson().c_str()); + MQTTManager.getInstance().publish("screen", DisplayManager.ledsAsJson().c_str()); delete[] payloadCopy; return; } @@ -252,7 +252,8 @@ void onMqttMessage(const char *topic, const uint8_t *payload, uint16_t length) DeserializationError error = deserializeJson(doc, payload); if (error) { - DEBUG_PRINTLN(F("Failed to parse json")); + if (DEBUG_MODE) + DEBUG_PRINTLN(F("Failed to parse json")); return; } if (doc.containsKey("power")) @@ -294,7 +295,8 @@ void onMqttMessage(const char *topic, const uint8_t *payload, uint16_t length) if (strTopic.equals(MQTT_PREFIX + "/reboot")) { - DEBUG_PRINTLN("REBOOT COMMAND RECEIVED") + if (DEBUG_MODE) + DEBUG_PRINTLN("REBOOT COMMAND RECEIVED") delay(1000); ESP.restart(); delete[] payloadCopy; @@ -321,13 +323,12 @@ void onMqttMessage(const char *topic, const uint8_t *payload, uint16_t length) delete[] payloadCopy; return; } - DEBUG_PRINTLN(F("Unknown MQTT command!")); } void onMqttConnected() { - DEBUG_PRINTLN(F("MQTT Connected")); - String prefix = MQTT_PREFIX; + if (DEBUG_MODE) + DEBUG_PRINTLN(F("MQTT Connected")); const char *topics[] PROGMEM = { "/brightness", "/notify/dismiss", @@ -353,9 +354,9 @@ void onMqttConnected() "/sendscreen"}; for (const char *topic : topics) { - DEBUG_PRINTF("Subscribe to topic %s", topic); - String fullTopic = prefix + topic; - mqtt.subscribe(fullTopic.c_str()); + if (DEBUG_MODE) + DEBUG_PRINTF("Subscribe to topic %s", topic); + mqtt.subscribe((MQTT_PREFIX + topic).c_str()); delay(30); } delay(200); @@ -381,21 +382,78 @@ void connect() if (MQTT_USER == "" || MQTT_PASS == "") { - DEBUG_PRINTLN(F("Connecting to MQTT w/o login")); + if (DEBUG_MODE) + DEBUG_PRINTLN(F("Connecting to MQTT w/o login")); mqtt.begin(MQTT_HOST.c_str(), MQTT_PORT, nullptr, nullptr, MQTT_PREFIX.c_str()); } else { - DEBUG_PRINTLN(F("Connecting to MQTT with login")); + if (DEBUG_MODE) + DEBUG_PRINTLN(F("Connecting to MQTT with login")); mqtt.begin(MQTT_HOST.c_str(), MQTT_PORT, MQTT_USER.c_str(), MQTT_PASS.c_str(), MQTT_PREFIX.c_str()); } } +void MQTTManager_::sendStats() +{ + if (mqtt.isConnected()) + { + if (HA_DISCOVERY) + { + char buffer[5]; +#ifdef ULANZI + snprintf(buffer, 5, "%d", BATTERY_PERCENT); + battery->setValue(buffer); +#endif + + if (SENSOR_READING) + { + snprintf(buffer, 5, "%.0f", CURRENT_TEMP); + temperature->setValue(buffer); + snprintf(buffer, 5, "%.0f", CURRENT_HUM); + humidity->setValue(buffer); + } + + snprintf(buffer, 5, "%.0f", CURRENT_LUX); + illuminance->setValue(buffer); + BriMode->setState(AUTO_BRIGHTNESS, false); + Matrix->setBrightness(BRIGHTNESS); + Matrix->setState(!MATRIX_OFF, false); + HALight::RGBColor color; + color.isSet = true; + color.red = (TEXTCOLOR_565 >> 11) & 0x1F; + color.green = (TEXTCOLOR_565 >> 5) & 0x3F; + color.blue = TEXTCOLOR_565 & 0x1F; + color.red <<= 3; + color.green <<= 2; + color.blue <<= 3; + Matrix->setRGBColor(color); + int8_t rssiValue = WiFi.RSSI(); + char rssiString[4]; + snprintf(rssiString, sizeof(rssiString), "%d", rssiValue); + strength->setValue(rssiString); + + char rambuffer[10]; + int freeHeapBytes = ESP.getFreeHeap(); + itoa(freeHeapBytes, rambuffer, 10); + ram->setValue(rambuffer); + long uptimeValue = PeripheryManager.readUptime(); + char uptimeStr[25]; // Buffer for string representation + sprintf(uptimeStr, "%ld", uptimeValue); + uptime->setValue(uptimeStr); + transition->setState(AUTO_TRANSITION, false); + } + + publish(StatsTopic, DisplayManager.getStats().c_str()); + } +} + void MQTTManager_::setup() { if (HA_DISCOVERY) { - DEBUG_PRINTLN(F("Starting Homeassistant discovery")); + if (DEBUG_MODE) + DEBUG_PRINTLN(F("Starting Homeassistant discovery")); mqtt.setDiscoveryPrefix(HA_PREFIX.c_str()); mqtt.setDataPrefix(MQTT_PREFIX.c_str()); uint8_t mac[6]; @@ -603,6 +661,12 @@ void MQTTManager_::tick() if (MQTT_HOST != "") { mqtt.loop(); + unsigned long currentMillis_Stats = millis(); + if (currentMillis_Stats - previousMillis_Stats >= STATS_INTERVAL) + { + previousMillis_Stats = currentMillis_Stats; + sendStats(); + } } } @@ -630,69 +694,14 @@ void MQTTManager_::rawPublish(const char *prefix, const char *topic, const char void MQTTManager_::setCurrentApp(String appName) { - DEBUG_PRINTF("Publish current app %s", appName.c_str()); + if (DEBUG_MODE) + DEBUG_PRINTF("Publish current app %s", appName.c_str()); if (HA_DISCOVERY) curApp->setValue(appName.c_str()); publish("stats/currentApp", appName.c_str()); } -void MQTTManager_::sendStats() -{ - if (HA_DISCOVERY) - { - char buffer[5]; -#ifdef ULANZI - snprintf(buffer, 5, "%d", BATTERY_PERCENT); - battery->setValue(buffer); - -#endif - - if (SENSOR_READING) - { - snprintf(buffer, 5, "%.0f", CURRENT_TEMP); - temperature->setValue(buffer); - snprintf(buffer, 5, "%.0f", CURRENT_HUM); - humidity->setValue(buffer); - } - - snprintf(buffer, 5, "%.0f", CURRENT_LUX); - illuminance->setValue(buffer); - - BriMode->setState(AUTO_BRIGHTNESS, false); - Matrix->setBrightness(BRIGHTNESS); - Matrix->setState(!MATRIX_OFF, false); - HALight::RGBColor color; - color.isSet = true; - color.red = (TEXTCOLOR_565 >> 11) & 0x1F; - color.green = (TEXTCOLOR_565 >> 5) & 0x3F; - color.blue = TEXTCOLOR_565 & 0x1F; - color.red <<= 3; - color.green <<= 2; - color.blue <<= 3; - Matrix->setRGBColor(color); - int8_t rssiValue = WiFi.RSSI(); - char rssiString[4]; - snprintf(rssiString, sizeof(rssiString), "%d", rssiValue); - strength->setValue(rssiString); - - char rambuffer[10]; - int freeHeapBytes = ESP.getFreeHeap(); - itoa(freeHeapBytes, rambuffer, 10); - ram->setValue(rambuffer); - uptime->setValue(PeripheryManager.readUptime()); - - transition->setState(AUTO_TRANSITION, false); - - // update->setState(UPDATE_AVAILABLE, false); - } - else - { - } - - publish(StatsTopic, DisplayManager.getStats().c_str()); -} - void MQTTManager_::sendButton(byte btn, bool state) { static bool btn0State, btn1State, btn2State; diff --git a/src/MatrixDisplayUi.cpp b/src/MatrixDisplayUi.cpp index ac944c54..816da927 100644 --- a/src/MatrixDisplayUi.cpp +++ b/src/MatrixDisplayUi.cpp @@ -304,55 +304,76 @@ void MatrixDisplayUi::drawIndicators() } } +uint8_t currentTransition; +bool gotNewTransition = true; +TransitionType getRandomTransition() +{ + // RANDOM is now index 0, so we add 1 to the result to ensure it's never selected + return static_cast((rand() % (CROSSFADE)) + 1); +} + void MatrixDisplayUi::drawApp() { switch (this->state.appState) { case IN_TRANSITION: { - if (TRANS_EFFECT == SLIDE) + gotNewTransition = false; + if (currentTransition == SLIDE) { slideTransition(); } - else if (TRANS_EFFECT == FADE) + else if (currentTransition == FADE) { fadeTransition(); } - else if (TRANS_EFFECT == ZOOM) + else if (currentTransition == ZOOM) { zoomTransition(); } - else if (TRANS_EFFECT == ROTATE) + else if (currentTransition == ROTATE) { rotateTransition(); } - else if (TRANS_EFFECT == PIXELATE) + else if (currentTransition == PIXELATE) { pixelateTransition(); } - else if (TRANS_EFFECT == CURTAIN) + else if (currentTransition == CURTAIN) { curtainTransition(); } - else if (TRANS_EFFECT == RIPPLE) + else if (currentTransition == RIPPLE) { rippleTransition(); } - else if (TRANS_EFFECT == BLINK) + else if (currentTransition == BLINK) { blinkTransition(); } - else if (TRANS_EFFECT == RELOAD) + else if (currentTransition == RELOAD) { reloadTransition(); } - else if (TRANS_EFFECT == CROSSFADE) + else if (currentTransition == CROSSFADE) { crossfadeTransition(); } break; } case FIXED: + if (TRANS_EFFECT == RANDOM) + { + if (gotNewTransition == false) + { + currentTransition = getRandomTransition(); // Wähle einen neuen zufälligen Übergang aus, wenn TRANS_EFFECT auf RANDOM gesetzt ist + gotNewTransition = true; + } + } + else + { + currentTransition = TRANS_EFFECT; // Wenn TRANS_EFFECT nicht RANDOM ist, setzen Sie currentTransition auf TRANS_EFFECT + } (this->AppFunctions[this->state.currentApp])(this->matrix, &this->state, 0, 0, &gif2); break; } diff --git a/src/MatrixDisplayUi.h b/src/MatrixDisplayUi.h index 640bdb9a..d6ce55b3 100644 --- a/src/MatrixDisplayUi.h +++ b/src/MatrixDisplayUi.h @@ -45,6 +45,7 @@ enum AnimationDirection enum TransitionType { + RANDOM, SLIDE, FADE, ZOOM, @@ -69,6 +70,7 @@ struct MatrixDisplayUiState u_int64_t lastUpdate = 0; long ticksSinceLastStateSwitch = 0; + AppState appState = FIXED; uint8_t currentApp = 0; @@ -132,7 +134,6 @@ class MatrixDisplayUi void reloadTransition(); void crossfadeTransition(); - public: MatrixDisplayUi(FastLED_NeoMatrix *matrix); uint8_t AppCount = 0; diff --git a/src/PeripheryManager.cpp b/src/PeripheryManager.cpp index 101f4f8a..6e14a568 100644 --- a/src/PeripheryManager.cpp +++ b/src/PeripheryManager.cpp @@ -110,7 +110,13 @@ void left_button_pressed() #endif DisplayManager.leftButton(); MenuManager.leftButton(); - DEBUG_PRINTLN(F("Left button clicked")); + if (DEBUG_MODE) + DEBUG_PRINTLN(F("Left button clicked")); + } + else + { + if (DEBUG_MODE) + DEBUG_PRINTLN(F("Left button clicked but blocked")); } } @@ -123,7 +129,13 @@ void right_button_pressed() #endif DisplayManager.rightButton(); MenuManager.rightButton(); - DEBUG_PRINTLN(F("Right button clicked")); + if (DEBUG_MODE) + DEBUG_PRINTLN(F("Right button clicked")); + } + else + { + if (DEBUG_MODE) + DEBUG_PRINTLN(F("Right button clicked but blocked")); } } @@ -136,7 +148,13 @@ void select_button_pressed() #endif DisplayManager.selectButton(); MenuManager.selectButton(); - DEBUG_PRINTLN(F("Select button clicked")); + if (DEBUG_MODE) + DEBUG_PRINTLN(F("Select button clicked")); + } + else + { + if (DEBUG_MODE) + DEBUG_PRINTLN(F("Select button clicked but blocked")); } } @@ -163,13 +181,15 @@ void select_button_pressed_long() DisplayManager.selectButtonLong(); - DEBUG_PRINTLN(F("Select button pressed long")); + if (DEBUG_MODE) + DEBUG_PRINTLN(F("Select button pressed long")); } } void select_button_double() { - DEBUG_PRINTLN(F("Select button double pressed")); + if (DEBUG_MODE) + DEBUG_PRINTLN(F("Select button double pressed")); if (!BLOCK_NAVIGATION) { #ifndef ULANZI @@ -188,10 +208,12 @@ void select_button_double() void PeripheryManager_::playBootSound() { - DEBUG_PRINTLN(F("Playing bootsound")); + if (DEBUG_MODE) + DEBUG_PRINTLN(F("Playing bootsound")); if (!SOUND_ACTIVE) { - DEBUG_PRINTLN(F("Sound output disabled")); + if (DEBUG_MODE) + DEBUG_PRINTLN(F("Sound output disabled")); return; } @@ -266,7 +288,8 @@ bool PeripheryManager_::playFromFile(String file) return true; #ifdef ULANZI - DEBUG_PRINTLN(F("Playing RTTTL sound file")); + if (DEBUG_MODE) + DEBUG_PRINTLN(F("Playing RTTTL sound file")); if (LittleFS.exists("/MELODIES/" + String(file) + ".txt")) { Melody melody = MelodyFactory.loadRtttlFile("/MELODIES/" + String(file) + ".txt"); @@ -278,7 +301,8 @@ bool PeripheryManager_::playFromFile(String file) return false; } #else - DEBUG_PRINTLN(F("Playing MP3 file")); + if (DEBUG_MODE) + DEBUG_PRINTLN(F("Playing MP3 file")); dfmp3.stop(); delay(50); dfmp3.playMp3FolderTrack(file.toInt()); @@ -300,7 +324,8 @@ bool PeripheryManager_::isPlaying() void PeripheryManager_::setup() { - DEBUG_PRINTLN(F("Setup periphery")); + if (DEBUG_MODE) + DEBUG_PRINTLN(F("Setup periphery")); startTime = millis(); pinMode(LDR_PIN, INPUT); #ifndef ULANZI @@ -334,17 +359,20 @@ void PeripheryManager_::setup() #else if (bme280.begin(BME280_ADDRESS) || bme280.begin(BME280_ADDRESS_ALTERNATE)) { - DEBUG_PRINTLN(F("BME280 sensor detected")); + if (DEBUG_MODE) + DEBUG_PRINTLN(F("BME280 sensor detected")); TEMP_SENSOR_TYPE = TEMP_SENSOR_TYPE_BME280; } else if (bmp280.begin(BMP280_ADDRESS) || bmp280.begin(BMP280_ADDRESS_ALT)) { - DEBUG_PRINTLN(F("BMP280 sensor detected")); + if (DEBUG_MODE) + DEBUG_PRINTLN(F("BMP280 sensor detected")); TEMP_SENSOR_TYPE = TEMP_SENSOR_TYPE_BMP280; } else if (htu21df.begin()) { - DEBUG_PRINTLN(F("HTU21DF sensor detected")); + if (DEBUG_MODE) + DEBUG_PRINTLN(F("HTU21DF sensor detected")); TEMP_SENSOR_TYPE = TEMP_SENSOR_TYPE_HTU21DF; } dfmp3.begin(); @@ -403,9 +431,6 @@ void PeripheryManager_::tick() CURRENT_TEMP += TEMP_OFFSET; CURRENT_HUM += HUM_OFFSET; } - - // checkAlarms(); - MQTTManager.sendStats(); } unsigned long currentMillis_LDR = millis(); @@ -445,7 +470,8 @@ void PeripheryManager_::checkAlarms() DeserializationError error = deserializeJson(doc, file); if (error) { - DEBUG_PRINTLN(F("Failed to read Alarm file")); + if (DEBUG_MODE) + DEBUG_PRINTLN(F("Failed to read Alarm file")); return; } JsonArray alarms = doc["alarms"]; @@ -496,12 +522,10 @@ void PeripheryManager_::checkAlarms() } } -const char *PeripheryManager_::readUptime() +long PeripheryManager_::readUptime() { - static char uptime[25]; // Make the array static to keep it from being destroyed when the function returns unsigned long currentTime = millis(); unsigned long elapsedTime = currentTime - startTime; - unsigned long uptimeSeconds = elapsedTime / 1000; - sprintf(uptime, "%lu", uptimeSeconds); - return uptime; -} \ No newline at end of file + long uptimeSeconds = elapsedTime / 1000; + return uptimeSeconds; +} diff --git a/src/PeripheryManager.h b/src/PeripheryManager.h index f824931d..a28bedda 100644 --- a/src/PeripheryManager.h +++ b/src/PeripheryManager.h @@ -40,10 +40,11 @@ class PeripheryManager_ bool parseSound(const char *json); bool isPlaying(); void stopSound(); + uint8_t getMatrixPin(); #ifndef ULANZI void setVolume(uint8_t); #endif - const char *readUptime(); + long readUptime(); }; extern PeripheryManager_ &PeripheryManager; diff --git a/src/ServerManager.cpp b/src/ServerManager.cpp index 54f5b713..21332cd9 100644 --- a/src/ServerManager.cpp +++ b/src/ServerManager.cpp @@ -10,6 +10,11 @@ #include "DisplayManager.h" #include "UpdateManager.h" #include "PeripheryManager.h" +#include + +WiFiUDP udp; +unsigned int localUdpPort = 4210; // Wählen Sie einen Port +char incomingPacket[255]; WebServer server(80); FSWebServer mws(LittleFS, server); @@ -30,33 +35,6 @@ void versionHandler() webRequest->send(200, F("text/plain"), VERSION); } -void handleBmpRequest() -{ - // Setze den Inhaltstyp auf BMP - server.setContentLength(CONTENT_LENGTH_UNKNOWN); - server.send_P(200, "image/bmp", ""); - int *ledColors = DisplayManager.getLedColors(); - - // Schreibe BMP-Header - uint16_t fileHeader[7] = {0x4D42, 0x1136, 0x0000, 0x0000, 0x0036, 0x0000, 0x0028}; - server.sendContent_P((char *)&fileHeader, sizeof(fileHeader)); - uint32_t header[9] = {32, 8, 0x00200001, 0x0000, 0x0000, 0x1136, 0x0B13, 0x0B13, 0x0000}; - server.sendContent_P((char *)&header, sizeof(header)); - - for (int y = 0; y < 8; y++) - { - for (int x = 0; x < 32; x++) - { - int color = ledColors[y * 32 + x]; - char pixel[3] = {static_cast(color & 0xFF), static_cast((color >> 8) & 0xFF), static_cast((color >> 16) & 0xFF)}; - server.sendContent_P(pixel, sizeof(pixel)); - } - } - delete[] ledColors; // Lösche das Array - - server.sendContent(""); -} - void saveHandler() { WebServerClass *webRequest = mws.getRequest(); @@ -68,7 +46,7 @@ void addHandler() { mws.addHandler("/api/power", HTTP_POST, []() { DisplayManager.powerStateParse(mws.webserver->arg("plain").c_str()); mws.webserver->send(200,F("text/plain"),F("OK")); }); - mws.addHandler("/api/loop", HTTP_GET, []() + mws.addHandler("/api/loop", HTTP_GET, []() { mws.webserver->send_P(200, "application/json", DisplayManager.getAppsAsJson().c_str()); }); mws.addHandler("/api/effects", HTTP_GET, []() { mws.webserver->send_P(200, "application/json", DisplayManager.getEffectNames().c_str()); }); @@ -103,8 +81,7 @@ void addHandler() mws.addHandler("/api/nextapp", HTTP_POST, []() { DisplayManager.nextApp(); mws.webserver->send(200,F("text/plain"),F("OK")); }); mws.addHandler("/screen", HTTP_GET, []() - { - mws.webserver->send(200, "text/html", screen_html); }); + { mws.webserver->send(200, "text/html", screen_html); }); mws.addHandler("/api/previousapp", HTTP_POST, []() { DisplayManager.previousApp(); mws.webserver->send(200,F("text/plain"),F("OK")); }); mws.addHandler("/api/timer", HTTP_POST, []() @@ -128,6 +105,8 @@ void addHandler() { mws.webserver->send_P(200, "application/json", DisplayManager.getAppsWithIcon().c_str()); }); mws.addHandler("/api/settings", HTTP_POST, []() { DisplayManager.setNewSettings(mws.webserver->arg("plain").c_str()); mws.webserver->send(200,F("text/plain"),F("OK")); }); + mws.addHandler("/api/erase", HTTP_POST, []() + { LittleFS.format(); delay(200); formatSettings(); mws.webserver->send(200,F("text/plain"),F("OK"));delay(200); ESP.restart(); }); mws.addHandler("/api/reorder", HTTP_POST, []() { DisplayManager.reorderApps(mws.webserver->arg("plain").c_str()); mws.webserver->send(200,F("text/plain"),F("OK")); }); mws.addHandler("/api/settings", HTTP_GET, []() @@ -185,7 +164,8 @@ void ServerManager_::setup() WiFi.setHostname(uniqueID); // define hostname myIP = mws.startWiFi(15000, uniqueID, "12345678"); isConnected = !(myIP == IPAddress(192, 168, 4, 1)); - DEBUG_PRINTF("My IP: %d.%d.%d.%d", myIP[0], myIP[1], myIP[2], myIP[3]); + if (DEBUG_MODE) + DEBUG_PRINTF("My IP: %d.%d.%d.%d", myIP[0], myIP[1], myIP[2], myIP[3]); if (isConnected) { mws.addOptionBox("Network"); @@ -212,15 +192,17 @@ void ServerManager_::setup() mws.addJavascript(custom_script); mws.addHandler("/save", HTTP_POST, saveHandler); addHandler(); - - DEBUG_PRINTLN(F("Webserver loaded")); + udp.begin(localUdpPort); + if (DEBUG_MODE) + DEBUG_PRINTLN(F("Webserver loaded")); } mws.addHandler("/version", HTTP_GET, versionHandler); mws.begin(); if (!MDNS.begin(uniqueID)) { - DEBUG_PRINTLN(F("Error starting mDNS")); + if (DEBUG_MODE) + DEBUG_PRINTLN(F("Error starting mDNS")); return; } @@ -232,6 +214,23 @@ void ServerManager_::setup() void ServerManager_::tick() { mws.run(); + int packetSize = udp.parsePacket(); + if (packetSize) + { + int len = udp.read(incomingPacket, 255); + if (len > 0) + { + incomingPacket[len] = 0; + } + if (DEBUG_MODE) + DEBUG_PRINTF("UDP-Paket: %s\n", incomingPacket); + if (strcmp(incomingPacket, "FIND_AWTRIX") == 0) + { + udp.beginPacket(udp.remoteIP(), udp.remotePort()); + udp.printf("%s|%s", uniqueID, WiFi.localIP().toString().c_str()); + udp.endPacket(); + } + } } uint16_t stringToColor(const String &str) @@ -300,10 +299,11 @@ void ServerManager_::loadSettings() NET_SDNS = doc["Secondary DNS"].as(); file.close(); DisplayManager.applyAllSettings(); - DEBUG_PRINTLN(F("Webserver configuration loaded")); + if (DEBUG_MODE) + DEBUG_PRINTLN(F("Webserver configuration loaded")); return; } - else + else if (DEBUG_MODE) DEBUG_PRINTLN(F("Webserver configuration file not exist")); return; } diff --git a/src/UpdateManager.cpp b/src/UpdateManager.cpp index 6667679c..6a3726de 100644 --- a/src/UpdateManager.cpp +++ b/src/UpdateManager.cpp @@ -68,15 +68,15 @@ void UpdateManager_::updateFirmware() switch (ret) { case HTTP_UPDATE_FAILED: - DEBUG_PRINTF("HTTP_UPDATE_FAILD Error (%d): %s\n", httpUpdate.getLastError(), httpUpdate.getLastErrorString().c_str()); + if (DEBUG_MODE) DEBUG_PRINTF("HTTP_UPDATE_FAILD Error (%d): %s\n", httpUpdate.getLastError(), httpUpdate.getLastErrorString().c_str()); break; case HTTP_UPDATE_NO_UPDATES: - DEBUG_PRINTLN(F("HTTP_UPDATE_NO_UPDATES")); + if (DEBUG_MODE) DEBUG_PRINTLN(F("HTTP_UPDATE_NO_UPDATES")); break; case HTTP_UPDATE_OK: - DEBUG_PRINTLN(F("HTTP_UPDATE_OK")); + if (DEBUG_MODE) DEBUG_PRINTLN(F("HTTP_UPDATE_OK")); break; } } @@ -96,7 +96,7 @@ bool UpdateManager_::checkUpdate(bool withScreen) fwurl += URL_fw_Version; fwurl += "?"; fwurl += String(rand()); - DEBUG_PRINTLN(F("Check firmwareversion")); + if (DEBUG_MODE) DEBUG_PRINTLN(F("Check firmwareversion")); static WiFiClientSecure client; // Statische Variable @@ -114,7 +114,7 @@ bool UpdateManager_::checkUpdate(bool withScreen) } else { - DEBUG_PRINTLN(F("Error in downloading version file")); + if (DEBUG_MODE) DEBUG_PRINTLN(F("Error in downloading version file")); if (withScreen) { DisplayManager.clear(); @@ -132,7 +132,7 @@ bool UpdateManager_::checkUpdate(bool withScreen) if (payload.equals(VERSION)) { UPDATE_AVAILABLE = false; - DEBUG_PRINTF("\nDevice already on latest firmware version: %s\n", VERSION); + if (DEBUG_MODE) DEBUG_PRINTF("\nDevice already on latest firmware version: %s\n", VERSION); if (withScreen) { DisplayManager.clear(); @@ -144,7 +144,7 @@ bool UpdateManager_::checkUpdate(bool withScreen) } else { - DEBUG_PRINTLN(F("New firmwareversion found!")); + if (DEBUG_MODE) DEBUG_PRINTLN(F("New firmwareversion found!")); UPDATE_AVAILABLE = true; return 1; } diff --git a/src/effects.cpp b/src/effects.cpp index 9306fbdb..8258f901 100644 --- a/src/effects.cpp +++ b/src/effects.cpp @@ -5,13 +5,11 @@ CRGB tempLeds[32][8]; void Pacifica(FastLED_NeoMatrix *matrix, int16_t x, int16_t y, EffectSettings *settings) { - const uint16_t cols = matrix->width(); - const uint16_t rows = matrix->height(); static uint32_t sPacificaTime = 0; sPacificaTime += settings->speed; - for (uint16_t i = 0; i < cols; i++) + for (uint16_t i = 0; i < 32; i++) { - for (uint16_t j = 0; j < rows; j++) + for (uint16_t j = 0; j < 8; j++) { uint16_t ulx, uly; @@ -33,8 +31,7 @@ void Pacifica(FastLED_NeoMatrix *matrix, int16_t x, int16_t y, EffectSettings *s void TheaterChase(FastLED_NeoMatrix *matrix, int16_t x, int16_t y, EffectSettings *settings) { - const uint16_t cols = matrix->width(); - const uint16_t rows = matrix->height(); + static uint16_t j = 0; static uint32_t lastUpdate = 0; // Hier verwenden wir settings->speed um die Geschwindigkeit der Animation zu steuern. @@ -45,13 +42,13 @@ void TheaterChase(FastLED_NeoMatrix *matrix, int16_t x, int16_t y, EffectSetting j += 1; } - for (uint16_t i = 0; i < cols; i++) + for (uint16_t i = 0; i < 32; i++) { - for (uint16_t k = 0; k < rows; k++) + for (uint16_t k = 0; k < 8; k++) { if ((i + j) % 3 == 0) { - uint8_t colorIndex = (i * 256 / cols) & 255; + uint8_t colorIndex = (i * 256 / 32) & 255; CRGB color = ColorFromPalette(settings->palette, colorIndex, 255, settings->blend ? LINEARBLEND : NOBLEND); matrix->drawPixel(x + i, y + k, color); } @@ -65,13 +62,11 @@ void TheaterChase(FastLED_NeoMatrix *matrix, int16_t x, int16_t y, EffectSetting void Plasma(FastLED_NeoMatrix *matrix, int16_t x, int16_t y, EffectSettings *settings) { - const uint16_t cols = matrix->width(); - const uint16_t rows = matrix->height(); static double time = 0; - for (uint16_t i = 0; i < cols; i++) + for (uint16_t i = 0; i < 32; i++) { - for (uint16_t j = 0; j < rows; j++) + for (uint16_t j = 0; j < 8; j++) { uint8_t value = sin8(i * 10 + time) + sin8(j * 10 + time / 2) + sin8((i + j) * 10 + time / 3) / 3; CRGB color = ColorFromPalette(settings->palette, value, 255, settings->blend ? LINEARBLEND : NOBLEND); @@ -89,8 +84,6 @@ struct MatrixColumn void Matrix(FastLED_NeoMatrix *matrix, int16_t x, int16_t y, EffectSettings *settings) { - const uint16_t cols = matrix->width(); - const uint16_t rows = matrix->height(); static uint32_t lastMove = 0; // Retrieve colors from the palette @@ -113,17 +106,17 @@ void Matrix(FastLED_NeoMatrix *matrix, int16_t x, int16_t y, EffectSettings *set { lastMove = millis(); - // Shift rows down and update the ledState array - for (uint16_t i = 0; i < cols; i++) + // Shift 8 down and update the ledState array + for (uint16_t i = 0; i < 32; i++) { - for (uint16_t j = rows - 1; j > 0; j--) + for (uint16_t j = 8 - 1; j > 0; j--) { ledState[i][j] = ledState[i][j - 1]; } } // Fade top row and spawn new pixels - for (uint16_t i = 0; i < cols; i++) + for (uint16_t i = 0; i < 32; i++) { if (ledState[i][0] == spawnColor) { @@ -143,9 +136,9 @@ void Matrix(FastLED_NeoMatrix *matrix, int16_t x, int16_t y, EffectSettings *set } // Always draw the current state - for (uint16_t i = 0; i < cols; i++) + for (uint16_t i = 0; i < 32; i++) { - for (uint16_t j = 0; j < rows; j++) + for (uint16_t j = 0; j < 8; j++) { matrix->drawPixel(x + i, y + j, ledState[i][j]); } @@ -232,14 +225,12 @@ Star stars[32][8]; // Create a buffer to store the state of the LEDs void TwinklingStars(FastLED_NeoMatrix *matrix, int16_t x, int16_t y, EffectSettings *settings) { - const uint16_t cols = matrix->width(); - const uint16_t rows = matrix->height(); static uint32_t lastUpdate = 0; // Fade all LEDs each frame - for (uint16_t i = 0; i < cols; i++) + for (uint16_t i = 0; i < 32; i++) { - for (uint16_t j = 0; j < rows; j++) + for (uint16_t j = 0; j < 8; j++) { stars[i][j].brightness -= 0.01; if (stars[i][j].brightness < 0) @@ -255,8 +246,8 @@ void TwinklingStars(FastLED_NeoMatrix *matrix, int16_t x, int16_t y, EffectSetti uint8_t numStars = random(1, 5); // Create between 1-5 new stars per frame for (uint8_t i = 0; i < numStars; i++) { - uint16_t starX = random(cols); - uint16_t starY = random(rows); + uint16_t starX = random(32); + uint16_t starY = random(8); // Star color - varying the saturation and value for shades of blue and white stars[starX][starY].color = ColorFromPalette(settings->palette, random8(), 255, settings->blend ? LINEARBLEND : NOBLEND); stars[starX][starY].brightness = 1.0; @@ -477,8 +468,6 @@ void updateDirection() void SnakeGame(FastLED_NeoMatrix *matrix, int16_t x, int16_t y, EffectSettings *settings) { - const uint16_t cols = matrix->width(); - const uint16_t rows = matrix->height(); if (isGameOver) { for (uint8_t i = 0; i < snakeLength; i++) @@ -544,11 +533,11 @@ void SnakeGame(FastLED_NeoMatrix *matrix, int16_t x, int16_t y, EffectSettings * // Check for wall collision and wrap around if (snake[0].x < 0) { - snake[0].x = cols - 1; + snake[0].x = 32 - 1; colorIndex = (colorIndex + 10) % 255; snake[0].colorIndex = colorIndex; // Update color index for head segment } - else if (snake[0].x >= cols) + else if (snake[0].x >= 32) { snake[0].x = 0; colorIndex = (colorIndex + 10) % 255; @@ -556,11 +545,11 @@ void SnakeGame(FastLED_NeoMatrix *matrix, int16_t x, int16_t y, EffectSettings * } else if (snake[0].y < 0) { - snake[0].y = rows - 1; + snake[0].y = 8 - 1; colorIndex = (colorIndex + 10) % 255; snake[0].colorIndex = colorIndex; // Update color index for head segment } - else if (snake[0].y >= rows) + else if (snake[0].y >= 8) { snake[0].y = 0; colorIndex = (colorIndex + 10) % 255; @@ -604,9 +593,6 @@ uint32_t fireworkInterval = 350; // milliseconds between new fireworks void Fireworks(FastLED_NeoMatrix *matrix, int16_t x, int16_t y, EffectSettings *settings) { - const uint16_t cols = matrix->width(); - const uint16_t rows = matrix->height(); - // Spawn new firework if enough time has passed if (millis() - lastFireworkTime >= 1000 / fireworkInterval && random(100) < 50) { // 50% chance to spawn firework @@ -614,13 +600,13 @@ void Fireworks(FastLED_NeoMatrix *matrix, int16_t x, int16_t y, EffectSettings * { if (fireworks[i].life == 0) { // find an unused firework - fireworks[i].x = random(cols); - fireworks[i].y = rows - 1; + fireworks[i].x = random(32); + fireworks[i].y = 8 - 1; fireworks[i].life = 255; fireworks[i].exploded = false; fireworks[i].color = CRGB::White; // The unexploded firework is white fireworks[i].peak = random(1, 5); // Set a random peak height for the firework - fireworks[i].speed = settings->speed * 0.3; // Set a speed for the firework + fireworks[i].speed = settings->speed * 0.5; // Set a speed for the firework lastFireworkTime = millis(); break; } @@ -648,11 +634,11 @@ void Fireworks(FastLED_NeoMatrix *matrix, int16_t x, int16_t y, EffectSettings * { if (fireworks[i].x > 0) matrix->drawPixel(x + fireworks[i].x - 1, y + fireworks[i].y, fireworks[i].color); - if (fireworks[i].x < cols - 1) + if (fireworks[i].x < 32 - 1) matrix->drawPixel(x + fireworks[i].x + 1, y + fireworks[i].y, fireworks[i].color); if (fireworks[i].y > 0) matrix->drawPixel(x + fireworks[i].x, y + fireworks[i].y - 1, fireworks[i].color); - if (fireworks[i].y < rows - 1) + if (fireworks[i].y < 8 - 1) matrix->drawPixel(x + fireworks[i].x, y + fireworks[i].y + 1, fireworks[i].color); } } @@ -824,27 +810,25 @@ struct Ball void PingPongEffect(FastLED_NeoMatrix *matrix, int16_t x, int16_t y, EffectSettings *settings) { - const uint16_t cols = matrix->width(); - const uint16_t rows = matrix->height(); - static Paddle paddle1 = {rows / 2, 1}; // Paddle on the left side - static Paddle paddle2 = {rows / 2, -1}; // Paddle on the right side - static Ball ball = {cols / 2, rows / 2, 1, 1}; // Ball in the middle of the matrix + static Paddle paddle1 = {8 / 2, 1}; // Paddle on the left side + static Paddle paddle2 = {8 / 2, -1}; // Paddle on the right side + static Ball ball = {32 / 2, 8 / 2, 1, 1}; // Ball in the middle of the matrix static uint32_t lastUpdate = 0; // Move the paddles - if (millis() - lastUpdate > settings->speed * 100) + if (millis() - lastUpdate > 150 - settings->speed * 10) { lastUpdate = millis(); paddle1.y += paddle1.dy; paddle2.y += paddle2.dy; // If a paddle has reached the top or bottom edge, change its direction - if (paddle1.y <= 0 || paddle1.y + PADDLE_HEIGHT >= rows) + if (paddle1.y <= 0 || paddle1.y + PADDLE_HEIGHT >= 8) { paddle1.dy = -paddle1.dy; } - if (paddle2.y <= 0 || paddle2.y + PADDLE_HEIGHT >= rows) + if (paddle2.y <= 0 || paddle2.y + PADDLE_HEIGHT >= 8) { paddle2.dy = -paddle2.dy; } @@ -854,17 +838,17 @@ void PingPongEffect(FastLED_NeoMatrix *matrix, int16_t x, int16_t y, EffectSetti ball.y += ball.dy; // If the ball has reached the top or bottom edge, change its direction - if (ball.y <= 0 || ball.y + BALL_SIZE >= rows) + if (ball.y <= 0 || ball.y + BALL_SIZE >= 8) { ball.dy = -ball.dy; } // If the ball has reached the left or right edge without hitting a paddle, restart the game if ((ball.x < 0 && (ball.y < paddle1.y || ball.y > paddle1.y + PADDLE_HEIGHT)) || - (ball.x + BALL_SIZE > cols && (ball.y < paddle2.y || ball.y > paddle2.y + PADDLE_HEIGHT))) + (ball.x + BALL_SIZE > 32 && (ball.y < paddle2.y || ball.y > paddle2.y + PADDLE_HEIGHT))) { - ball.x = cols / 2; // Reset ball position - ball.y = rows / 2; + ball.x = 32 / 2; // Reset ball position + ball.y = 8 / 2; ball.dx = 1; // Reset ball direction ball.dy = 1; } @@ -874,7 +858,7 @@ void PingPongEffect(FastLED_NeoMatrix *matrix, int16_t x, int16_t y, EffectSetti { ball.dx = -ball.dx; } - if (ball.x + BALL_SIZE >= cols && ball.y >= paddle2.y && ball.y < paddle2.y + PADDLE_HEIGHT) + if (ball.x + BALL_SIZE >= 32 && ball.y >= paddle2.y && ball.y < paddle2.y + PADDLE_HEIGHT) { ball.dx = -ball.dx; } @@ -883,7 +867,7 @@ void PingPongEffect(FastLED_NeoMatrix *matrix, int16_t x, int16_t y, EffectSetti for (int i = 0; i < PADDLE_HEIGHT; i++) { matrix->drawPixel(x, y + paddle1.y + i, matrix->Color(255, 255, 255)); - matrix->drawPixel(x + cols - 1, y + paddle2.y + i, matrix->Color(255, 255, 255)); + matrix->drawPixel(x + 32 - 1, y + paddle2.y + i, matrix->Color(255, 255, 255)); } for (int i = 0; i < BALL_SIZE; i++) { @@ -894,8 +878,8 @@ void PingPongEffect(FastLED_NeoMatrix *matrix, int16_t x, int16_t y, EffectSetti // ######## Bricks ############ #define PADDLE_WIDTH 3 // Width of the paddle #define BALL_SIZE 1 // Size of the ball -#define BRICK_COLS 16 // Number of bricks in a row -#define BRICK_ROWS 3 // Number of brick rows +#define BRICK_32 16 // Number of bricks in a row +#define BRICK_8 3 // Number of brick 8 struct BricksPaddle { @@ -909,13 +893,13 @@ struct BricksBall int dx, dy; // Speed of the ball }; -bool bricks[BRICK_ROWS][BRICK_COLS]; // State of the bricks +bool bricks[BRICK_8][BRICK_32]; // State of the bricks void resetBricks() { - for (int i = 0; i < BRICK_ROWS; i++) + for (int i = 0; i < BRICK_8; i++) { - for (int j = 0; j < BRICK_COLS; j++) + for (int j = 0; j < BRICK_32; j++) { bricks[i][j] = true; } @@ -924,9 +908,9 @@ void resetBricks() bool bricksRemain() { - for (int i = 0; i < BRICK_ROWS; i++) + for (int i = 0; i < BRICK_8; i++) { - for (int j = 0; j < BRICK_COLS; j++) + for (int j = 0; j < BRICK_32; j++) { if (bricks[i][j]) { @@ -939,19 +923,16 @@ bool bricksRemain() void BrickBreakerEffect(FastLED_NeoMatrix *matrix, int16_t x, int16_t y, EffectSettings *settings) { - const uint16_t cols = matrix->width(); - const uint16_t rows = matrix->height(); - - static BricksPaddle paddle = {cols / 2, 1}; // Paddle in the middle of the matrix - static BricksBall ball = {cols / 2, rows / 2, 1, 1}; // Ball in the middle of the matrix + static BricksPaddle paddle = {32 / 2, 1}; // Paddle in the middle of the matrix + static BricksBall ball = {32 / 2, 8 / 2, 1, 1}; // Ball in the middle of the matrix // Initialize the bricks static bool firstTime = true; if (firstTime) { - for (int i = 0; i < BRICK_ROWS; i++) + for (int i = 0; i < BRICK_8; i++) { - for (int j = 0; j < BRICK_COLS; j++) + for (int j = 0; j < BRICK_32; j++) { bricks[i][j] = true; // All bricks exist at the beginning } @@ -960,7 +941,7 @@ void BrickBreakerEffect(FastLED_NeoMatrix *matrix, int16_t x, int16_t y, EffectS } static uint32_t lastUpdate = 0; - if (millis() - lastUpdate > 100) + if (millis() - lastUpdate > 150 - settings->speed * 10) { lastUpdate = millis(); @@ -972,26 +953,26 @@ void BrickBreakerEffect(FastLED_NeoMatrix *matrix, int16_t x, int16_t y, EffectS paddle.x = ball.x - 1; // If the paddle has reached the left or right edge, change its direction - if (paddle.x <= 0 || paddle.x + PADDLE_WIDTH >= cols) + if (paddle.x <= 0 || paddle.x + PADDLE_WIDTH >= 32) { paddle.dx = -paddle.dx; } // If the ball has reached the top, bottom, left, or right edge, change its direction - if (ball.x <= 0 || ball.x + BALL_SIZE >= cols) + if (ball.x <= 0 || ball.x + BALL_SIZE >= 32) { ball.dx = -ball.dx; } - if (ball.y <= 0 || ball.y + BALL_SIZE >= rows) + if (ball.y <= 0 || ball.y + BALL_SIZE >= 8) { ball.dy = -ball.dy; } - for (int i = 0; i < BRICK_ROWS; i++) + for (int i = 0; i < BRICK_8; i++) { - for (int j = 0; j < BRICK_COLS; j++) + for (int j = 0; j < BRICK_32; j++) { - if (bricks[i][j] && ball.y == i && ball.x >= j * (cols / BRICK_COLS) && ball.x < (j + 1) * (cols / BRICK_COLS)) + if (bricks[i][j] && ball.y == i && ball.x >= j * (32 / BRICK_32) && ball.x < (j + 1) * (32 / BRICK_32)) { bricks[i][j] = false; ball.dy = -ball.dy; @@ -1008,19 +989,19 @@ void BrickBreakerEffect(FastLED_NeoMatrix *matrix, int16_t x, int16_t y, EffectS // Draw the paddle, the ball, and the bricks for (int i = 0; i < PADDLE_WIDTH; i++) { - matrix->drawPixel(x + paddle.x + i, y + rows - 1, matrix->Color(255, 255, 255)); + matrix->drawPixel(x + paddle.x + i, y + 8 - 1, matrix->Color(255, 255, 255)); } for (int i = 0; i < BALL_SIZE; i++) { matrix->drawPixel(x + ball.x, y + ball.y + i, matrix->Color(255, 0, 0)); } - for (int i = 0; i < BRICK_ROWS; i++) + for (int i = 0; i < BRICK_8; i++) { - for (int j = 0; j < BRICK_COLS; j++) + for (int j = 0; j < BRICK_32; j++) { if (bricks[i][j]) { - matrix->drawPixel(x + j * (cols / BRICK_COLS), y + i, matrix->Color(0, 0, 255)); + matrix->drawPixel(x + j * (32 / BRICK_32), y + i, matrix->Color(0, 0, 255)); } } } @@ -1058,10 +1039,10 @@ void MovingLine(FastLED_NeoMatrix *matrix, int16_t x, int16_t y, EffectSettings void Fade(FastLED_NeoMatrix *matrix, int16_t x, int16_t y, EffectSettings *settings) { - static uint8_t hue = 0; // Hue for the color of the rows - // Change the color of the rows based on speed + static uint8_t hue = 0; // Hue for the color of the 8 + // Change the color of the 8 based on speed hue += settings->speed; - // Draw the rows with fading colors based on the palette + // Draw the 8 with fading colors based on the palette for (int16_t i = 8 - 1; i >= 0; i--) { @@ -1080,19 +1061,19 @@ Effect effects[] = { {"PingPong", PingPongEffect, EffectSettings(8, RainbowColors_p, true)}, {"Radar", RadarEffect, EffectSettings(1, RainbowColors_p, true)}, {"Checkerboard", CheckerboardEffect, EffectSettings(1, RainbowColors_p, true)}, - {"Fireworks", Fireworks, EffectSettings(0.8, RainbowColors_p)}, + {"Fireworks", Fireworks, EffectSettings(0, RainbowColors_p)}, {"PlasmaCloud", PlasmaCloudEffect, EffectSettings(3, RainbowColors_p, true)}, {"Ripple", RippleEffect, EffectSettings(3, RainbowColors_p, true)}, {"Snake", SnakeGame, EffectSettings(3, RainbowColors_p, true)}, {"Pacifica", Pacifica, EffectSettings(3, OceanColors_p, true)}, - {"TheaterChase", TheaterChase, EffectSettings(3, RainbowColors_p,true)}, + {"TheaterChase", TheaterChase, EffectSettings(3, RainbowColors_p, true)}, {"Plasma", Plasma, EffectSettings(2, RainbowColors_p, true)}, {"Matrix", Matrix, EffectSettings(8, ForestColors_p, false)}, - {"SwirlIn", SwirlIn, EffectSettings(4, RainbowColors_p, true)}, - {"SwirlOut", SwirlOut, EffectSettings(4, RainbowColors_p, true)}, + {"SwirlIn", SwirlIn, EffectSettings(5, RainbowColors_p, true)}, + {"SwirlOut", SwirlOut, EffectSettings(5, RainbowColors_p, true)}, {"LookingEyes", LookingEyes, EffectSettings()}, {"TwinklingStars", TwinklingStars, EffectSettings(4, OceanColors_p, true)}, - {"ColorWaves", ColorWaves, EffectSettings(3, RainbowColors_p, true)}}; + {"ColorWaves", ColorWaves, EffectSettings(5, RainbowColors_p, true)}}; // ######## Helper functions ############ @@ -1144,48 +1125,63 @@ CRGBPalette16 loadPaletteFromLittleFS(String paletteName) return palette; } -// Returns a palette based on the given name -CRGBPalette16 getPalette(String palette) +CRGBPalette16 getPalette(const JsonVariant &paletteVariant) { - if (palette == "Cloud") - { - return CloudColors_p; - } - else if (palette == "Lava") - { - return LavaColors_p; - } - else if (palette == "Ocean") + if (paletteVariant.is()) { - return OceanColors_p; - } - else if (palette == "Forest") - { - return ForestColors_p; - } - else if (palette == "Stripe") - { - return RainbowStripeColors_p; - } - else if (palette == "Party") - { - return PartyColors_p; - } - else if (palette == "Heat") - { - return HeatColors_p; + String palette = paletteVariant.as(); + if (palette == "Cloud") + { + return CloudColors_p; + } + else if (palette == "Lava") + { + return LavaColors_p; + } + else if (palette == "Ocean") + { + return OceanColors_p; + } + else if (palette == "Forest") + { + return ForestColors_p; + } + else if (palette == "Stripe") + { + return RainbowStripeColors_p; + } + else if (palette == "Party") + { + return PartyColors_p; + } + else if (palette == "Heat") + { + return HeatColors_p; + } + else + { + return loadPaletteFromLittleFS(palette); + } } - else + else if (paletteVariant.is()) { - return loadPaletteFromLittleFS(palette); + CRGBPalette16 palette; + JsonArray array = paletteVariant.as(); + for (int i = 0; i < 16 && i < array.size(); i++) + { + uint32_t colorValue = (uint32_t)strtol(array[i].as().c_str(), NULL, 16); + palette[i] = CRGB(colorValue); + } + return palette; } + return RainbowColors_p; // Default fallback } void updateEffectSettings(u_int8_t index, String json) { if (index != -1) { - StaticJsonDocument<200> doc; + StaticJsonDocument<2048> doc; deserializeJson(doc, json); if (doc.containsKey("speed")) @@ -1195,7 +1191,7 @@ void updateEffectSettings(u_int8_t index, String json) if (doc.containsKey("palette")) { - effects[index].settings.palette = getPalette(doc["palette"].as()); + effects[index].settings.palette = getPalette(doc["palette"]); } if (doc.containsKey("blend"))