Issues sending DMX-512 packets - missing bytes, hardware not waiting #2500
Unanswered
cinderblock
asked this question in
Q&A
Replies: 0 comments
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
-
I'm trying to send DMX-512 packets from node-serialport with an FT232R + RS485 transmitter but am running into a number of issues that might even be bugs in the implementation.
Protocol Reference
DMX-512 is based on single directional RS-485 running at 250k baud with 2 stop bits. To simplify a little bit, an "ideal" DMX-512 packet looks something like this:
In case people aren't familiar, "Mark" is the default idle state of a standard serial port. On TTL level ports, this would usually be represented as a logical "high" on the wire.
A "Break" is (usually) a logical "low" on the wire that lasts longer than a standard byte.
The 513 bytes of data are an initial "address" byte and then 512 "slots" of data that usually corresponds to light levels of controlled lights.
Hardware Setup
The first complication in the setup was forcing the RS485 transceiver to always be "transmitting". Letting the bus go to "idle" is theoretically similar to a "break" but not guaranteed to be identical depending on the receiver implementation, so it was easier to just force my RS-485 TXDEN on.
To
brk
or not tobrk
The first complication in implementing such a protocol generator is getting a serial port to go "low" for an extended period of time. There are two main ways people have achieved this.
brk: true
The simplest, imho, is to use the "break" command of standard serial ports. This tells the hardware to go to this abnormal state. No other sending is possible during this time. Traditionally, you break, wait, release the break, and then transmit normal data (after ensuring a minimum 12us "mark" time).
This kinda works. However the timing is very variable - 6ms or 175ms gaps when I'm asking for 1ms (thanks Windows).
While I don't expect JavaScript and Windows to be excellent at timing in this kind of situation, this seems to be a symptom of a larger problem.
Zooming in on the data that was actually sent over the wire, odd numbered packets are sent correctly (ending in
3
,2
,1
):You can ignore the protocol decoding issues from Logic
But even numbered packets end abruptly at approximately slot number 460:
Zooming out slightly, I'm thinking the "break" is sometimes starting more quickly than I'd like as this is when the break comes out to be ~4ms long.
So maybe the writes aren't completing as expected? This led me to try some alternate implementations that each had their own problems.
brk: false
An alternate way people have tried to generate a "break" is to temporarily change the baud rate to something much slower and then send a single
0x00
byte on the wire:But this doesn't reliably work. It seems there is a very variable race condition on the order of actually changing baud rates etc.
Packet 1 works:

Packet 2's break is way to short, like it was sent at the wrong baud rate:

But it's even more broken... The

0x00
I send with a different baud rate comes out at the normal baud rate at the end of the standard packet:Even weirder is that the middle of packet 2 is messed up. There are two "breaks" at ~byte 450:

So, this is starting to make me really confused. Maybe my FTDI IC is bad? I've never run into this kind of issue before. Or maybe I'm not waiting for events to "finish" properly?
port.*
orport.port.*
That confusion got me looking into the APIs. It seems there are two independent and very different APIs for writing data to (and changing settings of) serial port hardware.
I noticed the
port.port.*
functions because they all Promises and I assumed that waiting for those to be fulfilled would be the correct way to "wait" for things to complete (be passed off to the OS layer).I've tried adding
await port.port.drain()
after my writes, but that doesn't seem to change anything.Then I remembered the old API:
port.write(data, err => {})
which actually returns a value based on if one should wait for aport.on('drain', ...)
event, but after playing with that a bunch, I was not able to get different behavior. It's also a rather difficult to use API as waiting for thedrain
event can only be done whenport.write()
says you should, otherwise the program seems to just exit.Of course, I could add a lot more delays... but that gets cumbersome quickly and dramatically limits the achievable frame rate. DMX-512 devices also often timeout after just 1s of inactivity.
Questions
await
s actually wait for the hardware settings to change?Versions
v16.13.2
8.1.2
[email protected]
Beta Was this translation helpful? Give feedback.
All reactions