Question regarding TeensyDMX.h

Status
Not open for further replies.

SteveSFX

Well-known member
Hello all

I have been tinkering around with a Teensy 3.2 and the TeensyDMX library.
Has anyone experience of this library?

My question is regarding 2 way DMX communication. I am reading conflicting reports that I need separate serial ports allocated to the send and receive channels.

My program will have the ability to both send and receive. Do I simply use Serial1 for both the RX and TX, or do I allocate Serial1 to say TX and Serial2 to RX?

I think the link saying to use two separate serial ports is incorrect. Surely with only one DMX cable you can only send or receive anyway.

Thanks
 
For separate communication paths, for example for different controlled or controlling hardware needing separate cables, you need two serial ports. However, if you’re trying to do something like RDM, where the direction switches from time to time on the same cable, you can use the same serial port.

If you just need to change direction, then you can create a Sender and a Receiver on the same serial port and then do your own arbitration with mutually exclusive behaviour between begin() and end() calls.

Note that there’s no support for concurrent send/receive without actually switching direction manually (that is, in the general case). For example, to switch to receive, you’d end() the sender and begin() the receiver. Your transceiver likely has an input that controls direction.

For something like RDM, however, there’s actually support in the library to respond to input and switch direction internally. There’s no need for manual begin()/end() calls. See the Responder interface.

What is your use case?
 
Last edited:
Thanks Shawn

I have 'freed up' a second serial port in case I need it, but I am going to being using the project as primarily a receiver with the occasional command sent.
Therefore I can leave it sitting in RX and just swap over to TX when I need to. It's not a complicated project.

What is the default pin allocated for direction change?
 
[Prologue:] How many transceivers/wires do you have? Are you using the same 2 DMX wires (i.e. 2 wires for the differential signalling side for a single data flow direction) for both TX and RX, and with a single transceiver with a direction pin? Or do you have two transceivers attached to 4 wires (2 directions total). For the latter case, the easiest thing to do is to just use two serial ports, but my text below could apply to this case with the correct HW/SW setup. For now, I'll assume you're using a single transceiver connected to a pair of wires for one-direction-at-a-time data.

I'll answer your last question first. You can choose your own GPIO pin to connect to the transceiver's direction pin, but you need to toggle it yourself at the appropriate time, eg. with `digitalWriteFast` when you want to change its state, and with `pinMode(the_pin, OUTPUT)` in setup(). See: TX/RX pins.

Does TX happen in response to something received in RX mode? If so, I'd suggest using the `Responder` approach because it takes care of pin toggles and UART direction swapping for you. Here's a list of things you need to do to set this up:

  1. Create and set a `Responder` derived class using your receiver's `setResponder`. (Sorry about the shared pointers, I plan to remove them in the next release.)
  2. Set your receiver's "how to toggle TX or RX" function by calling `setSetTXNotRXFunc` with a function that knows how to toggle TX/RX.
  3. In your `Responder`, implement `processByte` and, when it detects something that needs a response, that function fills in the given output buffer and the library will take care of everything for you, including toggling the pin, changing the UART as needed, and then changing back.

Go with this one -> But that may a little complex for your needs at the moment. You can still toggle the RX/TX pin between begin()/end() calls for a `Sender` and `Receiver` that have been initialized with the same serial port. Take care to end() one before you begin() another.

[Epilogue:]
Fun fact: When the UART starts with begin(), an IDLE character (11 bits) will likely be sent on the line. If that's not an issue, and it likely isn't an issue, then nothing needs to be done. There's two ways to avoid it. First, it can be avoided by changing the transceiver to TX (for the transmit case, that is) after calling `begin()` and after waiting 1 character time (about 44 microseconds). Of course, there's no avoiding waiting 44us; the best you can do is avoid transmission of the IDLE state. The second way will only avoid it when receiving, and that's to call `setTXEnabled(false)` on your `Receiver` before calling `begin()`. (It actually only needs to be called once, but calling every time won't harm anything.) That will avoid that 1-character delay/value entirely.

Again, it's not likely to be a problem because transmitting an IDLE character each time you switch UART directions (due to the `begin()` call) shouldn't affect the DMX transmission. I'm just putting this here for future reference, in case someone encounters this and wonders why.
 
Thanks. . Complicated!

The DMXserial library for Arduino has pin 2 allocated to go high automatically when transmitting and return low after transmission. I wondered if the Teensy library did the same.

I have allowed a pin for DMX direction change, so I will have to add that to all my transmit commands then.

My RX, TX and direction change connections are all optically isolated from the DMX signal, along with an isolated 5v supply on the Max481 transceiver (also feeding it's associated Opto-isolators).
 
If I switch to transmit, send the data and then switch back to receive immediately (as follows), it's clearly going to not send the data as the direction status will change too quickly.
What would be the best solution to ensure the data is sent, before switching back to receive?

Code:
        digitalWriteFast(DMXdirection, HIGH);              // Switch to send                                                                                                                     
        dmxTx.set(DMXaddressOUT, DMXmax);          //  Data                                                                                                     
        DMX1flag = 1;                                                                                                                                                    
        digitalWriteFast(DMXdirection, LOW);             // Switch back to receive
 
TeensyDMX operates completely asynchronously. All actual DMX traffic and data transfer is handled in interrupts. There's two ways to do what you need:
  1. Use the Responder interface. It takes care of all that for you.
  2. Use a specific sequence of steps to change to transmit mode and then back, detailed below.

Relevant references:
  1. Most general comments: Synchronous vs. asynchronous operation
  2. Responder: Synchronous operation by using custom responder
  3. Most likely the one you're looking for: Synchronous operation by pausing and resuming

How to respond to an RX by switching to and from TX:
  1. Receive until you need to transmit.
  2. rx.end() // End RX
  3. tx.pause() // Pause TX before it starts transmitting
  4. tx.begin() // Start TX
  5. tx.setTheDataYouNeedToSetIncludingSize() // Set your data
  6. tx.resumeFor(1) // Send only one packet
  7. while (tx.isTransmitting()) { yield(); } // Wait until done
  8. tx.end() // End TX
  9. rx.begin() // Restart RX

Note that, after these steps are done, the transmitter is still in the "paused" state because it only resumed transmission for one packet and then paused again. If you need to, you can go back to the "transmitting" state, depending on your needs.

If you tell me more about your use case, specifically when you need to switch between RX and TX, I could possibly help more.
 
One other point: DMX is continuously transmitted. This means that once you set some values in some channels, those will be sent continuously until changed. i.e. There's no need to continuously call `sender.set` with the same values.
 
Many thanks.

I believe my current setup is probably not quite correct then (not at that laptop at the moment, so doing this from memory)... I think I may be trying to do this the way I did another project using DMXserial on a Pro-Mini and getting confused.

I declared my sender and receiver (all on one serial port)

teensydmx::Sender dmxTx{Serial1};
teensydmx::Receiver dmxRx{Serial1};

I put my direction pin to default low, which switches my optically isolated data to incoming. I just Googled and found the circuit I used (which I have used successfully before)..

http://www.mathertel.de/Arduino/DMXShield.aspx

Then I check the incoming DMX data with: DMXinVal = dmxRx.get(DMXaddressIN);

If I want to send any DMX data, I take the direction pin high to swap to transmit.

Then dmxTx.set(DMXaddressOUT, 255);
I also tinkered with the idea of putting Serial.flush(); after this command to wait for the buffer to empty? Whether that would work or not, I am not sure.

I only ever send out this DMX data command once, or when the value changes.

But, looking at your examples above, I am clearly not doing this correctly at all.

My question also would be, if I tell it to send DMX value 255 once for example to my DMX light fitting, does the device receiving this command stay at 255 until I tell it a new value? Or does DMX rely on a constant stream of 0-255 data? (therefore dropping to zero if the data stream stops).

Otherwise, when I switch to receive, it would lose the DMX data stream sending the example 255 constantly.

Shame I don't yet have a DMX device to experiment with. I have ordered one, but it's not arrived yet.

EDIT... OK, so the transmit library continually sends the data using interrupts (just looked it up). So what happens when you switch to receive? Does this data stream stop momentarily?
 
Last edited:
Here are a set of details you may or may not already know:
  • DMX uses 2 wires to transmit, a differential pair, per RS-485. That can only send data one direction at a time. It's either transmitting or receiving, but not both.
  • With the appropriate arbitration code and hardware, you can switch between transmitting and receiving.
  • The Serial API isn't compatible with TeensyDMX, including flush(). TeensyDMX does everything asynchronously so you don't have to do anything in the main code, other than setting and reading data and options.
  • DMX is a protocol that continuously transmits (or receives, depending on your perspective). If data transmission stops, it is up to the device on the receiving end to decide what to do. The DMX spec describes minimum and maxmum timings, so you can determine an appropriate timeout. Note that data not being transmitted is not the same as transmitting zero. The behaviour is device dependent, so you, as a DMX device designer, get to decide what you want to do if you stop receiving data.

I'm still unsure what your protocol looks like. The questions posed here and in the "threading" thread don't really give a complete picture. If I knew what you'd like your behaviour to be, on a higher level than just "pause/restart/change direction/etc", I could help you more. Unless managed properly, threading is not likely to work as you expect.

IMHO: I have a feeling you don't really need threads for your application, but I could be wrong.
 
Thanks Shawn. It does appear to be working, but I am still tweaking about with it all and learning as I go.

I'll explain the basic idea.

5x 16A 230v input sockets. These go through 5x 30A relays to 5x 230v output sockets

Serial 1 is used to run the Teensy DMX library. It's sole purpose is to sit in listen mode constantly for detection of a value>125 on any one of 5 preset addresses.

Serial 2 is used to control a small Nextion touchscreen. This displays 5x buttons and the information from 5x PZEM004t AC power monitoring modules.

Serial 3 talks to the PZEM004t AC monitor modules and retrieves the voltage, current, frequency etc. These 5x modules are quizzed within the threaded subroutine that fires once a second. This is my only thread.

The 5x PZEM004t modules have their RX and TX connected through a pair of CD4051 multiplexers. This switches the data lines of each module also each second (so you read each module in sequence).
You can re-address the PZM004t modules to all be on the same serial bus, but this has proved to be very troublesome and this was far easier.

The reason for using a thread was to ensure that the modules were quizzed exactly once a second (so the screen data updates at a nice consistent rate). Otherwise, I found the screen changed data at odd intervals, depending on what the main loop was doing.

Should you either press the required channel touchscreen button, or receive the correct DMX signal, then that channels relay operates.

Then there is a second facility within this project.

There are also 5x manual input buttons. If you press one of these, then you latch the relay, but also transmit a preset DMX value on a preset DMX channel.

I hope that explains my use
 
Thanks for that bigger picture. Yeah, threading probably makes it a little easier. A timer‘s also an option, but you’re trading “thread difficulties” for “ISR difficulties”, plus the synchronization concepts they involve.

Can you tell me more about having to switch DMX directions on a single line? (Aka single twisted pair.)
 
The switching is done with this circuit: (I mentioned it above)...

http://www.mathertel.de/Arduino/DMXShield.aspx

I have breadboarded this circuit.

I have used this circuit before and it works well. I have Serial1 connected to it, and a pin allocated to the direction change. I leave it in receive and simply take that pin high to swap over to transmit.
The Arduino DMX library does this for you automatically (there is a pin allocated to direction change), but I don't believe the Teensy DMX library has that facility
 
Tell me an example sequence of steps the DMX line needs to see. I’d love to make this easier, but I’m not quite following the DMX use case when you need to switch directions. (I saw the hardware link you posted above, but don't know what you're trying to do in the software.) For example, is it every second or two seconds or something? Is it whenever a certain sequence of bytes is received? Something else?

If I can understand what you’re doing, maybe there’s an example template I can add to the release. For example, there’s a few ways automatic direction change via a pin can be done. TeensyDMX does have this capability: The Responder stuff does this already, but I don’t yet understand what you’re trying to do and what you need the behaviour to look like. Heck, I’m even happy to have a private chat about it just so I could improve my documentation and examples.

Update: I’ll add to my question: is it that you just need a simple call to transmit a single DMX packet and then switch back to receive, at any time of your choosing? If it’s a piece of equipment that expects a DMX stream, then a single packet likely won’t have the effect you expect. That’s one of the reasons Receiver and Sender are separate.
 
Last edited:
Shaun. Thank you for your time and attention on this.
I sat down last night and re-evaluated the project. I realised that you are correct (you knew that!)... I should really be using two separate DMX circuits and not one I can switch between.

I have used the dual direction circuit before for a control desk I built, but the receive function was purely for testing other equipment.

I need a stand alone serial channel allocated to the transmission. This will do nothing but send my required DMX value when the relay/channel is switched on.

A separate receive circuit will monitor the incoming DMX line (on it’s own incoming DMX socket) for the correct channel and >125 value to operate the coil of the relay.

My questions are...

If I send a DMX value of say 100 on channel 1, does it continue to do that until I tell it different?, or do I constantly need to issue the dmxTx.set(DMXaddressOUT, DMXvalue); (that seems unlikely)
I understood (possibly incorrectly) that it continues to transmit after one issue of the command until told different.

MANY thanks for battering my thick head into submission
 
When the Sender is active, it starts off at a default of zero for all channels (unless told otherwise before starting it). The values are continuously transmitted until told otherwise. Then those new values are continuously transmitted. I designed the API to make things as simple as possible, so no special “update” function needs to be called. The library manages all the timing and updating for you, per the DMX spec. Sometimes, that can throw people off if they’ve used other libraries before.

In fact, you don’t even need to worry about ISR synchronization or worry when and how you call TeensyDMX functions. I’ve made every attempt to get atomicity and synchronization correct. I hope there’s no bugs lurking…

So yes, you’re correct. It continuously transmits until told different. Have another look at the BasicSend example, note how there’s nothing in loop(), and then look at the output on a scope.

Example commits showing that this stuff is hard to get right, even well after I considered the library “mature”:
1. https://github.com/ssilverman/TeensyDMX/commit/06a70024ce5cad75c62f6ed0393e8c4a9febed69
2. https://github.com/ssilverman/TeensyDMX/commit/3a7c090e0c517ab4ddec79851f4e359bee63ef59

I also hope now that my pause() instructions, above, make more sense. It’s a more unusual use case specifically because DMX is continuously transmitted.

Hope that helps. :)
 
Last edited:
Shawn you are a legend. Thank you.

I have amended my design. I have freed-up a hardware serial port (by using altSoftSerial for the Nextion screen). I now have Serial1 for DMX TX and Serial2 for DMX RX.

I will alter my original DMX interface circuit to be only TX, and add a second DMX interface circuit so that I have the same opto-isolation but for the RX.

Many thanks
 
Just a word of caution about grounding and isolation. I did a quick search for “dmx512 isolation design” and found this link: https://www.analog.com/en/analog-dialogue/articles/isolated-rs-485-in-dmx512-lighting.html
That’s just one of many links you can peruse (that was conveniently near the top, and not a specific recommendation.)

The reason I bring this up is the DMX512 spec talks about recommended designs; I suggest looking at those. It’s suggested that TX be ground-referenced and RX be isolated. (Note: there’s probably a way to still isolate your TX circuitry, but it should be ground-referenced when talking with the outside world.) There’s an upcoming ESTA standard, E1.68, that goes a little more in-depth about DMX512 testing. It’s not yet in public review, but you could probably email the maintainer to talk a little about it, or even join the committee to get access.

Do perform a more in-depth review of those designs and your design regarding grounding, and it’s likely your equipment, both the Teensy and your DMX devices, will be happier.
 
Last edited:
Thanks Shawn, I will investigate that further.
I will eventually put this onto a proper PCB, so I will prototype a few options on a breadboard first and see how they perform
 
Status
Not open for further replies.
Back
Top