commit 6f4c3e33578375e9528f95b68646668dfcc9bb2a Author: kevin Date: Tue Dec 1 19:40:58 2020 -0500 initial commit diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..dfccf58 --- /dev/null +++ b/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2020 Kevin Matz + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..29cc48f --- /dev/null +++ b/README.md @@ -0,0 +1,166 @@ +# WiFlash + +Uses a GPIO to control a lighting console over OSC, whilst also controlling an LED strobe light with DMX data received over sACN. + +This software is designed for the ESP32, but will probably be usable on other Espressif platforms, + +![example](assets/wiflash_schem.png) + +## Requirements +* [Arduino IDE](https://www.arduino.cc/en/software) + + > Refer to the [Getting Started](https://www.arduino.cc/en/Guide/) page for Installation instructions. + +* [Arduino core for ESP32](https://github.com/espressif/arduino-esp32) +> - Start Arduino and open Preferences window. + - Enter `https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_index.json` into *Additional Board Manager URLs* field. You can add multiple URLs, separating them with commas. + - Open Boards Manager from Tools > Board menu and install *esp32* platform (and don't forget to select your ESP32 board from Tools > Board menu after installation). + + +* [ArduinoOSC](https://github.com/hideakitai/ArduinoOSC) - OSC subscriber / publisher for Arduino + > Install from the Arduino library manager. + + +* [WiFlash for ESP32](https://git.company235.com/kevin/wiflash) +> - Unzip the downloaded file. + - Open `wiflash_esp32.ino` in Arduino. + - Select your ESP32 board from the Tools > Board menu. + + +## Configuring + +### WiFi +``` +// ----------------------------------- +// Configure the WiFi network +// ----------------------------------- +const char ssid[] = "WiFi NETWORK"; +const char pwd[] = "PASSWORD"; +``` +Change these lines to enter your own SSID and WEP/WPA2 password. + +### IPv4 +``` +// ----------------------------------- +// Configure the IPv4 network +// ----------------------------------- +const IPAddress ip(127, 0, 0, 1); +const IPAddress gateway(0, 0, 0, 0); +const IPAddress subnet(255, 255, 255, 255); +``` +Enter your own IP configuration. **Note the commas** between bytes in the address. + +### OSC +``` +// ----------------------------------- +// Configure OSC +// ----------------------------------- +const String host = "2.0.0.1"; +const uint16_t port = 7001; +``` +Set the value of `host` to be the IP address of the OSC server. Here, **the IP address bytes are separated by a period**. +``` +const String pressed_addr = "/hog/playback/go/0"; +const String pressed_value = "99.1"; +const String released_addr = "/hog/playback/go/0"; +const String released_value = "99.2"; +``` +Change these lines to suit your setup. These examples will send `GOTO LIST 99 CUE 1` when the button is pressed, and `GOTO LIST 99 CUE 2` when the button is released. Check the [Hog 4 OSC mappings](https://www.highend.com/pub/support/controllers/documents/HTML/en/sect-osc_mappings.htm) manual page for additional inspiration. + +### E1.31 sACN +``` +// ----------------------------------- +// Configure E1.31 sACN +// ----------------------------------- +Strobe strobe(E131_MULTICAST, 1, 001); +``` +Set these 3 arguments to configure sACN. +1. Change this to `E131_MULTICAST` to receive multicast sACN, or `E131_UNICAST` to receive unicast sACN. +1. This is the sACN universe to respond to. +1. Set the DMX start address for the strobe light. + +###### Hog4 fixture profile: +[Import from XML](728%20Fixtures%20Wiflash%20rev%200.xml) + +#### DMX Mapping + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ChanCommandDMX RangeValue RangeResolution
1Intensity Coarse0 - 655350 - 100%15 bit
2.4kHz PWM
2Intensity Fine
3Duration0 - 2550.02 - 0.5s0.004s
4Rate0 - 2550.5 - 25Hz0.1Hz
+ +### Hardware +``` +/* + * This section to be configured by the Fixtures Dept. + */ +const int button = A0; +const int led = LED_BUILTIN; +``` +These values need to match the hardware setup. + +## Finishing +Use the upload button in Arduino to compile your changes and upload the software to your ESP32 device. + +## Credits + +While not the first device of this class to be constructed, this version is credited to: + +- Concept & electronics: Phil Abeyta +- System design & firmware: Kevin Matz + +## Copying +This Software is released under the MIT License. + +Copyright © 2020, Kevin Matz + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/assets/728 Fixtures WiFlash rev 0.xml b/assets/728 Fixtures WiFlash rev 0.xml new file mode 100644 index 0000000..4f496ec --- /dev/null +++ b/assets/728 Fixtures WiFlash rev 0.xml @@ -0,0 +1,46 @@ + + + + + + + + + + User + + + + + + + + + + + + 2 + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/DMX values.ods b/assets/DMX values.ods new file mode 100644 index 0000000..519f321 Binary files /dev/null and b/assets/DMX values.ods differ diff --git a/assets/wiflash.fzz b/assets/wiflash.fzz new file mode 100644 index 0000000..556c4e7 Binary files /dev/null and b/assets/wiflash.fzz differ diff --git a/assets/wiflash_schem.png b/assets/wiflash_schem.png new file mode 100644 index 0000000..ad193d5 Binary files /dev/null and b/assets/wiflash_schem.png differ diff --git a/wiflash_esp32/sacn.cpp b/wiflash_esp32/sacn.cpp new file mode 100644 index 0000000..377191b --- /dev/null +++ b/wiflash_esp32/sacn.cpp @@ -0,0 +1,132 @@ +/* + ESPAsyncE131.cpp + + Project: ESPAsyncE131 - Asynchronous E.131 (sACN) library for Arduino ESP8266 and ESP32 + Copyright (c) 2019 Shelby Merrick + http://www.forkineye.com + + This program is provided free for you to use in any way that you wish, + subject to the laws and regulations where you are using it. Due diligence + is strongly suggested before using this code. Please give credit where due. + + The Author makes no warranty of any kind, express or implied, with regard + to this program or the documentation contained in this document. The + Author shall not be liable in any event for incidental or consequential + damages in connection with, or arising out of, the furnishing, performance + or use of these programs. +*/ + +#include +#include +#include "sacn.h" + +// E1.17 ACN Packet Identifier +const uint8_t ESPAsyncE131::ACN_ID[12] = { 0x41, 0x53, 0x43, 0x2d, 0x45, 0x31, 0x2e, 0x31, 0x37, 0x00, 0x00, 0x00 }; + +ESPAsyncE131::ESPAsyncE131(uint8_t buffers) { + stats.num_packets = 0; + stats.packet_errors = 0; + _handler = NULL; +} + +bool ESPAsyncE131::begin(e131_listen_t type, uint16_t universe) { + bool success = false; + + if (type == E131_UNICAST) + success = initUnicast(); + if (type == E131_MULTICAST) + success = initMulticast(universe); + + return success; +} + +bool ESPAsyncE131::initUnicast() { + bool success = false; + + if (udp.listen(E131_DEFAULT_PORT)) { + udp.onPacket(std::bind(&ESPAsyncE131::parsePacket, this, + std::placeholders::_1)); + success = true; + } + return success; +} + +bool ESPAsyncE131::initMulticast(uint16_t universe) { + bool success = false; + + IPAddress address = IPAddress(239, 255, ((universe >> 8) & 0xff), + ((universe >> 0) & 0xff)); + + if (udp.listenMulticast(address, E131_DEFAULT_PORT)) { + udp.onPacket(std::bind(&ESPAsyncE131::parsePacket, this, + std::placeholders::_1)); + success = true; + } + return success; +} + +void ESPAsyncE131::parsePacket(AsyncUDPPacket _packet) { + e131_error_t error = ERROR_NONE; + + sbuff = reinterpret_cast(_packet.data()); + if (memcmp(sbuff->acn_id, ESPAsyncE131::ACN_ID, sizeof(sbuff->acn_id))) + error = ERROR_ACN_ID; + else if (htonl(sbuff->root_vector) != ESPAsyncE131::VECTOR_ROOT) + error = ERROR_VECTOR_ROOT; + else if (htonl(sbuff->frame_vector) != ESPAsyncE131::VECTOR_FRAME) + error = ERROR_VECTOR_FRAME; + else if (sbuff->dmp_vector != ESPAsyncE131::VECTOR_DMP) + error = ERROR_VECTOR_DMP; + else if (sbuff->property_values[0] != 0) + error = ERROR_IGNORE; + + if (error == ERROR_NONE) { + stats.num_packets++; + stats.last_clientIP = _packet.remoteIP(); + stats.last_clientPort = _packet.remotePort(); + stats.last_seen = millis(); + if (_handler) { + _handler(sbuff); + } + } else if (error == ERROR_IGNORE) { + // Do nothing + } else { + if (Serial) + dumpError(sbuff, error); + stats.packet_errors++; + } +} + +void ESPAsyncE131::dumpError(e131_packet_t *packet, e131_error_t error) { + switch (error) { + case ERROR_ACN_ID: + Serial.print(F("INVALID PACKET ID: ")); + for (uint i = 0; i < sizeof(ACN_ID); i++) + Serial.print(packet->acn_id[i], HEX); + Serial.println(""); + break; + case ERROR_PACKET_SIZE: + Serial.println(F("INVALID PACKET SIZE: ")); + break; + case ERROR_VECTOR_ROOT: + Serial.print(F("INVALID ROOT VECTOR: 0x")); + Serial.println(htonl(packet->root_vector), HEX); + break; + case ERROR_VECTOR_FRAME: + Serial.print(F("INVALID FRAME VECTOR: 0x")); + Serial.println(htonl(packet->frame_vector), HEX); + break; + case ERROR_VECTOR_DMP: + Serial.print(F("INVALID DMP VECTOR: 0x")); + Serial.println(packet->dmp_vector, HEX); + case ERROR_NONE: + break; + case ERROR_IGNORE: + break; + } +} + +void ESPAsyncE131::onPacket(E131PacketHandlerFunction callback) +{ + _handler = callback; +} diff --git a/wiflash_esp32/sacn.h b/wiflash_esp32/sacn.h new file mode 100644 index 0000000..6963f1c --- /dev/null +++ b/wiflash_esp32/sacn.h @@ -0,0 +1,147 @@ +/* + ESPAsyncE131.h + + Project: ESPAsyncE131 - Asynchronous E.131 (sACN) library for Arduino ESP8266 and ESP32 + Copyright (c) 2019 Shelby Merrick + http://www.forkineye.com + + This program is provided free for you to use in any way that you wish, + subject to the laws and regulations where you are using it. Due diligence + is strongly suggested before using this code. Please give credit where due. + + The Author makes no warranty of any kind, express or implied, with regard + to this program or the documentation contained in this document. The + Author shall not be liable in any event for incidental or consequential + damages in connection with, or arising out of, the furnishing, performance + or use of these programs. + +*/ + +#ifndef ESPASYNCE131_H_ +#define ESPASYNCE131_H_ + +#include + +// Defaults +#define E131_DEFAULT_PORT 5568 + +// E1.31 Packet Offsets +#define E131_ROOT_PREAMBLE_SIZE 0 +#define E131_ROOT_POSTAMBLE_SIZE 2 +#define E131_ROOT_ID 4 +#define E131_ROOT_FLENGTH 16 +#define E131_ROOT_VECTOR 18 +#define E131_ROOT_CID 22 + +#define E131_FRAME_FLENGTH 38 +#define E131_FRAME_VECTOR 40 +#define E131_FRAME_SOURCE 44 +#define E131_FRAME_PRIORITY 108 +#define E131_FRAME_RESERVED 109 +#define E131_FRAME_SEQ 111 +#define E131_FRAME_OPT 112 +#define E131_FRAME_UNIVERSE 113 + +#define E131_DMP_FLENGTH 115 +#define E131_DMP_VECTOR 117 +#define E131_DMP_TYPE 118 +#define E131_DMP_ADDR_FIRST 119 +#define E131_DMP_ADDR_INC 121 +#define E131_DMP_COUNT 123 +#define E131_DMP_DATA 125 + +// E1.31 Packet Structure +typedef union { + struct { + // Root Layer + uint16_t preamble_size; + uint16_t postamble_size; + uint8_t acn_id[12]; + uint16_t root_flength; + uint32_t root_vector; + uint8_t cid[16]; + + // Frame Layer + uint16_t frame_flength; + uint32_t frame_vector; + uint8_t source_name[64]; + uint8_t priority; + uint16_t reserved; + uint8_t sequence_number; + uint8_t options; + uint16_t universe; + + // DMP Layer + uint16_t dmp_flength; + uint8_t dmp_vector; + uint8_t type; + uint16_t first_address; + uint16_t address_increment; + uint16_t property_value_count; + uint8_t property_values[513]; + } __attribute__((packed)); + + uint8_t raw[638]; +} e131_packet_t; + +// Error Types +typedef enum { + ERROR_NONE, + ERROR_IGNORE, + ERROR_ACN_ID, + ERROR_PACKET_SIZE, + ERROR_VECTOR_ROOT, + ERROR_VECTOR_FRAME, + ERROR_VECTOR_DMP +} e131_error_t; + +// E1.31 Listener Types +typedef enum { + E131_UNICAST, + E131_MULTICAST +} e131_listen_t; + +// Status structure +typedef struct { + uint32_t num_packets; + uint32_t packet_errors; + IPAddress last_clientIP; + uint16_t last_clientPort; + unsigned long last_seen; +} e131_stats_t; + + +typedef std::function E131PacketHandlerFunction; + +class ESPAsyncE131 { + public: + ESPAsyncE131(uint8_t buffers = 1); + + void onPacket(E131PacketHandlerFunction callback); + bool begin(e131_listen_t type, uint16_t universe = 1); + static void dumpError(e131_packet_t *packet, e131_error_t error); + + e131_stats_t stats; // Statistics tracker + + protected: + E131PacketHandlerFunction _handler; + + private: + // Constants for packet validation + static const uint8_t ACN_ID[]; + static const uint32_t VECTOR_ROOT = 4; + static const uint32_t VECTOR_FRAME = 2; + static const uint8_t VECTOR_DMP = 2; + + e131_packet_t *sbuff; // Pointer to scratch packet buffer + AsyncUDP udp; // AsyncUDP + + // Internal Initializers + bool initUnicast(); + bool initMulticast(uint16_t universe); + + // UDP packet parser callback + void parsePacket(AsyncUDPPacket _packet); +}; + +#endif // ESPASYNCE131_H_ diff --git a/wiflash_esp32/strobe_esp32.cpp b/wiflash_esp32/strobe_esp32.cpp new file mode 100644 index 0000000..bc68f55 --- /dev/null +++ b/wiflash_esp32/strobe_esp32.cpp @@ -0,0 +1,153 @@ +/* + strobe_esp32.cpp + + Part of WiFlash_esp32 + + Copyright (c) 2020 Kevin Matz (kevin.matz@gmail.com) + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. +*/ + +#include +#include "strobe_esp32.h" + +/* + calculate period from DMX value +*/ +uint32_t hz_to_micros(uint8_t dmx) { + if (dmx == 0) { + return 0; + } + // ( millis / ( percent * scalor ) + offset ) * micros + return (1000 / (((dmx / 255.0) * 24.5) + 0.5)) * 1000; +} + + +/* + calculate duration from DMX value +*/ +uint32_t dr_to_micros(uint8_t dmx) { + // (( percent * scalor) + offset) * micros + return (((dmx / 255.0) * .5 ) + .01 ) * 1000 * 1000; +} + + +/* + Constructor +*/ +Strobe::Strobe(e131_listen_t type, uint16_t universe, uint16_t address) { + m_type = type; + m_universe = universe; + m_address = address; + m_e131 = new ESPAsyncE131(1); + m_e131->onPacket(std::bind(&Strobe::recvData, this, + std::placeholders::_1)); +} + +/* + call durring setup() +*/ +bool Strobe::begin(uint8_t pin, uint8_t pwm) { + bool success = false; + // set state + m_pwm = pwm; + m_state = STROBE_INACTIVE; + m_level = 0; + + // configure hardware + ledcAttachPin(pin, m_pwm); // Attach GPIO to PWM timer + ledcSetup(m_pwm, 2400, 15); // 2.4KHz PWM, 15 bit resolutio + ledcWrite(m_pwm, m_level); // LED to 0% + + // start sACN + if (!m_e131->begin(m_type, m_universe)) { + Serial.println("Failed to start E1.31"); + } else { + Serial.println("Listening for sACN"); + success = true; + } + + return success; +} + +void Strobe::update(void * args) { + // stop stobing on sACN loss of signal +// if (millis() - m_e131->stats.last_seen > 10000) { +// Serial.println("No Signal!"); +// m_int = 0; +// } + + // intensity changes start/stop the cycle + if (m_int == 0) { + // intensity off breaks the cycle + m_state = STROBE_INACTIVE; + } else { + if (m_state == STROBE_INACTIVE) { + // going active + m_state = STROBE_ACTIVE_ON; + } + } + + // time based cycle changes + if (m_state == STROBE_ACTIVE_ON || + m_state == STROBE_ACTIVE_OFF) { + uint32_t now, elapsed, period, durr; + now = micros(); + + elapsed = now - m_time; + period = hz_to_micros(m_rat); + durr = dr_to_micros(m_dur); + + if (elapsed > period || // hz period completed + now < m_time) { // micros() wraps 32 bits every ~70 min. + // cycle restarts + m_state = STROBE_ACTIVE_ON; + m_time = now; + } else if (elapsed > durr) { // durration period completed + // cycle enters dark phase + m_state = STROBE_ACTIVE_OFF; + } + } + + // set LED output + if (m_state == STROBE_ACTIVE_ON) { + setLevel(m_int); + } else { + setLevel(0); + } +} + + +/* + Write a 15bit level to PWM +*/ +void Strobe::setLevel(uint16_t level) { + if (level != m_level) { + m_level = level; + ledcWrite(m_pwm, ((m_level >> 1) & 0xffff)); // write at 15 bit PWM + } +} + +void Strobe::recvData(e131_packet_t *packet) { + //Serial.println(m_e131->stats.num_packets); + m_int = packet->property_values[m_address + 0] << 8 | + packet->property_values[m_address + 1]; + m_dur = packet->property_values[m_address + 2]; + m_rat = packet->property_values[m_address + 3]; +} diff --git a/wiflash_esp32/strobe_esp32.h b/wiflash_esp32/strobe_esp32.h new file mode 100644 index 0000000..b119bb3 --- /dev/null +++ b/wiflash_esp32/strobe_esp32.h @@ -0,0 +1,75 @@ +/* + strobe_esp32.h + +Part of WiFlash_esp32 + +Copyright (c) 2020 Kevin Matz (kevin.matz@gmail.com) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +#ifndef STROBE_ESP32_H_ +#define STROBE_ESP32_H_ + +#include "sacn.h" + +inline uint32_t hz_to_micros(uint8_t dmx) __attribute__((always_inline)); +inline uint32_t dr_to_micros(uint8_t dmx) __attribute__((always_inline)); + +typedef enum { + STROBE_INACTIVE, + STROBE_ACTIVE_ON, + STROBE_ACTIVE_OFF +} strState; + + +class Strobe { + public: + Strobe(e131_listen_t type, uint16_t universe = 1, uint16_t address = 1); + + bool begin(uint8_t pin, uint8_t pwm); // call in setup() + + void update(void *args = NULL); // update task + + const uint8_t profile = 4; // DMX profile length + + private: + e131_listen_t m_type; // IP Unicast/Multicast + uint16_t m_universe; // sACN universe + uint16_t m_address; // DMX address + + uint16_t m_int; // state machine intensity + uint8_t m_dur; // state machine durration + uint8_t m_rat; // state machine rate + strState m_state; // state machine cycle state + uint16_t m_level; // state machine LED level + + uint8_t m_pwm; // LEDc PWM channel + + uint32_t m_time; // micros() of current cycle start + + ESPAsyncE131 *m_e131; // ESPAsyncE131 instance + + void recvData(e131_packet_t *packet); // data recieved callback + + inline void setLevel(uint16_t level) __attribute__((always_inline)); +}; + + +#endif // STROBE_ESP32_H_ diff --git a/wiflash_esp32/wiflash_esp32.ino b/wiflash_esp32/wiflash_esp32.ino new file mode 100644 index 0000000..ca7b296 --- /dev/null +++ b/wiflash_esp32/wiflash_esp32.ino @@ -0,0 +1,153 @@ +/* + WiFlash_esp32 - Concept & electronics: Phil Abeyta + - System design & firmware: Kevin Matz + +Uses a GPIO to control a lighting console over OSC, whilst also +controlling an LED strobe light with DMX data recieved over sACN. + +Copyright (c) 2020 Kevin Matz (kevin.matz@gmail.com) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +#include +#include "sacn.h" +#include +#include "strobe_esp32.h" + + +/* + * This section to be configured by Lighting Control + */ +// ----------------------------------- +// Configure the WiFi network +// ----------------------------------- +const char ssid[] = "........"; +const char pwd[] = "........"; + +// ----------------------------------- +// Configure the IPv4 network +// ----------------------------------- +const IPAddress ip(127, 0, 0, 1); +const IPAddress subnet(255, 255, 255, 0); +const IPAddress gateway(127, 0, 0, 1); + +// ----------------------------------- +// Configure OSC +// ----------------------------------- +const String host = "127.0.0.1"; +const uint16_t port = 7001; +const String pressed_addr = "/hog/playback/go/0"; +const String pressed_value = "99.1"; +const String released_addr = "/hog/playback/go/0"; +const String released_value = "99.2"; + +// ----------------------------------- +// Configure E1.31 sACN +// ----------------------------------- +Strobe strobe(E131_UNICAST, 1, 001); // (uni|multi)cast, sACN Universe, Address +/* DMX Value + * | chan | Command | range | range | + * |------+-----------+-------+------------| + * | 1 | Intensity | 0- | 0-100% | 15 bit 2.4kHz PWM + * | 2 | fine | 65535 | | + * |------+-----------+-------+------------| + * | 3 | Duration | 0-255 | 0.02-0.5s | 0.002s resolution + * |------+-----------+-------+------------| + * | 4 | Rate | 0-255 | 0.5-25hz | 0.1 hz resolution + * |------+-----------+-------+------------| + */ + + +/* + * This section to be configured by the Fixtures Dept. + */ +const int button = A0; // A0, use 100nF to ground +const int led = LED_BUILTIN; // IO13 + + +/* + * Change nothing else unless you're really sure. + */ + +//// Global button variables +bool pressed = false; // track button state +uint32_t change_time; // time of button press (ms) + + +/* + * Arduino powerup + */ +void setup() { + Serial.begin(115200); + +//// start WiFi: + // set IPv4 + WiFi.mode(WIFI_STA); + WiFi.config(ip, gateway, subnet); + WiFi.begin(ssid, pwd); + + // join 802.11 + if (WiFi.config(ip, gateway, subnet)) { + Serial.print("Connected to "); + Serial.println(ssid); + Serial.print("IP address is "); + Serial.println(WiFi.localIP()); + } else { + Serial.println("STA Failed to configure"); + } + + +//// start fixtures hardware: + // button + pinMode(button, INPUT_PULLUP); + // led_trigPres_trigPress _trigPress _trigPress _trigPress s + if (!strobe.begin(led, 0)) { + Serial.println("Strobe failed to configure."); + } +} + + +/* + * Arduino process loop + */ +void loop() { + if (WiFi.status() != WL_CONNECTED) { + Serial.println("Waiting for WiFi..."); + delay(1000); + return; + } + + strobe.update(); + + // Be a remote trigger + // + bool val = !digitalRead(button); + if (val != pressed) { + pressed = val; + change_time = millis(); + if (pressed) { + Serial.println("Pressed"); + OscWiFi.send(host, port, pressed_addr, pressed_value); + } else { + Serial.println("Released"); + OscWiFi.send(host, port, released_addr, released_value); + } + } +}