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.
This commit is contained in:
Arne 2023-08-25 20:59:46 +02:00 committed by Will Tatam
parent 68e9d701de
commit 8f398dfd08
2 changed files with 103 additions and 38 deletions

View File

@ -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); USER_PRINTF("DMX personality changed to to: %d\n", DMXMode);
} }
} }
void rdmAddressChangedCb(dmx_port_t dmxPort, const rdm_header_t *header, void rdmAddressChangedCb(dmx_port_t dmxPort, const rdm_header_t *header,
void *context) 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; dmx_config_t config;
config.pd_size = 255; config.pd_size = 255;
config.dmx_start_address = DMXAddress; // TODO split between input and output address config.dmx_start_address = DMXAddress; // TODO split between input and output address
@ -91,9 +91,55 @@ dmx_config_t DMXInput::createConfig() const
return config; 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();
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) 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) if (inputPortNum < 3 && inputPortNum > 0)
{ {
this->inputPortNum = inputPortNum; this->inputPortNum = inputPortNum;
@ -121,21 +167,17 @@ void DMXInput::init(uint8_t rxPin, uint8_t txPin, uint8_t enPin, uint8_t inputPo
return; return;
} }
const auto config = createConfig(); this->rxPin = rxPin;
if (!dmx_driver_install(inputPortNum, &config, DMX_INTR_FLAGS_DEFAULT)) 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"); USER_PRINTF("Error: Failed to create dmx rcv task");
return;
} }
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 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) if (!initialized)
{ {
@ -153,45 +195,43 @@ void DMXInput::update()
checkAndUpdateConfig(); checkAndUpdateConfig();
byte dmxdata[DMX_PACKET_SIZE];
dmx_packet_t packet; dmx_packet_t packet;
unsigned long now = millis(); unsigned long now = millis();
if (dmx_receive(inputPortNum, &packet, 0)) if (dmx_receive(inputPortNum, &packet, DMX_TIMEOUT_TICK))
{ {
if (!packet.err) if (!packet.err)
{ {
if (!connected) connected = true;
{ identify = isIdentifyOn();
USER_PRINTLN("DMX is connected!"); if (!packet.is_rdm)
connected = true;
}
else if (!packet.is_rdm)
{ {
const std::lock_guard<std::mutex> lock(dmxDataLock);
dmx_read(inputPortNum, dmxdata, packet.size); dmx_read(inputPortNum, dmxdata, packet.size);
handleDMXData(1, 512, dmxdata, REALTIME_MODE_DMX, 0);
} }
lastUpdate = now;
} }
else else
{ {
/*This can happen when you first connect or disconnect your DMX devices. connected = false;
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
} }
} }
else if (connected && (now - lastUpdate > 5000)) else
{ {
connected = false; connected = false;
USER_PRINTLN("DMX was disconnected.");
} }
}
if (isIdentifyOn())
void DMXInput::update()
{
if (identify)
{ {
DEBUG_PRINTLN("RDM Identify active");
turnOnAllLeds(); turnOnAllLeds();
} }
else if (connected)
{
const std::lock_guard<std::mutex> lock(dmxDataLock);
handleDMXData(1, 512, dmxdata, REALTIME_MODE_DMX, 0);
}
} }
void DMXInput::turnOnAllLeds() void DMXInput::turnOnAllLeds()

View File

@ -1,6 +1,9 @@
#pragma once #pragma once
#include <cstdint> #include <cstdint>
#include <esp_dmx.h> #include <esp_dmx.h>
#include <atomic>
#include <mutex>
/* /*
* Support for DMX/RDM input via serial (e.g. max485) on ESP32 * Support for DMX/RDM input via serial (e.g. max485) on ESP32
* ESP32 Library from: * ESP32 Library from:
@ -28,7 +31,12 @@ private:
/// overrides everything and turns on all leds /// overrides everything and turns on all leds
void turnOnAllLeds(); 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 // is invoked whenver the dmx start address is changed via rdm
friend void rdmAddressChangedCb(dmx_port_t dmxPort, const rdm_header_t *header, 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, friend void rdmPersonalityChangedCb(dmx_port_t dmxPort, const rdm_header_t *header,
void *context); 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 /// True once the dmx input has been initialized successfully
bool initialized = false; // true once init finished successfully bool initialized = false; // true once init finished successfully
/// True if dmx is currently connected /// True if dmx is currently connected
bool connected = false; std::atomic<bool> connected{false};
std::atomic<bool> identify{false};
/// Timestamp of the last time a dmx frame was received /// Timestamp of the last time a dmx frame was received
unsigned long lastUpdate = 0; 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;
}; };