NeoPixelBus icon indicating copy to clipboard operation
NeoPixelBus copied to clipboard

support dmx512 esp8266

Open mahmoud2802 opened this issue 5 years ago • 31 comments

dose it support dmx512 for ic like ws2821 and other ic like that?

mahmoud2802 avatar Aug 06 '18 04:08 mahmoud2802

No it currently does not.

Makuna avatar Aug 07 '18 01:08 Makuna

do you have any plan to add?

mahmoud2802 avatar Aug 07 '18 06:08 mahmoud2802

What platform do you use?

Currently I do not have plans, but I doubt I would support it for platforms other than esp if I did at first.

Makuna avatar Aug 07 '18 14:08 Makuna

sorry. i don't understand what is your mean of platform .

i want build a led WiFi controller(esp8266 12f) with support multiple led chip (ws2812-SK6812 ,ws2821 (DMX512)).i use your library and it doesn't support for DMX 512 protocol .

mahmoud2802 avatar Aug 08 '18 05:08 mahmoud2802

Platforms or architecture refers to the CPU type. Many arduinos are Atmel AVR, but you mentioneded esp8266, which is another.

If I was gifted some of these ws2821 I would add support for esp.

Makuna avatar Aug 08 '18 13:08 Makuna

I was alos thinking about this option, even considered hacking into your code, but the structure of the library is a little beyond me. Reason is actually that i have not found a reliable DMX output for ESP8266 in combination with wifi, and i know your DMA output is reliable. DMX does require some additional hardware (The MAX485), and the format is a little different from the normal one, being that it does use 1 start & 2 stop bits, but it is normal Serial 8N2 (inverted polarity for the MAX485 but that does not affect the software side of things) at 250kbps for the rest of it, with a minimum reset-break of 88us (most libraries use more though, starting at about 100us) So not the strange HIGH - LOW timing that is common for ledstrips. I am willing to gift you some ledstrip and be involved in the enhancement, if only i can find a place where they still sell it. It seems like a simple enhancement, where all that is really needed is to convert 8 bytes into 11 bytes, which include the start & stop bits, and setting the transmission speed & break length.

devarishi7 avatar Mar 26 '22 10:03 devarishi7

https://en.wikipedia.org/wiki/DMX512 https://www.ledlightinghut.com/files/WS2821%20Datasheet.pdf

I can't even find an official spec on the WS2821. World Semi website doesn't even reference them at all.

I found these on alibaba. https://www.alibaba.com/product-detail/WS2821-led-pixel-panel_60724183923.html?spm=a2700.galleryofferlist.normal_offer.d_title.4c746839xBd8DK

Makuna avatar Mar 26 '22 20:03 Makuna

Good find ! I did find a datasheet, though, i know they basically receive DMX and there is some kind of addressing function, though in the fixtures i have seen, the address is simply cascading. They are not cheap, but i guess for testing you'd only need a few. I ran into fixtures which contained these LEDs which were built several years ago, but where the limitation has now become apparent. They are slow, and there is a limit in addresses on the chain. In the end DMX is a lighting standard system, and that is what i would use it for. (other than obviously fixing setups that were built with DMX LED-strip) There is reliable transmission available for AVR's but on an ESP i have not managed. Actually a UART method is the most obvious of course, no complications, but the libraries that i have found for it have incomplete frames in combination with WiFi traffic and connection. DMA mode also should be easy enough, but within your code, i am pretty sure i'll mess it up. Anyway if you want some DMX-Led-dots.. Let me know.

devarishi7 avatar Mar 26 '22 22:03 devarishi7

@devarishi7 Just send me a donation (see top of the wiki) and I will order some for testing.

Makuna avatar Mar 27 '22 19:03 Makuna

Done, i think it should do the trick. Keep me updated on the progress.

devarishi7 avatar Mar 27 '22 20:03 devarishi7

So howz the progress ?

devarishi7 avatar Apr 18 '22 10:04 devarishi7

I could not get the i2s method (DMA) to function correctly yet, I was going to look at just using the Uart to do the work (there are uart methods for WS2812 already).

The order for actual LEDs is a complete unknown. It just states in transit and can take months or just days.

Makuna avatar Apr 21 '22 04:04 Makuna

Ah well let's hope they arrive quickly. I thought the DMA would be fairly straightforward, somehow creating a way to add the start and stop bits and set the speed. When i was looking at it, i got a little stuck on setting the speed, with all templates and all, it is not a programming style that i am familiar with. The adding the start and stop bits would appear similar to creating the neopixel bytes i guess. I have never tried you UART mode to be honest. i am willing to do some testing on it. At DMX speed.

devarishi7 avatar Apr 21 '22 21:04 devarishi7

Mmm, DMX512 is not the same as the WS2821. The differences are minor but interesting. DMX512: the first slot/channel is always zero data for lighting; with another 512 slots possibly sent after. Also, the data is least significant first bit order (0-7). WS2821: it omits the weird slot 0 and the data order most significant first (7-0).

I guess my question is, DMX512 or WS2821?

Makuna avatar May 03 '22 04:05 Makuna

hehe.. Eh well i was not aware of a couple of things here. I know about the startcode. The LED strips i've found actually respond to DMX512 without issue, but it is possible that these are not WS2821's or that WS2821's do discard the startcode (i think it's that !) In any case, these DMX LED-strip are meant to be connected to a DMX512 system without extra interface, that is what made them popular for a while. No extra controllers. About the reverse order of the data-bits, i was not aware of that, and i am not even sure that is the case anymore. I have seen it on the WIKI page as well, but nowhere in any library or DMX reception transmission system anywhere.
I know that the logic levels are inverted compared to normal Serial (eg Logic 'HIGH' == '1' & 'LOW' == '0') but since there is normally speaking a 2 wire input to a RS485 transceiver, this compensation can be done hardware wise really easily. Now about the order of bits, i know this sketch works on an AtMega328p ` #define PWM_LED_PIN 5 #define DMX_PING 13 #define MAX_DMX_CHANNELS 512 #define DMX_OUTPUT_CHANNELS 512 #define NUMBER_OF_CHANNELS 512

#define BAUDDMX 250000UL #define TRANSMIT_FORMAT 0x0E // 8N2 #define BREAK_FORMAT 0x26 // 8E1 #define RECEIVE_FORMAT 0x06 // 8N1 (could be 8N2 aswell. #define BAUDBREAK 49950UL // should give 176us break same as Conceptinetics #define UBDMX ((F_CPU / (8 * BAUDDMX)) - 1) #define UBBREAK ((F_CPU / (8 * BAUDBREAK)) - 1)

volatile uint16_t dmxaddress = 1; volatile uint16_t i = 0; volatile uint8_t dmxreceived = 0; volatile uint16_t dmxcurrent = 0; volatile uint8_t dmxvalue[NUMBER_OF_CHANNELS]; uint8_t dmxoutput[DMX_OUTPUT_CHANNELS];

volatile uint8_t dmxnewvalue = 0; volatile uint8_t zerocounter = 0; volatile bool storeSerial = false;

void setup() { pinMode(PWM_LED_PIN, OUTPUT); // first dmxchannel LED value pinMode(DMX_PING, OUTPUT); //ping LED init_uart(); SetupTimerRegisters(); }

void loop() { ReceiveDMX(); Processing(); SendDMX(); }

void ReceiveDMX() { dmxnewvalue = 0; dmxcurrent = 0; zerocounter = 0; i = 0; UCSR0C = RECEIVE_FORMAT; //0x06; // 8N1 bitSet(UCSR0B, RXCIE0); // enable RX bitSet(TIMSK2, OCIE2A); while (dmxnewvalue != 1); }

void Processing() { // main processing static bool pin = true; pin = !pin; analogWrite(5, dmxvalue[0]); digitalWrite(13, pin); CopyValues(); }

void CopyValues() { for (uint8_t j = 0; j < 3; j++) { for (uint8_t k = 0; k < 9; k += 3) { dmxoutput[k + j] = dmxvalue[j + 3]; } } }

void SendDMX() { UCSR0C = BREAK_FORMAT; //0x06; // 8N1 UBRR0H = (UBBREAK >> 8); // set baud rate UBRR0L = (UBBREAK & 0xFF); // HL register uart_write(0); UCSR0C = TRANSMIT_FORMAT; //0x0E; // 8N2 UBRR0H = (UBDMX >> 8); // set baud rate UBRR0L = (UBDMX & 0xFF); // HL register uart_write(0); // start code uart_write_buffer(); }

void uart_write(char data) { UDR0 = data; while ((UCSR0A & 1 << TXC0) == 0); UCSR0A |= 1 << TXC0; }

void uart_write_buffer() { for (uint16_t j = 0; j < DMX_OUTPUT_CHANNELS; j++) { UDR0 = dmxoutput[j]; while ((UCSR0A & 1 << TXC0) == 0); UCSR0A |= 1 << TXC0; } }

void init_uart() { DDRD |= (1 << PORTD1); // set TX pin to output DDRD &= ~(1 << PORTD0); // set RX pin to input UCSR0A = 0x02; // 1<<U2X | 0<<MPCM; UCSR0B = 1 << RXCIE0 | 0 << TXCIE0 | 0 << UDRIE0 | 1 << RXEN0 | 1 << TXEN0 | 0 << UCSZ02; // Enable TX & RX, disable RX interrupt UCSR0C = RECEIVE_FORMAT; // 8N1 UBRR0H = (UBDMX >> 8); // set baud rate UBRR0L = (UBDMX & 0xFF); // HL register }

void SetupTimerRegisters() { // Sets up Timer2 to fire every 4us cli(); bitClear(TCCR2A, COM2A1); bitClear(TCCR2A, COM2A0); bitClear(TCCR2A, COM2B1); bitClear(TCCR2A, COM2B0); bitSet(TCCR2A, WGM21); bitClear(TCCR2A, WGM20); bitClear(TCCR2B, FOC2A); bitClear(TCCR2B, FOC2B); bitClear(TCCR2B, WGM22); bitClear(TCCR2B, CS22); bitClear(TCCR2B, CS21); bitSet(TCCR2B, CS20); OCR2A = 64; bitClear(TIMSK2, OCIE2B); bitSet(TIMSK2, OCIE2A); bitClear(TIMSK2, TOIE2); sei(); }

ISR(TIMER2_COMPA_vect) { if (bitRead(PIND, PIND0)) { // checks if the pin has gone HIGH zerocounter = 0; } else { zerocounter++; if (zerocounter == 20) // if 80us have passed 'Break' has been detected { bitClear(TIMSK2, OCIE2A); // disable Timer2 Interrupt storeSerial = true; } } } //end Timer2 ISR

ISR(USART_RX_vect) { /* The receive buffer (UDR0) must be read during the reception ISR, or the ISR will just execute again immediately upon exiting. */ dmxreceived = UDR0; if (storeSerial) { dmxcurrent++; //increment address counter starts at 0 // and skip the start code if (dmxcurrent > dmxaddress) { //check if the current address is the one we want. dmxvalue[i] = dmxreceived; i++; if ((i == NUMBER_OF_CHANNELS) || (dmxcurrent > MAX_DMX_CHANNELS + 1 )) { bitClear(UCSR0B, RXCIE0); storeSerial = false; dmxnewvalue = 1; //set newvalue, so that the main code can be executed. } } } } // end ISR ` And that the test LED on pin 5 Lights up correctly (active HIGH) and so i don't see any inverted order (unless there is something about the UART i don't know) So. The startcode (0), Yes ! For the rest DMX512 is Serial at 250kbps (inverted logic) and i did see that the WS2821's can go up to 750kbps, but that is optional, I don't think there is any inverted bit order. (unless the UART always does, but in that case i am confident that WS2821's would have them inverted as well) I can confirm the DMX strips functionality with an object that is at the entrance to a club here in Amsterdam if needed. nb CopyValues() is just a function to copy 1 input pixel into a few output pixels, confirming reception and transmission.

devarishi7 avatar May 03 '22 08:05 devarishi7

The LSB (reverse order of the bits) is something the UART does automatically, but not the i2s hardware that is used for the ESP8266 DMA methods.

Makuna avatar May 03 '22 18:05 Makuna

Ok, well the LED strip 'claims' to be fully DMX512 compatible (at least the ones that are the target) If WS2821's don't have that, they would not claim that, but regardless of that, easiest would i guess be to just add the possibility for either way, like you do with the speed for WS2812's and even that for the inverted logic level and even the startcode. Anyway, DMX512 is very commonly used, the strips are rare.

devarishi7 avatar May 03 '22 20:05 devarishi7

Here is my current work on using the i2s (DMA). While it does send a data stream, and the general timing is correct, the output stream doesn't match the data in the i2s buffer. The break and first slot are malformed even when the bits in the buffer are correct. The refactored i2s (DMA) for WS2812x still works fine and these share that core code, so I am really confused as to why it doesn't work.

image

Makuna avatar May 04 '22 23:05 Makuna

Test Sketch

#include <NeoPixelBus.h>

const uint16_t PixelCount = 10; // this example assumes at least 4 pixels, making it smaller will cause a failure
const uint8_t PixelPin = 2;  // make sure to set this to the correct pin, ignored for Esp8266
const uint8_t DebugPin = 5;

// NeoEsp8266Dmx512Method
// NeoWs2812xMethod
typedef NeoPixelBus<NeoRgbFeature, NeoEsp8266Dmx512Method> NeoBus;

NeoBus* strip;

void setup() {
    Serial.begin(115200);
    while (!Serial); // wait for serial attach

    Serial.println();
    Serial.println("Initializing...");
    Serial.flush();

    strip = new NeoBus(PixelCount, PixelPin);
    // this resets all the neopixels to an off state
    strip->Begin();
    strip->Show();

    pinMode(DebugPin, OUTPUT);
    digitalWrite(DebugPin, LOW);

    Serial.println();
    Serial.println("Running...");
}

const RgbColor cTest1(0b01000100, 0b10001000, 0b00000001);
 
void loop() {
    delay(10000);

    Serial.println("Colors ...");

    strip->SetPixelColor(0, cTest1);

    digitalWrite(DebugPin, HIGH);
    strip->Show();
    digitalWrite(DebugPin, LOW);

    delay(10);

    Serial.println("Off ...");

    // turn off the pixels
    strip->SetPixelColor(0, 0);
    strip->SetPixelColor(1, 0);
    strip->SetPixelColor(2, 0);
    strip->SetPixelColor(3, 0);
    strip->Show();

}

Makuna avatar May 04 '22 23:05 Makuna

Hmm that is a bit disturbing. I assume that you tested it by simply replacing the method within the same sketch (i mean that it still works for WS2812)
If the data in the buffer is correct, i am stumped as well of course. As for the conversion, The nibble lookup table is nice, it took me a bit to figure it out. I think i would have first added the start & stop bits first and then inverted (or not) but it is clearly not the issue. I can't see from your oscilloscope what i am supposed to see, Is that the datastream for the first pixel as set in the sketch ? The startcode appears to be not correct. Maybe it becomes a bit more clear when i see a few results. Also i think it may be better to increase the MAB to 12us, but that may have to come of the break time really. Actually shouldn't the whole break + MAB be a total of 160us (4 bytes) There is no issue in the break being to long. I am just thinking out loud here, i don't know in what way the UART and the I2S stream interact.

devarishi7 avatar May 05 '22 08:05 devarishi7

Found the problem. The i2s requires 32bit values otherwise endianness of the bytes are a problem. I wrote the original so long ago I forgot this detail and code just did things inside to work with it. Due to this, I had to rework the encoding part completely but the new one works on byte boundaries of source data now.

Here is the logic capture for both normal (top) and inverted (bottom) image

image

The branch mentioned above contains the changes if you would like to pull it down and give it a try. I will merge this in after a little more testing.

Makuna avatar May 05 '22 23:05 Makuna

Ah perfect ! I am going to set up for testing over the weekend.

devarishi7 avatar May 06 '22 07:05 devarishi7

added WS2821 (3x faster, same protocol) and merged into master

Makuna avatar May 07 '22 20:05 Makuna

v2.7.0

Makuna avatar May 27 '22 19:05 Makuna

Sorry about the late response, but i ran into some issue testing, which is probably related to me using an older core for the esp (2.4.2) which is the least bloated (for flash, i am actually quite tight on flash use of the ESP-01's i am using) and functioning core, but that core throws me a lot of warnings and errors in combination with your latest version. Also just as a small note, NeoEsp8266Dmx512Method has not been added to the keyword list

devarishi7 avatar Jul 28 '22 12:07 devarishi7

Ok i can see you are reading what i write. Now i got the thing upload properly, though i had to decrease my FS size to keep OTA working, but that is of later concern. Then i somehow had the wires on the XLR inverted, but once that was fixed, i kept having the inconsistent behaviour, basically it appeared as if either the address was shifting which would mean that maybe the break was not recognized correctly. I started out making sure i would send at least a full frame (increasing the number of pixels to 171, which didn't help, then i went into the SRC, to increase the breaktime, but to no avail, then i tried to increase ByteSendTimeUs , bu that didn't matter, and then doing the math i saw. // 4.2 us bit send, 250Kbps and i was under the impression that 250Kbps would make a bitlength of 4 us (not 4.2 us) I mean 1 / 250000 = 0.000004 (if i have the number of zeroes correctly) So with a bit of guesswork i modified static const uint32_t I2sClockDivisor = 21; // 0-63 to static const uint32_t I2sClockDivisor = 20; // 0-63 And that did the trick ! I will have to do some more testing in regards to how the whole program responds to wifi requests etc, but i am not expecting any issues. Also i will have to move all the new code into the older version, since i do want my FS to be 128KB, and ESPcore 3.0.2 just won't fit and have OTA updates, but i think i've got it sorted. Thanx a ton !

devarishi7 avatar Aug 02 '22 13:08 devarishi7

Was this for DM512 or ws2821?

Makuna avatar Aug 02 '22 14:08 Makuna

This was for DMX512. I also changed the ByteSendTimeUs to 44us (11 * 4uS) Of course i am not actually using WS2821's , but am actually receiving DMX on a UART, but regardless it should be 250Kbps, which is 4uS per bit, and increasing to 4.2us like you have done is beyond the 3.5% tolerance of the UART i am using. to receive But that really does work as expected, and the great thing about I2S is that it has no disturbance from the wifi the way a UART mode tends to on an ESP. (don't know about your UART method, but all others i've tested create a broken frame at times particularly when connecting to it as an Access point) Anther question i have is (well i can find out through testing i guess as well) Is I2S mode affected by Serial.swap() ? In other words can i receive on UART0 on the alternate pins while using GPIO3 as an I2S output ?

devarishi7 avatar Aug 02 '22 23:08 devarishi7

with regards to the WS2821SpeedBase i actually recommend setting

I2sClockDivisor = 27
I2sBaseClockDivisor = 8

whoich results in a bit length of 1.35us resulting in 740740bps which is a lot closer to the 750Kbps that the datasheets suggests. ByuteSentTimeUs can be reduced to 15 in that case.

devarishi7 avatar Aug 05 '22 00:08 devarishi7

So about the 'other question' i had, the documentation wasn't quite clear about it. But testing has confirmed that the UART0 swapped RX pin can be used without issues as long as Serial.begin(); Serial.swap() get called before NeoPixelBus::begin() Since RX0 is actually the only available RX on almost any ESP unit (although the swap pin isn't exposed on the ESP-01) it might be good to update the documentation. (now i am wondering if i should open a separate issue for this. !? )

devarishi7 avatar Aug 16 '22 09:08 devarishi7