mirror of
https://github.com/wled/WLED.git
synced 2025-04-19 12:27:17 +00:00
Merge pull request #4495 from netmindz/DMX-Input-AC
Add Wired DMX Input support
This commit is contained in:
commit
48f5099646
@ -282,8 +282,10 @@ build_flags = -g
|
||||
-DARDUINO_ARCH_ESP32 -DESP32
|
||||
-D CONFIG_ASYNC_TCP_USE_WDT=0
|
||||
-DARDUINO_USB_CDC_ON_BOOT=0 ;; this flag is mandatory for "classic ESP32" when building with arduino-esp32 >=2.0.3
|
||||
-D WLED_ENABLE_DMX_INPUT
|
||||
lib_deps =
|
||||
https://github.com/pbolduc/AsyncTCP.git @ 1.2.0
|
||||
https://github.com/someweisguy/esp_dmx.git#47db25d
|
||||
${env.lib_deps}
|
||||
board_build.partitions = ${esp32.default_partitions} ;; default partioning for 4MB Flash - can be overridden in build envs
|
||||
|
||||
|
@ -522,6 +522,14 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
|
||||
|
||||
tdd = if_live[F("timeout")] | -1;
|
||||
if (tdd >= 0) realtimeTimeoutMs = tdd * 100;
|
||||
|
||||
#ifdef WLED_ENABLE_DMX_INPUT
|
||||
CJSON(dmxInputTransmitPin, if_live_dmx[F("inputRxPin")]);
|
||||
CJSON(dmxInputReceivePin, if_live_dmx[F("inputTxPin")]);
|
||||
CJSON(dmxInputEnablePin, if_live_dmx[F("inputEnablePin")]);
|
||||
CJSON(dmxInputPort, if_live_dmx[F("dmxInputPort")]);
|
||||
#endif
|
||||
|
||||
CJSON(arlsForceMaxBri, if_live[F("maxbri")]);
|
||||
CJSON(arlsDisableGammaCorrection, if_live[F("no-gc")]); // false
|
||||
CJSON(arlsOffset, if_live[F("offset")]); // 0
|
||||
@ -1001,6 +1009,12 @@ void serializeConfig() {
|
||||
if_live_dmx[F("addr")] = DMXAddress;
|
||||
if_live_dmx[F("dss")] = DMXSegmentSpacing;
|
||||
if_live_dmx["mode"] = DMXMode;
|
||||
#ifdef WLED_ENABLE_DMX_INPUT
|
||||
if_live_dmx[F("inputRxPin")] = dmxInputTransmitPin;
|
||||
if_live_dmx[F("inputTxPin")] = dmxInputReceivePin;
|
||||
if_live_dmx[F("inputEnablePin")] = dmxInputEnablePin;
|
||||
if_live_dmx[F("dmxInputPort")] = dmxInputPort;
|
||||
#endif
|
||||
|
||||
if_live[F("timeout")] = realtimeTimeoutMs / 100;
|
||||
if_live[F("maxbri")] = arlsForceMaxBri;
|
||||
|
@ -250,6 +250,7 @@
|
||||
#define REALTIME_MODE_ARTNET 6
|
||||
#define REALTIME_MODE_TPM2NET 7
|
||||
#define REALTIME_MODE_DDP 8
|
||||
#define REALTIME_MODE_DMX 9
|
||||
|
||||
//realtime override modes
|
||||
#define REALTIME_OVERRIDE_NONE 0
|
||||
|
@ -151,6 +151,19 @@ Timeout: <input name="ET" type="number" min="1" max="65000" required> ms<br>
|
||||
Force max brightness: <input type="checkbox" name="FB"><br>
|
||||
Disable realtime gamma correction: <input type="checkbox" name="RG"><br>
|
||||
Realtime LED offset: <input name="WO" type="number" min="-255" max="255" required>
|
||||
<div id="dmxInput">
|
||||
<h4>Wired DMX Input Pins</h4>
|
||||
DMX RX: <input name="IDMR" type="number" min="-1" max="99">RO<br/>
|
||||
DMX TX: <input name="IDMT" type="number" min="-1" max="99">DI<br/>
|
||||
DMX Enable: <input name="IDME" type="number" min="-1" max="99">RE+DE<br/>
|
||||
DMX Port: <input name="IDMP" type="number" min="1" max="2"><br/>
|
||||
</div>
|
||||
<div id="dmxInputOff">
|
||||
<br><em style="color:darkorange">This firmware build does not include DMX Input support. <br></em>
|
||||
</div>
|
||||
<div id="dmxOnOff2">
|
||||
<br><em style="color:darkorange">This firmware build does not include DMX output support. <br></em>
|
||||
</div>
|
||||
<hr class="sml">
|
||||
<h3>Alexa Voice Assistant</h3>
|
||||
<div id="NoAlexa" class="hide">
|
||||
|
280
wled00/dmx_input.cpp
Normal file
280
wled00/dmx_input.cpp
Normal file
@ -0,0 +1,280 @@
|
||||
#include "wled.h"
|
||||
|
||||
#ifdef WLED_ENABLE_DMX_INPUT
|
||||
|
||||
#ifdef ESP8266
|
||||
#error DMX input is only supported on ESP32
|
||||
#endif
|
||||
|
||||
#include "dmx_input.h"
|
||||
#include <rdm/responder.h>
|
||||
|
||||
void rdmPersonalityChangedCb(dmx_port_t dmxPort, const rdm_header_t *header,
|
||||
void *context)
|
||||
{
|
||||
DMXInput *dmx = static_cast<DMXInput *>(context);
|
||||
|
||||
if (!dmx) {
|
||||
DEBUG_PRINTLN("DMX: Error: no context in rdmPersonalityChangedCb");
|
||||
return;
|
||||
}
|
||||
|
||||
if (header->cc == RDM_CC_SET_COMMAND_RESPONSE) {
|
||||
const uint8_t personality = dmx_get_current_personality(dmx->inputPortNum);
|
||||
DMXMode = std::min(DMX_MODE_PRESET, std::max(DMX_MODE_SINGLE_RGB, int(personality)));
|
||||
doSerializeConfig = true;
|
||||
DEBUG_PRINTF("DMX personality changed to to: %d\n", DMXMode);
|
||||
}
|
||||
}
|
||||
|
||||
void rdmAddressChangedCb(dmx_port_t dmxPort, const rdm_header_t *header,
|
||||
void *context)
|
||||
{
|
||||
DMXInput *dmx = static_cast<DMXInput *>(context);
|
||||
|
||||
if (!dmx) {
|
||||
DEBUG_PRINTLN("DMX: Error: no context in rdmAddressChangedCb");
|
||||
return;
|
||||
}
|
||||
|
||||
if (header->cc == RDM_CC_SET_COMMAND_RESPONSE) {
|
||||
const uint16_t addr = dmx_get_start_address(dmx->inputPortNum);
|
||||
DMXAddress = std::min(512, int(addr));
|
||||
doSerializeConfig = true;
|
||||
DEBUG_PRINTF("DMX start addr changed to: %d\n", DMXAddress);
|
||||
}
|
||||
}
|
||||
|
||||
static dmx_config_t createConfig()
|
||||
{
|
||||
dmx_config_t config;
|
||||
config.pd_size = 255;
|
||||
config.dmx_start_address = DMXAddress;
|
||||
config.model_id = 0;
|
||||
config.product_category = RDM_PRODUCT_CATEGORY_FIXTURE;
|
||||
config.software_version_id = VERSION;
|
||||
strcpy(config.device_label, "WLED_MM");
|
||||
|
||||
const std::string versionString = "WLED_V" + std::to_string(VERSION);
|
||||
strncpy(config.software_version_label, versionString.c_str(), 32);
|
||||
config.software_version_label[32] = '\0'; // zero termination in case versionString string was longer than 32 chars
|
||||
|
||||
config.personalities[0].description = "SINGLE_RGB";
|
||||
config.personalities[0].footprint = 3;
|
||||
config.personalities[1].description = "SINGLE_DRGB";
|
||||
config.personalities[1].footprint = 4;
|
||||
config.personalities[2].description = "EFFECT";
|
||||
config.personalities[2].footprint = 15;
|
||||
config.personalities[3].description = "MULTIPLE_RGB";
|
||||
config.personalities[3].footprint = std::min(512, int(strip.getLengthTotal()) * 3);
|
||||
config.personalities[4].description = "MULTIPLE_DRGB";
|
||||
config.personalities[4].footprint = std::min(512, int(strip.getLengthTotal()) * 3 + 1);
|
||||
config.personalities[5].description = "MULTIPLE_RGBW";
|
||||
config.personalities[5].footprint = std::min(512, int(strip.getLengthTotal()) * 4);
|
||||
config.personalities[6].description = "EFFECT_W";
|
||||
config.personalities[6].footprint = 18;
|
||||
config.personalities[7].description = "EFFECT_SEGMENT";
|
||||
config.personalities[7].footprint = std::min(512, strip.getSegmentsNum() * 15);
|
||||
config.personalities[8].description = "EFFECT_SEGMENT_W";
|
||||
config.personalities[8].footprint = std::min(512, strip.getSegmentsNum() * 18);
|
||||
config.personalities[9].description = "PRESET";
|
||||
config.personalities[9].footprint = 1;
|
||||
|
||||
config.personality_count = 10;
|
||||
// rdm personalities are numbered from 1, thus we can just set the DMXMode directly.
|
||||
config.current_personality = DMXMode;
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
void dmxReceiverTask(void *context)
|
||||
{
|
||||
DMXInput *instance = static_cast<DMXInput *>(context);
|
||||
if (instance == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (instance->installDriver()) {
|
||||
while (true) {
|
||||
instance->updateInternal();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool DMXInput::installDriver()
|
||||
{
|
||||
|
||||
const auto config = createConfig();
|
||||
DEBUG_PRINTF("DMX port: %u\n", inputPortNum);
|
||||
if (!dmx_driver_install(inputPortNum, &config, DMX_INTR_FLAGS_DEFAULT)) {
|
||||
DEBUG_PRINTF("Error: Failed to install dmx driver\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
DEBUG_PRINTF("Listening for DMX on pin %u\n", rxPin);
|
||||
DEBUG_PRINTF("Sending DMX on pin %u\n", txPin);
|
||||
DEBUG_PRINTF("DMX enable pin is: %u\n", enPin);
|
||||
dmx_set_pin(inputPortNum, txPin, rxPin, enPin);
|
||||
|
||||
rdm_register_dmx_start_address(inputPortNum, rdmAddressChangedCb, this);
|
||||
rdm_register_dmx_personality(inputPortNum, rdmPersonalityChangedCb, this);
|
||||
initialized = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
void DMXInput::init(uint8_t rxPin, uint8_t txPin, uint8_t enPin, uint8_t inputPortNum)
|
||||
{
|
||||
|
||||
#ifdef WLED_ENABLE_DMX_OUTPUT
|
||||
//TODO add again once dmx output has been merged
|
||||
// if(inputPortNum == dmxOutputPort)
|
||||
// {
|
||||
// DEBUG_PRINTF("DMXInput: Error: Input port == output port");
|
||||
// return;
|
||||
// }
|
||||
#endif
|
||||
|
||||
if (inputPortNum <= (SOC_UART_NUM - 1) && inputPortNum > 0) {
|
||||
this->inputPortNum = inputPortNum;
|
||||
}
|
||||
else {
|
||||
DEBUG_PRINTF("DMXInput: Error: invalid inputPortNum: %d\n", inputPortNum);
|
||||
return;
|
||||
}
|
||||
|
||||
if (rxPin > 0 && enPin > 0 && txPin > 0) {
|
||||
|
||||
const managed_pin_type pins[] = {
|
||||
{(int8_t)txPin, false}, // these are not used as gpio pins, thus isOutput is always false.
|
||||
{(int8_t)rxPin, false},
|
||||
{(int8_t)enPin, false}};
|
||||
const bool pinsAllocated = PinManager::allocateMultiplePins(pins, 3, PinOwner::DMX_INPUT);
|
||||
if (!pinsAllocated) {
|
||||
DEBUG_PRINTF("DMXInput: Error: Failed to allocate pins for DMX_INPUT. Pins already in use:\n");
|
||||
DEBUG_PRINTF("rx in use by: %s\n", pinManager.getPinOwnerText(rxPin).c_str());
|
||||
DEBUG_PRINTF("tx in use by: %s\n", pinManager.getPinOwnerText(txPin).c_str());
|
||||
DEBUG_PRINTF("en in use by: %s\n", pinManager.getPinOwnerText(enPin).c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
this->rxPin = rxPin;
|
||||
this->txPin = txPin;
|
||||
this->enPin = enPin;
|
||||
|
||||
// put dmx receiver into seperate task because it should not be blocked
|
||||
// pin to core 0 because wled is running on core 1
|
||||
xTaskCreatePinnedToCore(dmxReceiverTask, "DMX_RCV_TASK", 10240, this, 2, &task, 0);
|
||||
if (!task) {
|
||||
DEBUG_PRINTF("Error: Failed to create dmx rcv task");
|
||||
}
|
||||
}
|
||||
else {
|
||||
DEBUG_PRINTLN("DMX input disabled due to rxPin, enPin or txPin not set");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void DMXInput::updateInternal()
|
||||
{
|
||||
if (!initialized) {
|
||||
return;
|
||||
}
|
||||
|
||||
checkAndUpdateConfig();
|
||||
|
||||
dmx_packet_t packet;
|
||||
unsigned long now = millis();
|
||||
if (dmx_receive(inputPortNum, &packet, DMX_TIMEOUT_TICK)) {
|
||||
if (!packet.err) {
|
||||
if(!connected) {
|
||||
DEBUG_PRINTLN("DMX Input - connected");
|
||||
}
|
||||
connected = true;
|
||||
identify = isIdentifyOn();
|
||||
if (!packet.is_rdm) {
|
||||
const std::lock_guard<std::mutex> lock(dmxDataLock);
|
||||
dmx_read(inputPortNum, dmxdata, packet.size);
|
||||
}
|
||||
}
|
||||
else {
|
||||
connected = false;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if(connected) {
|
||||
DEBUG_PRINTLN("DMX Input - disconnected");
|
||||
}
|
||||
connected = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void DMXInput::update()
|
||||
{
|
||||
if (identify) {
|
||||
turnOnAllLeds();
|
||||
}
|
||||
else if (connected) {
|
||||
const std::lock_guard<std::mutex> lock(dmxDataLock);
|
||||
handleDMXData(1, 512, dmxdata, REALTIME_MODE_DMX, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void DMXInput::turnOnAllLeds()
|
||||
{
|
||||
// TODO not sure if this is the correct way?
|
||||
const uint16_t numPixels = strip.getLengthTotal();
|
||||
for (uint16_t i = 0; i < numPixels; ++i)
|
||||
{
|
||||
strip.setPixelColor(i, 255, 255, 255, 255);
|
||||
}
|
||||
strip.setBrightness(255, true);
|
||||
strip.show();
|
||||
}
|
||||
|
||||
void DMXInput::disable()
|
||||
{
|
||||
if (initialized) {
|
||||
dmx_driver_disable(inputPortNum);
|
||||
}
|
||||
}
|
||||
void DMXInput::enable()
|
||||
{
|
||||
if (initialized) {
|
||||
dmx_driver_enable(inputPortNum);
|
||||
}
|
||||
}
|
||||
|
||||
bool DMXInput::isIdentifyOn() const
|
||||
{
|
||||
|
||||
uint8_t identify = 0;
|
||||
const bool gotIdentify = rdm_get_identify_device(inputPortNum, &identify);
|
||||
// gotIdentify should never be false because it is a default parameter in rdm
|
||||
// but just in case we check for it anyway
|
||||
return bool(identify) && gotIdentify;
|
||||
}
|
||||
|
||||
void DMXInput::checkAndUpdateConfig()
|
||||
{
|
||||
|
||||
/**
|
||||
* The global configuration variables are modified by the web interface.
|
||||
* If they differ from the driver configuration, we have to update the driver
|
||||
* configuration.
|
||||
*/
|
||||
|
||||
const uint8_t currentPersonality = dmx_get_current_personality(inputPortNum);
|
||||
if (currentPersonality != DMXMode) {
|
||||
DEBUG_PRINTF("DMX personality has changed from %d to %d\n", currentPersonality, DMXMode);
|
||||
dmx_set_current_personality(inputPortNum, DMXMode);
|
||||
}
|
||||
|
||||
const uint16_t currentAddr = dmx_get_start_address(inputPortNum);
|
||||
if (currentAddr != DMXAddress) {
|
||||
DEBUG_PRINTF("DMX address has changed from %d to %d\n", currentAddr, DMXAddress);
|
||||
dmx_set_start_address(inputPortNum, DMXAddress);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
73
wled00/dmx_input.h
Normal file
73
wled00/dmx_input.h
Normal file
@ -0,0 +1,73 @@
|
||||
#pragma once
|
||||
#include <cstdint>
|
||||
#include <esp_dmx.h>
|
||||
#include <atomic>
|
||||
#include <mutex>
|
||||
|
||||
/*
|
||||
* Support for DMX/RDM input via serial (e.g. max485) on ESP32
|
||||
* ESP32 Library from:
|
||||
* https://github.com/someweisguy/esp_dmx
|
||||
*/
|
||||
class DMXInput
|
||||
{
|
||||
public:
|
||||
void init(uint8_t rxPin, uint8_t txPin, uint8_t enPin, uint8_t inputPortNum);
|
||||
void update();
|
||||
|
||||
/**disable dmx receiver (do this before disabling the cache)*/
|
||||
void disable();
|
||||
void enable();
|
||||
|
||||
private:
|
||||
/// @return true if rdm identify is active
|
||||
bool isIdentifyOn() const;
|
||||
|
||||
/**
|
||||
* Checks if the global dmx config has changed and updates the changes in rdm
|
||||
*/
|
||||
void checkAndUpdateConfig();
|
||||
|
||||
/// overrides everything and turns on all leds
|
||||
void turnOnAllLeds();
|
||||
|
||||
/// installs the dmx driver
|
||||
/// @return false on fail
|
||||
bool installDriver();
|
||||
|
||||
/// is called by the dmx receive task regularly to receive new dmx data
|
||||
void updateInternal();
|
||||
|
||||
// is invoked whenver the dmx start address is changed via rdm
|
||||
friend void rdmAddressChangedCb(dmx_port_t dmxPort, const rdm_header_t *header,
|
||||
void *context);
|
||||
|
||||
// is invoked whenever the personality is changed via rdm
|
||||
friend void rdmPersonalityChangedCb(dmx_port_t dmxPort, const rdm_header_t *header,
|
||||
void *context);
|
||||
|
||||
/// The internal dmx task.
|
||||
/// This is the main loop of the dmx receiver. It never returns.
|
||||
friend void dmxReceiverTask(void * context);
|
||||
|
||||
uint8_t inputPortNum = 255;
|
||||
uint8_t rxPin = 255;
|
||||
uint8_t txPin = 255;
|
||||
uint8_t enPin = 255;
|
||||
|
||||
/// is written to by the dmx receive task.
|
||||
byte dmxdata[DMX_PACKET_SIZE];
|
||||
/// True once the dmx input has been initialized successfully
|
||||
bool initialized = false; // true once init finished successfully
|
||||
/// True if dmx is currently connected
|
||||
std::atomic<bool> connected{false};
|
||||
std::atomic<bool> identify{false};
|
||||
/// Timestamp of the last time a dmx frame was received
|
||||
unsigned long lastUpdate = 0;
|
||||
|
||||
/// Taskhandle of the dmx task that is running in the background
|
||||
TaskHandle_t task;
|
||||
/// Guards access to dmxData
|
||||
std::mutex dmxDataLock;
|
||||
|
||||
};
|
@ -1,7 +1,7 @@
|
||||
#include "wled.h"
|
||||
|
||||
/*
|
||||
* Support for DMX Output via MAX485.
|
||||
* Support for DMX output via serial (e.g. MAX485).
|
||||
* Change the output pin in src/dependencies/ESPDMX.cpp, if needed (ESP8266)
|
||||
* Change the output pin in src/dependencies/SparkFunDMX.cpp, if needed (ESP32)
|
||||
* ESP8266 Library from:
|
||||
@ -12,7 +12,7 @@
|
||||
|
||||
#ifdef WLED_ENABLE_DMX
|
||||
|
||||
void handleDMX()
|
||||
void handleDMXOutput()
|
||||
{
|
||||
// don't act, when in DMX Proxy mode
|
||||
if (e131ProxyUniverse != 0) return;
|
||||
@ -68,11 +68,14 @@ void handleDMX()
|
||||
dmx.update(); // update the DMX bus
|
||||
}
|
||||
|
||||
void initDMX() {
|
||||
void initDMXOutput() {
|
||||
#if defined(ESP8266) || defined(CONFIG_IDF_TARGET_ESP32C3) || defined(CONFIG_IDF_TARGET_ESP32S2)
|
||||
dmx.init(512); // initialize with bus length
|
||||
#else
|
||||
dmx.initWrite(512); // initialize with bus length
|
||||
#endif
|
||||
}
|
||||
#else
|
||||
void initDMXOutput(){}
|
||||
void handleDMXOutput() {}
|
||||
#endif
|
@ -116,6 +116,11 @@ void handleE131Packet(e131_packet_t* p, IPAddress clientIP, byte protocol){
|
||||
|
||||
// update status info
|
||||
realtimeIP = clientIP;
|
||||
|
||||
handleDMXData(uni, dmxChannels, e131_data, mde, previousUniverses);
|
||||
}
|
||||
|
||||
void handleDMXData(uint16_t uni, uint16_t dmxChannels, uint8_t* e131_data, uint8_t mde, uint8_t previousUniverses) {
|
||||
byte wChannel = 0;
|
||||
unsigned totalLen = strip.getLengthTotal();
|
||||
unsigned availDMXLen = 0;
|
||||
@ -130,7 +135,7 @@ void handleE131Packet(e131_packet_t* p, IPAddress clientIP, byte protocol){
|
||||
}
|
||||
|
||||
// DMX data in Art-Net packet starts at index 0, for E1.31 at index 1
|
||||
if (protocol == P_ARTNET && dataOffset > 0) {
|
||||
if (mde == REALTIME_MODE_ARTNET && dataOffset > 0) {
|
||||
dataOffset--;
|
||||
}
|
||||
|
||||
@ -211,7 +216,7 @@ void handleE131Packet(e131_packet_t* p, IPAddress clientIP, byte protocol){
|
||||
else
|
||||
dataOffset = DMXAddress;
|
||||
// Modify address for Art-Net data
|
||||
if (protocol == P_ARTNET && dataOffset > 0)
|
||||
if (mde == REALTIME_MODE_ARTNET && dataOffset > 0)
|
||||
dataOffset--;
|
||||
// Skip out of universe addresses
|
||||
if (dataOffset > dmxChannels - dmxEffectChannels + 1)
|
||||
@ -285,7 +290,7 @@ void handleE131Packet(e131_packet_t* p, IPAddress clientIP, byte protocol){
|
||||
}
|
||||
} else {
|
||||
// All subsequent universes start at the first channel.
|
||||
dmxOffset = (protocol == P_ARTNET) ? 0 : 1;
|
||||
dmxOffset = (mde == REALTIME_MODE_ARTNET) ? 0 : 1;
|
||||
const unsigned dimmerOffset = (DMXMode == DMX_MODE_MULTIPLE_DRGB) ? 1 : 0;
|
||||
unsigned ledsInFirstUniverse = (((MAX_CHANNELS_PER_UNIVERSE - DMXAddress) + dmxLenOffset) - dimmerOffset) / dmxChannelsPerLed;
|
||||
previousLeds = ledsInFirstUniverse + (previousUniverses - 1) * ledsPerUniverse;
|
||||
|
@ -183,12 +183,17 @@ uint32_t colorBalanceFromKelvin(uint16_t kelvin, uint32_t rgb);
|
||||
uint16_t approximateKelvinFromRGB(uint32_t rgb);
|
||||
void setRandomColor(byte* rgb);
|
||||
|
||||
//dmx.cpp
|
||||
void initDMX();
|
||||
void handleDMX();
|
||||
//dmx_output.cpp
|
||||
void initDMXOutput();
|
||||
void handleDMXOutput();
|
||||
|
||||
//dmx_input.cpp
|
||||
void initDMXInput();
|
||||
void handleDMXInput();
|
||||
|
||||
//e131.cpp
|
||||
void handleE131Packet(e131_packet_t* p, IPAddress clientIP, byte protocol);
|
||||
void handleDMXData(uint16_t uni, uint16_t dmxChannels, uint8_t* e131_data, uint8_t mde, uint8_t previousUniverses);
|
||||
void handleArtnetPollReply(IPAddress ipAddress);
|
||||
void prepareArtnetPollReply(ArtPollReply* reply);
|
||||
void sendArtnetPollReply(ArtPollReply* reply, IPAddress ipAddress, uint16_t portAddress);
|
||||
|
@ -141,7 +141,9 @@ bool PinManager::allocateMultiplePins(const managed_pin_type * mptArray, byte ar
|
||||
bool PinManager::allocatePin(byte gpio, bool output, PinOwner tag)
|
||||
{
|
||||
// HW I2C & SPI pins have to be allocated using allocateMultiplePins variant since there is always SCL/SDA pair
|
||||
if (!isPinOk(gpio, output) || (gpio >= WLED_NUM_PINS) || tag==PinOwner::HW_I2C || tag==PinOwner::HW_SPI) {
|
||||
// DMX_INPUT pins have to be allocated using allocateMultiplePins variant since there is always RX/TX/EN triple
|
||||
if (!isPinOk(gpio, output) || (gpio >= WLED_NUM_PINS) || tag==PinOwner::HW_I2C || tag==PinOwner::HW_SPI
|
||||
|| tag==PinOwner::DMX_INPUT) {
|
||||
#ifdef WLED_DEBUG
|
||||
if (gpio < 255) { // 255 (-1) is the "not defined GPIO"
|
||||
if (!isPinOk(gpio, output)) {
|
||||
|
@ -35,15 +35,16 @@ enum struct PinOwner : uint8_t {
|
||||
Ethernet = 0x81,
|
||||
BusDigital = 0x82,
|
||||
BusOnOff = 0x83,
|
||||
BusPwm = 0x84, // 'BusP' == PWM output using BusPwm
|
||||
Button = 0x85, // 'Butn' == button from configuration
|
||||
IR = 0x86, // 'IR' == IR receiver pin from configuration
|
||||
Relay = 0x87, // 'Rly' == Relay pin from configuration
|
||||
SPI_RAM = 0x88, // 'SpiR' == SPI RAM
|
||||
DebugOut = 0x89, // 'Dbg' == debug output always IO1
|
||||
DMX = 0x8A, // 'DMX' == hard-coded to IO2
|
||||
HW_I2C = 0x8B, // 'I2C' == hardware I2C pins (4&5 on ESP8266, 21&22 on ESP32)
|
||||
HW_SPI = 0x8C, // 'SPI' == hardware (V)SPI pins (13,14&15 on ESP8266, 5,18&23 on ESP32)
|
||||
BusPwm = 0x84, // 'BusP' == PWM output using BusPwm
|
||||
Button = 0x85, // 'Butn' == button from configuration
|
||||
IR = 0x86, // 'IR' == IR receiver pin from configuration
|
||||
Relay = 0x87, // 'Rly' == Relay pin from configuration
|
||||
SPI_RAM = 0x88, // 'SpiR' == SPI RAM
|
||||
DebugOut = 0x89, // 'Dbg' == debug output always IO1
|
||||
DMX = 0x8A, // 'DMX' == hard-coded to IO2
|
||||
HW_I2C = 0x8B, // 'I2C' == hardware I2C pins (4&5 on ESP8266, 21&22 on ESP32)
|
||||
HW_SPI = 0x8C, // 'SPI' == hardware (V)SPI pins (13,14&15 on ESP8266, 5,18&23 on ESP32)
|
||||
DMX_INPUT = 0x8D, // 'DMX_INPUT' == DMX input via serial
|
||||
// Use UserMod IDs from const.h here
|
||||
UM_Unspecified = USERMOD_ID_UNSPECIFIED, // 0x01
|
||||
UM_Example = USERMOD_ID_EXAMPLE, // 0x02 // Usermod "usermod_v2_example.h"
|
||||
|
@ -420,6 +420,14 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
|
||||
t = request->arg(F("WO")).toInt();
|
||||
if (t >= -255 && t <= 255) arlsOffset = t;
|
||||
|
||||
#ifdef WLED_ENABLE_DMX_INPUT
|
||||
dmxInputTransmitPin = request->arg(F("IDMT")).toInt();
|
||||
dmxInputReceivePin = request->arg(F("IDMR")).toInt();
|
||||
dmxInputEnablePin = request->arg(F("IDME")).toInt();
|
||||
dmxInputPort = request->arg(F("IDMP")).toInt();
|
||||
if(dmxInputPort <= 0 || dmxInputPort > 2) dmxInputPort = 2;
|
||||
#endif
|
||||
|
||||
#ifndef WLED_DISABLE_ALEXA
|
||||
alexaEnabled = request->hasArg(F("AL"));
|
||||
strlcpy(alexaInvocationName, request->arg(F("AI")).c_str(), 33);
|
||||
|
@ -65,7 +65,10 @@ void WLED::loop()
|
||||
handleNotifications();
|
||||
handleTransitions();
|
||||
#ifdef WLED_ENABLE_DMX
|
||||
handleDMX();
|
||||
handleDMXOutput();
|
||||
#endif
|
||||
#ifdef WLED_ENABLE_DMX_INPUT
|
||||
dmxInput.update();
|
||||
#endif
|
||||
|
||||
#ifdef WLED_DEBUG
|
||||
@ -527,7 +530,10 @@ void WLED::setup()
|
||||
}
|
||||
#endif
|
||||
#ifdef WLED_ENABLE_DMX
|
||||
initDMX();
|
||||
initDMXOutput();
|
||||
#endif
|
||||
#ifdef WLED_ENABLE_DMX_INPUT
|
||||
dmxInput.init(dmxInputReceivePin, dmxInputTransmitPin, dmxInputEnablePin, dmxInputPort);
|
||||
#endif
|
||||
|
||||
#ifdef WLED_ENABLE_ADALIGHT
|
||||
@ -778,7 +784,6 @@ int8_t WLED::findWiFi(bool doScan) {
|
||||
void WLED::initConnection()
|
||||
{
|
||||
DEBUG_PRINTF_P(PSTR("initConnection() called @ %lus.\n"), millis()/1000);
|
||||
|
||||
#ifdef WLED_ENABLE_WEBSOCKETS
|
||||
ws.onEvent(wsEvent);
|
||||
#endif
|
||||
@ -807,6 +812,7 @@ void WLED::initConnection()
|
||||
if (!WLED_WIFI_CONFIGURED) {
|
||||
DEBUG_PRINTLN(F("No connection configured."));
|
||||
if (!apActive) initAP(); // instantly go to ap mode
|
||||
return;
|
||||
} else if (!apActive) {
|
||||
if (apBehavior == AP_BEHAVIOR_ALWAYS) {
|
||||
DEBUG_PRINTLN(F("Access point ALWAYS enabled."));
|
||||
|
@ -144,6 +144,10 @@
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef WLED_ENABLE_DMX_INPUT
|
||||
#include "dmx_input.h"
|
||||
#endif
|
||||
|
||||
#include "src/dependencies/e131/ESPAsyncE131.h"
|
||||
#ifndef WLED_DISABLE_MQTT
|
||||
#include "src/dependencies/async-mqtt-client/AsyncMqttClient.h"
|
||||
@ -459,7 +463,15 @@ WLED_GLOBAL bool arlsForceMaxBri _INIT(false); // enable to f
|
||||
WLED_GLOBAL uint16_t DMXStart _INIT(10); // start address of the first fixture
|
||||
WLED_GLOBAL uint16_t DMXStartLED _INIT(0); // LED from which DMX fixtures start
|
||||
#endif
|
||||
WLED_GLOBAL uint16_t e131Universe _INIT(1); // settings for E1.31 (sACN) protocol (only DMX_MODE_MULTIPLE_* can span over consecutive universes)
|
||||
#ifdef WLED_ENABLE_DMX_INPUT
|
||||
WLED_GLOBAL int dmxInputTransmitPin _INIT(0);
|
||||
WLED_GLOBAL int dmxInputReceivePin _INIT(0);
|
||||
WLED_GLOBAL int dmxInputEnablePin _INIT(0);
|
||||
WLED_GLOBAL int dmxInputPort _INIT(2);
|
||||
WLED_GLOBAL DMXInput dmxInput;
|
||||
#endif
|
||||
|
||||
WLED_GLOBAL uint16_t e131Universe _INIT(1); // settings for E1.31 (sACN) protocol (only DMX_MODE_MULTIPLE_* can span over consequtive universes)
|
||||
WLED_GLOBAL uint16_t e131Port _INIT(5568); // DMX in port. E1.31 default is 5568, Art-Net is 6454
|
||||
WLED_GLOBAL byte e131Priority _INIT(0); // E1.31 port priority (if != 0 priority handling is active)
|
||||
WLED_GLOBAL E131Priority highPriority _INIT(3); // E1.31 highest priority tracking, init = timeout in seconds
|
||||
|
@ -436,6 +436,18 @@ void getSettingsJS(byte subPage, Print& settingsScript)
|
||||
printSetFormCheckbox(settingsScript,PSTR("ES"),e131SkipOutOfSequence);
|
||||
printSetFormCheckbox(settingsScript,PSTR("EM"),e131Multicast);
|
||||
printSetFormValue(settingsScript,PSTR("EU"),e131Universe);
|
||||
#ifdef WLED_ENABLE_DMX
|
||||
settingsScript.print(SET_F("hideNoDMX();")); // hide "not compiled in" message
|
||||
#endif
|
||||
#ifndef WLED_ENABLE_DMX_INPUT
|
||||
settingsScript.print(SET_F("hideDMXInput();")); // hide "dmx input" settings
|
||||
#else
|
||||
settingsScript.print(SET_F("hideNoDMXInput();")); //hide "not compiled in" message
|
||||
printSetFormValue(settingsScript,SET_F("IDMT"),dmxInputTransmitPin);
|
||||
printSetFormValue(settingsScript,SET_F("IDMR"),dmxInputReceivePin);
|
||||
printSetFormValue(settingsScript,SET_F("IDME"),dmxInputEnablePin);
|
||||
printSetFormValue(settingsScript,SET_F("IDMP"),dmxInputPort);
|
||||
#endif
|
||||
printSetFormValue(settingsScript,PSTR("DA"),DMXAddress);
|
||||
printSetFormValue(settingsScript,PSTR("XX"),DMXSegmentSpacing);
|
||||
printSetFormValue(settingsScript,PSTR("PY"),e131Priority);
|
||||
|
Loading…
x
Reference in New Issue
Block a user