Makuna / NeoPixelBus

An Arduino NeoPixel support library supporting a large variety of individually addressable LEDs. Please refer to the Wiki for more details. Please use the GitHub Discussions to ask questions as the GitHub Issues feature is used for bug tracking.
GNU Lesser General Public License v3.0
1.18k stars 264 forks source link

support dmx512 esp8266 #215

Closed mahmoud2802 closed 1 year ago

mahmoud2802 commented 6 years ago

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

Makuna commented 6 years ago

No it currently does not.

mahmoud2802 commented 6 years ago

do you have any plan to add?

Makuna commented 6 years ago

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.

mahmoud2802 commented 6 years ago

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 .

Makuna commented 6 years ago

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.

devarishi7 commented 2 years ago

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.

Makuna commented 2 years ago

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

devarishi7 commented 2 years ago

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.

Makuna commented 2 years ago

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

devarishi7 commented 2 years ago

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

devarishi7 commented 2 years ago

So howz the progress ?

Makuna commented 2 years ago

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.

devarishi7 commented 2 years ago

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.

Makuna commented 2 years ago

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?

devarishi7 commented 2 years ago

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.

Makuna commented 2 years ago

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.

devarishi7 commented 2 years ago

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.

Makuna commented 2 years ago

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 commented 2 years ago

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();

}
devarishi7 commented 2 years ago

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.

Makuna commented 2 years ago

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.

devarishi7 commented 2 years ago

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

Makuna commented 2 years ago

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

Makuna commented 2 years ago

v2.7.0

devarishi7 commented 2 years ago

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 commented 2 years ago

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 !

Makuna commented 2 years ago

Was this for DM512 or ws2821?

devarishi7 commented 2 years ago

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 commented 2 years ago

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 commented 2 years ago

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. !? )

Makuna commented 2 years ago

@devarishi7 You are asking questions like this is a discussion area, it is not, issues are for tracking bugs. Further, with topics that are not what the issue is about. Please use the appropriate forum, like the discussion area or gitter channel.

https://github.com/Makuna/NeoPixelBus/issues/78 and https://github.com/Makuna/NeoPixelBus/wiki/ESP8266-NeoMethods.

But maybe a complete FAQ on the topic is needed.

Makuna commented 1 year ago

https://github.com/Makuna/NeoPixelBus/releases/tag/2.7.2

devarishi7 commented 1 year ago

I checked the SRC, and my correction from August 2nd has not been implemented. 250Kbps = 4.0us per bit (not 4.2us) etc. The way it is now it will not work for the standard DMX512 format.

Makuna commented 1 year ago

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.

This comment was missed. Checking the DMX512 E1.11 timing update, the pulse needs to be within a very narrow range. Description Min Typical Max Unit Bit Rate 245 250 255 kbit / s Bit Time 3.92 4 4.08 μs

devarishi7 commented 1 year ago

Yes and i tested it with 4.0us (250Kbps) and that works perfectly, and the current 4.2us setting doesn't really. For now i have been giving the correction when i refer to it for new users. I don't have a way of testing the WS2821SpeedBase unfortunately.

Makuna commented 1 year ago

https://github.com/Makuna/NeoPixelBus/pull/636

I also found a bug that caused the WS2821 to output incorrect signal. I will put a new release with these fixes within the week as I may have introduced other bugs with other changes 8-/

Makuna commented 1 year ago

https://github.com/Makuna/NeoPixelBus/pull/642

Continue to find more bugs where break was incorrectly sized for WS2821 due to improper assumptions of speed and bit size.

This does affect the DMX512 just a little and would appreciate a confirmation with these changes. It should allow a slightly less wait between shows.

devarishi7 commented 1 year ago

Oh eh, well i'm on holidays at the moment for 6 weeks, and i didn't bring all required devices, but as long as the break for DMX512 is at least 88us all is good and any length up to 140us i have never had any issues (conceptinetics.h uses 136us and that's fine)

Makuna commented 1 year ago

v.2.7.3

devarishi7 commented 3 months ago

Now i am also using ESP32, and i was wondering if / hoping that you plan to add / adapt the current working method for the ESP8266 into the ESP32 I2S & I2Sx methods ? I think i might be able to manage myself but i can not do better than you i am sure.

Makuna commented 3 months ago

Now i am also using ESP32, and i was wondering if / hoping that you plan to add / adapt the current working method for the ESP8266 into the ESP32 I2S & I2Sx methods ? I think i might be able to manage myself but i can not do better than you i am sure.

Make a formal request in a new conversation or issue and reference this one.