mirror of
https://github.com/wled/WLED.git
synced 2025-07-19 08:46:34 +00:00
Merge remote-tracking branch 'upstream/main' into usermod-libs
This commit is contained in:
commit
e00789f838
16
.github/workflows/nightly.yml
vendored
16
.github/workflows/nightly.yml
vendored
@ -19,6 +19,14 @@ jobs:
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
merge-multiple: true
|
||||
- name: Show Files
|
||||
run: ls -la
|
||||
- name: "✏️ Generate release changelog"
|
||||
id: changelog
|
||||
uses: janheinrichmerker/action-github-changelog-generator@v2.3
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
sinceTag: v0.15.0
|
||||
- name: Update Nightly Release
|
||||
uses: andelf/nightly-release@main
|
||||
env:
|
||||
@ -27,7 +35,13 @@ jobs:
|
||||
tag_name: nightly
|
||||
name: 'Nightly Release $$'
|
||||
prerelease: true
|
||||
body: 'nightly'
|
||||
body: ${{ steps.changelog.outputs.changelog }}
|
||||
files: |
|
||||
*.bin
|
||||
*.bin.gz
|
||||
- name: Repository Dispatch
|
||||
uses: peter-evans/repository-dispatch@v3
|
||||
with:
|
||||
repository: wled/WLED-WebInstaller
|
||||
event-type: release-nightly
|
||||
token: ${{ secrets.PAT_PUBLIC }}
|
||||
|
13
.github/workflows/pr-merge.yaml
vendored
Normal file
13
.github/workflows/pr-merge.yaml
vendored
Normal file
@ -0,0 +1,13 @@
|
||||
name: Notify Discord on PR Merge
|
||||
on:
|
||||
pull_request:
|
||||
types: [closed]
|
||||
|
||||
jobs:
|
||||
notify:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Send Discord notification
|
||||
if: github.event.pull_request.merged == true
|
||||
run: |
|
||||
curl -H "Content-Type: application/json" -d '{"content": "Pull Request #{{ github.event.pull_request.number }} merged by {{ github.actor }}"}' ${{ secrets.DISCORD_WEBHOOK_BETA_TESTERS }}
|
7
.github/workflows/release.yml
vendored
7
.github/workflows/release.yml
vendored
@ -18,9 +18,16 @@ jobs:
|
||||
- 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
|
||||
- name: Create draft release
|
||||
uses: softprops/action-gh-release@v1
|
||||
with:
|
||||
body: ${{ steps.changelog.outputs.changelog }}
|
||||
draft: True
|
||||
files: |
|
||||
*.bin
|
||||
|
13
.github/workflows/test.yaml
vendored
Normal file
13
.github/workflows/test.yaml
vendored
Normal file
@ -0,0 +1,13 @@
|
||||
on:
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
dispatch:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Repository Dispatch
|
||||
uses: peter-evans/repository-dispatch@v3
|
||||
with:
|
||||
repository: wled/WLED-WebInstaller
|
||||
event-type: release-nightly
|
||||
token: ${{ secrets.PAT_PUBLIC }}
|
@ -10,7 +10,7 @@ if node_ex is None:
|
||||
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")
|
||||
env.Execute("npm ci")
|
||||
|
||||
# Call the bundling script
|
||||
exitCode = env.Execute("npm run build")
|
||||
|
@ -360,6 +360,7 @@ 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
|
||||
-D WLED_DISABLE_PARTICLESYSTEM2D
|
||||
lib_deps = ${esp8266.lib_deps}
|
||||
monitor_filters = esp8266_exception_decoder
|
||||
|
||||
@ -369,12 +370,14 @@ extends = env:nodemcuv2
|
||||
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
|
||||
-D WLED_DISABLE_PARTICLESYSTEM2D
|
||||
;; 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
|
||||
-D WLED_DISABLE_PARTICLESYSTEM2D
|
||||
custom_usermods = audioreactive
|
||||
|
||||
[env:esp8266_2m]
|
||||
@ -384,6 +387,8 @@ 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\"
|
||||
-D WLED_DISABLE_PARTICLESYSTEM2D
|
||||
-D WLED_DISABLE_PARTICLESYSTEM1D
|
||||
lib_deps = ${esp8266.lib_deps}
|
||||
|
||||
[env:esp8266_2m_compat]
|
||||
@ -392,11 +397,15 @@ extends = env:esp8266_2m
|
||||
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
|
||||
-D WLED_DISABLE_PARTICLESYSTEM1D
|
||||
-D WLED_DISABLE_PARTICLESYSTEM2D
|
||||
|
||||
[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\"
|
||||
-D WLED_DISABLE_PARTICLESYSTEM1D
|
||||
-D WLED_DISABLE_PARTICLESYSTEM2D
|
||||
custom_usermods = audioreactive
|
||||
|
||||
[env:esp01_1m_full]
|
||||
@ -407,6 +416,8 @@ 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
|
||||
; -D WLED_USE_REAL_MATH ;; may fix wrong sunset/sunrise times, at the cost of 7064 bytes FLASH and 975 bytes RAM
|
||||
-D WLED_DISABLE_PARTICLESYSTEM1D
|
||||
-D WLED_DISABLE_PARTICLESYSTEM2D
|
||||
lib_deps = ${esp8266.lib_deps}
|
||||
|
||||
[env:esp01_1m_full_compat]
|
||||
@ -415,12 +426,16 @@ extends = env:esp01_1m_full
|
||||
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
|
||||
-D WLED_DISABLE_PARTICLESYSTEM1D
|
||||
-D WLED_DISABLE_PARTICLESYSTEM2D
|
||||
|
||||
[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
|
||||
; -D WLED_USE_REAL_MATH ;; may fix wrong sunset/sunrise times, at the cost of 7064 bytes FLASH and 975 bytes RAM
|
||||
-D WLED_DISABLE_PARTICLESYSTEM1D
|
||||
-D WLED_DISABLE_PARTICLESYSTEM2D
|
||||
custom_usermods = audioreactive
|
||||
|
||||
[env:esp32dev]
|
||||
|
2762
wled00/FX.cpp
2762
wled00/FX.cpp
File diff suppressed because it is too large
Load Diff
34
wled00/FX.h
34
wled00/FX.h
@ -323,8 +323,35 @@ extern byte realtimeMode; // used in getMappedPixelIndex()
|
||||
#define FX_MODE_WAVESINS 184
|
||||
#define FX_MODE_ROCKTAVES 185
|
||||
#define FX_MODE_2DAKEMI 186
|
||||
|
||||
#define MODE_COUNT 187
|
||||
#define FX_MODE_PARTICLEVOLCANO 187
|
||||
#define FX_MODE_PARTICLEFIRE 188
|
||||
#define FX_MODE_PARTICLEFIREWORKS 189
|
||||
#define FX_MODE_PARTICLEVORTEX 190
|
||||
#define FX_MODE_PARTICLEPERLIN 191
|
||||
#define FX_MODE_PARTICLEPIT 192
|
||||
#define FX_MODE_PARTICLEBOX 193
|
||||
#define FX_MODE_PARTICLEATTRACTOR 194
|
||||
#define FX_MODE_PARTICLEIMPACT 195
|
||||
#define FX_MODE_PARTICLEWATERFALL 196
|
||||
#define FX_MODE_PARTICLESPRAY 197
|
||||
#define FX_MODE_PARTICLESGEQ 198
|
||||
#define FX_MODE_PARTICLECENTERGEQ 199
|
||||
#define FX_MODE_PARTICLEGHOSTRIDER 200
|
||||
#define FX_MODE_PARTICLEBLOBS 201
|
||||
#define FX_MODE_PSDRIP 202
|
||||
#define FX_MODE_PSPINBALL 203
|
||||
#define FX_MODE_PSDANCINGSHADOWS 204
|
||||
#define FX_MODE_PSFIREWORKS1D 205
|
||||
#define FX_MODE_PSSPARKLER 206
|
||||
#define FX_MODE_PSHOURGLASS 207
|
||||
#define FX_MODE_PS1DSPRAY 208
|
||||
#define FX_MODE_PSBALANCE 209
|
||||
#define FX_MODE_PSCHASE 210
|
||||
#define FX_MODE_PSSTARBURST 211
|
||||
#define FX_MODE_PS1DGEQ 212
|
||||
#define FX_MODE_PSFIRE1D 213
|
||||
#define FX_MODE_PS1DSONICSTREAM 214
|
||||
#define MODE_COUNT 215
|
||||
|
||||
|
||||
#define BLEND_STYLE_FADE 0x00 // universal
|
||||
@ -481,6 +508,7 @@ typedef struct Segment {
|
||||
uint8_t _prevPaletteBlends; // number of previous palette blends (there are max 255 blends possible)
|
||||
unsigned long _start; // must accommodate millis()
|
||||
uint16_t _dur;
|
||||
// -> here is one byte of padding
|
||||
Transition(uint16_t dur=750)
|
||||
: _palT(CRGBPalette16(CRGB::Black))
|
||||
, _prevPaletteBlends(0)
|
||||
@ -579,6 +607,7 @@ typedef struct Segment {
|
||||
inline static void addUsedSegmentData(int len) { Segment::_usedSegmentData += len; }
|
||||
#ifndef WLED_DISABLE_MODE_BLEND
|
||||
inline static void modeBlend(bool blend) { _modeBlend = blend; }
|
||||
inline static bool getmodeBlend(void) { return _modeBlend; }
|
||||
#endif
|
||||
inline static unsigned vLength() { return Segment::_vLength; }
|
||||
inline static unsigned vWidth() { return Segment::_vWidth; }
|
||||
@ -627,6 +656,7 @@ typedef struct Segment {
|
||||
uint8_t currentMode() const; // currently active effect/mode (while in transition)
|
||||
[[gnu::hot]] uint32_t currentColor(uint8_t slot) const; // currently active segment color (blended while in transition)
|
||||
CRGBPalette16 &loadPalette(CRGBPalette16 &tgt, uint8_t pal);
|
||||
void loadOldPalette(); // loads old FX palette into _currentPalette
|
||||
|
||||
// 1D strip
|
||||
[[gnu::hot]] uint16_t virtualLength() const;
|
||||
|
@ -11,6 +11,7 @@
|
||||
*/
|
||||
#include "wled.h"
|
||||
#include "FX.h"
|
||||
#include "FXparticleSystem.h" // TODO: better define the required function (mem service) in FX.h?
|
||||
#include "palettes.h"
|
||||
|
||||
/*
|
||||
@ -470,6 +471,12 @@ void Segment::beginDraw() {
|
||||
}
|
||||
}
|
||||
|
||||
// loads palette of the old FX during transitions (used by particle system)
|
||||
void Segment::loadOldPalette(void) {
|
||||
if(isInTransition())
|
||||
loadPalette(_currentPalette, _t->_palTid);
|
||||
}
|
||||
|
||||
// relies on WS2812FX::service() to call it for each frame
|
||||
void Segment::handleRandomPalette() {
|
||||
// is it time to generate a new palette?
|
||||
@ -1592,6 +1599,9 @@ void WS2812FX::service() {
|
||||
_segment_index++;
|
||||
}
|
||||
Segment::setClippingRect(0, 0); // disable clipping for overlays
|
||||
#if !(defined(WLED_DISABLE_PARTICLESYSTEM2D) && defined(WLED_DISABLE_PARTICLESYSTEM1D))
|
||||
servicePSmem(); // handle segment particle system memory
|
||||
#endif
|
||||
_isServicing = false;
|
||||
_triggered = false;
|
||||
|
||||
@ -1991,12 +2001,17 @@ bool WS2812FX::deserializeMap(unsigned n) {
|
||||
|
||||
if (!isFile || !requestJSONBufferLock(7)) return false;
|
||||
|
||||
if (!readObjectFromFile(fileName, nullptr, pDoc)) {
|
||||
StaticJsonDocument<64> filter;
|
||||
filter[F("width")] = true;
|
||||
filter[F("height")] = true;
|
||||
if (!readObjectFromFile(fileName, nullptr, pDoc, &filter)) {
|
||||
DEBUG_PRINT(F("ERROR Invalid ledmap in ")); DEBUG_PRINTLN(fileName);
|
||||
releaseJSONBufferLock();
|
||||
return false; // if file does not load properly then exit
|
||||
}
|
||||
|
||||
suspend();
|
||||
|
||||
JsonObject root = pDoc->as<JsonObject>();
|
||||
// if we are loading default ledmap (at boot) set matrix width and height from the ledmap (compatible with WLED MM ledmaps)
|
||||
if (isMatrix && n == 0 && (!root[F("width")].isNull() || !root[F("height")].isNull())) {
|
||||
@ -2009,16 +2024,52 @@ bool WS2812FX::deserializeMap(unsigned n) {
|
||||
|
||||
if (customMappingTable) {
|
||||
DEBUG_PRINT(F("Reading LED map from ")); DEBUG_PRINTLN(fileName);
|
||||
File f = WLED_FS.open(fileName, "r");
|
||||
f.find("\"map\":[");
|
||||
while (f.available()) { // f.position() < f.size() - 1
|
||||
char number[32];
|
||||
size_t numRead = f.readBytesUntil(',', number, sizeof(number)-1); // read a single number (may include array terminating "]" but not number separator ',')
|
||||
number[numRead] = 0;
|
||||
if (numRead > 0) {
|
||||
char *end = strchr(number,']'); // we encountered end of array so stop processing if no digit found
|
||||
bool foundDigit = (end == nullptr);
|
||||
int i = 0;
|
||||
if (end != nullptr) do {
|
||||
if (number[i] >= '0' && number[i] <= '9') foundDigit = true;
|
||||
if (foundDigit || &number[i++] == end) break;
|
||||
} while (i < 32);
|
||||
if (!foundDigit) break;
|
||||
int index = atoi(number);
|
||||
if (index < 0 || index > 16384) index = 0xFFFF;
|
||||
customMappingTable[customMappingSize++] = index;
|
||||
if (customMappingSize > getLengthTotal()) break;
|
||||
} else break; // there was nothing to read, stop
|
||||
}
|
||||
currentLedmap = n;
|
||||
f.close();
|
||||
|
||||
#ifdef WLED_DEBUG
|
||||
DEBUG_PRINT(F("Loaded ledmap:"));
|
||||
for (unsigned i=0; i<customMappingSize; i++) {
|
||||
if (!(i%Segment::maxWidth)) DEBUG_PRINTLN();
|
||||
DEBUG_PRINTF_P(PSTR("%4d,"), customMappingTable[i]);
|
||||
}
|
||||
DEBUG_PRINTLN();
|
||||
#endif
|
||||
/*
|
||||
JsonArray map = root[F("map")];
|
||||
if (!map.isNull() && map.size()) { // not an empty map
|
||||
customMappingSize = min((unsigned)map.size(), (unsigned)getLengthTotal());
|
||||
for (unsigned i=0; i<customMappingSize; i++) customMappingTable[i] = (uint16_t) (map[i]<0 ? 0xFFFFU : map[i]);
|
||||
currentLedmap = n;
|
||||
}
|
||||
*/
|
||||
} else {
|
||||
DEBUG_PRINTLN(F("ERROR LED map allocation error."));
|
||||
}
|
||||
|
||||
resume();
|
||||
|
||||
releaseJSONBufferLock();
|
||||
return (customMappingSize > 0);
|
||||
}
|
||||
|
2433
wled00/FXparticleSystem.cpp
Normal file
2433
wled00/FXparticleSystem.cpp
Normal file
File diff suppressed because it is too large
Load Diff
418
wled00/FXparticleSystem.h
Normal file
418
wled00/FXparticleSystem.h
Normal file
@ -0,0 +1,418 @@
|
||||
/*
|
||||
FXparticleSystem.cpp
|
||||
|
||||
Particle system with functions for particle generation, particle movement and particle rendering to RGB matrix.
|
||||
by DedeHai (Damian Schneider) 2013-2024
|
||||
|
||||
Copyright (c) 2024 Damian Schneider
|
||||
Licensed under the EUPL v. 1.2 or later
|
||||
*/
|
||||
|
||||
#ifdef WLED_DISABLE_2D
|
||||
#define WLED_DISABLE_PARTICLESYSTEM2D
|
||||
#endif
|
||||
|
||||
#if !(defined(WLED_DISABLE_PARTICLESYSTEM2D) && defined(WLED_DISABLE_PARTICLESYSTEM1D)) // not both disabled
|
||||
|
||||
#include <stdint.h>
|
||||
#include "wled.h"
|
||||
|
||||
#define PS_P_MAXSPEED 120 // maximum speed a particle can have (vx/vy is int8)
|
||||
#define MAX_MEMIDLE 10 // max idle time (in frames) before memory is deallocated (if deallocated during an effect, it will crash!)
|
||||
|
||||
//#define WLED_DEBUG_PS // note: enabling debug uses ~3k of flash
|
||||
|
||||
#ifdef WLED_DEBUG_PS
|
||||
#define PSPRINT(x) Serial.print(x)
|
||||
#define PSPRINTLN(x) Serial.println(x)
|
||||
#else
|
||||
#define PSPRINT(x)
|
||||
#define PSPRINTLN(x)
|
||||
#endif
|
||||
|
||||
// memory and transition manager
|
||||
struct partMem {
|
||||
void* particleMemPointer; // pointer to particle memory
|
||||
uint32_t buffersize; // buffer size in bytes
|
||||
uint8_t particleType; // type of particles currently in memory: 0 = none, particle struct size otherwise (required for 1D<->2D transitions)
|
||||
uint8_t id; // ID of segment this memory belongs to
|
||||
uint8_t watchdog; // counter to handle deallocation
|
||||
uint8_t inTransition; // to track PS to PS FX transitions (is set to new FX ID during transitions), not set if not both FX are PS FX
|
||||
uint8_t currentFX; // current FX ID, is set when transition is complete, used to detect back and forth transitions
|
||||
bool finalTransfer; // used to update buffer in rendering function after transition has ended
|
||||
bool transferParticles; // if set, particles in buffer are transferred to new FX
|
||||
};
|
||||
|
||||
void* particleMemoryManager(const uint32_t requestedParticles, size_t structSize, uint32_t &availableToPS, uint32_t numParticlesUsed, const uint8_t effectID); // update particle memory pointer, handles memory transitions
|
||||
void particleHandover(void *buffer, size_t structSize, int32_t numParticles);
|
||||
void updateUsedParticles(const uint32_t allocated, const uint32_t available, const uint8_t percentage, uint32_t &used);
|
||||
bool segmentIsOverlay(void); // check if segment is fully overlapping with at least one underlying segment
|
||||
partMem* getPartMem(void); // returns pointer to memory struct for current segment or nullptr
|
||||
void updateRenderingBuffer(uint32_t requiredpixels, bool isFramebuffer, bool initialize); // allocate CRGB rendering buffer, update size if needed
|
||||
void transferBuffer(uint32_t width, uint32_t height, bool useAdditiveTransfer = false); // transfer the buffer to the segment (supports 1D and 2D)
|
||||
void servicePSmem(); // increments watchdog, frees memory if idle too long
|
||||
|
||||
// limit speed of particles (used in 1D and 2D)
|
||||
static inline int32_t limitSpeed(const int32_t speed) {
|
||||
return speed > PS_P_MAXSPEED ? PS_P_MAXSPEED : (speed < -PS_P_MAXSPEED ? -PS_P_MAXSPEED : speed); // note: this is slightly faster than using min/max at the cost of 50bytes of flash
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef WLED_DISABLE_PARTICLESYSTEM2D
|
||||
// memory allocation
|
||||
#define ESP8266_MAXPARTICLES 300 // enough up to 20x20 pixels
|
||||
#define ESP8266_MAXSOURCES 24
|
||||
#define ESP32S2_MAXPARTICLES 1024 // enough up to 32x32 pixels
|
||||
#define ESP32S2_MAXSOURCES 64
|
||||
#define ESP32_MAXPARTICLES 2048 // enough up to 64x32 pixels
|
||||
#define ESP32_MAXSOURCES 128
|
||||
|
||||
// particle dimensions (subpixel division)
|
||||
#define PS_P_RADIUS 64 // subpixel size, each pixel is divided by this for particle movement (must be a power of 2)
|
||||
#define PS_P_HALFRADIUS (PS_P_RADIUS >> 1)
|
||||
#define PS_P_RADIUS_SHIFT 6 // shift for RADIUS
|
||||
#define PS_P_SURFACE 12 // shift: 2^PS_P_SURFACE = (PS_P_RADIUS)^2
|
||||
#define PS_P_MINHARDRADIUS 64 // minimum hard surface radius for collisions
|
||||
#define PS_P_MINSURFACEHARDNESS 128 // minimum hardness used in collision impulse calculation, below this hardness, particles become sticky
|
||||
|
||||
// struct for PS settings (shared for 1D and 2D class)
|
||||
typedef union {
|
||||
struct{ // one byte bit field for 2D settings
|
||||
bool wrapX : 1;
|
||||
bool wrapY : 1;
|
||||
bool bounceX : 1;
|
||||
bool bounceY : 1;
|
||||
bool killoutofbounds : 1; // if set, out of bound particles are killed immediately
|
||||
bool useGravity : 1; // set to 1 if gravity is used, disables bounceY at the top
|
||||
bool useCollisions : 1;
|
||||
bool colorByAge : 1; // if set, particle hue is set by ttl value in render function
|
||||
};
|
||||
byte asByte; // access as a byte, order is: LSB is first entry in the list above
|
||||
} PSsettings2D;
|
||||
|
||||
//struct for a single particle
|
||||
typedef struct { // 10 bytes
|
||||
int16_t x; // x position in particle system
|
||||
int16_t y; // y position in particle system
|
||||
uint16_t ttl; // time to live in frames
|
||||
int8_t vx; // horizontal velocity
|
||||
int8_t vy; // vertical velocity
|
||||
uint8_t hue; // color hue
|
||||
uint8_t sat; // particle color saturation
|
||||
} PSparticle;
|
||||
|
||||
//struct for particle flags note: this is separate from the particle struct to save memory (ram alignment)
|
||||
typedef union {
|
||||
struct { // 1 byte
|
||||
bool outofbounds : 1; // out of bounds flag, set to true if particle is outside of display area
|
||||
bool collide : 1; // if set, particle takes part in collisions
|
||||
bool perpetual : 1; // if set, particle does not age (TTL is not decremented in move function, it still dies from killoutofbounds)
|
||||
bool custom1 : 1; // unused custom flags, can be used by FX to track particle states
|
||||
bool custom2 : 1;
|
||||
bool custom3 : 1;
|
||||
bool custom4 : 1;
|
||||
bool custom5 : 1;
|
||||
};
|
||||
byte asByte; // access as a byte, order is: LSB is first entry in the list above
|
||||
} PSparticleFlags;
|
||||
|
||||
// struct for additional particle settings (option)
|
||||
typedef struct { // 2 bytes
|
||||
uint8_t size; // particle size, 255 means 10 pixels in diameter
|
||||
uint8_t forcecounter; // counter for applying forces to individual particles
|
||||
} PSadvancedParticle;
|
||||
|
||||
// struct for advanced particle size control (option)
|
||||
typedef struct { // 8 bytes
|
||||
uint8_t asymmetry; // asymmetrical size (0=symmetrical, 255 fully asymmetric)
|
||||
uint8_t asymdir; // direction of asymmetry, 64 is x, 192 is y (0 and 128 is symmetrical)
|
||||
uint8_t maxsize; // target size for growing
|
||||
uint8_t minsize; // target size for shrinking
|
||||
uint8_t sizecounter : 4; // counters used for size contol (grow/shrink/wobble)
|
||||
uint8_t wobblecounter : 4;
|
||||
uint8_t growspeed : 4;
|
||||
uint8_t shrinkspeed : 4;
|
||||
uint8_t wobblespeed : 4;
|
||||
bool grow : 1; // flags
|
||||
bool shrink : 1;
|
||||
bool pulsate : 1; // grows & shrinks & grows & ...
|
||||
bool wobble : 1; // alternate x and y size
|
||||
} PSsizeControl;
|
||||
|
||||
|
||||
//struct for a particle source (20 bytes)
|
||||
typedef struct {
|
||||
uint16_t minLife; // minimum ttl of emittet particles
|
||||
uint16_t maxLife; // maximum ttl of emitted particles
|
||||
PSparticle source; // use a particle as the emitter source (speed, position, color)
|
||||
PSparticleFlags sourceFlags; // flags for the source particle
|
||||
int8_t var; // variation of emitted speed (adds random(+/- var) to speed)
|
||||
int8_t vx; // emitting speed
|
||||
int8_t vy;
|
||||
uint8_t size; // particle size (advanced property)
|
||||
} PSsource;
|
||||
|
||||
// class uses approximately 60 bytes
|
||||
class ParticleSystem2D {
|
||||
public:
|
||||
ParticleSystem2D(const uint32_t width, const uint32_t height, const uint32_t numberofparticles, const uint32_t numberofsources, const bool isadvanced = false, const bool sizecontrol = false); // constructor
|
||||
// note: memory is allcated in the FX function, no deconstructor needed
|
||||
void update(void); //update the particles according to set options and render to the matrix
|
||||
void updateFire(const uint8_t intensity, const bool renderonly); // update function for fire, if renderonly is set, particles are not updated (required to fix transitions with frameskips)
|
||||
void updateSystem(void); // call at the beginning of every FX, updates pointers and dimensions
|
||||
void particleMoveUpdate(PSparticle &part, PSparticleFlags &partFlags, PSsettings2D *options = NULL, PSadvancedParticle *advancedproperties = NULL); // move function
|
||||
// particle emitters
|
||||
int32_t sprayEmit(const PSsource &emitter);
|
||||
void flameEmit(const PSsource &emitter);
|
||||
int32_t angleEmit(PSsource& emitter, const uint16_t angle, const int32_t speed);
|
||||
//particle physics
|
||||
void applyGravity(PSparticle &part); // applies gravity to single particle (use this for sources)
|
||||
[[gnu::hot]] void applyForce(PSparticle &part, const int8_t xforce, const int8_t yforce, uint8_t &counter);
|
||||
[[gnu::hot]] void applyForce(const uint32_t particleindex, const int8_t xforce, const int8_t yforce); // use this for advanced property particles
|
||||
void applyForce(const int8_t xforce, const int8_t yforce); // apply a force to all particles
|
||||
void applyAngleForce(PSparticle &part, const int8_t force, const uint16_t angle, uint8_t &counter);
|
||||
void applyAngleForce(const uint32_t particleindex, const int8_t force, const uint16_t angle); // use this for advanced property particles
|
||||
void applyAngleForce(const int8_t force, const uint16_t angle); // apply angular force to all particles
|
||||
void applyFriction(PSparticle &part, const int32_t coefficient); // apply friction to specific particle
|
||||
void applyFriction(const int32_t coefficient); // apply friction to all used particles
|
||||
void pointAttractor(const uint32_t particleindex, PSparticle &attractor, const uint8_t strength, const bool swallow);
|
||||
// set options note: inlining the set function uses more flash so dont optimize
|
||||
void setUsedParticles(const uint8_t percentage); // set the percentage of particles used in the system, 255=100%
|
||||
inline uint32_t getAvailableParticles(void) { return availableParticles; } // available particles in the buffer, use this to check if buffer changed during FX init
|
||||
void setCollisionHardness(const uint8_t hardness); // hardness for particle collisions (255 means full hard)
|
||||
void setWallHardness(const uint8_t hardness); // hardness for bouncing on the wall if bounceXY is set
|
||||
void setWallRoughness(const uint8_t roughness); // wall roughness randomizes wall collisions
|
||||
void setMatrixSize(const uint32_t x, const uint32_t y);
|
||||
void setWrapX(const bool enable);
|
||||
void setWrapY(const bool enable);
|
||||
void setBounceX(const bool enable);
|
||||
void setBounceY(const bool enable);
|
||||
void setKillOutOfBounds(const bool enable); // if enabled, particles outside of matrix instantly die
|
||||
void setSaturation(const uint8_t sat); // set global color saturation
|
||||
void setColorByAge(const bool enable);
|
||||
void setMotionBlur(const uint8_t bluramount); // note: motion blur can only be used if 'particlesize' is set to zero
|
||||
void setSmearBlur(const uint8_t bluramount); // enable 2D smeared blurring of full frame
|
||||
void setParticleSize(const uint8_t size);
|
||||
void setGravity(const int8_t force = 8);
|
||||
void enableParticleCollisions(const bool enable, const uint8_t hardness = 255);
|
||||
|
||||
PSparticle *particles; // pointer to particle array
|
||||
PSparticleFlags *particleFlags; // pointer to particle flags array
|
||||
PSsource *sources; // pointer to sources
|
||||
PSadvancedParticle *advPartProps; // pointer to advanced particle properties (can be NULL)
|
||||
PSsizeControl *advPartSize; // pointer to advanced particle size control (can be NULL)
|
||||
uint8_t* PSdataEnd; // points to first available byte after the PSmemory, is set in setPointers(). use this for FX custom data
|
||||
int32_t maxX, maxY; // particle system size i.e. width-1 / height-1 in subpixels, Note: all "max" variables must be signed to compare to coordinates (which are signed)
|
||||
int32_t maxXpixel, maxYpixel; // last physical pixel that can be drawn to (FX can read this to read segment size if required), equal to width-1 / height-1
|
||||
uint32_t numSources; // number of sources
|
||||
uint32_t usedParticles; // number of particles used in animation, is relative to 'numParticles'
|
||||
//note: some variables are 32bit for speed and code size at the cost of ram
|
||||
|
||||
private:
|
||||
//rendering functions
|
||||
void ParticleSys_render();
|
||||
[[gnu::hot]] void renderParticle(const uint32_t particleindex, const uint32_t brightness, const CRGB& color, const bool wrapX, const bool wrapY);
|
||||
//paricle physics applied by system if flags are set
|
||||
void applyGravity(); // applies gravity to all particles
|
||||
void handleCollisions();
|
||||
[[gnu::hot]] void collideParticles(PSparticle &particle1, PSparticle &particle2, const int32_t dx, const int32_t dy, const int32_t collDistSq);
|
||||
void fireParticleupdate();
|
||||
//utility functions
|
||||
void updatePSpointers(const bool isadvanced, const bool sizecontrol); // update the data pointers to current segment data space
|
||||
bool updateSize(PSadvancedParticle *advprops, PSsizeControl *advsize); // advanced size control
|
||||
void getParticleXYsize(PSadvancedParticle *advprops, PSsizeControl *advsize, uint32_t &xsize, uint32_t &ysize);
|
||||
[[gnu::hot]] void bounce(int8_t &incomingspeed, int8_t ¶llelspeed, int32_t &position, const uint32_t maxposition); // bounce on a wall
|
||||
// note: variables that are accessed often are 32bit for speed
|
||||
PSsettings2D particlesettings; // settings used when updating particles (can also used by FX to move sources), do not edit properties directly, use functions above
|
||||
uint32_t numParticles; // total number of particles allocated by this system note: during transitions, less are available, use availableParticles
|
||||
uint32_t availableParticles; // number of particles available for use (can be more or less than numParticles, assigned by memory manager)
|
||||
uint32_t emitIndex; // index to count through particles to emit so searching for dead pixels is faster
|
||||
int32_t collisionHardness;
|
||||
uint32_t wallHardness;
|
||||
uint32_t wallRoughness; // randomizes wall collisions
|
||||
uint32_t particleHardRadius; // hard surface radius of a particle, used for collision detection (32bit for speed)
|
||||
uint16_t collisionStartIdx; // particle array start index for collision detection
|
||||
uint8_t fireIntesity = 0; // fire intensity, used for fire mode (flash use optimization, better than passing an argument to render function)
|
||||
uint8_t fractionOfParticlesUsed; // percentage of particles used in the system (255=100%), used during transition updates
|
||||
uint8_t forcecounter; // counter for globally applied forces
|
||||
uint8_t gforcecounter; // counter for global gravity
|
||||
int8_t gforce; // gravity strength, default is 8 (negative is allowed, positive is downwards)
|
||||
// global particle properties for basic particles
|
||||
uint8_t particlesize; // global particle size, 0 = 1 pixel, 1 = 2 pixels, 255 = 10 pixels (note: this is also added to individual sized particles)
|
||||
uint8_t motionBlur; // motion blur, values > 100 gives smoother animations. Note: motion blurring does not work if particlesize is > 0
|
||||
uint8_t smearBlur; // 2D smeared blurring of full frame
|
||||
uint8_t effectID; // ID of the effect that is using this particle system, used for transitions
|
||||
uint32_t lastRender; // last time the particles were rendered, intermediate fix for speedup
|
||||
};
|
||||
|
||||
void blur2D(CRGB *colorbuffer, const uint32_t xsize, uint32_t ysize, const uint32_t xblur, const uint32_t yblur, const uint32_t xstart = 0, uint32_t ystart = 0, const bool isparticle = false);
|
||||
// initialization functions (not part of class)
|
||||
bool initParticleSystem2D(ParticleSystem2D *&PartSys, const uint32_t requestedsources, const uint32_t additionalbytes = 0, const bool advanced = false, const bool sizecontrol = false);
|
||||
uint32_t calculateNumberOfParticles2D(const uint32_t pixels, const bool advanced, const bool sizecontrol);
|
||||
uint32_t calculateNumberOfSources2D(const uint32_t pixels, const uint32_t requestedsources);
|
||||
bool allocateParticleSystemMemory2D(const uint32_t numparticles, const uint32_t numsources, const bool advanced, const bool sizecontrol, const uint32_t additionalbytes);
|
||||
#endif // WLED_DISABLE_PARTICLESYSTEM2D
|
||||
|
||||
////////////////////////
|
||||
// 1D Particle System //
|
||||
////////////////////////
|
||||
#ifndef WLED_DISABLE_PARTICLESYSTEM1D
|
||||
// memory allocation
|
||||
#define ESP8266_MAXPARTICLES_1D 450
|
||||
#define ESP8266_MAXSOURCES_1D 16
|
||||
#define ESP32S2_MAXPARTICLES_1D 1300
|
||||
#define ESP32S2_MAXSOURCES_1D 32
|
||||
#define ESP32_MAXPARTICLES_1D 2600
|
||||
#define ESP32_MAXSOURCES_1D 64
|
||||
|
||||
// particle dimensions (subpixel division)
|
||||
#define PS_P_RADIUS_1D 32 // subpixel size, each pixel is divided by this for particle movement, if this value is changed, also change the shift defines (next two lines)
|
||||
#define PS_P_HALFRADIUS_1D (PS_P_RADIUS_1D >> 1)
|
||||
#define PS_P_RADIUS_SHIFT_1D 5 // 1 << PS_P_RADIUS_SHIFT = PS_P_RADIUS
|
||||
#define PS_P_SURFACE_1D 5 // shift: 2^PS_P_SURFACE = PS_P_RADIUS_1D
|
||||
#define PS_P_MINHARDRADIUS_1D 32 // minimum hard surface radius note: do not change or hourglass effect will be broken
|
||||
#define PS_P_MINSURFACEHARDNESS_1D 120 // minimum hardness used in collision impulse calculation
|
||||
|
||||
// struct for PS settings (shared for 1D and 2D class)
|
||||
typedef union {
|
||||
struct{
|
||||
// one byte bit field for 1D settings
|
||||
bool wrap : 1;
|
||||
bool bounce : 1;
|
||||
bool killoutofbounds : 1; // if set, out of bound particles are killed immediately
|
||||
bool useGravity : 1; // set to 1 if gravity is used, disables bounceY at the top
|
||||
bool useCollisions : 1;
|
||||
bool colorByAge : 1; // if set, particle hue is set by ttl value in render function
|
||||
bool colorByPosition : 1; // if set, particle hue is set by its position in the strip segment
|
||||
bool unused : 1;
|
||||
};
|
||||
byte asByte; // access as a byte, order is: LSB is first entry in the list above
|
||||
} PSsettings1D;
|
||||
|
||||
//struct for a single particle (8 bytes)
|
||||
typedef struct {
|
||||
int32_t x; // x position in particle system
|
||||
uint16_t ttl; // time to live in frames
|
||||
int8_t vx; // horizontal velocity
|
||||
uint8_t hue; // color hue
|
||||
} PSparticle1D;
|
||||
|
||||
//struct for particle flags
|
||||
typedef union {
|
||||
struct { // 1 byte
|
||||
bool outofbounds : 1; // out of bounds flag, set to true if particle is outside of display area
|
||||
bool collide : 1; // if set, particle takes part in collisions
|
||||
bool perpetual : 1; // if set, particle does not age (TTL is not decremented in move function, it still dies from killoutofbounds)
|
||||
bool reversegrav : 1; // if set, gravity is reversed on this particle
|
||||
bool forcedirection : 1; // direction the force was applied, 1 is positive x-direction (used for collision stacking, similar to reversegrav) TODO: not used anymore, can be removed
|
||||
bool fixed : 1; // if set, particle does not move (and collisions make other particles revert direction),
|
||||
bool custom1 : 1; // unused custom flags, can be used by FX to track particle states
|
||||
bool custom2 : 1;
|
||||
};
|
||||
byte asByte; // access as a byte, order is: LSB is first entry in the list above
|
||||
} PSparticleFlags1D;
|
||||
|
||||
// struct for additional particle settings (optional)
|
||||
typedef struct {
|
||||
uint8_t sat; //color saturation
|
||||
uint8_t size; // particle size, 255 means 10 pixels in diameter
|
||||
uint8_t forcecounter;
|
||||
} PSadvancedParticle1D;
|
||||
|
||||
//struct for a particle source (20 bytes)
|
||||
typedef struct {
|
||||
uint16_t minLife; // minimum ttl of emittet particles
|
||||
uint16_t maxLife; // maximum ttl of emitted particles
|
||||
PSparticle1D source; // use a particle as the emitter source (speed, position, color)
|
||||
PSparticleFlags1D sourceFlags; // flags for the source particle
|
||||
int8_t var; // variation of emitted speed (adds random(+/- var) to speed)
|
||||
int8_t v; // emitting speed
|
||||
uint8_t sat; // color saturation (advanced property)
|
||||
uint8_t size; // particle size (advanced property)
|
||||
// note: there is 3 bytes of padding added here
|
||||
} PSsource1D;
|
||||
|
||||
class ParticleSystem1D
|
||||
{
|
||||
public:
|
||||
ParticleSystem1D(const uint32_t length, const uint32_t numberofparticles, const uint32_t numberofsources, const bool isadvanced = false); // constructor
|
||||
// note: memory is allcated in the FX function, no deconstructor needed
|
||||
void update(void); //update the particles according to set options and render to the matrix
|
||||
void updateSystem(void); // call at the beginning of every FX, updates pointers and dimensions
|
||||
// particle emitters
|
||||
int32_t sprayEmit(const PSsource1D &emitter);
|
||||
void particleMoveUpdate(PSparticle1D &part, PSparticleFlags1D &partFlags, PSsettings1D *options = NULL, PSadvancedParticle1D *advancedproperties = NULL); // move function
|
||||
//particle physics
|
||||
[[gnu::hot]] void applyForce(PSparticle1D &part, const int8_t xforce, uint8_t &counter); //apply a force to a single particle
|
||||
void applyForce(const int8_t xforce); // apply a force to all particles
|
||||
void applyGravity(PSparticle1D &part, PSparticleFlags1D &partFlags); // applies gravity to single particle (use this for sources)
|
||||
void applyFriction(const int32_t coefficient); // apply friction to all used particles
|
||||
// set options
|
||||
void setUsedParticles(const uint8_t percentage); // set the percentage of particles used in the system, 255=100%
|
||||
inline uint32_t getAvailableParticles(void) { return availableParticles; } // available particles in the buffer, use this to check if buffer changed during FX init
|
||||
void setWallHardness(const uint8_t hardness); // hardness for bouncing on the wall if bounceXY is set
|
||||
void setSize(const uint32_t x); //set particle system size (= strip length)
|
||||
void setWrap(const bool enable);
|
||||
void setBounce(const bool enable);
|
||||
void setKillOutOfBounds(const bool enable); // if enabled, particles outside of matrix instantly die
|
||||
// void setSaturation(uint8_t sat); // set global color saturation
|
||||
void setColorByAge(const bool enable);
|
||||
void setColorByPosition(const bool enable);
|
||||
void setMotionBlur(const uint8_t bluramount); // note: motion blur can only be used if 'particlesize' is set to zero
|
||||
void setSmearBlur(const uint8_t bluramount); // enable 1D smeared blurring of full frame
|
||||
void setParticleSize(const uint8_t size); //size 0 = 1 pixel, size 1 = 2 pixels, is overruled by advanced particle size
|
||||
void setGravity(int8_t force = 8);
|
||||
void enableParticleCollisions(bool enable, const uint8_t hardness = 255);
|
||||
|
||||
PSparticle1D *particles; // pointer to particle array
|
||||
PSparticleFlags1D *particleFlags; // pointer to particle flags array
|
||||
PSsource1D *sources; // pointer to sources
|
||||
PSadvancedParticle1D *advPartProps; // pointer to advanced particle properties (can be NULL)
|
||||
//PSsizeControl *advPartSize; // pointer to advanced particle size control (can be NULL)
|
||||
uint8_t* PSdataEnd; // points to first available byte after the PSmemory, is set in setPointers(). use this for FX custom data
|
||||
int32_t maxX; // particle system size i.e. width-1, Note: all "max" variables must be signed to compare to coordinates (which are signed)
|
||||
int32_t maxXpixel; // last physical pixel that can be drawn to (FX can read this to read segment size if required), equal to width-1
|
||||
uint32_t numSources; // number of sources
|
||||
uint32_t usedParticles; // number of particles used in animation, is relative to 'numParticles'
|
||||
|
||||
private:
|
||||
//rendering functions
|
||||
void ParticleSys_render(void);
|
||||
void renderParticle(const uint32_t particleindex, const uint32_t brightness, const CRGB &color, const bool wrap);
|
||||
|
||||
//paricle physics applied by system if flags are set
|
||||
void applyGravity(); // applies gravity to all particles
|
||||
void handleCollisions();
|
||||
[[gnu::hot]] void collideParticles(PSparticle1D &particle1, const PSparticleFlags1D &particle1flags, PSparticle1D &particle2, const PSparticleFlags1D &particle2flags, int32_t dx, int32_t relativeVx, const int32_t collisiondistance);
|
||||
|
||||
//utility functions
|
||||
void updatePSpointers(const bool isadvanced); // update the data pointers to current segment data space
|
||||
//void updateSize(PSadvancedParticle *advprops, PSsizeControl *advsize); // advanced size control
|
||||
[[gnu::hot]] void bounce(int8_t &incomingspeed, int8_t ¶llelspeed, int32_t &position, const uint32_t maxposition); // bounce on a wall
|
||||
// note: variables that are accessed often are 32bit for speed
|
||||
PSsettings1D particlesettings; // settings used when updating particles
|
||||
uint32_t numParticles; // total number of particles allocated by this system note: never use more than this, even if more are available (only this many advanced particles are allocated)
|
||||
uint32_t availableParticles; // number of particles available for use (can be more or less than numParticles, assigned by memory manager)
|
||||
uint8_t fractionOfParticlesUsed; // percentage of particles used in the system (255=100%), used during transition updates
|
||||
uint32_t emitIndex; // index to count through particles to emit so searching for dead pixels is faster
|
||||
int32_t collisionHardness;
|
||||
uint32_t particleHardRadius; // hard surface radius of a particle, used for collision detection
|
||||
uint32_t wallHardness;
|
||||
uint8_t gforcecounter; // counter for global gravity
|
||||
int8_t gforce; // gravity strength, default is 8 (negative is allowed, positive is downwards)
|
||||
uint8_t forcecounter; // counter for globally applied forces
|
||||
uint16_t collisionStartIdx; // particle array start index for collision detection
|
||||
//global particle properties for basic particles
|
||||
uint8_t particlesize; // global particle size, 0 = 1 pixel, 1 = 2 pixels
|
||||
uint8_t motionBlur; // enable motion blur, values > 100 gives smoother animations
|
||||
uint8_t smearBlur; // smeared blurring of full frame
|
||||
uint8_t effectID; // ID of the effect that is using this particle system, used for transitions
|
||||
uint32_t lastRender; // last time the particles were rendered, intermediate fix for speedup
|
||||
};
|
||||
|
||||
bool initParticleSystem1D(ParticleSystem1D *&PartSys, const uint32_t requestedsources, const uint8_t fractionofparticles = 255, const uint32_t additionalbytes = 0, const bool advanced = false);
|
||||
uint32_t calculateNumberOfParticles1D(const uint32_t fraction, const bool isadvanced);
|
||||
uint32_t calculateNumberOfSources1D(const uint32_t requestedsources);
|
||||
bool allocateParticleSystemMemory1D(const uint32_t numparticles, const uint32_t numsources, const bool isadvanced, const uint32_t additionalbytes);
|
||||
void blur1D(CRGB *colorbuffer, uint32_t size, uint32_t blur, uint32_t start);
|
||||
#endif // WLED_DISABLE_PARTICLESYSTEM1D
|
@ -20,11 +20,11 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
|
||||
|
||||
//long vid = doc[F("vid")]; // 2010020
|
||||
|
||||
#ifdef WLED_USE_ETHERNET
|
||||
#if defined(ARDUINO_ARCH_ESP32) && defined(WLED_USE_ETHERNET)
|
||||
JsonObject ethernet = doc[F("eth")];
|
||||
CJSON(ethernetType, ethernet["type"]);
|
||||
// NOTE: Ethernet configuration takes priority over other use of pins
|
||||
WLED::instance().initEthernet();
|
||||
initEthernet();
|
||||
#endif
|
||||
|
||||
JsonObject id = doc["id"];
|
||||
@ -53,9 +53,11 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
|
||||
JsonArray sn = wifi["sn"];
|
||||
char ssid[33] = "";
|
||||
char pass[65] = "";
|
||||
char bssid[13] = "";
|
||||
IPAddress nIP = (uint32_t)0U, nGW = (uint32_t)0U, nSN = (uint32_t)0x00FFFFFF; // little endian
|
||||
getStringFromJson(ssid, wifi[F("ssid")], 33);
|
||||
getStringFromJson(pass, wifi["psk"], 65); // password is not normally present but if it is, use it
|
||||
getStringFromJson(bssid, wifi[F("bssid")], 13);
|
||||
for (size_t i = 0; i < 4; i++) {
|
||||
CJSON(nIP[i], ip[i]);
|
||||
CJSON(nGW[i], gw[i]);
|
||||
@ -63,6 +65,7 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
|
||||
}
|
||||
if (strlen(ssid) > 0) strlcpy(multiWiFi[n].clientSSID, ssid, 33); // this will keep old SSID intact if not present in JSON
|
||||
if (strlen(pass) > 0) strlcpy(multiWiFi[n].clientPass, pass, 65); // this will keep old password intact if not present in JSON
|
||||
if (strlen(bssid) > 0) fillStr2MAC(multiWiFi[n].bssid, bssid);
|
||||
multiWiFi[n].staticIP = nIP;
|
||||
multiWiFi[n].staticGW = nGW;
|
||||
multiWiFi[n].staticSN = nSN;
|
||||
@ -669,8 +672,8 @@ void deserializeConfigFromFS() {
|
||||
UsermodManager::readFromConfig(empty);
|
||||
serializeConfig();
|
||||
// init Ethernet (in case default type is set at compile time)
|
||||
#ifdef WLED_USE_ETHERNET
|
||||
WLED::instance().initEthernet();
|
||||
#if defined(ARDUINO_ARCH_ESP32) && defined(WLED_USE_ETHERNET)
|
||||
initEthernet();
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
@ -718,6 +721,9 @@ void serializeConfig() {
|
||||
JsonObject wifi = nw_ins.createNestedObject();
|
||||
wifi[F("ssid")] = multiWiFi[n].clientSSID;
|
||||
wifi[F("pskl")] = strlen(multiWiFi[n].clientPass);
|
||||
char bssid[13];
|
||||
fillMAC2Str(bssid, multiWiFi[n].bssid);
|
||||
wifi[F("bssid")] = bssid;
|
||||
JsonArray wifi_ip = wifi.createNestedArray("ip");
|
||||
JsonArray wifi_gw = wifi.createNestedArray("gw");
|
||||
JsonArray wifi_sn = wifi.createNestedArray("sn");
|
||||
@ -753,7 +759,7 @@ void serializeConfig() {
|
||||
wifi[F("txpwr")] = txPower;
|
||||
#endif
|
||||
|
||||
#ifdef WLED_USE_ETHERNET
|
||||
#if defined(ARDUINO_ARCH_ESP32) && defined(WLED_USE_ETHERNET)
|
||||
JsonObject ethernet = root.createNestedObject("eth");
|
||||
ethernet["type"] = ethernetType;
|
||||
if (ethernetType != WLED_ETH_NONE && ethernetType < WLED_NUM_ETH_TYPES) {
|
||||
|
@ -47,7 +47,7 @@
|
||||
scanLoops = 0;
|
||||
|
||||
if (networks.length > 0) {
|
||||
let cs = d.querySelectorAll("#wifi_entries input[type=text]");
|
||||
let cs = d.querySelectorAll("#wifi_entries input[type=text][name^=CS]");
|
||||
for (let input of (cs||[])) {
|
||||
let found = false;
|
||||
let select = cE("select");
|
||||
@ -64,7 +64,7 @@
|
||||
const option = cE("option");
|
||||
|
||||
option.setAttribute("value", networks[i].ssid);
|
||||
option.textContent = `${networks[i].ssid} (${networks[i].rssi} dBm)`;
|
||||
option.textContent = `${networks[i].ssid} (${networks[i].rssi} dBm)`; // [${networks[i].bssid.replaceAll(':','')}]
|
||||
|
||||
if (networks[i].ssid === input.value) {
|
||||
option.setAttribute("selected", "selected");
|
||||
@ -109,12 +109,13 @@
|
||||
gId("wifi_add").style.display = (i<maxNetworks) ? "inline":"none";
|
||||
gId("wifi_rem").style.display = (i>1) ? "inline":"none";
|
||||
}
|
||||
function addWiFi(ssid="",pass="",ip=0,gw=0,sn=0x00ffffff) { // little endian
|
||||
function addWiFi(ssid="",pass="",bssid="",ip=0,gw=0,sn=0x00ffffff) { // little endian
|
||||
var i = gId("wifi_entries").childNodes.length;
|
||||
if (i >= maxNetworks) return;
|
||||
var b = `<div id="net${i}"><hr class="sml">
|
||||
Network name (SSID${i==0?", empty to not connect":""}):<br><input type="text" id="CS${i}" name="CS${i}" maxlength="32" value="${ssid}" ${i>0?"required":""}><br>
|
||||
Network password:<br><input type="password" name="PW${i}" maxlength="64" value="${pass}"><br>
|
||||
BSSID (optional):<br><input type="text" id="BS${i}" name="BS${i}" maxlength="12" value="${bssid}"><br>
|
||||
Static IP (leave at 0.0.0.0 for DHCP)${i==0?"<br>Also used by Ethernet":""}:<br>
|
||||
<input name="IP${i}0" type="number" class="s" min="0" max="255" value="${ip&0xFF}" required>.<input name="IP${i}1" type="number" class="s" min="0" max="255" value="${(ip>>8)&0xFF}" required>.<input name="IP${i}2" type="number" class="s" min="0" max="255" value="${(ip>>16)&0xFF}" required>.<input name="IP${i}3" type="number" class="s" min="0" max="255" value="${(ip>>24)&0xFF}" required><br>
|
||||
Static gateway:<br>
|
||||
|
@ -53,6 +53,7 @@ bool getJsonValue(const JsonVariant& element, DestType& destination, const Defau
|
||||
typedef struct WiFiConfig {
|
||||
char clientSSID[33];
|
||||
char clientPass[65];
|
||||
uint8_t bssid[6];
|
||||
IPAddress staticIP;
|
||||
IPAddress staticGW;
|
||||
IPAddress staticSN;
|
||||
@ -63,6 +64,7 @@ typedef struct WiFiConfig {
|
||||
{
|
||||
strncpy(clientSSID, ssid, 32); clientSSID[32] = 0;
|
||||
strncpy(clientPass, pass, 64); clientPass[64] = 0;
|
||||
memset(bssid, 0, sizeof(bssid));
|
||||
}
|
||||
} wifi_config;
|
||||
|
||||
@ -203,14 +205,14 @@ void sendArtnetPollReply(ArtPollReply* reply, IPAddress ipAddress, uint16_t port
|
||||
bool handleFileRead(AsyncWebServerRequest*, String path);
|
||||
bool writeObjectToFileUsingId(const char* file, uint16_t id, const JsonDocument* content);
|
||||
bool writeObjectToFile(const char* file, const char* key, const JsonDocument* content);
|
||||
bool readObjectFromFileUsingId(const char* file, uint16_t id, JsonDocument* dest);
|
||||
bool readObjectFromFile(const char* file, const char* key, JsonDocument* dest);
|
||||
bool readObjectFromFileUsingId(const char* file, uint16_t id, JsonDocument* dest, const JsonDocument* filter = nullptr);
|
||||
bool readObjectFromFile(const char* file, const char* key, JsonDocument* dest, const JsonDocument* filter = nullptr);
|
||||
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) { 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); };
|
||||
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); };
|
||||
|
||||
//hue.cpp
|
||||
void handleHue();
|
||||
@ -360,7 +362,12 @@ void espNowReceiveCB(uint8_t* address, uint8_t* data, uint8_t len, signed int rs
|
||||
#endif
|
||||
|
||||
//network.cpp
|
||||
bool initEthernet(); // result is informational
|
||||
int getSignalQuality(int rssi);
|
||||
void fillMAC2Str(char *str, const uint8_t *mac);
|
||||
void fillStr2MAC(uint8_t *mac, const char *str);
|
||||
int findWiFi(bool doScan = false);
|
||||
bool isWiFiConfigured();
|
||||
void WiFiEvent(WiFiEvent_t event);
|
||||
|
||||
//um_manager.cpp
|
||||
@ -478,6 +485,7 @@ void userLoop();
|
||||
#include "soc/wdev_reg.h"
|
||||
#define HW_RND_REGISTER REG_READ(WDEV_RND_REG)
|
||||
#endif
|
||||
#define hex2int(a) (((a)>='0' && (a)<='9') ? (a)-'0' : ((a)>='A' && (a)<='F') ? (a)-'A'+10 : ((a)>='a' && (a)<='f') ? (a)-'a'+10 : 0)
|
||||
[[gnu::pure]] 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 vmin=0, byte vmax=255); // getVal supports inc/decrementing and random ("X~Y(r|[w]~[-][Z])" form)
|
||||
|
@ -325,15 +325,15 @@ bool writeObjectToFile(const char* file, const char* key, const JsonDocument* co
|
||||
return true;
|
||||
}
|
||||
|
||||
bool readObjectFromFileUsingId(const char* file, uint16_t id, JsonDocument* dest)
|
||||
bool readObjectFromFileUsingId(const char* file, uint16_t id, JsonDocument* dest, const JsonDocument* filter)
|
||||
{
|
||||
char objKey[10];
|
||||
sprintf(objKey, "\"%d\":", id);
|
||||
return readObjectFromFile(file, objKey, dest);
|
||||
return readObjectFromFile(file, objKey, dest, filter);
|
||||
}
|
||||
|
||||
//if the key is a nullptr, deserialize entire object
|
||||
bool readObjectFromFile(const char* file, const char* key, JsonDocument* dest)
|
||||
bool readObjectFromFile(const char* file, const char* key, JsonDocument* dest, const JsonDocument* filter)
|
||||
{
|
||||
if (doCloseFile) closeFile();
|
||||
#ifdef WLED_DEBUG_FS
|
||||
@ -352,7 +352,8 @@ bool readObjectFromFile(const char* file, const char* key, JsonDocument* dest)
|
||||
return false;
|
||||
}
|
||||
|
||||
deserializeJson(*dest, f);
|
||||
if (filter) deserializeJson(*dest, f, DeserializationOption::Filter(*filter));
|
||||
else deserializeJson(*dest, f);
|
||||
|
||||
f.close();
|
||||
DEBUGFS_PRINTF("Read, took %d ms\n", millis() - s);
|
||||
|
@ -3,7 +3,7 @@
|
||||
#include "wled_ethernet.h"
|
||||
|
||||
|
||||
#ifdef WLED_USE_ETHERNET
|
||||
#if defined(ARDUINO_ARCH_ESP32) && defined(WLED_USE_ETHERNET)
|
||||
// The following six pins are neither configurable nor
|
||||
// can they be re-assigned through IOMUX / GPIO matrix.
|
||||
// See https://docs.espressif.com/projects/esp-idf/en/latest/esp32/hw-reference/esp32/get-started-ethernet-kit-v1.1.html#ip101gri-phy-interface
|
||||
@ -146,6 +146,101 @@ const ethernet_settings ethernetBoards[] = {
|
||||
ETH_CLOCK_GPIO0_OUT // eth_clk_mode
|
||||
}
|
||||
};
|
||||
|
||||
bool initEthernet()
|
||||
{
|
||||
static bool successfullyConfiguredEthernet = false;
|
||||
|
||||
if (successfullyConfiguredEthernet) {
|
||||
// DEBUG_PRINTLN(F("initE: ETH already successfully configured, ignoring"));
|
||||
return false;
|
||||
}
|
||||
if (ethernetType == WLED_ETH_NONE) {
|
||||
return false;
|
||||
}
|
||||
if (ethernetType >= WLED_NUM_ETH_TYPES) {
|
||||
DEBUG_PRINTF_P(PSTR("initE: Ignoring attempt for invalid ethernetType (%d)\n"), ethernetType);
|
||||
return false;
|
||||
}
|
||||
|
||||
DEBUG_PRINTF_P(PSTR("initE: Attempting ETH config: %d\n"), ethernetType);
|
||||
|
||||
// Ethernet initialization should only succeed once -- else reboot required
|
||||
ethernet_settings es = ethernetBoards[ethernetType];
|
||||
managed_pin_type pinsToAllocate[10] = {
|
||||
// first six pins are non-configurable
|
||||
esp32_nonconfigurable_ethernet_pins[0],
|
||||
esp32_nonconfigurable_ethernet_pins[1],
|
||||
esp32_nonconfigurable_ethernet_pins[2],
|
||||
esp32_nonconfigurable_ethernet_pins[3],
|
||||
esp32_nonconfigurable_ethernet_pins[4],
|
||||
esp32_nonconfigurable_ethernet_pins[5],
|
||||
{ (int8_t)es.eth_mdc, true }, // [6] = MDC is output and mandatory
|
||||
{ (int8_t)es.eth_mdio, true }, // [7] = MDIO is bidirectional and mandatory
|
||||
{ (int8_t)es.eth_power, true }, // [8] = optional pin, not all boards use
|
||||
{ ((int8_t)0xFE), false }, // [9] = replaced with eth_clk_mode, mandatory
|
||||
};
|
||||
// update the clock pin....
|
||||
if (es.eth_clk_mode == ETH_CLOCK_GPIO0_IN) {
|
||||
pinsToAllocate[9].pin = 0;
|
||||
pinsToAllocate[9].isOutput = false;
|
||||
} else if (es.eth_clk_mode == ETH_CLOCK_GPIO0_OUT) {
|
||||
pinsToAllocate[9].pin = 0;
|
||||
pinsToAllocate[9].isOutput = true;
|
||||
} else if (es.eth_clk_mode == ETH_CLOCK_GPIO16_OUT) {
|
||||
pinsToAllocate[9].pin = 16;
|
||||
pinsToAllocate[9].isOutput = true;
|
||||
} else if (es.eth_clk_mode == ETH_CLOCK_GPIO17_OUT) {
|
||||
pinsToAllocate[9].pin = 17;
|
||||
pinsToAllocate[9].isOutput = true;
|
||||
} else {
|
||||
DEBUG_PRINTF_P(PSTR("initE: Failing due to invalid eth_clk_mode (%d)\n"), es.eth_clk_mode);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!PinManager::allocateMultiplePins(pinsToAllocate, 10, PinOwner::Ethernet)) {
|
||||
DEBUG_PRINTLN(F("initE: Failed to allocate ethernet pins"));
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
For LAN8720 the most correct way is to perform clean reset each time before init
|
||||
applying LOW to power or nRST pin for at least 100 us (please refer to datasheet, page 59)
|
||||
ESP_IDF > V4 implements it (150 us, lan87xx_reset_hw(esp_eth_phy_t *phy) function in
|
||||
/components/esp_eth/src/esp_eth_phy_lan87xx.c, line 280)
|
||||
but ESP_IDF < V4 does not. Lets do it:
|
||||
[not always needed, might be relevant in some EMI situations at startup and for hot resets]
|
||||
*/
|
||||
#if ESP_IDF_VERSION_MAJOR==3
|
||||
if(es.eth_power>0 && es.eth_type==ETH_PHY_LAN8720) {
|
||||
pinMode(es.eth_power, OUTPUT);
|
||||
digitalWrite(es.eth_power, 0);
|
||||
delayMicroseconds(150);
|
||||
digitalWrite(es.eth_power, 1);
|
||||
delayMicroseconds(10);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!ETH.begin(
|
||||
(uint8_t) es.eth_address,
|
||||
(int) es.eth_power,
|
||||
(int) es.eth_mdc,
|
||||
(int) es.eth_mdio,
|
||||
(eth_phy_type_t) es.eth_type,
|
||||
(eth_clock_mode_t) es.eth_clk_mode
|
||||
)) {
|
||||
DEBUG_PRINTLN(F("initC: ETH.begin() failed"));
|
||||
// de-allocate the allocated pins
|
||||
for (managed_pin_type mpt : pinsToAllocate) {
|
||||
PinManager::deallocatePin(mpt.pin, PinOwner::Ethernet);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
successfullyConfiguredEthernet = true;
|
||||
DEBUG_PRINTLN(F("initC: *** Ethernet successfully configured! ***"));
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
@ -170,19 +265,136 @@ int getSignalQuality(int rssi)
|
||||
}
|
||||
|
||||
|
||||
void fillMAC2Str(char *str, const uint8_t *mac) {
|
||||
sprintf_P(str, PSTR("%02x%02x%02x%02x%02x%02x"), MAC2STR(mac));
|
||||
byte nul = 0;
|
||||
for (int i = 0; i < 6; i++) nul |= *mac++; // do we have 0
|
||||
if (!nul) str[0] = '\0'; // empty string
|
||||
}
|
||||
|
||||
void fillStr2MAC(uint8_t *mac, const char *str) {
|
||||
for (int i = 0; i < 6; i++) *mac++ = 0; // clear
|
||||
if (!str) return; // null string
|
||||
uint64_t MAC = strtoull(str, nullptr, 16);
|
||||
for (int i = 0; i < 6; i++) { *--mac = MAC & 0xFF; MAC >>= 8; }
|
||||
}
|
||||
|
||||
|
||||
// performs asynchronous scan for available networks (which may take couple of seconds to finish)
|
||||
// returns configured WiFi ID with the strongest signal (or default if no configured networks available)
|
||||
int findWiFi(bool doScan) {
|
||||
if (multiWiFi.size() <= 1) {
|
||||
DEBUG_PRINTF_P(PSTR("WiFi: Defaulf SSID (%s) used.\n"), multiWiFi[0].clientSSID);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int status = WiFi.scanComplete(); // complete scan may take as much as several seconds (usually <6s with not very crowded air)
|
||||
|
||||
if (doScan || status == WIFI_SCAN_FAILED) {
|
||||
DEBUG_PRINTF_P(PSTR("WiFi: Scan started. @ %lus\n"), millis()/1000);
|
||||
WiFi.scanNetworks(true); // start scanning in asynchronous mode (will delete old scan)
|
||||
} else if (status >= 0) { // status contains number of found networks (including duplicate SSIDs with different BSSID)
|
||||
DEBUG_PRINTF_P(PSTR("WiFi: Found %d SSIDs. @ %lus\n"), status, millis()/1000);
|
||||
int rssi = -9999;
|
||||
int selected = selectedWiFi;
|
||||
for (int o = 0; o < status; o++) {
|
||||
DEBUG_PRINTF_P(PSTR(" SSID: %s (BSSID: %s) RSSI: %ddB\n"), WiFi.SSID(o).c_str(), WiFi.BSSIDstr(o).c_str(), WiFi.RSSI(o));
|
||||
for (unsigned n = 0; n < multiWiFi.size(); n++)
|
||||
if (!strcmp(WiFi.SSID(o).c_str(), multiWiFi[n].clientSSID)) {
|
||||
bool foundBSSID = memcmp(multiWiFi[n].bssid, WiFi.BSSID(o), 6) == 0;
|
||||
// find the WiFi with the strongest signal (but keep priority of entry if signal difference is not big)
|
||||
if (foundBSSID || (n < selected && WiFi.RSSI(o) > rssi-10) || WiFi.RSSI(o) > rssi) {
|
||||
rssi = foundBSSID ? 0 : WiFi.RSSI(o); // RSSI is only ever negative
|
||||
selected = n;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
DEBUG_PRINTF_P(PSTR("WiFi: Selected SSID: %s RSSI: %ddB\n"), multiWiFi[selected].clientSSID, rssi);
|
||||
return selected;
|
||||
}
|
||||
//DEBUG_PRINT(F("WiFi scan running."));
|
||||
return status; // scan is still running or there was an error
|
||||
}
|
||||
|
||||
|
||||
bool isWiFiConfigured() {
|
||||
return multiWiFi.size() > 1 || (strlen(multiWiFi[0].clientSSID) >= 1 && strcmp_P(multiWiFi[0].clientSSID, PSTR(DEFAULT_CLIENT_SSID)) != 0);
|
||||
}
|
||||
|
||||
#if defined(ESP8266)
|
||||
#define ARDUINO_EVENT_WIFI_AP_STADISCONNECTED WIFI_EVENT_SOFTAPMODE_STADISCONNECTED
|
||||
#define ARDUINO_EVENT_WIFI_AP_STACONNECTED WIFI_EVENT_SOFTAPMODE_STACONNECTED
|
||||
#define ARDUINO_EVENT_WIFI_STA_GOT_IP WIFI_EVENT_STAMODE_GOT_IP
|
||||
#define ARDUINO_EVENT_WIFI_STA_CONNECTED WIFI_EVENT_STAMODE_CONNECTED
|
||||
#define ARDUINO_EVENT_WIFI_STA_DISCONNECTED WIFI_EVENT_STAMODE_DISCONNECTED
|
||||
#elif defined(ARDUINO_ARCH_ESP32) && !defined(ESP_ARDUINO_VERSION_MAJOR) //ESP_IDF_VERSION_MAJOR==3
|
||||
// not strictly IDF v3 but Arduino core related
|
||||
#define ARDUINO_EVENT_WIFI_AP_STADISCONNECTED SYSTEM_EVENT_AP_STADISCONNECTED
|
||||
#define ARDUINO_EVENT_WIFI_AP_STACONNECTED SYSTEM_EVENT_AP_STACONNECTED
|
||||
#define ARDUINO_EVENT_WIFI_STA_GOT_IP SYSTEM_EVENT_STA_GOT_IP
|
||||
#define ARDUINO_EVENT_WIFI_STA_CONNECTED SYSTEM_EVENT_STA_CONNECTED
|
||||
#define ARDUINO_EVENT_WIFI_STA_DISCONNECTED SYSTEM_EVENT_STA_DISCONNECTED
|
||||
#define ARDUINO_EVENT_WIFI_AP_START SYSTEM_EVENT_AP_START
|
||||
#define ARDUINO_EVENT_WIFI_AP_STOP SYSTEM_EVENT_AP_STOP
|
||||
#define ARDUINO_EVENT_WIFI_SCAN_DONE SYSTEM_EVENT_SCAN_DONE
|
||||
#define ARDUINO_EVENT_ETH_START SYSTEM_EVENT_ETH_START
|
||||
#define ARDUINO_EVENT_ETH_CONNECTED SYSTEM_EVENT_ETH_CONNECTED
|
||||
#define ARDUINO_EVENT_ETH_DISCONNECTED SYSTEM_EVENT_ETH_DISCONNECTED
|
||||
#endif
|
||||
|
||||
//handle Ethernet connection event
|
||||
void WiFiEvent(WiFiEvent_t event)
|
||||
{
|
||||
switch (event) {
|
||||
#if defined(ARDUINO_ARCH_ESP32) && defined(WLED_USE_ETHERNET)
|
||||
case SYSTEM_EVENT_ETH_START:
|
||||
DEBUG_PRINTLN(F("ETH Started"));
|
||||
case ARDUINO_EVENT_WIFI_AP_STADISCONNECTED:
|
||||
// AP client disconnected
|
||||
if (--apClients == 0 && isWiFiConfigured()) forceReconnect = true; // no clients reconnect WiFi if awailable
|
||||
DEBUG_PRINTF_P(PSTR("WiFi-E: AP Client Disconnected (%d) @ %lus.\n"), (int)apClients, millis()/1000);
|
||||
break;
|
||||
case SYSTEM_EVENT_ETH_CONNECTED:
|
||||
case ARDUINO_EVENT_WIFI_AP_STACONNECTED:
|
||||
// AP client connected
|
||||
apClients++;
|
||||
DEBUG_PRINTF_P(PSTR("WiFi-E: AP Client Connected (%d) @ %lus.\n"), (int)apClients, millis()/1000);
|
||||
break;
|
||||
case ARDUINO_EVENT_WIFI_STA_GOT_IP:
|
||||
DEBUG_PRINT(F("WiFi-E: IP address: ")); DEBUG_PRINTLN(Network.localIP());
|
||||
break;
|
||||
case ARDUINO_EVENT_WIFI_STA_CONNECTED:
|
||||
// followed by IDLE and SCAN_DONE
|
||||
DEBUG_PRINTF_P(PSTR("WiFi-E: Connected! @ %lus\n"), millis()/1000);
|
||||
wasConnected = true;
|
||||
break;
|
||||
case ARDUINO_EVENT_WIFI_STA_DISCONNECTED:
|
||||
if (wasConnected && interfacesInited) {
|
||||
DEBUG_PRINTF_P(PSTR("WiFi-E: Disconnected! @ %lus\n"), millis()/1000);
|
||||
if (interfacesInited && multiWiFi.size() > 1 && WiFi.scanComplete() >= 0) {
|
||||
findWiFi(true); // reinit WiFi scan
|
||||
forceReconnect = true;
|
||||
}
|
||||
interfacesInited = false;
|
||||
}
|
||||
break;
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
case ARDUINO_EVENT_WIFI_SCAN_DONE:
|
||||
// also triggered when connected to selected SSID
|
||||
DEBUG_PRINTLN(F("WiFi-E: SSID scan completed."));
|
||||
break;
|
||||
case ARDUINO_EVENT_WIFI_AP_START:
|
||||
DEBUG_PRINTLN(F("WiFi-E: AP Started"));
|
||||
break;
|
||||
case ARDUINO_EVENT_WIFI_AP_STOP:
|
||||
DEBUG_PRINTLN(F("WiFi-E: AP Stopped"));
|
||||
break;
|
||||
#if defined(WLED_USE_ETHERNET)
|
||||
case ARDUINO_EVENT_ETH_START:
|
||||
DEBUG_PRINTLN(F("ETH-E: Started"));
|
||||
break;
|
||||
case ARDUINO_EVENT_ETH_CONNECTED:
|
||||
{
|
||||
DEBUG_PRINTLN(F("ETH Connected"));
|
||||
DEBUG_PRINTLN(F("ETH-E: Connected"));
|
||||
if (!apActive) {
|
||||
WiFi.disconnect(true);
|
||||
WiFi.disconnect(true); // disable WiFi entirely
|
||||
}
|
||||
if (multiWiFi[0].staticIP != (uint32_t)0x00000000 && multiWiFi[0].staticGW != (uint32_t)0x00000000) {
|
||||
ETH.config(multiWiFi[0].staticIP, multiWiFi[0].staticGW, multiWiFi[0].staticSN, dnsAddress);
|
||||
@ -196,18 +408,20 @@ void WiFiEvent(WiFiEvent_t event)
|
||||
showWelcomePage = false;
|
||||
break;
|
||||
}
|
||||
case SYSTEM_EVENT_ETH_DISCONNECTED:
|
||||
DEBUG_PRINTLN(F("ETH Disconnected"));
|
||||
case ARDUINO_EVENT_ETH_DISCONNECTED:
|
||||
DEBUG_PRINTLN(F("ETH-E: Disconnected"));
|
||||
// This doesn't really affect ethernet per se,
|
||||
// as it's only configured once. Rather, it
|
||||
// may be necessary to reconnect the WiFi when
|
||||
// ethernet disconnects, as a way to provide
|
||||
// alternative access to the device.
|
||||
if (interfacesInited && WiFi.scanComplete() >= 0) findWiFi(true); // reinit WiFi scan
|
||||
forceReconnect = true;
|
||||
break;
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
default:
|
||||
DEBUG_PRINTF_P(PSTR("Network event: %d\n"), (int)event);
|
||||
DEBUG_PRINTF_P(PSTR("WiFi-E: Event %d\n"), (int)event);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -23,6 +23,7 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
|
||||
for (size_t n = 0; n < WLED_MAX_WIFI_COUNT; n++) {
|
||||
char cs[4] = "CS"; cs[2] = 48+n; cs[3] = 0; //client SSID
|
||||
char pw[4] = "PW"; pw[2] = 48+n; pw[3] = 0; //client password
|
||||
char bs[4] = "BS"; bs[2] = 48+n; bs[3] = 0; //BSSID
|
||||
char ip[5] = "IP"; ip[2] = 48+n; ip[4] = 0; //IP address
|
||||
char gw[5] = "GW"; gw[2] = 48+n; gw[4] = 0; //GW address
|
||||
char sn[5] = "SN"; sn[2] = 48+n; sn[4] = 0; //subnet mask
|
||||
@ -39,6 +40,7 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
|
||||
strlcpy(multiWiFi[n].clientPass, request->arg(pw).c_str(), 65);
|
||||
forceReconnect = true;
|
||||
}
|
||||
fillStr2MAC(multiWiFi[n].bssid, request->arg(bs).c_str());
|
||||
for (size_t i = 0; i < 4; i++) {
|
||||
ip[3] = 48+i;
|
||||
gw[3] = 48+i;
|
||||
@ -93,9 +95,9 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
|
||||
strlwr(linked_remote); //Normalize MAC format to lowercase
|
||||
#endif
|
||||
|
||||
#ifdef WLED_USE_ETHERNET
|
||||
#if defined(ARDUINO_ARCH_ESP32) && defined(WLED_USE_ETHERNET)
|
||||
ethernetType = request->arg(F("ETH")).toInt();
|
||||
WLED::instance().initEthernet();
|
||||
initEthernet();
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -530,6 +530,8 @@ um_data_t* simulateSound(uint8_t simulationId)
|
||||
static const char s_ledmap_tmpl[] PROGMEM = "ledmap%d.json";
|
||||
// enumerate all ledmapX.json files on FS and extract ledmap names if existing
|
||||
void enumerateLedmaps() {
|
||||
StaticJsonDocument<64> filter;
|
||||
filter["n"] = true;
|
||||
ledMaps = 1;
|
||||
for (size_t i=1; i<WLED_MAX_LEDMAPS; i++) {
|
||||
char fileName[33] = "/";
|
||||
@ -548,7 +550,7 @@ void enumerateLedmaps() {
|
||||
|
||||
#ifndef ESP8266
|
||||
if (requestJSONBufferLock(21)) {
|
||||
if (readObjectFromFile(fileName, nullptr, pDoc)) {
|
||||
if (readObjectFromFile(fileName, nullptr, pDoc, &filter)) {
|
||||
size_t len = 0;
|
||||
JsonObject root = pDoc->as<JsonObject>();
|
||||
if (!root["n"].isNull()) {
|
||||
|
140
wled00/wled.cpp
140
wled00/wled.cpp
@ -601,146 +601,6 @@ void WLED::initAP(bool resetAP)
|
||||
apActive = true;
|
||||
}
|
||||
|
||||
bool WLED::initEthernet()
|
||||
{
|
||||
#if defined(ARDUINO_ARCH_ESP32) && defined(WLED_USE_ETHERNET)
|
||||
|
||||
static bool successfullyConfiguredEthernet = false;
|
||||
|
||||
if (successfullyConfiguredEthernet) {
|
||||
// DEBUG_PRINTLN(F("initE: ETH already successfully configured, ignoring"));
|
||||
return false;
|
||||
}
|
||||
if (ethernetType == WLED_ETH_NONE) {
|
||||
return false;
|
||||
}
|
||||
if (ethernetType >= WLED_NUM_ETH_TYPES) {
|
||||
DEBUG_PRINTF_P(PSTR("initE: Ignoring attempt for invalid ethernetType (%d)\n"), ethernetType);
|
||||
return false;
|
||||
}
|
||||
|
||||
DEBUG_PRINTF_P(PSTR("initE: Attempting ETH config: %d\n"), ethernetType);
|
||||
|
||||
// Ethernet initialization should only succeed once -- else reboot required
|
||||
ethernet_settings es = ethernetBoards[ethernetType];
|
||||
managed_pin_type pinsToAllocate[10] = {
|
||||
// first six pins are non-configurable
|
||||
esp32_nonconfigurable_ethernet_pins[0],
|
||||
esp32_nonconfigurable_ethernet_pins[1],
|
||||
esp32_nonconfigurable_ethernet_pins[2],
|
||||
esp32_nonconfigurable_ethernet_pins[3],
|
||||
esp32_nonconfigurable_ethernet_pins[4],
|
||||
esp32_nonconfigurable_ethernet_pins[5],
|
||||
{ (int8_t)es.eth_mdc, true }, // [6] = MDC is output and mandatory
|
||||
{ (int8_t)es.eth_mdio, true }, // [7] = MDIO is bidirectional and mandatory
|
||||
{ (int8_t)es.eth_power, true }, // [8] = optional pin, not all boards use
|
||||
{ ((int8_t)0xFE), false }, // [9] = replaced with eth_clk_mode, mandatory
|
||||
};
|
||||
// update the clock pin....
|
||||
if (es.eth_clk_mode == ETH_CLOCK_GPIO0_IN) {
|
||||
pinsToAllocate[9].pin = 0;
|
||||
pinsToAllocate[9].isOutput = false;
|
||||
} else if (es.eth_clk_mode == ETH_CLOCK_GPIO0_OUT) {
|
||||
pinsToAllocate[9].pin = 0;
|
||||
pinsToAllocate[9].isOutput = true;
|
||||
} else if (es.eth_clk_mode == ETH_CLOCK_GPIO16_OUT) {
|
||||
pinsToAllocate[9].pin = 16;
|
||||
pinsToAllocate[9].isOutput = true;
|
||||
} else if (es.eth_clk_mode == ETH_CLOCK_GPIO17_OUT) {
|
||||
pinsToAllocate[9].pin = 17;
|
||||
pinsToAllocate[9].isOutput = true;
|
||||
} else {
|
||||
DEBUG_PRINTF_P(PSTR("initE: Failing due to invalid eth_clk_mode (%d)\n"), es.eth_clk_mode);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!PinManager::allocateMultiplePins(pinsToAllocate, 10, PinOwner::Ethernet)) {
|
||||
DEBUG_PRINTLN(F("initE: Failed to allocate ethernet pins"));
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
For LAN8720 the most correct way is to perform clean reset each time before init
|
||||
applying LOW to power or nRST pin for at least 100 us (please refer to datasheet, page 59)
|
||||
ESP_IDF > V4 implements it (150 us, lan87xx_reset_hw(esp_eth_phy_t *phy) function in
|
||||
/components/esp_eth/src/esp_eth_phy_lan87xx.c, line 280)
|
||||
but ESP_IDF < V4 does not. Lets do it:
|
||||
[not always needed, might be relevant in some EMI situations at startup and for hot resets]
|
||||
*/
|
||||
#if ESP_IDF_VERSION_MAJOR==3
|
||||
if(es.eth_power>0 && es.eth_type==ETH_PHY_LAN8720) {
|
||||
pinMode(es.eth_power, OUTPUT);
|
||||
digitalWrite(es.eth_power, 0);
|
||||
delayMicroseconds(150);
|
||||
digitalWrite(es.eth_power, 1);
|
||||
delayMicroseconds(10);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!ETH.begin(
|
||||
(uint8_t) es.eth_address,
|
||||
(int) es.eth_power,
|
||||
(int) es.eth_mdc,
|
||||
(int) es.eth_mdio,
|
||||
(eth_phy_type_t) es.eth_type,
|
||||
(eth_clock_mode_t) es.eth_clk_mode
|
||||
)) {
|
||||
DEBUG_PRINTLN(F("initC: ETH.begin() failed"));
|
||||
// de-allocate the allocated pins
|
||||
for (managed_pin_type mpt : pinsToAllocate) {
|
||||
PinManager::deallocatePin(mpt.pin, PinOwner::Ethernet);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
successfullyConfiguredEthernet = true;
|
||||
DEBUG_PRINTLN(F("initC: *** Ethernet successfully configured! ***"));
|
||||
return true;
|
||||
#else
|
||||
return false; // Ethernet not enabled for build
|
||||
#endif
|
||||
}
|
||||
|
||||
// performs asynchronous scan for available networks (which may take couple of seconds to finish)
|
||||
// returns configured WiFi ID with the strongest signal (or default if no configured networks available)
|
||||
int8_t WLED::findWiFi(bool doScan) {
|
||||
if (multiWiFi.size() <= 1) {
|
||||
DEBUG_PRINTLN(F("Defaulf WiFi used."));
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (doScan) WiFi.scanDelete(); // restart scan
|
||||
|
||||
int status = WiFi.scanComplete(); // complete scan may take as much as several seconds (usually <3s with not very crowded air)
|
||||
|
||||
if (status == WIFI_SCAN_FAILED) {
|
||||
DEBUG_PRINTLN(F("WiFi scan started."));
|
||||
WiFi.scanNetworks(true); // start scanning in asynchronous mode
|
||||
} else if (status >= 0) { // status contains number of found networks
|
||||
DEBUG_PRINT(F("WiFi scan completed: ")); DEBUG_PRINTLN(status);
|
||||
int rssi = -9999;
|
||||
unsigned selected = selectedWiFi;
|
||||
for (int o = 0; o < status; o++) {
|
||||
DEBUG_PRINT(F(" WiFi available: ")); DEBUG_PRINT(WiFi.SSID(o));
|
||||
DEBUG_PRINT(F(" RSSI: ")); DEBUG_PRINT(WiFi.RSSI(o)); DEBUG_PRINTLN(F("dB"));
|
||||
for (unsigned n = 0; n < multiWiFi.size(); n++)
|
||||
if (!strcmp(WiFi.SSID(o).c_str(), multiWiFi[n].clientSSID)) {
|
||||
// find the WiFi with the strongest signal (but keep priority of entry if signal difference is not big)
|
||||
if ((n < selected && WiFi.RSSI(o) > rssi-10) || WiFi.RSSI(o) > rssi) {
|
||||
rssi = WiFi.RSSI(o);
|
||||
selected = n;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
DEBUG_PRINT(F("Selected: ")); DEBUG_PRINT(multiWiFi[selected].clientSSID);
|
||||
DEBUG_PRINT(F(" RSSI: ")); DEBUG_PRINT(rssi); DEBUG_PRINTLN(F("dB"));
|
||||
return selected;
|
||||
}
|
||||
//DEBUG_PRINT(F("WiFi scan running."));
|
||||
return status; // scan is still running or there was an error
|
||||
}
|
||||
|
||||
void WLED::initConnection()
|
||||
{
|
||||
DEBUG_PRINTF_P(PSTR("initConnection() called @ %lus.\n"), millis()/1000);
|
||||
|
@ -359,7 +359,7 @@ WLED_GLOBAL wifi_options_t wifiOpt _INIT_N(({0, 1, false, AP_BEHAVIOR_BOOT_NO_CO
|
||||
#define noWifiSleep wifiOpt.noWifiSleep
|
||||
#define force802_3g wifiOpt.force802_3g
|
||||
#else
|
||||
WLED_GLOBAL uint8_t selectedWiFi _INIT(0);
|
||||
WLED_GLOBAL int8_t selectedWiFi _INIT(0);
|
||||
WLED_GLOBAL byte apChannel _INIT(1); // 2.4GHz WiFi AP channel (1-13)
|
||||
WLED_GLOBAL byte apHide _INIT(0); // hidden AP SSID
|
||||
WLED_GLOBAL byte apBehavior _INIT(AP_BEHAVIOR_BOOT_NO_CONN); // access point opens when no connection after boot by default
|
||||
@ -377,9 +377,9 @@ WLED_GLOBAL uint8_t txPower _INIT(WIFI_POWER_8_5dBm);
|
||||
WLED_GLOBAL uint8_t txPower _INIT(WIFI_POWER_19_5dBm);
|
||||
#endif
|
||||
#endif
|
||||
#define WLED_WIFI_CONFIGURED (strlen(multiWiFi[0].clientSSID) >= 1 && strcmp(multiWiFi[0].clientSSID, DEFAULT_CLIENT_SSID) != 0)
|
||||
#define WLED_WIFI_CONFIGURED isWiFiConfigured()
|
||||
|
||||
#ifdef WLED_USE_ETHERNET
|
||||
#if defined(ARDUINO_ARCH_ESP32) && defined(WLED_USE_ETHERNET)
|
||||
#ifdef WLED_ETH_DEFAULT // default ethernet board type if specified
|
||||
WLED_GLOBAL int ethernetType _INIT(WLED_ETH_DEFAULT); // ethernet board type
|
||||
#else
|
||||
@ -582,6 +582,7 @@ WLED_GLOBAL uint16_t userVar0 _INIT(0), userVar1 _INIT(0); //available for use i
|
||||
// internal global variable declarations
|
||||
// wifi
|
||||
WLED_GLOBAL bool apActive _INIT(false);
|
||||
WLED_GLOBAL byte apClients _INIT(0);
|
||||
WLED_GLOBAL bool forceReconnect _INIT(false);
|
||||
WLED_GLOBAL unsigned long lastReconnectAttempt _INIT(0);
|
||||
WLED_GLOBAL bool interfacesInited _INIT(false);
|
||||
@ -1049,11 +1050,9 @@ public:
|
||||
|
||||
void beginStrip();
|
||||
void handleConnection();
|
||||
bool initEthernet(); // result is informational
|
||||
void initAP(bool resetAP = false);
|
||||
void initConnection();
|
||||
void initInterfaces();
|
||||
int8_t findWiFi(bool doScan = false);
|
||||
#if defined(STATUSLED)
|
||||
void handleStatusLED();
|
||||
#endif
|
||||
|
@ -110,7 +110,7 @@ void appendGPIOinfo(Print& settingsScript) {
|
||||
settingsScript.print(hardwareTX); // debug output (TX) pin
|
||||
firstPin = false;
|
||||
#endif
|
||||
#ifdef WLED_USE_ETHERNET
|
||||
#if defined(ARDUINO_ARCH_ESP32) && defined(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); }
|
||||
@ -178,9 +178,12 @@ void getSettingsJS(byte subPage, Print& settingsScript)
|
||||
char fpass[l+1]; //fill password field with ***
|
||||
fpass[l] = 0;
|
||||
memset(fpass,'*',l);
|
||||
settingsScript.printf_P(PSTR("addWiFi(\"%s\",\"%s\",0x%X,0x%X,0x%X);"),
|
||||
char bssid[13];
|
||||
fillMAC2Str(bssid, multiWiFi[n].bssid);
|
||||
settingsScript.printf_P(PSTR("addWiFi(\"%s\",\"%s\",\"%s\",0x%X,0x%X,0x%X);"),
|
||||
multiWiFi[n].clientSSID,
|
||||
fpass,
|
||||
bssid,
|
||||
(uint32_t) multiWiFi[n].staticIP, // explicit cast required as this is a struct
|
||||
(uint32_t) multiWiFi[n].staticGW,
|
||||
(uint32_t) multiWiFi[n].staticSN);
|
||||
@ -219,7 +222,7 @@ void getSettingsJS(byte subPage, Print& settingsScript)
|
||||
settingsScript.print(F("toggle('ESPNOW');")); // hide ESP-NOW setting
|
||||
#endif
|
||||
|
||||
#ifdef WLED_USE_ETHERNET
|
||||
#if defined(ARDUINO_ARCH_ESP32) && defined(WLED_USE_ETHERNET)
|
||||
printSetFormValue(settingsScript,PSTR("ETH"),ethernetType);
|
||||
#else
|
||||
//hide ethernet setting if not compiled in
|
||||
|
Loading…
x
Reference in New Issue
Block a user