Merge branch 'master' into patch-1

This commit is contained in:
Joey Babcock 2016-12-30 23:04:34 -08:00 committed by GitHub
commit daf08d45bb
12 changed files with 774 additions and 24 deletions

View File

@ -63,10 +63,10 @@ If `pip` is not found try using `python -m pip install` instead.
## Arduino dependencies
ESP8266 firmare is uploaded using the Arduino IDE. See [this tutorial](https://learn.sparkfun.com/tutorials/esp8266-thing-hookup-guide/installing-the-esp8266-arduino-addon) to setup the Arduino IDE for ESP8266.
This [ws2812b i2s library](https://github.com/JoDaNl/esp8266_ws2812_i2s) must be downloaded and installed in the Arduino libraries folder.
<!-- This [ws2812b i2s library](https://github.com/JoDaNl/esp8266_ws2812_i2s) must be downloaded and installed in the Arduino libraries folder.
-->
# Hardware Connections
The ESP8266 has hardware support for [I²S](https://en.wikipedia.org/wiki/I%C2%B2S) and this peripheral is used by the [ws2812b i2s library](https://github.com/JoDaNl/esp8266_ws2812_i2s) to control the ws2812b LED strip. This signficantly improves performance compared to bit-banging the IO pin. Unfortunately, this means that the LED strip **must** be connected to the RX1 pin, which is not accessible in some ESP8266 modules (such as the ESP-01).
The ESP8266 has hardware support for [I²S](https://en.wikipedia.org/wiki/I%C2%B2S) and this peripheral is used <!-- by the [ws2812b i2s library](https://github.com/JoDaNl/esp8266_ws2812_i2s) -->to control the ws2812b LED strip. This signficantly improves performance compared to bit-banging the IO pin. Unfortunately, this means that the LED strip **must** be connected to the RX1 pin, which is not accessible in some ESP8266 modules (such as the ESP-01).
The RX1 pin on the ESP8266 module should be connected to the data input pin of the ws2812b LED strip (often labelled DIN or D0).
@ -99,5 +99,8 @@ A PyQtGraph GUI will open to display the output of the visualization on the comp
If you encounter any issues or have questions about this project, feel free to open a new issue.
# Limitations
The visualization code currently supports up to 256 LEDs. Support for additional LEDs will be added in the near future.
# License
All code in this project is released under the MIT License.

View File

@ -0,0 +1,26 @@
// ws2812.h
#ifndef __WS2812_H__
#define __WS2812_H__
// Gamma Correction
// Uses a nonlinear lookup table to correct for human perception of light.
// When gamma correction is used, a brightness value of 2X should appear twice
// as bright as a value of X.
// 1 = Enable gamma correction
// 0 = Disable gamma correction
// Note: There seems to be a bug and you can't actually disable this
#define WS2812_GAMMA_CORRECTION (0)
// Temporal Dithering
// Dithering preserves color and light when brightness is low.
// Sometimes this can cause undesirable flickering.
// 1 = Disable temporal dithering
// 2, 6, 8 = Enable temporal dithering (larger values = more dithering)
#define WS2812_DITHER_NUM (4)
#define WS2812_USE_INTERRUPT (0) // not supported yet
#endif
// end of file

View File

@ -3,10 +3,12 @@
#include <WebSocketsServer.h>
#include <Hash.h>
#include <WiFiUdp.h>
#include <ws2812_i2s.h>
#include "ws2812_i2s.h"
// Set this to the number of LEDs in your LED strip
#define NUM_LEDS 50
#define NUM_LEDS 60
// Maximum number of packets to hold in the buffer. Don't change this.
#define BUFFER_LEN 1024
#define PRINT_FPS 1

View File

@ -0,0 +1,172 @@
// w2812_defs.h
//
// contains material from Charles Lohr, but changed by me.
#ifndef __WS2812_I2S_DEFS_H__
#define __WS2812_I2S_DEFS_H__
#include <stdint.h>
// include file from project folder
#include "ws2812.h"
// -----------------------------------------------------
// Helper macro's
#define NUM_RGB_BYTES (num_leds * 3)
#define NUM_I2S_PIXEL_BYTES (num_leds * 3 * 4) // (#leds) * (RGB) * (4 i2s bits)
#define NUM_I2S_PIXEL_WORDS (num_leds * 3)
// -----------------------------------------------------
// I2S timing parameters
// Creates an I2S SR of 93,750 Hz, or 3 MHz Bitclock (.333us/sample)
// Measured on scope : 4 bitclock-ticks every 1200ns --> 300ns bitclock ???
// 12000000L/(div*bestbck*2)
#define WS_I2S_BCK (17)
#define WS_I2S_DIV (4)
#define NUM_I2S_ZERO_BYTES (28) // WS2812 LED Treset = 67us (must be >50us)
#define NUM_I2S_ZERO_WORDS (NUM_I2S_ZERO_BYTES / 4)
// -----------------------------------------------------
#ifndef i2c_bbpll
#define i2c_bbpll 0x67
#define i2c_bbpll_en_audio_clock_out 4
#define i2c_bbpll_en_audio_clock_out_msb 7
#define i2c_bbpll_en_audio_clock_out_lsb 7
#define i2c_bbpll_hostid 4
#endif
// -----------------------------------------------------
#define i2c_writeReg_Mask(block, host_id, reg_add, Msb, Lsb, indata) rom_i2c_writeReg_Mask(block, host_id, reg_add, Msb, Lsb, indata)
#define i2c_readReg_Mask(block, host_id, reg_add, Msb, Lsb) rom_i2c_readReg_Mask(block, host_id, reg_add, Msb, Lsb)
#define i2c_writeReg_Mask_def(block, reg_add, indata) i2c_writeReg_Mask(block, block##_hostid, reg_add, reg_add##_msb, reg_add##_lsb, indata)
#define i2c_readReg_Mask_def(block, reg_add) i2c_readReg_Mask(block, block##_hostid, reg_add, reg_add##_msb, reg_add##_lsb)
// -----------------------------------------------------
#ifndef ETS_SLC_INUM
#define ETS_SLC_INUM 1
#endif
// -----------------------------------------------------
// from i2s_reg.h
#define DR_REG_I2S_BASE (0x60000e00)
#define I2STXFIFO (DR_REG_I2S_BASE + 0x0000)
#define I2SRXFIFO (DR_REG_I2S_BASE + 0x0004)
#define I2SCONF (DR_REG_I2S_BASE + 0x0008)
#define I2S_BCK_DIV_NUM 0x0000003F
#define I2S_BCK_DIV_NUM_S 22
#define I2S_CLKM_DIV_NUM 0x0000003F
#define I2S_CLKM_DIV_NUM_S 16
#define I2S_BITS_MOD 0x0000000F
#define I2S_BITS_MOD_S 12
#define I2S_RECE_MSB_SHIFT (BIT(11))
#define I2S_TRANS_MSB_SHIFT (BIT(10))
#define I2S_I2S_RX_START (BIT(9))
#define I2S_I2S_TX_START (BIT(8))
#define I2S_MSB_RIGHT (BIT(7))
#define I2S_RIGHT_FIRST (BIT(6))
#define I2S_RECE_SLAVE_MOD (BIT(5))
#define I2S_TRANS_SLAVE_MOD (BIT(4))
#define I2S_I2S_RX_FIFO_RESET (BIT(3))
#define I2S_I2S_TX_FIFO_RESET (BIT(2))
#define I2S_I2S_RX_RESET (BIT(1))
#define I2S_I2S_TX_RESET (BIT(0))
#define I2S_I2S_RESET_MASK 0xf
#define I2SINT_RAW (DR_REG_I2S_BASE + 0x000c)
#define I2S_I2S_TX_REMPTY_INT_RAW (BIT(5))
#define I2S_I2S_TX_WFULL_INT_RAW (BIT(4))
#define I2S_I2S_RX_REMPTY_INT_RAW (BIT(3))
#define I2S_I2S_RX_WFULL_INT_RAW (BIT(2))
#define I2S_I2S_TX_PUT_DATA_INT_RAW (BIT(1))
#define I2S_I2S_RX_TAKE_DATA_INT_RAW (BIT(0))
#define I2SINT_ST (DR_REG_I2S_BASE + 0x0010)
#define I2S_I2S_TX_REMPTY_INT_ST (BIT(5))
#define I2S_I2S_TX_WFULL_INT_ST (BIT(4))
#define I2S_I2S_RX_REMPTY_INT_ST (BIT(3))
#define I2S_I2S_RX_WFULL_INT_ST (BIT(2))
#define I2S_I2S_TX_PUT_DATA_INT_ST (BIT(1))
#define I2S_I2S_RX_TAKE_DATA_INT_ST (BIT(0))
#define I2SINT_ENA (DR_REG_I2S_BASE + 0x0014)
#define I2S_I2S_TX_REMPTY_INT_ENA (BIT(5))
#define I2S_I2S_TX_WFULL_INT_ENA (BIT(4))
#define I2S_I2S_RX_REMPTY_INT_ENA (BIT(3))
#define I2S_I2S_RX_WFULL_INT_ENA (BIT(2))
#define I2S_I2S_TX_PUT_DATA_INT_ENA (BIT(1))
#define I2S_I2S_RX_TAKE_DATA_INT_ENA (BIT(0))
#define I2SINT_CLR (DR_REG_I2S_BASE + 0x0018)
#define I2S_I2S_TX_REMPTY_INT_CLR (BIT(5))
#define I2S_I2S_TX_WFULL_INT_CLR (BIT(4))
#define I2S_I2S_RX_REMPTY_INT_CLR (BIT(3))
#define I2S_I2S_RX_WFULL_INT_CLR (BIT(2))
#define I2S_I2S_PUT_DATA_INT_CLR (BIT(1))
#define I2S_I2S_TAKE_DATA_INT_CLR (BIT(0))
#define I2STIMING (DR_REG_I2S_BASE + 0x001c)
#define I2S_TRANS_BCK_IN_INV (BIT(22))
#define I2S_RECE_DSYNC_SW (BIT(21))
#define I2S_TRANS_DSYNC_SW (BIT(20))
#define I2S_RECE_BCK_OUT_DELAY 0x00000003
#define I2S_RECE_BCK_OUT_DELAY_S 18
#define I2S_RECE_WS_OUT_DELAY 0x00000003
#define I2S_RECE_WS_OUT_DELAY_S 16
#define I2S_TRANS_SD_OUT_DELAY 0x00000003
#define I2S_TRANS_SD_OUT_DELAY_S 14
#define I2S_TRANS_WS_OUT_DELAY 0x00000003
#define I2S_TRANS_WS_OUT_DELAY_S 12
#define I2S_TRANS_BCK_OUT_DELAY 0x00000003
#define I2S_TRANS_BCK_OUT_DELAY_S 10
#define I2S_RECE_SD_IN_DELAY 0x00000003
#define I2S_RECE_SD_IN_DELAY_S 8
#define I2S_RECE_WS_IN_DELAY 0x00000003
#define I2S_RECE_WS_IN_DELAY_S 6
#define I2S_RECE_BCK_IN_DELAY 0x00000003
#define I2S_RECE_BCK_IN_DELAY_S 4
#define I2S_TRANS_WS_IN_DELAY 0x00000003
#define I2S_TRANS_WS_IN_DELAY_S 2
#define I2S_TRANS_BCK_IN_DELAY 0x00000003
#define I2S_TRANS_BCK_IN_DELAY_S 0
#define I2S_FIFO_CONF (DR_REG_I2S_BASE + 0x0020)
#define I2S_I2S_RX_FIFO_MOD 0x00000007
#define I2S_I2S_RX_FIFO_MOD_S 16
#define I2S_I2S_TX_FIFO_MOD 0x00000007
#define I2S_I2S_TX_FIFO_MOD_S 13
#define I2S_I2S_DSCR_EN (BIT(12))
#define I2S_I2S_TX_DATA_NUM 0x0000003F
#define I2S_I2S_TX_DATA_NUM_S 6
#define I2S_I2S_RX_DATA_NUM 0x0000003F
#define I2S_I2S_RX_DATA_NUM_S 0
#define I2SRXEOF_NUM (DR_REG_I2S_BASE + 0x0024)
#define I2S_I2S_RX_EOF_NUM 0xFFFFFFFF
#define I2S_I2S_RX_EOF_NUM_S 0
#define I2SCONF_SIGLE_DATA (DR_REG_I2S_BASE + 0x0028)
#define I2S_I2S_SIGLE_DATA 0xFFFFFFFF
#define I2S_I2S_SIGLE_DATA_S 0
#define I2SCONF_CHAN (DR_REG_I2S_BASE + 0x002c)
#define I2S_RX_CHAN_MOD 0x00000003
#define I2S_RX_CHAN_MOD_S 3
#define I2S_TX_CHAN_MOD 0x00000007
#define I2S_TX_CHAN_MOD_S 0
// -----------------------------------------------------
#endif
// end of file

View File

@ -0,0 +1,102 @@
// ws2812_init.c
// C-based helper function for initilalizing
// the I2S system
#include <string.h>
#include "slc_register.h"
#include "user_interface.h"
#include "ws2812_defs.h"
#include "ws2812_dma.h"
#if WS2812_USE_INTERRUPT == 1
// for debugging purposes
static volatile uint32_t interrupt_count = 0;
static void ws2812_isr(void)
{
//clear all intr flags
WRITE_PERI_REG(SLC_INT_CLR, 0xffffffff);//slc_intr_status);
interrupt_count++;
}
#endif
void ws2812_dma(sdio_queue_t *i2s_pixels_queue)
{
// Reset DMA
SET_PERI_REG_MASK(SLC_CONF0, SLC_RXLINK_RST); //|SLC_TXLINK_RST);
CLEAR_PERI_REG_MASK(SLC_CONF0, SLC_RXLINK_RST); //|SLC_TXLINK_RST);
// Clear DMA int flags
SET_PERI_REG_MASK(SLC_INT_CLR, 0xffffffff);
CLEAR_PERI_REG_MASK(SLC_INT_CLR, 0xffffffff);
// Enable and configure DMA
CLEAR_PERI_REG_MASK(SLC_CONF0,(SLC_MODE<<SLC_MODE_S));
SET_PERI_REG_MASK(SLC_CONF0,(1<<SLC_MODE_S));
SET_PERI_REG_MASK(SLC_RX_DSCR_CONF,SLC_INFOR_NO_REPLACE|SLC_TOKEN_NO_REPLACE);
CLEAR_PERI_REG_MASK(SLC_RX_DSCR_CONF, SLC_RX_FILL_EN|SLC_RX_EOF_MODE | SLC_RX_FILL_MODE);
// configure DMA descriptor
CLEAR_PERI_REG_MASK(SLC_RX_LINK,SLC_RXLINK_DESCADDR_MASK);
SET_PERI_REG_MASK(SLC_RX_LINK, ((uint32)i2s_pixels_queue) & SLC_RXLINK_DESCADDR_MASK);
#if WS2812_USE_INTERRUPT == 1
// Attach the DMA interrupt
ets_isr_attach(ETS_SLC_INUM, (int_handler_t)ws2812_isr , (void *)0);
//Enable DMA operation intr
// WRITE_PERI_REG(SLC_INT_ENA, SLC_RX_EOF_INT_ENA);
//clear any interrupt flags that are set
WRITE_PERI_REG(SLC_INT_CLR, 0xffffffff);
///enable DMA intr in cpu
ets_isr_unmask(1<<ETS_SLC_INUM);
#endif
//Start transmission
SET_PERI_REG_MASK(SLC_RX_LINK, SLC_RXLINK_START);
//Init pins to i2s functions
PIN_FUNC_SELECT(PERIPHS_IO_MUX_U0RXD_U, FUNC_I2SO_DATA);
//Enable clock to i2s subsystem
i2c_writeReg_Mask_def(i2c_bbpll, i2c_bbpll_en_audio_clock_out, 1);
//Reset I2S subsystem
CLEAR_PERI_REG_MASK(I2SCONF,I2S_I2S_RESET_MASK);
SET_PERI_REG_MASK(I2SCONF,I2S_I2S_RESET_MASK);
CLEAR_PERI_REG_MASK(I2SCONF,I2S_I2S_RESET_MASK);
//Select 16bits per channel (FIFO_MOD=0), no DMA access (FIFO only)
CLEAR_PERI_REG_MASK(I2S_FIFO_CONF, I2S_I2S_DSCR_EN|(I2S_I2S_RX_FIFO_MOD<<I2S_I2S_RX_FIFO_MOD_S)|(I2S_I2S_TX_FIFO_MOD<<I2S_I2S_TX_FIFO_MOD_S));
//Enable DMA in i2s subsystem
SET_PERI_REG_MASK(I2S_FIFO_CONF, I2S_I2S_DSCR_EN);
//trans master&rece slave,MSB shift,right_first,msb right
CLEAR_PERI_REG_MASK(I2SCONF, I2S_TRANS_SLAVE_MOD|
(I2S_BITS_MOD<<I2S_BITS_MOD_S)|
(I2S_BCK_DIV_NUM <<I2S_BCK_DIV_NUM_S)|
(I2S_CLKM_DIV_NUM<<I2S_CLKM_DIV_NUM_S));
SET_PERI_REG_MASK(I2SCONF, I2S_RIGHT_FIRST|I2S_MSB_RIGHT|I2S_RECE_SLAVE_MOD|
I2S_RECE_MSB_SHIFT|I2S_TRANS_MSB_SHIFT|
(((WS_I2S_BCK-1)&I2S_BCK_DIV_NUM )<<I2S_BCK_DIV_NUM_S)|
(((WS_I2S_DIV-1)&I2S_CLKM_DIV_NUM)<<I2S_CLKM_DIV_NUM_S));
#if WS2812_USE_INTERRUPT == 1
//clear int
SET_PERI_REG_MASK(I2SINT_CLR, I2S_I2S_RX_WFULL_INT_CLR|I2S_I2S_PUT_DATA_INT_CLR|I2S_I2S_TAKE_DATA_INT_CLR);
CLEAR_PERI_REG_MASK(I2SINT_CLR, I2S_I2S_RX_WFULL_INT_CLR|I2S_I2S_PUT_DATA_INT_CLR|I2S_I2S_TAKE_DATA_INT_CLR);
//enable int
SET_PERI_REG_MASK(I2SINT_ENA, I2S_I2S_RX_REMPTY_INT_ENA|I2S_I2S_RX_TAKE_DATA_INT_ENA);
#endif
//Start transmission
SET_PERI_REG_MASK(I2SCONF,I2S_I2S_TX_START);
}
// end of file

View File

@ -0,0 +1,25 @@
// ws2812_dma.h
#ifndef __WS2812_DMA_H__
#define __WS2812_DMA_H__
// type definition taken from : sdio_slv.h
typedef struct
{
uint32_t blocksize:12;
uint32_t datalen:12;
uint32_t unused:5;
uint32_t sub_sof:1;
uint32_t eof:1;
uint32_t owner:1;
uint32_t buf_ptr;
uint32_t next_link_ptr;
} sdio_queue_t;
// -----------------------------------------------------
extern void ws2812_dma(sdio_queue_t *);
#endif

View File

@ -0,0 +1,165 @@
// ws2812_gamma.cpp
//
// dithering tables with gamma correction
#include <stdint.h>
#include "ws2812_gamma.h"
static const uint8_t gamma0[] = {
0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 4, 4, 4, 4,
5, 5, 5, 5, 6, 6, 6, 6, 7, 7, 7, 8, 8, 8, 9, 9,
10, 10, 10, 11, 11, 12, 12, 12, 13, 13, 14, 14, 15, 15, 16, 16,
17, 17, 18, 18, 19, 19, 20, 20, 21, 21, 22, 23, 23, 24, 24, 25,
26, 26, 27, 28, 28, 29, 30, 30, 31, 32, 32, 33, 34, 34, 35, 36,
37, 37, 38, 39, 40, 41, 41, 42, 43, 44, 45, 45, 46, 47, 48, 49,
50, 51, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64,
65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 80, 81,
82, 83, 84, 85, 86, 88, 89, 90, 91, 92, 94, 95, 96, 97, 98, 100,
101, 102, 103, 105, 106, 107, 109, 110, 111, 113, 114, 115, 117, 118, 119, 121,
122, 123, 125, 126, 128, 129, 130, 132, 133, 135, 136, 138, 139, 141, 142, 144,
145, 147, 148, 150, 151, 153, 154, 156, 157, 159, 161, 162, 164, 165, 167, 169,
170, 172, 173, 175, 177, 178, 180, 182, 183, 185, 187, 189, 190, 192, 194, 196,
197, 199, 201, 203, 204, 206, 208, 210, 212, 213, 215, 217, 219, 221, 223, 225,
226, 228, 230, 232, 234, 236, 238, 240, 242, 244, 246, 248, 250, 252, 254, 255 };
static const uint8_t gamma1[] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1,
1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4,
4, 4, 5, 5, 5, 5, 6, 6, 6, 7, 7, 7, 8, 8, 8, 9,
9, 9, 10, 10, 11, 11, 11, 12, 12, 13, 13, 14, 14, 15, 15, 16,
16, 17, 17, 18, 18, 19, 19, 20, 20, 21, 21, 22, 23, 23, 24, 24,
25, 26, 26, 27, 28, 28, 29, 30, 30, 31, 32, 32, 33, 34, 35, 35,
36, 37, 38, 38, 39, 40, 41, 42, 42, 43, 44, 45, 46, 47, 47, 48,
49, 50, 51, 52, 53, 54, 55, 56, 56, 57, 58, 59, 60, 61, 62, 63,
64, 65, 66, 67, 68, 69, 70, 71, 73, 74, 75, 76, 77, 78, 79, 80,
81, 82, 84, 85, 86, 87, 88, 89, 91, 92, 93, 94, 95, 97, 98, 99,
100, 102, 103, 104, 105, 107, 108, 109, 111, 112, 113, 115, 116, 117, 119, 120,
121, 123, 124, 126, 127, 128, 130, 131, 133, 134, 136, 137, 139, 140, 142, 143,
145, 146, 148, 149, 151, 152, 154, 155, 157, 158, 160, 162, 163, 165, 166, 168,
170, 171, 173, 175, 176, 178, 180, 181, 183, 185, 186, 188, 190, 192, 193, 195,
197, 199, 200, 202, 204, 206, 207, 209, 211, 213, 215, 217, 218, 220, 222, 224,
226, 228, 230, 232, 233, 235, 237, 239, 241, 243, 245, 247, 249, 251, 253, 255 };
static const uint8_t gamma2[] = {
0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 4, 4, 4,
4, 5, 5, 5, 5, 6, 6, 6, 7, 7, 7, 8, 8, 8, 9, 9,
9, 10, 10, 10, 11, 11, 12, 12, 13, 13, 13, 14, 14, 15, 15, 16,
16, 17, 17, 18, 18, 19, 19, 20, 21, 21, 22, 22, 23, 24, 24, 25,
25, 26, 27, 27, 28, 29, 29, 30, 31, 31, 32, 33, 33, 34, 35, 36,
36, 37, 38, 39, 39, 40, 41, 42, 43, 43, 44, 45, 46, 47, 48, 49,
49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64,
65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80,
82, 83, 84, 85, 86, 87, 88, 90, 91, 92, 93, 94, 96, 97, 98, 99,
101, 102, 103, 104, 106, 107, 108, 110, 111, 112, 114, 115, 116, 118, 119, 120,
122, 123, 125, 126, 127, 129, 130, 132, 133, 134, 136, 137, 139, 140, 142, 143,
145, 146, 148, 149, 151, 152, 154, 156, 157, 159, 160, 162, 163, 165, 167, 168,
170, 172, 173, 175, 177, 178, 180, 182, 183, 185, 187, 188, 190, 192, 194, 195,
197, 199, 201, 202, 204, 206, 208, 210, 211, 213, 215, 217, 219, 221, 222, 224,
226, 228, 230, 232, 234, 236, 238, 240, 241, 243, 245, 247, 249, 251, 253, 255 };
static const uint8_t gamma3[] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1,
1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 3, 4,
4, 4, 4, 5, 5, 5, 5, 6, 6, 6, 7, 7, 7, 8, 8, 8,
9, 9, 10, 10, 10, 11, 11, 12, 12, 12, 13, 13, 14, 14, 15, 15,
16, 16, 17, 17, 18, 18, 19, 20, 20, 21, 21, 22, 22, 23, 24, 24,
25, 25, 26, 27, 27, 28, 29, 29, 30, 31, 32, 32, 33, 34, 34, 35,
36, 37, 37, 38, 39, 40, 41, 41, 42, 43, 44, 45, 45, 46, 47, 48,
49, 50, 51, 52, 53, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 76, 77, 78, 79, 80,
81, 82, 83, 84, 86, 87, 88, 89, 90, 92, 93, 94, 95, 96, 98, 99,
100, 101, 103, 104, 105, 107, 108, 109, 110, 112, 113, 114, 116, 117, 118, 120,
121, 123, 124, 125, 127, 128, 130, 131, 133, 134, 135, 137, 138, 140, 141, 143,
144, 146, 147, 149, 150, 152, 153, 155, 157, 158, 160, 161, 163, 165, 166, 168,
169, 171, 173, 174, 176, 178, 179, 181, 183, 184, 186, 188, 190, 191, 193, 195,
197, 198, 200, 202, 204, 205, 207, 209, 211, 213, 214, 216, 218, 220, 222, 224,
226, 228, 229, 231, 233, 235, 237, 239, 241, 243, 245, 247, 249, 251, 253, 255 };
static const uint8_t gamma4[] = {
0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 4, 4, 4,
4, 5, 5, 5, 5, 6, 6, 6, 7, 7, 7, 8, 8, 8, 9, 9,
9, 10, 10, 11, 11, 11, 12, 12, 13, 13, 14, 14, 14, 15, 15, 16,
16, 17, 17, 18, 19, 19, 20, 20, 21, 21, 22, 22, 23, 24, 24, 25,
25, 26, 27, 27, 28, 29, 29, 30, 31, 31, 32, 33, 34, 34, 35, 36,
37, 37, 38, 39, 40, 40, 41, 42, 43, 44, 44, 45, 46, 47, 48, 49,
50, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64,
65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 81,
82, 83, 84, 85, 86, 87, 89, 90, 91, 92, 93, 95, 96, 97, 98, 100,
101, 102, 103, 105, 106, 107, 108, 110, 111, 112, 114, 115, 116, 118, 119, 120,
122, 123, 125, 126, 127, 129, 130, 132, 133, 135, 136, 138, 139, 140, 142, 143,
145, 146, 148, 149, 151, 153, 154, 156, 157, 159, 160, 162, 164, 165, 167, 168,
170, 172, 173, 175, 177, 178, 180, 182, 183, 185, 187, 188, 190, 192, 194, 195,
197, 199, 201, 202, 204, 206, 208, 210, 211, 213, 215, 217, 219, 221, 223, 224,
226, 228, 230, 232, 234, 236, 238, 240, 242, 244, 245, 247, 249, 251, 253, 255 };
static const uint8_t gamma5[] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1,
1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 4,
4, 4, 4, 5, 5, 5, 6, 6, 6, 6, 7, 7, 7, 8, 8, 9,
9, 9, 10, 10, 10, 11, 11, 12, 12, 13, 13, 14, 14, 14, 15, 15,
16, 16, 17, 17, 18, 19, 19, 20, 20, 21, 21, 22, 23, 23, 24, 24,
25, 26, 26, 27, 28, 28, 29, 30, 30, 31, 32, 32, 33, 34, 35, 35,
36, 37, 38, 38, 39, 40, 41, 41, 42, 43, 44, 45, 46, 46, 47, 48,
49, 50, 51, 52, 53, 54, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 75, 76, 77, 78, 79, 80,
81, 82, 83, 85, 86, 87, 88, 89, 90, 92, 93, 94, 95, 97, 98, 99,
100, 102, 103, 104, 105, 107, 108, 109, 111, 112, 113, 115, 116, 117, 119, 120,
121, 123, 124, 126, 127, 128, 130, 131, 133, 134, 136, 137, 138, 140, 141, 143,
144, 146, 147, 149, 151, 152, 154, 155, 157, 158, 160, 161, 163, 165, 166, 168,
170, 171, 173, 174, 176, 178, 179, 181, 183, 185, 186, 188, 190, 191, 193, 195,
197, 198, 200, 202, 204, 206, 207, 209, 211, 213, 215, 216, 218, 220, 222, 224,
226, 228, 230, 231, 233, 235, 237, 239, 241, 243, 245, 247, 249, 251, 253, 255 };
static const uint8_t gamma6[] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1,
1, 1, 1, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 4, 4,
4, 4, 5, 5, 5, 5, 6, 6, 6, 7, 7, 7, 8, 8, 8, 9,
9, 10, 10, 10, 11, 11, 12, 12, 12, 13, 13, 14, 14, 15, 15, 16,
16, 17, 17, 18, 18, 19, 19, 20, 20, 21, 22, 22, 23, 23, 24, 25,
25, 26, 26, 27, 28, 28, 29, 30, 30, 31, 32, 33, 33, 34, 35, 36,
36, 37, 38, 39, 39, 40, 41, 42, 43, 43, 44, 45, 46, 47, 48, 48,
49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 58, 59, 60, 61, 62, 63,
64, 65, 66, 67, 68, 69, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80,
81, 83, 84, 85, 86, 87, 88, 90, 91, 92, 93, 94, 96, 97, 98, 99,
101, 102, 103, 104, 106, 107, 108, 109, 111, 112, 113, 115, 116, 117, 119, 120,
122, 123, 124, 126, 127, 129, 130, 131, 133, 134, 136, 137, 139, 140, 142, 143,
145, 146, 148, 149, 151, 152, 154, 155, 157, 159, 160, 162, 163, 165, 167, 168,
170, 171, 173, 175, 176, 178, 180, 181, 183, 185, 186, 188, 190, 192, 193, 195,
197, 199, 200, 202, 204, 206, 208, 209, 211, 213, 215, 217, 219, 220, 222, 224,
226, 228, 230, 232, 234, 236, 237, 239, 241, 243, 245, 247, 249, 251, 253, 255 };
static const uint8_t gamma7[] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3,
4, 4, 4, 4, 5, 5, 5, 6, 6, 6, 7, 7, 7, 8, 8, 8,
9, 9, 9, 10, 10, 11, 11, 11, 12, 12, 13, 13, 14, 14, 15, 15,
16, 16, 17, 17, 18, 18, 19, 19, 20, 21, 21, 22, 22, 23, 23, 24,
25, 25, 26, 27, 27, 28, 29, 29, 30, 31, 31, 32, 33, 34, 34, 35,
36, 37, 37, 38, 39, 40, 40, 41, 42, 43, 44, 45, 45, 46, 47, 48,
49, 50, 51, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 78, 79, 80,
81, 82, 83, 84, 86, 87, 88, 89, 90, 91, 93, 94, 95, 96, 98, 99,
100, 101, 103, 104, 105, 106, 108, 109, 110, 112, 113, 114, 116, 117, 118, 120,
121, 122, 124, 125, 127, 128, 130, 131, 132, 134, 135, 137, 138, 140, 141, 143,
144, 146, 147, 149, 150, 152, 153, 155, 156, 158, 160, 161, 163, 164, 166, 168,
169, 171, 173, 174, 176, 178, 179, 181, 183, 184, 186, 188, 189, 191, 193, 195,
196, 198, 200, 202, 203, 205, 207, 209, 211, 213, 214, 216, 218, 220, 222, 224,
226, 227, 229, 231, 233, 235, 237, 239, 241, 243, 245, 247, 249, 251, 253, 255 };
#if WS2812_DITHER_NUM == 1
const uint8_t *gamma_dither[WS2812_DITHER_NUM] = {gamma0};
#elif WS2812_DITHER_NUM == 2
const uint8_t *gamma_dither[WS2812_DITHER_NUM] = {gamma0,gamma1};
#elif WS2812_DITHER_NUM == 4
const uint8_t *gamma_dither[WS2812_DITHER_NUM] = {gamma0,gamma1,gamma2,gamma3};
#elif WS2812_DITHER_NUM == 8
const uint8_t *gamma_dither[WS2812_DITHER_NUM] = {gamma0,gamma1,gamma2,gamma3,gamma4,gamma5,gamma6,gamma7};
#else
#error Invalid WS2812_DITHER_NUM value. Allowed values are 1, 2, 4, 8
#endif
// end of file

View File

@ -0,0 +1,12 @@
// ws2812_gamma.h
#ifndef __WS2812_GAMMA_H__
#define __WS2812_GAMMA_H__
#include <stdint.h>
#include "ws2812.h"
extern const uint8_t *gamma_dither[WS2812_DITHER_NUM];
#endif

View File

@ -0,0 +1,169 @@
// ws2812_lib.cpp
//
// main library file / contains class implementation
//
// Need to give credits to Charles Lohr (https://github.com/cnlohr due
// to his work on his software for driving ws2812 led-strips using
// the i2s interface of the ESP8266.
//
// This inspired me to create an ESP8266 library for the Arduino IDE
// I've added temporal dithering & gamma-correction
// for a 'more natural' light intensity profile.
//
// No pin-definitions/mapings are required/possible as the library used the I2S
// output-pin. This pin in it's turn is shared with GPIO3 & RXD0
//
#include <Arduino.h>
#include <stdint.h>
#include "ws2812_i2s.h"
#include "ws2812_defs.h"
#include "ws2812_gamma.h"
// include C-style header
extern "C"
{
#include "ws2812_dma.h"
};
// class constructor
WS2812::WS2812(void)
{
// empty for now
}
// class de-constructor
WS2812::~WS2812(void)
{
// empty for now
// TODO : should implement switching of DMA
}
// Init led-string / memory buffers etc.
void WS2812::init(uint16_t _num_leds)
{
uint8_t i;
uint16_t j;
num_leds = _num_leds;
// clear zero buffer
for(j=0; j<NUM_I2S_ZERO_WORDS; j++)
{
i2s_zeros_buffer[j] = 0;
}
// do memory allocation for i2s buffer(s)
for(i=0; i<WS2812_DITHER_NUM; i++)
{
i2s_pixels_buffer[i] = new uint32_t[NUM_I2S_PIXEL_WORDS];
// TODO : handle memory allocation error better
if (i2s_pixels_buffer[i] == 0)
{
Serial.println("WS2812_I2S : ERROR ALLOCATING MEMORY");
return;
}
for(j=0; j<NUM_I2S_PIXEL_WORDS; j++)
{
i2s_pixels_buffer[i][j] = 0;
}
}
// set-up DMA descriptors / 1 pair per dither-factor
for(i=0; i<WS2812_DITHER_NUM; i++)
{
i2s_pixels_queue[i].owner = 1;
i2s_pixels_queue[i].eof = 1;
i2s_pixels_queue[i].sub_sof = 0;
i2s_pixels_queue[i].datalen = NUM_I2S_PIXEL_BYTES; // Size in bytes
i2s_pixels_queue[i].blocksize = NUM_I2S_PIXEL_BYTES; // Size in bytes
i2s_pixels_queue[i].buf_ptr = (uint32_t)i2s_pixels_buffer[i];
i2s_pixels_queue[i].unused = 0;
i2s_pixels_queue[i].next_link_ptr = (uint32_t)&i2s_zeros_queue[i]; // always link to zeros-buffer
i2s_zeros_queue[i].owner = 1;
i2s_zeros_queue[i].eof = 1;
i2s_zeros_queue[i].sub_sof = 0;
i2s_zeros_queue[i].datalen = NUM_I2S_ZERO_BYTES; // Size in bytes)
i2s_zeros_queue[i].blocksize = NUM_I2S_ZERO_BYTES; // Size in bytes
i2s_zeros_queue[i].buf_ptr = (uint32_t)i2s_zeros_buffer;
i2s_zeros_queue[i].unused = 0;
if (i == (WS2812_DITHER_NUM-1)) // last DMA descriptor in linked list ?
{
// yes : link to first DMA descriptor
i2s_zeros_queue[i].next_link_ptr = (uint32_t)&i2s_pixels_queue[0];
}
else
{
// no : link to next DMA descriptor
i2s_zeros_queue[i].next_link_ptr = (uint32_t)&i2s_pixels_queue[i+1];
}
}
// call C based helper function
// I did not really succeed in putting the code from the helper
// funtion directly into this constructor...has something to do
// with C vs. C++
// the i2c_writeReg_Mask macro (+ others) failed.. see ws2812_defs.h
// may be I solve this later
// parameter = first entry in DMA descriptor list, i.e the descriptor list
ws2812_dma(i2s_pixels_queue);
}
// left this comment in tact....credit to Charles
// All functions below this line are Public Domain 2015 Charles Lohr.
// this code may be used by anyone in any way without restriction or limitation.
// Send out WS2812 bits with coded pulses, one nibble, then the other.
static const uint16_t bitpatterns[16] =
{
0b1000100010001000, 0b1000100010001110, 0b1000100011101000, 0b1000100011101110,
0b1000111010001000, 0b1000111010001110, 0b1000111011101000, 0b1000111011101110,
0b1110100010001000, 0b1110100010001110, 0b1110100011101000, 0b1110100011101110,
0b1110111010001000, 0b1110111010001110, 0b1110111011101000, 0b1110111011101110,
};
// display the pixels
void WS2812::show(Pixel_t *pixels)
{
uint8_t *buffer;
uint8_t pixelbyte;
uint8_t gammabyte;
uint16_t i,b;
uint16_t *i2s_ptr[WS2812_DITHER_NUM];
buffer = (uint8_t *)pixels;
// set-up pointers into the i2s-pixel-buffers
for(i=0; i<WS2812_DITHER_NUM; i++)
{
i2s_ptr[i] = (uint16_t *)i2s_pixels_buffer[i];
}
// for every pixel in the input-array
// - get the pixel value (either R,G, or B)
// - for every dithered buffer
// get the gamma-corrected output value
// - and transform into i2s nibble
for(b=0; b<NUM_RGB_BYTES; b++)
{
pixelbyte = *buffer++;
for(i=0; i<WS2812_DITHER_NUM; i++)
{
gammabyte = gamma_dither[i][pixelbyte];
*(i2s_ptr[i]++) = bitpatterns[ (gammabyte & 0x0f) ];
*(i2s_ptr[i]++) = bitpatterns[ (gammabyte>>4) & 0x0f ];
}
}
}
// end of file

View File

@ -0,0 +1,41 @@
// ws2812_lib.h
#ifndef __WS2812_I2S_H__
#define __WS2812_I2S_H__
#include <stdint.h>
#include "ws2812_defs.h"
// include C-style header
extern "C"
{
#include "ws2812_dma.h"
};
typedef struct
{
uint8_t G; // G,R,B order is determined by WS2812B
uint8_t R;
uint8_t B;
} Pixel_t;
class WS2812
{
public:
WS2812(void);
~WS2812(void);
void init(uint16_t num_leds);
void show(Pixel_t *);
private:
uint16_t num_leds;
uint32_t *i2s_pixels_buffer[WS2812_DITHER_NUM];
uint32_t i2s_zeros_buffer[NUM_I2S_ZERO_WORDS];
sdio_queue_t i2s_zeros_queue[WS2812_DITHER_NUM];
sdio_queue_t i2s_pixels_queue[WS2812_DITHER_NUM];
};
#endif
// end of file

View File

@ -15,7 +15,7 @@ pixels = np.tile(1, (3, config.N_PIXELS))
def update():
global pixels, _prev_pixels
pixels = np.clip(pixels, 0, 255)
pixels = np.clip(pixels, 0, 255).astype(int)
m = ''
p = _gamma[pixels] if config.GAMMA_CORRECTION else np.copy(pixels)
for i in range(config.N_PIXELS):
@ -27,6 +27,19 @@ def update():
_sock.sendto(m.encode(), (config.UDP_IP, config.UDP_PORT))
# Execute this file to run a LED strand test
# If everything is working, you should see a red, green, and blue pixel scroll
# across the LED strip continously
if __name__ == '__main__':
import time
# Turn all pixels off
pixels *= 0
update()
pixels[0, 0] = 255 # Set 1st pixel red
pixels[1, 1] = 255 # Set 2nd pixel green
pixels[2, 2] = 255 # Set 3rd pixel blue
print('Starting LED strand test')
while True:
pixels = np.roll(pixels, 1, axis=1)
update()
time.sleep(0.2)

View File

@ -75,13 +75,13 @@ b_filt = dsp.ExpFilter(np.tile(0.01, config.N_PIXELS // 2),
alpha_decay=0.25, alpha_rise=0.99)
p_filt = dsp.ExpFilter(np.tile(1, (3, config.N_PIXELS // 2)),
alpha_decay=0.05, alpha_rise=0.8)
p = np.tile(1, (3, config.N_PIXELS // 2))
p = np.tile(1.0, (3, config.N_PIXELS // 2))
gain = dsp.ExpFilter(np.tile(0.01, config.N_FFT_BINS),
alpha_decay=0.001, alpha_rise=0.99)
def largest_indices(ary, n):
"""Returns the n largest indices from a numpy array."""
"""Returns indices of the n largest values in the given a numpy array"""
flat = ary.flatten()
indices = np.argpartition(flat, -n)[-n:]
indices = indices[np.argsort(-flat[indices])]
@ -89,11 +89,12 @@ def largest_indices(ary, n):
def visualize_max(y):
"""Experimental sandbox effect. Not recommended for use"""
y = np.copy(interpolate(y, config.N_PIXELS // 2)) * 255.0
ind = largest_indices(y, 15)
y[ind] *= -1
y[y > 0] = 0
y[ind] *= -1
y[ind] *= -1.0
y[y > 0] = 0.0
y[ind] *= -1.0
# Blur the color channels with different strengths
r = gaussian_filter1d(y, sigma=0.25)
g = gaussian_filter1d(y, sigma=0.10)
@ -111,14 +112,15 @@ def visualize_max(y):
led.pixels[0, :] = pixel_r
led.pixels[1, :] = pixel_g
led.pixels[2, :] = pixel_b
led.update()
# Update the GUI plots
GUI.curve[0][0].setData(y=pixel_r)
GUI.curve[0][1].setData(y=pixel_g)
GUI.curve[0][2].setData(y=pixel_b)
led.update()
def visualize_scroll(y):
"""Effect that originates in the center and scrolls outwards"""
global p
y = gaussian_filter1d(y, sigma=1.0)**3.0
y = np.copy(y)
@ -134,11 +136,17 @@ def visualize_scroll(y):
p[0, 0] = r
p[1, 0] = g
p[2, 0] = b
# Update the LED strip
led.pixels = np.concatenate((p[:, ::-1], p), axis=1)
led.update()
# Update the GUI plots
GUI.curve[0][0].setData(y=np.concatenate((p[0, :][::-1], p[0, :])))
GUI.curve[0][1].setData(y=np.concatenate((p[1, :][::-1], p[1, :])))
GUI.curve[0][2].setData(y=np.concatenate((p[2, :][::-1], p[2, :])))
def visualize_energy(y):
"""Effect that expands from the center with increasing sound energy"""
global p
y = gaussian_filter1d(y, sigma=1.0)**3.0
gain.update(y)
@ -147,22 +155,28 @@ def visualize_energy(y):
r = int(np.mean(y[:len(y) // 3]))
g = int(np.mean(y[len(y) // 3: 2 * len(y) // 3]))
b = int(np.mean(y[2 * len(y) // 3:]))
p[0, :r] = 255
p[0, r:] = 0
p[1, :g] = 255
p[1, g:] = 0
p[2, :b] = 255
p[2, b:] = 0
p[0, :r] = 255.0
p[0, r:] = 0.0
p[1, :g] = 255.0
p[1, g:] = 0.0
p[2, :b] = 255.0
p[2, b:] = 0.0
p_filt.update(p)
p = p_filt.value.astype(int)
p[0, :] = gaussian_filter1d(p[0, :], sigma=4.0)
p[1, :] = gaussian_filter1d(p[1, :], sigma=4.0)
p[2, :] = gaussian_filter1d(p[2, :], sigma=4.0)
# Update LED pixel arrays
led.pixels = np.concatenate((p[:, ::-1], p), axis=1)
led.update()
# Update the GUI plots
GUI.curve[0][0].setData(y=np.concatenate((p[0, :][::-1], p[0, :])))
GUI.curve[0][1].setData(y=np.concatenate((p[1, :][::-1], p[1, :])))
GUI.curve[0][2].setData(y=np.concatenate((p[2, :][::-1], p[2, :])))
def visualize_spectrum(y):
"""Effect that maps the Mel filterbank frequencies onto the LED strip"""
y = np.copy(interpolate(y, config.N_PIXELS // 2)) * 255.0
# Blur the color channels with different strengths
r = gaussian_filter1d(y, sigma=0.25)
@ -179,11 +193,11 @@ def visualize_spectrum(y):
led.pixels[0, :] = pixel_r
led.pixels[1, :] = pixel_g
led.pixels[2, :] = pixel_b
led.update()
# Update the GUI plots
GUI.curve[0][0].setData(y=pixel_r)
GUI.curve[0][1].setData(y=pixel_g)
GUI.curve[0][2].setData(y=pixel_b)
led.update()
mel_gain = dsp.ExpFilter(np.tile(1e-1, config.N_FFT_BINS),
@ -194,7 +208,7 @@ volume = dsp.ExpFilter(config.MIN_VOLUME_THRESHOLD,
def microphone_update(stream):
global y_roll, prev_rms, prev_exp
# Normalize new audio samples
# Retrieve and normalize the new audio samples
y = np.fromstring(stream.read(samples_per_frame,
exception_on_overflow=False), dtype=np.int16)
y = y / 2.0**15
@ -209,19 +223,25 @@ def microphone_update(stream):
led.pixels = np.tile(0, (3, config.N_PIXELS))
led.update()
else:
# Transform audio input into the frequency domain
XS, YS = dsp.fft(y_data, window=np.hamming)
# Remove half of the FFT data because of symmetry
YS = YS[:len(YS) // 2]
XS = XS[:len(XS) // 2]
# Construct a Mel filterbank from the FFT data
YS = np.atleast_2d(np.abs(YS)).T * dsp.mel_y.T
# Scale data to values more suitable for visualization
YS = np.sum(YS, axis=0)**2.0
mel = YS**0.5
mel = gaussian_filter1d(mel, sigma=1.0)
# Normalize the Mel filterbank to make it volume independent
mel_gain.update(np.max(mel))
mel = mel / mel_gain.value
visualize_spectrum(mel)
# Visualize the filterbank output
# visualize_spectrum(mel)
# visualize_max(mel)
# visualize_scroll(mel)
# visualize_energy(mel)
visualize_energy(mel)
GUI.app.processEvents()
print('FPS {:.0f} / {:.0f}'.format(frames_per_second(), config.FPS))
@ -235,8 +255,8 @@ y_roll = np.random.rand(config.N_ROLLING_HISTORY, samples_per_frame) / 1e16
if __name__ == '__main__':
import pyqtgraph as pg
# Create GUI plot for visualizing LED strip output
GUI = gui.GUI(width=800, height=400, title='Audio Visualization')
# Audio plot
GUI.add_plot('Color Channels')
r_pen = pg.mkPen((255, 30, 30, 200), width=6)
g_pen = pg.mkPen((30, 255, 30, 200), width=6)