mirror of
https://github.com/esphome/esphome.git
synced 2026-10-10 21:39:06 +00:00
[core] Bind InternalGPIOPin to the platform pin class instead of a virtual base (#20455)
This commit is contained in:
@@ -15,7 +15,6 @@ from esphome.components.const import (
|
||||
CONF_ENABLE_OTA_DOWNGRADE_PROTECTION,
|
||||
CONF_IGNORE_NOT_FOUND,
|
||||
)
|
||||
from esphome.config_helpers import filter_source_files_from_defines
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import (
|
||||
CONF_ADVANCED,
|
||||
@@ -3967,10 +3966,3 @@ def process_stacktrace(config, line, backtrace_state):
|
||||
_decode_pc(config, addr.group())
|
||||
|
||||
return backtrace_state
|
||||
|
||||
|
||||
# gpio.cpp only implements ESP32InternalGPIOPin and its ISR helpers, which
|
||||
# are instantiated solely by the pin schema codegen (esp32_pin_to_code)
|
||||
FILTER_SOURCE_FILES = filter_source_files_from_defines(
|
||||
{"gpio.cpp": "USE_ESP32_INTERNAL_GPIO"}
|
||||
)
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
#include "esphome/core/defines.h"
|
||||
// Also defines the core ISRInternalGPIOPin methods; those are only reachable
|
||||
// via ESP32InternalGPIOPin::to_isr(), so the same define gates both safely.
|
||||
#if defined(USE_ESP32) && defined(USE_ESP32_INTERNAL_GPIO)
|
||||
// Always built: InternalGPIOPin is this class, so components that merely hold a
|
||||
// pointer to it reference these methods even in configs without a pin.
|
||||
#ifdef USE_ESP32
|
||||
|
||||
#include "gpio.h"
|
||||
#include "esphome/core/hal.h"
|
||||
#include "esphome/core/log.h"
|
||||
#include "driver/gpio.h"
|
||||
#include "driver/rtc_io.h"
|
||||
@@ -68,7 +69,7 @@ ISRInternalGPIOPin ESP32InternalGPIOPin::to_isr() const {
|
||||
return ISRInternalGPIOPin((void *) arg);
|
||||
}
|
||||
|
||||
void ESP32InternalGPIOPin::attach_interrupt(void (*func)(void *), void *arg, gpio::InterruptType type) const {
|
||||
void ESP32InternalGPIOPin::attach_interrupt_(void (*func)(void *), void *arg, gpio::InterruptType type) const {
|
||||
gpio_int_type_t idf_type = GPIO_INTR_ANYEDGE;
|
||||
switch (type) {
|
||||
case gpio::INTERRUPT_RISING_EDGE:
|
||||
@@ -207,4 +208,4 @@ void IRAM_ATTR ISRInternalGPIOPin::pin_mode(gpio::Flags flags) {
|
||||
|
||||
} // namespace esphome
|
||||
|
||||
#endif // USE_ESP32 && USE_ESP32_INTERNAL_GPIO
|
||||
#endif // USE_ESP32
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef USE_ESP32
|
||||
#include "esphome/core/hal.h"
|
||||
#include "esphome/core/gpio_pin.h"
|
||||
#include <esp_system.h>
|
||||
#include <driver/gpio.h>
|
||||
|
||||
namespace esphome::esp32 {
|
||||
@@ -10,7 +11,7 @@ namespace esphome::esp32 {
|
||||
static_assert(GPIO_NUM_MAX <= 256, "gpio_num_t has too many values for uint8_t");
|
||||
static_assert(GPIO_DRIVE_CAP_MAX <= 4, "gpio_drive_cap_t has too many values for 2-bit field");
|
||||
|
||||
class ESP32InternalGPIOPin final : public InternalGPIOPin {
|
||||
class ESP32InternalGPIOPin final : public GPIOPin {
|
||||
public:
|
||||
void set_pin(gpio_num_t pin) { this->pin_ = static_cast<uint8_t>(pin); }
|
||||
void set_inverted(bool inverted) { this->pin_flags_.inverted = inverted; }
|
||||
@@ -24,11 +25,15 @@ class ESP32InternalGPIOPin final : public InternalGPIOPin {
|
||||
bool digital_read() override;
|
||||
void digital_write(bool value) override;
|
||||
size_t dump_summary(char *buffer, size_t len) const override;
|
||||
void detach_interrupt() const override;
|
||||
ISRInternalGPIOPin to_isr() const override;
|
||||
uint8_t get_pin() const override { return this->pin_; }
|
||||
void detach_interrupt() const;
|
||||
template<typename T> void attach_interrupt(void (*func)(T *), T *arg, gpio::InterruptType type) const {
|
||||
this->attach_interrupt_(reinterpret_cast<void (*)(void *)>(func), arg, type);
|
||||
}
|
||||
ISRInternalGPIOPin to_isr() const;
|
||||
uint8_t get_pin() const { return this->pin_; }
|
||||
gpio::Flags get_flags() const override { return this->flags_; }
|
||||
bool is_inverted() const override { return this->pin_flags_.inverted; }
|
||||
bool is_inverted() const { return this->pin_flags_.inverted; }
|
||||
bool is_internal() override { return true; }
|
||||
gpio_num_t get_pin_num() const { return static_cast<gpio_num_t>(this->pin_); }
|
||||
gpio_drive_cap_t get_drive_strength() const { return static_cast<gpio_drive_cap_t>(this->pin_flags_.drive_strength); }
|
||||
#ifdef USE_GPIO_HOLD
|
||||
@@ -41,7 +46,7 @@ class ESP32InternalGPIOPin final : public InternalGPIOPin {
|
||||
#endif
|
||||
|
||||
protected:
|
||||
void attach_interrupt(void (*func)(void *), void *arg, gpio::InterruptType type) const override;
|
||||
void attach_interrupt_(void (*func)(void *), void *arg, gpio::InterruptType type) const;
|
||||
|
||||
// Memory layout: 8 bytes total on 32-bit systems
|
||||
// - 3 bytes for members below
|
||||
|
||||
@@ -257,7 +257,6 @@ ESP32_PIN_SCHEMA = cv.All(
|
||||
|
||||
@pins.PIN_SCHEMA_REGISTRY.register(PLATFORM_ESP32, ESP32_PIN_SCHEMA)
|
||||
async def esp32_pin_to_code(config):
|
||||
cg.add_define("USE_ESP32_INTERNAL_GPIO")
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
num = config[CONF_NUMBER]
|
||||
cg.add(var.set_pin(getattr(gpio_num_t, f"GPIO_NUM_{num}")))
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#ifdef USE_ESP8266
|
||||
|
||||
#include "gpio.h"
|
||||
#include "esphome/core/hal.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome::esp8266 {
|
||||
@@ -74,7 +75,7 @@ ISRInternalGPIOPin ESP8266GPIOPin::to_isr() const {
|
||||
return ISRInternalGPIOPin((void *) arg);
|
||||
}
|
||||
|
||||
void ESP8266GPIOPin::attach_interrupt(void (*func)(void *), void *arg, gpio::InterruptType type) const {
|
||||
void ESP8266GPIOPin::attach_interrupt_(void (*func)(void *), void *arg, gpio::InterruptType type) const {
|
||||
uint8_t arduino_mode = 0;
|
||||
switch (type) {
|
||||
case gpio::INTERRUPT_RISING_EDGE:
|
||||
|
||||
@@ -2,12 +2,12 @@
|
||||
|
||||
#ifdef USE_ESP8266
|
||||
|
||||
#include "esphome/core/hal.h"
|
||||
#include "esphome/core/gpio_pin.h"
|
||||
#include <Arduino.h>
|
||||
|
||||
namespace esphome::esp8266 {
|
||||
|
||||
class ESP8266GPIOPin final : public InternalGPIOPin {
|
||||
class ESP8266GPIOPin final : public GPIOPin {
|
||||
public:
|
||||
void set_pin(uint8_t pin) { pin_ = pin; }
|
||||
void set_inverted(bool inverted) { inverted_ = inverted; }
|
||||
@@ -18,14 +18,18 @@ class ESP8266GPIOPin final : public InternalGPIOPin {
|
||||
bool digital_read() override;
|
||||
void digital_write(bool value) override;
|
||||
size_t dump_summary(char *buffer, size_t len) const override;
|
||||
void detach_interrupt() const override;
|
||||
ISRInternalGPIOPin to_isr() const override;
|
||||
uint8_t get_pin() const override { return pin_; }
|
||||
void detach_interrupt() const;
|
||||
template<typename T> void attach_interrupt(void (*func)(T *), T *arg, gpio::InterruptType type) const {
|
||||
this->attach_interrupt_(reinterpret_cast<void (*)(void *)>(func), arg, type);
|
||||
}
|
||||
ISRInternalGPIOPin to_isr() const;
|
||||
uint8_t get_pin() const { return pin_; }
|
||||
gpio::Flags get_flags() const override { return flags_; }
|
||||
bool is_inverted() const override { return inverted_; }
|
||||
bool is_inverted() const { return inverted_; }
|
||||
bool is_internal() override { return true; }
|
||||
|
||||
protected:
|
||||
void attach_interrupt(void (*func)(void *), void *arg, gpio::InterruptType type) const override;
|
||||
void attach_interrupt_(void (*func)(void *), void *arg, gpio::InterruptType type) const;
|
||||
|
||||
uint8_t pin_;
|
||||
bool inverted_{};
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#ifdef USE_HOST
|
||||
|
||||
#include "gpio.h"
|
||||
#include "esphome/core/hal.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome {
|
||||
@@ -20,7 +21,7 @@ ISRInternalGPIOPin HostGPIOPin::to_isr() const {
|
||||
return ISRInternalGPIOPin((void *) arg);
|
||||
}
|
||||
|
||||
void HostGPIOPin::attach_interrupt(void (*func)(void *), void *arg, gpio::InterruptType type) const {
|
||||
void HostGPIOPin::attach_interrupt_(void (*func)(void *), void *arg, gpio::InterruptType type) const {
|
||||
ESP_LOGD(TAG, "Attaching interrupt %p to pin %d and mode %d", func, pin_, (uint32_t) type);
|
||||
}
|
||||
void HostGPIOPin::pin_mode(gpio::Flags flags) { ESP_LOGD(TAG, "Setting pin %d mode to %02X", pin_, (uint32_t) flags); }
|
||||
|
||||
@@ -2,11 +2,11 @@
|
||||
|
||||
#ifdef USE_HOST
|
||||
|
||||
#include "esphome/core/hal.h"
|
||||
#include "esphome/core/gpio_pin.h"
|
||||
|
||||
namespace esphome::host {
|
||||
|
||||
class HostGPIOPin final : public InternalGPIOPin {
|
||||
class HostGPIOPin final : public GPIOPin {
|
||||
public:
|
||||
void set_pin(uint8_t pin) { pin_ = pin; }
|
||||
void set_inverted(bool inverted) { inverted_ = inverted; }
|
||||
@@ -17,14 +17,18 @@ class HostGPIOPin final : public InternalGPIOPin {
|
||||
bool digital_read() override;
|
||||
void digital_write(bool value) override;
|
||||
size_t dump_summary(char *buffer, size_t len) const override;
|
||||
void detach_interrupt() const override;
|
||||
ISRInternalGPIOPin to_isr() const override;
|
||||
uint8_t get_pin() const override { return pin_; }
|
||||
void detach_interrupt() const;
|
||||
template<typename T> void attach_interrupt(void (*func)(T *), T *arg, gpio::InterruptType type) const {
|
||||
this->attach_interrupt_(reinterpret_cast<void (*)(void *)>(func), arg, type);
|
||||
}
|
||||
ISRInternalGPIOPin to_isr() const;
|
||||
uint8_t get_pin() const { return pin_; }
|
||||
gpio::Flags get_flags() const override { return flags_; }
|
||||
bool is_inverted() const override { return inverted_; }
|
||||
bool is_inverted() const { return inverted_; }
|
||||
bool is_internal() override { return true; }
|
||||
|
||||
protected:
|
||||
void attach_interrupt(void (*func)(void *), void *arg, gpio::InterruptType type) const override;
|
||||
void attach_interrupt_(void (*func)(void *), void *arg, gpio::InterruptType type) const;
|
||||
|
||||
uint8_t pin_;
|
||||
bool inverted_{};
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#ifdef USE_LIBRETINY
|
||||
|
||||
#include "gpio_arduino.h"
|
||||
#include "esphome/core/hal.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome::libretiny {
|
||||
@@ -37,7 +38,7 @@ ISRInternalGPIOPin ArduinoInternalGPIOPin::to_isr() const {
|
||||
return ISRInternalGPIOPin((void *) arg);
|
||||
}
|
||||
|
||||
void ArduinoInternalGPIOPin::attach_interrupt(void (*func)(void *), void *arg, gpio::InterruptType type) const {
|
||||
void ArduinoInternalGPIOPin::attach_interrupt_(void (*func)(void *), void *arg, gpio::InterruptType type) const {
|
||||
PinStatus arduino_mode = (PinStatus) 255;
|
||||
switch (type) {
|
||||
case gpio::INTERRUPT_RISING_EDGE:
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef USE_LIBRETINY
|
||||
#include "esphome/core/hal.h"
|
||||
#include "esphome/core/gpio_pin.h"
|
||||
|
||||
namespace esphome::libretiny {
|
||||
|
||||
class ArduinoInternalGPIOPin final : public InternalGPIOPin {
|
||||
class ArduinoInternalGPIOPin final : public GPIOPin {
|
||||
public:
|
||||
void set_pin(uint8_t pin) { pin_ = pin; }
|
||||
void set_inverted(bool inverted) { inverted_ = inverted; }
|
||||
@@ -16,14 +16,18 @@ class ArduinoInternalGPIOPin final : public InternalGPIOPin {
|
||||
bool digital_read() override;
|
||||
void digital_write(bool value) override;
|
||||
size_t dump_summary(char *buffer, size_t len) const override;
|
||||
void detach_interrupt() const override;
|
||||
ISRInternalGPIOPin to_isr() const override;
|
||||
uint8_t get_pin() const override { return pin_; }
|
||||
void detach_interrupt() const;
|
||||
template<typename T> void attach_interrupt(void (*func)(T *), T *arg, gpio::InterruptType type) const {
|
||||
this->attach_interrupt_(reinterpret_cast<void (*)(void *)>(func), arg, type);
|
||||
}
|
||||
ISRInternalGPIOPin to_isr() const;
|
||||
uint8_t get_pin() const { return pin_; }
|
||||
gpio::Flags get_flags() const override { return flags_; }
|
||||
bool is_inverted() const override { return inverted_; }
|
||||
bool is_inverted() const { return inverted_; }
|
||||
bool is_internal() override { return true; }
|
||||
|
||||
protected:
|
||||
void attach_interrupt(void (*func)(void *), void *arg, gpio::InterruptType type) const override;
|
||||
void attach_interrupt_(void (*func)(void *), void *arg, gpio::InterruptType type) const;
|
||||
|
||||
uint8_t pin_;
|
||||
bool inverted_{};
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#ifdef USE_RP2
|
||||
|
||||
#include "gpio.h"
|
||||
#include "esphome/core/hal.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome {
|
||||
@@ -40,7 +41,7 @@ ISRInternalGPIOPin RP2GPIOPin::to_isr() const {
|
||||
return ISRInternalGPIOPin((void *) arg);
|
||||
}
|
||||
|
||||
void RP2GPIOPin::attach_interrupt(void (*func)(void *), void *arg, gpio::InterruptType type) const {
|
||||
void RP2GPIOPin::attach_interrupt_(void (*func)(void *), void *arg, gpio::InterruptType type) const {
|
||||
PinStatus arduino_mode = LOW;
|
||||
switch (type) {
|
||||
case gpio::INTERRUPT_RISING_EDGE:
|
||||
|
||||
@@ -3,11 +3,11 @@
|
||||
#ifdef USE_RP2
|
||||
|
||||
#include <Arduino.h>
|
||||
#include "esphome/core/hal.h"
|
||||
#include "esphome/core/gpio_pin.h"
|
||||
|
||||
namespace esphome::rp2 {
|
||||
|
||||
class RP2GPIOPin final : public InternalGPIOPin {
|
||||
class RP2GPIOPin final : public GPIOPin {
|
||||
public:
|
||||
void set_pin(uint8_t pin) { pin_ = pin; }
|
||||
void set_inverted(bool inverted) { inverted_ = inverted; }
|
||||
@@ -18,14 +18,18 @@ class RP2GPIOPin final : public InternalGPIOPin {
|
||||
bool digital_read() override;
|
||||
void digital_write(bool value) override;
|
||||
size_t dump_summary(char *buffer, size_t len) const override;
|
||||
void detach_interrupt() const override;
|
||||
ISRInternalGPIOPin to_isr() const override;
|
||||
uint8_t get_pin() const override { return pin_; }
|
||||
void detach_interrupt() const;
|
||||
template<typename T> void attach_interrupt(void (*func)(T *), T *arg, gpio::InterruptType type) const {
|
||||
this->attach_interrupt_(reinterpret_cast<void (*)(void *)>(func), arg, type);
|
||||
}
|
||||
ISRInternalGPIOPin to_isr() const;
|
||||
uint8_t get_pin() const { return pin_; }
|
||||
gpio::Flags get_flags() const override { return flags_; }
|
||||
bool is_inverted() const override { return inverted_; }
|
||||
bool is_inverted() const { return inverted_; }
|
||||
bool is_internal() override { return true; }
|
||||
|
||||
protected:
|
||||
void attach_interrupt(void (*func)(void *), void *arg, gpio::InterruptType type) const override;
|
||||
void attach_interrupt_(void (*func)(void *), void *arg, gpio::InterruptType type) const;
|
||||
|
||||
uint8_t pin_;
|
||||
bool inverted_{};
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#ifdef USE_ZEPHYR
|
||||
#include "gpio.h"
|
||||
#include "esphome/core/hal.h"
|
||||
#include <zephyr/drivers/gpio.h>
|
||||
#include <zephyr/sys/util.h>
|
||||
#include "esphome/core/log.h"
|
||||
@@ -79,7 +80,7 @@ ISRInternalGPIOPin ZephyrGPIOPin::to_isr() const {
|
||||
return ISRInternalGPIOPin((void *) arg);
|
||||
}
|
||||
|
||||
void ZephyrGPIOPin::attach_interrupt(void (*func)(void *), void *arg, gpio::InterruptType type) const {
|
||||
void ZephyrGPIOPin::attach_interrupt_(void (*func)(void *), void *arg, gpio::InterruptType type) const {
|
||||
if (!device_is_ready(this->gpio_)) {
|
||||
ESP_LOGE(TAG, "Cannot attach interrupt: GPIO device not ready");
|
||||
return;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef USE_ZEPHYR
|
||||
#include "esphome/core/hal.h"
|
||||
#include "esphome/core/gpio_pin.h"
|
||||
#include <zephyr/device.h>
|
||||
#include <zephyr/drivers/gpio.h>
|
||||
namespace esphome::zephyr {
|
||||
@@ -16,7 +16,7 @@ struct ZephyrGPIOInterrupt {
|
||||
void *arg{nullptr};
|
||||
};
|
||||
|
||||
class ZephyrGPIOPin final : public InternalGPIOPin {
|
||||
class ZephyrGPIOPin final : public GPIOPin {
|
||||
public:
|
||||
ZephyrGPIOPin(const device *gpio, int gpio_size, const char *pin_name_prefix) {
|
||||
this->gpio_ = gpio;
|
||||
@@ -32,14 +32,18 @@ class ZephyrGPIOPin final : public InternalGPIOPin {
|
||||
bool digital_read() override;
|
||||
void digital_write(bool value) override;
|
||||
size_t dump_summary(char *buffer, size_t len) const override;
|
||||
void detach_interrupt() const override;
|
||||
ISRInternalGPIOPin to_isr() const override;
|
||||
uint8_t get_pin() const override { return this->pin_; }
|
||||
bool is_inverted() const override { return this->inverted_; }
|
||||
void detach_interrupt() const;
|
||||
template<typename T> void attach_interrupt(void (*func)(T *), T *arg, gpio::InterruptType type) const {
|
||||
this->attach_interrupt_(reinterpret_cast<void (*)(void *)>(func), arg, type);
|
||||
}
|
||||
ISRInternalGPIOPin to_isr() const;
|
||||
uint8_t get_pin() const { return this->pin_; }
|
||||
bool is_inverted() const { return this->inverted_; }
|
||||
bool is_internal() override { return true; }
|
||||
gpio::Flags get_flags() const override { return flags_; }
|
||||
|
||||
protected:
|
||||
void attach_interrupt(void (*func)(void *), void *arg, gpio::InterruptType type) const override;
|
||||
void attach_interrupt_(void (*func)(void *), void *arg, gpio::InterruptType type) const;
|
||||
const device *gpio_{nullptr};
|
||||
const char *pin_name_prefix_{nullptr};
|
||||
gpio::Flags flags_{};
|
||||
@@ -48,8 +52,7 @@ class ZephyrGPIOPin final : public InternalGPIOPin {
|
||||
bool inverted_{};
|
||||
bool value_{false};
|
||||
|
||||
// attach_interrupt()/detach_interrupt() are const (matching the base class), so
|
||||
// the interrupt state they manage has to be mutable.
|
||||
// attach_interrupt_()/detach_interrupt() are const, so their interrupt state is mutable.
|
||||
mutable ZephyrGPIOInterrupt interrupt_{};
|
||||
};
|
||||
|
||||
|
||||
@@ -364,7 +364,6 @@
|
||||
#ifdef USE_ESP32
|
||||
#define USE_ESP32_CRASH_HANDLER
|
||||
#define USE_ESP32_VASPRINTF_STUB
|
||||
#define USE_ESP32_INTERNAL_GPIO
|
||||
#define USE_MQTT_IDF_ENQUEUE
|
||||
#define USE_ESPHOME_TASK_LOG_BUFFER
|
||||
#define ESPHOME_TASK_LOG_BUFFER_SIZE 768
|
||||
|
||||
+39
-145
@@ -1,150 +1,44 @@
|
||||
#pragma once
|
||||
#include <algorithm>
|
||||
#include <cstdint>
|
||||
#include "esphome/core/gpio_pin.h"
|
||||
|
||||
#include "esphome/core/defines.h"
|
||||
#include "esphome/core/helpers.h"
|
||||
#include "esphome/core/log.h"
|
||||
// Each platform has exactly one internal pin class, so InternalGPIOPin is an alias for
|
||||
// it instead of an abstract base class: calls through the alias are direct and can be inlined.
|
||||
#if defined(USE_ESP32)
|
||||
#include "esphome/components/esp32/gpio.h"
|
||||
namespace esphome {
|
||||
using InternalGPIOPin = esp32::ESP32InternalGPIOPin;
|
||||
}
|
||||
#elif defined(USE_ESP8266)
|
||||
#include "esphome/components/esp8266/gpio.h"
|
||||
namespace esphome {
|
||||
using InternalGPIOPin = esp8266::ESP8266GPIOPin;
|
||||
}
|
||||
#elif defined(USE_LIBRETINY)
|
||||
#include "esphome/components/libretiny/gpio_arduino.h"
|
||||
namespace esphome {
|
||||
using InternalGPIOPin = libretiny::ArduinoInternalGPIOPin;
|
||||
}
|
||||
#elif defined(USE_RP2)
|
||||
#include "esphome/components/rp2/gpio.h"
|
||||
namespace esphome {
|
||||
using InternalGPIOPin = rp2::RP2GPIOPin;
|
||||
}
|
||||
#elif defined(USE_HOST)
|
||||
#include "esphome/components/host/gpio.h"
|
||||
namespace esphome {
|
||||
using InternalGPIOPin = host::HostGPIOPin;
|
||||
}
|
||||
#elif defined(USE_ZEPHYR)
|
||||
#include "esphome/components/zephyr/gpio.h"
|
||||
namespace esphome {
|
||||
using InternalGPIOPin = zephyr::ZephyrGPIOPin;
|
||||
}
|
||||
#else
|
||||
#error "gpio.h: no internal pin class for this platform"
|
||||
#endif
|
||||
|
||||
namespace esphome {
|
||||
|
||||
/// Maximum buffer size for dump_summary output
|
||||
inline constexpr size_t GPIO_SUMMARY_MAX_LEN = 48;
|
||||
|
||||
#ifdef USE_ESP8266
|
||||
#define LOG_PIN(prefix, pin) log_pin(TAG, F(prefix), pin)
|
||||
#else
|
||||
#define LOG_PIN(prefix, pin) log_pin(TAG, prefix, pin)
|
||||
#endif
|
||||
|
||||
// put GPIO flags in a namespace to not pollute esphome namespace
|
||||
namespace gpio {
|
||||
|
||||
enum Flags : uint8_t {
|
||||
// Can't name these just INPUT because of Arduino defines :(
|
||||
FLAG_NONE = 0x00,
|
||||
FLAG_INPUT = 0x01,
|
||||
FLAG_OUTPUT = 0x02,
|
||||
FLAG_OPEN_DRAIN = 0x04,
|
||||
FLAG_PULLUP = 0x08,
|
||||
FLAG_PULLDOWN = 0x10,
|
||||
FLAG_HOLD = 0x20,
|
||||
};
|
||||
|
||||
class FlagsHelper {
|
||||
public:
|
||||
constexpr FlagsHelper(Flags val) : val_(val) {}
|
||||
constexpr operator Flags() const { return val_; }
|
||||
|
||||
protected:
|
||||
Flags val_;
|
||||
};
|
||||
constexpr FlagsHelper operator&(Flags lhs, Flags rhs) {
|
||||
return static_cast<Flags>(static_cast<uint8_t>(lhs) & static_cast<uint8_t>(rhs));
|
||||
}
|
||||
constexpr FlagsHelper operator|(Flags lhs, Flags rhs) {
|
||||
return static_cast<Flags>(static_cast<uint8_t>(lhs) | static_cast<uint8_t>(rhs));
|
||||
}
|
||||
|
||||
enum InterruptType : uint8_t {
|
||||
INTERRUPT_RISING_EDGE = 1,
|
||||
INTERRUPT_FALLING_EDGE = 2,
|
||||
INTERRUPT_ANY_EDGE = 3,
|
||||
INTERRUPT_LOW_LEVEL = 4,
|
||||
INTERRUPT_HIGH_LEVEL = 5,
|
||||
};
|
||||
|
||||
} // namespace gpio
|
||||
|
||||
class GPIOPin {
|
||||
public:
|
||||
virtual void setup() = 0;
|
||||
|
||||
virtual void pin_mode(gpio::Flags flags) = 0;
|
||||
|
||||
/**
|
||||
* @brief Retrieve GPIO pin flags.
|
||||
*
|
||||
* @return The GPIO flags describing the pin mode and properties.
|
||||
*/
|
||||
virtual gpio::Flags get_flags() const = 0;
|
||||
|
||||
virtual bool digital_read() = 0;
|
||||
|
||||
virtual void digital_write(bool value) = 0;
|
||||
|
||||
/// Write a summary of this pin to the provided buffer.
|
||||
/// @param buffer The buffer to write to
|
||||
/// @param len The size of the buffer (must be > 0)
|
||||
/// @return The number of characters that would be written (excluding null terminator),
|
||||
/// which may exceed len-1 if truncation occurred (snprintf semantics)
|
||||
virtual size_t dump_summary(char *buffer, size_t len) const;
|
||||
|
||||
virtual bool is_internal() { return false; }
|
||||
#ifdef USE_GPIO_HOLD
|
||||
bool get_hold() const { return this->get_flags() & gpio::FLAG_HOLD; }
|
||||
virtual bool is_held() const { return false; }
|
||||
#else
|
||||
bool get_hold() const { return false; }
|
||||
bool is_held() const { return false; }
|
||||
#endif
|
||||
};
|
||||
|
||||
/// Copy of GPIOPin that is safe to use from ISRs (with no virtual functions)
|
||||
class ISRInternalGPIOPin {
|
||||
public:
|
||||
ISRInternalGPIOPin() = default;
|
||||
ISRInternalGPIOPin(void *arg) : arg_(arg) {}
|
||||
bool digital_read();
|
||||
void digital_write(bool value);
|
||||
void clear_interrupt();
|
||||
void pin_mode(gpio::Flags flags);
|
||||
|
||||
protected:
|
||||
void *arg_{nullptr};
|
||||
};
|
||||
|
||||
class InternalGPIOPin : public GPIOPin {
|
||||
public:
|
||||
template<typename T> void attach_interrupt(void (*func)(T *), T *arg, gpio::InterruptType type) const {
|
||||
this->attach_interrupt(reinterpret_cast<void (*)(void *)>(func), arg, type);
|
||||
}
|
||||
|
||||
virtual void detach_interrupt() const = 0;
|
||||
|
||||
virtual ISRInternalGPIOPin to_isr() const = 0;
|
||||
|
||||
virtual uint8_t get_pin() const = 0;
|
||||
|
||||
bool is_internal() override { return true; }
|
||||
|
||||
virtual bool is_inverted() const = 0;
|
||||
|
||||
protected:
|
||||
virtual void attach_interrupt(void (*func)(void *), void *arg, gpio::InterruptType type) const = 0;
|
||||
};
|
||||
|
||||
// Inline default implementation for GPIOPin::dump_summary.
|
||||
// Writes an empty summary; subclasses override to provide pin details.
|
||||
inline size_t GPIOPin::dump_summary(char *buffer, size_t len) const {
|
||||
if (len > 0)
|
||||
buffer[0] = '\0';
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Inline helper for log_pin - allows compiler to inline into log_pin in gpio.cpp
|
||||
inline void log_pin_with_prefix(const char *tag, const char *prefix, GPIOPin *pin) {
|
||||
char buffer[GPIO_SUMMARY_MAX_LEN];
|
||||
size_t len = pin->dump_summary(buffer, sizeof(buffer));
|
||||
len = std::min(len, sizeof(buffer) - 1);
|
||||
esp_log_printf_(ESPHOME_LOG_LEVEL_CONFIG, tag, __LINE__, "%s%.*s", prefix, (int) len, buffer);
|
||||
}
|
||||
|
||||
// log_pin function declarations - implementation in gpio.cpp
|
||||
#ifdef USE_ESP8266
|
||||
void log_pin(const char *tag, const __FlashStringHelper *prefix, GPIOPin *pin);
|
||||
#else
|
||||
void log_pin(const char *tag, const char *prefix, GPIOPin *pin);
|
||||
#endif
|
||||
|
||||
static_assert(InternalGPIOPinContract<InternalGPIOPin>,
|
||||
"The platform's internal pin class is missing part of the InternalGPIOPin surface "
|
||||
"(esphome/core/gpio_pin.h)");
|
||||
} // namespace esphome
|
||||
|
||||
@@ -0,0 +1,147 @@
|
||||
#pragma once
|
||||
#include <algorithm>
|
||||
#include <concepts>
|
||||
#include <cstdint>
|
||||
#include <type_traits>
|
||||
|
||||
#include "esphome/core/defines.h"
|
||||
#include "esphome/core/helpers.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome {
|
||||
|
||||
/// Maximum buffer size for dump_summary output
|
||||
inline constexpr size_t GPIO_SUMMARY_MAX_LEN = 48;
|
||||
|
||||
#ifdef USE_ESP8266
|
||||
#define LOG_PIN(prefix, pin) log_pin(TAG, F(prefix), pin)
|
||||
#else
|
||||
#define LOG_PIN(prefix, pin) log_pin(TAG, prefix, pin)
|
||||
#endif
|
||||
|
||||
// put GPIO flags in a namespace to not pollute esphome namespace
|
||||
namespace gpio {
|
||||
|
||||
enum Flags : uint8_t {
|
||||
// Can't name these just INPUT because of Arduino defines :(
|
||||
FLAG_NONE = 0x00,
|
||||
FLAG_INPUT = 0x01,
|
||||
FLAG_OUTPUT = 0x02,
|
||||
FLAG_OPEN_DRAIN = 0x04,
|
||||
FLAG_PULLUP = 0x08,
|
||||
FLAG_PULLDOWN = 0x10,
|
||||
FLAG_HOLD = 0x20,
|
||||
};
|
||||
|
||||
class FlagsHelper {
|
||||
public:
|
||||
constexpr FlagsHelper(Flags val) : val_(val) {}
|
||||
constexpr operator Flags() const { return val_; }
|
||||
|
||||
protected:
|
||||
Flags val_;
|
||||
};
|
||||
constexpr FlagsHelper operator&(Flags lhs, Flags rhs) {
|
||||
return static_cast<Flags>(static_cast<uint8_t>(lhs) & static_cast<uint8_t>(rhs));
|
||||
}
|
||||
constexpr FlagsHelper operator|(Flags lhs, Flags rhs) {
|
||||
return static_cast<Flags>(static_cast<uint8_t>(lhs) | static_cast<uint8_t>(rhs));
|
||||
}
|
||||
|
||||
enum InterruptType : uint8_t {
|
||||
INTERRUPT_RISING_EDGE = 1,
|
||||
INTERRUPT_FALLING_EDGE = 2,
|
||||
INTERRUPT_ANY_EDGE = 3,
|
||||
INTERRUPT_LOW_LEVEL = 4,
|
||||
INTERRUPT_HIGH_LEVEL = 5,
|
||||
};
|
||||
|
||||
} // namespace gpio
|
||||
|
||||
class GPIOPin {
|
||||
public:
|
||||
virtual void setup() = 0;
|
||||
|
||||
virtual void pin_mode(gpio::Flags flags) = 0;
|
||||
|
||||
/**
|
||||
* @brief Retrieve GPIO pin flags.
|
||||
*
|
||||
* @return The GPIO flags describing the pin mode and properties.
|
||||
*/
|
||||
virtual gpio::Flags get_flags() const = 0;
|
||||
|
||||
virtual bool digital_read() = 0;
|
||||
|
||||
virtual void digital_write(bool value) = 0;
|
||||
|
||||
/// Write a summary of this pin to the provided buffer.
|
||||
/// @param buffer The buffer to write to
|
||||
/// @param len The size of the buffer (must be > 0)
|
||||
/// @return The number of characters that would be written (excluding null terminator),
|
||||
/// which may exceed len-1 if truncation occurred (snprintf semantics)
|
||||
virtual size_t dump_summary(char *buffer, size_t len) const;
|
||||
|
||||
/// Only the platform pin class behind the InternalGPIOPin alias may return true:
|
||||
/// callers static_cast to InternalGPIOPin on it.
|
||||
virtual bool is_internal() { return false; }
|
||||
#ifdef USE_GPIO_HOLD
|
||||
bool get_hold() const { return this->get_flags() & gpio::FLAG_HOLD; }
|
||||
virtual bool is_held() const { return false; }
|
||||
#else
|
||||
bool get_hold() const { return false; }
|
||||
bool is_held() const { return false; }
|
||||
#endif
|
||||
};
|
||||
|
||||
/// Copy of GPIOPin that is safe to use from ISRs (with no virtual functions)
|
||||
class ISRInternalGPIOPin {
|
||||
public:
|
||||
ISRInternalGPIOPin() = default;
|
||||
ISRInternalGPIOPin(void *arg) : arg_(arg) {}
|
||||
bool digital_read();
|
||||
void digital_write(bool value);
|
||||
void clear_interrupt();
|
||||
void pin_mode(gpio::Flags flags);
|
||||
|
||||
protected:
|
||||
void *arg_{nullptr};
|
||||
};
|
||||
|
||||
// What callers of the InternalGPIOPin alias (bound in esphome/core/gpio.h) rely on beyond
|
||||
// GPIOPin. The class must be final so calls to the GPIOPin virtuals through the alias are direct.
|
||||
// int is arbitrary: any T * exercises the public attach_interrupt<T> template.
|
||||
template<typename T>
|
||||
concept InternalGPIOPinContract = std::derived_from<T, GPIOPin> && std::is_final_v<T> &&
|
||||
requires(const T pin, void (*func)(int *), int *arg, gpio::InterruptType type) {
|
||||
{ pin.get_pin() } -> std::same_as<uint8_t>;
|
||||
{ pin.is_inverted() } -> std::same_as<bool>;
|
||||
{ pin.to_isr() } -> std::same_as<ISRInternalGPIOPin>;
|
||||
{ pin.detach_interrupt() } -> std::same_as<void>;
|
||||
{ pin.attach_interrupt(func, arg, type) } -> std::same_as<void>;
|
||||
};
|
||||
|
||||
// Inline default implementation for GPIOPin::dump_summary.
|
||||
// Writes an empty summary; subclasses override to provide pin details.
|
||||
inline size_t GPIOPin::dump_summary(char *buffer, size_t len) const {
|
||||
if (len > 0)
|
||||
buffer[0] = '\0';
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Inline helper for log_pin - allows compiler to inline into log_pin in gpio.cpp
|
||||
inline void log_pin_with_prefix(const char *tag, const char *prefix, GPIOPin *pin) {
|
||||
char buffer[GPIO_SUMMARY_MAX_LEN];
|
||||
size_t len = pin->dump_summary(buffer, sizeof(buffer));
|
||||
len = std::min(len, sizeof(buffer) - 1);
|
||||
esp_log_printf_(ESPHOME_LOG_LEVEL_CONFIG, tag, __LINE__, "%s%.*s", prefix, (int) len, buffer);
|
||||
}
|
||||
|
||||
// log_pin function declarations - implementation in gpio.cpp
|
||||
#ifdef USE_ESP8266
|
||||
void log_pin(const char *tag, const __FlashStringHelper *prefix, GPIOPin *pin);
|
||||
#else
|
||||
void log_pin(const char *tag, const char *prefix, GPIOPin *pin);
|
||||
#endif
|
||||
|
||||
} // namespace esphome
|
||||
@@ -0,0 +1,4 @@
|
||||
# No pin anywhere in the config: the pin class methods deep_sleep calls must still link
|
||||
deep_sleep:
|
||||
run_duration: 10s
|
||||
sleep_duration: 50s
|
||||
@@ -1,3 +1,4 @@
|
||||
#ifdef USE_HOST
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "esphome/components/power_supply/power_supply.h"
|
||||
@@ -6,28 +7,9 @@
|
||||
|
||||
namespace esphome::power_supply::testing {
|
||||
|
||||
// Minimal dummy internal GPIO pin implementation for testing
|
||||
class DummyInternalPin : public InternalGPIOPin {
|
||||
public:
|
||||
DummyInternalPin() = default;
|
||||
void setup() override {}
|
||||
void pin_mode(esphome::gpio::Flags) override {}
|
||||
esphome::gpio::Flags get_flags() const override { return esphome::gpio::FLAG_NONE; }
|
||||
bool digital_read() override { return false; }
|
||||
void digital_write(bool) override {}
|
||||
void detach_interrupt() const override {}
|
||||
ISRInternalGPIOPin to_isr() const override { return ISRInternalGPIOPin(); }
|
||||
uint8_t get_pin() const override { return 0; }
|
||||
bool is_inverted() const override { return false; }
|
||||
|
||||
protected:
|
||||
// Implement protected attach_interrupt required by InternalGPIOPin
|
||||
void attach_interrupt(void (*func)(void *), void *arg, esphome::gpio::InterruptType type) const override {}
|
||||
};
|
||||
|
||||
TEST(PowerSupply, HasHigherPriorityThanBusWhenInternalAndEnableOnBoot) {
|
||||
power_supply::PowerSupply ps;
|
||||
DummyInternalPin pin;
|
||||
InternalGPIOPin pin;
|
||||
ps.set_pin(&pin);
|
||||
ps.set_enable_on_boot(true);
|
||||
|
||||
@@ -37,7 +19,7 @@ TEST(PowerSupply, HasHigherPriorityThanBusWhenInternalAndEnableOnBoot) {
|
||||
|
||||
TEST(PowerSupply, FallsBackToIOWhenNotEnableOnBoot) {
|
||||
power_supply::PowerSupply ps;
|
||||
DummyInternalPin pin;
|
||||
InternalGPIOPin pin;
|
||||
ps.set_pin(&pin);
|
||||
ps.set_enable_on_boot(false);
|
||||
|
||||
@@ -45,3 +27,4 @@ TEST(PowerSupply, FallsBackToIOWhenNotEnableOnBoot) {
|
||||
}
|
||||
|
||||
} // namespace esphome::power_supply::testing
|
||||
#endif // USE_HOST
|
||||
|
||||
Reference in New Issue
Block a user