From 8f398dfd08e44497b17ed16f0432995858a0c363 Mon Sep 17 00:00:00 2001 From: Arne Date: Fri, 25 Aug 2023 20:59:46 +0200 Subject: [PATCH] Move dmx_input into its own task on core 0. This was necessary because otherwise it is not able to respond to rdm in time. --- wled00/dmx_input.cpp | 110 +++++++++++++++++++++++++++++-------------- wled00/dmx_input.h | 31 ++++++++++-- 2 files changed, 103 insertions(+), 38 deletions(-) diff --git a/wled00/dmx_input.cpp b/wled00/dmx_input.cpp index f90515fd0..4ccfbab76 100644 --- a/wled00/dmx_input.cpp +++ b/wled00/dmx_input.cpp @@ -28,6 +28,7 @@ void rdmPersonalityChangedCb(dmx_port_t dmxPort, const rdm_header_t *header, USER_PRINTF("DMX personality changed to to: %d\n", DMXMode); } } + void rdmAddressChangedCb(dmx_port_t dmxPort, const rdm_header_t *header, void *context) { @@ -48,9 +49,8 @@ void rdmAddressChangedCb(dmx_port_t dmxPort, const rdm_header_t *header, } } -dmx_config_t DMXInput::createConfig() const +static dmx_config_t createConfig() { - dmx_config_t config; config.pd_size = 255; config.dmx_start_address = DMXAddress; // TODO split between input and output address @@ -91,9 +91,55 @@ dmx_config_t DMXInput::createConfig() const return config; } +void dmxReceiverTask(void *context) +{ + DMXInput *instance = static_cast(context); + if (instance == nullptr) + { + return; + } + + if (instance->installDriver()) + { + while (true) + { + instance->updateInternal(); + } + } +} + +bool DMXInput::installDriver() +{ + + const auto config = createConfig(); + if (!dmx_driver_install(inputPortNum, &config, DMX_INTR_FLAGS_DEFAULT)) + { + USER_PRINTF("Error: Failed to install dmx driver\n"); + return false; + } + + USER_PRINTF("Listening for DMX on pin %u\n", rxPin); + USER_PRINTF("Sending DMX on pin %u\n", txPin); + USER_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 + if(inputPortNum == dmxOutputPort) + { + USER_PRINTF("DMXInput: Error: Input port == output port"); + return; + } +#endif + if (inputPortNum < 3 && inputPortNum > 0) { this->inputPortNum = inputPortNum; @@ -121,21 +167,17 @@ void DMXInput::init(uint8_t rxPin, uint8_t txPin, uint8_t enPin, uint8_t inputPo return; } - const auto config = createConfig(); - if (!dmx_driver_install(inputPortNum, &config, DMX_INTR_FLAGS_DEFAULT)) + 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) { - USER_PRINTF("Error: Failed to install dmx driver\n"); - return; + USER_PRINTF("Error: Failed to create dmx rcv task"); } - - USER_PRINTF("Listening for DMX on pin %u\n", rxPin); - USER_PRINTF("Sending DMX on pin %u\n", txPin); - USER_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; } else { @@ -144,7 +186,7 @@ void DMXInput::init(uint8_t rxPin, uint8_t txPin, uint8_t enPin, uint8_t inputPo } } -void DMXInput::update() +void DMXInput::updateInternal() { if (!initialized) { @@ -153,45 +195,43 @@ void DMXInput::update() checkAndUpdateConfig(); - byte dmxdata[DMX_PACKET_SIZE]; dmx_packet_t packet; unsigned long now = millis(); - if (dmx_receive(inputPortNum, &packet, 0)) + if (dmx_receive(inputPortNum, &packet, DMX_TIMEOUT_TICK)) { if (!packet.err) { - if (!connected) - { - USER_PRINTLN("DMX is connected!"); - connected = true; - } - else if (!packet.is_rdm) + connected = true; + identify = isIdentifyOn(); + if (!packet.is_rdm) { + const std::lock_guard lock(dmxDataLock); dmx_read(inputPortNum, dmxdata, packet.size); - handleDMXData(1, 512, dmxdata, REALTIME_MODE_DMX, 0); } - - lastUpdate = now; } else { - /*This can happen when you first connect or disconnect your DMX devices. - If you are consistently getting DMX errors, then something may have gone wrong. */ - DEBUG_PRINT("A DMX error occurred - "); - DEBUG_PRINTLN(packet.err); // TODO translate err code to string for output + connected = false; } } - else if (connected && (now - lastUpdate > 5000)) + else { connected = false; - USER_PRINTLN("DMX was disconnected."); } +} - if (isIdentifyOn()) + +void DMXInput::update() +{ + if (identify) { - DEBUG_PRINTLN("RDM Identify active"); turnOnAllLeds(); } + else if (connected) + { + const std::lock_guard lock(dmxDataLock); + handleDMXData(1, 512, dmxdata, REALTIME_MODE_DMX, 0); + } } void DMXInput::turnOnAllLeds() diff --git a/wled00/dmx_input.h b/wled00/dmx_input.h index b762e59be..7a6266c50 100644 --- a/wled00/dmx_input.h +++ b/wled00/dmx_input.h @@ -1,6 +1,9 @@ #pragma once #include #include +#include +#include + /* * Support for DMX/RDM input via serial (e.g. max485) on ESP32 * ESP32 Library from: @@ -28,7 +31,12 @@ private: /// overrides everything and turns on all leds void turnOnAllLeds(); - dmx_config_t createConfig() const; + /// 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, @@ -38,11 +46,28 @@ private: friend void rdmPersonalityChangedCb(dmx_port_t dmxPort, const rdm_header_t *header, void *context); - uint8_t inputPortNum = 255; // TODO make this configurable + /// 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]; //TODO add locking somehow? maybe double buffer? /// True once the dmx input has been initialized successfully bool initialized = false; // true once init finished successfully /// True if dmx is currently connected - bool connected = false; + std::atomic connected{false}; + std::atomic 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; + };