-
-
Notifications
You must be signed in to change notification settings - Fork 3.4k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'main' of https://github.com/Christanoid/WLED-DNRGBW_fix
- Loading branch information
Showing
15 changed files
with
460 additions
and
23 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 |
Oops, something went wrong.