Repository: jostlowe/Pico-DMX
Branch: main
Commit: c8ecde5a3267
Files: 21
Total size: 41.6 KB
Directory structure:
gitextract_7q0zf7g5/
├── .github/
│ └── workflows/
│ └── Arduino-lint.yml
├── .gitignore
├── LICENSE
├── README.md
├── examples/
│ ├── RGB_input/
│ │ └── RGB_input.ino
│ ├── RGB_input_async/
│ │ └── RGB_input_async.ino
│ ├── highlight_all/
│ │ └── highlight_all.ino
│ └── parallel_output/
│ └── parallel_output.ino
├── extras/
│ ├── DmxInput.pio
│ ├── DmxInputInverted.pio
│ ├── DmxOutput.pio
│ └── pioasm
├── interfaceLibForPicoSDK.cmake
├── library.properties
└── src/
├── DmxInput.cpp
├── DmxInput.h
├── DmxInput.pio.h
├── DmxInputInverted.pio.h
├── DmxOutput.cpp
├── DmxOutput.h
└── DmxOutput.pio.h
================================================
FILE CONTENTS
================================================
================================================
FILE: .github/workflows/Arduino-lint.yml
================================================
# This is a basic workflow to help you get started with Actions
name: Arduino Linter
# Controls when the action will run.
on:
# Triggers the workflow on push or pull request events but only for the main branch
push:
branches: [ main ]
pull_request:
branches: [ main ]
# Allows you to run this workflow manually from the Actions tab
workflow_dispatch:
# A workflow run is made up of one or more jobs that can run sequentially or in parallel
jobs:
# This workflow contains a single job called "build"
arduino-lint:
# The type of runner that the job will run on
runs-on: ubuntu-latest
# Steps represent a sequence of tasks that will be executed as part of the job
steps:
# Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
- uses: actions/checkout@v2
- name: arduino/arduino-lint-action
uses: arduino/arduino-lint-action@v1.0.0
with:
library-manager: update
================================================
FILE: .gitignore
================================================
# Prerequisites
*.d
# Object files
*.o
*.ko
*.obj
*.elf
# Linker output
*.ilk
*.map
*.exp
# Precompiled Headers
*.gch
*.pch
# Libraries
*.lib
*.a
*.la
*.lo
# Shared objects (inc. Windows DLLs)
*.dll
*.so
*.so.*
*.dylib
# Executables
*.exe
*.out
*.app
*.i*86
*.x86_64
*.hex
# Debug files
*.dSYM/
*.su
*.idb
*.pdb
# Kernel Module Compile Results
*.mod*
*.cmd
.tmp_versions/
modules.order
Module.symvers
Mkfile.old
dkms.conf
================================================
FILE: LICENSE
================================================
BSD 3-Clause License
Copyright (c) 2021, Jostein Løwer
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
================================================
FILE: README.md
================================================
# Pico-DMX

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](https://www.arduino.cc/en/guide/libraries) 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.
```C++
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
```C++
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
```C++
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.
```C++
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.
```C++
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.
```C++
while(myDmxOutput.busy()) {
// Patiently wait, or do other computing stuff
}
```
See the [examples](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)
```C++
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:
```C++
uint8_t buffer[num_channels];
```
Use the `.read(...)` method to read the 3 channels for our RGB fixture into our buffer.
```C++
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:
```C++
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`
================================================
FILE: examples/RGB_input/RGB_input.ino
================================================
/*
* Copyright (c) 2021 Jostein Løwer
*
* SPDX-License-Identifier: BSD-3-Clause
*
* Description:
* Starts a DMX Input on GPIO pin 0 and read channel 1-3 repeatedly
*/
#include <Arduino.h>
#include "DmxInput.h"
DmxInput dmxInput;
#define START_CHANNEL 1
#define NUM_CHANNELS 3
volatile uint8_t buffer[DMXINPUT_BUFFER_SIZE(START_CHANNEL, NUM_CHANNELS)];
void setup()
{
// Setup our DMX Input to read on GPIO 0, from channel 1 to 3
dmxInput.begin(0, START_CHANNEL, NUM_CHANNELS);
// Setup the onboard LED so that we can blink when we receives packets
pinMode(LED_BUILTIN, OUTPUT);
}
void loop()
{
// Wait for next DMX packet
dmxInput.read(buffer);
// Print the DMX channels
Serial.print("Received packet: ");
for (uint i = 0; i < sizeof(buffer); i++)
{
Serial.print(buffer[i]);
Serial.print(", ");
}
Serial.println("");
// Blink the LED to indicate that a packet was received
digitalWrite(LED_BUILTIN, HIGH);
delay(10);
digitalWrite(LED_BUILTIN, LOW);
}
================================================
FILE: examples/RGB_input_async/RGB_input_async.ino
================================================
/*
* Copyright (c) 2021 Jostein Løwer
*
* SPDX-License-Identifier: BSD-3-Clause
*
* Description:
* Starts a DMX Input on GPIO pin 0 and read channel 1-3 repeatedly
*/
#include <Arduino.h>
#include "DmxInput.h"
DmxInput dmxInput;
#define START_CHANNEL 1
#define NUM_CHANNELS 3
volatile uint8_t buffer[DMXINPUT_BUFFER_SIZE(START_CHANNEL, NUM_CHANNELS)];
void setup()
{
// Setup our DMX Input to read on GPIO 0, from channel 1 to 3
dmxInput.begin(0, START_CHANNEL, NUM_CHANNELS);
dmxInput.read_async(buffer);
// Setup the onboard LED so that we can blink when we receives packets
pinMode(LED_BUILTIN, OUTPUT);
}
void loop()
{
delay(30);
if(millis() > 100+dmxInput.latest_packet_timestamp()) {
Serial.println("no data!");
return;
}
// Print the DMX channels
Serial.print("Received packet: ");
for (uint i = 0; i < sizeof(buffer); i++)
{
Serial.print(buffer[i]);
Serial.print(", ");
}
Serial.println("");
// Blink the LED to indicate that a packet was received
digitalWrite(LED_BUILTIN, HIGH);
delay(10);
digitalWrite(LED_BUILTIN, LOW);
}
================================================
FILE: examples/highlight_all/highlight_all.ino
================================================
/*
* Copyright (c) 2021 Jostein Løwer
*
* SPDX-License-Identifier: BSD-3-Clause
*
* Description:
* Starts a DMX Output on GPIO pin 0 and sets all channels to max (255)
*/
#include <Arduino.h>
#include <DmxOutput.h>
// Declare an instance of the DMX Output
DmxOutput dmx;
// Create a universe that we want to send.
// The universe must be maximum 512 bytes + 1 byte of start code
#define UNIVERSE_LENGTH 512
uint8_t universe[UNIVERSE_LENGTH + 1];
void setup()
{
// Start the DMX Output on GPIO-pin 0
dmx.begin(0);
// Set all channels in the universe to the max allowed value (512)
for (int i = 1; i < UNIVERSE_LENGTH + 1; i++)
{
universe[i] = 255;
}
}
void loop()
{
// Send out universe on GPIO-pin 1
dmx.write(universe, UNIVERSE_LENGTH);
while (dmx.busy())
{
/* Do nothing while the DMX frame transmits */
}
// delay a millisecond for stability (Not strictly necessary)
delay(1);
}
================================================
FILE: examples/parallel_output/parallel_output.ino
================================================
/*
* Copyright (c) 2021 Jostein Løwer
*
* SPDX-License-Identifier: BSD-3-Clause
*
* Description:
* Starts a 8 DMX Output on GPIO pins 0-7
*/
#include <Arduino.h>
#include <DmxOutput.h>
// Declare 8 instances of the DMX output
DmxOutput dmxOutputs[8];
// Create a universe that we want to send in parallel on all 8 outputs.
// The universe must be maximum 512 bytes + 1 byte for the start
#define UNIVERSE_LENGTH 512
uint8_t universe[UNIVERSE_LENGTH + 1];
void setup()
{
// Start the DMX outputs on GPIO-pins 0-7.
// Only 4 outputs can run on a single PIO instance, so
// the 8 outputs are divided onto the two PIO instances
// pio0 and pio1
for (int i = 0; i < 4; i++)
{
dmxOutputs[i].begin(i, pio0);
}
for (int i = 4; i < 8; i++)
{
dmxOutputs[i].begin(i, pio1);
}
}
void loop()
{
// Send out universe on all 8 DMX outputs
for (int i = 0; i < 8; i++)
{
dmxOutputs[i].write(universe, UNIVERSE_LENGTH + 1);
}
for (int i = 0; i < 8; i++)
{
while (dmxOutputs[i].busy())
{
// Wait patiently until all outputs are done transmitting
}
}
// delay a millisecond for stability (Not strictly necessary)
delay(1);
}
================================================
FILE: extras/DmxInput.pio
================================================
; Author: Jostein Løwer, github: jostlowe
; SPDX-License-Identifier: BSD-3-Clause
;
; PIO program for inputting the DMX lighting protocol.
; (Almost) compliant with ANSI E1.11-2008 (R2018)
; The program assumes a PIO clock frequency of exactly 1MHz
.program DmxInput
.define dmx_bit 4 ; As DMX has a baudrate of 250.000kBaud, a single bit is 4us
break_reset:
set x, 29 ; Setup a counter to count the iterations on break_loop
break_loop: ; Break loop lasts for 8us. The entire break must be minimum 30*3us = 90us
jmp pin break_reset ; Go back to start if pin goes high during the break
jmp x-- break_loop [1] ; Decrease the counter and go back to break loop if x>0 so that the break is not done
wait 1 pin 0 ; Stall until line goes high for the Mark-After-Break (MAB)
.wrap_target
wait 0 pin 0 ; Stall until start bit is asserted
set x, 7 [dmx_bit] ; Preload bit counter, then delay until halfway through
bitloop:
in pins, 1 ; Shift data bit into ISR
jmp x-- bitloop [dmx_bit-2] ; Loop 8 times, each loop iteration is 4us
wait 1 pin 0 ; Wait for pin to go high for stop bits
in NULL, 24 ; Push 24 more bits into the ISR so that our one byte is at the position where the DMA expects it
push ; Should probably do error checking on the stop bits some time in the future....
.wrap
================================================
FILE: extras/DmxInputInverted.pio
================================================
; Author: Jostein Løwer, github: jostlowe; modified by @functionpointer
; SPDX-License-Identifier: BSD-3-Clause
;
; PIO program for inputting the DMX lighting protocol.
; (Almost) compliant with ANSI E1.11-2008 (R2018)
; The program assumes a PIO clock frequency of exactly 1MHz
.program DmxInputInverted
.define dmx_bit 4 ; As DMX has a baudrate of 250.000kBaud, a single bit is 4us
break_reset:
set x, 29 ; Setup a counter to count the iterations on break_loop
break_in_progress:
jmp pin break_continue
jmp break_reset ; break should be high the entire time. if it goes low, start over
break_continue:
jmp x-- break_in_progress
wait 0 pin 0 ; wait until MAB started
.wrap_target
wait 1 pin 0 ; Stall until start bit is asserted, i.e. MAB is over
set x, 7 [dmx_bit] ; Preload bit counter, then delay until halfway through
bitloop:
in pins, 1 ; Shift data bit into ISR
jmp x-- bitloop [dmx_bit-2] ; Loop 8 times, each loop iteration is 4us
wait 0 pin 0 ; Wait for pin to go high for stop bits
mov isr, ~ isr ; invert result before pushing
in NULL, 24 ; Push 24 more bits into the ISR so that our one byte is at the position where the DMA expects it
push ; Should probably do error checking on the stop bits some time in the future....
.wrap
================================================
FILE: extras/DmxOutput.pio
================================================
; Author: Jostein Løwer, github: jostlowe
; SPDX-License-Identifier: BSD-3-Clause
;
; PIO program for outputting the DMX lighting protocol.
; Compliant with ANSI E1.11-2008 (R2018)
; The program assumes a PIO clock frequency of exactly 1MHz
.program DmxOutput
.side_set 1 opt
; Assert break condition
set x, 21 side 0 ; Preload bit counter, assert break condition for 176us
breakloop: ; This loop will run 22 times
jmp x-- breakloop [7] ; Each loop iteration is 8 cycles.
; Assert start condition
nop [7] side 1 ; Assert MAB. 8 cycles nop and 8 cycles stop-bits = 16us
; Send data frame
.wrap_target
pull side 1 [7] ; Assert 2 stop bits, or stall with line in idle state
set x, 7 side 0 [3] ; Preload bit counter, assert start bit for 4 clocks
bitloop: ; This loop will run 8 times (8n1 UART)
out pins, 1 ; Shift 1 bit from OSR to the first OUT pin
jmp x-- bitloop [2] ; Each loop iteration is 4 cycles.
.wrap
================================================
FILE: interfaceLibForPicoSDK.cmake
================================================
## Include this file if you want to use the Pico-DMX library
## in YOUR (Pico-C-SDK-based) project.
cmake_minimum_required(VERSION 3.12)
# Define the Pico-DMX library
add_library(picodmx INTERFACE)
target_sources(picodmx INTERFACE
${CMAKE_CURRENT_LIST_DIR}/src/DmxInput.cpp
${CMAKE_CURRENT_LIST_DIR}/src/DmxOutput.cpp
)
pico_generate_pio_header(picodmx
${CMAKE_CURRENT_LIST_DIR}/extras/DmxInput.pio
)
pico_generate_pio_header(picodmx
${CMAKE_CURRENT_LIST_DIR}/extras/DmxOutput.pio
)
target_include_directories(picodmx INTERFACE
${CMAKE_CURRENT_LIST_DIR}/src
)
================================================
FILE: library.properties
================================================
name=Pico-DMX
version=3.1.0
author=Jostein Løwer <jostlowe@gmail.com>
maintainer=Jostein Løwer <jostlowe@gmail.com>
sentence=DMX protocol library for the RaspberryPi Pico
paragraph=A library for inputting and outputting the DMX protocol for entertainment lighting on the RaspberryPi Pico using the Programmable IO (PIO) feature of the Pico
category=Signal Input/Output
url=https://github.com/jostlowe/Pico-DMX
architectures=rp2040, mbed_rp2040, mbed_nano
================================================
FILE: src/DmxInput.cpp
================================================
/*
* Copyright (c) 2021 Jostein Løwer
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include "DmxInput.h"
#include "DmxInput.pio.h"
#include "DmxInputInverted.pio.h"
#if defined(ARDUINO_ARCH_MBED)
#include <clocks.h>
#include <irq.h>
#include <Arduino.h> // REMOVE ME
#else
#include "pico/time.h"
#include "hardware/clocks.h"
#include "hardware/irq.h"
#endif
bool prgm_loaded[] = {false,false};
volatile uint prgm_offsets[] = {0,0};
/*
This array tells the interrupt handler which instance has interrupted.
The interrupt handler has only the ints0 register to go on, so this array needs as many spots as there are DMA channels.
*/
#define NUM_DMA_CHANS 12
volatile DmxInput *active_inputs[NUM_DMA_CHANS] = {nullptr};
DmxInput::return_code DmxInput::begin(uint pin, uint start_channel, uint num_channels, PIO pio, bool inverted)
{
uint pio_ind = pio_get_index(pio);
if(!prgm_loaded[pio_ind]) {
/*
Attempt to load the DMX PIO assembly program into the PIO program memory
*/
if(!inverted) {
if (!pio_can_add_program(pio, &DmxInput_program))
{
return ERR_INSUFFICIENT_PRGM_MEM;
}
prgm_offsets[pio_ind] = pio_add_program(pio, &DmxInput_program);
} else {
if (!pio_can_add_program(pio, &DmxInputInverted_program))
{
return ERR_INSUFFICIENT_PRGM_MEM;
}
prgm_offsets[pio_ind] = pio_add_program(pio, &DmxInputInverted_program);
}
prgm_loaded[pio_ind] = true;
}
/*
Attempt to claim an unused State Machine into the PIO program memory
*/
int sm = pio_claim_unused_sm(pio, false);
if (sm == -1)
{
return ERR_NO_SM_AVAILABLE;
}
// Set this pin's GPIO function (connect PIO to the pad)
pio_sm_set_consecutive_pindirs(pio, sm, pin, 1, false);
pio_gpio_init(pio, pin);
gpio_pull_up(pin);
// Generate the default PIO state machine config provided by pioasm
pio_sm_config sm_conf;
if(!inverted) {
sm_conf = DmxInput_program_get_default_config(prgm_offsets[pio_ind]);
} else {
sm_conf = DmxInputInverted_program_get_default_config(prgm_offsets[pio_ind]);
}
sm_config_set_in_pins(&sm_conf, pin); // for WAIT, IN
sm_config_set_jmp_pin(&sm_conf, pin); // for JMP
// Setup the side-set pins for the PIO state machine
// Shift to right, autopush disabled
sm_config_set_in_shift(&sm_conf, true, false, 8);
// Deeper FIFO as we're not doing any TX
sm_config_set_fifo_join(&sm_conf, PIO_FIFO_JOIN_RX);
// Setup the clock divider to run the state machine at exactly 1MHz
uint clk_div = clock_get_hz(clk_sys) / DMX_SM_FREQ;
sm_config_set_clkdiv(&sm_conf, clk_div);
// Load our configuration, jump to the start of the program and run the State Machine
pio_sm_init(pio, sm, prgm_offsets[pio_ind], &sm_conf);
//sm_config_set_in_shift(&c, true, false, n_bits)
//pio_sm_put_blocking(pio, sm, (start_channel + num_channels) - 1);
_pio = pio;
_sm = sm;
_pin = pin;
_start_channel = start_channel;
_num_channels = num_channels;
_buf = nullptr;
_cb = nullptr;
_dma_chan = dma_claim_unused_channel(true);
if (active_inputs[_dma_chan] != nullptr) {
return ERR_NO_SM_AVAILABLE;
}
active_inputs[_dma_chan] = this;
return SUCCESS;
}
void DmxInput::read(volatile uint8_t *buffer)
{
if(_buf==nullptr) {
read_async(buffer);
}
unsigned long start = _last_packet_timestamp;
while(_last_packet_timestamp == start) {
tight_loop_contents();
}
}
void dmxinput_dma_handler() {
for(int i=0;i<NUM_DMA_CHANS;i++) {
if(active_inputs[i]!=nullptr && (dma_hw->ints0 & (1u<<i))) {
dma_hw->ints0 = 1u << i;
volatile DmxInput *instance = active_inputs[i];
dma_channel_set_write_addr(i, instance->_buf, true);
pio_sm_exec(instance->_pio, instance->_sm, pio_encode_jmp(prgm_offsets[pio_get_index(instance->_pio)]));
pio_sm_clear_fifos(instance->_pio, instance->_sm);
#ifdef ARDUINO
instance->_last_packet_timestamp = millis();
#else
instance->_last_packet_timestamp = to_ms_since_boot(get_absolute_time());
#endif
// Trigger the callback if we have one
if (instance->_cb != nullptr) {
(*(instance->_cb))((DmxInput*)instance);
}
}
}
}
void DmxInput::read_async(volatile uint8_t *buffer, void (*inputUpdatedCallback)(DmxInput*)) {
_buf = buffer;
if (inputUpdatedCallback!=nullptr) {
_cb = inputUpdatedCallback;
}
pio_sm_set_enabled(_pio, _sm, false);
// Reset the PIO state machine to a consistent state. Clear the buffers and registers
pio_sm_restart(_pio, _sm);
//setup dma
dma_channel_config cfg = dma_channel_get_default_config(_dma_chan);
// Reading from constant address, writing to incrementing byte addresses
channel_config_set_transfer_data_size(&cfg, DMA_SIZE_8);
channel_config_set_read_increment(&cfg, false);
channel_config_set_write_increment(&cfg, true);
// Pace transfers based on DREQ_PIO0_RX0 (or whichever pio and sm we are using)
channel_config_set_dreq(&cfg, pio_get_dreq(_pio, _sm, false));
//channel_config_set_ring(&cfg, true, 5);
dma_channel_configure(
_dma_chan,
&cfg,
NULL, // dst
&_pio->rxf[_sm], // src
DMXINPUT_BUFFER_SIZE(_start_channel, _num_channels), // transfer count,
false
);
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);
//aaand start!
dma_channel_set_write_addr(_dma_chan, buffer, true);
pio_sm_exec(_pio, _sm, pio_encode_jmp(prgm_offsets[pio_get_index(_pio)]));
pio_sm_clear_fifos(_pio, _sm);
#ifdef ARDUINO
_last_packet_timestamp = millis();
#else
_last_packet_timestamp = to_ms_since_boot(get_absolute_time());
#endif
pio_sm_set_enabled(_pio, _sm, true);
}
unsigned long DmxInput::latest_packet_timestamp() {
return _last_packet_timestamp;
}
uint DmxInput::pin() {
return _pin;
}
void DmxInput::end()
{
// Stop the PIO state machine
pio_sm_set_enabled(_pio, _sm, false);
// Remove the PIO DMX program from the PIO program memory
uint pio_id = pio_get_index(_pio);
bool inuse = false;
for(uint i=0;i<NUM_DMA_CHANS;i++) {
if(i==_dma_chan) {
continue;
}
if(pio_id == pio_get_index(active_inputs[i]->_pio)) {
inuse = true;
break;
}
}
if(!inuse) {
prgm_loaded[pio_id] = false;
pio_remove_program(_pio, &DmxInput_program, prgm_offsets[pio_id]);
prgm_offsets[pio_id]=0;
}
// Unclaim the sm
pio_sm_unclaim(_pio, _sm);
dma_channel_unclaim(_dma_chan);
active_inputs[_dma_chan] = nullptr;
_buf = nullptr;
}
================================================
FILE: src/DmxInput.h
================================================
/*
* Copyright (c) 2021 Jostein Løwer
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#ifndef DMX_INPUT_H
#define DMX_INPUT_H
#if defined(ARDUINO_ARCH_MBED)
#include <dma.h>
#include <pio.h>
#else
#ifdef ARDUINO
#include <Arduino.h>
#endif
#include "hardware/dma.h"
#include "hardware/pio.h"
#endif
#define DMX_UNIVERSE_SIZE 512
#define DMX_SM_FREQ 1000000
#define DMXINPUT_BUFFER_SIZE(start_channel, num_channels) (num_channels+1)
class DmxInput
{
uint _pin;
int32_t _start_channel;
int32_t _num_channels;
public:
/*
private properties that are declared public so the interrupt handler has access
*/
volatile uint8_t *_buf;
volatile PIO _pio;
volatile uint _sm;
volatile uint _dma_chan;
volatile unsigned long _last_packet_timestamp=0;
void (*_cb)(DmxInput*);
/*
All different return codes for the DMX class. Only the SUCCESS
Return code guarantees that the DMX output instance was properly configured
and is ready to run
*/
enum return_code
{
SUCCESS = 0,
// There were no available state machines left in the
// pio instance.
ERR_NO_SM_AVAILABLE = -1,
// There is not enough program memory left in the PIO to fit
// The DMX PIO program
ERR_INSUFFICIENT_PRGM_MEM = -2
};
/*
Starts a new DMX input instance.
Param: pin
Any valid GPIO pin on the Pico
Param: pio
defaults to pio0. pio0 can run up to 3
DMX input instances. If you really need more, you can
run 3 more on pio1
*/
return_code begin(uint pin, uint start_channel, uint num_channels, PIO pio = pio0, bool inverted = false);
/*
Read the selected channels from .begin(...) into a buffer.
Method call blocks until the selected channels have been received
Param: buffer
A pointer to the location where the channels should be received
The buffer should have a max length of
513 bytes (1 byte start code + 512 bytes frame). For ordinary
DMX data frames, the start code should be 0x00.
*/
void read(volatile uint8_t *buffer);
/*
Start async read process. This should only be called once.
From then on, the buffer will always contain the latest DMX data.
If you want to be notified whenever a new DMX frame has been received,
provide a callback function that will be called without arguments.
*/
void read_async(volatile uint8_t *buffer, void (*inputUpdatedCallback)(DmxInput* instance) = nullptr);
/*
Get the timestamp (like millis()) from the moment the latest dmx packet was received.
May be used to detect if the dmx signal has stopped coming in.
*/
unsigned long latest_packet_timestamp();
/*
Get the pin this instance is listening on
*/
uint pin();
/*
De-inits the DMX input instance. Releases PIO resources.
The instance can safely be destroyed after this method is called
*/
void end();
};
#endif
================================================
FILE: src/DmxInput.pio.h
================================================
// -------------------------------------------------- //
// This file is autogenerated by pioasm; do not edit! //
// -------------------------------------------------- //
#pragma once
#if !PICO_NO_HARDWARE
#include "hardware/pio.h"
#endif
// -------- //
// DmxInput //
// -------- //
#define DmxInput_wrap_target 4
#define DmxInput_wrap 10
static const uint16_t DmxInput_program_instructions[] = {
0xe03d, // 0: set x, 29
0x00c0, // 1: jmp pin, 0
0x0141, // 2: jmp x--, 1 [1]
0x20a0, // 3: wait 1 pin, 0
// .wrap_target
0x2020, // 4: wait 0 pin, 0
0xe427, // 5: set x, 7 [4]
0x4001, // 6: in pins, 1
0x0246, // 7: jmp x--, 6 [2]
0x20a0, // 8: wait 1 pin, 0
0x4078, // 9: in null, 24
0x8020, // 10: push block
// .wrap
};
#if !PICO_NO_HARDWARE
static const struct pio_program DmxInput_program = {
.instructions = DmxInput_program_instructions,
.length = 11,
.origin = -1,
};
static inline pio_sm_config DmxInput_program_get_default_config(uint offset) {
pio_sm_config c = pio_get_default_sm_config();
sm_config_set_wrap(&c, offset + DmxInput_wrap_target, offset + DmxInput_wrap);
return c;
}
#endif
================================================
FILE: src/DmxInputInverted.pio.h
================================================
// -------------------------------------------------- //
// This file is autogenerated by pioasm; do not edit! //
// -------------------------------------------------- //
#if !PICO_NO_HARDWARE
#include "hardware/pio.h"
#endif
// ---------------- //
// DmxInputInverted //
// ---------------- //
#define DmxInputInverted_wrap_target 5
#define DmxInputInverted_wrap 12
static const uint16_t DmxInputInverted_program_instructions[] = {
0xe03d, // 0: set x, 29
0x00c3, // 1: jmp pin, 3
0x0000, // 2: jmp 0
0x0041, // 3: jmp x--, 1
0x2020, // 4: wait 0 pin, 0
// .wrap_target
0x20a0, // 5: wait 1 pin, 0
0xe427, // 6: set x, 7 [4]
0x4001, // 7: in pins, 1
0x0247, // 8: jmp x--, 7 [2]
0x2020, // 9: wait 0 pin, 0
0xa0ce, // 10: mov isr, !isr
0x4078, // 11: in null, 24
0x8020, // 12: push block
// .wrap
};
#if !PICO_NO_HARDWARE
static const struct pio_program DmxInputInverted_program = {
.instructions = DmxInputInverted_program_instructions,
.length = 13,
.origin = -1,
};
static inline pio_sm_config DmxInputInverted_program_get_default_config(uint offset) {
pio_sm_config c = pio_get_default_sm_config();
sm_config_set_wrap(&c, offset + DmxInputInverted_wrap_target, offset + DmxInputInverted_wrap);
return c;
}
#endif
================================================
FILE: src/DmxOutput.cpp
================================================
/*
* Copyright (c) 2021 Jostein Løwer
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include "DmxOutput.h"
#include "DmxOutput.pio.h"
#if defined(ARDUINO_ARCH_MBED)
#include <clocks.h>
#include <irq.h>
#else
#include "hardware/clocks.h"
#include "hardware/irq.h"
#endif
DmxOutput::return_code DmxOutput::begin(uint pin, PIO pio)
{
/*
Attempt to load the DMX PIO assembly program
into the PIO program memory
*/
if (!pio_can_add_program(pio, &DmxOutput_program))
{
return ERR_INSUFFICIENT_PRGM_MEM;
}
uint prgm_offset = pio_add_program(pio, &DmxOutput_program);
/*
Attempt to claim an unused State Machine
into the PIO program memory
*/
int sm = pio_claim_unused_sm(pio, false);
if (sm == -1)
{
return ERR_NO_SM_AVAILABLE;
}
// Set this pin's GPIO function (connect PIO to the pad)
pio_sm_set_pins_with_mask(pio, sm, 1u << pin, 1u << pin);
pio_sm_set_pindirs_with_mask(pio, sm, 1u << pin, 1u << pin);
pio_gpio_init(pio, pin);
// Generate the default PIO state machine config provided by pioasm
pio_sm_config sm_conf = DmxOutput_program_get_default_config(prgm_offset);
// Setup the side-set pins for the PIO state machine
sm_config_set_out_pins(&sm_conf, pin, 1);
sm_config_set_sideset_pins(&sm_conf, pin);
// Setup the clock divider to run the state machine at exactly 1MHz
uint clk_div = clock_get_hz(clk_sys) / DMX_SM_FREQ;
sm_config_set_clkdiv(&sm_conf, clk_div);
// Load our configuration, jump to the start of the program and run the State Machine
pio_sm_init(pio, sm, prgm_offset, &sm_conf);
pio_sm_set_enabled(pio, sm, true);
// Claim an unused DMA channel.
// The channel is kept througout the lifetime of the DMX source
int dma = dma_claim_unused_channel(false);
if (dma == -1)
return ERR_NO_DMA_AVAILABLE;
// Get the default DMA config for our claimed channel
dma_channel_config dma_conf = dma_channel_get_default_config(dma);
// Set the DMA to move one byte per DREQ signal
channel_config_set_transfer_data_size(&dma_conf, DMA_SIZE_8);
// Setup the DREQ so that the DMA only moves data when there
// is available room in the TXF buffer of our PIO state machine
channel_config_set_dreq(&dma_conf, pio_get_dreq(pio, sm, true));
// Setup the DMA to write to the TXF buffer of the PIO state machine
dma_channel_set_write_addr(dma, &pio->txf[sm], false);
// Apply the config
dma_channel_set_config(dma, &dma_conf, false);
// Set member values of C++ class
_prgm_offset = prgm_offset;
_pio = pio;
_sm = sm;
_pin = pin;
_dma = dma;
return SUCCESS;
}
void DmxOutput::write(uint8_t *universe, uint length)
{
// Temporarily disable the PIO state machine
pio_sm_set_enabled(_pio, _sm, false);
// Reset the PIO state machine to a consistent state. Clear the buffers and registers
pio_sm_restart(_pio, _sm);
// Start the DMX PIO program from the beginning
pio_sm_exec(_pio, _sm, pio_encode_jmp(_prgm_offset));
// Restart the PIO state machinge
pio_sm_set_enabled(_pio, _sm, true);
// Start the DMA transfer
dma_channel_transfer_from_buffer_now(_dma, universe, length);
}
bool DmxOutput::busy()
{
if (dma_channel_is_busy(_dma))
return true;
return !pio_sm_is_tx_fifo_empty(_pio, _sm);
}
/*
void Dmx::await()
{
dma_channel_wait_for_finish_blocking(_dma);
while (!pio_sm_is_tx_fifo_empty(_pio, _sm))
{
}
}
*/
void DmxOutput::end()
{
// Stop the PIO state machine
pio_sm_set_enabled(_pio, _sm, false);
// Remove the PIO DMX program from the PIO program memory
pio_remove_program(_pio, &DmxOutput_program, _prgm_offset);
// Unclaim the DMA channel
dma_channel_unclaim(_dma);
// Unclaim the sm
pio_sm_unclaim(_pio, _sm);
}
================================================
FILE: src/DmxOutput.h
================================================
/*
* Copyright (c) 2021 Jostein Løwer
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#ifndef DMX_OUTPUT_H
#define DMX_OUTPUT_H
#if defined(ARDUINO_ARCH_MBED)
#include <dma.h>
#include <pio.h>
#else
#ifdef ARDUINO
#include <Arduino.h>
#endif
#include "hardware/dma.h"
#include "hardware/pio.h"
#endif
#define DMX_UNIVERSE_SIZE 512
#define DMX_SM_FREQ 1000000
class DmxOutput
{
uint _prgm_offset;
uint _pin;
uint _sm;
PIO _pio;
uint _dma;
public:
/*
All different return codes for the DMX class. Only the SUCCESS
Return code guarantees that the DMX transmitter instance was properly configured
and is ready to run
*/
enum return_code
{
SUCCESS = 0,
// There were no available state machines left in the
// pio instance.
ERR_NO_SM_AVAILABLE = -1,
// There is not enough program memory left in the PIO to fit
// The DMX PIO program
ERR_INSUFFICIENT_PRGM_MEM = -2,
// There are no available DMA channels to handle
// the transfer of DMX data to the PIO
ERR_NO_DMA_AVAILABLE = -3
};
/*
Starts a new DMX transmitter instance.
Param: pin
Any valid GPIO pin on the RPi Pico
Param: pio
defaults to pio0. pio0 can run up to 4
DMX instances. If you really need more, you can
run 4 more on pio1
*/
return_code begin(uint pin, PIO pio = pio0);
/*
write a DMX universe to the DMX transmitter instance.
Returns imediatly after function call and does not block.
The status of the DMX transmission can be checked using
busy() or you can block until the transmission is done
using await()
Param: universe
A pointer to the location of the DMX frame that should
be transmitted. the universe should have a max length of
513 bytes (1 byte start code + 512 bytes frame). For ordinary
DMX data frames, the start code should be 0x00.
Param: length
The number of bytes from the DMX frame that should be
transmitted
*/
void write(uint8_t *universe, uint length);
/*
Checks whether the DMX transmitter is busy sending
a DMX data frame. Returns immediately
*/
bool busy();
/*
Wait for the DMX transmitter to finish transmitting
the current DMX frame. Returns immediately if no
frame is currently being transmitted
*/
// void await();
/*
De-inits the DMX transmitter instance. Releases PIO
and DMA resources. The instance can safely be destroyed
after this method is called
*/
void end();
};
#endif
================================================
FILE: src/DmxOutput.pio.h
================================================
// -------------------------------------------------- //
// This file is autogenerated by pioasm; do not edit! //
// -------------------------------------------------- //
#if !PICO_NO_HARDWARE
#include "hardware/pio.h"
#endif
// --------- //
// DmxOutput //
// --------- //
#define DmxOutput_wrap_target 3
#define DmxOutput_wrap 6
static const uint16_t DmxOutput_program_instructions[] = {
0xf035, // 0: set x, 21 side 0
0x0741, // 1: jmp x--, 1 [7]
0xbf42, // 2: nop side 1 [7]
// .wrap_target
0x9fa0, // 3: pull block side 1 [7]
0xf327, // 4: set x, 7 side 0 [3]
0x6001, // 5: out pins, 1
0x0245, // 6: jmp x--, 5 [2]
// .wrap
};
#if !PICO_NO_HARDWARE
static const struct pio_program DmxOutput_program = {
.instructions = DmxOutput_program_instructions,
.length = 7,
.origin = -1,
};
static inline pio_sm_config DmxOutput_program_get_default_config(uint offset) {
pio_sm_config c = pio_get_default_sm_config();
sm_config_set_wrap(&c, offset + DmxOutput_wrap_target, offset + DmxOutput_wrap);
sm_config_set_sideset(&c, 2, true, false);
return c;
}
#endif
gitextract_7q0zf7g5/
├── .github/
│ └── workflows/
│ └── Arduino-lint.yml
├── .gitignore
├── LICENSE
├── README.md
├── examples/
│ ├── RGB_input/
│ │ └── RGB_input.ino
│ ├── RGB_input_async/
│ │ └── RGB_input_async.ino
│ ├── highlight_all/
│ │ └── highlight_all.ino
│ └── parallel_output/
│ └── parallel_output.ino
├── extras/
│ ├── DmxInput.pio
│ ├── DmxInputInverted.pio
│ ├── DmxOutput.pio
│ └── pioasm
├── interfaceLibForPicoSDK.cmake
├── library.properties
└── src/
├── DmxInput.cpp
├── DmxInput.h
├── DmxInput.pio.h
├── DmxInputInverted.pio.h
├── DmxOutput.cpp
├── DmxOutput.h
└── DmxOutput.pio.h
SYMBOL INDEX (10 symbols across 6 files)
FILE: src/DmxInput.cpp
function dmxinput_dma_handler (line 125) | void dmxinput_dma_handler() {
function uint (line 201) | uint DmxInput::pin() {
FILE: src/DmxInput.h
function class (line 26) | class DmxInput
FILE: src/DmxInput.pio.h
type pio_program (line 35) | struct pio_program
function pio_sm_config (line 41) | static inline pio_sm_config DmxInput_program_get_default_config(uint off...
FILE: src/DmxInputInverted.pio.h
type pio_program (line 35) | struct pio_program
function pio_sm_config (line 41) | static inline pio_sm_config DmxInputInverted_program_get_default_config(...
FILE: src/DmxOutput.h
function class (line 24) | class DmxOutput
FILE: src/DmxOutput.pio.h
type pio_program (line 29) | struct pio_program
function pio_sm_config (line 35) | static inline pio_sm_config DmxOutput_program_get_default_config(uint of...
Condensed preview — 21 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (45K chars).
[
{
"path": ".github/workflows/Arduino-lint.yml",
"chars": 973,
"preview": "# This is a basic workflow to help you get started with Actions\n\nname: Arduino Linter\n\n# Controls when the action will r"
},
{
"path": ".gitignore",
"chars": 430,
"preview": "# Prerequisites\n*.d\n\n# Object files\n*.o\n*.ko\n*.obj\n*.elf\n\n# Linker output\n*.ilk\n*.map\n*.exp\n\n# Precompiled Headers\n*.gch"
},
{
"path": "LICENSE",
"chars": 1521,
"preview": "BSD 3-Clause License\n\nCopyright (c) 2021, Jostein Løwer\nAll rights reserved.\n\nRedistribution and use in source and binar"
},
{
"path": "README.md",
"chars": 8947,
"preview": "# Pico-DMX\n\n 2021 Jostein Løwer \n *\n * SPDX-License-Identifier: BSD-3-Clause\n * \n * Description: \n * Starts a DMX"
},
{
"path": "examples/RGB_input_async/RGB_input_async.ino",
"chars": 1155,
"preview": "/*\n * Copyright (c) 2021 Jostein Løwer \n *\n * SPDX-License-Identifier: BSD-3-Clause\n * \n * Description: \n * Starts a DMX"
},
{
"path": "examples/highlight_all/highlight_all.ino",
"chars": 967,
"preview": "/*\n * Copyright (c) 2021 Jostein Løwer \n *\n * SPDX-License-Identifier: BSD-3-Clause\n * \n * Description: \n * Starts a DMX"
},
{
"path": "examples/parallel_output/parallel_output.ino",
"chars": 1258,
"preview": "\n/*\n * Copyright (c) 2021 Jostein Løwer \n *\n * SPDX-License-Identifier: BSD-3-Clause\n * \n * Description: \n * Starts a 8 "
},
{
"path": "extras/DmxInput.pio",
"chars": 1581,
"preview": "; Author: Jostein Løwer, github: jostlowe\n; SPDX-License-Identifier: BSD-3-Clause\n; \n; PIO program for inputting the DMX"
},
{
"path": "extras/DmxInputInverted.pio",
"chars": 1538,
"preview": "; Author: Jostein Løwer, github: jostlowe; modified by @functionpointer\n; SPDX-License-Identifier: BSD-3-Clause\n; \n; PIO"
},
{
"path": "extras/DmxOutput.pio",
"chars": 1019,
"preview": "; Author: Jostein Løwer, github: jostlowe\n; SPDX-License-Identifier: BSD-3-Clause\n; \n; PIO program for outputting the DM"
},
{
"path": "interfaceLibForPicoSDK.cmake",
"chars": 585,
"preview": "## Include this file if you want to use the Pico-DMX library\n## in YOUR (Pico-C-SDK-based) project.\n\ncmake_minimum_requi"
},
{
"path": "library.properties",
"chars": 455,
"preview": "name=Pico-DMX\nversion=3.1.0\nauthor=Jostein Løwer <jostlowe@gmail.com>\nmaintainer=Jostein Løwer <jostlowe@gmail.com>\nsent"
},
{
"path": "src/DmxInput.cpp",
"chars": 7003,
"preview": "/*\n * Copyright (c) 2021 Jostein Løwer \n *\n * SPDX-License-Identifier: BSD-3-Clause\n */\n\n#include \"DmxInput.h\"\n#include "
},
{
"path": "src/DmxInput.h",
"chars": 3104,
"preview": "\n/*\n * Copyright (c) 2021 Jostein Løwer \n *\n * SPDX-License-Identifier: BSD-3-Clause\n */\n\n#ifndef DMX_INPUT_H\n#define DM"
},
{
"path": "src/DmxInput.pio.h",
"chars": 1461,
"preview": "// -------------------------------------------------- //\n// This file is autogenerated by pioasm; do not edit! //\n// ---"
},
{
"path": "src/DmxInputInverted.pio.h",
"chars": 1643,
"preview": "// -------------------------------------------------- //\n// This file is autogenerated by pioasm; do not edit! //\n// ---"
},
{
"path": "src/DmxOutput.cpp",
"chars": 3905,
"preview": "\n/*\n * Copyright (c) 2021 Jostein Løwer \n *\n * SPDX-License-Identifier: BSD-3-Clause\n */\n\n#include \"DmxOutput.h\"\n#includ"
},
{
"path": "src/DmxOutput.h",
"chars": 2744,
"preview": "/*\n * Copyright (c) 2021 Jostein Løwer \n *\n * SPDX-License-Identifier: BSD-3-Clause\n */\n\n#ifndef DMX_OUTPUT_H\n#define DM"
},
{
"path": "src/DmxOutput.pio.h",
"chars": 1287,
"preview": "// -------------------------------------------------- //\n// This file is autogenerated by pioasm; do not edit! //\n// ---"
}
]
// ... and 1 more files (download for full content)
About this extraction
This page contains the full source code of the jostlowe/Pico-DMX GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 21 files (41.6 KB), approximately 11.8k tokens, and a symbol index with 10 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.