My previous post on building an Art-Net to DMX interface using an ESP8266 seems to be getting a lot of attention. However, from the comments it is clear that a lot of people that build it themselves have difficulties to get it to work, or don’t get it to work at all. This post investigates this in more detail.
We have not been using these interfaces in our performances for quite some time, and started wondering whether there is something wrong my firmware. My implementation goes back to April 2017. Over the course of time there have been some updates to my code. Furthermore, the Arduino IDE has been updated, as well as the ESP8266 core for Arduino.
Recently I received all three interfaces back that I had built for my 1+1=3 collaborators and decided to update the firmware and to test them. One of them did not work at all due to a broken connection between the power supply and the Wemos D1 mini; two of them started just fine. After fixing the broken wire and updating the firmware on all three of them; they started up just fine, showing the green light (indicating a connection to the WiFi network) and on the monitor page of the web interface I cold see that Art-Net packets were being received. However, with my DMX controlled light it did not work at all.
Testing and initial diagnosis
Using an Enttec Open DMX interface and the very nice JV Lightning DmxControl software (which supports both Art-Net and the Enttec Open DMX), I set out to debug the issue. Since DMX is all about timing, I connected my DS203 mini oscilloscope to pin 2 and 3 of the DMX connector.
I found detailed schematic information about the timing of the DMX protocol on this page. Searching for oscilloscope images of DMX signals, I also found this page with information.
Comparing the output voltage with the DMX512 schematics, it became clear that something was wrong in the signal. To make it easier to see the full signal on the oscilloscope, I configured only three DMX output channels, all set to zero. The oscilloscope shows 5 similar blocks; changing the value for DMX channel 1, I see that the 3rd block changes – that is apparently the first channel. Prior to that should be a “start code” with value 0, so the last 4 blocks make sense. But the first block is too short; there is also a very short pulse all the way at the start which does not match the specification.
Output voltage with the initial firmware:
By connecting the Enttec Open DMX to my DMX controlled light, I could confirm that the combination works and that the DMX interface to my light is not broken. Connecting the Enttec Open DMX to the oscilloscope, I see that it has a much longer 1st block which is the “space for break” (labeled 1 according to this), and slightly different 2nd block which is the slot with the “start code”. Following is a whole series of blocks/slots corresponding to the DMX channels. In this case I cannot limit the number of DMX channels, so the series contains a full universe of 512 channels, each of them set to zero. Changing the values of the first few channels in Lighting DmxControl, I can see the corresponding change in the signal on the oscilloscope.
Since a static photo of the oscilloscope screen does not show that there is quite some jitter, especially in the first part of the signal, I uploaded a short video recording of the Enttec Open DXM signal.
Output voltage of the Enttec Open DMX:
As the initial “break” signal was apparently not correctly implemented in my firmware, I changed the code to use the low-level implementation of the break that was contributed here. Using 3 DMX channels again, this results in a good DMX signal.
Output voltage with the firmware, using the low-level break:
I investigated the problem with the original code, which consists of switching to a slower baud rate, sending a single byte as the break signal, and switching back to the original baud rate of 250000. Given that the “break” appears just as long as the other bytes, the switching of the serial port speed apparently fails. On the Arduino forum I found a post that suggested to flush the serial port prior to and after changing the speed; I implemented this and now the break signal looks fine again.
I think that the reason that it broke is due to updates in the Arduino version that I am using, and/or updates to the ESP8266 Arduino core. I guess I must have been lucky with my buggy initial implementation; this may explain why it failed for some people and worked for others.
Output voltage with the firmware, using the slow serial break:
DMX512 universe size
Connecting my Art-Net to DMX512 adapter with updated firmware to the DMX controlled light initially did not work; increasing the size of the DMX universe that is is passed on by my Art-Net to DMX512 adapter solved this. After some trial and error it turned out that – although I had configured the DMX light for three channels (R, G, B) – it would not work if it would receive less than 8 channels. I suppose this is because the light has a 3, 4, and 8-channel DMX mode, and that 8 channels is apparently the minimum for DMX input to be properly handled.
Comparing the timing of the three systems
Looking back at the videos, I see that the timing of the Enttec Open DXM controller is the worst, especially in the space for break and the mark after break (MAB). This is actually a known characteristic of the Open DMX, since timing is controlled by the serial (USB) port of the computer rather than by a dedicated microprocessor; that is also the reason why it is not recommended for serious applications.
My own EXP8266 firmware with the low-level code for the break shows less jitter for the break. Using the “slow serial byte” the jitter is totally gone for the break itself, but there is still some jitter in the duration of the MAB.
Regardless of the jitter, all three are working fine with my DMX controlled light! Having seen the timing of the signals in detail, I am also more confident that they should work with other DMX equipment. One thing I noticed however and don’t have a solution for right now, is that the differential voltage of the Max485 modules that I am using is considerably lower at ~3.5V than that of the Enttec at nearly 8V.
Summary
To summarize, I was glad to have the Enttec Open DMX USB adapter for testing, and especially for having the DS203. If you are doing electronics like this, I can really recommend to get an oscilloscope!
Besides testing with the Lightning DmxController software, I was also able to confirm that it all works smoothly again with our EEGsynth, using a patch consisting of the redis, inputcontrol and outputartnet modules.
Hi Roberto…sorry fo my poor english.
I’m trying to buil you artnet node with Wemos D1Mini
My router ip is 192.168.0.254.I follow your istructio9n and post on blog but I am not able to login.
this is my serial monito read out. have you any comment to help my?
thank’s in advance .Mario
————————————————————
SDK:2.2.1(cfd48f3)/Core:2.4.2/lwIP:2.0.3(STABLE-2_0_3_RELEASE/glue:arduino-2.4.1-13-g163bb82)/BearSSL:6d1cefc
setup starting
loadConfig
Failed to open config file
*WM:
*WM: AutoConnect
bcn 0
del if1
usl
mode : sta(3c:71:bf:28:de:6e)
add if0
*WM: Connecting as wifi client…
*WM: Using last saved values, should be faster
*WM: Connection result:
*WM: 0
del if0
usl
mode : softAP(3e:71:bf:28:de:6e)
*WM:
*WM: Configuring access point…
*WM: ARTNET
*WM: Custom AP IP/GW/Subnet
add if1
dhcp server start:(ip:192.168.244.1,mask:255.255.255.0,gw:192.168.244.1)
bcn 100
*WM: AP IP address:
*WM: 192.168.244.1
*WM: HTTP server started
——————————————————————–
Sorry…I have restart the board (my pc is on dhcp) and I’ve read this:
—————————————————————————————
SDK:2.2.1(cfd48f3)/Core:2.4.2/lwIP:2.0.3(STABLE-2_0_3_RELEASE/glue:arduino-2.4.1-13-g163bb82)/BearSSL:6d1cefc
setup starting
loadConfig
Failed to open config file
*WM:
*WM: AutoConnect
*WM: Connecting as wifi client…
*WM: Using last saved values, should be faster
*WM: Connection result:
*WM: 0
del if0
usl
mode : softAP(3e:71:bf:28:de:6e)
add if1
dhcp server start:(ip:192.168.4.1,mask:255.255.255.0,gw:192.168.4.1)
bcn 100
*WM:
*WM: Configuring access point…
*WM: ARTNET
*WM: Custom AP IP/GW/Subnet
*WM: AP IP address:
*WM: 192.168.1.1
*WM: HTTP server started
————————————————————————————–
how can i login in my 192.168.0.x LAN?
I think you beter look this up or ask this on https://github.com/tzapu/WiFiManager. In short: if the node cannot connect to an existing WiFi network it makes it’s own access point with the name ARTNET, you connect to it and configure the netoork settings to which it should connect. See line 122 in the source code and my previous post https://robertoostenveld.nl/art-net-to-dmx512-with-esp8266/
Hi Robert.
I built an ESP8266 based Artnet device following your instructions some time ago and it worked so far with all kind of devices perfectly (LED PAR spots, relay controller etc.).
Now I tried to connect it also to some moving head lights. I can basically control those lights but it seems like they are occasinally receiving wrong frames. I read your very interesting article here and checked my signal. It looks the same as in your video after the fix. But I still cannot get rid of those small random moved and flickering. Here is a movie of the oscilloscope output:
https://www.youtube.com/shorts/QVXJLAivi6M
I checked the same lights with one of those cheap 512 channel manual controllers and the lamps work smootly. I saw that this controller outputs a slightly higher Vpp voltage – but I can exclude that this is the reason. I inserted a voltage divider in the signal path and still everything remains smooth.
I checked also the output from the manual controller and I am confused that the bytes following the start byte apparently look completely different. Here is also an output. I had to freeze the osci because I was unable to sync the trigger to a stable position:
https://www.youtube.com/shorts/5rgkgvvmQV0
Do you have an idea what I might be doing wrong? Why does the seemingly “wrong” DMX signal work but not the correct one from the Artned device.
Thank you
Hi Artur,
Although the ESP8266 (and ESP32) from the Arduino IDE programming perspective look just like an ATMEL ATmega328P microcontroller unit (MCU) ion the Arduino Uno, a striking difference is that the ESP8266 is running a real-time operating system (RTOS) in the background to keep track of all wifi related tasks. When a wifi packet arrives, the MCU immediately needs to deal with it. The wifi being the highest priority, the remainder of the time the main loop() runs and the MCU is pushing the DMX output over the `Serial1` connection. Since the incoming wifi is unpredictable, I understand it that the timing of the SendBreak() and `Serial1.write()` commands is not as stable as it should be.
To confirm that the wifi is the culprit, you may want to write a sketch that sends a constant pattern over DMX (e.g., fade up and fade down) and run that sketch on the ESP8266 with wifi switched off completely, and with wifi on (and preferably in a busy wifi environment, where you are more likely to see wifi-related problems).
Perhaps that using a hardware timer the temporal stability might be improved; in that case the DMX output runs at the same priority level as the wifi. But that is too difficult for me to implement.
Alternatively, linking the ESP8266 to a secondary MCU like the ATTiny might provide a work-around. In that setup the ATTiny would be responsible for DMX timing and the ESP8266 for incoming ArtNet (UDP) and web interface (TCP) wifi traffic.
Hope this helps,
Robert
Hi Robert.
Thank you for your quick reply and valuable suggestions on what to try.
In fact, I did also what you suggested before – namely writing a very basic sketch with wifi disabled and only sending DMX packets in a loop following the techniques from you sketch.
After that showed no difference, I further analyzed the signal from the manual (working) controller and tried to mimic it’s timing using a series of bit toggling and delay instructions as close as possible.
The jittering went away. Well, almost – the moving head still showed some hookups once in a while. Every 10 seconds or so it did some hickups.
Poking around a bit I think I found the reason. The commercial controller gives slow devices a lot more of time by adding way more than the 2 stop bits after every byte – or extending the break between the slots, which is equivalency. I saw it inserts a 66μs series of logic 1s after every byte which corresponds to roughly >15 (!!!) stop bits. It extends also the durations of the pause before mark etc. a lot if compared to the standard.
The remaining jitter assumedly was caused because my test code sporadically missed a byte because of the 4μs bit duration is hard to come close with those delayMicroseconds() calls.
I then tried to go with a hardware timer. Calling an interrupt routine every 4μs and toggling bits there.
This completely eliminated jitter. However – since the esp8266 is so busy with that 250k interrupts per second – it can hardly do anything else.
It won’t even be able to run the loop() function nor handle wifi.
Finally I came up now with replacing USART by I2S (not to be mixed up with I2C). It’s actually a protocol used to hook up the esp to external DACs. Most people use it to build MP3 players or web radios.
Using I2S, I can perfection send out up to 1 million bits per second via DMA using almost no CPU cycles and a perfect timing which only depends on the accuracy of the CPU crystal and can be adjusted if necessary.
Using IS2, the jitter disappeared entirely. I can connect via WiFi and control the moving head super smoothly. Another device that sometimes worked in the past and sometimes not, is super stable now, too I still did no final testing with other devices but the approach looks very promising.
There is no free lunch though – the extra stop bits and extra wait times reduce the overall throughput to 30 f/s that I am able to send via DMX. I think I can optimize that a bit step by step without re-adding jitter.
And unfortunately the output pin changes because I2S is hardcoded to a pin on the 8266.
if you allow, I would like to share my code with you after I gave it some more thorough testing.
It’s very easy to understand. Instead of writing bytes to serial in a loop, it used a single command to send a structure of bytes with DMX pay breajs, marks, start bits, payload etc. stored in RAM via DMA to the GPIO pin.
I also implemented some modifications which allow to use the device in the wild. Without a wifi network but as a standalone AP. This is done by not waiting for the wifi manager in the setup but doing it asynchronously and in the meantime already accepting UDP packets sent to the preliminary AP.
Are you interested in my changes? Would you prefer a change request request. Or a diff. I could also send you a zip with changes and you can test it in advance.
I really like your project and used it a lot in the past and would be happy to support it.
Cheers, Artur
Hi Artur,
Thanks for the thorough follow-up investigation and detailed report! Very interesting findings. Using I2S for the precisely timed output is smart! I am a bit familiar with I2S as I used it with an ESP32 for a microphone system, but not for DAC output.
I would certainly like to receive input to improve the documentation and code. For the code and documentation that is hosted on github I would prefer if you could make a pull request. That ensures that the version details and history are maintained and that your github account is linked and receives the credit. Since it is easier to jointly work on GitHub, I think that follow up documentation should also go there, i.e., the README.md file can explain the different options of using the TX1 pin (as now) and the improved option of using the I2S pin. Code wise, it might also be possible (with a conditional #define) to have both options.
We may also want to split off this code from my arduino repository, which is a mixed bag, into a stand-alone repository. I know from the anonymous usage statistics of my blog that this “art-net-to-dmx512-with-esp8266” page is visited a lot, so there appear to be many other people interested in it. Offering them a better solution would be great. And I would not mind cracking opening some of the (now unused) DMX interface boxes that I have lying around to rewire the input to the MAX485 modules.
I don’t know how familiar you are with GitHub, but could you start by filing an issue to continue the discussion there? I can then guide you through making the code changes as a pull request.
Thanks, Robert
FYI, the code has been split off from https://github.com/robertoostenveld/arduino and now lives on https://github.com/robertoostenveld/esp8266_artnet_dmx512. This allows to make the suggested improvements to the timing and jitter using I2S.