mirror of
https://github.com/esphome/esphome.git
synced 2024-11-10 01:07:45 +01:00
Add Heap Sensors - free/max block/fragmentation (#1578)
Co-authored-by: Oxan van Leeuwen <oxan@oxanvanleeuwen.nl> Co-authored-by: Otto winter <otto@otto-winter.com>
This commit is contained in:
parent
297824e2d7
commit
3a62455948
4 changed files with 194 additions and 23 deletions
|
@ -1,19 +1,68 @@
|
|||
import esphome.config_validation as cv
|
||||
import esphome.codegen as cg
|
||||
from esphome.const import CONF_ID
|
||||
from esphome.components import sensor, text_sensor
|
||||
from esphome.const import (
|
||||
CONF_ID,
|
||||
CONF_DEVICE,
|
||||
CONF_FREE,
|
||||
CONF_FRAGMENTATION,
|
||||
CONF_BLOCK,
|
||||
CONF_LOOP_TIME,
|
||||
UNIT_MILLISECOND,
|
||||
UNIT_PERCENT,
|
||||
UNIT_BYTES,
|
||||
ICON_COUNTER,
|
||||
ICON_TIMER,
|
||||
)
|
||||
|
||||
CODEOWNERS = ["@OttoWinter"]
|
||||
DEPENDENCIES = ["logger"]
|
||||
|
||||
debug_ns = cg.esphome_ns.namespace("debug")
|
||||
DebugComponent = debug_ns.class_("DebugComponent", cg.Component)
|
||||
DebugComponent = debug_ns.class_("DebugComponent", cg.PollingComponent)
|
||||
|
||||
|
||||
CONFIG_SCHEMA = cv.Schema(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(DebugComponent),
|
||||
cv.Optional(CONF_DEVICE): text_sensor.TEXT_SENSOR_SCHEMA.extend(
|
||||
{cv.GenerateID(): cv.declare_id(text_sensor.TextSensor)}
|
||||
),
|
||||
cv.Optional(CONF_FREE): sensor.sensor_schema(UNIT_BYTES, ICON_COUNTER, 0),
|
||||
cv.Optional(CONF_BLOCK): sensor.sensor_schema(UNIT_BYTES, ICON_COUNTER, 0),
|
||||
cv.Optional(CONF_FRAGMENTATION): cv.All(
|
||||
cv.only_on_esp8266,
|
||||
cv.require_framework_version(esp8266_arduino=cv.Version(2, 5, 2)),
|
||||
sensor.sensor_schema(UNIT_PERCENT, ICON_COUNTER, 1),
|
||||
),
|
||||
cv.Optional(CONF_LOOP_TIME): sensor.sensor_schema(
|
||||
UNIT_MILLISECOND, ICON_TIMER, 1
|
||||
),
|
||||
}
|
||||
).extend(cv.COMPONENT_SCHEMA)
|
||||
).extend(cv.polling_component_schema("60s"))
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
await cg.register_component(var, config)
|
||||
|
||||
if CONF_DEVICE in config:
|
||||
sens = cg.new_Pvariable(config[CONF_DEVICE][CONF_ID])
|
||||
await text_sensor.register_text_sensor(sens, config[CONF_DEVICE])
|
||||
cg.add(var.set_device_info_sensor(sens))
|
||||
|
||||
if CONF_FREE in config:
|
||||
sens = await sensor.new_sensor(config[CONF_FREE])
|
||||
cg.add(var.set_free_sensor(sens))
|
||||
|
||||
if CONF_BLOCK in config:
|
||||
sens = await sensor.new_sensor(config[CONF_BLOCK])
|
||||
cg.add(var.set_block_sensor(sens))
|
||||
|
||||
if CONF_FRAGMENTATION in config:
|
||||
sens = await sensor.new_sensor(config[CONF_FRAGMENTATION])
|
||||
cg.add(var.set_fragmentation_sensor(sens))
|
||||
|
||||
if CONF_LOOP_TIME in config:
|
||||
sens = await sensor.new_sensor(config[CONF_LOOP_TIME])
|
||||
cg.add(var.set_loop_time_sensor(sens))
|
||||
|
|
|
@ -1,21 +1,23 @@
|
|||
#include "debug_component.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include "esphome/core/log.h"
|
||||
#include "esphome/core/hal.h"
|
||||
#include "esphome/core/helpers.h"
|
||||
#include "esphome/core/defines.h"
|
||||
#include "esphome/core/version.h"
|
||||
|
||||
#ifdef USE_ESP_IDF
|
||||
#ifdef USE_ESP32
|
||||
|
||||
#include <esp_heap_caps.h>
|
||||
#include <esp_system.h>
|
||||
#endif
|
||||
|
||||
#ifdef USE_ESP32
|
||||
#if ESP_IDF_VERSION_MAJOR >= 4
|
||||
#include <esp32/rom/rtc.h>
|
||||
#else
|
||||
#include <rom/rtc.h>
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#endif // USE_ESP32
|
||||
|
||||
#ifdef USE_ARDUINO
|
||||
#include <Esp.h>
|
||||
|
@ -26,19 +28,36 @@ namespace debug {
|
|||
|
||||
static const char *const TAG = "debug";
|
||||
|
||||
static uint32_t get_free_heap() {
|
||||
#if defined(USE_ESP8266)
|
||||
return ESP.getFreeHeap(); // NOLINT(readability-static-accessed-through-instance)
|
||||
#elif defined(USE_ESP32)
|
||||
return heap_caps_get_free_size(MALLOC_CAP_INTERNAL);
|
||||
#endif
|
||||
}
|
||||
|
||||
void DebugComponent::dump_config() {
|
||||
std::string device_info;
|
||||
device_info.reserve(256);
|
||||
|
||||
#ifndef ESPHOME_LOG_HAS_DEBUG
|
||||
ESP_LOGE(TAG, "Debug Component requires debug log level!");
|
||||
this->status_set_error();
|
||||
return;
|
||||
#endif
|
||||
|
||||
ESP_LOGD(TAG, "ESPHome version %s", ESPHOME_VERSION);
|
||||
#ifdef USE_ARDUINO
|
||||
this->free_heap_ = ESP.getFreeHeap(); // NOLINT(readability-static-accessed-through-instance)
|
||||
#elif defined(USE_ESP_IDF)
|
||||
this->free_heap_ = heap_caps_get_free_size(MALLOC_CAP_INTERNAL);
|
||||
ESP_LOGCONFIG(TAG, "Debug component:");
|
||||
LOG_TEXT_SENSOR(" ", "Device info", this->device_info_);
|
||||
LOG_SENSOR(" ", "Free space on heap", this->free_sensor_);
|
||||
LOG_SENSOR(" ", "Largest free heap block", this->block_sensor_);
|
||||
#if defined(USE_ESP8266) && ARDUINO_VERSION_CODE >= VERSION_CODE(2, 5, 2)
|
||||
LOG_SENSOR(" ", "Heap fragmentation", this->fragmentation_sensor_);
|
||||
#endif
|
||||
|
||||
ESP_LOGD(TAG, "ESPHome version %s", ESPHOME_VERSION);
|
||||
device_info += ESPHOME_VERSION;
|
||||
|
||||
this->free_heap_ = get_free_heap();
|
||||
ESP_LOGD(TAG, "Free Heap Size: %u bytes", this->free_heap_);
|
||||
|
||||
#ifdef USE_ARDUINO
|
||||
|
@ -67,9 +86,12 @@ void DebugComponent::dump_config() {
|
|||
default:
|
||||
flash_mode = "UNKNOWN";
|
||||
}
|
||||
// NOLINTNEXTLINE(readability-static-accessed-through-instance)
|
||||
ESP_LOGD(TAG, "Flash Chip: Size=%ukB Speed=%uMHz Mode=%s", ESP.getFlashChipSize() / 1024,
|
||||
ESP.getFlashChipSpeed() / 1000000, flash_mode);
|
||||
ESP_LOGD(TAG, "Flash Chip: Size=%ukB Speed=%uMHz Mode=%s",
|
||||
ESP.getFlashChipSize() / 1024, // NOLINT
|
||||
ESP.getFlashChipSpeed() / 1000000, flash_mode); // NOLINT
|
||||
device_info += "|Flash: " + to_string(ESP.getFlashChipSize() / 1024) + // NOLINT
|
||||
"kB Speed:" + to_string(ESP.getFlashChipSpeed() / 1000000) + "MHz Mode:"; // NOLINT
|
||||
device_info += flash_mode;
|
||||
#endif // USE_ARDUINO
|
||||
|
||||
#ifdef USE_ESP32
|
||||
|
@ -104,10 +126,21 @@ void DebugComponent::dump_config() {
|
|||
features += "Other:" + format_hex(info.features);
|
||||
ESP_LOGD(TAG, "Chip: Model=%s, Features=%s Cores=%u, Revision=%u", model, features.c_str(), info.cores,
|
||||
info.revision);
|
||||
device_info += "|Chip: ";
|
||||
device_info += model;
|
||||
device_info += " Features:";
|
||||
device_info += features;
|
||||
device_info += " Cores:" + to_string(info.cores);
|
||||
device_info += " Revision:" + to_string(info.revision);
|
||||
|
||||
ESP_LOGD(TAG, "ESP-IDF Version: %s", esp_get_idf_version());
|
||||
device_info += "|ESP-IDF: ";
|
||||
device_info += esp_get_idf_version();
|
||||
|
||||
ESP_LOGD(TAG, "EFuse MAC: %s", get_mac_address_pretty().c_str());
|
||||
std::string mac = get_mac_address_pretty();
|
||||
ESP_LOGD(TAG, "EFuse MAC: %s", mac.c_str());
|
||||
device_info += "|EFuse MAC: ";
|
||||
device_info += mac;
|
||||
|
||||
const char *reset_reason;
|
||||
switch (rtc_get_reset_reason(0)) {
|
||||
|
@ -160,6 +193,8 @@ void DebugComponent::dump_config() {
|
|||
reset_reason = "Unknown Reset Reason";
|
||||
}
|
||||
ESP_LOGD(TAG, "Reset Reason: %s", reset_reason);
|
||||
device_info += "|Reset: ";
|
||||
device_info += reset_reason;
|
||||
|
||||
const char *wakeup_reason;
|
||||
switch (rtc_get_wakeup_cause()) {
|
||||
|
@ -203,6 +238,8 @@ void DebugComponent::dump_config() {
|
|||
wakeup_reason = "Unknown";
|
||||
}
|
||||
ESP_LOGD(TAG, "Wakeup Reason: %s", wakeup_reason);
|
||||
device_info += "|Wakeup: ";
|
||||
device_info += wakeup_reason;
|
||||
#endif
|
||||
|
||||
#if defined(USE_ESP8266) && !defined(CLANG_TIDY)
|
||||
|
@ -214,20 +251,75 @@ void DebugComponent::dump_config() {
|
|||
ESP_LOGD(TAG, "Flash Chip ID=0x%08X", ESP.getFlashChipId());
|
||||
ESP_LOGD(TAG, "Reset Reason: %s", ESP.getResetReason().c_str());
|
||||
ESP_LOGD(TAG, "Reset Info: %s", ESP.getResetInfo().c_str());
|
||||
|
||||
device_info += "|Chip: 0x" + format_hex(ESP.getChipId());
|
||||
device_info += "|SDK: ";
|
||||
device_info += ESP.getSdkVersion();
|
||||
device_info += "|Core: ";
|
||||
device_info += ESP.getCoreVersion().c_str();
|
||||
device_info += "|Boot: ";
|
||||
device_info += to_string(ESP.getBootVersion());
|
||||
device_info += "|Mode: " + to_string(ESP.getBootMode());
|
||||
device_info += "|CPU: " + to_string(ESP.getCpuFreqMHz());
|
||||
device_info += "|Flash: 0x" + format_hex(ESP.getFlashChipId());
|
||||
device_info += "|Reset: ";
|
||||
device_info += ESP.getResetReason().c_str();
|
||||
device_info += "|";
|
||||
device_info += ESP.getResetInfo().c_str();
|
||||
#endif
|
||||
|
||||
if (this->device_info_ != nullptr) {
|
||||
if (device_info.length() > 255)
|
||||
device_info.resize(255);
|
||||
this->device_info_->publish_state(device_info);
|
||||
}
|
||||
}
|
||||
|
||||
void DebugComponent::loop() {
|
||||
#ifdef USE_ARDUINO
|
||||
uint32_t new_free_heap = ESP.getFreeHeap(); // NOLINT(readability-static-accessed-through-instance)
|
||||
#elif defined(USE_ESP_IDF)
|
||||
uint32_t new_free_heap = heap_caps_get_free_size(MALLOC_CAP_INTERNAL);
|
||||
#endif
|
||||
// log when free heap space has halved
|
||||
uint32_t new_free_heap = get_free_heap();
|
||||
if (new_free_heap < this->free_heap_ / 2) {
|
||||
this->free_heap_ = new_free_heap;
|
||||
ESP_LOGD(TAG, "Free Heap Size: %u bytes", this->free_heap_);
|
||||
this->status_momentary_warning("heap", 1000);
|
||||
}
|
||||
|
||||
// calculate loop time - from last call to this one
|
||||
if (this->loop_time_sensor_ != nullptr) {
|
||||
uint32_t now = millis();
|
||||
uint32_t loop_time = now - this->last_loop_timetag_;
|
||||
this->max_loop_time_ = std::max(this->max_loop_time_, loop_time);
|
||||
this->last_loop_timetag_ = now;
|
||||
}
|
||||
}
|
||||
|
||||
void DebugComponent::update() {
|
||||
if (this->free_sensor_ != nullptr) {
|
||||
this->free_sensor_->publish_state(get_free_heap());
|
||||
}
|
||||
|
||||
if (this->block_sensor_ != nullptr) {
|
||||
#if defined(USE_ESP8266)
|
||||
// NOLINTNEXTLINE(readability-static-accessed-through-instance)
|
||||
this->block_sensor_->publish_state(ESP.getMaxFreeBlockSize());
|
||||
#elif defined(USE_ESP32)
|
||||
this->block_sensor_->publish_state(heap_caps_get_largest_free_block(MALLOC_CAP_INTERNAL));
|
||||
#endif
|
||||
}
|
||||
|
||||
#if defined(USE_ESP8266) && ARDUINO_VERSION_CODE >= VERSION_CODE(2, 5, 2)
|
||||
if (this->fragmentation_sensor_ != nullptr) {
|
||||
// NOLINTNEXTLINE(readability-static-accessed-through-instance)
|
||||
this->fragmentation_sensor_->publish_state(ESP.getHeapFragmentation());
|
||||
}
|
||||
#endif
|
||||
|
||||
if (this->loop_time_sensor_ != nullptr) {
|
||||
this->loop_time_sensor_->publish_state(this->max_loop_time_);
|
||||
this->max_loop_time_ = 0;
|
||||
}
|
||||
}
|
||||
|
||||
float DebugComponent::get_setup_priority() const { return setup_priority::LATE; }
|
||||
|
||||
} // namespace debug
|
||||
|
|
|
@ -1,18 +1,42 @@
|
|||
#pragma once
|
||||
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/core/macros.h"
|
||||
#include "esphome/core/helpers.h"
|
||||
#include "esphome/components/text_sensor/text_sensor.h"
|
||||
#include "esphome/components/sensor/sensor.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace debug {
|
||||
|
||||
class DebugComponent : public Component {
|
||||
class DebugComponent : public PollingComponent {
|
||||
public:
|
||||
void loop() override;
|
||||
void update() override;
|
||||
float get_setup_priority() const override;
|
||||
void dump_config() override;
|
||||
|
||||
void set_device_info_sensor(text_sensor::TextSensor *device_info) { device_info_ = device_info; }
|
||||
void set_free_sensor(sensor::Sensor *free_sensor) { free_sensor_ = free_sensor; }
|
||||
void set_block_sensor(sensor::Sensor *block_sensor) { block_sensor_ = block_sensor; }
|
||||
#if defined(USE_ESP8266) && ARDUINO_VERSION_CODE >= VERSION_CODE(2, 5, 2)
|
||||
void set_fragmentation_sensor(sensor::Sensor *fragmentation_sensor) { fragmentation_sensor_ = fragmentation_sensor; }
|
||||
#endif
|
||||
void set_loop_time_sensor(sensor::Sensor *loop_time_sensor) { loop_time_sensor_ = loop_time_sensor; }
|
||||
|
||||
protected:
|
||||
uint32_t free_heap_{};
|
||||
|
||||
uint32_t last_loop_timetag_{0};
|
||||
uint32_t max_loop_time_{0};
|
||||
|
||||
text_sensor::TextSensor *device_info_{nullptr};
|
||||
sensor::Sensor *free_sensor_{nullptr};
|
||||
sensor::Sensor *block_sensor_{nullptr};
|
||||
#if defined(USE_ESP8266) && ARDUINO_VERSION_CODE >= VERSION_CODE(2, 5, 2)
|
||||
sensor::Sensor *fragmentation_sensor_{nullptr};
|
||||
#endif
|
||||
sensor::Sensor *loop_time_sensor_{nullptr};
|
||||
};
|
||||
|
||||
} // namespace debug
|
||||
|
|
|
@ -62,6 +62,7 @@ CONF_BINARY_SENSORS = "binary_sensors"
|
|||
CONF_BINDKEY = "bindkey"
|
||||
CONF_BIRTH_MESSAGE = "birth_message"
|
||||
CONF_BIT_DEPTH = "bit_depth"
|
||||
CONF_BLOCK = "block"
|
||||
CONF_BLUE = "blue"
|
||||
CONF_BOARD = "board"
|
||||
CONF_BOARD_FLASH_MODE = "board_flash_mode"
|
||||
|
@ -239,7 +240,9 @@ CONF_FORCE_UPDATE = "force_update"
|
|||
CONF_FORMALDEHYDE = "formaldehyde"
|
||||
CONF_FORMAT = "format"
|
||||
CONF_FORWARD_ACTIVE_ENERGY = "forward_active_energy"
|
||||
CONF_FRAGMENTATION = "fragmentation"
|
||||
CONF_FRAMEWORK = "framework"
|
||||
CONF_FREE = "free"
|
||||
CONF_FREQUENCY = "frequency"
|
||||
CONF_FROM = "from"
|
||||
CONF_FULL_SPECTRUM = "full_spectrum"
|
||||
|
@ -335,6 +338,7 @@ CONF_LOG_TOPIC = "log_topic"
|
|||
CONF_LOGGER = "logger"
|
||||
CONF_LOGS = "logs"
|
||||
CONF_LONGITUDE = "longitude"
|
||||
CONF_LOOP_TIME = "loop_time"
|
||||
CONF_LOW = "low"
|
||||
CONF_LOW_VOLTAGE_REFERENCE = "low_voltage_reference"
|
||||
CONF_MAC_ADDRESS = "mac_address"
|
||||
|
@ -806,6 +810,7 @@ ICON_WIFI = "mdi:wifi"
|
|||
|
||||
UNIT_AMPERE = "A"
|
||||
UNIT_BECQUEREL_PER_CUBIC_METER = "Bq/m³"
|
||||
UNIT_BYTES = "B"
|
||||
UNIT_CELSIUS = "°C"
|
||||
UNIT_COUNT_DECILITRE = "/dL"
|
||||
UNIT_COUNTS_PER_CUBIC_METER = "#/m³"
|
||||
|
@ -834,6 +839,7 @@ UNIT_MICROMETER = "µm"
|
|||
UNIT_MICROSIEMENS_PER_CENTIMETER = "µS/cm"
|
||||
UNIT_MICROTESLA = "µT"
|
||||
UNIT_MILLIGRAMS_PER_CUBIC_METER = "mg/m³"
|
||||
UNIT_MILLISECOND = "ms"
|
||||
UNIT_MINUTE = "min"
|
||||
UNIT_OHM = "Ω"
|
||||
UNIT_PARTS_PER_BILLION = "ppb"
|
||||
|
|
Loading…
Reference in a new issue