Merge branch 'audioreactive-prototype' into 2D-support
This commit is contained in:
commit
cf3faa1170
@ -8,7 +8,7 @@ aiofiles==0.6.0
|
||||
# via platformio
|
||||
ajsonrpc==1.1.0
|
||||
# via platformio
|
||||
bottle==0.12.19
|
||||
bottle==0.12.20
|
||||
# via platformio
|
||||
certifi==2020.12.5
|
||||
# via requests
|
||||
|
@ -406,6 +406,14 @@ class Animated_Staircase : public Usermod {
|
||||
}
|
||||
}
|
||||
|
||||
void appendConfigData() {
|
||||
//oappend(SET_F("dd=addDropdown('staircase','selectfield');"));
|
||||
//oappend(SET_F("addOption(dd,'1st value',0);"));
|
||||
//oappend(SET_F("addOption(dd,'2nd value',1);"));
|
||||
//oappend(SET_F("addInfo('staircase:selectfield',1,'additional info');")); // 0 is field type, 1 is actual field
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Writes the configuration to internal flash memory.
|
||||
*/
|
||||
@ -458,15 +466,11 @@ class Animated_Staircase : public Usermod {
|
||||
|
||||
useUSSensorTop = top[FPSTR(_useTopUltrasoundSensor)] | useUSSensorTop;
|
||||
topPIRorTriggerPin = top[FPSTR(_topPIRorTrigger_pin)] | topPIRorTriggerPin;
|
||||
// topPIRorTriggerPin = min(33,max(-1,(int)topPIRorTriggerPin)); // bounds check
|
||||
topEchoPin = top[FPSTR(_topEcho_pin)] | topEchoPin;
|
||||
// topEchoPin = min(39,max(-1,(int)topEchoPin)); // bounds check
|
||||
|
||||
useUSSensorBottom = top[FPSTR(_useBottomUltrasoundSensor)] | useUSSensorBottom;
|
||||
bottomPIRorTriggerPin = top[FPSTR(_bottomPIRorTrigger_pin)] | bottomPIRorTriggerPin;
|
||||
// bottomPIRorTriggerPin = min(33,max(-1,(int)bottomPIRorTriggerPin)); // bounds check
|
||||
bottomEchoPin = top[FPSTR(_bottomEcho_pin)] | bottomEchoPin;
|
||||
// bottomEchoPin = min(39,max(-1,(int)bottomEchoPin)); // bounds check
|
||||
|
||||
topMaxDist = top[FPSTR(_topEchoCm)] | topMaxDist;
|
||||
topMaxDist = min(150,max(30,(int)topMaxDist)); // max distnace ~1.5m (a lag of 9ms may be expected)
|
||||
@ -504,22 +508,22 @@ class Animated_Staircase : public Usermod {
|
||||
* tab of the web-UI.
|
||||
*/
|
||||
void addToJsonInfo(JsonObject& root) {
|
||||
JsonObject staircase = root["u"];
|
||||
if (staircase.isNull()) {
|
||||
staircase = root.createNestedObject("u");
|
||||
JsonObject user = root["u"];
|
||||
if (user.isNull()) {
|
||||
user = root.createNestedObject("u");
|
||||
}
|
||||
|
||||
JsonArray usermodEnabled = staircase.createNestedArray(F("Staircase")); // name
|
||||
String btn = F("<button class=\"btn infobtn\" onclick=\"requestJson({staircase:{enabled:");
|
||||
if (enabled) {
|
||||
btn += F("false}});\">");
|
||||
btn += F("enabled");
|
||||
} else {
|
||||
btn += F("true}});\">");
|
||||
btn += F("disabled");
|
||||
}
|
||||
btn += F("</button>");
|
||||
usermodEnabled.add(btn); // value
|
||||
JsonArray infoArr = user.createNestedArray(FPSTR(_name)); // name
|
||||
|
||||
String uiDomString = F("<button class=\"btn btn-xs\" onclick=\"requestJson({");
|
||||
uiDomString += FPSTR(_name);
|
||||
uiDomString += F(":{");
|
||||
uiDomString += FPSTR(_enabled);
|
||||
uiDomString += enabled ? F(":false}});\">") : F(":true}});\">");
|
||||
uiDomString += F("<i class=\"icons ");
|
||||
uiDomString += enabled ? "on" : "off";
|
||||
uiDomString += F("\"></i></button>");
|
||||
infoArr.add(uiDomString);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -275,20 +275,9 @@ public:
|
||||
JsonObject user = root["u"];
|
||||
if (user.isNull()) user = root.createNestedObject("u");
|
||||
|
||||
String uiDomString = F("<button class=\"btn\" onclick=\"requestJson({");
|
||||
uiDomString += FPSTR(_name);
|
||||
uiDomString += F(":{");
|
||||
uiDomString += FPSTR(_enabled);
|
||||
if (enabled) {
|
||||
uiDomString += F(":false}});\">");
|
||||
uiDomString += F("PIR <i class=\"icons\"></i>");
|
||||
} else {
|
||||
uiDomString += F(":true}});\">");
|
||||
uiDomString += F("PIR <i class=\"icons\"></i>");
|
||||
}
|
||||
uiDomString += F("</button>");
|
||||
JsonArray infoArr = user.createNestedArray(uiDomString); // timer value
|
||||
JsonArray infoArr = user.createNestedArray(FPSTR(_name));
|
||||
|
||||
String uiDomString;
|
||||
if (enabled) {
|
||||
if (offTimerStart > 0)
|
||||
{
|
||||
@ -322,6 +311,20 @@ public:
|
||||
infoArr.add(F("disabled"));
|
||||
}
|
||||
|
||||
uiDomString = F(" <button class=\"btn btn-xs\" onclick=\"requestJson({");
|
||||
uiDomString += FPSTR(_name);
|
||||
uiDomString += F(":{");
|
||||
uiDomString += FPSTR(_enabled);
|
||||
if (enabled) {
|
||||
uiDomString += F(":false}});\">");
|
||||
uiDomString += F("<i class=\"icons on\"></i>");
|
||||
} else {
|
||||
uiDomString += F(":true}});\">");
|
||||
uiDomString += F("<i class=\"icons off\"></i>");
|
||||
}
|
||||
uiDomString += F("</button>");
|
||||
infoArr.add(uiDomString);
|
||||
|
||||
JsonObject sensor = root[F("sensor")];
|
||||
if (sensor.isNull()) sensor = root.createNestedObject(F("sensor"));
|
||||
sensor[F("motion")] = sensorPinState || offTimerStart>0 ? true : false;
|
||||
|
1220
usermods/audioreactive/audio_reactive.h
Normal file
1220
usermods/audioreactive/audio_reactive.h
Normal file
File diff suppressed because it is too large
Load Diff
426
usermods/audioreactive/audio_source.h
Normal file
426
usermods/audioreactive/audio_source.h
Normal file
@ -0,0 +1,426 @@
|
||||
#pragma once
|
||||
|
||||
#include <Wire.h>
|
||||
#include "wled.h"
|
||||
#include <driver/i2s.h>
|
||||
|
||||
/* ToDo: remove. ES7243 is controlled via compiler defines
|
||||
Until this configuration is moved to the webinterface
|
||||
*/
|
||||
|
||||
// data type requested from the I2S driver - currently we always use 32bit
|
||||
//#define I2S_USE_16BIT_SAMPLES // (experimental) define this to request 16bit - more efficient but possibly less compatible
|
||||
#ifdef I2S_USE_16BIT_SAMPLES
|
||||
#define I2S_SAMPLE_RESOLUTION I2S_BITS_PER_SAMPLE_16BIT
|
||||
#define I2S_datatype int16_t
|
||||
#undef I2S_SAMPLE_DOWNSCALE_TO_16BIT
|
||||
#else
|
||||
#define I2S_SAMPLE_RESOLUTION I2S_BITS_PER_SAMPLE_32BIT
|
||||
#define I2S_datatype int32_t
|
||||
#define I2S_SAMPLE_DOWNSCALE_TO_16BIT
|
||||
#endif
|
||||
|
||||
|
||||
/* Interface class
|
||||
AudioSource serves as base class for all microphone types
|
||||
This enables accessing all microphones with one single interface
|
||||
which simplifies the caller code
|
||||
*/
|
||||
class AudioSource {
|
||||
public:
|
||||
/* All public methods are virtual, so they can be overridden
|
||||
Everything but the destructor is also removed, to make sure each mic
|
||||
Implementation provides its version of this function
|
||||
*/
|
||||
virtual ~AudioSource() {};
|
||||
|
||||
/* Initialize
|
||||
This function needs to take care of anything that needs to be done
|
||||
before samples can be obtained from the microphone.
|
||||
*/
|
||||
virtual void initialize(int8_t = I2S_PIN_NO_CHANGE, int8_t = I2S_PIN_NO_CHANGE, int8_t = I2S_PIN_NO_CHANGE, int8_t = I2S_PIN_NO_CHANGE, int8_t = I2S_PIN_NO_CHANGE, int8_t = I2S_PIN_NO_CHANGE) = 0;
|
||||
|
||||
/* Deinitialize
|
||||
Release all resources and deactivate any functionality that is used
|
||||
by this microphone
|
||||
*/
|
||||
virtual void deinitialize() = 0;
|
||||
|
||||
/* getSamples
|
||||
Read num_samples from the microphone, and store them in the provided
|
||||
buffer
|
||||
*/
|
||||
virtual void getSamples(double *buffer, uint16_t num_samples) = 0;
|
||||
|
||||
/* Get an up-to-date sample without DC offset */
|
||||
virtual int getSampleWithoutDCOffset() { return _sampleNoDCOffset; };
|
||||
|
||||
protected:
|
||||
// Private constructor, to make sure it is not callable except from derived classes
|
||||
AudioSource(int sampleRate, int blockSize, int16_t lshift, uint32_t mask) :
|
||||
_sampleRate(sampleRate),
|
||||
_blockSize(blockSize),
|
||||
_sampleNoDCOffset(0),
|
||||
_dcOffset(0.0f),
|
||||
_shift(lshift),
|
||||
_mask(mask),
|
||||
_initialized(false)
|
||||
{};
|
||||
|
||||
int _sampleRate; // Microphone sampling rate
|
||||
int _blockSize; // I2S block size
|
||||
volatile int _sampleNoDCOffset; // Up-to-date sample without DCOffset
|
||||
float _dcOffset; // Rolling average DC offset
|
||||
int16_t _shift; // Shift obtained samples to the right (positive) or left(negative) by this amount
|
||||
uint32_t _mask; // Bitmask for sample data after shifting. Bitmask 0X0FFF means that we need to convert 12bit ADC samples from unsigned to signed
|
||||
bool _initialized; // Gets set to true if initialization is successful
|
||||
};
|
||||
|
||||
/* Basic I2S microphone source
|
||||
All functions are marked virtual, so derived classes can replace them
|
||||
*/
|
||||
class I2SSource : public AudioSource {
|
||||
public:
|
||||
I2SSource(int sampleRate, int blockSize, int16_t lshift, uint32_t mask) :
|
||||
AudioSource(sampleRate, blockSize, lshift, mask) {
|
||||
_config = {
|
||||
.mode = i2s_mode_t(I2S_MODE_MASTER | I2S_MODE_RX),
|
||||
.sample_rate = _sampleRate,
|
||||
.bits_per_sample = I2S_SAMPLE_RESOLUTION,
|
||||
.channel_format = I2S_CHANNEL_FMT_ONLY_LEFT,
|
||||
.communication_format = i2s_comm_format_t(I2S_COMM_FORMAT_I2S | I2S_COMM_FORMAT_I2S_MSB),
|
||||
.intr_alloc_flags = ESP_INTR_FLAG_LEVEL1,
|
||||
.dma_buf_count = 8,
|
||||
.dma_buf_len = _blockSize
|
||||
};
|
||||
}
|
||||
|
||||
virtual void initialize(int8_t i2swsPin = I2S_PIN_NO_CHANGE, int8_t i2ssdPin = I2S_PIN_NO_CHANGE, int8_t i2sckPin = I2S_PIN_NO_CHANGE, int8_t mclkPin = I2S_PIN_NO_CHANGE, int8_t = I2S_PIN_NO_CHANGE, int8_t = I2S_PIN_NO_CHANGE) {
|
||||
if (i2swsPin != I2S_PIN_NO_CHANGE && i2ssdPin != I2S_PIN_NO_CHANGE) {
|
||||
if (!pinManager.allocatePin(i2swsPin, true, PinOwner::UM_Audioreactive) ||
|
||||
!pinManager.allocatePin(i2ssdPin, false, PinOwner::UM_Audioreactive)) { // #206
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// i2ssckPin needs special treatment, since it might be unused on PDM mics
|
||||
if (i2sckPin != I2S_PIN_NO_CHANGE) {
|
||||
if (!pinManager.allocatePin(i2sckPin, true, PinOwner::UM_Audioreactive)) return;
|
||||
} else {
|
||||
// This is an I2S PDM microphone, these microphones only use a clock and
|
||||
// data line, to make it simpler to debug, use the WS pin as CLK and SD
|
||||
// pin as DATA
|
||||
_config.mode = i2s_mode_t(I2S_MODE_MASTER | I2S_MODE_RX | I2S_MODE_PDM); // Change mode to pdm if clock pin not provided
|
||||
}
|
||||
|
||||
// Reserve the master clock pin if provided
|
||||
_mclkPin = mclkPin;
|
||||
if (mclkPin != I2S_PIN_NO_CHANGE) {
|
||||
if(!pinManager.allocatePin(mclkPin, true, PinOwner::UM_Audioreactive)) return;
|
||||
_routeMclk(mclkPin);
|
||||
}
|
||||
|
||||
_pinConfig = {
|
||||
.bck_io_num = i2sckPin,
|
||||
.ws_io_num = i2swsPin,
|
||||
.data_out_num = I2S_PIN_NO_CHANGE,
|
||||
.data_in_num = i2ssdPin
|
||||
};
|
||||
|
||||
esp_err_t err = i2s_driver_install(I2S_NUM_0, &_config, 0, nullptr);
|
||||
if (err != ESP_OK) {
|
||||
DEBUGSR_PRINTF("Failed to install i2s driver: %d\n", err);
|
||||
return;
|
||||
}
|
||||
|
||||
err = i2s_set_pin(I2S_NUM_0, &_pinConfig);
|
||||
if (err != ESP_OK) {
|
||||
DEBUGSR_PRINTF("Failed to set i2s pin config: %d\n", err);
|
||||
return;
|
||||
}
|
||||
|
||||
_initialized = true;
|
||||
}
|
||||
|
||||
virtual void deinitialize() {
|
||||
_initialized = false;
|
||||
esp_err_t err = i2s_driver_uninstall(I2S_NUM_0);
|
||||
if (err != ESP_OK) {
|
||||
DEBUGSR_PRINTF("Failed to uninstall i2s driver: %d\n", err);
|
||||
return;
|
||||
}
|
||||
if (_pinConfig.ws_io_num != I2S_PIN_NO_CHANGE) pinManager.deallocatePin(_pinConfig.ws_io_num, PinOwner::UM_Audioreactive);
|
||||
if (_pinConfig.data_in_num != I2S_PIN_NO_CHANGE) pinManager.deallocatePin(_pinConfig.data_in_num, PinOwner::UM_Audioreactive);
|
||||
if (_pinConfig.bck_io_num != I2S_PIN_NO_CHANGE) pinManager.deallocatePin(_pinConfig.bck_io_num, PinOwner::UM_Audioreactive);
|
||||
// Release the master clock pin
|
||||
if (_mclkPin != I2S_PIN_NO_CHANGE) pinManager.deallocatePin(_mclkPin, PinOwner::UM_Audioreactive);
|
||||
}
|
||||
|
||||
virtual void getSamples(double *buffer, uint16_t num_samples) {
|
||||
if (_initialized) {
|
||||
esp_err_t err;
|
||||
size_t bytes_read = 0; /* Counter variable to check if we actually got enough data */
|
||||
I2S_datatype newSamples[num_samples]; /* Intermediary sample storage */
|
||||
|
||||
// Reset dc offset
|
||||
_dcOffset = 0.0f;
|
||||
|
||||
err = i2s_read(I2S_NUM_0, (void *)newSamples, sizeof(newSamples), &bytes_read, portMAX_DELAY);
|
||||
if (err != ESP_OK) {
|
||||
DEBUGSR_PRINTF("Failed to get samples: %d\n", err);
|
||||
return;
|
||||
}
|
||||
|
||||
// For correct operation, we need to read exactly sizeof(samples) bytes from i2s
|
||||
if (bytes_read != sizeof(newSamples)) {
|
||||
DEBUGSR_PRINTF("Failed to get enough samples: wanted: %d read: %d\n", sizeof(newSamples), bytes_read);
|
||||
return;
|
||||
}
|
||||
|
||||
// Store samples in sample buffer and update DC offset
|
||||
for (int i = 0; i < num_samples; i++) {
|
||||
// pre-shift samples down to 16bit
|
||||
#ifdef I2S_SAMPLE_DOWNSCALE_TO_16BIT
|
||||
if (_shift != 0)
|
||||
newSamples[i] >>= 16;
|
||||
#endif
|
||||
double currSample = 0.0;
|
||||
if(_shift > 0)
|
||||
currSample = (double) (newSamples[i] >> _shift);
|
||||
else {
|
||||
if(_shift < 0)
|
||||
currSample = (double) (newSamples[i] << (- _shift)); // need to "pump up" 12bit ADC to full 16bit as delivered by other digital mics
|
||||
else
|
||||
#ifdef I2S_SAMPLE_DOWNSCALE_TO_16BIT
|
||||
currSample = (double) newSamples[i] / 65536.0; // _shift == 0 -> use the chance to keep lower 16bits
|
||||
#else
|
||||
currSample = (double) newSamples[i];
|
||||
#endif
|
||||
}
|
||||
buffer[i] = currSample;
|
||||
_dcOffset = ((_dcOffset * 31) + currSample) / 32;
|
||||
}
|
||||
|
||||
// Update no-DC sample
|
||||
_sampleNoDCOffset = buffer[num_samples - 1] - _dcOffset;
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
void _routeMclk(int8_t mclkPin) {
|
||||
/* Enable the mclk routing depending on the selected mclk pin
|
||||
Only I2S_NUM_0 is supported
|
||||
*/
|
||||
if (mclkPin == GPIO_NUM_0) {
|
||||
PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO0_U, FUNC_GPIO0_CLK_OUT1);
|
||||
WRITE_PERI_REG(PIN_CTRL,0xFFF0);
|
||||
} else if (mclkPin == GPIO_NUM_1) {
|
||||
PIN_FUNC_SELECT(PERIPHS_IO_MUX_U0TXD_U, FUNC_U0TXD_CLK_OUT3);
|
||||
WRITE_PERI_REG(PIN_CTRL, 0xF0F0);
|
||||
} else {
|
||||
PIN_FUNC_SELECT(PERIPHS_IO_MUX_U0RXD_U, FUNC_U0RXD_CLK_OUT2);
|
||||
WRITE_PERI_REG(PIN_CTRL, 0xFF00);
|
||||
}
|
||||
}
|
||||
|
||||
i2s_config_t _config;
|
||||
i2s_pin_config_t _pinConfig;
|
||||
int8_t _mclkPin;
|
||||
};
|
||||
|
||||
/* ES7243 Microphone
|
||||
This is an I2S microphone that requires ininitialization over
|
||||
I2C before I2S data can be received
|
||||
*/
|
||||
class ES7243 : public I2SSource {
|
||||
private:
|
||||
// I2C initialization functions for ES7243
|
||||
void _es7243I2cBegin() {
|
||||
Wire.begin(pin_ES7243_SDA, pin_ES7243_SCL, 100000U);
|
||||
}
|
||||
|
||||
void _es7243I2cWrite(uint8_t reg, uint8_t val) {
|
||||
#ifndef ES7243_ADDR
|
||||
Wire.beginTransmission(0x13);
|
||||
#else
|
||||
Wire.beginTransmission(ES7243_ADDR);
|
||||
#endif
|
||||
Wire.write((uint8_t)reg);
|
||||
Wire.write((uint8_t)val);
|
||||
Wire.endTransmission();
|
||||
}
|
||||
|
||||
void _es7243InitAdc() {
|
||||
_es7243I2cBegin();
|
||||
_es7243I2cWrite(0x00, 0x01);
|
||||
_es7243I2cWrite(0x06, 0x00);
|
||||
_es7243I2cWrite(0x05, 0x1B);
|
||||
_es7243I2cWrite(0x01, 0x00); // 0x00 for 24 bit to match INMP441 - not sure if this needs adjustment to get 16bit samples from I2S
|
||||
_es7243I2cWrite(0x08, 0x43);
|
||||
_es7243I2cWrite(0x05, 0x13);
|
||||
}
|
||||
|
||||
public:
|
||||
ES7243(int sampleRate, int blockSize, int16_t lshift, uint32_t mask) :
|
||||
I2SSource(sampleRate, blockSize, lshift, mask) {
|
||||
_config.channel_format = I2S_CHANNEL_FMT_ONLY_RIGHT;
|
||||
};
|
||||
|
||||
void initialize(int8_t sdaPin, int8_t sclPin, int8_t i2swsPin, int8_t i2ssdPin, int8_t i2sckPin, int8_t mclkPin) {
|
||||
// Reserve SDA and SCL pins of the I2C interface
|
||||
if (!pinManager.allocatePin(sdaPin, true, PinOwner::HW_I2C) ||
|
||||
!pinManager.allocatePin(sclPin, true, PinOwner::HW_I2C)) {
|
||||
return;
|
||||
}
|
||||
|
||||
pin_ES7243_SDA = sdaPin;
|
||||
pin_ES7243_SCL = sclPin;
|
||||
|
||||
// First route mclk, then configure ADC over I2C, then configure I2S
|
||||
_es7243InitAdc();
|
||||
I2SSource::initialize(i2swsPin, i2ssdPin, i2sckPin, mclkPin);
|
||||
}
|
||||
|
||||
void deinitialize() {
|
||||
// Release SDA and SCL pins of the I2C interface
|
||||
pinManager.deallocatePin(pin_ES7243_SDA, PinOwner::HW_I2C);
|
||||
pinManager.deallocatePin(pin_ES7243_SCL, PinOwner::HW_I2C);
|
||||
I2SSource::deinitialize();
|
||||
}
|
||||
|
||||
private:
|
||||
int8_t pin_ES7243_SDA;
|
||||
int8_t pin_ES7243_SCL;
|
||||
};
|
||||
|
||||
/* ADC over I2S Microphone
|
||||
This microphone is an ADC pin sampled via the I2S interval
|
||||
This allows to use the I2S API to obtain ADC samples with high sample rates
|
||||
without the need of manual timing of the samples
|
||||
*/
|
||||
class I2SAdcSource : public I2SSource {
|
||||
public:
|
||||
I2SAdcSource(int sampleRate, int blockSize, int16_t lshift, uint32_t mask) :
|
||||
I2SSource(sampleRate, blockSize, lshift, mask) {
|
||||
_config = {
|
||||
.mode = i2s_mode_t(I2S_MODE_MASTER | I2S_MODE_RX | I2S_MODE_ADC_BUILT_IN),
|
||||
.sample_rate = _sampleRate,
|
||||
.bits_per_sample = I2S_SAMPLE_RESOLUTION,
|
||||
.channel_format = I2S_CHANNEL_FMT_ONLY_LEFT,
|
||||
.communication_format = i2s_comm_format_t(I2S_COMM_FORMAT_I2S | I2S_COMM_FORMAT_I2S_MSB),
|
||||
.intr_alloc_flags = ESP_INTR_FLAG_LEVEL2,
|
||||
.dma_buf_count = 8,
|
||||
.dma_buf_len = _blockSize
|
||||
};
|
||||
}
|
||||
|
||||
void initialize(int8_t audioPin, int8_t = I2S_PIN_NO_CHANGE, int8_t = I2S_PIN_NO_CHANGE, int8_t = I2S_PIN_NO_CHANGE, int8_t = I2S_PIN_NO_CHANGE, int8_t = I2S_PIN_NO_CHANGE) {
|
||||
if(!pinManager.allocatePin(audioPin, false, PinOwner::UM_Audioreactive)) {
|
||||
return;
|
||||
}
|
||||
_audioPin = audioPin;
|
||||
|
||||
// Determine Analog channel. Only Channels on ADC1 are supported
|
||||
int8_t channel = digitalPinToAnalogChannel(_audioPin);
|
||||
if (channel > 9) {
|
||||
DEBUGSR_PRINTF("Incompatible GPIO used for audio in: %d\n", _audioPin);
|
||||
return;
|
||||
} else {
|
||||
adc_gpio_init(ADC_UNIT_1, adc_channel_t(channel));
|
||||
}
|
||||
|
||||
// Install Driver
|
||||
esp_err_t err = i2s_driver_install(I2S_NUM_0, &_config, 0, nullptr);
|
||||
if (err != ESP_OK) {
|
||||
DEBUGSR_PRINTF("Failed to install i2s driver: %d\n", err);
|
||||
return;
|
||||
}
|
||||
|
||||
// Enable I2S mode of ADC
|
||||
err = i2s_set_adc_mode(ADC_UNIT_1, adc1_channel_t(channel));
|
||||
if (err != ESP_OK) {
|
||||
DEBUGSR_PRINTF("Failed to set i2s adc mode: %d\n", err);
|
||||
return;
|
||||
}
|
||||
|
||||
#if defined(ARDUINO_ARCH_ESP32)
|
||||
// according to docs from espressif, the ADC needs to be started explicitly
|
||||
// fingers crossed
|
||||
err = i2s_adc_enable(I2S_NUM_0);
|
||||
if (err != ESP_OK) {
|
||||
DEBUGSR_PRINTF("Failed to enable i2s adc: %d\n", err);
|
||||
//return;
|
||||
}
|
||||
#endif
|
||||
|
||||
_initialized = true;
|
||||
}
|
||||
|
||||
void getSamples(double *buffer, uint16_t num_samples) {
|
||||
/* Enable ADC. This has to be enabled and disabled directly before and
|
||||
* after sampling, otherwise Wifi dies
|
||||
*/
|
||||
if (_initialized) {
|
||||
#if !defined(ARDUINO_ARCH_ESP32)
|
||||
// old code - works for me without enable/disable, at least on ESP32.
|
||||
esp_err_t err = i2s_adc_enable(I2S_NUM_0);
|
||||
//esp_err_t err = i2s_start(I2S_NUM_0);
|
||||
if (err != ESP_OK) {
|
||||
DEBUGSR_PRINTF("Failed to enable i2s adc: %d\n", err);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
I2SSource::getSamples(buffer, num_samples);
|
||||
|
||||
#if !defined(ARDUINO_ARCH_ESP32)
|
||||
// old code - works for me without enable/disable, at least on ESP32.
|
||||
err = i2s_adc_disable(I2S_NUM_0);
|
||||
//err = i2s_stop(I2S_NUM_0);
|
||||
if (err != ESP_OK) {
|
||||
DEBUGSR_PRINTF("Failed to disable i2s adc: %d\n", err);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void deinitialize() {
|
||||
pinManager.deallocatePin(_audioPin, PinOwner::UM_Audioreactive);
|
||||
_initialized = false;
|
||||
esp_err_t err;
|
||||
#if defined(ARDUINO_ARCH_ESP32)
|
||||
// according to docs from espressif, the ADC needs to be stopped explicitly
|
||||
// fingers crossed
|
||||
err = i2s_adc_disable(I2S_NUM_0);
|
||||
if (err != ESP_OK) {
|
||||
DEBUGSR_PRINTF("Failed to disable i2s adc: %d\n", err);
|
||||
//return;
|
||||
}
|
||||
#endif
|
||||
err = i2s_driver_uninstall(I2S_NUM_0);
|
||||
if (err != ESP_OK) {
|
||||
DEBUGSR_PRINTF("Failed to uninstall i2s driver: %d\n", err);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
int8_t _audioPin;
|
||||
};
|
||||
|
||||
/* SPH0645 Microphone
|
||||
This is an I2S microphone with some timing quirks that need
|
||||
special consideration.
|
||||
*/
|
||||
class SPH0654 : public I2SSource {
|
||||
public:
|
||||
SPH0654(int sampleRate, int blockSize, int16_t lshift, uint32_t mask) :
|
||||
I2SSource(sampleRate, blockSize, lshift, mask)
|
||||
{}
|
||||
|
||||
void initialize(uint8_t i2swsPin, uint8_t i2ssdPin, uint8_t i2sckPin, int8_t = I2S_PIN_NO_CHANGE, int8_t = I2S_PIN_NO_CHANGE, int8_t = I2S_PIN_NO_CHANGE) {
|
||||
I2SSource::initialize(i2swsPin, i2ssdPin, i2sckPin);
|
||||
REG_SET_BIT(I2S_TIMING_REG(I2S_NUM_0), BIT(9));
|
||||
REG_SET_BIT(I2S_CONF_REG(I2S_NUM_0), I2S_RX_MSB_SHIFT);
|
||||
}
|
||||
};
|
34
usermods/audioreactive/readme.md
Normal file
34
usermods/audioreactive/readme.md
Normal file
@ -0,0 +1,34 @@
|
||||
# Audioreactive usermod
|
||||
|
||||
This usermod allows controlling LEDs using audio input. Audio input can be either microphone or analog-in (AUX) using appropriate adapter.
|
||||
Supported microphones range from analog (MAX4466, MAX9814, ...) to digital (INMP441, ICS-43434, ...).
|
||||
|
||||
The usermod does audio processing and provides data structure that specially written effect can use.
|
||||
|
||||
The usermod **does not** provide effects or draws anything to LED strip/matrix.
|
||||
|
||||
## Installation
|
||||
|
||||
Add `-D USERMOD_AUDIOREACTIVE` to your PlatformIO environment as well as `arduinoFFT @ 1.5.6` to your `lib_deps`.
|
||||
If you are not using PlatformIO (which you should) try adding `#define USERMOD_AUDIOREACTIVE` to *my_config.h* and make sure you have _arduinoFFT_ library downloaded and installed.
|
||||
|
||||
## Configuration
|
||||
|
||||
All parameters are runtime configurable though some may require hard boot after change (I2S microphone or selected GPIOs).
|
||||
|
||||
If you want to define default GPIOs during compile time use the following (default values in parentheses):
|
||||
|
||||
- `DMTYPE=x` : defines digital microphone type: 0=analog, 1=generic I2S, 2=ES7243 I2S, 3=SPH0645 I2S, 4=generic I2S with master clock, 5=PDM I2S
|
||||
- `AUDIOPIN=x` : GPIO for analog microphone/AUX-in (36)
|
||||
- `I2S_SDPIN=x` : GPIO for SD pin on digital mcrophone (32)
|
||||
- `I2S_WSPIN=x` : GPIO for WS pin on digital mcrophone (15)
|
||||
- `I2S_CKPIN=x` : GPIO for SCK pin on digital mcrophone (14)
|
||||
- `ES7243_SDAPIN` : GPIO for I2C SDA pin on ES7243 microphone (-1)
|
||||
- `ES7243_SCLPIN` : GPIO for I2C SCL pin on ES7243 microphone (-1)
|
||||
- `MCLK_PIN=x` : GPIO for master clock pin on digital mcrophone (-1)
|
||||
|
||||
**NOTE** Due to the fact that usermod uses I2S peripherial for analog audio sampling, use of analog *buttons* (i.e. potentiometers) is disabled while running this usermod with analog microphone.
|
||||
|
||||
## Release notes
|
||||
|
||||
2022-06 Ported from [soundreactive](https://github.com/atuline/WLED) by @blazoncek (AKA Blaz Kristan)
|
@ -468,13 +468,17 @@ class MultiRelay : public Usermod {
|
||||
if (user.isNull())
|
||||
user = root.createNestedObject("u");
|
||||
|
||||
JsonArray infoArr = user.createNestedArray(F("Number of relays")); //name
|
||||
JsonArray infoArr = user.createNestedArray(FPSTR(_name)); //name
|
||||
infoArr.add(String(getActiveRelayCount()));
|
||||
infoArr.add(F(" relays"));
|
||||
|
||||
String uiDomString;
|
||||
for (uint8_t i=0; i<MULTI_RELAY_MAX_RELAYS; i++) {
|
||||
if (_relay[i].pin<0 || !_relay[i].external) continue;
|
||||
uiDomString = F("<button class=\"btn\" onclick=\"requestJson({");
|
||||
uiDomString = F("Relay "); uiDomString += i;
|
||||
JsonArray infoArr = user.createNestedArray(uiDomString); // timer value
|
||||
|
||||
uiDomString = F("<button class=\"btn btn-xs\" onclick=\"requestJson({");
|
||||
uiDomString += FPSTR(_name);
|
||||
uiDomString += F(":{");
|
||||
uiDomString += FPSTR(_relay_str);
|
||||
@ -483,12 +487,10 @@ class MultiRelay : public Usermod {
|
||||
uiDomString += F(",on:");
|
||||
uiDomString += _relay[i].state ? "false" : "true";
|
||||
uiDomString += F("}});\">");
|
||||
uiDomString += F("Relay ");
|
||||
uiDomString += i;
|
||||
uiDomString += F(" <i class=\"icons\"></i></button>");
|
||||
JsonArray infoArr = user.createNestedArray(uiDomString); // timer value
|
||||
|
||||
infoArr.add(_relay[i].state ? "on" : "off");
|
||||
uiDomString += F("<i class=\"icons");
|
||||
uiDomString += _relay[i].state ? F(" on") : F(" off");
|
||||
uiDomString += F("\"></i></button>");
|
||||
infoArr.add(uiDomString);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -581,6 +581,10 @@ class FourLineDisplayUsermod : public Usermod {
|
||||
// remove "* " from dynamic palettes
|
||||
for (byte i=2; i<=printedChars; i++) lineBuffer[i-2] = lineBuffer[i]; //include '\0'
|
||||
printedChars -= 2;
|
||||
} else if ((lineBuffer[0]==' ' && lineBuffer[1]>127)) {
|
||||
// remove note symbol from effect names
|
||||
for (byte i=5; i<=printedChars; i++) lineBuffer[i-5] = lineBuffer[i]; //include '\0'
|
||||
printedChars -= 5;
|
||||
}
|
||||
if (lineHeight == 2) { // use this code for 8 line display
|
||||
char smallBuffer1[MAX_MODE_LINE_SPACE];
|
||||
@ -941,6 +945,23 @@ class FourLineDisplayUsermod : public Usermod {
|
||||
// if (!initDone) return; // prevent crash on boot applyPreset()
|
||||
//}
|
||||
|
||||
void appendConfigData() {
|
||||
oappend(SET_F("dd=addDropdown('4LineDisplay','type');"));
|
||||
oappend(SET_F("addOption(dd,'None',0);"));
|
||||
oappend(SET_F("addOption(dd,'SSD1306',1);"));
|
||||
oappend(SET_F("addOption(dd,'SH1106',2);"));
|
||||
oappend(SET_F("addOption(dd,'SSD1306 128x64',3);"));
|
||||
oappend(SET_F("addOption(dd,'SSD1305',4);"));
|
||||
oappend(SET_F("addOption(dd,'SSD1305 128x64',5);"));
|
||||
oappend(SET_F("addOption(dd,'SSD1306 SPI',6);"));
|
||||
oappend(SET_F("addOption(dd,'SSD1306 SPI 128x64',7);"));
|
||||
oappend(SET_F("addInfo('4LineDisplay:pin[]',0,'I2C/SPI CLK');"));
|
||||
oappend(SET_F("addInfo('4LineDisplay:pin[]',1,'I2C/SPI DTA');"));
|
||||
oappend(SET_F("addInfo('4LineDisplay:pin[]',2,'SPI CS');"));
|
||||
oappend(SET_F("addInfo('4LineDisplay:pin[]',3,'SPI DC');"));
|
||||
oappend(SET_F("addInfo('4LineDisplay:pin[]',4,'SPI RST');"));
|
||||
}
|
||||
|
||||
/*
|
||||
* addToConfig() can be used to add custom persistent settings to the cfg.json file in the "um" (usermod) object.
|
||||
* It will be called by WLED when settings are actually saved (for example, LED settings are saved)
|
||||
@ -960,9 +981,7 @@ class FourLineDisplayUsermod : public Usermod {
|
||||
top[FPSTR(_enabled)] = enabled;
|
||||
JsonArray io_pin = top.createNestedArray("pin");
|
||||
for (byte i=0; i<5; i++) io_pin.add(ioPin[i]);
|
||||
top["help4Pins"] = F("Clk,Data,CS,DC,RST"); // help for Settings page
|
||||
top["type"] = type;
|
||||
top["help4Type"] = F("1=SSD1306,2=SH1106,3=SSD1306_128x64,4=SSD1305,5=SSD1305_128x64,6=SSD1306_SPI,7=SSD1306_SPI_128x64"); // help for Settings page
|
||||
top[FPSTR(_flip)] = (bool) flip;
|
||||
top[FPSTR(_contrast)] = contrast;
|
||||
top[FPSTR(_contrastFix)] = (bool) contrastFix;
|
||||
|
@ -186,7 +186,7 @@ private:
|
||||
*/
|
||||
void sortModesAndPalettes() {
|
||||
//modes_qstrings = re_findModeStrings(JSON_mode_names, strip.getModeCount());
|
||||
modes_qstrings = WS2812FX::_modeData;
|
||||
modes_qstrings = strip.getModeDataSrc();
|
||||
modes_alpha_indexes = re_initIndexArray(strip.getModeCount());
|
||||
re_sortModes(modes_qstrings, modes_alpha_indexes, strip.getModeCount(), MODE_SORT_SKIP_COUNT);
|
||||
|
||||
|
@ -6,8 +6,30 @@ The mod takes the colors from the first few pixels and sends them to the lights.
|
||||
|
||||
## Configuration
|
||||
|
||||
First, enter how often the data will be sent to the lights (in ms).
|
||||
|
||||
Then enter the IPs for the lights to be controlled, in order. There is currently a limit of 10 devices that can be controled, but that number
|
||||
- Interval (ms)
|
||||
- How frequently to update the WiZ lights, in milliseconds.
|
||||
- Setting too low may causse ESP to become unresponsive.
|
||||
- Send Delay (ms)
|
||||
- An optional millisecond delay after updating each WiZ light.
|
||||
- Can help smooth out effects when using a larger number of WiZ lights
|
||||
- Use Enhanced White
|
||||
- Enables using the WiZ lights onboard white LEDs instead of sending maximum RGB values.
|
||||
- Tunable with warm and cool LEDs as supported by WiZ bulbs
|
||||
- Note: Only sent when max RGB value is set, need to have automatic brightness limiter disabled
|
||||
- ToDo: Have better logic for white value mixing to better take advantage of the lights capabilities
|
||||
- Always Force Update
|
||||
- Can be enabled to always send update message to light, even when color matches what was previously sent.
|
||||
- Force update every x minutes
|
||||
- Configuration option to allow adjusting the default force update timeout of 5 minutes.
|
||||
- Setting to 0 has the same impact as enabling Always Force Update
|
||||
-
|
||||
Then enter the IPs for the lights to be controlled, in order. There is currently a limit of 15 devices that can be controled, but that number
|
||||
can be easily changed by updating _MAX_WIZ_LIGHTS_.
|
||||
|
||||
|
||||
|
||||
|
||||
## Related project
|
||||
|
||||
If you use these lights and python, make sure to check out the [pywizlight](https://github.com/sbidy/pywizlight) project. I learned how to
|
||||
format the messages to control the lights from that project.
|
||||
|
@ -4,18 +4,66 @@
|
||||
#include <WiFiUdp.h>
|
||||
|
||||
// Maximum number of lights supported
|
||||
#define MAX_WIZ_LIGHTS 10
|
||||
#define MAX_WIZ_LIGHTS 15
|
||||
|
||||
// UDP object, to send messages
|
||||
WiFiUDP UDP;
|
||||
|
||||
// Function to send a color to a light
|
||||
void sendColor(IPAddress ip, uint32_t color) {
|
||||
|
||||
|
||||
|
||||
class WizLightsUsermod : public Usermod {
|
||||
|
||||
private:
|
||||
unsigned long lastTime = 0;
|
||||
long updateInterval;
|
||||
long sendDelay;
|
||||
|
||||
long forceUpdateMinutes;
|
||||
bool forceUpdate;
|
||||
|
||||
bool useEnhancedWhite;
|
||||
long warmWhite;
|
||||
long coldWhite;
|
||||
|
||||
IPAddress lightsIP[MAX_WIZ_LIGHTS]; // Stores Light IP addresses
|
||||
bool lightsValid[MAX_WIZ_LIGHTS]; // Stores Light IP address validity
|
||||
uint32_t colorsSent[MAX_WIZ_LIGHTS]; // Stores last color sent for each light
|
||||
|
||||
|
||||
|
||||
public:
|
||||
|
||||
|
||||
|
||||
// Send JSON blob to WiZ Light over UDP
|
||||
// RGB or C/W white
|
||||
// TODO:
|
||||
// Better utilize WLED existing white mixing logic
|
||||
void wizSendColor(IPAddress ip, uint32_t color) {
|
||||
UDP.beginPacket(ip, 38899);
|
||||
|
||||
// If no LED color, turn light off. Note wiz light setting for "Off fade-out" will be applied by the light itself.
|
||||
if (color == 0) {
|
||||
UDP.print("{\"method\":\"setPilot\",\"params\":{\"state\":false}}");
|
||||
|
||||
// If color is WHITE, try and use the lights WHITE LEDs instead of mixing RGB LEDs
|
||||
} else if (color == 16777215 && useEnhancedWhite){
|
||||
|
||||
// set cold white light only
|
||||
if (coldWhite > 0 && warmWhite == 0){
|
||||
UDP.print("{\"method\":\"setPilot\",\"params\":{\"c\":"); UDP.print(coldWhite) ;UDP.print("}}");}
|
||||
|
||||
// set warm white light only
|
||||
if (warmWhite > 0 && coldWhite == 0){
|
||||
UDP.print("{\"method\":\"setPilot\",\"params\":{\"w\":"); UDP.print(warmWhite) ;UDP.print("}}");}
|
||||
|
||||
// set combination of warm and cold white light
|
||||
if (coldWhite > 0 && warmWhite > 0){
|
||||
UDP.print("{\"method\":\"setPilot\",\"params\":{\"c\":"); UDP.print(coldWhite) ;UDP.print(",\"w\":"); UDP.print(warmWhite); UDP.print("}}");}
|
||||
|
||||
// Send color as RGB
|
||||
} else {
|
||||
UDP.print("{\"method\":\"setPilot\",\"params\":{\"state\":true, \"r\":");
|
||||
UDP.print("{\"method\":\"setPilot\",\"params\":{\"r\":");
|
||||
UDP.print(R(color));
|
||||
UDP.print(",\"g\":");
|
||||
UDP.print(G(color));
|
||||
@ -23,98 +71,67 @@ void sendColor(IPAddress ip, uint32_t color) {
|
||||
UDP.print(B(color));
|
||||
UDP.print("}}");
|
||||
}
|
||||
|
||||
UDP.endPacket();
|
||||
}
|
||||
}
|
||||
|
||||
// Create label for the usermode page (I cannot make it work with JSON arrays...)
|
||||
String getJsonLabel(uint8_t i) {
|
||||
return "ip_light_" + String(i);
|
||||
}
|
||||
|
||||
class WizLightsUsermod : public Usermod {
|
||||
private:
|
||||
// Keep track of the last time the lights were updated
|
||||
unsigned long lastTime = 0;
|
||||
|
||||
// Specify how often WLED sends data to the Wiz lights
|
||||
long updateInterval;
|
||||
|
||||
// Save the IP of the lights
|
||||
IPAddress lightsIP[MAX_WIZ_LIGHTS];
|
||||
bool lightsValid[MAX_WIZ_LIGHTS];
|
||||
|
||||
// Variable that keeps track of RBG values for the lights
|
||||
uint32_t colorsSent[MAX_WIZ_LIGHTS];
|
||||
|
||||
public:
|
||||
//Functions called by WLED
|
||||
|
||||
/*
|
||||
* loop() is called continuously. Here you can check for events, read sensors, etc.
|
||||
*/
|
||||
// TODO: Check millis() rollover
|
||||
void loop() {
|
||||
// Calculate how long since the last update
|
||||
|
||||
// Make sure we are connected first
|
||||
if (!WLED_CONNECTED) return;
|
||||
|
||||
unsigned long ellapsedTime = millis() - lastTime;
|
||||
|
||||
if (ellapsedTime > updateInterval) {
|
||||
// Keep track of whether we are updating any of the lights
|
||||
bool update = false;
|
||||
|
||||
// Loop through the lights
|
||||
for (uint8_t i = 0; i < MAX_WIZ_LIGHTS; i++) {
|
||||
// Check if we have a valid IP
|
||||
if (!lightsValid[i]) { continue; }
|
||||
|
||||
// Get the first colors in the strip
|
||||
uint32_t new_color = strip.getPixelColor(i);
|
||||
|
||||
// Check if the color has changed from the last one sent
|
||||
// Force an update every 5 minutes, in case the colors don't change
|
||||
// (the lights could have been reset by turning off and on)
|
||||
if ((new_color != colorsSent[i]) | (ellapsedTime > 5*60000)) {
|
||||
// It has changed, send the new color to the light
|
||||
uint32_t newColor = strip.getPixelColor(i);
|
||||
if (forceUpdate || (newColor != colorsSent[i]) || (ellapsedTime > forceUpdateMinutes*60000)){
|
||||
wizSendColor(lightsIP[i], newColor);
|
||||
colorsSent[i] = newColor;
|
||||
update = true;
|
||||
sendColor(lightsIP[i], new_color);
|
||||
colorsSent[i] = new_color;
|
||||
delay(sendDelay);
|
||||
}
|
||||
}
|
||||
if (update) lastTime = millis();
|
||||
}
|
||||
}
|
||||
|
||||
// We sent an update, wait until we do this again
|
||||
if (update) {
|
||||
lastTime = millis();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* addToConfig() can be used to add custom persistent settings to the cfg.json file in the "um" (usermod) object.
|
||||
* It will be called by WLED when settings are actually saved (for example, LED settings are saved)
|
||||
* If you want to force saving the current state, use serializeConfig() in your loop().
|
||||
*/
|
||||
|
||||
void addToConfig(JsonObject& root)
|
||||
{
|
||||
JsonObject top = root.createNestedObject("wizLightsUsermod");
|
||||
top["interval_ms"] = updateInterval;
|
||||
top["Interval (ms)"] = updateInterval;
|
||||
top["Send Delay (ms)"] = sendDelay;
|
||||
top["Use Enhanced White *"] = useEnhancedWhite;
|
||||
top["* Warm White Value (0-255)"] = warmWhite;
|
||||
top["* Cold White Value (0-255)"] = coldWhite;
|
||||
top["Always Force Update"] = forceUpdate;
|
||||
top["Force Update Every x Minutes"] = forceUpdateMinutes;
|
||||
|
||||
for (uint8_t i = 0; i < MAX_WIZ_LIGHTS; i++) {
|
||||
top[getJsonLabel(i)] = lightsIP[i].toString();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* readFromConfig() can be used to read back the custom settings you added with addToConfig().
|
||||
* This is called by WLED when settings are loaded (currently this only happens immediately after boot, or after saving on the Usermod Settings page)
|
||||
*/
|
||||
|
||||
|
||||
bool readFromConfig(JsonObject& root)
|
||||
{
|
||||
// default settings values could be set here (or below using the 3-argument getJsonValue()) instead of in the class definition or constructor
|
||||
// setting them inside readFromConfig() is slightly more robust, handling the rare but plausible use case of single value being missing after boot (e.g. if the cfg.json was manually edited and a value was removed)
|
||||
|
||||
JsonObject top = root["wizLightsUsermod"];
|
||||
|
||||
bool configComplete = !top.isNull();
|
||||
|
||||
// Read interval to update the lights
|
||||
configComplete &= getJsonValue(top["interval_ms"], updateInterval, 1000);
|
||||
configComplete &= getJsonValue(top["Interval (ms)"], updateInterval, 1000); // How frequently to update the wiz lights
|
||||
configComplete &= getJsonValue(top["Send Delay (ms)"], sendDelay, 0); // Optional delay after sending each UDP message
|
||||
configComplete &= getJsonValue(top["Use Enhanced White *"], useEnhancedWhite, false); // When color is white use wiz white LEDs instead of mixing RGB
|
||||
configComplete &= getJsonValue(top["* Warm White Value (0-255)"], warmWhite, 0); // Warm White LED value for Enhanced White
|
||||
configComplete &= getJsonValue(top["* Cold White Value (0-255)"], coldWhite, 50); // Cold White LED value for Enhanced White
|
||||
configComplete &= getJsonValue(top["Always Force Update"], forceUpdate, false); // Update wiz light every loop, even if color value has not changed
|
||||
configComplete &= getJsonValue(top["Force Update Every x Minutes"], forceUpdateMinutes, 5); // Update wiz light if color value has not changed, every x minutes
|
||||
|
||||
// Read list of IPs
|
||||
String tempIp;
|
||||
@ -123,20 +140,15 @@ class WizLightsUsermod : public Usermod {
|
||||
lightsValid[i] = lightsIP[i].fromString(tempIp);
|
||||
|
||||
// If the IP is not valid, force the value to be empty
|
||||
if (!lightsValid[i]) {
|
||||
lightsIP[i].fromString("0.0.0.0");
|
||||
}
|
||||
if (!lightsValid[i]){lightsIP[i].fromString("0.0.0.0");}
|
||||
}
|
||||
|
||||
return configComplete;
|
||||
}
|
||||
|
||||
/*
|
||||
* getId() allows you to optionally give your V2 usermod an unique ID (please define it in const.h!).
|
||||
* This could be used in the future for the system to determine whether your usermod is installed.
|
||||
*/
|
||||
uint16_t getId()
|
||||
{
|
||||
return USERMOD_ID_WIZLIGHTS;
|
||||
}
|
||||
|
||||
// Create label for the usermod page (I cannot make it work with JSON arrays...)
|
||||
String getJsonLabel(uint8_t i) {return "WiZ Light IP #" + String(i+1);}
|
||||
|
||||
uint16_t getId(){return USERMOD_ID_WIZLIGHTS;}
|
||||
};
|
2370
wled00/FX.cpp
2370
wled00/FX.cpp
File diff suppressed because it is too large
Load Diff
474
wled00/FX.h
474
wled00/FX.h
@ -238,37 +238,125 @@
|
||||
#define FX_MODE_BLENDS 115
|
||||
#define FX_MODE_TV_SIMULATOR 116
|
||||
#define FX_MODE_DYNAMIC_SMOOTH 117
|
||||
#define FX_MODE_BLACK_HOLE 118
|
||||
#define FX_MODE_DNA 119
|
||||
#define FX_MODE_DNA_SPIRAL 120
|
||||
#define FX_MODE_DRIFT 121
|
||||
#define FX_MODE_FIRENOISE 122
|
||||
#define FX_MODE_FRIZZLES 123
|
||||
#define FX_MODE_HIPNOTIC 124
|
||||
#define FX_MODE_LISSAJOUS 125
|
||||
#define FX_MODE_MATRIX 126
|
||||
#define FX_MODE_AKEMI 127
|
||||
#define FX_MODE_COLORED_BURSTS 128
|
||||
#define FX_MODE_GAMEOFLIFE 129
|
||||
#define FX_MODE_JULIA 130
|
||||
#define FX_MODE_MEATBALS 131
|
||||
#define FX_MODE_2DNOISE 132
|
||||
#define FX_MODE_PLASMA_BALL 133
|
||||
#define FX_MODE_POLAR_LIGHTS 134
|
||||
#define FX_MODE_PULSER 135
|
||||
#define FX_MODE_SINDOTS 136
|
||||
#define FX_MODE_SQUARED_SWIRL 137
|
||||
#define FX_MODE_SUN_RADIATION 138
|
||||
#define FX_MODE_TARTAN 139
|
||||
#define FX_MODE_WAVERLY 140
|
||||
#define FX_MODE_SPACESHIPS 141
|
||||
#define FX_MODE_CRAZYBEES 142
|
||||
#define FX_MODE_GHOST_RIDER 143
|
||||
#define FX_MODE_BLOBS 144
|
||||
#define FX_MODE_SCROLL_TEXT 145
|
||||
#define FX_MODE_DRFIT_ROSE 146
|
||||
// new 2D effects
|
||||
#define FX_MODE_SPACESHIPS 118
|
||||
#define FX_MODE_CRAZYBEES 119
|
||||
#define FX_MODE_GHOST_RIDER 120
|
||||
#define FX_MODE_BLOBS 121
|
||||
#define FX_MODE_SCROLL_TEXT 122
|
||||
#define FX_MODE_DRIFT_ROSE 123
|
||||
|
||||
#define MODE_COUNT 147
|
||||
// WLED-SR effects
|
||||
#ifndef USERMOD_AUDIOREACTIVE
|
||||
|
||||
#define FX_MODE_PERLINMOVE 53 // should be moved to 53
|
||||
#define FX_MODE_FLOWSTRIPE 114 // should be moved to 114
|
||||
#define FX_MODE_WAVESINS 48 // should be moved to 48
|
||||
#define FX_MODE_2DBLACKHOLE 124 // non audio
|
||||
#define FX_MODE_2DDNASPIRAL 125 // non audio
|
||||
#define FX_MODE_2DHIPHOTIC 126 // non audio
|
||||
#define FX_MODE_2DPLASMABALL 127 // non audio
|
||||
#define FX_MODE_2DSINDOTS 128 // non audio
|
||||
#define FX_MODE_PIXELWAVE 129 // audio enhanced
|
||||
#define FX_MODE_JUGGLES 130 // audio enhanced
|
||||
#define FX_MODE_MATRIPIX 131 // audio enhanced
|
||||
#define FX_MODE_GRAVIMETER 132 // audio enhanced
|
||||
#define FX_MODE_PLASMOID 133 // audio enhanced
|
||||
#define FX_MODE_PUDDLES 134 // audio enhanced
|
||||
#define FX_MODE_MIDNOISE 135 // audio enhanced
|
||||
#define FX_MODE_NOISEMETER 136 // audio enhanced
|
||||
#define FX_MODE_2DFRIZZLES 137 // non audio
|
||||
#define FX_MODE_2DLISSAJOUS 138 // non audio
|
||||
#define FX_MODE_2DPOLARLIGHTS 139 // non audio
|
||||
#define FX_MODE_2DTARTAN 140 // non audio
|
||||
#define FX_MODE_2DGAMEOFLIFE 141 // non audio
|
||||
#define FX_MODE_2DJULIA 142 // non audio
|
||||
#define FX_MODE_NOISEFIRE 143 // audio enhanced
|
||||
#define FX_MODE_PUDDLEPEAK 144 // audio enhanced
|
||||
#define FX_MODE_2DCOLOREDBURSTS 145 // non audio
|
||||
#define FX_MODE_2DSUNRADIATION 146 // non audio
|
||||
#define FX_MODE_2DNOISE 147 // non audio
|
||||
#define FX_MODE_RIPPLEPEAK 148 // audio enhanced
|
||||
#define FX_MODE_2DFIRENOISE 149 // non audio
|
||||
#define FX_MODE_2DSQUAREDSWIRL 150 // non audio
|
||||
#define FX_MODE_2DDNA 151 // non audio
|
||||
#define FX_MODE_2DMATRIX 152 // non audio
|
||||
#define FX_MODE_2DMETABALLS 153 // non audio
|
||||
#define FX_MODE_2DPULSER 154 // non audio
|
||||
#define FX_MODE_2DDRIFT 155 // non audio
|
||||
#define FX_MODE_2DWAVERLY 156 // audio enhanced
|
||||
#define FX_MODE_GRAVCENTER 157 // audio enhanced
|
||||
#define FX_MODE_GRAVCENTRIC 158 // audio enhanced
|
||||
#define FX_MODE_2DSWIRL 159 // audio enhanced
|
||||
#define FX_MODE_2DAKEMI 160 // audio enhanced
|
||||
|
||||
#define MODE_COUNT 161
|
||||
|
||||
#else
|
||||
|
||||
#define FX_MODE_PIXELS 128
|
||||
#define FX_MODE_PIXELWAVE 129 // audio enhanced
|
||||
#define FX_MODE_JUGGLES 130 // audio enhanced
|
||||
#define FX_MODE_MATRIPIX 131 // audio enhanced
|
||||
#define FX_MODE_GRAVIMETER 132 // audio enhanced
|
||||
#define FX_MODE_PLASMOID 133 // audio enhanced
|
||||
#define FX_MODE_PUDDLES 134 // audio enhanced
|
||||
#define FX_MODE_MIDNOISE 135 // audio enhanced
|
||||
#define FX_MODE_NOISEMETER 136 // audio enhanced
|
||||
#define FX_MODE_FREQWAVE 137
|
||||
#define FX_MODE_FREQMATRIX 138
|
||||
#define FX_MODE_2DGEQ 139
|
||||
#define FX_MODE_WATERFALL 140
|
||||
#define FX_MODE_FREQPIXELS 141
|
||||
#define FX_MODE_BINMAP 142
|
||||
#define FX_MODE_NOISEFIRE 143 // audio enhanced
|
||||
#define FX_MODE_PUDDLEPEAK 144 // audio enhanced
|
||||
#define FX_MODE_NOISEMOVE 145
|
||||
#define FX_MODE_2DNOISE 146 // non audio
|
||||
#define FX_MODE_PERLINMOVE 147 // should be moved to 53
|
||||
#define FX_MODE_RIPPLEPEAK 148 // audio enhanced
|
||||
#define FX_MODE_2DFIRENOISE 149 // non audio
|
||||
#define FX_MODE_2DSQUAREDSWIRL 150 // non audio
|
||||
//#define FX_MODE_2DFIRE2012 151 // implemented in native Fire2012
|
||||
#define FX_MODE_2DDNA 152 // non audio
|
||||
#define FX_MODE_2DMATRIX 153 // non audio
|
||||
#define FX_MODE_2DMETABALLS 154 // non audio
|
||||
#define FX_MODE_FREQMAP 155
|
||||
#define FX_MODE_GRAVCENTER 156 // audio enhanced
|
||||
#define FX_MODE_GRAVCENTRIC 157 // audio enhanced
|
||||
#define FX_MODE_GRAVFREQ 158
|
||||
#define FX_MODE_DJLIGHT 159
|
||||
#define FX_MODE_2DFUNKYPLANK 160
|
||||
#define FX_MODE_2DCENTERBARS 161 // obsolete by X & Y mirroring
|
||||
#define FX_MODE_2DPULSER 162 // non audio
|
||||
#define FX_MODE_BLURZ 163
|
||||
#define FX_MODE_2DDRIFT 164 // non audio
|
||||
#define FX_MODE_2DWAVERLY 165 // audio enhanced
|
||||
#define FX_MODE_2DSUNRADIATION 166 // non audio
|
||||
#define FX_MODE_2DCOLOREDBURSTS 167 // non audio
|
||||
#define FX_MODE_2DJULIA 168 // non audio
|
||||
#define FX_MODE_2DPOOLNOISE 169 // reserved in JSON_mode_names
|
||||
#define FX_MODE_2DTWISTER 170 // reserved in JSON_mode_names
|
||||
#define FX_MODE_2DCAELEMENTATY 171 // reserved in JSON_mode_names
|
||||
#define FX_MODE_2DGAMEOFLIFE 172 // non audio
|
||||
#define FX_MODE_2DTARTAN 173 // non audio
|
||||
#define FX_MODE_2DPOLARLIGHTS 174 // non audio
|
||||
#define FX_MODE_2DSWIRL 175 // audio enhanced
|
||||
#define FX_MODE_2DLISSAJOUS 176 // non audio
|
||||
#define FX_MODE_2DFRIZZLES 177 // non audio
|
||||
#define FX_MODE_2DPLASMABALL 178 // non audio
|
||||
#define FX_MODE_FLOWSTRIPE 179 // should be moved to 114
|
||||
#define FX_MODE_2DHIPHOTIC 180 // non audio
|
||||
#define FX_MODE_2DSINDOTS 181 // non audio
|
||||
#define FX_MODE_2DDNASPIRAL 182 // non audio
|
||||
#define FX_MODE_2DBLACKHOLE 183 // non audio
|
||||
#define FX_MODE_WAVESINS 184 // should be moved to 48
|
||||
#define FX_MODE_ROCKTAVES 185
|
||||
#define FX_MODE_2DAKEMI 186 // audio enhanced
|
||||
//#define FX_MODE_CUSTOMEFFECT 187 //WLEDSR Custom Effects
|
||||
|
||||
#define MODE_COUNT 187
|
||||
#endif
|
||||
|
||||
|
||||
class WS2812FX {
|
||||
@ -281,9 +369,6 @@ class WS2812FX {
|
||||
|
||||
public:
|
||||
|
||||
// mode (effect) name and its slider control data array
|
||||
static const char *_modeData[];
|
||||
|
||||
// segment parameters
|
||||
typedef struct Segment { // 35 (36 in memory) bytes
|
||||
uint16_t start; // start index / start X coordinate 2D (left)
|
||||
@ -519,155 +604,7 @@ class WS2812FX {
|
||||
|
||||
WS2812FX() {
|
||||
WS2812FX::instance = this;
|
||||
//assign each member of the _mode[] array to its respective function reference
|
||||
_mode[FX_MODE_STATIC] = &WS2812FX::mode_static;
|
||||
_mode[FX_MODE_BLINK] = &WS2812FX::mode_blink;
|
||||
_mode[FX_MODE_COLOR_WIPE] = &WS2812FX::mode_color_wipe;
|
||||
_mode[FX_MODE_COLOR_WIPE_RANDOM] = &WS2812FX::mode_color_wipe_random;
|
||||
_mode[FX_MODE_RANDOM_COLOR] = &WS2812FX::mode_random_color;
|
||||
_mode[FX_MODE_COLOR_SWEEP] = &WS2812FX::mode_color_sweep;
|
||||
_mode[FX_MODE_DYNAMIC] = &WS2812FX::mode_dynamic;
|
||||
_mode[FX_MODE_RAINBOW] = &WS2812FX::mode_rainbow;
|
||||
_mode[FX_MODE_RAINBOW_CYCLE] = &WS2812FX::mode_rainbow_cycle;
|
||||
_mode[FX_MODE_SCAN] = &WS2812FX::mode_scan;
|
||||
_mode[FX_MODE_DUAL_SCAN] = &WS2812FX::mode_dual_scan;
|
||||
_mode[FX_MODE_FADE] = &WS2812FX::mode_fade;
|
||||
_mode[FX_MODE_THEATER_CHASE] = &WS2812FX::mode_theater_chase;
|
||||
_mode[FX_MODE_THEATER_CHASE_RAINBOW] = &WS2812FX::mode_theater_chase_rainbow;
|
||||
_mode[FX_MODE_SAW] = &WS2812FX::mode_saw;
|
||||
_mode[FX_MODE_TWINKLE] = &WS2812FX::mode_twinkle;
|
||||
_mode[FX_MODE_DISSOLVE] = &WS2812FX::mode_dissolve;
|
||||
_mode[FX_MODE_DISSOLVE_RANDOM] = &WS2812FX::mode_dissolve_random;
|
||||
_mode[FX_MODE_SPARKLE] = &WS2812FX::mode_sparkle;
|
||||
_mode[FX_MODE_FLASH_SPARKLE] = &WS2812FX::mode_flash_sparkle;
|
||||
_mode[FX_MODE_HYPER_SPARKLE] = &WS2812FX::mode_hyper_sparkle;
|
||||
_mode[FX_MODE_STROBE] = &WS2812FX::mode_strobe;
|
||||
_mode[FX_MODE_STROBE_RAINBOW] = &WS2812FX::mode_strobe_rainbow;
|
||||
_mode[FX_MODE_MULTI_STROBE] = &WS2812FX::mode_multi_strobe;
|
||||
_mode[FX_MODE_BLINK_RAINBOW] = &WS2812FX::mode_blink_rainbow;
|
||||
_mode[FX_MODE_ANDROID] = &WS2812FX::mode_android;
|
||||
_mode[FX_MODE_CHASE_COLOR] = &WS2812FX::mode_chase_color;
|
||||
_mode[FX_MODE_CHASE_RANDOM] = &WS2812FX::mode_chase_random;
|
||||
_mode[FX_MODE_CHASE_RAINBOW] = &WS2812FX::mode_chase_rainbow;
|
||||
_mode[FX_MODE_CHASE_FLASH] = &WS2812FX::mode_chase_flash;
|
||||
_mode[FX_MODE_CHASE_FLASH_RANDOM] = &WS2812FX::mode_chase_flash_random;
|
||||
_mode[FX_MODE_CHASE_RAINBOW_WHITE] = &WS2812FX::mode_chase_rainbow_white;
|
||||
_mode[FX_MODE_COLORFUL] = &WS2812FX::mode_colorful;
|
||||
_mode[FX_MODE_TRAFFIC_LIGHT] = &WS2812FX::mode_traffic_light;
|
||||
_mode[FX_MODE_COLOR_SWEEP_RANDOM] = &WS2812FX::mode_color_sweep_random;
|
||||
_mode[FX_MODE_RUNNING_COLOR] = &WS2812FX::mode_running_color;
|
||||
_mode[FX_MODE_AURORA] = &WS2812FX::mode_aurora;
|
||||
_mode[FX_MODE_RUNNING_RANDOM] = &WS2812FX::mode_running_random;
|
||||
_mode[FX_MODE_LARSON_SCANNER] = &WS2812FX::mode_larson_scanner;
|
||||
_mode[FX_MODE_COMET] = &WS2812FX::mode_comet;
|
||||
_mode[FX_MODE_FIREWORKS] = &WS2812FX::mode_fireworks;
|
||||
_mode[FX_MODE_RAIN] = &WS2812FX::mode_rain;
|
||||
_mode[FX_MODE_TETRIX] = &WS2812FX::mode_tetrix;
|
||||
_mode[FX_MODE_FIRE_FLICKER] = &WS2812FX::mode_fire_flicker;
|
||||
_mode[FX_MODE_GRADIENT] = &WS2812FX::mode_gradient;
|
||||
_mode[FX_MODE_LOADING] = &WS2812FX::mode_loading;
|
||||
_mode[FX_MODE_POLICE] = &WS2812FX::mode_police;
|
||||
_mode[FX_MODE_FAIRY] = &WS2812FX::mode_fairy;
|
||||
_mode[FX_MODE_TWO_DOTS] = &WS2812FX::mode_two_dots;
|
||||
_mode[FX_MODE_FAIRYTWINKLE] = &WS2812FX::mode_fairytwinkle;
|
||||
_mode[FX_MODE_RUNNING_DUAL] = &WS2812FX::mode_running_dual;
|
||||
_mode[FX_MODE_HALLOWEEN] = &WS2812FX::mode_halloween;
|
||||
_mode[FX_MODE_TRICOLOR_CHASE] = &WS2812FX::mode_tricolor_chase;
|
||||
_mode[FX_MODE_TRICOLOR_WIPE] = &WS2812FX::mode_tricolor_wipe;
|
||||
_mode[FX_MODE_TRICOLOR_FADE] = &WS2812FX::mode_tricolor_fade;
|
||||
_mode[FX_MODE_BREATH] = &WS2812FX::mode_breath;
|
||||
_mode[FX_MODE_RUNNING_LIGHTS] = &WS2812FX::mode_running_lights;
|
||||
_mode[FX_MODE_LIGHTNING] = &WS2812FX::mode_lightning;
|
||||
_mode[FX_MODE_ICU] = &WS2812FX::mode_icu;
|
||||
_mode[FX_MODE_MULTI_COMET] = &WS2812FX::mode_multi_comet;
|
||||
_mode[FX_MODE_DUAL_LARSON_SCANNER] = &WS2812FX::mode_dual_larson_scanner;
|
||||
_mode[FX_MODE_RANDOM_CHASE] = &WS2812FX::mode_random_chase;
|
||||
_mode[FX_MODE_OSCILLATE] = &WS2812FX::mode_oscillate;
|
||||
_mode[FX_MODE_FIRE_2012] = &WS2812FX::mode_fire_2012;
|
||||
_mode[FX_MODE_PRIDE_2015] = &WS2812FX::mode_pride_2015;
|
||||
_mode[FX_MODE_BPM] = &WS2812FX::mode_bpm;
|
||||
_mode[FX_MODE_JUGGLE] = &WS2812FX::mode_juggle;
|
||||
_mode[FX_MODE_PALETTE] = &WS2812FX::mode_palette;
|
||||
_mode[FX_MODE_COLORWAVES] = &WS2812FX::mode_colorwaves;
|
||||
_mode[FX_MODE_FILLNOISE8] = &WS2812FX::mode_fillnoise8;
|
||||
_mode[FX_MODE_NOISE16_1] = &WS2812FX::mode_noise16_1;
|
||||
_mode[FX_MODE_NOISE16_2] = &WS2812FX::mode_noise16_2;
|
||||
_mode[FX_MODE_NOISE16_3] = &WS2812FX::mode_noise16_3;
|
||||
_mode[FX_MODE_NOISE16_4] = &WS2812FX::mode_noise16_4;
|
||||
_mode[FX_MODE_COLORTWINKLE] = &WS2812FX::mode_colortwinkle;
|
||||
_mode[FX_MODE_LAKE] = &WS2812FX::mode_lake;
|
||||
_mode[FX_MODE_METEOR] = &WS2812FX::mode_meteor;
|
||||
_mode[FX_MODE_METEOR_SMOOTH] = &WS2812FX::mode_meteor_smooth;
|
||||
_mode[FX_MODE_RAILWAY] = &WS2812FX::mode_railway;
|
||||
_mode[FX_MODE_RIPPLE] = &WS2812FX::mode_ripple;
|
||||
_mode[FX_MODE_TWINKLEFOX] = &WS2812FX::mode_twinklefox;
|
||||
_mode[FX_MODE_TWINKLECAT] = &WS2812FX::mode_twinklecat;
|
||||
_mode[FX_MODE_HALLOWEEN_EYES] = &WS2812FX::mode_halloween_eyes;
|
||||
_mode[FX_MODE_STATIC_PATTERN] = &WS2812FX::mode_static_pattern;
|
||||
_mode[FX_MODE_TRI_STATIC_PATTERN] = &WS2812FX::mode_tri_static_pattern;
|
||||
_mode[FX_MODE_SPOTS] = &WS2812FX::mode_spots;
|
||||
_mode[FX_MODE_SPOTS_FADE] = &WS2812FX::mode_spots_fade;
|
||||
_mode[FX_MODE_GLITTER] = &WS2812FX::mode_glitter;
|
||||
_mode[FX_MODE_CANDLE] = &WS2812FX::mode_candle;
|
||||
_mode[FX_MODE_STARBURST] = &WS2812FX::mode_starburst;
|
||||
_mode[FX_MODE_EXPLODING_FIREWORKS] = &WS2812FX::mode_exploding_fireworks;
|
||||
_mode[FX_MODE_BOUNCINGBALLS] = &WS2812FX::mode_bouncing_balls;
|
||||
_mode[FX_MODE_SINELON] = &WS2812FX::mode_sinelon;
|
||||
_mode[FX_MODE_SINELON_DUAL] = &WS2812FX::mode_sinelon_dual;
|
||||
_mode[FX_MODE_SINELON_RAINBOW] = &WS2812FX::mode_sinelon_rainbow;
|
||||
_mode[FX_MODE_POPCORN] = &WS2812FX::mode_popcorn;
|
||||
_mode[FX_MODE_DRIP] = &WS2812FX::mode_drip;
|
||||
_mode[FX_MODE_PLASMA] = &WS2812FX::mode_plasma;
|
||||
_mode[FX_MODE_PERCENT] = &WS2812FX::mode_percent;
|
||||
_mode[FX_MODE_RIPPLE_RAINBOW] = &WS2812FX::mode_ripple_rainbow;
|
||||
_mode[FX_MODE_HEARTBEAT] = &WS2812FX::mode_heartbeat;
|
||||
_mode[FX_MODE_PACIFICA] = &WS2812FX::mode_pacifica;
|
||||
_mode[FX_MODE_CANDLE_MULTI] = &WS2812FX::mode_candle_multi;
|
||||
_mode[FX_MODE_SOLID_GLITTER] = &WS2812FX::mode_solid_glitter;
|
||||
_mode[FX_MODE_SUNRISE] = &WS2812FX::mode_sunrise;
|
||||
_mode[FX_MODE_PHASED] = &WS2812FX::mode_phased;
|
||||
_mode[FX_MODE_TWINKLEUP] = &WS2812FX::mode_twinkleup;
|
||||
_mode[FX_MODE_NOISEPAL] = &WS2812FX::mode_noisepal;
|
||||
_mode[FX_MODE_SINEWAVE] = &WS2812FX::mode_sinewave;
|
||||
_mode[FX_MODE_PHASEDNOISE] = &WS2812FX::mode_phased_noise;
|
||||
_mode[FX_MODE_FLOW] = &WS2812FX::mode_flow;
|
||||
_mode[FX_MODE_CHUNCHUN] = &WS2812FX::mode_chunchun;
|
||||
_mode[FX_MODE_DANCING_SHADOWS] = &WS2812FX::mode_dancing_shadows;
|
||||
_mode[FX_MODE_WASHING_MACHINE] = &WS2812FX::mode_washing_machine;
|
||||
_mode[FX_MODE_CANDY_CANE] = &WS2812FX::mode_candy_cane;
|
||||
_mode[FX_MODE_BLENDS] = &WS2812FX::mode_blends;
|
||||
_mode[FX_MODE_TV_SIMULATOR] = &WS2812FX::mode_tv_simulator;
|
||||
_mode[FX_MODE_DYNAMIC_SMOOTH] = &WS2812FX::mode_dynamic_smooth;
|
||||
_mode[FX_MODE_BLACK_HOLE] = &WS2812FX::mode_2DBlackHole;
|
||||
_mode[FX_MODE_COLORED_BURSTS] = &WS2812FX::mode_2DColoredBursts;
|
||||
_mode[FX_MODE_DNA] = &WS2812FX::mode_2Ddna;
|
||||
_mode[FX_MODE_DNA_SPIRAL] = &WS2812FX::mode_2DDNASpiral;
|
||||
_mode[FX_MODE_DRIFT] = &WS2812FX::mode_2DDrift;
|
||||
_mode[FX_MODE_FIRENOISE] = &WS2812FX::mode_2Dfirenoise;
|
||||
_mode[FX_MODE_FRIZZLES] = &WS2812FX::mode_2DFrizzles;
|
||||
_mode[FX_MODE_HIPNOTIC] = &WS2812FX::mode_2DHiphotic;
|
||||
_mode[FX_MODE_JULIA] = &WS2812FX::mode_2DJulia;
|
||||
_mode[FX_MODE_GAMEOFLIFE] = &WS2812FX::mode_2Dgameoflife;
|
||||
_mode[FX_MODE_LISSAJOUS] = &WS2812FX::mode_2DLissajous;
|
||||
_mode[FX_MODE_MATRIX] = &WS2812FX::mode_2Dmatrix;
|
||||
_mode[FX_MODE_MEATBALS] = &WS2812FX::mode_2Dmetaballs;
|
||||
_mode[FX_MODE_2DNOISE] = &WS2812FX::mode_2Dnoise;
|
||||
_mode[FX_MODE_PLASMA_BALL] = &WS2812FX::mode_2DPlasmaball;
|
||||
_mode[FX_MODE_POLAR_LIGHTS] = &WS2812FX::mode_2DPolarLights;
|
||||
_mode[FX_MODE_PULSER] = &WS2812FX::mode_2DPulser;
|
||||
_mode[FX_MODE_SINDOTS] = &WS2812FX::mode_2DSindots;
|
||||
_mode[FX_MODE_SQUARED_SWIRL] = &WS2812FX::mode_2Dsquaredswirl;
|
||||
_mode[FX_MODE_SUN_RADIATION] = &WS2812FX::mode_2DSunradiation;
|
||||
_mode[FX_MODE_TARTAN] = &WS2812FX::mode_2Dtartan;
|
||||
_mode[FX_MODE_WAVERLY] = &WS2812FX::mode_2DWaverly;
|
||||
_mode[FX_MODE_AKEMI] = &WS2812FX::mode_2DAkemi;
|
||||
_mode[FX_MODE_SPACESHIPS] = &WS2812FX::mode_2Dspaceships;
|
||||
_mode[FX_MODE_CRAZYBEES] = &WS2812FX::mode_2Dcrazybees;
|
||||
_mode[FX_MODE_GHOST_RIDER] = &WS2812FX::mode_2Dghostrider;
|
||||
_mode[FX_MODE_BLOBS] = &WS2812FX::mode_2Dfloatingblobs;
|
||||
_mode[FX_MODE_SCROLL_TEXT] = &WS2812FX::mode_2Dscrollingtext;
|
||||
_mode[FX_MODE_DRFIT_ROSE] = &WS2812FX::mode_2Ddriftrose;
|
||||
|
||||
setupEffectData();
|
||||
_brightness = DEFAULT_BRIGHTNESS;
|
||||
currentPalette = CRGBPalette16(CRGB::Black);
|
||||
targetPalette = CloudColors_p;
|
||||
@ -681,7 +618,7 @@ class WS2812FX {
|
||||
finalizeInit(),
|
||||
service(void),
|
||||
blur(uint8_t),
|
||||
fill(uint32_t),
|
||||
fill(uint32_t c, uint8_t seg=255),
|
||||
fade_out(uint8_t r),
|
||||
fadeToBlackBy(uint8_t fadeBy),
|
||||
setMode(uint8_t segid, uint8_t m),
|
||||
@ -701,19 +638,20 @@ class WS2812FX {
|
||||
resetSegments(),
|
||||
makeAutoSegments(bool forceReset = false),
|
||||
fixInvalidSegments(),
|
||||
setPixelColor(uint16_t n, uint8_t r, uint8_t g, uint8_t b, uint8_t w = 0),
|
||||
setPixelColor(int n, uint8_t r, uint8_t g, uint8_t b, uint8_t w = 0),
|
||||
setPixelColor(float i, uint8_t r, uint8_t g, uint8_t b, uint8_t w = 0, bool aa = false),
|
||||
show(void),
|
||||
setTargetFps(uint8_t fps),
|
||||
deserializeMap(uint8_t n=0);
|
||||
|
||||
// satisfy compiler by providing multiple casts
|
||||
void addEffect(uint8_t id, mode_ptr mode_fn, const char *mode_name) { if (id < _modeCount) { _mode[id] = mode_fn; _modeData[id] = mode_name;} }
|
||||
void setupEffectData(void); // defined in FX.cpp
|
||||
|
||||
// outsmart the compiler :) by correctly overloading
|
||||
inline void setPixelColor(int n, uint32_t c) {setPixelColor(n, byte(c>>16), byte(c>>8), byte(c), byte(c>>24));}
|
||||
inline void setPixelColor(int n, byte r, byte g, byte b, byte w = 0) {setPixelColor(n, r, g, b, w);}
|
||||
inline void setPixelColor(uint16_t n, uint32_t c) {setPixelColor(n, byte(c>>16), byte(c>>8), byte(c), byte(c>>24));}
|
||||
inline void setPixelColor(uint16_t n, CRGB c) {setPixelColor(n, c.red, c.green, c.blue);}
|
||||
inline void setPixelColor(float i, uint32_t c, bool aa = false) {setPixelColor(i, byte(c>>16), byte(c>>8), byte(c), byte(c>>24), aa);}
|
||||
inline void setPixelColor(float i, CRGB c, bool aa = false) {setPixelColor(i, c.red, c.green, c.blue, 0, aa);}
|
||||
inline void setPixelColor(int n, CRGB c) {setPixelColor(n, c.red, c.green, c.blue);}
|
||||
inline void setPixelColor(float i, uint32_t c, bool aa=true) {setPixelColor(i, byte(c>>16), byte(c>>8), byte(c), byte(c>>24), aa);}
|
||||
inline void setPixelColor(float i, CRGB c, bool aa=true) {setPixelColor(i, c.red, c.green, c.blue, 0, aa);}
|
||||
|
||||
bool
|
||||
gammaCorrectBri = false,
|
||||
@ -730,7 +668,6 @@ class WS2812FX {
|
||||
milliampsPerLed = 55,
|
||||
cctBlending = 0,
|
||||
getBrightness(void),
|
||||
getModeCount(void),
|
||||
getPaletteCount(void),
|
||||
getMaxSegments(void),
|
||||
getActiveSegmentsNum(void),
|
||||
@ -743,6 +680,7 @@ class WS2812FX {
|
||||
gamma8_cal(uint8_t, float),
|
||||
get_random_wheel_index(uint8_t);
|
||||
|
||||
inline uint8_t getModeCount() { return _modeCount; }
|
||||
inline uint8_t sin_gap(uint16_t in) {
|
||||
if (in & 0x100) return 0;
|
||||
return sin8(in + 192); // correct phase shift of sine so that it starts and stops at 0
|
||||
@ -773,6 +711,12 @@ class WS2812FX {
|
||||
getLastShow(void),
|
||||
getPixelColor(uint16_t);
|
||||
|
||||
const char *
|
||||
getModeData(uint8_t id = 0) { return id<_modeCount ? _modeData[id] : nullptr; }
|
||||
|
||||
const char **
|
||||
getModeDataSrc(void) { return _modeData; }
|
||||
|
||||
WS2812FX::Segment
|
||||
&getSegment(uint8_t n),
|
||||
&getFirstSelectedSeg(void),
|
||||
@ -901,7 +845,11 @@ class WS2812FX {
|
||||
mode_candy_cane(void),
|
||||
mode_blends(void),
|
||||
mode_tv_simulator(void),
|
||||
mode_dynamic_smooth(void);
|
||||
mode_dynamic_smooth(void),
|
||||
// non-audio transfered from WLED-SR
|
||||
mode_perlinmove(void),
|
||||
mode_wavesins(void),
|
||||
mode_FlowStripe(void);
|
||||
|
||||
// 2D support (panels)
|
||||
bool
|
||||
@ -931,9 +879,10 @@ class WS2812FX {
|
||||
|
||||
void
|
||||
setUpMatrix(),
|
||||
setPixelColorXY(uint16_t x, uint16_t y, uint8_t r, uint8_t g, uint8_t b, uint8_t w = 0),
|
||||
setPixelColorXY(int x, int y, uint8_t r, uint8_t g, uint8_t b, uint8_t w = 0),
|
||||
setPixelColorXY(float x, float y, byte r, byte g, byte b, byte w = 0, bool aa = false),
|
||||
blendPixelColorXY(uint16_t x, uint16_t y, uint32_t color, uint8_t blend),
|
||||
addPixelColorXY(uint16_t x, uint16_t y, uint32_t color),
|
||||
blur1d(CRGB* leds, fract8 blur_amount),
|
||||
blur1d(uint16_t i, bool vertical, fract8 blur_amount, CRGB* leds=nullptr), // 1D box blur (with weight)
|
||||
blur2d(CRGB* leds, fract8 blur_amount),
|
||||
@ -948,15 +897,16 @@ class WS2812FX {
|
||||
nscale8(CRGB* leds, uint8_t scale),
|
||||
setPixels(CRGB* leds),
|
||||
drawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, CRGB c, CRGB *leds = nullptr),
|
||||
drawCharacter(unsigned char chr, int16_t x, int16_t y, CRGB color, CRGB *leds = nullptr),
|
||||
drawCharacter(unsigned char chr, int16_t x, int16_t y, uint8_t w, uint8_t h, CRGB color, CRGB *leds = nullptr),
|
||||
wu_pixel(CRGB *leds, uint32_t x, uint32_t y, CRGB c);
|
||||
|
||||
inline void setPixelColorXY(uint16_t x, uint16_t y, uint32_t c) { setPixelColorXY(x, y, byte(c>>16), byte(c>>8), byte(c), byte(c>>24)); }
|
||||
inline void setPixelColorXY(uint16_t x, uint16_t y, CRGB c) { setPixelColorXY(x, y, c.red, c.green, c.blue); }
|
||||
inline void setPixelColorXY(float x, float y, uint32_t c, bool aa) { setPixelColorXY(x, y, byte(c>>16), byte(c>>8), byte(c), byte(c>>24), aa); }
|
||||
inline void setPixelColorXY(float x, float y, CRGB c, bool aa) { setPixelColorXY(x, y, c.red, c.green, c.blue, 0, aa); }
|
||||
// outsmart the compiler :) by correctly overloading
|
||||
inline void setPixelColorXY(int x, int y, uint32_t c) { setPixelColorXY(x, y, byte(c>>16), byte(c>>8), byte(c), byte(c>>24)); }
|
||||
inline void setPixelColorXY(int x, int y, CRGB c) { setPixelColorXY(x, y, c.red, c.green, c.blue, 0); }
|
||||
inline void setPixelColorXY(float x, float y, uint32_t c, bool aa=true) { setPixelColorXY(x, y, byte(c>>16), byte(c>>8), byte(c), byte(c>>24), aa); }
|
||||
inline void setPixelColorXY(float x, float y, CRGB c, bool aa=true) { setPixelColorXY(x, y, c.red, c.green, c.blue, 0, aa); }
|
||||
inline void drawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint32_t c) { drawLine(x0, y0, x1, y1, CRGB(byte(c>>16), byte(c>>8), byte(c))); }
|
||||
inline void drawCharacter(unsigned char chr, int16_t x, int16_t y, uint32_t c) { drawCharacter(chr, x, y, CRGB(byte(c>>16), byte(c>>8), byte(c))); }
|
||||
inline void drawCharacter(unsigned char chr, int16_t x, int16_t y, uint8_t w, uint8_t h, uint32_t c) { drawCharacter(chr, x, y, w, h, CRGB(byte(c>>16), byte(c>>8), byte(c))); }
|
||||
|
||||
uint16_t
|
||||
XY(uint16_t, uint16_t),
|
||||
@ -965,31 +915,10 @@ class WS2812FX {
|
||||
uint32_t
|
||||
getPixelColorXY(uint16_t, uint16_t);
|
||||
|
||||
// end 2D support
|
||||
|
||||
// 2D modes
|
||||
uint16_t
|
||||
mode_2DBlackHole(void),
|
||||
mode_2DColoredBursts(void),
|
||||
mode_2Ddna(void),
|
||||
mode_2DDNASpiral(void),
|
||||
mode_2DDrift(void),
|
||||
mode_2Dfirenoise(void),
|
||||
mode_2DFrizzles(void),
|
||||
mode_2Dgameoflife(void),
|
||||
mode_2DHiphotic(void),
|
||||
mode_2DJulia(void),
|
||||
mode_2DLissajous(void),
|
||||
mode_2Dmatrix(void),
|
||||
mode_2Dmetaballs(void),
|
||||
mode_2Dnoise(void),
|
||||
mode_2DPlasmaball(void),
|
||||
mode_2DPolarLights(void),
|
||||
mode_2DPulser(void),
|
||||
mode_2DSindots(void),
|
||||
mode_2Dsquaredswirl(void),
|
||||
mode_2DSunradiation(void),
|
||||
mode_2Dtartan(void),
|
||||
mode_2DWaverly(void),
|
||||
mode_2DAkemi(void),
|
||||
mode_2Dspaceships(void),
|
||||
mode_2Dcrazybees(void),
|
||||
mode_2Dghostrider(void),
|
||||
@ -997,7 +926,104 @@ class WS2812FX {
|
||||
mode_2Dscrollingtext(void),
|
||||
mode_2Ddriftrose(void);
|
||||
|
||||
// end 2D support
|
||||
// WLED-SR modes
|
||||
#ifndef USERMOD_AUDIOREACTIVE
|
||||
uint16_t
|
||||
mode_2Dnoise(void),
|
||||
mode_2Dfirenoise(void),
|
||||
mode_2Dsquaredswirl(void),
|
||||
mode_2Ddna(void),
|
||||
mode_2Dmatrix(void),
|
||||
mode_2Dmetaballs(void),
|
||||
mode_2DPulser(void),
|
||||
mode_2Dgameoflife(void),
|
||||
mode_2Dtartan(void),
|
||||
mode_2DPolarLights(void),
|
||||
mode_2DSwirl(void),
|
||||
mode_2DLissajous(void),
|
||||
mode_2DFrizzles(void),
|
||||
mode_2DPlasmaball(void),
|
||||
mode_2DHiphotic(void),
|
||||
mode_2DSindots(void),
|
||||
mode_2DDNASpiral(void),
|
||||
mode_2DBlackHole(void),
|
||||
mode_2DSunradiation(void),
|
||||
mode_2DWaverly(void),
|
||||
mode_2DDrift(void),
|
||||
mode_2DColoredBursts(void),
|
||||
mode_2DJulia(void),
|
||||
mode_gravimeter(void),
|
||||
mode_gravcenter(void),
|
||||
mode_gravcentric(void),
|
||||
mode_juggles(void),
|
||||
mode_matripix(void),
|
||||
mode_midnoise(void),
|
||||
mode_noisemeter(void),
|
||||
mode_noisefire(void),
|
||||
mode_pixelwave(void),
|
||||
mode_plasmoid(void),
|
||||
mode_puddles(void),
|
||||
mode_puddlepeak(void),
|
||||
mode_ripplepeak(void),
|
||||
mode_2DAkemi(void);
|
||||
#else
|
||||
uint16_t
|
||||
GEQ_base(bool centered_horizontal, bool centered_vertical, bool color_vertical),
|
||||
mode_pixels(void),
|
||||
mode_pixelwave(void),
|
||||
mode_juggles(void),
|
||||
mode_matripix(void),
|
||||
mode_gravimeter(void),
|
||||
mode_plasmoid(void),
|
||||
mode_puddles(void),
|
||||
mode_midnoise(void),
|
||||
mode_noisemeter(void),
|
||||
mode_freqwave(void),
|
||||
mode_freqmatrix(void),
|
||||
mode_2DGEQ(void),
|
||||
mode_waterfall(void),
|
||||
mode_freqpixels(void),
|
||||
mode_binmap(void),
|
||||
mode_noisefire(void),
|
||||
mode_puddlepeak(void),
|
||||
mode_noisemove(void),
|
||||
mode_2Dnoise(void),
|
||||
mode_ripplepeak(void),
|
||||
mode_2Dfirenoise(void),
|
||||
mode_2Dsquaredswirl(void),
|
||||
//mode_2Dfire2012(void),
|
||||
mode_2Ddna(void),
|
||||
mode_2Dmatrix(void),
|
||||
mode_2Dmetaballs(void),
|
||||
mode_freqmap(void),
|
||||
mode_gravcenter(void),
|
||||
mode_gravcentric(void),
|
||||
mode_gravfreq(void),
|
||||
mode_DJLight(void),
|
||||
mode_2DFunkyPlank(void),
|
||||
mode_2DCenterBars(void),
|
||||
mode_2DPulser(void),
|
||||
mode_blurz(void),
|
||||
mode_2Dgameoflife(void),
|
||||
mode_2Dtartan(void),
|
||||
mode_2DPolarLights(void),
|
||||
mode_2DSwirl(void),
|
||||
mode_2DLissajous(void),
|
||||
mode_2DFrizzles(void),
|
||||
mode_2DPlasmaball(void),
|
||||
mode_2DHiphotic(void),
|
||||
mode_2DSindots(void),
|
||||
mode_2DDNASpiral(void),
|
||||
mode_2DBlackHole(void),
|
||||
mode_rocktaves(void),
|
||||
mode_2DAkemi(void),
|
||||
mode_2DSunradiation(void),
|
||||
mode_2DWaverly(void),
|
||||
mode_2DDrift(void),
|
||||
mode_2DColoredBursts(void),
|
||||
mode_2DJulia(void),
|
||||
mode_customEffect(void); //WLEDSR Custom Effects
|
||||
#endif
|
||||
|
||||
private:
|
||||
uint32_t crgb_to_col(CRGB fastled);
|
||||
@ -1020,7 +1046,9 @@ class WS2812FX {
|
||||
_hasWhiteChannel = false,
|
||||
_triggered;
|
||||
|
||||
uint8_t _modeCount = MODE_COUNT;
|
||||
mode_ptr _mode[MODE_COUNT]; // SRAM footprint: 4 bytes per element
|
||||
const char *_modeData[MODE_COUNT];// mode (effect) name and its slider control data array
|
||||
|
||||
show_callback _callback = nullptr;
|
||||
|
||||
|
3159
wled00/FX_2Dfcn.cpp
3159
wled00/FX_2Dfcn.cpp
File diff suppressed because it is too large
Load Diff
@ -187,31 +187,31 @@ void /*IRAM_ATTR*/ WS2812FX::setPixelColor(float i, byte r, byte g, byte b, byte
|
||||
{
|
||||
if (i<0.0f || i>1.0f) return; // not normalized
|
||||
|
||||
if (aa) {
|
||||
float fC = i * (SEGLEN-1);
|
||||
float fL = floorf(i * (SEGLEN-1));
|
||||
float fR = ceilf(i * (SEGLEN-1));
|
||||
uint16_t iL = fL;
|
||||
uint16_t iR = fR;
|
||||
if (aa) {
|
||||
uint16_t iL = roundf(fC-0.49f);
|
||||
uint16_t iR = roundf(fC+0.49f);
|
||||
float dL = fC - iL;
|
||||
float dR = iR - fC;
|
||||
uint32_t cIL = getPixelColor(iL);
|
||||
uint32_t cIR = getPixelColor(iR);
|
||||
if (iR!=iL) {
|
||||
// blend L pixel
|
||||
cIL = color_blend(RGBW32(r,g,b,w), cIL, (fR - fC)*UINT16_MAX, true);
|
||||
cIL = color_blend(RGBW32(r,g,b,w), cIL, uint8_t(dL*255.0f));
|
||||
setPixelColor(iL, R(cIL), G(cIL), B(cIL), W(cIL));
|
||||
// blend R pixel
|
||||
cIR = color_blend(RGBW32(r,g,b,w), cIR, (fC - fL)*UINT16_MAX, true);
|
||||
cIR = color_blend(RGBW32(r,g,b,w), cIR, uint8_t(dR*255.0f));
|
||||
setPixelColor(iR, R(cIR), G(cIR), B(cIR), W(cIR));
|
||||
} else {
|
||||
// exact match (x & y land on a pixel)
|
||||
setPixelColor(iL, r, g, b, w);
|
||||
}
|
||||
} else {
|
||||
setPixelColor((uint16_t)roundf(i * (SEGLEN-1)), r, g, b, w);
|
||||
setPixelColor(uint16_t(roundf(fC)), r, g, b, w);
|
||||
}
|
||||
}
|
||||
|
||||
void IRAM_ATTR WS2812FX::setPixelColor(uint16_t i, byte r, byte g, byte b, byte w)
|
||||
void IRAM_ATTR WS2812FX::setPixelColor(int i, byte r, byte g, byte b, byte w)
|
||||
{
|
||||
uint8_t segIdx = SEGLEN ? _segment_index : _mainSegment;
|
||||
if (isMatrix && SEGLEN) {
|
||||
@ -411,7 +411,7 @@ void WS2812FX::trigger() {
|
||||
void WS2812FX::setMode(uint8_t segid, uint8_t m) {
|
||||
if (segid >= MAX_NUM_SEGMENTS) return;
|
||||
|
||||
if (m >= MODE_COUNT) m = MODE_COUNT - 1;
|
||||
if (m >= getModeCount()) m = getModeCount() - 1;
|
||||
|
||||
if (_segments[segid].mode != m)
|
||||
{
|
||||
@ -420,11 +420,6 @@ void WS2812FX::setMode(uint8_t segid, uint8_t m) {
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t WS2812FX::getModeCount()
|
||||
{
|
||||
return MODE_COUNT;
|
||||
}
|
||||
|
||||
uint8_t WS2812FX::getPaletteCount()
|
||||
{
|
||||
return 13 + GRADIENT_PALETTE_COUNT;
|
||||
@ -937,13 +932,16 @@ uint32_t WS2812FX::color_add(uint32_t c1, uint32_t c2)
|
||||
/*
|
||||
* Fills segment with color
|
||||
*/
|
||||
void WS2812FX::fill(uint32_t c) {
|
||||
void WS2812FX::fill(uint32_t c, uint8_t seg) {
|
||||
uint8_t oldSeg;
|
||||
if (seg != 255) oldSeg = setPixelSegment(seg);
|
||||
const uint16_t cols = isMatrix ? SEGMENT.virtualWidth() : SEGMENT.virtualLength();
|
||||
const uint16_t rows = SEGMENT.virtualHeight(); // will be 1 for 1D
|
||||
for(uint16_t y = 0; y < rows; y++) for (uint16_t x = 0; x < cols; x++) {
|
||||
if (isMatrix) setPixelColorXY(x, y, c);
|
||||
else setPixelColor(x, c);
|
||||
}
|
||||
if (seg != 255) setPixelSegment(oldSeg);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -970,7 +968,7 @@ void WS2812FX::fade_out(uint8_t rate) {
|
||||
int g2 = G(color);
|
||||
int b2 = B(color);
|
||||
|
||||
for(uint16_t y = 0; y < rows; y++) for (uint16_t x = 0; x < cols; x++) {
|
||||
for (uint16_t y = 0; y < rows; y++) for (uint16_t x = 0; x < cols; x++) {
|
||||
color = isMatrix ? getPixelColorXY(x, y) : getPixelColor(x);
|
||||
int w1 = W(color);
|
||||
int r1 = R(color);
|
||||
@ -995,8 +993,12 @@ void WS2812FX::fade_out(uint8_t rate) {
|
||||
|
||||
// fades all pixels to black using nscale8()
|
||||
void WS2812FX::fadeToBlackBy(uint8_t fadeBy) {
|
||||
for (uint16_t i = 0; i < SEGLEN; i++) {
|
||||
setPixelColor(i, col_to_crgb(getPixelColor(i)).nscale8(255-fadeBy));
|
||||
const uint16_t cols = isMatrix ? SEGMENT.virtualWidth() : SEGMENT.virtualLength();
|
||||
const uint16_t rows = SEGMENT.virtualHeight(); // will be 1 for 1D
|
||||
|
||||
for (uint16_t y = 0; y < rows; y++) for (uint16_t x = 0; x < cols; x++) {
|
||||
if (isMatrix) setPixelColorXY(x, y, col_to_crgb(getPixelColorXY(x,y)).nscale8(255-fadeBy));
|
||||
else setPixelColor(x, col_to_crgb(getPixelColor(x)).nscale8(255-fadeBy));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -461,7 +461,7 @@ class BusPwm : public Bus {
|
||||
return numPins;
|
||||
}
|
||||
|
||||
inline void cleanup() {
|
||||
void cleanup() {
|
||||
deallocatePins();
|
||||
}
|
||||
|
||||
@ -494,6 +494,71 @@ class BusPwm : public Bus {
|
||||
};
|
||||
|
||||
|
||||
class BusOnOff : public Bus {
|
||||
public:
|
||||
BusOnOff(BusConfig &bc) : Bus(bc.type, bc.start, bc.autoWhite) {
|
||||
_valid = false;
|
||||
if (bc.type != TYPE_ONOFF) return;
|
||||
|
||||
uint8_t currentPin = bc.pins[0];
|
||||
if (!pinManager.allocatePin(currentPin, true, PinOwner::BusOnOff)) {
|
||||
deallocatePins(); return;
|
||||
}
|
||||
_pin = currentPin; //store only after allocatePin() succeeds
|
||||
pinMode(_pin, OUTPUT);
|
||||
reversed = bc.reversed;
|
||||
_valid = true;
|
||||
};
|
||||
|
||||
void setPixelColor(uint16_t pix, uint32_t c) {
|
||||
if (pix != 0 || !_valid) return; //only react to first pixel
|
||||
c = autoWhiteCalc(c);
|
||||
uint8_t r = R(c);
|
||||
uint8_t g = G(c);
|
||||
uint8_t b = B(c);
|
||||
uint8_t w = W(c);
|
||||
|
||||
_data = bool((r+g+b+w)*_bri) ? 0xFF : 0;
|
||||
}
|
||||
|
||||
uint32_t getPixelColor(uint16_t pix) {
|
||||
if (!_valid) return 0;
|
||||
return RGBW32(_data, _data, _data, _data);
|
||||
}
|
||||
|
||||
void show() {
|
||||
if (!_valid) return;
|
||||
digitalWrite(_pin, reversed ? !(bool)_data : (bool)_data);
|
||||
}
|
||||
|
||||
inline void setBrightness(uint8_t b) {
|
||||
_bri = b;
|
||||
}
|
||||
|
||||
uint8_t getPins(uint8_t* pinArray) {
|
||||
if (!_valid) return 0;
|
||||
pinArray[0] = _pin;
|
||||
return 1;
|
||||
}
|
||||
|
||||
void cleanup() {
|
||||
deallocatePins();
|
||||
}
|
||||
|
||||
~BusOnOff() {
|
||||
cleanup();
|
||||
}
|
||||
|
||||
private:
|
||||
uint8_t _pin = 255;
|
||||
uint8_t _data = 0;
|
||||
|
||||
void deallocatePins() {
|
||||
pinManager.deallocatePin(_pin, PinOwner::BusOnOff);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
class BusNetwork : public Bus {
|
||||
public:
|
||||
BusNetwork(BusConfig &bc) : Bus(bc.type, bc.start, bc.autoWhite) {
|
||||
|
@ -131,21 +131,42 @@ void handleSwitch(uint8_t b)
|
||||
}
|
||||
}
|
||||
|
||||
#define ANALOG_BTN_READ_CYCLE 250 // min time between two analog reading cycles
|
||||
#define STRIP_WAIT_TIME 6 // max wait time in case of strip.isUpdating()
|
||||
#define POT_SMOOTHING 0.25f // smoothing factor for raw potentiometer readings
|
||||
#define POT_SENSITIVITY 4 // changes below this amount are noise (POT scratching, or ADC noise)
|
||||
|
||||
void handleAnalog(uint8_t b)
|
||||
{
|
||||
static uint8_t oldRead[WLED_MAX_BUTTONS];
|
||||
static uint8_t oldRead[WLED_MAX_BUTTONS] = {0};
|
||||
static float filteredReading[WLED_MAX_BUTTONS] = {0.0f};
|
||||
uint16_t rawReading; // raw value from analogRead, scaled to 12bit
|
||||
|
||||
#ifdef ESP8266
|
||||
uint16_t aRead = analogRead(A0) >> 2; // convert 10bit read to 8bit
|
||||
rawReading = analogRead(A0) << 2; // convert 10bit read to 12bit
|
||||
#else
|
||||
uint16_t aRead = analogRead(btnPin[b]) >> 4; // convert 12bit read to 8bit
|
||||
rawReading = analogRead(btnPin[b]); // collect at full 12bit resolution
|
||||
#endif
|
||||
yield(); // keep WiFi task running - analog read may take several millis on ESP8266
|
||||
|
||||
filteredReading[b] += POT_SMOOTHING * ((float(rawReading) / 16.0f) - filteredReading[b]); // filter raw input, and scale to [0..255]
|
||||
uint16_t aRead = max(min(int(filteredReading[b]), 255), 0); // squash into 8bit
|
||||
if(aRead <= POT_SENSITIVITY) aRead = 0; // make sure that 0 and 255 are used
|
||||
if(aRead >= 255-POT_SENSITIVITY) aRead = 255;
|
||||
|
||||
if (buttonType[b] == BTN_TYPE_ANALOG_INVERTED) aRead = 255 - aRead;
|
||||
|
||||
// remove noise & reduce frequency of UI updates
|
||||
aRead &= 0xFC;
|
||||
if (abs(int(aRead) - int(oldRead[b])) <= POT_SENSITIVITY) return; // no significant change in reading
|
||||
|
||||
// Unomment the next lines if you still see flickering related to potentiometer
|
||||
// This waits until strip finishes updating (why: strip was not updating at the start of handleButton() but may have started during analogRead()?)
|
||||
//unsigned long wait_started = millis();
|
||||
//while(strip.isUpdating() && (millis() - wait_started < STRIP_WAIT_TIME)) {
|
||||
// delay(1);
|
||||
//}
|
||||
//if (strip.isUpdating()) return; // give up
|
||||
|
||||
if (oldRead[b] == aRead) return; // no change in reading
|
||||
oldRead[b] = aRead;
|
||||
|
||||
// if no macro for "short press" and "long press" is defined use brightness control
|
||||
@ -168,6 +189,7 @@ void handleAnalog(uint8_t b)
|
||||
} else if (macroDoublePress[b] == 247) {
|
||||
// selected palette
|
||||
effectPalette = map(aRead, 0, 252, 0, strip.getPaletteCount()-1);
|
||||
effectPalette = constrain(effectPalette, 0, strip.getPaletteCount()-1); // map is allowed to "overshoot", so we need to contrain the result
|
||||
} else if (macroDoublePress[b] == 200) {
|
||||
// primary color, hue, full saturation
|
||||
colorHStoRGB(aRead*256,255,col);
|
||||
@ -197,6 +219,8 @@ void handleButton()
|
||||
bool analog = false;
|
||||
unsigned long now = millis();
|
||||
|
||||
if (strip.isUpdating()) return; // don't interfere with strip updates. Our button will still be there in 1ms (next cycle)
|
||||
|
||||
for (uint8_t b=0; b<WLED_MAX_BUTTONS; b++) {
|
||||
#ifdef ESP8266
|
||||
if ((btnPin[b]<0 && !(buttonType[b] == BTN_TYPE_ANALOG || buttonType[b] == BTN_TYPE_ANALOG_INVERTED)) || buttonType[b] == BTN_TYPE_NONE) continue;
|
||||
@ -206,7 +230,7 @@ void handleButton()
|
||||
|
||||
if (usermods.handleButton(b)) continue; // did usermod handle buttons
|
||||
|
||||
if ((buttonType[b] == BTN_TYPE_ANALOG || buttonType[b] == BTN_TYPE_ANALOG_INVERTED) && now - lastRead > 250) { // button is not a button but a potentiometer
|
||||
if ((buttonType[b] == BTN_TYPE_ANALOG || buttonType[b] == BTN_TYPE_ANALOG_INVERTED) && now - lastRead > ANALOG_BTN_READ_CYCLE) { // button is not a button but a potentiometer
|
||||
analog = true;
|
||||
handleAnalog(b); continue;
|
||||
}
|
||||
|
@ -76,6 +76,7 @@
|
||||
#define USERMOD_ID_WORDCLOCK 27 //Usermod "usermod_v2_word_clock.h"
|
||||
#define USERMOD_ID_MY9291 28 //Usermod "usermod_MY9291.h"
|
||||
#define USERMOD_ID_SI7021_MQTT_HA 29 //Usermod "usermod_si7021_mqtt_ha.h"
|
||||
#define USERMOD_ID_AUDIOREACTIVE 30 //Usermod "audioreactive.h"
|
||||
|
||||
//Access point behavior
|
||||
#define AP_BEHAVIOR_BOOT_NO_CONN 0 //Open AP when no connection after boot
|
||||
|
@ -123,11 +123,19 @@ button {
|
||||
.icons {
|
||||
font-family: 'WIcons';
|
||||
font-style: normal;
|
||||
font-size: 24px;
|
||||
line-height: 1;
|
||||
font-size: 24px !important;
|
||||
line-height: 1 !important;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.icons.on {
|
||||
color: var(--c-g);
|
||||
}
|
||||
|
||||
.icons.off {
|
||||
color: var(--c-6);
|
||||
}
|
||||
|
||||
.top .icons, .bot .icons {
|
||||
margin: -2px 0 4px 0;
|
||||
}
|
||||
@ -359,27 +367,77 @@ button {
|
||||
bottom: 5px;
|
||||
}
|
||||
|
||||
#fx {
|
||||
height: calc(100% - 121px);
|
||||
overflow-y: scroll;
|
||||
padding-left: 6px; /* compensate scroll bar */
|
||||
}
|
||||
|
||||
#sliders {
|
||||
width: 310px;
|
||||
margin: 0 auto;
|
||||
position: sticky;
|
||||
bottom: 0;
|
||||
}
|
||||
|
||||
#sliders .labels {
|
||||
padding-top: 3px;
|
||||
font-size: small;
|
||||
}
|
||||
|
||||
#slider0, #slider1, #slider2, #slider3, #slider4 {
|
||||
.slider {
|
||||
background: var(--c-2);
|
||||
max-width: 310px;
|
||||
min-width: 280px;
|
||||
margin: 0 auto 5px;
|
||||
margin: 0 auto; /* add 5px; if you want some vertical space but looks ugly */
|
||||
border-radius: 15px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
/* Tooltip text */
|
||||
.slider .tooltiptext {
|
||||
visibility: hidden;
|
||||
background-color: var(--c-5);
|
||||
color: var(--c-f);
|
||||
text-align: center;
|
||||
padding: 5px 10px;
|
||||
border-radius: 6px;
|
||||
|
||||
/* Position the tooltip text */
|
||||
width: 160px;
|
||||
position: absolute;
|
||||
z-index: 1;
|
||||
bottom: 100%;
|
||||
left: 50%;
|
||||
margin-left: -92px;
|
||||
|
||||
/* Fade in tooltip */
|
||||
opacity: 0;
|
||||
transition: opacity 0.75s;
|
||||
}
|
||||
/*
|
||||
.slider.top .tooltiptext {
|
||||
top: 100%;
|
||||
bottom: unset;
|
||||
}
|
||||
*/
|
||||
/* Tooltip arrow */
|
||||
.slider .tooltiptext::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 100%;
|
||||
left: 50%;
|
||||
margin-left: -5px;
|
||||
border-width: 5px;
|
||||
border-style: solid;
|
||||
border-color: var(--c-5) transparent transparent transparent;
|
||||
}
|
||||
/*
|
||||
.slider.top .tooltiptext::after {
|
||||
bottom: 100%;
|
||||
left: 50%;
|
||||
top: unset;
|
||||
border-color: transparent transparent var(--c-5) transparent;
|
||||
}
|
||||
*/
|
||||
/* Show the tooltip text when you mouse over the tooltip container */
|
||||
.slider:hover .tooltiptext {
|
||||
visibility: visible;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
#pql, .edit-icon {
|
||||
@ -473,15 +531,6 @@ button {
|
||||
z-index: -2;
|
||||
}
|
||||
|
||||
#imgw {
|
||||
display: inline-block;
|
||||
margin: 8px;
|
||||
}
|
||||
|
||||
#kv, #kn {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
#info table, #nodes table {
|
||||
table-layout: fixed;
|
||||
width: 100%;
|
||||
@ -502,6 +551,15 @@ button {
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
#imgw {
|
||||
/*display: inline-block;*/
|
||||
margin: 8px auto;
|
||||
}
|
||||
/*
|
||||
#kv, #kn {
|
||||
display: inline-block;
|
||||
}
|
||||
*/
|
||||
#lv {
|
||||
max-width: 600px;
|
||||
display: inline-block;
|
||||
@ -708,6 +766,9 @@ input[type=range]::-moz-range-thumb {
|
||||
#putil .btn-xs {
|
||||
margin: 0;
|
||||
}
|
||||
#info .btn-xs {
|
||||
border: 1px solid var(--c-4);
|
||||
}
|
||||
|
||||
#putil .btn-s {
|
||||
width: 135px;
|
||||
@ -872,23 +933,17 @@ textarea {
|
||||
overflow: clip;
|
||||
text-overflow: ellipsis;
|
||||
line-height: 24px;
|
||||
padding: 8px 0;
|
||||
max-width: 160px;
|
||||
padding: 8px 24px;
|
||||
max-width: 170px;
|
||||
margin: 0 auto;
|
||||
position: relative;
|
||||
}
|
||||
.segname {
|
||||
padding-left: 28px;
|
||||
}
|
||||
|
||||
.segname .flr {
|
||||
.segname .flr, .pname .flr {
|
||||
transform: rotate(0deg);
|
||||
right: -6px;
|
||||
}
|
||||
|
||||
.expanded .segname {
|
||||
max-width: 184px;
|
||||
padding: 8px 24px;
|
||||
}
|
||||
/* segment power wrapper */
|
||||
.sbs {
|
||||
padding: 4px 0 4px 8px;
|
||||
@ -921,19 +976,24 @@ textarea {
|
||||
}
|
||||
|
||||
.xxs {
|
||||
border: 2px solid var(--c-e) !important;
|
||||
width: 44px;
|
||||
height: 44px;
|
||||
margin: 5px;
|
||||
/*box-shadow: 0 0 0 2px #fff;*/
|
||||
}
|
||||
|
||||
.xxs-w {
|
||||
border-width: 5px !important;
|
||||
margin: 2px;
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
/*box-shadow: 0 0 0 5px #fff;*/
|
||||
}
|
||||
|
||||
#csl .xxs {
|
||||
border: 2px solid var(--c-d) !important;
|
||||
/*box-shadow: 0 0 0 2px var(--c-d);*/
|
||||
}
|
||||
#csl .xxs-w {
|
||||
border-width: 5px !important;
|
||||
/*box-shadow: 0 0 0 5px var(--c-d);*/
|
||||
}
|
||||
|
||||
.qcs, #namelabel {
|
||||
@ -962,7 +1022,7 @@ textarea {
|
||||
}
|
||||
|
||||
.frz {
|
||||
left: 36px;
|
||||
left: 32px;
|
||||
position: absolute;
|
||||
top: -3px;
|
||||
cursor: pointer;
|
||||
|
@ -217,50 +217,50 @@
|
||||
</div>
|
||||
</div>
|
||||
<div id="sliders">
|
||||
<p class="labels hd" id="sliderLabel0">Effect speed</p>
|
||||
<div id="slider0">
|
||||
<div id="slider0" class="slider">
|
||||
<i class="icons slider-icon" style="cursor: pointer;" onclick="tglFreeze()"></i>
|
||||
<div class="sliderwrap il">
|
||||
<input id="sliderSpeed" class="noslide" onchange="setSpeed()" oninput="updateTrail(this)" max="255" min="0" type="range" value="128" />
|
||||
<div class="sliderdisplay"></div>
|
||||
</div>
|
||||
<output class="sliderbubble"></output>
|
||||
<span id="sliderLabel0" class="tooltiptext">Effect speed</span>
|
||||
</div>
|
||||
<p class="labels hd" id="sliderLabel1">Effect intensity</p>
|
||||
<div id="slider1">
|
||||
<div id="slider1" class="slider">
|
||||
<i class="icons slider-icon" style="cursor: pointer;" onclick="tglLabels()"></i>
|
||||
<div class="sliderwrap il">
|
||||
<input id="sliderIntensity" class="noslide" onchange="setIntensity()" oninput="updateTrail(this)" max="255" min="0" type="range" value="128" />
|
||||
<div class="sliderdisplay"></div>
|
||||
</div>
|
||||
<output class="sliderbubble"></output>
|
||||
<span class="tooltiptext" id="sliderLabel1">Effect intensity</span>
|
||||
</div>
|
||||
<p class="labels hd" id="sliderLabel2">Custom 1</p>
|
||||
<div id="slider2" class="hide">
|
||||
<div id="slider2" class="slider hide">
|
||||
<i class="icons slider-icon"></i>
|
||||
<div class="sliderwrap il">
|
||||
<input id="sliderC1" class="noslide" onchange="setCustom(1)" oninput="updateTrail(this)" max="255" min="0" type="range" value="6" />
|
||||
<div class="sliderdisplay"></div>
|
||||
</div>
|
||||
<output class="sliderbubble"></output>
|
||||
<span class="tooltiptext" id="sliderLabel2">Custom 1</span>
|
||||
</div>
|
||||
<p class="labels hd" id="sliderLabel3">Custom 2</p>
|
||||
<div id="slider3" class="hide">
|
||||
<div id="slider3" class="slider hide">
|
||||
<i class="icons slider-icon"></i>
|
||||
<div class="sliderwrap il">
|
||||
<input id="sliderC2" class="noslide" onchange="setCustom(2)" oninput="updateTrail(this)" max="255" min="0" type="range" value="6" />
|
||||
<div class="sliderdisplay"></div>
|
||||
</div>
|
||||
<output class="sliderbubble"></output>
|
||||
<span class="tooltiptext" id="sliderLabel3">Custom 2</span>
|
||||
</div>
|
||||
<p class="labels hd" id="sliderLabel4">Custom 3</p>
|
||||
<div id="slider4" class="hide">
|
||||
<div id="slider4" class="slider hide">
|
||||
<i class="icons slider-icon"></i>
|
||||
<div class="sliderwrap il">
|
||||
<input id="sliderC3" class="noslide" onchange="setCustom(3)" oninput="updateTrail(this)" max="255" min="0" type="range" value="6" />
|
||||
<div class="sliderdisplay"></div>
|
||||
</div>
|
||||
<output class="sliderbubble"></output>
|
||||
<span class="tooltiptext" id="sliderLabel4">Custom 3</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -308,7 +308,7 @@
|
||||
<button class="btn btn-xs close" onclick="toggleInfo()"><i class="icons rot45"></i></button>
|
||||
<div id="imgw">
|
||||
<img class="wi" alt="" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB0AAAAFCAYAAAC5Fuf5AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAABbSURBVChTlY9bDoAwDMNW7n9nwCipytQN4Z8tbrTHmDmF4oPzyldwRqp1SSdnV/NuZuzqerAByxXznBw3igkeFEfXyUuhK/yFM0CxJfyqXZEOc6/Sr9/bf7uIC5Nwd7orMvAPAAAAAElFTkSuQmCC" />
|
||||
</div><br>
|
||||
</div>
|
||||
<div id="kv">Loading...</div><br>
|
||||
<div>
|
||||
<button class="btn infobtn" onclick="requestJson()">Refresh</button>
|
||||
|
@ -126,8 +126,8 @@ function cTheme(light) {
|
||||
sCol('--c-c','#333');
|
||||
sCol('--c-e','#111');
|
||||
sCol('--c-d','#222');
|
||||
sCol('--c-r','#c21');
|
||||
sCol('--c-g','#2c1');
|
||||
sCol('--c-r','#a21');
|
||||
sCol('--c-g','#2a1');
|
||||
sCol('--c-l','#26c');
|
||||
sCol('--c-o','rgba(204, 204, 204, 0.9)');
|
||||
sCol('--c-sb','#0003'); sCol('--c-sbh','#0006');
|
||||
@ -398,7 +398,7 @@ function presetError(empty)
|
||||
if (bckstr.length > 10) hasBackup = true;
|
||||
} catch (e) {}
|
||||
|
||||
var cn = `<div class="pres c" ${empty?'style="padding:8px;"':'onclick="loadPresets()" style="cursor:pointer;padding:8px;"'}>`;
|
||||
var cn = `<div class="pres c" ${empty?'style="padding:8px 0;margin-top: 15px;"':'onclick="loadPresets()" style="cursor:pointer;padding:8px 0;"'}>`;
|
||||
if (empty)
|
||||
cn += `You have no presets yet!`;
|
||||
else
|
||||
@ -568,7 +568,7 @@ function populatePresets(fromls)
|
||||
cn += `<div class="pres lstI" id="p${i}o">`;
|
||||
if (cfg.comp.pid) cn += `<div class="pid">${i}</div>`;
|
||||
cn += `<div class="pname lstIname" onclick="setPreset(${i})">${isPlaylist(i)?"<i class='icons btn-icon'></i>":""}${pName(i)}
|
||||
<i class="icons edit-icon" id="p${i}nedit" onclick="tglSegn(${i+100})"></i></div>
|
||||
<i class="icons edit-icon flr" id="p${i}nedit" onclick="tglSegn(${i+100})"></i></div>
|
||||
<i class="icons e-icon flr" id="sege${i+100}" onclick="expand(${i+100})"></i>
|
||||
<div class="presin lstIcontent" id="seg${i+100}"></div>
|
||||
</div>`;
|
||||
@ -604,7 +604,8 @@ function parseInfo(i) {
|
||||
mw = i.leds.matrix ? i.leds.matrix.w : 0;
|
||||
mh = i.leds.matrix ? i.leds.matrix.h : 0;
|
||||
isM = mw>0 && mh>0;
|
||||
if (!isM) hide2DModes();
|
||||
if (!isM) hideModes("2D ");
|
||||
if (!i.u || !i.u.AudioReactive) { /*hideModes("♪ ");*/ hideModes("♫ "); } // hide /*audio*/ frequency reactive effects
|
||||
}
|
||||
|
||||
function populateInfo(i)
|
||||
@ -752,7 +753,7 @@ function populateSegments(s)
|
||||
}
|
||||
if (segCount < 2) gId(`segd${lSeg}`).style.display = "none";
|
||||
if (!isM && !noNewSegs && (cfg.comp.seglen?parseInt(gId(`seg${lSeg}s`).value):0)+parseInt(gId(`seg${lSeg}e`).value)<ledCount) gId(`segr${lSeg}`).style.display = "inline";
|
||||
gId('rsbtn').style.display = (segCount > 1) ? "inline":"none";
|
||||
gId('segutil2').style.display = (segCount > 1) ? "block":"none"; // rsbtn parent
|
||||
}
|
||||
|
||||
function populateEffects()
|
||||
@ -772,14 +773,14 @@ function populateEffects()
|
||||
for (let i = 0; i < effects.length; i++) {
|
||||
// WLEDSR: add slider and color control to setX (used by requestjson)
|
||||
if (effects[i].name.indexOf("Reserved") < 0) {
|
||||
var extra = !(Array.isArray(fxdata) && fxdata.length>i) ? '' : fxdata[i].substr(1);
|
||||
let id = effects[i].id;
|
||||
html += generateListItemHtml(
|
||||
'fx',
|
||||
effects[i].id,
|
||||
id,
|
||||
effects[i].name,
|
||||
'setX',
|
||||
'',
|
||||
extra
|
||||
!(Array.isArray(fxdata) && fxdata.length>id) ? '' : fxdata[id].substr(1)
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -1278,7 +1279,6 @@ function readState(s,command=false)
|
||||
function setSliderAndColorControl(idx, applyDef=false)
|
||||
{
|
||||
if (!(Array.isArray(fxdata) && fxdata.length>idx)) return;
|
||||
var topPosition = 0;
|
||||
var controlDefined = (fxdata[idx].substr(0,1) == "@");
|
||||
var extra = fxdata[idx].substr(1);
|
||||
var extras = (extra == '')?[]:extra.split(";");
|
||||
@ -1288,7 +1288,8 @@ function setSliderAndColorControl(idx, applyDef=false)
|
||||
var obj = {"seg":{}};
|
||||
|
||||
// set html slider items on/off
|
||||
var nSliders = Math.min(5,Math.floor(gId("sliders").children.length / 2)); // p (label) & div for each slider
|
||||
var nSliders = Math.min(5,Math.floor(gId("sliders").children.length)); // div for each slider
|
||||
var sldCnt = 0;
|
||||
for (let i=0; i<nSliders; i++) {
|
||||
var slider = gId("slider" + i);
|
||||
var label = gId("sliderLabel" + i);
|
||||
@ -1307,22 +1308,21 @@ function setSliderAndColorControl(idx, applyDef=false)
|
||||
else if (i==0) label.innerHTML = "Effect speed";
|
||||
else if (i==1) label.innerHTML = "Effect intensity";
|
||||
else label.innerHTML = "Custom" + (i-1);
|
||||
label.classList.remove("hide");
|
||||
sldCnt++;
|
||||
//if (sldCnt++===0) slider.classList.add("top");
|
||||
slider.classList.remove("hide");
|
||||
if (getComputedStyle(label).display === "block") topPosition += 58; // increase top position for the next control
|
||||
else topPosition += 35;
|
||||
slider.setAttribute('title',label.innerHTML);
|
||||
//slider.setAttribute('title',label.innerHTML);
|
||||
} else {
|
||||
// disable label and slider
|
||||
slider.classList.add("hide");
|
||||
label.classList.add("hide");
|
||||
//slider.classList.remove("top");
|
||||
}
|
||||
}
|
||||
if (topPosition>0) { topPosition += 5; gId("sliders").style.paddingTop = "5px"; }
|
||||
else gId("sliders").style.padding = 0;
|
||||
|
||||
// set size of fx list
|
||||
gId("fx").style.height = `calc(100% - ${topPosition}px)`;
|
||||
// set the bottom position of selected effect (sticky) as the top of sliders div
|
||||
let top = parseInt(getComputedStyle(gId("sliders")).height);
|
||||
/*if (sldCnt===1)*/ top += 28; // size of tooltip
|
||||
let sel = d.querySelector('#fxlist .selected');
|
||||
if (sel) sel.style.bottom = top + "px"; // we will need to remove this when unselected (in setX())
|
||||
|
||||
// set html color items on/off
|
||||
var cslLabel = '';
|
||||
@ -1387,7 +1387,7 @@ function setSliderAndColorControl(idx, applyDef=false)
|
||||
}
|
||||
// not all color selectors shown, hide palettes created from color selectors
|
||||
for (let e of (gId('pallist').querySelectorAll('.lstI')||[])) {
|
||||
if (cslCnt < 3 && e.querySelector('.lstIname').innerText.indexOf("* ")>=0) e.classList.add('hide'); else e.classList.remove('hide');
|
||||
if (cslCnt < 3 && e.querySelector('.lstIname').innerText.indexOf("* C")>=0) e.classList.add('hide'); else e.classList.remove('hide');
|
||||
}
|
||||
if (!isEmpty(obj.seg) && applyDef) requestJson(obj); // update default values (may need throttling on ESP8266)
|
||||
}
|
||||
@ -1972,7 +1972,10 @@ function setX(ind = null)
|
||||
d.querySelector(`#fxlist input[name="fx"][value="${ind}"]`).checked = true;
|
||||
}
|
||||
var selElement = d.querySelector('#fxlist .selected');
|
||||
if (selElement) selElement.classList.remove('selected');
|
||||
if (selElement) {
|
||||
selElement.classList.remove('selected');
|
||||
selElement.style.bottom = null; // remove element style added in slider handling
|
||||
}
|
||||
|
||||
d.querySelector(`#fxlist .lstI[data-id="${ind}"]`).classList.add('selected');
|
||||
|
||||
@ -2035,12 +2038,12 @@ function setPreset(i)
|
||||
{
|
||||
var obj = {"ps":i};
|
||||
if (pJson && pJson[i] && (!pJson[i].win || pJson[i].win.indexOf("Please") <= 0)) {
|
||||
// we will send complete preset content as to avoid delay introduced by
|
||||
// async nature of applyPreset(). json.cpp has to decide wether to call applyPreset()
|
||||
// or not (by looking at the JSON content, if "ps" only)
|
||||
Object.assign(obj, pJson[i]);
|
||||
delete obj.ql; // no need for quick load
|
||||
delete obj.n; // no need for name
|
||||
// obj.pt = i; // this will set preset ID but not force state update
|
||||
// } else {
|
||||
// obj.ps = i;
|
||||
}
|
||||
if (isPlaylist(i)) obj.on = true; // force on
|
||||
showToast("Loading preset " + pName(i) +" (" + i + ")");
|
||||
@ -2395,12 +2398,10 @@ function getPalettesData(page, callback)
|
||||
});
|
||||
}
|
||||
|
||||
function hide2DModes()
|
||||
function hideModes(txt)
|
||||
{
|
||||
var el = gId('fxlist').querySelectorAll('.lstI');
|
||||
for (let it of el) {
|
||||
var itT = it.querySelector('.lstIname').innerText;
|
||||
if (itT.indexOf("2D ") >= 0) it.style.display = 'none';
|
||||
for (let e of (gId('fxlist').querySelectorAll('.lstI')||[])) {
|
||||
if (e.querySelector('.lstIname').innerText.indexOf(txt) >= 0) e.classList.add("hide"); //else e.classList.remove("hide");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -7,9 +7,43 @@
|
||||
<title>2D Set-up</title>
|
||||
<script>
|
||||
var d=document;
|
||||
var loc = false, locip;
|
||||
function H(){window.open("https://kno.wled.ge/features/2D");}
|
||||
function B(){window.open("/settings","_self");}
|
||||
function gId(n){return d.getElementById(n);}
|
||||
// https://www.educative.io/edpresso/how-to-dynamically-load-a-js-file-in-javascript
|
||||
function loadJS(FILE_URL, async = true) {
|
||||
let scE = d.createElement("script");
|
||||
scE.setAttribute("src", FILE_URL);
|
||||
scE.setAttribute("type", "text/javascript");
|
||||
scE.setAttribute("async", async);
|
||||
d.body.appendChild(scE);
|
||||
// success event
|
||||
scE.addEventListener("load", () => {
|
||||
//console.log("File loaded");
|
||||
GetV();
|
||||
UI();
|
||||
});
|
||||
// error event
|
||||
scE.addEventListener("error", (ev) => {
|
||||
console.log("Error on loading file", ev);
|
||||
alert("Loading of configuration script failed.\nIncomplete page data!");
|
||||
});
|
||||
}
|
||||
function S() {
|
||||
if (window.location.protocol == "file:") {
|
||||
loc = true;
|
||||
locip = localStorage.getItem('locIp');
|
||||
if (!locip) {
|
||||
locip = prompt("File Mode. Please enter WLED IP!");
|
||||
localStorage.setItem('locIp', locip);
|
||||
}
|
||||
}
|
||||
var url = (loc?`http://${locip}`:'') + '/settings/s.js?p=10';
|
||||
loadJS(url, false); // If we set async false, file is loaded and executed, then next statement is processed
|
||||
//if (loc) loadJS(url, false); // If we set async false, file is loaded and executed, then next statement is processed
|
||||
//else { GetV(); UI(); }
|
||||
}
|
||||
|
||||
var maxPanels=64;
|
||||
function UI(change=false)
|
||||
@ -71,9 +105,8 @@ Serpentine: <input type="checkbox" name="P${i}S"></div>`;
|
||||
gId("pnl_rem").style.display = (i>1) ? "inline":"none";
|
||||
}
|
||||
|
||||
function S() {GetV();UI()}
|
||||
//values injected by server while sending HTML
|
||||
function GetV() {}
|
||||
//fun-ction GetV() {}
|
||||
</script>
|
||||
<style>@import url("style.css");</style>
|
||||
</head>
|
||||
|
@ -131,7 +131,7 @@
|
||||
if (s[i].name.substring(0,2)=="LT") {
|
||||
var n = s[i].name.substring(2);
|
||||
var t = parseInt(s[i].value,10);
|
||||
gId("p0d"+n).innerHTML = (t>=80 && t<96) ? "IP address:" : (t > 49) ? "Data GPIO:" : (t >41) ? "GPIOs:" : "GPIO:";
|
||||
gId("p0d"+n).innerHTML = (t>=80 && t<96) ? "IP address:" : (t > 49) ? "Data GPIO:" : (t > 41) ? "GPIOs:" : "GPIO:";
|
||||
gId("p1d"+n).innerHTML = (t> 49 && t<64) ? "Clk GPIO:" : "";
|
||||
var LK = d.getElementsByName("L1"+n)[0]; // clock pin
|
||||
|
||||
@ -159,16 +159,16 @@
|
||||
}
|
||||
gId("rf"+n).onclick = (t == 31) ? (function(){return false}) : (function(){}); // prevent change for TM1814
|
||||
isRGBW |= (t == 30 || t == 31 || (t > 40 && t < 46 && t != 43)); // RGBW checkbox, TYPE_xxxx values from const.h
|
||||
gId("co"+n).style.display = ((t>=80 && t<96) || (t > 40 && t < 48)) ? "none":"inline"; // hide color order for PWM
|
||||
gId("co"+n).style.display = ((t >= 80 && t < 96) || (t >= 40 && t < 48)) ? "none":"inline"; // hide color order for PWM
|
||||
gId("dig"+n+"w").style.display = (t == 30 || t == 31) ? "inline":"none"; // show swap channels dropdown
|
||||
if (!(t == 30 || t == 31)) d.getElementsByName("WO"+n)[0].value = 0; // reset swapping
|
||||
gId("dig"+n+"c").style.display = (t > 40 && t < 48) ? "none":"inline"; // hide count for analog
|
||||
gId("dig"+n+"r").style.display = (t>=80 && t<96) ? "none":"inline"; // hide reversed for virtual
|
||||
gId("dig"+n+"s").style.display = ((t>=80 && t<96) || (t > 40 && t < 48)) ? "none":"inline"; // hide skip 1st for virtual & analog
|
||||
gId("dig"+n+"f").style.display = (t>=16 && t<32 || t>=50 && t<64) ? "inline":"none"; // hide refresh
|
||||
gId("dig"+n+"a").style.display = (isRGBW) ? "inline":"none"; // auto calculate white
|
||||
gId("rev"+n).innerHTML = (t > 40 && t < 48) ? "Inverted output":"Reversed (rotated 180°)"; // change reverse text for analog
|
||||
gId("psd"+n).innerHTML = (t > 40 && t < 48) ? "Index:":"Start:"; // change analog start description
|
||||
gId("dig"+n+"c").style.display = (t >= 40 && t < 48) ? "none":"inline"; // hide count for analog
|
||||
gId("dig"+n+"r").style.display = (t >= 80 && t < 96) ? "none":"inline"; // hide reversed for virtual
|
||||
gId("dig"+n+"s").style.display = ((t >= 80 && t < 96) || (t >= 40 && t < 48)) ? "none":"inline"; // hide skip 1st for virtual & analog
|
||||
gId("dig"+n+"f").style.display = ((t >= 16 && t < 32) || (t >= 50 && t < 64)) ? "inline":"none"; // hide refresh
|
||||
gId("dig"+n+"a").style.display = (isRGBW && t != 40) ? "inline":"none"; // auto calculate white
|
||||
gId("rev"+n).innerHTML = (t >= 40 && t < 48) ? "Inverted output":"Reversed (rotated 180°)"; // change reverse text for analog
|
||||
gId("psd"+n).innerHTML = (t >= 40 && t < 48) ? "Index:":"Start:"; // change analog start description
|
||||
}
|
||||
}
|
||||
// display white channel calculation method
|
||||
@ -301,6 +301,7 @@ ${i+1}:
|
||||
<option value="51">APA102</option>
|
||||
<option value="52">LPD8806</option>
|
||||
<option value="53">P9813</option>
|
||||
<option value="40">On/Off</option>
|
||||
<option value="41">PWM White</option>
|
||||
<option value="42">PWM CCT</option>
|
||||
<option value="43">PWM RGB</option>
|
||||
|
@ -17,6 +17,24 @@
|
||||
function isO(i) { return (i && typeof i === 'object' && !Array.isArray(i)); }
|
||||
function H() { window.open("https://github.com/Aircoookie/WLED/wiki/Settings#usermod-settings"); }
|
||||
function B() { window.open("/settings","_self"); }
|
||||
// https://www.educative.io/edpresso/how-to-dynamically-load-a-js-file-in-javascript
|
||||
function loadJS(FILE_URL, async = true) {
|
||||
let scE = d.createElement("script");
|
||||
scE.setAttribute("src", FILE_URL);
|
||||
scE.setAttribute("type", "text/javascript");
|
||||
scE.setAttribute("async", async);
|
||||
d.body.appendChild(scE);
|
||||
// success event
|
||||
scE.addEventListener("load", () => {
|
||||
//console.log("File loaded");
|
||||
GetV();
|
||||
});
|
||||
// error event
|
||||
scE.addEventListener("error", (ev) => {
|
||||
console.log("Error on loading file", ev);
|
||||
alert("Loading of configuration script failed.\nIncomplete page data!");
|
||||
});
|
||||
}
|
||||
function S() {
|
||||
if (window.location.protocol == "file:") {
|
||||
loc = true;
|
||||
@ -26,9 +44,8 @@
|
||||
localStorage.setItem('locIp', locip);
|
||||
}
|
||||
}
|
||||
GetV();
|
||||
if (numM > 0 || locip) ldS();
|
||||
else gId("um").innerHTML = "No Usermods installed.";
|
||||
ldS();
|
||||
if (!numM) gId("um").innerHTML = "No Usermods installed.";
|
||||
}
|
||||
// https://stackoverflow.com/questions/3885817/how-do-i-check-that-a-number-is-float-or-integer
|
||||
function isF(n) { return n === +n && n !== (n|0); }
|
||||
@ -100,6 +117,47 @@
|
||||
urows += `<input type="${t==="int"?"number":t}" name="${k}:${f}${a?"[]":""}" ${c} oninput="check(this,'${k.substr(k.indexOf(":")+1)}')"><br>`;
|
||||
}
|
||||
}
|
||||
// https://stackoverflow.com/questions/39729741/javascript-change-input-text-to-select-option
|
||||
function addDropdown(um,fld) {
|
||||
let sel = d.createElement('select');
|
||||
let arr = d.getElementsByName(um+":"+fld);
|
||||
let inp = arr[1]; // assume 1st field to be hidden (type)
|
||||
if (inp && inp.tagName === "INPUT" && (inp.type === "text" || inp.type === "number")) { // may also use nodeName
|
||||
let v = inp.value;
|
||||
let n = inp.name;
|
||||
// copy the existing input element's attributes to the new select element
|
||||
for (var i = 0; i < inp.attributes.length; ++ i) {
|
||||
var att = inp.attributes[i];
|
||||
// type and value don't apply, so skip them
|
||||
// ** you might also want to skip style, or others -- modify as needed **
|
||||
if (att.name != 'type' && att.name != 'value' && att.name != 'class' && att.name != 'style') {
|
||||
sel.setAttribute(att.name, att.value);
|
||||
}
|
||||
}
|
||||
sel.setAttribute("data-val", v);
|
||||
// finally, replace the old input element with the new select element
|
||||
inp.parentElement.replaceChild(sel, inp);
|
||||
return sel;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
function addOption(sel,txt,val) {
|
||||
if (sel===null) return; // select object missing
|
||||
let opt = d.createElement("option");
|
||||
opt.value = val;
|
||||
opt.text = txt;
|
||||
sel.appendChild(opt);
|
||||
for (let i=0; i<sel.childNodes.length; i++) {
|
||||
let c = sel.childNodes[i];
|
||||
if (c.value == sel.dataset.val) sel.selectedIndex = i;
|
||||
}
|
||||
}
|
||||
// https://stackoverflow.com/questions/26440494/insert-text-after-this-input-element-with-javascript
|
||||
function addInfo(name,el,txt) {
|
||||
let obj = d.getElementsByName(name);
|
||||
if (obj[el]) obj[el].insertAdjacentHTML('afterend', ' '+txt);
|
||||
}
|
||||
// load settings and insert values into DOM
|
||||
function ldS() {
|
||||
var url = (loc?`http://${locip}`:'') + '/cfg.json';
|
||||
fetch(url, {
|
||||
@ -121,18 +179,19 @@
|
||||
}
|
||||
if (urows==="") urows = "Usermods configuration not found.<br>Press <i>Save</i> to initialize defaults.";
|
||||
gId("um").innerHTML = urows;
|
||||
var url = (loc?`http://${locip}`:'') + '/settings/s.js?p=8';
|
||||
loadJS(url, false); // If we set async false, file is loaded and executed, then next statement is processed
|
||||
})
|
||||
.catch(function (error) {
|
||||
gId('lserr').style.display = "inline"
|
||||
.catch((error)=>{
|
||||
gId('lserr').style.display = "inline";
|
||||
console.log(error);
|
||||
});
|
||||
}
|
||||
function svS(e) {
|
||||
e.preventDefault();
|
||||
console.log(d.Sf);
|
||||
if (d.Sf.checkValidity()) d.Sf.submit(); //https://stackoverflow.com/q/37323914
|
||||
}
|
||||
function GetV() {}
|
||||
//fun-ction GetV() {} // replaced during 'npm run build'
|
||||
</script>
|
||||
<style>@import url("style.css");</style>
|
||||
</head>
|
||||
|
@ -1,10 +1,13 @@
|
||||
#include "wled.h"
|
||||
|
||||
/*
|
||||
* Support for DMX via MAX485.
|
||||
* Change the output pin in src/dependencies/ESPDMX.cpp if needed.
|
||||
* Library from:
|
||||
* Support for DMX Output via MAX485.
|
||||
* Change the output pin in src/dependencies/ESPDMX.cpp, if needed (ESP8266)
|
||||
* Change the output pin in src/dependencies/SparkFunDMX.cpp, if needed (ESP32)
|
||||
* ESP8266 Library from:
|
||||
* https://github.com/Rickgg/ESP-Dmx
|
||||
* ESP32 Library from:
|
||||
* https://github.com/sparkfun/SparkFunDMX
|
||||
*/
|
||||
|
||||
#ifdef WLED_ENABLE_DMX
|
||||
@ -14,10 +17,16 @@ void handleDMX()
|
||||
// don't act, when in DMX Proxy mode
|
||||
if (e131ProxyUniverse != 0) return;
|
||||
|
||||
// TODO: calculate brightness manually if no shutter channel is set
|
||||
|
||||
uint8_t brightness = strip.getBrightness();
|
||||
|
||||
bool calc_brightness = true;
|
||||
|
||||
// check if no shutter channel is set
|
||||
for (byte i = 0; i < DMXChannels; i++)
|
||||
{
|
||||
if (DMXFixtureMap[i] == 5) calc_brightness = false;
|
||||
}
|
||||
|
||||
uint16_t len = strip.getLengthTotal();
|
||||
for (int i = DMXStartLED; i < len; i++) { // uses the amount of LEDs as fixture count
|
||||
|
||||
@ -35,16 +44,16 @@ void handleDMX()
|
||||
dmx.write(DMXAddr, 0);
|
||||
break;
|
||||
case 1: // Red
|
||||
dmx.write(DMXAddr, r);
|
||||
dmx.write(DMXAddr, calc_brightness ? (r * brightness) / 255 : r);
|
||||
break;
|
||||
case 2: // Green
|
||||
dmx.write(DMXAddr, g);
|
||||
dmx.write(DMXAddr, calc_brightness ? (g * brightness) / 255 : g);
|
||||
break;
|
||||
case 3: // Blue
|
||||
dmx.write(DMXAddr, b);
|
||||
dmx.write(DMXAddr, calc_brightness ? (b * brightness) / 255 : b);
|
||||
break;
|
||||
case 4: // White
|
||||
dmx.write(DMXAddr, w);
|
||||
dmx.write(DMXAddr, calc_brightness ? (w * brightness) / 255 : w);
|
||||
break;
|
||||
case 5: // Shutter channel. Controls the brightness.
|
||||
dmx.write(DMXAddr, brightness);
|
||||
@ -60,7 +69,11 @@ void handleDMX()
|
||||
}
|
||||
|
||||
void initDMX() {
|
||||
#ifdef ESP8266
|
||||
dmx.init(512); // initialize with bus length
|
||||
#else
|
||||
dmx.initWrite(512); // initialize with bus length
|
||||
#endif
|
||||
}
|
||||
|
||||
#else
|
||||
|
@ -154,7 +154,7 @@ void handleE131Packet(e131_packet_t* p, IPAddress clientIP, byte protocol){
|
||||
DMXOldDimmer = e131_data[dataOffset+0];
|
||||
bri = e131_data[dataOffset+0];
|
||||
}
|
||||
if (e131_data[dataOffset+1] < MODE_COUNT)
|
||||
if (e131_data[dataOffset+1] < strip.getModeCount())
|
||||
effectCurrent = e131_data[dataOffset+ 1];
|
||||
effectSpeed = e131_data[dataOffset+ 2]; // flickers
|
||||
effectIntensity = e131_data[dataOffset+ 3];
|
||||
|
@ -216,13 +216,52 @@ int getSignalQuality(int rssi);
|
||||
void WiFiEvent(WiFiEvent_t event);
|
||||
|
||||
//um_manager.cpp
|
||||
typedef enum UM_Data_Types {
|
||||
UMT_BYTE = 0,
|
||||
UMT_UINT16,
|
||||
UMT_INT16,
|
||||
UMT_UINT32,
|
||||
UMT_INT32,
|
||||
UMT_FLOAT,
|
||||
UMT_DOUBLE,
|
||||
UMT_BYTE_ARR,
|
||||
UMT_UINT16_ARR,
|
||||
UMT_INT16_ARR,
|
||||
UMT_UINT32_ARR,
|
||||
UMT_INT32_ARR,
|
||||
UMT_FLOAT_ARR,
|
||||
UMT_DOUBLE_ARR
|
||||
} um_types_t;
|
||||
typedef struct UM_Exchange_Data {
|
||||
// should just use: size_t arr_size, void **arr_ptr, byte *ptr_type
|
||||
size_t u_size; // size of u_data array
|
||||
um_types_t *u_type; // array of data types
|
||||
void **u_data; // array of pointers to data
|
||||
UM_Exchange_Data() {
|
||||
u_size = 0;
|
||||
u_type = nullptr;
|
||||
u_data = nullptr;
|
||||
}
|
||||
~UM_Exchange_Data() {
|
||||
if (u_type) delete[] u_type;
|
||||
if (u_data) delete[] u_data;
|
||||
}
|
||||
} um_data_t;
|
||||
const unsigned int um_data_size = sizeof(um_data_t); // 12 bytes
|
||||
|
||||
class Usermod {
|
||||
protected:
|
||||
um_data_t *um_data; // um_data should be allocated using new in (derived) Usermod's setup() or constructor
|
||||
public:
|
||||
virtual void loop() {}
|
||||
Usermod() { um_data = nullptr; }
|
||||
virtual ~Usermod() { if (um_data) delete um_data; }
|
||||
virtual void setup() = 0; // pure virtual, has to be overriden
|
||||
virtual void loop() = 0; // pure virtual, has to be overriden
|
||||
virtual void handleOverlayDraw() {}
|
||||
virtual bool handleButton(uint8_t b) { return false; }
|
||||
virtual void setup() {}
|
||||
virtual bool getUMData(um_data_t **data) { if (data) *data = nullptr; return false; };
|
||||
virtual void connected() {}
|
||||
virtual void appendConfigData() {}
|
||||
virtual void addToJsonState(JsonObject& obj) {}
|
||||
virtual void addToJsonInfo(JsonObject& obj) {}
|
||||
virtual void readFromJsonState(JsonObject& obj) {}
|
||||
@ -230,6 +269,7 @@ class Usermod {
|
||||
virtual bool readFromConfig(JsonObject& obj) { return true; } // Note as of 2021-06 readFromConfig() now needs to return a bool, see usermod_v2_example.h
|
||||
virtual void onMqttConnect(bool sessionPresent) {}
|
||||
virtual bool onMqttMessage(char* topic, char* payload) { return false; }
|
||||
virtual void onUpdateBegin(bool) {}
|
||||
virtual uint16_t getId() {return USERMOD_ID_UNSPECIFIED;}
|
||||
};
|
||||
|
||||
@ -242,8 +282,10 @@ class UsermodManager {
|
||||
void loop();
|
||||
void handleOverlayDraw();
|
||||
bool handleButton(uint8_t b);
|
||||
bool getUMData(um_data_t **um_data, uint8_t mod_id = USERMOD_ID_RESERVED); // USERMOD_ID_RESERVED will poll all usermods
|
||||
void setup();
|
||||
void connected();
|
||||
void appendConfigData();
|
||||
void addToJsonState(JsonObject& obj);
|
||||
void addToJsonInfo(JsonObject& obj);
|
||||
void readFromJsonState(JsonObject& obj);
|
||||
@ -251,6 +293,7 @@ class UsermodManager {
|
||||
bool readFromConfig(JsonObject& obj);
|
||||
void onMqttConnect(bool sessionPresent);
|
||||
bool onMqttMessage(char* topic, char* payload);
|
||||
void onUpdateBegin(bool);
|
||||
bool add(Usermod* um);
|
||||
Usermod* lookup(uint16_t mod_id);
|
||||
byte getModCount();
|
||||
|
File diff suppressed because it is too large
Load Diff
3539
wled00/html_ui.h
3539
wled00/html_ui.h
File diff suppressed because it is too large
Load Diff
@ -502,8 +502,8 @@ void decodeIR44(uint32_t code)
|
||||
case IR44_WARMWHITE : changeColor(COLOR_WARMWHITE, 63); changeEffect(FX_MODE_STATIC); break;
|
||||
case IR44_COLDWHITE : changeColor(COLOR_COLDWHITE, 191); changeEffect(FX_MODE_STATIC); break;
|
||||
case IR44_COLDWHITE2 : changeColor(COLOR_COLDWHITE2, 255); changeEffect(FX_MODE_STATIC); break;
|
||||
case IR44_REDPLUS : changeEffect(relativeChange(effectCurrent, 1, 0, MODE_COUNT -1)); break;
|
||||
case IR44_REDMINUS : changeEffect(relativeChange(effectCurrent, -1, 0, MODE_COUNT -1)); break;
|
||||
case IR44_REDPLUS : changeEffect(relativeChange(effectCurrent, 1, 0, strip.getModeCount() -1)); break;
|
||||
case IR44_REDMINUS : changeEffect(relativeChange(effectCurrent, -1, 0, strip.getModeCount() -1)); break;
|
||||
case IR44_GREENPLUS : changePalette(relativeChange(effectPalette, 1, 0, strip.getPaletteCount() -1)); break;
|
||||
case IR44_GREENMINUS : changePalette(relativeChange(effectPalette, -1, 0, strip.getPaletteCount() -1)); break;
|
||||
case IR44_BLUEPLUS : changeEffectIntensity( 16); break;
|
||||
@ -562,7 +562,7 @@ void decodeIR6(uint32_t code)
|
||||
case IR6_POWER: toggleOnOff(); break;
|
||||
case IR6_CHANNEL_UP: incBrightness(); break;
|
||||
case IR6_CHANNEL_DOWN: decBrightness(); break;
|
||||
case IR6_VOLUME_UP: changeEffect(relativeChange(effectCurrent, 1, 0, MODE_COUNT -1)); break;
|
||||
case IR6_VOLUME_UP: changeEffect(relativeChange(effectCurrent, 1, 0, strip.getModeCount() -1)); break;
|
||||
case IR6_VOLUME_DOWN: changePalette(relativeChange(effectPalette, 1, 0, strip.getPaletteCount() -1));
|
||||
switch(lastIR6ColourIdx) {
|
||||
case 0: changeColor(COLOR_RED); break;
|
||||
@ -600,7 +600,7 @@ void decodeIR9(uint32_t code)
|
||||
case IR9_DOWN : decBrightness(); break;
|
||||
case IR9_LEFT : changeEffectSpeed(-16); break;
|
||||
case IR9_RIGHT : changeEffectSpeed(16); break;
|
||||
case IR9_SELECT : changeEffect(relativeChange(effectCurrent, 1, 0, MODE_COUNT -1)); break;
|
||||
case IR9_SELECT : changeEffect(relativeChange(effectCurrent, 1, 0, strip.getModeCount() -1)); break;
|
||||
default: return;
|
||||
}
|
||||
lastValidCode = code;
|
||||
@ -670,7 +670,7 @@ void decodeIRJson(uint32_t code)
|
||||
decBrightness();
|
||||
} else if (cmdStr.startsWith(F("!presetF"))) { //!presetFallback
|
||||
uint8_t p1 = fdo["PL"] | 1;
|
||||
uint8_t p2 = fdo["FX"] | random8(MODE_COUNT -1);
|
||||
uint8_t p2 = fdo["FX"] | random8(strip.getModeCount() -1);
|
||||
uint8_t p3 = fdo["FP"] | 0;
|
||||
presetFallback(p1, p2, p3);
|
||||
}
|
||||
|
@ -72,6 +72,8 @@ void deserializeSegment(JsonObject elem, byte it, byte presetId)
|
||||
uint16_t spc = elem[F("spc")] | seg.spacing;
|
||||
uint16_t of = seg.offset;
|
||||
|
||||
if (spc>0 && spc!=seg.spacing) strip.fill(BLACK, id); // clear spacing gaps
|
||||
|
||||
uint16_t len = 1;
|
||||
if (stop > start) len = stop - start;
|
||||
int offset = elem[F("of")] | INT32_MAX;
|
||||
@ -829,9 +831,9 @@ void serializeNodes(JsonObject root)
|
||||
|
||||
void serializeModeData(JsonArray fxdata)
|
||||
{
|
||||
for (size_t i = 0; i < MODE_COUNT; i++) {
|
||||
for (size_t i = 0; i < strip.getModeCount(); i++) {
|
||||
//String lineBuffer = (const char*)pgm_read_dword(&(WS2812FX::_modeData[i]));
|
||||
String lineBuffer = WS2812FX::_modeData[i];
|
||||
String lineBuffer = strip.getModeData(i);
|
||||
if (lineBuffer.length() > 0) {
|
||||
uint8_t endPos = lineBuffer.indexOf('@');
|
||||
if (endPos>0) fxdata.add(lineBuffer.substring(endPos));
|
||||
@ -843,12 +845,12 @@ void serializeModeData(JsonArray fxdata)
|
||||
// deserializes mode names string into JsonArray
|
||||
// also removes WLED-SR extensions (@...) from deserialised names
|
||||
void serializeModeNames(JsonArray arr) {
|
||||
for (size_t i = 0; i < MODE_COUNT; i++) {
|
||||
for (size_t i = 0; i < strip.getModeCount(); i++) {
|
||||
//String lineBuffer = (const char*)pgm_read_dword(&(WS2812FX::_modeData[i]));
|
||||
String lineBuffer = WS2812FX::_modeData[i];
|
||||
String lineBuffer = strip.getModeData(i);
|
||||
if (lineBuffer.length() > 0) {
|
||||
uint8_t endPos = lineBuffer.indexOf('@');
|
||||
if (endPos>0) arr.add(lineBuffer.substring(0,endPos));
|
||||
if (endPos>0) arr.add(lineBuffer.substring(0, endPos));
|
||||
else arr.add(lineBuffer);
|
||||
}
|
||||
}
|
||||
|
@ -25,7 +25,7 @@ enum struct PinOwner : uint8_t {
|
||||
// High bit is set for all built-in pin owners
|
||||
Ethernet = 0x81,
|
||||
BusDigital = 0x82,
|
||||
BusDigital2 = 0x83,
|
||||
BusOnOff = 0x83,
|
||||
BusPwm = 0x84, // 'BusP' == PWM output using BusPwm
|
||||
Button = 0x85, // 'Butn' == button from configuration
|
||||
IR = 0x86, // 'IR' == IR receiver pin from configuration
|
||||
@ -34,6 +34,8 @@ enum struct PinOwner : uint8_t {
|
||||
DebugOut = 0x89, // 'Dbg' == debug output always IO1
|
||||
DMX = 0x8A, // 'DMX' == hard-coded to IO2
|
||||
HW_I2C = 0x8B, // 'I2C' == hardware I2C pins (4&5 on ESP8266, 21&22 on ESP32)
|
||||
AnalogMic = 0x8C, // WLEDSR
|
||||
DigitalMic = 0x8D, // WLEDSR
|
||||
// Use UserMod IDs from const.h here
|
||||
UM_Unspecified = USERMOD_ID_UNSPECIFIED, // 0x01
|
||||
UM_Example = USERMOD_ID_EXAMPLE, // 0x02 // Usermod "usermod_v2_example.h"
|
||||
@ -53,7 +55,8 @@ enum struct PinOwner : uint8_t {
|
||||
// #define USERMOD_ID_ELEKSTUBE_IPS // 0x10 // Usermod "usermod_elekstube_ips.h" -- Uses quite a few pins ... see Hardware.h and User_Setup.h
|
||||
// #define USERMOD_ID_SN_PHOTORESISTOR // 0x11 // Usermod "usermod_sn_photoresistor.h" -- Uses hard-coded pin (PHOTORESISTOR_PIN == A0), but could be easily updated to use pinManager
|
||||
UM_RGBRotaryEncoder = USERMOD_RGB_ROTARY_ENCODER, // 0x16 // Usermod "rgb-rotary-encoder.h"
|
||||
UM_QuinLEDAnPenta = USERMOD_ID_QUINLED_AN_PENTA // 0x17 // Usermod "quinled-an-penta.h"
|
||||
UM_QuinLEDAnPenta = USERMOD_ID_QUINLED_AN_PENTA, // 0x17 // Usermod "quinled-an-penta.h"
|
||||
UM_Audioreactive = USERMOD_ID_AUDIOREACTIVE // 0x1E // Usermod: "audio_reactive.h"
|
||||
};
|
||||
static_assert(0u == static_cast<uint8_t>(PinOwner::None), "PinOwner::None must be zero, so default array initialization works as expected");
|
||||
|
||||
|
@ -11,6 +11,8 @@
|
||||
// - - - - -
|
||||
|
||||
/* ----- LIBRARIES ----- */
|
||||
#ifdef ESP8266
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
#include "ESPDMX.h"
|
||||
@ -29,12 +31,12 @@ bool dmxStarted = false;
|
||||
int sendPin = 2; //dafault on ESP8266
|
||||
|
||||
//DMX value array and size. Entry 0 will hold startbyte
|
||||
uint8_t dmxData[dmxMaxChannel] = {};
|
||||
int chanSize;
|
||||
uint8_t dmxDataStore[dmxMaxChannel] = {};
|
||||
int channelSize;
|
||||
|
||||
|
||||
void DMXESPSerial::init() {
|
||||
chanSize = defaultMax;
|
||||
channelSize = defaultMax;
|
||||
|
||||
Serial1.begin(DMXSPEED);
|
||||
pinMode(sendPin, OUTPUT);
|
||||
@ -48,7 +50,7 @@ void DMXESPSerial::init(int chanQuant) {
|
||||
chanQuant = defaultMax;
|
||||
}
|
||||
|
||||
chanSize = chanQuant;
|
||||
channelSize = chanQuant;
|
||||
|
||||
Serial1.begin(DMXSPEED);
|
||||
pinMode(sendPin, OUTPUT);
|
||||
@ -61,7 +63,7 @@ uint8_t DMXESPSerial::read(int Channel) {
|
||||
|
||||
if (Channel < 1) Channel = 1;
|
||||
if (Channel > dmxMaxChannel) Channel = dmxMaxChannel;
|
||||
return(dmxData[Channel]);
|
||||
return(dmxDataStore[Channel]);
|
||||
}
|
||||
|
||||
// Function to send DMX data
|
||||
@ -69,15 +71,15 @@ void DMXESPSerial::write(int Channel, uint8_t value) {
|
||||
if (dmxStarted == false) init();
|
||||
|
||||
if (Channel < 1) Channel = 1;
|
||||
if (Channel > chanSize) Channel = chanSize;
|
||||
if (Channel > channelSize) Channel = channelSize;
|
||||
if (value < 0) value = 0;
|
||||
if (value > 255) value = 255;
|
||||
|
||||
dmxData[Channel] = value;
|
||||
dmxDataStore[Channel] = value;
|
||||
}
|
||||
|
||||
void DMXESPSerial::end() {
|
||||
chanSize = 0;
|
||||
channelSize = 0;
|
||||
Serial1.end();
|
||||
dmxStarted = false;
|
||||
}
|
||||
@ -96,10 +98,12 @@ void DMXESPSerial::update() {
|
||||
//send data
|
||||
Serial1.begin(DMXSPEED, DMXFORMAT);
|
||||
digitalWrite(sendPin, LOW);
|
||||
Serial1.write(dmxData, chanSize);
|
||||
Serial1.write(dmxDataStore, channelSize);
|
||||
Serial1.flush();
|
||||
delay(1);
|
||||
Serial1.end();
|
||||
}
|
||||
|
||||
// Function to update the DMX bus
|
||||
|
||||
#endif
|
55
wled00/src/dependencies/dmx/LICENSE.md
Normal file
55
wled00/src/dependencies/dmx/LICENSE.md
Normal file
@ -0,0 +1,55 @@
|
||||
SparkFun License Information
|
||||
============================
|
||||
|
||||
SparkFun uses two different licenses for our files — one for hardware and one for code.
|
||||
|
||||
Hardware
|
||||
---------
|
||||
|
||||
**SparkFun hardware is released under [Creative Commons Share-alike 4.0 International](http://creativecommons.org/licenses/by-sa/4.0/).**
|
||||
|
||||
Note: This is a human-readable summary of (and not a substitute for) the [license](http://creativecommons.org/licenses/by-sa/4.0/legalcode).
|
||||
|
||||
You are free to:
|
||||
|
||||
Share — copy and redistribute the material in any medium or format
|
||||
Adapt — remix, transform, and build upon the material
|
||||
for any purpose, even commercially.
|
||||
The licensor cannot revoke these freedoms as long as you follow the license terms.
|
||||
Under the following terms:
|
||||
|
||||
Attribution — You must give appropriate credit, provide a link to the license, and indicate if changes were made. You may do so in any reasonable manner, but not in any way that suggests the licensor endorses you or your use.
|
||||
ShareAlike — If you remix, transform, or build upon the material, you must distribute your contributions under the same license as the original.
|
||||
No additional restrictions — You may not apply legal terms or technological measures that legally restrict others from doing anything the license permits.
|
||||
Notices:
|
||||
|
||||
You do not have to comply with the license for elements of the material in the public domain or where your use is permitted by an applicable exception or limitation.
|
||||
No warranties are given. The license may not give you all of the permissions necessary for your intended use. For example, other rights such as publicity, privacy, or moral rights may limit how you use the material.
|
||||
|
||||
|
||||
Code
|
||||
--------
|
||||
|
||||
**SparkFun code, firmware, and software is released under the MIT License(http://opensource.org/licenses/MIT).**
|
||||
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2016 SparkFun Electronics
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
160
wled00/src/dependencies/dmx/SparkFunDMX.cpp
Normal file
160
wled00/src/dependencies/dmx/SparkFunDMX.cpp
Normal file
@ -0,0 +1,160 @@
|
||||
/******************************************************************************
|
||||
SparkFunDMX.h
|
||||
Arduino Library for the SparkFun ESP32 LED to DMX Shield
|
||||
Andy England @ SparkFun Electronics
|
||||
7/22/2019
|
||||
|
||||
Development environment specifics:
|
||||
Arduino IDE 1.6.4
|
||||
|
||||
This code is released under the [MIT License](http://opensource.org/licenses/MIT).
|
||||
Please review the LICENSE.md file included with this example. If you have any questions
|
||||
or concerns with licensing, please contact techsupport@sparkfun.com.
|
||||
Distributed as-is; no warranty is given.
|
||||
******************************************************************************/
|
||||
|
||||
/* ----- LIBRARIES ----- */
|
||||
#ifdef ESP32
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
#include "SparkFunDMX.h"
|
||||
#include <HardwareSerial.h>
|
||||
|
||||
#define dmxMaxChannel 512
|
||||
#define defaultMax 32
|
||||
|
||||
#define DMXSPEED 250000
|
||||
#define DMXFORMAT SERIAL_8N2
|
||||
#define BREAKSPEED 83333
|
||||
#define BREAKFORMAT SERIAL_8N1
|
||||
|
||||
int enablePin = -1; // disable the enable pin because it is not needed
|
||||
int rxPin = -1; // disable the receiving pin because it is not needed
|
||||
int txPin = 2; // transmit DMX data over this pin (default is pin 2)
|
||||
|
||||
//DMX value array and size. Entry 0 will hold startbyte
|
||||
uint8_t dmxData[dmxMaxChannel] = {};
|
||||
int chanSize;
|
||||
int currentChannel = 0;
|
||||
|
||||
HardwareSerial DMXSerial(2);
|
||||
|
||||
/* Interrupt Timer for DMX Receive */
|
||||
hw_timer_t * timer = NULL;
|
||||
portMUX_TYPE timerMux = portMUX_INITIALIZER_UNLOCKED;
|
||||
|
||||
volatile int _interruptCounter;
|
||||
volatile bool _startCodeDetected = false;
|
||||
|
||||
|
||||
/* Start Code is detected by 21 low interrupts */
|
||||
void IRAM_ATTR onTimer() {
|
||||
if (digitalRead(rxPin) == 1)
|
||||
{
|
||||
_interruptCounter = 0; //If the RX Pin is high, we are not in an interrupt
|
||||
}
|
||||
else
|
||||
{
|
||||
_interruptCounter++;
|
||||
}
|
||||
if (_interruptCounter > 9)
|
||||
{
|
||||
portENTER_CRITICAL_ISR(&timerMux);
|
||||
_startCodeDetected = true;
|
||||
DMXSerial.begin(DMXSPEED, DMXFORMAT, rxPin, txPin);
|
||||
portEXIT_CRITICAL_ISR(&timerMux);
|
||||
_interruptCounter = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void SparkFunDMX::initRead(int chanQuant) {
|
||||
|
||||
timer = timerBegin(0, 1, true);
|
||||
timerAttachInterrupt(timer, &onTimer, true);
|
||||
timerAlarmWrite(timer, 320, true);
|
||||
timerAlarmEnable(timer);
|
||||
_READWRITE = _READ;
|
||||
if (chanQuant > dmxMaxChannel || chanQuant <= 0)
|
||||
{
|
||||
chanQuant = defaultMax;
|
||||
}
|
||||
chanSize = chanQuant;
|
||||
pinMode(enablePin, OUTPUT);
|
||||
digitalWrite(enablePin, LOW);
|
||||
pinMode(rxPin, INPUT);
|
||||
}
|
||||
|
||||
// Set up the DMX-Protocol
|
||||
void SparkFunDMX::initWrite (int chanQuant) {
|
||||
|
||||
_READWRITE = _WRITE;
|
||||
if (chanQuant > dmxMaxChannel || chanQuant <= 0) {
|
||||
chanQuant = defaultMax;
|
||||
}
|
||||
|
||||
chanSize = chanQuant + 1; //Add 1 for start code
|
||||
|
||||
DMXSerial.begin(DMXSPEED, DMXFORMAT, rxPin, txPin);
|
||||
pinMode(enablePin, OUTPUT);
|
||||
digitalWrite(enablePin, HIGH);
|
||||
}
|
||||
|
||||
// Function to read DMX data
|
||||
uint8_t SparkFunDMX::read(int Channel) {
|
||||
if (Channel > chanSize) Channel = chanSize;
|
||||
return(dmxData[Channel - 1]); //subtract one to account for start byte
|
||||
}
|
||||
|
||||
// Function to send DMX data
|
||||
void SparkFunDMX::write(int Channel, uint8_t value) {
|
||||
if (Channel < 0) Channel = 0;
|
||||
if (Channel > chanSize) chanSize = Channel;
|
||||
dmxData[0] = 0;
|
||||
dmxData[Channel] = value; //add one to account for start byte
|
||||
}
|
||||
|
||||
|
||||
|
||||
void SparkFunDMX::update() {
|
||||
if (_READWRITE == _WRITE)
|
||||
{
|
||||
//Send DMX break
|
||||
digitalWrite(txPin, HIGH);
|
||||
DMXSerial.begin(BREAKSPEED, BREAKFORMAT, rxPin, txPin);//Begin the Serial port
|
||||
DMXSerial.write(0);
|
||||
DMXSerial.flush();
|
||||
delay(1);
|
||||
DMXSerial.end();
|
||||
|
||||
//Send DMX data
|
||||
DMXSerial.begin(DMXSPEED, DMXFORMAT, rxPin, txPin);//Begin the Serial port
|
||||
DMXSerial.write(dmxData, chanSize);
|
||||
DMXSerial.flush();
|
||||
DMXSerial.end();//clear our DMX array, end the Hardware Serial port
|
||||
}
|
||||
else if (_READWRITE == _READ)//In a perfect world, this function ends serial communication upon packet completion and attaches RX to a CHANGE interrupt so the start code can be read again
|
||||
{
|
||||
if (_startCodeDetected == true)
|
||||
{
|
||||
while (DMXSerial.available())
|
||||
{
|
||||
dmxData[currentChannel++] = DMXSerial.read();
|
||||
}
|
||||
if (currentChannel > chanSize) //Set the channel counter back to 0 if we reach the known end size of our packet
|
||||
{
|
||||
|
||||
portENTER_CRITICAL(&timerMux);
|
||||
_startCodeDetected = false;
|
||||
DMXSerial.flush();
|
||||
DMXSerial.end();
|
||||
portEXIT_CRITICAL(&timerMux);
|
||||
currentChannel = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Function to update the DMX bus
|
||||
|
||||
#endif
|
38
wled00/src/dependencies/dmx/SparkFunDMX.h
Normal file
38
wled00/src/dependencies/dmx/SparkFunDMX.h
Normal file
@ -0,0 +1,38 @@
|
||||
/******************************************************************************
|
||||
SparkFunDMX.h
|
||||
Arduino Library for the SparkFun ESP32 LED to DMX Shield
|
||||
Andy England @ SparkFun Electronics
|
||||
7/22/2019
|
||||
|
||||
Development environment specifics:
|
||||
Arduino IDE 1.6.4
|
||||
|
||||
This code is released under the [MIT License](http://opensource.org/licenses/MIT).
|
||||
Please review the LICENSE.md file included with this example. If you have any questions
|
||||
or concerns with licensing, please contact techsupport@sparkfun.com.
|
||||
Distributed as-is; no warranty is given.
|
||||
******************************************************************************/
|
||||
|
||||
#include <inttypes.h>
|
||||
|
||||
|
||||
#ifndef SparkFunDMX_h
|
||||
#define SparkFunDMX_h
|
||||
|
||||
// ---- Methods ----
|
||||
|
||||
class SparkFunDMX {
|
||||
public:
|
||||
void initRead(int maxChan);
|
||||
void initWrite(int maxChan);
|
||||
uint8_t read(int Channel);
|
||||
void write(int channel, uint8_t value);
|
||||
void update();
|
||||
private:
|
||||
uint8_t _startCodeValue = 0xFF;
|
||||
bool _READ = true;
|
||||
bool _WRITE = false;
|
||||
bool _READWRITE;
|
||||
};
|
||||
|
||||
#endif
|
@ -4,8 +4,11 @@
|
||||
*/
|
||||
|
||||
//Usermod Manager internals
|
||||
void UsermodManager::setup() { for (byte i = 0; i < numMods; i++) ums[i]->setup(); }
|
||||
void UsermodManager::connected() { for (byte i = 0; i < numMods; i++) ums[i]->connected(); }
|
||||
void UsermodManager::loop() { for (byte i = 0; i < numMods; i++) ums[i]->loop(); }
|
||||
void UsermodManager::handleOverlayDraw() { for (byte i = 0; i < numMods; i++) ums[i]->handleOverlayDraw(); }
|
||||
void UsermodManager::appendConfigData() { for (byte i = 0; i < numMods; i++) ums[i]->appendConfigData(); }
|
||||
bool UsermodManager::handleButton(uint8_t b) {
|
||||
bool overrideIO = false;
|
||||
for (byte i = 0; i < numMods; i++) {
|
||||
@ -13,10 +16,13 @@ bool UsermodManager::handleButton(uint8_t b) {
|
||||
}
|
||||
return overrideIO;
|
||||
}
|
||||
|
||||
void UsermodManager::setup() { for (byte i = 0; i < numMods; i++) ums[i]->setup(); }
|
||||
void UsermodManager::connected() { for (byte i = 0; i < numMods; i++) ums[i]->connected(); }
|
||||
|
||||
bool UsermodManager::getUMData(um_data_t **data, uint8_t mod_id) {
|
||||
for (byte i = 0; i < numMods; i++) {
|
||||
if (mod_id > 0 && ums[i]->getId() != mod_id) continue; // only get data form requested usermod if provided
|
||||
if (ums[i]->getUMData(data)) return true; // if usermod does provide data return immediately (only one usermod can povide data at one time)
|
||||
}
|
||||
return false;
|
||||
}
|
||||
void UsermodManager::addToJsonState(JsonObject& obj) { for (byte i = 0; i < numMods; i++) ums[i]->addToJsonState(obj); }
|
||||
void UsermodManager::addToJsonInfo(JsonObject& obj) { for (byte i = 0; i < numMods; i++) ums[i]->addToJsonInfo(obj); }
|
||||
void UsermodManager::readFromJsonState(JsonObject& obj) { for (byte i = 0; i < numMods; i++) ums[i]->readFromJsonState(obj); }
|
||||
@ -33,6 +39,7 @@ bool UsermodManager::onMqttMessage(char* topic, char* payload) {
|
||||
for (byte i = 0; i < numMods; i++) if (ums[i]->onMqttMessage(topic, payload)) return true;
|
||||
return false;
|
||||
}
|
||||
void UsermodManager::onUpdateBegin(bool init) { for (byte i = 0; i < numMods; i++) ums[i]->onUpdateBegin(init); } // notify usermods that update is to begin
|
||||
|
||||
/*
|
||||
* Enables usermods to lookup another Usermod.
|
||||
@ -49,8 +56,7 @@ Usermod* UsermodManager::lookup(uint16_t mod_id) {
|
||||
bool UsermodManager::add(Usermod* um)
|
||||
{
|
||||
if (numMods >= WLED_MAX_USERMODS || um == nullptr) return false;
|
||||
ums[numMods] = um;
|
||||
numMods++;
|
||||
ums[numMods++] = um;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -132,6 +132,10 @@
|
||||
#include "../usermods/Si7021_MQTT_HA/usermod_si7021_mqtt_ha.h"
|
||||
#endif
|
||||
|
||||
#ifdef USERMOD_AUDIOREACTIVE
|
||||
#include "../usermods/audioreactive/audio_reactive.h"
|
||||
#endif
|
||||
|
||||
void registerUsermods()
|
||||
{
|
||||
/*
|
||||
@ -251,4 +255,8 @@ void registerUsermods()
|
||||
#ifdef USERMOD_SI7021_MQTT_HA
|
||||
usermods.add(new Si7021_MQTT_HA());
|
||||
#endif
|
||||
|
||||
#ifdef USERMOD_AUDIOREACTIVE
|
||||
usermods.add(new AudioReactive());
|
||||
#endif
|
||||
}
|
||||
|
@ -234,10 +234,10 @@ void releaseJSONBufferLock()
|
||||
uint8_t extractModeName(uint8_t mode, const char *src, char *dest, uint8_t maxLen)
|
||||
{
|
||||
if (src == JSON_mode_names || src == nullptr) {
|
||||
if (mode < MODE_COUNT) {
|
||||
if (mode < strip.getModeCount()) {
|
||||
char lineBuffer[256];
|
||||
//strcpy_P(lineBuffer, (const char*)pgm_read_dword(&(WS2812FX::_modeData[mode])));
|
||||
strcpy_P(lineBuffer, WS2812FX::_modeData[mode]);
|
||||
strcpy_P(lineBuffer, strip.getModeData(mode));
|
||||
if (strlen(lineBuffer) > 0) {
|
||||
size_t j = 0;
|
||||
for (; j < maxLen; j++) {
|
||||
@ -286,8 +286,8 @@ uint8_t extractModeSlider(uint8_t mode, uint8_t slider, char *dest, uint8_t maxL
|
||||
{
|
||||
dest[0] = '\0'; // start by clearing buffer
|
||||
|
||||
if (mode < MODE_COUNT) {
|
||||
String lineBuffer = WS2812FX::_modeData[mode];
|
||||
if (mode < strip.getModeCount()) {
|
||||
String lineBuffer = strip.getModeData(mode);
|
||||
if (lineBuffer.length() > 0) {
|
||||
int16_t start = lineBuffer.indexOf('@');
|
||||
int16_t stop = lineBuffer.indexOf(';', start);
|
||||
|
@ -367,7 +367,9 @@ void WLED::setup()
|
||||
sprintf(mqttClientID + 5, "%*s", 6, escapedMac.c_str() + 6);
|
||||
}
|
||||
|
||||
#ifdef WLED_ENABLE_ADALIGHT
|
||||
if (Serial.available() > 0 && Serial.peek() == 'I') handleImprovPacket();
|
||||
#endif
|
||||
|
||||
strip.service();
|
||||
|
||||
@ -392,7 +394,10 @@ void WLED::setup()
|
||||
initDMX();
|
||||
#endif
|
||||
|
||||
#ifdef WLED_ENABLE_ADALIGHT
|
||||
if (Serial.available() > 0 && Serial.peek() == 'I') handleImprovPacket();
|
||||
#endif
|
||||
|
||||
// HTTP server page init
|
||||
initServer();
|
||||
|
||||
|
@ -8,7 +8,7 @@
|
||||
*/
|
||||
|
||||
// version code in format yymmddb (b = daily build)
|
||||
#define VERSION 2206171
|
||||
#define VERSION 2206291
|
||||
|
||||
//uncomment this if you have a "my_config.h" file you'd like to use
|
||||
//#define WLED_USE_MY_CONFIG
|
||||
@ -118,7 +118,11 @@
|
||||
#endif
|
||||
|
||||
#ifdef WLED_ENABLE_DMX
|
||||
#ifdef ESP8266
|
||||
#include "src/dependencies/dmx/ESPDMX.h"
|
||||
#else //ESP32
|
||||
#include "src/dependencies/dmx/SparkFunDMX.h"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#include "src/dependencies/e131/ESPAsyncE131.h"
|
||||
@ -359,7 +363,11 @@ WLED_GLOBAL bool arlsDisableGammaCorrection _INIT(true); // activate if
|
||||
WLED_GLOBAL bool arlsForceMaxBri _INIT(false); // enable to force max brightness if source has very dark colors that would be black
|
||||
|
||||
#ifdef WLED_ENABLE_DMX
|
||||
WLED_GLOBAL DMXESPSerial dmx;
|
||||
#ifdef ESP8266
|
||||
WLED_GLOBAL DMXESPSerial dmx;
|
||||
#else //ESP32
|
||||
WLED_GLOBAL SparkFunDMX dmx;
|
||||
#endif
|
||||
WLED_GLOBAL uint16_t e131ProxyUniverse _INIT(0); // output this E1.31 (sACN) / ArtNet universe via MAX485 (0 = disabled)
|
||||
#endif
|
||||
WLED_GLOBAL uint16_t e131Universe _INIT(1); // settings for E1.31 (sACN) protocol (only DMX_MODE_MULTIPLE_* can span over consequtive universes)
|
||||
|
@ -86,8 +86,8 @@ void handleSerial()
|
||||
Serial.write(0xC9); Serial.write(0xDA);
|
||||
uint16_t used = strip.getLengthTotal();
|
||||
uint16_t len = used*3;
|
||||
Serial.write((len << 8) & 0xFF);
|
||||
Serial.write( len & 0xFF);
|
||||
Serial.write(highByte(len));
|
||||
Serial.write(lowByte(len));
|
||||
for (uint16_t i=0; i < used; i++) {
|
||||
uint32_t c = strip.getPixelColor(i);
|
||||
Serial.write(qadd8(W(c), R(c))); //R, add white channel to RGB channels as a simple RGBW -> RGB map
|
||||
|
@ -284,6 +284,7 @@ void initServer()
|
||||
if(!index){
|
||||
DEBUG_PRINTLN(F("OTA Update Start"));
|
||||
WLED::instance().disableWatchdog();
|
||||
usermods.onUpdateBegin(true); // notify usermods that update is about to begin (some may require task de-init)
|
||||
lastEditTime = millis(); // make sure PIN does not lock during update
|
||||
#ifdef ESP8266
|
||||
Update.runAsync(true);
|
||||
@ -297,6 +298,7 @@ void initServer()
|
||||
} else {
|
||||
DEBUG_PRINTLN(F("Update Failed"));
|
||||
WLED::instance().enableWatchdog();
|
||||
usermods.onUpdateBegin(false); // notify usermods that update has failed (some may require task init)
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -615,6 +615,7 @@ void getSettingsJS(byte subPage, char* dest)
|
||||
oappend(SET_F("numM="));
|
||||
oappendi(usermods.getModCount());
|
||||
oappend(";");
|
||||
usermods.appendConfigData();
|
||||
}
|
||||
|
||||
if (subPage == 9) // update
|
||||
|
Loading…
Reference in New Issue
Block a user