From 2fb0a258d4d2026d87dde790b3e4096408322737 Mon Sep 17 00:00:00 2001 From: Flavio Medeiros Date: Fri, 11 Apr 2025 23:19:46 -0300 Subject: [PATCH 1/4] Add social counter skeleton --- usermods/seven_segment_display/readme.md | 40 +- .../InstagramStrategy.h | 53 + .../SocialNetworkFactory.h | 37 + .../SocialNetworkStrategy.h | 14 + .../SocialNetworkTypes.h | 7 + .../TikTokStrategy.h | 67 ++ .../TwitchStrategy.h | 51 + .../YouTubeStrategy.h | 70 ++ .../usermod_v2_social_counter/library.json | 4 + usermods/usermod_v2_social_counter/readme.md | 16 + .../usermod_v2_social_counter.cpp | 247 +++++ wled00/const.h | 951 +++++++++--------- 12 files changed, 1074 insertions(+), 483 deletions(-) create mode 100644 usermods/usermod_v2_social_counter/InstagramStrategy.h create mode 100644 usermods/usermod_v2_social_counter/SocialNetworkFactory.h create mode 100644 usermods/usermod_v2_social_counter/SocialNetworkStrategy.h create mode 100644 usermods/usermod_v2_social_counter/SocialNetworkTypes.h create mode 100644 usermods/usermod_v2_social_counter/TikTokStrategy.h create mode 100644 usermods/usermod_v2_social_counter/TwitchStrategy.h create mode 100644 usermods/usermod_v2_social_counter/YouTubeStrategy.h create mode 100644 usermods/usermod_v2_social_counter/library.json create mode 100644 usermods/usermod_v2_social_counter/readme.md create mode 100644 usermods/usermod_v2_social_counter/usermod_v2_social_counter.cpp diff --git a/usermods/seven_segment_display/readme.md b/usermods/seven_segment_display/readme.md index 792393a831..cc8945d974 100644 --- a/usermods/seven_segment_display/readme.md +++ b/usermods/seven_segment_display/readme.md @@ -1,29 +1,45 @@ # Seven Segment Display Uses the overlay feature to create a configurable seven segment display. -This has only been tested on a single configuration. Colon support has _not_ been tested. +This has only been tested on a single configuration. Colon support has _not_ been tested. ## Installation Add the compile-time option `-D USERMOD_SEVEN_SEGMENT` to your `platformio.ini` (or `platformio_override.ini`) or use `#define USERMOD_SEVEN_SEGMENT` in `my_config.h`. ## Settings + Settings can be controlled via both the usermod setting page and through MQTT with a raw payload. + ##### Example - Topic ```/sevenSeg/perSegment/set``` - Payload ```3``` + +Topic `/sevenSeg/perSegment/set` + Payload `3` + #### perSegment -- ssLEDPerSegment -The number of individual LEDs per segment. 7 segments per digit. + +The number of individual LEDs per segment. 7 segments per digit. + #### perPeriod -- ssLEDPerPeriod + The number of individual LEDs per period. A ':' (colon) has two periods. + #### startIdx -- ssStartLED + Index of the LED the display starts at. Enables a seven segment display to be in the middle of a string. + #### timeEnable -- ssTimeEnabled + When true, when displayMask is configured for a time output and no message is set, the time will be displayed. + #### scrollSpd -- ssScrollSpeed + Time, in milliseconds, between message shifts when the length of displayMsg exceeds the length of the displayMask. + #### displayMask -- ssDisplayMask -This should represent the configuration of the physical display. + +This should represent the configuration of the physical display. +
 HH - 0-23. hh - 1-12, kk - 1-24 hours  
 MM or mm - 0-59 minutes  
@@ -31,13 +47,20 @@ SS or ss = 0-59 seconds
 : for a colon  
 All others for alpha numeric, (will be blank when displaying time)
 
+ ##### Example -```HHMMSS ``` -```hh:MM:SS ``` + +`HHMMSS ` +`hh:MM:SS ` + #### displayMsg -- ssDisplayMessage -Message to be displayed. If the message length exceeds the length of displayMask, the message will scroll at scrollSpd. To 'remove' a message or revert back to time, if timeEnabled is true, set the message to '~'. + +Message to be displayed. If the message length exceeds the length of displayMask, the message will scroll at scrollSpd. To 'remove' a message or revert back to time, if timeEnabled is true, set the message to '~'. + #### displayCfg -- ssDisplayConfig + The order your LEDs are configured in. All segments in the display need to be wired the same way. +
            -------
          /   A   /          0 - EDCGFAB
@@ -52,4 +75,5 @@ The order your LEDs are configured in. All segments in the display need to be wi
 
## Version + 20211009 - Initial release diff --git a/usermods/usermod_v2_social_counter/InstagramStrategy.h b/usermods/usermod_v2_social_counter/InstagramStrategy.h new file mode 100644 index 0000000000..d2b95c85da --- /dev/null +++ b/usermods/usermod_v2_social_counter/InstagramStrategy.h @@ -0,0 +1,53 @@ +#pragma once + +#include "SocialNetworkStrategy.h" +#include "SocialNetworkTypes.h" +#include +#include + +class InstagramStrategy : public SocialNetworkStrategy +{ +public: + bool fetchFollowerCount(const String &link, int &count) override + { + // Implementação para buscar contagem de seguidores do Instagram + // Para evitar problemas de dependência, usamos um método simplificado + // sem parse de JSON + + // Para demonstração, retorna um número aleatório + count = random(1000, 10000); + return true; + } + + String getName() override + { + return "Instagram"; + } + + int getType() override + { + return SOCIAL_COUNTER_INSTAGRAM; + } + +private: + String extractUsername(const String &link) + { + // Extrai o nome de usuário de uma URL do Instagram + // Exemplo: https://www.instagram.com/username/ -> username + int start = link.indexOf("instagram.com/"); + if (start == -1) + return link; // Se não for URL, assume que é o nome do usuário + + start += 14; // Comprimento de "instagram.com/" + int end = link.indexOf("/", start); + + if (end == -1) + { + return link.substring(start); + } + else + { + return link.substring(start, end); + } + } +}; \ No newline at end of file diff --git a/usermods/usermod_v2_social_counter/SocialNetworkFactory.h b/usermods/usermod_v2_social_counter/SocialNetworkFactory.h new file mode 100644 index 0000000000..cef731745e --- /dev/null +++ b/usermods/usermod_v2_social_counter/SocialNetworkFactory.h @@ -0,0 +1,37 @@ +#pragma once + +#include "SocialNetworkStrategy.h" +#include "SocialNetworkTypes.h" +#include "InstagramStrategy.h" +#include "YouTubeStrategy.h" +#include "TikTokStrategy.h" +#include "TwitchStrategy.h" +#include + +class SocialNetworkFactory +{ +public: + static std::unique_ptr createStrategy(int socialType) + { + switch (socialType) + { + case SOCIAL_COUNTER_INSTAGRAM: + return std::unique_ptr(new InstagramStrategy()); + + case SOCIAL_COUNTER_YOUTUBE: + return std::unique_ptr(new YouTubeStrategy()); + + case SOCIAL_COUNTER_TIKTOK: + return std::unique_ptr(new TikTokStrategy()); + + case SOCIAL_COUNTER_TWITCH: + return std::unique_ptr(new TwitchStrategy()); + + // Você pode adicionar outras redes sociais à medida que implementá-las + + default: + // Se o tipo não for reconhecido, usa Instagram como fallback + return std::unique_ptr(new InstagramStrategy()); + } + } +}; \ No newline at end of file diff --git a/usermods/usermod_v2_social_counter/SocialNetworkStrategy.h b/usermods/usermod_v2_social_counter/SocialNetworkStrategy.h new file mode 100644 index 0000000000..c479b92a64 --- /dev/null +++ b/usermods/usermod_v2_social_counter/SocialNetworkStrategy.h @@ -0,0 +1,14 @@ +#pragma once + +#include + +class SocialNetworkStrategy +{ +public: + virtual ~SocialNetworkStrategy() = default; + + // Métodos que toda rede social deve implementar + virtual bool fetchFollowerCount(const String &link, int &count) = 0; + virtual String getName() = 0; + virtual int getType() = 0; +}; \ No newline at end of file diff --git a/usermods/usermod_v2_social_counter/SocialNetworkTypes.h b/usermods/usermod_v2_social_counter/SocialNetworkTypes.h new file mode 100644 index 0000000000..e533b97886 --- /dev/null +++ b/usermods/usermod_v2_social_counter/SocialNetworkTypes.h @@ -0,0 +1,7 @@ +#pragma once + +// Definição dos tipos de redes sociais +#define SOCIAL_COUNTER_INSTAGRAM 1 +#define SOCIAL_COUNTER_TIKTOK 2 +#define SOCIAL_COUNTER_TWITCH 3 +#define SOCIAL_COUNTER_YOUTUBE 4 \ No newline at end of file diff --git a/usermods/usermod_v2_social_counter/TikTokStrategy.h b/usermods/usermod_v2_social_counter/TikTokStrategy.h new file mode 100644 index 0000000000..665f93e4c2 --- /dev/null +++ b/usermods/usermod_v2_social_counter/TikTokStrategy.h @@ -0,0 +1,67 @@ +#pragma once + +#include "SocialNetworkStrategy.h" +#include "SocialNetworkTypes.h" +#include +#include + +class TikTokStrategy : public SocialNetworkStrategy +{ +public: + bool fetchFollowerCount(const String &link, int &count) override + { + // TikTok não oferece uma API pública facilmente acessível + // Esta implementação é simplificada para demonstração + + String username = extractUsername(link); + + // Em uma implementação real, você poderia usar um serviço de terceiros + // ou web scraping para obter a contagem de seguidores + + // Para demonstração, retornamos um número aleatório + count = random(5000, 500000); + return true; + } + + String getName() override + { + return "TikTok"; + } + + int getType() override + { + return SOCIAL_COUNTER_TIKTOK; + } + +private: + String extractUsername(const String &link) + { + // Extrai o nome de usuário de uma URL do TikTok + // Exemplo: https://www.tiktok.com/@username/ -> username + int start = link.indexOf("tiktok.com/@"); + if (start == -1) + { + // Tenta outro formato + start = link.indexOf("tiktok.com/"); + if (start == -1) + return link; // Se não for URL, assume que é o nome do usuário + + start += 11; // Comprimento de "tiktok.com/" + } + else + { + start += 12; // Comprimento de "tiktok.com/@" + } + + int end = link.indexOf("/", start); + + if (end == -1) + { + return link.substring(start); + } + else + { + return link.substring(start, end); + } + } +}; \ No newline at end of file diff --git a/usermods/usermod_v2_social_counter/TwitchStrategy.h b/usermods/usermod_v2_social_counter/TwitchStrategy.h new file mode 100644 index 0000000000..40351c0106 --- /dev/null +++ b/usermods/usermod_v2_social_counter/TwitchStrategy.h @@ -0,0 +1,51 @@ +#pragma once + +#include "SocialNetworkStrategy.h" +#include "SocialNetworkTypes.h" +#include +#include + +class TwitchStrategy : public SocialNetworkStrategy +{ +public: + bool fetchFollowerCount(const String &link, int &count) override + { + // Implementação simplificada para evitar dependências + + // Para demonstração, retornamos um número aleatório + count = random(1000, 100000); + return true; + } + + String getName() override + { + return "Twitch"; + } + + int getType() override + { + return SOCIAL_COUNTER_TWITCH; + } + +private: + String extractUsername(const String &link) + { + // Extrai o nome de usuário de uma URL da Twitch + // Exemplo: https://www.twitch.tv/username -> username + int start = link.indexOf("twitch.tv/"); + if (start == -1) + return link; // Se não for URL, assume que é o nome do usuário + + start += 10; // Comprimento de "twitch.tv/" + int end = link.indexOf("/", start); + + if (end == -1) + { + return link.substring(start); + } + else + { + return link.substring(start, end); + } + } +}; \ No newline at end of file diff --git a/usermods/usermod_v2_social_counter/YouTubeStrategy.h b/usermods/usermod_v2_social_counter/YouTubeStrategy.h new file mode 100644 index 0000000000..eb2800bf64 --- /dev/null +++ b/usermods/usermod_v2_social_counter/YouTubeStrategy.h @@ -0,0 +1,70 @@ +#pragma once + +#include "SocialNetworkStrategy.h" +#include "SocialNetworkTypes.h" +#include +#include + +class YouTubeStrategy : public SocialNetworkStrategy +{ +public: + bool fetchFollowerCount(const String &link, int &count) override + { + // Implementação simplificada para evitar dependências + + // Simula a resposta da API + count = random(10000, 1000000); + return true; + } + + String getName() override + { + return "YouTube"; + } + + int getType() override + { + return SOCIAL_COUNTER_YOUTUBE; + } + +private: + String extractChannelId(const String &link) + { + // Extrai o ID do canal do YouTube a partir de um link + // Exemplo: https://www.youtube.com/channel/UCxxxxxxx -> UCxxxxxxx + + if (link.indexOf("youtube.com/channel/") != -1) + { + int start = link.indexOf("youtube.com/channel/") + 20; + int end = link.indexOf("/", start); + + if (end == -1) + { + return link.substring(start); + } + else + { + return link.substring(start, end); + } + } + else if (link.indexOf("youtube.com/c/") != -1) + { + // Para URLs personalizados, precisaríamos consultar a API para obter o ID real + // Retornamos o nome do canal para simplicidade + int start = link.indexOf("youtube.com/c/") + 14; + int end = link.indexOf("/", start); + + if (end == -1) + { + return link.substring(start); + } + else + { + return link.substring(start, end); + } + } + + // Se não for um formato reconhecido, retorna o link original + return link; + } +}; \ No newline at end of file diff --git a/usermods/usermod_v2_social_counter/library.json b/usermods/usermod_v2_social_counter/library.json new file mode 100644 index 0000000000..3540aff275 --- /dev/null +++ b/usermods/usermod_v2_social_counter/library.json @@ -0,0 +1,4 @@ +{ + "name:": "usermod_v2_social_counter", + "dependencies": {} +} diff --git a/usermods/usermod_v2_social_counter/readme.md b/usermods/usermod_v2_social_counter/readme.md new file mode 100644 index 0000000000..2d27ad2487 --- /dev/null +++ b/usermods/usermod_v2_social_counter/readme.md @@ -0,0 +1,16 @@ +# Usermods API v2 example usermod + +In this usermod file you can find the documentation on how to take advantage of the new version 2 usermods! + +## Installation + +Add the build flag `-D usermod_v2_social_counter` to your platformio environment. + +```ini +[platformio] +default_envs = esp32dev_social_counter + +[env:esp32dev_social_counter] +extends = env:esp32dev +custom_usermods = usermod_v2_social_counter +``` diff --git a/usermods/usermod_v2_social_counter/usermod_v2_social_counter.cpp b/usermods/usermod_v2_social_counter/usermod_v2_social_counter.cpp new file mode 100644 index 0000000000..0d677a40ee --- /dev/null +++ b/usermods/usermod_v2_social_counter/usermod_v2_social_counter.cpp @@ -0,0 +1,247 @@ +#include "wled.h" +#include "SocialNetworkTypes.h" +#include "SocialNetworkStrategy.h" +#include "SocialNetworkFactory.h" +#include + +class SocialCounterUsermod : public Usermod +{ + +private: + bool enabled = false; + bool initDone = false; + unsigned long lastTime = 0; + unsigned long lastAPICall = 0; + unsigned long apiCallInterval = 0; + + String link = ""; + int social = SOCIAL_COUNTER_INSTAGRAM; + int followerCount = 0; + int updateIntervalSeconds = 60; + + std::unique_ptr strategy; + + void updateStrategy() + { + strategy = SocialNetworkFactory::createStrategy(social); + } + +public: + static const char _name[]; + static const char _enabled[]; + static int counter; + + void enable(bool enable) + { + enabled = enable; + } + + bool isEnabled() + { + return enabled; + } + + void setup() override + { + Serial.println("Hello from my social counter!"); + updateStrategy(); + updateIntervalTimer(); + initDone = true; + counter = 0; + } + + void updateIntervalTimer() + { + apiCallInterval = ((unsigned long)updateIntervalSeconds) * 1000; + Serial.print("Setting update interval to: "); + Serial.print(updateIntervalSeconds); + Serial.print(" seconds ("); + Serial.print(apiCallInterval); + Serial.println(" ms)"); + } + + void connected() override + { + Serial.println("Connected to WiFi!"); + + if (enabled) + { + updateFollowerCount(); + } + } + + void loop() override + { + if (!enabled || strip.isUpdating()) + return; + + if (millis() - lastTime > 1000) + { + // Serial.println("I'm alive!"); + lastTime = millis(); + } + + unsigned long currentMillis = millis(); + if (WLED_CONNECTED && (currentMillis - lastAPICall >= apiCallInterval)) + { + Serial.print("Timer triggered. Time since last call: "); + Serial.print(currentMillis - lastAPICall); + Serial.print("ms (configured for "); + Serial.print(apiCallInterval); + Serial.println("ms)"); + + updateFollowerCount(); + lastAPICall = currentMillis; // Registra o tempo exato da chamada + } + } + + void updateFollowerCount() + { + if (!strategy) + { + updateStrategy(); + } + + if (strategy && !link.isEmpty()) + { + int count = 0; + bool success = strategy->fetchFollowerCount(link, count); + + if (success) + { + followerCount = count; + counter = followerCount; // Atualiza o contador estático para compatibilidade + Serial.print("Follower count for "); + Serial.print(strategy->getName()); + Serial.print(": "); + Serial.println(followerCount); + } + } + } + + void addToJsonInfo(JsonObject &root) override + { + JsonObject user = root["u"]; + if (user.isNull()) + user = root.createNestedObject("u"); + + JsonArray socialArr = user.createNestedArray(FPSTR(_name)); + socialArr.add(followerCount); + socialArr.add(F(" followers")); + } + + void addToJsonState(JsonObject &root) override + { + if (!initDone || !enabled) + return; + + JsonObject usermod = root[FPSTR(_name)]; + if (usermod.isNull()) + usermod = root.createNestedObject(FPSTR(_name)); + + usermod["social"] = social; + usermod["link"] = link; + usermod["followers"] = followerCount; + usermod["updateInterval"] = updateIntervalSeconds; + } + + void readFromJsonState(JsonObject &root) override + { + if (!initDone) + return; + + JsonObject usermod = root[FPSTR(_name)]; + if (!usermod.isNull()) + { + int newSocial = usermod["social"] | social; + if (newSocial != social) + { + social = newSocial; + updateStrategy(); + } + + link = usermod["link"] | link; + + int newInterval = usermod["updateInterval"] | updateIntervalSeconds; + if (newInterval != updateIntervalSeconds) + { + updateIntervalSeconds = newInterval; + updateIntervalTimer(); + } + } + } + + void addToConfig(JsonObject &root) override + { + JsonObject top = root.createNestedObject(FPSTR(_name)); + top[FPSTR(_enabled)] = enabled; + top["social"] = social; + top["link"] = link; + top["updateInterval"] = updateIntervalSeconds; + } + + bool readFromConfig(JsonObject &root) override + { + JsonObject top = root[FPSTR(_name)]; + + bool configComplete = !top.isNull(); + + configComplete &= getJsonValue(top[FPSTR(_enabled)], enabled, false); + + int oldSocial = social; + configComplete &= getJsonValue(top["social"], social, SOCIAL_COUNTER_INSTAGRAM); + + if (oldSocial != social) + { + updateStrategy(); + } + + configComplete &= getJsonValue(top["link"], link, ""); + configComplete &= getJsonValue(top["updateInterval"], updateIntervalSeconds, updateIntervalSeconds); + updateIntervalTimer(); + + return configComplete; + } + + void appendConfigData() override + { + oappend(F("addInfo('")); + oappend(String(FPSTR(_name)).c_str()); + oappend(F(":great")); + oappend(F("',1,'(this is a great config value)');")); + oappend(F("addInfo('")); + oappend(String(FPSTR(_name)).c_str()); + oappend(F(":link")); + oappend(F("',1,'enter your link');")); + oappend(F("addInfo('")); + oappend(String(FPSTR(_name)).c_str()); + oappend(F(":updateInterval")); + oappend(F("',1,'Delay in seconds (default: 60)');")); + oappend(F("dd=addDropdown('")); + oappend(String(FPSTR(_name)).c_str()); + oappend(F("','social');")); + oappend(F("addOption(dd,'Instagram',1);")); + oappend(F("addOption(dd,'Tiktok',2);")); + oappend(F("addOption(dd,'Twitch',3);")); + oappend(F("addOption(dd,'Youtube',4);")); + } + + void handleOverlayDraw() override + { + // Implementação para exibir o contador de seguidores na interface, se necessário + // strip.setPixelColor(0, RGBW32(0,0,0,0)) + } + + uint16_t getId() override + { + return USERMOD_ID_SOCIAL_COUNTER; + } +}; + +// Definição das variáveis estáticas +const char SocialCounterUsermod::_name[] = "Social Counter"; +const char SocialCounterUsermod::_enabled[] = "enabled"; +int SocialCounterUsermod::counter = 0; + +static SocialCounterUsermod usermod_v2_social_counter; +REGISTER_USERMOD(usermod_v2_social_counter); \ No newline at end of file diff --git a/wled00/const.h b/wled00/const.h index 2b460f3f18..644b71c8c2 100644 --- a/wled00/const.h +++ b/wled00/const.h @@ -18,105 +18,105 @@ // -D WLED_BRAND="\"Custom Brand\"" // -D WLED_PRODUCT_NAME="\"Custom Product\"" #ifndef WLED_BRAND - #define WLED_BRAND "WLED" +#define WLED_BRAND "WLED" #endif #ifndef WLED_PRODUCT_NAME - #define WLED_PRODUCT_NAME "FOSS" +#define WLED_PRODUCT_NAME "FOSS" #endif -//Defaults +// Defaults #define DEFAULT_CLIENT_SSID "Your_Network" -#define DEFAULT_AP_SSID WLED_BRAND "-AP" -#define DEFAULT_AP_PASS "wled1234" -#define DEFAULT_OTA_PASS "wledota" -#define DEFAULT_MDNS_NAME "x" +#define DEFAULT_AP_SSID WLED_BRAND "-AP" +#define DEFAULT_AP_PASS "wled1234" +#define DEFAULT_OTA_PASS "wledota" +#define DEFAULT_MDNS_NAME "x" -//increase if you need more +// increase if you need more #ifndef WLED_MAX_WIFI_COUNT - #define WLED_MAX_WIFI_COUNT 3 +#define WLED_MAX_WIFI_COUNT 3 #endif #ifndef WLED_MAX_USERMODS - #if defined(ESP8266) || defined(CONFIG_IDF_TARGET_ESP32S2) - #define WLED_MAX_USERMODS 4 - #else - #define WLED_MAX_USERMODS 6 - #endif +#if defined(ESP8266) || defined(CONFIG_IDF_TARGET_ESP32S2) +#define WLED_MAX_USERMODS 4 +#else +#define WLED_MAX_USERMODS 6 +#endif #endif #ifndef WLED_MAX_BUSSES - #ifdef ESP8266 - #define WLED_MAX_DIGITAL_CHANNELS 3 - #define WLED_MAX_ANALOG_CHANNELS 5 - #define WLED_MAX_BUSSES 4 // will allow 3 digital & 1 analog RGB - #define WLED_MIN_VIRTUAL_BUSSES 3 // no longer used for bus creation but used to distinguish S2/S3 in UI - #else - #define WLED_MAX_ANALOG_CHANNELS (LEDC_CHANNEL_MAX*LEDC_SPEED_MODE_MAX) - #if defined(CONFIG_IDF_TARGET_ESP32C3) // 2 RMT, 6 LEDC, only has 1 I2S but NPB does not support it ATM - #define WLED_MAX_BUSSES 6 // will allow 2 digital & 2 analog RGB or 6 PWM white - #define WLED_MAX_DIGITAL_CHANNELS 2 - //#define WLED_MAX_ANALOG_CHANNELS 6 - #define WLED_MIN_VIRTUAL_BUSSES 4 // no longer used for bus creation but used to distinguish S2/S3 in UI - #elif defined(CONFIG_IDF_TARGET_ESP32S2) // 4 RMT, 8 LEDC, only has 1 I2S bus, supported in NPB - // the 5th bus (I2S) will prevent Audioreactive usermod from functioning (it is last used though) - #define WLED_MAX_BUSSES 7 // will allow 5 digital & 2 analog RGB - #define WLED_MAX_DIGITAL_CHANNELS 5 - //#define WLED_MAX_ANALOG_CHANNELS 8 - #define WLED_MIN_VIRTUAL_BUSSES 4 // no longer used for bus creation but used to distinguish S2/S3 in UI - #elif defined(CONFIG_IDF_TARGET_ESP32S3) // 4 RMT, 8 LEDC, has 2 I2S but NPB supports parallel x8 LCD on I2S1 - #define WLED_MAX_BUSSES 14 // will allow 12 digital & 2 analog RGB - #define WLED_MAX_DIGITAL_CHANNELS 12 // x4 RMT + x8 I2S-LCD - //#define WLED_MAX_ANALOG_CHANNELS 8 - #define WLED_MIN_VIRTUAL_BUSSES 6 // no longer used for bus creation but used to distinguish S2/S3 in UI - #else - // the last digital bus (I2S0) will prevent Audioreactive usermod from functioning - #define WLED_MAX_BUSSES 19 // will allow 16 digital & 3 analog RGB - #define WLED_MAX_DIGITAL_CHANNELS 16 // x1/x8 I2S1 + x8 RMT - //#define WLED_MAX_ANALOG_CHANNELS 16 - #define WLED_MIN_VIRTUAL_BUSSES 6 // no longer used for bus creation but used to distinguish S2/S3 in UI - #endif - #endif +#ifdef ESP8266 +#define WLED_MAX_DIGITAL_CHANNELS 3 +#define WLED_MAX_ANALOG_CHANNELS 5 +#define WLED_MAX_BUSSES 4 // will allow 3 digital & 1 analog RGB +#define WLED_MIN_VIRTUAL_BUSSES 3 // no longer used for bus creation but used to distinguish S2/S3 in UI +#else +#define WLED_MAX_ANALOG_CHANNELS (LEDC_CHANNEL_MAX * LEDC_SPEED_MODE_MAX) +#if defined(CONFIG_IDF_TARGET_ESP32C3) // 2 RMT, 6 LEDC, only has 1 I2S but NPB does not support it ATM +#define WLED_MAX_BUSSES 6 // will allow 2 digital & 2 analog RGB or 6 PWM white +#define WLED_MAX_DIGITAL_CHANNELS 2 +// #define WLED_MAX_ANALOG_CHANNELS 6 +#define WLED_MIN_VIRTUAL_BUSSES 4 // no longer used for bus creation but used to distinguish S2/S3 in UI +#elif defined(CONFIG_IDF_TARGET_ESP32S2) // 4 RMT, 8 LEDC, only has 1 I2S bus, supported in NPB +// the 5th bus (I2S) will prevent Audioreactive usermod from functioning (it is last used though) +#define WLED_MAX_BUSSES 7 // will allow 5 digital & 2 analog RGB +#define WLED_MAX_DIGITAL_CHANNELS 5 +// #define WLED_MAX_ANALOG_CHANNELS 8 +#define WLED_MIN_VIRTUAL_BUSSES 4 // no longer used for bus creation but used to distinguish S2/S3 in UI +#elif defined(CONFIG_IDF_TARGET_ESP32S3) // 4 RMT, 8 LEDC, has 2 I2S but NPB supports parallel x8 LCD on I2S1 +#define WLED_MAX_BUSSES 14 // will allow 12 digital & 2 analog RGB +#define WLED_MAX_DIGITAL_CHANNELS 12 // x4 RMT + x8 I2S-LCD +// #define WLED_MAX_ANALOG_CHANNELS 8 +#define WLED_MIN_VIRTUAL_BUSSES 6 // no longer used for bus creation but used to distinguish S2/S3 in UI +#else +// the last digital bus (I2S0) will prevent Audioreactive usermod from functioning +#define WLED_MAX_BUSSES 19 // will allow 16 digital & 3 analog RGB +#define WLED_MAX_DIGITAL_CHANNELS 16 // x1/x8 I2S1 + x8 RMT +// #define WLED_MAX_ANALOG_CHANNELS 16 +#define WLED_MIN_VIRTUAL_BUSSES 6 // no longer used for bus creation but used to distinguish S2/S3 in UI +#endif +#endif #else - #ifdef ESP8266 - #if WLED_MAX_BUSSES > 5 - #error Maximum number of buses is 5. - #endif - #ifndef WLED_MAX_ANALOG_CHANNELS - #error You must also define WLED_MAX_ANALOG_CHANNELS. - #endif - #ifndef WLED_MAX_DIGITAL_CHANNELS - #error You must also define WLED_MAX_DIGITAL_CHANNELS. - #endif - #define WLED_MIN_VIRTUAL_BUSSES 3 - #else - #if WLED_MAX_BUSSES > 20 - #error Maximum number of buses is 20. - #endif - #ifndef WLED_MAX_ANALOG_CHANNELS - #error You must also define WLED_MAX_ANALOG_CHANNELS. - #endif - #ifndef WLED_MAX_DIGITAL_CHANNELS - #error You must also define WLED_MAX_DIGITAL_CHANNELS. - #endif - #if defined(CONFIG_IDF_TARGET_ESP32S2) || defined(CONFIG_IDF_TARGET_ESP32C3) - #define WLED_MIN_VIRTUAL_BUSSES 4 - #else - #define WLED_MIN_VIRTUAL_BUSSES 6 - #endif - #endif +#ifdef ESP8266 +#if WLED_MAX_BUSSES > 5 +#error Maximum number of buses is 5. +#endif +#ifndef WLED_MAX_ANALOG_CHANNELS +#error You must also define WLED_MAX_ANALOG_CHANNELS. +#endif +#ifndef WLED_MAX_DIGITAL_CHANNELS +#error You must also define WLED_MAX_DIGITAL_CHANNELS. +#endif +#define WLED_MIN_VIRTUAL_BUSSES 3 +#else +#if WLED_MAX_BUSSES > 20 +#error Maximum number of buses is 20. +#endif +#ifndef WLED_MAX_ANALOG_CHANNELS +#error You must also define WLED_MAX_ANALOG_CHANNELS. +#endif +#ifndef WLED_MAX_DIGITAL_CHANNELS +#error You must also define WLED_MAX_DIGITAL_CHANNELS. +#endif +#if defined(CONFIG_IDF_TARGET_ESP32S2) || defined(CONFIG_IDF_TARGET_ESP32C3) +#define WLED_MIN_VIRTUAL_BUSSES 4 +#else +#define WLED_MIN_VIRTUAL_BUSSES 6 +#endif +#endif #endif #ifndef WLED_MAX_BUTTONS - #ifdef ESP8266 - #define WLED_MAX_BUTTONS 2 - #else - #define WLED_MAX_BUTTONS 4 - #endif +#ifdef ESP8266 +#define WLED_MAX_BUTTONS 2 #else - #if WLED_MAX_BUTTONS < 2 - #undef WLED_MAX_BUTTONS - #define WLED_MAX_BUTTONS 2 - #endif +#define WLED_MAX_BUTTONS 4 +#endif +#else +#if WLED_MAX_BUTTONS < 2 +#undef WLED_MAX_BUTTONS +#define WLED_MAX_BUTTONS 2 +#endif #endif #if defined(ESP8266) || defined(CONFIG_IDF_TARGET_ESP32S2) @@ -126,215 +126,216 @@ #endif #if defined(WLED_MAX_LEDMAPS) && (WLED_MAX_LEDMAPS > 32 || WLED_MAX_LEDMAPS < 10) - #undef WLED_MAX_LEDMAPS +#undef WLED_MAX_LEDMAPS #endif #ifndef WLED_MAX_LEDMAPS - #if defined(ESP8266) || defined(CONFIG_IDF_TARGET_ESP32S2) - #define WLED_MAX_LEDMAPS 10 - #else - #define WLED_MAX_LEDMAPS 16 - #endif +#if defined(ESP8266) || defined(CONFIG_IDF_TARGET_ESP32S2) +#define WLED_MAX_LEDMAPS 10 +#else +#define WLED_MAX_LEDMAPS 16 +#endif #endif #ifndef WLED_MAX_SEGNAME_LEN - #ifdef ESP8266 - #define WLED_MAX_SEGNAME_LEN 32 - #else - #define WLED_MAX_SEGNAME_LEN 64 - #endif +#ifdef ESP8266 +#define WLED_MAX_SEGNAME_LEN 32 +#else +#define WLED_MAX_SEGNAME_LEN 64 +#endif #else - #if WLED_MAX_SEGNAME_LEN<32 - #undef WLED_MAX_SEGNAME_LEN - #define WLED_MAX_SEGNAME_LEN 32 - #else - #warning WLED UI does not support modified maximum segment name length! - #endif -#endif - -//Usermod IDs -#define USERMOD_ID_RESERVED 0 //Unused. Might indicate no usermod present -#define USERMOD_ID_UNSPECIFIED 1 //Default value for a general user mod that does not specify a custom ID -#define USERMOD_ID_EXAMPLE 2 //Usermod "usermod_v2_example.h" -#define USERMOD_ID_TEMPERATURE 3 //Usermod "usermod_temperature.h" -#define USERMOD_ID_FIXNETSERVICES 4 //Usermod "usermod_Fix_unreachable_netservices.h" -#define USERMOD_ID_PIRSWITCH 5 //Usermod "usermod_PIR_sensor_switch.h" -#define USERMOD_ID_IMU 6 //Usermod "usermod_mpu6050_imu.h" -#define USERMOD_ID_FOUR_LINE_DISP 7 //Usermod "usermod_v2_four_line_display.h -#define USERMOD_ID_ROTARY_ENC_UI 8 //Usermod "usermod_v2_rotary_encoder_ui.h" -#define USERMOD_ID_AUTO_SAVE 9 //Usermod "usermod_v2_auto_save.h" -#define USERMOD_ID_DHT 10 //Usermod "usermod_dht.h" -#define USERMOD_ID_MODE_SORT 11 //Usermod "usermod_v2_mode_sort.h" -#define USERMOD_ID_VL53L0X 12 //Usermod "usermod_vl53l0x_gestures.h" -#define USERMOD_ID_MULTI_RELAY 13 //Usermod "usermod_multi_relay.h" -#define USERMOD_ID_ANIMATED_STAIRCASE 14 //Usermod "Animated_Staircase.h" -#define USERMOD_ID_RTC 15 //Usermod "usermod_rtc.h" -#define USERMOD_ID_ELEKSTUBE_IPS 16 //Usermod "usermod_elekstube_ips.h" -#define USERMOD_ID_SN_PHOTORESISTOR 17 //Usermod "usermod_sn_photoresistor.h" -#define USERMOD_ID_BATTERY 18 //Usermod "usermod_v2_battery.h" -#define USERMOD_ID_PWM_FAN 19 //Usermod "usermod_PWM_fan.h" -#define USERMOD_ID_BH1750 20 //Usermod "usermod_bh1750.h" -#define USERMOD_ID_SEVEN_SEGMENT_DISPLAY 21 //Usermod "usermod_v2_seven_segment_display.h" -#define USERMOD_RGB_ROTARY_ENCODER 22 //Usermod "rgb-rotary-encoder.h" -#define USERMOD_ID_QUINLED_AN_PENTA 23 //Usermod "quinled-an-penta.h" -#define USERMOD_ID_SSDR 24 //Usermod "usermod_v2_seven_segment_display_reloaded.h" -#define USERMOD_ID_CRONIXIE 25 //Usermod "usermod_cronixie.h" -#define USERMOD_ID_WIZLIGHTS 26 //Usermod "wizlights.h" -#define USERMOD_ID_WORDCLOCK 27 //Usermod "usermod_v2_word_clock.h" -#define USERMOD_ID_MY9291 28 //Usermod "usermod_MY9291.h" -#define USERMOD_ID_SI7021_MQTT_HA 29 //Usermod "usermod_si7021_mqtt_ha.h" -#define USERMOD_ID_BME280 30 //Usermod "usermod_bme280.h -#define USERMOD_ID_SMARTNEST 31 //Usermod "usermod_smartnest.h" -#define USERMOD_ID_AUDIOREACTIVE 32 //Usermod "audioreactive.h" -#define USERMOD_ID_ANALOG_CLOCK 33 //Usermod "Analog_Clock.h" -#define USERMOD_ID_PING_PONG_CLOCK 34 //Usermod "usermod_v2_ping_pong_clock.h" -#define USERMOD_ID_ADS1115 35 //Usermod "usermod_ads1115.h" -#define USERMOD_ID_BOBLIGHT 36 //Usermod "boblight.h" -#define USERMOD_ID_SD_CARD 37 //Usermod "usermod_sd_card.h" -#define USERMOD_ID_PWM_OUTPUTS 38 //Usermod "usermod_pwm_outputs.h -#define USERMOD_ID_SHT 39 //Usermod "usermod_sht.h -#define USERMOD_ID_KLIPPER 40 //Usermod Klipper percentage -#define USERMOD_ID_WIREGUARD 41 //Usermod "wireguard.h" -#define USERMOD_ID_INTERNAL_TEMPERATURE 42 //Usermod "usermod_internal_temperature.h" -#define USERMOD_ID_LDR_DUSK_DAWN 43 //Usermod "usermod_LDR_Dusk_Dawn_v2.h" -#define USERMOD_ID_STAIRWAY_WIPE 44 //Usermod "stairway-wipe-usermod-v2.h" -#define USERMOD_ID_ANIMARTRIX 45 //Usermod "usermod_v2_animartrix.h" -#define USERMOD_ID_HTTP_PULL_LIGHT_CONTROL 46 //usermod "usermod_v2_HttpPullLightControl.h" -#define USERMOD_ID_TETRISAI 47 //Usermod "usermod_v2_tetris.h" -#define USERMOD_ID_MAX17048 48 //Usermod "usermod_max17048.h" -#define USERMOD_ID_BME68X 49 //Usermod "usermod_bme68x.h -#define USERMOD_ID_INA226 50 //Usermod "usermod_ina226.h" -#define USERMOD_ID_AHT10 51 //Usermod "usermod_aht10.h" -#define USERMOD_ID_LD2410 52 //Usermod "usermod_ld2410.h" -#define USERMOD_ID_POV_DISPLAY 53 //Usermod "usermod_pov_display.h" -#define USERMOD_ID_PIXELS_DICE_TRAY 54 //Usermod "pixels_dice_tray.h" -#define USERMOD_ID_DEEP_SLEEP 55 //Usermod "usermod_deep_sleep.h" -#define USERMOD_ID_RF433 56 //Usermod "usermod_v2_RF433.h" -#define USERMOD_ID_BRIGHTNESS_FOLLOW_SUN 57 //Usermod "usermod_v2_brightness_follow_sun.h" - -//Access point behavior -#define AP_BEHAVIOR_BOOT_NO_CONN 0 //Open AP when no connection after boot -#define AP_BEHAVIOR_NO_CONN 1 //Open when no connection (either after boot or if connection is lost) -#define AP_BEHAVIOR_ALWAYS 2 //Always open -#define AP_BEHAVIOR_BUTTON_ONLY 3 //Only when button pressed for 6 sec -#define AP_BEHAVIOR_TEMPORARY 4 //Open AP when no connection after boot but only temporary +#if WLED_MAX_SEGNAME_LEN < 32 +#undef WLED_MAX_SEGNAME_LEN +#define WLED_MAX_SEGNAME_LEN 32 +#else +#warning WLED UI does not support modified maximum segment name length! +#endif +#endif + +// Usermod IDs +#define USERMOD_ID_RESERVED 0 // Unused. Might indicate no usermod present +#define USERMOD_ID_UNSPECIFIED 1 // Default value for a general user mod that does not specify a custom ID +#define USERMOD_ID_EXAMPLE 2 // Usermod "usermod_v2_example.h" +#define USERMOD_ID_TEMPERATURE 3 // Usermod "usermod_temperature.h" +#define USERMOD_ID_FIXNETSERVICES 4 // Usermod "usermod_Fix_unreachable_netservices.h" +#define USERMOD_ID_PIRSWITCH 5 // Usermod "usermod_PIR_sensor_switch.h" +#define USERMOD_ID_IMU 6 // Usermod "usermod_mpu6050_imu.h" +#define USERMOD_ID_FOUR_LINE_DISP 7 // Usermod "usermod_v2_four_line_display.h +#define USERMOD_ID_ROTARY_ENC_UI 8 // Usermod "usermod_v2_rotary_encoder_ui.h" +#define USERMOD_ID_AUTO_SAVE 9 // Usermod "usermod_v2_auto_save.h" +#define USERMOD_ID_DHT 10 // Usermod "usermod_dht.h" +#define USERMOD_ID_MODE_SORT 11 // Usermod "usermod_v2_mode_sort.h" +#define USERMOD_ID_VL53L0X 12 // Usermod "usermod_vl53l0x_gestures.h" +#define USERMOD_ID_MULTI_RELAY 13 // Usermod "usermod_multi_relay.h" +#define USERMOD_ID_ANIMATED_STAIRCASE 14 // Usermod "Animated_Staircase.h" +#define USERMOD_ID_RTC 15 // Usermod "usermod_rtc.h" +#define USERMOD_ID_ELEKSTUBE_IPS 16 // Usermod "usermod_elekstube_ips.h" +#define USERMOD_ID_SN_PHOTORESISTOR 17 // Usermod "usermod_sn_photoresistor.h" +#define USERMOD_ID_BATTERY 18 // Usermod "usermod_v2_battery.h" +#define USERMOD_ID_PWM_FAN 19 // Usermod "usermod_PWM_fan.h" +#define USERMOD_ID_BH1750 20 // Usermod "usermod_bh1750.h" +#define USERMOD_ID_SEVEN_SEGMENT_DISPLAY 21 // Usermod "usermod_v2_seven_segment_display.h" +#define USERMOD_RGB_ROTARY_ENCODER 22 // Usermod "rgb-rotary-encoder.h" +#define USERMOD_ID_QUINLED_AN_PENTA 23 // Usermod "quinled-an-penta.h" +#define USERMOD_ID_SSDR 24 // Usermod "usermod_v2_seven_segment_display_reloaded.h" +#define USERMOD_ID_CRONIXIE 25 // Usermod "usermod_cronixie.h" +#define USERMOD_ID_WIZLIGHTS 26 // Usermod "wizlights.h" +#define USERMOD_ID_WORDCLOCK 27 // Usermod "usermod_v2_word_clock.h" +#define USERMOD_ID_MY9291 28 // Usermod "usermod_MY9291.h" +#define USERMOD_ID_SI7021_MQTT_HA 29 // Usermod "usermod_si7021_mqtt_ha.h" +#define USERMOD_ID_BME280 30 // Usermod "usermod_bme280.h +#define USERMOD_ID_SMARTNEST 31 // Usermod "usermod_smartnest.h" +#define USERMOD_ID_AUDIOREACTIVE 32 // Usermod "audioreactive.h" +#define USERMOD_ID_ANALOG_CLOCK 33 // Usermod "Analog_Clock.h" +#define USERMOD_ID_PING_PONG_CLOCK 34 // Usermod "usermod_v2_ping_pong_clock.h" +#define USERMOD_ID_ADS1115 35 // Usermod "usermod_ads1115.h" +#define USERMOD_ID_BOBLIGHT 36 // Usermod "boblight.h" +#define USERMOD_ID_SD_CARD 37 // Usermod "usermod_sd_card.h" +#define USERMOD_ID_PWM_OUTPUTS 38 // Usermod "usermod_pwm_outputs.h +#define USERMOD_ID_SHT 39 // Usermod "usermod_sht.h +#define USERMOD_ID_KLIPPER 40 // Usermod Klipper percentage +#define USERMOD_ID_WIREGUARD 41 // Usermod "wireguard.h" +#define USERMOD_ID_INTERNAL_TEMPERATURE 42 // Usermod "usermod_internal_temperature.h" +#define USERMOD_ID_LDR_DUSK_DAWN 43 // Usermod "usermod_LDR_Dusk_Dawn_v2.h" +#define USERMOD_ID_STAIRWAY_WIPE 44 // Usermod "stairway-wipe-usermod-v2.h" +#define USERMOD_ID_ANIMARTRIX 45 // Usermod "usermod_v2_animartrix.h" +#define USERMOD_ID_HTTP_PULL_LIGHT_CONTROL 46 // usermod "usermod_v2_HttpPullLightControl.h" +#define USERMOD_ID_TETRISAI 47 // Usermod "usermod_v2_tetris.h" +#define USERMOD_ID_MAX17048 48 // Usermod "usermod_max17048.h" +#define USERMOD_ID_BME68X 49 // Usermod "usermod_bme68x.h +#define USERMOD_ID_INA226 50 // Usermod "usermod_ina226.h" +#define USERMOD_ID_AHT10 51 // Usermod "usermod_aht10.h" +#define USERMOD_ID_LD2410 52 // Usermod "usermod_ld2410.h" +#define USERMOD_ID_POV_DISPLAY 53 // Usermod "usermod_pov_display.h" +#define USERMOD_ID_PIXELS_DICE_TRAY 54 // Usermod "pixels_dice_tray.h" +#define USERMOD_ID_DEEP_SLEEP 55 // Usermod "usermod_deep_sleep.h" +#define USERMOD_ID_RF433 56 // Usermod "usermod_v2_RF433.h" +#define USERMOD_ID_BRIGHTNESS_FOLLOW_SUN 57 // Usermod "usermod_v2_brightness_follow_sun.h" +#define USERMOD_ID_SOCIAL_COUNTER 58 // Usermod "usermod_v2_social_counter.cpp" + +// Access point behavior +#define AP_BEHAVIOR_BOOT_NO_CONN 0 // Open AP when no connection after boot +#define AP_BEHAVIOR_NO_CONN 1 // Open when no connection (either after boot or if connection is lost) +#define AP_BEHAVIOR_ALWAYS 2 // Always open +#define AP_BEHAVIOR_BUTTON_ONLY 3 // Only when button pressed for 6 sec +#define AP_BEHAVIOR_TEMPORARY 4 // Open AP when no connection after boot but only temporary #ifndef WLED_AP_TIMEOUT - #define WLED_AP_TIMEOUT 300000 //Temporary AP timeout -#endif - -//Notifier callMode -#define CALL_MODE_INIT 0 //no updates on init, can be used to disable updates -#define CALL_MODE_DIRECT_CHANGE 1 -#define CALL_MODE_BUTTON 2 //default button actions applied to selected segments -#define CALL_MODE_NOTIFICATION 3 //caused by incoming notification (UDP or DMX preset) -#define CALL_MODE_NIGHTLIGHT 4 //nightlight progress -#define CALL_MODE_NO_NOTIFY 5 //change state but do not send notifications (UDP) -#define CALL_MODE_FX_CHANGED 6 //no longer used -#define CALL_MODE_HUE 7 -#define CALL_MODE_PRESET_CYCLE 8 //no longer used -#define CALL_MODE_BLYNK 9 //no longer used -#define CALL_MODE_ALEXA 10 -#define CALL_MODE_WS_SEND 11 //special call mode, not for notifier, updates websocket only -#define CALL_MODE_BUTTON_PRESET 12 //button/IR JSON preset/macro - -//RGB to RGBW conversion mode -#define RGBW_MODE_MANUAL_ONLY 0 // No automatic white channel calculation. Manual white channel slider -#define RGBW_MODE_AUTO_BRIGHTER 1 // New algorithm. Adds as much white as the darkest RGBW channel -#define RGBW_MODE_AUTO_ACCURATE 2 // New algorithm. Adds as much white as the darkest RGBW channel and subtracts this amount from each RGB channel -#define RGBW_MODE_DUAL 3 // Manual slider + auto calculation. Automatically calculates only if manual slider is set to off (0) -#define RGBW_MODE_MAX 4 // Sets white to the value of the brightest RGB channel (good for white-only LEDs without any RGB) -//#define RGBW_MODE_LEGACY 4 // Old floating algorithm. Too slow for realtime and palette support (unused) -#define AW_GLOBAL_DISABLED 255 // Global auto white mode override disabled. Per-bus setting is used - -//realtime modes -#define REALTIME_MODE_INACTIVE 0 -#define REALTIME_MODE_GENERIC 1 -#define REALTIME_MODE_UDP 2 -#define REALTIME_MODE_HYPERION 3 -#define REALTIME_MODE_E131 4 -#define REALTIME_MODE_ADALIGHT 5 -#define REALTIME_MODE_ARTNET 6 -#define REALTIME_MODE_TPM2NET 7 -#define REALTIME_MODE_DDP 8 -#define REALTIME_MODE_DMX 9 - -//realtime override modes -#define REALTIME_OVERRIDE_NONE 0 -#define REALTIME_OVERRIDE_ONCE 1 -#define REALTIME_OVERRIDE_ALWAYS 2 - -//E1.31 DMX modes -#define DMX_MODE_DISABLED 0 //not used -#define DMX_MODE_SINGLE_RGB 1 //all LEDs same RGB color (3 channels) -#define DMX_MODE_SINGLE_DRGB 2 //all LEDs same RGB color and master dimmer (4 channels) -#define DMX_MODE_EFFECT 3 //trigger standalone effects of WLED (15 channels) -#define DMX_MODE_EFFECT_W 7 //trigger standalone effects of WLED (18 channels) -#define DMX_MODE_MULTIPLE_RGB 4 //every LED is addressed with its own RGB (ledCount * 3 channels) -#define DMX_MODE_MULTIPLE_DRGB 5 //every LED is addressed with its own RGB and share a master dimmer (ledCount * 3 + 1 channels) -#define DMX_MODE_MULTIPLE_RGBW 6 //every LED is addressed with its own RGBW (ledCount * 4 channels) -#define DMX_MODE_EFFECT_SEGMENT 8 //trigger standalone effects of WLED (15 channels per segment) -#define DMX_MODE_EFFECT_SEGMENT_W 9 //trigger standalone effects of WLED (18 channels per segment) -#define DMX_MODE_PRESET 10 //apply presets (1 channel) - -//Light capability byte (unused) 0bRCCCTTTT -//bits 0/1/2/3: specifies a type of LED driver. A single "driver" may have different chip models but must have the same protocol/behavior -//bits 4/5/6: specifies the class of LED driver - 0b000 (dec. 0-15) unconfigured/reserved -// - 0b001 (dec. 16-31) digital (data pin only) -// - 0b010 (dec. 32-47) analog (PWM) -// - 0b011 (dec. 48-63) digital (data + clock / SPI) -// - 0b100 (dec. 64-79) unused/reserved -// - 0b101 (dec. 80-95) virtual network busses -// - 0b110 (dec. 96-111) unused/reserved -// - 0b111 (dec. 112-127) unused/reserved -//bit 7 is reserved and set to 0 - -#define TYPE_NONE 0 //light is not configured -#define TYPE_RESERVED 1 //unused. Might indicate a "virtual" light -//Digital types (data pin only) (16-39) -#define TYPE_DIGITAL_MIN 16 // first usable digital type -#define TYPE_WS2812_1CH 18 //white-only chips (1 channel per IC) (unused) -#define TYPE_WS2812_1CH_X3 19 //white-only chips (3 channels per IC) -#define TYPE_WS2812_2CH_X3 20 //CCT chips (1st IC controls WW + CW of 1st zone and CW of 2nd zone, 2nd IC controls WW of 2nd zone and WW + CW of 3rd zone) -#define TYPE_WS2812_WWA 21 //amber + warm + cold white -#define TYPE_WS2812_RGB 22 -#define TYPE_GS8608 23 //same driver as WS2812, but will require signal 2x per second (else displays test pattern) -#define TYPE_WS2811_400KHZ 24 //half-speed WS2812 protocol, used by very old WS2811 units -#define TYPE_TM1829 25 -#define TYPE_UCS8903 26 -#define TYPE_APA106 27 -#define TYPE_FW1906 28 //RGB + CW + WW + unused channel (6 channels per IC) -#define TYPE_UCS8904 29 //first RGBW digital type (hardcoded in busmanager.cpp, memUsage()) -#define TYPE_SK6812_RGBW 30 -#define TYPE_TM1814 31 -#define TYPE_WS2805 32 //RGB + WW + CW -#define TYPE_TM1914 33 //RGB -#define TYPE_SM16825 34 //RGB + WW + CW -#define TYPE_DIGITAL_MAX 39 // last usable digital type +#define WLED_AP_TIMEOUT 300000 // Temporary AP timeout +#endif + +// Notifier callMode +#define CALL_MODE_INIT 0 // no updates on init, can be used to disable updates +#define CALL_MODE_DIRECT_CHANGE 1 +#define CALL_MODE_BUTTON 2 // default button actions applied to selected segments +#define CALL_MODE_NOTIFICATION 3 // caused by incoming notification (UDP or DMX preset) +#define CALL_MODE_NIGHTLIGHT 4 // nightlight progress +#define CALL_MODE_NO_NOTIFY 5 // change state but do not send notifications (UDP) +#define CALL_MODE_FX_CHANGED 6 // no longer used +#define CALL_MODE_HUE 7 +#define CALL_MODE_PRESET_CYCLE 8 // no longer used +#define CALL_MODE_BLYNK 9 // no longer used +#define CALL_MODE_ALEXA 10 +#define CALL_MODE_WS_SEND 11 // special call mode, not for notifier, updates websocket only +#define CALL_MODE_BUTTON_PRESET 12 // button/IR JSON preset/macro + +// RGB to RGBW conversion mode +#define RGBW_MODE_MANUAL_ONLY 0 // No automatic white channel calculation. Manual white channel slider +#define RGBW_MODE_AUTO_BRIGHTER 1 // New algorithm. Adds as much white as the darkest RGBW channel +#define RGBW_MODE_AUTO_ACCURATE 2 // New algorithm. Adds as much white as the darkest RGBW channel and subtracts this amount from each RGB channel +#define RGBW_MODE_DUAL 3 // Manual slider + auto calculation. Automatically calculates only if manual slider is set to off (0) +#define RGBW_MODE_MAX 4 // Sets white to the value of the brightest RGB channel (good for white-only LEDs without any RGB) +// #define RGBW_MODE_LEGACY 4 // Old floating algorithm. Too slow for realtime and palette support (unused) +#define AW_GLOBAL_DISABLED 255 // Global auto white mode override disabled. Per-bus setting is used + +// realtime modes +#define REALTIME_MODE_INACTIVE 0 +#define REALTIME_MODE_GENERIC 1 +#define REALTIME_MODE_UDP 2 +#define REALTIME_MODE_HYPERION 3 +#define REALTIME_MODE_E131 4 +#define REALTIME_MODE_ADALIGHT 5 +#define REALTIME_MODE_ARTNET 6 +#define REALTIME_MODE_TPM2NET 7 +#define REALTIME_MODE_DDP 8 +#define REALTIME_MODE_DMX 9 + +// realtime override modes +#define REALTIME_OVERRIDE_NONE 0 +#define REALTIME_OVERRIDE_ONCE 1 +#define REALTIME_OVERRIDE_ALWAYS 2 + +// E1.31 DMX modes +#define DMX_MODE_DISABLED 0 // not used +#define DMX_MODE_SINGLE_RGB 1 // all LEDs same RGB color (3 channels) +#define DMX_MODE_SINGLE_DRGB 2 // all LEDs same RGB color and master dimmer (4 channels) +#define DMX_MODE_EFFECT 3 // trigger standalone effects of WLED (15 channels) +#define DMX_MODE_EFFECT_W 7 // trigger standalone effects of WLED (18 channels) +#define DMX_MODE_MULTIPLE_RGB 4 // every LED is addressed with its own RGB (ledCount * 3 channels) +#define DMX_MODE_MULTIPLE_DRGB 5 // every LED is addressed with its own RGB and share a master dimmer (ledCount * 3 + 1 channels) +#define DMX_MODE_MULTIPLE_RGBW 6 // every LED is addressed with its own RGBW (ledCount * 4 channels) +#define DMX_MODE_EFFECT_SEGMENT 8 // trigger standalone effects of WLED (15 channels per segment) +#define DMX_MODE_EFFECT_SEGMENT_W 9 // trigger standalone effects of WLED (18 channels per segment) +#define DMX_MODE_PRESET 10 // apply presets (1 channel) + +// Light capability byte (unused) 0bRCCCTTTT +// bits 0/1/2/3: specifies a type of LED driver. A single "driver" may have different chip models but must have the same protocol/behavior +// bits 4/5/6: specifies the class of LED driver - 0b000 (dec. 0-15) unconfigured/reserved +// - 0b001 (dec. 16-31) digital (data pin only) +// - 0b010 (dec. 32-47) analog (PWM) +// - 0b011 (dec. 48-63) digital (data + clock / SPI) +// - 0b100 (dec. 64-79) unused/reserved +// - 0b101 (dec. 80-95) virtual network busses +// - 0b110 (dec. 96-111) unused/reserved +// - 0b111 (dec. 112-127) unused/reserved +// bit 7 is reserved and set to 0 + +#define TYPE_NONE 0 // light is not configured +#define TYPE_RESERVED 1 // unused. Might indicate a "virtual" light +// Digital types (data pin only) (16-39) +#define TYPE_DIGITAL_MIN 16 // first usable digital type +#define TYPE_WS2812_1CH 18 // white-only chips (1 channel per IC) (unused) +#define TYPE_WS2812_1CH_X3 19 // white-only chips (3 channels per IC) +#define TYPE_WS2812_2CH_X3 20 // CCT chips (1st IC controls WW + CW of 1st zone and CW of 2nd zone, 2nd IC controls WW of 2nd zone and WW + CW of 3rd zone) +#define TYPE_WS2812_WWA 21 // amber + warm + cold white +#define TYPE_WS2812_RGB 22 +#define TYPE_GS8608 23 // same driver as WS2812, but will require signal 2x per second (else displays test pattern) +#define TYPE_WS2811_400KHZ 24 // half-speed WS2812 protocol, used by very old WS2811 units +#define TYPE_TM1829 25 +#define TYPE_UCS8903 26 +#define TYPE_APA106 27 +#define TYPE_FW1906 28 // RGB + CW + WW + unused channel (6 channels per IC) +#define TYPE_UCS8904 29 // first RGBW digital type (hardcoded in busmanager.cpp, memUsage()) +#define TYPE_SK6812_RGBW 30 +#define TYPE_TM1814 31 +#define TYPE_WS2805 32 // RGB + WW + CW +#define TYPE_TM1914 33 // RGB +#define TYPE_SM16825 34 // RGB + WW + CW +#define TYPE_DIGITAL_MAX 39 // last usable digital type //"Analog" types (40-47) -#define TYPE_ONOFF 40 //binary output (relays etc.; NOT PWM) -#define TYPE_ANALOG_MIN 41 // first usable analog type -#define TYPE_ANALOG_1CH 41 //single channel PWM. Uses value of brightest RGBW channel -#define TYPE_ANALOG_2CH 42 //analog WW + CW -#define TYPE_ANALOG_3CH 43 //analog RGB -#define TYPE_ANALOG_4CH 44 //analog RGBW -#define TYPE_ANALOG_5CH 45 //analog RGB + WW + CW -#define TYPE_ANALOG_6CH 46 //analog RGB + A + WW + CW -#define TYPE_ANALOG_MAX 47 // last usable analog type -//Digital types (data + clock / SPI) (48-63) -#define TYPE_2PIN_MIN 48 -#define TYPE_WS2801 50 -#define TYPE_APA102 51 -#define TYPE_LPD8806 52 -#define TYPE_P9813 53 -#define TYPE_LPD6803 54 -#define TYPE_2PIN_MAX 63 -//Network types (master broadcast) (80-95) -#define TYPE_VIRTUAL_MIN 80 -#define TYPE_NET_DDP_RGB 80 //network DDP RGB bus (master broadcast bus) -#define TYPE_NET_E131_RGB 81 //network E131 RGB bus (master broadcast bus, unused) -#define TYPE_NET_ARTNET_RGB 82 //network ArtNet RGB bus (master broadcast bus, unused) -#define TYPE_NET_DDP_RGBW 88 //network DDP RGBW bus (master broadcast bus) -#define TYPE_NET_ARTNET_RGBW 89 //network ArtNet RGB bus (master broadcast bus, unused) -#define TYPE_VIRTUAL_MAX 95 +#define TYPE_ONOFF 40 // binary output (relays etc.; NOT PWM) +#define TYPE_ANALOG_MIN 41 // first usable analog type +#define TYPE_ANALOG_1CH 41 // single channel PWM. Uses value of brightest RGBW channel +#define TYPE_ANALOG_2CH 42 // analog WW + CW +#define TYPE_ANALOG_3CH 43 // analog RGB +#define TYPE_ANALOG_4CH 44 // analog RGBW +#define TYPE_ANALOG_5CH 45 // analog RGB + WW + CW +#define TYPE_ANALOG_6CH 46 // analog RGB + A + WW + CW +#define TYPE_ANALOG_MAX 47 // last usable analog type +// Digital types (data + clock / SPI) (48-63) +#define TYPE_2PIN_MIN 48 +#define TYPE_WS2801 50 +#define TYPE_APA102 51 +#define TYPE_LPD8806 52 +#define TYPE_P9813 53 +#define TYPE_LPD6803 54 +#define TYPE_2PIN_MAX 63 +// Network types (master broadcast) (80-95) +#define TYPE_VIRTUAL_MIN 80 +#define TYPE_NET_DDP_RGB 80 // network DDP RGB bus (master broadcast bus) +#define TYPE_NET_E131_RGB 81 // network E131 RGB bus (master broadcast bus, unused) +#define TYPE_NET_ARTNET_RGB 82 // network ArtNet RGB bus (master broadcast bus, unused) +#define TYPE_NET_DDP_RGBW 88 // network DDP RGBW bus (master broadcast bus) +#define TYPE_NET_ARTNET_RGBW 89 // network ArtNet RGB bus (master broadcast bus, unused) +#define TYPE_VIRTUAL_MAX 95 /* // old macros that have been moved to Bus class @@ -348,211 +349,211 @@ #define IS_VIRTUAL(t) ((t) >= 80 && (t) < 96) //this was a poor choice a better would be 96-111 */ -//Color orders -#define COL_ORDER_GRB 0 //GRB(w),defaut -#define COL_ORDER_RGB 1 //common for WS2811 -#define COL_ORDER_BRG 2 -#define COL_ORDER_RBG 3 -#define COL_ORDER_BGR 4 -#define COL_ORDER_GBR 5 -#define COL_ORDER_MAX 5 - -//ESP-NOW -#define ESP_NOW_STATE_UNINIT 0 -#define ESP_NOW_STATE_ON 1 -#define ESP_NOW_STATE_ERROR 2 - -//Button type -#define BTN_TYPE_NONE 0 -#define BTN_TYPE_RESERVED 1 -#define BTN_TYPE_PUSH 2 -#define BTN_TYPE_PUSH_ACT_HIGH 3 -#define BTN_TYPE_SWITCH 4 -#define BTN_TYPE_PIR_SENSOR 5 -#define BTN_TYPE_TOUCH 6 -#define BTN_TYPE_ANALOG 7 -#define BTN_TYPE_ANALOG_INVERTED 8 -#define BTN_TYPE_TOUCH_SWITCH 9 - -//Ethernet board types -#define WLED_NUM_ETH_TYPES 13 - -#define WLED_ETH_NONE 0 -#define WLED_ETH_WT32_ETH01 1 -#define WLED_ETH_ESP32_POE 2 -#define WLED_ETH_WESP32 3 -#define WLED_ETH_QUINLED 4 -#define WLED_ETH_TWILIGHTLORD 5 -#define WLED_ETH_ESP32DEUX 6 -#define WLED_ETH_ESP32ETHKITVE 7 -#define WLED_ETH_QUINLED_OCTA 8 -#define WLED_ETH_ABCWLEDV43ETH 9 -#define WLED_ETH_SERG74 10 +// Color orders +#define COL_ORDER_GRB 0 // GRB(w),defaut +#define COL_ORDER_RGB 1 // common for WS2811 +#define COL_ORDER_BRG 2 +#define COL_ORDER_RBG 3 +#define COL_ORDER_BGR 4 +#define COL_ORDER_GBR 5 +#define COL_ORDER_MAX 5 + +// ESP-NOW +#define ESP_NOW_STATE_UNINIT 0 +#define ESP_NOW_STATE_ON 1 +#define ESP_NOW_STATE_ERROR 2 + +// Button type +#define BTN_TYPE_NONE 0 +#define BTN_TYPE_RESERVED 1 +#define BTN_TYPE_PUSH 2 +#define BTN_TYPE_PUSH_ACT_HIGH 3 +#define BTN_TYPE_SWITCH 4 +#define BTN_TYPE_PIR_SENSOR 5 +#define BTN_TYPE_TOUCH 6 +#define BTN_TYPE_ANALOG 7 +#define BTN_TYPE_ANALOG_INVERTED 8 +#define BTN_TYPE_TOUCH_SWITCH 9 + +// Ethernet board types +#define WLED_NUM_ETH_TYPES 13 + +#define WLED_ETH_NONE 0 +#define WLED_ETH_WT32_ETH01 1 +#define WLED_ETH_ESP32_POE 2 +#define WLED_ETH_WESP32 3 +#define WLED_ETH_QUINLED 4 +#define WLED_ETH_TWILIGHTLORD 5 +#define WLED_ETH_ESP32DEUX 6 +#define WLED_ETH_ESP32ETHKITVE 7 +#define WLED_ETH_QUINLED_OCTA 8 +#define WLED_ETH_ABCWLEDV43ETH 9 +#define WLED_ETH_SERG74 10 #define WLED_ETH_ESP32_POE_WROVER 11 #define WLED_ETH_LILYGO_T_POE_PRO 12 -//Hue error codes -#define HUE_ERROR_INACTIVE 0 -#define HUE_ERROR_UNAUTHORIZED 1 -#define HUE_ERROR_LIGHTID 3 -#define HUE_ERROR_PUSHLINK 101 -#define HUE_ERROR_JSON_PARSING 250 -#define HUE_ERROR_TIMEOUT 251 -#define HUE_ERROR_ACTIVE 255 - -//Segment option byte bits -#define SEG_OPTION_SELECTED 0 -#define SEG_OPTION_REVERSED 1 -#define SEG_OPTION_ON 2 -#define SEG_OPTION_MIRROR 3 //Indicates that the effect will be mirrored within the segment -#define SEG_OPTION_FREEZE 4 //Segment contents will not be refreshed -#define SEG_OPTION_RESET 5 //Segment runtime requires reset -#define SEG_OPTION_REVERSED_Y 6 -#define SEG_OPTION_MIRROR_Y 7 -#define SEG_OPTION_TRANSPOSED 8 - -//Segment differs return byte -#define SEG_DIFFERS_BRI 0x01 // opacity -#define SEG_DIFFERS_OPT 0x02 // all segment options except: selected, reset & transitional -#define SEG_DIFFERS_COL 0x04 // colors -#define SEG_DIFFERS_FX 0x08 // effect/mode parameters -#define SEG_DIFFERS_BOUNDS 0x10 // segment start/stop bounds -#define SEG_DIFFERS_GSO 0x20 // grouping, spacing & offset -#define SEG_DIFFERS_SEL 0x80 // selected - -//Playlist option byte -#define PL_OPTION_SHUFFLE 0x01 -#define PL_OPTION_RESTORE 0x02 +// Hue error codes +#define HUE_ERROR_INACTIVE 0 +#define HUE_ERROR_UNAUTHORIZED 1 +#define HUE_ERROR_LIGHTID 3 +#define HUE_ERROR_PUSHLINK 101 +#define HUE_ERROR_JSON_PARSING 250 +#define HUE_ERROR_TIMEOUT 251 +#define HUE_ERROR_ACTIVE 255 + +// Segment option byte bits +#define SEG_OPTION_SELECTED 0 +#define SEG_OPTION_REVERSED 1 +#define SEG_OPTION_ON 2 +#define SEG_OPTION_MIRROR 3 // Indicates that the effect will be mirrored within the segment +#define SEG_OPTION_FREEZE 4 // Segment contents will not be refreshed +#define SEG_OPTION_RESET 5 // Segment runtime requires reset +#define SEG_OPTION_REVERSED_Y 6 +#define SEG_OPTION_MIRROR_Y 7 +#define SEG_OPTION_TRANSPOSED 8 + +// Segment differs return byte +#define SEG_DIFFERS_BRI 0x01 // opacity +#define SEG_DIFFERS_OPT 0x02 // all segment options except: selected, reset & transitional +#define SEG_DIFFERS_COL 0x04 // colors +#define SEG_DIFFERS_FX 0x08 // effect/mode parameters +#define SEG_DIFFERS_BOUNDS 0x10 // segment start/stop bounds +#define SEG_DIFFERS_GSO 0x20 // grouping, spacing & offset +#define SEG_DIFFERS_SEL 0x80 // selected + +// Playlist option byte +#define PL_OPTION_SHUFFLE 0x01 +#define PL_OPTION_RESTORE 0x02 // Segment capability byte -#define SEG_CAPABILITY_RGB 0x01 -#define SEG_CAPABILITY_W 0x02 -#define SEG_CAPABILITY_CCT 0x04 +#define SEG_CAPABILITY_RGB 0x01 +#define SEG_CAPABILITY_W 0x02 +#define SEG_CAPABILITY_CCT 0x04 // WLED Error modes -#define ERR_NONE 0 // All good :) -#define ERR_DENIED 1 // Permission denied -#define ERR_CONCURRENCY 2 // Conurrency (client active) -#define ERR_NOBUF 3 // JSON buffer was not released in time, request cannot be handled at this time -#define ERR_NOT_IMPL 4 // Not implemented -#define ERR_NORAM 8 // effect RAM depleted -#define ERR_JSON 9 // JSON parsing failed (input too large?) -#define ERR_FS_BEGIN 10 // Could not init filesystem (no partition?) -#define ERR_FS_QUOTA 11 // The FS is full or the maximum file size is reached -#define ERR_FS_PLOAD 12 // It was attempted to load a preset that does not exist -#define ERR_FS_IRLOAD 13 // It was attempted to load an IR JSON cmd, but the "ir.json" file does not exist -#define ERR_FS_RMLOAD 14 // It was attempted to load an remote JSON cmd, but the "remote.json" file does not exist -#define ERR_FS_GENERAL 19 // A general unspecified filesystem error occurred -#define ERR_OVERTEMP 30 // An attached temperature sensor has measured above threshold temperature (not implemented) -#define ERR_OVERCURRENT 31 // An attached current sensor has measured a current above the threshold (not implemented) -#define ERR_UNDERVOLT 32 // An attached voltmeter has measured a voltage below the threshold (not implemented) +#define ERR_NONE 0 // All good :) +#define ERR_DENIED 1 // Permission denied +#define ERR_CONCURRENCY 2 // Conurrency (client active) +#define ERR_NOBUF 3 // JSON buffer was not released in time, request cannot be handled at this time +#define ERR_NOT_IMPL 4 // Not implemented +#define ERR_NORAM 8 // effect RAM depleted +#define ERR_JSON 9 // JSON parsing failed (input too large?) +#define ERR_FS_BEGIN 10 // Could not init filesystem (no partition?) +#define ERR_FS_QUOTA 11 // The FS is full or the maximum file size is reached +#define ERR_FS_PLOAD 12 // It was attempted to load a preset that does not exist +#define ERR_FS_IRLOAD 13 // It was attempted to load an IR JSON cmd, but the "ir.json" file does not exist +#define ERR_FS_RMLOAD 14 // It was attempted to load an remote JSON cmd, but the "remote.json" file does not exist +#define ERR_FS_GENERAL 19 // A general unspecified filesystem error occurred +#define ERR_OVERTEMP 30 // An attached temperature sensor has measured above threshold temperature (not implemented) +#define ERR_OVERCURRENT 31 // An attached current sensor has measured a current above the threshold (not implemented) +#define ERR_UNDERVOLT 32 // An attached voltmeter has measured a voltage below the threshold (not implemented) // Timer mode types -#define NL_MODE_SET 0 //After nightlight time elapsed, set to target brightness -#define NL_MODE_FADE 1 //Fade to target brightness gradually -#define NL_MODE_COLORFADE 2 //Fade to target brightness and secondary color gradually -#define NL_MODE_SUN 3 //Sunrise/sunset. Target brightness is set immediately, then Sunrise effect is started. Max 60 min. +#define NL_MODE_SET 0 // After nightlight time elapsed, set to target brightness +#define NL_MODE_FADE 1 // Fade to target brightness gradually +#define NL_MODE_COLORFADE 2 // Fade to target brightness and secondary color gradually +#define NL_MODE_SUN 3 // Sunrise/sunset. Target brightness is set immediately, then Sunrise effect is started. Max 60 min. // Settings sub page IDs -#define SUBPAGE_MENU 0 -#define SUBPAGE_WIFI 1 -#define SUBPAGE_LEDS 2 -#define SUBPAGE_UI 3 -#define SUBPAGE_SYNC 4 -#define SUBPAGE_TIME 5 -#define SUBPAGE_SEC 6 -#define SUBPAGE_DMX 7 -#define SUBPAGE_UM 8 -#define SUBPAGE_UPDATE 9 -#define SUBPAGE_2D 10 -#define SUBPAGE_LOCK 251 -#define SUBPAGE_PINREQ 252 -#define SUBPAGE_CSS 253 -#define SUBPAGE_JS 254 -#define SUBPAGE_WELCOME 255 - -#define NTP_PACKET_SIZE 48 // size of NTP receive buffer -#define NTP_MIN_PACKET_SIZE 48 // min expected size - NTP v4 allows for "extended information" appended to the standard fields +#define SUBPAGE_MENU 0 +#define SUBPAGE_WIFI 1 +#define SUBPAGE_LEDS 2 +#define SUBPAGE_UI 3 +#define SUBPAGE_SYNC 4 +#define SUBPAGE_TIME 5 +#define SUBPAGE_SEC 6 +#define SUBPAGE_DMX 7 +#define SUBPAGE_UM 8 +#define SUBPAGE_UPDATE 9 +#define SUBPAGE_2D 10 +#define SUBPAGE_LOCK 251 +#define SUBPAGE_PINREQ 252 +#define SUBPAGE_CSS 253 +#define SUBPAGE_JS 254 +#define SUBPAGE_WELCOME 255 + +#define NTP_PACKET_SIZE 48 // size of NTP receive buffer +#define NTP_MIN_PACKET_SIZE 48 // min expected size - NTP v4 allows for "extended information" appended to the standard fields // Maximum number of pins per output. 5 for RGBCCT analog LEDs. #define OUTPUT_MAX_PINS 5 -//maximum number of rendered LEDs - this does not have to match max. physical LEDs, e.g. if there are virtual busses +// maximum number of rendered LEDs - this does not have to match max. physical LEDs, e.g. if there are virtual busses #ifndef MAX_LEDS #ifdef ESP8266 -#define MAX_LEDS 1664 //can't rely on memory limit to limit this to 1600 LEDs +#define MAX_LEDS 1664 // can't rely on memory limit to limit this to 1600 LEDs #elif defined(CONFIG_IDF_TARGET_ESP32S2) -#define MAX_LEDS 2048 //due to memory constraints +#define MAX_LEDS 2048 // due to memory constraints #else #define MAX_LEDS 8192 #endif #endif #ifndef MAX_LED_MEMORY - #ifdef ESP8266 - #define MAX_LED_MEMORY 4000 - #else - #if defined(ARDUINO_ARCH_ESP32S2) - #define MAX_LED_MEMORY 16000 - #elif defined(ARDUINO_ARCH_ESP32C3) - #define MAX_LED_MEMORY 32000 - #else - #define MAX_LED_MEMORY 64000 - #endif - #endif +#ifdef ESP8266 +#define MAX_LED_MEMORY 4000 +#else +#if defined(ARDUINO_ARCH_ESP32S2) +#define MAX_LED_MEMORY 16000 +#elif defined(ARDUINO_ARCH_ESP32C3) +#define MAX_LED_MEMORY 32000 +#else +#define MAX_LED_MEMORY 64000 +#endif +#endif #endif #ifndef MAX_LEDS_PER_BUS -#define MAX_LEDS_PER_BUS 2048 // may not be enough for fast LEDs (i.e. APA102) +#define MAX_LEDS_PER_BUS 2048 // may not be enough for fast LEDs (i.e. APA102) #endif // string temp buffer (now stored in stack locally) #ifdef ESP8266 #define SETTINGS_STACK_BUF_SIZE 2560 #else -#define SETTINGS_STACK_BUF_SIZE 3840 // warning: quite a large value for stack (640 * WLED_MAX_USERMODS) +#define SETTINGS_STACK_BUF_SIZE 3840 // warning: quite a large value for stack (640 * WLED_MAX_USERMODS) #endif #ifdef WLED_USE_ETHERNET - #define E131_MAX_UNIVERSE_COUNT 20 +#define E131_MAX_UNIVERSE_COUNT 20 #else - #ifdef ESP8266 - #define E131_MAX_UNIVERSE_COUNT 9 - #else - #define E131_MAX_UNIVERSE_COUNT 12 - #endif +#ifdef ESP8266 +#define E131_MAX_UNIVERSE_COUNT 9 +#else +#define E131_MAX_UNIVERSE_COUNT 12 +#endif #endif #ifndef ABL_MILLIAMPS_DEFAULT - #define ABL_MILLIAMPS_DEFAULT 850 // auto lower brightness to stay close to milliampere limit +#define ABL_MILLIAMPS_DEFAULT 850 // auto lower brightness to stay close to milliampere limit #else - #if ABL_MILLIAMPS_DEFAULT == 0 // disable ABL - #elif ABL_MILLIAMPS_DEFAULT < 250 // make sure value is at least 250 - #warning "make sure value is at least 250" - #define ABL_MILLIAMPS_DEFAULT 250 - #endif +#if ABL_MILLIAMPS_DEFAULT == 0 // disable ABL +#elif ABL_MILLIAMPS_DEFAULT < 250 // make sure value is at least 250 +#warning "make sure value is at least 250" +#define ABL_MILLIAMPS_DEFAULT 250 +#endif #endif #ifndef LED_MILLIAMPS_DEFAULT - #define LED_MILLIAMPS_DEFAULT 55 // common WS2812B +#define LED_MILLIAMPS_DEFAULT 55 // common WS2812B #else - #if LED_MILLIAMPS_DEFAULT < 1 || LED_MILLIAMPS_DEFAULT > 100 - #warning "Unusual LED mA current, overriding with default value." - #undef LED_MILLIAMPS_DEFAULT - #define LED_MILLIAMPS_DEFAULT 55 - #endif +#if LED_MILLIAMPS_DEFAULT < 1 || LED_MILLIAMPS_DEFAULT > 100 +#warning "Unusual LED mA current, overriding with default value." +#undef LED_MILLIAMPS_DEFAULT +#define LED_MILLIAMPS_DEFAULT 55 +#endif #endif // PWM settings #ifndef WLED_PWM_FREQ #ifdef ESP8266 - #define WLED_PWM_FREQ 880 //PWM frequency proven as good for LEDs +#define WLED_PWM_FREQ 880 // PWM frequency proven as good for LEDs +#else +#ifdef SOC_LEDC_SUPPORT_XTAL_CLOCK +#define WLED_PWM_FREQ 9765 // XTAL clock is 40MHz (this will allow 12 bit resolution) #else - #ifdef SOC_LEDC_SUPPORT_XTAL_CLOCK - #define WLED_PWM_FREQ 9765 // XTAL clock is 40MHz (this will allow 12 bit resolution) - #else - #define WLED_PWM_FREQ 19531 // APB clock is 80MHz - #endif +#define WLED_PWM_FREQ 19531 // APB clock is 80MHz +#endif #endif #endif @@ -560,30 +561,30 @@ // Size of buffer for API JSON object (increase for more segments) #ifdef ESP8266 - #define JSON_BUFFER_SIZE 10240 +#define JSON_BUFFER_SIZE 10240 +#else +#if defined(ARDUINO_ARCH_ESP32S2) +#define JSON_BUFFER_SIZE 24576 #else - #if defined(ARDUINO_ARCH_ESP32S2) - #define JSON_BUFFER_SIZE 24576 - #else - #define JSON_BUFFER_SIZE 32767 - #endif +#define JSON_BUFFER_SIZE 32767 +#endif #endif -//#define MIN_HEAP_SIZE +// #define MIN_HEAP_SIZE #define MIN_HEAP_SIZE 2048 // Web server limits #ifdef ESP8266 // Minimum heap to consider handling a request -#define WLED_REQUEST_MIN_HEAP (8*1024) +#define WLED_REQUEST_MIN_HEAP (8 * 1024) // Estimated maximum heap required by any one request -#define WLED_REQUEST_HEAP_USAGE (6*1024) +#define WLED_REQUEST_HEAP_USAGE (6 * 1024) #else // ESP32 TCP stack needs much more RAM than ESP8266 // Minimum heap remaining before queuing a request -#define WLED_REQUEST_MIN_HEAP (12*1024) +#define WLED_REQUEST_MIN_HEAP (12 * 1024) // Estimated maximum heap required by any one request -#define WLED_REQUEST_HEAP_USAGE (12*1024) +#define WLED_REQUEST_HEAP_USAGE (12 * 1024) #endif // Maximum number of requests in queue; absolute cap on web server resource usage. // Websockets do not count against this limit. @@ -591,91 +592,91 @@ // Maximum size of node map (list of other WLED instances) #ifdef ESP8266 - #define WLED_MAX_NODES 24 +#define WLED_MAX_NODES 24 #else - #define WLED_MAX_NODES 150 +#define WLED_MAX_NODES 150 #endif // Defaults pins, type and counts to configure LED output #if defined(ESP8266) || defined(CONFIG_IDF_TARGET_ESP32C3) - #ifdef WLED_ENABLE_DMX - #define DEFAULT_LED_PIN 1 - #warning "Compiling with DMX. The default LED pin has been changed to pin 1." - #else - #define DEFAULT_LED_PIN 2 // GPIO2 (D4) on Wemos D1 mini compatible boards, safe to use on any board - #endif +#ifdef WLED_ENABLE_DMX +#define DEFAULT_LED_PIN 1 +#warning "Compiling with DMX. The default LED pin has been changed to pin 1." +#else +#define DEFAULT_LED_PIN 2 // GPIO2 (D4) on Wemos D1 mini compatible boards, safe to use on any board +#endif #else - #define DEFAULT_LED_PIN 16 // aligns with GPIO2 (D4) on Wemos D1 mini32 compatible boards (if it is unusable it will be reassigned in WS2812FX::finalizeInit()) +#define DEFAULT_LED_PIN 16 // aligns with GPIO2 (D4) on Wemos D1 mini32 compatible boards (if it is unusable it will be reassigned in WS2812FX::finalizeInit()) #endif #define DEFAULT_LED_TYPE TYPE_WS2812_RGB #define DEFAULT_LED_COUNT 30 #define INTERFACE_UPDATE_COOLDOWN 1000 // time in ms to wait between websockets, alexa, and MQTT updates -#define PIN_RETRY_COOLDOWN 3000 // time in ms after an incorrect attempt PIN and OTA pass will be rejected even if correct -#define PIN_TIMEOUT 900000 // time in ms after which the PIN will be required again, 15 minutes +#define PIN_RETRY_COOLDOWN 3000 // time in ms after an incorrect attempt PIN and OTA pass will be rejected even if correct +#define PIN_TIMEOUT 900000 // time in ms after which the PIN will be required again, 15 minutes // HW_PIN_SCL & HW_PIN_SDA are used for information in usermods settings page and usermods themselves // which GPIO pins are actually used in a hardware layout (controller board) #if defined(I2CSCLPIN) && !defined(HW_PIN_SCL) - #define HW_PIN_SCL I2CSCLPIN +#define HW_PIN_SCL I2CSCLPIN #endif #if defined(I2CSDAPIN) && !defined(HW_PIN_SDA) - #define HW_PIN_SDA I2CSDAPIN +#define HW_PIN_SDA I2CSDAPIN #endif // you cannot change HW I2C pins on 8266 #if defined(ESP8266) && defined(HW_PIN_SCL) - #undef HW_PIN_SCL +#undef HW_PIN_SCL #endif #if defined(ESP8266) && defined(HW_PIN_SDA) - #undef HW_PIN_SDA +#undef HW_PIN_SDA #endif // defaults for 1st I2C on ESP32 (Wire global) #ifndef HW_PIN_SCL - #define HW_PIN_SCL SCL +#define HW_PIN_SCL SCL #endif #ifndef HW_PIN_SDA - #define HW_PIN_SDA SDA +#define HW_PIN_SDA SDA #endif // HW_PIN_SCLKSPI & HW_PIN_MOSISPI & HW_PIN_MISOSPI are used for information in usermods settings page and usermods themselves // which GPIO pins are actually used in a hardware layout (controller board) #if defined(SPISCLKPIN) && !defined(HW_PIN_CLOCKSPI) - #define HW_PIN_CLOCKSPI SPISCLKPIN +#define HW_PIN_CLOCKSPI SPISCLKPIN #endif #if defined(SPIMOSIPIN) && !defined(HW_PIN_MOSISPI) - #define HW_PIN_MOSISPI SPIMOSIPIN +#define HW_PIN_MOSISPI SPIMOSIPIN #endif #if defined(SPIMISOPIN) && !defined(HW_PIN_MISOSPI) - #define HW_PIN_MISOSPI SPIMISOPIN +#define HW_PIN_MISOSPI SPIMISOPIN #endif // you cannot change HW SPI pins on 8266 #if defined(ESP8266) && defined(HW_PIN_CLOCKSPI) - #undef HW_PIN_CLOCKSPI +#undef HW_PIN_CLOCKSPI #endif #if defined(ESP8266) && defined(HW_PIN_DATASPI) - #undef HW_PIN_DATASPI +#undef HW_PIN_DATASPI #endif #if defined(ESP8266) && defined(HW_PIN_MISOSPI) - #undef HW_PIN_MISOSPI +#undef HW_PIN_MISOSPI #endif // defaults for VSPI on ESP32 (SPI global, SPI.cpp) as HSPI is used by WLED (bus_wrapper.h) #ifndef HW_PIN_CLOCKSPI - #define HW_PIN_CLOCKSPI SCK +#define HW_PIN_CLOCKSPI SCK #endif #ifndef HW_PIN_DATASPI - #define HW_PIN_DATASPI MOSI +#define HW_PIN_DATASPI MOSI #endif #ifndef HW_PIN_MISOSPI - #define HW_PIN_MISOSPI MISO +#define HW_PIN_MISOSPI MISO #endif // IRAM_ATTR for 8266 with 32Kb IRAM causes error: section `.text1' will not fit in region `iram1_0_seg' // this hack removes the IRAM flag for some 1D/2D functions - somewhat slower, but it solves problems with some older 8266 chips #ifdef WLED_SAVE_IRAM - #define IRAM_ATTR_YN +#define IRAM_ATTR_YN #else - #define IRAM_ATTR_YN IRAM_ATTR +#define IRAM_ATTR_YN IRAM_ATTR #endif #endif From c72e12e79743983e5d2601d09f2684008b267a09 Mon Sep 17 00:00:00 2001 From: Flavio Medeiros Date: Tue, 15 Apr 2025 23:20:21 -0300 Subject: [PATCH 2/4] Added simulator counter --- .../SocialNetworkFactory.h | 10 +- .../SocialNetworkTypes.h | 8 +- .../usermod_v2_social_counter/library.json | 2 +- usermods/usermod_v2_social_counter/readme.md | 76 ++++- .../{ => strategies}/InstagramStrategy.h | 14 +- .../{ => strategies}/SocialNetworkStrategy.h | 0 .../{ => strategies}/TikTokStrategy.h | 0 .../{ => strategies}/TwitchStrategy.h | 0 .../{ => strategies}/YouTubeStrategy.h | 0 .../usermod_v2_social_counter.cpp | 321 ++++++++++++++++-- 10 files changed, 378 insertions(+), 53 deletions(-) rename usermods/usermod_v2_social_counter/{ => strategies}/InstagramStrategy.h (76%) rename usermods/usermod_v2_social_counter/{ => strategies}/SocialNetworkStrategy.h (100%) rename usermods/usermod_v2_social_counter/{ => strategies}/TikTokStrategy.h (100%) rename usermods/usermod_v2_social_counter/{ => strategies}/TwitchStrategy.h (100%) rename usermods/usermod_v2_social_counter/{ => strategies}/YouTubeStrategy.h (100%) diff --git a/usermods/usermod_v2_social_counter/SocialNetworkFactory.h b/usermods/usermod_v2_social_counter/SocialNetworkFactory.h index cef731745e..f7995c18fa 100644 --- a/usermods/usermod_v2_social_counter/SocialNetworkFactory.h +++ b/usermods/usermod_v2_social_counter/SocialNetworkFactory.h @@ -1,11 +1,11 @@ #pragma once -#include "SocialNetworkStrategy.h" +#include "strategies/SocialNetworkStrategy.h" #include "SocialNetworkTypes.h" -#include "InstagramStrategy.h" -#include "YouTubeStrategy.h" -#include "TikTokStrategy.h" -#include "TwitchStrategy.h" +#include "strategies/InstagramStrategy.h" +#include "strategies/YouTubeStrategy.h" +#include "strategies/TikTokStrategy.h" +#include "strategies/TwitchStrategy.h" #include class SocialNetworkFactory diff --git a/usermods/usermod_v2_social_counter/SocialNetworkTypes.h b/usermods/usermod_v2_social_counter/SocialNetworkTypes.h index e533b97886..c86476992f 100644 --- a/usermods/usermod_v2_social_counter/SocialNetworkTypes.h +++ b/usermods/usermod_v2_social_counter/SocialNetworkTypes.h @@ -1,7 +1,7 @@ #pragma once // Definição dos tipos de redes sociais -#define SOCIAL_COUNTER_INSTAGRAM 1 -#define SOCIAL_COUNTER_TIKTOK 2 -#define SOCIAL_COUNTER_TWITCH 3 -#define SOCIAL_COUNTER_YOUTUBE 4 \ No newline at end of file +#define SOCIAL_COUNTER_INSTAGRAM 0 +#define SOCIAL_COUNTER_TIKTOK 1 +#define SOCIAL_COUNTER_TWITCH 2 +#define SOCIAL_COUNTER_YOUTUBE 3 \ No newline at end of file diff --git a/usermods/usermod_v2_social_counter/library.json b/usermods/usermod_v2_social_counter/library.json index 3540aff275..28d883fb35 100644 --- a/usermods/usermod_v2_social_counter/library.json +++ b/usermods/usermod_v2_social_counter/library.json @@ -1,4 +1,4 @@ { - "name:": "usermod_v2_social_counter", + "name:": "social_counter", "dependencies": {} } diff --git a/usermods/usermod_v2_social_counter/readme.md b/usermods/usermod_v2_social_counter/readme.md index 2d27ad2487..a534b1dc62 100644 --- a/usermods/usermod_v2_social_counter/readme.md +++ b/usermods/usermod_v2_social_counter/readme.md @@ -1,16 +1,78 @@ -# Usermods API v2 example usermod +# Social Counter Usermod -In this usermod file you can find the documentation on how to take advantage of the new version 2 usermods! +This usermod allows you to display follower counts from various social networks on a 7-segment display using WLED LEDs. ## Installation -Add the build flag `-D usermod_v2_social_counter` to your platformio environment. +There are two ways to enable this usermod: -```ini -[platformio] -default_envs = esp32dev_social_counter +### Method 1: Using platformio_override.ini (Recommended) + +Create or modify your `platformio_override.ini` file to include the Social Counter: +```ini [env:esp32dev_social_counter] extends = env:esp32dev -custom_usermods = usermod_v2_social_counter +custom_usermods = social_counter ``` + +### Method 2: Using the compile option + +Add the compile-time option `-D USERMOD_SOCIAL_COUNTER` to your `platformio.ini` (or `platformio_override.ini`) or use `#define USERMOD_SOCIAL_COUNTER` in `my_config.h`. + +## Usage + +After installation, configure the module through the WLED interface: + +1. Access the WLED web interface +2. Go to "Config" > "Usermods" +3. Find the "Social Counter" section +4. Set: + - Enable/disable the module + - Desired social network (Instagram, TikTok, Twitch, YouTube) + - Link to your profile + - Update interval (in seconds) + - WLED segment index to use for display + - LEDs per segment + - Digit spacing + - Segment direction + +## Supported Social Networks + +- Instagram +- TikTok +- Twitch +- YouTube + +## 7-Segment Display + +This usermod uses a 7-segment display to show the follower count: + +- Segments are arranged in the FABCDEG pattern +- You can define how many LEDs each segment uses +- The display automatically uses WLED segments and their defined colors +- The maximum number of digits displayed is automatically calculated based on segment size + +## Customization + +The code uses the Strategy pattern to implement different social networks. You can easily add support for new social networks by creating a new strategy class in the `strategies` folder. + +## File Structure + +- `usermod_v2_social_counter.cpp` - Main usermod implementation +- `SocialNetworkTypes.h` - Social network type definitions +- `SocialNetworkFactory.h` - Factory for creating strategy instances +- `strategies/` - Folder containing implementations for each social network + - `SocialNetworkStrategy.h` - Interface for strategies + - `InstagramStrategy.h` - Instagram implementation + - `TikTokStrategy.h` - TikTok implementation + - `TwitchStrategy.h` - Twitch implementation + - `YouTubeStrategy.h` - YouTube implementation + +## Debugging + +If you're experiencing issues: + +1. Enable serial logging in WLED +2. Check for messages with the "Social Counter" prefix in the serial output +3. Verify that the time interval configuration is correct diff --git a/usermods/usermod_v2_social_counter/InstagramStrategy.h b/usermods/usermod_v2_social_counter/strategies/InstagramStrategy.h similarity index 76% rename from usermods/usermod_v2_social_counter/InstagramStrategy.h rename to usermods/usermod_v2_social_counter/strategies/InstagramStrategy.h index d2b95c85da..9910344950 100644 --- a/usermods/usermod_v2_social_counter/InstagramStrategy.h +++ b/usermods/usermod_v2_social_counter/strategies/InstagramStrategy.h @@ -5,17 +5,21 @@ #include #include +// Variável global para o contador +static int mockCounter = -1; + class InstagramStrategy : public SocialNetworkStrategy { public: bool fetchFollowerCount(const String &link, int &count) override { - // Implementação para buscar contagem de seguidores do Instagram - // Para evitar problemas de dependência, usamos um método simplificado - // sem parse de JSON + // Incrementa o contador a cada chamada + mockCounter = (mockCounter + 1) % 100; // Incrementa de 0 a 99 + + // Retorna o valor atual do contador + count = mockCounter; - // Para demonstração, retorna um número aleatório - count = random(1000, 10000); + Serial.printf("[MOCK] Instagram contador: %d\n", count); return true; } diff --git a/usermods/usermod_v2_social_counter/SocialNetworkStrategy.h b/usermods/usermod_v2_social_counter/strategies/SocialNetworkStrategy.h similarity index 100% rename from usermods/usermod_v2_social_counter/SocialNetworkStrategy.h rename to usermods/usermod_v2_social_counter/strategies/SocialNetworkStrategy.h diff --git a/usermods/usermod_v2_social_counter/TikTokStrategy.h b/usermods/usermod_v2_social_counter/strategies/TikTokStrategy.h similarity index 100% rename from usermods/usermod_v2_social_counter/TikTokStrategy.h rename to usermods/usermod_v2_social_counter/strategies/TikTokStrategy.h diff --git a/usermods/usermod_v2_social_counter/TwitchStrategy.h b/usermods/usermod_v2_social_counter/strategies/TwitchStrategy.h similarity index 100% rename from usermods/usermod_v2_social_counter/TwitchStrategy.h rename to usermods/usermod_v2_social_counter/strategies/TwitchStrategy.h diff --git a/usermods/usermod_v2_social_counter/YouTubeStrategy.h b/usermods/usermod_v2_social_counter/strategies/YouTubeStrategy.h similarity index 100% rename from usermods/usermod_v2_social_counter/YouTubeStrategy.h rename to usermods/usermod_v2_social_counter/strategies/YouTubeStrategy.h diff --git a/usermods/usermod_v2_social_counter/usermod_v2_social_counter.cpp b/usermods/usermod_v2_social_counter/usermod_v2_social_counter.cpp index 0d677a40ee..5614146d30 100644 --- a/usermods/usermod_v2_social_counter/usermod_v2_social_counter.cpp +++ b/usermods/usermod_v2_social_counter/usermod_v2_social_counter.cpp @@ -1,9 +1,25 @@ #include "wled.h" #include "SocialNetworkTypes.h" -#include "SocialNetworkStrategy.h" +#include "strategies/SocialNetworkStrategy.h" #include "SocialNetworkFactory.h" #include +#define SOCIAL_SEGMENT_INDEX 0 + +/* +// wire layout: FABCDEG +// ------- +// / A / +// / F / B +// / / +// ------- +// / G / +// / E / C +// / / +// ------- +// D +*/ + class SocialCounterUsermod : public Usermod { @@ -17,7 +33,45 @@ class SocialCounterUsermod : public Usermod String link = ""; int social = SOCIAL_COUNTER_INSTAGRAM; int followerCount = 0; - int updateIntervalSeconds = 60; + int updateIntervalSeconds = 3; + + // Informações sobre o segmento atual + uint16_t segmentStart = 0; + uint16_t segmentLength = 0; + bool segmentValid = false; + + // Configurações do display de 7 segmentos + int ledsPerSegment = 2; // Quantidade de LEDs por segmento + int digitSpacing = 0; // Espaço entre dígitos + + // Posições dos segmentos no hardware (wire layout: FABCDEG) + typedef enum + { + SEG_F = 0, + SEG_A = 1, + SEG_B = 2, + SEG_C = 3, + SEG_D = 4, + SEG_E = 5, + SEG_G = 6 + } SegmentPosition; + + // Definição dos segmentos ativos para cada dígito + // Usando array bidimensional: [dígito][segmento] + // 1 = segmento ligado, 0 = segmento desligado + const bool digitSegments[10][7] = { + // F, A, B, C, D, E, G (FABCDEG - ordem física) + {1, 1, 1, 1, 1, 1, 0}, // 0 + {0, 0, 1, 1, 0, 0, 0}, // 1 + {0, 1, 1, 0, 1, 1, 1}, // 2 + {0, 1, 1, 1, 1, 0, 1}, // 3 + {1, 0, 1, 1, 0, 0, 1}, // 4 + {1, 1, 0, 1, 1, 0, 1}, // 5 + {1, 1, 0, 1, 1, 1, 1}, // 6 + {0, 1, 1, 1, 0, 0, 0}, // 7 + {1, 1, 1, 1, 1, 1, 1}, // 8 + {1, 1, 1, 1, 1, 0, 1} // 9 + }; std::unique_ptr strategy; @@ -26,6 +80,161 @@ class SocialCounterUsermod : public Usermod strategy = SocialNetworkFactory::createStrategy(social); } + /** + * Detecta e configura informações sobre o segmento atual + * @return true se o segmento foi encontrado e configurado com sucesso + */ + bool detectSegment() + { + if (strip.getActiveSegmentsNum() == 0) + { + Serial.println("[ERRO] Nenhum segmento WLED encontrado!"); + segmentValid = false; + return false; + } + + // Verifica se existe pelo menos um segmento + if (SOCIAL_SEGMENT_INDEX >= strip.getActiveSegmentsNum()) + { + Serial.printf("[ALERTA] Não há segmento %d, mas encontrados %d segmentos\n", + SOCIAL_SEGMENT_INDEX, strip.getActiveSegmentsNum()); + segmentValid = false; + return false; + } + + // Obtém as informações do segmento + Segment &seg = strip.getSegment(SOCIAL_SEGMENT_INDEX); + segmentStart = seg.start; + + // Calcula o comprimento do segmento, sem adicionar +1 + // Isso assume que seg.stop é o índice após o último LED (exclusivo), não o último índice (inclusivo) + segmentLength = seg.stop - seg.start; + + Serial.printf("[INFO] Segmento %d configurado: LEDs %d a %d (total: %d)\n", + SOCIAL_SEGMENT_INDEX, segmentStart, seg.stop - 1, segmentLength); + + segmentValid = true; + return true; + } + + /** + * Obtém o índice do LED inicial para uma posição específica de segmento + * @param digit Posição do dígito (0 = dígito mais à direita) + * @param segPos Posição do segmento (0-6 = F,A,B,C,D,E,G) + * @return Índice do primeiro LED do segmento + */ + uint16_t getSegmentLedIndex(int digit, int segPos) + { + // Calcula a posição base do dígito + int digitWidth = 7 * ledsPerSegment + digitSpacing; + int digitPosition = digitWidth * digit; + + // Calcula o índice do LED + return segmentStart + digitPosition + (segPos * ledsPerSegment); + } + + /** + * Define o estado dos LEDs para um segmento específico + * @param digit Posição do dígito + * @param segPos Posição do segmento (0-6) + * @param on Estado do segmento (true = ligado, false = desligado) + */ + void setSegmentLeds(int digit, int segPos, bool on) + { + uint16_t ledIndex = getSegmentLedIndex(digit, segPos); + + for (int i = 0; i < ledsPerSegment; i++) + { + if (!on) + { + // Se o segmento deve estar apagado, desligamos o LED + strip.setPixelColor(ledIndex + i, 0); + } + // Se o segmento está ativo, não fazemos nada e mantemos os efeitos originais + } + } + + /** + * Exibe um número no display de 7 segmentos + * @param number Número a ser exibido + */ + void displayNumber(int number) + { + if (!segmentValid) + { + return; // Não temos um segmento válido para exibir + } + + // Calcula quantos dígitos podem caber no segmento + int digitWidth = 7 * ledsPerSegment + digitSpacing; + int maxDigits = segmentLength / digitWidth; + + if (maxDigits <= 0) + { + Serial.println("[ERRO] Segmento muito pequeno para exibir qualquer dígito!"); + return; + } + + // Forçamos no mínimo 2 dígitos para valores entre 0-99 + int minDigits = 2; + + // Limita o número ao máximo de dígitos disponíveis + int maxValue = 1; + for (int i = 0; i < maxDigits; i++) + { + maxValue *= 10; + } + number = number % maxValue; + + // Exibe cada dígito - primeiro preparamos um mapa dos LEDs ativos + bool ledActive[segmentLength]; + // Inicializa todos como inativos + for (int i = 0; i < segmentLength; i++) + { + ledActive[i] = false; + } + + // Marca os LEDs que devem estar ativos + int digitsShown = 0; + for (int digit = 0; digit < maxDigits; digit++) + { + int digitValue = number % 10; + + // Para cada segmento do dígito + for (int seg = 0; seg < 7; seg++) + { + if (digitSegments[digitValue][seg]) + { + // Este segmento deve estar ativo + int ledStart = getSegmentLedIndex(digit, seg) - segmentStart; + for (int i = 0; i < ledsPerSegment; i++) + { + if (ledStart + i < segmentLength) + { + ledActive[ledStart + i] = true; + } + } + } + } + + digitsShown++; + number /= 10; + + // Continua exibindo dígitos até atingir o mínimo de dígitos ou zerar o número + if (number == 0 && digitsShown >= minDigits) + break; + } + + // Agora, desliga apenas os LEDs que devem estar inativos + for (int i = 0; i < segmentLength; i++) + { + if (!ledActive[i]) + { + strip.setPixelColor(segmentStart + i, 0); + } + } + } + public: static const char _name[]; static const char _enabled[]; @@ -33,6 +242,7 @@ class SocialCounterUsermod : public Usermod void enable(bool enable) { + Serial.printf("[INFO] Social Counter %s\n", enable ? "ativo" : "inativo"); enabled = enable; } @@ -43,16 +253,19 @@ class SocialCounterUsermod : public Usermod void setup() override { - Serial.println("Hello from my social counter!"); + Serial.println("[INFO] Iniciando Social Counter usermod"); updateStrategy(); updateIntervalTimer(); + detectSegment(); // Detecta o segmento durante a inicialização initDone = true; counter = 0; } void updateIntervalTimer() { - apiCallInterval = ((unsigned long)updateIntervalSeconds) * 1000; + // apiCallInterval = ((unsigned long)updateIntervalSeconds) * 1000; + + apiCallInterval = 500; // para agilizar os testes Serial.print("Setting update interval to: "); Serial.print(updateIntervalSeconds); Serial.print(" seconds ("); @@ -62,7 +275,12 @@ class SocialCounterUsermod : public Usermod void connected() override { - Serial.println("Connected to WiFi!"); + Serial.println("[INFO] Conectado ao WiFi!"); + Serial.print("[INFO] Endereço IP local: "); + Serial.println(WiFi.localIP()); + + // Atualiza as informações do segmento após a conexão + detectSegment(); if (enabled) { @@ -75,21 +293,14 @@ class SocialCounterUsermod : public Usermod if (!enabled || strip.isUpdating()) return; - if (millis() - lastTime > 1000) + if (millis() - lastTime > 10000) // Log a cada 10 segundos apenas { - // Serial.println("I'm alive!"); lastTime = millis(); } unsigned long currentMillis = millis(); if (WLED_CONNECTED && (currentMillis - lastAPICall >= apiCallInterval)) { - Serial.print("Timer triggered. Time since last call: "); - Serial.print(currentMillis - lastAPICall); - Serial.print("ms (configured for "); - Serial.print(apiCallInterval); - Serial.println("ms)"); - updateFollowerCount(); lastAPICall = currentMillis; // Registra o tempo exato da chamada } @@ -97,6 +308,8 @@ class SocialCounterUsermod : public Usermod void updateFollowerCount() { + Serial.println("[INFO] Atualizando contagem de seguidores"); + if (!strategy) { updateStrategy(); @@ -111,12 +324,17 @@ class SocialCounterUsermod : public Usermod { followerCount = count; counter = followerCount; // Atualiza o contador estático para compatibilidade - Serial.print("Follower count for "); - Serial.print(strategy->getName()); - Serial.print(": "); - Serial.println(followerCount); + Serial.printf("[INFO] %s followers: %d\n", strategy->getName().c_str(), followerCount); + } + else + { + Serial.println("[ERRO] Falha ao buscar contagem de seguidores"); } } + else + { + Serial.println("[ERRO] Impossível atualizar - estratégia inválida ou link vazio"); + } } void addToJsonInfo(JsonObject &root) override @@ -168,6 +386,12 @@ class SocialCounterUsermod : public Usermod updateIntervalSeconds = newInterval; updateIntervalTimer(); } + + bool newEnabled = usermod["enabled"] | enabled; + if (newEnabled != enabled) + { + enable(newEnabled); + } } } @@ -178,6 +402,10 @@ class SocialCounterUsermod : public Usermod top["social"] = social; top["link"] = link; top["updateInterval"] = updateIntervalSeconds; + + // Configurações do display de 7 segmentos + top["ledsPerSegment"] = ledsPerSegment; + top["digitSpacing"] = digitSpacing; } bool readFromConfig(JsonObject &root) override @@ -198,6 +426,11 @@ class SocialCounterUsermod : public Usermod configComplete &= getJsonValue(top["link"], link, ""); configComplete &= getJsonValue(top["updateInterval"], updateIntervalSeconds, updateIntervalSeconds); + + // Configurações do display de 7 segmentos + configComplete &= getJsonValue(top["ledsPerSegment"], ledsPerSegment, 2); + configComplete &= getJsonValue(top["digitSpacing"], digitSpacing, 0); + updateIntervalTimer(); return configComplete; @@ -205,37 +438,63 @@ class SocialCounterUsermod : public Usermod void appendConfigData() override { - oappend(F("addInfo('")); - oappend(String(FPSTR(_name)).c_str()); - oappend(F(":great")); - oappend(F("',1,'(this is a great config value)');")); oappend(F("addInfo('")); oappend(String(FPSTR(_name)).c_str()); oappend(F(":link")); - oappend(F("',1,'enter your link');")); + oappend(F("',1,'Enter your link');")); + oappend(F("addInfo('")); oappend(String(FPSTR(_name)).c_str()); oappend(F(":updateInterval")); - oappend(F("',1,'Delay in seconds (default: 60)');")); + oappend(F("',1,'Delay in seconds between updates (default: 3)');")); + + oappend(F("addInfo('")); + oappend(String(FPSTR(_name)).c_str()); + oappend(F(":ledsPerSegment")); + oappend(F("',1,'LEDs por segmento (default: 2)');")); + + oappend(F("addInfo('")); + oappend(String(FPSTR(_name)).c_str()); + oappend(F(":digitSpacing")); + oappend(F("',1,'Espaço entre dígitos (default: 0)');")); + oappend(F("dd=addDropdown('")); oappend(String(FPSTR(_name)).c_str()); oappend(F("','social');")); - oappend(F("addOption(dd,'Instagram',1);")); - oappend(F("addOption(dd,'Tiktok',2);")); - oappend(F("addOption(dd,'Twitch',3);")); - oappend(F("addOption(dd,'Youtube',4);")); + oappend(F("addOption(dd,'Instagram',0);")); + oappend(F("addOption(dd,'Tiktok',1);")); + oappend(F("addOption(dd,'Twitch',2);")); + oappend(F("addOption(dd,'Youtube',3);")); } void handleOverlayDraw() override { - // Implementação para exibir o contador de seguidores na interface, se necessário - // strip.setPixelColor(0, RGBW32(0,0,0,0)) + // Exibe o número de seguidores no display de 7 segmentos + if (enabled && initDone && segmentValid) + { + displayNumber(followerCount); + } } uint16_t getId() override { return USERMOD_ID_SOCIAL_COUNTER; } + + /** + * Chamado quando o estado do WLED é alterado (por exemplo, quando segmentos são modificados) + */ + void onStateChange(uint8_t callMode) override + { + // Atualiza as informações do segmento quando o estado muda + if (callMode == CALL_MODE_DIRECT_CHANGE || + callMode == CALL_MODE_BUTTON || + callMode == CALL_MODE_NOTIFICATION) + { + Serial.printf("[INFO] Estado WLED alterado (modo: %d), verificando segmento\n", callMode); + detectSegment(); + } + } }; // Definição das variáveis estáticas @@ -243,5 +502,5 @@ const char SocialCounterUsermod::_name[] = "Social Counter"; const char SocialCounterUsermod::_enabled[] = "enabled"; int SocialCounterUsermod::counter = 0; -static SocialCounterUsermod usermod_v2_social_counter; -REGISTER_USERMOD(usermod_v2_social_counter); \ No newline at end of file +static SocialCounterUsermod social_counter; +REGISTER_USERMOD(social_counter); \ No newline at end of file From 3a019d7d9c400546fa138f0ad2f77fe13a04ac76 Mon Sep 17 00:00:00 2001 From: Flavio Medeiros Date: Tue, 22 Apr 2025 15:17:47 -0300 Subject: [PATCH 3/4] WIP adding 7 segments social counter --- ...workFactory.h => social_network_factory.h} | 12 ++-- ...lNetworkTypes.h => social_network_types.h} | 0 ...stagramStrategy.h => instagram_strategy.h} | 17 +++-- ...rkStrategy.h => social_network_strategy.h} | 0 .../{TikTokStrategy.h => tiktok_strategy.h} | 4 +- .../{TwitchStrategy.h => twitch_strategy.h} | 4 +- .../{YouTubeStrategy.h => youtube_strategy.h} | 4 +- .../usermod_v2_social_counter.cpp | 68 ++++++++++++++----- 8 files changed, 75 insertions(+), 34 deletions(-) rename usermods/usermod_v2_social_counter/{SocialNetworkFactory.h => social_network_factory.h} (78%) rename usermods/usermod_v2_social_counter/{SocialNetworkTypes.h => social_network_types.h} (100%) rename usermods/usermod_v2_social_counter/strategies/{InstagramStrategy.h => instagram_strategy.h} (71%) rename usermods/usermod_v2_social_counter/strategies/{SocialNetworkStrategy.h => social_network_strategy.h} (100%) rename usermods/usermod_v2_social_counter/strategies/{TikTokStrategy.h => tiktok_strategy.h} (95%) rename usermods/usermod_v2_social_counter/strategies/{TwitchStrategy.h => twitch_strategy.h} (93%) rename usermods/usermod_v2_social_counter/strategies/{YouTubeStrategy.h => youtube_strategy.h} (95%) diff --git a/usermods/usermod_v2_social_counter/SocialNetworkFactory.h b/usermods/usermod_v2_social_counter/social_network_factory.h similarity index 78% rename from usermods/usermod_v2_social_counter/SocialNetworkFactory.h rename to usermods/usermod_v2_social_counter/social_network_factory.h index f7995c18fa..8a6a0302ef 100644 --- a/usermods/usermod_v2_social_counter/SocialNetworkFactory.h +++ b/usermods/usermod_v2_social_counter/social_network_factory.h @@ -1,11 +1,11 @@ #pragma once -#include "strategies/SocialNetworkStrategy.h" -#include "SocialNetworkTypes.h" -#include "strategies/InstagramStrategy.h" -#include "strategies/YouTubeStrategy.h" -#include "strategies/TikTokStrategy.h" -#include "strategies/TwitchStrategy.h" +#include "strategies/social_network_strategy.h" +#include "social_network_types.h" +#include "strategies/instagram_strategy.h" +#include "strategies/youtube_strategy.h" +#include "strategies/tiktok_strategy.h" +#include "strategies/twitch_strategy.h" #include class SocialNetworkFactory diff --git a/usermods/usermod_v2_social_counter/SocialNetworkTypes.h b/usermods/usermod_v2_social_counter/social_network_types.h similarity index 100% rename from usermods/usermod_v2_social_counter/SocialNetworkTypes.h rename to usermods/usermod_v2_social_counter/social_network_types.h diff --git a/usermods/usermod_v2_social_counter/strategies/InstagramStrategy.h b/usermods/usermod_v2_social_counter/strategies/instagram_strategy.h similarity index 71% rename from usermods/usermod_v2_social_counter/strategies/InstagramStrategy.h rename to usermods/usermod_v2_social_counter/strategies/instagram_strategy.h index 9910344950..a3a01a1066 100644 --- a/usermods/usermod_v2_social_counter/strategies/InstagramStrategy.h +++ b/usermods/usermod_v2_social_counter/strategies/instagram_strategy.h @@ -1,7 +1,7 @@ #pragma once -#include "SocialNetworkStrategy.h" -#include "SocialNetworkTypes.h" +#include "social_network_strategy.h" +#include "../social_network_types.h" #include #include @@ -14,10 +14,17 @@ class InstagramStrategy : public SocialNetworkStrategy bool fetchFollowerCount(const String &link, int &count) override { // Incrementa o contador a cada chamada - mockCounter = (mockCounter + 1) % 100; // Incrementa de 0 a 99 + mockCounter = (mockCounter + 1) % 10; // Incrementa de 0 a 9 para cada dígito - // Retorna o valor atual do contador - count = mockCounter; + // Gera uma sequência de 6 dígitos repetidos (111111, 222222, etc) + int repeatedDigits = 0; + for (int i = 0; i < 6; i++) + { + repeatedDigits = repeatedDigits * 10 + mockCounter; + } + + // Retorna o valor com dígitos repetidos + count = repeatedDigits; Serial.printf("[MOCK] Instagram contador: %d\n", count); return true; diff --git a/usermods/usermod_v2_social_counter/strategies/SocialNetworkStrategy.h b/usermods/usermod_v2_social_counter/strategies/social_network_strategy.h similarity index 100% rename from usermods/usermod_v2_social_counter/strategies/SocialNetworkStrategy.h rename to usermods/usermod_v2_social_counter/strategies/social_network_strategy.h diff --git a/usermods/usermod_v2_social_counter/strategies/TikTokStrategy.h b/usermods/usermod_v2_social_counter/strategies/tiktok_strategy.h similarity index 95% rename from usermods/usermod_v2_social_counter/strategies/TikTokStrategy.h rename to usermods/usermod_v2_social_counter/strategies/tiktok_strategy.h index 665f93e4c2..c1c8bda13b 100644 --- a/usermods/usermod_v2_social_counter/strategies/TikTokStrategy.h +++ b/usermods/usermod_v2_social_counter/strategies/tiktok_strategy.h @@ -1,7 +1,7 @@ #pragma once -#include "SocialNetworkStrategy.h" -#include "SocialNetworkTypes.h" +#include "social_network_strategy.h" +#include "../social_network_types.h" #include #include diff --git a/usermods/usermod_v2_social_counter/strategies/TwitchStrategy.h b/usermods/usermod_v2_social_counter/strategies/twitch_strategy.h similarity index 93% rename from usermods/usermod_v2_social_counter/strategies/TwitchStrategy.h rename to usermods/usermod_v2_social_counter/strategies/twitch_strategy.h index 40351c0106..4e26a0d946 100644 --- a/usermods/usermod_v2_social_counter/strategies/TwitchStrategy.h +++ b/usermods/usermod_v2_social_counter/strategies/twitch_strategy.h @@ -1,7 +1,7 @@ #pragma once -#include "SocialNetworkStrategy.h" -#include "SocialNetworkTypes.h" +#include "social_network_strategy.h" +#include "../social_network_types.h" #include #include diff --git a/usermods/usermod_v2_social_counter/strategies/YouTubeStrategy.h b/usermods/usermod_v2_social_counter/strategies/youtube_strategy.h similarity index 95% rename from usermods/usermod_v2_social_counter/strategies/YouTubeStrategy.h rename to usermods/usermod_v2_social_counter/strategies/youtube_strategy.h index eb2800bf64..e4cce23aef 100644 --- a/usermods/usermod_v2_social_counter/strategies/YouTubeStrategy.h +++ b/usermods/usermod_v2_social_counter/strategies/youtube_strategy.h @@ -1,7 +1,7 @@ #pragma once -#include "SocialNetworkStrategy.h" -#include "SocialNetworkTypes.h" +#include "social_network_strategy.h" +#include "../social_network_types.h" #include #include diff --git a/usermods/usermod_v2_social_counter/usermod_v2_social_counter.cpp b/usermods/usermod_v2_social_counter/usermod_v2_social_counter.cpp index 5614146d30..289d25eebd 100644 --- a/usermods/usermod_v2_social_counter/usermod_v2_social_counter.cpp +++ b/usermods/usermod_v2_social_counter/usermod_v2_social_counter.cpp @@ -1,7 +1,7 @@ #include "wled.h" -#include "SocialNetworkTypes.h" -#include "strategies/SocialNetworkStrategy.h" -#include "SocialNetworkFactory.h" +#include "social_network_types.h" +#include "strategies/social_network_strategy.h" +#include "social_network_factory.h" #include #define SOCIAL_SEGMENT_INDEX 0 @@ -41,8 +41,9 @@ class SocialCounterUsermod : public Usermod bool segmentValid = false; // Configurações do display de 7 segmentos - int ledsPerSegment = 2; // Quantidade de LEDs por segmento - int digitSpacing = 0; // Espaço entre dígitos + int ledsPerSegment = 2; // Quantidade de LEDs por segmento + int digitSpacing = 0; // Espaço entre dígitos + bool showLeadingZeros = true; // Controla se zeros à esquerda devem ser exibidos (alterado para true por padrão) // Posições dos segmentos no hardware (wire layout: FABCDEG) typedef enum @@ -175,8 +176,9 @@ class SocialCounterUsermod : public Usermod return; } - // Forçamos no mínimo 2 dígitos para valores entre 0-99 - int minDigits = 2; + // Forçamos minDigits = 1 para garantir que pelo menos um dígito seja exibido + // A exibição de zeros à esquerda é controlada por showLeadingZeros + int minDigits = 1; // Limita o número ao máximo de dígitos disponíveis int maxValue = 1; @@ -186,6 +188,18 @@ class SocialCounterUsermod : public Usermod } number = number % maxValue; + // Calcula quantos dígitos o número possui + int tempNumber = number; + int numDigits = 0; + do + { + numDigits++; + tempNumber /= 10; + } while (tempNumber > 0); + + // Garantir que temos pelo menos o número mínimo de dígitos + numDigits = max(numDigits, minDigits); + // Exibe cada dígito - primeiro preparamos um mapa dos LEDs ativos bool ledActive[segmentLength]; // Inicializa todos como inativos @@ -200,18 +214,31 @@ class SocialCounterUsermod : public Usermod { int digitValue = number % 10; - // Para cada segmento do dígito - for (int seg = 0; seg < 7; seg++) + // Decide se mostra este dígito baseado nas configurações de zeros à esquerda + bool showDigit = true; + + // Se este dígito é zero e está além dos dígitos significativos do número + // e não estamos mostrando zeros à esquerda, pulamos este dígito + if (digitValue == 0 && digit >= numDigits && !showLeadingZeros) + { + showDigit = false; + } + + if (showDigit) { - if (digitSegments[digitValue][seg]) + // Para cada segmento do dígito + for (int seg = 0; seg < 7; seg++) { - // Este segmento deve estar ativo - int ledStart = getSegmentLedIndex(digit, seg) - segmentStart; - for (int i = 0; i < ledsPerSegment; i++) + if (digitSegments[digitValue][seg]) { - if (ledStart + i < segmentLength) + // Este segmento deve estar ativo + int ledStart = getSegmentLedIndex(digit, seg) - segmentStart; + for (int i = 0; i < ledsPerSegment; i++) { - ledActive[ledStart + i] = true; + if (ledStart + i < segmentLength) + { + ledActive[ledStart + i] = true; + } } } } @@ -263,9 +290,9 @@ class SocialCounterUsermod : public Usermod void updateIntervalTimer() { - // apiCallInterval = ((unsigned long)updateIntervalSeconds) * 1000; + // Usa o valor configurado pelo usuário + apiCallInterval = ((unsigned long)updateIntervalSeconds) * 1000; - apiCallInterval = 500; // para agilizar os testes Serial.print("Setting update interval to: "); Serial.print(updateIntervalSeconds); Serial.print(" seconds ("); @@ -406,6 +433,7 @@ class SocialCounterUsermod : public Usermod // Configurações do display de 7 segmentos top["ledsPerSegment"] = ledsPerSegment; top["digitSpacing"] = digitSpacing; + top["showLeadingZeros"] = showLeadingZeros; } bool readFromConfig(JsonObject &root) override @@ -430,6 +458,7 @@ class SocialCounterUsermod : public Usermod // Configurações do display de 7 segmentos configComplete &= getJsonValue(top["ledsPerSegment"], ledsPerSegment, 2); configComplete &= getJsonValue(top["digitSpacing"], digitSpacing, 0); + configComplete &= getJsonValue(top["showLeadingZeros"], showLeadingZeros, true); updateIntervalTimer(); @@ -458,6 +487,11 @@ class SocialCounterUsermod : public Usermod oappend(F(":digitSpacing")); oappend(F("',1,'Espaço entre dígitos (default: 0)');")); + oappend(F("addInfo('")); + oappend(String(FPSTR(_name)).c_str()); + oappend(F(":showLeadingZeros")); + oappend(F("',1,'Mostrar zeros à esquerda (default: true)');")); + oappend(F("dd=addDropdown('")); oappend(String(FPSTR(_name)).c_str()); oappend(F("','social');")); From b7fb2808ac955f9fc6c300d607800251ac6c834c Mon Sep 17 00:00:00 2001 From: Flavio Medeiros Date: Thu, 15 May 2025 18:03:48 -0300 Subject: [PATCH 4/4] Added strategy pattener --- .../social_network_factory.h | 4 + .../social_network_types.h | 15 +- .../strategies/instagram_strategy.h | 65 +++-- .../strategies/mock_strategy.h | 35 +++ .../strategies/social_network_strategy.h | 2 +- .../strategies/tiktok_strategy.h | 2 +- .../strategies/twitch_strategy.h | 2 +- .../strategies/youtube_strategy.h | 2 +- .../usermod_v2_social_counter.cpp | 256 +++++++++++------- 9 files changed, 241 insertions(+), 142 deletions(-) create mode 100644 usermods/usermod_v2_social_counter/strategies/mock_strategy.h diff --git a/usermods/usermod_v2_social_counter/social_network_factory.h b/usermods/usermod_v2_social_counter/social_network_factory.h index 8a6a0302ef..e40a2accc1 100644 --- a/usermods/usermod_v2_social_counter/social_network_factory.h +++ b/usermods/usermod_v2_social_counter/social_network_factory.h @@ -2,6 +2,7 @@ #include "strategies/social_network_strategy.h" #include "social_network_types.h" +#include "strategies/mock_strategy.h" #include "strategies/instagram_strategy.h" #include "strategies/youtube_strategy.h" #include "strategies/tiktok_strategy.h" @@ -15,6 +16,9 @@ class SocialNetworkFactory { switch (socialType) { + case SOCIAL_COUNTER_MOCK: + return std::unique_ptr(new MockStrategy()); + case SOCIAL_COUNTER_INSTAGRAM: return std::unique_ptr(new InstagramStrategy()); diff --git a/usermods/usermod_v2_social_counter/social_network_types.h b/usermods/usermod_v2_social_counter/social_network_types.h index c86476992f..58c3c79c39 100644 --- a/usermods/usermod_v2_social_counter/social_network_types.h +++ b/usermods/usermod_v2_social_counter/social_network_types.h @@ -1,7 +1,14 @@ #pragma once // Definição dos tipos de redes sociais -#define SOCIAL_COUNTER_INSTAGRAM 0 -#define SOCIAL_COUNTER_TIKTOK 1 -#define SOCIAL_COUNTER_TWITCH 2 -#define SOCIAL_COUNTER_YOUTUBE 3 \ No newline at end of file +#define SOCIAL_COUNTER_MOCK 0 +#define SOCIAL_COUNTER_INSTAGRAM 1 +#define SOCIAL_COUNTER_TIKTOK 2 +#define SOCIAL_COUNTER_TWITCH 3 +#define SOCIAL_COUNTER_YOUTUBE 4 + +// Tipos de métrica disponíveis (por enquanto só Instagram usa) +#define METRIC_FOLLOWERS 0 +#define METRIC_VIEWS 1 +#define METRIC_LIVE 2 +#define METRIC_SUBSCRIBERS 3 diff --git a/usermods/usermod_v2_social_counter/strategies/instagram_strategy.h b/usermods/usermod_v2_social_counter/strategies/instagram_strategy.h index a3a01a1066..c7dc84f489 100644 --- a/usermods/usermod_v2_social_counter/strategies/instagram_strategy.h +++ b/usermods/usermod_v2_social_counter/strategies/instagram_strategy.h @@ -5,29 +5,24 @@ #include #include -// Variável global para o contador +// Variável global para simular o contador static int mockCounter = -1; class InstagramStrategy : public SocialNetworkStrategy { public: - bool fetchFollowerCount(const String &link, int &count) override + bool fetchMetric(int metric, const String &link, int &count) override { - // Incrementa o contador a cada chamada - mockCounter = (mockCounter + 1) % 10; // Incrementa de 0 a 9 para cada dígito - - // Gera uma sequência de 6 dígitos repetidos (111111, 222222, etc) - int repeatedDigits = 0; - for (int i = 0; i < 6; i++) + // Simula diferentes métricas para o Instagram (ainda que iguais por enquanto) + switch (metric) { - repeatedDigits = repeatedDigits * 10 + mockCounter; + case METRIC_FOLLOWERS: + case METRIC_VIEWS: + case METRIC_LIVE: + case METRIC_SUBSCRIBERS: + default: + return simulateMockFollowerCount(count); } - - // Retorna o valor com dígitos repetidos - count = repeatedDigits; - - Serial.printf("[MOCK] Instagram contador: %d\n", count); - return true; } String getName() override @@ -41,24 +36,36 @@ class InstagramStrategy : public SocialNetworkStrategy } private: + /** + * Simula a contagem de seguidores: 111111, 222222, ... + */ + bool simulateMockFollowerCount(int &count) + { + mockCounter = (mockCounter + 1) % 10; + + int repeatedDigits = 0; + for (int i = 0; i < 6; i++) + { + repeatedDigits = repeatedDigits * 10 + mockCounter; + } + + count = repeatedDigits; + + Serial.printf("[MOCK] Instagram seguidores simulados: %d\n", count); + return true; + } + + /** + * Extrai o nome de usuário a partir de uma URL do Instagram + */ String extractUsername(const String &link) { - // Extrai o nome de usuário de uma URL do Instagram - // Exemplo: https://www.instagram.com/username/ -> username int start = link.indexOf("instagram.com/"); if (start == -1) - return link; // Se não for URL, assume que é o nome do usuário + return link; - start += 14; // Comprimento de "instagram.com/" + start += 14; int end = link.indexOf("/", start); - - if (end == -1) - { - return link.substring(start); - } - else - { - return link.substring(start, end); - } + return (end == -1) ? link.substring(start) : link.substring(start, end); } -}; \ No newline at end of file +}; diff --git a/usermods/usermod_v2_social_counter/strategies/mock_strategy.h b/usermods/usermod_v2_social_counter/strategies/mock_strategy.h new file mode 100644 index 0000000000..b378480429 --- /dev/null +++ b/usermods/usermod_v2_social_counter/strategies/mock_strategy.h @@ -0,0 +1,35 @@ +#pragma once + +#include "social_network_strategy.h" +#include "../social_network_types.h" +#include + +// Simula um contador incremental por métrica +class MockStrategy : public SocialNetworkStrategy +{ +public: + bool fetchMetric(int metric, const String &link, int &count) override + { + static int values[4] = {0, 1000, 5000, 100}; + + // Simula cada métrica com incremento independente + values[metric] += (metric + 1) * 111; // muda a taxa por tipo + if (values[metric] > 999999) + values[metric] = 0; + + count = values[metric]; + + Serial.printf("[MOCK] Métrica %d simulada: %d\n", metric, count); + return true; + } + + String getName() override + { + return "Mock"; + } + + int getType() override + { + return SOCIAL_COUNTER_MOCK; + } +}; diff --git a/usermods/usermod_v2_social_counter/strategies/social_network_strategy.h b/usermods/usermod_v2_social_counter/strategies/social_network_strategy.h index c479b92a64..fa0da945f9 100644 --- a/usermods/usermod_v2_social_counter/strategies/social_network_strategy.h +++ b/usermods/usermod_v2_social_counter/strategies/social_network_strategy.h @@ -8,7 +8,7 @@ class SocialNetworkStrategy virtual ~SocialNetworkStrategy() = default; // Métodos que toda rede social deve implementar - virtual bool fetchFollowerCount(const String &link, int &count) = 0; + virtual bool fetchMetric(int metric, const String &link, int &count) = 0; virtual String getName() = 0; virtual int getType() = 0; }; \ No newline at end of file diff --git a/usermods/usermod_v2_social_counter/strategies/tiktok_strategy.h b/usermods/usermod_v2_social_counter/strategies/tiktok_strategy.h index c1c8bda13b..891c4e53e2 100644 --- a/usermods/usermod_v2_social_counter/strategies/tiktok_strategy.h +++ b/usermods/usermod_v2_social_counter/strategies/tiktok_strategy.h @@ -8,7 +8,7 @@ class TikTokStrategy : public SocialNetworkStrategy { public: - bool fetchFollowerCount(const String &link, int &count) override + bool fetchMetric(int metric, const String &link, int &count) override { // TikTok não oferece uma API pública facilmente acessível // Esta implementação é simplificada para demonstração diff --git a/usermods/usermod_v2_social_counter/strategies/twitch_strategy.h b/usermods/usermod_v2_social_counter/strategies/twitch_strategy.h index 4e26a0d946..8271b2e30f 100644 --- a/usermods/usermod_v2_social_counter/strategies/twitch_strategy.h +++ b/usermods/usermod_v2_social_counter/strategies/twitch_strategy.h @@ -8,7 +8,7 @@ class TwitchStrategy : public SocialNetworkStrategy { public: - bool fetchFollowerCount(const String &link, int &count) override + bool fetchMetric(int metric, const String &link, int &count) override { // Implementação simplificada para evitar dependências diff --git a/usermods/usermod_v2_social_counter/strategies/youtube_strategy.h b/usermods/usermod_v2_social_counter/strategies/youtube_strategy.h index e4cce23aef..b32249186d 100644 --- a/usermods/usermod_v2_social_counter/strategies/youtube_strategy.h +++ b/usermods/usermod_v2_social_counter/strategies/youtube_strategy.h @@ -8,7 +8,7 @@ class YouTubeStrategy : public SocialNetworkStrategy { public: - bool fetchFollowerCount(const String &link, int &count) override + bool fetchMetric(int metric, const String &link, int &count) override { // Implementação simplificada para evitar dependências diff --git a/usermods/usermod_v2_social_counter/usermod_v2_social_counter.cpp b/usermods/usermod_v2_social_counter/usermod_v2_social_counter.cpp index 289d25eebd..829afccacf 100644 --- a/usermods/usermod_v2_social_counter/usermod_v2_social_counter.cpp +++ b/usermods/usermod_v2_social_counter/usermod_v2_social_counter.cpp @@ -29,9 +29,17 @@ class SocialCounterUsermod : public Usermod unsigned long lastTime = 0; unsigned long lastAPICall = 0; unsigned long apiCallInterval = 0; + int lastDisplayedCount = -1; + bool isAnimating = false; + int animationStep = 0; + const int totalAnimationSteps = 10; + unsigned long animationStartTime = 0; + int previousCount = 0; + int selectedMetric = METRIC_FOLLOWERS; + int maxDigits = 0; String link = ""; - int social = SOCIAL_COUNTER_INSTAGRAM; + int social = SOCIAL_COUNTER_MOCK; int followerCount = 0; int updateIntervalSeconds = 3; @@ -114,6 +122,11 @@ class SocialCounterUsermod : public Usermod Serial.printf("[INFO] Segmento %d configurado: LEDs %d a %d (total: %d)\n", SOCIAL_SEGMENT_INDEX, segmentStart, seg.stop - 1, segmentLength); + int digitWidth = 7 * ledsPerSegment + digitSpacing; + maxDigits = segmentLength / digitWidth; + + Serial.printf("[INFO] Capacidade máxima de dígitos: %d\n", maxDigits); + segmentValid = true; return true; } @@ -146,12 +159,15 @@ class SocialCounterUsermod : public Usermod for (int i = 0; i < ledsPerSegment; i++) { - if (!on) + if (on) { - // Se o segmento deve estar apagado, desligamos o LED - strip.setPixelColor(ledIndex + i, 0); + uint32_t color = strip.getColor(); // cor global do WLED + strip.setPixelColor(ledIndex + i, color); + } + else + { + strip.setPixelColor(ledIndex + i, 0); // desliga o LED } - // Se o segmento está ativo, não fazemos nada e mantemos os efeitos originais } } @@ -161,111 +177,105 @@ class SocialCounterUsermod : public Usermod */ void displayNumber(int number) { - if (!segmentValid) - { - return; // Não temos um segmento válido para exibir - } + if (!segmentValid || maxDigits <= 0) + return; - // Calcula quantos dígitos podem caber no segmento - int digitWidth = 7 * ledsPerSegment + digitSpacing; - int maxDigits = segmentLength / digitWidth; + // Limita o valor ao número máximo de dígitos + int maxValue = pow(10, maxDigits); + number %= maxValue; - if (maxDigits <= 0) - { - Serial.println("[ERRO] Segmento muito pequeno para exibir qualquer dígito!"); - return; - } + // Converte número em string + String numStr = String(number); - // Forçamos minDigits = 1 para garantir que pelo menos um dígito seja exibido - // A exibição de zeros à esquerda é controlada por showLeadingZeros - int minDigits = 1; + // Preenche com zeros à esquerda, se necessário + while (numStr.length() < maxDigits) + numStr = "0" + numStr; - // Limita o número ao máximo de dígitos disponíveis - int maxValue = 1; - for (int i = 0; i < maxDigits; i++) - { - maxValue *= 10; - } - number = number % maxValue; + bool skippingLeading = !showLeadingZeros; - // Calcula quantos dígitos o número possui - int tempNumber = number; - int numDigits = 0; - do + for (int digit = 0; digit < maxDigits; digit++) { - numDigits++; - tempNumber /= 10; - } while (tempNumber > 0); + int index = maxDigits - 1 - digit; // começa do dígito menos significativo + char c = numStr.charAt(index); + + // Se estamos ignorando zeros à esquerda + if (skippingLeading && c == '0') + { + // Apaga os segmentos desse dígito + for (int seg = 0; seg < 7; seg++) + setSegmentLeds(digit, seg, false); + continue; + } + + skippingLeading = false; // encontrou um dígito significativo - // Garantir que temos pelo menos o número mínimo de dígitos - numDigits = max(numDigits, minDigits); + int digitValue = c - '0'; - // Exibe cada dígito - primeiro preparamos um mapa dos LEDs ativos - bool ledActive[segmentLength]; - // Inicializa todos como inativos - for (int i = 0; i < segmentLength; i++) + for (int seg = 0; seg < 7; seg++) + { + bool segOn = digitSegments[digitValue][seg]; + setSegmentLeds(digit, seg, segOn); + } + } + + strip.show(); + } + + void animateNumberTransition(int from, int to) + { + if (animationStep >= totalAnimationSteps) { - ledActive[i] = false; + isAnimating = false; + displayNumber(to); + lastDisplayedCount = to; + return; } - // Marca os LEDs que devem estar ativos - int digitsShown = 0; + // Aplica um efeito simples: piscar ou fade nos segmentos (temporário) + float progress = (float)animationStep / totalAnimationSteps; + uint8_t fade = 255 * progress; + + // Por enquanto, vamos apenas mostrar o novo número com brilho parcial + // Depois podemos simular o deslocamento real com mapeamento avançado + // Calcula cor com fade antes do loop + uint32_t baseColor = strip.getColor(strip.getSegment(0).colors[0]); + uint8_t r = R(baseColor) * progress; + uint8_t g = G(baseColor) * progress; + uint8_t b = B(baseColor) * progress; + for (int digit = 0; digit < maxDigits; digit++) { - int digitValue = number % 10; + int fromDigit = (from / (int)pow(10, digit)) % 10; + int toDigit = (to / (int)pow(10, digit)) % 10; - // Decide se mostra este dígito baseado nas configurações de zeros à esquerda - bool showDigit = true; + int valueToShow = (progress < 0.5) ? fromDigit : toDigit; - // Se este dígito é zero e está além dos dígitos significativos do número - // e não estamos mostrando zeros à esquerda, pulamos este dígito - if (digitValue == 0 && digit >= numDigits && !showLeadingZeros) + for (int seg = 0; seg < 7; seg++) { - showDigit = false; - } + bool on = digitSegments[valueToShow][seg]; + uint16_t ledIndex = getSegmentLedIndex(digit, seg); - if (showDigit) - { - // Para cada segmento do dígito - for (int seg = 0; seg < 7; seg++) + for (int i = 0; i < ledsPerSegment; i++) { - if (digitSegments[digitValue][seg]) + if (on) + { + strip.setPixelColor(ledIndex + i, WS2812FX::Color(r, g, b)); + } + else { - // Este segmento deve estar ativo - int ledStart = getSegmentLedIndex(digit, seg) - segmentStart; - for (int i = 0; i < ledsPerSegment; i++) - { - if (ledStart + i < segmentLength) - { - ledActive[ledStart + i] = true; - } - } + strip.setPixelColor(ledIndex + i, 0); } } } - - digitsShown++; - number /= 10; - - // Continua exibindo dígitos até atingir o mínimo de dígitos ou zerar o número - if (number == 0 && digitsShown >= minDigits) - break; } - // Agora, desliga apenas os LEDs que devem estar inativos - for (int i = 0; i < segmentLength; i++) - { - if (!ledActive[i]) - { - strip.setPixelColor(segmentStart + i, 0); - } - } + strip.show(); + animationStep++; } public: static const char _name[]; static const char _enabled[]; - static int counter; void enable(bool enable) { @@ -326,10 +336,29 @@ class SocialCounterUsermod : public Usermod } unsigned long currentMillis = millis(); + if (WLED_CONNECTED && (currentMillis - lastAPICall >= apiCallInterval)) { updateFollowerCount(); - lastAPICall = currentMillis; // Registra o tempo exato da chamada + lastAPICall = currentMillis; + } + + // Só redesenha se o valor exibido mudou + if (enabled && segmentValid) + { + if (followerCount != lastDisplayedCount && !isAnimating) + { + previousCount = lastDisplayedCount; + isAnimating = true; + animationStep = 0; + animationStartTime = millis(); + } + + if (isAnimating && millis() - animationStartTime > 50) + { // 50ms por frame + animationStartTime = millis(); + animateNumberTransition(previousCount, followerCount); + } } } @@ -345,12 +374,11 @@ class SocialCounterUsermod : public Usermod if (strategy && !link.isEmpty()) { int count = 0; - bool success = strategy->fetchFollowerCount(link, count); + bool success = strategy->fetchMetric(selectedMetric, link, count); if (success) { followerCount = count; - counter = followerCount; // Atualiza o contador estático para compatibilidade Serial.printf("[INFO] %s followers: %d\n", strategy->getName().c_str(), followerCount); } else @@ -388,6 +416,7 @@ class SocialCounterUsermod : public Usermod usermod["link"] = link; usermod["followers"] = followerCount; usermod["updateInterval"] = updateIntervalSeconds; + usermod["metric"] = selectedMetric; } void readFromJsonState(JsonObject &root) override @@ -434,6 +463,7 @@ class SocialCounterUsermod : public Usermod top["ledsPerSegment"] = ledsPerSegment; top["digitSpacing"] = digitSpacing; top["showLeadingZeros"] = showLeadingZeros; + top["metric"] = selectedMetric; } bool readFromConfig(JsonObject &root) override @@ -445,7 +475,7 @@ class SocialCounterUsermod : public Usermod configComplete &= getJsonValue(top[FPSTR(_enabled)], enabled, false); int oldSocial = social; - configComplete &= getJsonValue(top["social"], social, SOCIAL_COUNTER_INSTAGRAM); + configComplete &= getJsonValue(top["social"], social, SOCIAL_COUNTER_MOCK); if (oldSocial != social) { @@ -459,6 +489,7 @@ class SocialCounterUsermod : public Usermod configComplete &= getJsonValue(top["ledsPerSegment"], ledsPerSegment, 2); configComplete &= getJsonValue(top["digitSpacing"], digitSpacing, 0); configComplete &= getJsonValue(top["showLeadingZeros"], showLeadingZeros, true); + configComplete &= getJsonValue(top["metric"], selectedMetric, METRIC_FOLLOWERS); updateIntervalTimer(); @@ -467,48 +498,64 @@ class SocialCounterUsermod : public Usermod void appendConfigData() override { + // Link input info oappend(F("addInfo('")); oappend(String(FPSTR(_name)).c_str()); oappend(F(":link")); - oappend(F("',1,'Enter your link');")); + oappend(F("',1,'Enter the profile link');")); + // Update interval info oappend(F("addInfo('")); oappend(String(FPSTR(_name)).c_str()); oappend(F(":updateInterval")); - oappend(F("',1,'Delay in seconds between updates (default: 3)');")); + oappend(F("',1,'Delay between updates (in seconds, default: 3)');")); + // LEDs per segment info oappend(F("addInfo('")); oappend(String(FPSTR(_name)).c_str()); oappend(F(":ledsPerSegment")); - oappend(F("',1,'LEDs por segmento (default: 2)');")); + oappend(F("',1,'LEDs per segment (default: 2)');")); + // Digit spacing info oappend(F("addInfo('")); oappend(String(FPSTR(_name)).c_str()); oappend(F(":digitSpacing")); - oappend(F("',1,'Espaço entre dígitos (default: 0)');")); + oappend(F("',1,'Spacing between digits (default: 0)');")); + // Leading zeros info oappend(F("addInfo('")); oappend(String(FPSTR(_name)).c_str()); oappend(F(":showLeadingZeros")); - oappend(F("',1,'Mostrar zeros à esquerda (default: true)');")); + oappend(F("',1,'Show leading zeros (default: true)');")); - oappend(F("dd=addDropdown('")); + // Social network dropdown + oappend(F("dds=addDropdown('")); oappend(String(FPSTR(_name)).c_str()); oappend(F("','social');")); - oappend(F("addOption(dd,'Instagram',0);")); - oappend(F("addOption(dd,'Tiktok',1);")); - oappend(F("addOption(dd,'Twitch',2);")); - oappend(F("addOption(dd,'Youtube',3);")); + oappend(F("addOption(dds,'Mock for Testing',0);")); + oappend(F("addOption(dds,'Instagram',1);")); + oappend(F("addOption(dds,'TikTok',2);")); + oappend(F("addOption(dds,'Twitch',3);")); + oappend(F("addOption(dds,'YouTube',4);")); + + // Metric dropdown + oappend(F("ddm=addDropdown('")); + oappend(String(FPSTR(_name)).c_str()); + oappend(F("','metric');")); + oappend(F("addOption(ddm,'Followers',0);")); + oappend(F("addOption(ddm,'Views',1);")); + oappend(F("addOption(ddm,'Live',2);")); + oappend(F("addOption(ddm,'Subscribers',3);")); } - void handleOverlayDraw() override - { - // Exibe o número de seguidores no display de 7 segmentos - if (enabled && initDone && segmentValid) - { - displayNumber(followerCount); - } - } + // void handleOverlayDraw() override + // { + // // Exibe o número de seguidores no display de 7 segmentos + // if (enabled && initDone && segmentValid) + // { + // displayNumber(followerCount); + // } + // } uint16_t getId() override { @@ -534,7 +581,6 @@ class SocialCounterUsermod : public Usermod // Definição das variáveis estáticas const char SocialCounterUsermod::_name[] = "Social Counter"; const char SocialCounterUsermod::_enabled[] = "enabled"; -int SocialCounterUsermod::counter = 0; static SocialCounterUsermod social_counter; -REGISTER_USERMOD(social_counter); \ No newline at end of file +REGISTER_USERMOD(social_counter);