mirror of
https://github.com/wled/WLED.git
synced 2025-11-23 01:37:33 +00:00
Compare commits
20 Commits
copilot/ad
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
730205ded5 | ||
|
|
90ca6ccf8b | ||
|
|
5b3cc753e2 | ||
|
|
4615eb8258 | ||
|
|
9b787e13d1 | ||
|
|
3dbcd79b3c | ||
|
|
a1aac452de | ||
|
|
b90fbe6b1a | ||
|
|
1860258deb | ||
|
|
a2935b87c2 | ||
|
|
c1ce1d8aba | ||
|
|
54b7dfe04b | ||
|
|
4a33809d66 | ||
|
|
336e074b4a | ||
|
|
aaad450175 | ||
|
|
85b3c5d91b | ||
|
|
4db86ebf7f | ||
|
|
65c43b5224 | ||
|
|
c649ec1d8c | ||
|
|
66ffd65476 |
10
tools/partitions-16MB_spiffs-tinyuf2.csv
Normal file
10
tools/partitions-16MB_spiffs-tinyuf2.csv
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
# ESP-IDF Partition Table
|
||||||
|
# Name, Type, SubType, Offset, Size, Flags
|
||||||
|
# bootloader.bin,, 0x1000, 32K
|
||||||
|
# partition table,, 0x8000, 4K
|
||||||
|
nvs, data, nvs, 0x9000, 20K,
|
||||||
|
otadata, data, ota, 0xe000, 8K,
|
||||||
|
ota_0, app, ota_0, 0x10000, 2048K,
|
||||||
|
ota_1, app, ota_1, 0x210000, 2048K,
|
||||||
|
uf2, app, factory,0x410000, 256K,
|
||||||
|
spiffs, data, spiffs, 0x450000, 11968K,
|
||||||
|
11
tools/partitions-4MB_spiffs-tinyuf2.csv
Normal file
11
tools/partitions-4MB_spiffs-tinyuf2.csv
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
# ESP-IDF Partition Table
|
||||||
|
# Name, Type, SubType, Offset, Size, Flags
|
||||||
|
# bootloader.bin,, 0x1000, 32K
|
||||||
|
# partition table, 0x8000, 4K
|
||||||
|
|
||||||
|
nvs, data, nvs, 0x9000, 20K,
|
||||||
|
otadata, data, ota, 0xe000, 8K,
|
||||||
|
ota_0, 0, ota_0, 0x10000, 1408K,
|
||||||
|
ota_1, 0, ota_1, 0x170000, 1408K,
|
||||||
|
uf2, app, factory,0x2d0000, 256K,
|
||||||
|
spiffs, data, spiffs, 0x310000, 960K,
|
||||||
|
10
tools/partitions-8MB_spiffs-tinyuf2.csv
Normal file
10
tools/partitions-8MB_spiffs-tinyuf2.csv
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
# ESP-IDF Partition Table
|
||||||
|
# Name, Type, SubType, Offset, Size, Flags
|
||||||
|
# bootloader.bin,, 0x1000, 32K
|
||||||
|
# partition table,, 0x8000, 4K
|
||||||
|
nvs, data, nvs, 0x9000, 20K,
|
||||||
|
otadata, data, ota, 0xe000, 8K,
|
||||||
|
ota_0, app, ota_0, 0x10000, 2048K,
|
||||||
|
ota_1, app, ota_1, 0x210000, 2048K,
|
||||||
|
uf2, app, factory,0x410000, 256K,
|
||||||
|
spiffs, data, spiffs, 0x450000, 3776K,
|
||||||
|
@@ -1227,7 +1227,6 @@ class AudioReactive : public Usermod {
|
|||||||
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32S3)
|
#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
|
// ADC over I2S is only possible on "classic" ESP32
|
||||||
case 0:
|
case 0:
|
||||||
default:
|
|
||||||
DEBUGSR_PRINTLN(F("AR: Analog Microphone (left channel only)."));
|
DEBUGSR_PRINTLN(F("AR: Analog Microphone (left channel only)."));
|
||||||
audioSource = new I2SAdcSource(SAMPLE_RATE, BLOCK_SIZE);
|
audioSource = new I2SAdcSource(SAMPLE_RATE, BLOCK_SIZE);
|
||||||
delay(100);
|
delay(100);
|
||||||
@@ -1235,10 +1234,25 @@ class AudioReactive : public Usermod {
|
|||||||
if (audioSource) audioSource->initialize(audioPin);
|
if (audioSource) audioSource->initialize(audioPin);
|
||||||
break;
|
break;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
case 254: // dummy "network receive only" mode
|
||||||
|
if (audioSource) delete audioSource; audioSource = nullptr;
|
||||||
|
disableSoundProcessing = true;
|
||||||
|
audioSyncEnabled = 2; // force udp sound receive mode
|
||||||
|
enabled = true;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 255: // 255 = -1 = no audio source
|
||||||
|
// falls through to default
|
||||||
|
default:
|
||||||
|
if (audioSource) delete audioSource; audioSource = nullptr;
|
||||||
|
disableSoundProcessing = true;
|
||||||
|
enabled = false;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
delay(250); // give microphone enough time to initialise
|
delay(250); // give microphone enough time to initialise
|
||||||
|
|
||||||
if (!audioSource) enabled = false; // audio failed to initialise
|
if (!audioSource && (dmType != 254)) enabled = false;// audio failed to initialise
|
||||||
#endif
|
#endif
|
||||||
if (enabled) onUpdateBegin(false); // create FFT task, and initialize network
|
if (enabled) onUpdateBegin(false); // create FFT task, and initialize network
|
||||||
|
|
||||||
|
|||||||
@@ -1687,10 +1687,11 @@ void WS2812FX::setTransitionMode(bool t) {
|
|||||||
// rare circumstances are: setting FPS to high number (i.e. 120) and have very slow effect that will need more
|
// rare circumstances are: setting FPS to high number (i.e. 120) and have very slow effect that will need more
|
||||||
// time than 2 * _frametime (1000/FPS) to draw content
|
// time than 2 * _frametime (1000/FPS) to draw content
|
||||||
void WS2812FX::waitForIt() {
|
void WS2812FX::waitForIt() {
|
||||||
unsigned long maxWait = millis() + 2*getFrameTime() + 100; // TODO: this needs a proper fix for timeout! see #4779
|
unsigned long waitStart = millis();
|
||||||
while (isServicing() && maxWait > millis()) delay(1);
|
unsigned long maxWait = 2*getFrameTime() + 100; // TODO: this needs a proper fix for timeout! see #4779
|
||||||
|
while (isServicing() && (millis() - waitStart < maxWait)) delay(1); // safe even when millis() rolls over
|
||||||
#ifdef WLED_DEBUG
|
#ifdef WLED_DEBUG
|
||||||
if (millis() >= maxWait) DEBUG_PRINTLN(F("Waited for strip to finish servicing."));
|
if (millis()-waitStart >= maxWait) DEBUG_PRINTLN(F("Waited for strip to finish servicing."));
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -213,7 +213,7 @@ function createTop(element, editor){
|
|||||||
function httpPostCb(st,resp){
|
function httpPostCb(st,resp){
|
||||||
if (st!=200) alert("ERROR "+st+": "+resp);
|
if (st!=200) alert("ERROR "+st+": "+resp);
|
||||||
else {
|
else {
|
||||||
alert("Upload successful!");
|
showToast("Upload successful!");
|
||||||
refreshTree();
|
refreshTree();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -264,7 +264,7 @@ function createTree(element, editor){
|
|||||||
leaf.textContent=name;
|
leaf.textContent=name;
|
||||||
var span = cE("span");
|
var span = cE("span");
|
||||||
span.style.cssText = "font-size: 14px; color: #aaa; margin-left: 8px;";
|
span.style.cssText = "font-size: 14px; color: #aaa; margin-left: 8px;";
|
||||||
span.textContent = (size / 1024).toFixed(1) + "KB";
|
span.textContent = (size > 0 ? Math.max(0.1, (size / 1024)).toFixed(1) : 0) + "KB"; // show size in KB, minimum 0.1 to not show 0KB for small files
|
||||||
leaf.appendChild(span);
|
leaf.appendChild(span);
|
||||||
leaf.onmouseover=function(){ leaf.style.background="#333"; };
|
leaf.onmouseover=function(){ leaf.style.background="#333"; };
|
||||||
leaf.onmouseout=function(){ leaf.style.background=""; };
|
leaf.onmouseout=function(){ leaf.style.background=""; };
|
||||||
@@ -377,13 +377,13 @@ function prettyLedmap(json){
|
|||||||
rows.push(" " + obj.map.slice(i, i + width).map(pad).join(", "));
|
rows.push(" " + obj.map.slice(i, i + width).map(pad).join(", "));
|
||||||
}
|
}
|
||||||
|
|
||||||
let pretty = "{\n \"map\": [\n" + rows.join(",\n") + "\n ]";
|
let pretty = "{\n";
|
||||||
for (let k of Object.keys(obj)) {
|
for (let k of Object.keys(obj)) {
|
||||||
if (k !== "map") {
|
if (k !== "map") {
|
||||||
pretty += ",\n \"" + k + "\": " + JSON.stringify(obj[k]);
|
pretty += " \"" + k + "\": " + JSON.stringify(obj[k]) + ",\n"; // print all keys first (speeds up loading)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pretty += "\n}";
|
pretty += " \"map\": [\n" + rows.join(",\n") + "\n ]\n}";
|
||||||
return pretty;
|
return pretty;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return json;
|
return json;
|
||||||
@@ -493,7 +493,7 @@ function createEditor(element,file){
|
|||||||
req.add("POST","/upload",fd,function(st,resp){
|
req.add("POST","/upload",fd,function(st,resp){
|
||||||
if (st!=200) alert("ERROR "+st+": "+resp);
|
if (st!=200) alert("ERROR "+st+": "+resp);
|
||||||
else {
|
else {
|
||||||
alert("File saved successfully!");
|
showToast("File saved");
|
||||||
refreshTree();
|
refreshTree();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -567,10 +567,18 @@ function onBodyLoad(){
|
|||||||
var editor=createEditor("editor",vars.file);
|
var editor=createEditor("editor",vars.file);
|
||||||
globalTree=createTree("tree",editor);
|
globalTree=createTree("tree",editor);
|
||||||
createTop("top",editor);
|
createTop("top",editor);
|
||||||
|
// Add Ctrl+S / Cmd+S override to save the file
|
||||||
|
document.addEventListener('keydown', function(e) {
|
||||||
|
if ((e.ctrlKey || e.metaKey) && e.key === 's') {
|
||||||
|
e.preventDefault();
|
||||||
|
editor.save();
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
</head>
|
</head>
|
||||||
<body onload="onBodyLoad()">
|
<body onload="onBodyLoad()">
|
||||||
|
<div id="toast"></div>
|
||||||
<div id="loader"><div class="loader"></div></div>
|
<div id="loader"><div class="loader"></div></div>
|
||||||
<div id="top"></div>
|
<div id="top"></div>
|
||||||
<div style="flex:1;position:relative">
|
<div style="flex:1;position:relative">
|
||||||
|
|||||||
@@ -401,6 +401,8 @@ uint8_t extractModeSlider(uint8_t mode, uint8_t slider, char *dest, uint8_t maxL
|
|||||||
int16_t extractModeDefaults(uint8_t mode, const char *segVar);
|
int16_t extractModeDefaults(uint8_t mode, const char *segVar);
|
||||||
void checkSettingsPIN(const char *pin);
|
void checkSettingsPIN(const char *pin);
|
||||||
uint16_t crc16(const unsigned char* data_p, size_t length);
|
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 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);
|
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);
|
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);
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
#include "wled.h"
|
#include "wled.h"
|
||||||
|
|
||||||
|
|
||||||
#define JSON_PATH_STATE 1
|
#define JSON_PATH_STATE 1
|
||||||
#define JSON_PATH_INFO 2
|
#define JSON_PATH_INFO 2
|
||||||
#define JSON_PATH_STATE_INFO 3
|
#define JSON_PATH_STATE_INFO 3
|
||||||
@@ -690,6 +691,7 @@ void serializeState(JsonObject root, bool forPreset, bool includeBri, bool segme
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void serializeInfo(JsonObject root)
|
void serializeInfo(JsonObject root)
|
||||||
{
|
{
|
||||||
root[F("ver")] = versionString;
|
root[F("ver")] = versionString;
|
||||||
@@ -697,6 +699,7 @@ void serializeInfo(JsonObject root)
|
|||||||
root[F("cn")] = F(WLED_CODENAME);
|
root[F("cn")] = F(WLED_CODENAME);
|
||||||
root[F("release")] = releaseString;
|
root[F("release")] = releaseString;
|
||||||
root[F("repo")] = repoString;
|
root[F("repo")] = repoString;
|
||||||
|
root[F("deviceId")] = getDeviceId();
|
||||||
|
|
||||||
JsonObject leds = root.createNestedObject(F("leds"));
|
JsonObject leds = root.createNestedObject(F("leds"));
|
||||||
leds[F("count")] = strip.getLengthTotal();
|
leds[F("count")] = strip.getLengthTotal();
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
#include "const.h"
|
#include "const.h"
|
||||||
#ifdef ESP8266
|
#ifdef ESP8266
|
||||||
#include "user_interface.h" // for bootloop detection
|
#include "user_interface.h" // for bootloop detection
|
||||||
|
#include <Hash.h> // for SHA1 on ESP8266
|
||||||
#else
|
#else
|
||||||
#include <Update.h>
|
#include <Update.h>
|
||||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 4, 0)
|
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 4, 0)
|
||||||
@@ -10,6 +11,8 @@
|
|||||||
#elif ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(3, 3, 0)
|
#elif ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(3, 3, 0)
|
||||||
#include "soc/rtc.h"
|
#include "soc/rtc.h"
|
||||||
#endif
|
#endif
|
||||||
|
#include "mbedtls/sha1.h" // for SHA1 on ESP32
|
||||||
|
#include "esp_efuse.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
@@ -1125,4 +1128,96 @@ uint8_t perlin8(uint16_t x, uint16_t y) {
|
|||||||
|
|
||||||
uint8_t perlin8(uint16_t x, uint16_t y, uint16_t z) {
|
uint8_t perlin8(uint16_t x, uint16_t y, uint16_t z) {
|
||||||
return (((perlin3D_raw((uint32_t)x << 8, (uint32_t)y << 8, (uint32_t)z << 8, true) * 2015) >> 10) + 33168) >> 8; //scale to 16 bit, offset, then scale to 8bit
|
return (((perlin3D_raw((uint32_t)x << 8, (uint32_t)y << 8, (uint32_t)z << 8, true) * 2015) >> 10) + 33168) >> 8; //scale to 16 bit, offset, then scale to 8bit
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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
|
||||||
|
static String dump_raw_block(esp_efuse_block_t block)
|
||||||
|
{
|
||||||
|
const int WORDS = 8; // ESP32: 8×32-bit words per block i.e. 256bits
|
||||||
|
uint32_t buf[WORDS] = {0};
|
||||||
|
|
||||||
|
const esp_efuse_desc_t d = {
|
||||||
|
.efuse_block = block,
|
||||||
|
.bit_start = 0,
|
||||||
|
.bit_count = WORDS * 32
|
||||||
|
};
|
||||||
|
const esp_efuse_desc_t* field[2] = { &d, NULL };
|
||||||
|
|
||||||
|
esp_err_t err = esp_efuse_read_field_blob(field, buf, WORDS * 32);
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
String result = "";
|
||||||
|
for (const unsigned int i : buf) {
|
||||||
|
char line[32];
|
||||||
|
sprintf(line, "0x%08X", i);
|
||||||
|
result += line;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
// Generate a device ID based on SHA1 hash of MAC address salted with "WLED"
|
||||||
|
// Returns: original SHA1 + last 2 chars of double-hashed SHA1 (42 chars total)
|
||||||
|
String getDeviceId() {
|
||||||
|
static String cachedDeviceId = "";
|
||||||
|
if (cachedDeviceId.length() > 0) return cachedDeviceId;
|
||||||
|
|
||||||
|
uint8_t mac[6];
|
||||||
|
WiFi.macAddress(mac);
|
||||||
|
char macStr[18];
|
||||||
|
sprintf(macStr, "%02x:%02x:%02x:%02x:%02x:%02x", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
|
||||||
|
|
||||||
|
// 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
|
||||||
|
#ifdef ESP8266
|
||||||
|
String deviceString = String(macStr) + "WLED" + ESP.getFlashChipId();
|
||||||
|
#else
|
||||||
|
String deviceString = String(macStr) + "WLED" + ESP.getChipModel() + ESP.getChipRevision();
|
||||||
|
deviceString += dump_raw_block(EFUSE_BLK0);
|
||||||
|
deviceString += dump_raw_block(EFUSE_BLK1);
|
||||||
|
deviceString += dump_raw_block(EFUSE_BLK2);
|
||||||
|
deviceString += dump_raw_block(EFUSE_BLK3);
|
||||||
|
#endif
|
||||||
|
String firstHash = computeSHA1(deviceString);
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user