Merge remote-tracking branch 'upstream/dev' into color-gradient-integer-math

This commit is contained in:
J. Nick Koston
2026-02-27 08:33:17 -10:00
26 changed files with 172 additions and 52 deletions
+1 -1
View File
@@ -214,4 +214,4 @@ async def to_code(config):
cg.add_define("USE_AUDIO_MP3_SUPPORT")
if data.opus_support:
cg.add_define("USE_AUDIO_OPUS_SUPPORT")
add_idf_component(name="esphome/micro-opus", ref="0.3.3")
add_idf_component(name="esphome/micro-opus", ref="0.3.4")
+1
View File
@@ -23,6 +23,7 @@ namespace esphome {
void HOT yield() { vPortYield(); }
uint32_t IRAM_ATTR HOT millis() { return (uint32_t) (esp_timer_get_time() / 1000ULL); }
uint64_t HOT millis_64() { return static_cast<uint64_t>(esp_timer_get_time()) / 1000ULL; }
void HOT delay(uint32_t ms) { vTaskDelay(ms / portTICK_PERIOD_MS); }
uint32_t IRAM_ATTR HOT micros() { return (uint32_t) esp_timer_get_time(); }
void IRAM_ATTR HOT delayMicroseconds(uint32_t us) { delay_microseconds_safe(us); }
@@ -23,6 +23,7 @@ CONF_D1_PIN = "d1_pin"
CONF_D2_PIN = "d2_pin"
CONF_D3_PIN = "d3_pin"
CONF_SLOT = "slot"
CONF_SDIO_FREQUENCY = "sdio_frequency"
CONFIG_SCHEMA = cv.All(
cv.Schema(
@@ -37,6 +38,9 @@ CONFIG_SCHEMA = cv.All(
cv.Required(CONF_D3_PIN): pins.internal_gpio_output_pin_number,
cv.Required(CONF_RESET_PIN): pins.internal_gpio_output_pin_number,
cv.Optional(CONF_SLOT, default=1): cv.int_range(min=0, max=1),
cv.Optional(CONF_SDIO_FREQUENCY, default="40MHz"): cv.All(
cv.frequency, cv.Range(min=400e3, max=50e6)
),
}
),
)
@@ -91,6 +95,10 @@ async def to_code(config):
config[CONF_D3_PIN],
)
esp32.add_idf_sdkconfig_option("CONFIG_ESP_HOSTED_CUSTOM_SDIO_PINS", True)
esp32.add_idf_sdkconfig_option(
"CONFIG_ESP_HOSTED_SDIO_CLOCK_FREQ_KHZ",
int(config[CONF_SDIO_FREQUENCY] // 1000),
)
framework_ver: cv.Version = CORE.data[KEY_CORE][KEY_FRAMEWORK_VERSION]
os.environ["ESP_IDF_VERSION"] = f"{framework_ver.major}.{framework_ver.minor}"
+2
View File
@@ -3,6 +3,7 @@
#include "core.h"
#include "esphome/core/defines.h"
#include "esphome/core/hal.h"
#include "esphome/core/application.h"
#include "esphome/core/helpers.h"
#include "preferences.h"
#include <Arduino.h>
@@ -16,6 +17,7 @@ namespace esphome {
void HOT yield() { ::yield(); }
uint32_t IRAM_ATTR HOT millis() { return ::millis(); }
uint64_t millis_64() { return App.scheduler.millis_64_impl_(::millis()); }
void HOT delay(uint32_t ms) { ::delay(ms); }
uint32_t IRAM_ATTR HOT micros() { return ::micros(); }
void IRAM_ATTR HOT delayMicroseconds(uint32_t us) { delay_microseconds_safe(us); }
+2
View File
@@ -1,5 +1,6 @@
#ifdef USE_HOST
#include "esphome/core/application.h"
#include "esphome/core/hal.h"
#include "esphome/core/helpers.h"
#include "preferences.h"
@@ -19,6 +20,7 @@ uint32_t IRAM_ATTR HOT millis() {
uint32_t ms = round(spec.tv_nsec / 1e6);
return ((uint32_t) seconds) * 1000U + ms;
}
uint64_t millis_64() { return App.scheduler.millis_64_impl_(millis()); }
void HOT delay(uint32_t ms) {
struct timespec ts;
ts.tv_sec = ms / 1000;
+2
View File
@@ -3,6 +3,7 @@
#include "core.h"
#include "esphome/core/defines.h"
#include "esphome/core/hal.h"
#include "esphome/core/application.h"
#include "esphome/core/helpers.h"
#include "preferences.h"
@@ -13,6 +14,7 @@ namespace esphome {
void HOT yield() { ::yield(); }
uint32_t IRAM_ATTR HOT millis() { return ::millis(); }
uint64_t millis_64() { return App.scheduler.millis_64_impl_(::millis()); }
uint32_t IRAM_ATTR HOT micros() { return ::micros(); }
void HOT delay(uint32_t ms) { ::delay(ms); }
void IRAM_ATTR HOT delayMicroseconds(uint32_t us) { ::delayMicroseconds(us); }
+1 -1
View File
@@ -163,7 +163,7 @@ async def to_code(config):
cg.add_library("LEAmDNS", None)
if CORE.is_esp32:
add_idf_component(name="espressif/mdns", ref="1.9.1")
add_idf_component(name="espressif/mdns", ref="1.10.0")
cg.add_define("USE_MDNS")
+2
View File
@@ -3,6 +3,7 @@
#include "core.h"
#include "esphome/core/defines.h"
#include "esphome/core/hal.h"
#include "esphome/core/application.h"
#include "esphome/core/helpers.h"
#include "hardware/watchdog.h"
@@ -11,6 +12,7 @@ namespace esphome {
void HOT yield() { ::yield(); }
uint32_t IRAM_ATTR HOT millis() { return ::millis(); }
uint64_t millis_64() { return App.scheduler.millis_64_impl_(::millis()); }
void HOT delay(uint32_t ms) { ::delay(ms); }
uint32_t IRAM_ATTR HOT micros() { return ::micros(); }
void IRAM_ATTR HOT delayMicroseconds(uint32_t us) { delay_microseconds_safe(us); }
+17
View File
@@ -21,6 +21,7 @@ CONF_ON_SAFE_MODE = "on_safe_mode"
safe_mode_ns = cg.esphome_ns.namespace("safe_mode")
SafeModeComponent = safe_mode_ns.class_("SafeModeComponent", cg.Component)
SafeModeTrigger = safe_mode_ns.class_("SafeModeTrigger", automation.Trigger.template())
MarkSuccessfulAction = safe_mode_ns.class_("MarkSuccessfulAction", automation.Action)
def _remove_id_if_disabled(value):
@@ -53,6 +54,22 @@ CONFIG_SCHEMA = cv.All(
)
@automation.register_action(
"safe_mode.mark_successful",
MarkSuccessfulAction,
cv.Schema(
{
cv.GenerateID(): cv.use_id(SafeModeComponent),
}
),
)
async def safe_mode_mark_successful_to_code(config, action_id, template_arg, args):
parent = await cg.get_variable(config[CONF_ID])
var = cg.new_Pvariable(action_id, template_arg)
cg.add(var.set_parent(parent))
return var
@coroutine_with_priority(CoroPriority.APPLICATION)
async def to_code(config):
if not config[CONF_DISABLED]:
+8 -6
View File
@@ -1,20 +1,22 @@
#pragma once
#include "esphome/core/defines.h"
#ifdef USE_SAFE_MODE_CALLBACK
#include "safe_mode.h"
#include "esphome/core/automation.h"
#include "safe_mode.h"
namespace esphome::safe_mode {
#ifdef USE_SAFE_MODE_CALLBACK
class SafeModeTrigger final : public Trigger<> {
public:
explicit SafeModeTrigger(SafeModeComponent *parent) {
parent->add_on_safe_mode_callback([this]() { trigger(); });
}
};
#endif // USE_SAFE_MODE_CALLBACK
template<typename... Ts> class MarkSuccessfulAction : public Action<Ts...>, public Parented<SafeModeComponent> {
public:
void play(const Ts &...x) override { this->parent_->mark_successful(); }
};
} // namespace esphome::safe_mode
#endif // USE_SAFE_MODE_CALLBACK
+12 -8
View File
@@ -63,18 +63,22 @@ void SafeModeComponent::dump_config() {
float SafeModeComponent::get_setup_priority() const { return setup_priority::AFTER_WIFI; }
void SafeModeComponent::mark_successful() {
this->clean_rtc();
this->boot_successful_ = true;
#if defined(USE_ESP32) && defined(USE_OTA_ROLLBACK)
// Mark OTA partition as valid to prevent rollback
esp_ota_mark_app_valid_cancel_rollback();
#endif
// Disable loop since we no longer need to check
this->disable_loop();
}
void SafeModeComponent::loop() {
if (!this->boot_successful_ && (millis() - this->safe_mode_start_time_) > this->safe_mode_boot_is_good_after_) {
// successful boot, reset counter
ESP_LOGI(TAG, "Boot seems successful; resetting boot loop counter");
this->clean_rtc();
this->boot_successful_ = true;
#if defined(USE_ESP32) && defined(USE_OTA_ROLLBACK)
// Mark OTA partition as valid to prevent rollback
esp_ota_mark_app_valid_cancel_rollback();
#endif
// Disable loop since we no longer need to check
this->disable_loop();
this->mark_successful();
}
}
+2
View File
@@ -31,6 +31,8 @@ class SafeModeComponent final : public Component {
void on_safe_shutdown() override;
void mark_successful();
#ifdef USE_SAFE_MODE_CALLBACK
void add_on_safe_mode_callback(std::function<void()> &&callback) {
this->safe_mode_callback_.add(std::move(callback));
@@ -1,6 +1,6 @@
#include "uptime_seconds_sensor.h"
#include "esphome/core/application.h"
#include "esphome/core/hal.h"
#include "esphome/core/log.h"
namespace esphome::uptime {
@@ -8,7 +8,7 @@ namespace esphome::uptime {
static const char *const TAG = "uptime.sensor";
void UptimeSecondsSensor::update() {
const uint64_t uptime = App.scheduler.millis_64();
const uint64_t uptime = millis_64();
const uint64_t seconds_int = uptime / 1000ULL;
const float seconds = float(seconds_int) + (uptime % 1000ULL) / 1000.0f;
this->publish_state(seconds);
@@ -1,6 +1,6 @@
#include "uptime_text_sensor.h"
#include "esphome/core/application.h"
#include "esphome/core/hal.h"
#include "esphome/core/helpers.h"
#include "esphome/core/log.h"
@@ -19,7 +19,7 @@ static void append_unit(char *buf, size_t buf_size, size_t &pos, const char *sep
void UptimeTextSensor::setup() { this->update(); }
void UptimeTextSensor::update() {
uint32_t uptime = static_cast<uint32_t>(App.scheduler.millis_64() / 1000);
uint32_t uptime = static_cast<uint32_t>(millis_64() / 1000);
unsigned interval = this->get_update_interval() / 1000;
// Calculate all time units
+2 -2
View File
@@ -385,7 +385,7 @@ json::SerializationBuffer<> WebServer::get_config_json() {
#endif
root[ESPHOME_F("log")] = this->expose_log_;
root[ESPHOME_F("lang")] = "en";
root[ESPHOME_F("uptime")] = static_cast<uint32_t>(App.scheduler.millis_64() / 1000);
root[ESPHOME_F("uptime")] = static_cast<uint32_t>(millis_64() / 1000);
return builder.serialize();
}
@@ -414,7 +414,7 @@ void WebServer::setup() {
// getting a lot of events
this->set_interval(10000, [this]() {
char buf[32];
auto uptime = static_cast<uint32_t>(App.scheduler.millis_64() / 1000);
auto uptime = static_cast<uint32_t>(millis_64() / 1000);
buf_append_printf(buf, sizeof(buf), 0, "{\"uptime\":%u}", uptime);
this->events_.try_send_nodefer(buf, "ping", millis(), 30000);
});
@@ -470,10 +470,6 @@ const LogString *get_disconnect_reason_str(uint8_t reason) {
return LOG_STR("Unspecified");
}
// TODO: This callback runs in ESP8266 system context with limited stack (~2KB).
// All listener notifications should be deferred to wifi_loop_() via pending_ flags
// to avoid stack overflow. Currently only connect_state is deferred; disconnect,
// IP, and scan listeners still run in this context and should be migrated.
void WiFiComponent::wifi_event_callback(System_Event_t *event) {
switch (event->event) {
case EVENT_STAMODE_CONNECTED: {
@@ -13,6 +13,19 @@
#include <FreeRTOS.h>
#include <queue.h>
#ifdef USE_BK72XX
extern "C" {
#include <wlan_ui_pub.h>
}
#endif
#ifdef USE_RTL87XX
extern "C" {
#include <wifi_conf.h>
#include <wifi_structures.h>
}
#endif
#include "esphome/core/application.h"
#include "esphome/core/hal.h"
#include "esphome/core/helpers.h"
@@ -760,10 +773,22 @@ bssid_t WiFiComponent::wifi_bssid() {
}
std::string WiFiComponent::wifi_ssid() { return WiFi.SSID().c_str(); }
const char *WiFiComponent::wifi_ssid_to(std::span<char, SSID_BUFFER_SIZE> buffer) {
// TODO: Find direct LibreTiny API to avoid Arduino String allocation
#ifdef USE_BK72XX
LinkStatusTypeDef link_status{};
bk_wlan_get_link_status(&link_status);
size_t len = strnlen(reinterpret_cast<const char *>(link_status.ssid), SSID_BUFFER_SIZE - 1);
memcpy(buffer.data(), link_status.ssid, len);
#elif defined(USE_RTL87XX)
rtw_wifi_setting_t setting{};
wifi_get_setting("wlan0", &setting);
size_t len = strnlen(reinterpret_cast<const char *>(setting.ssid), SSID_BUFFER_SIZE - 1);
memcpy(buffer.data(), setting.ssid, len);
#else
// LN882X: wifi_get_sta_conn_info() provides direct pointer access
String ssid = WiFi.SSID();
size_t len = std::min(static_cast<size_t>(ssid.length()), SSID_BUFFER_SIZE - 1);
memcpy(buffer.data(), ssid.c_str(), len);
#endif
buffer[len] = '\0';
return buffer.data();
}
+2
View File
@@ -4,6 +4,7 @@
#include <zephyr/drivers/watchdog.h>
#include <zephyr/sys/reboot.h>
#include <zephyr/random/random.h>
#include "esphome/core/application.h"
#include "esphome/core/hal.h"
#include "esphome/core/helpers.h"
#include "esphome/core/defines.h"
@@ -17,6 +18,7 @@ static const device *const WDT = DEVICE_DT_GET(DT_ALIAS(watchdog0));
void yield() { ::k_yield(); }
uint32_t millis() { return k_ticks_to_ms_floor32(k_uptime_ticks()); }
uint64_t millis_64() { return App.scheduler.millis_64_impl_(millis()); }
uint32_t micros() { return k_ticks_to_us_floor32(k_uptime_ticks()); }
void delayMicroseconds(uint32_t us) { ::k_usleep(us); }
void delay(uint32_t ms) { ::k_msleep(ms); }
+2 -1
View File
@@ -8,7 +8,7 @@ from esphome.components.zephyr import zephyr_add_pm_static, zephyr_data
from esphome.components.zephyr.const import KEY_BOOTLOADER
import esphome.config_validation as cv
from esphome.const import CONF_ID, CONF_INTERNAL, CONF_NAME
from esphome.core import CORE
from esphome.core import CORE, CoroPriority, coroutine_with_priority
from esphome.types import ConfigType
from .const_zephyr import (
@@ -96,6 +96,7 @@ FINAL_VALIDATE_SCHEMA = cv.All(
)
@coroutine_with_priority(CoroPriority.CORE)
async def to_code(config: ConfigType) -> None:
cg.add_define("USE_ZIGBEE")
if CORE.using_zephyr:
+7 -1
View File
@@ -179,6 +179,13 @@ async def zephyr_to_code(config: ConfigType) -> None:
"USE_ZIGBEE_WIPE_ON_BOOT_MAGIC", random.randint(0x000001, 0xFFFFFF)
)
cg.add_define("USE_ZIGBEE_WIPE_ON_BOOT")
# Generate attribute lists before any await that could yield (e.g., build_automation
# waiting for variables from other components). If the hub's priority decays while
# yielding, deferred entity jobs may add cluster list globals that reference these
# attribute lists before they're declared.
await _attr_to_code(config)
var = cg.new_Pvariable(config[CONF_ID])
if on_join_config := config.get(CONF_ON_JOIN):
@@ -186,7 +193,6 @@ async def zephyr_to_code(config: ConfigType) -> None:
await cg.register_component(var, config)
await _attr_to_code(config)
CORE.add_job(_ctx_to_code, config)
+1
View File
@@ -32,6 +32,7 @@ namespace esphome {
void yield();
uint32_t millis();
uint64_t millis_64();
uint32_t micros();
void delay(uint32_t ms);
void delayMicroseconds(uint32_t us); // NOLINT(readability-identifier-naming)
+19 -15
View File
@@ -28,8 +28,10 @@ static constexpr size_t MAX_POOL_SIZE = 5;
// Set to 5 to match the pool size - when we have as many cancelled items as our
// pool can hold, it's time to clean up and recycle them.
static constexpr uint32_t MAX_LOGICALLY_DELETED_ITEMS = 5;
#ifndef USE_ESP32
// Half the 32-bit range - used to detect rollovers vs normal time progression
static constexpr uint32_t HALF_MAX_UINT32 = std::numeric_limits<uint32_t>::max() / 2;
#endif
// max delay to start an interval sequence
static constexpr uint32_t MAX_INTERVAL_DELAY = 5000;
@@ -150,8 +152,8 @@ void HOT Scheduler::set_timer_common_(Component *component, SchedulerItem::Type
return;
}
// Get fresh timestamp BEFORE taking lock - millis_64_ may need to acquire lock itself
const uint64_t now = this->millis_64_(millis());
// Get fresh 64-bit timestamp BEFORE taking lock
const uint64_t now_64 = millis_64();
// Take lock early to protect scheduler_item_pool_ access
LockGuard guard{this->lock_};
@@ -184,7 +186,7 @@ void HOT Scheduler::set_timer_common_(Component *component, SchedulerItem::Type
item->interval = delay;
// first execution happens immediately after a random smallish offset
uint32_t offset = this->calculate_interval_offset_(delay);
item->set_next_execution(now + offset);
item->set_next_execution(now_64 + offset);
#ifdef ESPHOME_LOG_HAS_VERBOSE
SchedulerNameLog name_log;
ESP_LOGV(TAG, "Scheduler interval for %s is %" PRIu32 "ms, offset %" PRIu32 "ms",
@@ -192,11 +194,11 @@ void HOT Scheduler::set_timer_common_(Component *component, SchedulerItem::Type
#endif
} else {
item->interval = 0;
item->set_next_execution(now + delay);
item->set_next_execution(now_64 + delay);
}
#ifdef ESPHOME_DEBUG_SCHEDULER
this->debug_log_timer_(item.get(), name_type, static_name, hash_or_id, type, delay, now);
this->debug_log_timer_(item.get(), name_type, static_name, hash_or_id, type, delay, now_64);
#endif /* ESPHOME_DEBUG_SCHEDULER */
// For retries, check if there's a cancelled timeout first
@@ -399,8 +401,7 @@ optional<uint32_t> HOT Scheduler::next_schedule_in(uint32_t now) {
return {};
auto &item = this->items_[0];
// Convert the fresh timestamp from caller (usually Application::loop()) to 64-bit
const auto now_64 = this->millis_64_(now); // 'now' from parameter - fresh from caller
const auto now_64 = this->millis_64_from_(now);
const uint64_t next_exec = item->get_next_execution();
if (next_exec < now_64)
return 0;
@@ -461,8 +462,8 @@ void HOT Scheduler::call(uint32_t now) {
this->process_defer_queue_(now);
#endif /* not ESPHOME_THREAD_SINGLE */
// Convert the fresh timestamp from main loop to 64-bit for scheduler operations
const auto now_64 = this->millis_64_(now); // 'now' from parameter - fresh from Application::loop()
// Extend the caller's 32-bit timestamp to 64-bit for scheduler operations
const auto now_64 = this->millis_64_from_(now);
this->process_to_add();
// Track if any items were added to to_add_ during this call (intervals or from callbacks)
@@ -474,15 +475,18 @@ void HOT Scheduler::call(uint32_t now) {
if (now_64 - last_print > 2000) {
last_print = now_64;
std::vector<SchedulerItemPtr> old_items;
#ifdef ESPHOME_THREAD_MULTI_ATOMICS
#if !defined(USE_ESP32) && defined(ESPHOME_THREAD_MULTI_ATOMICS)
const auto last_dbg = this->last_millis_.load(std::memory_order_relaxed);
const auto major_dbg = this->millis_major_.load(std::memory_order_relaxed);
ESP_LOGD(TAG, "Items: count=%zu, pool=%zu, now=%" PRIu64 " (%" PRIu16 ", %" PRIu32 ")", this->items_.size(),
this->scheduler_item_pool_.size(), now_64, major_dbg, last_dbg);
#else /* not ESPHOME_THREAD_MULTI_ATOMICS */
#elif !defined(USE_ESP32)
ESP_LOGD(TAG, "Items: count=%zu, pool=%zu, now=%" PRIu64 " (%" PRIu16 ", %" PRIu32 ")", this->items_.size(),
this->scheduler_item_pool_.size(), now_64, this->millis_major_, this->last_millis_);
#endif /* else ESPHOME_THREAD_MULTI_ATOMICS */
#else
ESP_LOGD(TAG, "Items: count=%zu, pool=%zu, now=%" PRIu64, this->items_.size(), this->scheduler_item_pool_.size(),
now_64);
#endif
// Cleanup before debug output
this->cleanup_();
while (!this->items_.empty()) {
@@ -710,9 +714,8 @@ bool HOT Scheduler::cancel_item_locked_(Component *component, NameType name_type
return total_cancelled > 0;
}
uint64_t Scheduler::millis_64() { return this->millis_64_(millis()); }
uint64_t Scheduler::millis_64_(uint32_t now) {
#ifndef USE_ESP32
uint64_t Scheduler::millis_64_impl_(uint32_t now) {
// THREAD SAFETY NOTE:
// This function has three implementations, based on the precompiler flags
// - ESPHOME_THREAD_SINGLE - Runs on single-threaded platforms (ESP8266, RP2040, etc.)
@@ -869,6 +872,7 @@ uint64_t Scheduler::millis_64_(uint32_t now) {
"No platform threading model defined. One of ESPHOME_THREAD_SINGLE, ESPHOME_THREAD_MULTI_NO_ATOMICS, or ESPHOME_THREAD_MULTI_ATOMICS must be defined."
#endif
}
#endif // not USE_ESP32
bool HOT Scheduler::SchedulerItem::cmp(const SchedulerItemPtr &a, const SchedulerItemPtr &b) {
// High bits are almost always equal (change only on 32-bit rollover ~49 days)
+29 -6
View File
@@ -10,6 +10,7 @@
#endif
#include "esphome/core/component.h"
#include "esphome/core/hal.h"
#include "esphome/core/helpers.h"
namespace esphome {
@@ -117,12 +118,12 @@ class Scheduler {
bool cancel_retry(Component *component, uint32_t id);
/// Get 64-bit millisecond timestamp (handles 32-bit millis() rollover)
uint64_t millis_64();
uint64_t millis_64() { return esphome::millis_64(); }
// Calculate when the next scheduled item should run
// @param now Fresh timestamp from millis() - must not be stale/cached
// Returns the time in milliseconds until the next scheduled item, or nullopt if no items
// This method performs cleanup of removed items before checking the schedule
// Calculate when the next scheduled item should run.
// @param now On ESP32, unused for 64-bit extension (native); on other platforms, extended to 64-bit via rollover.
// Returns the time in milliseconds until the next scheduled item, or nullopt if no items.
// This method performs cleanup of removed items before checking the schedule.
// IMPORTANT: This method should only be called from the main thread (loop task).
optional<uint32_t> next_schedule_in(uint32_t now);
@@ -282,7 +283,25 @@ class Scheduler {
// Common implementation for cancel_retry
bool cancel_retry_(Component *component, NameType name_type, const char *static_name, uint32_t hash_or_id);
uint64_t millis_64_(uint32_t now);
// Extend a 32-bit millis() value to 64-bit. Use when the caller already has a fresh now.
// On ESP32, ignores now and uses esp_timer_get_time() directly (native 64-bit).
// On non-ESP32, extends now to 64-bit using rollover tracking.
uint64_t millis_64_from_(uint32_t now) {
#ifdef USE_ESP32
(void) now;
return millis_64();
#else
return this->millis_64_impl_(now);
#endif
}
#ifndef USE_ESP32
// On non-ESP32 platforms, millis_64() HAL function delegates to this method
// which tracks 32-bit millis() rollover using millis_major_ and last_millis_.
// On ESP32, millis_64() uses esp_timer_get_time() directly.
friend uint64_t millis_64();
uint64_t millis_64_impl_(uint32_t now);
#endif
// Cleanup logically deleted items from the scheduler
// Returns the number of items remaining after cleanup
// IMPORTANT: This method should only be called from the main thread (loop task).
@@ -549,6 +568,9 @@ class Scheduler {
// to synchronize between tasks (see https://github.com/esphome/backlog/issues/52)
std::vector<SchedulerItemPtr> scheduler_item_pool_;
#ifndef USE_ESP32
// On ESP32, millis_64() uses esp_timer_get_time() directly; no rollover tracking needed.
// On other platforms, these fields track 32-bit millis() rollover for millis_64_impl_().
#ifdef ESPHOME_THREAD_MULTI_ATOMICS
/*
* Multi-threaded platforms with atomic support: last_millis_ needs atomic for lock-free updates
@@ -577,6 +599,7 @@ class Scheduler {
#else /* not ESPHOME_THREAD_MULTI_ATOMICS */
uint16_t millis_major_{0};
#endif /* else ESPHOME_THREAD_MULTI_ATOMICS */
#endif /* not USE_ESP32 */
};
} // namespace esphome
+2 -2
View File
@@ -4,13 +4,13 @@ dependencies:
esphome/esp-audio-libs:
version: 2.0.3
esphome/micro-opus:
version: 0.3.3
version: 0.3.4
espressif/esp-tflite-micro:
version: 1.3.3~1
espressif/esp32-camera:
version: 2.1.1
espressif/mdns:
version: 1.9.1
version: 1.10.0
espressif/esp_wifi_remote:
version: 1.3.2
rules:
@@ -0,0 +1,16 @@
esp32_hosted:
variant: ESP32C6
slot: 1
active_high: true
reset_pin: GPIO15
cmd_pin: GPIO13
clk_pin: GPIO12
d0_pin: GPIO11
d1_pin: GPIO10
d2_pin: GPIO9
d3_pin: GPIO8
sdio_frequency: 8MHz
wifi:
ssid: MySSID
password: password1
@@ -16,3 +16,7 @@ button:
switch:
- platform: safe_mode
name: Safe Mode Switch
esphome:
on_boot:
- safe_mode.mark_successful