Compare commits

..

4 Commits

Author SHA1 Message Date
Will Miles cab2f91bc6 handleHttpApi: Move web response to web context
No need to even consider this for non-web requests.  Move the request
special case to that context as well.
2024-11-10 19:30:01 -05:00
Will Miles e65f6c7bc7 handleHttpApi: Remove 'IN' tag
Internal requests are indicated by passing nullptr for the request
argument.  If we *are* in fact called from an HTTP request, we must
generate some kind of reply anyways, so this parameter is obsolete.
2024-11-10 19:30:01 -05:00
Will Miles 4d0b79f300 HTTP API: Extract 'win' prefix to web server only
Rather add this token in to all call sites, check for it only where it
matters: when we've got an HTTP request.  If it's passed in other
contexts, it will be safely ignored.
2024-11-10 19:30:01 -05:00
Will Miles e570460cd9 Rename handleSet to handleHttpApi
Clarify what this function does: it implements the legacy HTTP
API parser.
2024-11-10 19:30:01 -05:00
70 changed files with 1714 additions and 4889 deletions
-80
View File
@@ -1,80 +0,0 @@
name: WLED Build
# Only included into other workflows
on:
workflow_call:
jobs:
get_default_envs:
name: Gather Environments
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.12'
cache: 'pip'
- name: Install PlatformIO
run: pip install -r requirements.txt
- name: Get default environments
id: envs
run: |
echo "environments=$(pio project config --json-output | jq -cr '.[0][1][0][1]')" >> $GITHUB_OUTPUT
outputs:
environments: ${{ steps.envs.outputs.environments }}
build:
name: Build Enviornments
runs-on: ubuntu-latest
needs: get_default_envs
strategy:
fail-fast: false
matrix:
environment: ${{ fromJSON(needs.get_default_envs.outputs.environments) }}
steps:
- uses: actions/checkout@v4
- name: Set up Node.js
uses: actions/setup-node@v4
with:
cache: 'npm'
- run: npm ci
- name: Cache PlatformIO
uses: actions/cache@v4
with:
path: |
~/.platformio/.cache
~/.buildcache
build_output
key: pio-${{ runner.os }}-${{ matrix.environment }}-${{ hashFiles('platformio.ini', 'pio-scripts/output_bins.py') }}-${{ hashFiles('wled00/**', 'usermods/**') }}
restore-keys: pio-${{ runner.os }}-${{ matrix.environment }}-${{ hashFiles('platformio.ini', 'pio-scripts/output_bins.py') }}-
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.12'
cache: 'pip'
- name: Install PlatformIO
run: pip install -r requirements.txt
- name: Build firmware
run: pio run -e ${{ matrix.environment }}
- uses: actions/upload-artifact@v4
with:
name: firmware-${{ matrix.environment }}
path: |
build_output/release/*.bin
build_output/release/*_ESP02*.bin.gz
testCdata:
name: Test cdata.js
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Use Node.js
uses: actions/setup-node@v4
with:
node-version: '20.x'
cache: 'npm'
- run: npm ci
- run: npm test
-36
View File
@@ -1,36 +0,0 @@
name: WLED Release CI
on:
push:
tags:
- '*'
jobs:
wled_build:
uses: ./.github/workflows/build.yml
release:
name: Create Release
runs-on: ubuntu-latest
needs: wled_build
steps:
- uses: actions/download-artifact@v4
with:
merge-multiple: true
- name: "✏️ Generate release changelog"
id: changelog
uses: janheinrichmerker/action-github-changelog-generator@v2.3
with:
token: ${{ secrets.GITHUB_TOKEN }}
sinceTag: v0.15.0
releaseBranch: 0_15_x
- name: Create draft release
uses: softprops/action-gh-release@v1
with:
body: ${{ steps.changelog.outputs.changelog }}
draft: True
files: |
*.bin
*.bin.gz
+90 -7
View File
@@ -1,11 +1,94 @@
name: WLED CI
on:
push:
branches:
- '*'
pull_request:
on: [push, pull_request]
jobs:
wled_build:
uses: ./.github/workflows/build.yml
get_default_envs:
name: Gather Environments
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.12'
cache: 'pip'
- name: Install PlatformIO
run: pip install -r requirements.txt
- name: Get default environments
id: envs
run: |
echo "environments=$(pio project config --json-output | jq -cr '.[0][1][0][1]')" >> $GITHUB_OUTPUT
outputs:
environments: ${{ steps.envs.outputs.environments }}
build:
name: Build Enviornments
runs-on: ubuntu-latest
needs: get_default_envs
strategy:
fail-fast: false
matrix:
environment: ${{ fromJSON(needs.get_default_envs.outputs.environments) }}
steps:
- uses: actions/checkout@v4
- name: Set up Node.js
uses: actions/setup-node@v4
with:
cache: 'npm'
- run: npm ci
- name: Cache PlatformIO
uses: actions/cache@v4
with:
path: |
~/.platformio/.cache
~/.buildcache
build_output
key: pio-${{ runner.os }}-${{ matrix.environment }}-${{ hashFiles('platformio.ini', 'pio-scripts/output_bins.py') }}-${{ hashFiles('wled00/**', 'usermods/**') }}
restore-keys: pio-${{ runner.os }}-${{ matrix.environment }}-${{ hashFiles('platformio.ini', 'pio-scripts/output_bins.py') }}-
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.12'
cache: 'pip'
- name: Install PlatformIO
run: pip install -r requirements.txt
- name: Build firmware
run: pio run -e ${{ matrix.environment }}
- uses: actions/upload-artifact@v4
with:
name: firmware-${{ matrix.environment }}
path: |
build_output/release/*.bin
build_output/release/*_ESP02*.bin.gz
release:
name: Create Release
runs-on: ubuntu-latest
needs: build
if: startsWith(github.ref, 'refs/tags/')
steps:
- uses: actions/download-artifact@v4
with:
merge-multiple: true
- name: Create draft release
uses: softprops/action-gh-release@v1
with:
draft: True
files: |
*.bin
*.bin.gz
testCdata:
name: Test cdata.js
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Use Node.js
uses: actions/setup-node@v4
with:
node-version: '20.x'
cache: 'npm'
- run: npm ci
- run: npm test
-2
View File
@@ -15,7 +15,6 @@ wled-update.sh
/build_output/
/node_modules/
/logs/
/wled00/extLibs
/wled00/LittleFS
@@ -23,4 +22,3 @@ wled-update.sh
/wled00/Release
/wled00/wled00.ino.cpp
/wled00/html_*.h
_codeql_detected_source_root
-18
View File
@@ -1,23 +1,5 @@
## WLED changelog
#### Build 2412100
- WLED 0.15.0 release
- Usermod BME280: Fix "Unit of Measurement" for temperature
- WiFi reconnect bugfix (@blazoncek)
#### Build 2411250
- WLED 0.15.0-rc1 release
- Add support for esp32S3_wroom2 (#4243 by @softhack007)
- Fix mixed LED SK6812 and ws2812b booloop (#4301 by @willmmiles)
- Improved FPS calculation (by DedeHai)
- Fix crashes when using HTTP API within MQTT (#4269 by @willmmiles)
- Fix array overflow in exploding_fireworks (#4120 by @willmmiles)
- Fix MQTT topic buffer length (#4293 by @WouterGritter)
- Fix SparkFunDMX fix for possible array bounds violation in DMX.write (by @softhack007)
- Allow TV Simulator on single LED segments (by @softhack007)
- Fix WLED_RELEASE_NAME (by @netmindz)
#### Build 2410270
- WLED 0.15.0-b7 release
- Re-license the WLED project from MIT to EUPL (#4194 by @Aircoookie)
@@ -1,469 +0,0 @@
/*-------------------------------------------------------------------------
NeoPixel driver for ESP32 RMTs using High-priority Interrupt
(NB. This cannot be mixed with the non-HI driver.)
Written by Will M. Miles.
I invest time and resources providing this open source code,
please support me by donating (see https://github.com/Makuna/NeoPixelBus)
-------------------------------------------------------------------------
This file is part of the Makuna/NeoPixelBus library.
NeoPixelBus is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
NeoPixelBus is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with NeoPixel. If not, see
<http://www.gnu.org/licenses/>.
-------------------------------------------------------------------------*/
#pragma once
#if defined(ARDUINO_ARCH_ESP32)
// Use the NeoEspRmtSpeed types from the driver-based implementation
#include <NeoPixelBus.h>
namespace NeoEsp32RmtHiMethodDriver {
// Install the driver for a specific channel, specifying timing properties
esp_err_t Install(rmt_channel_t channel, uint32_t rmtBit0, uint32_t rmtBit1, uint32_t resetDuration);
// Remove the driver on a specific channel
esp_err_t Uninstall(rmt_channel_t channel);
// Write a buffer of data to a specific channel.
// Buffer reference is held until write completes.
esp_err_t Write(rmt_channel_t channel, const uint8_t *src, size_t src_size);
// Wait until transaction is complete.
esp_err_t WaitForTxDone(rmt_channel_t channel, TickType_t wait_time);
};
template<typename T_SPEED, typename T_CHANNEL> class NeoEsp32RmtHIMethodBase
{
public:
typedef NeoNoSettings SettingsObject;
NeoEsp32RmtHIMethodBase(uint8_t pin, uint16_t pixelCount, size_t elementSize, size_t settingsSize) :
_sizeData(pixelCount * elementSize + settingsSize),
_pin(pin)
{
construct();
}
NeoEsp32RmtHIMethodBase(uint8_t pin, uint16_t pixelCount, size_t elementSize, size_t settingsSize, NeoBusChannel channel) :
_sizeData(pixelCount* elementSize + settingsSize),
_pin(pin),
_channel(channel)
{
construct();
}
~NeoEsp32RmtHIMethodBase()
{
// wait until the last send finishes before destructing everything
// arbitrary time out of 10 seconds
ESP_ERROR_CHECK_WITHOUT_ABORT(NeoEsp32RmtHiMethodDriver::WaitForTxDone(_channel.RmtChannelNumber, 10000 / portTICK_PERIOD_MS));
ESP_ERROR_CHECK(NeoEsp32RmtHiMethodDriver::Uninstall(_channel.RmtChannelNumber));
gpio_matrix_out(_pin, SIG_GPIO_OUT_IDX, false, false);
pinMode(_pin, INPUT);
free(_dataEditing);
free(_dataSending);
}
bool IsReadyToUpdate() const
{
return (ESP_OK == ESP_ERROR_CHECK_WITHOUT_ABORT_SILENT_TIMEOUT(NeoEsp32RmtHiMethodDriver::WaitForTxDone(_channel.RmtChannelNumber, 0)));
}
void Initialize()
{
rmt_config_t config = {};
config.rmt_mode = RMT_MODE_TX;
config.channel = _channel.RmtChannelNumber;
config.gpio_num = static_cast<gpio_num_t>(_pin);
config.mem_block_num = 1;
config.tx_config.loop_en = false;
config.tx_config.idle_output_en = true;
config.tx_config.idle_level = T_SPEED::IdleLevel;
config.tx_config.carrier_en = false;
config.tx_config.carrier_level = RMT_CARRIER_LEVEL_LOW;
config.clk_div = T_SPEED::RmtClockDivider;
ESP_ERROR_CHECK(rmt_config(&config)); // Uses ESP library
ESP_ERROR_CHECK(NeoEsp32RmtHiMethodDriver::Install(_channel.RmtChannelNumber, T_SPEED::RmtBit0, T_SPEED::RmtBit1, T_SPEED::RmtDurationReset));
}
void Update(bool maintainBufferConsistency)
{
// wait for not actively sending data
// this will time out at 10 seconds, an arbitrarily long period of time
// and do nothing if this happens
if (ESP_OK == ESP_ERROR_CHECK_WITHOUT_ABORT(NeoEsp32RmtHiMethodDriver::WaitForTxDone(_channel.RmtChannelNumber, 10000 / portTICK_PERIOD_MS)))
{
// now start the RMT transmit with the editing buffer before we swap
ESP_ERROR_CHECK_WITHOUT_ABORT(NeoEsp32RmtHiMethodDriver::Write(_channel.RmtChannelNumber, _dataEditing, _sizeData));
if (maintainBufferConsistency)
{
// copy editing to sending,
// this maintains the contract that "colors present before will
// be the same after", otherwise GetPixelColor will be inconsistent
memcpy(_dataSending, _dataEditing, _sizeData);
}
// swap so the user can modify without affecting the async operation
std::swap(_dataSending, _dataEditing);
}
}
bool AlwaysUpdate()
{
// this method requires update to be called only if changes to buffer
return false;
}
bool SwapBuffers()
{
std::swap(_dataSending, _dataEditing);
return true;
}
uint8_t* getData() const
{
return _dataEditing;
};
size_t getDataSize() const
{
return _sizeData;
}
void applySettings([[maybe_unused]] const SettingsObject& settings)
{
}
private:
const size_t _sizeData; // Size of '_data*' buffers
const uint8_t _pin; // output pin number
const T_CHANNEL _channel; // holds instance for multi channel support
// Holds data stream which include LED color values and other settings as needed
uint8_t* _dataEditing; // exposed for get and set
uint8_t* _dataSending; // used for async send using RMT
void construct()
{
_dataEditing = static_cast<uint8_t*>(malloc(_sizeData));
// data cleared later in Begin()
_dataSending = static_cast<uint8_t*>(malloc(_sizeData));
// no need to initialize it, it gets overwritten on every send
}
};
// normal
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedWs2811, NeoEsp32RmtChannelN> NeoEsp32RmtHINWs2811Method;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedWs2812x, NeoEsp32RmtChannelN> NeoEsp32RmtHINWs2812xMethod;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedWs2812x, NeoEsp32RmtChannelN> NeoEsp32RmtHINWs2816Method;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedWs2805, NeoEsp32RmtChannelN> NeoEsp32RmtHINWs2805Method;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedSk6812, NeoEsp32RmtChannelN> NeoEsp32RmtHINSk6812Method;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedTm1814, NeoEsp32RmtChannelN> NeoEsp32RmtHINTm1814Method;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedTm1829, NeoEsp32RmtChannelN> NeoEsp32RmtHINTm1829Method;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedTm1914, NeoEsp32RmtChannelN> NeoEsp32RmtHINTm1914Method;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedApa106, NeoEsp32RmtChannelN> NeoEsp32RmtHINApa106Method;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedTx1812, NeoEsp32RmtChannelN> NeoEsp32RmtHINTx1812Method;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedGs1903, NeoEsp32RmtChannelN> NeoEsp32RmtHINGs1903Method;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeed800Kbps, NeoEsp32RmtChannelN> NeoEsp32RmtHIN800KbpsMethod;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeed400Kbps, NeoEsp32RmtChannelN> NeoEsp32RmtHIN400KbpsMethod;
typedef NeoEsp32RmtHINWs2805Method NeoEsp32RmtHINWs2814Method;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedWs2811, NeoEsp32RmtChannel0> NeoEsp32RmtHI0Ws2811Method;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedWs2812x, NeoEsp32RmtChannel0> NeoEsp32RmtHI0Ws2812xMethod;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedWs2812x, NeoEsp32RmtChannel0> NeoEsp32RmtHI0Ws2816Method;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedWs2805, NeoEsp32RmtChannel0> NeoEsp32RmtHI0Ws2805Method;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedSk6812, NeoEsp32RmtChannel0> NeoEsp32RmtHI0Sk6812Method;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedTm1814, NeoEsp32RmtChannel0> NeoEsp32RmtHI0Tm1814Method;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedTm1829, NeoEsp32RmtChannel0> NeoEsp32RmtHI0Tm1829Method;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedTm1914, NeoEsp32RmtChannel0> NeoEsp32RmtHI0Tm1914Method;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedApa106, NeoEsp32RmtChannel0> NeoEsp32RmtHI0Apa106Method;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedTx1812, NeoEsp32RmtChannel0> NeoEsp32RmtHI0Tx1812Method;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedGs1903, NeoEsp32RmtChannel0> NeoEsp32RmtHI0Gs1903Method;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeed800Kbps, NeoEsp32RmtChannel0> NeoEsp32RmtHI0800KbpsMethod;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeed400Kbps, NeoEsp32RmtChannel0> NeoEsp32RmtHI0400KbpsMethod;
typedef NeoEsp32RmtHI0Ws2805Method NeoEsp32RmtHI0Ws2814Method;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedWs2811, NeoEsp32RmtChannel1> NeoEsp32RmtHI1Ws2811Method;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedWs2812x, NeoEsp32RmtChannel1> NeoEsp32RmtHI1Ws2812xMethod;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedWs2812x, NeoEsp32RmtChannel1> NeoEsp32RmtHI1Ws2816Method;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedWs2805, NeoEsp32RmtChannel1> NeoEsp32RmtHI1Ws2805Method;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedSk6812, NeoEsp32RmtChannel1> NeoEsp32RmtHI1Sk6812Method;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedTm1814, NeoEsp32RmtChannel1> NeoEsp32RmtHI1Tm1814Method;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedTm1829, NeoEsp32RmtChannel1> NeoEsp32RmtHI1Tm1829Method;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedTm1914, NeoEsp32RmtChannel1> NeoEsp32RmtHI1Tm1914Method;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedApa106, NeoEsp32RmtChannel1> NeoEsp32RmtHI1Apa106Method;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedTx1812, NeoEsp32RmtChannel1> NeoEsp32RmtHI1Tx1812Method;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedGs1903, NeoEsp32RmtChannel1> NeoEsp32RmtHI1Gs1903Method;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeed800Kbps, NeoEsp32RmtChannel1> NeoEsp32RmtHI1800KbpsMethod;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeed400Kbps, NeoEsp32RmtChannel1> NeoEsp32RmtHI1400KbpsMethod;
typedef NeoEsp32RmtHI1Ws2805Method NeoEsp32RmtHI1Ws2814Method;
#if !defined(CONFIG_IDF_TARGET_ESP32C3)
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedWs2811, NeoEsp32RmtChannel2> NeoEsp32RmtHI2Ws2811Method;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedWs2812x, NeoEsp32RmtChannel2> NeoEsp32RmtHI2Ws2812xMethod;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedWs2812x, NeoEsp32RmtChannel2> NeoEsp32RmtHI2Ws2816Method;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedWs2805, NeoEsp32RmtChannel2> NeoEsp32RmtHI2Ws2805Method;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedSk6812, NeoEsp32RmtChannel2> NeoEsp32RmtHI2Sk6812Method;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedTm1814, NeoEsp32RmtChannel2> NeoEsp32RmtHI2Tm1814Method;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedTm1829, NeoEsp32RmtChannel2> NeoEsp32RmtHI2Tm1829Method;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedTm1914, NeoEsp32RmtChannel2> NeoEsp32RmtHI2Tm1914Method;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedApa106, NeoEsp32RmtChannel2> NeoEsp32RmtHI2Apa106Method;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedTx1812, NeoEsp32RmtChannel2> NeoEsp32RmtHI2Tx1812Method;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedGs1903, NeoEsp32RmtChannel2> NeoEsp32RmtHI2Gs1903Method;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeed800Kbps, NeoEsp32RmtChannel2> NeoEsp32RmtHI2800KbpsMethod;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeed400Kbps, NeoEsp32RmtChannel2> NeoEsp32RmtHI2400KbpsMethod;
typedef NeoEsp32RmtHI2Ws2805Method NeoEsp32RmtHI2Ws2814Method;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedWs2811, NeoEsp32RmtChannel3> NeoEsp32RmtHI3Ws2811Method;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedWs2812x, NeoEsp32RmtChannel3> NeoEsp32RmtHI3Ws2812xMethod;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedWs2812x, NeoEsp32RmtChannel3> NeoEsp32RmtHI3Ws2816Method;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedWs2805, NeoEsp32RmtChannel3> NeoEsp32RmtHI3Ws2805Method;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedSk6812, NeoEsp32RmtChannel3> NeoEsp32RmtHI3Sk6812Method;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedTm1814, NeoEsp32RmtChannel3> NeoEsp32RmtHI3Tm1814Method;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedTm1829, NeoEsp32RmtChannel3> NeoEsp32RmtHI3Tm1829Method;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedTm1914, NeoEsp32RmtChannel3> NeoEsp32RmtHI3Tm1914Method;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedApa106, NeoEsp32RmtChannel3> NeoEsp32RmtHI3Apa106Method;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedTx1812, NeoEsp32RmtChannel3> NeoEsp32RmtHI3Tx1812Method;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedGs1903, NeoEsp32RmtChannel3> NeoEsp32RmtHI3Gs1903Method;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeed800Kbps, NeoEsp32RmtChannel3> NeoEsp32RmtHI3800KbpsMethod;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeed400Kbps, NeoEsp32RmtChannel3> NeoEsp32RmtHI3400KbpsMethod;
typedef NeoEsp32RmtHI3Ws2805Method NeoEsp32RmtHI3Ws2814Method;
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32S3)
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedWs2811, NeoEsp32RmtChannel4> NeoEsp32RmtHI4Ws2811Method;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedWs2812x, NeoEsp32RmtChannel4> NeoEsp32RmtHI4Ws2812xMethod;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedWs2812x, NeoEsp32RmtChannel4> NeoEsp32RmtHI4Ws2816Method;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedWs2805, NeoEsp32RmtChannel4> NeoEsp32RmtHI4Ws2805Method;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedSk6812, NeoEsp32RmtChannel4> NeoEsp32RmtHI4Sk6812Method;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedTm1814, NeoEsp32RmtChannel4> NeoEsp32RmtHI4Tm1814Method;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedTm1829, NeoEsp32RmtChannel4> NeoEsp32RmtHI4Tm1829Method;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedTm1914, NeoEsp32RmtChannel4> NeoEsp32RmtHI4Tm1914Method;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedApa106, NeoEsp32RmtChannel4> NeoEsp32RmtHI4Apa106Method;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedTx1812, NeoEsp32RmtChannel4> NeoEsp32RmtHI4Tx1812Method;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedGs1903, NeoEsp32RmtChannel4> NeoEsp32RmtHI4Gs1903Method;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeed800Kbps, NeoEsp32RmtChannel4> NeoEsp32RmtHI4800KbpsMethod;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeed400Kbps, NeoEsp32RmtChannel4> NeoEsp32RmtHI4400KbpsMethod;
typedef NeoEsp32RmtHI4Ws2805Method NeoEsp32RmtHI4Ws2814Method;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedWs2811, NeoEsp32RmtChannel5> NeoEsp32RmtHI5Ws2811Method;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedWs2812x, NeoEsp32RmtChannel5> NeoEsp32RmtHI5Ws2812xMethod;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedWs2812x, NeoEsp32RmtChannel5> NeoEsp32RmtHI5Ws2816Method;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedWs2805, NeoEsp32RmtChannel5> NeoEsp32RmtHI5Ws2805Method;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedSk6812, NeoEsp32RmtChannel5> NeoEsp32RmtHI5Sk6812Method;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedTm1814, NeoEsp32RmtChannel5> NeoEsp32RmtHI5Tm1814Method;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedTm1829, NeoEsp32RmtChannel5> NeoEsp32RmtHI5Tm1829Method;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedTm1914, NeoEsp32RmtChannel5> NeoEsp32RmtHI5Tm1914Method;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedApa106, NeoEsp32RmtChannel5> NeoEsp32RmtHI5Apa106Method;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedTx1812, NeoEsp32RmtChannel5> NeoEsp32RmtHI5Tx1812Method;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedGs1903, NeoEsp32RmtChannel5> NeoEsp32RmtHI5Gs1903Method;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeed800Kbps, NeoEsp32RmtChannel5> NeoEsp32RmtHI5800KbpsMethod;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeed400Kbps, NeoEsp32RmtChannel5> NeoEsp32RmtHI5400KbpsMethod;
typedef NeoEsp32RmtHI5Ws2805Method NeoEsp32RmtHI5Ws2814Method;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedWs2811, NeoEsp32RmtChannel6> NeoEsp32RmtHI6Ws2811Method;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedWs2812x, NeoEsp32RmtChannel6> NeoEsp32RmtHI6Ws2812xMethod;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedWs2812x, NeoEsp32RmtChannel6> NeoEsp32RmtHI6Ws2816Method;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedWs2805, NeoEsp32RmtChannel6> NeoEsp32RmtHI6Ws2805Method;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedSk6812, NeoEsp32RmtChannel6> NeoEsp32RmtHI6Sk6812Method;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedTm1814, NeoEsp32RmtChannel6> NeoEsp32RmtHI6Tm1814Method;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedTm1829, NeoEsp32RmtChannel6> NeoEsp32RmtHI6Tm1829Method;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedTm1914, NeoEsp32RmtChannel6> NeoEsp32RmtHI6Tm1914Method;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedApa106, NeoEsp32RmtChannel6> NeoEsp32RmtHI6Apa106Method;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedTx1812, NeoEsp32RmtChannel6> NeoEsp32RmtHI6Tx1812Method;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedGs1903, NeoEsp32RmtChannel6> NeoEsp32RmtHI6Gs1903Method;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeed800Kbps, NeoEsp32RmtChannel6> NeoEsp32RmtHI6800KbpsMethod;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeed400Kbps, NeoEsp32RmtChannel6> NeoEsp32RmtHI6400KbpsMethod;
typedef NeoEsp32RmtHI6Ws2805Method NeoEsp32RmtHI6Ws2814Method;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedWs2811, NeoEsp32RmtChannel7> NeoEsp32RmtHI7Ws2811Method;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedWs2812x, NeoEsp32RmtChannel7> NeoEsp32RmtHI7Ws2812xMethod;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedWs2812x, NeoEsp32RmtChannel7> NeoEsp32RmtHI7Ws2816Method;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedWs2805, NeoEsp32RmtChannel7> NeoEsp32RmtHI7Ws2805Method;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedSk6812, NeoEsp32RmtChannel7> NeoEsp32RmtHI7Sk6812Method;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedTm1814, NeoEsp32RmtChannel7> NeoEsp32RmtHI7Tm1814Method;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedTm1829, NeoEsp32RmtChannel7> NeoEsp32RmtHI7Tm1829Method;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedTm1914, NeoEsp32RmtChannel7> NeoEsp32RmtHI7Tm1914Method;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedApa106, NeoEsp32RmtChannel7> NeoEsp32RmtHI7Apa106Method;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedTx1812, NeoEsp32RmtChannel7> NeoEsp32RmtHI7Tx1812Method;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeedGs1903, NeoEsp32RmtChannel7> NeoEsp32RmtHI7Gs1903Method;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeed800Kbps, NeoEsp32RmtChannel7> NeoEsp32RmtHI7800KbpsMethod;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtSpeed400Kbps, NeoEsp32RmtChannel7> NeoEsp32RmtHI7400KbpsMethod;
typedef NeoEsp32RmtHI7Ws2805Method NeoEsp32RmtHI7Ws2814Method;
#endif // !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32S3)
#endif // !defined(CONFIG_IDF_TARGET_ESP32C3)
// inverted
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedWs2811, NeoEsp32RmtChannelN> NeoEsp32RmtHINWs2811InvertedMethod;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedWs2812x, NeoEsp32RmtChannelN> NeoEsp32RmtHINWs2812xInvertedMethod;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedWs2812x, NeoEsp32RmtChannelN> NeoEsp32RmtHINWs2816InvertedMethod;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedWs2805, NeoEsp32RmtChannelN> NeoEsp32RmtHINWs2805InvertedMethod;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedSk6812, NeoEsp32RmtChannelN> NeoEsp32RmtHINSk6812InvertedMethod;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedTm1814, NeoEsp32RmtChannelN> NeoEsp32RmtHINTm1814InvertedMethod;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedTm1829, NeoEsp32RmtChannelN> NeoEsp32RmtHINTm1829InvertedMethod;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedTm1914, NeoEsp32RmtChannelN> NeoEsp32RmtHINTm1914InvertedMethod;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedApa106, NeoEsp32RmtChannelN> NeoEsp32RmtHINApa106InvertedMethod;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedTx1812, NeoEsp32RmtChannelN> NeoEsp32RmtHINTx1812InvertedMethod;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedGs1903, NeoEsp32RmtChannelN> NeoEsp32RmtHINGs1903InvertedMethod;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeed800Kbps, NeoEsp32RmtChannelN> NeoEsp32RmtHIN800KbpsInvertedMethod;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeed400Kbps, NeoEsp32RmtChannelN> NeoEsp32RmtHIN400KbpsInvertedMethod;
typedef NeoEsp32RmtHINWs2805InvertedMethod NeoEsp32RmtHINWs2814InvertedMethod;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedWs2811, NeoEsp32RmtChannel0> NeoEsp32RmtHI0Ws2811InvertedMethod;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedWs2812x, NeoEsp32RmtChannel0> NeoEsp32RmtHI0Ws2812xInvertedMethod;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedWs2812x, NeoEsp32RmtChannel0> NeoEsp32RmtHI0Ws2816InvertedMethod;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedWs2805, NeoEsp32RmtChannel0> NeoEsp32RmtHI0Ws2805InvertedMethod;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedSk6812, NeoEsp32RmtChannel0> NeoEsp32RmtHI0Sk6812InvertedMethod;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedTm1814, NeoEsp32RmtChannel0> NeoEsp32RmtHI0Tm1814InvertedMethod;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedTm1829, NeoEsp32RmtChannel0> NeoEsp32RmtHI0Tm1829InvertedMethod;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedTm1914, NeoEsp32RmtChannel0> NeoEsp32RmtHI0Tm1914InvertedMethod;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedApa106, NeoEsp32RmtChannel0> NeoEsp32RmtHI0Apa106InvertedMethod;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedTx1812, NeoEsp32RmtChannel0> NeoEsp32RmtHI0Tx1812InvertedMethod;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedGs1903, NeoEsp32RmtChannel0> NeoEsp32RmtHI0Gs1903InvertedMethod;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeed800Kbps, NeoEsp32RmtChannel0> NeoEsp32RmtHI0800KbpsInvertedMethod;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeed400Kbps, NeoEsp32RmtChannel0> NeoEsp32RmtHI0400KbpsInvertedMethod;
typedef NeoEsp32RmtHI0Ws2805InvertedMethod NeoEsp32RmtHI0Ws2814InvertedMethod;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedWs2811, NeoEsp32RmtChannel1> NeoEsp32RmtHI1Ws2811InvertedMethod;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedWs2812x, NeoEsp32RmtChannel1> NeoEsp32RmtHI1Ws2812xInvertedMethod;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedWs2812x, NeoEsp32RmtChannel1> NeoEsp32RmtHI1Ws2816InvertedMethod;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedWs2805, NeoEsp32RmtChannel1> NeoEsp32RmtHI1Ws2805InvertedMethod;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedSk6812, NeoEsp32RmtChannel1> NeoEsp32RmtHI1Sk6812InvertedMethod;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedTm1814, NeoEsp32RmtChannel1> NeoEsp32RmtHI1Tm1814InvertedMethod;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedTm1829, NeoEsp32RmtChannel1> NeoEsp32RmtHI1Tm1829InvertedMethod;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedTm1914, NeoEsp32RmtChannel1> NeoEsp32RmtHI1Tm1914InvertedMethod;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedApa106, NeoEsp32RmtChannel1> NeoEsp32RmtHI1Apa106InvertedMethod;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedTx1812, NeoEsp32RmtChannel1> NeoEsp32RmtHI1Tx1812InvertedMethod;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedGs1903, NeoEsp32RmtChannel1> NeoEsp32RmtHI1Gs1903InvertedMethod;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeed800Kbps, NeoEsp32RmtChannel1> NeoEsp32RmtHI1800KbpsInvertedMethod;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeed400Kbps, NeoEsp32RmtChannel1> NeoEsp32RmtHI1400KbpsInvertedMethod;
typedef NeoEsp32RmtHI1Ws2805InvertedMethod NeoEsp32RmtHI1Ws2814InvertedMethod;
#if !defined(CONFIG_IDF_TARGET_ESP32C3)
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedWs2811, NeoEsp32RmtChannel2> NeoEsp32RmtHI2Ws2811InvertedMethod;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedWs2812x, NeoEsp32RmtChannel2> NeoEsp32RmtHI2Ws2812xInvertedMethod;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedWs2812x, NeoEsp32RmtChannel2> NeoEsp32RmtHI2Ws2816InvertedMethod;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedWs2805, NeoEsp32RmtChannel2> NeoEsp32RmtHI2Ws2805InvertedMethod;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedSk6812, NeoEsp32RmtChannel2> NeoEsp32RmtHI2Sk6812InvertedMethod;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedTm1814, NeoEsp32RmtChannel2> NeoEsp32RmtHI2Tm1814InvertedMethod;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedTm1829, NeoEsp32RmtChannel2> NeoEsp32RmtHI2Tm1829InvertedMethod;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedTm1914, NeoEsp32RmtChannel2> NeoEsp32RmtHI2Tm1914InvertedMethod;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedApa106, NeoEsp32RmtChannel2> NeoEsp32RmtHI2Apa106InvertedMethod;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedTx1812, NeoEsp32RmtChannel2> NeoEsp32RmtHI2Tx1812InvertedMethod;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedGs1903, NeoEsp32RmtChannel2> NeoEsp32RmtHI2Gs1903InvertedMethod;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeed800Kbps, NeoEsp32RmtChannel2> NeoEsp32RmtHI2800KbpsInvertedMethod;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeed400Kbps, NeoEsp32RmtChannel2> NeoEsp32RmtHI2400KbpsInvertedMethod;
typedef NeoEsp32RmtHI2Ws2805InvertedMethod NeoEsp32RmtHI2Ws2814InvertedMethod;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedWs2811, NeoEsp32RmtChannel3> NeoEsp32RmtHI3Ws2811InvertedMethod;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedWs2812x, NeoEsp32RmtChannel3> NeoEsp32RmtHI3Ws2812xInvertedMethod;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedWs2805, NeoEsp32RmtChannel3> NeoEsp32RmtHI3Ws2805InvertedMethod;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedWs2812x, NeoEsp32RmtChannel3> NeoEsp32RmtHI3Ws2816InvertedMethod;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedSk6812, NeoEsp32RmtChannel3> NeoEsp32RmtHI3Sk6812InvertedMethod;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedTm1814, NeoEsp32RmtChannel3> NeoEsp32RmtHI3Tm1814InvertedMethod;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedTm1829, NeoEsp32RmtChannel3> NeoEsp32RmtHI3Tm1829InvertedMethod;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedTm1914, NeoEsp32RmtChannel3> NeoEsp32RmtHI3Tm1914InvertedMethod;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedApa106, NeoEsp32RmtChannel3> NeoEsp32RmtHI3Apa106InvertedMethod;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedTx1812, NeoEsp32RmtChannel3> NeoEsp32RmtHI3Tx1812InvertedMethod;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedGs1903, NeoEsp32RmtChannel3> NeoEsp32RmtHI3Gs1903InvertedMethod;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeed800Kbps, NeoEsp32RmtChannel3> NeoEsp32RmtHI3800KbpsInvertedMethod;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeed400Kbps, NeoEsp32RmtChannel3> NeoEsp32RmtHI3400KbpsInvertedMethod;
typedef NeoEsp32RmtHI3Ws2805InvertedMethod NeoEsp32RmtHI3Ws2814InvertedMethod;
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32S3)
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedWs2811, NeoEsp32RmtChannel4> NeoEsp32RmtHI4Ws2811InvertedMethod;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedWs2812x, NeoEsp32RmtChannel4> NeoEsp32RmtHI4Ws2812xInvertedMethod;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedWs2812x, NeoEsp32RmtChannel4> NeoEsp32RmtHI4Ws2816InvertedMethod;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedWs2805, NeoEsp32RmtChannel4> NeoEsp32RmtHI4Ws2805InvertedMethod;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedSk6812, NeoEsp32RmtChannel4> NeoEsp32RmtHI4Sk6812InvertedMethod;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedTm1814, NeoEsp32RmtChannel4> NeoEsp32RmtHI4Tm1814InvertedMethod;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedTm1829, NeoEsp32RmtChannel4> NeoEsp32RmtHI4Tm1829InvertedMethod;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedTm1914, NeoEsp32RmtChannel4> NeoEsp32RmtHI4Tm1914InvertedMethod;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedApa106, NeoEsp32RmtChannel4> NeoEsp32RmtHI4Apa106InvertedMethod;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedTx1812, NeoEsp32RmtChannel4> NeoEsp32RmtHI4Tx1812InvertedMethod;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedGs1903, NeoEsp32RmtChannel4> NeoEsp32RmtHI4Gs1903InvertedMethod;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeed800Kbps, NeoEsp32RmtChannel4> NeoEsp32RmtHI4800KbpsInvertedMethod;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeed400Kbps, NeoEsp32RmtChannel4> NeoEsp32RmtHI4400KbpsInvertedMethod;
typedef NeoEsp32RmtHI4Ws2805InvertedMethod NeoEsp32RmtHI4Ws2814InvertedMethod;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedWs2811, NeoEsp32RmtChannel5> NeoEsp32RmtHI5Ws2811InvertedMethod;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedWs2812x, NeoEsp32RmtChannel5> NeoEsp32RmtHI5Ws2812xInvertedMethod;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedWs2812x, NeoEsp32RmtChannel5> NeoEsp32RmtHI5Ws2816InvertedMethod;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedWs2805, NeoEsp32RmtChannel5> NeoEsp32RmtHI5Ws2805InvertedMethod;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedSk6812, NeoEsp32RmtChannel5> NeoEsp32RmtHI5Sk6812InvertedMethod;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedTm1814, NeoEsp32RmtChannel5> NeoEsp32RmtHI5Tm1814InvertedMethod;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedTm1829, NeoEsp32RmtChannel5> NeoEsp32RmtHI5Tm1829InvertedMethod;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedTm1914, NeoEsp32RmtChannel5> NeoEsp32RmtHI5Tm1914InvertedMethod;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedApa106, NeoEsp32RmtChannel5> NeoEsp32RmtHI5Apa106InvertedMethod;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedTx1812, NeoEsp32RmtChannel5> NeoEsp32RmtHI5Tx1812InvertedMethod;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedGs1903, NeoEsp32RmtChannel5> NeoEsp32RmtHI5Gs1903InvertedMethod;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeed800Kbps, NeoEsp32RmtChannel5> NeoEsp32RmtHI5800KbpsInvertedMethod;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeed400Kbps, NeoEsp32RmtChannel5> NeoEsp32RmtHI5400KbpsInvertedMethod;
typedef NeoEsp32RmtHI5Ws2805InvertedMethod NeoEsp32RmtHI5Ws2814InvertedMethod;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedWs2811, NeoEsp32RmtChannel6> NeoEsp32RmtHI6Ws2811InvertedMethod;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedWs2812x, NeoEsp32RmtChannel6> NeoEsp32RmtHI6Ws2812xInvertedMethod;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedWs2812x, NeoEsp32RmtChannel6> NeoEsp32RmtHI6Ws2816InvertedMethod;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedWs2805, NeoEsp32RmtChannel6> NeoEsp32RmtHI6Ws2805InvertedMethod;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedSk6812, NeoEsp32RmtChannel6> NeoEsp32RmtHI6Sk6812InvertedMethod;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedTm1814, NeoEsp32RmtChannel6> NeoEsp32RmtHI6Tm1814InvertedMethod;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedTm1829, NeoEsp32RmtChannel6> NeoEsp32RmtHI6Tm1829InvertedMethod;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedTm1914, NeoEsp32RmtChannel6> NeoEsp32RmtHI6Tm1914InvertedMethod;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedApa106, NeoEsp32RmtChannel6> NeoEsp32RmtHI6Apa106InvertedMethod;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedTx1812, NeoEsp32RmtChannel6> NeoEsp32RmtHI6Tx1812InvertedMethod;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedGs1903, NeoEsp32RmtChannel6> NeoEsp32RmtHI6Gs1903InvertedMethod;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeed800Kbps, NeoEsp32RmtChannel6> NeoEsp32RmtHI6800KbpsInvertedMethod;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeed400Kbps, NeoEsp32RmtChannel6> NeoEsp32RmtHI6400KbpsInvertedMethod;
typedef NeoEsp32RmtHI6Ws2805InvertedMethod NeoEsp32RmtHI6Ws2814InvertedMethod;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedWs2811, NeoEsp32RmtChannel7> NeoEsp32RmtHI7Ws2811InvertedMethod;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedWs2812x, NeoEsp32RmtChannel7> NeoEsp32RmtHI7Ws2812xInvertedMethod;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedWs2812x, NeoEsp32RmtChannel7> NeoEsp32RmtHI7Ws2816InvertedMethod;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedWs2805, NeoEsp32RmtChannel7> NeoEsp32RmtHI7Ws2805InvertedMethod;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedSk6812, NeoEsp32RmtChannel7> NeoEsp32RmtHI7Sk6812InvertedMethod;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedTm1814, NeoEsp32RmtChannel7> NeoEsp32RmtHI7Tm1814InvertedMethod;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedTm1829, NeoEsp32RmtChannel7> NeoEsp32RmtHI7Tm1829InvertedMethod;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedTm1914, NeoEsp32RmtChannel7> NeoEsp32RmtHI7Tm1914InvertedMethod;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedApa106, NeoEsp32RmtChannel7> NeoEsp32RmtHI7Apa106InvertedMethod;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedTx1812, NeoEsp32RmtChannel7> NeoEsp32RmtHI7Tx1812InvertedMethod;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeedGs1903, NeoEsp32RmtChannel7> NeoEsp32RmtHI7Gs1903InvertedMethod;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeed800Kbps, NeoEsp32RmtChannel7> NeoEsp32RmtHI7800KbpsInvertedMethod;
typedef NeoEsp32RmtHIMethodBase<NeoEsp32RmtInvertedSpeed400Kbps, NeoEsp32RmtChannel7> NeoEsp32RmtHI7400KbpsInvertedMethod;
typedef NeoEsp32RmtHI7Ws2805InvertedMethod NeoEsp32RmtHI7Ws2814InvertedMethod;
#endif // !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32S3)
#endif // !defined(CONFIG_IDF_TARGET_ESP32C3)
#endif
-12
View File
@@ -1,12 +0,0 @@
{
"name": "NeoESP32RmtHI",
"build": { "libArchive": false },
"platforms": ["espressif32"],
"dependencies": [
{
"owner": "makuna",
"name": "NeoPixelBus",
"version": "^2.8.3"
}
]
}
-263
View File
@@ -1,263 +0,0 @@
/* RMT ISR shim
* Bridges from a high-level interrupt to the C++ code.
*
* This code is largely derived from Espressif's 'hli_vector.S' Bluetooth ISR.
*
*/
#if defined(__XTENSA__) && defined(ESP32) && !defined(CONFIG_BTDM_CTRL_HLI)
#include <freertos/xtensa_context.h>
#include "sdkconfig.h"
#include "soc/soc.h"
/* If the Bluetooth driver has hooked the high-priority interrupt, we piggyback on it and don't need this. */
#ifndef CONFIG_BTDM_CTRL_HLI
/*
Select interrupt based on system check level
- Base ESP32: could be 4 or 5, depends on platform config
- S2: 5
- S3: 5
*/
#if CONFIG_ESP_SYSTEM_CHECK_INT_LEVEL_5
/* Use level 4 */
#define RFI_X 4
#define xt_highintx xt_highint4
#else /* !CONFIG_ESP_SYSTEM_CHECK_INT_LEVEL_5 */
/* Use level 5 */
#define RFI_X 5
#define xt_highintx xt_highint5
#endif /* CONFIG_ESP_SYSTEM_CHECK_INT_LEVEL_5 */
// Register map, based on interrupt level
#define EPC_X (EPC + RFI_X)
#define EXCSAVE_X (EXCSAVE + RFI_X)
// The sp mnemonic is used all over in ESP's assembly, though I'm not sure where it's expected to be defined?
#define sp a1
/* Interrupt stack size, for C code. */
#define RMT_INTR_STACK_SIZE 512
/* Save area for the CPU state:
* - 64 words for the general purpose registers
* - 7 words for some of the special registers:
* - WINDOWBASE, WINDOWSTART only WINDOWSTART is truly needed
* - SAR, LBEG, LEND, LCOUNT since the C code might use these
* - EPC1 since the C code might cause window overflow exceptions
* This is not laid out as standard exception frame structure
* for simplicity of the save/restore code.
*/
#define REG_FILE_SIZE (64 * 4)
#define SPECREG_OFFSET REG_FILE_SIZE
#define SPECREG_SIZE (7 * 4)
#define REG_SAVE_AREA_SIZE (SPECREG_OFFSET + SPECREG_SIZE)
.data
_rmt_intr_stack:
.space RMT_INTR_STACK_SIZE
_rmt_save_ctx:
.space REG_SAVE_AREA_SIZE
.section .iram1,"ax"
.global xt_highintx
.type xt_highintx,@function
.align 4
xt_highintx:
movi a0, _rmt_save_ctx
/* save 4 lower registers */
s32i a1, a0, 4
s32i a2, a0, 8
s32i a3, a0, 12
rsr a2, EXCSAVE_X /* holds the value of a0 */
s32i a2, a0, 0
/* Save special registers */
addi a0, a0, SPECREG_OFFSET
rsr a2, WINDOWBASE
s32i a2, a0, 0
rsr a2, WINDOWSTART
s32i a2, a0, 4
rsr a2, SAR
s32i a2, a0, 8
#if XCHAL_HAVE_LOOPS
rsr a2, LBEG
s32i a2, a0, 12
rsr a2, LEND
s32i a2, a0, 16
rsr a2, LCOUNT
s32i a2, a0, 20
#endif
rsr a2, EPC1
s32i a2, a0, 24
/* disable exception mode, window overflow */
movi a0, PS_INTLEVEL(RFI_X+1) | PS_EXCM
wsr a0, PS
rsync
/* Save the remaining physical registers.
* 4 registers are already saved, which leaves 60 registers to save.
* (FIXME: consider the case when the CPU is configured with physical 32 registers)
* These 60 registers are saved in 5 iterations, 12 registers at a time.
*/
movi a1, 5
movi a3, _rmt_save_ctx + 4 * 4
/* This is repeated 5 times, each time the window is shifted by 12 registers.
* We come here with a1 = downcounter, a3 = save pointer, a2 and a0 unused.
*/
1:
s32i a4, a3, 0
s32i a5, a3, 4
s32i a6, a3, 8
s32i a7, a3, 12
s32i a8, a3, 16
s32i a9, a3, 20
s32i a10, a3, 24
s32i a11, a3, 28
s32i a12, a3, 32
s32i a13, a3, 36
s32i a14, a3, 40
s32i a15, a3, 44
/* We are about to rotate the window, so that a12-a15 will become the new a0-a3.
* Copy a0-a3 to a12-15 to still have access to these values.
* At the same time we can decrement the counter and adjust the save area pointer
*/
/* a0 is constant (_rmt_save_ctx), no need to copy */
addi a13, a1, -1 /* copy and decrement the downcounter */
/* a2 is scratch so no need to copy */
addi a15, a3, 48 /* copy and adjust the save area pointer */
beqz a13, 2f /* have saved all registers ? */
rotw 3 /* rotate the window and go back */
j 1b
/* the loop is complete */
2:
rotw 4 /* this brings us back to the original window */
/* a0 still points to _rmt_save_ctx */
/* Can clear WINDOWSTART now, all registers are saved */
rsr a2, WINDOWBASE
/* WINDOWSTART = (1 << WINDOWBASE) */
movi a3, 1
ssl a2
sll a3, a3
wsr a3, WINDOWSTART
_highint_stack_switch:
movi a0, 0
movi sp, _rmt_intr_stack + RMT_INTR_STACK_SIZE - 16
s32e a0, sp, -12 /* For GDB: set null SP */
s32e a0, sp, -16 /* For GDB: set null PC */
movi a0, _highint_stack_switch /* For GDB: cosmetics, for the frame where stack switch happened */
/* Set up PS for C, disable all interrupts except NMI and debug, and clear EXCM. */
movi a6, PS_INTLEVEL(RFI_X) | PS_UM | PS_WOE
wsr a6, PS
rsync
/* Call C handler */
mov a6, sp
call4 NeoEsp32RmtMethodIsr
l32e sp, sp, -12 /* switch back to the original stack */
/* Done with C handler; re-enable exception mode, disabling window overflow */
movi a2, PS_INTLEVEL(RFI_X+1) | PS_EXCM /* TOCHECK */
wsr a2, PS
rsync
/* Restore the special registers.
* WINDOWSTART will be restored near the end.
*/
movi a0, _rmt_save_ctx + SPECREG_OFFSET
l32i a2, a0, 8
wsr a2, SAR
#if XCHAL_HAVE_LOOPS
l32i a2, a0, 12
wsr a2, LBEG
l32i a2, a0, 16
wsr a2, LEND
l32i a2, a0, 20
wsr a2, LCOUNT
#endif
l32i a2, a0, 24
wsr a2, EPC1
/* Restoring the physical registers.
* This is the reverse to the saving process above.
*/
/* Rotate back to the final window, then start loading 12 registers at a time,
* in 5 iterations.
* Again, a1 is the downcounter and a3 is the save area pointer.
* After each rotation, a1 and a3 are copied from a13 and a15.
* To simplify the loop, we put the initial values into a13 and a15.
*/
rotw -4
movi a15, _rmt_save_ctx + 64 * 4 /* point to the end of the save area */
movi a13, 5
1:
/* Copy a1 and a3 from their previous location,
* at the same time decrementing and adjusting the save area pointer.
*/
addi a1, a13, -1
addi a3, a15, -48
/* Load 12 registers */
l32i a4, a3, 0
l32i a5, a3, 4
l32i a6, a3, 8
l32i a7, a3, 12
l32i a8, a3, 16
l32i a9, a3, 20
l32i a10, a3, 24
l32i a11, a3, 28 /* ensure PS and EPC written */
l32i a12, a3, 32
l32i a13, a3, 36
l32i a14, a3, 40
l32i a15, a3, 44
/* Done with the loop? */
beqz a1, 2f
/* If no, rotate the window and repeat */
rotw -3
j 1b
2:
/* Done with the loop. Only 4 registers (a0-a3 in the original window) remain
* to be restored. Also need to restore WINDOWSTART, since all the general
* registers are now in place.
*/
movi a0, _rmt_save_ctx
l32i a2, a0, SPECREG_OFFSET + 4
wsr a2, WINDOWSTART
l32i a1, a0, 4
l32i a2, a0, 8
l32i a3, a0, 12
rsr a0, EXCSAVE_X /* holds the value of a0 before the interrupt handler */
/* Return from the interrupt, restoring PS from EPS_X */
rfi RFI_X
/* The linker has no reason to link in this file; all symbols it exports are already defined
(weakly!) in the default int handler. Define a symbol here so we can use it to have the
linker inspect this anyway. */
.global ld_include_hli_vectors_rmt
ld_include_hli_vectors_rmt:
#endif // CONFIG_BTDM_CTRL_HLI
#endif // XTensa
@@ -1,507 +0,0 @@
/*-------------------------------------------------------------------------
NeoPixel library helper functions for Esp32.
A BIG thanks to Andreas Merkle for the investigation and implementation of
a workaround to the GCC bug that drops method attributes from template methods
Written by Michael C. Miller.
I invest time and resources providing this open source code,
please support me by donating (see https://github.com/Makuna/NeoPixelBus)
-------------------------------------------------------------------------
This file is part of the Makuna/NeoPixelBus library.
NeoPixelBus is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
NeoPixelBus is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with NeoPixel. If not, see
<http://www.gnu.org/licenses/>.
-------------------------------------------------------------------------*/
#include <Arduino.h>
#if defined(ARDUINO_ARCH_ESP32)
#include <algorithm>
#include "esp_idf_version.h"
#include "NeoEsp32RmtHIMethod.h"
#include "soc/soc.h"
#include "soc/rmt_reg.h"
#ifdef __riscv
#include "riscv/interrupt.h"
#endif
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 0, 0)
#include "hal/rmt_ll.h"
#else
/* Shims for older ESP-IDF v3; we can safely assume original ESP32 */
#include "soc/rmt_struct.h"
// Selected RMT API functions borrowed from ESP-IDF v4.4.8
// components/hal/esp32/include/hal/rmt_ll.h
// Copyright 2019 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
__attribute__((always_inline))
static inline void rmt_ll_tx_reset_pointer(rmt_dev_t *dev, uint32_t channel)
{
dev->conf_ch[channel].conf1.mem_rd_rst = 1;
dev->conf_ch[channel].conf1.mem_rd_rst = 0;
}
__attribute__((always_inline))
static inline void rmt_ll_tx_start(rmt_dev_t *dev, uint32_t channel)
{
dev->conf_ch[channel].conf1.tx_start = 1;
}
__attribute__((always_inline))
static inline void rmt_ll_tx_stop(rmt_dev_t *dev, uint32_t channel)
{
RMTMEM.chan[channel].data32[0].val = 0;
dev->conf_ch[channel].conf1.tx_start = 0;
dev->conf_ch[channel].conf1.mem_rd_rst = 1;
dev->conf_ch[channel].conf1.mem_rd_rst = 0;
}
__attribute__((always_inline))
static inline void rmt_ll_tx_enable_pingpong(rmt_dev_t *dev, uint32_t channel, bool enable)
{
dev->apb_conf.mem_tx_wrap_en = enable;
}
__attribute__((always_inline))
static inline void rmt_ll_tx_enable_loop(rmt_dev_t *dev, uint32_t channel, bool enable)
{
dev->conf_ch[channel].conf1.tx_conti_mode = enable;
}
__attribute__((always_inline))
static inline uint32_t rmt_ll_tx_get_channel_status(rmt_dev_t *dev, uint32_t channel)
{
return dev->status_ch[channel];
}
__attribute__((always_inline))
static inline void rmt_ll_tx_set_limit(rmt_dev_t *dev, uint32_t channel, uint32_t limit)
{
dev->tx_lim_ch[channel].limit = limit;
}
__attribute__((always_inline))
static inline void rmt_ll_enable_interrupt(rmt_dev_t *dev, uint32_t mask, bool enable)
{
if (enable) {
dev->int_ena.val |= mask;
} else {
dev->int_ena.val &= ~mask;
}
}
__attribute__((always_inline))
static inline void rmt_ll_enable_tx_end_interrupt(rmt_dev_t *dev, uint32_t channel, bool enable)
{
dev->int_ena.val &= ~(1 << (channel * 3));
dev->int_ena.val |= (enable << (channel * 3));
}
__attribute__((always_inline))
static inline void rmt_ll_enable_tx_err_interrupt(rmt_dev_t *dev, uint32_t channel, bool enable)
{
dev->int_ena.val &= ~(1 << (channel * 3 + 2));
dev->int_ena.val |= (enable << (channel * 3 + 2));
}
__attribute__((always_inline))
static inline void rmt_ll_enable_tx_thres_interrupt(rmt_dev_t *dev, uint32_t channel, bool enable)
{
dev->int_ena.val &= ~(1 << (channel + 24));
dev->int_ena.val |= (enable << (channel + 24));
}
__attribute__((always_inline))
static inline void rmt_ll_clear_tx_end_interrupt(rmt_dev_t *dev, uint32_t channel)
{
dev->int_clr.val = (1 << (channel * 3));
}
__attribute__((always_inline))
static inline void rmt_ll_clear_tx_err_interrupt(rmt_dev_t *dev, uint32_t channel)
{
dev->int_clr.val = (1 << (channel * 3 + 2));
}
__attribute__((always_inline))
static inline void rmt_ll_clear_tx_thres_interrupt(rmt_dev_t *dev, uint32_t channel)
{
dev->int_clr.val = (1 << (channel + 24));
}
__attribute__((always_inline))
static inline uint32_t rmt_ll_get_tx_thres_interrupt_status(rmt_dev_t *dev)
{
uint32_t status = dev->int_st.val;
return (status & 0xFF000000) >> 24;
}
#endif
// *********************************
// Select method for binding interrupt
//
// - If the Bluetooth driver has registered a high-level interrupt, piggyback on that API
// - If we're on a modern core, allocate the interrupt with the API (old cores are bugged)
// - Otherwise use the low-level hardware API to manually bind the interrupt
#if defined(CONFIG_BTDM_CTRL_HLI)
// Espressif's bluetooth driver offers a helpful sharing layer; bring in the interrupt management calls
#include "hal/interrupt_controller_hal.h"
extern "C" esp_err_t hli_intr_register(intr_handler_t handler, void* arg, uint32_t intr_reg, uint32_t intr_mask);
#else /* !CONFIG_BTDM_CTRL_HLI*/
// Declare the our high-priority ISR handler
extern "C" void ld_include_hli_vectors_rmt(); // an object with an address, but no space
#if defined(CONFIG_IDF_TARGET_ESP32S2) || defined(CONFIG_IDF_TARGET_ESP32S3) || defined(CONFIG_IDF_TARGET_ESP32C3)
#include "soc/periph_defs.h"
#endif
// Select level flag
#if defined(__riscv)
// RISCV chips don't block interrupts while scheduling; all we need to do is be higher than the WiFi ISR
#define INT_LEVEL_FLAG ESP_INTR_FLAG_LEVEL3
#elif defined(CONFIG_ESP_SYSTEM_CHECK_INT_LEVEL_5)
#define INT_LEVEL_FLAG ESP_INTR_FLAG_LEVEL4
#else
#define INT_LEVEL_FLAG ESP_INTR_FLAG_LEVEL5
#endif
// ESP-IDF v3 cannot enable high priority interrupts through the API at all;
// and ESP-IDF v4 on XTensa cannot enable Level 5 due to incorrect interrupt descriptor tables
#if !defined(__XTENSA__) || (ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)) || ((ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 0, 0) && CONFIG_ESP_SYSTEM_CHECK_INT_LEVEL_5))
#define NEOESP32_RMT_CAN_USE_INTR_ALLOC
// XTensa cores require the assembly bridge
#ifdef __XTENSA__
#define HI_IRQ_HANDLER nullptr
#define HI_IRQ_HANDLER_ARG ld_include_hli_vectors_rmt
#else
#define HI_IRQ_HANDLER NeoEsp32RmtMethodIsr
#define HI_IRQ_HANDLER_ARG nullptr
#endif
#else
/* !CONFIG_BTDM_CTRL_HLI && !NEOESP32_RMT_CAN_USE_INTR_ALLOC */
// This is the index of the LV5 interrupt vector - see interrupt descriptor table in idf components/hal/esp32/interrupt_descriptor_table.c
#define ESP32_LV5_IRQ_INDEX 26
#endif /* NEOESP32_RMT_CAN_USE_INTR_ALLOC */
#endif /* CONFIG_BTDM_CTRL_HLI */
// RMT driver implementation
struct NeoEsp32RmtHIChannelState {
uint32_t rmtBit0, rmtBit1;
uint32_t resetDuration;
const byte* txDataStart; // data array
const byte* txDataEnd; // one past end
const byte* txDataCurrent; // current location
size_t rmtOffset;
};
// Global variables
#if defined(NEOESP32_RMT_CAN_USE_INTR_ALLOC)
static intr_handle_t isrHandle = nullptr;
#endif
static NeoEsp32RmtHIChannelState** driverState = nullptr;
constexpr size_t rmtBatchSize = RMT_MEM_ITEM_NUM / 2;
// Fill the RMT buffer memory
// This is implemented using many arguments instead of passing the structure object to ensure we do only one lookup
// All the arguments are passed in registers, so they don't need to be looked up again
static void IRAM_ATTR RmtFillBuffer(uint8_t channel, const byte** src_ptr, const byte* end, uint32_t bit0, uint32_t bit1, size_t* offset_ptr, size_t reserve) {
// We assume that (rmtToWrite % 8) == 0
size_t rmtToWrite = rmtBatchSize - reserve;
rmt_item32_t* dest =(rmt_item32_t*) &RMTMEM.chan[channel].data32[*offset_ptr + reserve]; // write directly in to RMT memory
const byte* psrc = *src_ptr;
*offset_ptr ^= rmtBatchSize;
if (psrc != end) {
while (rmtToWrite > 0) {
uint8_t data = *psrc;
for (uint8_t bit = 0; bit < 8; bit++)
{
dest->val = (data & 0x80) ? bit1 : bit0;
dest++;
data <<= 1;
}
rmtToWrite -= 8;
psrc++;
if (psrc == end) {
break;
}
}
*src_ptr = psrc;
}
if (rmtToWrite > 0) {
// Add end event
rmt_item32_t bit0_val = {{.val = bit0 }};
*dest = rmt_item32_t {{{ .duration0 = 0, .level0 = bit0_val.level1, .duration1 = 0, .level1 = bit0_val.level1 }}};
}
}
static void IRAM_ATTR RmtStartWrite(uint8_t channel, NeoEsp32RmtHIChannelState& state) {
// Reset context state
state.rmtOffset = 0;
// Fill the first part of the buffer with a reset event
// FUTURE: we could do timing analysis with the last interrupt on this channel
// Use 8 words to stay aligned with the buffer fill logic
rmt_item32_t bit0_val = {{.val = state.rmtBit0 }};
rmt_item32_t fill = {{{ .duration0 = 100, .level0 = bit0_val.level1, .duration1 = 100, .level1 = bit0_val.level1 }}};
rmt_item32_t* dest = (rmt_item32_t*) &RMTMEM.chan[channel].data32[0];
for (auto i = 0; i < 7; ++i) dest[i] = fill;
fill.duration1 = state.resetDuration > 1400 ? (state.resetDuration - 1400) : 100;
dest[7] = fill;
// Fill the remaining buffer with real data
RmtFillBuffer(channel, &state.txDataCurrent, state.txDataEnd, state.rmtBit0, state.rmtBit1, &state.rmtOffset, 8);
RmtFillBuffer(channel, &state.txDataCurrent, state.txDataEnd, state.rmtBit0, state.rmtBit1, &state.rmtOffset, 0);
// Start operation
rmt_ll_clear_tx_thres_interrupt(&RMT, channel);
rmt_ll_tx_reset_pointer(&RMT, channel);
rmt_ll_tx_start(&RMT, channel);
}
extern "C" void IRAM_ATTR NeoEsp32RmtMethodIsr(void *arg) {
// Tx threshold interrupt
uint32_t status = rmt_ll_get_tx_thres_interrupt_status(&RMT);
while (status) {
uint8_t channel = __builtin_ffs(status) - 1;
if (driverState[channel]) {
// Normal case
NeoEsp32RmtHIChannelState& state = *driverState[channel];
RmtFillBuffer(channel, &state.txDataCurrent, state.txDataEnd, state.rmtBit0, state.rmtBit1, &state.rmtOffset, 0);
} else {
// Danger - another driver got invoked?
rmt_ll_tx_stop(&RMT, channel);
}
rmt_ll_clear_tx_thres_interrupt(&RMT, channel);
status = rmt_ll_get_tx_thres_interrupt_status(&RMT);
}
};
// Wrapper around the register analysis defines
// For all currently supported chips, this is constant for all channels; but this is not true of *all* ESP32
static inline bool _RmtStatusIsTransmitting(rmt_channel_t channel, uint32_t status) {
uint32_t v;
switch(channel) {
#ifdef RMT_STATE_CH0
case 0: v = (status >> RMT_STATE_CH0_S) & RMT_STATE_CH0_V; break;
#endif
#ifdef RMT_STATE_CH1
case 1: v = (status >> RMT_STATE_CH1_S) & RMT_STATE_CH1_V; break;
#endif
#ifdef RMT_STATE_CH2
case 2: v = (status >> RMT_STATE_CH2_S) & RMT_STATE_CH2_V; break;
#endif
#ifdef RMT_STATE_CH3
case 3: v = (status >> RMT_STATE_CH3_S) & RMT_STATE_CH3_V; break;
#endif
#ifdef RMT_STATE_CH4
case 4: v = (status >> RMT_STATE_CH4_S) & RMT_STATE_CH4_V; break;
#endif
#ifdef RMT_STATE_CH5
case 5: v = (status >> RMT_STATE_CH5_S) & RMT_STATE_CH5_V; break;
#endif
#ifdef RMT_STATE_CH6
case 6: v = (status >> RMT_STATE_CH6_S) & RMT_STATE_CH6_V; break;
#endif
#ifdef RMT_STATE_CH7
case 7: v = (status >> RMT_STATE_CH7_S) & RMT_STATE_CH7_V; break;
#endif
default: v = 0;
}
return v != 0;
}
esp_err_t NeoEsp32RmtHiMethodDriver::Install(rmt_channel_t channel, uint32_t rmtBit0, uint32_t rmtBit1, uint32_t reset) {
// Validate channel number
if (channel >= RMT_CHANNEL_MAX) {
return ESP_ERR_INVALID_ARG;
}
esp_err_t err = ESP_OK;
if (!driverState) {
// First time init
driverState = reinterpret_cast<NeoEsp32RmtHIChannelState**>(heap_caps_calloc(RMT_CHANNEL_MAX, sizeof(NeoEsp32RmtHIChannelState*), MALLOC_CAP_INTERNAL));
if (!driverState) return ESP_ERR_NO_MEM;
// Ensure all interrupts are cleared before binding
RMT.int_ena.val = 0;
RMT.int_clr.val = 0xFFFFFFFF;
// Bind interrupt handler
#if defined(CONFIG_BTDM_CTRL_HLI)
// Bluetooth driver has taken the empty high-priority interrupt. Fortunately, it allows us to
// hook up another handler.
err = hli_intr_register(NeoEsp32RmtMethodIsr, nullptr, (uintptr_t) &RMT.int_st, 0xFF000000);
// 25 is the magic number of the bluetooth ISR on ESP32 - see soc/soc.h.
intr_matrix_set(cpu_hal_get_core_id(), ETS_RMT_INTR_SOURCE, 25);
intr_cntrl_ll_enable_interrupts(1<<25);
#elif defined(NEOESP32_RMT_CAN_USE_INTR_ALLOC)
// Use the platform code to allocate the interrupt
// If we need the additional assembly bridge, we pass it as the "arg" to the IDF so it gets linked in
err = esp_intr_alloc(ETS_RMT_INTR_SOURCE, INT_LEVEL_FLAG | ESP_INTR_FLAG_IRAM, HI_IRQ_HANDLER, (void*) HI_IRQ_HANDLER_ARG, &isrHandle);
//err = ESP_ERR_NOT_FINISHED;
#else
// Broken IDF API does not allow us to reserve the interrupt; do it manually
static volatile const void* __attribute__((used)) pleaseLinkAssembly = (void*) ld_include_hli_vectors_rmt;
intr_matrix_set(xPortGetCoreID(), ETS_RMT_INTR_SOURCE, ESP32_LV5_IRQ_INDEX);
ESP_INTR_ENABLE(ESP32_LV5_IRQ_INDEX);
#endif
if (err != ESP_OK) {
heap_caps_free(driverState);
driverState = nullptr;
return err;
}
}
if (driverState[channel] != nullptr) {
return ESP_ERR_INVALID_STATE; // already in use
}
NeoEsp32RmtHIChannelState* state = reinterpret_cast<NeoEsp32RmtHIChannelState*>(heap_caps_calloc(1, sizeof(NeoEsp32RmtHIChannelState), MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT));
if (state == nullptr) {
return ESP_ERR_NO_MEM;
}
// Store timing information
state->rmtBit0 = rmtBit0;
state->rmtBit1 = rmtBit1;
state->resetDuration = reset;
// Initialize hardware
rmt_ll_tx_stop(&RMT, channel);
rmt_ll_tx_reset_pointer(&RMT, channel);
rmt_ll_enable_tx_err_interrupt(&RMT, channel, false);
rmt_ll_enable_tx_end_interrupt(&RMT, channel, false);
rmt_ll_enable_tx_thres_interrupt(&RMT, channel, false);
rmt_ll_clear_tx_err_interrupt(&RMT, channel);
rmt_ll_clear_tx_end_interrupt(&RMT, channel);
rmt_ll_clear_tx_thres_interrupt(&RMT, channel);
rmt_ll_tx_enable_loop(&RMT, channel, false);
rmt_ll_tx_enable_pingpong(&RMT, channel, true);
rmt_ll_tx_set_limit(&RMT, channel, rmtBatchSize);
driverState[channel] = state;
rmt_ll_enable_tx_thres_interrupt(&RMT, channel, true);
return err;
}
esp_err_t NeoEsp32RmtHiMethodDriver::Uninstall(rmt_channel_t channel) {
if ((channel >= RMT_CHANNEL_MAX) || !driverState || !driverState[channel]) return ESP_ERR_INVALID_ARG;
NeoEsp32RmtHIChannelState* state = driverState[channel];
WaitForTxDone(channel, 10000 / portTICK_PERIOD_MS);
// Done or not, we're out of here
rmt_ll_tx_stop(&RMT, channel);
rmt_ll_enable_tx_thres_interrupt(&RMT, channel, false);
driverState[channel] = nullptr;
heap_caps_free(state);
#if !defined(CONFIG_BTDM_CTRL_HLI) /* Cannot unbind from bluetooth ISR */
// Turn off the driver ISR and release global state if none are left
for (uint8_t channelIndex = 0; channelIndex < RMT_CHANNEL_MAX; ++channelIndex) {
if (driverState[channelIndex]) return ESP_OK; // done
}
#if defined(NEOESP32_RMT_CAN_USE_INTR_ALLOC)
esp_intr_free(isrHandle);
#else
ESP_INTR_DISABLE(ESP32_LV5_IRQ_INDEX);
#endif
heap_caps_free(driverState);
driverState = nullptr;
#endif /* !defined(CONFIG_BTDM_CTRL_HLI) */
return ESP_OK;
}
esp_err_t NeoEsp32RmtHiMethodDriver::Write(rmt_channel_t channel, const uint8_t *src, size_t src_size) {
if ((channel >= RMT_CHANNEL_MAX) || !driverState || !driverState[channel]) return ESP_ERR_INVALID_ARG;
NeoEsp32RmtHIChannelState& state = *driverState[channel];
esp_err_t result = WaitForTxDone(channel, 10000 / portTICK_PERIOD_MS);
if (result == ESP_OK) {
state.txDataStart = src;
state.txDataCurrent = src;
state.txDataEnd = src + src_size;
RmtStartWrite(channel, state);
}
return result;
}
esp_err_t NeoEsp32RmtHiMethodDriver::WaitForTxDone(rmt_channel_t channel, TickType_t wait_time) {
if ((channel >= RMT_CHANNEL_MAX) || !driverState || !driverState[channel]) return ESP_ERR_INVALID_ARG;
NeoEsp32RmtHIChannelState& state = *driverState[channel];
// yield-wait until wait_time
esp_err_t rv = ESP_OK;
uint32_t status;
while(1) {
status = rmt_ll_tx_get_channel_status(&RMT, channel);
if (!_RmtStatusIsTransmitting(channel, status)) break;
if (wait_time == 0) { rv = ESP_ERR_TIMEOUT; break; };
TickType_t sleep = std::min(wait_time, (TickType_t) 5);
vTaskDelay(sleep);
wait_time -= sleep;
};
return rv;
}
#endif
+3 -6
View File
@@ -1,21 +1,18 @@
{
"name": "wled",
"version": "0.15.3",
"version": "0.15.0-b7",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "wled",
"version": "0.15.3",
"version": "0.15.0-b7",
"license": "ISC",
"dependencies": {
"clean-css": "^5.3.3",
"html-minifier-terser": "^7.2.0",
"inliner": "^1.13.1",
"nodemon": "^3.1.7"
},
"engines": {
"node": ">=20.0.0"
"nodemon": "^3.0.2"
}
},
"node_modules/@jridgewell/gen-mapping": {
+1 -4
View File
@@ -1,6 +1,6 @@
{
"name": "wled",
"version": "0.15.3",
"version": "0.15.0-b7",
"description": "Tools for WLED project",
"main": "tools/cdata.js",
"directories": {
@@ -27,8 +27,5 @@
"html-minifier-terser": "^7.2.0",
"inliner": "^1.13.1",
"nodemon": "^3.1.7"
},
"engines": {
"node": ">=20.0.0"
}
}
+2 -20
View File
@@ -1,21 +1,3 @@
Import("env")
import shutil
Import('env')
node_ex = shutil.which("node")
# Check if Node.js is installed and present in PATH if it failed, abort the build
if node_ex is None:
print('\x1b[0;31;43m' + 'Node.js is not installed or missing from PATH html css js will not be processed check https://kno.wled.ge/advanced/compiling-wled/' + '\x1b[0m')
exitCode = env.Execute("null")
exit(exitCode)
else:
# Install the necessary node packages for the pre-build asset bundling script
print('\x1b[6;33;42m' + 'Installing node packages' + '\x1b[0m')
env.Execute("npm install")
# Call the bundling script
exitCode = env.Execute("npm run build")
# If it failed, abort the build
if (exitCode):
print('\x1b[0;31;43m' + 'npm run build fails check https://kno.wled.ge/advanced/compiling-wled/' + '\x1b[0m')
exit(exitCode)
env.Execute("npm run build")
+3 -6
View File
@@ -2,7 +2,6 @@ Import('env')
import os
import shutil
import gzip
import json
OUTPUT_DIR = "build_output{}".format(os.path.sep)
#OUTPUT_DIR = os.path.join("build_output")
@@ -20,11 +19,9 @@ def _create_dirs(dirs=["map", "release", "firmware"]):
os.makedirs(os.path.join(OUTPUT_DIR, d), exist_ok=True)
def create_release(source):
release_name_def = _get_cpp_define_value(env, "WLED_RELEASE_NAME")
if release_name_def:
release_name = release_name_def.replace("\\\"", "")
with open("package.json", "r") as package:
version = json.load(package)["version"]
release_name = _get_cpp_define_value(env, "WLED_RELEASE_NAME")
if release_name:
version = _get_cpp_define_value(env, "WLED_VERSION")
release_file = os.path.join(OUTPUT_DIR, "release", f"WLED_{version}_{release_name}.bin")
release_gz_file = release_file + ".gz"
print(f"Copying {source} to {release_file}")
-116
View File
@@ -1,116 +0,0 @@
Import('env')
import subprocess
import json
import re
def get_github_repo():
"""Extract GitHub repository name from git remote URL.
Uses the remote that the current branch tracks, falling back to 'origin'.
This handles cases where repositories have multiple remotes or where the
main remote is not named 'origin'.
Returns:
str: Repository name in 'owner/repo' format for GitHub repos,
'unknown' for non-GitHub repos, missing git CLI, or any errors.
"""
try:
remote_name = 'origin' # Default fallback
# Try to get the remote for the current branch
try:
# Get current branch name
branch_result = subprocess.run(['git', 'rev-parse', '--abbrev-ref', 'HEAD'],
capture_output=True, text=True, check=True)
current_branch = branch_result.stdout.strip()
# Get the remote for the current branch
remote_result = subprocess.run(['git', 'config', f'branch.{current_branch}.remote'],
capture_output=True, text=True, check=True)
tracked_remote = remote_result.stdout.strip()
# Use the tracked remote if we found one
if tracked_remote:
remote_name = tracked_remote
except subprocess.CalledProcessError:
# If branch config lookup fails, continue with 'origin' as fallback
pass
# Get the remote URL for the determined remote
result = subprocess.run(['git', 'remote', 'get-url', remote_name],
capture_output=True, text=True, check=True)
remote_url = result.stdout.strip()
# Check if it's a GitHub URL
if 'github.com' not in remote_url.lower():
return None
# Parse GitHub URL patterns:
# https://github.com/owner/repo.git
# git@github.com:owner/repo.git
# https://github.com/owner/repo
# Remove .git suffix if present
if remote_url.endswith('.git'):
remote_url = remote_url[:-4]
# Handle HTTPS URLs
https_match = re.search(r'github\.com/([^/]+/[^/]+)', remote_url, re.IGNORECASE)
if https_match:
return https_match.group(1)
# Handle SSH URLs
ssh_match = re.search(r'github\.com:([^/]+/[^/]+)', remote_url, re.IGNORECASE)
if ssh_match:
return ssh_match.group(1)
return None
except FileNotFoundError:
# Git CLI is not installed or not in PATH
return None
except subprocess.CalledProcessError:
# Git command failed (e.g., not a git repo, no remote, etc.)
return None
except Exception:
# Any other unexpected error
return None
# WLED version is managed by package.json; this is picked up in several places
# - It's integrated in to the UI code
# - Here, for wled_metadata.cpp
# - The output_bins script
# We always take it from package.json to ensure consistency
with open("package.json", "r") as package:
WLED_VERSION = json.load(package)["version"]
def has_def(cppdefs, name):
""" Returns true if a given name is set in a CPPDEFINES collection """
for f in cppdefs:
if isinstance(f, tuple):
f = f[0]
if f == name:
return True
return False
def add_wled_metadata_flags(env, node):
cdefs = env["CPPDEFINES"].copy()
if not has_def(cdefs, "WLED_REPO"):
repo = get_github_repo()
if repo:
cdefs.append(("WLED_REPO", f"\\\"{repo}\\\""))
cdefs.append(("WLED_VERSION", WLED_VERSION))
# This transforms the node in to a Builder; it cannot be modified again
return env.Object(
node,
CPPDEFINES=cdefs
)
env.AddBuildMiddleware(
add_wled_metadata_flags,
"*/wled_metadata.cpp"
)
+8
View File
@@ -0,0 +1,8 @@
Import('env')
import json
PACKAGE_FILE = "package.json"
with open(PACKAGE_FILE, "r") as package:
version = json.load(package)["version"]
env.Append(BUILD_FLAGS=[f"-DWLED_VERSION={version}"])
+31 -61
View File
@@ -10,7 +10,7 @@
# ------------------------------------------------------------------------------
# CI/release binaries
default_envs = nodemcuv2, esp8266_2m, esp01_1m_full, nodemcuv2_160, esp8266_2m_160, esp01_1m_full_160, nodemcuv2_compat, esp8266_2m_compat, esp01_1m_full_compat, esp32dev, esp32dev_V4, esp32_eth, lolin_s2_mini, esp32c3dev, esp32s3dev_16MB_opi, esp32s3dev_8MB_opi, esp32s3_4M_qspi, esp32_wrover
default_envs = nodemcuv2, esp8266_2m, esp01_1m_full, nodemcuv2_160, esp8266_2m_160, esp01_1m_full_160, nodemcuv2_compat, esp8266_2m_compat, esp01_1m_full_compat, esp32dev, esp32_eth, lolin_s2_mini, esp32c3dev, esp32s3dev_16MB_opi, esp32s3dev_8MB_opi, esp32s3_4M_qspi, esp32_wrover
src_dir = ./wled00
data_dir = ./wled00/data
@@ -110,7 +110,7 @@ ldscript_4m1m = eagle.flash.4m1m.ld
[scripts_defaults]
extra_scripts =
pre:pio-scripts/set_metadata.py
pre:pio-scripts/set_version.py
post:pio-scripts/output_bins.py
post:pio-scripts/strip-floats.py
pre:pio-scripts/user_config_copy.py
@@ -138,8 +138,9 @@ lib_compat_mode = strict
lib_deps =
fastled/FastLED @ 3.6.0
IRremoteESP8266 @ 2.8.2
https://github.com/Makuna/NeoPixelBus.git#a0919d1c10696614625978dd6fb750a1317a14ce
https://github.com/Aircoookie/ESPAsyncWebServer.git#v2.4.2
makuna/NeoPixelBus @ 2.8.0
#https://github.com/makuna/NeoPixelBus.git#CoreShaderBeta
https://github.com/Aircoookie/ESPAsyncWebServer.git#v2.2.1
# for I2C interface
;Wire
# ESP-NOW library
@@ -175,7 +176,6 @@ lib_deps =
extra_scripts = ${scripts_defaults.extra_scripts}
[esp8266]
build_unflags = ${common.build_unflags}
build_flags =
-DESP8266
-DFP_IN_IROM
@@ -242,12 +242,10 @@ lib_deps_compat =
#platform = https://github.com/tasmota/platform-espressif32/releases/download/v2.0.2.3/platform-espressif32-2.0.2.3.zip
platform = espressif32@3.5.0
platform_packages = framework-arduinoespressif32 @ https://github.com/Aircoookie/arduino-esp32.git#1.0.6.4
build_unflags = ${common.build_unflags}
build_flags = -g
-DARDUINO_ARCH_ESP32
#-DCONFIG_LITTLEFS_FOR_IDF_3_2
-D CONFIG_ASYNC_TCP_USE_WDT=0
-D CONFIG_ASYNC_TCP_STACK_SIZE=8192
#use LITTLEFS library by lorol in ESP32 core 1.x.x instead of built-in in 2.x.x
-D LOROL_LITTLEFS
; -DARDUINO_USB_CDC_ON_BOOT=0 ;; this flag is mandatory for "classic ESP32" when building with arduino-esp32 >=2.0.3
@@ -259,13 +257,12 @@ large_partitions = tools/WLED_ESP32_8MB.csv
extreme_partitions = tools/WLED_ESP32_16MB_9MB_FS.csv
lib_deps =
https://github.com/lorol/LITTLEFS.git
esp32async/AsyncTCP @ 3.4.7
https://github.com/pbolduc/AsyncTCP.git @ 1.2.0
${env.lib_deps}
# additional build flags for audioreactive
AR_build_flags = -D USERMOD_AUDIOREACTIVE
-D sqrt_internal=sqrtf ;; -fsingle-precision-constant ;; forces ArduinoFFT to use float math (2x faster)
AR_lib_deps = kosme/arduinoFFT @ 2.0.1
board_build.partitions = ${esp32.default_partitions} ;; default partioning for 4MB Flash - can be overridden in build envs
[esp32_idf_V4]
;; experimental build environment for ESP32 using ESP-IDF 4.4.x / arduino-esp32 v2.0.5
@@ -275,81 +272,67 @@ board_build.partitions = ${esp32.default_partitions} ;; default partioning for
;; You need to completely erase your device (esptool erase_flash) first, then install the "V4" build from VSCode+platformio.
platform = espressif32@ ~6.3.2
platform_packages = platformio/framework-arduinoespressif32 @ 3.20009.0 ;; select arduino-esp32 v2.0.9 (arduino-esp32 2.0.10 thru 2.0.14 are buggy so avoid them)
build_unflags = ${common.build_unflags}
build_flags = -g
-Wshadow=compatible-local ;; emit warning in case a local variable "shadows" another local one
-DARDUINO_ARCH_ESP32 -DESP32
-D CONFIG_ASYNC_TCP_USE_WDT=0
-D CONFIG_ASYNC_TCP_STACK_SIZE=8192
-DARDUINO_USB_CDC_ON_BOOT=0 ;; this flag is mandatory for "classic ESP32" when building with arduino-esp32 >=2.0.3
-D WLED_ENABLE_DMX_INPUT
lib_deps =
esp32async/AsyncTCP @ 3.4.7
https://github.com/someweisguy/esp_dmx.git#47db25d
https://github.com/pbolduc/AsyncTCP.git @ 1.2.0
${env.lib_deps}
board_build.partitions = ${esp32.default_partitions} ;; default partioning for 4MB Flash - can be overridden in build envs
[esp32s2]
;; generic definitions for all ESP32-S2 boards
platform = espressif32@ ~6.3.2
platform_packages = platformio/framework-arduinoespressif32 @ 3.20009.0 ;; select arduino-esp32 v2.0.9 (arduino-esp32 2.0.10 thru 2.0.14 are buggy so avoid them)
build_unflags = ${common.build_unflags}
build_flags = -g
-DARDUINO_ARCH_ESP32
-DARDUINO_ARCH_ESP32S2
-DCONFIG_IDF_TARGET_ESP32S2=1
-D CONFIG_ASYNC_TCP_USE_WDT=0
-D CONFIG_ASYNC_TCP_STACK_SIZE=8192
-DARDUINO_USB_MSC_ON_BOOT=0 -DARDUINO_USB_DFU_ON_BOOT=0
-DCO
-DARDUINO_USB_MODE=0 ;; this flag is mandatory for ESP32-S2 !
;; please make sure that the following flags are properly set (to 0 or 1) by your board.json, or included in your custom platformio_override.ini entry:
;; ARDUINO_USB_CDC_ON_BOOT
lib_deps =
esp32async/AsyncTCP @ 3.4.7
https://github.com/pbolduc/AsyncTCP.git @ 1.2.0
${env.lib_deps}
board_build.partitions = ${esp32.default_partitions} ;; default partioning for 4MB Flash - can be overridden in build envs
[esp32c3]
;; generic definitions for all ESP32-C3 boards
platform = espressif32@ ~6.3.2
platform_packages = platformio/framework-arduinoespressif32 @ 3.20009.0 ;; select arduino-esp32 v2.0.9 (arduino-esp32 2.0.10 thru 2.0.14 are buggy so avoid them)
build_unflags = ${common.build_unflags}
build_flags = -g
-DARDUINO_ARCH_ESP32
-DARDUINO_ARCH_ESP32C3
-DCONFIG_IDF_TARGET_ESP32C3=1
-D CONFIG_ASYNC_TCP_USE_WDT=0
-D CONFIG_ASYNC_TCP_STACK_SIZE=8192
-DCO
-DARDUINO_USB_MODE=1 ;; this flag is mandatory for ESP32-C3
;; please make sure that the following flags are properly set (to 0 or 1) by your board.json, or included in your custom platformio_override.ini entry:
;; ARDUINO_USB_CDC_ON_BOOT
lib_deps =
esp32async/AsyncTCP @ 3.4.7
https://github.com/pbolduc/AsyncTCP.git @ 1.2.0
${env.lib_deps}
board_build.partitions = ${esp32.default_partitions} ;; default partioning for 4MB Flash - can be overridden in build envs
[esp32s3]
;; generic definitions for all ESP32-S3 boards
platform = espressif32@ ~6.3.2
platform_packages = platformio/framework-arduinoespressif32 @ 3.20009.0 ;; select arduino-esp32 v2.0.9 (arduino-esp32 2.0.10 thru 2.0.14 are buggy so avoid them)
build_unflags = ${common.build_unflags}
build_flags = -g
-DESP32
-DARDUINO_ARCH_ESP32
-DARDUINO_ARCH_ESP32S3
-DCONFIG_IDF_TARGET_ESP32S3=1
-D CONFIG_ASYNC_TCP_USE_WDT=0
-D CONFIG_ASYNC_TCP_STACK_SIZE=8192
-DARDUINO_USB_MSC_ON_BOOT=0 -DARDUINO_DFU_ON_BOOT=0
-DCO
;; please make sure that the following flags are properly set (to 0 or 1) by your board.json, or included in your custom platformio_override.ini entry:
;; ARDUINO_USB_MODE, ARDUINO_USB_CDC_ON_BOOT
lib_deps =
esp32async/AsyncTCP @ 3.4.7
https://github.com/pbolduc/AsyncTCP.git @ 1.2.0
${env.lib_deps}
board_build.partitions = ${esp32.large_partitions} ;; default partioning for 8MB flash - can be overridden in build envs
# ------------------------------------------------------------------------------
@@ -362,7 +345,7 @@ platform = ${common.platform_wled_default}
platform_packages = ${common.platform_packages}
board_build.ldscript = ${common.ldscript_4m1m}
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags} ${esp8266.build_flags} -D WLED_RELEASE_NAME=\"ESP8266\" #-DWLED_DISABLE_2D
build_flags = ${common.build_flags} ${esp8266.build_flags} -D WLED_RELEASE_NAME=ESP8266 #-DWLED_DISABLE_2D
lib_deps = ${esp8266.lib_deps}
monitor_filters = esp8266_exception_decoder
@@ -371,13 +354,13 @@ extends = env:nodemcuv2
;; using platform version and build options from WLED 0.14.0
platform = ${esp8266.platform_compat}
platform_packages = ${esp8266.platform_packages_compat}
build_flags = ${common.build_flags} ${esp8266.build_flags_compat} -D WLED_RELEASE_NAME=\"ESP8266_compat\" #-DWLED_DISABLE_2D
build_flags = ${common.build_flags} ${esp8266.build_flags_compat} -D WLED_RELEASE_NAME=ESP8266_compat #-DWLED_DISABLE_2D
;; lib_deps = ${esp8266.lib_deps_compat} ;; experimental - use older NeoPixelBus 2.7.9
[env:nodemcuv2_160]
extends = env:nodemcuv2
board_build.f_cpu = 160000000L
build_flags = ${common.build_flags} ${esp8266.build_flags} -D WLED_RELEASE_NAME=\"ESP8266_160\" #-DWLED_DISABLE_2D
build_flags = ${common.build_flags} ${esp8266.build_flags} -D WLED_RELEASE_NAME=ESP8266_160 #-DWLED_DISABLE_2D
-D USERMOD_AUDIOREACTIVE
[env:esp8266_2m]
@@ -386,7 +369,7 @@ platform = ${common.platform_wled_default}
platform_packages = ${common.platform_packages}
board_build.ldscript = ${common.ldscript_2m512k}
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags} ${esp8266.build_flags} -D WLED_RELEASE_NAME=\"ESP02\"
build_flags = ${common.build_flags} ${esp8266.build_flags} -D WLED_RELEASE_NAME=ESP02
lib_deps = ${esp8266.lib_deps}
[env:esp8266_2m_compat]
@@ -394,12 +377,12 @@ extends = env:esp8266_2m
;; using platform version and build options from WLED 0.14.0
platform = ${esp8266.platform_compat}
platform_packages = ${esp8266.platform_packages_compat}
build_flags = ${common.build_flags} ${esp8266.build_flags_compat} -D WLED_RELEASE_NAME=\"ESP02_compat\" #-DWLED_DISABLE_2D
build_flags = ${common.build_flags} ${esp8266.build_flags_compat} -D WLED_RELEASE_NAME=ESP02_compat #-DWLED_DISABLE_2D
[env:esp8266_2m_160]
extends = env:esp8266_2m
board_build.f_cpu = 160000000L
build_flags = ${common.build_flags} ${esp8266.build_flags} -D WLED_RELEASE_NAME=\"ESP02_160\"
build_flags = ${common.build_flags} ${esp8266.build_flags} -D WLED_RELEASE_NAME=ESP02_160
-D USERMOD_AUDIOREACTIVE
[env:esp01_1m_full]
@@ -408,7 +391,7 @@ platform = ${common.platform_wled_default}
platform_packages = ${common.platform_packages}
board_build.ldscript = ${common.ldscript_1m128k}
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags} ${esp8266.build_flags} -D WLED_RELEASE_NAME=\"ESP01\" -D WLED_DISABLE_OTA
build_flags = ${common.build_flags} ${esp8266.build_flags} -D WLED_RELEASE_NAME=ESP01 -D WLED_DISABLE_OTA
; -D WLED_USE_REAL_MATH ;; may fix wrong sunset/sunrise times, at the cost of 7064 bytes FLASH and 975 bytes RAM
lib_deps = ${esp8266.lib_deps}
@@ -417,12 +400,12 @@ extends = env:esp01_1m_full
;; using platform version and build options from WLED 0.14.0
platform = ${esp8266.platform_compat}
platform_packages = ${esp8266.platform_packages_compat}
build_flags = ${common.build_flags} ${esp8266.build_flags_compat} -D WLED_RELEASE_NAME=\"ESP01_compat\" -D WLED_DISABLE_OTA #-DWLED_DISABLE_2D
build_flags = ${common.build_flags} ${esp8266.build_flags_compat} -D WLED_RELEASE_NAME=ESP01_compat -D WLED_DISABLE_OTA #-DWLED_DISABLE_2D
[env:esp01_1m_full_160]
extends = env:esp01_1m_full
board_build.f_cpu = 160000000L
build_flags = ${common.build_flags} ${esp8266.build_flags} -D WLED_RELEASE_NAME=\"ESP01_160\" -D WLED_DISABLE_OTA
build_flags = ${common.build_flags} ${esp8266.build_flags} -D WLED_RELEASE_NAME=ESP01_160 -D WLED_DISABLE_OTA
-D USERMOD_AUDIOREACTIVE
; -D WLED_USE_REAL_MATH ;; may fix wrong sunset/sunrise times, at the cost of 7064 bytes FLASH and 975 bytes RAM
@@ -431,31 +414,19 @@ board = esp32dev
platform = ${esp32.platform}
platform_packages = ${esp32.platform_packages}
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags} ${esp32.build_flags} -D WLED_RELEASE_NAME=\"ESP32\" #-D WLED_DISABLE_BROWNOUT_DET
build_flags = ${common.build_flags} ${esp32.build_flags} -D WLED_RELEASE_NAME=ESP32 #-D WLED_DISABLE_BROWNOUT_DET
${esp32.AR_build_flags}
lib_deps = ${esp32.lib_deps}
${esp32.AR_lib_deps}
monitor_filters = esp32_exception_decoder
board_build.partitions = ${esp32.default_partitions}
[env:esp32dev_V4]
board = esp32dev
platform = ${esp32_idf_V4.platform}
platform_packages = ${esp32_idf_V4.platform_packages}
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags} ${esp32_idf_V4.build_flags} -D WLED_RELEASE_NAME=\"ESP32_V4\" #-D WLED_DISABLE_BROWNOUT_DET
${esp32.AR_build_flags}
lib_deps = ${esp32_idf_V4.lib_deps}
${esp32.AR_lib_deps}
monitor_filters = esp32_exception_decoder
board_build.partitions = ${esp32.default_partitions}
[env:esp32dev_8M]
board = esp32dev
platform = ${esp32_idf_V4.platform}
platform_packages = ${esp32_idf_V4.platform_packages}
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags} ${esp32_idf_V4.build_flags} -D WLED_RELEASE_NAME=\"ESP32_8M\" #-D WLED_DISABLE_BROWNOUT_DET
build_flags = ${common.build_flags} ${esp32_idf_V4.build_flags} -D WLED_RELEASE_NAME=ESP32_8M #-D WLED_DISABLE_BROWNOUT_DET
${esp32.AR_build_flags}
lib_deps = ${esp32_idf_V4.lib_deps}
${esp32.AR_lib_deps}
@@ -471,7 +442,7 @@ board = esp32dev
platform = ${esp32_idf_V4.platform}
platform_packages = ${esp32_idf_V4.platform_packages}
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags} ${esp32_idf_V4.build_flags} -D WLED_RELEASE_NAME=\"ESP32_16M\" #-D WLED_DISABLE_BROWNOUT_DET
build_flags = ${common.build_flags} ${esp32_idf_V4.build_flags} -D WLED_RELEASE_NAME=ESP32_16M #-D WLED_DISABLE_BROWNOUT_DET
${esp32.AR_build_flags}
lib_deps = ${esp32_idf_V4.lib_deps}
${esp32.AR_lib_deps}
@@ -487,7 +458,7 @@ board_build.flash_mode = dio
;platform = ${esp32.platform}
;platform_packages = ${esp32.platform_packages}
;build_unflags = ${common.build_unflags}
;build_flags = ${common.build_flags} ${esp32.build_flags} -D WLED_RELEASE_NAME=\"ESP32_audioreactive\" #-D WLED_DISABLE_BROWNOUT_DET
;build_flags = ${common.build_flags} ${esp32.build_flags} -D WLED_RELEASE_NAME=ESP32_audioreactive #-D WLED_DISABLE_BROWNOUT_DET
; ${esp32.AR_build_flags}
;lib_deps = ${esp32.lib_deps}
; ${esp32.AR_lib_deps}
@@ -502,7 +473,7 @@ platform = ${esp32.platform}
platform_packages = ${esp32.platform_packages}
upload_speed = 921600
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags} ${esp32.build_flags} -D WLED_RELEASE_NAME=\"ESP32_Ethernet\" -D RLYPIN=-1 -D WLED_USE_ETHERNET -D BTNPIN=-1
build_flags = ${common.build_flags} ${esp32.build_flags} -D WLED_RELEASE_NAME=ESP32_Ethernet -D RLYPIN=-1 -D WLED_USE_ETHERNET -D BTNPIN=-1
; -D WLED_DISABLE_ESPNOW ;; ESP-NOW requires wifi, may crash with ethernet only
${esp32.AR_build_flags}
lib_deps = ${esp32.lib_deps}
@@ -518,7 +489,7 @@ board_build.f_flash = 80000000L
board_build.flash_mode = qio
board_build.partitions = ${esp32.extended_partitions}
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags} ${esp32_idf_V4.build_flags} -D WLED_RELEASE_NAME=\"ESP32_WROVER\"
build_flags = ${common.build_flags} ${esp32_idf_V4.build_flags} -D WLED_RELEASE_NAME=ESP32_WROVER
-DBOARD_HAS_PSRAM -mfix-esp32-psram-cache-issue ;; Older ESP32 (rev.<3) need a PSRAM fix (increases static RAM used) https://docs.espressif.com/projects/esp-idf/en/stable/esp32/api-guides/external-ram.html
-D DATA_PINS=25
${esp32.AR_build_flags}
@@ -532,7 +503,7 @@ platform_packages = ${esp32c3.platform_packages}
framework = arduino
board = esp32-c3-devkitm-1
board_build.partitions = ${esp32.default_partitions}
build_flags = ${common.build_flags} ${esp32c3.build_flags} -D WLED_RELEASE_NAME=\"ESP32-C3\"
build_flags = ${common.build_flags} ${esp32c3.build_flags} -D WLED_RELEASE_NAME=ESP32-C3
-D WLED_WATCHDOG_TIMEOUT=0
-DLOLIN_WIFI_FIX ; seems to work much better with this
-DARDUINO_USB_CDC_ON_BOOT=1 ;; for virtual CDC USB
@@ -549,7 +520,7 @@ platform = ${esp32s3.platform}
platform_packages = ${esp32s3.platform_packages}
upload_speed = 921600
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags} ${esp32s3.build_flags} -D WLED_RELEASE_NAME=\"ESP32-S3_16MB_opi\"
build_flags = ${common.build_flags} ${esp32s3.build_flags} -D WLED_RELEASE_NAME=ESP32-S3_16MB_opi
-D CONFIG_LITTLEFS_FOR_IDF_3_2 -D WLED_WATCHDOG_TIMEOUT=0
;-D ARDUINO_USB_CDC_ON_BOOT=0 ;; -D ARDUINO_USB_MODE=1 ;; for boards with serial-to-USB chip
-D ARDUINO_USB_CDC_ON_BOOT=1 -D ARDUINO_USB_MODE=1 ;; for boards with USB-OTG connector only (USBCDC or "TinyUSB")
@@ -572,7 +543,7 @@ platform = ${esp32s3.platform}
platform_packages = ${esp32s3.platform_packages}
upload_speed = 921600
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags} ${esp32s3.build_flags} -D WLED_RELEASE_NAME=\"ESP32-S3_8MB_opi\"
build_flags = ${common.build_flags} ${esp32s3.build_flags} -D WLED_RELEASE_NAME=ESP32-S3_8MB_opi
-D CONFIG_LITTLEFS_FOR_IDF_3_2 -D WLED_WATCHDOG_TIMEOUT=0
;-D ARDUINO_USB_CDC_ON_BOOT=0 ;; -D ARDUINO_USB_MODE=1 ;; for boards with serial-to-USB chip
-D ARDUINO_USB_CDC_ON_BOOT=1 -D ARDUINO_USB_MODE=1 ;; for boards with USB-OTG connector only (USBCDC or "TinyUSB")
@@ -594,7 +565,7 @@ board = esp32s3camlcd ;; this is the only standard board with "opi_opi"
board_build.arduino.memory_type = opi_opi
upload_speed = 921600
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags} ${esp32s3.build_flags} -D WLED_RELEASE_NAME=\"ESP32-S3_WROOM-2\"
build_flags = ${common.build_flags} ${esp32s3.build_flags} -D WLED_RELEASE_NAME=ESP32-S3_WROOM-2
-D CONFIG_LITTLEFS_FOR_IDF_3_2 -D WLED_WATCHDOG_TIMEOUT=0
-D ARDUINO_USB_CDC_ON_BOOT=0 ;; -D ARDUINO_USB_MODE=1 ;; for boards with serial-to-USB chip
;; -D ARDUINO_USB_CDC_ON_BOOT=1 -D ARDUINO_USB_MODE=1 ;; for boards with USB-OTG connector only (USBCDC or "TinyUSB")
@@ -619,7 +590,7 @@ platform = ${esp32s3.platform}
platform_packages = ${esp32s3.platform_packages}
upload_speed = 921600
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags} ${esp32s3.build_flags} -D WLED_RELEASE_NAME=\"ESP32-S3_4M_qspi\"
build_flags = ${common.build_flags} ${esp32s3.build_flags} -D WLED_RELEASE_NAME=ESP32-S3_4M_qspi
-DARDUINO_USB_CDC_ON_BOOT=1 -DARDUINO_USB_MODE=1 ;; for boards with USB-OTG connector only (USBCDC or "TinyUSB")
-DBOARD_HAS_PSRAM
-DLOLIN_WIFI_FIX ; seems to work much better with this
@@ -640,7 +611,7 @@ board_build.partitions = ${esp32.default_partitions}
board_build.flash_mode = qio
board_build.f_flash = 80000000L
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags} ${esp32s2.build_flags} -D WLED_RELEASE_NAME=\"ESP32-S2\"
build_flags = ${common.build_flags} ${esp32s2.build_flags} -D WLED_RELEASE_NAME=ESP32-S2
-DARDUINO_USB_CDC_ON_BOOT=1
-DARDUINO_USB_MSC_ON_BOOT=0
-DARDUINO_USB_DFU_ON_BOOT=0
@@ -648,7 +619,6 @@ build_flags = ${common.build_flags} ${esp32s2.build_flags} -D WLED_RELEASE_NAME=
-DLOLIN_WIFI_FIX ; seems to work much better with this
-D WLED_WATCHDOG_TIMEOUT=0
-D CONFIG_ASYNC_TCP_USE_WDT=0
-D CONFIG_ASYNC_TCP_STACK_SIZE=8192
-D DATA_PINS=16
-D HW_PIN_SCL=35
-D HW_PIN_SDA=33
+45 -55
View File
@@ -5,7 +5,7 @@
# Please visit documentation: https://docs.platformio.org/page/projectconf.html
[platformio]
default_envs = WLED_generic8266_1M, esp32dev_V4_dio80 # put the name(s) of your own build environment here. You can define as many as you need
default_envs = WLED_tasmota_1M # define as many as you need
#----------
# SAMPLE
@@ -28,8 +28,8 @@ lib_deps = ${esp8266.lib_deps}
; robtillaart/SHT85@~0.3.3
; ;gmag11/QuickESPNow @ ~0.7.0 # will also load QuickDebug
; https://github.com/blazoncek/QuickESPNow.git#optional-debug ;; exludes debug library
; ${esp32.AR_lib_deps} ;; used for USERMOD_AUDIOREACTIVE
; bitbank2/PNGdec@^1.0.1 ;; used for POV display uncomment following
; ${esp32.AR_lib_deps} ;; needed for USERMOD_AUDIOREACTIVE
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags} ${esp8266.build_flags}
@@ -37,7 +37,7 @@ build_flags = ${common.build_flags} ${esp8266.build_flags}
; *** To use the below defines/overrides, copy and paste each onto it's own line just below build_flags in the section above.
;
; Set a release name that may be used to distinguish required binary for flashing
; -D WLED_RELEASE_NAME=\"ESP32_MULTI_USREMODS\"
; -D WLED_RELEASE_NAME=ESP32_MULTI_USREMODS
;
; disable specific features
; -D WLED_DISABLE_OTA
@@ -111,6 +111,7 @@ build_flags = ${common.build_flags} ${esp8266.build_flags}
;
; Use 4 Line Display usermod with SPI display
; -D USERMOD_FOUR_LINE_DISPLAY
; -D USE_ALT_DISPlAY # mandatory
; -DFLD_SPI_DEFAULT
; -D FLD_TYPE=SSD1306_SPI64
; -D FLD_PIN_CLOCKSPI=14
@@ -141,8 +142,7 @@ build_flags = ${common.build_flags} ${esp8266.build_flags}
; -D PIR_SENSOR_MAX_SENSORS=2 # max allowable sensors (uses OR logic for triggering)
;
; Use Audioreactive usermod and configure I2S microphone
; ${esp32.AR_build_flags} ;; default flags required to properly configure ArduinoFFT
; ;; don't forget to add ArduinoFFT to your libs_deps: ${esp32.AR_lib_deps}
; -D USERMOD_AUDIOREACTIVE
; -D AUDIOPIN=-1
; -D DMTYPE=1 # 0-analog/disabled, 1-I2S generic, 2-ES7243, 3-SPH0645, 4-I2S+mclk, 5-I2S PDM
; -D I2S_SDPIN=36
@@ -158,22 +158,17 @@ build_flags = ${common.build_flags} ${esp8266.build_flags}
; -D USERMOD_POV_DISPLAY
; Use built-in or custom LED as a status indicator (assumes LED is connected to GPIO16)
; -D STATUSLED=16
;
;
; set the name of the module - make sure there is a quote-backslash-quote before the name and a backslash-quote-quote after the name
; -D SERVERNAME="\"WLED\""
;
;
; set the number of LEDs
; -D PIXEL_COUNTS=30
; -D DEFAULT_LED_COUNT=30
; or this for multiple outputs
; -D PIXEL_COUNTS=30,30
;
; set the default LED type
; -D LED_TYPES=22 # see const.h (TYPE_xxxx)
; or this for multiple outputs
; -D LED_TYPES=TYPE_SK6812_RGBW,TYPE_WS2812_RGB
;
; set default color order of your led strip
; -D DEFAULT_LED_COLOR_ORDER=COL_ORDER_GRB
; -D DEFAULT_LED_TYPE=22 # see const.h (TYPE_xxxx)
;
; set milliampere limit when using ESP power pin (or inadequate PSU) to power LEDs
; -D ABL_MILLIAMPS_DEFAULT=850
@@ -182,6 +177,9 @@ build_flags = ${common.build_flags} ${esp8266.build_flags}
; enable IR by setting remote type
; -D IRTYPE=0 # 0 Remote disabled | 1 24-key RGB | 2 24-key with CT | 3 40-key blue | 4 40-key RGB | 5 21-key RGB | 6 6-key black | 7 9-key red | 8 JSON remote
;
; set default color order of your led strip
; -D DEFAULT_LED_COLOR_ORDER=COL_ORDER_GRB
;
; use PSRAM on classic ESP32 rev.1 (rev.3 or above has no issues)
; -DBOARD_HAS_PSRAM -mfix-esp32-psram-cache-issue # needed only for classic ESP32 rev.1
;
@@ -239,13 +237,14 @@ build_flags = ${common.build_flags} ${esp8266.build_flags} -D DATA_PINS=1 -D WLE
lib_deps = ${esp8266.lib_deps}
[env:esp32dev_qio80]
extends = env:esp32dev # we want to extend the existing esp32dev environment (and define only updated options)
board = esp32dev
platform = ${esp32.platform}
platform_packages = ${esp32.platform_packages}
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags} ${esp32.build_flags} #-D WLED_DISABLE_BROWNOUT_DET
${esp32.AR_build_flags} ;; optional - includes USERMOD_AUDIOREACTIVE
lib_deps = ${esp32.lib_deps}
${esp32.AR_lib_deps} ;; needed for USERMOD_AUDIOREACTIVE
monitor_filters = esp32_exception_decoder
board_build.partitions = ${esp32.default_partitions}
board_build.f_flash = 80000000L
board_build.flash_mode = qio
@@ -253,25 +252,26 @@ board_build.flash_mode = qio
;; experimental ESP32 env using ESP-IDF V4.4.x
;; Warning: this build environment is not stable!!
;; please erase your device before installing.
extends = esp32_idf_V4 # based on newer "esp-idf V4" platform environment
board = esp32dev
platform = ${esp32_idf_V4.platform}
platform_packages = ${esp32_idf_V4.platform_packages}
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags} ${esp32_idf_V4.build_flags} #-D WLED_DISABLE_BROWNOUT_DET
${esp32.AR_build_flags} ;; includes USERMOD_AUDIOREACTIVE
lib_deps = ${esp32_idf_V4.lib_deps}
${esp32.AR_lib_deps} ;; needed for USERMOD_AUDIOREACTIVE
monitor_filters = esp32_exception_decoder
board_build.partitions = ${esp32.default_partitions} ;; if you get errors about "out of program space", change this to ${esp32.extended_partitions} or even ${esp32.big_partitions}
board_build.partitions = ${esp32_idf_V4.default_partitions}
board_build.f_flash = 80000000L
board_build.flash_mode = dio
[env:esp32s2_saola]
extends = esp32s2
board = esp32-s2-saola-1
platform = ${esp32s2.platform}
platform_packages = ${esp32s2.platform_packages}
framework = arduino
board_build.partitions = tools/WLED_ESP32_4MB_1MB_FS.csv
board_build.flash_mode = qio
upload_speed = 460800
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags} ${esp32s2.build_flags}
;-DLOLIN_WIFI_FIX ;; try this in case Wifi does not work
-DARDUINO_USB_CDC_ON_BOOT=1
@@ -308,7 +308,7 @@ platform = ${common.platform_wled_default}
platform_packages = ${common.platform_packages}
board_build.ldscript = ${common.ldscript_4m1m}
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags} ${esp8266.build_flags} -D WLED_USE_SHOJO_PCB ;; NB: WLED_USE_SHOJO_PCB is not used anywhere in the source code. Not sure why its needed.
build_flags = ${common.build_flags} ${esp8266.build_flags} -D WLED_USE_SHOJO_PCB
lib_deps = ${esp8266.lib_deps}
[env:d1_mini_debug]
@@ -363,48 +363,36 @@ board_upload.flash_size = 2MB
board_upload.maximum_size = 2097152
[env:wemos_shield_esp32]
extends = esp32 ;; use default esp32 platform
board = esp32dev
platform = ${esp32.platform}
platform_packages = ${esp32.platform_packages}
upload_speed = 460800
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags} ${esp32.build_flags}
-D WLED_RELEASE_NAME=\"ESP32_wemos_shield\"
-D DATA_PINS=16
-D RLYPIN=19
-D BTNPIN=17
-D IRPIN=18
-UWLED_USE_MY_CONFIG
-D UWLED_USE_MY_CONFIG
-D USERMOD_DALLASTEMPERATURE
-D USERMOD_FOUR_LINE_DISPLAY
-D TEMPERATURE_PIN=23
${esp32.AR_build_flags} ;; includes USERMOD_AUDIOREACTIVE
-D USE_ALT_DISPlAY ; new versions of USERMOD_FOUR_LINE_DISPLAY and USERMOD_ROTARY_ENCODER_UI
-D USERMOD_AUDIOREACTIVE
lib_deps = ${esp32.lib_deps}
OneWire@~2.3.5 ;; needed for USERMOD_DALLASTEMPERATURE
olikraus/U8g2 @ ^2.28.8 ;; needed for USERMOD_FOUR_LINE_DISPLAY
${esp32.AR_lib_deps} ;; needed for USERMOD_AUDIOREACTIVE
OneWire@~2.3.5
olikraus/U8g2 @ ^2.28.8
https://github.com/blazoncek/arduinoFFT.git
board_build.partitions = ${esp32.default_partitions}
[env:esp32_pico-D4]
extends = esp32 ;; use default esp32 platform
board = pico32 ;; pico32-D4 is different from the standard esp32dev
;; hardware details from https://github.com/srg74/WLED-ESP32-pico
build_flags = ${common.build_flags} ${esp32.build_flags}
-D WLED_RELEASE_NAME=\"pico32-D4\" -D SERVERNAME='"WLED-pico32"'
-D WLED_DISABLE_ADALIGHT ;; no serial-to-USB chip on this board - better to disable serial protocols
-D DATA_PINS=2,18 ;; LED pins
-D RLYPIN=19 -D BTNPIN=0 -D IRPIN=-1 ;; no default pin for IR
${esp32.AR_build_flags} ;; include USERMOD_AUDIOREACTIVE
-D UM_AUDIOREACTIVE_ENABLE ;; enable AR by default
;; Audioreactive settings for on-board microphone (ICS-43432)
-D SR_DMTYPE=1 -D I2S_SDPIN=25 -D I2S_WSPIN=15 -D I2S_CKPIN=14
-D SR_SQUELCH=5 -D SR_GAIN=30
lib_deps = ${esp32.lib_deps}
${esp32.AR_lib_deps} ;; needed for USERMOD_AUDIOREACTIVE
board_build.partitions = ${esp32.default_partitions}
board_build.f_flash = 80000000L
[env:m5atom]
extends = env:esp32dev # we want to extend the existing esp32dev environment (and define only updated options)
board = esp32dev
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags} ${esp32.build_flags} -D DATA_PINS=27 -D BTNPIN=39
lib_deps = ${esp32.lib_deps}
platform = ${esp32.platform}
platform_packages = ${esp32.platform_packages}
board_build.partitions = ${esp32.default_partitions}
[env:sp501e]
board = esp_wroom_02
@@ -427,7 +415,7 @@ platform_packages = ${common.platform_packages}
board_build.ldscript = ${common.ldscript_2m512k}
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags} ${esp8266.build_flags} -D BTNPIN=-1 -D RLYPIN=-1 -D DATA_PINS=4,12,14,13,5
-D LED_TYPES=TYPE_ANALOG_5CH -D WLED_DISABLE_INFRARED -D WLED_MAX_CCT_BLEND=0
-D DEFAULT_LED_TYPE=TYPE_ANALOG_5CH -D WLED_DISABLE_INFRARED -D WLED_MAX_CCT_BLEND=0
lib_deps = ${esp8266.lib_deps}
[env:Athom_15w_RGBCW] ;15w bulb
@@ -437,7 +425,7 @@ platform_packages = ${common.platform_packages}
board_build.ldscript = ${common.ldscript_2m512k}
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags} ${esp8266.build_flags} -D BTNPIN=-1 -D RLYPIN=-1 -D DATA_PINS=4,12,14,5,13
-D LED_TYPES=TYPE_ANALOG_5CH -D WLED_DISABLE_INFRARED -D WLED_MAX_CCT_BLEND=0 -D WLED_USE_IC_CCT
-D DEFAULT_LED_TYPE=TYPE_ANALOG_5CH -D WLED_DISABLE_INFRARED -D WLED_MAX_CCT_BLEND=0 -D WLED_USE_IC_CCT
lib_deps = ${esp8266.lib_deps}
[env:Athom_3Pin_Controller] ;small controller with only data
@@ -503,8 +491,9 @@ lib_deps = ${esp8266.lib_deps}
# EleksTube-IPS
# ------------------------------------------------------------------------------
[env:elekstube_ips]
extends = esp32 ;; use default esp32 platform
board = esp32dev
platform = ${esp32.platform}
platform_packages = ${esp32.platform_packages}
upload_speed = 921600
build_flags = ${common.build_flags} ${esp32.build_flags} -D WLED_DISABLE_BROWNOUT_DET -D WLED_DISABLE_INFRARED
-D USERMOD_RTC
@@ -512,7 +501,7 @@ build_flags = ${common.build_flags} ${esp32.build_flags} -D WLED_DISABLE_BROWNOU
-D DATA_PINS=12
-D RLYPIN=27
-D BTNPIN=34
-D PIXEL_COUNTS=6
-D DEFAULT_LED_COUNT=6
# Display config
-D ST7789_DRIVER
-D TFT_WIDTH=135
@@ -528,4 +517,5 @@ build_flags = ${common.build_flags} ${esp32.build_flags} -D WLED_DISABLE_BROWNOU
monitor_filters = esp32_exception_decoder
lib_deps =
${esp32.lib_deps}
TFT_eSPI @ 2.5.33 ;; this is the last version that compiles with the WLED default framework - newer versions require platform = espressif32 @ ^6.3.2
TFT_eSPI @ ^2.3.70
board_build.partitions = ${esp32.default_partitions}
Binary file not shown.
+6 -1
View File
@@ -101,7 +101,6 @@ function adoptVersionAndRepo(html) {
async function minify(str, type = "plain") {
const options = {
collapseWhitespace: true,
conservativeCollapse: true, // preserve spaces in text
collapseBooleanAttributes: true,
collapseInlineTagWhitespace: true,
minifyCSS: true,
@@ -370,6 +369,12 @@ const char PAGE_dmxmap[] PROGMEM = R"=====()=====";
name: "PAGE_update",
method: "gzip",
filter: "html-minify",
mangle: (str) =>
str
.replace(
/function GetV().*\<\/script\>/gms,
"</script><script src=\"/settings/s.js?p=9\"></script>"
)
},
{
file: "welcome.htm",
+2 -2
View File
@@ -102,9 +102,9 @@ private:
void secondsEffectSineFade(int16_t secondLed, Toki::Time const& time) {
uint32_t ms = time.ms % 1000;
uint8_t b0 = (cos8_t(ms * 64 / 1000) - 128) * 2;
uint8_t b0 = (cos8(ms * 64 / 1000) - 128) * 2;
setPixelColor(secondLed, gamma32(scale32(secondColor, b0)));
uint8_t b1 = (sin8_t(ms * 64 / 1000) - 128) * 2;
uint8_t b1 = (sin8(ms * 64 / 1000) - 128) * 2;
setPixelColor(inc(secondLed, 1, secondsSegment), gamma32(scale32(secondColor, b1)));
}
-1
View File
@@ -444,7 +444,6 @@ public:
configComplete &= getJsonValue(top[F("PublishAlways")], PublishAlways, false);
configComplete &= getJsonValue(top[F("UseCelsius")], UseCelsius, true);
configComplete &= getJsonValue(top[F("HomeAssistantDiscovery")], HomeAssistantDiscovery, false);
tempScale = UseCelsius ? "°C" : "°F";
DEBUG_PRINT(FPSTR(_name));
if (!initDone) {
+10 -12
View File
@@ -7,6 +7,10 @@
#include <driver/i2s.h>
#include <driver/adc.h>
#ifdef WLED_ENABLE_DMX
#error This audio reactive usermod is not compatible with DMX Out.
#endif
#endif
#if defined(ARDUINO_ARCH_ESP32) && (defined(WLED_DEBUG) || defined(SR_DEBUG))
@@ -71,7 +75,7 @@ static uint8_t soundAgc = 0; // Automagic gain control: 0 - n
//static float volumeSmth = 0.0f; // either sampleAvg or sampleAgc depending on soundAgc; smoothed sample
static float FFT_MajorPeak = 1.0f; // FFT: strongest (peak) frequency
static float FFT_Magnitude = 0.0f; // FFT: volume (magnitude) of peak frequency
static bool samplePeak = false; // Boolean flag for peak - used in effects. Responding routine may reset this flag. Auto-reset after strip.getFrameTime()
static bool samplePeak = false; // Boolean flag for peak - used in effects. Responding routine may reset this flag. Auto-reset after strip.getMinShowDelay()
static bool udpSamplePeak = false; // Boolean flag for peak. Set at the same time as samplePeak, but reset by transmitAudioData
static unsigned long timeOfPeak = 0; // time of last sample peak detection.
static uint8_t fftResult[NUM_GEQ_CHANNELS]= {0};// Our calculated freq. channel result table to be used by effects
@@ -532,8 +536,8 @@ static void detectSamplePeak(void) {
#endif
static void autoResetPeak(void) {
uint16_t peakDelay = max(uint16_t(50), strip.getFrameTime());
if (millis() - timeOfPeak > peakDelay) { // Auto-reset of samplePeak after at least one complete frame has passed.
uint16_t MinShowDelay = MAX(50, strip.getMinShowDelay()); // Fixes private class variable compiler error. Unsure if this is the correct way of fixing the root problem. -THATDONFC
if (millis() - timeOfPeak > MinShowDelay) { // Auto-reset of samplePeak after a complete frame has passed.
samplePeak = false;
if (audioSyncEnabled == 0) udpSamplePeak = false; // this is normally reset by transmitAudioData
}
@@ -1221,6 +1225,7 @@ class AudioReactive : public Usermod {
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32S3)
// ADC over I2S is only possible on "classic" ESP32
case 0:
default:
DEBUGSR_PRINTLN(F("AR: Analog Microphone (left channel only)."));
audioSource = new I2SAdcSource(SAMPLE_RATE, BLOCK_SIZE);
delay(100);
@@ -1228,13 +1233,6 @@ class AudioReactive : public Usermod {
if (audioSource) audioSource->initialize(audioPin);
break;
#endif
case 255: // 255 = -1 = no audio source
// falls through to default
default:
if (audioSource) delete audioSource; audioSource = nullptr;
enabled = false;
break;
}
delay(250); // give microphone enough time to initialise
@@ -1321,7 +1319,7 @@ class AudioReactive : public Usermod {
disableSoundProcessing = true;
} else {
#if defined(ARDUINO_ARCH_ESP32) && defined(WLED_DEBUG)
if ((disableSoundProcessing == true) && (audioSyncEnabled == 0) && audioSource && audioSource->isInitialized()) { // we just switched to "enabled"
if ((disableSoundProcessing == true) && (audioSyncEnabled == 0) && audioSource->isInitialized()) { // we just switched to "enabled"
DEBUG_PRINTLN(F("[AR userLoop] realtime mode ended - audio processing resumed."));
DEBUG_PRINTF_P(PSTR(" RealtimeMode = %d; RealtimeOverride = %d\n"), int(realtimeMode), int(realtimeOverride));
}
@@ -1333,7 +1331,7 @@ class AudioReactive : public Usermod {
if (audioSyncEnabled & 0x02) disableSoundProcessing = true; // make sure everything is disabled IF in audio Receive mode
if (audioSyncEnabled & 0x01) disableSoundProcessing = false; // keep running audio IF we're in audio Transmit mode
#ifdef ARDUINO_ARCH_ESP32
if (!audioSource || !audioSource->isInitialized()) disableSoundProcessing = true; // no audio source
if (!audioSource->isInitialized()) disableSoundProcessing = true; // no audio source
// Only run the sampling code IF we're not in Receive mode or realtime mode
+3 -3
View File
@@ -9,7 +9,7 @@ The actual / original code that controls the LED modes is from Adam Zeloof. I ta
It was quite a bit more work than I hoped, but I got there eventually :)
## Requirements
* "ESP Rotary" by Lennart Hennigs, v2.1.1 or higher: https://github.com/LennartHennigs/ESPRotary
* "ESP Rotary" by Lennart Hennigs, v1.5.0 or higher: https://github.com/LennartHennigs/ESPRotary
## Usermod installation
Simply copy the below block (build task) to your `platformio_override.ini` and compile WLED using this new build task. Or use an existing one and add the buildflag `-D RGB_ROTARY_ENCODER`.
@@ -20,7 +20,7 @@ ESP32:
extends = env:esp32dev
build_flags = ${common.build_flags_esp32} -D WLED_RELEASE_NAME=ESP32 -D RGB_ROTARY_ENCODER
lib_deps = ${esp32.lib_deps}
lennarthennigs/ESP Rotary@^2.1.1
lennarthennigs/ESP Rotary@^1.5.0
```
ESP8266 / D1 Mini:
@@ -29,7 +29,7 @@ ESP8266 / D1 Mini:
extends = env:d1_mini
build_flags = ${common.build_flags_esp8266} -D RGB_ROTARY_ENCODER
lib_deps = ${esp8266.lib_deps}
lennarthennigs/ESP Rotary@^2.1.1
lennarthennigs/ESP Rotary@^1.5.0
```
## How to connect the board to your ESP
@@ -95,9 +95,9 @@ public:
}
else
{
fastled_col.red = colPri[0];
fastled_col.green = colPri[1];
fastled_col.blue = colPri[2];
fastled_col.red = col[0];
fastled_col.green = col[1];
fastled_col.blue = col[2];
prim_hsv = rgb2hsv_approximate(fastled_col);
new_val = (int16_t)prim_hsv.h + fadeAmount;
if (new_val > 255)
@@ -106,9 +106,9 @@ public:
new_val += 255; // roll-over if smaller than 0
prim_hsv.h = (byte)new_val;
hsv2rgb_rainbow(prim_hsv, fastled_col);
colPri[0] = fastled_col.red;
colPri[1] = fastled_col.green;
colPri[2] = fastled_col.blue;
col[0] = fastled_col.red;
col[1] = fastled_col.green;
col[2] = fastled_col.blue;
}
}
else if (Enc_B == LOW)
@@ -120,9 +120,9 @@ public:
}
else
{
fastled_col.red = colPri[0];
fastled_col.green = colPri[1];
fastled_col.blue = colPri[2];
fastled_col.red = col[0];
fastled_col.green = col[1];
fastled_col.blue = col[2];
prim_hsv = rgb2hsv_approximate(fastled_col);
new_val = (int16_t)prim_hsv.h - fadeAmount;
if (new_val > 255)
@@ -131,9 +131,9 @@ public:
new_val += 255; // roll-over if smaller than 0
prim_hsv.h = (byte)new_val;
hsv2rgb_rainbow(prim_hsv, fastled_col);
colPri[0] = fastled_col.red;
colPri[1] = fastled_col.green;
colPri[2] = fastled_col.blue;
col[0] = fastled_col.red;
col[1] = fastled_col.green;
col[2] = fastled_col.blue;
}
}
//call for notifier -> 0: init 1: direct change 2: button 3: notification 4: nightlight 5: other (No notification)
@@ -1,8 +1,16 @@
# I2C/SPI 4 Line Display Usermod ALT
This usermod could be used in compination with `usermod_v2_rotary_encoder_ui_ALT`.
Thank you to the authors of the original version of these usermods. It would not have been possible without them!
"usermod_v2_four_line_display"
"usermod_v2_rotary_encoder_ui"
## Functionalities
The core of these usermods are a copy of the originals. The main changes are to the FourLineDisplay usermod.
The display usermod UI has been completely changed.
The changes made to the RotaryEncoder usermod were made to support the new UI in the display usermod.
Without the display, it functions identical to the original.
The original "usermod_v2_auto_save" will not work with the display just yet.
Press the encoder to cycle through the options:
* Brightness
@@ -10,18 +18,26 @@ Press the encoder to cycle through the options:
* Intensity
* Palette
* Effect
* Main Color
* Saturation
* Main Color (only if display is used)
* Saturation (only if display is used)
Press and hold the encoder to display Network Info. If AP is active, it will display the AP, SSID and Password
Press and hold the encoder to display Network Info. If AP is active, it will display AP, SSID and password
Also shows if the timer is enabled.
Also shows if the timer is enabled
[See the pair of usermods in action](https://www.youtube.com/watch?v=ulZnBt9z3TI)
## Installation
Copy the example `platformio_override.sample.ini` to the root directory of your particular build.
Please refer to the original `usermod_v2_rotary_encoder_ui` readme for the main instructions.
Copy the example `platformio_override.sample.ini` from the usermod_v2_rotary_encoder_ui_ALT folder to the root directory of your particular build and rename it to `platformio_override.ini`.
This file should be placed in the same directory as `platformio.ini`.
Then, to activate this alternative usermod, add `#define USE_ALT_DISPlAY` (NOTE: CASE SENSITIVE) to the `usermods_list.cpp` file,
or add `-D USE_ALT_DISPlAY` to the original `platformio_override.ini.sample` file
## Configuration
@@ -1,14 +0,0 @@
[platformio]
default_envs = esp32dev
[env:esp32dev]
board = esp32dev
platform = ${esp32.platform}
build_unflags = ${common.build_unflags}
build_flags =
${common.build_flags_esp32}
-D USERMOD_ROTARY_ENCODER_UI
-D USERMOD_ROTARY_ENCODER_GPIO=INPUT
-D ENCODER_DT_PIN=21
-D ENCODER_CLK_PIN=23
-D ENCODER_SW_PIN=0
@@ -7,12 +7,11 @@ platform = ${esp32.platform}
build_unflags = ${common.build_unflags}
build_flags =
${common.build_flags_esp32}
-D USERMOD_FOUR_LINE_DISPLAY
-D FLD_TYPE=SH1106
-D I2CSCLPIN=27
-D I2CSDAPIN=26
-D USERMOD_FOUR_LINE_DISPLAY -D USE_ALT_DISPlAY
-D USERMOD_ROTARY_ENCODER_UI -D ENCODER_DT_PIN=18 -D ENCODER_CLK_PIN=5 -D ENCODER_SW_PIN=19
upload_speed = 460800
lib_deps =
${esp32.lib_deps}
U8g2@~2.34.4
Wire
@@ -1,8 +1,16 @@
# Rotary Encoder UI Usermod ALT
This usermod supports the UI of the `usermod_v2_rotary_encoder_ui_ALT`.
Thank you to the authors of the original version of these usermods. It would not have been possible without them!
"usermod_v2_four_line_display"
"usermod_v2_rotary_encoder_ui"
## Functionalities
The core of these usermods are a copy of the originals. The main changes are to the FourLineDisplay usermod.
The display usermod UI has been completely changed.
The changes made to the RotaryEncoder usermod were made to support the new UI in the display usermod.
Without the display, it functions identical to the original.
The original "usermod_v2_auto_save" will not work with the display just yet.
Press the encoder to cycle through the options:
* Brightness
@@ -13,7 +21,8 @@ Press the encoder to cycle through the options:
* Main Color (only if display is used)
* Saturation (only if display is used)
Press and hold the encoder to display Network Info. If AP is active, it will display the AP, SSID and Password
Press and hold the encoder to display Network Info
if AP is active, it will display the AP, SSID and Password
Also shows if the timer is enabled.
@@ -21,7 +30,9 @@ Also shows if the timer is enabled.
## Installation
Copy the example `platformio_override.sample.ini` to the root directory of your particular build.
Copy the example `platformio_override.sample.ini` to the root directory of your particular build and rename it to `platformio_override.ini`.
To activate this alternative usermod, add `#define USE_ALT_DISPlAY` (NOTE: CASE SENSITIVE) to the `usermods_list.cpp` file, or add `-D USE_ALT_DISPlAY` to your `platformio_override.ini` file
### Define Your Options
@@ -29,6 +40,7 @@ Copy the example `platformio_override.sample.ini` to the root directory of your
* `USERMOD_FOUR_LINE_DISPLAY` - define this to have this the Four Line Display mod included wled00\usermods_list.cpp
also tells this usermod that the display is available
(see the Four Line Display usermod `readme.md` for more details)
* `USE_ALT_DISPlAY` - Mandatory to use Four Line Display
* `ENCODER_DT_PIN` - defaults to 18
* `ENCODER_CLK_PIN` - defaults to 5
* `ENCODER_SW_PIN` - defaults to 19
@@ -38,7 +50,7 @@ Copy the example `platformio_override.sample.ini` to the root directory of your
### PlatformIO requirements
No special requirements.
Note: the Four Line Display usermod requires the libraries `U8g2` and `Wire`.
## Change Log
@@ -518,7 +518,7 @@ void RotaryEncoderUIUsermod::setup()
loopTime = millis();
currentCCT = (approximateKelvinFromRGB(RGBW32(colPri[0], colPri[1], colPri[2], colPri[3])) - 1900) >> 5;
currentCCT = (approximateKelvinFromRGB(RGBW32(col[0], col[1], col[2], col[3])) - 1900) >> 5;
if (!initDone) sortModesAndPalettes();
@@ -920,17 +920,17 @@ void RotaryEncoderUIUsermod::changeHue(bool increase){
display->updateRedrawTime();
#endif
currentHue1 = max(min((increase ? currentHue1+fadeAmount : currentHue1-fadeAmount), 255), 0);
colorHStoRGB(currentHue1*256, currentSat1, colPri);
colorHStoRGB(currentHue1*256, currentSat1, col);
stateChanged = true;
if (applyToAll) {
for (unsigned i=0; i<strip.getSegmentsNum(); i++) {
Segment& seg = strip.getSegment(i);
if (!seg.isActive()) continue;
seg.colors[0] = RGBW32(colPri[0], colPri[1], colPri[2], colPri[3]);
seg.colors[0] = RGBW32(col[0], col[1], col[2], col[3]);
}
} else {
Segment& seg = strip.getSegment(strip.getMainSegmentId());
seg.colors[0] = RGBW32(colPri[0], colPri[1], colPri[2], colPri[3]);
seg.colors[0] = RGBW32(col[0], col[1], col[2], col[3]);
}
lampUdated();
#ifdef USERMOD_FOUR_LINE_DISPLAY
@@ -950,16 +950,16 @@ void RotaryEncoderUIUsermod::changeSat(bool increase){
display->updateRedrawTime();
#endif
currentSat1 = max(min((increase ? currentSat1+fadeAmount : currentSat1-fadeAmount), 255), 0);
colorHStoRGB(currentHue1*256, currentSat1, colPri);
colorHStoRGB(currentHue1*256, currentSat1, col);
if (applyToAll) {
for (unsigned i=0; i<strip.getSegmentsNum(); i++) {
Segment& seg = strip.getSegment(i);
if (!seg.isActive()) continue;
seg.colors[0] = RGBW32(colPri[0], colPri[1], colPri[2], colPri[3]);
seg.colors[0] = RGBW32(col[0], col[1], col[2], col[3]);
}
} else {
Segment& seg = strip.getSegment(strip.getMainSegmentId());
seg.colors[0] = RGBW32(colPri[0], colPri[1], colPri[2], colPri[3]);
seg.colors[0] = RGBW32(col[0], col[1], col[2], col[3]);
}
lampUdated();
#ifdef USERMOD_FOUR_LINE_DISPLAY
+185 -198
View File
@@ -25,7 +25,7 @@
// effect utility functions
uint8_t sin_gap(uint16_t in) {
if (in & 0x100) return 0;
return sin8_t(in + 192); // correct phase shift of sine so that it starts and stops at 0
return sin8(in + 192); // correct phase shift of sine so that it starts and stops at 0
}
uint16_t triwave16(uint16_t in) {
@@ -183,7 +183,8 @@ uint16_t color_wipe(bool rev, bool useRandomColors) {
}
unsigned ledIndex = (prog * SEGLEN) >> 15;
uint16_t rem = (prog * SEGLEN) * 2; //mod 0xFFFF by truncating
unsigned rem = 0;
rem = (prog * SEGLEN) * 2; //mod 0xFFFF
rem /= (SEGMENT.intensity +1);
if (rem > 255) rem = 255;
@@ -334,7 +335,7 @@ uint16_t mode_breath(void) {
counter = (counter >> 2) + (counter >> 4); //0-16384 + 0-2048
if (counter < 16384) {
if (counter > 8192) counter = 8192 - (counter - 8192);
var = sin16_t(counter) / 103; //close to parabolic in range 0-8192, max val. 23170
var = sin16(counter) / 103; //close to parabolic in range 0-8192, max val. 23170
}
unsigned lum = 30 + var;
@@ -516,7 +517,7 @@ static uint16_t running_base(bool saw, bool dual=false) {
}
a = 255 - a;
}
uint8_t s = dual ? sin_gap(a) : sin8_t(a);
uint8_t s = dual ? sin_gap(a) : sin8(a);
uint32_t ca = color_blend(SEGCOLOR(1), SEGMENT.color_from_palette(i, true, PALETTE_SOLID_WRAP, 0), s);
if (dual) {
unsigned b = (SEGLEN-1-i)*x_scale - counter;
@@ -580,7 +581,7 @@ uint16_t mode_twinkle(void) {
SEGENV.step = it;
}
uint16_t PRNG16 = SEGENV.aux1;
unsigned PRNG16 = SEGENV.aux1;
for (unsigned i = 0; i < SEGENV.aux0; i++)
{
@@ -599,12 +600,11 @@ static const char _data_FX_MODE_TWINKLE[] PROGMEM = "Twinkle@!,!;!,!;!;;m12=0";
* Dissolve function
*/
uint16_t dissolve(uint32_t color) {
unsigned dataSize = sizeof(uint32_t) * SEGLEN;
unsigned dataSize = (SEGLEN+7) >> 3; //1 bit per LED
if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed
uint32_t* pixels = reinterpret_cast<uint32_t*>(SEGENV.data);
if (SEGENV.call == 0) {
for (unsigned i = 0; i < SEGLEN; i++) pixels[i] = SEGCOLOR(1);
memset(SEGMENT.data, 0xFF, dataSize); // start by fading pixels up
SEGENV.aux0 = 1;
}
@@ -612,26 +612,33 @@ uint16_t dissolve(uint32_t color) {
if (random8() <= SEGMENT.intensity) {
for (size_t times = 0; times < 10; times++) { //attempt to spawn a new pixel 10 times
unsigned i = random16(SEGLEN);
unsigned index = i >> 3;
unsigned bitNum = i & 0x07;
bool fadeUp = bitRead(SEGENV.data[index], bitNum);
if (SEGENV.aux0) { //dissolve to primary/palette
if (pixels[i] == SEGCOLOR(1)) {
pixels[i] = color == SEGCOLOR(0) ? SEGMENT.color_from_palette(i, true, PALETTE_SOLID_WRAP, 0) : color;
if (fadeUp) {
if (color == SEGCOLOR(0)) {
SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(i, true, PALETTE_SOLID_WRAP, 0));
} else {
SEGMENT.setPixelColor(i, color);
}
bitWrite(SEGENV.data[index], bitNum, false);
break; //only spawn 1 new pixel per frame per 50 LEDs
}
} else { //dissolve to secondary
if (pixels[i] != SEGCOLOR(1)) {
pixels[i] = SEGCOLOR(1);
break;
if (!fadeUp) {
SEGMENT.setPixelColor(i, SEGCOLOR(1)); break;
bitWrite(SEGENV.data[index], bitNum, true);
}
}
}
}
}
// fix for #4401
for (unsigned i = 0; i < SEGLEN; i++) SEGMENT.setPixelColor(i, pixels[i]);
if (SEGENV.step > (255 - SEGMENT.speed) + 15U) {
SEGENV.aux0 = !SEGENV.aux0;
SEGENV.step = 0;
memset(SEGMENT.data, (SEGENV.aux0 ? 0xFF : 0), dataSize); // switch fading
} else {
SEGENV.step++;
}
@@ -1090,7 +1097,7 @@ uint16_t mode_running_random(void) {
unsigned z = it % zoneSize;
bool nzone = (!z && it != SEGENV.aux1);
for (int i=SEGLEN-1; i >= 0; i--) {
for (unsigned i=SEGLEN-1; i > 0; i--) {
if (nzone || z >= zoneSize) {
unsigned lastrand = PRNG16 >> 8;
int16_t diff = 0;
@@ -1434,7 +1441,7 @@ uint16_t mode_fairy() {
if (z == zones-1) flashersInZone = numFlashers-(flashersInZone*(zones-1));
for (unsigned f = firstFlasher; f < firstFlasher + flashersInZone; f++) {
unsigned stateTime = uint16_t(now16 - flashers[f].stateStart);
unsigned stateTime = now16 - flashers[f].stateStart;
//random on/off time reached, switch state
if (stateTime > flashers[f].stateDur * 10) {
flashers[f].stateOn = !flashers[f].stateOn;
@@ -1493,7 +1500,7 @@ uint16_t mode_fairytwinkle() {
unsigned maxDur = riseFallTime/100 + ((255 - SEGMENT.intensity) >> 2) + 13 + ((255 - SEGMENT.intensity) >> 1);
for (int f = 0; f < SEGLEN; f++) {
uint16_t stateTime = now16 - flashers[f].stateStart;
unsigned stateTime = now16 - flashers[f].stateStart;
//random on/off time reached, switch state
if (stateTime > flashers[f].stateDur * 100) {
flashers[f].stateOn = !flashers[f].stateOn;
@@ -1646,8 +1653,8 @@ static const char _data_FX_MODE_TRICOLOR_WIPE[] PROGMEM = "Tri Wipe@!;1,2,3;!";
* Modified by Aircoookie
*/
uint16_t mode_tricolor_fade(void) {
uint16_t counter = strip.now * ((SEGMENT.speed >> 3) +1);
uint32_t prog = (counter * 768) >> 16;
unsigned counter = strip.now * ((SEGMENT.speed >> 3) +1);
uint16_t prog = (counter * 768) >> 16;
uint32_t color1 = 0, color2 = 0;
unsigned stage = 0;
@@ -1738,7 +1745,7 @@ uint16_t mode_random_chase(void) {
uint32_t color = SEGENV.step;
random16_set_seed(SEGENV.aux0);
for (int i = SEGLEN -1; i >= 0; i--) {
for (unsigned i = SEGLEN -1; i > 0; i--) {
uint8_t r = random8(6) != 0 ? (color >> 16 & 0xFF) : random8();
uint8_t g = random8(6) != 0 ? (color >> 8 & 0xFF) : random8();
uint8_t b = random8(6) != 0 ? (color & 0xFF) : random8();
@@ -1791,7 +1798,7 @@ uint16_t mode_oscillate(void) {
// if the counter has increased, move the oscillator by the random step
if (it != SEGENV.step) oscillators[i].pos += oscillators[i].dir * oscillators[i].speed;
oscillators[i].size = SEGLEN/(3+SEGMENT.intensity/8);
if((oscillators[i].dir == -1) && (oscillators[i].pos > SEGLEN << 1)) { // use integer overflow
if((oscillators[i].dir == -1) && (oscillators[i].pos <= 0)) {
oscillators[i].pos = 0;
oscillators[i].dir = 1;
// make bigger steps for faster speeds
@@ -1807,8 +1814,8 @@ uint16_t mode_oscillate(void) {
for (unsigned i = 0; i < SEGLEN; i++) {
uint32_t color = BLACK;
for (unsigned j = 0; j < numOscillators; j++) {
if((int)i >= (int)oscillators[j].pos - oscillators[j].size && i <= oscillators[j].pos + oscillators[j].size) {
color = (color == BLACK) ? SEGCOLOR(j) : color_blend(color, SEGCOLOR(j), uint8_t(128));
if(i >= (unsigned)oscillators[j].pos - oscillators[j].size && i <= oscillators[j].pos + oscillators[j].size) {
color = (color == BLACK) ? SEGCOLOR(j) : color_blend(color, SEGCOLOR(j), 128);
}
}
SEGMENT.setPixelColor(i, color);
@@ -1872,16 +1879,16 @@ uint16_t mode_pride_2015(void) {
unsigned sPseudotime = SEGENV.step;
unsigned sHue16 = SEGENV.aux0;
uint8_t sat8 = beatsin88_t( 87, 220, 250);
uint8_t brightdepth = beatsin88_t( 341, 96, 224);
unsigned brightnessthetainc16 = beatsin88_t( 203, (25 * 256), (40 * 256));
unsigned msmultiplier = beatsin88_t(147, 23, 60);
uint8_t sat8 = beatsin88( 87, 220, 250);
uint8_t brightdepth = beatsin88( 341, 96, 224);
unsigned brightnessthetainc16 = beatsin88( 203, (25 * 256), (40 * 256));
unsigned msmultiplier = beatsin88(147, 23, 60);
unsigned hue16 = sHue16;//gHue * 256;
unsigned hueinc16 = beatsin88_t(113, 1, 3000);
unsigned hueinc16 = beatsin88(113, 1, 3000);
sPseudotime += duration * msmultiplier;
sHue16 += duration * beatsin88_t( 400, 5,9);
sHue16 += duration * beatsin88( 400, 5,9);
unsigned brightnesstheta16 = sPseudotime;
for (unsigned i = 0 ; i < SEGLEN; i++) {
@@ -1889,7 +1896,7 @@ uint16_t mode_pride_2015(void) {
uint8_t hue8 = hue16 >> 8;
brightnesstheta16 += brightnessthetainc16;
unsigned b16 = sin16_t( brightnesstheta16 ) + 32768;
unsigned b16 = sin16( brightnesstheta16 ) + 32768;
unsigned bri16 = (uint32_t)((uint32_t)b16 * (uint32_t)b16) / 65536;
uint8_t bri8 = (uint32_t)(((uint32_t)bri16) * brightdepth) / 65536;
@@ -1914,7 +1921,7 @@ uint16_t mode_juggle(void) {
CRGB fastled_col;
byte dothue = 0;
for (int i = 0; i < 8; i++) {
int index = 0 + beatsin88_t((16 + SEGMENT.speed)*(i + 7), 0, SEGLEN -1);
int index = 0 + beatsin88((16 + SEGMENT.speed)*(i + 7), 0, SEGLEN -1);
fastled_col = CRGB(SEGMENT.getPixelColor(index));
fastled_col |= (SEGMENT.palette==0)?CHSV(dothue, 220, 255):ColorFromPalette(SEGPALETTE, dothue, 255);
SEGMENT.setPixelColor(index, fastled_col);
@@ -1935,8 +1942,8 @@ uint16_t mode_palette() {
constexpr mathType maxAngle = 0x8000;
constexpr mathType staticRotationScale = 256;
constexpr mathType animatedRotationScale = 1;
constexpr int16_t (*sinFunction)(uint16_t) = &sin16_t;
constexpr int16_t (*cosFunction)(uint16_t) = &cos16_t;
constexpr int16_t (*sinFunction)(uint16_t) = &sin16;
constexpr int16_t (*cosFunction)(uint16_t) = &cos16;
#else
using mathType = float;
using wideMathType = float;
@@ -1959,7 +1966,7 @@ uint16_t mode_palette() {
const bool inputAnimateRotation = SEGMENT.check2;
const bool inputAssumeSquare = SEGMENT.check3;
const angleType theta = (!inputAnimateRotation) ? ((inputRotation + 128) * maxAngle / staticRotationScale) : (((strip.now * ((inputRotation >> 4) +1)) & 0xFFFF) * animatedRotationScale);
const angleType theta = (!inputAnimateRotation) ? (inputRotation * maxAngle / staticRotationScale) : (((strip.now * ((inputRotation >> 4) +1)) & 0xFFFF) * animatedRotationScale);
const mathType sinTheta = sinFunction(theta);
const mathType cosTheta = cosFunction(theta);
@@ -1978,7 +1985,7 @@ uint16_t mode_palette() {
// So the rectangle needs to have exactly the right size. That size depends on the rotation.
// This scale computation here only considers one dimension. You can think of it like the rectangle is always scaled so that
// the left and right most points always match the left and right side of the display.
const mathType scale = std::abs(sinTheta) + (std::abs(cosTheta) * maxYOut / maxXOut);
const mathType scale = std::abs(sinTheta) + (std::abs(cosTheta) * maxYOut / maxXOut);
// 2D simulation:
// If we are dealing with a 1D setup, we assume that each segment represents one line on a 2-dimensional display.
// The function is called once per segments, so we need to handle one line at a time.
@@ -1996,7 +2003,7 @@ uint16_t mode_palette() {
const mathType sourceX = xtSinTheta + ytCosTheta + centerX;
// The computation was scaled just right so that the result should always be in range [0, maxXOut], but enforce this anyway
// to account for imprecision. Then scale it so that the range is [0, 255], which we can use with the palette.
int colorIndex = (std::min(std::max(sourceX, mathType(0)), maxXOut * sInt16Scale) * wideMathType(255)) / (sInt16Scale * maxXOut);
int colorIndex = (std::min(std::max(sourceX, mathType(0)), maxXOut * sInt16Scale) * 255) / (sInt16Scale * maxXOut);
// inputSize determines by how much we want to scale the palette:
// values < 128 display a fraction of a palette,
// values > 128 display multiple palettes.
@@ -2009,8 +2016,8 @@ uint16_t mode_palette() {
colorIndex = ((inputSize - 112) * colorIndex) / 16;
}
// Finally, shift the palette a bit.
const int paletteOffset = (!inputAnimateShift) ? (inputShift) : (((strip.now * ((inputShift >> 3) +1)) & 0xFFFF) >> 8);
colorIndex -= paletteOffset;
const int paletteOffset = (!inputAnimateShift) ? (inputShift-128) : (((strip.now * ((inputShift >> 3) +1)) & 0xFFFF) >> 8);
colorIndex += paletteOffset;
const uint32_t color = SEGMENT.color_wheel((uint8_t)colorIndex);
if (isMatrix) {
SEGMENT.setPixelColorXY(x, y, color);
@@ -2021,7 +2028,7 @@ uint16_t mode_palette() {
}
return FRAMETIME;
}
static const char _data_FX_MODE_PALETTE[] PROGMEM = "Palette@Shift,Size,Rotation,,,Animate Shift,Animate Rotation,Anamorphic;;!;12;ix=112,c1=0,o1=1,o2=0,o3=1";
static const char _data_FX_MODE_PALETTE[] PROGMEM = "Palette@Shift,Size,Rotation,,,Animate Shift,Animate Rotation,Anamorphic;;!;12;c1=128,c2=128,c3=128,o1=1,o2=1,o3=0";
// WLED limitation: Analog Clock overlay will NOT work when Fire2012 is active
@@ -2120,15 +2127,15 @@ uint16_t mode_colorwaves() {
unsigned sPseudotime = SEGENV.step;
unsigned sHue16 = SEGENV.aux0;
unsigned brightdepth = beatsin88_t(341, 96, 224);
unsigned brightnessthetainc16 = beatsin88_t( 203, (25 * 256), (40 * 256));
unsigned msmultiplier = beatsin88_t(147, 23, 60);
unsigned brightdepth = beatsin88(341, 96, 224);
unsigned brightnessthetainc16 = beatsin88( 203, (25 * 256), (40 * 256));
unsigned msmultiplier = beatsin88(147, 23, 60);
unsigned hue16 = sHue16;//gHue * 256;
unsigned hueinc16 = beatsin88_t(113, 60, 300)*SEGMENT.intensity*10/255; // Use the Intensity Slider for the hues
unsigned hueinc16 = beatsin88(113, 60, 300)*SEGMENT.intensity*10/255; // Use the Intensity Slider for the hues
sPseudotime += duration * msmultiplier;
sHue16 += duration * beatsin88_t(400, 5, 9);
sHue16 += duration * beatsin88(400, 5, 9);
unsigned brightnesstheta16 = sPseudotime;
for (int i = 0 ; i < SEGLEN; i++) {
@@ -2142,7 +2149,7 @@ uint16_t mode_colorwaves() {
}
brightnesstheta16 += brightnessthetainc16;
unsigned b16 = sin16_t(brightnesstheta16) + 32768;
unsigned b16 = sin16(brightnesstheta16) + 32768;
unsigned bri16 = (uint32_t)((uint32_t)b16 * (uint32_t)b16) / 65536;
uint8_t bri8 = (uint32_t)(((uint32_t)bri16) * brightdepth) / 65536;
@@ -2161,7 +2168,7 @@ static const char _data_FX_MODE_COLORWAVES[] PROGMEM = "Colorwaves@!,Hue;!;!";
// colored stripes pulsing at a defined Beats-Per-Minute (BPM)
uint16_t mode_bpm() {
uint32_t stp = (strip.now / 20) & 0xFF;
uint8_t beat = beatsin8_t(SEGMENT.speed, 64, 255);
uint8_t beat = beatsin8(SEGMENT.speed, 64, 255);
for (int i = 0; i < SEGLEN; i++) {
SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(stp + (i * 2), false, PALETTE_SOLID_WRAP, 0, beat - stp + (i * 10)));
}
@@ -2177,7 +2184,7 @@ uint16_t mode_fillnoise8() {
unsigned index = inoise8(i * SEGLEN, SEGENV.step + i * SEGLEN);
SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(index, false, PALETTE_SOLID_WRAP, 0));
}
SEGENV.step += beatsin8_t(SEGMENT.speed, 1, 6); //10,1,4
SEGENV.step += beatsin8(SEGMENT.speed, 1, 6); //10,1,4
return FRAMETIME;
}
@@ -2189,13 +2196,13 @@ uint16_t mode_noise16_1() {
SEGENV.step += (1 + SEGMENT.speed/16);
for (int i = 0; i < SEGLEN; i++) {
unsigned shift_x = beatsin8_t(11); // the x position of the noise field swings @ 17 bpm
unsigned shift_x = beatsin8(11); // the x position of the noise field swings @ 17 bpm
unsigned shift_y = SEGENV.step/42; // the y position becomes slowly incremented
unsigned real_x = (i + shift_x) * scale; // the x position of the noise field swings @ 17 bpm
unsigned real_y = (i + shift_y) * scale; // the y position becomes slowly incremented
uint32_t real_z = SEGENV.step; // the z position becomes quickly incremented
unsigned noise = inoise16(real_x, real_y, real_z) >> 8; // get the noise data and scale it down
unsigned index = sin8_t(noise * 3); // map LED color based on noise data
unsigned index = sin8(noise * 3); // map LED color based on noise data
SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(index, false, PALETTE_SOLID_WRAP, 0));
}
@@ -2213,7 +2220,7 @@ uint16_t mode_noise16_2() {
unsigned shift_x = SEGENV.step >> 6; // x as a function of time
uint32_t real_x = (i + shift_x) * scale; // calculate the coordinates within the noise field
unsigned noise = inoise16(real_x, 0, 4223) >> 8; // get the noise data and scale it down
unsigned index = sin8_t(noise * 3); // map led color based on noise data
unsigned index = sin8(noise * 3); // map led color based on noise data
SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(index, false, PALETTE_SOLID_WRAP, 0, noise));
}
@@ -2234,7 +2241,7 @@ uint16_t mode_noise16_3() {
uint32_t real_y = (i + shift_y) * scale; // based on the precalculated positions
uint32_t real_z = SEGENV.step*8;
unsigned noise = inoise16(real_x, real_y, real_z) >> 8; // get the noise data and scale it down
unsigned index = sin8_t(noise * 3); // map led color based on noise data
unsigned index = sin8(noise * 3); // map led color based on noise data
SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(index, false, PALETTE_SOLID_WRAP, 0, noise));
}
@@ -2314,13 +2321,13 @@ static const char _data_FX_MODE_COLORTWINKLE[] PROGMEM = "Colortwinkles@Fade spe
//Calm effect, like a lake at night
uint16_t mode_lake() {
unsigned sp = SEGMENT.speed/10;
int wave1 = beatsin8_t(sp +2, -64,64);
int wave2 = beatsin8_t(sp +1, -64,64);
int wave3 = beatsin8_t(sp +2, 0,80);
int wave1 = beatsin8(sp +2, -64,64);
int wave2 = beatsin8(sp +1, -64,64);
int wave3 = beatsin8(sp +2, 0,80);
for (int i = 0; i < SEGLEN; i++)
{
int index = cos8_t((i*15)+ wave1)/2 + cubicwave8((i*23)+ wave2)/2;
int index = cos8((i*15)+ wave1)/2 + cubicwave8((i*23)+ wave2)/2;
uint8_t lum = (index > wave3) ? index - wave3 : 0;
SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(index, false, false, 0, lum));
}
@@ -2493,7 +2500,7 @@ static uint16_t ripple_base() {
propI /= 2;
unsigned cx = rippleorigin >> 8;
unsigned cy = rippleorigin & 0xFF;
unsigned mag = scale8(sin8_t((propF>>2)), amp);
unsigned mag = scale8(sin8((propF>>2)), amp);
if (propI > 0) SEGMENT.drawCircle(cx, cy, propI, color_blend(SEGMENT.getPixelColorXY(cx + propI, cy), col, mag), true);
} else
#endif
@@ -2558,11 +2565,11 @@ static CRGB twinklefox_one_twinkle(uint32_t ms, uint8_t salt, bool cat)
{
// Overall twinkle speed (changed)
unsigned ticks = ms / SEGENV.aux0;
unsigned fastcycle8 = uint8_t(ticks);
uint16_t slowcycle16 = (ticks >> 8) + salt;
slowcycle16 += sin8_t(slowcycle16);
unsigned fastcycle8 = ticks;
unsigned slowcycle16 = (ticks >> 8) + salt;
slowcycle16 += sin8(slowcycle16);
slowcycle16 = (slowcycle16 * 2053) + 1384;
uint8_t slowcycle8 = (slowcycle16 & 0xFF) + (slowcycle16 >> 8);
unsigned slowcycle8 = (slowcycle16 & 0xFF) + (slowcycle16 >> 8);
// Overall twinkle density.
// 0 (NONE lit) to 8 (ALL lit at once).
@@ -3131,7 +3138,7 @@ static const char _data_FX_MODE_ROLLINGBALLS[] PROGMEM = "Rolling Balls@!,# of b
static uint16_t sinelon_base(bool dual, bool rainbow=false) {
if (SEGLEN == 1) return mode_static();
SEGMENT.fade_out(SEGMENT.intensity);
unsigned pos = beatsin16_t(SEGMENT.speed/10,0,SEGLEN-1);
unsigned pos = beatsin16(SEGMENT.speed/10,0,SEGLEN-1);
if (SEGENV.call == 0) SEGENV.aux0 = pos;
uint32_t color1 = SEGMENT.color_from_palette(pos, true, false, 0);
uint32_t color2 = SEGCOLOR(2);
@@ -3540,7 +3547,7 @@ uint16_t mode_exploding_fireworks(void)
if (segs <= (strip.getMaxSegments() /4)) maxData *= 2; //ESP8266: 1024 if <= 4 segs ESP32: 2560 if <= 8 segs
int maxSparks = maxData / sizeof(spark); //ESP8266: max. 21/42/85 sparks/seg, ESP32: max. 53/106/213 sparks/seg
unsigned numSparks = min(5 + ((rows*cols) >> 1), maxSparks);
unsigned numSparks = min(2 + ((rows*cols) >> 1), maxSparks);
unsigned dataSize = sizeof(spark) * numSparks;
if (!SEGENV.allocateData(dataSize + sizeof(float))) return mode_static(); //allocation failed
float *dying_gravity = reinterpret_cast<float*>(SEGENV.data + dataSize);
@@ -3595,8 +3602,7 @@ uint16_t mode_exploding_fireworks(void)
* Size is proportional to the height.
*/
unsigned nSparks = flare->pos + random8(4);
nSparks = std::max(nSparks, 4U); // This is not a standard constrain; numSparks is not guaranteed to be at least 4
nSparks = std::min(nSparks, numSparks);
nSparks = constrain(nSparks, 4, numSparks);
// initialize sparks
if (SEGENV.aux0 == 2) {
@@ -3848,13 +3854,13 @@ uint16_t mode_plasma(void) {
if (SEGENV.call == 0) {
SEGENV.aux0 = random8(0,2); // add a bit of randomness
}
unsigned thisPhase = beatsin8_t(6+SEGENV.aux0,-64,64);
unsigned thatPhase = beatsin8_t(7+SEGENV.aux0,-64,64);
unsigned thisPhase = beatsin8(6+SEGENV.aux0,-64,64);
unsigned thatPhase = beatsin8(7+SEGENV.aux0,-64,64);
for (unsigned i = 0; i < SEGLEN; i++) { // For each of the LED's in the strand, set color & brightness based on a wave as follows:
unsigned colorIndex = cubicwave8((i*(2+ 3*(SEGMENT.speed >> 5))+thisPhase) & 0xFF)/2 // factor=23 // Create a wave and add a phase change and add another wave with its own phase change.
+ cos8_t((i*(1+ 2*(SEGMENT.speed >> 5))+thatPhase) & 0xFF)/2; // factor=15 // Hey, you can even change the frequencies if you wish.
unsigned thisBright = qsub8(colorIndex, beatsin8_t(7,0, (128 - (SEGMENT.intensity>>1))));
+ cos8((i*(1+ 2*(SEGMENT.speed >> 5))+thatPhase) & 0xFF)/2; // factor=15 // Hey, you can even change the frequencies if you wish.
unsigned thisBright = qsub8(colorIndex, beatsin8(7,0, (128 - (SEGMENT.intensity>>1))));
SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(colorIndex, false, PALETTE_SOLID_WRAP, 0, thisBright));
}
@@ -3913,7 +3919,7 @@ uint16_t mode_percent(void) {
return FRAMETIME;
}
static const char _data_FX_MODE_PERCENT[] PROGMEM = "Percent@!,% of fill,,,,One color;!,!;!";
static const char _data_FX_MODE_PERCENT[] PROGMEM = "Percent@,% of fill,,,,One color;!,!;!";
/*
@@ -3981,10 +3987,10 @@ static CRGB pacifica_one_layer(uint16_t i, CRGBPalette16& p, uint16_t cistart, u
unsigned wavescale_half = (wavescale >> 1) + 20;
waveangle += ((120 + SEGMENT.intensity) * i); //original 250 * i
unsigned s16 = sin16_t(waveangle) + 32768;
unsigned s16 = sin16(waveangle) + 32768;
unsigned cs = scale16(s16, wavescale_half) + wavescale_half;
ci += (cs * i);
unsigned sindex16 = sin16_t(ci) + 32768;
unsigned sindex16 = sin16(ci) + 32768;
unsigned sindex8 = scale16(sindex16, 240);
return ColorFromPalette(p, sindex8, bri, LINEARBLEND);
}
@@ -4016,34 +4022,34 @@ uint16_t mode_pacifica()
uint64_t deltat = (strip.now >> 2) + ((strip.now * SEGMENT.speed) >> 7);
strip.now = deltat;
unsigned speedfactor1 = beatsin16_t(3, 179, 269);
unsigned speedfactor2 = beatsin16_t(4, 179, 269);
unsigned speedfactor1 = beatsin16(3, 179, 269);
unsigned speedfactor2 = beatsin16(4, 179, 269);
uint32_t deltams1 = (deltams * speedfactor1) / 256;
uint32_t deltams2 = (deltams * speedfactor2) / 256;
uint32_t deltams21 = (deltams1 + deltams2) / 2;
sCIStart1 += (deltams1 * beatsin88_t(1011,10,13));
sCIStart2 -= (deltams21 * beatsin88_t(777,8,11));
sCIStart3 -= (deltams1 * beatsin88_t(501,5,7));
sCIStart4 -= (deltams2 * beatsin88_t(257,4,6));
sCIStart1 += (deltams1 * beatsin88(1011,10,13));
sCIStart2 -= (deltams21 * beatsin88(777,8,11));
sCIStart3 -= (deltams1 * beatsin88(501,5,7));
sCIStart4 -= (deltams2 * beatsin88(257,4,6));
SEGENV.aux0 = sCIStart1; SEGENV.aux1 = sCIStart2;
SEGENV.step = (sCIStart4 << 16) | (sCIStart3 & 0xFFFF);
// Clear out the LED array to a dim background blue-green
//SEGMENT.fill(132618);
unsigned basethreshold = beatsin8_t( 9, 55, 65);
unsigned basethreshold = beatsin8( 9, 55, 65);
unsigned wave = beat8( 7 );
for (int i = 0; i < SEGLEN; i++) {
CRGB c = CRGB(2, 6, 10);
// Render each of four layers, with different scales and speeds, that vary over time
c += pacifica_one_layer(i, pacifica_palette_1, sCIStart1, beatsin16_t(3, 11 * 256, 14 * 256), beatsin8_t(10, 70, 130), 0-beat16(301));
c += pacifica_one_layer(i, pacifica_palette_2, sCIStart2, beatsin16_t(4, 6 * 256, 9 * 256), beatsin8_t(17, 40, 80), beat16(401));
c += pacifica_one_layer(i, pacifica_palette_3, sCIStart3, 6 * 256 , beatsin8_t(9, 10,38) , 0-beat16(503));
c += pacifica_one_layer(i, pacifica_palette_3, sCIStart4, 5 * 256 , beatsin8_t(8, 10,28) , beat16(601));
c += pacifica_one_layer(i, pacifica_palette_1, sCIStart1, beatsin16(3, 11 * 256, 14 * 256), beatsin8(10, 70, 130), 0-beat16(301));
c += pacifica_one_layer(i, pacifica_palette_2, sCIStart2, beatsin16(4, 6 * 256, 9 * 256), beatsin8(17, 40, 80), beat16(401));
c += pacifica_one_layer(i, pacifica_palette_3, sCIStart3, 6 * 256 , beatsin8(9, 10,38) , 0-beat16(503));
c += pacifica_one_layer(i, pacifica_palette_3, sCIStart4, 5 * 256 , beatsin8(8, 10,28) , beat16(601));
// Add extra 'white' to areas where the four layers of light have lined up brightly
unsigned threshold = scale8( sin8_t( wave), 20) + basethreshold;
unsigned threshold = scale8( sin8( wave), 20) + basethreshold;
wave += 7;
unsigned l = c.getAverageLight();
if (l > threshold) {
@@ -4167,7 +4173,7 @@ uint16_t mode_twinkleup(void) { // A very short twinkle routine
for (int i = 0; i < SEGLEN; i++) {
unsigned ranstart = random8(); // The starting value (aka brightness) for each pixel. Must be consistent each time through the loop for this to work.
unsigned pixBri = sin8_t(ranstart + 16 * strip.now/(256-SEGMENT.speed));
unsigned pixBri = sin8(ranstart + 16 * strip.now/(256-SEGMENT.speed));
if (random8() > SEGMENT.intensity) pixBri = 0;
SEGMENT.setPixelColor(i, color_blend(SEGCOLOR(1), SEGMENT.color_from_palette(random8()+strip.now/100, false, PALETTE_SOLID_WRAP, 0), pixBri));
}
@@ -4210,7 +4216,7 @@ uint16_t mode_noisepal(void) { // Slow noise
SEGMENT.setPixelColor(i, color.red, color.green, color.blue);
}
SEGENV.aux0 += beatsin8_t(10,1,4); // Moving along the distance. Vary it a bit with a sine wave.
SEGENV.aux0 += beatsin8(10,1,4); // Moving along the distance. Vary it a bit with a sine wave.
return FRAMETIME;
}
@@ -4292,7 +4298,7 @@ uint16_t mode_chunchun(void)
for (unsigned i = 0; i < numBirds; i++)
{
counter -= span;
unsigned megumin = sin16_t(counter) + 0x8000;
unsigned megumin = sin16(counter) + 0x8000;
unsigned bird = uint32_t(megumin * SEGLEN) >> 16;
uint32_t c = SEGMENT.color_from_palette((i * 255)/ numBirds, false, false, 0); // no palette wrapping
bird = constrain(bird, 0U, SEGLEN-1U);
@@ -4461,7 +4467,7 @@ uint16_t mode_washing_machine(void) {
SEGENV.step += (speed * 2048) / (512 - SEGMENT.speed);
for (int i = 0; i < SEGLEN; i++) {
uint8_t col = sin8_t(((SEGMENT.intensity / 25 + 1) * 255 * i / SEGLEN) + (SEGENV.step >> 7));
uint8_t col = sin8(((SEGMENT.intensity / 25 + 1) * 255 * i / SEGLEN) + (SEGENV.step >> 7));
SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(col, false, PALETTE_SOLID_WRAP, 3));
}
@@ -4624,7 +4630,7 @@ uint16_t mode_tv_simulator(void) {
return FRAMETIME;
}
static const char _data_FX_MODE_TV_SIMULATOR[] PROGMEM = "TV Simulator@!,!;;!;01";
static const char _data_FX_MODE_TV_SIMULATOR[] PROGMEM = "TV Simulator@!,!;;";
/*
@@ -4817,8 +4823,8 @@ static const char _data_FX_MODE_PERLINMOVE[] PROGMEM = "Perlin Move@!,# of pixel
uint16_t mode_wavesins(void) {
for (int i = 0; i < SEGLEN; i++) {
uint8_t bri = sin8_t(strip.now/4 + i * SEGMENT.intensity);
uint8_t index = beatsin8_t(SEGMENT.speed, SEGMENT.custom1, SEGMENT.custom1+SEGMENT.custom2, 0, i * (SEGMENT.custom3<<3)); // custom3 is reduced resolution slider
uint8_t bri = sin8(strip.now/4 + i * SEGMENT.intensity);
uint8_t index = beatsin8(SEGMENT.speed, SEGMENT.custom1, SEGMENT.custom1+SEGMENT.custom2, 0, i * (SEGMENT.custom3<<3)); // custom3 is reduced resolution slider
//SEGMENT.setPixelColor(i, ColorFromPalette(SEGPALETTE, index, bri, LINEARBLEND));
SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(index, false, PALETTE_SOLID_WRAP, 0, bri));
}
@@ -4840,9 +4846,9 @@ uint16_t mode_FlowStripe(void) {
for (int i = 0; i < SEGLEN; i++) {
int c = (abs(i - hl) / hl) * 127;
c = sin8_t(c);
c = sin8_t(c / 2 + t);
byte b = sin8_t(c + t/8);
c = sin8(c);
c = sin8(c / 2 + t);
byte b = sin8(c + t/8);
SEGMENT.setPixelColor(i, CHSV(b + hue, 255, 255));
}
@@ -4869,14 +4875,14 @@ uint16_t mode_2DBlackHole(void) { // By: Stepko https://editor.soulma
unsigned long t = strip.now/128; // timebase
// outer stars
for (size_t i = 0; i < 8; i++) {
x = beatsin8_t(SEGMENT.custom1>>3, 0, cols - 1, 0, ((i % 2) ? 128 : 0) + t * i);
y = beatsin8_t(SEGMENT.intensity>>3, 0, rows - 1, 0, ((i % 2) ? 192 : 64) + t * i);
x = beatsin8(SEGMENT.custom1>>3, 0, cols - 1, 0, ((i % 2) ? 128 : 0) + t * i);
y = beatsin8(SEGMENT.intensity>>3, 0, rows - 1, 0, ((i % 2) ? 192 : 64) + t * i);
SEGMENT.addPixelColorXY(x, y, SEGMENT.color_from_palette(i*32, false, PALETTE_SOLID_WRAP, SEGMENT.check1?0:255));
}
// inner stars
for (size_t i = 0; i < 4; i++) {
x = beatsin8_t(SEGMENT.custom2>>3, cols/4, cols - 1 - cols/4, 0, ((i % 2) ? 128 : 0) + t * i);
y = beatsin8_t(SEGMENT.custom3 , rows/4, rows - 1 - rows/4, 0, ((i % 2) ? 192 : 64) + t * i);
x = beatsin8(SEGMENT.custom2>>3, cols/4, cols - 1 - cols/4, 0, ((i % 2) ? 128 : 0) + t * i);
y = beatsin8(SEGMENT.custom3 , rows/4, rows - 1 - rows/4, 0, ((i % 2) ? 192 : 64) + t * i);
SEGMENT.addPixelColorXY(x, y, SEGMENT.color_from_palette(255-i*64, false, PALETTE_SOLID_WRAP, SEGMENT.check1?0:255));
}
// central white dot
@@ -4910,10 +4916,10 @@ uint16_t mode_2DColoredBursts() { // By: ldirko https://editor.so
SEGENV.aux0++; // hue
SEGMENT.fadeToBlackBy(40);
for (size_t i = 0; i < numLines; i++) {
byte x1 = beatsin8_t(2 + SEGMENT.speed/16, 0, (cols - 1));
byte x2 = beatsin8_t(1 + SEGMENT.speed/16, 0, (rows - 1));
byte y1 = beatsin8_t(5 + SEGMENT.speed/16, 0, (cols - 1), 0, i * 24);
byte y2 = beatsin8_t(3 + SEGMENT.speed/16, 0, (rows - 1), 0, i * 48 + 64);
byte x1 = beatsin8(2 + SEGMENT.speed/16, 0, (cols - 1));
byte x2 = beatsin8(1 + SEGMENT.speed/16, 0, (rows - 1));
byte y1 = beatsin8(5 + SEGMENT.speed/16, 0, (cols - 1), 0, i * 24);
byte y2 = beatsin8(3 + SEGMENT.speed/16, 0, (rows - 1), 0, i * 48 + 64);
CRGB color = ColorFromPalette(SEGPALETTE, i * 255 / numLines + (SEGENV.aux0&0xFF), 255, LINEARBLEND);
byte xsteps = abs8(x1 - y1) + 1;
@@ -4952,8 +4958,8 @@ uint16_t mode_2Ddna(void) { // dna originally by by ldirko at https://pa
SEGMENT.fadeToBlackBy(64);
for (int i = 0; i < cols; i++) {
SEGMENT.setPixelColorXY(i, beatsin8_t(SEGMENT.speed/8, 0, rows-1, 0, i*4 ), ColorFromPalette(SEGPALETTE, i*5+strip.now/17, beatsin8_t(5, 55, 255, 0, i*10), LINEARBLEND));
SEGMENT.setPixelColorXY(i, beatsin8_t(SEGMENT.speed/8, 0, rows-1, 0, i*4+128), ColorFromPalette(SEGPALETTE, i*5+128+strip.now/17, beatsin8_t(5, 55, 255, 0, i*10+128), LINEARBLEND));
SEGMENT.setPixelColorXY(i, beatsin8(SEGMENT.speed/8, 0, rows-1, 0, i*4 ), ColorFromPalette(SEGPALETTE, i*5+strip.now/17, beatsin8(5, 55, 255, 0, i*10), LINEARBLEND));
SEGMENT.setPixelColorXY(i, beatsin8(SEGMENT.speed/8, 0, rows-1, 0, i*4+128), ColorFromPalette(SEGPALETTE, i*5+128+strip.now/17, beatsin8(5, 55, 255, 0, i*10+128), LINEARBLEND));
}
SEGMENT.blur(SEGMENT.intensity>>3);
@@ -4982,8 +4988,8 @@ uint16_t mode_2DDNASpiral() { // By: ldirko https://editor.soulma
SEGMENT.fadeToBlackBy(135);
for (int i = 0; i < rows; i++) {
int x = beatsin8_t(speeds, 0, cols - 1, 0, i * freq) + beatsin8_t(speeds - 7, 0, cols - 1, 0, i * freq + 128);
int x1 = beatsin8_t(speeds, 0, cols - 1, 0, 128 + i * freq) + beatsin8_t(speeds - 7, 0, cols - 1, 0, 128 + 64 + i * freq);
int x = beatsin8(speeds, 0, cols - 1, 0, i * freq) + beatsin8(speeds - 7, 0, cols - 1, 0, i * freq + 128);
int x1 = beatsin8(speeds, 0, cols - 1, 0, 128 + i * freq) + beatsin8(speeds - 7, 0, cols - 1, 0, 128 + 64 + i * freq);
unsigned hue = (i * 128 / rows) + ms;
// skip every 4th row every now and then (fade it more)
if ((i + ms / 8) & 3) {
@@ -5084,9 +5090,9 @@ uint16_t mode_2DFrizzles(void) { // By: Stepko https://editor.so
SEGMENT.fadeToBlackBy(16);
for (size_t i = 8; i > 0; i--) {
SEGMENT.addPixelColorXY(beatsin8_t(SEGMENT.speed/8 + i, 0, cols - 1),
beatsin8_t(SEGMENT.intensity/8 - i, 0, rows - 1),
ColorFromPalette(SEGPALETTE, beatsin8_t(12, 0, 255), 255, LINEARBLEND));
SEGMENT.addPixelColorXY(beatsin8(SEGMENT.speed/8 + i, 0, cols - 1),
beatsin8(SEGMENT.intensity/8 - i, 0, rows - 1),
ColorFromPalette(SEGPALETTE, beatsin8(12, 0, 255), 255, LINEARBLEND));
}
SEGMENT.blur(SEGMENT.custom1>>3);
@@ -5163,7 +5169,7 @@ uint16_t mode_2Dgameoflife(void) { // Written by Ewoud Wijma, inspired by https:
neighbors++;
bool colorFound = false;
int k;
for (k=0; k<9 && colorsCount[k].count != 0; k++)
for (k=0; k<9 && colorsCount[i].count != 0; k++)
if (colorsCount[k].color == prevLeds[xy]) {
colorsCount[k].count++;
colorFound = true;
@@ -5218,7 +5224,7 @@ uint16_t mode_2DHiphotic() { // By: ldirko https://edit
for (int x = 0; x < cols; x++) {
for (int y = 0; y < rows; y++) {
SEGMENT.setPixelColorXY(x, y, SEGMENT.color_from_palette(sin8_t(cos8_t(x * SEGMENT.speed/16 + a / 3) + sin8_t(y * SEGMENT.intensity/16 + a / 4) + a), false, PALETTE_SOLID_WRAP, 0));
SEGMENT.setPixelColorXY(x, y, SEGMENT.color_from_palette(sin8(cos8(x * SEGMENT.speed/16 + a / 3) + sin8(y * SEGMENT.intensity/16 + a / 4) + a), false, PALETTE_SOLID_WRAP, 0));
}
}
@@ -5294,8 +5300,8 @@ uint16_t mode_2DJulia(void) { // An animated Julia set
reAl = -0.94299f; // PixelBlaze example
imAg = 0.3162f;
reAl += (float)sin16_t(strip.now * 34) / 655340.f;
imAg += (float)sin16_t(strip.now * 26) / 655340.f;
reAl += sin_t((float)strip.now/305.f)/20.f;
imAg += sin_t((float)strip.now/405.f)/20.f;
dx = (xmax - xmin) / (cols); // Scale the delta x and y values to our matrix size.
dy = (ymax - ymin) / (rows);
@@ -5358,10 +5364,10 @@ uint16_t mode_2DLissajous(void) { // By: Andrew Tuline
//for (int i=0; i < 4*(cols+rows); i ++) {
for (int i=0; i < 256; i ++) {
//float xlocn = float(sin8_t(now/4+i*(SEGMENT.speed>>5))) / 255.0f;
//float ylocn = float(cos8_t(now/4+i*2)) / 255.0f;
uint_fast8_t xlocn = sin8_t(phase/2 + (i*SEGMENT.speed)/32);
uint_fast8_t ylocn = cos8_t(phase/2 + i*2);
//float xlocn = float(sin8(now/4+i*(SEGMENT.speed>>5))) / 255.0f;
//float ylocn = float(cos8(now/4+i*2)) / 255.0f;
uint_fast8_t xlocn = sin8(phase/2 + (i*SEGMENT.speed)/32);
uint_fast8_t ylocn = cos8(phase/2 + i*2);
xlocn = (cols < 2) ? 1 : (map(2*xlocn, 0,511, 0,2*(cols-1)) +1) /2; // softhack007: "(2* ..... +1) /2" for proper rounding
ylocn = (rows < 2) ? 1 : (map(2*ylocn, 0,511, 0,2*(rows-1)) +1) /2; // "rows > 1" is needed to avoid div/0 in map()
SEGMENT.setPixelColorXY((uint8_t)xlocn, (uint8_t)ylocn, SEGMENT.color_from_palette(strip.now/100+i, false, PALETTE_SOLID_WRAP, 0));
@@ -5461,8 +5467,8 @@ uint16_t mode_2Dmetaballs(void) { // Metaballs by Stefan Petrick. Cannot have
int y3 = map(inoise8(strip.now * speed, 25355, 22685), 0, 255, 0, rows-1);
// and one Lissajou function
int x1 = beatsin8_t(23 * speed, 0, cols-1);
int y1 = beatsin8_t(28 * speed, 0, rows-1);
int x1 = beatsin8(23 * speed, 0, cols-1);
int y1 = beatsin8(28 * speed, 0, rows-1);
for (int y = 0; y < rows; y++) {
for (int x = 0; x < cols; x++) {
@@ -5627,7 +5633,7 @@ uint16_t mode_2DPulser(void) { // By: ldirko https://edi
SEGMENT.fadeToBlackBy(8 - (SEGMENT.intensity>>5));
uint32_t a = strip.now / (18 - SEGMENT.speed / 16);
int x = (a / 14) % cols;
int y = map((sin8_t(a * 5) + sin8_t(a * 4) + sin8_t(a * 2)), 0, 765, rows-1, 0);
int y = map((sin8(a * 5) + sin8(a * 4) + sin8(a * 2)), 0, 765, rows-1, 0);
SEGMENT.setPixelColorXY(x, y, ColorFromPalette(SEGPALETTE, map(y, 0, rows-1, 0, 255), 255, LINEARBLEND));
SEGMENT.blur(SEGMENT.intensity>>4);
@@ -5653,10 +5659,10 @@ uint16_t mode_2DSindots(void) { // By: ldirko http
SEGMENT.fadeToBlackBy(SEGMENT.custom1>>3);
byte t1 = strip.now / (257 - SEGMENT.speed); // 20;
byte t2 = sin8_t(t1) / 4 * 2;
byte t2 = sin8(t1) / 4 * 2;
for (int i = 0; i < 13; i++) {
int x = sin8_t(t1 + i * SEGMENT.intensity/8)*(cols-1)/255; // max index now 255x15/255=15!
int y = sin8_t(t2 + i * SEGMENT.intensity/8)*(rows-1)/255; // max index now 255x15/255=15!
int x = sin8(t1 + i * SEGMENT.intensity/8)*(cols-1)/255; // max index now 255x15/255=15!
int y = sin8(t2 + i * SEGMENT.intensity/8)*(rows-1)/255; // max index now 255x15/255=15!
SEGMENT.setPixelColorXY(x, y, ColorFromPalette(SEGPALETTE, i * 255 / 13, 255, LINEARBLEND));
}
SEGMENT.blur(SEGMENT.custom2>>3);
@@ -5683,12 +5689,12 @@ uint16_t mode_2Dsquaredswirl(void) { // By: Mark Kriegsman. https://g
SEGMENT.blur(SEGMENT.custom3>>1);
// Use two out-of-sync sine waves
int i = beatsin8_t(19, kBorderWidth, cols-kBorderWidth);
int j = beatsin8_t(22, kBorderWidth, cols-kBorderWidth);
int k = beatsin8_t(17, kBorderWidth, cols-kBorderWidth);
int m = beatsin8_t(18, kBorderWidth, rows-kBorderWidth);
int n = beatsin8_t(15, kBorderWidth, rows-kBorderWidth);
int p = beatsin8_t(20, kBorderWidth, rows-kBorderWidth);
int i = beatsin8(19, kBorderWidth, cols-kBorderWidth);
int j = beatsin8(22, kBorderWidth, cols-kBorderWidth);
int k = beatsin8(17, kBorderWidth, cols-kBorderWidth);
int m = beatsin8(18, kBorderWidth, rows-kBorderWidth);
int n = beatsin8(15, kBorderWidth, rows-kBorderWidth);
int p = beatsin8(20, kBorderWidth, rows-kBorderWidth);
SEGMENT.addPixelColorXY(i, m, ColorFromPalette(SEGPALETTE, strip.now/29, 255, LINEARBLEND));
SEGMENT.addPixelColorXY(j, n, ColorFromPalette(SEGPALETTE, strip.now/41, 255, LINEARBLEND));
@@ -5764,19 +5770,19 @@ uint16_t mode_2Dtartan(void) { // By: Elliott Kember https://editor.so
uint8_t hue, bri;
size_t intensity;
int offsetX = beatsin16_t(3, -360, 360);
int offsetY = beatsin16_t(2, -360, 360);
int offsetX = beatsin16(3, -360, 360);
int offsetY = beatsin16(2, -360, 360);
int sharpness = SEGMENT.custom3 / 8; // 0-3
for (int x = 0; x < cols; x++) {
for (int y = 0; y < rows; y++) {
hue = x * beatsin16_t(10, 1, 10) + offsetY;
intensity = bri = sin8_t(x * SEGMENT.speed/2 + offsetX);
hue = x * beatsin16(10, 1, 10) + offsetY;
intensity = bri = sin8(x * SEGMENT.speed/2 + offsetX);
for (int i=0; i<sharpness; i++) intensity *= bri;
intensity >>= 8*sharpness;
SEGMENT.setPixelColorXY(x, y, ColorFromPalette(SEGPALETTE, hue, intensity, LINEARBLEND));
hue = y * 3 + offsetX;
intensity = bri = sin8_t(y * SEGMENT.intensity/2 + offsetY);
intensity = bri = sin8(y * SEGMENT.intensity/2 + offsetY);
for (int i=0; i<sharpness; i++) intensity *= bri;
intensity >>= 8*sharpness;
SEGMENT.addPixelColorXY(x, y, ColorFromPalette(SEGPALETTE, hue, intensity, LINEARBLEND));
@@ -5811,9 +5817,9 @@ uint16_t mode_2Dspaceships(void) { //// Space ships by stepko (c)05.02.21 [ht
SEGMENT.move(SEGENV.aux0, 1);
for (size_t i = 0; i < 8; i++) {
int x = beatsin8_t(12 + i, 2, cols - 3);
int y = beatsin8_t(15 + i, 2, rows - 3);
CRGB color = ColorFromPalette(SEGPALETTE, beatsin8_t(12 + i, 0, 255), 255);
int x = beatsin8(12 + i, 2, cols - 3);
int y = beatsin8(15 + i, 2, rows - 3);
CRGB color = ColorFromPalette(SEGPALETTE, beatsin8(12 + i, 0, 255), 255);
SEGMENT.addPixelColorXY(x, y, color);
if (cols > 24 || rows > 24) {
SEGMENT.addPixelColorXY(x+1, y, color);
@@ -6209,8 +6215,8 @@ uint16_t mode_2Ddriftrose(void) {
SEGMENT.fadeToBlackBy(32+(SEGMENT.speed>>3));
for (size_t i = 1; i < 37; i++) {
float angle = radians(i * 10);
uint32_t x = (CX + (sin_t(angle) * (beatsin8_t(i, 0, L*2)-L))) * 255.f;
uint32_t y = (CY + (cos_t(angle) * (beatsin8_t(i, 0, L*2)-L))) * 255.f;
uint32_t x = (CX + (sin_t(angle) * (beatsin8(i, 0, L*2)-L))) * 255.f;
uint32_t y = (CY + (cos_t(angle) * (beatsin8(i, 0, L*2)-L))) * 255.f;
SEGMENT.wu_pixel(x, y, CHSV(i * 10, 255, 255));
}
SEGMENT.blur(SEGMENT.intensity>>4);
@@ -6259,7 +6265,6 @@ uint16_t mode_2Dplasmarotozoom() {
}
}
*a -= 0.03f + float(SEGENV.speed-128)*0.0002f; // rotation speed
if(*a < -6283.18530718f) *a += 6283.18530718f; // 1000*2*PI, protect sin/cos from very large input float values (will give wrong results)
return FRAMETIME;
}
@@ -6407,8 +6412,8 @@ uint16_t mode_2DSwirl(void) {
SEGMENT.blur(SEGMENT.custom1);
int i = beatsin8_t( 27*SEGMENT.speed/255, borderWidth, cols - borderWidth);
int j = beatsin8_t( 41*SEGMENT.speed/255, borderWidth, rows - borderWidth);
int i = beatsin8( 27*SEGMENT.speed/255, borderWidth, cols - borderWidth);
int j = beatsin8( 41*SEGMENT.speed/255, borderWidth, rows - borderWidth);
int ni = (cols - 1) - i;
int nj = (cols - 1) - j;
@@ -6620,7 +6625,7 @@ uint16_t mode_juggles(void) { // Juggles. By Andrew Tuline.
for (size_t i=0; i<SEGMENT.intensity/32+1U; i++) {
// if SEGLEN equals 1, we will always set color to the first and only pixel, but the effect is still good looking
SEGMENT.setPixelColor(beatsin16_t(SEGMENT.speed/4+i*2,0,SEGLEN-1), color_blend(SEGCOLOR(1), SEGMENT.color_from_palette(strip.now/4+i*2, false, PALETTE_SOLID_WRAP, 0), my_sampleAgc));
SEGMENT.setPixelColor(beatsin16(SEGMENT.speed/4+i*2,0,SEGLEN-1), color_blend(SEGCOLOR(1), SEGMENT.color_from_palette(strip.now/4+i*2, false, PALETTE_SOLID_WRAP, 0), my_sampleAgc));
}
return FRAMETIME;
@@ -6632,16 +6637,14 @@ static const char _data_FX_MODE_JUGGLES[] PROGMEM = "Juggles@!,# of balls;!,!;!;
// * MATRIPIX //
//////////////////////
uint16_t mode_matripix(void) { // Matripix. By Andrew Tuline.
// effect can work on single pixels, we just lose the shifting effect
unsigned dataSize = sizeof(uint32_t) * SEGLEN;
if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed
uint32_t* pixels = reinterpret_cast<uint32_t*>(SEGENV.data);
if (SEGLEN == 1) return mode_static();
// even with 1D effect we have to take logic for 2D segments for allocation as fill_solid() fills whole segment
um_data_t *um_data = getAudioData();
int volumeRaw = *(int16_t*)um_data->u_data[1];
if (SEGENV.call == 0) {
for (unsigned i = 0; i < SEGLEN; i++) pixels[i] = BLACK; // may not be needed as resetIfRequired() clears buffer
SEGMENT.fill(BLACK);
}
uint8_t secondHand = micros()/(256-SEGMENT.speed)/500 % 16;
@@ -6649,14 +6652,8 @@ uint16_t mode_matripix(void) { // Matripix. By Andrew Tuline.
SEGENV.aux0 = secondHand;
int pixBri = volumeRaw * SEGMENT.intensity / 64;
unsigned k = SEGLEN-1;
// loop will not execute if SEGLEN equals 1
for (unsigned i = 0; i < k; i++) {
pixels[i] = pixels[i+1]; // shift left
SEGMENT.setPixelColor(i, pixels[i]);
}
pixels[k] = color_blend(SEGCOLOR(1), SEGMENT.color_from_palette(strip.now, false, PALETTE_SOLID_WRAP, 0), pixBri);
SEGMENT.setPixelColor(k, pixels[k]);
for (int i = 0; i < SEGLEN-1; i++) SEGMENT.setPixelColor(i, SEGMENT.getPixelColor(i+1)); // shift left
SEGMENT.setPixelColor(SEGLEN-1, color_blend(SEGCOLOR(1), SEGMENT.color_from_palette(strip.now, false, PALETTE_SOLID_WRAP, 0), pixBri));
}
return FRAMETIME;
@@ -6688,8 +6685,8 @@ uint16_t mode_midnoise(void) { // Midnoise. By Andrew Tuline.
SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(index, false, PALETTE_SOLID_WRAP, 0));
}
SEGENV.aux0=SEGENV.aux0+beatsin8_t(5,0,10);
SEGENV.aux1=SEGENV.aux1+beatsin8_t(4,0,10);
SEGENV.aux0=SEGENV.aux0+beatsin8(5,0,10);
SEGENV.aux1=SEGENV.aux1+beatsin8(4,0,10);
return FRAMETIME;
} // mode_midnoise()
@@ -6748,8 +6745,8 @@ uint16_t mode_noisemeter(void) { // Noisemeter. By Andrew Tuline.
SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(index, false, PALETTE_SOLID_WRAP, 0));
}
SEGENV.aux0+=beatsin8_t(5,0,10);
SEGENV.aux1+=beatsin8_t(4,0,10);
SEGENV.aux0+=beatsin8(5,0,10);
SEGENV.aux1+=beatsin8(4,0,10);
return FRAMETIME;
} // mode_noisemeter()
@@ -6804,13 +6801,13 @@ uint16_t mode_plasmoid(void) { // Plasmoid. By Andrew Tuline.
SEGMENT.fadeToBlackBy(32);
plasmoip->thisphase += beatsin8_t(6,-4,4); // You can change direction and speed individually.
plasmoip->thatphase += beatsin8_t(7,-4,4); // Two phase values to make a complex pattern. By Andrew Tuline.
plasmoip->thisphase += beatsin8(6,-4,4); // You can change direction and speed individually.
plasmoip->thatphase += beatsin8(7,-4,4); // Two phase values to make a complex pattern. By Andrew Tuline.
for (int i = 0; i < SEGLEN; i++) { // For each of the LED's in the strand, set a brightness based on a wave as follows.
// updated, similar to "plasma" effect - softhack007
uint8_t thisbright = cubicwave8(((i*(1 + (3*SEGMENT.speed/32)))+plasmoip->thisphase) & 0xFF)/2;
thisbright += cos8_t(((i*(97 +(5*SEGMENT.speed/32)))+plasmoip->thatphase) & 0xFF)/2; // Let's munge the brightness a bit and animate it all with the phases.
thisbright += cos8(((i*(97 +(5*SEGMENT.speed/32)))+plasmoip->thatphase) & 0xFF)/2; // Let's munge the brightness a bit and animate it all with the phases.
uint8_t colorIndex=thisbright;
if (volumeSmth * SEGMENT.intensity / 64 < thisbright) {thisbright = 0;}
@@ -7269,7 +7266,7 @@ uint16_t mode_rocktaves(void) { // Rocktaves. Same note from eac
frTemp -= 132.0f; // This should give us a base musical note of C3
frTemp = fabsf(frTemp * 2.1f); // Fudge factors to compress octave range starting at 0 and going to 255;
unsigned i = map(beatsin8_t(8+octCount*4, 0, 255, 0, octCount*8), 0, 255, 0, SEGLEN-1);
unsigned i = map(beatsin8(8+octCount*4, 0, 255, 0, octCount*8), 0, 255, 0, SEGLEN-1);
i = constrain(i, 0U, SEGLEN-1U);
SEGMENT.addPixelColor(i, color_blend(SEGCOLOR(1), SEGMENT.color_from_palette((uint8_t)frTemp, false, PALETTE_SOLID_WRAP, 0), volTemp));
@@ -7284,11 +7281,8 @@ static const char _data_FX_MODE_ROCKTAVES[] PROGMEM = "Rocktaves@;!,!;!;01f;m12=
// Combines peak detection with FFT_MajorPeak and FFT_Magnitude.
uint16_t mode_waterfall(void) { // Waterfall. By: Andrew Tuline
// effect can work on single pixels, we just lose the shifting effect
unsigned dataSize = sizeof(uint32_t) * SEGLEN;
if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed
uint32_t* pixels = reinterpret_cast<uint32_t*>(SEGENV.data);
um_data_t *um_data = getAudioData();
um_data_t *um_data = getAudioData();
uint8_t samplePeak = *(uint8_t*)um_data->u_data[3];
float FFT_MajorPeak = *(float*) um_data->u_data[4];
uint8_t *maxVol = (uint8_t*)um_data->u_data[6];
@@ -7298,7 +7292,7 @@ uint16_t mode_waterfall(void) { // Waterfall. By: Andrew Tulin
if (FFT_MajorPeak < 1) FFT_MajorPeak = 1; // log10(0) is "forbidden" (throws exception)
if (SEGENV.call == 0) {
for (unsigned i = 0; i < SEGLEN; i++) pixels[i] = BLACK; // may not be needed as resetIfRequired() clears buffer
SEGMENT.fill(BLACK);
SEGENV.aux0 = 255;
SEGMENT.custom1 = *binNum;
SEGMENT.custom2 = *maxVol * 2;
@@ -7315,18 +7309,13 @@ uint16_t mode_waterfall(void) { // Waterfall. By: Andrew Tulin
uint8_t pixCol = (log10f(FFT_MajorPeak) - 2.26f) * 150; // 22Khz sampling - log10 frequency range is from 2.26 (182hz) to 3.967 (9260hz). Let's scale accordingly.
if (FFT_MajorPeak < 182.0f) pixCol = 0; // handle underflow
unsigned k = SEGLEN-1;
if (samplePeak) {
pixels[k] = (uint32_t)CRGB(CHSV(92,92,92));
SEGMENT.setPixelColor(SEGLEN-1, CHSV(92,92,92));
} else {
pixels[k] = color_blend(SEGCOLOR(1), SEGMENT.color_from_palette(pixCol+SEGMENT.intensity, false, PALETTE_SOLID_WRAP, 0), (uint8_t)my_magnitude);
SEGMENT.setPixelColor(SEGLEN-1, color_blend(SEGCOLOR(1), SEGMENT.color_from_palette(pixCol+SEGMENT.intensity, false, PALETTE_SOLID_WRAP, 0), (int)my_magnitude));
}
SEGMENT.setPixelColor(k, pixels[k]);
// loop will not execute if SEGLEN equals 1
for (unsigned i = 0; i < k; i++) {
pixels[i] = pixels[i+1]; // shift left
SEGMENT.setPixelColor(i, pixels[i]);
}
for (int i = 0; i < SEGLEN-1; i++) SEGMENT.setPixelColor(i, SEGMENT.getPixelColor(i+1)); // shift left
}
return FRAMETIME;
@@ -7564,12 +7553,12 @@ uint16_t mode_2Ddistortionwaves() {
unsigned a2 = a/2;
unsigned a3 = a/3;
unsigned cx = beatsin8_t(10-speed,0,cols-1)*scale;
unsigned cy = beatsin8_t(12-speed,0,rows-1)*scale;
unsigned cx1 = beatsin8_t(13-speed,0,cols-1)*scale;
unsigned cy1 = beatsin8_t(15-speed,0,rows-1)*scale;
unsigned cx2 = beatsin8_t(17-speed,0,cols-1)*scale;
unsigned cy2 = beatsin8_t(14-speed,0,rows-1)*scale;
unsigned cx = beatsin8(10-speed,0,cols-1)*scale;
unsigned cy = beatsin8(12-speed,0,rows-1)*scale;
unsigned cx1 = beatsin8(13-speed,0,cols-1)*scale;
unsigned cy1 = beatsin8(15-speed,0,rows-1)*scale;
unsigned cx2 = beatsin8(17-speed,0,cols-1)*scale;
unsigned cy2 = beatsin8(14-speed,0,rows-1)*scale;
unsigned xoffs = 0;
for (int x = 0; x < cols; x++) {
@@ -7579,17 +7568,17 @@ uint16_t mode_2Ddistortionwaves() {
for (int y = 0; y < rows; y++) {
yoffs += scale;
byte rdistort = cos8_t((cos8_t(((x<<3)+a )&255)+cos8_t(((y<<3)-a2)&255)+a3 )&255)>>1;
byte gdistort = cos8_t((cos8_t(((x<<3)-a2)&255)+cos8_t(((y<<3)+a3)&255)+a+32 )&255)>>1;
byte bdistort = cos8_t((cos8_t(((x<<3)+a3)&255)+cos8_t(((y<<3)-a) &255)+a2+64)&255)>>1;
byte rdistort = cos8((cos8(((x<<3)+a )&255)+cos8(((y<<3)-a2)&255)+a3 )&255)>>1;
byte gdistort = cos8((cos8(((x<<3)-a2)&255)+cos8(((y<<3)+a3)&255)+a+32 )&255)>>1;
byte bdistort = cos8((cos8(((x<<3)+a3)&255)+cos8(((y<<3)-a) &255)+a2+64)&255)>>1;
byte valueR = rdistort+ w* (a- ( ((xoffs - cx) * (xoffs - cx) + (yoffs - cy) * (yoffs - cy))>>7 ));
byte valueG = gdistort+ w* (a2-( ((xoffs - cx1) * (xoffs - cx1) + (yoffs - cy1) * (yoffs - cy1))>>7 ));
byte valueB = bdistort+ w* (a3-( ((xoffs - cx2) * (xoffs - cx2) + (yoffs - cy2) * (yoffs - cy2))>>7 ));
valueR = gamma8(cos8_t(valueR));
valueG = gamma8(cos8_t(valueG));
valueB = gamma8(cos8_t(valueB));
valueR = gamma8(cos8(valueR));
valueG = gamma8(cos8(valueG));
valueB = gamma8(cos8(valueB));
SEGMENT.setPixelColorXY(x, y, RGBW32(valueR, valueG, valueB, 0));
}
@@ -7746,10 +7735,8 @@ uint16_t mode_2Doctopus() {
const int C_Y = (rows / 2) + ((SEGMENT.custom2 - 128)*rows)/255;
for (int x = 0; x < cols; x++) {
for (int y = 0; y < rows; y++) {
int dx = (x - C_X);
int dy = (y - C_Y);
rMap[XY(x, y)].angle = int(40.7436f * atan2_t(dy, dx)); // avoid 128*atan2()/PI
rMap[XY(x, y)].radius = sqrtf(dx * dx + dy * dy) * mapp; //thanks Sutaburosu
rMap[XY(x, y)].angle = int(40.7436f * atan2f((y - C_Y), (x - C_X))); // avoid 128*atan2()/PI
rMap[XY(x, y)].radius = hypotf((x - C_X), (y - C_Y)) * mapp; //thanks Sutaburosu
}
}
}
@@ -7759,8 +7746,8 @@ uint16_t mode_2Doctopus() {
for (int y = 0; y < rows; y++) {
byte angle = rMap[XY(x,y)].angle;
byte radius = rMap[XY(x,y)].radius;
//CRGB c = CHSV(SEGENV.step / 2 - radius, 255, sin8_t(sin8_t((angle * 4 - radius) / 4 + SEGENV.step) + radius - SEGENV.step * 2 + angle * (SEGMENT.custom3/3+1)));
unsigned intensity = sin8_t(sin8_t((angle * 4 - radius) / 4 + SEGENV.step/2) + radius - SEGENV.step + angle * (SEGMENT.custom3/4+1));
//CRGB c = CHSV(SEGENV.step / 2 - radius, 255, sin8(sin8((angle * 4 - radius) / 4 + SEGENV.step) + radius - SEGENV.step * 2 + angle * (SEGMENT.custom3/3+1)));
unsigned intensity = sin8(sin8((angle * 4 - radius) / 4 + SEGENV.step/2) + radius - SEGENV.step + angle * (SEGMENT.custom3/4+1));
intensity = map((intensity*intensity) & 0xFFFF, 0, 65535, 0, 255); // add a bit of non-linearity for cleaner display
CRGB c = ColorFromPalette(SEGPALETTE, SEGENV.step / 2 - radius, intensity);
SEGMENT.setPixelColorXY(x, y, c);
@@ -7768,7 +7755,7 @@ uint16_t mode_2Doctopus() {
}
return FRAMETIME;
}
static const char _data_FX_MODE_2DOCTOPUS[] PROGMEM = "Octopus@!,,Offset X,Offset Y,Legs,fasttan;;!;2;";
static const char _data_FX_MODE_2DOCTOPUS[] PROGMEM = "Octopus@!,,Offset X,Offset Y,Legs;;!;2;";
//Waving Cell
@@ -7785,7 +7772,7 @@ uint16_t mode_2Dwavingcell() {
uint8_t aY = SEGMENT.custom2/16 + 1;
uint8_t aZ = SEGMENT.custom3 + 1;
for (int x = 0; x < cols; x++) for (int y = 0; y <rows; y++)
SEGMENT.setPixelColorXY(x, y, ColorFromPalette(SEGPALETTE, ((sin8_t((x*aX)+sin8_t((y+t)*aY))+cos8_t(y*aZ))+1)+t));
SEGMENT.setPixelColorXY(x, y, ColorFromPalette(SEGPALETTE, ((sin8((x*aX)+sin8((y+t)*aY))+cos8(y*aZ))+1)+t));
return FRAMETIME;
}
+15 -37
View File
@@ -1,4 +1,3 @@
#pragma once
/*
WS2812FX.h - Library for WS2812 LED effects.
Harm Aldick - 2016
@@ -9,15 +8,12 @@
Adapted from code originally licensed under the MIT license
Modified for WLED
Segment class/struct (c) 2022 Blaz Kristan (@blazoncek)
*/
#ifndef WS2812FX_h
#define WS2812FX_h
#include <vector>
#include "wled.h"
#include "const.h"
@@ -50,42 +46,30 @@
#define WLED_FPS 42
#define FRAMETIME_FIXED (1000/WLED_FPS)
#define FRAMETIME strip.getFrameTime()
#if defined(ARDUINO_ARCH_ESP32) && !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32S2)
#define MIN_FRAME_DELAY 2 // minimum wait between repaints, to keep other functions like WiFi alive
#elif defined(CONFIG_IDF_TARGET_ESP32S2) || defined(CONFIG_IDF_TARGET_ESP32C3)
#define MIN_FRAME_DELAY 3 // S2/C3 are slower than normal esp32, and only have one core
#else
#define MIN_FRAME_DELAY 8 // 8266 legacy MIN_SHOW_DELAY
#endif
#define FPS_UNLIMITED 0
// FPS calculation (can be defined as compile flag for debugging)
#ifndef FPS_CALC_AVG
#define FPS_CALC_AVG 7 // average FPS calculation over this many frames (moving average)
#endif
#ifndef FPS_MULTIPLIER
#define FPS_MULTIPLIER 1 // dev option: multiplier to get sub-frame FPS without floats
#endif
#define FPS_CALC_SHIFT 7 // bit shift for fixed point math
/* each segment uses 82 bytes of SRAM memory, so if you're application fails because of
insufficient memory, decreasing MAX_NUM_SEGMENTS may help */
#ifdef ESP8266
#define MAX_NUM_SEGMENTS 16
#define MAX_NUM_SEGMENTS 16
/* How much data bytes all segments combined may allocate */
#define MAX_SEGMENT_DATA 5120
#elif defined(CONFIG_IDF_TARGET_ESP32S2)
#define MAX_NUM_SEGMENTS 20
#define MAX_SEGMENT_DATA (MAX_NUM_SEGMENTS*512) // 10k by default (S2 is short on free RAM)
#else
#define MAX_NUM_SEGMENTS 32 // warning: going beyond 32 may consume too much RAM for stable operation
#define MAX_SEGMENT_DATA (MAX_NUM_SEGMENTS*1280) // 40k by default
#ifndef MAX_NUM_SEGMENTS
#define MAX_NUM_SEGMENTS 32
#endif
#if defined(ARDUINO_ARCH_ESP32S2)
#define MAX_SEGMENT_DATA MAX_NUM_SEGMENTS*768 // 24k by default (S2 is short on free RAM)
#else
#define MAX_SEGMENT_DATA MAX_NUM_SEGMENTS*1280 // 40k by default
#endif
#endif
/* How much data bytes each segment should max allocate to leave enough space for other segments,
assuming each segment uses the same amount of data. 256 for ESP8266, 640 for ESP32. */
#define FAIR_DATA_PER_SEG (MAX_SEGMENT_DATA / strip.getMaxSegments())
#define MIN_SHOW_DELAY (_frametime < 16 ? 8 : 15)
#define NUM_COLORS 3 /* number of colors per segment */
#define SEGMENT strip._segments[strip.getCurrSegmentId()]
#define SEGENV strip._segments[strip.getCurrSegmentId()]
@@ -531,9 +515,6 @@ typedef struct Segment {
inline uint16_t length() const { return width() * height(); } // segment length (count) in physical pixels
inline uint16_t groupLength() const { return grouping + spacing; }
inline uint8_t getLightCapabilities() const { return _capabilities; }
inline void deactivate() { setGeometry(0,0); }
inline Segment &clearName() { if (name) free(name); name = nullptr; return *this; }
inline Segment &setName(const String &name) { return setName(name.c_str()); }
inline static uint16_t getUsedSegmentData() { return _usedSegmentData; }
inline static void addUsedSegmentData(int len) { _usedSegmentData += len; }
@@ -543,15 +524,14 @@ typedef struct Segment {
static void handleRandomPalette();
inline static const CRGBPalette16 &getCurrentPalette() { return Segment::_currentPalette; }
void setGeometry(uint16_t i1, uint16_t i2, uint8_t grp=1, uint8_t spc=0, uint16_t ofs=UINT16_MAX, uint16_t i1Y=0, uint16_t i2Y=1, uint8_t m12 = 0);
void setUp(uint16_t i1, uint16_t i2, uint8_t grp=1, uint8_t spc=0, uint16_t ofs=UINT16_MAX, uint16_t i1Y=0, uint16_t i2Y=1);
Segment &setColor(uint8_t slot, uint32_t c);
Segment &setCCT(uint16_t k);
Segment &setOpacity(uint8_t o);
Segment &setOption(uint8_t n, bool val);
Segment &setMode(uint8_t fx, bool loadDefaults = false);
Segment &setPalette(uint8_t pal);
Segment &setName(const char* name);
uint8_t differs(const Segment& b) const;
uint8_t differs(Segment& b) const;
void refreshLightCapabilities();
// runtime data functions
@@ -749,7 +729,7 @@ class WS2812FX { // 96 bytes
_transitionDur(750),
_targetFps(WLED_FPS),
_frametime(FRAMETIME_FIXED),
_cumulativeFps(50 << FPS_CALC_SHIFT),
_cumulativeFps(2),
_isServicing(false),
_isOffRefreshRequired(false),
_hasWhiteChannel(false),
@@ -759,7 +739,6 @@ class WS2812FX { // 96 bytes
customMappingTable(nullptr),
customMappingSize(0),
_lastShow(0),
_lastServiceShow(0),
_segment_index(0),
_mainSegment(0)
{
@@ -858,7 +837,7 @@ class WS2812FX { // 96 bytes
getMappedPixelIndex(uint16_t index) const;
inline uint16_t getFrameTime() const { return _frametime; } // returns amount of time a frame should take (in ms)
inline uint16_t getMinShowDelay() const { return MIN_FRAME_DELAY; } // returns minimum amount of time strip.service() can be delayed (constant)
inline uint16_t getMinShowDelay() const { return MIN_SHOW_DELAY; } // returns minimum amount of time strip.service() can be delayed (constant)
inline uint16_t getLength() const { return _length; } // returns actual amount of LEDs on a strip (2D matrix may have less LEDs than W*H)
inline uint16_t getTransition() const { return _transitionDur; } // returns currently set transition time (in ms)
@@ -970,7 +949,6 @@ class WS2812FX { // 96 bytes
uint16_t customMappingSize;
unsigned long _lastShow;
unsigned long _lastServiceShow;
uint8_t _segment_index;
uint8_t _mainSegment;
+66 -117
View File
@@ -438,44 +438,37 @@ void Segment::setCurrentPalette() {
// relies on WS2812FX::service() to call it for each frame
void Segment::handleRandomPalette() {
uint16_t time_ms = millis();
uint16_t time_s = millis()/1000U;
// is it time to generate a new palette?
if ((uint16_t)(time_s - _lastPaletteChange) > randomPaletteChangeTime) {
_newRandomPalette = useHarmonicRandomPalette ? generateHarmonicRandomPalette(_randomPalette) : generateRandomPalette();
_lastPaletteChange = time_s;
_lastPaletteBlend = time_ms - 512; // starts blending immediately
if ((uint16_t)((uint16_t)(millis() / 1000U) - _lastPaletteChange) > randomPaletteChangeTime){
_newRandomPalette = useHarmonicRandomPalette ? generateHarmonicRandomPalette(_randomPalette) : generateRandomPalette();
_lastPaletteChange = (uint16_t)(millis() / 1000U);
_lastPaletteBlend = (uint16_t)((uint16_t)millis() - 512); // starts blending immediately
}
// if palette transitions is enabled, blend it according to Transition Time (if longer than minimum given by service calls)
if (strip.paletteFade) {
// assumes that 128 updates are sufficient to blend a palette, so shift by 7 (can be more, can be less)
// in reality there need to be 255 blends to fully blend two entirely different palettes
if ((uint16_t)(time_ms - _lastPaletteBlend) < strip.getTransition() >> 7) return; // not yet time to fade, delay the update
if ((uint16_t)((uint16_t)millis() - _lastPaletteBlend) < strip.getTransition() >> 7) return; // not yet time to fade, delay the update
_lastPaletteBlend = (uint16_t)millis();
}
nblendPaletteTowardPalette(_randomPalette, _newRandomPalette, 48);
}
// segId is given when called from network callback, changes are queued if that segment is currently in its effect function
void Segment::setGeometry(uint16_t i1, uint16_t i2, uint8_t grp, uint8_t spc, uint16_t ofs, uint16_t i1Y, uint16_t i2Y, uint8_t m12) {
void Segment::setUp(uint16_t i1, uint16_t i2, uint8_t grp, uint8_t spc, uint16_t ofs, uint16_t i1Y, uint16_t i2Y) {
// return if neither bounds nor grouping have changed
bool boundsUnchanged = (start == i1 && stop == i2);
#ifndef WLED_DISABLE_2D
if (Segment::maxHeight>1) boundsUnchanged &= (startY == i1Y && stopY == i2Y); // 2D
#endif
m12 = constrain(m12, 0, 7);
if (stop && (spc > 0 || m12 != map1D2D)) fill(BLACK);
if (m12 != map1D2D) map1D2D = m12;
/*
if (boundsUnchanged
&& (!grp || (grouping == grp && spacing == spc))
&& (m12 == map1D2D)
) return;
*/
&& (ofs == UINT16_MAX || ofs == offset)) return;
stateChanged = true; // send UDP/WS broadcast
if (stop) fill(BLACK); // turn old segment range off (clears pixels if changing spacing)
if (grp) { // prevent assignment of 0
grouping = grp;
spacing = spc;
@@ -485,9 +478,12 @@ void Segment::setGeometry(uint16_t i1, uint16_t i2, uint8_t grp, uint8_t spc, ui
}
if (ofs < UINT16_MAX) offset = ofs;
DEBUG_PRINTF_P(PSTR("Segment geometry: %d,%d -> %d,%d\n"), (int)i1, (int)i2, (int)i1Y, (int)i2Y);
if (boundsUnchanged) return;
DEBUG_PRINT(F("setUp segment: ")); DEBUG_PRINT(i1);
DEBUG_PRINT(','); DEBUG_PRINT(i2);
DEBUG_PRINT(F(" -> ")); DEBUG_PRINT(i1Y);
DEBUG_PRINT(','); DEBUG_PRINTLN(i2Y);
markForReset();
if (boundsUnchanged) return;
// apply change immediately
if (i2 <= i1) { //disable segment
@@ -605,20 +601,6 @@ Segment &Segment::setPalette(uint8_t pal) {
return *this;
}
Segment &Segment::setName(const char *newName) {
if (newName) {
const int newLen = min(strlen(newName), (size_t)WLED_MAX_SEGNAME_LEN);
if (newLen) {
if (name) name = static_cast<char*>(realloc(name, newLen+1));
else name = static_cast<char*>(malloc(newLen+1));
if (name) strlcpy(name, newName, newLen+1);
name[newLen] = 0;
return *this;
}
}
return clearName();
}
// 2D matrix
unsigned IRAM_ATTR Segment::virtualWidth() const {
unsigned groupLen = groupLength();
@@ -969,7 +951,7 @@ uint32_t IRAM_ATTR_YN Segment::getPixelColor(int i) const
return strip.getPixelColor(i);
}
uint8_t Segment::differs(const Segment& b) const {
uint8_t Segment::differs(Segment& b) const {
uint8_t d = 0;
if (start != b.start) d |= SEG_DIFFERS_BOUNDS;
if (stop != b.stop) d |= SEG_DIFFERS_BOUNDS;
@@ -1065,25 +1047,36 @@ void Segment::fade_out(uint8_t rate) {
const int cols = is2D() ? virtualWidth() : virtualLength();
const int rows = virtualHeight(); // will be 1 for 1D
rate = (256-rate) >> 1;
const int mappedRate = 256 / (rate + 1);
rate = (255-rate) >> 1;
float mappedRate = 1.0f / (float(rate) + 1.1f);
uint32_t color = colors[1]; // SEGCOLOR(1); // target color
int w2 = W(color);
int r2 = R(color);
int g2 = G(color);
int b2 = B(color);
for (int y = 0; y < rows; y++) for (int x = 0; x < cols; x++) {
uint32_t color = is2D() ? getPixelColorXY(x, y) : getPixelColor(x);
color = is2D() ? getPixelColorXY(x, y) : getPixelColor(x);
if (color == colors[1]) continue; // already at target color
for (int i = 0; i < 32; i += 8) {
uint8_t c2 = (colors[1]>>i); // get background channel
uint8_t c1 = (color>>i); // get foreground channel
// we can't use bitshift since we are using int
int delta = (c2 - c1) * mappedRate / 256;
// if fade isn't complete, make sure delta is at least 1 (fixes rounding issues)
if (delta == 0) delta += (c2 == c1) ? 0 : (c2 > c1) ? 1 : -1;
// stuff new value back into color
color &= ~(0xFF<<i);
color |= ((c1 + delta) & 0xFF) << i;
}
if (is2D()) setPixelColorXY(x, y, color);
else setPixelColor(x, color);
int w1 = W(color);
int r1 = R(color);
int g1 = G(color);
int b1 = B(color);
int wdelta = (w2 - w1) * mappedRate;
int rdelta = (r2 - r1) * mappedRate;
int gdelta = (g2 - g1) * mappedRate;
int bdelta = (b2 - b1) * mappedRate;
// if fade isn't complete, make sure delta is at least 1 (fixes rounding issues)
wdelta += (w2 == w1) ? 0 : (w2 > w1) ? 1 : -1;
rdelta += (r2 == r1) ? 0 : (r2 > r1) ? 1 : -1;
gdelta += (g2 == g1) ? 0 : (g2 > g1) ? 1 : -1;
bdelta += (b2 == b1) ? 0 : (b2 > b1) ? 1 : -1;
if (is2D()) setPixelColorXY(x, y, r1 + rdelta, g1 + gdelta, b1 + bdelta, w1 + wdelta);
else setPixelColor(x, r1 + rdelta, g1 + gdelta, b1 + bdelta, w1 + wdelta);
}
}
@@ -1199,40 +1192,6 @@ void WS2812FX::finalizeInit() {
_hasWhiteChannel = _isOffRefreshRequired = false;
unsigned digitalCount = 0;
#if defined(ARDUINO_ARCH_ESP32) && !defined(CONFIG_IDF_TARGET_ESP32C3)
// determine if it is sensible to use parallel I2S outputs on ESP32 (i.e. more than 5 outputs = 1 I2S + 4 RMT)
unsigned maxLedsOnBus = 0;
unsigned busType = 0;
for (const auto &bus : busConfigs) {
if (Bus::isDigital(bus.type) && !Bus::is2Pin(bus.type)) {
digitalCount++;
if (busType == 0) busType = bus.type; // remember first bus type
if (busType != bus.type) {
DEBUG_PRINTF_P(PSTR("Mixed digital bus types detected! Forcing single I2S output.\n"));
useParallelI2S = false; // mixed bus types, no parallel I2S
}
if (bus.count > maxLedsOnBus) maxLedsOnBus = bus.count;
}
}
DEBUG_PRINTF_P(PSTR("Maximum LEDs on a bus: %u\nDigital buses: %u\n"), maxLedsOnBus, digitalCount);
// we may remove 600 LEDs per bus limit when NeoPixelBus is updated beyond 2.8.3
if (digitalCount > 1 && maxLedsOnBus <= 600 && useParallelI2S) BusManager::useParallelOutput(); // must call before creating buses
else useParallelI2S = false; // enforce single I2S
digitalCount = 0;
#endif
// create buses/outputs
unsigned mem = 0;
for (const auto &bus : busConfigs) {
mem += bus.memUsage(Bus::isDigital(bus.type) && !Bus::is2Pin(bus.type) ? digitalCount++ : 0); // includes global buffer
if (mem <= MAX_LED_MEMORY) {
if (BusManager::add(bus) == -1) break;
} else DEBUG_PRINTF_P(PSTR("Out of LED memory! Bus %d (%d) #%u not created."), (int)bus.type, (int)bus.count, digitalCount);
}
busConfigs.clear();
busConfigs.shrink_to_fit();
//if busses failed to load, add default (fresh install, FS issue, ...)
if (BusManager::getNumBusses() == 0) {
DEBUG_PRINTLN(F("No busses, init default"));
@@ -1248,7 +1207,6 @@ void WS2812FX::finalizeInit() {
unsigned prevLen = 0;
unsigned pinsIndex = 0;
digitalCount = 0;
for (unsigned i = 0; i < WLED_MAX_BUSSES+WLED_MIN_VIRTUAL_BUSSES; i++) {
uint8_t defPin[OUTPUT_MAX_PINS];
// if we have less types than requested outputs and they do not align, use last known type to set current type
@@ -1313,11 +1271,9 @@ void WS2812FX::finalizeInit() {
if (Bus::isPWM(dataType) || Bus::isOnOff(dataType)) count = 1;
prevLen += count;
BusConfig defCfg = BusConfig(dataType, defPin, start, count, DEFAULT_LED_COLOR_ORDER, false, 0, RGBW_MODE_MANUAL_ONLY, 0, useGlobalLedBuffer);
mem += defCfg.memUsage(Bus::isDigital(dataType) && !Bus::is2Pin(dataType) ? digitalCount++ : 0);
if (BusManager::add(defCfg) == -1) break;
}
}
DEBUG_PRINTF_P(PSTR("LED buffer size: %uB/%uB\n"), mem, BusManager::memUsage());
_length = 0;
for (int i=0; i<BusManager::getNumBusses(); i++) {
@@ -1330,11 +1286,15 @@ void WS2812FX::finalizeInit() {
_isOffRefreshRequired |= bus->isOffRefreshRequired() && !bus->isPWM(); // use refresh bit for phase shift with analog
unsigned busEnd = bus->getStart() + bus->getLength();
if (busEnd > _length) _length = busEnd;
// This must be done after all buses have been created, as some kinds (parallel I2S) interact
bus->begin();
#ifdef ESP8266
// why do we need to reinitialise GPIO3???
//if (!bus->isDigital() || bus->is2Pin()) continue;
//uint8_t pins[5];
//if (!bus->getPins(pins)) continue;
//BusDigital* bd = static_cast<BusDigital*>(bus);
//if (pins[0] == 3) bd->reinit();
#endif
}
DEBUG_PRINTF_P(PSTR("Heap after buses: %d\n"), ESP.getFreeHeap());
Segment::maxWidth = _length;
Segment::maxHeight = 1;
@@ -1349,21 +1309,14 @@ void WS2812FX::finalizeInit() {
void WS2812FX::service() {
unsigned long nowUp = millis(); // Be aware, millis() rolls over every 49 days
now = nowUp + timebase;
if (_suspend) return;
unsigned long elapsed = nowUp - _lastServiceShow;
if (elapsed <= MIN_FRAME_DELAY) return; // keep wifi alive - no matter if triggered or unlimited
if ( !_triggered && (_targetFps != FPS_UNLIMITED)) { // unlimited mode = no frametime
if (elapsed < _frametime) return; // too early for service
}
if (nowUp - _lastShow < MIN_SHOW_DELAY || _suspend) return;
bool doShow = false;
_isServicing = true;
_segment_index = 0;
for (segment &seg : _segments) {
if (_suspend) break; // immediately stop processing segments if suspend requested during service()
if (_suspend) return; // immediately stop processing segments if suspend requested during service()
// process transition (mode changes in the middle of transition)
seg.handleTransition();
@@ -1373,7 +1326,7 @@ void WS2812FX::service() {
if (!seg.isActive()) continue;
// last condition ensures all solid segments are updated at the same time
if (nowUp >= seg.next_time || _triggered || (doShow && seg.mode == FX_MODE_STATIC))
if (nowUp > seg.next_time || _triggered || (doShow && seg.mode == FX_MODE_STATIC))
{
doShow = true;
unsigned frameDelay = FRAMETIME;
@@ -1423,16 +1376,15 @@ void WS2812FX::service() {
_triggered = false;
#ifdef WLED_DEBUG
if ((_targetFps != FPS_UNLIMITED) && (millis() - nowUp > _frametime)) DEBUG_PRINTF_P(PSTR("Slow effects %u/%d.\n"), (unsigned)(millis()-nowUp), (int)_frametime);
if (millis() - nowUp > _frametime) DEBUG_PRINTF_P(PSTR("Slow effects %u/%d.\n"), (unsigned)(millis()-nowUp), (int)_frametime);
#endif
if (doShow) {
yield();
Segment::handleRandomPalette(); // slowly transition random palette; move it into for loop when each segment has individual random palette
_lastServiceShow = nowUp; // update timestamp, for precise FPS control
if (!_suspend) show();
show();
}
#ifdef WLED_DEBUG
if ((_targetFps != FPS_UNLIMITED) && (millis() - nowUp > _frametime)) DEBUG_PRINTF_P(PSTR("Slow strip %u/%d.\n"), (unsigned)(millis()-nowUp), (int)_frametime);
if (millis() - nowUp > _frametime) DEBUG_PRINTF_P(PSTR("Slow strip %u/%d.\n"), (unsigned)(millis()-nowUp), (int)_frametime);
#endif
}
@@ -1452,20 +1404,18 @@ void WS2812FX::show() {
// avoid race condition, capture _callback value
show_callback callback = _callback;
if (callback) callback();
unsigned long showNow = millis();
// some buses send asynchronously and this method will return before
// all of the data has been sent.
// See https://github.com/Makuna/NeoPixelBus/wiki/ESP32-NeoMethods#neoesp32rmt-methods
BusManager::show();
unsigned long showNow = millis();
size_t diff = showNow - _lastShow;
if (diff > 0) { // skip calculation if no time has passed
size_t fpsCurr = (1000 << FPS_CALC_SHIFT) / diff; // fixed point math
_cumulativeFps = (FPS_CALC_AVG * _cumulativeFps + fpsCurr + FPS_CALC_AVG / 2) / (FPS_CALC_AVG + 1); // "+FPS_CALC_AVG/2" for proper rounding
_lastShow = showNow;
}
size_t fpsCurr = 200;
if (diff > 0) fpsCurr = 1000 / diff;
_cumulativeFps = (3 * _cumulativeFps + fpsCurr +2) >> 2; // "+2" for proper rounding (2/4 = 0.5)
_lastShow = showNow;
}
/**
@@ -1482,13 +1432,12 @@ bool WS2812FX::isUpdating() const {
*/
uint16_t WS2812FX::getFps() const {
if (millis() - _lastShow > 2000) return 0;
return (FPS_MULTIPLIER * _cumulativeFps) >> FPS_CALC_SHIFT; // _cumulativeFps is stored in fixed point
return _cumulativeFps +1;
}
void WS2812FX::setTargetFps(uint8_t fps) {
if (fps <= 250) _targetFps = fps;
if (_targetFps > 0) _frametime = 1000 / _targetFps;
else _frametime = MIN_FRAME_DELAY; // unlimited mode
if (fps > 0 && fps <= 120) _targetFps = fps;
_frametime = 1000 / _targetFps;
}
void WS2812FX::setMode(uint8_t segid, uint8_t m) {
@@ -1536,7 +1485,7 @@ void WS2812FX::setBrightness(uint8_t b, bool direct) {
BusManager::setBrightness(b);
if (!direct) {
unsigned long t = millis();
if (_segments[0].next_time > t + 22 && t - _lastShow > MIN_FRAME_DELAY) trigger(); //apply brightness change immediately if no refresh soon
if (_segments[0].next_time > t + 22 && t - _lastShow > MIN_SHOW_DELAY) trigger(); //apply brightness change immediately if no refresh soon
}
}
@@ -1646,7 +1595,7 @@ void WS2812FX::setSegment(uint8_t segId, uint16_t i1, uint16_t i2, uint8_t group
segId = getSegmentsNum()-1; // segments are added at the end of list
}
suspend();
_segments[segId].setGeometry(i1, i2, grouping, spacing, offset, startY, stopY);
_segments[segId].setUp(i1, i2, grouping, spacing, offset, startY, stopY);
resume();
if (segId > 0 && segId == getSegmentsNum()-1 && i2 <= i1) _segments.pop_back(); // if last segment was deleted remove it from vector
}
+163 -181
View File
@@ -18,12 +18,10 @@
#endif
#include "const.h"
#include "pin_manager.h"
#include "bus_manager.h"
#include "bus_wrapper.h"
#include <bits/unique_ptr.h>
#include "bus_manager.h"
extern bool cctICused;
extern bool useParallelI2S;
//colors.cpp
uint32_t colorBalanceFromKelvin(uint16_t kelvin, uint32_t rgb);
@@ -31,6 +29,28 @@ uint32_t colorBalanceFromKelvin(uint16_t kelvin, uint32_t rgb);
//udp.cpp
uint8_t realtimeBroadcast(uint8_t type, IPAddress client, uint16_t length, byte *buffer, uint8_t bri=255, bool isRGBW=false);
// enable additional debug output
#if defined(WLED_DEBUG_HOST)
#include "net_debug.h"
#define DEBUGOUT NetDebug
#else
#define DEBUGOUT Serial
#endif
#ifdef WLED_DEBUG
#ifndef ESP8266
#include <rom/rtc.h>
#endif
#define DEBUG_PRINT(x) DEBUGOUT.print(x)
#define DEBUG_PRINTLN(x) DEBUGOUT.println(x)
#define DEBUG_PRINTF(x...) DEBUGOUT.printf(x)
#define DEBUG_PRINTF_P(x...) DEBUGOUT.printf_P(x)
#else
#define DEBUG_PRINT(x)
#define DEBUG_PRINTLN(x)
#define DEBUG_PRINTF(x...)
#define DEBUG_PRINTF_P(x...)
#endif
//color mangling macros
#define RGBW32(r,g,b,w) (uint32_t((byte(w) << 24) | (byte(r) << 16) | (byte(g) << 8) | (byte(b))))
@@ -43,7 +63,6 @@ uint8_t realtimeBroadcast(uint8_t type, IPAddress client, uint16_t length, byte
bool ColorOrderMap::add(uint16_t start, uint16_t len, uint8_t colorOrder) {
if (count() >= WLED_MAX_COLOR_ORDER_MAPPINGS || len == 0 || (colorOrder & 0x0F) > COL_ORDER_MAX) return false; // upper nibble contains W swap information
_mappings.push_back({start,len,colorOrder});
DEBUGBUS_PRINTF_P(PSTR("Bus: Add COM (%d,%d,%d)\n"), (int)start, (int)len, (int)colorOrder);
return true;
}
@@ -97,16 +116,12 @@ uint32_t Bus::autoWhiteCalc(uint32_t c) const {
}
uint8_t *Bus::allocateData(size_t size) {
freeData(); // should not happen, but for safety
if (_data) free(_data); // should not happen, but for safety
return _data = (uint8_t *)(size>0 ? calloc(size, sizeof(uint8_t)) : nullptr);
}
void Bus::freeData() {
if (_data) free(_data);
_data = nullptr;
}
BusDigital::BusDigital(const BusConfig &bc, uint8_t nr, const ColorOrderMap &com)
BusDigital::BusDigital(BusConfig &bc, uint8_t nr, const ColorOrderMap &com)
: Bus(bc.type, bc.start, bc.autoWhite, bc.count, bc.reversed, (bc.refreshReq || bc.type == TYPE_TM1814))
, _skip(bc.skipAmount) //sacrificial pixels
, _colorOrder(bc.colorOrder)
@@ -114,43 +129,42 @@ BusDigital::BusDigital(const BusConfig &bc, uint8_t nr, const ColorOrderMap &com
, _milliAmpsMax(bc.milliAmpsMax)
, _colorOrderMap(com)
{
DEBUGBUS_PRINTLN(F("Bus: Creating digital bus."));
if (!isDigital(bc.type) || !bc.count) { DEBUGBUS_PRINTLN(F("Not digial or empty bus!")); return; }
if (!PinManager::allocatePin(bc.pins[0], true, PinOwner::BusDigital)) { DEBUGBUS_PRINTLN(F("Pin 0 allocated!")); return; }
if (!isDigital(bc.type) || !bc.count) return;
if (!PinManager::allocatePin(bc.pins[0], true, PinOwner::BusDigital)) return;
_frequencykHz = 0U;
_pins[0] = bc.pins[0];
if (is2Pin(bc.type)) {
if (!PinManager::allocatePin(bc.pins[1], true, PinOwner::BusDigital)) {
cleanup();
DEBUGBUS_PRINTLN(F("Pin 1 allocated!"));
return;
}
_pins[1] = bc.pins[1];
_frequencykHz = bc.frequency ? bc.frequency : 2000U; // 2MHz clock if undefined
}
_iType = PolyBus::getI(bc.type, _pins, nr);
if (_iType == I_NONE) { DEBUGBUS_PRINTLN(F("Incorrect iType!")); return; }
if (_iType == I_NONE) return;
_hasRgb = hasRGB(bc.type);
_hasWhite = hasWhite(bc.type);
_hasCCT = hasCCT(bc.type);
if (bc.doubleBuffer && !allocateData(bc.count * Bus::getNumberOfChannels(bc.type))) { DEBUGBUS_PRINTLN(F("Buffer allocation failed!")); return; }
if (bc.doubleBuffer && !allocateData(bc.count * Bus::getNumberOfChannels(bc.type))) return;
//_buffering = bc.doubleBuffer;
uint16_t lenToCreate = bc.count;
if (bc.type == TYPE_WS2812_1CH_X3) lenToCreate = NUM_ICS_WS2812_1CH_3X(bc.count); // only needs a third of "RGB" LEDs for NeoPixelBus
_busPtr = PolyBus::create(_iType, _pins, lenToCreate + _skip, nr);
_busPtr = PolyBus::create(_iType, _pins, lenToCreate + _skip, nr, _frequencykHz);
_valid = (_busPtr != nullptr);
DEBUGBUS_PRINTF_P(PSTR("Bus: %successfully inited #%u (len:%u, type:%u (RGB:%d, W:%d, CCT:%d), pins:%u,%u [itype:%u] mA=%d/%d)\n"),
_valid?"S":"Uns",
(int)nr,
(int)bc.count,
(int)bc.type,
(int)_hasRgb, (int)_hasWhite, (int)_hasCCT,
(unsigned)_pins[0], is2Pin(bc.type)?(unsigned)_pins[1]:255U,
(unsigned)_iType,
(int)_milliAmpsPerLed, (int)_milliAmpsMax
);
DEBUG_PRINTF_P(PSTR("%successfully inited strip %u (len %u) with type %u and pins %u,%u (itype %u). mA=%d/%d\n"), _valid?"S":"Uns", nr, bc.count, bc.type, _pins[0], is2Pin(bc.type)?_pins[1]:255, _iType, _milliAmpsPerLed, _milliAmpsMax);
}
//fine tune power estimation constants for your setup
//you can set it to 0 if the ESP is powered by USB and the LEDs by external
#ifndef MA_FOR_ESP
#ifdef ESP8266
#define MA_FOR_ESP 80 //how much mA does the ESP use (Wemos D1 about 80mA)
#else
#define MA_FOR_ESP 120 //how much mA does the ESP use (ESP32 about 120mA)
#endif
#endif
//DISCLAIMER
//The following function attemps to calculate the current LED power usage,
//and will limit the brightness to stay below a set amperage threshold.
@@ -159,7 +173,7 @@ BusDigital::BusDigital(const BusConfig &bc, uint8_t nr, const ColorOrderMap &com
//I am NOT to be held liable for burned down garages or houses!
// To disable brightness limiter we either set output max current to 0 or single LED current to 0
uint8_t BusDigital::estimateCurrentAndLimitBri() const {
uint8_t BusDigital::estimateCurrentAndLimitBri() {
bool useWackyWS2815PowerModel = false;
byte actualMilliampsPerLed = _milliAmpsPerLed;
@@ -172,7 +186,7 @@ uint8_t BusDigital::estimateCurrentAndLimitBri() const {
actualMilliampsPerLed = 12; // from testing an actual strip
}
unsigned powerBudget = (_milliAmpsMax - MA_FOR_ESP/BusManager::getNumBusses()); //80/120mA for ESP power
size_t powerBudget = (_milliAmpsMax - MA_FOR_ESP/BusManager::getNumBusses()); //80/120mA for ESP power
if (powerBudget > getLength()) { //each LED uses about 1mA in standby, exclude that from power budget
powerBudget -= getLength();
} else {
@@ -197,25 +211,26 @@ uint8_t BusDigital::estimateCurrentAndLimitBri() const {
}
// powerSum has all the values of channels summed (max would be getLength()*765 as white is excluded) so convert to milliAmps
BusDigital::_milliAmpsTotal = (busPowerSum * actualMilliampsPerLed * _bri) / (765*255);
busPowerSum = (busPowerSum * actualMilliampsPerLed) / 765;
_milliAmpsTotal = busPowerSum * _bri / 255;
uint8_t newBri = _bri;
if (BusDigital::_milliAmpsTotal > powerBudget) {
//scale brightness down to stay in current limit
unsigned scaleB = powerBudget * 255 / BusDigital::_milliAmpsTotal;
newBri = (_bri * scaleB) / 256 + 1;
BusDigital::_milliAmpsTotal = powerBudget;
//_milliAmpsTotal = (busPowerSum * actualMilliampsPerLed * newBri) / (765*255);
if (busPowerSum * _bri / 255 > powerBudget) { //scale brightness down to stay in current limit
float scale = (float)(powerBudget * 255) / (float)(busPowerSum * _bri);
if (scale >= 1.0f) return _bri;
_milliAmpsTotal = ceilf((float)_milliAmpsTotal * scale);
uint8_t scaleB = min((int)(scale * 255), 255);
newBri = unsigned(_bri * scaleB) / 256 + 1;
}
return newBri;
}
void BusDigital::show() {
BusDigital::_milliAmpsTotal = 0;
_milliAmpsTotal = 0;
if (!_valid) return;
uint8_t cctWW = 0, cctCW = 0;
unsigned newBri = estimateCurrentAndLimitBri(); // will fill _milliAmpsTotal (TODO: could use PolyBus::CalcTotalMilliAmpere())
unsigned newBri = estimateCurrentAndLimitBri(); // will fill _milliAmpsTotal
if (newBri < _bri) PolyBus::setBrightness(_busPtr, _iType, newBri); // limit brightness to stay within current limits
if (_data) {
@@ -241,7 +256,6 @@ void BusDigital::show() {
// TODO: there is an issue if CCT is calculated from RGB value (_cct==-1), we cannot do that with double buffer
Bus::_cct = _data[offset+channels-1];
Bus::calculateCCT(c, cctWW, cctCW);
if (_type == TYPE_WS2812_WWA) c = RGBW32(cctWW, cctCW, 0, W(c)); // may need swapping
}
unsigned pix = i;
if (_reversed) pix = _len - pix -1;
@@ -292,8 +306,9 @@ void BusDigital::setStatusPixel(uint32_t c) {
}
}
void IRAM_ATTR BusDigital::setPixelColor(unsigned pix, uint32_t c) {
void IRAM_ATTR BusDigital::setPixelColor(uint16_t pix, uint32_t c) {
if (!_valid) return;
uint8_t cctWW = 0, cctCW = 0;
if (hasWhite()) c = autoWhiteCalc(c);
if (Bus::_cct >= 1900) c = colorBalanceFromKelvin(Bus::_cct, c); //color correction from CCT
if (_data) {
@@ -321,19 +336,13 @@ void IRAM_ATTR BusDigital::setPixelColor(unsigned pix, uint32_t c) {
case 2: c = RGBW32(R(cOld), G(cOld), W(c) , 0); break;
}
}
uint16_t wwcw = 0;
if (hasCCT()) {
uint8_t cctWW = 0, cctCW = 0;
Bus::calculateCCT(c, cctWW, cctCW);
wwcw = (cctCW<<8) | cctWW;
if (_type == TYPE_WS2812_WWA) c = RGBW32(cctWW, cctCW, 0, W(c)); // may need swapping
}
PolyBus::setPixelColor(_busPtr, _iType, pix, c, co, wwcw);
if (hasCCT()) Bus::calculateCCT(c, cctWW, cctCW);
PolyBus::setPixelColor(_busPtr, _iType, pix, c, co, (cctCW<<8) | cctWW);
}
}
// returns original color if global buffering is enabled, else returns lossly restored color from bus
uint32_t IRAM_ATTR BusDigital::getPixelColor(unsigned pix) const {
uint32_t IRAM_ATTR BusDigital::getPixelColor(uint16_t pix) const {
if (!_valid) return 0;
if (_data) {
size_t offset = pix * getNumberOfChannels();
@@ -359,24 +368,16 @@ uint32_t IRAM_ATTR BusDigital::getPixelColor(unsigned pix) const {
case 2: c = RGBW32(b, b, b, b); break;
}
}
if (_type == TYPE_WS2812_WWA) {
uint8_t w = R(c) | G(c);
c = RGBW32(w, w, 0, w);
}
return c;
}
}
unsigned BusDigital::getPins(uint8_t* pinArray) const {
uint8_t BusDigital::getPins(uint8_t* pinArray) const {
unsigned numPins = is2Pin(_type) + 1;
if (pinArray) for (unsigned i = 0; i < numPins; i++) pinArray[i] = _pins[i];
return numPins;
}
unsigned BusDigital::getBusSize() const {
return sizeof(BusDigital) + (isOk() ? PolyBus::getDataSize(_busPtr, _iType) + (_data ? _len * getNumberOfChannels() : 0) : 0);
}
void BusDigital::setColorOrder(uint8_t colorOrder) {
// upper nibble contains W swap information
if ((colorOrder & 0x0F) > 5) return;
@@ -399,8 +400,8 @@ std::vector<LEDType> BusDigital::getLEDTypes() {
{TYPE_WS2805, "D", PSTR("WS2805 RGBCW")},
{TYPE_SM16825, "D", PSTR("SM16825 RGBCW")},
{TYPE_WS2812_1CH_X3, "D", PSTR("WS2811 White")},
//{TYPE_WS2812_2CH_X3, "D", PSTR("WS281x CCT")}, // not implemented
{TYPE_WS2812_WWA, "D", PSTR("WS281x WWA")}, // amber ignored
//{TYPE_WS2812_2CH_X3, "D", PSTR("WS2811 CCT")}, // not implemented
//{TYPE_WS2812_WWA, "D", PSTR("WS2811 WWA")}, // not implemented
{TYPE_WS2801, "2P", PSTR("WS2801")},
{TYPE_APA102, "2P", PSTR("APA102")},
{TYPE_LPD8806, "2P", PSTR("LPD8806")},
@@ -409,19 +410,18 @@ std::vector<LEDType> BusDigital::getLEDTypes() {
};
}
void BusDigital::begin() {
void BusDigital::reinit() {
if (!_valid) return;
PolyBus::begin(_busPtr, _iType, _pins, _frequencykHz);
PolyBus::begin(_busPtr, _iType, _pins);
}
void BusDigital::cleanup() {
DEBUGBUS_PRINTLN(F("Digital Cleanup."));
DEBUG_PRINTLN(F("Digital Cleanup."));
PolyBus::cleanup(_busPtr, _iType);
_iType = I_NONE;
_valid = false;
_busPtr = nullptr;
freeData();
//PinManager::deallocateMultiplePins(_pins, 2, PinOwner::BusDigital);
if (_data != nullptr) freeData();
PinManager::deallocatePin(_pins[1], PinOwner::BusDigital);
PinManager::deallocatePin(_pins[0], PinOwner::BusDigital);
}
@@ -452,7 +452,7 @@ void BusDigital::cleanup() {
#endif
#endif
BusPwm::BusPwm(const BusConfig &bc)
BusPwm::BusPwm(BusConfig &bc)
: Bus(bc.type, bc.start, bc.autoWhite, 1, bc.reversed, bc.refreshReq) // hijack Off refresh flag to indicate usage of dithering
{
if (!isPWM(bc.type)) return;
@@ -496,12 +496,12 @@ BusPwm::BusPwm(const BusConfig &bc)
_hasRgb = hasRGB(bc.type);
_hasWhite = hasWhite(bc.type);
_hasCCT = hasCCT(bc.type);
_data = _pwmdata; // avoid malloc() and use already allocated memory
_data = _pwmdata; // avoid malloc() and use stack
_valid = true;
DEBUGBUS_PRINTF_P(PSTR("%successfully inited PWM strip with type %u, frequency %u, bit depth %u and pins %u,%u,%u,%u,%u\n"), _valid?"S":"Uns", bc.type, _frequency, _depth, _pins[0], _pins[1], _pins[2], _pins[3], _pins[4]);
DEBUG_PRINTF_P(PSTR("%successfully inited PWM strip with type %u, frequency %u, bit depth %u and pins %u,%u,%u,%u,%u\n"), _valid?"S":"Uns", bc.type, _frequency, _depth, _pins[0], _pins[1], _pins[2], _pins[3], _pins[4]);
}
void BusPwm::setPixelColor(unsigned pix, uint32_t c) {
void BusPwm::setPixelColor(uint16_t pix, uint32_t c) {
if (pix != 0 || !_valid) return; //only react to first pixel
if (_type != TYPE_ANALOG_3CH) c = autoWhiteCalc(c);
if (Bus::_cct >= 1900 && (_type == TYPE_ANALOG_3CH || _type == TYPE_ANALOG_4CH)) {
@@ -538,7 +538,7 @@ void BusPwm::setPixelColor(unsigned pix, uint32_t c) {
}
//does no index check
uint32_t BusPwm::getPixelColor(unsigned pix) const {
uint32_t BusPwm::getPixelColor(uint16_t pix) const {
if (!_valid) return 0;
// TODO getting the reverse from CCT is involved (a quick approximation when CCT blending is ste to 0 implemented)
switch (_type) {
@@ -567,15 +567,19 @@ void BusPwm::show() {
const unsigned maxBri = (1<<_depth); // possible values: 16384 (14), 8192 (13), 4096 (12), 2048 (11), 1024 (10), 512 (9) and 256 (8)
[[maybe_unused]] const unsigned bitShift = dithering * 4; // if dithering, _depth is 12 bit but LEDC channel is set to 8 bit (using 4 fractional bits)
// use CIE brightness formula (linear + cubic) to approximate human eye perceived brightness
// use CIE brightness formula (cubic) to fit (or approximate linearity of) human eye perceived brightness
// the formula is based on 12 bit resolution as there is no need for greater precision
// see: https://en.wikipedia.org/wiki/Lightness
unsigned pwmBri = _bri;
if (pwmBri < 21) { // linear response for values [0-20]
pwmBri = (pwmBri * maxBri + 2300 / 2) / 2300 ; // adding '0.5' before division for correct rounding, 2300 gives a good match to CIE curve
} else { // cubic response for values [21-255]
float temp = float(pwmBri + 41) / float(255 + 41); // 41 is to match offset & slope to linear part
temp = temp * temp * temp * (float)maxBri;
pwmBri = (unsigned)temp; // pwmBri is in range [0-maxBri] C
unsigned pwmBri = (unsigned)_bri * 100; // enlarge to use integer math for linear response
if (pwmBri < 2040) {
// linear response for values [0-20]
pwmBri = ((pwmBri << 12) + 115043) / 230087; //adding '0.5' before division for correct rounding
} else {
// cubic response for values [21-255]
pwmBri += 4080;
float temp = (float)pwmBri / 29580.0f;
temp = temp * temp * temp * (float)maxBri;
pwmBri = (unsigned)temp; // pwmBri is in range [0-maxBri]
}
[[maybe_unused]] unsigned hPoint = 0; // phase shift (0 - maxBri)
@@ -614,7 +618,7 @@ void BusPwm::show() {
}
}
unsigned BusPwm::getPins(uint8_t* pinArray) const {
uint8_t BusPwm::getPins(uint8_t* pinArray) const {
if (!_valid) return 0;
unsigned numPins = numPWMPins(_type);
if (pinArray) for (unsigned i = 0; i < numPins; i++) pinArray[i] = _pins[i];
@@ -650,7 +654,7 @@ void BusPwm::deallocatePins() {
}
BusOnOff::BusOnOff(const BusConfig &bc)
BusOnOff::BusOnOff(BusConfig &bc)
: Bus(bc.type, bc.start, bc.autoWhite, 1, bc.reversed)
, _onoffdata(0)
{
@@ -667,10 +671,10 @@ BusOnOff::BusOnOff(const BusConfig &bc)
_hasCCT = false;
_data = &_onoffdata; // avoid malloc() and use stack
_valid = true;
DEBUGBUS_PRINTF_P(PSTR("%successfully inited On/Off strip with pin %u\n"), _valid?"S":"Uns", _pin);
DEBUG_PRINTF_P(PSTR("%successfully inited On/Off strip with pin %u\n"), _valid?"S":"Uns", _pin);
}
void BusOnOff::setPixelColor(unsigned pix, uint32_t c) {
void BusOnOff::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);
@@ -680,7 +684,7 @@ void BusOnOff::setPixelColor(unsigned pix, uint32_t c) {
_data[0] = bool(r|g|b|w) && bool(_bri) ? 0xFF : 0;
}
uint32_t BusOnOff::getPixelColor(unsigned pix) const {
uint32_t BusOnOff::getPixelColor(uint16_t pix) const {
if (!_valid) return 0;
return RGBW32(_data[0], _data[0], _data[0], _data[0]);
}
@@ -690,7 +694,7 @@ void BusOnOff::show() {
digitalWrite(_pin, _reversed ? !(bool)_data[0] : (bool)_data[0]);
}
unsigned BusOnOff::getPins(uint8_t* pinArray) const {
uint8_t BusOnOff::getPins(uint8_t* pinArray) const {
if (!_valid) return 0;
if (pinArray) pinArray[0] = _pin;
return 1;
@@ -703,7 +707,7 @@ std::vector<LEDType> BusOnOff::getLEDTypes() {
};
}
BusNetwork::BusNetwork(const BusConfig &bc)
BusNetwork::BusNetwork(BusConfig &bc)
: Bus(bc.type, bc.start, bc.autoWhite, bc.count)
, _broadcastLock(false)
{
@@ -727,10 +731,10 @@ BusNetwork::BusNetwork(const BusConfig &bc)
_UDPchannels = _hasWhite + 3;
_client = IPAddress(bc.pins[0],bc.pins[1],bc.pins[2],bc.pins[3]);
_valid = (allocateData(_len * _UDPchannels) != nullptr);
DEBUGBUS_PRINTF_P(PSTR("%successfully inited virtual strip with type %u and IP %u.%u.%u.%u\n"), _valid?"S":"Uns", bc.type, bc.pins[0], bc.pins[1], bc.pins[2], bc.pins[3]);
DEBUG_PRINTF_P(PSTR("%successfully inited virtual strip with type %u and IP %u.%u.%u.%u\n"), _valid?"S":"Uns", bc.type, bc.pins[0], bc.pins[1], bc.pins[2], bc.pins[3]);
}
void BusNetwork::setPixelColor(unsigned pix, uint32_t c) {
void BusNetwork::setPixelColor(uint16_t pix, uint32_t c) {
if (!_valid || pix >= _len) return;
if (_hasWhite) c = autoWhiteCalc(c);
if (Bus::_cct >= 1900) c = colorBalanceFromKelvin(Bus::_cct, c); //color correction from CCT
@@ -741,7 +745,7 @@ void BusNetwork::setPixelColor(unsigned pix, uint32_t c) {
if (_hasWhite) _data[offset+3] = W(c);
}
uint32_t BusNetwork::getPixelColor(unsigned pix) const {
uint32_t BusNetwork::getPixelColor(uint16_t pix) const {
if (!_valid || pix >= _len) return 0;
unsigned offset = pix * _UDPchannels;
return RGBW32(_data[offset], _data[offset+1], _data[offset+2], (hasWhite() ? _data[offset+3] : 0));
@@ -754,7 +758,7 @@ void BusNetwork::show() {
_broadcastLock = false;
}
unsigned BusNetwork::getPins(uint8_t* pinArray) const {
uint8_t BusNetwork::getPins(uint8_t* pinArray) const {
if (pinArray) for (unsigned i = 0; i < 4; i++) pinArray[i] = _client[i];
return 4;
}
@@ -775,7 +779,6 @@ std::vector<LEDType> BusNetwork::getLEDTypes() {
}
void BusNetwork::cleanup() {
DEBUGBUS_PRINTLN(F("Virtual Cleanup."));
_type = I_NONE;
_valid = false;
freeData();
@@ -783,66 +786,43 @@ void BusNetwork::cleanup() {
//utility to get the approx. memory usage of a given BusConfig
unsigned BusConfig::memUsage(unsigned nr) const {
if (Bus::isVirtual(type)) {
return sizeof(BusNetwork) + (count * Bus::getNumberOfChannels(type));
} else if (Bus::isDigital(type)) {
return sizeof(BusDigital) + PolyBus::memUsage(count + skipAmount, PolyBus::getI(type, pins, nr)) + doubleBuffer * (count + skipAmount) * Bus::getNumberOfChannels(type);
} else if (Bus::isOnOff(type)) {
return sizeof(BusOnOff);
} else {
return sizeof(BusPwm);
uint32_t BusManager::memUsage(BusConfig &bc) {
if (Bus::isOnOff(bc.type) || Bus::isPWM(bc.type)) return OUTPUT_MAX_PINS;
unsigned len = bc.count + bc.skipAmount;
unsigned channels = Bus::getNumberOfChannels(bc.type);
unsigned multiplier = 1;
if (Bus::isDigital(bc.type)) { // digital types
if (Bus::is16bit(bc.type)) len *= 2; // 16-bit LEDs
#ifdef ESP8266
if (bc.pins[0] == 3) { //8266 DMA uses 5x the mem
multiplier = 5;
}
#else //ESP32 RMT uses double buffer, parallel I2S uses 8x buffer (3 times)
multiplier = PolyBus::isParallelI2S1Output() ? 24 : 2;
#endif
}
return (len * multiplier + bc.doubleBuffer * (bc.count + bc.skipAmount)) * channels;
}
unsigned BusManager::memUsage() {
// when ESP32, S2 & S3 use parallel I2S only the largest bus determines the total memory requirements for back buffers
// front buffers are always allocated per bus
unsigned size = 0;
unsigned maxI2S = 0;
#if !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(ESP8266)
unsigned digitalCount = 0;
#if defined(CONFIG_IDF_TARGET_ESP32S2) || defined(CONFIG_IDF_TARGET_ESP32S3)
#define MAX_RMT 4
#else
#define MAX_RMT 8
#endif
#endif
for (const auto &bus : busses) {
unsigned busSize = bus->getBusSize();
#if !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(ESP8266)
if (bus->isDigital() && !bus->is2Pin()) digitalCount++;
if (PolyBus::isParallelI2S1Output() && digitalCount > MAX_RMT) {
unsigned i2sCommonSize = 3 * bus->getLength() * bus->getNumberOfChannels() * (bus->is16bit()+1);
if (i2sCommonSize > maxI2S) maxI2S = i2sCommonSize;
busSize -= i2sCommonSize;
}
#endif
size += busSize;
}
return size + maxI2S;
uint32_t BusManager::memUsage(unsigned maxChannels, unsigned maxCount, unsigned minBuses) {
//ESP32 RMT uses double buffer, parallel I2S uses 8x buffer (3 times)
unsigned multiplier = PolyBus::isParallelI2S1Output() ? 3 : 2;
return (maxChannels * maxCount * minBuses * multiplier);
}
int BusManager::add(const BusConfig &bc) {
DEBUGBUS_PRINTF_P(PSTR("Bus: Adding bus (%d - %d >= %d)\n"), getNumBusses(), getNumVirtualBusses(), WLED_MAX_BUSSES);
int BusManager::add(BusConfig &bc) {
if (getNumBusses() - getNumVirtualBusses() >= WLED_MAX_BUSSES) return -1;
unsigned numDigital = 0;
for (const auto &bus : busses) if (bus->isDigital() && !bus->is2Pin()) numDigital++;
if (Bus::isVirtual(bc.type)) {
//busses.push_back(std::make_unique<BusNetwork>(bc)); // when C++ >11
busses.push_back(new BusNetwork(bc));
busses[numBusses] = new BusNetwork(bc);
} else if (Bus::isDigital(bc.type)) {
//busses.push_back(std::make_unique<BusDigital>(bc, numDigital, colorOrderMap));
busses.push_back(new BusDigital(bc, numDigital, colorOrderMap));
busses[numBusses] = new BusDigital(bc, numBusses, colorOrderMap);
} else if (Bus::isOnOff(bc.type)) {
//busses.push_back(std::make_unique<BusOnOff>(bc));
busses.push_back(new BusOnOff(bc));
busses[numBusses] = new BusOnOff(bc);
} else {
//busses.push_back(std::make_unique<BusPwm>(bc));
busses.push_back(new BusPwm(bc));
busses[numBusses] = new BusPwm(bc);
}
return busses.size();
return numBusses++;
}
// credit @willmmiles
@@ -871,21 +851,18 @@ String BusManager::getLEDTypesJSONString() {
}
void BusManager::useParallelOutput() {
DEBUGBUS_PRINTLN(F("Bus: Enabling parallel I2S."));
_parallelOutputs = 8; // hardcoded since we use NPB I2S x8 methods
PolyBus::setParallelI2S1Output();
}
bool BusManager::hasParallelOutput() {
return PolyBus::isParallelI2S1Output();
}
//do not call this method from system context (network callback)
void BusManager::removeAll() {
DEBUGBUS_PRINTLN(F("Removing all."));
DEBUG_PRINTLN(F("Removing all."));
//prevents crashes due to deleting busses while in use.
while (!canAllShow()) yield();
for (auto &bus : busses) delete bus; // needed when not using std::unique_ptr C++ >11
busses.clear();
for (unsigned i = 0; i < numBusses; i++) delete busses[i];
numBusses = 0;
_parallelOutputs = 1;
PolyBus::setParallelI2S1Output(false);
}
@@ -896,9 +873,7 @@ void BusManager::removeAll() {
void BusManager::esp32RMTInvertIdle() {
bool idle_out;
unsigned rmt = 0;
unsigned u = 0;
for (auto &bus : busses) {
if (bus->getLength()==0 || !bus->isDigital() || bus->is2Pin()) continue;
for (unsigned u = 0; u < numBusses(); u++) {
#if defined(CONFIG_IDF_TARGET_ESP32C3) // 2 RMT, only has 1 I2S but NPB does not support it ATM
if (u > 1) return;
rmt = u;
@@ -909,11 +884,11 @@ void BusManager::esp32RMTInvertIdle() {
if (u > 3) return;
rmt = u;
#else
unsigned numI2S = !PolyBus::isParallelI2S1Output(); // if using parallel I2S, RMT is used 1st
if (numI2S > u) continue;
if (u > 7 + numI2S) return;
rmt = u - numI2S;
if (u < _parallelOutputs) continue;
if (u >= _parallelOutputs + 8) return; // only 8 RMT channels
rmt = u - _parallelOutputs;
#endif
if (busses[u]->getLength()==0 || !busses[u]->isDigital() || busses[u]->is2Pin()) continue;
//assumes that bus number to rmt channel mapping stays 1:1
rmt_channel_t ch = static_cast<rmt_channel_t>(rmt);
rmt_idle_level_t lvl;
@@ -922,7 +897,6 @@ void BusManager::esp32RMTInvertIdle() {
else if (lvl == RMT_IDLE_LEVEL_LOW) lvl = RMT_IDLE_LEVEL_HIGH;
else continue;
rmt_set_idle_level(ch, idle_out, lvl);
u++
}
}
#endif
@@ -931,12 +905,12 @@ void BusManager::on() {
#ifdef ESP8266
//Fix for turning off onboard LED breaking bus
if (PinManager::getPinOwner(LED_BUILTIN) == PinOwner::BusDigital) {
for (auto &bus : busses) {
for (unsigned i = 0; i < numBusses; i++) {
uint8_t pins[2] = {255,255};
if (bus->isDigital() && bus->getPins(pins)) {
if (busses[i]->isDigital() && busses[i]->getPins(pins)) {
if (pins[0] == LED_BUILTIN || pins[1] == LED_BUILTIN) {
BusDigital *b = static_cast<BusDigital*>(bus);
b->begin();
BusDigital *bus = static_cast<BusDigital*>(busses[i]);
bus->reinit();
break;
}
}
@@ -953,7 +927,7 @@ void BusManager::off() {
// turn off built-in LED if strip is turned off
// this will break digital bus so will need to be re-initialised on On
if (PinManager::getPinOwner(LED_BUILTIN) == PinOwner::BusDigital) {
for (const auto &bus : busses) if (bus->isOffRefreshRequired()) return;
for (unsigned i = 0; i < numBusses; i++) if (busses[i]->isOffRefreshRequired()) return;
pinMode(LED_BUILTIN, OUTPUT);
digitalWrite(LED_BUILTIN, HIGH);
}
@@ -965,26 +939,31 @@ void BusManager::off() {
void BusManager::show() {
_milliAmpsUsed = 0;
for (auto &bus : busses) {
bus->show();
_milliAmpsUsed += bus->getUsedCurrent();
for (unsigned i = 0; i < numBusses; i++) {
busses[i]->show();
_milliAmpsUsed += busses[i]->getUsedCurrent();
}
if (_milliAmpsUsed) _milliAmpsUsed += MA_FOR_ESP;
}
void BusManager::setStatusPixel(uint32_t c) {
for (auto &bus : busses) bus->setStatusPixel(c);
for (unsigned i = 0; i < numBusses; i++) {
busses[i]->setStatusPixel(c);
}
}
void IRAM_ATTR BusManager::setPixelColor(unsigned pix, uint32_t c) {
for (auto &bus : busses) {
unsigned bstart = bus->getStart();
if (pix < bstart || pix >= bstart + bus->getLength()) continue;
bus->setPixelColor(pix - bstart, c);
void IRAM_ATTR BusManager::setPixelColor(uint16_t pix, uint32_t c) {
for (unsigned i = 0; i < numBusses; i++) {
unsigned bstart = busses[i]->getStart();
if (pix < bstart || pix >= bstart + busses[i]->getLength()) continue;
busses[i]->setPixelColor(pix - bstart, c);
}
}
void BusManager::setBrightness(uint8_t b) {
for (auto &bus : busses) bus->setBrightness(b);
for (unsigned i = 0; i < numBusses; i++) {
busses[i]->setBrightness(b);
}
}
void BusManager::setSegmentCCT(int16_t cct, bool allowWBCorrection) {
@@ -996,33 +975,35 @@ void BusManager::setSegmentCCT(int16_t cct, bool allowWBCorrection) {
Bus::setCCT(cct);
}
uint32_t BusManager::getPixelColor(unsigned pix) {
for (auto &bus : busses) {
unsigned bstart = bus->getStart();
if (!bus->containsPixel(pix)) continue;
return bus->getPixelColor(pix - bstart);
uint32_t BusManager::getPixelColor(uint16_t pix) {
for (unsigned i = 0; i < numBusses; i++) {
unsigned bstart = busses[i]->getStart();
if (!busses[i]->containsPixel(pix)) continue;
return busses[i]->getPixelColor(pix - bstart);
}
return 0;
}
bool BusManager::canAllShow() {
for (const auto &bus : busses) if (!bus->canShow()) return false;
for (unsigned i = 0; i < numBusses; i++) {
if (!busses[i]->canShow()) return false;
}
return true;
}
Bus* BusManager::getBus(uint8_t busNr) {
if (busNr >= busses.size()) return nullptr;
if (busNr >= numBusses) return nullptr;
return busses[busNr];
}
//semi-duplicate of strip.getLengthTotal() (though that just returns strip._length, calculated in finalizeInit())
uint16_t BusManager::getTotalLength() {
unsigned len = 0;
for (const auto &bus : busses) len += bus->getLength();
for (unsigned i=0; i<numBusses; i++) len += busses[i]->getLength();
return len;
}
bool PolyBus::_useParallelI2S = false;
bool PolyBus::useParallelI2S = false;
// Bus static member definition
int16_t Bus::_cct = -1;
@@ -1031,8 +1012,9 @@ uint8_t Bus::_gAWM = 255;
uint16_t BusDigital::_milliAmpsTotal = 0;
//std::vector<std::unique_ptr<Bus>> BusManager::busses;
std::vector<Bus*> BusManager::busses;
uint8_t BusManager::numBusses = 0;
Bus* BusManager::busses[WLED_MAX_BUSSES+WLED_MIN_VIRTUAL_BUSSES];
ColorOrderMap BusManager::colorOrderMap = {};
uint16_t BusManager::_milliAmpsUsed = 0;
uint16_t BusManager::_milliAmpsMax = ABL_MILLIAMPS_DEFAULT;
uint8_t BusManager::_parallelOutputs = 1;
+74 -125
View File
@@ -1,4 +1,3 @@
#pragma once
#ifndef BusManager_h
#define BusManager_h
@@ -8,30 +7,6 @@
#include "const.h"
#include <vector>
#include <memory>
// enable additional debug output
#if defined(WLED_DEBUG_HOST)
#include "net_debug.h"
#define DEBUGOUT NetDebug
#else
#define DEBUGOUT Serial
#endif
#ifdef WLED_DEBUG_BUS
#ifndef ESP8266
#include <rom/rtc.h>
#endif
#define DEBUGBUS_PRINT(x) DEBUGOUT.print(x)
#define DEBUGBUS_PRINTLN(x) DEBUGOUT.println(x)
#define DEBUGBUS_PRINTF(x...) DEBUGOUT.printf(x)
#define DEBUGBUS_PRINTF_P(x...) DEBUGOUT.printf_P(x)
#else
#define DEBUGBUS_PRINT(x)
#define DEBUGBUS_PRINTLN(x)
#define DEBUGBUS_PRINTF(x...)
#define DEBUGBUS_PRINTF_P(x...)
#endif
//colors.cpp
uint16_t approximateKelvinFromRGB(uint32_t rgb);
@@ -102,51 +77,49 @@ class Bus {
_autoWhiteMode = Bus::hasWhite(type) ? aw : RGBW_MODE_MANUAL_ONLY;
};
virtual ~Bus() {} //throw the bus under the bus (derived class needs to freeData())
virtual ~Bus() {} //throw the bus under the bus
virtual void begin() {};
virtual void show() = 0;
virtual bool canShow() const { return true; }
virtual void setStatusPixel(uint32_t c) {}
virtual void setPixelColor(unsigned pix, uint32_t c) = 0;
virtual void setBrightness(uint8_t b) { _bri = b; };
virtual void setColorOrder(uint8_t co) {}
virtual uint32_t getPixelColor(unsigned pix) const { return 0; }
virtual unsigned getPins(uint8_t* pinArray = nullptr) const { return 0; }
virtual uint16_t getLength() const { return isOk() ? _len : 0; }
virtual uint8_t getColorOrder() const { return COL_ORDER_RGB; }
virtual unsigned skippedLeds() const { return 0; }
virtual uint16_t getFrequency() const { return 0U; }
virtual uint16_t getLEDCurrent() const { return 0; }
virtual uint16_t getUsedCurrent() const { return 0; }
virtual uint16_t getMaxCurrent() const { return 0; }
virtual unsigned getBusSize() const { return sizeof(Bus); }
virtual bool canShow() const { return true; }
virtual void setStatusPixel(uint32_t c) {}
virtual void setPixelColor(uint16_t pix, uint32_t c) = 0;
virtual void setBrightness(uint8_t b) { _bri = b; };
virtual void setColorOrder(uint8_t co) {}
virtual uint32_t getPixelColor(uint16_t pix) const { return 0; }
virtual uint8_t getPins(uint8_t* pinArray = nullptr) const { return 0; }
virtual uint16_t getLength() const { return isOk() ? _len : 0; }
virtual uint8_t getColorOrder() const { return COL_ORDER_RGB; }
virtual uint8_t skippedLeds() const { return 0; }
virtual uint16_t getFrequency() const { return 0U; }
virtual uint16_t getLEDCurrent() const { return 0; }
virtual uint16_t getUsedCurrent() const { return 0; }
virtual uint16_t getMaxCurrent() const { return 0; }
inline bool hasRGB() const { return _hasRgb; }
inline bool hasWhite() const { return _hasWhite; }
inline bool hasCCT() const { return _hasCCT; }
inline bool isDigital() const { return isDigital(_type); }
inline bool is2Pin() const { return is2Pin(_type); }
inline bool isOnOff() const { return isOnOff(_type); }
inline bool isPWM() const { return isPWM(_type); }
inline bool isVirtual() const { return isVirtual(_type); }
inline bool is16bit() const { return is16bit(_type); }
inline bool mustRefresh() const { return mustRefresh(_type); }
inline void setReversed(bool reversed) { _reversed = reversed; }
inline void setStart(uint16_t start) { _start = start; }
inline void setAutoWhiteMode(uint8_t m) { if (m < 5) _autoWhiteMode = m; }
inline uint8_t getAutoWhiteMode() const { return _autoWhiteMode; }
inline unsigned getNumberOfChannels() const { return hasWhite() + 3*hasRGB() + hasCCT(); }
inline uint16_t getStart() const { return _start; }
inline uint8_t getType() const { return _type; }
inline bool isOk() const { return _valid; }
inline bool isReversed() const { return _reversed; }
inline bool isOffRefreshRequired() const { return _needsRefresh; }
inline bool containsPixel(uint16_t pix) const { return pix >= _start && pix < _start + _len; }
inline bool hasRGB() const { return _hasRgb; }
inline bool hasWhite() const { return _hasWhite; }
inline bool hasCCT() const { return _hasCCT; }
inline bool isDigital() const { return isDigital(_type); }
inline bool is2Pin() const { return is2Pin(_type); }
inline bool isOnOff() const { return isOnOff(_type); }
inline bool isPWM() const { return isPWM(_type); }
inline bool isVirtual() const { return isVirtual(_type); }
inline bool is16bit() const { return is16bit(_type); }
inline bool mustRefresh() const { return mustRefresh(_type); }
inline void setReversed(bool reversed) { _reversed = reversed; }
inline void setStart(uint16_t start) { _start = start; }
inline void setAutoWhiteMode(uint8_t m) { if (m < 5) _autoWhiteMode = m; }
inline uint8_t getAutoWhiteMode() const { return _autoWhiteMode; }
inline uint8_t getNumberOfChannels() const { return hasWhite() + 3*hasRGB() + hasCCT(); }
inline uint16_t getStart() const { return _start; }
inline uint8_t getType() const { return _type; }
inline bool isOk() const { return _valid; }
inline bool isReversed() const { return _reversed; }
inline bool isOffRefreshRequired() const { return _needsRefresh; }
inline bool containsPixel(uint16_t pix) const { return pix >= _start && pix < _start + _len; }
static inline std::vector<LEDType> getLEDTypes() { return {{TYPE_NONE, "", PSTR("None")}}; } // not used. just for reference for derived classes
static constexpr unsigned getNumberOfPins(uint8_t type) { return isVirtual(type) ? 4 : isPWM(type) ? numPWMPins(type) : is2Pin(type) + 1; } // credit @PaoloTK
static constexpr unsigned getNumberOfChannels(uint8_t type) { return hasWhite(type) + 3*hasRGB(type) + hasCCT(type); }
static inline std::vector<LEDType> getLEDTypes() { return {{TYPE_NONE, "", PSTR("None")}}; } // not used. just for reference for derived classes
static constexpr uint8_t getNumberOfPins(uint8_t type) { return isVirtual(type) ? 4 : isPWM(type) ? numPWMPins(type) : is2Pin(type) + 1; } // credit @PaoloTK
static constexpr uint8_t getNumberOfChannels(uint8_t type) { return hasWhite(type) + 3*hasRGB(type) + hasCCT(type); }
static constexpr bool hasRGB(uint8_t type) {
return !((type >= TYPE_WS2812_1CH && type <= TYPE_WS2812_WWA) || type == TYPE_ANALOG_1CH || type == TYPE_ANALOG_2CH || type == TYPE_ONOFF);
}
@@ -178,7 +151,7 @@ class Bus {
static inline uint8_t getGlobalAWMode() { return _gAWM; }
static inline void setCCT(int16_t cct) { _cct = cct; }
static inline uint8_t getCCTBlend() { return _cctBlend; }
static inline void setCCTBlend(uint8_t b) {
static inline void setCCTBlend(uint8_t b) {
_cctBlend = (std::min((int)b,100) * 127) / 100;
//compile-time limiter for hardware that can't power both white channels at max
#ifdef WLED_MAX_CCT_BLEND
@@ -217,31 +190,30 @@ class Bus {
uint32_t autoWhiteCalc(uint32_t c) const;
uint8_t *allocateData(size_t size = 1);
void freeData();
void freeData() { if (_data != nullptr) free(_data); _data = nullptr; }
};
class BusDigital : public Bus {
public:
BusDigital(const BusConfig &bc, uint8_t nr, const ColorOrderMap &com);
BusDigital(BusConfig &bc, uint8_t nr, const ColorOrderMap &com);
~BusDigital() { cleanup(); }
void show() override;
bool canShow() const override;
void setBrightness(uint8_t b) override;
void setStatusPixel(uint32_t c) override;
[[gnu::hot]] void setPixelColor(unsigned pix, uint32_t c) override;
[[gnu::hot]] void setPixelColor(uint16_t pix, uint32_t c) override;
void setColorOrder(uint8_t colorOrder) override;
[[gnu::hot]] uint32_t getPixelColor(unsigned pix) const override;
[[gnu::hot]] uint32_t getPixelColor(uint16_t pix) const override;
uint8_t getColorOrder() const override { return _colorOrder; }
unsigned getPins(uint8_t* pinArray = nullptr) const override;
unsigned skippedLeds() const override { return _skip; }
uint8_t getPins(uint8_t* pinArray = nullptr) const override;
uint8_t skippedLeds() const override { return _skip; }
uint16_t getFrequency() const override { return _frequencykHz; }
uint16_t getLEDCurrent() const override { return _milliAmpsPerLed; }
uint16_t getUsedCurrent() const override { return _milliAmpsTotal; }
uint16_t getMaxCurrent() const override { return _milliAmpsMax; }
unsigned getBusSize() const override;
void begin() override;
void reinit();
void cleanup();
static std::vector<LEDType> getLEDTypes();
@@ -270,22 +242,21 @@ class BusDigital : public Bus {
return c;
}
uint8_t estimateCurrentAndLimitBri() const;
uint8_t estimateCurrentAndLimitBri();
};
class BusPwm : public Bus {
public:
BusPwm(const BusConfig &bc);
BusPwm(BusConfig &bc);
~BusPwm() { cleanup(); }
void setPixelColor(unsigned pix, uint32_t c) override;
uint32_t getPixelColor(unsigned pix) const override; //does no index check
unsigned getPins(uint8_t* pinArray = nullptr) const override;
void setPixelColor(uint16_t pix, uint32_t c) override;
uint32_t getPixelColor(uint16_t pix) const override; //does no index check
uint8_t getPins(uint8_t* pinArray = nullptr) const override;
uint16_t getFrequency() const override { return _frequency; }
unsigned getBusSize() const override { return sizeof(BusPwm); }
void show() override;
inline void cleanup() { deallocatePins(); _data = nullptr; }
void cleanup() { deallocatePins(); }
static std::vector<LEDType> getLEDTypes();
@@ -304,15 +275,14 @@ class BusPwm : public Bus {
class BusOnOff : public Bus {
public:
BusOnOff(const BusConfig &bc);
BusOnOff(BusConfig &bc);
~BusOnOff() { cleanup(); }
void setPixelColor(unsigned pix, uint32_t c) override;
uint32_t getPixelColor(unsigned pix) const override;
unsigned getPins(uint8_t* pinArray) const override;
unsigned getBusSize() const override { return sizeof(BusOnOff); }
void setPixelColor(uint16_t pix, uint32_t c) override;
uint32_t getPixelColor(uint16_t pix) const override;
uint8_t getPins(uint8_t* pinArray) const override;
void show() override;
inline void cleanup() { PinManager::deallocatePin(_pin, PinOwner::BusOnOff); _data = nullptr; }
void cleanup() { PinManager::deallocatePin(_pin, PinOwner::BusOnOff); }
static std::vector<LEDType> getLEDTypes();
@@ -324,14 +294,13 @@ class BusOnOff : public Bus {
class BusNetwork : public Bus {
public:
BusNetwork(const BusConfig &bc);
BusNetwork(BusConfig &bc);
~BusNetwork() { cleanup(); }
bool canShow() const override { return !_broadcastLock; } // this should be a return value from UDP routine if it is still sending data out
[[gnu::hot]] void setPixelColor(unsigned pix, uint32_t c) override;
[[gnu::hot]] uint32_t getPixelColor(unsigned pix) const override;
unsigned getPins(uint8_t* pinArray = nullptr) const override;
unsigned getBusSize() const override { return sizeof(BusNetwork) + (isOk() ? _len * _UDPchannels : 0); }
void setPixelColor(uint16_t pix, uint32_t c) override;
uint32_t getPixelColor(uint16_t pix) const override;
uint8_t getPins(uint8_t* pinArray = nullptr) const override;
void show() override;
void cleanup();
@@ -377,16 +346,6 @@ struct BusConfig {
type = busType & 0x7F; // bit 7 may be/is hacked to include refresh info (1=refresh in off state, 0=no refresh)
size_t nPins = Bus::getNumberOfPins(type);
for (size_t i = 0; i < nPins; i++) pins[i] = ppins[i];
DEBUGBUS_PRINTF_P(PSTR("Bus: Config (%d-%d, type:%d, CO:%d, rev:%d, skip:%d, AW:%d kHz:%d, mA:%d/%d)\n"),
(int)start, (int)(start+len),
(int)type,
(int)colorOrder,
(int)reversed,
(int)skipAmount,
(int)autoWhite,
(int)frequency,
(int)milliAmpsPerLed, (int)milliAmpsMax
);
}
//validates start and length and extends total if needed
@@ -400,32 +359,21 @@ struct BusConfig {
if (start + count > total) total = start + count;
return true;
}
unsigned memUsage(unsigned nr = 0) const;
};
//fine tune power estimation constants for your setup
//you can set it to 0 if the ESP is powered by USB and the LEDs by external
#ifndef MA_FOR_ESP
#ifdef ESP8266
#define MA_FOR_ESP 80 //how much mA does the ESP use (Wemos D1 about 80mA)
#else
#define MA_FOR_ESP 120 //how much mA does the ESP use (ESP32 about 120mA)
#endif
#endif
class BusManager {
public:
BusManager() {};
static unsigned memUsage();
static uint16_t currentMilliamps() { return _milliAmpsUsed + MA_FOR_ESP; }
//utility to get the approx. memory usage of a given BusConfig
static uint32_t memUsage(BusConfig &bc);
static uint32_t memUsage(unsigned channels, unsigned count, unsigned buses = 1);
static uint16_t currentMilliamps() { return _milliAmpsUsed; }
static uint16_t ablMilliampsMax() { return _milliAmpsMax; }
static int add(const BusConfig &bc);
static int add(BusConfig &bc);
static void useParallelOutput(); // workaround for inaccessible PolyBus
static bool hasParallelOutput(); // workaround for inaccessible PolyBus
//do not call this method from system context (network callback)
static void removeAll();
@@ -436,37 +384,38 @@ class BusManager {
static void show();
static bool canAllShow();
static void setStatusPixel(uint32_t c);
[[gnu::hot]] static void setPixelColor(unsigned pix, uint32_t c);
[[gnu::hot]] static void setPixelColor(uint16_t pix, uint32_t c);
static void setBrightness(uint8_t b);
// for setSegmentCCT(), cct can only be in [-1,255] range; allowWBCorrection will convert it to K
// WARNING: setSegmentCCT() is a misleading name!!! much better would be setGlobalCCT() or just setCCT()
static void setSegmentCCT(int16_t cct, bool allowWBCorrection = false);
static inline void setMilliampsMax(uint16_t max) { _milliAmpsMax = max;}
static uint32_t getPixelColor(unsigned pix);
static uint32_t getPixelColor(uint16_t pix);
static inline int16_t getSegmentCCT() { return Bus::getCCT(); }
static Bus* getBus(uint8_t busNr);
//semi-duplicate of strip.getLengthTotal() (though that just returns strip._length, calculated in finalizeInit())
static uint16_t getTotalLength();
static inline uint8_t getNumBusses() { return busses.size(); }
static inline uint8_t getNumBusses() { return numBusses; }
static String getLEDTypesJSONString();
static inline ColorOrderMap& getColorOrderMap() { return colorOrderMap; }
private:
//static std::vector<std::unique_ptr<Bus>> busses; // we'd need C++ >11
static std::vector<Bus*> busses;
static uint8_t numBusses;
static Bus* busses[WLED_MAX_BUSSES+WLED_MIN_VIRTUAL_BUSSES];
static ColorOrderMap colorOrderMap;
static uint16_t _milliAmpsUsed;
static uint16_t _milliAmpsMax;
static uint8_t _parallelOutputs;
#ifdef ESP32_DATA_IDLE_HIGH
static void esp32RMTInvertIdle() ;
#endif
static uint8_t getNumVirtualBusses() {
int j = 0;
for (const auto &bus : busses) j += bus->isVirtual();
for (int i=0; i<numBusses; i++) if (busses[i]->isVirtual()) j++;
return j;
}
};
+387 -462
View File
File diff suppressed because it is too large Load Diff
+2 -3
View File
@@ -40,7 +40,7 @@ void longPressAction(uint8_t b)
{
if (!macroLongPress[b]) {
switch (b) {
case 0: setRandomColor(colPri); colorUpdated(CALL_MODE_BUTTON); break;
case 0: setRandomColor(col); colorUpdated(CALL_MODE_BUTTON); break;
case 1:
if(buttonBriDirection) {
if (bri == 255) break; // avoid unnecessary updates to brightness
@@ -230,7 +230,7 @@ void handleAnalog(uint8_t b)
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,colPri);
colorHStoRGB(aRead*256,255,col);
} else {
// otherwise use "double press" for segment selection
Segment& seg = strip.getSegment(macroDoublePress[b]);
@@ -375,7 +375,6 @@ void handleIO()
if (rlyPin>=0) {
pinMode(rlyPin, rlyOpenDrain ? OUTPUT_OPEN_DRAIN : OUTPUT);
digitalWrite(rlyPin, rlyMde);
delay(50); // wait for relay to switch and power to stabilize
}
offMode = false;
}
+57 -51
View File
@@ -118,9 +118,6 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
Bus::setCCTBlend(strip.cctBlending);
strip.setTargetFps(hw_led["fps"]); //NOP if 0, default 42 FPS
CJSON(useGlobalLedBuffer, hw_led[F("ld")]);
#if defined(ARDUINO_ARCH_ESP32) && !defined(CONFIG_IDF_TARGET_ESP32C3)
CJSON(useParallelI2S, hw_led[F("prl")]);
#endif
#ifndef WLED_DISABLE_2D
// 2D Matrix Settings
@@ -165,6 +162,34 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
DEBUG_PRINTF_P(PSTR("Heap before buses: %d\n"), ESP.getFreeHeap());
int s = 0; // bus iterator
if (fromFS) BusManager::removeAll(); // can't safely manipulate busses directly in network callback
unsigned mem = 0;
// determine if it is sensible to use parallel I2S outputs on ESP32 (i.e. more than 5 outputs = 1 I2S + 4 RMT)
bool useParallel = false;
#if defined(ARDUINO_ARCH_ESP32) && !defined(ARDUINO_ARCH_ESP32S2) && !defined(ARDUINO_ARCH_ESP32S3) && !defined(ARDUINO_ARCH_ESP32C3)
unsigned digitalCount = 0;
unsigned maxLedsOnBus = 0;
unsigned maxChannels = 0;
for (JsonObject elm : ins) {
unsigned type = elm["type"] | TYPE_WS2812_RGB;
unsigned len = elm["len"] | DEFAULT_LED_COUNT;
if (!Bus::isDigital(type)) continue;
if (!Bus::is2Pin(type)) {
digitalCount++;
unsigned channels = Bus::getNumberOfChannels(type);
if (len > maxLedsOnBus) maxLedsOnBus = len;
if (channels > maxChannels) maxChannels = channels;
}
}
DEBUG_PRINTF_P(PSTR("Maximum LEDs on a bus: %u\nDigital buses: %u\n"), maxLedsOnBus, digitalCount);
// we may remove 300 LEDs per bus limit when NeoPixelBus is updated beyond 2.9.0
if (maxLedsOnBus <= 300 && digitalCount > 5) {
DEBUG_PRINTLN(F("Switching to parallel I2S."));
useParallel = true;
BusManager::useParallelOutput();
mem = BusManager::memUsage(maxChannels, maxLedsOnBus, 8); // use alternate memory calculation
}
#endif
for (JsonObject elm : ins) {
if (s >= WLED_MAX_BUSSES+WLED_MIN_VIRTUAL_BUSSES) break;
@@ -195,11 +220,24 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
maMax = 0;
}
ledType |= refresh << 7; // hack bit 7 to indicate strip requires off refresh
busConfigs.push_back(std::move(BusConfig(ledType, pins, start, length, colorOrder, reversed, skipFirst, AWmode, freqkHz, useGlobalLedBuffer, maPerLed, maMax)));
doInitBusses = true; // finalization done in beginStrip()
if (fromFS) {
BusConfig bc = BusConfig(ledType, pins, start, length, colorOrder, reversed, skipFirst, AWmode, freqkHz, useGlobalLedBuffer, maPerLed, maMax);
if (useParallel && s < 8) {
// if for some unexplained reason the above pre-calculation was wrong, update
unsigned memT = BusManager::memUsage(bc); // includes x8 memory allocation for parallel I2S
if (memT > mem) mem = memT; // if we have unequal LED count use the largest
} else
mem += BusManager::memUsage(bc); // includes global buffer
if (mem <= MAX_LED_MEMORY) if (BusManager::add(bc) == -1) break; // finalization will be done in WLED::beginStrip()
} else {
if (busConfigs[s] != nullptr) delete busConfigs[s];
busConfigs[s] = new BusConfig(ledType, pins, start, length, colorOrder, reversed, skipFirst, AWmode, freqkHz, useGlobalLedBuffer, maPerLed, maMax);
doInitBusses = true; // finalization done in beginStrip()
}
s++;
}
DEBUG_PRINTF_P(PSTR("LED buffer size: %uB\n"), mem);
DEBUG_PRINTF_P(PSTR("Heap after buses: %d\n"), ESP.getFreeHeap());
}
if (hw_led["rev"]) BusManager::getBus(0)->setReversed(true); //set 0.11 global reversed setting for first bus
@@ -636,42 +674,19 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
return (doc["sv"] | true);
}
static const char s_cfg_json[] PROGMEM = "/cfg.json";
bool backupConfig() {
return backupFile(s_cfg_json);
}
bool restoreConfig() {
return restoreFile(s_cfg_json);
}
bool verifyConfig() {
return validateJsonFile(s_cfg_json);
}
// rename config file and reboot
// if the cfg file doesn't exist, such as after a reset, do nothing
void resetConfig() {
if (WLED_FS.exists(s_cfg_json)) {
DEBUG_PRINTLN(F("Reset config"));
char backupname[32];
snprintf_P(backupname, sizeof(backupname), PSTR("/rst.%s"), &s_cfg_json[1]);
WLED_FS.rename(s_cfg_json, backupname);
doReboot = true;
}
}
bool deserializeConfigFromFS() {
[[maybe_unused]] bool success = deserializeConfigSec();
void deserializeConfigFromFS() {
bool success = deserializeConfigSec();
#ifdef WLED_ADD_EEPROM_SUPPORT
if (!success) { //if file does not exist, try reading from EEPROM
deEEPSettings();
return true;
return;
}
#endif
if (!requestJSONBufferLock(1)) return false;
if (!requestJSONBufferLock(1)) return;
DEBUG_PRINTLN(F("Reading settings from /cfg.json..."));
@@ -681,11 +696,17 @@ bool deserializeConfigFromFS() {
#ifdef WLED_ADD_EEPROM_SUPPORT
deEEPSettings();
#endif
// save default values to /cfg.json
// call readFromConfig() with an empty object so that usermods can initialize to defaults prior to saving
JsonObject empty = JsonObject();
UsermodManager::readFromConfig(empty);
serializeConfig();
// init Ethernet (in case default type is set at compile time)
#ifdef WLED_USE_ETHERNET
WLED::instance().initEthernet();
#endif
return true; // config does not exist (we will need to save it once strip is initialised)
return;
}
// NOTE: This routine deserializes *and* applies the configuration
@@ -694,12 +715,11 @@ bool deserializeConfigFromFS() {
bool needsSave = deserializeConfig(root, true);
releaseJSONBufferLock();
return needsSave;
if (needsSave) serializeConfig(); // usermods required new parameters
}
void serializeConfig() {
serializeConfigSec();
backupConfig(); // backup before writing new config
DEBUG_PRINTLN(F("Writing settings to /cfg.json..."));
@@ -804,9 +824,6 @@ void serializeConfig() {
hw_led["fps"] = strip.getTargetFps();
hw_led[F("rgbwm")] = Bus::getGlobalAWMode(); // global auto white mode override
hw_led[F("ld")] = useGlobalLedBuffer;
#if defined(ARDUINO_ARCH_ESP32) && !defined(CONFIG_IDF_TARGET_ESP32C3)
hw_led[F("prl")] = BusManager::hasParallelOutput();
#endif
#ifndef WLED_DISABLE_2D
// 2D Matrix Settings
@@ -831,19 +848,8 @@ void serializeConfig() {
JsonArray hw_led_ins = hw_led.createNestedArray("ins");
for (size_t s = 0; s < BusManager::getNumBusses(); s++) {
DEBUG_PRINTF_P(PSTR("Cfg: Saving bus #%u\n"), s);
Bus *bus = BusManager::getBus(s);
if (!bus || bus->getLength()==0) break;
DEBUG_PRINTF_P(PSTR(" (%d-%d, type:%d, CO:%d, rev:%d, skip:%d, AW:%d kHz:%d, mA:%d/%d)\n"),
(int)bus->getStart(), (int)(bus->getStart()+bus->getLength()),
(int)(bus->getType() & 0x7F),
(int)bus->getColorOrder(),
(int)bus->isReversed(),
(int)bus->skippedLeds(),
(int)bus->getAutoWhiteMode(),
(int)bus->getFrequency(),
(int)bus->getLEDCurrent(), (int)bus->getMaxCurrent()
);
JsonObject ins = hw_led_ins.createNestedObject();
ins["start"] = bus->getStart();
ins["len"] = bus->getLength();
+16 -20
View File
@@ -37,7 +37,7 @@
#endif
#ifndef WLED_MAX_USERMODS
#if defined(ESP8266) || defined(CONFIG_IDF_TARGET_ESP32S2)
#ifdef ESP8266
#define WLED_MAX_USERMODS 4
#else
#define WLED_MAX_USERMODS 6
@@ -49,31 +49,31 @@
#define WLED_MAX_DIGITAL_CHANNELS 3
#define WLED_MAX_ANALOG_CHANNELS 5
#define WLED_MAX_BUSSES 4 // will allow 3 digital & 1 analog RGB
#define WLED_MIN_VIRTUAL_BUSSES 3
#define WLED_MIN_VIRTUAL_BUSSES 2
#else
#define WLED_MAX_ANALOG_CHANNELS (LEDC_CHANNEL_MAX*LEDC_SPEED_MODE_MAX)
#if defined(CONFIG_IDF_TARGET_ESP32C3) // 2 RMT, 6 LEDC, only has 1 I2S but NPB does not support it ATM
#define WLED_MAX_BUSSES 6 // will allow 2 digital & 2 analog RGB or 6 PWM white
#define WLED_MAX_DIGITAL_CHANNELS 2
//#define WLED_MAX_ANALOG_CHANNELS 6
#define WLED_MIN_VIRTUAL_BUSSES 4
#define WLED_MIN_VIRTUAL_BUSSES 3
#elif defined(CONFIG_IDF_TARGET_ESP32S2) // 4 RMT, 8 LEDC, only has 1 I2S bus, supported in NPB
// the 5th bus (I2S) will prevent Audioreactive usermod from functioning (it is last used though)
#define WLED_MAX_BUSSES 14 // will allow 12 digital & 2 analog RGB
#define WLED_MAX_DIGITAL_CHANNELS 12 // x4 RMT + x1/x8 I2S0
#define WLED_MAX_BUSSES 7 // will allow 5 digital & 2 analog RGB
#define WLED_MAX_DIGITAL_CHANNELS 5
//#define WLED_MAX_ANALOG_CHANNELS 8
#define WLED_MIN_VIRTUAL_BUSSES 3
#elif defined(CONFIG_IDF_TARGET_ESP32S3) // 4 RMT, 8 LEDC, has 2 I2S but NPB does not support them ATM
#define WLED_MAX_BUSSES 6 // will allow 4 digital & 2 analog RGB
#define WLED_MAX_DIGITAL_CHANNELS 4
//#define WLED_MAX_ANALOG_CHANNELS 8
#define WLED_MIN_VIRTUAL_BUSSES 4
#elif defined(CONFIG_IDF_TARGET_ESP32S3) // 4 RMT, 8 LEDC, has 2 I2S but NPB supports parallel x8 LCD on I2S1
#define WLED_MAX_BUSSES 14 // will allow 12 digital & 2 analog RGB
#define WLED_MAX_DIGITAL_CHANNELS 12 // x4 RMT + x8 I2S-LCD
//#define WLED_MAX_ANALOG_CHANNELS 8
#define WLED_MIN_VIRTUAL_BUSSES 6
#else
// the last digital bus (I2S0) will prevent Audioreactive usermod from functioning
#define WLED_MAX_BUSSES 19 // will allow 16 digital & 3 analog RGB
#define WLED_MAX_DIGITAL_CHANNELS 16 // x1/x8 I2S1 + x8 RMT
#define WLED_MAX_BUSSES 20 // will allow 17 digital & 3 analog RGB
#define WLED_MAX_DIGITAL_CHANNELS 17
//#define WLED_MAX_ANALOG_CHANNELS 16
#define WLED_MIN_VIRTUAL_BUSSES 6
#define WLED_MIN_VIRTUAL_BUSSES 4
#endif
#endif
#else
@@ -115,7 +115,7 @@
#endif
#endif
#if defined(ESP8266) || defined(CONFIG_IDF_TARGET_ESP32S2)
#ifdef ESP8266
#define WLED_MAX_COLOR_ORDER_MAPPINGS 5
#else
#define WLED_MAX_COLOR_ORDER_MAPPINGS 10
@@ -125,7 +125,7 @@
#undef WLED_MAX_LEDMAPS
#endif
#ifndef WLED_MAX_LEDMAPS
#if defined(ESP8266) || defined(CONFIG_IDF_TARGET_ESP32S2)
#ifdef ESP8266
#define WLED_MAX_LEDMAPS 10
#else
#define WLED_MAX_LEDMAPS 16
@@ -473,8 +473,6 @@
#ifndef MAX_LEDS
#ifdef ESP8266
#define MAX_LEDS 1664 //can't rely on memory limit to limit this to 1600 LEDs
#elif defined(CONFIG_IDF_TARGET_ESP32S2)
#define MAX_LEDS 2048 //due to memory constraints
#else
#define MAX_LEDS 8192
#endif
@@ -484,9 +482,7 @@
#ifdef ESP8266
#define MAX_LED_MEMORY 4000
#else
#if defined(ARDUINO_ARCH_ESP32S2)
#define MAX_LED_MEMORY 16000
#elif defined(ARDUINO_ARCH_ESP32C3)
#if defined(ARDUINO_ARCH_ESP32S2) || defined(ARDUINO_ARCH_ESP32C3)
#define MAX_LED_MEMORY 32000
#else
#define MAX_LED_MEMORY 64000
+2 -2
View File
@@ -16,7 +16,7 @@ function isI(n) { return n === +n && n === (n|0); } // isInteger
function toggle(el) { gId(el).classList.toggle("hide"); gId('No'+el).classList.toggle("hide"); }
function tooltip(cont=null) {
d.querySelectorAll((cont?cont+" ":"")+"[title]").forEach((element)=>{
element.addEventListener("pointerover", ()=>{
element.addEventListener("mouseover", ()=>{
// save title
element.setAttribute("data-title", element.getAttribute("title"));
const tooltip = d.createElement("span");
@@ -41,7 +41,7 @@ function tooltip(cont=null) {
tooltip.classList.add("visible");
});
element.addEventListener("pointerout", ()=>{
element.addEventListener("mouseout", ()=>{
d.querySelectorAll('.tooltip').forEach((tooltip)=>{
tooltip.classList.remove("visible");
d.body.removeChild(tooltip);
+1 -1
View File
@@ -793,7 +793,7 @@ input[type=range]::-moz-range-thumb {
/* buttons */
.btn {
padding: 8px;
margin: 10px 4px;
/*margin: 10px 4px;*/
width: 230px;
font-size: 19px;
color: var(--c-d);
+1 -1
View File
@@ -128,7 +128,7 @@
<div style="padding: 8px 0;" id="btns">
<button class="btn btn-xs" title="File editor" type="button" id="edit" onclick="window.location.href=getURL('/edit')"><i class="icons btn-icon">&#xe2c6;</i></button>
<button class="btn btn-xs" title="Pixel Magic Tool" type="button" id="pxmb" onclick="window.location.href=getURL('/pxmagic.htm')"><i class="icons btn-icon">&#xe410;</i></button>
<button class="btn btn-xs" title="Add custom palette" type="button" id="adPal" onclick="window.location.href=getURL('/cpal.htm')"><i class="icons btn-icon">&#xe18a;</i></button>
<button class="btn btn-xs" title="Add custom palette" type="button" onclick="window.location.href=getURL('/cpal.htm')"><i class="icons btn-icon">&#xe18a;</i></button>
<button class="btn btn-xs" title="Remove last custom palette" type="button" id="rmPal" onclick="palettesData=null;localStorage.removeItem('wledPalx');requestJson({rmcpal:true});setTimeout(loadPalettes,250,loadPalettesData);"><i class="icons btn-icon">&#xe037;</i></button>
</div>
<p class="labels hd" id="pall"><i class="icons sel-icon" onclick="tglHex()">&#xe2b3;</i> Color palette</p>
+30 -224
View File
@@ -688,8 +688,6 @@ function parseInfo(i) {
// gId("filterVol").classList.add("hide"); hideModes(" ♪"); // hide volume reactive effects
// gId("filterFreq").classList.add("hide"); hideModes(" ♫"); // hide frequency reactive effects
// }
// Check for version upgrades on page load
checkVersionUpgrade(i);
}
//https://stackoverflow.com/questions/2592092/executing-script-elements-inserted-with-innerhtml
@@ -775,8 +773,8 @@ function populateSegments(s)
}
let segp = `<div id="segp${i}" class="sbs">`+
`<i class="icons slider-icon pwr ${inst.on ? "act":""}" id="seg${i}pwr" title="Power" onclick="setSegPwr(${i})">&#xe08f;</i>`+
`<div class="sliderwrap il" title="Opacity/Brightness">`+
`<i class="icons slider-icon pwr ${inst.on ? "act":""}" id="seg${i}pwr" onclick="setSegPwr(${i})">&#xe08f;</i>`+
`<div class="sliderwrap il">`+
`<input id="seg${i}bri" class="noslide" onchange="setSegBri(${i})" oninput="updateTrail(this)" max="255" min="1" type="range" value="${inst.bri}" />`+
`<div class="sliderdisplay"></div>`+
`</div>`+
@@ -812,7 +810,7 @@ function populateSegments(s)
cn += `<div class="seg lstI ${i==s.mainseg && !simplifiedUI ? 'selected' : ''} ${exp ? "expanded":""}" id="seg${i}" data-set="${inst.set}">`+
`<label class="check schkl ${smpl}">`+
`<input type="checkbox" id="seg${i}sel" onchange="selSeg(${i})" ${inst.sel ? "checked":""}>`+
`<span class="checkmark" title="Select"></span>`+
`<span class="checkmark"></span>`+
`</label>`+
`<div class="segname ${smpl}" onclick="selSegEx(${i})">`+
`<i class="icons e-icon frz" id="seg${i}frz" title="(un)Freeze" onclick="event.preventDefault();tglFreeze(${i});">&#x${inst.frz ? (li.live && li.liveseg==i?'e410':'e0e8') : 'e325'};</i>`+
@@ -1661,17 +1659,13 @@ function setEffectParameters(idx)
paOnOff[0] = paOnOff[0].substring(0,dPos);
}
if (paOnOff.length>0 && paOnOff[0] != "!") text = paOnOff[0];
gId("adPal").classList.remove("hide");
if (lastinfo.cpalcount>0) gId("rmPal").classList.remove("hide");
} else {
// disable palette list
text += ' not used';
palw.style.display = "none";
gId("adPal").classList.add("hide");
gId("rmPal").classList.add("hide");
// Close palette dialog if not available
if (palw.lastElementChild.tagName == "DIALOG") {
palw.lastElementChild.close();
if (gId("palw").lastElementChild.tagName == "DIALOG") {
gId("palw").lastElementChild.close();
}
}
pall.innerHTML = icon + text;
@@ -1885,7 +1879,7 @@ function makeSeg()
function resetUtil(off=false)
{
gId('segutil').innerHTML = `<div class="seg btn btn-s${off?' off':''}" style="padding:0;margin-bottom:12px;">`
+ '<label class="check schkl"><input type="checkbox" id="selall" onchange="selSegAll(this)"><span class="checkmark" title="Select all"></span></label>'
+ '<label class="check schkl"><input type="checkbox" id="selall" onchange="selSegAll(this)"><span class="checkmark"></span></label>'
+ `<div class="segname" ${off?'':'onclick="makeSeg()"'}><i class="icons btn-icon">&#xe18a;</i>Add segment</div>`
+ '<div class="pop hide" onclick="event.stopPropagation();">'
+ `<i class="icons g-icon" title="Select group" onclick="this.nextElementSibling.classList.toggle('hide');">&#xE34B;</i>`
@@ -2655,28 +2649,28 @@ function fromRgb()
var g = gId('sliderG').value;
var b = gId('sliderB').value;
setPicker(`rgb(${r},${g},${b})`);
let cd = gId('csl').children[csel]; // color slots
cd.dataset.r = r;
cd.dataset.g = g;
cd.dataset.b = b;
setCSL(cd);
let cd = gId('csl').children; // color slots
cd[csel].dataset.r = r;
cd[csel].dataset.g = g;
cd[csel].dataset.b = b;
setCSL(cd[csel]);
}
function fromW()
{
let w = gId('sliderW');
let cd = gId('csl').children[csel]; // color slots
cd.dataset.w = w.value;
setCSL(cd);
let cd = gId('csl').children; // color slots
cd[csel].dataset.w = w.value;
setCSL(cd[csel]);
updateTrail(w);
}
// sr 0: from RGB sliders, 1: from picker, 2: from hex
function setColor(sr)
{
var cd = gId('csl').children[csel]; // color slots
let cdd = cd.dataset;
let w = parseInt(cdd.w), r = parseInt(cdd.r), g = parseInt(cdd.g), b = parseInt(cdd.b);
var cd = gId('csl').children; // color slots
let cdd = cd[csel].dataset;
let w = 0, r,g,b;
if (sr == 1 && isRgbBlack(cdd)) cpick.color.setChannel('hsv', 'v', 100);
if (sr != 2 && hasWhite) w = parseInt(gId('sliderW').value);
var col = cpick.color.rgb;
@@ -2684,7 +2678,7 @@ function setColor(sr)
cdd.g = g = hasRGB ? col.g : w;
cdd.b = b = hasRGB ? col.b : w;
cdd.w = w;
setCSL(cd);
setCSL(cd[csel]);
var obj = {"seg": {"col": [[],[],[]]}};
obj.seg.col[csel] = [r, g, b, w];
requestJson(obj);
@@ -2833,7 +2827,7 @@ function search(field, listId = null) {
// restore default preset sorting if no search term is entered
if (!search) {
if (listId === 'pcont') { populatePresets(); return; }
if (listId === 'pcont') { populatePresets(); return; }
if (listId === 'pallist') {
let id = parseInt(d.querySelector('#pallist input[name="palette"]:checked').value); // preserve selected palette
populatePalettes();
@@ -2852,16 +2846,12 @@ function search(field, listId = null) {
// filter list items but leave (Default & Solid) always visible
const listItems = gId(listId).querySelectorAll('.lstI');
listItems.forEach((listItem, i) => {
if (listId !== 'pcont' && i === 0) return;
listItems.forEach((listItem,i)=>{
if (listId!=='pcont' && i===0) return;
const listItemName = listItem.querySelector('.lstIname').innerText.toUpperCase();
const searchIndex = listItemName.indexOf(field.value.toUpperCase());
if (searchIndex < 0) {
listItem.dataset.searchIndex = Number.MAX_SAFE_INTEGER;
} else {
listItem.dataset.searchIndex = searchIndex;
}
listItem.style.display = (searchIndex < 0) && !listItem.classList.contains("selected") ? 'none' : '';
listItem.style.display = (searchIndex < 0) ? 'none' : '';
listItem.dataset.searchIndex = searchIndex;
});
// sort list items by search index and name
@@ -2930,11 +2920,11 @@ function filterFx() {
inputField.value = '';
inputField.focus();
clean(inputField.nextElementSibling);
gId("fxlist").querySelectorAll('.lstI').forEach((listItem, i) => {
gId("fxlist").querySelectorAll('.lstI').forEach((listItem,i) => {
const listItemName = listItem.querySelector('.lstIname').innerText;
let hide = false;
gId("filters").querySelectorAll("input[type=checkbox]").forEach((e) => { if (e.checked && !listItemName.includes(e.dataset.flt)) hide = i > 0 /*true*/; });
listItem.style.display = hide && !listItem.classList.contains("selected") ? 'none' : '';
gId("filters").querySelectorAll("input[type=checkbox]").forEach((e) => { if (e.checked && !listItemName.includes(e.dataset.flt)) hide = i>0 /*true*/; });
listItem.style.display = hide ? 'none' : '';
});
}
@@ -3120,9 +3110,10 @@ function mergeDeep(target, ...sources)
return mergeDeep(target, ...sources);
}
function tooltip(cont=null) {
function tooltip(cont=null)
{
d.querySelectorAll((cont?cont+" ":"")+"[title]").forEach((element)=>{
element.addEventListener("pointerover", ()=>{
element.addEventListener("mouseover", ()=>{
// save title
element.setAttribute("data-title", element.getAttribute("title"));
const tooltip = d.createElement("span");
@@ -3147,7 +3138,7 @@ function tooltip(cont=null) {
tooltip.classList.add("visible");
});
element.addEventListener("pointerout", ()=>{
element.addEventListener("mouseout", ()=>{
d.querySelectorAll('.tooltip').forEach((tooltip)=>{
tooltip.classList.remove("visible");
d.body.removeChild(tooltip);
@@ -3250,191 +3241,6 @@ function simplifyUI() {
gId("btns").style.display = "none";
}
// Version reporting feature
var versionCheckDone = false;
function checkVersionUpgrade(info) {
// Only check once per page load
if (versionCheckDone) return;
versionCheckDone = true;
// Suppress feature if in AP mode (no internet connection available)
if (info.wifi && info.wifi.ap) return;
// Fetch version-info.json using existing /edit endpoint
fetch(getURL('/edit?edit=/version-info.json'), {
method: 'get'
})
.then(res => {
if (res.status === 404) {
// File doesn't exist - first install, show install prompt
showVersionUpgradePrompt(info, null, info.ver);
return null;
}
if (!res.ok) {
throw new Error('Failed to fetch version-info.json');
}
return res.json();
})
.then(versionInfo => {
if (!versionInfo) return; // 404 case already handled
// Check if user opted out
if (versionInfo.neverAsk) return;
// Check if version has changed
const currentVersion = info.ver;
const storedVersion = versionInfo.version || '';
if (storedVersion && storedVersion !== currentVersion) {
// Version has changed, show upgrade prompt
showVersionUpgradePrompt(info, storedVersion, currentVersion);
} else if (!storedVersion) {
// Empty version in file, show install prompt
showVersionUpgradePrompt(info, null, currentVersion);
}
})
.catch(e => {
console.log('Failed to load version-info.json', e);
});
}
function showVersionUpgradePrompt(info, oldVersion, newVersion) {
// Determine if this is an install or upgrade
const isInstall = !oldVersion;
// Create overlay and dialog
const overlay = d.createElement('div');
overlay.id = 'versionUpgradeOverlay';
overlay.style.cssText = 'position:fixed;top:0;left:0;width:100%;height:100%;background:rgba(0,0,0,0.7);z-index:10000;display:flex;align-items:center;justify-content:center;';
const dialog = d.createElement('div');
dialog.style.cssText = 'background:var(--c-1);border-radius:10px;padding:25px;max-width:500px;margin:20px;box-shadow:0 4px 6px rgba(0,0,0,0.3);';
// Build contextual message based on install vs upgrade
const title = isInstall
? '🎉 Thank you for installing WLED!'
: '🎉 WLED Upgrade Detected!';
const description = isInstall
? `You are now running WLED <strong style="text-wrap: nowrap">${newVersion}</strong>.`
: `Your WLED has been upgraded from <strong style="text-wrap: nowrap">${oldVersion}</strong> to <strong style="text-wrap: nowrap">${newVersion}</strong>.`;
const question = 'Help make WLED better with a one-time hardware report? It includes only device details like chip type, LED count, etc. — never personal data or your activities.'
dialog.innerHTML = `
<h2 style="margin-top:0;color:var(--c-f);">${title}</h2>
<p style="color:var(--c-f);">${description}</p>
<p style="color:var(--c-f);">${question}</p>
<p style="color:var(--c-f);font-size:0.9em;">
<a href="https://kno.wled.ge/about/privacy-policy/" target="_blank" style="color:var(--c-6);">Learn more about what data is collected and why</a>
</p>
<div style="margin-top:20px;">
<button id="versionReportYes" class="btn">Yes</button>
<button id="versionReportNo" class="btn">Not Now</button>
<button id="versionReportNever" class="btn">Never Ask</button>
</div>
`;
overlay.appendChild(dialog);
d.body.appendChild(overlay);
// Add event listeners
gId('versionReportYes').addEventListener('click', () => {
reportUpgradeEvent(info, oldVersion);
d.body.removeChild(overlay);
});
gId('versionReportNo').addEventListener('click', () => {
// Don't update version, will ask again on next load
d.body.removeChild(overlay);
});
gId('versionReportNever').addEventListener('click', () => {
updateVersionInfo(newVersion, true);
d.body.removeChild(overlay);
showToast('You will not be asked again.');
});
}
function reportUpgradeEvent(info, oldVersion) {
showToast('Reporting upgrade...');
// Fetch fresh data from /json/info endpoint as requested
fetch(getURL('/json/info'), {
method: 'get'
})
.then(res => res.json())
.then(infoData => {
// Map to UpgradeEventRequest structure per OpenAPI spec
// Required fields: deviceId, version, previousVersion, releaseName, chip, ledCount, isMatrix, bootloaderSHA256
const upgradeData = {
deviceId: infoData.deviceId, // Use anonymous unique device ID
version: infoData.ver || '', // Current version string
previousVersion: oldVersion || '', // Previous version from version-info.json
releaseName: infoData.release || '', // Release name (e.g., "WLED 0.15.0")
chip: infoData.arch || '', // Chip architecture (esp32, esp8266, etc)
ledCount: infoData.leds ? infoData.leds.count : 0, // Number of LEDs
isMatrix: !!(infoData.leds && infoData.leds.matrix), // Whether it's a 2D matrix setup
bootloaderSHA256: infoData.bootloaderSHA256 || '', // Bootloader SHA256 hash
brand: infoData.brand, // Device brand (always present)
product: infoData.product, // Product name (always present)
flashSize: infoData.flash // Flash size (always present)
};
// Add optional fields if available
if (infoData.psram !== undefined) upgradeData.psramSize = Math.round(infoData.psram / (1024 * 1024)); // convert bytes to MB
// Note: partitionSizes not currently available in /json/info endpoint
// Make AJAX call to postUpgradeEvent API
return fetch('https://usage.wled.me/api/usage/upgrade', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(upgradeData)
});
})
.then(res => {
if (res.ok) {
showToast('Thank you for reporting!');
updateVersionInfo(info.ver, false);
} else {
showToast('Report failed. Please try again later.', true);
// Do NOT update version info on failure - user will be prompted again
}
})
.catch(e => {
console.log('Failed to report upgrade', e);
showToast('Report failed. Please try again later.', true);
// Do NOT update version info on error - user will be prompted again
});
}
function updateVersionInfo(version, neverAsk) {
const versionInfo = {
version: version,
neverAsk: neverAsk
};
// Create a Blob with JSON content and use /upload endpoint
const blob = new Blob([JSON.stringify(versionInfo)], {type: 'application/json'});
const formData = new FormData();
formData.append('data', blob, 'version-info.json');
fetch(getURL('/upload'), {
method: 'POST',
body: formData
})
.then(res => res.text())
.then(data => {
console.log('Version info updated', data);
})
.catch(e => {
console.log('Failed to update version-info.json', e);
});
}
size();
_C.style.setProperty('--n', N);
+48 -96
View File
@@ -6,7 +6,8 @@
<title>LED Settings</title>
<script src="common.js" async type="text/javascript"></script>
<script>
var maxB=1,maxD=1,maxA=1,maxV=0,maxM=4000,maxPB=2048,maxL=1664,maxCO=5; //maximum bytes for LED allocation: 4kB for 8266, 32kB for 32
var laprev=55,maxB=1,maxD=1,maxA=1,maxV=0,maxM=4000,maxPB=2048,maxL=1664,maxCO=5,maxLbquot=0; //maximum bytes for LED allocation: 4kB for 8266, 32kB for 32
var oMaxB=1;
var customStarts=false,startsDirty=[];
function off(n) { gN(n).value = -1;}
// these functions correspond to C macros found in const.h
@@ -23,7 +24,6 @@
function is16b(t) { return !!(gT(t).c & 0x10); } // is digital 16 bit type
function mustR(t) { return !!(gT(t).c & 0x20); } // Off refresh is mandatory
function numPins(t){ return Math.max(gT(t).t.length, 1); } // type length determines number of GPIO pins
function chrID(x) { return String.fromCharCode((x<10?48:55)+x); }
function S() {
getLoc();
loadJS(getURL('/settings/s.js?p=2'), false, ()=>{
@@ -42,20 +42,16 @@
if (loc) d.Sf.action = getURL('/settings/leds');
}
function bLimits(b,v,p,m,l,o=5,d=2,a=6) {
maxB = b; // maxB - max physical (analog + digital) buses: 32 - ESP32, 14 - S3/S2, 6 - C3, 4 - 8266
maxD = d; // maxD - max digital channels (can be changed if using ESP32 parallel I2S): 16 - ESP32, 12 - S3/S2, 2 - C3, 3 - 8266
maxA = a; // maxA - max analog channels: 16 - ESP32, 8 - S3/S2, 6 - C3, 5 - 8266
maxV = v; // maxV - min virtual buses: 4 - ESP32/S3, 3 - S2/C3, 2 - ESP8266
maxPB = p; // maxPB - max LEDs per bus
maxM = m; // maxM - max LED memory
maxL = l; // maxL - max LEDs (will serve to determine ESP >1664 == ESP32)
maxCO = o; // maxCO - max Color Order mappings
// maxB - max buses (can be changed if using ESP32 parallel I2S)
// maxD - max digital channels (can be changed if using ESP32 parallel I2S)
// maxA - max analog channels
// maxV - min virtual buses
// maxPB - max LEDs per bus
// maxM - max LED memory
// maxL - max LEDs (will serve to determine ESP >1664 == ESP32)
// maxCO - max Color Order mappings
oMaxB = maxB = b; maxD = d, maxA = a, maxV = v; maxM = m; maxPB = p; maxL = l; maxCO = o;
}
function is8266() { return maxA == 5 && maxD == 3; } // NOTE: see const.h
function is32() { return maxA == 16 && maxD == 16; } // NOTE: see const.h
function isC3() { return maxA == 6 && maxD == 2; } // NOTE: see const.h
function isS2() { return maxA == 8 && maxD == 12 && maxV == 4; } // NOTE: see const.h
function isS3() { return maxA == 8 && maxD == 12 && maxV == 6; } // NOTE: see const.h
function pinsOK() {
var ok = true;
var nList = d.Sf.querySelectorAll("#mLC input[name^=L]");
@@ -143,7 +139,7 @@
gId("ppldis").style.display = ppl ? 'inline' : 'none';
// set PPL minimum value and clear actual PPL limit if ABL is disabled
d.Sf.querySelectorAll("#mLC input[name^=MA]").forEach((i,x)=>{
var n = chrID(x);
var n = String.fromCharCode((x<10?48:55)+x);
gId("PSU"+n).style.display = ppl ? "inline" : "none";
const t = parseInt(d.Sf["LT"+n].value); // LED type SELECT
const c = parseInt(d.Sf["LC"+n].value); //get LED count
@@ -174,7 +170,7 @@
// select appropriate LED current
d.Sf.querySelectorAll("#mLC select[name^=LAsel]").forEach((sel,x)=>{
sel.value = 0; // set custom
var n = chrID(x);
var n = String.fromCharCode((x<10?48:55)+x);
if (en)
switch (parseInt(d.Sf["LA"+n].value)) {
case 0: break; // disable ABL
@@ -198,7 +194,6 @@
let dbl = 0;
let ch = 3*hasRGB(t) + hasW(t) + hasCCT(t);
let mul = 1;
if (d.Sf.LD.checked) dbl = len * ch; // global buffer
if (isDig(t)) {
if (is16b(t)) len *= 2; // 16 bit LEDs
if (maxM < 10000 && d.getElementsByName("L0"+n)[0].value == 3) { //8266 DMA uses 5x the mem
@@ -207,6 +202,7 @@
if (maxM >= 10000) { //ESP32 RMT uses double buffer?
mul = 2;
}
if (d.Sf.LD.checked) dbl = len * ch; // double buffering
}
return len * ch * mul + dbl;
}
@@ -217,6 +213,7 @@
let busMA = 0;
let sLC = 0, sPC = 0, sDI = 0, maxLC = 0;
const abl = d.Sf.ABL.checked;
maxB = oMaxB; // TODO make sure we start with all possible buses
let setPinConfig = (n,t) => {
let p0d = "GPIO:";
let p1d = "";
@@ -254,15 +251,13 @@
}
// enable/disable LED fields
updateTypeDropdowns(); // restrict bus types in dropdowns to max allowed digital/analog buses
let dC = 0; // count of digital buses (for parallel I2S)
let LTs = d.Sf.querySelectorAll("#mLC select[name^=LT]");
LTs.forEach((s,i)=>{
if (i < LTs.length-1) s.disabled = true; // prevent changing type (as we can't update options)
// is the field a LED type?
var n = s.name.substring(2);
var t = parseInt(s.value);
memu += getMem(t, n); // calc memory
dC += (isDig(t) && !isD2P(t));
setPinConfig(n,t);
gId("abl"+n).style.display = (!abl || !isDig(t)) ? "none" : "inline"; // show/hide individual ABL settings
if (change) { // did we change LED type?
@@ -275,7 +270,7 @@
gRGBW |= hasW(t); // RGBW checkbox
gId("co"+n).style.display = (isVir(t) || isAna(t)) ? "none":"inline"; // hide color order for PWM
gId("dig"+n+"w").style.display = (isDig(t) && hasW(t)) ? "inline":"none"; // show swap channels dropdown
gId("dig"+n+"w").querySelector("[data-opt=CCT]").disabled = !hasCCT(t); // disable WW/CW swapping
gId("dig"+n+"w").querySelector("[data-opt=CCT]").disabled = !hasCCT(t); // disable WW/CW swapping
if (!(isDig(t) && hasW(t))) d.Sf["WO"+n].value = 0; // reset swapping
gId("dig"+n+"c").style.display = (isAna(t)) ? "none":"inline"; // hide count for analog
gId("dig"+n+"r").style.display = (isVir(t)) ? "none":"inline"; // hide reversed for virtual
@@ -293,20 +288,16 @@
d.Sf.CR.checked = false;
}
// update start indexes, max values, calculate current, etc
let sameType = 0;
var nList = d.Sf.querySelectorAll("#mLC input[name^=L]");
nList.forEach((LC,i)=>{
let nm = LC.name.substring(0,2); // field name
let n = LC.name.substring(2); // bus number
let t = parseInt(d.Sf["LT"+n].value); // LED type SELECT
if (isDig(t)) {
if (sameType == 0) sameType = t; // first bus type
else if (sameType != t) sameType = -1; // different bus type
}
// do we have a led count field
if (nm=="LC") {
let c = parseInt(LC.value,10); //get LED count
if (!customStarts || !startsDirty[n]) gId("ls"+n).value = sLC; //update start value
if (c > 300 && i < 8) maxB = oMaxB - Math.max(maxD-7,0); //TODO: hard limit for buses when using ESP32 parallel I2S
if (!customStarts || !startsDirty[n]) gId("ls"+n).value=sLC; //update start value
gId("ls"+n).disabled = !customStarts; //enable/disable field editing
if (c) {
let s = parseInt(gId("ls"+n).value); //start value
@@ -360,13 +351,6 @@
else LC.style.color = d.ro_gpio.some((e)=>e==parseInt(LC.value)) ? "orange" : "#fff";
}
});
if (is32() || isS2() || isS3()) {
if (maxLC > 600 || dC < 2 || sameType <= 0) {
d.Sf["PR"].checked = false;
gId("prl").classList.add("hide");
} else
gId("prl").classList.remove("hide");
} else d.Sf["PR"].checked = false;
// distribute ABL current if not using PPL
enPPL(sDI);
@@ -396,15 +380,10 @@
gId('psu').innerHTML = s;
gId('psu2').innerHTML = s2;
gId("json").style.display = d.Sf.IT.value==8 ? "" : "none";
// show/hide FPS warning messages
gId('fpsNone').style.display = (d.Sf.FR.value == 0) ? 'block':'none';
gId('fpsWarn').style.display = (d.Sf.FR.value == 0) || (d.Sf.FR.value >= 80) ? 'block':'none';
gId('fpsHigh').style.display = (d.Sf.FR.value >= 80) ? 'block':'none';
}
function lastEnd(i) {
if (i-- < 1) return 0;
var s = chrID(i);
var s = String.fromCharCode((i<10?48:55)+i);
v = parseInt(d.getElementsByName("LS"+s)[0].value) + parseInt(d.getElementsByName("LC"+s)[0].value);
var t = parseInt(d.getElementsByName("LT"+s)[0].value);
if (isPWM(t)) v = 1; //PWM busses
@@ -414,17 +393,27 @@
{
var o = gEBCN("iST");
var i = o.length;
let disable = (sel,opt) => { sel.querySelectorAll(opt).forEach((o)=>{o.disabled=true;}); }
var f = gId("mLC");
let digitalB = 0, analogB = 0, twopinB = 0, virtB = 0;
f.querySelectorAll("select[name^=LT]").forEach((s)=>{
let t = s.value;
if (isDig(t) && !isD2P(t)) digitalB++;
if (isD2P(t)) twopinB++;
if (isPWM(t)) analogB += numPins(t); // each GPIO is assigned to a channel
if (isVir(t)) virtB++;
});
if ((n==1 && i>=maxB+maxV) || (n==-1 && i==0)) return;
var s = chrID(i);
var s = String.fromCharCode((i<10?48:55)+i);
if (n==1) {
// npm run build has trouble minimizing spaces inside string
var cn = `<div class="iST">
<hr class="sml">
${i+1}:
<select name="LT${s}" onchange="updateTypeDropdowns();UI(true)"></select><br>
<select name="LT${s}" onchange="UI(true)"></select><br>
<div id="abl${s}">
mA/LED: <select name="LAsel${s}" onchange="enLA(this,'${s}');UI();">
<option value="55" selected>55mA (typ. 5V WS281x)</option>
@@ -473,20 +462,20 @@ mA/LED: <select name="LAsel${s}" onchange="enLA(this,'${s}');UI();">
if (type.t != undefined && type.t != "") {
opt.setAttribute('data-type', type.t);
}
sel.appendChild(opt);
sel.appendChild(opt);
}
}
});
enLA(d.Sf["LAsel"+s],s); // update LED mA
// temporarily set to virtual (network) type to avoid "same type" exception during dropdown update
let sel = d.getElementsByName("LT"+s)[0];
sel.value = sel.querySelector('option[data-type="N"]').value;
updateTypeDropdowns(); // update valid bus options including this new one
// disable inappropriate LED types
let sel = d.getElementsByName("LT"+s)[0]
if (i >= maxB || digitalB >= maxD) disable(sel,'option[data-type="D"]'); // NOTE: see isDig()
if (i >= maxB || twopinB >= 1) disable(sel,'option[data-type="2P"]'); // NOTE: see isD2P()
disable(sel,`option[data-type^="${'A'.repeat(maxA-analogB+1)}"]`); // NOTE: see isPWM()
sel.selectedIndex = sel.querySelector('option:not(:disabled)').index;
updateTypeDropdowns(); // update again for the newly selected type
}
if (n==-1) {
o[--i].remove();--i;
o[i].querySelector("[name^=LT]").disabled = false;
}
gId("+").style.display = (i<maxB+maxV-1) ? "inline":"none";
@@ -500,7 +489,7 @@ mA/LED: <select name="LAsel${s}" onchange="enLA(this,'${s}');UI();">
function addCOM(start=0,len=1,co=0) {
var i = gEBCN("com_entry").length;
if (i >= maxCO) return;
var s = chrID(i);
var s = String.fromCharCode((i<10?48:55)+i);
var b = `<div class="com_entry">
<hr class="sml">
${i+1}: Start: <input type="number" name="XS${s}" id="xs${s}" class="l starts" min="0" max="65535" value="${start}" oninput="UI();" required="">&nbsp;
@@ -554,7 +543,7 @@ Swap: <select id="xw${s}" name="XW${s}">
function addBtn(i,p,t) {
var c = gId("btns").innerHTML;
var s = chrID(i);
var s = String.fromCharCode((i<10?48:55)+i);
c += `Button ${i} GPIO: <input type="number" name="BT${s}" onchange="UI()" class="xs" value="${p}">`;
c += `&nbsp;<select name="BE${s}">`
c += `<option value="0" ${t==0?"selected":""}>Disabled</option>`;
@@ -578,10 +567,8 @@ Swap: <select id="xw${s}" name="XW${s}">
function checkSi() { //on load, checks whether there are custom start fields
var cs = false;
for (var i=1; i < gEBCN("iST").length; i++) {
var s = chrID(i);
var p = chrID(i-1); // cover edge case 'A' previous char being '9'
var v = parseInt(gId("ls"+p).value) + parseInt(gN("LC"+p).value);
if (v != parseInt(gId("ls"+s).value)) {cs = true; startsDirty[i] = true;}
var v = parseInt(gId("ls"+(i-1)).value) + parseInt(gN("LC"+(i-1)).value);
if (v != parseInt(gId("ls"+i).value)) {cs = true; startsDirty[i] = true;}
}
if (gId("ls0") && parseInt(gId("ls0").value) != 0) {cs = true; startsDirty[0] = true;}
gId("si").checked = cs;
@@ -610,10 +597,10 @@ Swap: <select id="xw${s}" name="XW${s}">
function receivedText(e) {
let lines = e.target.result;
var c = JSON.parse(lines);
var c = JSON.parse(lines);
if (c.hw) {
if (c.hw.led) {
for (var i=0; i<oMaxB+maxV; i++) addLEDs(-1);
for (var i=0; i<10; i++) addLEDs(-1);
var l = c.hw.led;
l.ins.forEach((v,i,a)=>{
addLEDs(1);
@@ -750,34 +737,6 @@ Swap: <select id="xw${s}" name="XW${s}">
}
return opt;
}
// dynamically enforce bus type availability based on current usage
function updateTypeDropdowns() {
let LTs = d.Sf.querySelectorAll("#mLC select[name^=LT]");
let digitalB = 0, analogB = 0, twopinB = 0, virtB = 0;
// count currently used buses
LTs.forEach(sel => {
let t = parseInt(sel.value);
if (isDig(t) && !isD2P(t)) digitalB++;
if (isPWM(t)) analogB += numPins(t);
if (isD2P(t)) twopinB++;
if (isVir(t)) virtB++;
});
// enable/disable type options according to limits in dropdowns
LTs.forEach(sel => {
const curType = parseInt(sel.value);
const disable = (q) => sel.querySelectorAll(q).forEach(o => o.disabled = true);
const enable = (q) => sel.querySelectorAll(q).forEach(o => o.disabled = false);
enable('option'); // reset all first
// max digital buses: ESP32 & S2 support mono I2S as well as parallel so we need to take that into account; S3 only supports parallel
// supported outputs using parallel I2S/mono I2S: S2: 12/5, S3: 12/4, ESP32: 16/9
let maxDB = maxD - ((is32() || isS2() || isS3()) ? (!d.Sf["PR"].checked) * 8 - (!isS3()) : 0); // adjust max digital buses if parallel I2S is not used
// disallow adding more of a type that has reached its limit but allow changing the current type
if (digitalB >= maxDB && !(isDig(curType) && !isD2P(curType))) disable('option[data-type="D"]');
if (twopinB >= 2 && !isD2P(curType)) disable('option[data-type="2P"]');
// Disable PWM types that need more pins than available (accounting for current type's pins if PWM)
disable(`option[data-type^="${'A'.repeat(maxA - analogB + (isPWM(curType)?numPins(curType):0) + 1)}"]`);
});
}
</script>
<style>@import url("style.css");</style>
</head>
@@ -813,11 +772,8 @@ Swap: <select id="xw${s}" name="XW${s}">
</div>
</div>
<h3>Hardware setup</h3>
<div id="mLC">
LED outputs:<br>
<hr class="sml">
<i>Only last output can be changed. Remove to edit others.</i><br>
</div>
<div id="mLC">LED outputs:</div>
<hr class="sml">
<button type="button" id="+" onclick="addLEDs(1,false)">+</button>
<button type="button" id="-" onclick="addLEDs(-1,false)">-</button><br>
LED memory usage: <span id="m0">0</span> / <span id="m1">?</span> B<br>
@@ -827,7 +783,6 @@ Swap: <select id="xw${s}" name="XW${s}">
Use less than <span id="wreason">800 LEDs per output</span> for the best experience!<br>
</div>
<hr class="sml">
<div id="prl" class="hide">Use parallel I2S: <input type="checkbox" name="PR"><br></div>
Make a segment for each output: <input type="checkbox" name="MS"><br>
Custom bus start indices: <input type="checkbox" onchange="tglSi(this.checked)" id="si"><br>
Use global LED buffer: <input type="checkbox" name="LD" onchange="UI()"><br>
@@ -915,10 +870,7 @@ Swap: <select id="xw${s}" name="XW${s}">
<option value="2">Linear (never wrap)</option>
<option value="3">None (not recommended)</option>
</select><br>
Target refresh rate: <input type="number" class="s" min="0" max="250" name="FR" oninput="UI()" required> FPS
<div id="fpsNone" class="warn" style="display: none;">&#9888; Unlimited FPS Mode is experimental &#9888;<br></div>
<div id="fpsHigh" class="warn" style="display: none;">&#9888; High FPS Mode is experimental.<br></div>
<div id="fpsWarn" class="warn" style="display: none;">Please <a class="lnk" href="sec#backup">backup</a> WLED configuration and presets first!<br></div>
Target refresh rate: <input type="number" class="s" min="1" max="120" name="FR" required> FPS
<hr class="sml">
<div id="cfg">Config template: <input type="file" name="data2" accept=".json"><button type="button" class="sml" onclick="loadCfg(d.Sf.data2)">Apply</button><br></div>
<hr>
+5 -5
View File
@@ -57,11 +57,11 @@
<h3>Software Update</h3>
<button type="button" onclick="U()">Manual OTA Update</button><br>
Enable ArduinoOTA: <input type="checkbox" name="AO">
<hr id="backup">
<hr>
<h3>Backup & Restore</h3>
<div class="warn">&#9888; Restoring presets/configuration will OVERWRITE your current presets/configuration.<br>
Incorrect upload or configuration may require a factory reset or re-flashing of your ESP.<br>
For security reasons, passwords are not backed up.</div>
Incorrect upload or configuration may require a factory reset or re-flashing of your ESP.</div>
For security reasons, passwords are not backed up.
<a class="btn lnk" id="bckcfg" href="/presets.json" download="presets">Backup presets</a><br>
<div>Restore presets<br><input type="file" name="data" accept=".json"> <button type="button" onclick="uploadFile(d.Sf.data,'/presets.json');">Upload</button><br></div><br>
<a class="btn lnk" id="bckpresets" href="/cfg.json" download="cfg">Backup configuration</a><br>
@@ -73,9 +73,9 @@
A huge thank you to everyone who helped me create WLED!<br><br>
(c) 2016-2024 Christian Schwinne <br>
<i>Licensed under the <a href="https://github.com/Aircoookie/WLED/blob/master/LICENSE" target="_blank">EUPL v1.2 license</a></i><br><br>
Installed version: <span class="sip">WLED ##VERSION##</span><hr>
Server message: <span class="sip"> Response error! </span><hr>
<div id="toast"></div>
<button type="button" onclick="B()">Back</button><button type="submit">Save</button>
</form>
</body>
</html>
</html>
+2 -25
View File
@@ -6,26 +6,7 @@
<script>
function B() { window.history.back(); }
function U() { document.getElementById("uf").style.display="none";document.getElementById("msg").style.display="block"; }
function GetV() {
// Fetch device info via JSON API instead of compiling it in
fetch('/json/info')
.then(response => response.json())
.then(data => {
document.querySelector('.installed-version').textContent = `${data.brand} ${data.ver} (${data.vid})`;
document.querySelector('.release-name').textContent = data.release;
// TODO - assemble update URL
// TODO - can this be done at build time?
if (data.arch == "esp8266") {
toggle('rev');
}
})
.catch(error => {
console.log('Could not fetch device info:', error);
// Fallback to compiled-in value if API call fails
document.querySelector('.installed-version').textContent = 'Unknown';
document.querySelector('.release-name').textContent = 'Unknown';
});
}
function GetV() {/*injected values here*/}
</script>
<style>
@import url("style.css");
@@ -35,15 +16,11 @@
<body onload="GetV()">
<h2>WLED Software Update</h2>
<form method='POST' action='./update' id='uf' enctype='multipart/form-data' onsubmit="U()">
Installed version: <span class="sip installed-version">Loading...</span><br>
Release: <span class="sip release-name">Loading...</span><br>
Installed version: <span class="sip">##VERSION##</span><br>
Download the latest binary:&nbsp;<a href="https://github.com/Aircoookie/WLED/releases" target="_blank"
style="vertical-align: text-bottom; display: inline-flex;">
<img src="https://img.shields.io/github/release/Aircoookie/WLED.svg?style=flat-square"></a><br>
<input type="hidden" name="skipValidation" value="" id="sV">
<input type='file' name='update' required><br> <!--should have accept='.bin', but it prevents file upload from android app-->
<input type='checkbox' onchange="sV.value=checked?1:''" id="skipValidation">
<label for='skipValidation'>Ignore firmware validation</label><br>
<button type="submit">Update!</button><br>
<button type="button" onclick="B()">Back</button>
</form>
+1 -1
View File
@@ -416,7 +416,7 @@ void prepareArtnetPollReply(ArtPollReply *reply) {
reply->reply_port = ARTNET_DEFAULT_PORT;
char * numberEnd = (char*) versionString; // strtol promises not to try to edit this.
char * numberEnd = versionString;
reply->reply_version_h = (uint8_t)strtol(numberEnd, &numberEnd, 10);
numberEnd++;
reply->reply_version_l = (uint8_t)strtol(numberEnd, &numberEnd, 10);
+28 -63
View File
@@ -1,4 +1,3 @@
#pragma once
#ifndef WLED_FCN_DECLARE_H
#define WLED_FCN_DECLARE_H
@@ -24,12 +23,8 @@ void handleIO();
void IRAM_ATTR touchButtonISR();
//cfg.cpp
bool backupConfig();
bool restoreConfig();
bool verifyConfig();
void resetConfig();
bool deserializeConfig(JsonObject doc, bool fromFS = false);
bool deserializeConfigFromFS();
void deserializeConfigFromFS();
bool deserializeConfigSec();
void serializeConfig();
void serializeConfigSec();
@@ -118,15 +113,10 @@ bool readObjectFromFileUsingId(const char* file, uint16_t id, JsonDocument* dest
bool readObjectFromFile(const char* file, const char* key, JsonDocument* dest);
void updateFSInfo();
void closeFile();
inline bool writeObjectToFileUsingId(const String &file, uint16_t id, const JsonDocument* content) { return writeObjectToFileUsingId(file.c_str(), id, content); };
inline bool writeObjectToFile(const String &file, const char* key, const JsonDocument* content) { return writeObjectToFile(file.c_str(), key, content); };
inline bool readObjectFromFileUsingId(const String &file, uint16_t id, JsonDocument* dest, const JsonDocument* filter = nullptr) { return readObjectFromFileUsingId(file.c_str(), id, dest); };
inline bool readObjectFromFile(const String &file, const char* key, JsonDocument* dest, const JsonDocument* filter = nullptr) { return readObjectFromFile(file.c_str(), key, dest); };
bool copyFile(const char* src_path, const char* dst_path);
bool backupFile(const char* filename);
bool restoreFile(const char* filename);
bool validateJsonFile(const char* filename);
void dumpFilesToSerial();
inline bool writeObjectToFileUsingId(const String &file, uint16_t id, JsonDocument* content) { return writeObjectToFileUsingId(file.c_str(), id, content); };
inline bool writeObjectToFile(const String &file, const char* key, JsonDocument* content) { return writeObjectToFile(file.c_str(), key, content); };
inline bool readObjectFromFileUsingId(const String &file, uint16_t id, JsonDocument* dest) { return readObjectFromFileUsingId(file.c_str(), id, dest); };
inline bool readObjectFromFile(const String &file, const char* key, JsonDocument* dest) { return readObjectFromFile(file.c_str(), key, dest); };
//hue.cpp
void handleHue();
@@ -240,13 +230,12 @@ void deletePreset(byte index);
bool getPresetName(byte index, String& name);
//remote.cpp
void handleWiZdata(uint8_t *incomingData, size_t len);
void handleRemote();
void handleRemote(uint8_t *data, size_t len);
//set.cpp
bool isAsterisksOnly(const char* str, byte maxLen);
void handleSettingsSet(AsyncWebServerRequest *request, byte subPage);
bool handleSet(AsyncWebServerRequest *request, const String& req, bool apply=true);
bool handleHttpApi(const String& req, bool apply=true);
//udp.cpp
void notify(byte callMode, bool followUp=false);
@@ -383,7 +372,7 @@ void userLoop();
//util.cpp
int getNumVal(const String* req, uint16_t pos);
void parseNumber(const char* str, byte* val, byte minv=0, byte maxv=255);
bool getVal(JsonVariant elem, byte* val, byte minv=0, byte maxv=255); // getVal supports inc/decrementing and random ("X~Y(r|~[w][-][Z])" form)
bool getVal(JsonVariant elem, byte* val, byte minv=0, byte maxv=255);
bool getBoolVal(JsonVariant elem, bool dflt);
bool updateVal(const char* req, const char* key, byte* val, byte minv=0, byte maxv=255);
size_t printSetFormCheckbox(Print& settingsScript, const char* key, int val);
@@ -400,25 +389,11 @@ uint8_t extractModeSlider(uint8_t mode, uint8_t slider, char *dest, uint8_t maxL
int16_t extractModeDefaults(uint8_t mode, const char *segVar);
void checkSettingsPIN(const char *pin);
uint16_t crc16(const unsigned char* data_p, size_t length);
String computeSHA1(const String& input);
String getDeviceId();
uint16_t beatsin88_t(accum88 beats_per_minute_88, uint16_t lowest = 0, uint16_t highest = 65535, uint32_t timebase = 0, uint16_t phase_offset = 0);
uint16_t beatsin16_t(accum88 beats_per_minute, uint16_t lowest = 0, uint16_t highest = 65535, uint32_t timebase = 0, uint16_t phase_offset = 0);
uint8_t beatsin8_t(accum88 beats_per_minute, uint8_t lowest = 0, uint8_t highest = 255, uint32_t timebase = 0, uint8_t phase_offset = 0);
um_data_t* simulateSound(uint8_t simulationId);
void enumerateLedmaps();
uint8_t get_random_wheel_index(uint8_t pos);
float mapf(float x, float in_min, float in_max, float out_min, float out_max);
void handleBootLoop(); // detect and handle bootloops
#ifndef ESP8266
void bootloopCheckOTA(); // swap boot image if bootloop is detected instead of restoring config
#endif
void handleBootLoop(); // detect and handle bootloops
#ifndef ESP8266
void bootloopCheckOTA(); // swap boot image if bootloop is detected instead of restoring config
#endif
// RAII guard class for the JSON Buffer lock
// Modeled after std::lock_guard
class JSONBufferGuard {
@@ -444,37 +419,27 @@ void clearEEPROM();
#endif
//wled_math.cpp
//float cos_t(float phi); // use float math
//float sin_t(float phi);
//float tan_t(float x);
int16_t sin16_t(uint16_t theta);
int16_t cos16_t(uint16_t theta);
uint8_t sin8_t(uint8_t theta);
uint8_t cos8_t(uint8_t theta);
float sin_approx(float theta); // uses integer math (converted to float), accuracy +/-0.0015 (compared to sinf())
float cos_approx(float theta);
float tan_approx(float x);
float atan2_t(float y, float x);
float acos_t(float x);
float asin_t(float x);
template <typename T> T atan_t(T x);
float floor_t(float x);
float fmod_t(float num, float denom);
#define sin_t sin_approx
#define cos_t cos_approx
#define tan_t tan_approx
#if defined(ESP8266) && !defined(WLED_USE_REAL_MATH)
template <typename T> T atan_t(T x);
float cos_t(float phi);
float sin_t(float x);
float tan_t(float x);
float acos_t(float x);
float asin_t(float x);
float floor_t(float x);
float fmod_t(float num, float denom);
#else
#include <math.h>
#define sin_t sinf
#define cos_t cosf
#define tan_t tanf
#define asin_t asinf
#define acos_t acosf
#define atan_t atanf
#define fmod_t fmodf
#define floor_t floorf
#endif
/*
#include <math.h> // standard math functions. use a lot of flash
#define sin_t sinf
#define cos_t cosf
#define tan_t tanf
#define asin_t asinf
#define acos_t acosf
#define atan_t atanf
#define fmod_t fmodf
#define floor_t floorf
*/
//wled_serial.cpp
void handleSerial();
void updateBaudRate(uint32_t rate);
-153
View File
@@ -438,156 +438,3 @@ bool handleFileRead(AsyncWebServerRequest* request, String path){
}
return false;
}
// copy a file, delete destination file if incomplete to prevent corrupted files
bool copyFile(const char* src_path, const char* dst_path) {
DEBUG_PRINTF("copyFile from %s to %s\n", src_path, dst_path);
if(!WLED_FS.exists(src_path)) {
DEBUG_PRINTLN(F("file not found"));
return false;
}
bool success = true; // is set to false on error
File src = WLED_FS.open(src_path, "r");
File dst = WLED_FS.open(dst_path, "w");
if (src && dst) {
uint8_t buf[128]; // copy file in 128-byte blocks
while (src.available() > 0) {
size_t bytesRead = src.read(buf, sizeof(buf));
if (bytesRead == 0) {
success = false;
break; // error, no data read
}
size_t bytesWritten = dst.write(buf, bytesRead);
if (bytesWritten != bytesRead) {
success = false;
break; // error, not all data written
}
}
} else {
success = false; // error, could not open files
}
if(src) src.close();
if(dst) dst.close();
if (!success) {
DEBUG_PRINTLN(F("copy failed"));
WLED_FS.remove(dst_path); // delete incomplete file
}
return success;
}
// compare two files, return true if identical
bool compareFiles(const char* path1, const char* path2) {
DEBUG_PRINTF("compareFile %s and %s\n", path1, path2);
if (!WLED_FS.exists(path1) || !WLED_FS.exists(path2)) {
DEBUG_PRINTLN(F("file not found"));
return false;
}
bool identical = true; // set to false on mismatch
File f1 = WLED_FS.open(path1, "r");
File f2 = WLED_FS.open(path2, "r");
if (f1 && f2) {
uint8_t buf1[128], buf2[128];
while (f1.available() > 0 || f2.available() > 0) {
size_t len1 = f1.read(buf1, sizeof(buf1));
size_t len2 = f2.read(buf2, sizeof(buf2));
if (len1 != len2) {
identical = false;
break; // files differ in size or read failed
}
if (memcmp(buf1, buf2, len1) != 0) {
identical = false;
break; // files differ in content
}
}
} else {
identical = false; // error opening files
}
if (f1) f1.close();
if (f2) f2.close();
return identical;
}
static const char s_backup_fmt[] PROGMEM = "/bkp.%s";
bool backupFile(const char* filename) {
DEBUG_PRINTF("backup %s \n", filename);
if (!validateJsonFile(filename)) {
DEBUG_PRINTLN(F("broken file"));
return false;
}
char backupname[32];
snprintf_P(backupname, sizeof(backupname), s_backup_fmt, filename + 1); // skip leading '/' in filename
if (copyFile(filename, backupname)) {
DEBUG_PRINTLN(F("backup ok"));
return true;
}
DEBUG_PRINTLN(F("backup failed"));
return false;
}
bool restoreFile(const char* filename) {
DEBUG_PRINTF("restore %s \n", filename);
char backupname[32];
snprintf_P(backupname, sizeof(backupname), s_backup_fmt, filename + 1); // skip leading '/' in filename
if (!WLED_FS.exists(backupname)) {
DEBUG_PRINTLN(F("no backup found"));
return false;
}
if (!validateJsonFile(backupname)) {
DEBUG_PRINTLN(F("broken backup"));
return false;
}
if (copyFile(backupname, filename)) {
DEBUG_PRINTLN(F("restore ok"));
return true;
}
DEBUG_PRINTLN(F("restore failed"));
return false;
}
bool validateJsonFile(const char* filename) {
if (!WLED_FS.exists(filename)) return false;
File file = WLED_FS.open(filename, "r");
if (!file) return false;
StaticJsonDocument<0> doc, filter; // https://arduinojson.org/v6/how-to/validate-json/
bool result = deserializeJson(doc, file, DeserializationOption::Filter(filter)) == DeserializationError::Ok;
file.close();
if (!result) {
DEBUG_PRINTF_P(PSTR("Invalid JSON file %s\n"), filename);
} else {
DEBUG_PRINTF_P(PSTR("Valid JSON file %s\n"), filename);
}
return result;
}
// print contents of all files in root dir to Serial except wsec files
void dumpFilesToSerial() {
File rootdir = WLED_FS.open("/", "r");
File rootfile = rootdir.openNextFile();
while (rootfile) {
size_t len = strlen(rootfile.name());
// skip files starting with "wsec" and dont end in .json
if (strncmp(rootfile.name(), "wsec", 4) != 0 && len >= 6 && strcmp(rootfile.name() + len - 5, ".json") == 0) {
Serial.println(rootfile.name());
while (rootfile.available()) {
Serial.write(rootfile.read());
}
Serial.println();
Serial.println();
}
rootfile.close();
rootfile = rootdir.openNextFile();
}
}
+3 -3
View File
@@ -195,9 +195,9 @@ void onHueData(void* arg, AsyncClient* client, void *data, size_t len)
{
switch(hueColormode)
{
case 1: if (hueX != hueXLast || hueY != hueYLast) colorXYtoRGB(hueX,hueY,colPri); hueXLast = hueX; hueYLast = hueY; break;
case 2: if (hueHue != hueHueLast || hueSat != hueSatLast) colorHStoRGB(hueHue,hueSat,colPri); hueHueLast = hueHue; hueSatLast = hueSat; break;
case 3: if (hueCt != hueCtLast) colorCTtoRGB(hueCt,colPri); hueCtLast = hueCt; break;
case 1: if (hueX != hueXLast || hueY != hueYLast) colorXYtoRGB(hueX,hueY,col); hueXLast = hueX; hueYLast = hueY; break;
case 2: if (hueHue != hueHueLast || hueSat != hueSatLast) colorHStoRGB(hueHue,hueSat,col); hueHueLast = hueHue; hueSatLast = hueSat; break;
case 3: if (hueCt != hueCtLast) colorCTtoRGB(hueCt,col); hueCtLast = hueCt; break;
}
}
hueReceived = true;
+1 -3
View File
@@ -599,16 +599,14 @@ static void decodeIRJson(uint32_t code)
}
} else {
// HTTP API command
String apireq = "win"; apireq += '&'; // reduce flash string usage
if (cmdStr.indexOf("~") > 0 || fdo["rpt"]) lastValidCode = code; // repeatable action
if (!cmdStr.startsWith(apireq)) cmdStr = apireq + cmdStr; // if no "win&" prefix
if (!irApplyToAllSelected && cmdStr.indexOf(F("SS="))<0) {
char tmp[10];
sprintf_P(tmp, PSTR("&SS=%d"), strip.getMainSegmentId());
cmdStr += tmp;
}
fdo.clear(); // clear JSON buffer (it is no longer needed)
handleSet(nullptr, cmdStr, false); // no stateUpdated() call here
handleHttpApi(cmdStr, false); // no stateUpdated() call here
}
} else {
// command is JSON object (TODO: currently will not handle irApplyToAllSelected correctly)
+69 -64
View File
@@ -1,5 +1,4 @@
#include "wled.h"
#include "ota_update.h"
#include "palettes.h"
@@ -92,20 +91,19 @@ bool deserializeSegment(JsonObject elem, byte it, byte presetId)
}
}
uint16_t grp = elem["grp"] | seg.grouping;
uint16_t spc = elem[F("spc")] | seg.spacing;
uint16_t of = seg.offset;
uint8_t soundSim = elem["si"] | seg.soundSim;
uint8_t map1D2D = elem["m12"] | seg.map1D2D;
uint8_t set = elem[F("set")] | seg.set;
bool selected = getBoolVal(elem["sel"], seg.selected);
bool reverse = getBoolVal(elem["rev"], seg.reverse);
bool mirror = getBoolVal(elem["mi"] , seg.mirror);
#ifndef WLED_DISABLE_2D
bool reverse_y = getBoolVal(elem["rY"] , seg.reverse_y);
bool mirror_y = getBoolVal(elem["mY"] , seg.mirror_y);
bool transpose = getBoolVal(elem[F("tp")], seg.transpose);
#endif
uint16_t grp = elem["grp"] | seg.grouping;
uint16_t spc = elem[F("spc")] | seg.spacing;
uint16_t of = seg.offset;
uint8_t soundSim = elem["si"] | seg.soundSim;
uint8_t map1D2D = elem["m12"] | seg.map1D2D;
if ((spc>0 && spc!=seg.spacing) || seg.map1D2D!=map1D2D) seg.fill(BLACK); // clear spacing gaps
seg.map1D2D = constrain(map1D2D, 0, 7);
seg.soundSim = constrain(soundSim, 0, 3);
uint8_t set = elem[F("set")] | seg.set;
seg.set = constrain(set, 0, 3);
int len = 1;
if (stop > start) len = stop - start;
@@ -119,7 +117,7 @@ bool deserializeSegment(JsonObject elem, byte it, byte presetId)
if (stop > start && of > len -1) of = len -1;
// update segment (delete if necessary)
seg.setGeometry(start, stop, grp, spc, of, startY, stopY, map1D2D); // strip needs to be suspended for this to work without issues
seg.setUp(start, stop, grp, spc, of, startY, stopY); // strip needs to be suspended for this to work without issues
if (newSeg) seg.refreshLightCapabilities(); // fix for #3403
@@ -208,30 +206,47 @@ bool deserializeSegment(JsonObject elem, byte it, byte presetId)
}
#endif
//seg.map1D2D = constrain(map1D2D, 0, 7); // done in setGeometry()
seg.set = constrain(set, 0, 3);
seg.soundSim = constrain(soundSim, 0, 3);
seg.selected = selected;
seg.reverse = reverse;
seg.mirror = mirror;
#ifndef WLED_DISABLE_2D
seg.reverse_y = reverse_y;
seg.mirror_y = mirror_y;
seg.transpose = transpose;
bool reverse = seg.reverse;
bool mirror = seg.mirror;
#endif
seg.selected = getBoolVal(elem["sel"], seg.selected);
seg.reverse = getBoolVal(elem["rev"], seg.reverse);
seg.mirror = getBoolVal(elem["mi"] , seg.mirror);
#ifndef WLED_DISABLE_2D
bool reverse_y = seg.reverse_y;
bool mirror_y = seg.mirror_y;
seg.reverse_y = getBoolVal(elem["rY"] , seg.reverse_y);
seg.mirror_y = getBoolVal(elem["mY"] , seg.mirror_y);
seg.transpose = getBoolVal(elem[F("tp")], seg.transpose);
if (seg.is2D() && seg.map1D2D == M12_pArc && (reverse != seg.reverse || reverse_y != seg.reverse_y || mirror != seg.mirror || mirror_y != seg.mirror_y)) seg.fill(BLACK); // clear entire segment (in case of Arc 1D to 2D expansion)
#endif
byte fx = seg.mode;
if (getVal(elem["fx"], &fx, 0, strip.getModeCount())) {
byte last = strip.getModeCount();
// partial fix for #3605
if (!elem["fx"].isNull() && elem["fx"].is<const char*>()) {
const char *tmp = elem["fx"].as<const char *>();
if (strlen(tmp) > 3 && (strchr(tmp,'r') || strchr(tmp,'~') != strrchr(tmp,'~'))) last = 0; // we have "X~Y(r|[w]~[-])" form
}
// end fix
if (getVal(elem["fx"], &fx, 0, last)) { //load effect ('r' random, '~' inc/dec, 0-255 exact value, 5~10r pick random between 5 & 10)
if (!presetId && currentPlaylist>=0) unloadPlaylist();
if (fx != seg.mode) seg.setMode(fx, elem[F("fxdef")]);
}
//getVal also supports inc/decrementing and random
getVal(elem["sx"], &seg.speed);
getVal(elem["ix"], &seg.intensity);
uint8_t pal = seg.palette;
last = strip.getPaletteCount();
if (!elem["pal"].isNull() && elem["pal"].is<const char*>()) {
const char *tmp = elem["pal"].as<const char *>();
if (strlen(tmp) > 3 && (strchr(tmp,'r') || strchr(tmp,'~') != strrchr(tmp,'~'))) last = 0; // we have "X~Y(r|[w]~[-])" form
}
if (seg.getLightCapabilities() & 1) { // ignore palette for White and On/Off segments
if (getVal(elem["pal"], &pal, 0, strip.getPaletteCount())) seg.setPalette(pal);
if (getVal(elem["pal"], &pal, 0, last)) seg.setPalette(pal);
}
getVal(elem["c1"], &seg.custom1);
@@ -391,38 +406,35 @@ bool deserializeState(JsonObject root, byte callMode, byte presetId)
int it = 0;
JsonVariant segVar = root["seg"];
if (!segVar.isNull()) {
// we may be called during strip.service() so we must not modify segments while effects are executing
strip.suspend();
const unsigned long waitUntil = millis() + strip.getFrameTime();
while (strip.isServicing() && millis() < waitUntil) delay(1); // wait until frame is over
#ifdef WLED_DEBUG
if (millis() >= waitUntil) DEBUG_PRINTLN(F("JSON: Waited for strip to finish servicing."));
#endif
if (segVar.is<JsonObject>()) {
int id = segVar["id"] | -1;
//if "seg" is not an array and ID not specified, apply to all selected/checked segments
if (id < 0) {
//apply all selected segments
for (size_t s = 0; s < strip.getSegmentsNum(); s++) {
Segment &sg = strip.getSegment(s);
if (sg.isActive() && sg.isSelected()) {
deserializeSegment(segVar, s, presetId);
}
if (!segVar.isNull()) strip.suspend();
if (segVar.is<JsonObject>())
{
int id = segVar["id"] | -1;
//if "seg" is not an array and ID not specified, apply to all selected/checked segments
if (id < 0) {
//apply all selected segments
//bool didSet = false;
for (size_t s = 0; s < strip.getSegmentsNum(); s++) {
Segment &sg = strip.getSegment(s);
if (sg.isActive() && sg.isSelected()) {
deserializeSegment(segVar, s, presetId);
//didSet = true;
}
} else {
deserializeSegment(segVar, id, presetId); //apply only the segment with the specified ID
}
//TODO: not sure if it is good idea to change first active but unselected segment
//if (!didSet) deserializeSegment(segVar, strip.getMainSegmentId(), presetId);
} else {
size_t deleted = 0;
JsonArray segs = segVar.as<JsonArray>();
for (JsonObject elem : segs) {
if (deserializeSegment(elem, it++, presetId) && !elem["stop"].isNull() && elem["stop"]==0) deleted++;
}
if (strip.getSegmentsNum() > 3 && deleted >= strip.getSegmentsNum()/2U) strip.purgeSegments(); // batch deleting more than half segments
deserializeSegment(segVar, id, presetId); //apply only the segment with the specified ID
}
strip.resume();
} else {
size_t deleted = 0;
JsonArray segs = segVar.as<JsonArray>();
for (JsonObject elem : segs) {
if (deserializeSegment(elem, it++, presetId) && !elem["stop"].isNull() && elem["stop"]==0) deleted++;
}
if (strip.getSegmentsNum() > 3 && deleted >= strip.getSegmentsNum()/2U) strip.purgeSegments(); // batch deleting more than half segments
}
strip.resume();
UsermodManager::readFromJsonState(root);
@@ -437,9 +449,7 @@ bool deserializeState(JsonObject root, byte callMode, byte presetId)
// HTTP API commands (must be handled before "ps")
const char* httpwin = root["win"];
if (httpwin) {
String apireq = "win"; apireq += '&'; // reduce flash string usage
apireq += httpwin;
handleSet(nullptr, apireq, false); // may set stateChanged
handleHttpApi(httpwin, false); // may set stateChanged
}
// Applying preset from JSON API has 2 cases: a) "pd" AKA "preset direct" and b) "ps" AKA "preset select"
@@ -450,12 +460,12 @@ bool deserializeState(JsonObject root, byte callMode, byte presetId)
if (!root[F("pd")].isNull() && stateChanged) {
// a) already applied preset content (requires "seg" or "win" but will ignore the rest)
currentPreset = root[F("pd")] | currentPreset;
if (root["win"].isNull()) presetCycCurr = currentPreset; // otherwise presetCycCurr was set in handleSet() [set.cpp]
if (root["win"].isNull()) presetCycCurr = currentPreset; // otherwise presetCycCurr was set in handleHttpApi() [set.cpp]
presetToRestore = currentPreset; // stateUpdated() will clear the preset, so we need to restore it after
DEBUG_PRINTF_P(PSTR("Preset direct: %d\n"), currentPreset);
} else if (!root["ps"].isNull()) {
// we have "ps" call (i.e. from button or external API call) or "pd" that includes "ps" (i.e. from UI call)
if (root["win"].isNull() && getVal(root["ps"], &presetCycCurr, 1, 250) && presetCycCurr > 0 && presetCycCurr < 251 && presetCycCurr != currentPreset) {
if (root["win"].isNull() && getVal(root["ps"], &presetCycCurr, 0, 0) && presetCycCurr > 0 && presetCycCurr < 251 && presetCycCurr != currentPreset) {
DEBUG_PRINTF_P(PSTR("Preset select: %d\n"), presetCycCurr);
// b) preset ID only or preset that does not change state (use embedded cycling limits if they exist in getVal())
applyPreset(presetCycCurr, callMode); // async load from file system (only preset ID was specified)
@@ -632,8 +642,6 @@ void serializeInfo(JsonObject root)
root[F("vid")] = VERSION;
root[F("cn")] = F(WLED_CODENAME);
root[F("release")] = releaseString;
root[F("repo")] = repoString;
root[F("deviceId")] = getDeviceId();
JsonObject leds = root.createNestedObject(F("leds"));
leds[F("count")] = strip.getLengthTotal();
@@ -756,9 +764,6 @@ void serializeInfo(JsonObject root)
root[F("resetReason1")] = (int)rtc_get_reset_reason(1);
#endif
root[F("lwip")] = 0; //deprecated
#ifndef WLED_DISABLE_OTA
root[F("bootloaderSHA256")] = getBootloaderSHA256Hex();
#endif
#else
root[F("arch")] = "esp8266";
root[F("core")] = ESP.getCoreVersion();
+12 -12
View File
@@ -9,10 +9,10 @@ void setValuesFromFirstSelectedSeg() { setValuesFromSegment(strip.getFirstSelect
void setValuesFromSegment(uint8_t s)
{
Segment& seg = strip.getSegment(s);
colPri[0] = R(seg.colors[0]);
colPri[1] = G(seg.colors[0]);
colPri[2] = B(seg.colors[0]);
colPri[3] = W(seg.colors[0]);
col[0] = R(seg.colors[0]);
col[1] = G(seg.colors[0]);
col[2] = B(seg.colors[0]);
col[3] = W(seg.colors[0]);
colSec[0] = R(seg.colors[1]);
colSec[1] = G(seg.colors[1]);
colSec[2] = B(seg.colors[1]);
@@ -39,7 +39,7 @@ void applyValuesToSelectedSegs()
if (effectIntensity != selsegPrev.intensity) {seg.intensity = effectIntensity; stateChanged = true;}
if (effectPalette != selsegPrev.palette) {seg.setPalette(effectPalette);}
if (effectCurrent != selsegPrev.mode) {seg.setMode(effectCurrent);}
uint32_t col0 = RGBW32(colPri[0], colPri[1], colPri[2], colPri[3]);
uint32_t col0 = RGBW32( col[0], col[1], col[2], col[3]);
uint32_t col1 = RGBW32(colSec[0], colSec[1], colSec[2], colSec[3]);
if (col0 != selsegPrev.colors[0]) {seg.setColor(0, col0);}
if (col1 != selsegPrev.colors[1]) {seg.setColor(1, col1);}
@@ -73,8 +73,8 @@ byte scaledBri(byte in)
//applies global brightness
void applyBri() {
if (!(realtimeMode && arlsForceMaxBri)) {
//DEBUG_PRINTF_P(PSTR("Applying strip brightness: %d (%d,%d)\n"), (int)briT, (int)bri, (int)briOld);
if (!realtimeMode || !arlsForceMaxBri)
{
strip.setBrightness(scaledBri(briT));
}
}
@@ -85,7 +85,6 @@ void applyFinalBri() {
briOld = bri;
briT = bri;
applyBri();
strip.trigger();
}
@@ -147,6 +146,7 @@ void stateUpdated(byte callMode) {
transitionStartTime = millis();
} else {
applyFinalBri();
strip.trigger();
}
}
@@ -157,14 +157,14 @@ void updateInterfaces(uint8_t callMode)
sendDataWs();
lastInterfaceUpdate = millis();
interfaceUpdateCallMode = CALL_MODE_INIT; //disable further updates
interfaceUpdateCallMode = 0; //disable further updates
if (callMode == CALL_MODE_WS_SEND) return;
#ifndef WLED_DISABLE_ALEXA
if (espalexaDevice != nullptr && callMode != CALL_MODE_ALEXA) {
espalexaDevice->setValue(bri);
espalexaDevice->setColor(colPri[0], colPri[1], colPri[2]);
espalexaDevice->setColor(col[0], col[1], col[2]);
}
#endif
#ifndef WLED_DISABLE_MQTT
@@ -221,7 +221,7 @@ void handleNightlight()
nightlightDelayMs = (unsigned)(nightlightDelayMins*60000);
nightlightActiveOld = true;
briNlT = bri;
for (unsigned i=0; i<4; i++) colNlT[i] = colPri[i]; // remember starting color
for (unsigned i=0; i<4; i++) colNlT[i] = col[i]; // remember starting color
if (nightlightMode == NL_MODE_SUN)
{
//save current
@@ -246,7 +246,7 @@ void handleNightlight()
bri = briNlT + ((nightlightTargetBri - briNlT)*nper);
if (nightlightMode == NL_MODE_COLORFADE) // color fading only is enabled with "NF=2"
{
for (unsigned i=0; i<4; i++) colPri[i] = colNlT[i]+ ((colSec[i] - colNlT[i])*nper); // fading from actual color to secondary color
for (unsigned i=0; i<4; i++) col[i] = colNlT[i]+ ((colSec[i] - colNlT[i])*nper); // fading from actual color to secondary color
}
colorUpdated(CALL_MODE_NO_NOTIFY);
}
+3 -5
View File
@@ -99,7 +99,7 @@ static void onMqttMessage(char* topic, char* payload, AsyncMqttClientMessageProp
//Prefix is stripped from the topic at this point
if (strcmp_P(topic, PSTR("/col")) == 0) {
colorFromDecOrHexString(colPri, payloadStr);
colorFromDecOrHexString(col, payloadStr);
colorUpdated(CALL_MODE_DIRECT_CHANGE);
} else if (strcmp_P(topic, PSTR("/api")) == 0) {
if (requestJSONBufferLock(15)) {
@@ -107,9 +107,7 @@ static void onMqttMessage(char* topic, char* payload, AsyncMqttClientMessageProp
deserializeJson(*pDoc, payloadStr);
deserializeState(pDoc->as<JsonObject>());
} else { //HTTP API
String apireq = "win"; apireq += '&'; // reduce flash string usage
apireq += payloadStr;
handleSet(nullptr, apireq);
handleHttpApi(payloadStr);
}
releaseJSONBufferLock();
}
@@ -165,7 +163,7 @@ void publishMqtt()
strcat_P(subuf, PSTR("/g"));
mqtt->publish(subuf, 0, retainMqttMsg, s); // optionally retain message (#2263)
sprintf_P(s, PSTR("#%06X"), (colPri[3] << 24) | (colPri[0] << 16) | (colPri[1] << 8) | (colPri[2]));
sprintf_P(s, PSTR("#%06X"), (col[3] << 24) | (col[0] << 16) | (col[1] << 8) | (col[2]));
strlcpy(subuf, mqttDeviceTopic, 33);
strcat_P(subuf, PSTR("/c"));
mqtt->publish(subuf, 0, retainMqttMsg, s); // optionally retain message (#2263)
-1
View File
@@ -207,7 +207,6 @@ void WiFiEvent(WiFiEvent_t event)
break;
#endif
default:
DEBUG_PRINTF_P(PSTR("Network event: %d\n"), (int)event);
break;
}
}
-323
View File
@@ -1,323 +0,0 @@
#include "ota_update.h"
#include "wled.h"
#ifdef ESP32
#include <esp_ota_ops.h>
#include <esp_spi_flash.h>
#include <mbedtls/sha256.h>
#endif
// Platform-specific metadata locations
#ifdef ESP32
constexpr size_t METADATA_OFFSET = 256; // ESP32: metadata appears after Espressif metadata
#define UPDATE_ERROR errorString
// Bootloader is at fixed offset 0x1000 (4KB), 0x0000 (0KB), or 0x2000 (8KB), and is typically 32KB
// Bootloader offsets for different MCUs => see https://github.com/wled/WLED/issues/5064
#if defined(CONFIG_IDF_TARGET_ESP32S3) || defined(CONFIG_IDF_TARGET_ESP32C3) || defined(CONFIG_IDF_TARGET_ESP32C6)
constexpr size_t BOOTLOADER_OFFSET = 0x0000; // esp32-S3, esp32-C3 and (future support) esp32-c6
constexpr size_t BOOTLOADER_SIZE = 0x8000; // 32KB, typical bootloader size
#elif defined(CONFIG_IDF_TARGET_ESP32P4) || defined(CONFIG_IDF_TARGET_ESP32C5)
constexpr size_t BOOTLOADER_OFFSET = 0x2000; // (future support) esp32-P4 and esp32-C5
constexpr size_t BOOTLOADER_SIZE = 0x8000; // 32KB, typical bootloader size
#else
constexpr size_t BOOTLOADER_OFFSET = 0x1000; // esp32 and esp32-s2
constexpr size_t BOOTLOADER_SIZE = 0x8000; // 32KB, typical bootloader size
#endif
#elif defined(ESP8266)
constexpr size_t METADATA_OFFSET = 0x1000; // ESP8266: metadata appears at 4KB offset
#define UPDATE_ERROR getErrorString
#endif
constexpr size_t METADATA_SEARCH_RANGE = 512; // bytes
/**
* Check if OTA should be allowed based on release compatibility using custom description
* @param binaryData Pointer to binary file data (not modified)
* @param dataSize Size of binary data in bytes
* @param errorMessage Buffer to store error message if validation fails
* @param errorMessageLen Maximum length of error message buffer
* @return true if OTA should proceed, false if it should be blocked
*/
static bool validateOTA(const uint8_t* binaryData, size_t dataSize, char* errorMessage, size_t errorMessageLen) {
// Clear error message
if (errorMessage && errorMessageLen > 0) {
errorMessage[0] = '\0';
}
// Try to extract WLED structure directly from binary data
wled_metadata_t extractedDesc;
bool hasDesc = findWledMetadata(binaryData, dataSize, &extractedDesc);
if (hasDesc) {
return shouldAllowOTA(extractedDesc, errorMessage, errorMessageLen);
} else {
// No custom description - this could be a legacy binary
if (errorMessage && errorMessageLen > 0) {
strncpy_P(errorMessage, PSTR("This firmware file is missing compatibility metadata."), errorMessageLen - 1);
errorMessage[errorMessageLen - 1] = '\0';
}
return false;
}
}
struct UpdateContext {
// State flags
// FUTURE: the flags could be replaced by a state machine
bool replySent = false;
bool needsRestart = false;
bool updateStarted = false;
bool uploadComplete = false;
bool releaseCheckPassed = false;
String errorMessage;
// Buffer to hold block data across posts, if needed
std::vector<uint8_t> releaseMetadataBuffer;
};
static void endOTA(AsyncWebServerRequest *request) {
UpdateContext* context = reinterpret_cast<UpdateContext*>(request->_tempObject);
request->_tempObject = nullptr;
DEBUG_PRINTF_P(PSTR("EndOTA %x --> %x (%d)\n"), (uintptr_t)request,(uintptr_t) context, context ? context->uploadComplete : 0);
if (context) {
if (context->updateStarted) { // We initialized the update
// We use Update.end() because not all forms of Update() support an abort.
// If the upload is incomplete, Update.end(false) should error out.
if (Update.end(context->uploadComplete)) {
// Update successful!
#ifndef ESP8266
bootloopCheckOTA(); // let the bootloop-checker know there was an OTA update
#endif
doReboot = true;
context->needsRestart = false;
}
}
if (context->needsRestart) {
strip.resume();
UsermodManager::onUpdateBegin(false);
#if WLED_WATCHDOG_TIMEOUT > 0
WLED::instance().enableWatchdog();
#endif
}
delete context;
}
};
static bool beginOTA(AsyncWebServerRequest *request, UpdateContext* context)
{
#ifdef ESP8266
Update.runAsync(true);
#endif
if (Update.isRunning()) {
request->send(503);
setOTAReplied(request);
return false;
}
#if WLED_WATCHDOG_TIMEOUT > 0
WLED::instance().disableWatchdog();
#endif
UsermodManager::onUpdateBegin(true); // notify usermods that update is about to begin (some may require task de-init)
strip.suspend();
strip.resetSegments(); // free as much memory as you can
context->needsRestart = true;
backupConfig(); // backup current config in case the update ends badly
DEBUG_PRINTF_P(PSTR("OTA Update Start, %x --> %x\n"), (uintptr_t)request,(uintptr_t) context);
auto skipValidationParam = request->getParam("skipValidation", true);
if (skipValidationParam && (skipValidationParam->value() == "1")) {
context->releaseCheckPassed = true;
DEBUG_PRINTLN(F("OTA validation skipped by user"));
}
// Begin update with the firmware size from content length
size_t updateSize = request->contentLength() > 0 ? request->contentLength() : ((ESP.getFreeSketchSpace() - 0x1000) & 0xFFFFF000);
if (!Update.begin(updateSize)) {
context->errorMessage = Update.UPDATE_ERROR();
DEBUG_PRINTF_P(PSTR("OTA Failed to begin: %s\n"), context->errorMessage.c_str());
return false;
}
context->updateStarted = true;
return true;
}
// Create an OTA context object on an AsyncWebServerRequest
// Returns true if successful, false on failure.
bool initOTA(AsyncWebServerRequest *request) {
// Allocate update context
UpdateContext* context = new (std::nothrow) UpdateContext {};
if (context) {
request->_tempObject = context;
request->onDisconnect([=]() { endOTA(request); }); // ensures we restart on failure
};
DEBUG_PRINTF_P(PSTR("OTA Update init, %x --> %x\n"), (uintptr_t)request,(uintptr_t) context);
return (context != nullptr);
}
void setOTAReplied(AsyncWebServerRequest *request) {
UpdateContext* context = reinterpret_cast<UpdateContext*>(request->_tempObject);
if (!context) return;
context->replySent = true;
};
// Returns pointer to error message, or nullptr if OTA was successful.
std::pair<bool, String> getOTAResult(AsyncWebServerRequest* request) {
UpdateContext* context = reinterpret_cast<UpdateContext*>(request->_tempObject);
if (!context) return { true, F("OTA context unexpectedly missing") };
if (context->replySent) return { false, {} };
if (context->errorMessage.length()) return { true, context->errorMessage };
if (context->updateStarted) {
// Release the OTA context now.
endOTA(request);
if (Update.hasError()) {
return { true, Update.UPDATE_ERROR() };
} else {
return { true, {} };
}
}
// Should never happen
return { true, F("Internal software failure") };
}
void handleOTAData(AsyncWebServerRequest *request, size_t index, uint8_t *data, size_t len, bool isFinal)
{
UpdateContext* context = reinterpret_cast<UpdateContext*>(request->_tempObject);
if (!context) return;
//DEBUG_PRINTF_P(PSTR("HandleOTAData: %d %d %d\n"), index, len, isFinal);
if (context->replySent || (context->errorMessage.length())) return;
if (index == 0) {
if (!beginOTA(request, context)) return;
}
// Perform validation if we haven't done it yet and we have reached the metadata offset
if (!context->releaseCheckPassed && (index+len) > METADATA_OFFSET) {
// Current chunk contains the metadata offset
size_t availableDataAfterOffset = (index + len) - METADATA_OFFSET;
DEBUG_PRINTF_P(PSTR("OTA metadata check: %d in buffer, %d received, %d available\n"), context->releaseMetadataBuffer.size(), len, availableDataAfterOffset);
if (availableDataAfterOffset >= METADATA_SEARCH_RANGE) {
// We have enough data to validate, one way or another
const uint8_t* search_data = data;
size_t search_len = len;
// If we have saved data, use that instead
if (context->releaseMetadataBuffer.size()) {
// Add this data
context->releaseMetadataBuffer.insert(context->releaseMetadataBuffer.end(), data, data+len);
search_data = context->releaseMetadataBuffer.data();
search_len = context->releaseMetadataBuffer.size();
}
// Do the checking
char errorMessage[128];
bool OTA_ok = validateOTA(search_data, search_len, errorMessage, sizeof(errorMessage));
// Release buffer if there was one
context->releaseMetadataBuffer = decltype(context->releaseMetadataBuffer){};
if (!OTA_ok) {
DEBUG_PRINTF_P(PSTR("OTA declined: %s\n"), errorMessage);
context->errorMessage = errorMessage;
context->errorMessage += F(" Enable 'Ignore firmware validation' to proceed anyway.");
return;
} else {
DEBUG_PRINTLN(F("OTA allowed: Release compatibility check passed"));
context->releaseCheckPassed = true;
}
} else {
// Store the data we just got for next pass
context->releaseMetadataBuffer.insert(context->releaseMetadataBuffer.end(), data, data+len);
}
}
// Check if validation was still pending (shouldn't happen normally)
// This is done before writing the last chunk, so endOTA can abort
if (isFinal && !context->releaseCheckPassed) {
DEBUG_PRINTLN(F("OTA failed: Validation never completed"));
// Don't write the last chunk to the updater: this will trip an error later
context->errorMessage = F("Release check data never arrived?");
return;
}
// Write chunk data to OTA update (only if release check passed or still pending)
if (!Update.hasError()) {
if (Update.write(data, len) != len) {
DEBUG_PRINTF_P(PSTR("OTA write failed on chunk %zu: %s\n"), index, Update.UPDATE_ERROR());
}
}
if(isFinal) {
DEBUG_PRINTLN(F("OTA Update End"));
// Upload complete
context->uploadComplete = true;
}
}
#if defined(ARDUINO_ARCH_ESP32) && !defined(WLED_DISABLE_OTA)
static String bootloaderSHA256HexCache = "";
// Calculate and cache the bootloader SHA256 digest as hex string
void calculateBootloaderSHA256() {
if (!bootloaderSHA256HexCache.isEmpty()) return;
// Calculate SHA256
uint8_t sha256[32];
mbedtls_sha256_context ctx;
mbedtls_sha256_init(&ctx);
mbedtls_sha256_starts(&ctx, 0); // 0 = SHA256 (not SHA224)
const size_t chunkSize = 256;
uint8_t buffer[chunkSize];
for (uint32_t offset = 0; offset < BOOTLOADER_SIZE; offset += chunkSize) {
size_t readSize = min((size_t)(BOOTLOADER_SIZE - offset), chunkSize);
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 4, 0)
if (esp_flash_read(NULL, buffer, BOOTLOADER_OFFSET + offset, readSize) == ESP_OK) { // use esp_flash_read for V4 framework (-S2, -S3, -C3)
#else
if (spi_flash_read(BOOTLOADER_OFFSET + offset, buffer, readSize) == ESP_OK) { // use spi_flash_read for old V3 framework (legacy esp32)
#endif
mbedtls_sha256_update(&ctx, buffer, readSize);
}
}
mbedtls_sha256_finish(&ctx, sha256);
mbedtls_sha256_free(&ctx);
// Convert to hex string and cache it
char hex[65];
for (int i = 0; i < 32; i++) {
sprintf(hex + (i * 2), "%02x", sha256[i]);
}
hex[64] = '\0';
bootloaderSHA256HexCache = hex;
}
// Get bootloader SHA256 as hex string
String getBootloaderSHA256Hex() {
calculateBootloaderSHA256();
return bootloaderSHA256HexCache;
}
// Invalidate cached bootloader SHA256 (call after bootloader update)
void invalidateBootloaderSHA256Cache() {
bootloaderSHA256HexCache = "";
}
#endif
-72
View File
@@ -1,72 +0,0 @@
// WLED OTA update interface
#include <Arduino.h>
#ifdef ESP8266
#include <Updater.h>
#else
#include <Update.h>
#endif
#pragma once
// Platform-specific metadata locations
#ifdef ESP32
#define BUILD_METADATA_SECTION ".rodata_custom_desc"
#elif defined(ESP8266)
#define BUILD_METADATA_SECTION ".ver_number"
#endif
class AsyncWebServerRequest;
/**
* Create an OTA context object on an AsyncWebServerRequest
* @param request Pointer to web request object
* @return true if allocation was successful, false if not
*/
bool initOTA(AsyncWebServerRequest *request);
/**
* Indicate to the OTA subsystem that a reply has already been generated
* @param request Pointer to web request object
*/
void setOTAReplied(AsyncWebServerRequest *request);
/**
* Retrieve the OTA result.
* @param request Pointer to web request object
* @return bool indicating if a reply is necessary; string with error message if the update failed.
*/
std::pair<bool, String> getOTAResult(AsyncWebServerRequest *request);
/**
* Process a block of OTA data. This is a passthrough of an ArUploadHandlerFunction.
* Requires that initOTA be called on the handler object before any work will be done.
* @param request Pointer to web request object
* @param index Offset in to uploaded file
* @param data New data bytes
* @param len Length of new data bytes
* @param isFinal Indicates that this is the last block
* @return bool indicating if a reply is necessary; string with error message if the update failed.
*/
void handleOTAData(AsyncWebServerRequest *request, size_t index, uint8_t *data, size_t len, bool isFinal);
#if defined(ARDUINO_ARCH_ESP32) && !defined(WLED_DISABLE_OTA)
/**
* Calculate and cache the bootloader SHA256 digest
* Reads the bootloader from flash at offset 0x1000 and computes SHA256 hash
*/
void calculateBootloaderSHA256();
/**
* Get bootloader SHA256 as hex string
* @return String containing 64-character hex representation of SHA256 hash
*/
String getBootloaderSHA256Hex();
/**
* Invalidate cached bootloader SHA256 (call after bootloader update)
* Forces recalculation on next call to calculateBootloaderSHA256 or getBootloaderSHA256Hex
*/
void invalidateBootloaderSHA256Cache();
#endif
+2 -14
View File
@@ -214,20 +214,8 @@ bool PinManager::isPinOk(byte gpio, bool output)
// JTAG: GPIO39-42 are usually used for inline debugging
// GPIO46 is input only and pulled down
#else
if ((strncmp_P(PSTR("ESP32-U4WDH"), ESP.getChipModel(), 11) == 0) || // this is the correct identifier, but....
(strncmp_P(PSTR("ESP32-PICO-D2"), ESP.getChipModel(), 13) == 0)) { // https://github.com/espressif/arduino-esp32/issues/10683
// this chip has 4 MB of internal Flash and different packaging, so available pins are different!
if (((gpio > 5) && (gpio < 9)) || (gpio == 11))
return false;
} else {
// for classic ESP32 (non-mini) modules, these are the SPI flash pins
if (gpio > 5 && gpio < 12) return false; //SPI flash pins
}
if (((strncmp_P(PSTR("ESP32-PICO"), ESP.getChipModel(), 10) == 0) ||
(strncmp_P(PSTR("ESP32-U4WDH"), ESP.getChipModel(), 11) == 0))
&& (gpio == 16 || gpio == 17)) return false; // PICO-D4/U4WDH: gpio16+17 are in use for onboard SPI FLASH
if (gpio > 5 && gpio < 12) return false; //SPI flash pins
if (strncmp_P(PSTR("ESP32-PICO"), ESP.getChipModel(), 10) == 0 && (gpio == 16 || gpio == 17)) return false; // PICO-D4: gpio16+17 are in use for onboard SPI FLASH
if (gpio == 16 || gpio == 17) return !psramFound(); //PSRAM pins on ESP32 (these are IO)
#endif
if (output) return digitalPinCanOutput(gpio);
+2 -10
View File
@@ -164,11 +164,6 @@ void handlePresets()
DEBUG_PRINTF_P(PSTR("Applying preset: %u\n"), (unsigned)tmpPreset);
#if defined(ARDUINO_ARCH_ESP32S3) || defined(ARDUINO_ARCH_ESP32S2) || defined(ARDUINO_ARCH_ESP32C3)
unsigned long start = millis();
while (strip.isUpdating() && millis() - start < FRAMETIME_FIXED) yield(); // wait for strip to finish updating, accessing FS during sendout causes glitches
#endif
#ifdef ARDUINO_ARCH_ESP32
if (tmpPreset==255 && tmpRAMbuffer!=nullptr) {
deserializeJson(*pDoc,tmpRAMbuffer);
@@ -185,10 +180,7 @@ void handlePresets()
//HTTP API commands
const char* httpwin = fdo["win"];
if (httpwin) {
String apireq = "win"; // reduce flash string usage
apireq += F("&IN&"); // internal call
apireq += httpwin;
handleSet(nullptr, apireq, false); // may call applyPreset() via PL=
handleHttpApi(httpwin, false); // may call applyPreset() via PL=
setValuesFromFirstSelectedSeg(); // fills legacy values
changePreset = true;
} else {
@@ -213,7 +205,7 @@ void handlePresets()
updateInterfaces(tmpMode);
}
//called from handleSet(PS=) [network callback (sObj is empty), IR (irrational), deserializeState, UDP] and deserializeState() [network callback (filedoc!=nullptr)]
//called from handleHttpApi(PS=) [network callback (sObj is empty), IR (irrational), deserializeState, UDP] and deserializeState() [network callback (filedoc!=nullptr)]
void savePreset(byte index, const char* pname, JsonObject sObj)
{
if (!saveName) saveName = new char[33];
+6 -22
View File
@@ -1,8 +1,6 @@
#include "wled.h"
#ifndef WLED_DISABLE_ESPNOW
#define ESPNOW_BUSWAIT_TIMEOUT 24 // one frame timeout to wait for bus to finish updating
#define NIGHT_MODE_DEACTIVATED -1
#define NIGHT_MODE_BRIGHTNESS 5
@@ -40,7 +38,6 @@ typedef struct WizMoteMessageStructure {
static uint32_t last_seq = UINT32_MAX;
static int brightnessBeforeNightMode = NIGHT_MODE_DEACTIVATED;
static int16_t ESPNowButton = -1; // set in callback if new button value is received
// Pulled from the IR Remote logic but reduced to 10 steps with a constant of 3
static const byte brightnessSteps[] = {
@@ -124,9 +121,6 @@ static bool remoteJson(int button)
sprintf_P(objKey, PSTR("\"%d\":"), button);
unsigned long start = millis();
while (strip.isUpdating() && millis()-start < ESPNOW_BUSWAIT_TIMEOUT) yield(); // wait for strip to finish updating, accessing FS during sendout causes glitches
// attempt to read command from remote.json
readObjectFromFile(PSTR("/remote.json"), objKey, pDoc);
JsonObject fdo = pDoc->as<JsonObject>();
@@ -159,16 +153,14 @@ static bool remoteJson(int button)
}
} else {
// HTTP API command
String apireq = "win"; apireq += '&'; // reduce flash string usage
//if (cmdStr.indexOf("~") || fdo["rpt"]) lastValidCode = code; // repeatable action
if (!cmdStr.startsWith(apireq)) cmdStr = apireq + cmdStr; // if no "win&" prefix
if (!irApplyToAllSelected && cmdStr.indexOf(F("SS="))<0) {
char tmp[10];
sprintf_P(tmp, PSTR("&SS=%d"), strip.getMainSegmentId());
cmdStr += tmp;
}
fdo.clear(); // clear JSON buffer (it is no longer needed)
handleSet(nullptr, cmdStr, false); // no stateUpdated() call here
handleHttpApi(cmdStr, false); // no stateUpdated() call here
stateUpdated(CALL_MODE_BUTTON);
parsed = true;
}
@@ -182,7 +174,7 @@ static bool remoteJson(int button)
}
// Callback function that will be executed when data is received
void handleWiZdata(uint8_t *incomingData, size_t len) {
void handleRemote(uint8_t *incomingData, size_t len) {
message_structure_t *incoming = reinterpret_cast<message_structure_t *>(incomingData);
if (strcmp(last_signal_src, linked_remote) != 0) {
@@ -208,15 +200,8 @@ void handleWiZdata(uint8_t *incomingData, size_t len) {
DEBUG_PRINT(F("] button: "));
DEBUG_PRINTLN(incoming->button);
ESPNowButton = incoming->button; // save state, do not process in callback (can cause glitches)
last_seq = cur_seq;
}
// process ESPNow button data (acesses FS, should not be called while update to avoid glitches)
void handleRemote() {
if(ESPNowButton >= 0) {
if (!remoteJson(ESPNowButton))
switch (ESPNowButton) {
if (!remoteJson(incoming->button))
switch (incoming->button) {
case WIZMOTE_BUTTON_ON : setOn(); break;
case WIZMOTE_BUTTON_OFF : setOff(); break;
case WIZMOTE_BUTTON_ONE : presetWithFallback(1, FX_MODE_STATIC, 0); break;
@@ -232,10 +217,9 @@ void handleRemote() {
case WIZ_SMART_BUTTON_BRIGHT_DOWN : brightnessDown(); break;
default: break;
}
}
ESPNowButton = -1;
last_seq = cur_seq;
}
#else
void handleRemote() {}
void handleRemote(uint8_t *incomingData, size_t len) {}
#endif
+55 -70
View File
@@ -134,17 +134,15 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
strip.correctWB = request->hasArg(F("CCT"));
strip.cctFromRgb = request->hasArg(F("CR"));
cctICused = request->hasArg(F("IC"));
Bus::setCCTBlend(request->arg(F("CB")).toInt());
strip.cctBlending = request->arg(F("CB")).toInt();
Bus::setCCTBlend(strip.cctBlending);
Bus::setGlobalAWMode(request->arg(F("AW")).toInt());
strip.setTargetFps(request->arg(F("FR")).toInt());
useGlobalLedBuffer = request->hasArg(F("LD"));
#if defined(ARDUINO_ARCH_ESP32) && !defined(CONFIG_IDF_TARGET_ESP32C3)
useParallelI2S = request->hasArg(F("PR"));
#endif
bool busesChanged = false;
for (int s = 0; s < WLED_MAX_BUSSES+WLED_MIN_VIRTUAL_BUSSES; s++) {
int offset = s < 10 ? '0' : 'A' - 10;
int offset = s < 10 ? 48 : 55;
char lp[4] = "L0"; lp[2] = offset+s; lp[3] = 0; //ascii 0-9 //strip data pin
char lc[4] = "LC"; lc[2] = offset+s; lc[3] = 0; //strip length
char co[4] = "CO"; co[2] = offset+s; co[3] = 0; //strip color order
@@ -163,7 +161,7 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
break;
}
for (int i = 0; i < 5; i++) {
lp[1] = '0'+i;
lp[1] = offset+i;
if (!request->hasArg(lp)) break;
pins[i] = (request->arg(lp).length() > 0) ? request->arg(lp).toInt() : 255;
}
@@ -210,15 +208,15 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
type |= request->hasArg(rf) << 7; // off refresh override
// actual finalization is done in WLED::loop() (removing old busses and adding new)
// this may happen even before this loop is finished so we do "doInitBusses" after the loop
busConfigs.push_back(std::move(BusConfig(type, pins, start, length, colorOrder | (channelSwap<<4), request->hasArg(cv), skip, awmode, freq, useGlobalLedBuffer, maPerLed, maMax)));
if (busConfigs[s] != nullptr) delete busConfigs[s];
busConfigs[s] = new BusConfig(type, pins, start, length, colorOrder | (channelSwap<<4), request->hasArg(cv), skip, awmode, freq, useGlobalLedBuffer, maPerLed, maMax);
busesChanged = true;
}
//doInitBusses = busesChanged; // we will do that below to ensure all input data is processed
// we will not bother with pre-allocating ColorOrderMappings vector
BusManager::getColorOrderMap().reset();
for (int s = 0; s < WLED_MAX_COLOR_ORDER_MAPPINGS; s++) {
int offset = s < 10 ? '0' : 'A' - 10;
int offset = s < 10 ? 48 : 55;
char xs[4] = "XS"; xs[2] = offset+s; xs[3] = 0; //start LED
char xc[4] = "XC"; xc[2] = offset+s; xc[3] = 0; //strip length
char xo[4] = "XO"; xo[2] = offset+s; xo[3] = 0; //color order
@@ -257,7 +255,7 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
disablePullUp = (bool)request->hasArg(F("IP"));
touchThreshold = request->arg(F("TT")).toInt();
for (int i = 0; i < WLED_MAX_BUTTONS; i++) {
int offset = i < 10 ? '0' : 'A' - 10;
int offset = i < 10 ? 48 : 55;
char bt[4] = "BT"; bt[2] = offset+i; bt[3] = 0; // button pin (use A,B,C,... if WLED_MAX_BUTTONS>10)
char be[4] = "BE"; be[2] = offset+i; be[3] = 0; // button type (use A,B,C,... if WLED_MAX_BUTTONS>10)
int hw_btn_pin = request->arg(bt).toInt();
@@ -805,16 +803,14 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
//HTTP API request parser
bool handleSet(AsyncWebServerRequest *request, const String& req, bool apply)
bool handleHttpApi(const String& req, bool apply)
{
if (!(req.indexOf("win") >= 0)) return false;
int pos = 0;
DEBUG_PRINTF_P(PSTR("API req: %s\n"), req.c_str());
//segment select (sets main segment)
pos = req.indexOf(F("SM="));
if (pos > 0 && !realtimeMode) {
if (pos >= 0 && !realtimeMode) {
strip.setMainSegmentId(getNumVal(&req, pos));
}
@@ -823,7 +819,7 @@ bool handleSet(AsyncWebServerRequest *request, const String& req, bool apply)
bool singleSegment = false;
pos = req.indexOf(F("SS="));
if (pos > 0) {
if (pos >= 0) {
unsigned t = getNumVal(&req, pos);
if (t < strip.getSegmentsNum()) {
selectedSeg = t;
@@ -833,7 +829,7 @@ bool handleSet(AsyncWebServerRequest *request, const String& req, bool apply)
Segment& selseg = strip.getSegment(selectedSeg);
pos = req.indexOf(F("SV=")); //segment selected
if (pos > 0) {
if (pos >= 0) {
unsigned t = getNumVal(&req, pos);
if (t == 2) for (unsigned i = 0; i < strip.getSegmentsNum(); i++) strip.getSegment(i).selected = false; // unselect other segments
selseg.selected = t;
@@ -861,31 +857,31 @@ bool handleSet(AsyncWebServerRequest *request, const String& req, bool apply)
uint8_t grpI = selseg.grouping;
uint16_t spcI = selseg.spacing;
pos = req.indexOf(F("&S=")); //segment start
if (pos > 0) {
if (pos >= 0) {
startI = std::abs(getNumVal(&req, pos));
}
pos = req.indexOf(F("S2=")); //segment stop
if (pos > 0) {
if (pos >= 0) {
stopI = std::abs(getNumVal(&req, pos));
}
pos = req.indexOf(F("GP=")); //segment grouping
if (pos > 0) {
if (pos >= 0) {
grpI = std::max(1,getNumVal(&req, pos));
}
pos = req.indexOf(F("SP=")); //segment spacing
if (pos > 0) {
if (pos >= 0) {
spcI = std::max(0,getNumVal(&req, pos));
}
strip.setSegment(selectedSeg, startI, stopI, grpI, spcI, UINT16_MAX, startY, stopY);
pos = req.indexOf(F("RV=")); //Segment reverse
if (pos > 0) selseg.reverse = req.charAt(pos+3) != '0';
if (pos >= 0) selseg.reverse = req.charAt(pos+3) != '0';
pos = req.indexOf(F("MI=")); //Segment mirror
if (pos > 0) selseg.mirror = req.charAt(pos+3) != '0';
if (pos >= 0) selseg.mirror = req.charAt(pos+3) != '0';
pos = req.indexOf(F("SB=")); //Segment brightness/opacity
if (pos > 0) {
if (pos >= 0) {
byte segbri = getNumVal(&req, pos);
selseg.setOption(SEG_OPTION_ON, segbri); // use transition
if (segbri) {
@@ -894,7 +890,7 @@ bool handleSet(AsyncWebServerRequest *request, const String& req, bool apply)
}
pos = req.indexOf(F("SW=")); //segment power
if (pos > 0) {
if (pos >= 0) {
switch (getNumVal(&req, pos)) {
case 0: selseg.setOption(SEG_OPTION_ON, false); break; // use transition
case 1: selseg.setOption(SEG_OPTION_ON, true); break; // use transition
@@ -903,13 +899,13 @@ bool handleSet(AsyncWebServerRequest *request, const String& req, bool apply)
}
pos = req.indexOf(F("PS=")); //saves current in preset
if (pos > 0) savePreset(getNumVal(&req, pos));
if (pos >= 0) savePreset(getNumVal(&req, pos));
pos = req.indexOf(F("P1=")); //sets first preset for cycle
if (pos > 0) presetCycMin = getNumVal(&req, pos);
if (pos >= 0) presetCycMin = getNumVal(&req, pos);
pos = req.indexOf(F("P2=")); //sets last preset for cycle
if (pos > 0) presetCycMax = getNumVal(&req, pos);
if (pos >= 0) presetCycMax = getNumVal(&req, pos);
//apply preset
if (updateVal(req.c_str(), "PL=", &presetCycCurr, presetCycMin, presetCycMax)) {
@@ -917,7 +913,7 @@ bool handleSet(AsyncWebServerRequest *request, const String& req, bool apply)
}
pos = req.indexOf(F("NP")); //advances to next preset in a playlist
if (pos > 0) doAdvancePlaylist = true;
if (pos >= 0) doAdvancePlaylist = true;
//set brightness
updateVal(req.c_str(), "&A=", &bri);
@@ -937,7 +933,7 @@ bool handleSet(AsyncWebServerRequest *request, const String& req, bool apply)
#ifdef WLED_ENABLE_LOXONE
//lox parser
pos = req.indexOf(F("LX=")); // Lox primary color
if (pos > 0) {
if (pos >= 0) {
int lxValue = getNumVal(&req, pos);
if (parseLx(lxValue, colIn)) {
bri = 255;
@@ -946,7 +942,7 @@ bool handleSet(AsyncWebServerRequest *request, const String& req, bool apply)
}
}
pos = req.indexOf(F("LY=")); // Lox secondary color
if (pos > 0) {
if (pos >= 0) {
int lxValue = getNumVal(&req, pos);
if(parseLx(lxValue, colInSec)) {
bri = 255;
@@ -958,11 +954,11 @@ bool handleSet(AsyncWebServerRequest *request, const String& req, bool apply)
//set hue
pos = req.indexOf(F("HU="));
if (pos > 0) {
if (pos >= 0) {
uint16_t temphue = getNumVal(&req, pos);
byte tempsat = 255;
pos = req.indexOf(F("SA="));
if (pos > 0) {
if (pos >= 0) {
tempsat = getNumVal(&req, pos);
}
byte sec = req.indexOf(F("H2"));
@@ -972,7 +968,7 @@ bool handleSet(AsyncWebServerRequest *request, const String& req, bool apply)
//set white spectrum (kelvin)
pos = req.indexOf(F("&K="));
if (pos > 0) {
if (pos >= 0) {
byte sec = req.indexOf(F("K2"));
colorKtoRGB(getNumVal(&req, pos), (sec>0) ? colInSec : colIn);
col0Changed |= (!sec); col1Changed |= sec;
@@ -981,17 +977,17 @@ bool handleSet(AsyncWebServerRequest *request, const String& req, bool apply)
//set color from HEX or 32bit DEC
byte tmpCol[4];
pos = req.indexOf(F("CL="));
if (pos > 0) {
if (pos >= 0) {
colorFromDecOrHexString(colIn, (char*)req.substring(pos + 3).c_str());
col0Changed = true;
}
pos = req.indexOf(F("C2="));
if (pos > 0) {
if (pos >= 0) {
colorFromDecOrHexString(colInSec, (char*)req.substring(pos + 3).c_str());
col1Changed = true;
}
pos = req.indexOf(F("C3="));
if (pos > 0) {
if (pos >= 0) {
colorFromDecOrHexString(tmpCol, (char*)req.substring(pos + 3).c_str());
uint32_t col2 = RGBW32(tmpCol[0], tmpCol[1], tmpCol[2], tmpCol[3]);
selseg.setColor(2, col2); // defined above (SS= or main)
@@ -1000,7 +996,7 @@ bool handleSet(AsyncWebServerRequest *request, const String& req, bool apply)
//set to random hue SR=0->1st SR=1->2nd
pos = req.indexOf(F("SR"));
if (pos > 0) {
if (pos >= 0) {
byte sec = getNumVal(&req, pos);
setRandomColor(sec? colInSec : colIn);
col0Changed |= (!sec); col1Changed |= sec;
@@ -1008,7 +1004,7 @@ bool handleSet(AsyncWebServerRequest *request, const String& req, bool apply)
//swap 2nd & 1st
pos = req.indexOf(F("SC"));
if (pos > 0) {
if (pos >= 0) {
byte temp;
for (unsigned i=0; i<4; i++) {
temp = colIn[i];
@@ -1034,10 +1030,7 @@ bool handleSet(AsyncWebServerRequest *request, const String& req, bool apply)
bool fxModeChanged = false, speedChanged = false, intensityChanged = false, paletteChanged = false;
bool custom1Changed = false, custom2Changed = false, custom3Changed = false, check1Changed = false, check2Changed = false, check3Changed = false;
// set effect parameters
if (updateVal(req.c_str(), "FX=", &effectIn, 0, strip.getModeCount()-1)) {
if (request != nullptr) unloadPlaylist(); // unload playlist if changing FX using web request
fxModeChanged = true;
}
fxModeChanged = updateVal(req.c_str(), "FX=", &effectIn, 0, strip.getModeCount()-1);
speedChanged = updateVal(req.c_str(), "SX=", &speedIn);
intensityChanged = updateVal(req.c_str(), "IX=", &intensityIn);
paletteChanged = updateVal(req.c_str(), "FP=", &paletteIn, 0, strip.getPaletteCount()-1);
@@ -1068,31 +1061,31 @@ bool handleSet(AsyncWebServerRequest *request, const String& req, bool apply)
//set advanced overlay
pos = req.indexOf(F("OL="));
if (pos > 0) {
if (pos >= 0) {
overlayCurrent = getNumVal(&req, pos);
}
//apply macro (deprecated, added for compatibility with pre-0.11 automations)
pos = req.indexOf(F("&M="));
if (pos > 0) {
if (pos >= 0) {
applyPreset(getNumVal(&req, pos) + 16);
}
//toggle send UDP direct notifications
pos = req.indexOf(F("SN="));
if (pos > 0) notifyDirect = (req.charAt(pos+3) != '0');
if (pos >= 0) notifyDirect = (req.charAt(pos+3) != '0');
//toggle receive UDP direct notifications
pos = req.indexOf(F("RN="));
if (pos > 0) receiveGroups = (req.charAt(pos+3) != '0') ? receiveGroups | 1 : receiveGroups & 0xFE;
if (pos >= 0) receiveGroups = (req.charAt(pos+3) != '0') ? receiveGroups | 1 : receiveGroups & 0xFE;
//receive live data via UDP/Hyperion
pos = req.indexOf(F("RD="));
if (pos > 0) receiveDirect = (req.charAt(pos+3) != '0');
if (pos >= 0) receiveDirect = (req.charAt(pos+3) != '0');
//main toggle on/off (parse before nightlight, #1214)
pos = req.indexOf(F("&T="));
if (pos > 0) {
if (pos >= 0) {
nightlightActive = false; //always disable nightlight when toggling
switch (getNumVal(&req, pos))
{
@@ -1104,9 +1097,9 @@ bool handleSet(AsyncWebServerRequest *request, const String& req, bool apply)
//toggle nightlight mode
bool aNlDef = false;
if (req.indexOf(F("&ND")) > 0) aNlDef = true;
if (req.indexOf(F("&ND")) >= 0) aNlDef = true;
pos = req.indexOf(F("NL="));
if (pos > 0)
if (pos >= 0)
{
if (req.charAt(pos+3) == '0')
{
@@ -1126,14 +1119,14 @@ bool handleSet(AsyncWebServerRequest *request, const String& req, bool apply)
//set nightlight target brightness
pos = req.indexOf(F("NT="));
if (pos > 0) {
if (pos >= 0) {
nightlightTargetBri = getNumVal(&req, pos);
nightlightActiveOld = false; //re-init
}
//toggle nightlight fade
pos = req.indexOf(F("NF="));
if (pos > 0)
if (pos >= 0)
{
nightlightMode = getNumVal(&req, pos);
@@ -1142,24 +1135,24 @@ bool handleSet(AsyncWebServerRequest *request, const String& req, bool apply)
if (nightlightMode > NL_MODE_SUN) nightlightMode = NL_MODE_SUN;
pos = req.indexOf(F("TT="));
if (pos > 0) transitionDelay = getNumVal(&req, pos);
if (pos >= 0) transitionDelay = getNumVal(&req, pos);
if (fadeTransition) strip.setTransition(transitionDelay);
//set time (unix timestamp)
pos = req.indexOf(F("ST="));
if (pos > 0) {
if (pos >= 0) {
setTimeFromAPI(getNumVal(&req, pos));
}
//set countdown goal (unix timestamp)
pos = req.indexOf(F("CT="));
if (pos > 0) {
if (pos >= 0) {
countdownTime = getNumVal(&req, pos);
if (countdownTime - toki.second() > 0) countdownOverTriggered = false;
}
pos = req.indexOf(F("LO="));
if (pos > 0) {
if (pos >= 0) {
realtimeOverride = getNumVal(&req, pos);
if (realtimeOverride > 2) realtimeOverride = REALTIME_OVERRIDE_ALWAYS;
if (realtimeMode && useMainSegmentOnly) {
@@ -1168,36 +1161,28 @@ bool handleSet(AsyncWebServerRequest *request, const String& req, bool apply)
}
pos = req.indexOf(F("RB"));
if (pos > 0) doReboot = true;
if (pos >= 0) doReboot = true;
// clock mode, 0: normal, 1: countdown
pos = req.indexOf(F("NM="));
if (pos > 0) countdownMode = (req.charAt(pos+3) != '0');
if (pos >= 0) countdownMode = (req.charAt(pos+3) != '0');
pos = req.indexOf(F("U0=")); //user var 0
if (pos > 0) {
if (pos >= 0) {
userVar0 = getNumVal(&req, pos);
}
pos = req.indexOf(F("U1=")); //user var 1
if (pos > 0) {
if (pos >= 0) {
userVar1 = getNumVal(&req, pos);
}
// you can add more if you need
// global colPri[], effectCurrent, ... are updated in stateChanged()
// global col[], effectCurrent, ... are updated in stateChanged()
if (!apply) return true; // when called by JSON API, do not call colorUpdated() here
pos = req.indexOf(F("&NN")); //do not send UDP notifications this time
stateUpdated((pos > 0) ? CALL_MODE_NO_NOTIFY : CALL_MODE_DIRECT_CHANGE);
// internal call, does not send XML response
pos = req.indexOf(F("IN"));
if ((request != nullptr) && (pos < 1)) {
auto response = request->beginResponseStream("text/xml");
XML_response(*response);
request->send(response);
}
stateUpdated((pos >= 0) ? CALL_MODE_NO_NOTIFY : CALL_MODE_DIRECT_CHANGE);
return true;
}
+2 -2
View File
@@ -34,8 +34,8 @@ static const int enablePin = -1; // disable the enable pin because it is not ne
static const int rxPin = -1; // disable the receiving pin because it is not needed - softhack007: Pin=-1 means "use default" not "disable"
static const int txPin = 2; // transmit DMX data over this pin (default is pin 2)
//DMX value array and size. Entry 0 will hold startbyte, so we need 512+1 elements
static uint8_t dmxData[dmxMaxChannel+1] = { 0 };
//DMX value array and size. Entry 0 will hold startbyte
static uint8_t dmxData[dmxMaxChannel] = { 0 };
static int chanSize = 0;
#if !defined(DMX_SEND_ONLY)
static int currentChannel = 0;
+5 -7
View File
@@ -300,7 +300,7 @@ void parseNotifyPacket(uint8_t *udpIn) {
if (!receiveSegmentOptions) {
DEBUG_PRINTF_P(PSTR("Set segment w/o options: %d [%d,%d;%d,%d]\n"), id, (int)start, (int)stop, (int)startY, (int)stopY);
strip.suspend(); //should not be needed as UDP handling is not done in ISR callbacks but still added "just in case"
selseg.setGeometry(start, stop, selseg.grouping, selseg.spacing, offset, startY, stopY);
selseg.setUp(start, stop, selseg.grouping, selseg.spacing, offset, startY, stopY);
strip.resume();
continue; // we do receive bounds, but not options
}
@@ -342,12 +342,12 @@ void parseNotifyPacket(uint8_t *udpIn) {
if (receiveSegmentBounds) {
DEBUG_PRINTF_P(PSTR("Set segment w/ options: %d [%d,%d;%d,%d]\n"), id, (int)start, (int)stop, (int)startY, (int)stopY);
strip.suspend(); //should not be needed as UDP handling is not done in ISR callbacks but still added "just in case"
selseg.setGeometry(start, stop, udpIn[5+ofs], udpIn[6+ofs], offset, startY, stopY);
selseg.setUp(start, stop, udpIn[5+ofs], udpIn[6+ofs], offset, startY, stopY);
strip.resume();
} else {
DEBUG_PRINTF_P(PSTR("Set segment grouping: %d [%d,%d]\n"), id, (int)udpIn[5+ofs], (int)udpIn[6+ofs]);
strip.suspend(); //should not be needed as UDP handling is not done in ISR callbacks but still added "just in case"
selseg.setGeometry(selseg.start, selseg.stop, udpIn[5+ofs], udpIn[6+ofs], selseg.offset, selseg.startY, selseg.stopY);
selseg.setUp(selseg.start, selseg.stop, udpIn[5+ofs], udpIn[6+ofs], selseg.offset, selseg.startY, selseg.stopY);
strip.resume();
}
}
@@ -681,9 +681,7 @@ void handleNotifications()
if (requestJSONBufferLock(18)) {
if (udpIn[0] >= 'A' && udpIn[0] <= 'Z') { //HTTP API
String apireq = "win"; apireq += '&'; // reduce flash string usage
apireq += (char*)udpIn;
handleSet(nullptr, apireq);
handleHttpApi((char*)udpIn);
} else if (udpIn[0] == '{') { //JSON API
DeserializationError error = deserializeJson(*pDoc, udpIn);
JsonObject root = pDoc->as<JsonObject>();
@@ -979,7 +977,7 @@ void espNowReceiveCB(uint8_t* address, uint8_t* data, uint8_t len, signed int rs
// handle WiZ Mote data
if (data[0] == 0x91 || data[0] == 0x81 || data[0] == 0x80) {
handleWiZdata(data, len);
handleRemote(data, len);
return;
}
+7 -294
View File
@@ -1,19 +1,6 @@
#include "wled.h"
#include "fcn_declare.h"
#include "const.h"
#ifdef ESP8266
#include "user_interface.h" // for bootloop detection
#include <Hash.h> // for SHA1 on ESP8266
#else
#include <Update.h>
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 4, 0)
#include "esp32/rtc.h" // for bootloop detection
#elif ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(3, 3, 0)
#include "soc/rtc.h"
#endif
#include "mbedtls/sha1.h" // for SHA1 on ESP32
#include "esp_adc_cal.h"
#endif
//helper to get int value at a position in string
@@ -65,7 +52,7 @@ void parseNumber(const char* str, byte* val, byte minv, byte maxv)
*val = atoi(str);
}
//getVal supports inc/decrementing and random ("X~Y(r|~[w][-][Z])" form)
bool getVal(JsonVariant elem, byte* val, byte vmin, byte vmax) {
if (elem.is<int>()) {
if (elem < 0) return false; //ignore e.g. {"ps":-1}
@@ -73,12 +60,8 @@ bool getVal(JsonVariant elem, byte* val, byte vmin, byte vmax) {
return true;
} else if (elem.is<const char*>()) {
const char* str = elem;
size_t len = strnlen(str, 14);
if (len == 0 || len > 12) return false;
// fix for #3605 & #4346
// ignore vmin and vmax and use as specified in API
if (len > 3 && (strchr(str,'r') || strchr(str,'~') != strrchr(str,'~'))) vmax = vmin = 0; // we have "X~Y(r|~[w][-][Z])" form
// end fix
size_t len = strnlen(str, 12);
if (len == 0 || len > 10) return false;
parseNumber(str, val, vmin, vmax);
return true;
}
@@ -389,39 +372,6 @@ uint16_t crc16(const unsigned char* data_p, size_t length) {
return crc;
}
// fastled beatsin: 1:1 replacements to remove the use of fastled sin16()
// Generates a 16-bit sine wave at a given BPM that oscillates within a given range. see fastled for details.
uint16_t beatsin88_t(accum88 beats_per_minute_88, uint16_t lowest, uint16_t highest, uint32_t timebase, uint16_t phase_offset)
{
uint16_t beat = beat88( beats_per_minute_88, timebase);
uint16_t beatsin (sin16_t( beat + phase_offset) + 32768);
uint16_t rangewidth = highest - lowest;
uint16_t scaledbeat = scale16( beatsin, rangewidth);
uint16_t result = lowest + scaledbeat;
return result;
}
// Generates a 16-bit sine wave at a given BPM that oscillates within a given range. see fastled for details.
uint16_t beatsin16_t(accum88 beats_per_minute, uint16_t lowest, uint16_t highest, uint32_t timebase, uint16_t phase_offset)
{
uint16_t beat = beat16( beats_per_minute, timebase);
uint16_t beatsin = (sin16_t( beat + phase_offset) + 32768);
uint16_t rangewidth = highest - lowest;
uint16_t scaledbeat = scale16( beatsin, rangewidth);
uint16_t result = lowest + scaledbeat;
return result;
}
// Generates an 8-bit sine wave at a given BPM that oscillates within a given range. see fastled for details.
uint8_t beatsin8_t(accum88 beats_per_minute, uint8_t lowest, uint8_t highest, uint32_t timebase, uint8_t phase_offset)
{
uint8_t beat = beat8( beats_per_minute, timebase);
uint8_t beatsin = sin8_t( beat + phase_offset);
uint8_t rangewidth = highest - lowest;
uint8_t scaledbeat = scale8( beatsin, rangewidth);
uint8_t result = lowest + scaledbeat;
return result;
}
///////////////////////////////////////////////////////////////////////////////
// Begin simulateSound (to enable audio enhanced effects to display something)
@@ -481,8 +431,8 @@ um_data_t* simulateSound(uint8_t simulationId)
default:
case UMS_BeatSin:
for (int i = 0; i<16; i++)
fftResult[i] = beatsin8_t(120 / (i+1), 0, 255);
// fftResult[i] = (beatsin8_t(120, 0, 255) + (256/16 * i)) % 256;
fftResult[i] = beatsin8(120 / (i+1), 0, 255);
// fftResult[i] = (beatsin8(120, 0, 255) + (256/16 * i)) % 256;
volumeSmth = fftResult[8];
break;
case UMS_WeWillRockYou:
@@ -519,12 +469,12 @@ um_data_t* simulateSound(uint8_t simulationId)
break;
case UMS_10_13:
for (int i = 0; i<16; i++)
fftResult[i] = inoise8(beatsin8_t(90 / (i+1), 0, 200)*15 + (ms>>10), ms>>3);
fftResult[i] = inoise8(beatsin8(90 / (i+1), 0, 200)*15 + (ms>>10), ms>>3);
volumeSmth = fftResult[8];
break;
case UMS_14_3:
for (int i = 0; i<16; i++)
fftResult[i] = inoise8(beatsin8_t(120 / (i+1), 10, 30)*10 + (ms>>14), ms>>3);
fftResult[i] = inoise8(beatsin8(120 / (i+1), 10, 30)*10 + (ms>>14), ms>>3);
volumeSmth = fftResult[8];
break;
}
@@ -607,240 +557,3 @@ uint8_t get_random_wheel_index(uint8_t pos) {
float mapf(float x, float in_min, float in_max, float out_min, float out_max) {
return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
}
// bootloop detection and handling
// checks if the ESP reboots multiple times due to a crash or watchdog timeout
// if a bootloop is detected: restore settings from backup, then reset settings, then switch boot image (and repeat)
#define BOOTLOOP_INTERVAL_MILLIS 120000 // time limit between crashes: 120 seconds (2 minutes)
#define BOOTLOOP_THRESHOLD 5 // number of consecutive crashes to trigger bootloop detection
#define BOOTLOOP_ACTION_RESTORE 0 // default action: restore config from /bkp.cfg.json
#define BOOTLOOP_ACTION_RESET 1 // if restore does not work, reset config (rename /cfg.json to /rst.cfg.json)
#define BOOTLOOP_ACTION_OTA 2 // swap the boot partition
#define BOOTLOOP_ACTION_DUMP 3 // nothing seems to help, dump files to serial and reboot (until hardware reset)
// Platform-agnostic abstraction
enum class ResetReason {
Power,
Software,
Crash,
Brownout
};
#ifdef ESP8266
// Place variables in RTC memory via references, since RTC memory is not exposed via the linker in the Non-OS SDK
// Use an offset of 32 as there's some hints that the first 128 bytes of "user" memory are used by the OTA system
// Ref: https://github.com/esp8266/Arduino/blob/78d0d0aceacc1553f45ad8154592b0af22d1eede/cores/esp8266/Esp.cpp#L168
static volatile uint32_t& bl_last_boottime = *(RTC_USER_MEM + 32);
static volatile uint32_t& bl_crashcounter = *(RTC_USER_MEM + 33);
static volatile uint32_t& bl_actiontracker = *(RTC_USER_MEM + 34);
static inline ResetReason rebootReason() {
uint32_t resetReason = system_get_rst_info()->reason;
if (resetReason == REASON_EXCEPTION_RST
|| resetReason == REASON_WDT_RST
|| resetReason == REASON_SOFT_WDT_RST)
return ResetReason::Crash;
if (resetReason == REASON_SOFT_RESTART)
return ResetReason::Software;
return ResetReason::Power;
}
static inline uint32_t getRtcMillis() { return system_get_rtc_time() / 160; }; // rtc ticks ~160000Hz
#else
// variables in RTC_NOINIT memory persist between reboots (but not on hardware reset)
RTC_NOINIT_ATTR static uint32_t bl_last_boottime;
RTC_NOINIT_ATTR static uint32_t bl_crashcounter;
RTC_NOINIT_ATTR static uint32_t bl_actiontracker;
static inline ResetReason rebootReason() {
esp_reset_reason_t reason = esp_reset_reason();
if (reason == ESP_RST_BROWNOUT) return ResetReason::Brownout;
if (reason == ESP_RST_SW) return ResetReason::Software;
if (reason == ESP_RST_PANIC || reason == ESP_RST_WDT || reason == ESP_RST_INT_WDT || reason == ESP_RST_TASK_WDT) return ResetReason::Crash;
return ResetReason::Power;
}
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 4, 0)
static inline uint32_t getRtcMillis() { return esp_rtc_get_time_us() / 1000; }
#elif ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(3, 3, 0)
static inline uint32_t getRtcMillis() { return rtc_time_slowclk_to_us(rtc_time_get(), rtc_clk_slow_freq_get_hz()) / 1000; }
#endif
void bootloopCheckOTA() { bl_actiontracker = BOOTLOOP_ACTION_OTA; } // swap boot image if bootloop is detected instead of restoring config
#endif
// detect bootloop by checking the reset reason and the time since last boot
static bool detectBootLoop() {
uint32_t rtctime = getRtcMillis();
bool result = false;
switch(rebootReason()) {
case ResetReason::Power:
bl_actiontracker = BOOTLOOP_ACTION_RESTORE; // init action tracker if not an intentional reboot (e.g. from OTA or bootloop handler)
// fall through
case ResetReason::Software:
// no crash detected, reset counter
bl_crashcounter = 0;
break;
case ResetReason::Crash:
{
uint32_t rebootinterval = rtctime - bl_last_boottime;
if (rebootinterval < BOOTLOOP_INTERVAL_MILLIS) {
bl_crashcounter++;
if (bl_crashcounter >= BOOTLOOP_THRESHOLD) {
DEBUG_PRINTLN(F("!BOOTLOOP DETECTED!"));
bl_crashcounter = 0;
if(bl_actiontracker > BOOTLOOP_ACTION_DUMP) bl_actiontracker = BOOTLOOP_ACTION_RESTORE; // reset action tracker if out of bounds
result = true;
}
} else {
// Reset counter on long intervals to track only consecutive short-interval crashes
bl_crashcounter = 0;
// TODO: crash reporting goes here
}
break;
}
case ResetReason::Brownout:
// crash due to brownout can't be detected unless using flash memory to store bootloop variables
DEBUG_PRINTLN(F("brownout detected"));
//restoreConfig(); // TODO: blindly restoring config if brownout detected is a bad idea, need a better way (if at all)
break;
}
bl_last_boottime = rtctime; // store current runtime for next reboot
return result;
}
void handleBootLoop() {
DEBUG_PRINTF_P(PSTR("checking for bootloop: time %d, counter %d, action %d\n"), bl_last_boottime, bl_crashcounter, bl_actiontracker);
if (!detectBootLoop()) return; // no bootloop detected
switch(bl_actiontracker) {
case BOOTLOOP_ACTION_RESTORE:
restoreConfig();
++bl_actiontracker;
break;
case BOOTLOOP_ACTION_RESET:
resetConfig();
++bl_actiontracker;
break;
case BOOTLOOP_ACTION_OTA:
#ifndef ESP8266
if(Update.canRollBack()) {
DEBUG_PRINTLN(F("Swapping boot partition..."));
Update.rollBack(); // swap boot partition
}
++bl_actiontracker;
break;
#else
// fall through
#endif
case BOOTLOOP_ACTION_DUMP:
dumpFilesToSerial();
break;
}
ESP.restart(); // restart cleanly and don't wait for another crash
}
// Platform-agnostic SHA1 computation from String input
String computeSHA1(const String& input) {
#ifdef ESP8266
return sha1(input); // ESP8266 has built-in sha1() function
#else
// ESP32: Compute SHA1 hash using mbedtls
unsigned char shaResult[20]; // SHA1 produces 20 bytes
mbedtls_sha1_context ctx;
mbedtls_sha1_init(&ctx);
mbedtls_sha1_starts_ret(&ctx);
mbedtls_sha1_update_ret(&ctx, (const unsigned char*)input.c_str(), input.length());
mbedtls_sha1_finish_ret(&ctx, shaResult);
mbedtls_sha1_free(&ctx);
// Convert to hexadecimal string
char hexString[41];
for (int i = 0; i < 20; i++) {
sprintf(&hexString[i*2], "%02x", shaResult[i]);
}
hexString[40] = '\0';
return String(hexString);
#endif
}
#ifdef ESP32
String generateDeviceFingerprint() {
uint32_t fp[2] = {0, 0}; // create 64 bit fingerprint
esp_chip_info_t chip_info;
esp_chip_info(&chip_info);
esp_efuse_mac_get_default((uint8_t*)fp);
fp[1] ^= ESP.getFlashChipSize();
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 4, 4)
fp[0] ^= chip_info.full_revision | (chip_info.model << 16);
#else
fp[0] ^= chip_info.revision | (chip_info.model << 16);
#endif
// mix in ADC calibration data:
esp_adc_cal_characteristics_t ch;
#if SOC_ADC_MAX_BITWIDTH == 13 // S2 has 13 bit ADC
constexpr auto myBIT_WIDTH = ADC_WIDTH_BIT_13;
#else
constexpr auto myBIT_WIDTH = ADC_WIDTH_BIT_12;
#endif
esp_adc_cal_characterize(ADC_UNIT_1, ADC_ATTEN_DB_11, myBIT_WIDTH, 1100, &ch);
fp[0] ^= ch.coeff_a;
fp[1] ^= ch.coeff_b;
if (ch.low_curve) {
for (int i = 0; i < 8; i++) {
fp[0] ^= ch.low_curve[i];
}
}
if (ch.high_curve) {
for (int i = 0; i < 8; i++) {
fp[1] ^= ch.high_curve[i];
}
}
char fp_string[17]; // 16 hex chars + null terminator
sprintf(fp_string, "%08X%08X", fp[1], fp[0]);
return String(fp_string);
}
#else // ESP8266
String generateDeviceFingerprint() {
uint32_t fp[2] = {0, 0}; // create 64 bit fingerprint
WiFi.macAddress((uint8_t*)&fp); // use MAC address as fingerprint base
fp[0] ^= ESP.getFlashChipId();
fp[1] ^= ESP.getFlashChipSize() | ESP.getFlashChipVendorId() << 16;
char fp_string[17]; // 16 hex chars + null terminator
sprintf(fp_string, "%08X%08X", fp[1], fp[0]);
return String(fp_string);
}
#endif
// Generate a device ID based on SHA1 hash of MAC address salted with other unique device info
// Returns: original SHA1 + last 2 chars of double-hashed SHA1 (42 chars total)
String getDeviceId() {
static String cachedDeviceId = "";
if (cachedDeviceId.length() > 0) return cachedDeviceId;
// The device string is deterministic as it needs to be consistent for the same device, even after a full flash erase
// MAC is salted with other consistent device info to avoid rainbow table attacks.
// If the MAC address is known by malicious actors, they could precompute SHA1 hashes to impersonate devices,
// but as WLED developers are just looking at statistics and not authenticating devices, this is acceptable.
// If the usage data was exfiltrated, you could not easily determine the MAC from the device ID without brute forcing SHA1
String firstHash = computeSHA1(generateDeviceFingerprint());
// Second hash: SHA1 of the first hash
String secondHash = computeSHA1(firstHash);
// Concatenate first hash + last 2 chars of second hash
cachedDeviceId = firstHash + secondHash.substring(38);
return cachedDeviceId;
}
+64 -38
View File
@@ -1,7 +1,6 @@
#define WLED_DEFINE_GLOBAL_VARS //only in one source file, wled.cpp!
#include "wled.h"
#include "wled_ethernet.h"
#include "ota_update.h"
#include <Arduino.h>
#if defined(ARDUINO_ARCH_ESP32) && defined(WLED_DISABLE_BROWNOUT_DET)
@@ -85,9 +84,6 @@ void WLED::loop()
#ifndef WLED_DISABLE_INFRARED
handleIR();
#endif
#ifndef WLED_DISABLE_ESPNOW
handleRemote();
#endif
#ifndef WLED_DISABLE_ALEXA
handleAlexa();
#endif
@@ -165,9 +161,9 @@ void WLED::loop()
if (millis() - heapTime > 15000) {
uint32_t heap = ESP.getFreeHeap();
if (heap < MIN_HEAP_SIZE && lastHeap < MIN_HEAP_SIZE) {
DEBUG_PRINTF_P(PSTR("Heap too low! %u\n"), heap);
DEBUG_PRINTF_P(PSTR("Heap too low! %u\n"), heap);
forceReconnect = true;
strip.resetSegments(); // remove all but one segments from memory
if (!Update.isRunning()) forceReconnect = true;
} else if (heap < MIN_HEAP_SIZE) {
DEBUG_PRINTLN(F("Heap low, purging segments."));
strip.purgeSegments();
@@ -183,10 +179,49 @@ void WLED::loop()
DEBUG_PRINTLN(F("Re-init busses."));
bool aligned = strip.checkSegmentAlignment(); //see if old segments match old bus(ses)
BusManager::removeAll();
strip.finalizeInit(); // will create buses and also load default ledmap if present
unsigned mem = 0;
// determine if it is sensible to use parallel I2S outputs on ESP32 (i.e. more than 5 outputs = 1 I2S + 4 RMT)
bool useParallel = false;
#if defined(ARDUINO_ARCH_ESP32) && !defined(ARDUINO_ARCH_ESP32S2) && !defined(ARDUINO_ARCH_ESP32S3) && !defined(ARDUINO_ARCH_ESP32C3)
unsigned digitalCount = 0;
unsigned maxLedsOnBus = 0;
unsigned maxChannels = 0;
for (unsigned i = 0; i < WLED_MAX_BUSSES+WLED_MIN_VIRTUAL_BUSSES; i++) {
if (busConfigs[i] == nullptr) break;
if (!Bus::isDigital(busConfigs[i]->type)) continue;
if (!Bus::is2Pin(busConfigs[i]->type)) {
digitalCount++;
unsigned channels = Bus::getNumberOfChannels(busConfigs[i]->type);
if (busConfigs[i]->count > maxLedsOnBus) maxLedsOnBus = busConfigs[i]->count;
if (channels > maxChannels) maxChannels = channels;
}
}
DEBUG_PRINTF_P(PSTR("Maximum LEDs on a bus: %u\nDigital buses: %u\n"), maxLedsOnBus, digitalCount);
// we may remove 300 LEDs per bus limit when NeoPixelBus is updated beyond 2.9.0
if (maxLedsOnBus <= 300 && digitalCount > 5) {
DEBUG_PRINTF_P(PSTR("Switching to parallel I2S."));
useParallel = true;
BusManager::useParallelOutput();
mem = BusManager::memUsage(maxChannels, maxLedsOnBus, 8); // use alternate memory calculation (hse to be used *after* useParallelOutput())
}
#endif
// create buses/outputs
for (unsigned i = 0; i < WLED_MAX_BUSSES+WLED_MIN_VIRTUAL_BUSSES; i++) {
if (busConfigs[i] == nullptr || (!useParallel && i > 10)) break;
if (useParallel && i < 8) {
// if for some unexplained reason the above pre-calculation was wrong, update
unsigned memT = BusManager::memUsage(*busConfigs[i]); // includes x8 memory allocation for parallel I2S
if (memT > mem) mem = memT; // if we have unequal LED count use the largest
} else
mem += BusManager::memUsage(*busConfigs[i]); // includes global buffer
if (mem <= MAX_LED_MEMORY) BusManager::add(*busConfigs[i]);
delete busConfigs[i];
busConfigs[i] = nullptr;
}
strip.finalizeInit(); // also loads default ledmap if present
BusManager::setBrightness(bri); // fix re-initialised bus' brightness #4005
if (aligned) strip.makeAutoSegments();
else strip.fixInvalidSegments();
BusManager::setBrightness(scaledBri(bri)); // fix re-initialised bus' brightness #4005 and #4824
doSerializeConfig = true;
}
if (loadLedmap >= 0) {
@@ -404,9 +439,6 @@ void WLED::setup()
DEBUGFS_PRINTLN(F("FS failed!"));
errorFlag = ERR_FS_BEGIN;
}
handleBootLoop(); // check for bootloop and take action (requires WLED_FS)
#ifdef WLED_ADD_EEPROM_SUPPORT
else deEEP();
#else
@@ -422,13 +454,8 @@ void WLED::setup()
WLED_SET_AP_SSID(); // otherwise it is empty on first boot until config is saved
multiWiFi.push_back(WiFiConfig(CLIENT_SSID,CLIENT_PASS)); // initialise vector with default WiFi
if(!verifyConfig()) {
if(!restoreConfig()) {
resetConfig();
}
}
DEBUG_PRINTLN(F("Reading config"));
bool needsCfgSave = deserializeConfigFromFS();
deserializeConfigFromFS();
DEBUG_PRINTF_P(PSTR("heap %u\n"), ESP.getFreeHeap());
#if defined(STATUSLED) && STATUSLED>=0
@@ -448,12 +475,13 @@ void WLED::setup()
UsermodManager::setup();
DEBUG_PRINTF_P(PSTR("heap %u\n"), ESP.getFreeHeap());
if (needsCfgSave) serializeConfig(); // usermods required new parameters; need to wait for strip to be initialised #4752
if (strcmp(multiWiFi[0].clientSSID, DEFAULT_CLIENT_SSID) == 0)
showWelcomePage = true;
WiFi.persistent(false);
#ifdef WLED_USE_ETHERNET
WiFi.onEvent(WiFiEvent);
#endif
WiFi.mode(WIFI_STA); // enable scanning
findWiFi(true); // start scanning for available WiFi-s
@@ -470,7 +498,7 @@ void WLED::setup()
#endif
// fill in unique mdns default
if (strcmp(cmDNS, DEFAULT_MDNS_NAME) == 0) sprintf_P(cmDNS, PSTR("wled-%*s"), 6, escapedMac.c_str() + 6);
if (strcmp(cmDNS, "x") == 0) sprintf_P(cmDNS, PSTR("wled-%*s"), 6, escapedMac.c_str() + 6);
#ifndef WLED_DISABLE_MQTT
if (mqttDeviceTopic[0] == 0) sprintf_P(mqttDeviceTopic, PSTR("wled/%*s"), 6, escapedMac.c_str() + 6);
if (mqttClientID[0] == 0) sprintf_P(mqttClientID, PSTR("WLED-%*s"), 6, escapedMac.c_str() + 6);
@@ -543,7 +571,6 @@ void WLED::beginStrip()
strip.makeAutoSegments();
strip.setBrightness(0);
strip.setShowCallback(handleOverlayDraw);
doInitBusses = false;
if (turnOnAtBoot) {
if (briS > 0) bri = briS;
@@ -551,12 +578,11 @@ void WLED::beginStrip()
} else {
// fix for #3196
if (bootPreset > 0) {
// set all segments black (no transition)
for (unsigned i = 0; i < strip.getSegmentsNum(); i++) {
Segment &seg = strip.getSegment(i);
if (seg.isActive()) seg.colors[0] = BLACK;
}
colPri[0] = colPri[1] = colPri[2] = colPri[3] = 0; // needed for colorUpdated()
bool oldTransition = fadeTransition; // workaround if transitions are enabled
fadeTransition = false; // ignore transitions temporarily
strip.setColor(0, BLACK); // set all segments black
fadeTransition = oldTransition; // restore transitions
col[0] = col[1] = col[2] = col[3] = 0; // needed for colorUpdated()
}
briLast = briS; bri = 0;
strip.fill(BLACK);
@@ -755,7 +781,7 @@ int8_t WLED::findWiFi(bool doScan) {
void WLED::initConnection()
{
DEBUG_PRINTF_P(PSTR("initConnection() called @ %lus.\n"), millis()/1000);
DEBUG_PRINTLN(F("initConnection() called."));
#ifdef WLED_ENABLE_WEBSOCKETS
ws.onEvent(wsEvent);
@@ -770,7 +796,6 @@ void WLED::initConnection()
#endif
WiFi.disconnect(true); // close old connections
delay(5); // wait for hardware to be ready
#ifdef ESP8266
WiFi.setPhyMode(force802_3g ? WIFI_PHY_MODE_11G : WIFI_PHY_MODE_11N);
#endif
@@ -800,7 +825,9 @@ void WLED::initConnection()
if (WLED_WIFI_CONFIGURED) {
showWelcomePage = false;
DEBUG_PRINTF_P(PSTR("Connecting to %s...\n"), multiWiFi[selectedWiFi].clientSSID);
DEBUG_PRINT(F("Connecting to "));
DEBUG_PRINT(multiWiFi[selectedWiFi].clientSSID);
DEBUG_PRINTLN(F("..."));
// convert the "serverDescription" into a valid DNS hostname (alphanumeric)
char hostname[25];
@@ -899,8 +926,7 @@ void WLED::handleConnection()
{
static bool scanDone = true;
static byte stacO = 0;
const unsigned long now = millis();
const unsigned long nowS = now/1000;
unsigned long now = millis();
const bool wifiConfigured = WLED_WIFI_CONFIGURED;
// ignore connection handling if WiFi is configured and scan still running
@@ -909,7 +935,7 @@ void WLED::handleConnection()
return;
if (lastReconnectAttempt == 0 || forceReconnect) {
DEBUG_PRINTF_P(PSTR("Initial connect or forced reconnect (@ %lus).\n"), nowS);
DEBUG_PRINTLN(F("Initial connect or forced reconnect."));
selectedWiFi = findWiFi(); // find strongest WiFi
initConnection();
interfacesInited = false;
@@ -929,7 +955,8 @@ void WLED::handleConnection()
#endif
if (stac != stacO) {
stacO = stac;
DEBUG_PRINTF_P(PSTR("Connected AP clients: %d\n"), (int)stac);
DEBUG_PRINT(F("Connected AP clients: "));
DEBUG_PRINTLN(stac);
if (!WLED_CONNECTED && wifiConfigured) { // trying to connect, but not connected
if (stac)
WiFi.disconnect(); // disable search so that AP can work
@@ -952,7 +979,6 @@ void WLED::handleConnection()
initConnection();
interfacesInited = false;
scanDone = true;
return;
}
//send improv failed 6 seconds after second init attempt (24 sec. after provisioning)
if (improvActive > 2 && now - lastReconnectAttempt > 6000) {
@@ -961,13 +987,13 @@ void WLED::handleConnection()
}
if (now - lastReconnectAttempt > ((stac) ? 300000 : 18000) && wifiConfigured) {
if (improvActive == 2) improvActive = 3;
DEBUG_PRINTF_P(PSTR("Last reconnect (%lus) too old (@ %lus).\n"), lastReconnectAttempt/1000, nowS);
DEBUG_PRINTLN(F("Last reconnect too old."));
if (++selectedWiFi >= multiWiFi.size()) selectedWiFi = 0; // we couldn't connect, try with another network from the list
initConnection();
}
if (!apActive && now - lastReconnectAttempt > 12000 && (!wasConnected || apBehavior == AP_BEHAVIOR_NO_CONN)) {
if (!(apBehavior == AP_BEHAVIOR_TEMPORARY && now > WLED_AP_TIMEOUT)) {
DEBUG_PRINTF_P(PSTR("Not connected AP (@ %lus).\n"), nowS);
DEBUG_PRINTLN(F("Not connected AP."));
initAP(); // start AP only within first 5min
}
}
@@ -977,7 +1003,7 @@ void WLED::handleConnection()
dnsServer.stop();
WiFi.softAPdisconnect(true);
apActive = false;
DEBUG_PRINTF_P(PSTR("Temporary AP disabled (@ %lus).\n"), nowS);
DEBUG_PRINTLN(F("Temporary AP disabled."));
}
}
} else if (!interfacesInited) { //newly connected
+18 -11
View File
@@ -3,11 +3,12 @@
/*
Main sketch, global variable declarations
@title WLED project sketch
@version 0.15.0-b7
@author Christian Schwinne
*/
// version code in format yymmddb (b = daily build)
#define VERSION 2508020
#define VERSION 2410270
//uncomment this if you have a "my_config.h" file you'd like to use
//#define WLED_USE_MY_CONFIG
@@ -187,7 +188,6 @@ using PSRAMDynamicJsonDocument = BasicJsonDocument<PSRAM_Allocator>;
#include "pin_manager.h"
#include "bus_manager.h"
#include "FX.h"
#include "wled_metadata.h"
#ifndef CLIENT_SSID
#define CLIENT_SSID DEFAULT_CLIENT_SSID
@@ -260,6 +260,16 @@ using PSRAMDynamicJsonDocument = BasicJsonDocument<PSRAM_Allocator>;
#define STRINGIFY(X) #X
#define TOSTRING(X) STRINGIFY(X)
#ifndef WLED_VERSION
#define WLED_VERSION dev
#endif
#ifndef WLED_RELEASE_NAME
#define WLED_RELEASE_NAME dev_release
#endif
// Global Variable definitions
WLED_GLOBAL char versionString[] _INIT(TOSTRING(WLED_VERSION));
WLED_GLOBAL char releaseString[] _INIT(TOSTRING(WLED_RELEASE_NAME)); // somehow this will not work if using "const char releaseString[]
#define WLED_CODENAME "Kōsen"
// AP and OTA default passwords (for maximum security change them!)
@@ -358,7 +368,7 @@ WLED_GLOBAL bool noWifiSleep _INIT(false);
WLED_GLOBAL bool force802_3g _INIT(false);
#endif // WLED_SAVE_RAM
#ifdef ARDUINO_ARCH_ESP32
#if defined(LOLIN_WIFI_FIX) && (defined(CONFIG_IDF_TARGET_ESP32C3) || defined(CONFIG_IDF_TARGET_ESP32S2) || defined(CONFIG_IDF_TARGET_ESP32S3))
#if defined(LOLIN_WIFI_FIX) && (defined(ARDUINO_ARCH_ESP32C3) || defined(ARDUINO_ARCH_ESP32S2) || defined(ARDUINO_ARCH_ESP32S3))
WLED_GLOBAL uint8_t txPower _INIT(WIFI_POWER_8_5dBm);
#else
WLED_GLOBAL uint8_t txPower _INIT(WIFI_POWER_19_5dBm);
@@ -385,9 +395,6 @@ WLED_GLOBAL byte bootPreset _INIT(0); // save preset to load
WLED_GLOBAL bool useGlobalLedBuffer _INIT(false); // double buffering disabled on ESP8266
#else
WLED_GLOBAL bool useGlobalLedBuffer _INIT(true); // double buffering enabled on ESP32
#ifndef CONFIG_IDF_TARGET_ESP32C3
WLED_GLOBAL bool useParallelI2S _INIT(true); // parallel I2S for ESP32
#endif
#endif
#ifdef WLED_USE_IC_CCT
WLED_GLOBAL bool cctICused _INIT(true); // CCT IC used (Athom 15W bulbs)
@@ -398,7 +405,7 @@ WLED_GLOBAL bool gammaCorrectCol _INIT(true); // use gamma correction on col
WLED_GLOBAL bool gammaCorrectBri _INIT(false); // use gamma correction on brightness
WLED_GLOBAL float gammaCorrectVal _INIT(2.8f); // gamma correction value
WLED_GLOBAL byte colPri[] _INIT_N(({ 255, 160, 0, 0 })); // current RGB(W) primary color. colPri[] should be updated if you want to change the color.
WLED_GLOBAL byte col[] _INIT_N(({ 255, 160, 0, 0 })); // current RGB(W) primary color. col[] should be updated if you want to change the color.
WLED_GLOBAL byte colSec[] _INIT_N(({ 0, 0, 0, 0 })); // current RGB(W) secondary color
WLED_GLOBAL byte nightlightTargetBri _INIT(0); // brightness after nightlight is over
@@ -688,10 +695,10 @@ WLED_GLOBAL bool receiveNotificationPalette _INIT(true); // apply palet
WLED_GLOBAL bool receiveSegmentOptions _INIT(false); // apply segment options
WLED_GLOBAL bool receiveSegmentBounds _INIT(false); // apply segment bounds (start, stop, offset)
WLED_GLOBAL bool receiveDirect _INIT(true); // receive UDP/Hyperion realtime
WLED_GLOBAL bool notifyDirect _INIT(true); // send notification if change via UI or HTTP API
WLED_GLOBAL bool notifyButton _INIT(true); // send if updated by button or infrared remote
WLED_GLOBAL bool notifyDirect _INIT(false); // send notification if change via UI or HTTP API
WLED_GLOBAL bool notifyButton _INIT(false); // send if updated by button or infrared remote
WLED_GLOBAL bool notifyAlexa _INIT(false); // send notification if updated via Alexa
WLED_GLOBAL bool notifyHue _INIT(false); // send notification if Hue light changes
WLED_GLOBAL bool notifyHue _INIT(true); // send notification if Hue light changes
#endif
// effects
@@ -877,7 +884,7 @@ WLED_GLOBAL bool e131NewData _INIT(false);
// led fx library object
WLED_GLOBAL BusManager busses _INIT(BusManager());
WLED_GLOBAL WS2812FX strip _INIT(WS2812FX());
WLED_GLOBAL std::vector<BusConfig> busConfigs; //temporary, to remember values from network callback until after
WLED_GLOBAL BusConfig* busConfigs[WLED_MAX_BUSSES+WLED_MIN_VIRTUAL_BUSSES] _INIT({nullptr}); //temporary, to remember values from network callback until after
WLED_GLOBAL bool doInitBusses _INIT(false);
WLED_GLOBAL int8_t loadLedmap _INIT(-1);
WLED_GLOBAL uint8_t currentLedmap _INIT(0);
+10 -93
View File
@@ -10,25 +10,16 @@
//#define WLED_DEBUG_MATH
// Note: cos_t, sin_t and tan_t are very accurate but slow
// the math.h functions use several kB of flash and are to be avoided if possible
// sin16_t / cos16_t are faster and much more accurate than the fastled variants
// sin_approx and cos_approx are float wrappers for sin16_t/cos16_t and have an accuracy better than +/-0.0015 compared to sinf()
// sin8_t / cos8_t are fastled replacements and use sin16_t / cos16_t. Slightly slower than fastled version but very accurate
// Taylor series approximations, replaced with Bhaskara I's approximation
/*
#define modd(x, y) ((x) - (int)((x) / (y)) * (y))
float cos_t(float phi)
{
float x = modd(phi, M_TWOPI);
float x = modd(phi, TWO_PI);
if (x < 0) x = -1 * x;
int8_t sign = 1;
if (x > M_PI)
if (x > PI)
{
x -= M_PI;
x -= PI;
sign = -1;
}
float xx = x * x;
@@ -40,8 +31,8 @@ float cos_t(float phi)
return res;
}
float sin_t(float phi) {
float res = cos_t(M_PI_2 - phi);
float sin_t(float x) {
float res = cos_t(HALF_PI - x);
#ifdef WLED_DEBUG_MATH
Serial.printf("sin: %f,%f,%f,(%f)\n",x,res,sin(x),res-sin(x));
#endif
@@ -57,80 +48,6 @@ float tan_t(float x) {
#endif
return res;
}
*/
// 16-bit, integer based Bhaskara I's sine approximation: 16*x*(pi - x) / (5*pi^2 - 4*x*(pi - x))
// input is 16bit unsigned (0-65535), output is 16bit signed (-32767 to +32767)
// optimized integer implementation by @dedehai
int16_t sin16_t(uint16_t theta) {
int scale = 1;
if (theta > 0x7FFF) {
theta = 0xFFFF - theta;
scale = -1; // second half of the sine function is negative (pi - 2*pi)
}
uint32_t precal = theta * (0x7FFF - theta);
uint64_t numerator = (uint64_t)precal * (4 * 0x7FFF); // 64bit required
int32_t denominator = 1342095361 - precal; // 1342095361 is 5 * 0x7FFF^2 / 4
int16_t result = numerator / denominator;
return result * scale;
}
int16_t cos16_t(uint16_t theta) {
return sin16_t(theta + 0x4000); //cos(x) = sin(x+pi/2)
}
uint8_t sin8_t(uint8_t theta) {
int32_t sin16 = sin16_t((uint16_t)theta * 257); // 255 * 257 = 0xFFFF
sin16 += 0x7FFF + 128; //shift result to range 0-0xFFFF, +128 for rounding
return min(sin16, int32_t(0xFFFF)) >> 8; // min performs saturation, and prevents overflow
}
uint8_t cos8_t(uint8_t theta) {
return sin8_t(theta + 64); //cos(x) = sin(x+pi/2)
}
float sin_approx(float theta) {
uint16_t scaled_theta = (int)(theta * (float)(0xFFFF / M_TWOPI)); // note: do not cast negative float to uint! cast to int first (undefined on C3)
int32_t result = sin16_t(scaled_theta);
float sin = float(result) / 0x7FFF;
return sin;
}
float cos_approx(float theta) {
uint16_t scaled_theta = (int)(theta * (float)(0xFFFF / M_TWOPI)); // note: do not cast negative float to uint! cast to int first (undefined on C3)
int32_t result = sin16_t(scaled_theta + 0x4000);
float cos = float(result) / 0x7FFF;
return cos;
}
float tan_approx(float x) {
float c = cos_approx(x);
if (c==0.0f) return 0;
float res = sin_approx(x) / c;
return res;
}
#define ATAN2_CONST_A 0.1963f
#define ATAN2_CONST_B 0.9817f
// atan2_t approximation, with the idea from https://gist.github.com/volkansalma/2972237?permalink_comment_id=3872525#gistcomment-3872525
float atan2_t(float y, float x) {
float abs_y = fabs(y);
float abs_x = fabs(x);
float r = (abs_x - abs_y) / (abs_y + abs_x + 1e-10f); // avoid division by zero by adding a small nubmer
float angle;
if(x < 0) {
r = -r;
angle = M_PI_2 + M_PI_4;
}
else
angle = M_PI_2 - M_PI_4;
float add = (ATAN2_CONST_A * (r * r) - ATAN2_CONST_B) * r;
angle += add;
angle = y < 0 ? -angle : angle;
return angle;
}
//https://stackoverflow.com/questions/3380628
// Absolute error <= 6.7e-5
@@ -143,10 +60,10 @@ float acos_t(float x) {
ret = ret * xabs;
ret = ret - 0.2121144f;
ret = ret * xabs;
ret = ret + M_PI_2;
ret = ret + HALF_PI;
ret = ret * sqrt(1.0f-xabs);
ret = ret - 2 * negate * ret;
float res = negate * M_PI + ret;
float res = negate * PI + ret;
#ifdef WLED_DEBUG_MATH
Serial.printf("acos: %f,%f,%f,(%f)\n",x,res,acos(x),res-acos(x));
#endif
@@ -154,7 +71,7 @@ float acos_t(float x) {
}
float asin_t(float x) {
float res = M_PI_2 - acos_t(x);
float res = HALF_PI - acos_t(x);
#ifdef WLED_DEBUG_MATH
Serial.printf("asin: %f,%f,%f,(%f)\n",x,res,asin(x),res-asin(x));
#endif
@@ -170,7 +87,7 @@ float atan_t(float x) {
//For A/B/C, see https://stackoverflow.com/a/42542593
static const double A { 0.0776509570923569 };
static const double B { -0.287434475393028 };
static const double C { ((M_PI_4) - A - B) };
static const double C { ((HALF_PI/2) - A - B) };
// polynominal factors for approximation between 1 and 5
static const float C0 { 0.089494f };
static const float C1 { 0.974207f };
@@ -185,7 +102,7 @@ float atan_t(float x) {
x = std::abs(x);
float res;
if (x > 5.0f) { // atan(x) converges to pi/2 - (1/x) for large values
res = M_PI_2 - (1.0f/x);
res = HALF_PI - (1.0f/x);
} else if (x > 1.0f) { //1 < x < 5
float xx = x * x;
res = (C4*xx*xx)+(C3*xx*x)+(C2*xx)+(C1*x)+C0;
-195
View File
@@ -1,195 +0,0 @@
#include "ota_update.h"
#include "wled.h"
#include "wled_metadata.h"
#ifndef WLED_VERSION
#warning WLED_VERSION was not set - using default value of 'dev'
#define WLED_VERSION dev
#endif
#ifndef WLED_RELEASE_NAME
#warning WLED_RELEASE_NAME was not set - using default value of 'Custom'
#define WLED_RELEASE_NAME "Custom"
#endif
#ifndef WLED_REPO
// No warning for this one: integrators are not always on GitHub
#define WLED_REPO "unknown"
#endif
constexpr uint32_t WLED_CUSTOM_DESC_MAGIC = 0x57535453; // "WSTS" (WLED System Tag Structure)
constexpr uint32_t WLED_CUSTOM_DESC_VERSION = 2; // v1 - original PR; v2 - "safe to update from" version
// Compile-time validation that release name doesn't exceed maximum length
static_assert(sizeof(WLED_RELEASE_NAME) <= WLED_RELEASE_NAME_MAX_LEN,
"WLED_RELEASE_NAME exceeds maximum length of WLED_RELEASE_NAME_MAX_LEN characters");
/**
* DJB2 hash function (C++11 compatible constexpr)
* Used for compile-time hash computation to validate structure contents
* Recursive for compile time: not usable at runtime due to stack depth
*
* Note that this only works on strings; there is no way to produce a compile-time
* hash of a struct in C++11 without explicitly listing all the struct members.
* So for now, we hash only the release name. This suffices for a "did you find
* valid structure" check.
*
*/
constexpr uint32_t djb2_hash_constexpr(const char* str, uint32_t hash = 5381) {
return (*str == '\0') ? hash : djb2_hash_constexpr(str + 1, ((hash << 5) + hash) + *str);
}
/**
* Runtime DJB2 hash function for validation
*/
inline uint32_t djb2_hash_runtime(const char* str) {
uint32_t hash = 5381;
while (*str) {
hash = ((hash << 5) + hash) + *str++;
}
return hash;
}
// ------------------------------------
// GLOBAL VARIABLES
// ------------------------------------
// Structure instantiation for this build
const wled_metadata_t __attribute__((section(BUILD_METADATA_SECTION))) WLED_BUILD_DESCRIPTION = {
WLED_CUSTOM_DESC_MAGIC, // magic
WLED_CUSTOM_DESC_VERSION, // version
TOSTRING(WLED_VERSION),
WLED_RELEASE_NAME, // release_name
std::integral_constant<uint32_t, djb2_hash_constexpr(WLED_RELEASE_NAME)>::value, // hash - computed at compile time; integral_constant enforces this
{ 0, 0, 0 }, // All other platforms can update safely
};
static const char repoString_s[] PROGMEM = WLED_REPO;
const __FlashStringHelper* repoString = FPSTR(repoString_s);
static const char productString_s[] PROGMEM = WLED_PRODUCT_NAME;
const __FlashStringHelper* productString = FPSTR(productString_s);
static const char brandString_s [] PROGMEM = WLED_BRAND;
const __FlashStringHelper* brandString = FPSTR(brandString_s);
/**
* Extract WLED custom description structure from binary
* @param binaryData Pointer to binary file data
* @param dataSize Size of binary data in bytes
* @param extractedDesc Buffer to store extracted custom description structure
* @return true if structure was found and extracted, false otherwise
*/
bool findWledMetadata(const uint8_t* binaryData, size_t dataSize, wled_metadata_t* extractedDesc) {
if (!binaryData || !extractedDesc || dataSize < sizeof(wled_metadata_t)) {
return false;
}
for (size_t offset = 0; offset <= dataSize - sizeof(wled_metadata_t); offset++) {
if ((binaryData[offset]) == static_cast<char>(WLED_CUSTOM_DESC_MAGIC)) {
// First byte matched; check next in an alignment-safe way
uint32_t data_magic;
memcpy(&data_magic, binaryData + offset, sizeof(data_magic));
// Check for magic number
if (data_magic == WLED_CUSTOM_DESC_MAGIC) {
wled_metadata_t candidate;
memcpy(&candidate, binaryData + offset, sizeof(candidate));
// Found potential match, validate version
if (candidate.desc_version > WLED_CUSTOM_DESC_VERSION) {
DEBUG_PRINTF_P(PSTR("Found WLED structure at offset %u but version mismatch: %u\n"),
offset, candidate.desc_version);
continue;
}
// Validate hash using runtime function
uint32_t expected_hash = djb2_hash_runtime(candidate.release_name);
if (candidate.hash != expected_hash) {
DEBUG_PRINTF_P(PSTR("Found WLED structure at offset %u but hash mismatch\n"), offset);
continue;
}
// Valid structure found - copy entire structure
*extractedDesc = candidate;
DEBUG_PRINTF_P(PSTR("Extracted WLED structure at offset %u: '%s'\n"),
offset, extractedDesc->release_name);
return true;
}
}
}
DEBUG_PRINTLN(F("No WLED custom description found in binary"));
return false;
}
/**
* Check if OTA should be allowed based on release compatibility using custom description
* @param binaryData Pointer to binary file data (not modified)
* @param dataSize Size of binary data in bytes
* @param errorMessage Buffer to store error message if validation fails
* @param errorMessageLen Maximum length of error message buffer
* @return true if OTA should proceed, false if it should be blocked
*/
bool shouldAllowOTA(const wled_metadata_t& firmwareDescription, char* errorMessage, size_t errorMessageLen) {
// Clear error message
if (errorMessage && errorMessageLen > 0) {
errorMessage[0] = '\0';
}
// Validate compatibility using extracted release name
// We make a stack copy so we can print it safely
char safeFirmwareRelease[WLED_RELEASE_NAME_MAX_LEN];
strncpy(safeFirmwareRelease, firmwareDescription.release_name, WLED_RELEASE_NAME_MAX_LEN - 1);
safeFirmwareRelease[WLED_RELEASE_NAME_MAX_LEN - 1] = '\0';
if (strlen(safeFirmwareRelease) == 0) {
return false;
}
if (strncmp_P(safeFirmwareRelease, releaseString, WLED_RELEASE_NAME_MAX_LEN) != 0) {
if (errorMessage && errorMessageLen > 0) {
snprintf_P(errorMessage, errorMessageLen, PSTR("Firmware release name mismatch: current='%s', uploaded='%s'."),
releaseString, safeFirmwareRelease);
errorMessage[errorMessageLen - 1] = '\0'; // Ensure null termination
}
return false;
}
if (firmwareDescription.desc_version > 1) {
// Add safe version check
// Parse our version (x.y.z) and compare it to the "safe version" array
const char* our_version = versionString;
for(unsigned v_index = 0; v_index < 3; ++v_index) {
char* our_version_end = nullptr;
long our_v_parsed = strtol(our_version, &our_version_end, 10);
if (!our_version_end || (our_version_end == our_version)) {
// We were built with a malformed version string
// We blame the integrator and attempt the update anyways - nothing the user can do to fix this
break;
}
if (firmwareDescription.safe_update_version[v_index] > our_v_parsed) {
if (errorMessage && errorMessageLen > 0) {
snprintf_P(errorMessage, errorMessageLen, PSTR("Cannot update from this version: requires at least %d.%d.%d, current='%s'."),
firmwareDescription.safe_update_version[0], firmwareDescription.safe_update_version[1], firmwareDescription.safe_update_version[2],
versionString);
errorMessage[errorMessageLen - 1] = '\0'; // Ensure null termination
}
return false;
} else if (firmwareDescription.safe_update_version[v_index] < our_v_parsed) {
break; // no need to check the other components
}
if (*our_version_end == '.') ++our_version_end;
our_version = our_version_end;
}
}
// TODO: additional checks go here
return true;
}
-62
View File
@@ -1,62 +0,0 @@
/*
WLED build metadata
Manages and exports information about the current WLED build.
*/
#pragma once
#include <cstdint>
#include <string.h>
#include <WString.h>
#define WLED_VERSION_MAX_LEN 48
#define WLED_RELEASE_NAME_MAX_LEN 48
/**
* WLED Custom Description Structure
* This structure is embedded in platform-specific sections at an approximately
* fixed offset in ESP32/ESP8266 binaries, where it can be found and validated
* by the OTA process.
*/
typedef struct {
uint32_t magic; // Magic number to identify WLED custom description
uint32_t desc_version; // Structure version for future compatibility
char wled_version[WLED_VERSION_MAX_LEN];
char release_name[WLED_RELEASE_NAME_MAX_LEN]; // Release name (null-terminated)
uint32_t hash; // Structure sanity check
uint8_t safe_update_version[3]; // Indicates version it's known to be safe to install this update from: major, minor, patch
} __attribute__((packed)) wled_metadata_t;
// Global build description
extern const wled_metadata_t WLED_BUILD_DESCRIPTION;
// Convenient metdata pointers
#define versionString (WLED_BUILD_DESCRIPTION.wled_version) // Build version, WLED_VERSION
#define releaseString (WLED_BUILD_DESCRIPTION.release_name) // Release name, WLED_RELEASE_NAME
extern const __FlashStringHelper* repoString; // Github repository (if available)
extern const __FlashStringHelper* productString; // Product, WLED_PRODUCT_NAME -- deprecated, use WLED_RELEASE_NAME
extern const __FlashStringHelper* brandString ; // Brand
// Metadata analysis functions
/**
* Extract WLED custom description structure from binary data
* @param binaryData Pointer to binary file data
* @param dataSize Size of binary data in bytes
* @param extractedDesc Buffer to store extracted custom description structure
* @return true if structure was found and extracted, false otherwise
*/
bool findWledMetadata(const uint8_t* binaryData, size_t dataSize, wled_metadata_t* extractedDesc);
/**
* Check if OTA should be allowed based on release compatibility
* @param firmwareDescription Pointer to firmware description
* @param errorMessage Buffer to store error message if validation fails
* @param errorMessageLen Maximum length of error message buffer
* @return true if OTA should proceed, false if it should be blocked
*/
bool shouldAllowOTA(const wled_metadata_t& firmwareDescription, char* errorMessage, size_t errorMessageLen);
+50 -35
View File
@@ -1,8 +1,5 @@
#include "wled.h"
#ifndef WLED_DISABLE_OTA
#include "ota_update.h"
#endif
#include "html_ui.h"
#include "html_settings.h"
#include "html_other.h"
@@ -19,7 +16,6 @@ static const char s_redirecting[] PROGMEM = "Redirecting...";
static const char s_content_enc[] PROGMEM = "Content-Encoding";
static const char s_unlock_ota [] PROGMEM = "Please unlock OTA in security settings!";
static const char s_unlock_cfg [] PROGMEM = "Please unlock settings using PIN code!";
static const char s_rebooting [] PROGMEM = "Rebooting now...";
static const char s_notimplemented[] PROGMEM = "Not implemented";
static const char s_accessdenied[] PROGMEM = "Access Denied";
static const char _common_js[] PROGMEM = "/common.js";
@@ -379,40 +375,49 @@ void initServer()
});
server.on(_update, HTTP_POST, [](AsyncWebServerRequest *request){
if (request->_tempObject) {
auto ota_result = getOTAResult(request);
if (ota_result.first) {
if (ota_result.second.length() > 0) {
serveMessage(request, 500, F("Update failed!"), ota_result.second, 254);
} else {
serveMessage(request, 200, F("Update successful!"), FPSTR(s_rebooting), 131);
}
}
if (!correctPIN) {
serveSettings(request, true); // handle PIN page POST request
return;
}
if (otaLock) {
serveMessage(request, 401, FPSTR(s_accessdenied), FPSTR(s_unlock_ota), 254);
return;
}
if (Update.hasError()) {
serveMessage(request, 500, F("Update failed!"), F("Please check your file and retry!"), 254);
} else {
// No context structure - something's gone horribly wrong
serveMessage(request, 500, F("Update failed!"), F("Internal server fault"), 254);
serveMessage(request, 200, F("Update successful!"), F("Rebooting..."), 131);
doReboot = true;
}
},[](AsyncWebServerRequest *request, String filename, size_t index, uint8_t *data, size_t len, bool isFinal){
if (index == 0) {
// Allocate the context structure
if (!initOTA(request)) {
return; // Error will be dealt with after upload in response handler, above
},[](AsyncWebServerRequest *request, String filename, size_t index, uint8_t *data, size_t len, bool final){
if (!correctPIN || otaLock) return;
if(!index){
DEBUG_PRINTLN(F("OTA Update Start"));
#if WLED_WATCHDOG_TIMEOUT > 0
WLED::instance().disableWatchdog();
#endif
UsermodManager::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
strip.suspend();
#ifdef ESP8266
strip.resetSegments(); // free as much memory as you can
Update.runAsync(true);
#endif
Update.begin((ESP.getFreeSketchSpace() - 0x1000) & 0xFFFFF000);
}
if(!Update.hasError()) Update.write(data, len);
if(final){
if(Update.end(true)){
DEBUG_PRINTLN(F("Update Success"));
} else {
DEBUG_PRINTLN(F("Update Failed"));
strip.resume();
UsermodManager::onUpdateBegin(false); // notify usermods that update has failed (some may require task init)
#if WLED_WATCHDOG_TIMEOUT > 0
WLED::instance().enableWatchdog();
#endif
}
// Privilege checks
if (!correctPIN) {
serveMessage(request, 401, FPSTR(s_accessdenied), FPSTR(s_unlock_cfg), 254);
setOTAReplied(request);
return;
};
if (otaLock) {
serveMessage(request, 401, FPSTR(s_accessdenied), FPSTR(s_unlock_ota), 254);
setOTAReplied(request);
return;
}
}
handleOTAData(request, index, data, len, isFinal);
});
#else
server.on(_update, HTTP_GET, [](AsyncWebServerRequest *request){
@@ -477,7 +482,17 @@ void initServer()
return;
}
if(handleSet(request, request->url())) return;
if (request->url().indexOf("win") == 0) {
if (handleHttpApi(request->url().substring(3))) {
if (request->url().indexOf(F("&FX=")) > 0) {
unloadPlaylist(); // Setting an FX via HTTP disables any active playlist.
}
auto response = request->beginResponseStream(CONTENT_TYPE_XML);
XML_response(*response);
request->send(response);
return;
}
}
#ifndef WLED_DISABLE_ALEXA
if(espalexa.handleAlexaApiCall(request)) return;
#endif
+55 -57
View File
@@ -11,7 +11,7 @@ void XML_response(Print& dest)
dest.printf_P(PSTR("<?xml version=\"1.0\" ?><vs><ac>%d</ac>"), (nightlightActive && nightlightMode > NL_MODE_SET) ? briT : bri);
for (int i = 0; i < 3; i++)
{
dest.printf_P(PSTR("<cl>%d</cl>"), colPri[i]);
dest.printf_P(PSTR("<cl>%d</cl>"), col[i]);
}
for (int i = 0; i < 3; i++)
{
@@ -20,14 +20,13 @@ void XML_response(Print& dest)
dest.printf_P(PSTR("<ns>%d</ns><nr>%d</nr><nl>%d</nl><nf>%d</nf><nd>%d</nd><nt>%d</nt><fx>%d</fx><sx>%d</sx><ix>%d</ix><fp>%d</fp><wv>%d</wv><ws>%d</ws><ps>%d</ps><cy>%d</cy><ds>%s%s</ds><ss>%d</ss></vs>"),
notifyDirect, receiveGroups!=0, nightlightActive, nightlightMode > NL_MODE_SET, nightlightDelayMins,
nightlightTargetBri, effectCurrent, effectSpeed, effectIntensity, effectPalette,
strip.hasWhiteChannel() ? colPri[3] : -1, colSec[3], currentPreset, currentPlaylist >= 0,
strip.hasWhiteChannel() ? col[3] : -1, colSec[3], currentPreset, currentPlaylist >= 0,
serverDescription, realtimeMode ? PSTR(" (live)") : "",
strip.getFirstSelectedSegId()
);
}
static void extractPin(Print& settingsScript, JsonObject &obj, const char *key)
{
static void extractPin(Print& settingsScript, JsonObject &obj, const char *key) {
if (obj[key].is<JsonArray>()) {
JsonArray pins = obj[key].as<JsonArray>();
for (JsonVariant pv : pins) {
@@ -38,22 +37,6 @@ static void extractPin(Print& settingsScript, JsonObject &obj, const char *key)
}
}
void fillWLEDVersion(char *buf, size_t len)
{
if (!buf || len == 0) return;
snprintf_P(buf,len,PSTR("WLED %s (%d)<br>\\\"%s\\\"<br>(Processor: %s)"),
versionString,
VERSION,
releaseString,
#if defined(ARDUINO_ARCH_ESP32)
ESP.getChipModel()
#else
"ESP8266"
#endif
);
}
// print used pins by scanning JsonObject (1 level deep)
static void fillUMPins(Print& settingsScript, JsonObject &mods)
{
@@ -89,8 +72,7 @@ static void fillUMPins(Print& settingsScript, JsonObject &mods)
}
}
void appendGPIOinfo(Print& settingsScript)
{
void appendGPIOinfo(Print& settingsScript) {
settingsScript.print(F("d.um_p=[-1")); // has to have 1 element
if (i2c_sda > -1 && i2c_scl > -1) {
settingsScript.printf_P(PSTR(",%d,%d"), i2c_sda, i2c_scl);
@@ -101,7 +83,7 @@ void appendGPIOinfo(Print& settingsScript)
// usermod pin reservations will become unnecessary when settings pages will read cfg.json directly
if (requestJSONBufferLock(6)) {
// if we can't allocate JSON buffer ignore usermod pins
JsonObject mods = pDoc->createNestedObject("um");
JsonObject mods = pDoc->createNestedObject(F("um"));
UsermodManager::addToConfig(mods);
if (!mods.isNull()) fillUMPins(settingsScript, mods);
releaseJSONBufferLock();
@@ -109,42 +91,35 @@ void appendGPIOinfo(Print& settingsScript)
settingsScript.print(F("];"));
// add reserved (unusable) pins
bool firstPin = true;
settingsScript.print(F("d.rsvd=["));
for (unsigned i = 0; i < WLED_NUM_PINS; i++) {
if (!PinManager::isPinOk(i, false)) { // include readonly pins
if (!firstPin) settingsScript.print(',');
settingsScript.print(i);
firstPin = false;
settingsScript.print(i); settingsScript.print(",");
}
}
#ifdef WLED_ENABLE_DMX
if (!firstPin) settingsScript.print(',');
settingsScript.print(2); // DMX hardcoded pin
firstPin = false;
settingsScript.print(F("2,")); // DMX hardcoded pin
#endif
#if defined(WLED_DEBUG) && !defined(WLED_DEBUG_HOST)
if (!firstPin) settingsScript.print(',');
settingsScript.print(hardwareTX); // debug output (TX) pin
firstPin = false;
settingsScript.printf_P(PSTR(",%d"),hardwareTX); // debug output (TX) pin
#endif
//Note: Using pin 3 (RX) disables Adalight / Serial JSON
#ifdef WLED_USE_ETHERNET
if (ethernetType != WLED_ETH_NONE && ethernetType < WLED_NUM_ETH_TYPES) {
if (!firstPin) settingsScript.print(',');
for (unsigned p=0; p<WLED_ETH_RSVD_PINS_COUNT; p++) { settingsScript.printf("%d,",esp32_nonconfigurable_ethernet_pins[p].pin); }
if (ethernetBoards[ethernetType].eth_power >= 0) { settingsScript.printf("%d,",ethernetBoards[ethernetType].eth_power); }
if (ethernetBoards[ethernetType].eth_mdc >= 0) { settingsScript.printf("%d,",ethernetBoards[ethernetType].eth_mdc); }
if (ethernetBoards[ethernetType].eth_mdio >= 0) { settingsScript.printf("%d,",ethernetBoards[ethernetType].eth_mdio); }
switch (ethernetBoards[ethernetType].eth_clk_mode) {
for (unsigned p=0; p<WLED_ETH_RSVD_PINS_COUNT; p++) { settingsScript.printf(",%d", esp32_nonconfigurable_ethernet_pins[p].pin); }
if (ethernetBoards[ethernetType].eth_power>=0) { settingsScript.printf(",%d", ethernetBoards[ethernetType].eth_power); }
if (ethernetBoards[ethernetType].eth_mdc>=0) { settingsScript.printf(",%d", ethernetBoards[ethernetType].eth_mdc); }
if (ethernetBoards[ethernetType].eth_mdio>=0) { settingsScript.printf(",%d", ethernetBoards[ethernetType].eth_mdio); }
switch (ethernetBoards[ethernetType].eth_clk_mode) {
case ETH_CLOCK_GPIO0_IN:
case ETH_CLOCK_GPIO0_OUT:
settingsScript.print(0);
settingsScript.print(F("0"));
break;
case ETH_CLOCK_GPIO16_OUT:
settingsScript.print(16);
settingsScript.print(F("16"));
break;
case ETH_CLOCK_GPIO17_OUT:
settingsScript.print(17);
settingsScript.print(F("17"));
break;
}
}
@@ -153,11 +128,11 @@ void appendGPIOinfo(Print& settingsScript)
// add info for read-only GPIO
settingsScript.print(F("d.ro_gpio=["));
firstPin = true;
bool firstPin = true;
for (unsigned i = 0; i < WLED_NUM_PINS; i++) {
if (PinManager::isReadOnlyPin(i)) {
// No comma before the first pin
if (!firstPin) settingsScript.print(',');
if (!firstPin) settingsScript.print(F(","));
settingsScript.print(i);
firstPin = false;
}
@@ -165,7 +140,9 @@ void appendGPIOinfo(Print& settingsScript)
settingsScript.print(F("];"));
// add info about max. # of pins
settingsScript.printf_P(PSTR("d.max_gpio=%d;"),WLED_NUM_PINS);
settingsScript.print(F("d.max_gpio="));
settingsScript.print(WLED_NUM_PINS);
settingsScript.print(F(";"));
}
//get values for settings form in javascript
@@ -175,7 +152,6 @@ void getSettingsJS(byte subPage, Print& settingsScript)
DEBUG_PRINTF_P(PSTR("settings resp %u\n"), (unsigned)subPage);
if (subPage <0 || subPage >10) return;
char nS[32];
if (subPage == SUBPAGE_MENU)
{
@@ -283,9 +259,11 @@ void getSettingsJS(byte subPage, Print& settingsScript)
if (subPage == SUBPAGE_LEDS)
{
char nS[32];
appendGPIOinfo(settingsScript);
settingsScript.printf_P(PSTR("d.ledTypes=%s;"), BusManager::getLEDTypesJSONString().c_str());
settingsScript.print(F("d.ledTypes=")); settingsScript.print(BusManager::getLEDTypesJSONString().c_str()); settingsScript.print(";");
// set limits
settingsScript.printf_P(PSTR("bLimits(%d,%d,%d,%d,%d,%d,%d,%d);"),
@@ -307,13 +285,12 @@ void getSettingsJS(byte subPage, Print& settingsScript)
printSetFormValue(settingsScript,PSTR("FR"),strip.getTargetFps());
printSetFormValue(settingsScript,PSTR("AW"),Bus::getGlobalAWMode());
printSetFormCheckbox(settingsScript,PSTR("LD"),useGlobalLedBuffer);
printSetFormCheckbox(settingsScript,PSTR("PR"),BusManager::hasParallelOutput()); // get it from bus manager not global variable
unsigned sumMa = 0;
for (int s = 0; s < BusManager::getNumBusses(); s++) {
const Bus* bus = BusManager::getBus(s);
if (!bus || !bus->isOk()) break; // should not happen but for safety
int offset = s < 10 ? '0' : 'A' - 10;
Bus* bus = BusManager::getBus(s);
if (bus == nullptr) continue;
int offset = s < 10 ? 48 : 55;
char lp[4] = "L0"; lp[2] = offset+s; lp[3] = 0; //ascii 0-9 //strip data pin
char lc[4] = "LC"; lc[2] = offset+s; lc[3] = 0; //strip length
char co[4] = "CO"; co[2] = offset+s; co[3] = 0; //strip color order
@@ -331,7 +308,7 @@ void getSettingsJS(byte subPage, Print& settingsScript)
uint8_t pins[5];
int nPins = bus->getPins(pins);
for (int i = 0; i < nPins; i++) {
lp[1] = '0'+i;
lp[1] = offset+i;
if (PinManager::isPinOk(pins[i]) || bus->isVirtual()) printSetFormValue(settingsScript,lp,pins[i]);
}
printSetFormValue(settingsScript,lc,bus->getLength());
@@ -422,6 +399,7 @@ void getSettingsJS(byte subPage, Print& settingsScript)
if (subPage == SUBPAGE_SYNC)
{
[[maybe_unused]] char nS[32];
printSetFormValue(settingsScript,PSTR("UP"),udpPort);
printSetFormValue(settingsScript,PSTR("U2"),udpPort2);
#ifndef WLED_DISABLE_ESPNOW
@@ -487,7 +465,7 @@ void getSettingsJS(byte subPage, Print& settingsScript)
printSetFormValue(settingsScript,PSTR("MG"),mqttGroupTopic);
printSetFormCheckbox(settingsScript,PSTR("BM"),buttonPublishMqtt);
printSetFormCheckbox(settingsScript,PSTR("RT"),retainMqttMsg);
settingsScript.printf_P(PSTR("d.Sf.MD.maxLength=%d;d.Sf.MG.maxLength=%d;d.Sf.MS.maxLength=%d;"),
settingsScript.printf_P(PSTR("d.Sf.MD.maxlength=%d;d.Sf.MG.maxlength=%d;d.Sf.MS.maxlength=%d;"),
MQTT_MAX_TOPIC_LEN, MQTT_MAX_TOPIC_LEN, MQTT_MAX_SERVER_LEN);
#else
settingsScript.print(F("toggle('MQTT');")); // hide MQTT settings
@@ -598,7 +576,7 @@ void getSettingsJS(byte subPage, Print& settingsScript)
printSetFormCheckbox(settingsScript,PSTR("OW"),wifiLock);
printSetFormCheckbox(settingsScript,PSTR("AO"),aOtaEnabled);
char tmp_buf[128];
fillWLEDVersion(tmp_buf,sizeof(tmp_buf));
snprintf_P(tmp_buf,sizeof(tmp_buf),PSTR("WLED %s (build %d)"),versionString,VERSION);
printSetClassElementHTML(settingsScript,PSTR("sip"),0,tmp_buf);
settingsScript.printf_P(PSTR("sd=\"%s\";"), serverDescription);
}
@@ -650,11 +628,28 @@ void getSettingsJS(byte subPage, Print& settingsScript)
UsermodManager::appendConfigData(settingsScript);
}
if (subPage == SUBPAGE_UPDATE) // update
{
char tmp_buf[128];
snprintf_P(tmp_buf,sizeof(tmp_buf),PSTR("WLED %s<br>%s<br>(%s build %d)"),
versionString,
releaseString,
#if defined(ARDUINO_ARCH_ESP32)
ESP.getChipModel(),
#else
F("esp8266"),
#endif
VERSION);
printSetClassElementHTML(settingsScript,PSTR("sip"),0,tmp_buf);
}
if (subPage == SUBPAGE_2D) // 2D matrices
{
printSetFormValue(settingsScript,PSTR("SOMP"),strip.isMatrix);
#ifndef WLED_DISABLE_2D
settingsScript.printf_P(PSTR("maxPanels=%d;resetPanels();"),WLED_MAX_PANELS);
settingsScript.printf_P(PSTR("maxPanels=%d;"),WLED_MAX_PANELS);
settingsScript.print(F("resetPanels();"));
if (strip.isMatrix) {
if(strip.panels>0){
printSetFormValue(settingsScript,PSTR("PW"),strip.panel[0].width); //Set generator Width and Height to first panel size for convenience
@@ -663,9 +658,12 @@ void getSettingsJS(byte subPage, Print& settingsScript)
printSetFormValue(settingsScript,PSTR("MPC"),strip.panels);
// panels
for (unsigned i=0; i<strip.panels; i++) {
settingsScript.printf_P(PSTR("addPanel(%d);"), i);
char n[5];
settingsScript.print(F("addPanel("));
settingsScript.print(itoa(i,n,10));
settingsScript.print(F(");"));
char pO[8] = { '\0' };
snprintf_P(pO, 7, PSTR("P%d"), i); // WLED_MAX_PANELS is 18 so pO will always only be 4 characters or less
snprintf_P(pO, 7, PSTR("P%d"), i); // MAX_PANELS is 64 so pO will always only be 4 characters or less
pO[7] = '\0';
unsigned l = strlen(pO);
// create P0B, P1B, ..., P63B, etc for other PxxX