mirror of
https://github.com/Threnklyn/esphome-dev.git
synced 2026-05-28 08:48:27 +02:00
Add host target platform (#4783)
Co-authored-by: Otto winter <otto@otto-winter.com>
This commit is contained in:
@@ -978,6 +978,8 @@ DeviceInfoResponse APIConnection::device_info(const DeviceInfoRequest &msg) {
|
||||
resp.manufacturer = "Espressif";
|
||||
#elif defined(USE_RP2040)
|
||||
resp.manufacturer = "Raspberry Pi";
|
||||
#elif defined(USE_HOST)
|
||||
resp.manufacturer = "Host";
|
||||
#endif
|
||||
resp.model = ESPHOME_BOARD;
|
||||
#ifdef USE_DEEP_SLEEP
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
from esphome.const import (
|
||||
KEY_CORE,
|
||||
KEY_FRAMEWORK_VERSION,
|
||||
KEY_TARGET_FRAMEWORK,
|
||||
KEY_TARGET_PLATFORM,
|
||||
)
|
||||
from esphome.core import CORE
|
||||
import esphome.config_validation as cv
|
||||
import esphome.codegen as cg
|
||||
|
||||
from .const import KEY_HOST
|
||||
|
||||
# force import gpio to register pin schema
|
||||
from .gpio import host_pin_to_code # noqa
|
||||
|
||||
|
||||
CODEOWNERS = ["@esphome/core"]
|
||||
AUTO_LOAD = ["network"]
|
||||
|
||||
|
||||
def set_core_data(config):
|
||||
CORE.data[KEY_HOST] = {}
|
||||
CORE.data[KEY_CORE][KEY_TARGET_PLATFORM] = "host"
|
||||
CORE.data[KEY_CORE][KEY_TARGET_FRAMEWORK] = "host"
|
||||
CORE.data[KEY_CORE][KEY_FRAMEWORK_VERSION] = cv.Version(1, 0, 0)
|
||||
return config
|
||||
|
||||
|
||||
CONFIG_SCHEMA = cv.All(
|
||||
cv.Schema({}),
|
||||
set_core_data,
|
||||
)
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
cg.add_build_flag("-DUSE_HOST")
|
||||
cg.add_define("ESPHOME_BOARD", "host")
|
||||
cg.add_platformio_option("platform", "platformio/native")
|
||||
@@ -0,0 +1,5 @@
|
||||
import esphome.codegen as cg
|
||||
|
||||
KEY_HOST = "host"
|
||||
|
||||
host_ns = cg.esphome_ns.namespace("host")
|
||||
@@ -0,0 +1,77 @@
|
||||
#ifdef USE_HOST
|
||||
|
||||
#include "esphome/core/hal.h"
|
||||
#include "esphome/core/helpers.h"
|
||||
#include "preferences.h"
|
||||
|
||||
#include <sched.h>
|
||||
#include <time.h>
|
||||
#include <cmath>
|
||||
#include <cstdlib>
|
||||
|
||||
namespace esphome {
|
||||
|
||||
void IRAM_ATTR HOT yield() { ::sched_yield(); }
|
||||
uint32_t IRAM_ATTR HOT millis() {
|
||||
struct timespec spec;
|
||||
clock_gettime(CLOCK_MONOTONIC, &spec);
|
||||
time_t seconds = spec.tv_sec;
|
||||
uint32_t ms = round(spec.tv_nsec / 1e6);
|
||||
return ((uint32_t) seconds) * 1000U + ms;
|
||||
}
|
||||
void IRAM_ATTR HOT delay(uint32_t ms) {
|
||||
struct timespec ts;
|
||||
ts.tv_sec = ms / 1000;
|
||||
ts.tv_nsec = (ms % 1000) * 1000000;
|
||||
int res;
|
||||
do {
|
||||
res = nanosleep(&ts, &ts);
|
||||
} while (res != 0 && errno == EINTR);
|
||||
}
|
||||
uint32_t IRAM_ATTR HOT micros() {
|
||||
struct timespec spec;
|
||||
clock_gettime(CLOCK_MONOTONIC, &spec);
|
||||
time_t seconds = spec.tv_sec;
|
||||
uint32_t us = round(spec.tv_nsec / 1e3);
|
||||
return ((uint32_t) seconds) * 1000000U + us;
|
||||
}
|
||||
void IRAM_ATTR HOT delayMicroseconds(uint32_t us) {
|
||||
struct timespec ts;
|
||||
ts.tv_sec = us / 1000000U;
|
||||
ts.tv_nsec = (us % 1000000U) * 1000U;
|
||||
int res;
|
||||
do {
|
||||
res = nanosleep(&ts, &ts);
|
||||
} while (res != 0 && errno == EINTR);
|
||||
}
|
||||
void arch_restart() { exit(0); }
|
||||
void arch_init() {
|
||||
// pass
|
||||
}
|
||||
void IRAM_ATTR HOT arch_feed_wdt() {
|
||||
// pass
|
||||
}
|
||||
|
||||
uint8_t progmem_read_byte(const uint8_t *addr) { return *addr; }
|
||||
uint32_t arch_get_cpu_cycle_count() {
|
||||
struct timespec spec;
|
||||
clock_gettime(CLOCK_MONOTONIC, &spec);
|
||||
time_t seconds = spec.tv_sec;
|
||||
uint32_t us = spec.tv_nsec;
|
||||
return ((uint32_t) seconds) * 1000000000U + us;
|
||||
}
|
||||
uint32_t arch_get_cpu_freq_hz() { return 1000000000U; }
|
||||
|
||||
} // namespace esphome
|
||||
|
||||
void setup();
|
||||
void loop();
|
||||
int main() {
|
||||
esphome::host::setup_preferences();
|
||||
setup();
|
||||
while (true) {
|
||||
loop();
|
||||
}
|
||||
}
|
||||
|
||||
#endif // USE_HOST
|
||||
@@ -0,0 +1,59 @@
|
||||
#ifdef USE_HOST
|
||||
|
||||
#include "gpio.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace host {
|
||||
|
||||
static const char *const TAG = "host";
|
||||
|
||||
struct ISRPinArg {
|
||||
uint8_t pin;
|
||||
bool inverted;
|
||||
};
|
||||
|
||||
ISRInternalGPIOPin HostGPIOPin::to_isr() const {
|
||||
auto *arg = new ISRPinArg{}; // NOLINT(cppcoreguidelines-owning-memory)
|
||||
arg->pin = pin_;
|
||||
arg->inverted = inverted_;
|
||||
return ISRInternalGPIOPin((void *) arg);
|
||||
}
|
||||
|
||||
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); }
|
||||
|
||||
std::string HostGPIOPin::dump_summary() const {
|
||||
char buffer[32];
|
||||
snprintf(buffer, sizeof(buffer), "GPIO%u", pin_);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
bool HostGPIOPin::digital_read() { return inverted_; }
|
||||
void HostGPIOPin::digital_write(bool value) {
|
||||
// pass
|
||||
ESP_LOGD(TAG, "Setting pin %d to %s", pin_, value != inverted_ ? "HIGH" : "LOW");
|
||||
}
|
||||
void HostGPIOPin::detach_interrupt() const {}
|
||||
|
||||
} // namespace host
|
||||
|
||||
using namespace host;
|
||||
|
||||
bool IRAM_ATTR ISRInternalGPIOPin::digital_read() {
|
||||
auto *arg = reinterpret_cast<ISRPinArg *>(arg_);
|
||||
return arg->inverted;
|
||||
}
|
||||
void IRAM_ATTR ISRInternalGPIOPin::digital_write(bool value) {
|
||||
// pass
|
||||
}
|
||||
void IRAM_ATTR ISRInternalGPIOPin::clear_interrupt() {
|
||||
auto *arg = reinterpret_cast<ISRPinArg *>(arg_);
|
||||
ESP_LOGD(TAG, "Clearing interrupt for pin %d", arg->pin);
|
||||
}
|
||||
|
||||
} // namespace esphome
|
||||
|
||||
#endif // USE_HOST
|
||||
@@ -0,0 +1,37 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef USE_HOST
|
||||
|
||||
#include "esphome/core/hal.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace host {
|
||||
|
||||
class HostGPIOPin : public InternalGPIOPin {
|
||||
public:
|
||||
void set_pin(uint8_t pin) { pin_ = pin; }
|
||||
void set_inverted(bool inverted) { inverted_ = inverted; }
|
||||
void set_flags(gpio::Flags flags) { flags_ = flags; }
|
||||
|
||||
void setup() override { pin_mode(flags_); }
|
||||
void pin_mode(gpio::Flags flags) override;
|
||||
bool digital_read() override;
|
||||
void digital_write(bool value) override;
|
||||
std::string dump_summary() const override;
|
||||
void detach_interrupt() const override;
|
||||
ISRInternalGPIOPin to_isr() const override;
|
||||
uint8_t get_pin() const override { return pin_; }
|
||||
bool is_inverted() const override { return inverted_; }
|
||||
|
||||
protected:
|
||||
void attach_interrupt(void (*func)(void *), void *arg, gpio::InterruptType type) const override;
|
||||
|
||||
uint8_t pin_;
|
||||
bool inverted_;
|
||||
gpio::Flags flags_;
|
||||
};
|
||||
|
||||
} // namespace host
|
||||
} // namespace esphome
|
||||
|
||||
#endif // USE_HOST
|
||||
@@ -0,0 +1,73 @@
|
||||
import logging
|
||||
|
||||
from esphome.const import (
|
||||
CONF_ID,
|
||||
CONF_INPUT,
|
||||
CONF_INVERTED,
|
||||
CONF_MODE,
|
||||
CONF_NUMBER,
|
||||
CONF_OPEN_DRAIN,
|
||||
CONF_OUTPUT,
|
||||
CONF_PULLDOWN,
|
||||
CONF_PULLUP,
|
||||
)
|
||||
from esphome import pins
|
||||
import esphome.config_validation as cv
|
||||
import esphome.codegen as cg
|
||||
|
||||
from .const import host_ns
|
||||
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
HostGPIOPin = host_ns.class_("HostGPIOPin", cg.InternalGPIOPin)
|
||||
|
||||
|
||||
def _translate_pin(value):
|
||||
if isinstance(value, dict) or value is None:
|
||||
raise cv.Invalid(
|
||||
"This variable only supports pin numbers, not full pin schemas "
|
||||
"(with inverted and mode)."
|
||||
)
|
||||
if isinstance(value, int):
|
||||
return value
|
||||
try:
|
||||
return int(value)
|
||||
except ValueError:
|
||||
pass
|
||||
if value.startswith("GPIO"):
|
||||
return cv.int_(value[len("GPIO") :].strip())
|
||||
return value
|
||||
|
||||
|
||||
def validate_gpio_pin(value):
|
||||
return _translate_pin(value)
|
||||
|
||||
|
||||
HOST_PIN_SCHEMA = cv.All(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(HostGPIOPin),
|
||||
cv.Required(CONF_NUMBER): validate_gpio_pin,
|
||||
cv.Optional(CONF_MODE, default={}): cv.Schema(
|
||||
{
|
||||
cv.Optional(CONF_INPUT, default=False): cv.boolean,
|
||||
cv.Optional(CONF_OUTPUT, default=False): cv.boolean,
|
||||
cv.Optional(CONF_OPEN_DRAIN, default=False): cv.boolean,
|
||||
cv.Optional(CONF_PULLUP, default=False): cv.boolean,
|
||||
cv.Optional(CONF_PULLDOWN, default=False): cv.boolean,
|
||||
}
|
||||
),
|
||||
cv.Optional(CONF_INVERTED, default=False): cv.boolean,
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
@pins.PIN_SCHEMA_REGISTRY.register("host", HOST_PIN_SCHEMA)
|
||||
async def host_pin_to_code(config):
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
num = config[CONF_NUMBER]
|
||||
cg.add(var.set_pin(num))
|
||||
cg.add(var.set_inverted(config[CONF_INVERTED]))
|
||||
cg.add(var.set_flags(pins.gpio_flags_expr(config[CONF_MODE])))
|
||||
return var
|
||||
@@ -0,0 +1,36 @@
|
||||
#ifdef USE_HOST
|
||||
|
||||
#include "preferences.h"
|
||||
#include <cstring>
|
||||
#include "esphome/core/preferences.h"
|
||||
#include "esphome/core/helpers.h"
|
||||
#include "esphome/core/log.h"
|
||||
#include "esphome/core/defines.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace host {
|
||||
|
||||
static const char *const TAG = "host.preferences";
|
||||
|
||||
class HostPreferences : public ESPPreferences {
|
||||
public:
|
||||
ESPPreferenceObject make_preference(size_t length, uint32_t type, bool in_flash) override { return {}; }
|
||||
|
||||
ESPPreferenceObject make_preference(size_t length, uint32_t type) override { return {}; }
|
||||
|
||||
bool sync() override { return true; }
|
||||
bool reset() override { return true; }
|
||||
};
|
||||
|
||||
void setup_preferences() {
|
||||
auto *pref = new HostPreferences(); // NOLINT(cppcoreguidelines-owning-memory)
|
||||
global_preferences = pref;
|
||||
}
|
||||
|
||||
} // namespace host
|
||||
|
||||
ESPPreferences *global_preferences; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
|
||||
} // namespace esphome
|
||||
|
||||
#endif // USE_HOST
|
||||
@@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef USE_HOST
|
||||
|
||||
namespace esphome {
|
||||
namespace host {
|
||||
|
||||
void setup_preferences();
|
||||
|
||||
} // namespace host
|
||||
} // namespace esphome
|
||||
|
||||
#endif // USE_HOST
|
||||
@@ -17,6 +17,9 @@ from esphome.const import (
|
||||
CONF_TAG,
|
||||
CONF_TRIGGER_ID,
|
||||
CONF_TX_BUFFER_SIZE,
|
||||
PLATFORM_ESP32,
|
||||
PLATFORM_ESP8266,
|
||||
PLATFORM_RP2040,
|
||||
)
|
||||
from esphome.core import CORE, EsphomeError, Lambda, coroutine_with_priority
|
||||
from esphome.components.esp32 import add_idf_sdkconfig_option, get_esp32_variant
|
||||
@@ -141,7 +144,10 @@ CONFIG_SCHEMA = cv.All(
|
||||
esp8266=UART0,
|
||||
esp32=UART0,
|
||||
rp2040=USB_CDC,
|
||||
): uart_selection,
|
||||
): cv.All(
|
||||
cv.only_on([PLATFORM_ESP8266, PLATFORM_ESP32, PLATFORM_RP2040]),
|
||||
uart_selection,
|
||||
),
|
||||
cv.Optional(CONF_LEVEL, default="DEBUG"): is_log_level,
|
||||
cv.Optional(CONF_LOGS, default={}): cv.Schema(
|
||||
{
|
||||
|
||||
@@ -145,6 +145,9 @@ void HOT Logger::log_message_(int level, const char *tag, int offset) {
|
||||
if (xPortGetFreeHeapSize() < 2048)
|
||||
return;
|
||||
#endif
|
||||
#ifdef USE_HOST
|
||||
puts(msg);
|
||||
#endif
|
||||
|
||||
this->log_callback_.call(level, tag, msg);
|
||||
}
|
||||
@@ -262,7 +265,11 @@ void Logger::set_baud_rate(uint32_t baud_rate) { this->baud_rate_ = baud_rate; }
|
||||
void Logger::set_log_level(const std::string &tag, int log_level) {
|
||||
this->log_levels_.push_back(LogLevelOverride{tag, log_level});
|
||||
}
|
||||
|
||||
#if defined(USE_ESP32) || defined(USE_ESP8266) || defined(USE_RP2040)
|
||||
UARTSelection Logger::get_uart() const { return this->uart_; }
|
||||
#endif
|
||||
|
||||
void Logger::add_on_log_callback(std::function<void(int, const char *, const char *)> &&callback) {
|
||||
this->log_callback_.add(std::move(callback));
|
||||
}
|
||||
@@ -294,7 +301,10 @@ void Logger::dump_config() {
|
||||
ESP_LOGCONFIG(TAG, "Logger:");
|
||||
ESP_LOGCONFIG(TAG, " Level: %s", LOG_LEVELS[ESPHOME_LOG_LEVEL]);
|
||||
ESP_LOGCONFIG(TAG, " Log Baud Rate: %" PRIu32, this->baud_rate_);
|
||||
#if defined(USE_ESP32) || defined(USE_ESP8266) || defined(USE_RP2040)
|
||||
ESP_LOGCONFIG(TAG, " Hardware UART: %s", UART_SELECTIONS[this->uart_]);
|
||||
#endif
|
||||
|
||||
for (auto &it : this->log_levels_) {
|
||||
ESP_LOGCONFIG(TAG, " Level for '%s': %s", it.tag.c_str(), LOG_LEVELS[it.level]);
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@ namespace esphome {
|
||||
|
||||
namespace logger {
|
||||
|
||||
#if defined(USE_ESP32) || defined(USE_ESP8266) || defined(USE_RP2040)
|
||||
/** Enum for logging UART selection
|
||||
*
|
||||
* Advanced configuration (pin selection, etc) is not supported.
|
||||
@@ -52,6 +53,7 @@ enum UARTSelection {
|
||||
UART_SELECTION_USB_CDC,
|
||||
#endif // USE_RP2040
|
||||
};
|
||||
#endif // USE_ESP32 || USE_ESP8266
|
||||
|
||||
class Logger : public Component {
|
||||
public:
|
||||
@@ -66,10 +68,11 @@ class Logger : public Component {
|
||||
#ifdef USE_ESP_IDF
|
||||
uart_port_t get_uart_num() const { return uart_num_; }
|
||||
#endif
|
||||
|
||||
#if defined(USE_ESP32) || defined(USE_ESP8266) || defined(USE_RP2040)
|
||||
void set_uart_selection(UARTSelection uart_selection) { uart_ = uart_selection; }
|
||||
/// Get the UART used by the logger.
|
||||
UARTSelection get_uart() const;
|
||||
#endif
|
||||
|
||||
/// Set the log level of the specified tag.
|
||||
void set_log_level(const std::string &tag, int log_level);
|
||||
@@ -139,7 +142,9 @@ class Logger : public Component {
|
||||
char *tx_buffer_{nullptr};
|
||||
int tx_buffer_at_{0};
|
||||
int tx_buffer_size_{0};
|
||||
#if defined(USE_ESP32) || defined(USE_ESP8266) || defined(USE_RP2040)
|
||||
UARTSelection uart_{UART_SELECTION_UART0};
|
||||
#endif
|
||||
#ifdef USE_ARDUINO
|
||||
Stream *hw_serial_{nullptr};
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
#ifdef USE_HOST
|
||||
|
||||
#include "esphome/components/network/ip_address.h"
|
||||
#include "esphome/components/network/util.h"
|
||||
#include "esphome/core/log.h"
|
||||
#include "mdns_component.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace mdns {
|
||||
|
||||
void MDNSComponent::setup() { this->compile_records_(); }
|
||||
|
||||
void MDNSComponent::on_shutdown() {}
|
||||
|
||||
} // namespace mdns
|
||||
} // namespace esphome
|
||||
|
||||
#endif
|
||||
@@ -23,6 +23,9 @@ bool is_connected() {
|
||||
return wifi::global_wifi_component->is_connected();
|
||||
#endif
|
||||
|
||||
#ifdef USE_HOST
|
||||
return true; // Assume its connected
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@ CONFIG_SCHEMA = cv.Schema(
|
||||
esp8266=IMPLEMENTATION_LWIP_TCP,
|
||||
esp32=IMPLEMENTATION_BSD_SOCKETS,
|
||||
rp2040=IMPLEMENTATION_LWIP_TCP,
|
||||
host=IMPLEMENTATION_BSD_SOCKETS,
|
||||
): cv.one_of(
|
||||
IMPLEMENTATION_LWIP_TCP, IMPLEMENTATION_BSD_SOCKETS, lower=True, space="_"
|
||||
),
|
||||
|
||||
@@ -130,6 +130,13 @@ struct iovec {
|
||||
#include <sys/uio.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#ifdef USE_HOST
|
||||
#include <arpa/inet.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/ip.h>
|
||||
#include <netinet/tcp.h>
|
||||
#endif // USE_HOST
|
||||
|
||||
#ifdef USE_ARDUINO
|
||||
// arduino-esp32 declares a global var called INADDR_NONE which is replaced
|
||||
// by the define
|
||||
|
||||
Reference in New Issue
Block a user