???
This commit is contained in:
99
data/Color.h
Normal file
99
data/Color.h
Normal file
@@ -0,0 +1,99 @@
|
|||||||
|
struct Color {
|
||||||
|
|
||||||
|
uint8_t r;
|
||||||
|
uint8_t g;
|
||||||
|
uint8_t b;
|
||||||
|
|
||||||
|
/** no-init */
|
||||||
|
Color() {
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
/** Hidden ctor. RGB */
|
||||||
|
Color(const uint8_t r, const uint8_t g, const uint8_t b) : r(r), g(g), b(b) {
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
/** get X-percent [0:100] of this color = darker/brighter */
|
||||||
|
Color inline getPercent(const int percent) const {
|
||||||
|
return Color(r*percent/100, g*percent/100, b*percent/100);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** mix the two given colors */
|
||||||
|
static Color mix(const Color c1, const Color c2, int percentC1) {
|
||||||
|
return Color(
|
||||||
|
((c1.r * percentC1) + (c2.r * (100-percentC1))) / 100,
|
||||||
|
((c1.g * percentC1) + (c2.g * (100-percentC1))) / 100,
|
||||||
|
((c1.b * percentC1) + (c2.b * (100-percentC1))) / 100
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline Color fromRGB(const uint8_t r, const uint8_t g, const uint8_t b) {
|
||||||
|
return Color(r,g,b);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline Color fromHSV(const uint8_t h, const uint8_t s, const uint8_t v) {
|
||||||
|
Color c; c.setHSV(h,s,v); return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setHSV(const uint8_t h, const uint8_t s, const uint8_t v) {
|
||||||
|
|
||||||
|
uint8_t region, remainder, p, q, t;
|
||||||
|
|
||||||
|
region = h / 43;
|
||||||
|
remainder = (h - (region * 43)) * 6;
|
||||||
|
|
||||||
|
p = (v * (255 - s)) >> 8;
|
||||||
|
q = (v * (255 - ((s * remainder) >> 8))) >> 8;
|
||||||
|
t = (v * (255 - ((s * (255 - remainder)) >> 8))) >> 8;
|
||||||
|
|
||||||
|
switch (region) {
|
||||||
|
case 0:
|
||||||
|
r = v; g = t; b = p;
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
r = q; g = v; b = p;
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
r = p; g = v; b = t;
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
r = p; g = q; b = v;
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
r = t; g = p; b = v;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
r = v; g = p; b = q;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void setRGB(const uint8_t r, const uint8_t g, const uint8_t b) {
|
||||||
|
this->r = r;
|
||||||
|
this->g = g;
|
||||||
|
this->b = b;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setOff() {
|
||||||
|
this->r = 0;
|
||||||
|
this->g = 0;
|
||||||
|
this->b = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/** add two colors */
|
||||||
|
Color operator + (const Color o) const {
|
||||||
|
return Color(r+o.r, g+o.g, b+o.g);
|
||||||
|
}
|
||||||
|
|
||||||
|
Color operator * (const float f) const {
|
||||||
|
return Color(r*f, g*f, b*f);
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
223
ext/led/WS2812B.h
Normal file
223
ext/led/WS2812B.h
Normal file
@@ -0,0 +1,223 @@
|
|||||||
|
#ifndef WS2812B_H
|
||||||
|
#define WS2812B_H
|
||||||
|
|
||||||
|
#include <rom/ets_sys.h>
|
||||||
|
#include <xtensa/core-macros.h>
|
||||||
|
|
||||||
|
//#include <driver/gpio.h>
|
||||||
|
#include <driver/rmt.h>
|
||||||
|
#include <soc/rmt_struct.h>
|
||||||
|
|
||||||
|
#include "../../data/Color.h"
|
||||||
|
|
||||||
|
template <int numLEDs> class WS2812B {
|
||||||
|
|
||||||
|
static constexpr const char* TAG = "WS2812";
|
||||||
|
static constexpr rmt_channel_t chan = RMT_CHANNEL_0;
|
||||||
|
static constexpr gpio_num_t outPin = GPIO_NUM_27;
|
||||||
|
|
||||||
|
//#define portDISABLE_INTERRUPTS() do { XTOS_SET_INTLEVEL(XCHAL_EXCM_LEVEL); portbenchmarkINTERRUPT_DISABLE(); } while (0)
|
||||||
|
//#define portENABLE_INTERRUPTS() do { portbenchmarkINTERRUPT_RESTORE(0); XTOS_SET_INTLEVEL(0); } while (0)
|
||||||
|
//#define ENABLE_INTERRUPTS() portENABLE_INTERRUPTS()
|
||||||
|
//#define DISABLE_INTERRUPTS() portDISABLE_INTERRUPTS()
|
||||||
|
|
||||||
|
static constexpr int numBits = (3*8) * 2; // (r,g,b) each 8 bit NOTE! must be < 64 to fit into the buffer!
|
||||||
|
//static constexpr int numItems = 256;//(numLEDs*3)*8;
|
||||||
|
//static constexpr int numItems = (numLEDs*3)*8;
|
||||||
|
rmt_item32_t items[numBits+1]; // + a 0-terminator (just like within strings)
|
||||||
|
|
||||||
|
/** enable/disable each led */
|
||||||
|
bool enabled[numLEDs] = {true};
|
||||||
|
|
||||||
|
/** color-value for each attached LED */
|
||||||
|
Color colors[numLEDs];
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
/** ctor */
|
||||||
|
WS2812B() {
|
||||||
|
init();
|
||||||
|
}
|
||||||
|
|
||||||
|
void init() {
|
||||||
|
|
||||||
|
ESP_LOGI(TAG, "init()");
|
||||||
|
|
||||||
|
rmt_config_t cfg;
|
||||||
|
cfg.rmt_mode = RMT_MODE_TX;
|
||||||
|
cfg.channel = chan;
|
||||||
|
cfg.gpio_num = outPin;
|
||||||
|
cfg.mem_block_num = 1;//chan+1;//8-chan;//chan+1;//8 - chan; //chan+1;//
|
||||||
|
cfg.clk_div = 8;
|
||||||
|
cfg.tx_config.loop_en = false;
|
||||||
|
cfg.tx_config.carrier_en = false;
|
||||||
|
cfg.tx_config.idle_output_en = true;
|
||||||
|
cfg.tx_config.idle_level = (rmt_idle_level_t)0;
|
||||||
|
cfg.tx_config.carrier_freq_hz = 10000;
|
||||||
|
cfg.tx_config.carrier_level = (rmt_carrier_level_t)1;
|
||||||
|
cfg.tx_config.carrier_duty_percent = 50;
|
||||||
|
|
||||||
|
// allocate one block for sending (64 bits)
|
||||||
|
//ESP_ERROR_CHECK(rmt_set_mem_block_num(chan, 1));
|
||||||
|
|
||||||
|
ESP_ERROR_CHECK(rmt_config(&cfg));
|
||||||
|
ESP_ERROR_CHECK(rmt_driver_install(chan, 0, 0));
|
||||||
|
|
||||||
|
// setup constant values once
|
||||||
|
for (int idx = 0; idx < numBits; ++idx) {
|
||||||
|
items[idx].duration0 = 1;
|
||||||
|
items[idx].level0 = 1;
|
||||||
|
items[idx].duration1 = 1;
|
||||||
|
items[idx].level1 = 0;
|
||||||
|
}
|
||||||
|
//items[numBits].val = 0; // 0 terminator
|
||||||
|
|
||||||
|
ESP_LOGI(TAG, "init OK!");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/** set the color for the given LED */
|
||||||
|
void setColor(const uint8_t idx, const Color rgb) {
|
||||||
|
colors[idx] = rgb;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** set the color for all LEDs */
|
||||||
|
void setColor(const Color rgb) {
|
||||||
|
for (int idx = 0; idx < numLEDs; ++idx) {
|
||||||
|
colors[idx] = rgb;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** enable/disable the given LED */
|
||||||
|
void setEnabled(const uint8_t idx, const bool en) {
|
||||||
|
enabled[idx] = en;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** enable/disable all LEDs */
|
||||||
|
void setEnabled(const bool en) {
|
||||||
|
for (int idx = 0; idx < numLEDs; ++idx) {
|
||||||
|
enabled[idx] = en;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** is the given LED enabled? */
|
||||||
|
bool isEnabled(const uint8_t idx) const {
|
||||||
|
return enabled[idx];
|
||||||
|
}
|
||||||
|
|
||||||
|
Color& getColor(const uint8_t idx) {
|
||||||
|
return colors[idx];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/** flush configured changes */
|
||||||
|
void flush() {
|
||||||
|
|
||||||
|
ESP_LOGI(TAG, "sending %d LEDs", numLEDs);
|
||||||
|
|
||||||
|
int idx = 0;
|
||||||
|
|
||||||
|
// process each LED
|
||||||
|
for (int i = 0; i < numLEDs; ++i) {
|
||||||
|
|
||||||
|
if (enabled[i]) {
|
||||||
|
const Color c = colors[i];
|
||||||
|
emplaceByte(c.g, idx);
|
||||||
|
emplaceByte(c.r, idx);
|
||||||
|
emplaceByte(c.b, idx);
|
||||||
|
} else {
|
||||||
|
emplaceByte(0, idx);
|
||||||
|
emplaceByte(0, idx);
|
||||||
|
emplaceByte(0, idx);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (idx >= numBits) {
|
||||||
|
|
||||||
|
// wait for pervious transmission to finish
|
||||||
|
//rmt_wait_tx_done(this->chan, (TickType_t)1);
|
||||||
|
|
||||||
|
items[idx].val = 0; // 0 terminator
|
||||||
|
ESP_ERROR_CHECK(rmt_fill_tx_items(this->chan, items, numBits, 0));
|
||||||
|
ESP_ERROR_CHECK(rmt_tx_start(this->chan, true));
|
||||||
|
|
||||||
|
rmt_wait_tx_done(this->chan, (TickType_t)1);
|
||||||
|
|
||||||
|
// send within the background
|
||||||
|
//const bool blockingWait = true;
|
||||||
|
//ESP_ERROR_CHECK(rmt_write_items(this->chan, items, numItems, blockingWait));
|
||||||
|
//rmt_wait_tx_done(this->chan, (TickType_t)1);
|
||||||
|
idx = 0;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
// bit counter
|
||||||
|
int idx = 0;
|
||||||
|
|
||||||
|
// process each LED
|
||||||
|
for (int i = 0; i < numLEDs; ++i) {
|
||||||
|
|
||||||
|
if (enabled[i]) {
|
||||||
|
const Color c = colors[i];
|
||||||
|
emplaceByte(c.g, idx);
|
||||||
|
emplaceByte(c.r, idx);
|
||||||
|
emplaceByte(c.b, idx);
|
||||||
|
} else {
|
||||||
|
emplaceByte(0, idx);
|
||||||
|
emplaceByte(0, idx);
|
||||||
|
emplaceByte(0, idx);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// blocking send all bits via RMT
|
||||||
|
const bool blockingWait = true;
|
||||||
|
ESP_ERROR_CHECK(rmt_write_items(this->chan, items, numItems, blockingWait));
|
||||||
|
|
||||||
|
ESP_LOGI(TAG, "%d bits sent", idx);
|
||||||
|
*/
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
/** send one whole byte */
|
||||||
|
inline void emplaceByte(const uint8_t b, int& idx) {
|
||||||
|
if (b & 0b10000000) {emplace1(idx);} else {emplace0(idx);}
|
||||||
|
if (b & 0b01000000) {emplace1(idx);} else {emplace0(idx);}
|
||||||
|
if (b & 0b00100000) {emplace1(idx);} else {emplace0(idx);}
|
||||||
|
if (b & 0b00010000) {emplace1(idx);} else {emplace0(idx);}
|
||||||
|
if (b & 0b00001000) {emplace1(idx);} else {emplace0(idx);}
|
||||||
|
if (b & 0b00000100) {emplace1(idx);} else {emplace0(idx);}
|
||||||
|
if (b & 0b00000010) {emplace1(idx);} else {emplace0(idx);}
|
||||||
|
if (b & 0b00000001) {emplace1(idx);} else {emplace0(idx);}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** emplace a 0 (short 1 pulse, long 0 pulse) */
|
||||||
|
inline void emplace0(int& idx) {
|
||||||
|
items[idx].duration0 = 2;
|
||||||
|
//items[idx].level0 = 1;
|
||||||
|
items[idx].duration1 = 8;
|
||||||
|
//items[idx].level1 = 0;
|
||||||
|
++idx;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** emplace a 1 (long 1 pulse, short 0 pulse) */
|
||||||
|
inline void emplace1(int& idx) {
|
||||||
|
items[idx].duration0 = 7;
|
||||||
|
//items[idx].level0 = 1;
|
||||||
|
items[idx].duration1 = 3;
|
||||||
|
//items[idx].level1 = 0;
|
||||||
|
++idx;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // WS2812B_H
|
||||||
195
ext/led/WS2812B_OLD.h
Normal file
195
ext/led/WS2812B_OLD.h
Normal file
@@ -0,0 +1,195 @@
|
|||||||
|
#ifndef WS2812B_H
|
||||||
|
#define WS2812B_H
|
||||||
|
|
||||||
|
#include "driver/gpio.h"
|
||||||
|
#include "rom/ets_sys.h"
|
||||||
|
#include "xtensa/core-macros.h"
|
||||||
|
|
||||||
|
#include "../../data/Color.h"
|
||||||
|
|
||||||
|
template <int numLEDs> class WS2812B {
|
||||||
|
|
||||||
|
#define WS2812B_PIN GPIO_NUM_27
|
||||||
|
#define LED_SET_PIN_TO_OUTPUT gpio_set_direction(WS2812B_PIN, GPIO_MODE_OUTPUT);
|
||||||
|
#define LED_SET_PIN_TO_INPUT gpio_set_direction(WS2812B_PIN, GPIO_MODE_INPUT);
|
||||||
|
//#define LED_SET_PIN_H gpio_set_level(WS2812B_PIN, 1)
|
||||||
|
//#define LED_SET_PIN_L gpio_set_level(WS2812B_PIN, 0)
|
||||||
|
//#define ENABLE_INTERRUPTS XTOS_RESTORE_INTLEVEL(ilevel);
|
||||||
|
//#define DISABLE_INTERRUPTS uint32_t volatile register ilevel = XTOS_DISABLE_ALL_INTERRUPTS
|
||||||
|
|
||||||
|
|
||||||
|
#define LED_SET_PIN_H GPIO.out_w1ts |= (1 << WS2812B_PIN)
|
||||||
|
#define LED_SET_PIN_L GPIO.out_w1tc |= (1 << WS2812B_PIN)
|
||||||
|
|
||||||
|
#define portDISABLE_INTERRUPTS() do { XTOS_SET_INTLEVEL(XCHAL_EXCM_LEVEL); portbenchmarkINTERRUPT_DISABLE(); } while (0)
|
||||||
|
#define portENABLE_INTERRUPTS() do { portbenchmarkINTERRUPT_RESTORE(0); XTOS_SET_INTLEVEL(0); } while (0)
|
||||||
|
#define ENABLE_INTERRUPTS() portENABLE_INTERRUPTS()
|
||||||
|
#define DISABLE_INTERRUPTS() portDISABLE_INTERRUPTS()
|
||||||
|
#define SLEEP_US(us) ets_delay_us(us)
|
||||||
|
|
||||||
|
//#define NS_PER_TICK ( (1000ul*1000ul*1000ul) / (80ul*1000ul*1000ul) )
|
||||||
|
|
||||||
|
|
||||||
|
// Color* colors;
|
||||||
|
|
||||||
|
/** enable/disable each led */
|
||||||
|
bool enabled[numLEDs] = {true};
|
||||||
|
|
||||||
|
/** color-value for each attached LED */
|
||||||
|
Color colors[numLEDs];
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
/** ctor */
|
||||||
|
WS2812B() {
|
||||||
|
init();
|
||||||
|
}
|
||||||
|
|
||||||
|
void init() {
|
||||||
|
LED_SET_PIN_TO_OUTPUT;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** set the color for the given LED */
|
||||||
|
void setColor(const uint8_t idx, const Color rgb) {
|
||||||
|
colors[idx] = rgb;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** set the color for all LEDs */
|
||||||
|
void setColor(const Color rgb) {
|
||||||
|
for (int idx = 0; idx < numLEDs; ++idx) {
|
||||||
|
colors[idx] = rgb;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** enable/disable the given LED */
|
||||||
|
void setEnabled(const uint8_t idx, const bool en) {
|
||||||
|
enabled[idx] = en;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** enable/disable all LEDs */
|
||||||
|
void setEnabled(const bool en) {
|
||||||
|
for (int idx = 0; idx < numLEDs; ++idx) {
|
||||||
|
enabled[idx] = en;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** is the given LED enabled? */
|
||||||
|
bool isEnabled(const uint8_t idx) const {
|
||||||
|
return enabled[idx];
|
||||||
|
}
|
||||||
|
|
||||||
|
Color& getColor(const uint8_t idx) {
|
||||||
|
return colors[idx];
|
||||||
|
}
|
||||||
|
|
||||||
|
/** flush configured changes */
|
||||||
|
IRAM_ATTR void flush() {
|
||||||
|
|
||||||
|
SLEEP_US(50);
|
||||||
|
|
||||||
|
DISABLE_INTERRUPTS();
|
||||||
|
|
||||||
|
LED_SET_PIN_TO_OUTPUT;
|
||||||
|
|
||||||
|
// process each LED
|
||||||
|
for (int i = 0; i < 8; ++i) {
|
||||||
|
|
||||||
|
//if (i == 0) {LED_SET_PIN_TO_INPUT;}
|
||||||
|
//if (i > 0) {LED_SET_PIN_TO_OUTPUT;}
|
||||||
|
|
||||||
|
const Color rgb = colors[i];
|
||||||
|
|
||||||
|
// send each LEDs 24-bit GRB data
|
||||||
|
//if (enabled[i]) {
|
||||||
|
// sendByte(rgb.g);
|
||||||
|
// sendByte(rgb.r);
|
||||||
|
// sendByte(rgb.b);
|
||||||
|
//} else {
|
||||||
|
sendByte(1);
|
||||||
|
sendByte(2);
|
||||||
|
sendByte(3);
|
||||||
|
//}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
ENABLE_INTERRUPTS();
|
||||||
|
|
||||||
|
reset();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
IRAM_ATTR __attribute__((always_inline)) inline void sendByte(const uint8_t b) {
|
||||||
|
if (b & 0b10000000) {send1();} else {send0();}
|
||||||
|
if (b & 0b01000000) {send1();} else {send0();}
|
||||||
|
if (b & 0b00100000) {send1();} else {send0();}
|
||||||
|
if (b & 0b00010000) {send1();} else {send0();}
|
||||||
|
if (b & 0b00001000) {send1();} else {send0();}
|
||||||
|
if (b & 0b00000100) {send1();} else {send0();}
|
||||||
|
if (b & 0b00000010) {send1();} else {send0();}
|
||||||
|
if (b & 0b00000001) {send1();} else {send0();}
|
||||||
|
}
|
||||||
|
|
||||||
|
IRAM_ATTR __attribute__((always_inline)) inline void send1() { // 800ns high, 450ns low
|
||||||
|
LED_SET_PIN_H;
|
||||||
|
delay800();
|
||||||
|
LED_SET_PIN_L;
|
||||||
|
delay100();
|
||||||
|
}
|
||||||
|
|
||||||
|
IRAM_ATTR __attribute__((always_inline)) inline void send0() { // 400ns high, 850ns low
|
||||||
|
LED_SET_PIN_H;
|
||||||
|
delay100();
|
||||||
|
//delay100();
|
||||||
|
LED_SET_PIN_L;
|
||||||
|
delay800();
|
||||||
|
}
|
||||||
|
|
||||||
|
IRAM_ATTR __attribute__((always_inline)) inline void reset() {
|
||||||
|
LED_SET_PIN_L;
|
||||||
|
SLEEP_US(50);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//#pragma GCC optimize 0
|
||||||
|
|
||||||
|
|
||||||
|
// one NOP is:
|
||||||
|
// 80 MHz 12.5 ns
|
||||||
|
// 240 MHz 4.166 ns
|
||||||
|
IRAM_ATTR __attribute__((always_inline)) inline void delay100() {
|
||||||
|
|
||||||
|
// 24 * 4.166ns = 100ns
|
||||||
|
asm volatile("nop"); asm volatile("nop"); asm volatile("nop"); asm volatile("nop");
|
||||||
|
asm volatile("nop"); asm volatile("nop"); asm volatile("nop"); asm volatile("nop");
|
||||||
|
asm volatile("nop"); asm volatile("nop"); asm volatile("nop"); asm volatile("nop");
|
||||||
|
|
||||||
|
asm volatile("nop"); asm volatile("nop"); asm volatile("nop"); asm volatile("nop");
|
||||||
|
|
||||||
|
// for (register int i = 0; i < 24/6; ++i) {
|
||||||
|
// asm volatile("nop");
|
||||||
|
// }
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
IRAM_ATTR __attribute__((always_inline)) inline void delay800() {
|
||||||
|
|
||||||
|
delay100();
|
||||||
|
delay100();
|
||||||
|
delay100();
|
||||||
|
delay100();
|
||||||
|
delay100();
|
||||||
|
delay100();
|
||||||
|
delay100();
|
||||||
|
delay100();
|
||||||
|
|
||||||
|
// for (register int i = 0; i < 24*8/6-8; ++i) {
|
||||||
|
// asm volatile("nop");
|
||||||
|
// }
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // WS2812B_H
|
||||||
Reference in New Issue
Block a user