claudeheintz / LXESP32DMX

BSD 3-Clause "New" or "Revised" License
66 stars 13 forks source link

Switch between receive & send modes? #19

Open m-elias opened 4 years ago

m-elias commented 4 years ago

Would this library be able to listen to the DMX input until there was no data for 1000 ms, then send DMX output once every 1000ms while listening/monitoring the DMX for data inbetween?

marmilicious commented 4 years ago

This is something I would be interested in too.

My goal is to have the MCU start receiving data until it's received one compete DMX512 packet, then analyze that DMX data, and finally send a completely separate new DMX packet to a different DMX universe. Then go back and listen again.

m-elias commented 4 years ago

My goal is to start as a DMX->MQTT bridge (DMX listen/probe mode), then if there's no active DMX master on the bus, switch to MQTT->DMX (DMX transmit mode) but still monitor the bus for any other DMX master.

m-elias commented 4 years ago

This is something I would be interested in too.

My goal is to have the MCU start receiving data until it's received one compete DMX512 packet, then analyze that DMX data, and finally send a completely separate new DMX packet to a different DMX universe. Then go back and listen again.

Sounds like you need two DMX/RS285 interfaces to handle the two universes? In that case, can't you just use two instances of the library, one to read and one to write?

claudeheintz commented 4 years ago

m-elias, yes, you can call startOutput() which continuously sends DMX. You would then do what ever you want to keep track of the 1000ms. At the end of that, call stop(). Or, you can even call startInput() without stop() because that will call stop() if the library task is running. Once you've checked to see if any input was received, start sending again by calling startOutput().

marmilicious, the library uses a single serial peripheral. What you want to do would require using one for your input connection and another for output. The library is not designed for this. However, you can see how it works and use its techniques. I'd suggest that you don't use ESP32 because of RTOS. The threaded/task aspect makes it really difficult to coordinate between one thread and another. Its much more straightforward to use an ESP8266 and do everything linearly where one function is not called until another has ended.

marmilicious commented 4 years ago

@claudeheintz Thank you, I appreciate for the info and suggestion.

@m-elias I was indeed planning to use two MAX485 chips, one set to only receive, and the other to only send.

m-elias commented 4 years ago

How does this look? I see a short flicker in my DMX light every 1000ms when there's no DMX master and right now I don't have another DMX master on hand to test the listening mode but do I have the startOutput() & startInput() done correctly? (For some reason I can't get the backticks/code insertion to work.)

include

include "esp_task_wdt.h"

define DMX_DIRECTION_PIN 21

define DMX_SERIAL_INPUT_PIN 16

define DMX_SERIAL_OUTPUT_PIN 17

bool slots_detected = 0;

define TOTAL_CHANNELS 12

unsigned long dmxTxTimerTriggerTime; unsigned long dmxTxTimerPeriod = 1000;

void receiveCallback(int slots) { if ( slots ) { xSemaphoreTake( ESP32DMX.lxDataLock, portMAX_DELAY ); // get data here xSemaphoreGive( ESP32DMX.lxDataLock ); slots_detected = 1; } }

void setup() { Serial.begin(115200); Serial.print("\n\nStarting sketch...");

pinMode(DMX_SERIAL_OUTPUT_PIN, OUTPUT); ESP32DMX.setDirectionPin(DMX_DIRECTION_PIN);

Serial.print(", set callback"); ESP32DMX.setDataReceivedCallback(receiveCallback);

Serial.print(", start dmx input"); ESP32DMX.startInput(DMX_SERIAL_INPUT_PIN); dmxTxTimerTriggerTime = millis() + dmxTxTimerPeriod;

Serial.println(", setup complete."); }

void loop() { if (slots_detected){ slots_detected = 0; Serial.print("\n"); Serial.print(millis()); Serial.print(" slots_detected"); } else { if (millis() > dmxTxTimerTriggerTime){ ESP32DMX.startOutput(DMX_SERIAL_OUTPUT_PIN); Serial.print("\n"); Serial.print(millis()); Serial.print(" TX'ing dmx"); xSemaphoreTake( ESP32DMX.lxDataLock, portMAX_DELAY ); ESP32DMX.setSlot(4 , 255); ESP32DMX.setSlot(6 , 127); ESP32DMX.setSlot(7 , 255); xSemaphoreGive( ESP32DMX.lxDataLock ); ESP32DMX.startInput(DMX_SERIAL_INPUT_PIN);

  if (millis() > dmxTxTimerTriggerTime + dmxTxTimerPeriod){
    dmxTxTimerTriggerTime = millis() + dmxTxTimerPeriod;
  } else {
    dmxTxTimerTriggerTime += dmxTxTimerPeriod;
  }
}    

}

esp_task_wdt_feed(); vTaskDelay(10); }

m-elias commented 4 years ago

With the previous Sparkfun library I was using, sending a DMX packet very 1000ms was fast enough for my RGB led that I'm testing with but with this library I was seeing a slight flicker so I decreased that period to 500ms and now the flicker is gone. I has also discovered that if I remove my Serial.print statements between startOutput & startInput in the main loop, that it doesn't have enough time to send any data before it stops the process and switches to input mode. Is there a blocking statement I could use to make sure it's finished sending at least one DMX packet before I switch back to input mode?

claudeheintz commented 4 years ago

The context switch between starting and stopping the send and receive tasks probably costs more than 250ms. (At least sometimes...The actual time may vary because of the multi-task nature of RTOS does not always guarantee when a particular task will run).

The maximum time between DMX packets according to the standard is 1.25 seconds. Your light probably assumes lost DMX after that time. You'd have to use a scope or logic probe to observe the exact timing. But, that's my guess at why the flicker. Cutting the delay to 500ms is a good solution. You can probably try increasing that until you notice the flicker again.

I added a while loop in the startOutput function that waits until the sendDMX task is actually started before it returns. This should guarantee that at least one frame of DMX is always sent. Note that this may also change the observed flicker behavior because it is entirely possible that you called stop() before the sendDMX task even started on some passes through the main loop making the time between DMX frames much longer than 1000ms + task switching time.

claudeheintz commented 4 years ago

The modification to startOutput did not work. At times it seemed to hang. Instead, there is now a flag that is set at the end of the output sendDMX task loop. A use might be as follows:

ESP32DMX.startOutput(DMXSERIAL_OUTPUT_PIN);
while ( ESP32DMX.dmxPacketSent() == 0 ) {
    vTaskDelay(1);
}
m-elias commented 4 years ago

Thanks for the work on the output flag. Sending is working but I am unable to receive any DMX packets. I'm using the Sparkfun ESP32 Thing DMX Shield and it works with the Sparkfun DMX library to send or receive but with your library I am unable to receive any DMX data. The inputTest example only displays zeros for the 3 test_slots once, then nothing. I'm not using a new Arduino installation on my w10 laptop. I installed the ESP32 core via the boards manager and I followed the instructions for the esp32 core and modified hal file in the sketch folder but I don't think it's working. How can I confirm it's using the modified hal file? Extracting the esp32 core into my sketch folder gives me a different folder structure then the readme mentions, but I just searched and replaced the existing hal file.