diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..9a80a94 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,14 @@ +language: python +python: + - "2.7" +sudo: false +cache: + directories: + - "~/.platformio" +env: + - PLATFORMIO_CI_SRC=Arduino/McLighting +install: + - pip install -U platformio + - platformio update +script: + - platformio ci --project-conf=./platformio.ini -v \ No newline at end of file diff --git a/Arduino/McLighting/McLighting.ino b/Arduino/McLighting/McLighting.ino index c44d59f..3806f66 100644 --- a/Arduino/McLighting/McLighting.ino +++ b/Arduino/McLighting/McLighting.ino @@ -58,6 +58,12 @@ #endif #endif +#ifdef ENABLE_E131 + #include <ESPAsyncUDP.h> //https://github.com/me-no-dev/ESPAsyncUDP + #include <ESPAsyncE131.h> //https://github.com/forkineye/ESPAsyncE131 + ESPAsyncE131 e131(END_UNIVERSE - START_UNIVERSE + 1); +#endif + // *************************************************************************** // Instanciate HTTP(80) / WebSockets(81) Server @@ -90,14 +96,18 @@ WS2812FX strip = WS2812FX(NUMLEDS, PIN, NEO_GRB + NEO_KHZ800); // and minimize distance between Arduino and first pixel. Avoid connecting // on a live circuit...if you must, connect GND first. -#ifdef USE_WS2812FX_DMA +#ifdef USE_WS2812FX_DMA // Uses GPIO3/RXD0/RX, more info: https://github.com/Makuna/NeoPixelBus/wiki/ESP8266-NeoMethods #include <NeoPixelBus.h> NeoEsp8266Dma800KbpsMethod dma = NeoEsp8266Dma800KbpsMethod(NUMLEDS, 3); //800 KHz bitstream (most NeoPixel products w/WS2812 LEDs) //NeoEsp8266Dma400KbpsMethod dma = NeoEsp8266Dma400KbpsMethod(NUMLEDS, 3); //400 KHz (classic 'v1' (not v2) FLORA pixels, WS2811 drivers) #endif -#ifdef USE_WS2812FX_UART +#ifdef USE_WS2812FX_UART1 // Uses UART1: GPIO1/TXD0/TX, more info: https://github.com/Makuna/NeoPixelBus/wiki/ESP8266-NeoMethods #include <NeoPixelBus.h> - NeoEsp8266Uart800KbpsMethod dma = NeoEsp8266Uart800KbpsMethod(NUMLEDS, 3); + NeoEsp8266Uart0800KbpsMethod dma = NeoEsp8266Uart0800KbpsMethod(NUMLEDS, 3); +#endif +#ifdef USE_WS2812FX_UART2 // Uses UART2: GPIO2/TXD1/D4, more info: https://github.com/Makuna/NeoPixelBus/wiki/ESP8266-NeoMethods + #include <NeoPixelBus.h> + NeoEsp8266Uart1800KbpsMethod dma = NeoEsp8266Uart1800KbpsMethod(NUMLEDS, 3); #endif #if defined(USE_WS2812FX_DMA) or defined(USE_WS2812FX_UART) void DMA_Show(void) { @@ -840,6 +850,26 @@ void setup() { if(!spiffs_save_state.active()) spiffs_save_state.once(3, tickerSpiffsSaveState); #endif }); + + #ifdef ENABLE_E131 + server.on("/e131", []() { + exit_func = true; + mode = E131; + getStatusJSON(); + #ifdef ENABLE_MQTT + mqtt_client.publish(mqtt_outtopic, String("OK =e131").c_str()); + #endif + #ifdef ENABLE_AMQTT + amqttClient.publish(mqtt_outtopic.c_str(), qospub, false, String("OK =131").c_str()); + #endif + #ifdef ENABLE_HOMEASSISTANT + stateOn = true; + #endif + #ifdef ENABLE_STATE_SAVE_SPIFFS + if(!spiffs_save_state.active()) spiffs_save_state.once(3, tickerSpiffsSaveState); + #endif + }); + #endif server.on("/tv", []() { exit_func = true; @@ -895,6 +925,15 @@ void setup() { if (mdns_result) { MDNS.addService("http", "tcp", 80); } + + #ifdef ENABLE_E131 + // Choose one to begin listening for E1.31 data + // if (e131.begin(E131_UNICAST)) // Listen via Unicast + if (e131.begin(E131_MULTICAST, START_UNIVERSE, END_UNIVERSE)) // Listen via Multicast + Serial.println(F("Listening for data...")); + else + Serial.println(F("*** e131.begin failed ***")); + #endif #ifdef ENABLE_STATE_SAVE_SPIFFS (readStateFS()) ? DBG_OUTPUT_PORT.println(" Success!") : DBG_OUTPUT_PORT.println(" Failure!"); #endif @@ -1013,6 +1052,11 @@ void loop() { strip.trigger(); mode = HOLD; } + #ifdef ENABLE_E131 + if (mode == E131) { + handleE131(); + } + #endif #endif if (mode == HOLD || mode == CUSTOM) { if(!strip.isRunning()) strip.start(); diff --git a/Arduino/McLighting/definitions.h b/Arduino/McLighting/definitions.h index 44bc8eb..d54de2b 100644 --- a/Arduino/McLighting/definitions.h +++ b/Arduino/McLighting/definitions.h @@ -1,5 +1,6 @@ //#define USE_WS2812FX_DMA // Uses PIN is ignored & set to RX/GPIO3 Uses WS2812FX, see: https://github.com/kitesurfer1404/WS2812FX -//#define USE_WS2812FX_UART // Uses PIN is ignored & set to D4/GPIO2 Uses WS2812FX, see: https://github.com/kitesurfer1404/WS2812FX +//#define USE_WS2812FX_UART1 // Uses PIN is ignored & set to D4/GPIO2 Uses WS2812FX, see: https://github.com/kitesurfer1404/WS2812FX +//#define USE_WS2812FX_UART2 // Uses PIN is ignored & set to TX/GPIO1 Uses WS2812FX, see: https://github.com/kitesurfer1404/WS2812FX // Neopixel #define PIN 14 // PIN (14 / D5) where neopixel / WS2811 strip is attached @@ -16,7 +17,13 @@ const char HOSTNAME[] = "McLighting01"; // Friedly hostname #define ENABLE_HOMEASSISTANT // If defined, enable Homeassistant integration, ENABLE_MQTT or ENABLE_AMQTT must be active #define ENABLE_BUTTON // If defined, enable button handling code, see: https://github.com/toblum/McLighting/wiki/Button-control //#define MQTT_HOME_ASSISTANT_SUPPORT // If defined, use AMQTT and select Tools -> IwIP Variant -> Higher Bandwidth -#define ENABLE_LEGACY_ANIMATIONS +#define ENABLE_LEGACY_ANIMATIONS // Dont disbale this for now +#define ENABLE_E131 // E1.31 implementation + +#ifdef ENABLE_E131 + #define START_UNIVERSE 1 // First DMX Universe to listen for + #define END_UNIVERSE 2 // Total number of Universes to listen for, starting at UNIVERSE +#endif //#define WIFIMGR_PORTAL_TIMEOUT 180 //#define WIFIMGR_SET_MANUAL_IP @@ -104,7 +111,11 @@ uint32_t autoParams[][4] = { // color, speed, mode, duration (seconds) // List of all color modes #ifdef ENABLE_LEGACY_ANIMATIONS - enum MODE { SET_MODE, HOLD, OFF, SETCOLOR, SETSPEED, BRIGHTNESS, WIPE, RAINBOW, RAINBOWCYCLE, THEATERCHASE, TWINKLERANDOM, THEATERCHASERAINBOW, TV, CUSTOM }; + #ifdef ENABLE_E131 + enum MODE { SET_MODE, HOLD, OFF, SETCOLOR, SETSPEED, BRIGHTNESS, WIPE, RAINBOW, RAINBOWCYCLE, THEATERCHASE, TWINKLERANDOM, THEATERCHASERAINBOW, TV, CUSTOM, E131 }; + #else + enum MODE { SET_MODE, HOLD, OFF, SETCOLOR, SETSPEED, BRIGHTNESS, WIPE, RAINBOW, RAINBOWCYCLE, THEATERCHASE, TWINKLERANDOM, THEATERCHASERAINBOW, TV, CUSTOM }; + #endif MODE mode = RAINBOW; // Standard mode that is active when software starts bool exit_func = false; // Global helper variable to get out of the color modes when mode changes #else diff --git a/Arduino/McLighting/request_handlers.h b/Arduino/McLighting/request_handlers.h index 1d8cf12..7c62099 100644 --- a/Arduino/McLighting/request_handlers.h +++ b/Arduino/McLighting/request_handlers.h @@ -1,6 +1,40 @@ // *************************************************************************** // Request handlers // *************************************************************************** +#ifdef ENABLE_E131 +void checkForRequests(void); //prototype + +void handleE131(){ + if (!e131.isEmpty()) + { + e131_packet_t packet; + e131.pull(&packet); // Pull packet from ring buffer + + uint16_t universe = htons(packet.universe); + uint8_t *data = packet.property_values + 1; + + if (universe < START_UNIVERSE || universe > END_UNIVERSE) return; //async will take care about filling the buffer + + // Serial.printf("Universe %u / %u Channels | Packet#: %u / Errors: %u / CH1: %u\n", + // htons(packet.universe), // The Universe for this packet + // htons(packet.property_value_count) - 1, // Start code is ignored, we're interested in dimmer data + // e131.stats.num_packets, // Packet counter + // e131.stats.packet_errors, // Packet error counter + // packet.property_values[1]); // Dimmer data for Channel 1 + + uint16_t multipacketOffset = (universe - START_UNIVERSE) * 170; //if more than 170 LEDs (510 channels), client will send in next higher universe + if (NUMLEDS <= multipacketOffset) return; + uint16_t len = (170 + multipacketOffset > NUMLEDS) ? (NUMLEDS - multipacketOffset) : 170; + for (uint16_t i = 0; i < len; i++){ + uint16_t j = i * 3; + strip.setPixelColor(i + multipacketOffset, data[j], data[j + 1], data[j + 2]); + } + strip.show(); + checkForRequests(); + } +} +#endif + #ifdef ENABLE_HOMEASSISTANT void tickerSendState(){ new_ha_mqtt_msg = true; @@ -268,6 +302,19 @@ void setModeByStateString(String saved_state_string) { } #endif +#ifdef ENABLE_E131 + void handleE131NamedMode(String str_mode) { + exit_func = true; + if (str_mode.startsWith("=e131") or str_mode.startsWith("/e131")) { + if(strip.isRunning()) strip.stop(); + mode = E131; + #ifdef ENABLE_HOMEASSISTANT + stateOn = true; + #endif + } + } +#endif + void handleSetWS2812FXMode(uint8_t * mypayload) { mode = SET_MODE; uint8_t ws2812fx_mode_tmp = (uint8_t) strtol((const char *) &mypayload[1], NULL, 10); @@ -305,6 +352,11 @@ String listModesJSON(void) { const size_t bufferSize = JSON_ARRAY_SIZE(strip.getModeCount()+1) + strip.getModeCount()*JSON_OBJECT_SIZE(2) + 1000; DynamicJsonDocument jsonBuffer(bufferSize); JsonArray json = jsonBuffer.to<JsonArray>(); + #ifdef ENABLE_E131 + JsonObject objecte131 = json.createNestedObject(); + objecte131["mode"] = "e131"; + objecte131["name"] = "E131"; + #endif for (uint8_t i = 0; i < strip.getModeCount(); i++) { JsonObject object = json.createNestedObject(); object["mode"] = i; @@ -556,6 +608,9 @@ void checkpayload(uint8_t * payload, bool mqtt = false, uint8_t num = 0) { String str_mode = String((char *) &payload[0]); handleSetNamedMode(str_mode); + #ifdef ENABLE_E131 + handleE131NamedMode(str_mode); + #endif if (mqtt == true) { DBG_OUTPUT_PORT.print("MQTT: "); } else { @@ -628,6 +683,10 @@ void checkpayload(uint8_t * payload, bool mqtt = false, uint8_t num = 0) { // / ==> Set WS2812 mode. if (payload[0] == '/') { handleSetWS2812FXMode(payload); + #ifdef ENABLE_E131 + String str_mode = String((char *) &payload[0]); + handleE131NamedMode(str_mode); + #endif if (mqtt == true) { DBG_OUTPUT_PORT.print("MQTT: "); } else { @@ -783,7 +842,14 @@ void webSocketEvent(uint8_t num, WStype_t type, uint8_t * payload, size_t lenght //char modeName[30]; //strncpy_P(modeName, (PGM_P)strip.getModeName(strip.getMode()), sizeof(modeName)); // copy from progmem - root["effect"] = strip.getModeName(strip.getMode()); + #if defined(ENABLE_E131) and defined(ENABLE_HOMEASSISTANT) + if (mode == E131) + root["effect"] = "E131"; + else + root["effect"] = strip.getModeName(strip.getMode()); + #else + root["effect"] = strip.getModeName(strip.getMode()); + #endif char buffer[measureJson(root) + 1]; serializeJson(root, buffer, sizeof(buffer)); @@ -862,6 +928,13 @@ void webSocketEvent(uint8_t num, WStype_t type, uint8_t * payload, size_t lenght String effectString = root["effect"].as<String>(); for (uint8_t i = 0; i < strip.getModeCount(); i++) { + #if defined(ENABLE_E131) and defined(ENABLE_HOMEASSISTANT) + if(effectString == "E131"){ + if(strip.isRunning()) strip.stop(); + mode = E131; + break; + } + #endif if(String(strip.getModeName(i)) == effectString) { mode = SET_MODE; ws2812fx_mode = i; @@ -956,6 +1029,9 @@ void webSocketEvent(uint8_t num, WStype_t type, uint8_t * payload, size_t lenght for (uint8_t i = 0; i < strip.getModeCount(); i++) { effect_list.add(strip.getModeName(i)); } + #if defined(ENABLE_E131) and defined(MQTT_HOME_ASSISTANT_SUPPORT) + effect_list.add("E131"); + #endif char buffer[measureJson(json) + 1]; serializeJson(json, buffer, sizeof(buffer)); mqtt_client.publish(String("homeassistant/light/" + String(HOSTNAME) + "/config").c_str(), buffer, true); @@ -1040,6 +1116,9 @@ void webSocketEvent(uint8_t num, WStype_t type, uint8_t * payload, size_t lenght for (uint8_t i = 0; i < strip.getModeCount(); i++) { effect_list.add(strip.getModeName(i)); } + #if defined(ENABLE_E131) and defined(MQTT_HOME_ASSISTANT_SUPPORT) + effect_list.add("E131"); + #endif char buffer[measureJson(json) + 1]; serializeJson(json, buffer, sizeof(buffer)); DBG_OUTPUT_PORT.println(buffer); @@ -1322,6 +1401,12 @@ bool readStateFS() { strip.setSpeed(convertSpeed(ws2812fx_speed)); strip.setBrightness(brightness); strip.setColor(main_color.red, main_color.green, main_color.blue); + + #ifdef ENABLE_E131 + if (mode == E131) { + strip.stop(); + } + #endif updateFS = false; return true; diff --git a/Arduino/McLighting/version.h b/Arduino/McLighting/version.h index 4da84ba..510ef45 100644 --- a/Arduino/McLighting/version.h +++ b/Arduino/McLighting/version.h @@ -1 +1 @@ -#define SKETCH_VERSION "2.1.11" \ No newline at end of file +#define SKETCH_VERSION "2.2.0" \ No newline at end of file diff --git a/Arduino/McLighting/version_info.ino b/Arduino/McLighting/version_info.ino index f039c4c..8364126 100644 --- a/Arduino/McLighting/version_info.ino +++ b/Arduino/McLighting/version_info.ino @@ -36,16 +36,27 @@ * - Fix Auto-Discovery for HA version >= 0.84 #286 * - Fix #283 * + * 12 Dec 2018 v 2.2.0 + * - Add E1.31 mode initial commit + * - E1.31 mode when activated now stops current animation + * * 13 Dec 2018 v 2.1.9 * - HA is not getting the correct animation name being run, boils down to changes to ArduinoJson library * - Bump ArduinoJson library requirment for v6.7.0-beta (better memory management) * - sendState() needs extra memory for jsonBuffer * - sensState() effect can be sent directly instead of copying from PROGMEM * - * 16 Dec 2018 v 1.1.10 + * 16 Dec 2018 v 2.1.10 * - more ArduinoJson library memory managment fixes * * 18 Dec 2018 v 2.1.11 * - More Auto-Discovery fix for HA version >= 0.84 #286 * - Suggestions from https://github.com/home-assistant/home-assistant/issues/19420 + * + * 23 Dec 2018 v 2.2.0 + * - Add E1.31 mode to getModes(), no need to change McLightingUI + * + * 6 Jan 2018 v 2.2.0 + * - fix webserver not responding when E1.31 is mode is acivated: do a webserver.loop() for every 1.31 packet + * - HA E1.31 mode added */ diff --git a/README.md b/README.md index ea89e6f..b56180f 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # McLighting v2 - The ESP8266 based multi-client lighting gadget -[](https://gitter.im/mclighting/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) +[](https://gitter.im/mclighting/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) [](https://travis-ci.com/toblum/McLighting) [](https://opensource.org/licenses/MIT) [](https://github.com/toblum/McLighting/blob/master/Arduino/McLighting/version.h) McLighting (the multi-client lighting gadget) is a very cheap internet-controllable lighting solution based on the famous ESP8266 microcontroller and WS2811/2812 led strips. It features among other things a web-interface, a REST-API and a websocket connector. diff --git a/clients/HomeAssistant/light.yaml b/clients/HomeAssistant/light.yaml index 0e2a23d..e845c51 100644 --- a/clients/HomeAssistant/light.yaml +++ b/clients/HomeAssistant/light.yaml @@ -64,6 +64,7 @@ light: - "Bicolor Chase" - "Tricolor Chase" - "ICU" + - "E131" brightness: true color_temp: true rgb: true @@ -80,7 +81,7 @@ input_number: step: 5 automation: - - id: 71938579813759813757 + - id: "71938579813759813757" alias: NeoPixel Animation Speed Send initial_state: true hide_entity: false @@ -94,7 +95,7 @@ automation: topic: home/McLighting01_ha/state/in service: mqtt.publish - - id: 93786598732698756967 + - id: "93786598732698756967" alias: NeoPixel Animation Speed Receive trigger: - platform: mqtt diff --git a/platformio.ini b/platformio.ini index ea4a6cc..7959436 100644 --- a/platformio.ini +++ b/platformio.ini @@ -49,5 +49,7 @@ lib_deps = AsyncMqttClient https://github.com/bblanchon/ArduinoJson.git#v6.7.0-beta WS2812FX - NeoPixelBus + NeoPixelBus@2.4.1 WebSockets + ESPAsyncE131 + ESPAsyncUDP \ No newline at end of file