├── .gitignore ├── HyperionRGB ├── .gitignore ├── LoggerInit.cpp ├── EnhancedThread.cpp ├── LoggerInit.h ├── WrapperOTA.h ├── BaseHeader.h ├── Config.h ├── WrapperLedControl.h ├── WrapperWiFi.h ├── EnhancedThread.h ├── WrapperUdpLed.h ├── ConfigStructures.h ├── WrapperJsonServer.h ├── WrapperOTA.cpp ├── ConfigStatic.h.example ├── WrapperWiFi.cpp ├── WrapperWebconfig.h ├── WrapperLedControl.cpp ├── WrapperJsonServer.cpp ├── Config.cpp ├── WrapperUdpLed.cpp ├── HyperionRGB.ino └── WrapperWebconfig.cpp ├── .github └── FUNDING.yml └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | .vscode/ 2 | -------------------------------------------------------------------------------- /HyperionRGB/.gitignore: -------------------------------------------------------------------------------- 1 | ConfigStatic.h 2 | ConfigStatic.h-* -------------------------------------------------------------------------------- /HyperionRGB/LoggerInit.cpp: -------------------------------------------------------------------------------- 1 | #include "LoggerInit.h" 2 | 3 | LoggerInit::LoggerInit(long baud) { 4 | Log.init(LOGLEVEL, baud); 5 | //Serial.setDebugOutput(true); 6 | } 7 | 8 | -------------------------------------------------------------------------------- /HyperionRGB/EnhancedThread.cpp: -------------------------------------------------------------------------------- 1 | //#include "EnhancedThread.h" 2 | /* 3 | * Put everything into the EnhancedThread.h to make it compatible with the ESP32 4 | * There seems to be a bug in the ArduinoThread lib 5 | * 6 | */ 7 | -------------------------------------------------------------------------------- /HyperionRGB/LoggerInit.h: -------------------------------------------------------------------------------- 1 | #ifndef LoggerInit_h 2 | #define LoggerInit_h 3 | 4 | #include "BaseHeader.h" 5 | 6 | class LoggerInit { 7 | public: 8 | LoggerInit() {}; 9 | LoggerInit(long baud); 10 | }; 11 | 12 | #endif 13 | -------------------------------------------------------------------------------- /HyperionRGB/WrapperOTA.h: -------------------------------------------------------------------------------- 1 | #ifndef WrapperOTA_h 2 | #define WrapperOTA_h 3 | 4 | #include "BaseHeader.h" 5 | #include 6 | 7 | class WrapperOTA { 8 | public: 9 | void 10 | begin(const char* hostname), 11 | handle(void); 12 | }; 13 | 14 | #endif 15 | -------------------------------------------------------------------------------- /HyperionRGB/BaseHeader.h: -------------------------------------------------------------------------------- 1 | #ifndef BaseHeader_h 2 | #define BaseHeader_h 3 | 4 | #include 5 | #include 6 | #include "Config.h" 7 | 8 | #define min(a,b) ((a)<(b)?(a):(b)) 9 | /* 10 | #define min(X, Y) \ 11 | ({ typeof (X) x_ = (X); \ 12 | typeof (Y) y_ = (Y); \ 13 | (x_ < y_) ? x_ : y_; })*/ 14 | 15 | enum Mode { OFF, HYPERION_UDP, STATIC_COLOR, RAINBOW, FIRE2012, RAINBOW_V2, RAINBOW_FULL, MODE_NONE }; 16 | enum UdpProtocol { UDP_RAW, UDP_FILLER, UDP_FRAGMENT, UDP_TPM2 }; 17 | 18 | #endif 19 | -------------------------------------------------------------------------------- /HyperionRGB/Config.h: -------------------------------------------------------------------------------- 1 | #ifndef Config_h 2 | #define Config_h 3 | 4 | #include "BaseHeader.h" 5 | 6 | #include 7 | 8 | #include "ConfigStatic.h" 9 | #include "ConfigStructures.h" 10 | 11 | class Config { 12 | public: 13 | static ConfigStruct *getConfig(void); 14 | static void saveConfig(); 15 | static void loadStaticConfig(void); 16 | static byte *cfg2ip(ConfigIP ip); 17 | static ConfigIP ip2cfg(const byte ip[4]); 18 | static void logConfig(void); 19 | private: 20 | static void initConfig(void); 21 | static ConfigStruct _cfgStruct; 22 | static boolean _cfgLoaded; 23 | }; 24 | 25 | #endif 26 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: SciLor # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] 4 | patreon: # Replace with a single Patreon username 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: # Replace with a single Ko-fi username 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | otechie: # Replace with a single Otechie username 12 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] 13 | -------------------------------------------------------------------------------- /HyperionRGB/WrapperLedControl.h: -------------------------------------------------------------------------------- 1 | #ifndef WrapperLedControl_h 2 | #define WrapperLedControl_h 3 | 4 | #include "BaseHeader.h" 5 | 6 | #include 7 | 8 | class WrapperLedControl { 9 | public: 10 | void 11 | begin(uint16_t ledCount), 12 | show(void), 13 | clear(void), 14 | fillSolid(CRGB color), 15 | fillSolid(byte r, byte g, byte b), 16 | rainbowStep(void), 17 | rainbowV2Step(void), 18 | rainbowFullStep(void), 19 | fire2012Step(void); 20 | 21 | CRGB* leds; 22 | 23 | private: 24 | CRGB wheel(byte wheelPos); 25 | byte _rainbowStepState; 26 | uint16_t _rainbowV2StepState; 27 | boolean _fire2012Direction; 28 | byte* _fire2012Heat; 29 | uint16_t _ledCount; 30 | }; 31 | 32 | #endif 33 | -------------------------------------------------------------------------------- /HyperionRGB/WrapperWiFi.h: -------------------------------------------------------------------------------- 1 | #ifndef WrapperWiFi_h 2 | #define WrapperWiFi_h 3 | 4 | #include "BaseHeader.h" 5 | 6 | #if defined(ESP8266) 7 | #include 8 | #include 9 | #elif defined(ESP32) 10 | #include 11 | #include 12 | #endif 13 | 14 | class WrapperWiFi { 15 | public: 16 | WrapperWiFi() {}; 17 | WrapperWiFi(const char* ssid, const char* password, const char* hostname); 18 | WrapperWiFi(const char* ssid, const char* password, const byte ip[4], const byte subnet[4], const byte dns[4], const char* hostname); 19 | void begin(void); 20 | bool isAP(void); 21 | bool isAPConnected(void); 22 | private: 23 | bool connect(void); 24 | const char* _ssid; 25 | const char* _password; 26 | const char* _hostname; 27 | byte _ip[4]; 28 | byte _subnet[4]; 29 | byte _dns[4]; 30 | bool _apMode; 31 | }; 32 | 33 | #endif 34 | -------------------------------------------------------------------------------- /HyperionRGB/EnhancedThread.h: -------------------------------------------------------------------------------- 1 | #ifndef EnhancedThread_h 2 | #define EnhancedThread_h 3 | 4 | #include "BaseHeader.h" 5 | #include 6 | 7 | class EnhancedThread : public Thread { 8 | public: 9 | void 10 | run(void), 11 | runIfNeeded(void), 12 | reset(void), 13 | setRunOnce(bool); 14 | unsigned long getInterval(void); 15 | private: 16 | bool _runOnce; 17 | }; 18 | 19 | void EnhancedThread::run() { 20 | Thread::run(); 21 | if (_runOnce) 22 | Thread::enabled = false; 23 | } 24 | 25 | void EnhancedThread::runIfNeeded(void) { 26 | if(Thread::shouldRun()) 27 | Thread::run(); 28 | } 29 | 30 | void EnhancedThread::reset(void) { 31 | Thread::enabled = true; 32 | Thread::runned(); 33 | } 34 | 35 | void EnhancedThread::setRunOnce(bool runOnce) { 36 | _runOnce = runOnce; 37 | } 38 | unsigned long EnhancedThread::getInterval(void) { 39 | return interval; 40 | } 41 | 42 | #endif 43 | -------------------------------------------------------------------------------- /HyperionRGB/WrapperUdpLed.h: -------------------------------------------------------------------------------- 1 | #ifndef WrapperUdpLed_h 2 | #define WrapperUdpLed_h 3 | 4 | #include "BaseHeader.h" 5 | #include 6 | 7 | class WrapperUdpLed { 8 | public: 9 | WrapperUdpLed() {}; 10 | WrapperUdpLed(uint16_t ledCount, uint16_t udpPort, UdpProtocol udpProtocol); 11 | 12 | void 13 | begin(void), 14 | stop(void), 15 | handle(void); 16 | 17 | void 18 | onUpdateLed(void(* function) (int, byte, byte, byte)), 19 | onRefreshLeds(void(* function) (void)); 20 | 21 | private: 22 | WiFiUDP _udp; 23 | uint16_t _ledCount; 24 | uint16_t _udpPort; 25 | UdpProtocol _udpProtocol; 26 | byte* _udpBuffer; 27 | uint16_t _bufferSize; 28 | boolean _opened; 29 | uint16_t _tpm2LedId; 30 | 31 | void 32 | updateLed(int id, byte r, byte g, byte b), 33 | (* updateLedPointer) (int, byte, byte, byte); 34 | 35 | void 36 | refreshLeds(void), 37 | (* refreshLedsPointer) (void); 38 | 39 | void 40 | handleProtocolRaw(int bytes), 41 | handleProtocolFragment(int bytes), 42 | handleProtocolTPM2(int bytes); 43 | }; 44 | 45 | #endif 46 | 47 | -------------------------------------------------------------------------------- /HyperionRGB/ConfigStructures.h: -------------------------------------------------------------------------------- 1 | #ifndef ConfigStructures_h 2 | #define ConfigStructures_h 3 | 4 | #define CONFIG_START_ADDRESS 0 5 | #define CONFIG_ACTIVE_VERSION 3 6 | 7 | #include 8 | 9 | typedef struct { 10 | uint8_t a; 11 | uint8_t b; 12 | uint8_t c; 13 | uint8_t d; 14 | } ConfigIP; 15 | 16 | typedef struct { 17 | char ssid[32]; 18 | char spacer1[32]; 19 | char password[64]; 20 | char spacer2[64]; 21 | 22 | ConfigIP ip; 23 | ConfigIP subnet; 24 | ConfigIP dns; 25 | char spacer3[16]; 26 | 27 | char hostname[32]; 28 | char spacer4[128]; 29 | } ConfigWifi; 30 | 31 | typedef struct { 32 | uint8_t idleMode; 33 | uint32_t timeoutMs; 34 | boolean autoswitch; 35 | uint16_t count; 36 | CRGB color; 37 | char spacer[54]; 38 | } ConfigLed; 39 | 40 | typedef struct { 41 | uint16_t jsonServer; 42 | uint16_t udpLed; 43 | 44 | char spacer[32]; 45 | } ConfigPort; 46 | 47 | typedef struct { 48 | uint8_t udpProtocol; 49 | 50 | char spacer[63]; 51 | } ConfigMisc; 52 | 53 | typedef struct { 54 | uint8_t version; 55 | ConfigWifi wifi; 56 | ConfigLed led; 57 | ConfigPort ports; 58 | ConfigMisc misc; 59 | } ConfigStruct; 60 | 61 | #endif 62 | -------------------------------------------------------------------------------- /HyperionRGB/WrapperJsonServer.h: -------------------------------------------------------------------------------- 1 | #ifndef WrapperJsonServer_h 2 | #define WrapperJsonServer_h 3 | 4 | #include "BaseHeader.h" 5 | #include 6 | #include 7 | #include 8 | 9 | #define TCP_BUFFER 512 10 | 11 | class WrapperJsonServer { 12 | public: 13 | WrapperJsonServer(); 14 | WrapperJsonServer(uint16_t ledCount, uint16_t tcpPort); 15 | 16 | void 17 | begin(void), 18 | handle(void); 19 | 20 | void 21 | onLedColorWipe(void(* function) (byte, byte, byte)), 22 | onClearCmd(void(* function) (void)), 23 | onEffectChange(void(* function) (Mode, int)); 24 | private: 25 | void 26 | handleConnection(boolean newClient), 27 | readData(void); 28 | 29 | void 30 | ledColorWipe(byte r, byte g, byte b), 31 | (* ledColorWipePointer) (byte, byte, byte); 32 | void 33 | clearCmd(void), 34 | (* clearCmdPointer) (void); 35 | void 36 | effectChange(Mode effect, int interval = 0), 37 | (* effectChangePointer) (Mode, int); 38 | 39 | WiFiServer _tcpServer; 40 | WiFiClient _tcpClient; 41 | 42 | uint16_t _ledCount; 43 | uint16_t _tcpPort; 44 | 45 | byte* _activeLedColor; 46 | }; 47 | 48 | #endif 49 | -------------------------------------------------------------------------------- /HyperionRGB/WrapperOTA.cpp: -------------------------------------------------------------------------------- 1 | #include "WrapperOTA.h" 2 | 3 | void WrapperOTA::begin(const char* hostname) { 4 | Log.info("Prepare OTA"); 5 | Log.debug("WrapperOTA(hostname=\"%s\")", hostname); 6 | 7 | // Port defaults to 8266 8 | ArduinoOTA.setPort(8266); 9 | 10 | // Hostname defaults to esp8266-[ChipID] 11 | ArduinoOTA.setHostname(hostname); 12 | 13 | // No authentication by default 14 | ArduinoOTA.setPassword((const char *)CONFIG_OTA_AP_PASSWORD); 15 | 16 | ArduinoOTA.onStart([]() { 17 | Log.info("Start OTA Update"); 18 | }); 19 | ArduinoOTA.onEnd([]() { 20 | Log.info("OTA Update Finished"); 21 | }); 22 | ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) { 23 | Log.info("OTA Progress: %i%%", (progress / (total / 100))); 24 | }); 25 | ArduinoOTA.onError([](ota_error_t error) { 26 | if (error == OTA_AUTH_ERROR) Log.error("Auth Failed"); 27 | else if (error == OTA_BEGIN_ERROR) Log.error("Begin Failed"); 28 | else if (error == OTA_CONNECT_ERROR) Log.error("Connect Failed"); 29 | else if (error == OTA_RECEIVE_ERROR) Log.error("Receive Failed"); 30 | else if (error == OTA_END_ERROR) Log.error("End Failed"); 31 | }); 32 | ArduinoOTA.begin(); 33 | Log.info("OTA Ready"); 34 | } 35 | 36 | void WrapperOTA::handle(void) { 37 | ArduinoOTA.handle(); 38 | } 39 | -------------------------------------------------------------------------------- /HyperionRGB/ConfigStatic.h.example: -------------------------------------------------------------------------------- 1 | /*------------------------------------------------*/ 2 | /*Configuration type*/ 3 | 4 | #define CONFIG_ENABLE_WEBCONFIG 1 5 | 6 | //Replaces all values in the webconfig with the ConfigStatic.h values when CONFIG_ENABLE_WEBCONFIG is enabled 7 | //#define CONFIG_OVERWRITE_WEBCONFIG 1 8 | 9 | /*------------------------------------------------*/ 10 | /*Logging level*/ 11 | 12 | //#define LOGLEVEL LOG_LEVEL_INFOS 13 | //#define LOGLEVEL LOG_LEVEL_DEBUG 14 | //#define LOGLEVEL LOG_LEVEL_VERBOSE 15 | 16 | /*------------------------------------------------*/ 17 | /*Main static configuration*/ 18 | //This cannot be changed via web inteface 19 | 20 | #define CONFIG_LED_SPI_CHIPSET WS2801 //Comment out for clockless 21 | //#define CONFIG_LED_CLOCKLESS_CHIPSET WS2812B //Comment in for clockless 22 | //#define FASTLED_ALLOW_INTERRUPTS 0 //Comment in if clockless stripe (ex. WS2812B) is flickering 23 | //#define CONFIG_LED_PWM 1 //Comment in if PWM Stripe 24 | 25 | #define CONFIG_LED_DATAPIN D1 //Comment out for PWM 26 | #define CONFIG_LED_CLOCKPIN D2 //Comment out for clockless / PWM 27 | //#define CONFIG_LED_PWM_RED D1 //Comment in for PWM 28 | //#define CONFIG_LED_PWM_GREEN D2 //Comment in for PWM 29 | //#define CONFIG_LED_PWM_BLUE D3 //Comment in for PWM 30 | 31 | //Pin order, see FastLED doc. NodeMCU should work with FASTLED_ESP8266_RAW_PIN_ORDER 32 | #define FASTLED_ESP8266_RAW_PIN_ORDER 33 | //#define FASTLED_ESP8266_NODEMCU_PIN_ORDER 34 | //#define FASTLED_ESP8266_D1_PIN_ORDER 35 | 36 | #define CONFIG_LED_COLOR_ORDER RGB 37 | #define CONFIG_LED_COUNT 50 38 | 39 | //OFF, HYPERION_UDP, STATIC_COLOR, RAINBOW, FIRE2012, RAINBOW_V2, RAINBOW_FULL 40 | #define CONFIG_LED_STANDARD_MODE RAINBOW_V2 41 | #define CONFIG_LED_HYPERION_AUTOSWITCH true 42 | #define CONFIG_LED_STANDARD_MODE_TIMEOUT_MS 5000 43 | #define CONFIG_LED_STATIC_COLOR CRGB(254, 254, 254) 44 | 45 | /*------------------------------------------------*/ 46 | /*Main configuration*/ 47 | //You can leave it empty and override it via the web interface on port 80 48 | #define CONFIG_WIFI_SSID "ssid" 49 | #define CONFIG_WIFI_PASSWORD "password" 50 | 51 | //#define CONFIG_WIFI_STATIC_IP 1 52 | //const byte CONFIG_WIFI_IP[] = {192, 168, 0, 100}; 53 | //const byte CONFIG_WIFI_SUBNET[] = {255, 255, 255, 0}; 54 | //const byte CONFIG_WIFI_DNS[] = {192, 168, 0, 1}; 55 | 56 | #define CONFIG_WIFI_HOSTNAME "HyperionRGB" 57 | 58 | #define CONFIG_PORT_JSON_SERVER 19444 59 | #define CONFIG_PORT_UDP_LED 19446 60 | 61 | #define CONFIG_PROTOCOL_UDP UDP_RAW //UDP_RAW, UDP_FRAGMENT, UDP_TPM2 - Hyperion 0: RAW, 2: Fragmented, 3: TPM2 - Check the README 62 | 63 | #define CONFIG_OTA_AP_PASSWORD "HyperionRGB" 64 | -------------------------------------------------------------------------------- /HyperionRGB/WrapperWiFi.cpp: -------------------------------------------------------------------------------- 1 | #include "WrapperWiFi.h" 2 | 3 | WrapperWiFi::WrapperWiFi(const char* ssid, const char* password, const char* hostname) { 4 | _ssid = ssid; 5 | _password = password; 6 | _hostname = hostname; 7 | byte empty[4] = {0}; 8 | memcpy(_ip, empty, sizeof(_ip)); 9 | memcpy(_subnet, empty, sizeof(_subnet)); 10 | memcpy(_dns, empty, sizeof(_dns)); 11 | } 12 | 13 | WrapperWiFi::WrapperWiFi(const char* ssid, const char* password, const byte ip[4], const byte subnet[4], const byte dns[4], const char* hostname) { 14 | _ssid = ssid; 15 | _password = password; 16 | _hostname = hostname; 17 | if (ip[0] != 0) { 18 | memcpy(_ip, ip, sizeof(_ip)); 19 | memcpy(_subnet, subnet, sizeof(_subnet)); 20 | memcpy(_dns, dns, sizeof(_dns)); 21 | } else { 22 | byte empty[4] = {0}; 23 | memcpy(_ip, empty, sizeof(_ip)); 24 | memcpy(_subnet, empty, sizeof(_subnet)); 25 | memcpy(_dns, empty, sizeof(_dns)); 26 | } 27 | } 28 | 29 | bool WrapperWiFi::connect(void) { 30 | Log.debug("WrapperWiFi(ssid=\"%s\", password=\"%s\")", _ssid, _password); 31 | 32 | Log.info("Connecting to WiFi %s", _ssid); 33 | 34 | WiFi.mode(WIFI_STA); 35 | if (_ip[0] != 0) { 36 | Log.info("Using static ip"); 37 | WiFi.config(_ip, _dns, _subnet); 38 | } else { 39 | Log.info("Using dynamic ip"); 40 | } 41 | 42 | WiFi.begin(_ssid, _password); 43 | 44 | return (WiFi.waitForConnectResult() == WL_CONNECTED); 45 | } 46 | void WrapperWiFi::begin(void) { 47 | for (uint8 i=0; i<5; i++) { 48 | if (connect()) { 49 | Log.info("Connected successfully, IP address: %s", WiFi.localIP().toString().c_str()); 50 | _apMode = false; 51 | return; 52 | } 53 | } 54 | 55 | //WiFi connection failed. 56 | if (_hostname[0] == '\0') 57 | _hostname = "HyperionRGB"; 58 | Log.error("WiFi Connection failed!"); 59 | #ifdef CONFIG_ENABLE_WEBCONFIG || !CONFIG_OVERWRITE_WEBCONFIG 60 | WiFi.mode(WIFI_AP); 61 | delay(100); 62 | 63 | const char *ssid = _hostname; 64 | const char *password = CONFIG_OTA_AP_PASSWORD; 65 | Log.info("AP with SSID=%s and password %s for configuration is started...", ssid, password); 66 | if (WiFi.softAP(ssid, password)) { 67 | _apMode = true; 68 | Log.info("IP=%s", WiFi.softAPIP().toString().c_str()); 69 | return; 70 | } else { 71 | Log.error("Creating AP failed, check if hostname is okay and password isn't to short..."); 72 | } 73 | #endif 74 | Log.error("Restarting..."); 75 | delay(5000); 76 | ESP.restart(); 77 | } 78 | 79 | bool WrapperWiFi::isAP(void) { 80 | return _apMode; 81 | } 82 | bool WrapperWiFi::isAPConnected(void) { 83 | if (!isAP()) 84 | return false; 85 | return (WiFi.softAPgetStationNum() > 0); 86 | } 87 | 88 | -------------------------------------------------------------------------------- /HyperionRGB/WrapperWebconfig.h: -------------------------------------------------------------------------------- 1 | #ifndef WrapperWebconfig_h 2 | #define WrapperWebconfig_h 3 | #include "BaseHeader.h" 4 | 5 | #if defined(ESP8266) 6 | #include 7 | #elif defined(ESP32) 8 | #include 9 | #endif 10 | #include 11 | 12 | class SelectEntryBase { 13 | public: 14 | SelectEntryBase() {}; 15 | SelectEntryBase(String selectedValue, String text, boolean selected) { 16 | _selectedValue = selectedValue; 17 | _text = text; 18 | _selected = selected; 19 | }; 20 | 21 | String getSelectedValue(void) { return _selectedValue; }; 22 | String getText(void) { return _text; }; 23 | boolean isSelected(void) { return _selected; }; 24 | 25 | void setSelected(boolean selected) { _selected = selected; }; 26 | private: 27 | String _selectedValue; 28 | String _text; 29 | boolean _selected; 30 | }; 31 | 32 | template 33 | class SelectEntry : public SelectEntryBase { 34 | public: 35 | SelectEntry() { }; 36 | SelectEntry(String selectedValue, String text, boolean selected, T value) : SelectEntryBase(selectedValue, text, selected) { 37 | _value = value; 38 | } 39 | T getValue(void) { return _value; }; 40 | private: 41 | T _value; 42 | }; 43 | 44 | class WrapperWebconfig { 45 | public: 46 | void 47 | begin(); 48 | bool handle(void); 49 | private: 50 | void 51 | handleNotFound(void), 52 | handleRoot(void), 53 | changeConfig(void), 54 | parseBytes(const char* str, char sep, byte* bytes, int maxBytes, int base); 55 | 56 | String 57 | escape(String text), 58 | escape(char* text), 59 | escape(uint8_t text), 60 | escape(uint16_t text), 61 | escape(uint32_t text), 62 | color2hex(CRGB color), 63 | ipToString(ConfigIP ip), 64 | 65 | htmlTemplate(String title, String content), 66 | groupTemplate(String title, String body), 67 | entryTemplate(String label, String tooltip, String id, String content), 68 | textTemplate(String label, String tooltip, String id, String text, String placeholder, int maxLen), 69 | checkboxTemplate(String label, String tooltip, String id, boolean isChecked), 70 | selectTemplate(String label, String tooltip, String id, LinkedList* entries), 71 | config(void); 72 | 73 | void 74 | initHelperVars(void), 75 | clearHelperVars(void), 76 | clearLinkedList(LinkedList* target), 77 | getIdleModes(uint8_t active, LinkedList* target), 78 | getUdpProtocols(uint8_t active, LinkedList* target); 79 | 80 | template 81 | T getSelectedEntry(String selectedEntryValue, LinkedList* target); 82 | 83 | LinkedList* _idleModes; 84 | LinkedList* _udpProtocols; 85 | 86 | #if defined(ESP8266) 87 | ESP8266WebServer* _server = new ESP8266WebServer(80); 88 | #elif defined(ESP32) 89 | ESP32WebServer* _server = new ESP32WebServer(80); 90 | #endif 91 | bool _handled; 92 | }; 93 | 94 | #endif 95 | -------------------------------------------------------------------------------- /HyperionRGB/WrapperLedControl.cpp: -------------------------------------------------------------------------------- 1 | #include "WrapperLedControl.h" 2 | 3 | void WrapperLedControl::begin(uint16_t ledCount) { 4 | _ledCount = ledCount; 5 | 6 | #ifdef CONFIG_LED_CLOCKLESS_CHIPSET 7 | Log.debug("Chipset=%s, dataPin=%i, clockPin=%s, colorOrder=%i, ledCount=%i", "Clockless", CONFIG_LED_DATAPIN, "NONE", CONFIG_LED_COLOR_ORDER, ledCount); 8 | #elif defined CONFIG_LED_PWM 9 | Log.debug("Chipset=%s, redPin=%i, greenPin=%i, bluePin=%i, ledCount=%i", "PWM", CONFIG_LED_PWM_RED, CONFIG_LED_PWM_GREEN, CONFIG_LED_PWM_BLUE, CONFIG_LED_COUNT); 10 | #if CONFIG_LED_COUNT != 1 11 | #error "PWM only supports LED count set to one (even if you have multiple LEDs on your strip, they will all show the same color)" 12 | #endif 13 | #else 14 | Log.debug("Chipset=%i, dataPin=%i, clockPin=%i, colorOrder=%i, ledCount=%i", CONFIG_LED_SPI_CHIPSET, CONFIG_LED_DATAPIN, CONFIG_LED_CLOCKPIN, CONFIG_LED_COLOR_ORDER, _ledCount); 15 | #endif 16 | 17 | leds = new CRGB[_ledCount]; 18 | _fire2012Heat = new byte[_ledCount]; 19 | 20 | #ifdef CONFIG_LED_CLOCKLESS_CHIPSET 21 | FastLED.addLeds(leds, _ledCount); 22 | #elif defined CONFIG_LED_PWM 23 | //Nothing to to 24 | #else 25 | FastLED.addLeds(leds, _ledCount); 26 | #endif 27 | } 28 | 29 | void WrapperLedControl::show(void) { 30 | #if defined CONFIG_LED_PWM 31 | analogWrite(CONFIG_LED_PWM_RED, map(leds[0].red, 0, 255, 0, PWMRANGE)); 32 | analogWrite(CONFIG_LED_PWM_GREEN, map(leds[0].green, 0, 255, 0, PWMRANGE)); 33 | analogWrite(CONFIG_LED_PWM_BLUE, map(leds[0].blue, 0, 255, 0, PWMRANGE)); 34 | #else 35 | FastLED.show(); 36 | #endif 37 | } 38 | 39 | void WrapperLedControl::clear(void) { 40 | #if defined CONFIG_LED_PWM 41 | leds[0] = CRGB::Black; 42 | #else 43 | FastLED.clear(); 44 | #endif 45 | } 46 | 47 | void WrapperLedControl::fillSolid(CRGB color) { 48 | fill_solid(leds, _ledCount, color); 49 | show(); 50 | } 51 | 52 | void WrapperLedControl::fillSolid(byte r, byte g, byte b) { 53 | fillSolid(CRGB(r, g, b)); 54 | } 55 | 56 | void WrapperLedControl::rainbowStep(void) { 57 | for (int i=0; i < _ledCount; i++) { 58 | leds[i] = wheel((i + _rainbowStepState) % 255); 59 | } 60 | show(); 61 | 62 | if (_rainbowStepState < 255) { 63 | _rainbowStepState++; 64 | } else { 65 | _rainbowStepState = 0; 66 | } 67 | } 68 | void WrapperLedControl::rainbowFullStep(void) { 69 | for (int i=0; i < _ledCount; i++) { 70 | leds[i] = wheel(_rainbowStepState); 71 | } 72 | show(); 73 | 74 | if (_rainbowStepState < 255) { 75 | _rainbowStepState++; 76 | } else { 77 | _rainbowStepState = 0; 78 | } 79 | } 80 | 81 | CRGB WrapperLedControl::wheel(byte wheelPos) { 82 | CRGB color = CRGB(); 83 | if (wheelPos < 85) { 84 | return color.setRGB(wheelPos * 3, 255 - wheelPos * 3, 0); 85 | } else if (wheelPos < 170) { 86 | wheelPos -= 85; 87 | return color.setRGB(255 - wheelPos * 3, 0, wheelPos * 3); 88 | } else { 89 | wheelPos -= 170; 90 | return color.setRGB(0, wheelPos * 3, 255 - wheelPos * 3); 91 | } 92 | return color; 93 | } 94 | 95 | void WrapperLedControl::rainbowV2Step(void) { 96 | for (uint16_t i=0; i < _ledCount; i++) { 97 | leds[i] = wheel(((i + _rainbowV2StepState) % _ledCount) * 255 / _ledCount); 98 | } 99 | show(); 100 | 101 | if (_rainbowV2StepState < _ledCount) { 102 | _rainbowV2StepState++; 103 | } else { 104 | _rainbowV2StepState = 0; 105 | } 106 | } 107 | 108 | // Fire2012 by Mark Kriegsman, July 2012 109 | // as part of "Five Elements" shown here: http://youtu.be/knWiGsmgycY 110 | 111 | // COOLING: How much does the air cool as it rises? 112 | // Less cooling = taller flames. More cooling = shorter flames. 113 | // Default 50, suggested range 20-100 114 | #define COOLING 50 115 | 116 | // SPARKING: What chance (out of 255) is there that a new spark will be lit? 117 | // Higher chance = more roaring fire. Lower chance = more flickery fire. 118 | // Default 120, suggested range 50-200. 119 | #define SPARKING 120 120 | 121 | void WrapperLedControl::fire2012Step(void) { 122 | // Step 1. Cool down every cell a little 123 | for( int i = 0; i < _ledCount; i++) { 124 | _fire2012Heat[i] = qsub8( _fire2012Heat[i], random8(0, ((COOLING * 10) / _ledCount) + 2)); 125 | } 126 | 127 | // Step 2. _fire2012Heat from each cell drifts 'up' and diffuses a little 128 | for( int k=_ledCount - 1; k >= 2; k--) { 129 | _fire2012Heat[k] = (_fire2012Heat[k - 1] + _fire2012Heat[k - 2] + _fire2012Heat[k - 2] ) / 3; 130 | } 131 | 132 | // Step 3. Randomly ignite new 'sparks' of _fire2012Heat near the bottom 133 | if( random8() < SPARKING ) { 134 | int y = random8(min(7, _ledCount - 1)); 135 | _fire2012Heat[y] = qadd8(_fire2012Heat[y], random8(160,255)); 136 | } 137 | 138 | // Step 4. Map from _fire2012Heat cells to LED colors 139 | for( int j = 0; j < _ledCount; j++) { 140 | CRGB color = HeatColor( _fire2012Heat[j]); 141 | int pixelnumber; 142 | if(_fire2012Direction) { 143 | pixelnumber = (_ledCount-1) - j; 144 | } else { 145 | pixelnumber = j; 146 | } 147 | leds[pixelnumber] = color; 148 | } 149 | show(); 150 | } 151 | -------------------------------------------------------------------------------- /HyperionRGB/WrapperJsonServer.cpp: -------------------------------------------------------------------------------- 1 | #include "WrapperJsonServer.h" 2 | 3 | WrapperJsonServer::WrapperJsonServer() 4 | : _tcpServer(0) {} 5 | 6 | WrapperJsonServer::WrapperJsonServer(uint16_t ledCount, uint16_t tcpPort) 7 | : _tcpServer(tcpPort) { 8 | _ledCount = ledCount; 9 | _tcpPort = tcpPort; 10 | _activeLedColor = new byte[3]; 11 | } 12 | 13 | void WrapperJsonServer::begin(void) { 14 | Log.info("Open port %i for TCP...", _tcpPort); 15 | _tcpServer.begin(); 16 | } 17 | 18 | void WrapperJsonServer::handle(void) { 19 | if (_tcpClient) { 20 | handleConnection(false); 21 | } else { 22 | _tcpClient = _tcpServer.available(); 23 | if (_tcpClient) { 24 | handleConnection(true); 25 | } 26 | } 27 | } 28 | 29 | void WrapperJsonServer::handleConnection(boolean newClient) { 30 | if (_tcpClient.connected()) { 31 | if (newClient) { 32 | Log.info("TCP-Client connected"); 33 | } 34 | while (_tcpClient.available()) { 35 | readData(); 36 | } 37 | } else { 38 | Log.info("TCP-Client disconnected"); 39 | _tcpClient.stop(); 40 | } 41 | } 42 | 43 | void WrapperJsonServer::readData(void) { 44 | String data = _tcpClient.readStringUntil('\n'); 45 | Log.debug("Received data: %s", data.c_str()); 46 | StaticJsonDocument doc; 47 | deserializeJson(doc, data.c_str()); 48 | if (!doc.isNull()) { 49 | JsonObject root = doc.as(); 50 | String command = root["command"]; 51 | if (command.equals("serverinfo")) { 52 | Log.info("serverinfo"); 53 | _tcpClient.print("{\"info\":{\"effects\":[" 54 | "{\"args\":{\"speed\":1.0},\"name\":\"Hyperion UDP\",\"script\":\"hyperion_udp\"}," 55 | "{\"args\":{\"speed\":2.0},\"name\":\"Rainbow swirl\",\"script\":\"rainbow\"}," 56 | "{\"args\":{\"speed\":2.0},\"name\":\"Rainbow swirl v2\",\"script\":\"rainbow_v2\"}," 57 | "{\"args\":{\"speed\":2.0},\"name\":\"Rainbow full\",\"script\":\"rainbow_full\"}," 58 | "{\"args\":{\"speed\":62.5},\"name\":\"Fire2012\",\"script\":\"fire2012\"}" 59 | "]," 60 | "\"activeLedColor\":[{\"RGB Value\":["); 61 | //Dirty hack, workaround for each leds state of newer hyperion versions 62 | /* "activeLedColor" : [ 63 | { 64 | "HEX Value" : [ "0xFF0036" ], 65 | "HSL Value" : [ 347, 1.0, 0.50 ], 66 | "RGB Value" : [ 255, 0, 54 ] 67 | } 68 | ], */ 69 | _tcpClient.print(_activeLedColor[0]); 70 | _tcpClient.print(","); 71 | _tcpClient.print(_activeLedColor[1]); 72 | _tcpClient.print(","); 73 | _tcpClient.print(_activeLedColor[2]); 74 | 75 | _tcpClient.print("]}]," 76 | "\"hostname\":\"ESP8266\"," 77 | "\"priorities\":[],\"transform\":[{\"blacklevel\":[0.0,0.0,0.0],\"gamma\":[1.0,1.0,1.0],\"id\":\"default\",\"saturationGain\":1.0,\"threshold\":[0.0,0.0,0.0],\"valueGain\":1.0,\"whitelevel\":[1.0,1.0,1.0]}]}," 78 | "\"success\":true}"); 79 | _tcpClient.println(""); 80 | } else if (command.equals("color")) { 81 | int duration = root["duration"]; 82 | ledColorWipe(root["color"][0], root["color"][1], root["color"][2]); 83 | _tcpClient.println("{\"success\":true}"); 84 | } else if (command.equals("clear") || command.equals("clearall")) { 85 | clearCmd(); 86 | _tcpClient.println("{\"success\":true}"); 87 | } else if (command.equals("effect")) { 88 | String effect = root["effect"]["name"]; 89 | double effectSpeed = root["effect"]["args"]["speed"]; 90 | int interval = 0; 91 | if (effectSpeed > 0) { 92 | interval = (int)(1000.0 / effectSpeed); 93 | } 94 | 95 | if (effect.equals("Hyperion UDP")) { 96 | effectChange(HYPERION_UDP); 97 | } else if (effect.equals("Rainbow swirl")) { 98 | effectChange(RAINBOW, interval); 99 | } else if (effect.equals("Rainbow swirl v2")) { 100 | effectChange(RAINBOW_V2, interval); 101 | } else if (effect.equals("Rainbow full")) { 102 | effectChange(RAINBOW_FULL, interval); 103 | } else if (effect.equals("Fire2012")) { 104 | effectChange(FIRE2012, interval); 105 | } 106 | _tcpClient.println("{\"success\":true}"); 107 | } else { 108 | _tcpClient.println("{\"success\":false}"); 109 | } 110 | } else { 111 | Log.error("JSON not parsed"); 112 | } 113 | } 114 | 115 | void WrapperJsonServer::onLedColorWipe(void(* function) (byte, byte, byte)) { 116 | ledColorWipePointer = function; 117 | } 118 | void WrapperJsonServer::ledColorWipe(byte r, byte g, byte b) { 119 | _activeLedColor[0] = r; 120 | _activeLedColor[1] = g; 121 | _activeLedColor[2] = b; 122 | if (ledColorWipePointer) { 123 | ledColorWipePointer(r, g, b); 124 | } 125 | } 126 | 127 | void WrapperJsonServer::onClearCmd(void(* function) (void)) { 128 | clearCmdPointer = function; 129 | } 130 | void WrapperJsonServer::clearCmd(void) { 131 | _activeLedColor[0] = 0; 132 | _activeLedColor[1] = 0; 133 | _activeLedColor[2] = 0; 134 | if (clearCmdPointer) { 135 | clearCmdPointer(); 136 | } 137 | } 138 | 139 | void WrapperJsonServer::onEffectChange(void(* function) (Mode, int)) { 140 | effectChangePointer = function; 141 | } 142 | void WrapperJsonServer::effectChange(Mode effect, int interval/* = 0*/) { 143 | if (effectChangePointer) { 144 | effectChangePointer(effect, interval); 145 | } 146 | } 147 | -------------------------------------------------------------------------------- /HyperionRGB/Config.cpp: -------------------------------------------------------------------------------- 1 | #include "Config.h" 2 | 3 | ConfigStruct Config::_cfgStruct; 4 | boolean Config::_cfgLoaded = false; 5 | 6 | void Config::saveConfig() { 7 | EEPROM.begin(sizeof(ConfigStruct)); 8 | EEPROM.put(CONFIG_START_ADDRESS, _cfgStruct); 9 | _cfgStruct.version = CONFIG_ACTIVE_VERSION; 10 | Log.info("Configuration saved at 0x%x with v%i", CONFIG_START_ADDRESS, _cfgStruct.version); 11 | //EEPROM.commit(); (done with end()) 12 | EEPROM.end(); 13 | _cfgLoaded = false; 14 | } 15 | 16 | void Config::initConfig(void) { 17 | if (!_cfgLoaded) { 18 | EEPROM.begin(sizeof(ConfigStruct)); 19 | uint8_t version = EEPROM.read(CONFIG_START_ADDRESS); 20 | if (version == CONFIG_ACTIVE_VERSION) { 21 | EEPROM.get(CONFIG_START_ADDRESS, _cfgStruct); 22 | EEPROM.end(); 23 | Log.info("Configuration read at 0x%x with v%i", CONFIG_START_ADDRESS, version); 24 | } else { 25 | //init config 26 | _cfgStruct.version = CONFIG_ACTIVE_VERSION; 27 | strncpy(_cfgStruct.wifi.hostname, "ESP8266", 32); 28 | _cfgStruct.ports.jsonServer = 19444; 29 | _cfgStruct.ports.udpLed = 19446; 30 | _cfgStruct.led.timeoutMs = 5000; 31 | _cfgStruct.led.autoswitch = true; 32 | _cfgStruct.led.count = 1; 33 | _cfgStruct.led.color = CRGB(254, 254, 254); 34 | _cfgStruct.misc.udpProtocol = UDP_RAW; 35 | EEPROM.end(); 36 | saveConfig(); 37 | Log.info("Configuration at 0x%x with v%i (v%i expected), new configuration created", CONFIG_START_ADDRESS, version, CONFIG_ACTIVE_VERSION); 38 | } 39 | _cfgLoaded = true; 40 | } 41 | } 42 | 43 | ConfigStruct *Config::getConfig(void) { 44 | initConfig(); 45 | return &_cfgStruct; 46 | } 47 | 48 | void Config::loadStaticConfig(void) { 49 | Log.info("CFG=%s", "loadStaticConfig initConfig"); 50 | initConfig(); 51 | strlcpy(_cfgStruct.wifi.ssid, CONFIG_WIFI_SSID, sizeof(_cfgStruct.wifi.ssid)); 52 | strlcpy(_cfgStruct.wifi.password, CONFIG_WIFI_PASSWORD, sizeof(_cfgStruct.wifi.password)); 53 | strlcpy(_cfgStruct.wifi.hostname, CONFIG_WIFI_HOSTNAME, sizeof(_cfgStruct.wifi.hostname)); 54 | 55 | #ifdef CONFIG_WIFI_STATIC_IP 56 | _cfgStruct.wifi.ip = ip2cfg(CONFIG_WIFI_IP); 57 | _cfgStruct.wifi.subnet = ip2cfg(CONFIG_WIFI_SUBNET); 58 | _cfgStruct.wifi.dns = ip2cfg(CONFIG_WIFI_DNS); 59 | #else 60 | _cfgStruct.wifi.ip.a = 0; 61 | _cfgStruct.wifi.ip.b = 0; 62 | _cfgStruct.wifi.ip.c = 0; 63 | _cfgStruct.wifi.ip.d = 0; 64 | 65 | _cfgStruct.wifi.subnet.a = 0; 66 | _cfgStruct.wifi.subnet.b = 0; 67 | _cfgStruct.wifi.subnet.c = 0; 68 | _cfgStruct.wifi.subnet.d = 0; 69 | 70 | _cfgStruct.wifi.dns.a = 0; 71 | _cfgStruct.wifi.dns.b = 0; 72 | _cfgStruct.wifi.dns.c = 0; 73 | _cfgStruct.wifi.dns.d = 0; 74 | #endif 75 | _cfgStruct.led.idleMode = CONFIG_LED_STANDARD_MODE; 76 | _cfgStruct.led.timeoutMs = CONFIG_LED_STANDARD_MODE_TIMEOUT_MS; 77 | _cfgStruct.led.autoswitch = CONFIG_LED_HYPERION_AUTOSWITCH; 78 | _cfgStruct.led.count = CONFIG_LED_COUNT; 79 | _cfgStruct.led.color = CONFIG_LED_STATIC_COLOR; 80 | 81 | _cfgStruct.ports.jsonServer = CONFIG_PORT_JSON_SERVER; 82 | _cfgStruct.ports.udpLed = CONFIG_PORT_UDP_LED; 83 | 84 | _cfgStruct.misc.udpProtocol = CONFIG_PROTOCOL_UDP; 85 | 86 | saveConfig(); 87 | Log.info("CFG=%s", "loadStaticConfig END"); 88 | } 89 | 90 | void Config::logConfig(void) { 91 | initConfig(); 92 | Log.debug("CFG Show Config"); 93 | 94 | Log.debug("+WIFI+"); 95 | Log.debug(" ssid=%s", _cfgStruct.wifi.ssid); 96 | Log.debug(" password=%s", _cfgStruct.wifi.password); 97 | Log.debug(" ip=%i.%i.%i.%i", _cfgStruct.wifi.ip.a, _cfgStruct.wifi.ip.b, _cfgStruct.wifi.ip.c, _cfgStruct.wifi.ip.d); 98 | Log.debug(" subnet=%i.%i.%i.%i", _cfgStruct.wifi.subnet.a, _cfgStruct.wifi.subnet.b, _cfgStruct.wifi.subnet.c, _cfgStruct.wifi.subnet.d); 99 | Log.debug(" dns=%i.%i.%i.%i", _cfgStruct.wifi.dns.a, _cfgStruct.wifi.dns.b, _cfgStruct.wifi.dns.c, _cfgStruct.wifi.dns.d); 100 | Log.debug(" hostname=%s", _cfgStruct.wifi.hostname); 101 | 102 | Log.debug("+LED+"); 103 | Log.debug(" idleMode=%i", _cfgStruct.led.idleMode); 104 | Log.debug(" timeoutMs=%i", _cfgStruct.led.timeoutMs); 105 | Log.debug(" autoswitch=%i", _cfgStruct.led.autoswitch); 106 | Log.debug(" count=%i", _cfgStruct.led.count); 107 | Log.debug(" static-color.r=%i", _cfgStruct.led.color.r); 108 | Log.debug(" static-color.g=%i", _cfgStruct.led.color.g); 109 | Log.debug(" static-color.b=%i", _cfgStruct.led.color.b); 110 | 111 | Log.debug("+PORTS+"); 112 | Log.debug(" jsonServer=%i", _cfgStruct.ports.jsonServer); 113 | Log.debug(" udpLed=%i", _cfgStruct.ports.udpLed); 114 | 115 | Log.debug("+MISC+"); 116 | Log.debug(" udpProtocol=%i", _cfgStruct.misc.udpProtocol); 117 | 118 | } 119 | 120 | byte *Config::cfg2ip(ConfigIP ipStruct) { 121 | Log.verbose("CFG=cfg2ip: %i.%i.%i.%i", ipStruct.a, ipStruct.b, ipStruct.c, ipStruct.d); 122 | byte *ipByte = new byte[4]; 123 | ipByte[0] = ipStruct.a; 124 | ipByte[1] = ipStruct.b; 125 | ipByte[2] = ipStruct.c; 126 | ipByte[3] = ipStruct.d; 127 | //byte ipByte[] = { ipStruct.a, ipStruct.b, ipStruct.c, ipStruct.d }; 128 | return ipByte; 129 | } 130 | 131 | ConfigIP Config::ip2cfg(const byte ip[4]) { 132 | Log.verbose("CFG=ip2cfg: %i.%i.%i.%i", ip[0], ip[1], ip[2], ip[3]); 133 | ConfigIP cfgIp; 134 | cfgIp.a = ip[0]; 135 | cfgIp.b = ip[1]; 136 | cfgIp.c = ip[2]; 137 | cfgIp.d = ip[3]; 138 | return cfgIp; 139 | } 140 | -------------------------------------------------------------------------------- /HyperionRGB/WrapperUdpLed.cpp: -------------------------------------------------------------------------------- 1 | #include "WrapperUdpLed.h" 2 | 3 | WrapperUdpLed::WrapperUdpLed(uint16_t ledCount, uint16_t udpPort, UdpProtocol udpProtocol) { 4 | _udp = WiFiUDP(); 5 | _ledCount = ledCount; 6 | _udpPort = udpPort; 7 | _udpProtocol = udpProtocol; 8 | 9 | _bufferSize = _ledCount * 3; //3 bytes per LED 10 | 11 | _udpBuffer = new byte [_bufferSize + 1]; 12 | _udpBuffer[_bufferSize] = 0; 13 | 14 | _opened = false; 15 | } 16 | 17 | void WrapperUdpLed::begin(void) { 18 | if (!_opened) { 19 | Log.info("Open port %i for UDP with protocol %i...", _udpPort, _udpProtocol); 20 | if (_udp.begin(_udpPort)) { 21 | Log.info("success"); 22 | _opened = true; 23 | } else { 24 | Log.error("no success"); 25 | } 26 | } 27 | } 28 | void WrapperUdpLed::stop(void) { 29 | if (_opened) { 30 | Log.info("Close port %i for UDP...", _udpPort); 31 | _udp.stop(); 32 | _opened = false; 33 | } 34 | } 35 | void WrapperUdpLed::handle(void) { 36 | int bytes = _udp.parsePacket(); 37 | if (bytes > 0) { 38 | Log.debug("UDP-Packet received, length: %i", bytes); 39 | //See @https://hyperion-project.org/wiki/UDP-Device 40 | if (_udpProtocol == UDP_RAW) { 41 | handleProtocolRaw(bytes); 42 | } else if (_udpProtocol == UDP_FRAGMENT) { 43 | handleProtocolFragment(bytes); 44 | } else if (_udpProtocol == UDP_TPM2) { 45 | handleProtocolTPM2(bytes); 46 | } else { //Fallback 47 | handleProtocolRaw(bytes); 48 | } 49 | } 50 | } 51 | 52 | void WrapperUdpLed::handleProtocolRaw(int bytes) { 53 | //Raw Format, 3 bytes per LED, Full LED set. 54 | /// {0: R, 1: G, 2: B} 55 | if (bytes == _bufferSize) { 56 | _udp.readBytes(_udpBuffer, _bufferSize); 57 | Log.verbose("Contents: %s", _udpBuffer); 58 | for (int i = 0; i < _ledCount; i++) { 59 | updateLed(i, _udpBuffer[i * 3 + 0], _udpBuffer[i * 3 + 1], _udpBuffer[i * 3 + 2]); 60 | } 61 | refreshLeds(); 62 | } else { 63 | Log.debug("Packet size for protocol 0 invalid: expected=%i, actual=%i", _bufferSize, bytes); 64 | } 65 | } 66 | void WrapperUdpLed::handleProtocolFragment(int bytes) { 67 | /// 0: Update ID & 0xF 68 | /// 1: Fragment ?! 69 | /// 2: First LED ID, high byte / 3: First LED ID, low byte --> int16_t? 70 | /// 4: {0: R, 1: G, 2: B} 71 | if (bytes > 4 && bytes-4 <= _bufferSize) { 72 | _udp.readBytes(_udpBuffer, 4); 73 | 74 | byte updateId = _udpBuffer[0] & 0x0F; 75 | byte fragment = _udpBuffer[1]; 76 | int ledIdStart = 256 * _udpBuffer[2] + _udpBuffer[3]; //Multiply high byte 77 | int ledIdEnd = ledIdStart + (bytes - 4) / 3; 78 | 79 | Log.verbose("updateId: %X, fragment: %X, ledIdStart: %i, ledIdEnd: %i", updateId, fragment, ledIdStart, ledIdEnd); 80 | if (ledIdEnd <= _ledCount) { 81 | _udp.readBytes(_udpBuffer, bytes - 4); 82 | for (int i=0; i int16_t? 98 | /// 4: Fragment ID 99 | /// 5: Fragment ID maximum 100 | /// 6: {0: R, 1: G, 2: B} 101 | /// 7: 0x36 102 | if (bytes > 7 && bytes-7 <= _bufferSize) { 103 | _udp.readBytes(_udpBuffer, 5); 104 | Log.verbose("Magic byte: %X, Frame type: %X", _udpBuffer[0], _udpBuffer[1]); 105 | 106 | if (_udpBuffer[0] == 0x9C && _udpBuffer[1] == 0xDA) { 107 | _udp.readBytes(_udpBuffer, 3); 108 | int dataLength = 256 * _udpBuffer[2] + _udpBuffer[3]; //Multiply high byte 109 | int ledCount = dataLength / 3; 110 | byte fragmentId = _udpBuffer[4]; 111 | byte fragmentMax = _udpBuffer[5]; 112 | Log.verbose("Data length: %i, LED count: %i, Fragment ID: %i, Fragment Maximum: %i", dataLength, ledCount, fragmentId, fragmentMax); 113 | size_t len = _udp.readBytes(_udpBuffer, bytes - 5); 114 | if (_udpBuffer[len-1] == 0x36) { 115 | if (fragmentMax == 1) { 116 | for (int i=0; i Include Library -> Add .ZIP Library 37 | 38 | f) ESP32 Webserver https://github.com/nhatuan84/esp32-webserver - install manually (for esp32 only) 39 | 40 | ## Installation 41 | 42 | ### Configuration of the board 43 | Attention, enabling Logging can slowdown the leds responsiveness! 44 | 45 | #### Basic 46 | 1. Go to the `HyperionRGB` folder and create a copy of `ConfigStatic.h.example`. Remove the `.example` suffix 47 | 2. Configure the `ConfigStatic.h` for your needs: 48 | - Select your LED chip type. All LEDs of the [FastLed](https://github.com/FastLED/FastLED) libraries are supported 49 | - Configure the used LED pins. You can also change the Pin Order. The NodeMCU order doesn't work sometimes to please also try the `RAW_PIN_ORDER`` 50 | - Define the number of used LEDs 51 | - Define one of the standard modes which are active when your light is idle. Choose one from: OFF, HYPERION_UDP, STATIC_COLOR, RAINBOW, FIRE2012 52 | - You maydefine Wifi configuration but you can also change it from the Webinterface 53 | 3. Open the `HyperionRGB.ino` the Arduino IDE 54 | 4. Compile and upload to your board 55 | 56 | #### AP fallback 57 | HyperionRGB will try to connected to the saved WiFi for 3 times and then falls back into AP Mode for 60 seconds. It will open a new WiFi network with the standard name "HyperionRGB" and password "HyperionRGB"- The SSID/Password may be overriden via the static config. (hostname/password) 58 | 59 | #### UDP Protocol 60 | This section describes the different UDP Protocol formats. 61 | ##### RAW - Protocol 0 - Raw Format 62 | Standard format, will expect the leds data via udp as a block 63 | 3 bytes per LED, Full LED set 64 | ``` 65 | [LED1: {0: R, 1: G, 2: B}, LED2: ...] 66 | ``` 67 | ##### FRAGMENT - Protocol 2 - Fragmented Format 68 | One byte per block: 69 | 70 | 0. Update ID & 0xF 71 | 1. Fragment ?! 72 | 2. First LED ID, high byte 73 | 3. First LED ID, low byte 74 | 4. [LED1: {0: R, 1: G, 2: B}, LED2: ...] 75 | 76 | ##### TPM2 - Protocol 3 - TPM2.net 77 | 0. 0x9C //magic byte 78 | 1. 0xDA //data frame 79 | 2. Data length (LED count * 3), high byte 80 | 3. Data length (LED count * 3), low byte 81 | 4. Fragment ID 82 | 5. Fragment ID maximum 83 | 6. {0: R, 1: G, 2: B} 84 | 7. 0x36 85 | 86 | ### Configuration of Hyperion 87 | #### Standalone 88 | If you just want a single wireless LED-Stripe you can just configure hyperion as always, except you need to set the fitting device as following: 89 | ``` 90 | { 91 | "colorOrder" : "rgb", 92 | "maxpacket" : 1450, 93 | "name" : "AmbiSZ-ESP8266", 94 | "output" : "ESP8266:19446", /// 95 | "protocol" : 0, 96 | "rate" : 250000, 97 | "type" : "udp" 98 | }, 99 | ``` 100 | 101 | #### As second instance 102 | You need two running hyperion instances. The first grabs the data on e.g. a rasbperry pi and controls any local attached LED strips. This first instance is configured to forward its data to a second hyperion instance on the same machine. Be sure to only forward the UDP data: 103 | 104 | ``` 105 | "forwarder" : 106 | { 107 | "proto" : [ "localhost:19447" ] 108 | }, 109 | ``` 110 | 111 | The second hyperion instance is configured to use UDP as device so that it can talk to the ESP directly. This second hyperion instance can run on the same machine as the first instance. Just make sure that you set the UDP ports, hostnames/IPs and LED number accordingly to the values you've configured for the ESP. 112 | 113 | ``` 114 | { 115 | "colorOrder" : "rgb", 116 | "maxpacket" : 1450, 117 | "name" : "AmbiSZ-ESP8266", 118 | "output" : "ESP8266:19446", /// 119 | "protocol" : 0, 120 | "rate" : 250000, 121 | "type" : "udp" 122 | }, 123 | 124 | 125 | "protoServer" : 126 | { 127 | "port" : 19447 128 | }, 129 | "jsonServer" : 130 | { 131 | "port" : 19446 132 | }, 133 | ``` 134 | # For Hyperion NG throug web interface 135 | 136 | Go to Confiruration -> General, select Controller type: **udpraw** set Target IP (your ESP IP addr) and Port (**19446**). 137 | 138 | 139 | ### Configuration for Engimalight 140 | Please use UDP Protocol "UDP_RAW" in ConfigStatic.h for the ESP part. 141 | You may need to change the output to the IP of your ESP! The prefix doesn't matter as the ESP should ignore it (Untested). 142 | 143 | enigmalight.conf 144 | ``` 145 | [device] 146 | name ambilight 147 | output 192.168.1.123 148 | type udp 149 | port 19446 150 | interval 20000 151 | prefix 41 64 61 00 71 24 152 | channels 100 153 | rate 115200 154 | debug off 155 | ``` 156 | Source: https://www.vuplus-support.org/wbb4/index.php?thread/107895-enigmalight-ambilight-f%C3%BCr-4k-boxen-arm/&postID=1661992#post1661992 157 | 158 | There's a detailed instruction page for [controlling multiple devices](https://hyperion-project.org/wiki/Controlling-Multiple-Devices). 159 | 160 | If you like my work spread the word! 161 | Donation: http://www.scilor.com/donate.html 162 | -------------------------------------------------------------------------------- /HyperionRGB/HyperionRGB.ino: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | #include "BaseHeader.h" 5 | 6 | #include "EnhancedThread.h" 7 | 8 | #include "LoggerInit.h" 9 | 10 | #include "WrapperWiFi.h" 11 | #include "WrapperOTA.h" 12 | #include "WrapperLedControl.h" 13 | #include "WrapperUdpLed.h" 14 | #include "WrapperJsonServer.h" 15 | 16 | #include "WrapperWebconfig.h" 17 | #include 18 | 19 | #define LED LED_BUILTIN // LED in NodeMCU at pin GPIO16 (D0) or LED_BUILTIN @Lolin32. 20 | int ledState = LOW; 21 | 22 | LoggerInit loggerInit; 23 | 24 | WrapperWiFi wifi; 25 | WrapperOTA ota; 26 | 27 | WrapperLedControl ledStrip; 28 | 29 | WrapperUdpLed udpLed; 30 | WrapperJsonServer jsonServer; 31 | 32 | #ifdef CONFIG_ENABLE_WEBCONFIG 33 | WrapperWebconfig webServer; 34 | #endif 35 | 36 | Mode activeMode = MODE_NONE; 37 | boolean autoswitch; 38 | 39 | ThreadController threadController = ThreadController(); 40 | Thread statusThread = Thread(); 41 | EnhancedThread animationThread = EnhancedThread(); 42 | EnhancedThread resetThread = EnhancedThread(); 43 | EnhancedThread apThread = EnhancedThread(); 44 | 45 | DNSServer dnsServer; 46 | 47 | void statusInfo(void) { 48 | if (ledState == LOW) { 49 | ledState = HIGH; 50 | } else { 51 | ledState = LOW; 52 | Log.debug("HEAP=%i", ESP.getFreeHeap()); 53 | } 54 | digitalWrite(LED, ledState); 55 | } 56 | 57 | void animationStep() { 58 | switch (activeMode) { 59 | case RAINBOW: 60 | ledStrip.rainbowStep(); 61 | break; 62 | case FIRE2012: 63 | ledStrip.fire2012Step(); 64 | break; 65 | case RAINBOW_V2: 66 | ledStrip.rainbowV2Step(); 67 | break; 68 | case RAINBOW_FULL: 69 | ledStrip.rainbowFullStep(); 70 | break; 71 | } 72 | } 73 | 74 | void changeMode(Mode newMode, int interval = 0) { 75 | if (newMode != activeMode) { 76 | Log.info("Mode changed to %i", newMode); 77 | activeMode = newMode; 78 | if (!autoswitch) 79 | udpLed.stop(); 80 | 81 | switch (activeMode) { 82 | case OFF: 83 | ledStrip.clear(); 84 | ledStrip.show(); 85 | break; 86 | case STATIC_COLOR: 87 | ledStrip.fillSolid(static_cast(Config::getConfig()->led.color)); 88 | break; 89 | case RAINBOW: 90 | case RAINBOW_V2: 91 | case RAINBOW_FULL: 92 | if (interval == 0) 93 | interval = 500; 94 | animationThread.setInterval(interval); 95 | break; 96 | case FIRE2012: 97 | if (interval == 0) 98 | interval = 16; 99 | animationThread.setInterval(interval); 100 | break; 101 | case HYPERION_UDP: 102 | if (!autoswitch) 103 | udpLed.begin(); 104 | } 105 | if (interval > 0) 106 | Log.debug("Interval set to %ims", interval); 107 | } 108 | } 109 | 110 | void updateLed(int id, byte r, byte g, byte b) { 111 | if (activeMode == HYPERION_UDP) { 112 | Log.verbose("LED %i, r=%i, g=%i, b=%i", id + 1, r, g, b); 113 | ledStrip.leds[id].setRGB(r, g, b); 114 | } 115 | } 116 | void refreshLeds(void) { 117 | if (activeMode == HYPERION_UDP) { 118 | Log.debug("refresh LEDs"); 119 | ledStrip.show(); 120 | if (autoswitch) 121 | resetThread.reset(); 122 | } else if (autoswitch) { 123 | changeMode(HYPERION_UDP); 124 | Log.info("Autoswitch to HYPERION_UDP"); 125 | } 126 | } 127 | 128 | void ledColorWipe(byte r, byte g, byte b) { 129 | Log.debug("LED color wipe: r=%i, g=%i, b=%i", r, g, b); 130 | changeMode(STATIC_COLOR); 131 | ledStrip.fillSolid(r, g, b); 132 | } 133 | void resetMode(void) { 134 | Log.info("Reset Mode"); 135 | #ifdef CONFIG_ENABLE_WEBCONFIG 136 | changeMode(static_cast(Config::getConfig()->led.idleMode)); 137 | #else 138 | changeMode(CONFIG_LED_STANDARD_MODE); 139 | #endif 140 | resetThread.enabled = false; 141 | } 142 | 143 | void resetApIdle(void) { 144 | if (wifi.isAPConnected()) { 145 | Log.info("AP is used, keeping it alive..."); 146 | apThread.reset(); 147 | } else { 148 | Log.error("Restarting because nobody connected via ap..."); 149 | delay(1000); 150 | ESP.restart(); 151 | } 152 | } 153 | 154 | void initConfig(void) { 155 | #if defined(CONFIG_OVERWRITE_WEBCONFIG) && defined(CONFIG_ENABLE_WEBCONFIG) 156 | Config::loadStaticConfig(); 157 | #endif 158 | 159 | const char* ssid; 160 | const char* password; 161 | const char* hostname; 162 | const byte* ip; 163 | const byte* subnet; 164 | const byte* dns; 165 | uint16_t jsonServerPort; 166 | uint16_t udpLedPort; 167 | UdpProtocol udpProtocol; 168 | uint16_t ledCount; 169 | 170 | #ifdef CONFIG_ENABLE_WEBCONFIG 171 | //TODO Fallback 172 | ConfigStruct* cfg = Config::getConfig(); 173 | 174 | ssid = cfg->wifi.ssid; 175 | password = cfg->wifi.password; 176 | hostname = cfg->wifi.hostname; 177 | ip = Config::cfg2ip(cfg->wifi.ip); 178 | subnet = Config::cfg2ip(cfg->wifi.subnet); 179 | dns = Config::cfg2ip(cfg->wifi.dns); 180 | jsonServerPort = cfg->ports.jsonServer; 181 | udpLedPort = cfg->ports.udpLed; 182 | udpProtocol = static_cast(cfg->misc.udpProtocol); 183 | autoswitch = cfg->led.autoswitch; 184 | ledCount = cfg->led.count; 185 | 186 | Log.info("CFG=%s", "EEPROM config loaded"); 187 | Config::logConfig(); 188 | #else 189 | ssid = CONFIG_WIFI_SSID; 190 | password = CONFIG_WIFI_PASSWORD; 191 | hostname = CONFIG_WIFI_HOSTNAME; 192 | #ifdef CONFIG_WIFI_STATIC_IP 193 | ip = CONFIG_WIFI_IP; 194 | subnet = CONFIG_WIFI_SUBNET; 195 | dns = CONFIG_WIFI_DNS; 196 | #else 197 | const byte empty[4] = {0}; 198 | ip = empty; 199 | #endif 200 | jsonServerPort = CONFIG_PORT_JSON_SERVER; 201 | udpLedPort = CONFIG_PORT_UDP_LED; 202 | udpProtocol = CONFIG_PROTOCOL_UDP; 203 | autoswitch = CONFIG_LED_HYPERION_AUTOSWITCH; 204 | ledCount = CONFIG_LED_COUNT; 205 | 206 | Log.info("CFG=%s", "Static config loaded"); 207 | #endif 208 | 209 | wifi = WrapperWiFi(ssid, password, ip, subnet, dns, hostname); 210 | udpLed = WrapperUdpLed(ledCount, udpLedPort, udpProtocol); 211 | jsonServer = WrapperJsonServer(ledCount, jsonServerPort); 212 | } 213 | 214 | void handleEvents(void) { 215 | ota.handle(); 216 | udpLed.handle(); 217 | jsonServer.handle(); 218 | #ifdef CONFIG_ENABLE_WEBCONFIG 219 | if (wifi.isAP()) 220 | dnsServer.processNextRequest(); 221 | if (webServer.handle() && wifi.isAPConnected()) { 222 | apThread.reset(); 223 | } 224 | #endif 225 | 226 | threadController.run(); 227 | } 228 | 229 | void setup(void) { 230 | LoggerInit loggerInit = LoggerInit(115200); 231 | 232 | initConfig(); 233 | ota = WrapperOTA(); 234 | ledStrip = WrapperLedControl(); 235 | 236 | statusThread.onRun(statusInfo); 237 | statusThread.setInterval(5000); 238 | threadController.add(&statusThread); 239 | 240 | animationThread.onRun(animationStep); 241 | animationThread.setInterval(1000); 242 | 243 | resetThread.onRun(resetMode); 244 | #ifdef CONFIG_ENABLE_WEBCONFIG 245 | resetThread.setInterval(Config::getConfig()->led.timeoutMs); 246 | #else 247 | resetThread.setInterval(CONFIG_LED_STANDARD_MODE_TIMEOUT_MS); 248 | #endif 249 | resetThread.enabled = false; 250 | threadController.add(&resetThread); 251 | 252 | #ifdef CONFIG_ENABLE_WEBCONFIG 253 | ledStrip.begin(Config::getConfig()->led.count); 254 | #else 255 | ledStrip.begin(CONFIG_LED_COUNT); 256 | #endif 257 | resetMode(); 258 | animationStep(); 259 | 260 | wifi.begin(); 261 | if (wifi.isAP()) { 262 | apThread.onRun(resetApIdle); 263 | apThread.setInterval(60*1000); 264 | apThread.reset(); 265 | threadController.add(&apThread); 266 | 267 | dnsServer.start(53, "*", IPAddress(192, 168, 4, 1)); //Port 53 - standard port 268 | 269 | } else { 270 | apThread.enabled = false; 271 | } 272 | 273 | #ifdef CONFIG_ENABLE_WEBCONFIG 274 | webServer = WrapperWebconfig(); 275 | webServer.begin(); 276 | ota.begin(Config::getConfig()->wifi.hostname); 277 | #else 278 | ota.begin(CONFIG_WIFI_HOSTNAME); 279 | #endif 280 | 281 | if (autoswitch || activeMode == HYPERION_UDP) 282 | udpLed.begin(); 283 | 284 | udpLed.onUpdateLed(updateLed); 285 | udpLed.onRefreshLeds(refreshLeds); 286 | 287 | jsonServer.begin(); 288 | jsonServer.onLedColorWipe(ledColorWipe); 289 | jsonServer.onClearCmd(resetMode); 290 | jsonServer.onEffectChange(changeMode); 291 | 292 | pinMode(LED, OUTPUT); // LED pin as output. 293 | Log.info("HEAP=%i", ESP.getFreeHeap()); 294 | } 295 | 296 | void loop(void) { 297 | handleEvents(); 298 | switch (activeMode) { 299 | case RAINBOW: 300 | case FIRE2012: 301 | case RAINBOW_V2: 302 | case RAINBOW_FULL: 303 | animationThread.runIfNeeded(); 304 | break; 305 | case STATIC_COLOR: 306 | break; 307 | case HYPERION_UDP: 308 | break; 309 | } 310 | } 311 | -------------------------------------------------------------------------------- /HyperionRGB/WrapperWebconfig.cpp: -------------------------------------------------------------------------------- 1 | #include "WrapperWebconfig.h" 2 | 3 | void WrapperWebconfig::begin() { 4 | _server->onNotFound([&](){ WrapperWebconfig::handleNotFound(); }); 5 | _server->on("/", [&](){ WrapperWebconfig::handleRoot(); }); 6 | _server->begin(); 7 | } 8 | 9 | bool WrapperWebconfig::handle(void) { 10 | _server->handleClient(); 11 | bool handled = _handled; 12 | _handled = false; 13 | return handled; 14 | } 15 | 16 | void WrapperWebconfig::handleNotFound(void) { 17 | _handled = true; 18 | _server->sendHeader("Location", "/", true); 19 | _server->send(302, "text/plain",""); 20 | } 21 | 22 | void WrapperWebconfig::handleRoot(void) { 23 | _handled = true; 24 | Log.debug("Webconfig started HEAP=%i", ESP.getFreeHeap()); 25 | initHelperVars(); 26 | Log.debug("Webconfig initialized HEAP=%i", ESP.getFreeHeap()); 27 | if (_server->method() == HTTP_POST) { 28 | Log.debug("POST HEAP=%i", ESP.getFreeHeap()); 29 | changeConfig(); 30 | } 31 | clearHelperVars(); 32 | String message = htmlTemplate("ESP8266 LED Configuration", WrapperWebconfig::config()); 33 | Log.debug("Webconfig max HEAP=%i", ESP.getFreeHeap()); 34 | 35 | //Log.debug("Webconfig cleared HEAP=%i", ESP.getFreeHeap()); 36 | _server->send(200, "text/html", message); 37 | Log.debug("Webconfig sent HEAP=%i", ESP.getFreeHeap()); 38 | } 39 | 40 | String WrapperWebconfig::escape(String text) { 41 | return text; 42 | } 43 | String WrapperWebconfig::escape(char* text) { 44 | return String(text); 45 | } 46 | String WrapperWebconfig::escape(uint8_t text) { 47 | char buf[4]; 48 | sprintf(buf, "%u", text); 49 | return String(buf); 50 | } 51 | String WrapperWebconfig::escape(uint16_t text) { 52 | char buf[6]; 53 | sprintf(buf, "%u", text); 54 | return String(buf); 55 | } 56 | String WrapperWebconfig::escape(uint32_t text) { 57 | char buf[10]; 58 | sprintf(buf, "%u", text); 59 | return String(buf); 60 | } 61 | String WrapperWebconfig::color2hex(CRGB color) { 62 | char buf[6]; 63 | sprintf(buf, "%02X%02X%02X", color.r, color.g, color.b); 64 | return String(buf); 65 | } 66 | String WrapperWebconfig::ipToString(ConfigIP ip) { 67 | char buf[16]; 68 | if (ip.a == 0) 69 | return ""; 70 | 71 | sprintf(buf, "%d.%d.%d.%d", ip.a, ip.b, ip.c, ip.d); 72 | return String(buf); 73 | } 74 | void WrapperWebconfig::changeConfig(void) { 75 | ConfigStruct *cfg = Config::getConfig(); 76 | boolean restart = false; 77 | boolean loadStatic = false; 78 | 79 | for (uint8_t i=0; i<_server->args(); i++){ 80 | String argName = _server->argName(i); 81 | String argValue = _server->arg(i); 82 | 83 | Log.debug("Config: \"%s\":\"%s\"", argName.c_str(), argValue.c_str()); 84 | 85 | if (argName == "wifi-ssid" && argValue.length() < sizeof(cfg->wifi.ssid)) { 86 | strcpy(cfg->wifi.ssid, argValue.c_str()); 87 | } else if (argName == "wifi-password" && argValue.length() < sizeof(cfg->wifi.password) && argValue.length() > 0) { 88 | //only set if empty (Password field is always empty on the webpage) 89 | strcpy(cfg->wifi.password, argValue.c_str()); 90 | } else if (argName == "wifi-ip") { 91 | byte ip[4]; 92 | parseBytes(argValue.c_str(), '.', ip, 4, 10); 93 | cfg->wifi.ip.a = ip[0]; 94 | cfg->wifi.ip.b = ip[1]; 95 | cfg->wifi.ip.c = ip[2]; 96 | cfg->wifi.ip.d = ip[3]; 97 | } else if (argName == "wifi-subnet") { 98 | byte ip[4]; 99 | parseBytes(argValue.c_str(), '.', ip, 4, 10); 100 | cfg->wifi.subnet.a = ip[0]; 101 | cfg->wifi.subnet.b = ip[1]; 102 | cfg->wifi.subnet.c = ip[2]; 103 | cfg->wifi.subnet.d = ip[3]; 104 | } else if (argName == "wifi-dns") { 105 | byte ip[4]; 106 | parseBytes(argValue.c_str(), '.', ip, 4, 10); 107 | cfg->wifi.dns.a = ip[0]; 108 | cfg->wifi.dns.b = ip[1]; 109 | cfg->wifi.dns.c = ip[2]; 110 | cfg->wifi.dns.d = ip[3]; 111 | } else if (argName == "wifi-hostname" && argValue.length() < sizeof(cfg->wifi.hostname)) { 112 | strcpy(cfg->wifi.hostname, argValue.c_str()); 113 | } else if (argName == "ports-json") { 114 | cfg->ports.jsonServer = argValue.toInt(); 115 | if (cfg->ports.jsonServer == 0) 116 | cfg->ports.jsonServer = 19444; 117 | } else if (argName == "ports-udp") { 118 | cfg->ports.udpLed = argValue.toInt(); 119 | if (cfg->ports.udpLed == 0) 120 | cfg->ports.udpLed = 19446; 121 | } else if (argName == "led-idleMode") { 122 | cfg->led.idleMode = getSelectedEntry(argValue, _idleModes); 123 | } else if (argName == "led-udpProtocol") { 124 | cfg->misc.udpProtocol = getSelectedEntry(argValue, _udpProtocols); 125 | } else if (argName == "led-timeoutMs") { 126 | if (argValue.equals("0")) { 127 | cfg->led.timeoutMs = 0; 128 | } else { 129 | int val = argValue.toInt(); 130 | if (val > 0) { 131 | cfg->led.timeoutMs = val; 132 | } else { 133 | cfg->led.timeoutMs = 5000; 134 | } 135 | } 136 | } else if (argName == "led-autoswitch") { 137 | if (argValue.equals("led-autoswitch")) { 138 | cfg->led.autoswitch = true; 139 | } else { 140 | cfg->led.autoswitch = false; 141 | } 142 | } else if (argName == "led-count") { 143 | uint16_t val = argValue.toInt(); 144 | if (val > 0) { 145 | cfg->led.count = val; 146 | } else { 147 | cfg->led.count = 1; 148 | } 149 | } else if (argName == "led-color") { 150 | uint32_t val = strtol(argValue.c_str(), 0, 16); 151 | cfg->led.color = CRGB(val); 152 | } else if (argName == "saveRestart") { 153 | restart = true; 154 | } else if (argName == "loadStatic") { 155 | loadStatic = true; 156 | } 157 | } 158 | if (loadStatic) 159 | Config::loadStaticConfig(); 160 | 161 | Config::saveConfig(); 162 | 163 | if (restart) 164 | ESP.restart(); 165 | } 166 | 167 | void WrapperWebconfig::parseBytes(const char* str, char sep, byte* bytes, int maxBytes, int base) { 168 | //sscanf Workaround 169 | for (int i = 0; i < maxBytes; i++) { 170 | bytes[i] = strtoul(str, NULL, base); // Convert byte 171 | str = strchr(str, sep); // Find next separator 172 | if (str == NULL || *str == '\0') { 173 | break; // No more separators, exit 174 | } 175 | str++; // Point to next character after separator 176 | } 177 | } 178 | 179 | String WrapperWebconfig::htmlTemplate(String title, String content) { 180 | String html = ""; 181 | html += ""; 182 | html += ""; 183 | html += ""; 184 | html += ""; 185 | html += ""; 186 | 187 | html += "" + title + ""; 188 | 189 | html += ""; 190 | //html += ""; 191 | 192 | html += ""; 193 | html += ""; 194 | html += ""; 195 | 196 | html += ""; 205 | 206 | html += ""; 207 | 208 | html += ""; 209 | 210 | html += ""; 211 | html += "
"; 212 | html += content; 213 | html += "
"; 214 | html += ""; 215 | 216 | html += ""; 217 | 218 | return html; 219 | } 220 | 221 | String WrapperWebconfig::groupTemplate(String title, String body) { 222 | String html = ""; 223 | 224 | html += "
"; 225 | html += "
" + title + "
"; 226 | html += "
"; 227 | 228 | html += body; 229 | 230 | html += "
"; 231 | html += "
"; 232 | 233 | return html; 234 | } 235 | 236 | String WrapperWebconfig::entryTemplate(String label, String tooltip, String id, String content) { 237 | String html = ""; 238 | 239 | html += "
"; 240 | html += ""; 241 | html += "
"; 242 | html += content; 243 | html += "
"; 244 | 245 | if (tooltip.length() > 0) { 246 | html += "
"; 247 | html += "i"; 248 | html += "
"; 249 | } 250 | 251 | html += "
"; 252 | 253 | return html; 254 | } 255 | 256 | String WrapperWebconfig::textTemplate(String label, String tooltip, String id, String text, String placeholder = "", int maxLen = 524288) { 257 | String html = ""; 258 | 259 | html += ""; 260 | 261 | return entryTemplate(label, tooltip, id, html); 262 | } 263 | 264 | String WrapperWebconfig::checkboxTemplate(String label, String tooltip, String id, boolean isChecked) { 265 | String html = ""; 266 | String checked = ""; 267 | if (isChecked) { 268 | checked = " checked=\"checked\""; 269 | } 270 | html += ""; 274 | 275 | return entryTemplate(label, tooltip, id, html); 276 | } 277 | 278 | String WrapperWebconfig::selectTemplate(String label, String tooltip, String id, LinkedList* entries) { 279 | String html = ""; 280 | 281 | html += ""; 292 | 293 | return entryTemplate(label, tooltip, id, html); 294 | } 295 | 296 | String WrapperWebconfig::config(void) { 297 | String html = ""; 298 | String groupContent = ""; 299 | boolean wifiReady = false; 300 | 301 | ConfigStruct *cfg = Config::getConfig(); 302 | 303 | if (cfg->wifi.ssid[0] != 0) { 304 | //check Wifi 305 | wifiReady = true; 306 | } 307 | 308 | html += "
"; 309 | html += "
"; 310 | 311 | html += "ESP8266 LED Coniguration"; 312 | 313 | groupContent = ""; 314 | groupContent += textTemplate("WiFi SSID", "", "wifi-ssid", escape(cfg->wifi.ssid), "", sizeof(cfg->wifi.ssid)-1); 315 | 316 | String passwordPlaceholder = "no password set"; 317 | if (cfg->wifi.password[0] != 0) 318 | passwordPlaceholder = "password saved"; 319 | groupContent += textTemplate("WiFi Password", "", "wifi-password", "", passwordPlaceholder, sizeof(cfg->wifi.password)-1); 320 | 321 | groupContent += textTemplate("IP","", "wifi-ip", ipToString(cfg->wifi.ip), "leave empty for dhcp", 15); 322 | groupContent += textTemplate("Subnet","", "wifi-subnet", ipToString(cfg->wifi.subnet), "255.255.255.0", 15); 323 | groupContent += textTemplate("DNS Server","", "wifi-dns", ipToString(cfg->wifi.dns), "192.168.1.1", 15); 324 | 325 | groupContent += textTemplate("Module Hostname","", "wifi-hostname", escape(cfg->wifi.hostname), "ESP8266", sizeof(cfg->wifi.hostname)-1); 326 | 327 | html += groupTemplate("WiFi", groupContent); 328 | 329 | if (wifiReady) { 330 | groupContent = ""; 331 | groupContent += textTemplate("JSON Server Port","", "ports-json", escape(cfg->ports.jsonServer), "19444", 5); 332 | groupContent += textTemplate("UDP LED Port","", "ports-udp", escape(cfg->ports.udpLed), "19446", 5); 333 | html += groupTemplate("Ports", groupContent); 334 | groupContent = ""; 335 | 336 | initHelperVars(); 337 | groupContent += textTemplate("LED Count", "", "led-count", escape(cfg->led.count), "1", 5); 338 | groupContent += selectTemplate("LED Idle Mode", "", "led-idleMode", _idleModes); 339 | groupContent += textTemplate("LED Static color (hex)", "", "led-color", color2hex(cfg->led.color), "FFFFFF", 6); 340 | groupContent += checkboxTemplate("Autoswitch to Hyperion_UDP/Idle Mode", "Automatically switch to Hyperion_UDP when UDP Data arriving and switch back to idle mode after timeout", "led-autoswitch", cfg->led.autoswitch); 341 | groupContent += textTemplate("Timeout Fallback in MS", "Switches back to Idle Mode after x milliseconds when no UDP data is arriving", "led-timeoutMs", escape(cfg->led.timeoutMs), "5000", 10); 342 | groupContent += selectTemplate("LED UDP Protocol", "", "led-udpProtocol", _udpProtocols); 343 | clearHelperVars(); 344 | 345 | html += groupTemplate("LEDs", groupContent); 346 | 347 | } else { 348 | html += "
"; 349 | html += "More settings visible, when wifi-connection is ready (internet needed for design)"; 350 | html += "
"; 351 | } 352 | 353 | groupContent = ""; 354 | groupContent += "
"; 355 | groupContent += ""; 356 | groupContent += "
"; 357 | groupContent += ""; 358 | groupContent += "
"; 359 | groupContent += "
"; 360 | 361 | groupContent += "
"; 362 | groupContent += ""; 363 | groupContent += "
"; 364 | groupContent += ""; 365 | groupContent += "
"; 366 | groupContent += "
"; 367 | 368 | groupContent += "
"; 369 | groupContent += ""; 370 | groupContent += "
"; 371 | groupContent += ""; 372 | groupContent += "
"; 373 | groupContent += "
"; 374 | 375 | groupContent += "
"; 376 | groupContent += "
"; 377 | 378 | html += groupTemplate("Buttons", groupContent); 379 | 380 | return html; 381 | } 382 | 383 | void WrapperWebconfig::initHelperVars(void) { 384 | ConfigStruct *cfg = Config::getConfig(); 385 | 386 | _idleModes = new LinkedList(); 387 | _udpProtocols = new LinkedList(); 388 | getIdleModes(cfg->led.idleMode, _idleModes); 389 | getUdpProtocols(cfg->misc.udpProtocol, _udpProtocols); 390 | } 391 | void WrapperWebconfig::clearHelperVars(void) { 392 | clearLinkedList(_idleModes); 393 | clearLinkedList(_udpProtocols); 394 | } 395 | void WrapperWebconfig::clearLinkedList(LinkedList* target) { 396 | Log.debug("Clearing LinkedList HEAP=%i", ESP.getFreeHeap()); 397 | for (int i=0; isize(); i++) { 398 | SelectEntryBase* entry = target->get(i); 399 | delete(entry); 400 | } 401 | target->clear(); 402 | delete(target); 403 | Log.debug("Cleared LinkedList HEAP=%i", ESP.getFreeHeap()); 404 | } 405 | 406 | template 407 | T WrapperWebconfig::getSelectedEntry(String selectedEntryValue, LinkedList* target) { 408 | T result; 409 | SelectEntry* entry; 410 | for (int i=0; isize(); i++) { 411 | entry = (SelectEntry*)target->get(i); 412 | if (entry->getSelectedValue() == selectedEntryValue) { 413 | result = entry->getValue(); 414 | entry->setSelected(true); 415 | } else { 416 | entry->setSelected(false); 417 | } 418 | } 419 | return result; 420 | } 421 | 422 | void WrapperWebconfig::getIdleModes(uint8_t active, LinkedList* target) { 423 | target->add((SelectEntryBase*) new SelectEntry("Off", "Off", active == OFF, OFF)); 424 | target->add((SelectEntryBase*) new SelectEntry("Hyperion", "Hyperion UDP", active == HYPERION_UDP, HYPERION_UDP)); 425 | target->add((SelectEntryBase*) new SelectEntry("Static", "Static color", active == STATIC_COLOR, STATIC_COLOR)); 426 | target->add((SelectEntryBase*) new SelectEntry("Rainbow", "Rainbow swirl", active == RAINBOW, RAINBOW)); 427 | target->add((SelectEntryBase*) new SelectEntry("RainbowV2", "Rainbow swirl v2", active == RAINBOW_V2, RAINBOW_V2)); 428 | target->add((SelectEntryBase*) new SelectEntry("RainbowFull", "Rainbow full", active == RAINBOW_FULL, RAINBOW_FULL)); 429 | target->add((SelectEntryBase*) new SelectEntry("Fire2012", "Fire2012", active == FIRE2012, FIRE2012)); 430 | } 431 | 432 | void WrapperWebconfig::getUdpProtocols(uint8_t active, LinkedList* target) { 433 | target->add((SelectEntryBase*) new SelectEntry("P0", "Protocol 0 (Raw)", active == UDP_RAW, UDP_RAW)); 434 | target->add((SelectEntryBase*) new SelectEntry("P2", "Protocol 2 (Fragment)", active == UDP_FRAGMENT, UDP_FRAGMENT)); 435 | target->add((SelectEntryBase*) new SelectEntry("P3", "Protocol 3 (TPM2 Fragments)", active == UDP_TPM2, UDP_TPM2)); 436 | } 437 | --------------------------------------------------------------------------------