initial commit
This commit is contained in:
commit
6f4c3e3357
|
@ -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.
|
|
@ -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
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Chan</th>
|
||||
<th>Command</th>
|
||||
<th>DMX Range</th>
|
||||
<th>Value Range</th>
|
||||
<th>Resolution</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>1</td>
|
||||
<td>Intensity Coarse</td>
|
||||
<td rowspan=2>0 - 65535</td>
|
||||
<td rowspan=2>0 - 100%</td>
|
||||
<td rowspan=2>15 bit<br/>2.4kHz PWM</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>2</td>
|
||||
<td>Intensity Fine</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>3</td>
|
||||
<td>Duration</td>
|
||||
<td>0 - 255</td>
|
||||
<td>0.02 - 0.5s</td>
|
||||
<td>0.004s</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>4</td>
|
||||
<td>Rate</td>
|
||||
<td>0 - 255</td>
|
||||
<td>0.5 - 25Hz</td>
|
||||
<td>0.1Hz</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
### 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.
|
|
@ -0,0 +1,46 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Hog xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schema.hogfour.com/Fixture" xsi:schemaLocation="http://schema.hogfour.com/Fixture http://schema.hogfour.com/fixture.xsd">
|
||||
<Creator name="Hog" version="v3.14.0 (b 3042)"/>
|
||||
<Library>
|
||||
<Manufacturer name="User Created">
|
||||
<Product name="WiFlash">
|
||||
<Types>
|
||||
<Type name="WiFlash" revisionid="0" status="User" uuid="e604bef7-3306-11eb-8385-dc85deefb016">
|
||||
<History revision="0" author="" date="2020-11-30" comment=""/>
|
||||
<Origin>User</Origin>
|
||||
<Icon type="circle" size="medium"/>
|
||||
<Defaults>
|
||||
<Default function="Strobe" feature="Rate" value="1"/>
|
||||
<Default function="Strobe Duration" feature="Duration" value="0.50"/>
|
||||
</Defaults>
|
||||
<Protocols>
|
||||
<Protocol name="DMX">
|
||||
<Dmxfootprint size="4"/>
|
||||
<Channels>
|
||||
<Channel number="1">
|
||||
<AttachedChannels>
|
||||
<AttachedChannel>2</AttachedChannel>
|
||||
</AttachedChannels>
|
||||
<Ranges>
|
||||
<Range dmxstart="0" dmxend="65535" function="Intensity" feature="Intensity" start="0" end="100"/>
|
||||
</Ranges>
|
||||
</Channel>
|
||||
<Channel number="3">
|
||||
<Ranges>
|
||||
<Range dmxstart="0" dmxend="255" function="Strobe Duration" feature="Duration" start="0.02" end="1"/>
|
||||
</Ranges>
|
||||
</Channel>
|
||||
<Channel number="4">
|
||||
<Ranges>
|
||||
<Range dmxstart="1" dmxend="255" function="Strobe" feature="Rate" start="0.50" end="25"/>
|
||||
</Ranges>
|
||||
</Channel>
|
||||
</Channels>
|
||||
</Protocol>
|
||||
</Protocols>
|
||||
</Type>
|
||||
</Types>
|
||||
</Product>
|
||||
</Manufacturer>
|
||||
</Library>
|
||||
</Hog>
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
After Width: | Height: | Size: 63 KiB |
|
@ -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 <Arduino.h>
|
||||
#include <WiFi.h>
|
||||
#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<e131_packet_t *>(_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;
|
||||
}
|
|
@ -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 <AsyncUDP.h>
|
||||
|
||||
// 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<void(e131_packet_t *packet)> 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_
|
|
@ -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 <Arduino.h>
|
||||
#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];
|
||||
}
|
|
@ -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_
|
|
@ -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 <WiFi.h>
|
||||
#include "sacn.h"
|
||||
#include <ArduinoOSC.h>
|
||||
#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);
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue