Giter Club home page Giter Club logo

pico-dmx's Introduction

Pico-DMX

example branch parameter

A library for outputting and inputting the DMX512-A lighting control protocol from a RaspberryPi Pico

About

The RaspberryPi Pico provides an exciting new addition to the market on low-cost easy-to-use microcontroller board. The Pico has plenty of features making it particularly interesting for lighting control hackers:

  • Lighting control software can potentially become quite large. A large chunk of flash is required to run networked protocols such as Art-Net and sACN (ANSI e1.31). The Pico has a whopping 2MB of flash at disposal, which leaves plenty of room for your networked DMX software.
  • Storing DMX universes requires a lot of RAM (>512B for each full DMX frame). With other microcontrollers, RAM is a scarce resource and manipulating DMX universes can quickly consume the entire RAM of the microcontroller. The Pico has a solid 264kB of RAM, leaving plenty of room for dmx frame manipulation.
  • Libraries for sending DMX on other microcontrollers tend to rely on either bit-banging a GPIO or a hack on a hardware UART to allow the UART to assert a sufficiently long BREAK and MAB at the beginning of each transfer. Bit-banging consumes a processor core for the duration of a transfer and is extremely sensitive to interrupts. Using a hardware UART is less computationally intensive, but most low-cost microcontrollers only have 1 or 2 hardware UARTs, limiting the number of parallel universes one can transmit or receive. The Programmable IO (PIO) modules of the Pico gives the possibility to create what is basically hardware support for custom IO protocols (such as DMX). The Pico features 2 PIO modules with 4 State Machines each, making it possible to input or output up to 8 universes in parallel. Thats more universes than you can shake a proverbial stick at.
  • As if the PIO wasn't impressive enough, the Pico has a powerful 10 channel DMA controller. In conjunction with the PIO, the DMA makes it possible to input or output entire DMX universes with only a handful of instructions.
  • The Pico has a rather odd combination of 2 ARM Cortex M0+ cores running at speeds up to 133MHz. The dual core architecture could provide a huge benefit in processing DMX universes as computationally demanding data processing can be offloaded on the second core.

This library implements a DMX input and DMX output for the RPi Pico, leveraging its powerful DMA and PIO features.

Installation

The Pico-DMX library should be available in both the Arduino libraries and the PlatformIO libraries. Pico-DMX can also be used in pico-sdk-based projects.

The Pico-DMX library can also be manually added to any project in the same way as any other Arduino library. See the Arduino guide for more information.

Usage

The library is based around the DmxOutput and DmxInput classes, representing a single DMX output or input.

Outputting DMX

The DmxOutput class is simply instantiated, and requires no further arguments.

   DmxOutput myDmxOutput;

After instantiation, the DMX output must be assigned to a pin. The .begin(uint pin) method initializes the DMX output and binds it to a pin. To start a DMX output on GPIO1 on the Pico, call

   myDmxOutput.begin(1);

The library defaults to using pio0 as the underlying PIO instance. If you want to use pio1 as the underlying PIO, add the PIO you want to use as an argument

   myDmxOutput.begin(1, pio1);

Even though the DMX output is initialized and ready to party, the output is still idle, waiting for a universe to transmit. A universe is simply an array of uint8_t, with a maximum length of 513. The zero'th value in the array is the packet start code and should be set to 0 for ordinary DMX data packets. Let's make a universe with 3 channels (plus the start code), and set channel 1 to full.

   uint universe_length;
   uint8_t universe[universe_length + 1]; 
   universe[1] = 255;

To send the universe, call the .write(...) method to send the universe wooshing down your DMX line.

   myDmxOutput.write(universe, universe_length + 1);

The .write(...) method is not blocking, and executes immediately. To check the status of your DMX transmission, you can call .busy()to check if the DMX output is done transmitting your universe.

   while(myDmxOutput.busy()) {
        // Patiently wait, or do other computing stuff
   }

See the examples for complete examples on how to use the DMX output

Inputting DMX

The library also enables DMX inputs through the DmxInput class. The DMX input can either read an entire universe or just a couple specified channels. Let's say the Pico controls a simple RGB LED, and we want to read the first three channels on the DMX universe to control our RGB LED. First, instantiate your DMX input, specifying what pin you want to use (GPIO 0 in our case), what channel you want to read from (channel 1), and how many channels you want to read (3 channels in total)

   DmxInput myDmxInput;
   uint dmx_pin = 0;
   uint start_channel = 1;
   uint num_channels = 3;
   myDmxInput.begin(dmx_pin, start_channels, num_channels);

The DMX Input is now ready to receive your DMX data. Before we start receiving DMX data, we want to create a buffer where we can keep our received DMX channels:

   uint8_t buffer[num_channels]; 

Use the .read(...) method to read the 3 channels for our RGB fixture into our buffer.

   myDmxInput.read(buffer);

The .read(...) method blocks until it receives a valid DMX packet. Much like the DmxOutput, the zero'th channel in the DMX packet is the start code. You should always check that the start code is the one defined for DMX (zero) to ensure you have a valid DMX packet, unless you are messing around with other protocols such as RDM, in which case check it is their valid start codes.

As an alternative to the blocking .read(...) method, you can also start asynchronous buffer updates via the .read_async(...) method. This way, the buffer is automatically updated when DMX data comes in. Optionally, you can also pass a pointer to a callback-function that will be called everytime a new DMX frame has been received, processed and has been written to the buffer. This callback-function will be called with one parameter which is the instance that has received the new data. This way, you can use one callback-function to react on data from multiple universes. See this example below:

   void __isr dmxDataRecevied(DmxInput* instance) {
     // A DMX frame has been received :-)
     // Toggle some LED, depending on which pin the data arrived
     uint ledPin = instance->pin() + 8;
     digitalWrite(ledPin, !digitalRead(ledPin));
   }

   myDmxInput.read_async(buffer, dmxDataRecevied);

A note on DMX interfaces sending "partial universes" (= fewer channels)

There are multiple universes that can be configured to send less than 512 channels per frame. Some interfaces do this automatically without an option to configure this feature.

The reason why this is done is that if not all channels of a universe are in use, one can send a "shorter" frame but send this frame more often per second (= increase the refresh rate). The specification of DMX512 allows this.

The problem arises if start_channel + num_channels is larger than the number of channels sent by the interface since the code of DmxInput waits for this specific amount of channels until the callback is being triggered. So if the amount of channels arriving at the input, the callback will be triggered at a later point in time, not at the end of a DMX frame.

Voltage Transceivers

The Pico itself cannot be directly hooked up to your DMX line, as DMX operates on RS485 logic levels, which do not match the voltage levels of the GPIO pins on the Pico.

Fortunately TLL to RS485 transceivers are easily available. Simple transceiver modules can be bought through online retailers for only a couple of dollars. These tend to use the MAX485 series of voltage level transceivers, which work great for most purposes. If you're planning to implement DMX on an industrial level, your device should have some kind of EMC protection. Many RS485 transceivers are available that have galvanic isolation between the TLL side and the RS485 side. These should be the preferred option.

Modifying .pio files

Unfortunately, the mbed-based Arduino core has no native support for compiling .pio files yet. However, we can compile them manually using pioasm. The result is a header file that can be included into the arduino sources.

Manual compilation requires cloning the pico sdk, compiling the pioasm tool, and running it like so: pioasm src/DmxInput.pio src/DmxInput.pio.h

pico-dmx's People

Contributors

bollaberg avatar functionpointer avatar jostlowe avatar khoih-prog avatar kripton avatar peternewman avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

pico-dmx's Issues

Can't compile ESP32

According to Platform.IO this library should work with Espressif32 platform. However, I am missing headers dma.h

In file included from src\main.cpp:28:0:
.pio\libdeps\esp32dev\Pico-DMX\src/DmxOutput.h:10:17: fatal error: dma.h: No such file or directory

Any Idea, where I can get them?

Issue Using Pico-DMX together with Adafruit_NeoPXL8

When using "Pico-DMX" together with "Adafruit_NeoPXL8" on Raspberry Pi Pico with Arduino (earlephilhower core) the processor behaves unpredictable (normally blocks all communication).
The root source for this problem is that both libraries use the same Interrupt (IRQ0) for DMA:

Pico-DMX / DmxInput.cpp / DmxInput::read_async
dma_channel_set_irq0_enabled(_dma_chan, true);
irq_set_exclusive_handler(DMA_IRQ_0, dmxinput_dma_handler);
irq_set_enabled(DMA_IRQ_0, true);

Adafruit_NeoPXL8 / Adafruit_neoPXL8.cpp / Adafruit_NeoPixel::begin
irq_set_exclusive_handler(DMA_IRQ_0, dma_finish_irq);
dma_channel_set_irq0_enabled(dma_channel, true);
irq_set_enabled(DMA_IRQ_0, true);

Changing one of the two to IRQ1 in all three code lines solves the problem and everything runs like a charm.

I don't know whether it is possible to sort of claim an IRQ like for the DMA channels and the PIO state machines.
If not it would be very desirable to add a parameter to the read_async method. Which allows to select the IRQ by the application in case of conflicts.
Same is true for the DmxInput::read method. DmxOutput not tested yet.

I will also report same thing to Adafruit.

which pins are best used for simultaneous input and output

I am hoping to use the rpi pico to receive dmx data and send it out again. I am using a rs485 transceiver LTC2851.
I have not been able to find any info using GPIO 0 as input and GPIO as output would work. I think they are both on the same pio?

Would this work?

  dmxInput.begin(0, START_CHANNEL, NUM_CHANNELS);
  dmxOutput.begin(1, pio1);

RDM Compatibility

Hi There
With this library, do you have any plans to implement RDM packets?

Input and Output in the same program?

Hi, I really like this library, and have had no problems with it so far. but for my current project, i need to be able to both output and input dmx, (not at the same time, but without restarting the pico) can this be done by calling both functions, using 2 different pins with 2 different state machines? or do I have to end output before I can start input? also is there a pin predefined to pull high or low for switching the mode of rs485 modules, or do I need to code that in myself?

thanks,
Leon

missing dma.h and poi.h

Win11, Arduino 1.8.16
Using Arduino-pico board manager with Generic RP2040 (WaveShare)
Installed Pico-DMX from Library manager. No other package were loaded as dependencies.
When I try to compile I error on missing <dma.h> Can't find where to get this (or <pio.h> which will come up next)
Library looks great I can't wait to use it!

write not sending channel 512

setting num channels to 512 with
dmxOutput.write(workingBuffer, NUM_CHANNELS);
where NUM_CHANNELS is 512, doesnt seem to send channel 512, but
dmxOutput.write(workingBuffer, NUM_CHANNELS+1);
does.
small issue
thanks
Paul

parallel output not works as expected with multiple buffers

I'm trying to write parallel dmx with different buffers instead of one buffer.

// In example one buffer was used to send dmx data.
// Ex for 3 Universe
uint8_t universe0 [512+1];
uint8_t universe1 [512+1];
uint8_t universe2 [512+1];

universe0[0] = 255;
universe1[1] = 255;
universe2[2] = 255;

dmxOutputs[i].write (universe0, UNIVERSE_LENGTH +1);
dmxOutputs[i].write (universe1, UNIVERSE_LENGTH +1);
dmxOutputs[i].write (universe2, UNIVERSE_LENGTH +1);

getting problem in running inbuilt sample code RGB_input_async in pico in arduino....

I receive this on comport while run inbuilt sample code RGB_input_async in pico in arduino....
code description is this..

  • Description:
  • Starts a DMX Input on GPIO pin 0 and read channel 1-3 repeatedly
    */
    but it shows four lines as show below and in first column 255 value randomly occurred. What is the problem?

09:08:04.175 -> Received packet: 0, 0, 0, 0,
09:08:04.211 -> Received packet: 0, 0, 0, 0,
09:08:04.257 -> Received packet: 0, 0, 0, 0,
09:08:04.304 -> Received packet: 0, 0, 0, 0,
09:08:04.351 -> Received packet: 0, 0, 0, 0,
09:08:04.351 -> Received packet: 0, 0, 0, 0,
09:08:04.398 -> Received packet: 0, 0, 0, 0,
09:08:04.445 -> Received packet: 0, 0, 0, 0,
09:08:04.492 -> Received packet: 0, 0, 0, 0,
09:08:04.539 -> Received packet: 0, 0, 0, 0,
09:08:04.586 -> Received packet: 0, 0, 0, 0,
09:08:04.633 -> Received packet: 0, 0, 0, 0,
09:08:04.680 -> Received packet: 0, 0, 0, 0,
09:08:04.680 -> Received packet: 0, 0, 0, 0,
09:08:04.725 -> Received packet: 0, 0, 0, 0,
09:08:04.772 -> Received packet: 0, 0, 0, 0,
09:08:04.818 -> Received packet: 0, 0, 0, 0,
09:08:04.865 -> Received packet: 0, 0, 0, 0,
09:08:04.913 -> Received packet: 255, 0, 0, 0,
09:08:04.957 -> Received packet: 0, 0, 0, 0,
09:08:04.957 -> Received packet: 0, 0, 0, 0,
09:08:05.004 -> Received packet: 0, 0, 0, 0,
09:08:05.050 -> Received packet: 0, 0, 0, 0,
09:08:05.097 -> Received packet: 0, 0, 0, 0,
09:08:05.144 -> Received packet: 0, 0, 0, 0,
09:08:05.192 -> Received packet: 0, 0, 0, 0,
09:08:05.238 -> Received packet: 0, 0, 0, 0,
09:08:05.285 -> Received packet: 0, 0, 0, 0,
09:08:05.285 -> Received packet: 0, 0, 0, 0,
09:08:05.332 -> Received packet: 0, 0, 0, 0,
09:08:05.379 -> Received packet: 0, 0, 0, 0,
09:08:05.426 -> Received packet: 0, 0, 0, 0,
09:08:05.475 -> Received packet: 0, 0, 0, 0,
09:08:05.521 -> Received packet: 0, 0, 0, 0,
09:08:05.569 -> Received packet: 0, 0, 0, 0,
09:08:05.569 -> Received packet: 0, 0, 0, 0,
09:08:05.616 -> Received packet: 255, 0, 0, 0,
09:08:05.660 -> Received packet: 0, 0, 0, 0,
09:08:05.707 -> Received packet: 0, 0, 0, 0,
09:08:05.754 -> Received packet: 0, 0, 0, 0,
09:08:05.800 -> Received packet: 0, 0, 0, 0,
09:08:05.848 -> Received packet: 0, 0, 0, 0,
09:08:05.895 -> Received packet: 0, 0, 0, 0,
09:08:05.895 -> Received packet: 0, 0, 0, 0,
09:08:05.942 -> Received packet: 0, 0, 0, 0,
09:08:05.989 -> Received packet: 0, 0, 0, 0,
09:08:06.037 -> Received packet: 0, 0, 0, 0,
09:08:06.083 -> Received packet: 0, 0, 0, 0,
09:08:06.130 -> Received packet: 0, 0, 0, 0,
09:08:06.176 -> Received packet: 0, 0, 0, 0,
09:08:06.225 -> Received packet: 0, 0, 0, 0,
09:08:06.225 -> Received packet: 0, 0, 0, 0,
09:08:06.271 -> Received packet: 0, 0, 0, 0,
09:08:06.319 -> Received packet: 0, 0, 0, 0,
09:08:06.369 -> Received packet: 0, 0, 0, 0,
09:08:06.412 -> Received packet: 0, 0, 0, 0,
09:08:06.459 -> Received packet: 0, 0, 0, 0,
09:08:06.507 -> Received packet: 0, 0, 0, 0,
09:08:06.507 -> Received packet: 0, 0, 0, 0,
09:08:06.551 -> Received packet: 0, 0, 0, 0,
09:08:06.596 -> Received packet: 0, 0, 0, 0,
09:08:06.643 -> Received packet: 0, 0, 0, 0,
09:08:06.691 -> Received packet: 0, 0, 0, 0,
09:08:06.737 -> Received packet: 0, 0, 0, 0,
09:08:06.784 -> Received packet: 0, 0, 0, 0,
09:08:06.831 -> Received packet: 0, 0, 0, 0,
09:08:06.831 -> Received packet: 0, 0, 0, 0,
09:08:06.878 -> Received packet: 0, 0, 0, 0,
09:08:06.925 -> Received packet: 0, 0, 0, 0,
09:08:06.972 -> Received packet: 0, 0, 0, 0,
09:08:07.018 -> Received packet: 0, 0, 0, 0,
09:08:07.067 -> Received packet: 0, 0, 0, 0,
09:08:07.114 -> Received packet: 0, 0, 0, 0,
09:08:07.114 -> Received packet: 0, 0, 0, 0,
09:08:07.160 -> Received packet: 0, 0, 0, 0,
09:08:07.207 -> Received packet: 0, 0, 0, 0,
09:08:07.253 -> Received packet: 0, 0, 0, 0,
09:08:07.301 -> Received packet: 0, 0, 0, 0,
09:08:07.349 -> Received packet: 0, 0, 0, 0,
09:08:07.396 -> Received packet: 0, 0, 0, 0,
09:08:07.396 -> Received packet: 0, 0, 0, 0,
09:08:07.442 -> Received packet: 0, 0, 0, 0,
09:08:07.489 -> Received packet: 0, 0, 0, 0,
09:08:07.536 -> Received packet: 0, 0, 0, 0,
09:08:07.581 -> Received packet: 0, 0, 0, 0,
09:08:07.628 -> Received packet: 0, 0, 0, 0,
09:08:07.670 -> Received packet: 0, 0, 0, 0,
09:08:07.708 -> Received packet: 0, 0, 0, 0,
09:08:07.755 -> Received packet: 0, 0, 0, 0,
09:08:07.802 -> Received packet: 0, 0, 0, 0,
09:08:07.802 -> Received packet: 0, 0, 0, 0,
09:08:07.850 -> Received packet: 0, 0, 0, 0,
09:08:07.897 -> Received packet: 0, 0, 0, 0,
09:08:07.944 -> Received packet: 0, 0, 0, 0,
09:08:07.992 -> Received packet: 0, 0, 0, 0,
09:08:08.036 -> Received packet: 0, 0, 0, 0,
09:08:08.082 -> Received packet: 0, 0, 0, 0,
09:08:08.129 -> Received packet: 0, 0, 0, 0,
09:08:08.129 -> Received packet: 0, 0, 0, 0,
09:08:08.175 -> Received packet: 0, 0, 0, 0,
09:08:08.221 -> Received packet: 0, 0, 0, 0,
09:08:08.268 -> Received packet: 0, 0, 0, 0,
09:08:08.315 -> Received packet: 0, 0, 0, 0,
09:08:08.363 -> Received packet: 0, 0, 0, 0,
09:08:08.411 -> Received packet: 255, 0, 0, 0,
09:08:08.459 -> Received packet: 0, 0, 0, 0,
09:08:08.459 -> Received packet: 0, 0, 0, 0,
09:08:08.506 -> Received packet: 0, 0, 0, 0,
09:08:08.553 -> Received packet: 0, 0, 0, 0,
09:08:08.601 -> Received packet: 0, 0, 0, 0,
09:08:08.649 -> Received packet: 0, 0, 0, 0,
09:08:08.695 -> Received packet: 0, 0, 0, 0,
09:08:08.695 -> Received packet: 0, 0, 0, 0,
09:08:08.743 -> Received packet: 0, 0, 0, 0,
09:08:08.791 -> Received packet: 0, 0, 0, 0,
09:08:08.837 -> Received packet: 0, 0, 0, 0,
09:08:08.884 -> Received packet: 0, 0, 0, 0,
09:08:08.930 -> Received packet: 0, 0, 0, 0,
09:08:08.978 -> Received packet: 0, 0, 0, 0,
09:08:09.025 -> Received packet: 0, 0, 0, 0,
09:08:09.025 -> Received packet: 0, 0, 0, 0,
09:08:09.073 -> Received packet: 0, 0, 0, 0,
09:08:09.120 -> Received packet: 0, 0, 0, 0,
09:08:09.167 -> Received packet: 0, 0, 0, 0,
09:08:09.214 -> Received packet: 0, 0, 0, 0,
09:08:09.263 -> Received packet: 0, 0, 0, 0,
09:08:09.308 -> Received packet: 0, 0, 0, 0,
09:08:09.308 -> Received packet: 0, 0, 0, 0,
09:08:09.354 -> Received packet: 0, 0, 0, 0,
09:08:09.401 -> Received packet: 0, 0, 0, 0,
09:08:09.448 -> Received packet: 0, 0, 0, 0,
09:08:09.497 -> Received packet: 0, 0, 0, 0,
09:08:09.541 -> Received packet: 0, 0, 0, 0,
09:08:09.591 -> Received packet: 0, 0, 0, 0,
09:08:09.591 -> Received packet: 0, 0, 0, 0,
09:08:09.635 -> Received packet: 0, 0, 0, 0,
09:08:09.681 -> Received packet: 0, 0, 0, 0,
09:08:09.729 -> Received packet: 0, 0, 0, 0,
09:08:09.777 -> Received packet: 0, 0, 0, 0,
09:08:09.824 -> Received packet: 0, 0, 0, 0,
09:08:09.875 -> Received packet: 0, 0, 0, 0,
09:08:09.875 -> Received packet: 0, 0, 0, 0,
09:08:09.920 -> Received packet: 0, 0, 0, 0,
09:08:09.964 -> Received packet: 0, 0, 0, 0,
09:08:10.011 -> Received packet: 0, 0, 0, 0,
09:08:10.059 -> Received packet: 0, 0, 0, 0,
09:08:10.106 -> Received packet: 0, 0, 0, 0,
09:08:10.153 -> Received packet: 0, 0, 0, 0,
09:08:10.201 -> Received packet: 0, 0, 0, 0,
09:08:10.201 -> Received packet: 0, 0, 0, 0,
09:08:10.248 -> Received packet: 0, 0, 0, 0,
09:08:10.294 -> Received packet: 0, 0, 0, 0,
09:08:10.341 -> Received packet: 0, 0, 0, 0,
09:08:10.387 -> Received packet: 0, 0, 0, 0,
09:08:10.434 -> Received packet: 0, 0, 0, 0,
09:08:10.482 -> Received packet: 0, 0, 0, 0,
09:08:10.482 -> Received packet: 255, 0, 0, 0,
09:08:10.530 -> Received packet: 0, 0, 0, 0,
09:08:10.577 -> Received packet: 0, 0, 0, 0,
09:08:10.623 -> Received packet: 0, 0, 0, 0,
09:08:10.670 -> Received packet: 0, 0, 0, 0,
09:08:10.717 -> Received packet: 0, 0, 0, 0,
09:08:10.765 -> Received packet: 0, 0, 0, 0,
09:08:10.765 -> Received packet: 0, 0, 0, 0,
09:08:10.813 -> Received packet: 0, 0, 0, 0,
09:08:10.861 -> Received packet: 0, 0, 0, 0,
09:08:10.909 -> Received packet: 0, 0, 0, 0,
09:08:10.956 -> Received packet: 0, 0, 0, 0,
09:08:11.004 -> Received packet: 0, 0, 0, 0,
09:08:11.050 -> Received packet: 0, 0, 0, 0,
09:08:11.097 -> Received packet: 0, 0, 0, 0,
09:08:11.097 -> Received packet: 0, 0, 0, 0,
09:08:11.142 -> Received packet: 0, 0, 0, 0,
09:08:11.189 -> Received packet: 0, 0, 0, 0,
09:08:11.237 -> Received packet: 0, 0, 0, 0,
09:08:11.284 -> Received packet: 0, 0, 0, 0,
09:08:11.332 -> Received packet: 0, 0, 0, 0,
09:08:11.379 -> Received packet: 0, 0, 0, 0,
09:08:11.379 -> Received packet: 0, 0, 0, 0,
09:08:11.427 -> Received packet: 0, 0, 0, 0,
09:08:11.475 -> Received packet: 0, 0, 0, 0,
09:08:11.523 -> Received packet: 255, 0, 0, 0,
09:08:11.569 -> Received packet: 0, 0, 0, 0,
09:08:11.617 -> Received packet: 0, 0, 0, 0,
09:08:11.617 -> Received packet: 0, 0, 0, 0,
09:08:11.665 -> Received packet: 0, 0, 0, 0,
09:08:11.713 -> Received packet: 0, 0, 0, 0,

RDM Compatibility?

Hi! sorry for a couple things posted in a row, but i thought id put in a feature request. what would be great as something to make this library on par, if not better than its arduino counterparts would be the addition of RDM, while it definitely isnt necessary, it would make an already great library even better.

thanks,
Leon

cant change the start channel

Hi Jostein,
I've been using your library on a small project that I am working on. I works great when the starting channel is 1!
However, I don't seem to be able to move the Start_channel. For instance I would like the starting channel to be 20. When I change the starting channel like so
#define START_CHANNEL 20
#define NUM_CHANNELS 4
Its still working with starting channel as 1.

Is this not supposed to be the way that it works.

I suppose I can try to work around by inputing all 512 channels and then only looking for the channels that I want in the buffer.

Thanks
Michael Pierce

DmxInput improvement: Trigger the IRQ not based on number of received bytes but on the state of the line

As described in #20, some DMX senders might send less than 512 channels. Our current implementation waits for a specific amount of bytes (= DMA transfers) before the IRQ is triggered and the data is assumed to be ready.
Since DMX doesn't really define an "end of frame" condition, we could maybe assert one of the PIO_IRQs in the PIO program whenever a break has been detected. This way, the user of the library has the chance to use the data that is already in the buffer up to this point, even if it's not yet the number of bytes expected.

Open for discussion, I'm not sure yet if it makes sense?

Add possibility to use library with different DMA_IRQ

Thanks for grate library.
I hawed a issue to get work this library together with Adafruit_NeoPXL8, because both libraries using same DMA_IRQ.
so i edit DmxInput.cpp file in Pico-DMX:

dma_hw->ints0 = 1u << i;
dma_channel_set_irq0_enabled(_dma_chan, true);
irq_set_exclusive_handler(DMA_IRQ_0, dmxinput_dma_handler);
irq_set_enabled(DMA_IRQ_0, true);

to

dma_hw->ints1 = 1u << i;
dma_channel_set_irq1_enabled(_dma_chan, true);
irq_set_exclusive_handler(DMA_IRQ_1, dmxinput_dma_handler);
irq_set_enabled(DMA_IRQ_1, true);

Now I'm able to control animation patterns of 4000 WS2812 pixels at 35FPS (4 strips 1000LED length) by DMX signal in my project.

how to define START_CHANNEL during runtime?

Thank you for making this library.

I am starting the process of converting some of my Teensy code to work on a PICO.
My project reads a dip switch to determine the DMX start channel.

In you example you define volatile uint8_t buffer[DMXINPUT_BUFFER_SIZE(START_CHANNEL, NUM_CHANNELS)]; befor the code starts.
Is there a way to either change the buffer or set the buffer during void setup() ?

I guess more specifically i wonder if i can call dmxInput.begin(0, START_CHANNEL, NUM_CHANNELS); again after a dip switch change was noticed? Do i need to call dmxInput.end() first?

Thank you for your advice.

RDM ported to PICO

anyone ported RDM to this PICO yet ?
appreciate a quick reply back, as trying to determine PICO vs ESP32 for micro...

Help with c++

Hello! , Absolutely love your work! Thankyou for making this. I have this working well with the arduinoIDE but i need to get it working in VScode on the pico. Do you have any suggestions for me at all? I'm very new to Cmake. I am able to struggle through code with an example, but i am failing at adding your library to my current project.
Every time i incluide the DmcOutput.h or .cpp files it tells me i'm missing dma.h or pio.h

Thanks for any help you can provide.

Wiring / setup diagram

Hi, i know another user asked for this, but just some form of wiring or setup diagram for connecting the Pico to the rs485 modules would be really appreciated, even if its a drawing on the back of a napkin would go a long way to making the project more approachable.

Thanks

Edit: I have worked out the wiring for the project from the code, but if I had less technical experience with Arduino and raspberry pi (especially using max485 chips) I can see how it could be more complex to understand.

Sample Wiring

Could simple wiring diagrams be provided for those of us with a software background and not a hardware one? I would appreciate them as a guideline for your examples (input & 8 parallel output) and using them with MAX485 modules.

DmxInput: Cannot get value of channel 512

Using DmxInput::async_read I'm not able to get the value of channel 512, it's always 0.
Most probable cause after much debugging: The PIO pushes the channel values (byte-wise) from the input shift register to the state machine's RX FIFO. The DMA is configured to copy data from there to the user-provided buffer in 32-bit blocks. With the additional DMX start code, channel 511 ends such an 32-bit-block and the DMA copies over. Now when channel 512 is put into the RX FIFO, it won't trigger the DMA copy since the block will never be full. In this case, the DMA IRQ is not triggered as well right after the frame but only when the first bytes of the following frames came in (what I was and briefly mentioned in #13).

Until now I haven't found a proper solution to that problem. Modifying the DMA to work with 8-bit-transfers (instead of 32 bit) didn't work for me since I only saw every 4th channel in my buffer then.

The best I could do was:

  • In async_read, if num_channels is 512, request one transfer less than calculated so we get the IRQ fired after channel 511 came in
  • In the IRQ handler, check if num_channels is 512. If so, busy_wait 40ยตs so we are sure that channel 512 has reached the state machine. Then, make the state machine execute a PULL instruction (not sure why I needed to do so but otherwise the FIFO was empty?)
  • Read the FIFO manually and write the value to the buffer

However, that doesn't read channel 512 reliably, it just gives more or less random values :(

Ideas are welcome :) Or a proper solution with DMA transfer size of 8, maybe?

Question: Reason of a specific line calling the irq handler manually

dmxinput_dma_handler();
calls dmxinput_dma_handler(); directly and I don't really understand why.
I mean, of course, the write address of the DMA channel has not been set and this happens in the IRQ handler. But I don't understand why/how this works. At this line, the state machine is not yet running, it's only enabled in the line below. So, this DMA channel will NOT trigger any interrupts. That means that inside the IRQ handler, the DMA channel's bit in the INTS-register will not be set. So, when the IRQ handler is called in line 157, it will loop through all DMA channels (
for(int i=0;i<NUM_DMA_CHANS;i++) {
) and handle only those with their INTS-bits set (
if(active_inputs[i]!=nullptr && (dma_hw->ints0 & (1u<<i))) {
). So, in my eyes, calling the IRQ handler manually doesn't make sense.

In one of my branches of my fork, I've replaced the call by those four lines:
kripton@a5ec3a3#diff-42a9a8ae12afd6562fa620becac01e3c263d29fd4cbcd832a35abbca6cd76fa6R184
and it seems to work now.

Any explanation on why it works in the examples?

By the way, great library, thanks for making it! I'm currently coding a USB->DMX interface that outputs 16 universes and does Wireless DMX in addition (using optional nRF24 modules) (and has an integrated web server and speaks ArtNet and sACN): https://github.com/kripton/rp2040-dongle/tree/rewrite. I planned to do DMX input as well (so it could also do RDM if direction switching (DMX output to DMX input) doesn't take too long). However, I didn't have any real solution to do DMX inputs using the PIOs. I'm really happy I found your library and DMX input was working today for the very first time! ๐Ÿ˜ƒ
By the way, schematics for the modular interface is at https://github.com/kripton/rp2040-dongle/tree/kripton-hardware/hardware (Kicad) and some renderings are here: OpenLightingProject/rp2040-dmxsun#1 (reply in thread)

This library in rust

Hello there,

im tinkering around with my rpi-pico and want to set it up as a dmx receiver something.
Your library works great when in use with C/C++, but im also uinterested in the Rust lang.
I got my pico working in rust, and even write onto a WS2812b strip, but the dmx input part wont work.
I realy tried to work something out, that the garbage im receiving is interpreted as valid dmx, but i cant.
I tried to learn from the sourcecode of this lib, but im somewhat stuck..
The input im getting by setting the GPIO0 to uart with 250000 hz, eight data and two stop bits seems realy scuffed.
Are there any hints you could give me to succeed with this project? OC im willing to share any accomplishements i have made so far and will do in the future.
Thanks for the help!

Support for differential outputs (no RS-485 module necessary)

Fantastic project! Is it possible to add support for sending differential outputs in order to not have to purchase an RS-485 module? As far as I can tell using 2 pins to generate the differential signals (one the negation of the other) should work for DMX signals and the PIOs should be able to send them simultaneously. The RP2040 is using 3.3V logic and for the RS-485 receivers I've looked at they take ยฑ200 mV for an ON/OFF bit so it should work. I'd like to use something like the RP2040 XIAO for a tiny DMX controller (send only).

Waiting to send Dmx packets needed?

in the output example code, the lines:

while (dmx.busy()){
/* Do nothing while the DMX frame transmits */
}

are used. Are these needed?, as I'm trying to receive serial data over USB at the same time, and this while loop makes it multiple seconds until that data can be read and any acknowledgement sent back. you mention the library is not blocking, however this piece of code vastly slows down the execution of this program. I'm not a great programmer, so my serial read code isn't greatly optimised, but it functions well if this while loop is not in place. I can send my code if need be.

Thanks,

Confirmed working with pico w?

Hi, sorry to be posting another issue, has anybody had this library working with the pico w?
Iโ€™ve ran it on many standard picos, and had nearly no issues but today Iโ€™ve been trying to run it on a pico w and have had no luck. Iโ€™ve not got my oscilloscope with me at the moment, but I thought before I jump down a rabbit hole of debugging Iโ€™d ask if I was waisting my time.

DMX Input / RDM possible?

Hi Jostein, this is very cool! What are the possibilities of receiving DMX, and possibly bi-directional RDM?

Question: dma support and power saving tipps?

This is not about a problem with the library, but i wanted to dma to continually send my dmx universes and was wondering if it would be possible with this library.

As a short disclaimer: I did not look into anythin how dma works with the pico arduino libraries, so please don't hate me, if this is dumb, but I did not find a single mention of dma besides in the beginning of the README.

I want to build a dmx master that is working on battery power and that is mainly used for pretty static lighting, so i was hoping to put the RP2040 into a lower power state that still leaves dma and pio components running, but that saves at least a little energy.

If your answer is that dma is not supported and that there is no low power mode that suits my expectations (Still reading up on the rp2040, so i don't know yet), do you have ideas how I could save energy instead?
Not sending is not rally an option, since one of the fixtures randomly freaks out, if it does not receive a packet for some time. Also times until freakouts were not really predictable and vary form about 200ms to 3 hours

Implement checking of stop bits and channel counter

In my application I need to handle DMX input using sources that may send less than 512 channels. The issue is that I don't know how many channels are sent, a user can connect any source, so I need to dynamically support any number of channels (24-512) and accurately detect how many channels were sent.
I have been able to implement checking of the stop bits in PIO, which could be used to detect the end of a frame. But I'm struggling with how to communicate the number of received channels back to the C code. I've toyed with raising a IRQ at the end of the frame (which essentially is when the stop bits are missing = invalid). But I can't figure out how to capture that in the C code and use that as a channel counter, and somehow sync that with the DMA transfer.
I've also thought about using a 16-bit buffer for the DMX transfer, and set the high byte to indicate when the stop bits are wrong, so I could then count the number of channels in the C code. But not only would it be a waste of memory, it would also be a fairly breaking change to this library that I would like to avoid.
Would it be possible to have a counter in PIO and then push that at an invalid stop bit somehow? Is there another way? I'm fairly new to PIO so I may be missing something obvious.

DmxOutput.h void write not const

Is there a reason write does not take a const uint8_t *universe? I'm just delving a little deeper into C++ itself and from my latest understanding, this would make more sense. I could be wrong, so wondering your thoughts.

Sincerely,
Marcus

DMX for PICO without PIO

RP2040 Pico using Arduino 2.0 IDE on Win11
I like the way your library makes DMX easy!
I've used all the PIOs for my 16 PWM LED lights. Is there a way for this lib to work with UART instead of PIO?
Also I'm looking for channel 400 - 512 for this particular app. Has issue re Start Chanel been fixed yet?

DmxInput: start_channel is not honored

DmxInput::begin has a parameter called start_channel. It's meant to ignore the first X channels and only read input values from that value on.
However, the value is not actually used as expected. It is however (incorrectly) used to calculate the buffer size here:

DMXINPUT_BUFFER_SIZE(_start_channel, _num_channels)/4, // transfer count,

I wouldn't know a proper solution to tell the PIO state machine OR the DMA to ignore the first X bytes. Of course, we could let the DMX write into an intermediary buffer and then copy only the requested channels to the buffer provided by the library user. But in the worst case, that would mean copying 511 byte in an IRQ handler. Not what I would call ideal.

As such I would propose to remove the start_channel functionality

Possible RDM support?

Hi, I am back. With your help I managed to build a pretty good and pretty reliable master. Ist is working great, but recently I bought some devices that are meant to be installed permanently and that by chance support rdm - just in case somebody messes up the settings and does not test everything before installing them....... ๐Ÿ˜…๐Ÿ˜…๐Ÿ˜…

Is there a chance, that this library has protocol support for rdm? Or if not, is there anything that I could use to hack rdm support into my master?

Dmx Input problems.

This is almost definitely me doing something wrong, but I've had the issue with a couple different RS485 interfaces, where I can easily output DMX, but it just won't receive it. At first I was using a random china 3.3v interface, but I recently switched to an M5Stamp rs485 module, with a 5v booster on the power input, however it just won't receive data. I haven't really got any ideas. The logic level of the ttl side for the module I'm using is 3.3v according to the datasheet, altho it only specifies the input logic.

Bug in highlight_all

In the highlight_all example, line 36:

dmx.write(universe, UNIVERSE_LENGTH);

It should be UNIVERSE_LENGTH+1 unless I am confused. It seems to be correct in the other output example.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.