From 072b2c445cd33a13d7029e9ce36dad9122fce162 Mon Sep 17 00:00:00 2001 From: Otto Winter Date: Wed, 4 Dec 2019 19:30:10 +0100 Subject: [PATCH] Add ESP8266 core v2.6.2 (#905) * Add ESP8266 core v2.6.2 * Upstream ESP8266 Wifi fixes * Replace disable_interrupt with InterruptLock C++ class * Update code to use InterruptLock * Lint * Update dht.cpp * Improve InterruptLock docs, mark as ICACHE_RAM_ATTR * Fixes --- .../components/dallas/dallas_component.cpp | 68 +++++---- esphome/components/dallas/esp_one_wire.cpp | 26 ++-- esphome/components/dht/dht.cpp | 137 ++++++++++-------- esphome/components/hx711/hx711.cpp | 31 ++-- .../remote_transmitter_esp8266.cpp | 24 +-- esphome/components/uart/uart.cpp | 39 ++--- .../wifi/wifi_component_esp8266.cpp | 59 +++++++- esphome/core/helpers.cpp | 28 ++-- esphome/core/helpers.h | 34 ++++- esphome/core/preferences.cpp | 22 +-- esphome/platformio_api.py | 1 + 11 files changed, 289 insertions(+), 180 deletions(-) diff --git a/esphome/components/dallas/dallas_component.cpp b/esphome/components/dallas/dallas_component.cpp index 6eeddb1b56..aa839e7331 100644 --- a/esphome/components/dallas/dallas_component.cpp +++ b/esphome/components/dallas/dallas_component.cpp @@ -32,9 +32,11 @@ void DallasComponent::setup() { ESP_LOGCONFIG(TAG, "Setting up DallasComponent..."); yield(); - disable_interrupts(); - std::vector raw_sensors = this->one_wire_->search_vec(); - enable_interrupts(); + std::vector raw_sensors; + { + InterruptLock lock; + raw_sensors = this->one_wire_->search_vec(); + } for (auto &address : raw_sensors) { std::string s = uint64_to_string(address); @@ -108,16 +110,17 @@ DallasTemperatureSensor *DallasComponent::get_sensor_by_index(uint8_t index, uin void DallasComponent::update() { this->status_clear_warning(); - disable_interrupts(); bool result; - if (!this->one_wire_->reset()) { - result = false; - } else { - result = true; - this->one_wire_->skip(); - this->one_wire_->write8(DALLAS_COMMAND_START_CONVERSION); + { + InterruptLock lock; + if (!this->one_wire_->reset()) { + result = false; + } else { + result = true; + this->one_wire_->skip(); + this->one_wire_->write8(DALLAS_COMMAND_START_CONVERSION); + } } - enable_interrupts(); if (!result) { ESP_LOGE(TAG, "Requesting conversion failed"); @@ -127,9 +130,11 @@ void DallasComponent::update() { for (auto *sensor : this->sensors_) { this->set_timeout(sensor->get_address_name(), sensor->millis_to_wait_for_conversion(), [this, sensor] { - disable_interrupts(); - bool res = sensor->read_scratch_pad(); - enable_interrupts(); + bool res; + { + InterruptLock lock; + res = sensor->read_scratch_pad(); + } if (!res) { ESP_LOGW(TAG, "'%s' - Reseting bus for read failed!", sensor->get_name().c_str()); @@ -170,7 +175,7 @@ const std::string &DallasTemperatureSensor::get_address_name() { return this->address_name_; } -bool DallasTemperatureSensor::read_scratch_pad() { +bool ICACHE_RAM_ATTR DallasTemperatureSensor::read_scratch_pad() { ESPOneWire *wire = this->parent_->one_wire_; if (!wire->reset()) { return false; @@ -185,9 +190,11 @@ bool DallasTemperatureSensor::read_scratch_pad() { return true; } bool DallasTemperatureSensor::setup_sensor() { - disable_interrupts(); - bool r = this->read_scratch_pad(); - enable_interrupts(); + bool r; + { + InterruptLock lock; + r = this->read_scratch_pad(); + } if (!r) { ESP_LOGE(TAG, "Reading scratchpad failed: reset"); @@ -222,20 +229,21 @@ bool DallasTemperatureSensor::setup_sensor() { } ESPOneWire *wire = this->parent_->one_wire_; - disable_interrupts(); - if (wire->reset()) { - wire->select(this->address_); - wire->write8(DALLAS_COMMAND_WRITE_SCRATCH_PAD); - wire->write8(this->scratch_pad_[2]); // high alarm temp - wire->write8(this->scratch_pad_[3]); // low alarm temp - wire->write8(this->scratch_pad_[4]); // resolution - wire->reset(); + { + InterruptLock lock; + if (wire->reset()) { + wire->select(this->address_); + wire->write8(DALLAS_COMMAND_WRITE_SCRATCH_PAD); + wire->write8(this->scratch_pad_[2]); // high alarm temp + wire->write8(this->scratch_pad_[3]); // low alarm temp + wire->write8(this->scratch_pad_[4]); // resolution + wire->reset(); - // write value to EEPROM - wire->select(this->address_); - wire->write8(0x48); + // write value to EEPROM + wire->select(this->address_); + wire->write8(0x48); + } } - enable_interrupts(); delay(20); // allow it to finish operation wire->reset(); diff --git a/esphome/components/dallas/esp_one_wire.cpp b/esphome/components/dallas/esp_one_wire.cpp index e0db3118b9..d90b10894d 100644 --- a/esphome/components/dallas/esp_one_wire.cpp +++ b/esphome/components/dallas/esp_one_wire.cpp @@ -12,7 +12,7 @@ const int ONE_WIRE_ROM_SEARCH = 0xF0; ESPOneWire::ESPOneWire(GPIOPin *pin) : pin_(pin) {} -bool HOT ESPOneWire::reset() { +bool HOT ICACHE_RAM_ATTR ESPOneWire::reset() { uint8_t retries = 125; // Wait for communication to clear @@ -39,7 +39,7 @@ bool HOT ESPOneWire::reset() { return r; } -void HOT ESPOneWire::write_bit(bool bit) { +void HOT ICACHE_RAM_ATTR ESPOneWire::write_bit(bool bit) { // Initiate write/read by pulling low. this->pin_->pin_mode(OUTPUT); this->pin_->digital_write(false); @@ -60,7 +60,7 @@ void HOT ESPOneWire::write_bit(bool bit) { } } -bool HOT ESPOneWire::read_bit() { +bool HOT ICACHE_RAM_ATTR ESPOneWire::read_bit() { // Initiate read slot by pulling LOW for at least 1µs this->pin_->pin_mode(OUTPUT); this->pin_->digital_write(false); @@ -76,43 +76,43 @@ bool HOT ESPOneWire::read_bit() { return r; } -void ESPOneWire::write8(uint8_t val) { +void ICACHE_RAM_ATTR ESPOneWire::write8(uint8_t val) { for (uint8_t i = 0; i < 8; i++) { this->write_bit(bool((1u << i) & val)); } } -void ESPOneWire::write64(uint64_t val) { +void ICACHE_RAM_ATTR ESPOneWire::write64(uint64_t val) { for (uint8_t i = 0; i < 64; i++) { this->write_bit(bool((1ULL << i) & val)); } } -uint8_t ESPOneWire::read8() { +uint8_t ICACHE_RAM_ATTR ESPOneWire::read8() { uint8_t ret = 0; for (uint8_t i = 0; i < 8; i++) { ret |= (uint8_t(this->read_bit()) << i); } return ret; } -uint64_t ESPOneWire::read64() { +uint64_t ICACHE_RAM_ATTR ESPOneWire::read64() { uint64_t ret = 0; for (uint8_t i = 0; i < 8; i++) { ret |= (uint64_t(this->read_bit()) << i); } return ret; } -void ESPOneWire::select(uint64_t address) { +void ICACHE_RAM_ATTR ESPOneWire::select(uint64_t address) { this->write8(ONE_WIRE_ROM_SELECT); this->write64(address); } -void ESPOneWire::reset_search() { +void ICACHE_RAM_ATTR ESPOneWire::reset_search() { this->last_discrepancy_ = 0; this->last_device_flag_ = false; this->last_family_discrepancy_ = 0; this->rom_number_ = 0; } -uint64_t HOT ESPOneWire::search() { +uint64_t HOT ICACHE_RAM_ATTR ESPOneWire::search() { if (this->last_device_flag_) { return 0u; } @@ -196,7 +196,7 @@ uint64_t HOT ESPOneWire::search() { return this->rom_number_; } -std::vector ESPOneWire::search_vec() { +std::vector ICACHE_RAM_ATTR ESPOneWire::search_vec() { std::vector res; this->reset_search(); @@ -206,12 +206,12 @@ std::vector ESPOneWire::search_vec() { return res; } -void ESPOneWire::skip() { +void ICACHE_RAM_ATTR ESPOneWire::skip() { this->write8(0xCC); // skip ROM } GPIOPin *ESPOneWire::get_pin() { return this->pin_; } -uint8_t *ESPOneWire::rom_number8_() { return reinterpret_cast(&this->rom_number_); } +uint8_t ICACHE_RAM_ATTR *ESPOneWire::rom_number8_() { return reinterpret_cast(&this->rom_number_); } } // namespace dallas } // namespace esphome diff --git a/esphome/components/dht/dht.cpp b/esphome/components/dht/dht.cpp index 23d8c1d3e2..625a546e24 100644 --- a/esphome/components/dht/dht.cpp +++ b/esphome/components/dht/dht.cpp @@ -71,80 +71,101 @@ void DHT::set_dht_model(DHTModel model) { this->model_ = model; this->is_auto_detect_ = model == DHT_MODEL_AUTO_DETECT; } -bool HOT DHT::read_sensor_(float *temperature, float *humidity, bool report_errors) { +bool HOT ICACHE_RAM_ATTR DHT::read_sensor_(float *temperature, float *humidity, bool report_errors) { *humidity = NAN; *temperature = NAN; - disable_interrupts(); - this->pin_->digital_write(false); - this->pin_->pin_mode(OUTPUT); - this->pin_->digital_write(false); - - if (this->model_ == DHT_MODEL_DHT11) { - delayMicroseconds(18000); - } else if (this->model_ == DHT_MODEL_SI7021) { - delayMicroseconds(500); - this->pin_->digital_write(true); - delayMicroseconds(40); - } else { - delayMicroseconds(800); - } - this->pin_->pin_mode(INPUT_PULLUP); - delayMicroseconds(40); - + int error_code = 0; + int8_t i = 0; uint8_t data[5] = {0, 0, 0, 0, 0}; - uint8_t bit = 7; - uint8_t byte = 0; - for (int8_t i = -1; i < 40; i++) { - uint32_t start_time = micros(); + { + InterruptLock lock; - // Wait for rising edge - while (!this->pin_->digital_read()) { - if (micros() - start_time > 90) { - enable_interrupts(); - if (report_errors) { - if (i < 0) { - ESP_LOGW(TAG, "Waiting for DHT communication to clear failed!"); - } else { - ESP_LOGW(TAG, "Rising edge for bit %d failed!", i); - } + this->pin_->digital_write(false); + this->pin_->pin_mode(OUTPUT); + this->pin_->digital_write(false); + + if (this->model_ == DHT_MODEL_DHT11) { + delayMicroseconds(18000); + } else if (this->model_ == DHT_MODEL_SI7021) { + delayMicroseconds(500); + this->pin_->digital_write(true); + delayMicroseconds(40); + } else { + delayMicroseconds(800); + } + this->pin_->pin_mode(INPUT_PULLUP); + delayMicroseconds(40); + + uint8_t bit = 7; + uint8_t byte = 0; + + for (i = -1; i < 40; i++) { + uint32_t start_time = micros(); + + // Wait for rising edge + while (!this->pin_->digital_read()) { + if (micros() - start_time > 90) { + if (i < 0) + error_code = 1; + else + error_code = 2; + break; } - return false; } - } + if (error_code != 0) + break; - start_time = micros(); - uint32_t end_time = start_time; + start_time = micros(); + uint32_t end_time = start_time; - // Wait for falling edge - while (this->pin_->digital_read()) { - if ((end_time = micros()) - start_time > 90) { - enable_interrupts(); - if (report_errors) { - if (i < 0) { - ESP_LOGW(TAG, "Requesting data from DHT failed!"); - } else { - ESP_LOGW(TAG, "Falling edge for bit %d failed!", i); - } + // Wait for falling edge + while (this->pin_->digital_read()) { + if ((end_time = micros()) - start_time > 90) { + if (i < 0) + error_code = 3; + else + error_code = 4; + break; } - return false; } - } + if (error_code != 0) + break; - if (i < 0) - continue; + if (i < 0) + continue; - if (end_time - start_time >= 40) { - data[byte] |= 1 << bit; + if (end_time - start_time >= 40) { + data[byte] |= 1 << bit; + } + if (bit == 0) { + bit = 7; + byte++; + } else + bit--; } - if (bit == 0) { - bit = 7; - byte++; - } else - bit--; } - enable_interrupts(); + if (!report_errors && error_code != 0) + return false; + + switch (error_code) { + case 1: + ESP_LOGW(TAG, "Waiting for DHT communication to clear failed!"); + return false; + case 2: + ESP_LOGW(TAG, "Rising edge for bit %d failed!", i); + return false; + case 3: + ESP_LOGW(TAG, "Requesting data from DHT failed!"); + return false; + case 4: + ESP_LOGW(TAG, "Falling edge for bit %d failed!", i); + return false; + case 0: + default: + break; + } ESP_LOGVV(TAG, "Data: Hum=0b" BYTE_TO_BINARY_PATTERN BYTE_TO_BINARY_PATTERN diff --git a/esphome/components/hx711/hx711.cpp b/esphome/components/hx711/hx711.cpp index 1c808a2501..605f534f91 100644 --- a/esphome/components/hx711/hx711.cpp +++ b/esphome/components/hx711/hx711.cpp @@ -42,23 +42,24 @@ bool HX711Sensor::read_sensor_(uint32_t *result) { this->status_clear_warning(); uint32_t data = 0; - disable_interrupts(); - for (uint8_t i = 0; i < 24; i++) { - this->sck_pin_->digital_write(true); - delayMicroseconds(1); - data |= uint32_t(this->dout_pin_->digital_read()) << (23 - i); - this->sck_pin_->digital_write(false); - delayMicroseconds(1); - } + { + InterruptLock lock; + for (uint8_t i = 0; i < 24; i++) { + this->sck_pin_->digital_write(true); + delayMicroseconds(1); + data |= uint32_t(this->dout_pin_->digital_read()) << (23 - i); + this->sck_pin_->digital_write(false); + delayMicroseconds(1); + } - // Cycle clock pin for gain setting - for (uint8_t i = 0; i < this->gain_; i++) { - this->sck_pin_->digital_write(true); - delayMicroseconds(1); - this->sck_pin_->digital_write(false); - delayMicroseconds(1); + // Cycle clock pin for gain setting + for (uint8_t i = 0; i < this->gain_; i++) { + this->sck_pin_->digital_write(true); + delayMicroseconds(1); + this->sck_pin_->digital_write(false); + delayMicroseconds(1); + } } - enable_interrupts(); if (data & 0x800000ULL) { data |= 0xFF000000ULL; diff --git a/esphome/components/remote_transmitter/remote_transmitter_esp8266.cpp b/esphome/components/remote_transmitter/remote_transmitter_esp8266.cpp index 7704f1d9ab..e8906e87aa 100644 --- a/esphome/components/remote_transmitter/remote_transmitter_esp8266.cpp +++ b/esphome/components/remote_transmitter/remote_transmitter_esp8266.cpp @@ -67,22 +67,22 @@ void RemoteTransmitterComponent::send_internal(uint32_t send_times, uint32_t sen uint32_t on_time, off_time; this->calculate_on_off_time_(this->temp_.get_carrier_frequency(), &on_time, &off_time); for (uint32_t i = 0; i < send_times; i++) { - disable_interrupts(); - for (int32_t item : this->temp_.get_data()) { - if (item > 0) { - const auto length = uint32_t(item); - this->mark_(on_time, off_time, length); - } else { - const auto length = uint32_t(-item); - this->space_(length); + { + InterruptLock lock; + for (int32_t item : this->temp_.get_data()) { + if (item > 0) { + const auto length = uint32_t(item); + this->mark_(on_time, off_time, length); + } else { + const auto length = uint32_t(-item); + this->space_(length); + } + App.feed_wdt(); } - App.feed_wdt(); } - enable_interrupts(); if (i + 1 < send_times) { - delay(send_wait / 1000UL); - delayMicroseconds(send_wait % 1000UL); + delay_microseconds_accurate(send_wait); } } } diff --git a/esphome/components/uart/uart.cpp b/esphome/components/uart/uart.cpp index 1f40498606..205e9e2300 100644 --- a/esphome/components/uart/uart.cpp +++ b/esphome/components/uart/uart.cpp @@ -295,24 +295,25 @@ void ICACHE_RAM_ATTR HOT ESP8266SoftwareSerial::write_byte(uint8_t data) { return; } - disable_interrupts(); - uint32_t wait = this->bit_time_; - const uint32_t start = ESP.getCycleCount(); - // Start bit - this->write_bit_(false, &wait, start); - this->write_bit_(data & (1 << 0), &wait, start); - this->write_bit_(data & (1 << 1), &wait, start); - this->write_bit_(data & (1 << 2), &wait, start); - this->write_bit_(data & (1 << 3), &wait, start); - this->write_bit_(data & (1 << 4), &wait, start); - this->write_bit_(data & (1 << 5), &wait, start); - this->write_bit_(data & (1 << 6), &wait, start); - this->write_bit_(data & (1 << 7), &wait, start); - // Stop bit - this->write_bit_(true, &wait, start); - if (this->stop_bits_ == 2) - this->wait_(&wait, start); - enable_interrupts(); + { + InterruptLock lock; + uint32_t wait = this->bit_time_; + const uint32_t start = ESP.getCycleCount(); + // Start bit + this->write_bit_(false, &wait, start); + this->write_bit_(data & (1 << 0), &wait, start); + this->write_bit_(data & (1 << 1), &wait, start); + this->write_bit_(data & (1 << 2), &wait, start); + this->write_bit_(data & (1 << 3), &wait, start); + this->write_bit_(data & (1 << 4), &wait, start); + this->write_bit_(data & (1 << 5), &wait, start); + this->write_bit_(data & (1 << 6), &wait, start); + this->write_bit_(data & (1 << 7), &wait, start); + // Stop bit + this->write_bit_(true, &wait, start); + if (this->stop_bits_ == 2) + this->wait_(&wait, start); + } } void ICACHE_RAM_ATTR ESP8266SoftwareSerial::wait_(uint32_t *wait, const uint32_t &start) { while (ESP.getCycleCount() - start < *wait) @@ -323,7 +324,7 @@ bool ICACHE_RAM_ATTR ESP8266SoftwareSerial::read_bit_(uint32_t *wait, const uint this->wait_(wait, start); return this->rx_pin_->digital_read(); } -void ESP8266SoftwareSerial::write_bit_(bool bit, uint32_t *wait, const uint32_t &start) { +void ICACHE_RAM_ATTR ESP8266SoftwareSerial::write_bit_(bool bit, uint32_t *wait, const uint32_t &start) { this->tx_pin_->digital_write(bit); this->wait_(wait, start); } diff --git a/esphome/components/wifi/wifi_component_esp8266.cpp b/esphome/components/wifi/wifi_component_esp8266.cpp index 88bcb03450..deee578b4c 100644 --- a/esphome/components/wifi/wifi_component_esp8266.cpp +++ b/esphome/components/wifi/wifi_component_esp8266.cpp @@ -6,8 +6,16 @@ #include #include + +extern "C" { #include "lwip/err.h" #include "lwip/dns.h" +#include "lwip/dhcp.h" +#include "lwip/init.h" // LWIP_VERSION_ +#if LWIP_IPV6 +#include "lwip/netif.h" // struct netif +#endif +} #include "esphome/core/helpers.h" #include "esphome/core/log.h" @@ -74,6 +82,19 @@ bool WiFiComponent::wifi_apply_power_save_() { } return wifi_set_sleep_type(power_save); } + +#if LWIP_VERSION_MAJOR != 1 +/* + lwip v2 needs to be notified of IP changes, see also + https://github.com/d-a-v/Arduino/blob/0e7d21e17144cfc5f53c016191daca8723e89ee8/libraries/ESP8266WiFi/src/ESP8266WiFiSTA.cpp#L251 + */ +#undef netif_set_addr // need to call lwIP-v1.4 netif_set_addr() +extern "C" { +struct netif *eagle_lwip_getif(int netif_index); +void netif_set_addr(struct netif *netif, const ip4_addr_t *ip, const ip4_addr_t *netmask, const ip4_addr_t *gw); +}; +#endif + bool WiFiComponent::wifi_sta_ip_config_(optional manual_ip) { // enable STA if (!this->wifi_mode_(true, {})) @@ -94,6 +115,13 @@ bool WiFiComponent::wifi_sta_ip_config_(optional manual_ip) { bool ret = true; +#if LWIP_VERSION_MAJOR != 1 + // get current->previous IP address + // (check below) + ip_info previp{}; + wifi_get_ip_info(STATION_IF, &previp); +#endif + struct ip_info info {}; info.ip.addr = static_cast(manual_ip->static_ip); info.gw.addr = static_cast(manual_ip->gateway); @@ -122,6 +150,14 @@ bool WiFiComponent::wifi_sta_ip_config_(optional manual_ip) { dns_setserver(1, &dns); } +#if LWIP_VERSION_MAJOR != 1 + // trigger address change by calling lwIP-v1.4 api + // only when ip is already set by other mean (generally dhcp) + if (previp.ip.addr != 0 && previp.ip.addr != info.ip.addr) { + netif_set_addr(eagle_lwip_getif(STATION_IF), reinterpret_cast(&info.ip), + reinterpret_cast(&info.netmask), reinterpret_cast(&info.gw)); + } +#endif return ret; } @@ -133,10 +169,31 @@ IPAddress WiFiComponent::wifi_sta_ip_() { return {ip.ip.addr}; } bool WiFiComponent::wifi_apply_hostname_() { - bool ret = wifi_station_set_hostname(const_cast(App.get_name().c_str())); + const std::string &hostname = App.get_name(); + bool ret = wifi_station_set_hostname(const_cast(hostname.c_str())); if (!ret) { ESP_LOGV(TAG, "Setting WiFi Hostname failed!"); } + + // inform dhcp server of hostname change using dhcp_renew() + for (netif *intf = netif_list; intf; intf = intf->next) { + // unconditionally update all known interfaces +#if LWIP_VERSION_MAJOR == 1 + intf->hostname = (char *) wifi_station_get_hostname(); +#else + intf->hostname = wifi_station_get_hostname(); +#endif + if (netif_dhcp_data(intf) != nullptr) { + // renew already started DHCP leases + err_t lwipret = dhcp_renew(intf); + if (lwipret != ERR_OK) { + ESP_LOGW(TAG, "wifi_apply_hostname_(%s): lwIP error %d on interface %c%c (index %d)", intf->hostname, + (int) lwipret, intf->name[0], intf->name[1], intf->num); + ret = false; + } + } + } + return ret; } diff --git a/esphome/core/helpers.cpp b/esphome/core/helpers.cpp index 6d6aa80b66..3ff87678e8 100644 --- a/esphome/core/helpers.cpp +++ b/esphome/core/helpers.cpp @@ -156,21 +156,6 @@ ParseOnOffState parse_on_off(const char *str, const char *on, const char *off) { const char *HOSTNAME_CHARACTER_WHITELIST = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_"; -void disable_interrupts() { -#ifdef ARDUINO_ARCH_ESP32 - portDISABLE_INTERRUPTS(); -#else - noInterrupts(); -#endif -} -void enable_interrupts() { -#ifdef ARDUINO_ARCH_ESP32 - portENABLE_INTERRUPTS(); -#else - interrupts(); -#endif -} - uint8_t crc8(uint8_t *data, uint8_t len) { uint8_t crc = 0; @@ -193,8 +178,8 @@ void delay_microseconds_accurate(uint32_t usec) { if (usec <= 16383UL) { delayMicroseconds(usec); } else { - delay(usec / 1000UL); - delayMicroseconds(usec % 1000UL); + delay(usec / 16383UL); + delayMicroseconds(usec % 16383UL); } } @@ -330,4 +315,13 @@ std::string hexencode(const uint8_t *data, uint32_t len) { return res; } +#ifdef ARDUINO_ARCH_ESP8266 +ICACHE_RAM_ATTR InterruptLock::InterruptLock() { xt_state_ = xt_rsil(15); } +ICACHE_RAM_ATTR InterruptLock::~InterruptLock() { xt_wsr_ps(xt_state_); } +#endif +#ifdef ARDUINO_ARCH_ESP32 +ICACHE_RAM_ATTR InterruptLock::InterruptLock() { portENABLE_INTERRUPTS(); } +ICACHE_RAM_ATTR InterruptLock::~InterruptLock() { portDISABLE_INTERRUPTS(); } +#endif + } // namespace esphome diff --git a/esphome/core/helpers.h b/esphome/core/helpers.h index 91b78b73a0..ab3d883e05 100644 --- a/esphome/core/helpers.h +++ b/esphome/core/helpers.h @@ -133,16 +133,38 @@ uint16_t encode_uint16(uint8_t msb, uint8_t lsb); /// Decode a 16-bit unsigned integer into an array of two values: most significant byte, least significant byte. std::array decode_uint16(uint16_t value); -/** Cross-platform method to disable interrupts. +/*** + * An interrupt helper class. * - * Useful when you need to do some timing-dependent communication. + * This behaves like std::lock_guard. As long as the value is visible in the current stack, all interrupts + * (including flash reads) will be disabled. * - * @see Do not forget to call `enable_interrupts()` again or otherwise things will go very wrong. + * Please note all functions called when the interrupt lock must be marked ICACHE_RAM_ATTR (loading code into + * instruction cache is done via interrupts; disabling interrupts prevents data not already in cache from being + * pulled from flash). + * + * Example: + * + * ```cpp + * // interrupts are enabled + * { + * InterruptLock lock; + * // do something + * // interrupts are disabled + * } + * // interrupts are enabled + * ``` */ -void disable_interrupts(); +class InterruptLock { + public: + InterruptLock(); + ~InterruptLock(); -/// Cross-platform method to enable interrupts after they have been disabled. -void enable_interrupts(); + protected: +#ifdef ARDUINO_ARCH_ESP8266 + uint32_t xt_state_; +#endif +}; /// Calculate a crc8 of data with the provided data length. uint8_t crc8(uint8_t *data, uint8_t len); diff --git a/esphome/core/preferences.cpp b/esphome/core/preferences.cpp index 2329ed34f5..8b41cbc7b5 100644 --- a/esphome/core/preferences.cpp +++ b/esphome/core/preferences.cpp @@ -105,16 +105,18 @@ void ESPPreferences::save_esp8266_flash_() { return; ESP_LOGVV(TAG, "Saving preferences to flash..."); - disable_interrupts(); - auto erase_res = spi_flash_erase_sector(get_esp8266_flash_sector()); + SpiFlashOpResult erase_res, write_res = SPI_FLASH_RESULT_OK; + { + InterruptLock lock; + erase_res = spi_flash_erase_sector(get_esp8266_flash_sector()); + if (erase_res == SPI_FLASH_RESULT_OK) { + write_res = spi_flash_write(get_esp8266_flash_address(), this->flash_storage_, ESP8266_FLASH_STORAGE_SIZE * 4); + } + } if (erase_res != SPI_FLASH_RESULT_OK) { - enable_interrupts(); ESP_LOGV(TAG, "Erase ESP8266 flash failed!"); return; } - - auto write_res = spi_flash_write(get_esp8266_flash_address(), this->flash_storage_, ESP8266_FLASH_STORAGE_SIZE * 4); - enable_interrupts(); if (write_res != SPI_FLASH_RESULT_OK) { ESP_LOGV(TAG, "Write ESP8266 flash failed!"); return; @@ -173,9 +175,11 @@ ESPPreferences::ESPPreferences() void ESPPreferences::begin() { this->flash_storage_ = new uint32_t[ESP8266_FLASH_STORAGE_SIZE]; ESP_LOGVV(TAG, "Loading preferences from flash..."); - disable_interrupts(); - spi_flash_read(get_esp8266_flash_address(), this->flash_storage_, ESP8266_FLASH_STORAGE_SIZE * 4); - enable_interrupts(); + + { + InterruptLock lock; + spi_flash_read(get_esp8266_flash_address(), this->flash_storage_, ESP8266_FLASH_STORAGE_SIZE * 4); + } } ESPPreferenceObject ESPPreferences::make_preference(size_t length, uint32_t type, bool in_flash) { diff --git a/esphome/platformio_api.py b/esphome/platformio_api.py index 36e451c21d..317670710b 100644 --- a/esphome/platformio_api.py +++ b/esphome/platformio_api.py @@ -60,6 +60,7 @@ FILTER_PLATFORMIO_LINES = [ r"Using cache: .*", r'Installing dependencies', r'.* @ .* is already installed', + r'Building in .* mode', ]