Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 0 additions & 6 deletions platformio.ini
Original file line number Diff line number Diff line change
Expand Up @@ -368,7 +368,6 @@ build_flags = -g
-DARDUINO_ARCH_ESP32S2
-DCONFIG_IDF_TARGET_ESP32S2=1
-DARDUINO_USB_MSC_ON_BOOT=0 -DARDUINO_USB_DFU_ON_BOOT=0
-DCO
-DARDUINO_USB_MODE=0 ;; this flag is mandatory for ESP32-S2 !
;; please make sure that the following flags are properly set (to 0 or 1) by your board.json, or included in your custom platformio_override.ini entry:
;; ARDUINO_USB_CDC_ON_BOOT
Expand All @@ -388,7 +387,6 @@ build_flags = -g
-DARDUINO_ARCH_ESP32
-DARDUINO_ARCH_ESP32C3
-DCONFIG_IDF_TARGET_ESP32C3=1
-DCO
-DARDUINO_USB_MODE=1 ;; this flag is mandatory for ESP32-C3
;; please make sure that the following flags are properly set (to 0 or 1) by your board.json, or included in your custom platformio_override.ini entry:
;; ARDUINO_USB_CDC_ON_BOOT
Expand All @@ -411,7 +409,6 @@ build_flags = -g
-DARDUINO_ARCH_ESP32S3
-DCONFIG_IDF_TARGET_ESP32S3=1
-DARDUINO_USB_MSC_ON_BOOT=0 -DARDUINO_DFU_ON_BOOT=0
-DCO
;; please make sure that the following flags are properly set (to 0 or 1) by your board.json, or included in your custom platformio_override.ini entry:
;; ARDUINO_USB_MODE, ARDUINO_USB_CDC_ON_BOOT
${esp32_idf_V5.build_flags}
Expand All @@ -436,7 +433,6 @@ build_flags = -g
-DARDUINO_ARCH_ESP32C5
-DCONFIG_IDF_TARGET_ESP32C5=1
-D CONFIG_ASYNC_TCP_USE_WDT=0
-DCO
;;-D WLED_DISABLE_INFRARED ;; library not tested yet with -C5
-D WLED_DISABLE_ESPNOW ;; ToDO: ESP-NOW support not yet tested
;; please make sure that the following flags are properly set (to 0 or 1) by your board.json, or included in your custom platformio_override.ini entry:
Expand Down Expand Up @@ -468,7 +464,6 @@ build_flags = -g
-DARDUINO_ARCH_ESP32C6
-DCONFIG_IDF_TARGET_ESP32C6=1
-D CONFIG_ASYNC_TCP_USE_WDT=0
-DCO
;; -D WLED_DISABLE_INFRARED ;; library not tested yet with -C6
-D WLED_DISABLE_ESPNOW ;; ToDO: temporarily disabled, until we find a solution for esp-now build errors with -C6
-DARDUINO_USB_MODE=1 ;; this flag is - most likely - mandatory for ESP32-C6
Expand Down Expand Up @@ -499,7 +494,6 @@ build_flags = -g
-DCONFIG_IDF_TARGET_ESP32P4=1
-DBOARD_HAS_PSRAM ;; all P4 boards have PSRAM support
-D CONFIG_ASYNC_TCP_USE_WDT=0
-DCO
-DARDUINO_USB_MODE=1 ;; this flag is - most likely - mandatory for ESP32-P4
;;-D WLED_DISABLE_INFRARED ;; library not tested yet with -P4
-D WLED_DISABLE_ESPNOW ;; ESP-NOW is not possible on P4 (no native Wi-Fi radio)
Expand Down
39 changes: 39 additions & 0 deletions usermods/audioreactive/audio_reactive.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,47 @@

#include <driver/i2s.h>
#include <driver/adc.h>
#endif

// AI: below section was generated by an AI
#if defined(CONFIG_IDF_TARGET_ESP32) && (ESP_IDF_VERSION_MAJOR >= 5)
/*
* Bootloop workaround for classic ESP32 on esp-idf 5.x (WLED V5):
* AR still uses the legacy I2S driver (driver/i2s.h) which pulls in the legacy ADC driver.
* This causes a "abort" on boot as IDF V5 does not allow the legacy ADC driver to be used
* if any other function (i.e. Arduino's analogRead()) pulls in the new driver.
*
* The no-op stubs below satisfy exactly the symbols i2s_legacy.c.obj needs, so the real
* adc_i2s_deprecated.c.obj / adc_legacy.c.obj are never linked. This is safe because on IDF >= 5
* the analog built-in-ADC mic class (I2SAdcSource) is compiled out anyway (see audio_source.h),
* so these paths are never executed; digital I2S/PDM mics don't use them.
* Delete this block once the usermod is ported to the new I2S driver (driver/i2s_std.h).
*/
#include <esp_err.h>

// Stub: would configure the (removed) I2S built-in ADC mode. Never called for digital mics.
extern "C" esp_err_t adc_i2s_mode_init(int adcUnit, int channel) {
(void)adcUnit; (void)channel;
return ESP_ERR_NOT_SUPPORTED;
}

// Stub: would select the ADC data source for I2S DMA. Never called for digital mics.
extern "C" esp_err_t adc_set_i2s_data_source(int source) {
(void)source;
return ESP_ERR_NOT_SUPPORTED;
}

// Stub: would acquire ADC1 for DMA (built-in ADC mode only). Report failure if ever called.
extern "C" esp_err_t adc1_dma_mode_acquire(void) {
return ESP_ERR_NOT_SUPPORTED;
}

// Stub: would release the legacy ADC1 lock. No lock is ever taken, so nothing to do.
extern "C" esp_err_t adc1_lock_release(void) {
return ESP_OK;
}
#endif
// AI: end
Comment thread
DedeHai marked this conversation as resolved.

#if defined(ARDUINO_ARCH_ESP32) && (defined(WLED_DEBUG) || defined(SR_DEBUG))
#include <esp_timer.h>
Expand Down
175 changes: 160 additions & 15 deletions wled00/util.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1327,39 +1327,171 @@ String computeSHA1(const String& input) {
}

#ifdef ESP32
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)
#include "esp_adc_cal.h" // ToDO: deprecated API
//#include "esp_adc/adc_cali.h" // new API
//#include "esp_adc/adc_cali_scheme.h" // new API
#else
#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 0, 0)
#include "esp_adc_cal.h"
#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(4,4,7) // backwards compatibility patch
#define ADC_ATTEN_DB_12 ADC_ATTEN_DB_11
#endif
#else // IDF V5
#if CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32S3
// Functions to reproduce the legacy ADC calibration factors to be used as "salt" for device ID
// AI: below section was (mostly) generated by an AI, tested and confirmed working as intended on all targets
#if CONFIG_IDF_TARGET_ESP32
// ESP32 legacy ADC calibration factors Line Fitting + LUT (LUT is consolidated into a single value)
#include "soc/soc.h"
#include "soc/efuse_reg.h"
#define VREF_REG EFUSE_BLK0_RDATA4_REG
#define VREF_MASK 0x1F
#define VREF_STEP_SIZE 7
#define VREF_OFFSET 1100
#define TP_REG EFUSE_BLK3_RDATA3_REG
#define BLK3_RESERVED_REG EFUSE_BLK0_RDATA3_REG
#define TP_LOW1_OFFSET 278
#define TP_LOW_MASK 0x7F
#define TP_LOW_VOLTAGE 150
#define TP_HIGH1_OFFSET 3265
#define TP_HIGH_MASK 0x1FF
#define TP_HIGH_VOLTAGE 850
#define TP_STEP_SIZE 4
#define ADC_12_BIT_RES 4096
// Hardcoded legacy constants for ADC_ATTEN_DB_12 (Index 3)
constexpr uint32_t TP_ATTEN_SCALE_DB12 = 224310;
constexpr uint32_t TP_ATTEN_OFFSET_DB12 = 54;
constexpr uint32_t VREF_ATTEN_SCALE_DB12 = 196602;
constexpr uint32_t VREF_ATTEN_OFFSET_DB12 = 142;
// Compile-time pre-calculated XOR sums of the first 8 elements of the legacy 20-point LUTs (replaces the V4 XOR for-loop)
constexpr uint32_t LUT_ADC1_LOW_XOR_8 = 2240 ^ 2297 ^ 2352 ^ 2405 ^ 2457 ^ 2512 ^ 2564 ^ 2616;
constexpr uint32_t LUT_ADC1_HIGH_XOR_8 = 2667 ^ 2706 ^ 2745 ^ 2780 ^ 2813 ^ 2844 ^ 2873 ^ 2901;

static inline int32_t decodeBits(uint32_t bits, uint32_t mask, bool twosComplement) {
if (bits & (~(mask >> 1) & mask)) {
return twosComplement ? -(int32_t)(((~bits) + 1) & (mask >> 1)) : -(int32_t)(bits & (mask >> 1));
}
return (int32_t)(bits & (mask >> 1));
}

static void getLegacyAdcCoeffs(uint32_t *coeff_a, uint32_t *coeff_b) {
bool tpBurned = (REG_GET_FIELD(BLK3_RESERVED_REG, EFUSE_RD_BLK3_PART_RESERVE) != 0) &&
(REG_GET_FIELD(TP_REG, EFUSE_RD_ADC1_TP_LOW) != 0) &&
(REG_GET_FIELD(TP_REG, EFUSE_RD_ADC1_TP_HIGH) != 0);

if (tpBurned) {
uint32_t lowBits = REG_GET_FIELD(TP_REG, EFUSE_RD_ADC1_TP_LOW);
uint32_t highBits = REG_GET_FIELD(TP_REG, EFUSE_RD_ADC1_TP_HIGH);

uint32_t low = TP_LOW1_OFFSET + decodeBits(lowBits, TP_LOW_MASK, true) * TP_STEP_SIZE;
uint32_t high = TP_HIGH1_OFFSET + decodeBits(highBits, TP_HIGH_MASK, true) * TP_STEP_SIZE;

uint32_t delta_x = high - low;
uint32_t delta_v = TP_HIGH_VOLTAGE - TP_LOW_VOLTAGE;

*coeff_a = (delta_v * TP_ATTEN_SCALE_DB12 + delta_x / 2) / delta_x;
*coeff_b = TP_HIGH_VOLTAGE - ((delta_v * high + delta_x / 2) / delta_x) + TP_ATTEN_OFFSET_DB12;
} else {
bool vrefBurned = (REG_GET_FIELD(VREF_REG, EFUSE_RD_ADC_VREF) != 0);
uint32_t vrefBits = REG_GET_FIELD(VREF_REG, EFUSE_ADC_VREF);
uint32_t vref = vrefBurned ? (VREF_OFFSET + decodeBits(vrefBits, VREF_MASK, false) * VREF_STEP_SIZE) : 1100;

*coeff_a = (vref * VREF_ATTEN_SCALE_DB12) / ADC_12_BIT_RES;
*coeff_b = VREF_ATTEN_OFFSET_DB12;
}
}
#endif

#if CONFIG_IDF_TARGET_ESP32S2
#include "hal/adc_types.h"
#include "esp_efuse_rtc_table.h"
// note: this is confirmed working on v2 calib S2 chips, had no v1 S2 to test
static void getLegacyAdcCoeffs(uint32_t *coeff_a, uint32_t *coeff_b) {
*coeff_a = 0;
*coeff_b = 0;
static const uint32_t v_high = 2000; // Hardcoded legacy constant for ADC_ATTEN_DB_12
const uint32_t v_low = 250;
const int atten = ADC_ATTEN_DB_12; // 3

int version = esp_efuse_rtc_table_read_calib_version();
if (version == 1) {
int tag_l = esp_efuse_rtc_table_get_tag(1, ADC_UNIT_1, atten, RTCCALIB_V1_PARAM_VLOW);
int tag_h = esp_efuse_rtc_table_get_tag(1, ADC_UNIT_1, atten, RTCCALIB_V1_PARAM_VHIGH);
uint32_t low = (uint32_t)esp_efuse_rtc_table_get_parsed_efuse_value(tag_l, false);
uint32_t high = (uint32_t)esp_efuse_rtc_table_get_parsed_efuse_value(tag_h, false);
uint32_t delta = high - low;
if (delta == 0) return;
// identical operand types/order to v4.4 characterize_using_two_point()
*coeff_a = 65536u * (v_high - v_low) / delta;
*coeff_b = 1024u * (v_low * high - v_high * low) / delta; // non-zero on v1 chips!
} else if (version == 2) {
int tag = esp_efuse_rtc_table_get_tag(2, ADC_UNIT_1, atten, RTCCALIB_V2_PARAM_VHIGH);
uint32_t high = (uint32_t)esp_efuse_rtc_table_get_parsed_efuse_value(tag, false);
if (high != 0) *coeff_a = 65536u * v_high / high;
// v4.4: coeff_b = 0
}
}
#endif
// ESP32-S3 & ESP32-C3 coefficient extraction matching IDF V4
#if CONFIG_IDF_TARGET_ESP32S3 || CONFIG_IDF_TARGET_ESP32C3
#include "hal/adc_types.h"
#include "esp_efuse_rtc_calib.h"

static void getLegacyAdcCoeffs(uint32_t *coeff_a, uint32_t *coeff_b) {
*coeff_a = 0;
*coeff_b = 0; // v4.4: coeff_b = 0 on both targets
int ver = esp_efuse_rtc_calib_get_ver();
if (ver != 1) return; // v4.4: ESP_ADC_CAL_VAL_NOT_SUPPORTED

uint32_t digi = 0, voltage = 0;
// C3 ignores the unit arg (V1 calib exists only for ADC1); S3 uses it
esp_err_t ret = esp_efuse_rtc_calib_get_cal_voltage(ver, ADC_UNIT_1, ADC_ATTEN_DB_12, &digi, &voltage);
if (ret != ESP_OK || digi == 0) return;

#if CONFIG_IDF_TARGET_ESP32S3
*coeff_a = 1000000u * voltage / digi; // v4.4 S3: coeff_a_scaling = 1000000
#else
*coeff_a = 65536u * voltage / digi; // v4.4 C3: coeff_a_scaling = 65536
#endif
}
#endif // S3 or C3
#else // not ESP32, S2, C3 or S3 and V5: read data from efuse BLOCK2 as salt
#include "esp_efuse.h"
uint32_t get_calib_entropy(void) {
// Bytes 0-15 are OPTIONAL_UNIQUE_ID, which may be all-zero (undocumented feature)
// Bytes 16+ fall in the calibration-trim region (temp calib, ADC init codes, etc.),
// which IS reliably burned on every chip and varies die-to-die due to manufacturing variance.
uint8_t sys_data[32] = {0};
esp_efuse_read_block(EFUSE_BLK2, sys_data, 0, sizeof(sys_data) * 8); // size is in bits
uint32_t entropy = 0;
for (int i = 16; i < 32; i++) entropy = entropy * 16777619u ^ sys_data[i];
return entropy;
}
#endif // ESP32, S2, C3, S3
#endif // IDF V5
// AI: end

String generateDeviceFingerprint() {
uint32_t fp[2] = {0, 0}; // create 64 bit fingerprint
esp_chip_info_t chip_info;
esp_chip_info(&chip_info);
esp_efuse_mac_get_default((uint8_t*)fp);
fp[1] ^= ESP.getFlashChipSize();
#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 0, 0)
#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 0, 0)
fp[0] ^= chip_info.full_revision | (chip_info.model << 16);
#else
#else
fp[0] ^= chip_info.revision | (chip_info.model << 16);
#endif
#endif

#if CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32S3
// mix in ADC calibration data - legacy adc calibration API is not supported on new MCUs (-C5, -C6, -C61, -P4)
#if CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32S3
#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 0, 0)
// mix in ADC calibration data - legacy adc calibration API is not supported on new MCUs (-C5, -C6, -C61, -P4)
esp_adc_cal_characteristics_t ch;
#if (SOC_ADC_MAX_BITWIDTH == 13) || (CONFIG_SOC_ADC_RTC_MAX_BITWIDTH == 13) // S2 has 13 bit ADC
constexpr auto myBIT_WIDTH = ADC_WIDTH_BIT_13;
#else
constexpr auto myBIT_WIDTH = ADC_WIDTH_BIT_12;
#endif
esp_adc_cal_characterize(ADC_UNIT_1, ADC_ATTEN_DB_12, myBIT_WIDTH, 1100, &ch);
esp_adc_cal_characterize(ADC_UNIT_1, ADC_ATTEN_DB_12, myBIT_WIDTH, 1100, &ch); // note: deprecated in IDF V5 hence the complicated workaround
fp[0] ^= ch.coeff_a;
fp[1] ^= ch.coeff_b;
// note: these curves are LUT, hardcoded and only on ESP32
if (ch.low_curve) {
for (int i = 0; i < 8; i++) {
fp[0] ^= ch.low_curve[i];
Expand All @@ -1370,11 +1502,24 @@ String generateDeviceFingerprint() {
fp[1] ^= ch.high_curve[i];
}
}
#else
// some extra salt, instead of ADC calibration
fp[0] ^= chip_info.features | chip_info.cores << 16;
#else // V5
uint32_t coeff_a = 0, coeff_b = 0;
// get equivalent to V4 adc coefficients without using the deprecated esp_adc_cal_characterize()
getLegacyAdcCoeffs(&coeff_a, &coeff_b); // use ADC calibration coefficients
fp[0] ^= coeff_a;
fp[1] ^= coeff_b; // note: is zero on S3 and C3 as well as S2 "v2"
#if CONFIG_IDF_TARGET_ESP32
// Mix in the pre-calculated LUT XOR sums to match pre 17.0 fingerprint
fp[0] ^= LUT_ADC1_LOW_XOR_8;
fp[1] ^= LUT_ADC1_HIGH_XOR_8;
#endif // ESP32
#endif // V4 or V5

#else // ESP32 family but not classic ESP32, S2, S3 or C3
fp[0] ^= get_calib_entropy(); // add BLOCK2 efuse values containing calibration data
fp[0] ^= chip_info.features | chip_info.cores << 16; // some extra salt
fp[1] ^= ESP.getFlashSourceFrequencyMHz() | ESP.getFlashClockDivider() << 8 ;
#endif
#endif
char fp_string[17]; // 16 hex chars + null terminator
sprintf(fp_string, "%08X%08X", fp[1], fp[0]);
return String(fp_string);
Expand Down