mirror of
https://github.com/esphome/esphome.git
synced 2024-11-24 16:08:10 +01:00
Initial Support for RP2040 platform (#3284)
Co-authored-by: Paulus Schoutsen <balloob@gmail.com>
This commit is contained in:
parent
e87edcc77a
commit
6153bcc6ad
49 changed files with 1270 additions and 61 deletions
4
.github/workflows/ci.yml
vendored
4
.github/workflows/ci.yml
vendored
|
@ -48,6 +48,10 @@ jobs:
|
|||
file: tests/test5.yaml
|
||||
name: Test tests/test5.yaml
|
||||
pio_cache_key: test5
|
||||
- id: test
|
||||
file: tests/test6.yaml
|
||||
name: Test tests/test6.yaml
|
||||
pio_cache_key: test6
|
||||
- id: pytest
|
||||
name: Run pytest
|
||||
- id: clang-format
|
||||
|
|
|
@ -184,6 +184,8 @@ esphome/components/rc522_spi/* @glmnet
|
|||
esphome/components/restart/* @esphome/core
|
||||
esphome/components/rf_bridge/* @jesserockz
|
||||
esphome/components/rgbct/* @jesserockz
|
||||
esphome/components/rp2040/* @jesserockz
|
||||
esphome/components/rp2040_pwm/* @jesserockz
|
||||
esphome/components/rtttl/* @glmnet
|
||||
esphome/components/safe_mode/* @jsuanet @paulmonigatti
|
||||
esphome/components/scd4x/* @martgras @sjtrny
|
||||
|
|
|
@ -4,6 +4,7 @@ import logging
|
|||
import os
|
||||
import re
|
||||
import sys
|
||||
import time
|
||||
from datetime import datetime
|
||||
|
||||
from esphome import const, writer, yaml_util
|
||||
|
@ -22,6 +23,9 @@ from esphome.const import (
|
|||
CONF_ESPHOME,
|
||||
CONF_PLATFORMIO_OPTIONS,
|
||||
CONF_SUBSTITUTIONS,
|
||||
PLATFORM_ESP32,
|
||||
PLATFORM_ESP8266,
|
||||
PLATFORM_RP2040,
|
||||
SECRETS_FILES,
|
||||
)
|
||||
from esphome.core import CORE, EsphomeError, coroutine
|
||||
|
@ -101,11 +105,11 @@ def run_miniterm(config, port):
|
|||
|
||||
if CONF_LOGGER not in config:
|
||||
_LOGGER.info("Logger is not enabled. Not starting UART logs.")
|
||||
return
|
||||
return 1
|
||||
baud_rate = config["logger"][CONF_BAUD_RATE]
|
||||
if baud_rate == 0:
|
||||
_LOGGER.info("UART logging is disabled (baud_rate=0). Not starting UART logs.")
|
||||
return
|
||||
return 1
|
||||
_LOGGER.info("Starting log output from %s with baud rate %s", port, baud_rate)
|
||||
|
||||
backtrace_state = False
|
||||
|
@ -119,25 +123,34 @@ def run_miniterm(config, port):
|
|||
ser.dtr = False
|
||||
ser.rts = False
|
||||
|
||||
with ser:
|
||||
while True:
|
||||
try:
|
||||
raw = ser.readline()
|
||||
except serial.SerialException:
|
||||
_LOGGER.error("Serial port closed!")
|
||||
return
|
||||
line = (
|
||||
raw.replace(b"\r", b"")
|
||||
.replace(b"\n", b"")
|
||||
.decode("utf8", "backslashreplace")
|
||||
)
|
||||
time = datetime.now().time().strftime("[%H:%M:%S]")
|
||||
message = time + line
|
||||
safe_print(message)
|
||||
tries = 0
|
||||
while tries < 5:
|
||||
try:
|
||||
with ser:
|
||||
while True:
|
||||
try:
|
||||
raw = ser.readline()
|
||||
except serial.SerialException:
|
||||
_LOGGER.error("Serial port closed!")
|
||||
return 0
|
||||
line = (
|
||||
raw.replace(b"\r", b"")
|
||||
.replace(b"\n", b"")
|
||||
.decode("utf8", "backslashreplace")
|
||||
)
|
||||
time_str = datetime.now().time().strftime("[%H:%M:%S]")
|
||||
message = time_str + line
|
||||
safe_print(message)
|
||||
|
||||
backtrace_state = platformio_api.process_stacktrace(
|
||||
config, line, backtrace_state=backtrace_state
|
||||
)
|
||||
backtrace_state = platformio_api.process_stacktrace(
|
||||
config, line, backtrace_state=backtrace_state
|
||||
)
|
||||
except serial.SerialException:
|
||||
tries += 1
|
||||
time.sleep(1)
|
||||
if tries >= 5:
|
||||
_LOGGER.error("Could not connect to serial port %s", port)
|
||||
return 1
|
||||
|
||||
|
||||
def wrap_to_code(name, comp):
|
||||
|
@ -258,9 +271,21 @@ def upload_using_esptool(config, port):
|
|||
|
||||
|
||||
def upload_program(config, args, host):
|
||||
# if upload is to a serial port use platformio, otherwise assume ota
|
||||
if get_port_type(host) == "SERIAL":
|
||||
return upload_using_esptool(config, host)
|
||||
if CORE.target_platform in (PLATFORM_ESP32, PLATFORM_ESP8266):
|
||||
return upload_using_esptool(config, host)
|
||||
|
||||
if CORE.target_platform in (PLATFORM_RP2040):
|
||||
from esphome import platformio_api
|
||||
|
||||
upload_args = ["-t", "upload"]
|
||||
if args.device is not None:
|
||||
upload_args += ["--upload-port", args.device]
|
||||
return platformio_api.run_platformio_cli_run(
|
||||
config, CORE.verbose, *upload_args
|
||||
)
|
||||
|
||||
return 1 # Unknown target platform
|
||||
|
||||
from esphome import espota2
|
||||
|
||||
|
@ -280,8 +305,7 @@ def show_logs(config, args, port):
|
|||
if "logger" not in config:
|
||||
raise EsphomeError("Logger is not configured!")
|
||||
if get_port_type(port) == "SERIAL":
|
||||
run_miniterm(config, port)
|
||||
return 0
|
||||
return run_miniterm(config, port)
|
||||
if get_port_type(port) == "NETWORK" and "api" in config:
|
||||
from esphome.components.api.client import run_logs
|
||||
|
||||
|
|
|
@ -77,6 +77,8 @@ UART_SELECTION_ESP8266 = [UART0, UART0_SWAP, UART1]
|
|||
|
||||
ESP_IDF_UARTS = [USB_CDC, USB_SERIAL_JTAG]
|
||||
|
||||
UART_SELECTION_RP2040 = [UART0, UART1]
|
||||
|
||||
HARDWARE_UART_TO_UART_SELECTION = {
|
||||
UART0: logger_ns.UART_SELECTION_UART0,
|
||||
UART0_SWAP: logger_ns.UART_SELECTION_UART0_SWAP,
|
||||
|
@ -106,6 +108,8 @@ def uart_selection(value):
|
|||
return cv.one_of(*UART_SELECTION_ESP32[variant], upper=True)(value)
|
||||
if CORE.is_esp8266:
|
||||
return cv.one_of(*UART_SELECTION_ESP8266, upper=True)(value)
|
||||
if CORE.is_rp2040:
|
||||
return cv.one_of(*UART_SELECTION_RP2040, upper=True)(value)
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
|
@ -158,12 +162,13 @@ CONFIG_SCHEMA = cv.All(
|
|||
@coroutine_with_priority(90.0)
|
||||
async def to_code(config):
|
||||
baud_rate = config[CONF_BAUD_RATE]
|
||||
rhs = Logger.new(
|
||||
baud_rate,
|
||||
config[CONF_TX_BUFFER_SIZE],
|
||||
HARDWARE_UART_TO_UART_SELECTION[config[CONF_HARDWARE_UART]],
|
||||
)
|
||||
log = cg.Pvariable(config[CONF_ID], rhs)
|
||||
log = cg.new_Pvariable(config[CONF_ID], baud_rate, config[CONF_TX_BUFFER_SIZE])
|
||||
if CONF_HARDWARE_UART in config:
|
||||
cg.add(
|
||||
log.set_uart_selection(
|
||||
HARDWARE_UART_TO_UART_SELECTION[config[CONF_HARDWARE_UART]]
|
||||
)
|
||||
)
|
||||
cg.add(log.pre_setup())
|
||||
|
||||
for tag, level in config[CONF_LOGS].items():
|
||||
|
|
|
@ -148,8 +148,7 @@ void HOT Logger::log_message_(int level, const char *tag, int offset) {
|
|||
this->log_callback_.call(level, tag, msg);
|
||||
}
|
||||
|
||||
Logger::Logger(uint32_t baud_rate, size_t tx_buffer_size, UARTSelection uart)
|
||||
: baud_rate_(baud_rate), tx_buffer_size_(tx_buffer_size), uart_(uart) {
|
||||
Logger::Logger(uint32_t baud_rate, size_t tx_buffer_size) : baud_rate_(baud_rate), tx_buffer_size_(tx_buffer_size) {
|
||||
// add 1 to buffer size for null terminator
|
||||
this->tx_buffer_ = new char[this->tx_buffer_size_ + 1]; // NOLINT
|
||||
}
|
||||
|
@ -270,6 +269,9 @@ const char *const UART_SELECTIONS[] = {
|
|||
#endif // USE_ESP32
|
||||
#ifdef USE_ESP8266
|
||||
const char *const UART_SELECTIONS[] = {"UART0", "UART1", "UART0_SWAP"};
|
||||
#endif
|
||||
#ifdef USE_RP2040
|
||||
const char *const UART_SELECTIONS[] = {"UART0", "UART1"};
|
||||
#endif // USE_ESP8266
|
||||
void Logger::dump_config() {
|
||||
ESP_LOGCONFIG(TAG, "Logger:");
|
||||
|
|
|
@ -7,8 +7,15 @@
|
|||
#include <cstdarg>
|
||||
|
||||
#ifdef USE_ARDUINO
|
||||
#if defined(USE_ESP8266) || defined(USE_ESP32)
|
||||
#include <HardwareSerial.h>
|
||||
#endif
|
||||
#endif // USE_ESP8266 || USE_ESP32
|
||||
#ifdef USE_RP2040
|
||||
#include <HardwareSerial.h>
|
||||
#include <SerialUSB.h>
|
||||
#endif // USE_RP2040
|
||||
#endif // USE_ARDUINO
|
||||
|
||||
#ifdef USE_ESP_IDF
|
||||
#include <driver/uart.h>
|
||||
#endif
|
||||
|
@ -44,7 +51,7 @@ enum UARTSelection {
|
|||
|
||||
class Logger : public Component {
|
||||
public:
|
||||
explicit Logger(uint32_t baud_rate, size_t tx_buffer_size, UARTSelection uart);
|
||||
explicit Logger(uint32_t baud_rate, size_t tx_buffer_size);
|
||||
|
||||
/// Manually set the baud rate for serial, set to 0 to disable.
|
||||
void set_baud_rate(uint32_t baud_rate);
|
||||
|
@ -56,6 +63,7 @@ class Logger : public Component {
|
|||
uart_port_t get_uart_num() const { return uart_num_; }
|
||||
#endif
|
||||
|
||||
void set_uart_selection(UARTSelection uart_selection) { uart_ = uart_selection; }
|
||||
/// Get the UART used by the logger.
|
||||
UARTSelection get_uart() const;
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
namespace esphome {
|
||||
namespace md5 {
|
||||
|
||||
#ifdef USE_ARDUINO
|
||||
#if defined(USE_ARDUINO) && !defined(USE_RP2040)
|
||||
void MD5Digest::init() {
|
||||
memset(this->digest_, 0, 16);
|
||||
MD5Init(&this->ctx_);
|
||||
|
@ -15,7 +15,7 @@ void MD5Digest::init() {
|
|||
void MD5Digest::add(const uint8_t *data, size_t len) { MD5Update(&this->ctx_, data, len); }
|
||||
|
||||
void MD5Digest::calculate() { MD5Final(this->digest_, &this->ctx_); }
|
||||
#endif // USE_ARDUINO
|
||||
#endif // USE_ARDUINO && !USE_RP2040
|
||||
|
||||
#ifdef USE_ESP_IDF
|
||||
void MD5Digest::init() {
|
||||
|
@ -28,6 +28,17 @@ void MD5Digest::add(const uint8_t *data, size_t len) { esp_rom_md5_update(&this-
|
|||
void MD5Digest::calculate() { esp_rom_md5_final(this->digest_, &this->ctx_); }
|
||||
#endif // USE_ESP_IDF
|
||||
|
||||
#ifdef USE_RP2040
|
||||
void MD5Digest::init() {
|
||||
memset(this->digest_, 0, 16);
|
||||
br_md5_init(&this->ctx_);
|
||||
}
|
||||
|
||||
void MD5Digest::add(const uint8_t *data, size_t len) { br_md5_update(&this->ctx_, data, len); }
|
||||
|
||||
void MD5Digest::calculate() { br_md5_out(&this->ctx_, this->digest_); }
|
||||
#endif // USE_RP2040
|
||||
|
||||
void MD5Digest::get_bytes(uint8_t *output) { memcpy(output, this->digest_, 16); }
|
||||
|
||||
void MD5Digest::get_hex(char *output) {
|
||||
|
|
|
@ -17,6 +17,11 @@
|
|||
#define MD5_CTX_TYPE md5_context_t
|
||||
#endif
|
||||
|
||||
#ifdef USE_RP2040
|
||||
#include <MD5Builder.h>
|
||||
#define MD5_CTX_TYPE br_md5_context
|
||||
#endif
|
||||
|
||||
namespace esphome {
|
||||
namespace md5 {
|
||||
|
||||
|
|
|
@ -38,6 +38,9 @@ void MDNSComponent::compile_records_() {
|
|||
#endif
|
||||
#ifdef USE_ESP32
|
||||
platform = "ESP32";
|
||||
#endif
|
||||
#ifdef USE_RP2040
|
||||
platform = "RP2040";
|
||||
#endif
|
||||
if (platform != nullptr) {
|
||||
service.txt_records.push_back({"platform", platform});
|
||||
|
|
40
esphome/components/mdns/mdns_rp2040.cpp
Normal file
40
esphome/components/mdns/mdns_rp2040.cpp
Normal file
|
@ -0,0 +1,40 @@
|
|||
#ifdef USE_RP2040
|
||||
|
||||
#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_();
|
||||
|
||||
network::IPAddress addr = network::get_ip_address();
|
||||
// MDNS.begin(this->hostname_.c_str(), (uint32_t) addr);
|
||||
|
||||
// for (const auto &service : this->services_) {
|
||||
// // Strip the leading underscore from the proto and service_type. While it is
|
||||
// // part of the wire protocol to have an underscore, and for example ESP-IDF
|
||||
// // expects the underscore to be there, the ESP8266 implementation always adds
|
||||
// // the underscore itself.
|
||||
// auto *proto = service.proto.c_str();
|
||||
// while (*proto == '_') {
|
||||
// proto++;
|
||||
// }
|
||||
// auto *service_type = service.service_type.c_str();
|
||||
// while (*service_type == '_') {
|
||||
// service_type++;
|
||||
// }
|
||||
// MDNS.addService(service_type, proto, service.port);
|
||||
// for (const auto &record : service.txt_records) {
|
||||
// MDNS.addServiceTxt(service_type, proto, record.key.c_str(), record.value.c_str());
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
} // namespace mdns
|
||||
} // namespace esphome
|
||||
|
||||
#endif
|
|
@ -39,7 +39,7 @@ CONFIG_SCHEMA = cv.Schema(
|
|||
{
|
||||
cv.GenerateID(): cv.declare_id(OTAComponent),
|
||||
cv.Optional(CONF_SAFE_MODE, default=True): cv.boolean,
|
||||
cv.SplitDefault(CONF_PORT, esp8266=8266, esp32=3232): cv.port,
|
||||
cv.SplitDefault(CONF_PORT, esp8266=8266, esp32=3232, rp2040=2040): cv.port,
|
||||
cv.Optional(CONF_PASSWORD): cv.string,
|
||||
cv.Optional(
|
||||
CONF_REBOOT_TIMEOUT, default="5min"
|
||||
|
@ -94,6 +94,9 @@ async def to_code(config):
|
|||
if CORE.is_esp32 and CORE.using_arduino:
|
||||
cg.add_library("Update", None)
|
||||
|
||||
if CORE.is_rp2040 and CORE.using_arduino:
|
||||
cg.add_library("Updater", None)
|
||||
|
||||
use_state_callback = False
|
||||
for conf in config.get(CONF_ON_STATE_CHANGE, []):
|
||||
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
|
||||
|
|
55
esphome/components/ota/ota_backend_arduino_rp2040.cpp
Normal file
55
esphome/components/ota/ota_backend_arduino_rp2040.cpp
Normal file
|
@ -0,0 +1,55 @@
|
|||
#include "esphome/core/defines.h"
|
||||
#ifdef USE_ARDUINO
|
||||
#ifdef USE_RP2040
|
||||
|
||||
#include "esphome/components/rp2040/preferences.h"
|
||||
#include "ota_backend.h"
|
||||
#include "ota_backend_arduino_rp2040.h"
|
||||
#include "ota_component.h"
|
||||
|
||||
#include <Updater.h>
|
||||
|
||||
namespace esphome {
|
||||
namespace ota {
|
||||
|
||||
OTAResponseTypes ArduinoRP2040OTABackend::begin(size_t image_size) {
|
||||
bool ret = Update.begin(image_size, U_FLASH);
|
||||
if (ret) {
|
||||
return OTA_RESPONSE_OK;
|
||||
}
|
||||
|
||||
uint8_t error = Update.getError();
|
||||
if (error == UPDATE_ERROR_BOOTSTRAP)
|
||||
return OTA_RESPONSE_ERROR_INVALID_BOOTSTRAPPING;
|
||||
if (error == UPDATE_ERROR_NEW_FLASH_CONFIG)
|
||||
return OTA_RESPONSE_ERROR_WRONG_NEW_FLASH_CONFIG;
|
||||
if (error == UPDATE_ERROR_FLASH_CONFIG)
|
||||
return OTA_RESPONSE_ERROR_WRONG_CURRENT_FLASH_CONFIG;
|
||||
if (error == UPDATE_ERROR_SPACE)
|
||||
return OTA_RESPONSE_ERROR_RP2040_NOT_ENOUGH_SPACE;
|
||||
return OTA_RESPONSE_ERROR_UNKNOWN;
|
||||
}
|
||||
|
||||
void ArduinoRP2040OTABackend::set_update_md5(const char *md5) { Update.setMD5(md5); }
|
||||
|
||||
OTAResponseTypes ArduinoRP2040OTABackend::write(uint8_t *data, size_t len) {
|
||||
size_t written = Update.write(data, len);
|
||||
if (written != len) {
|
||||
return OTA_RESPONSE_ERROR_WRITING_FLASH;
|
||||
}
|
||||
return OTA_RESPONSE_OK;
|
||||
}
|
||||
|
||||
OTAResponseTypes ArduinoRP2040OTABackend::end() {
|
||||
if (!Update.end())
|
||||
return OTA_RESPONSE_ERROR_UPDATE_END;
|
||||
return OTA_RESPONSE_OK;
|
||||
}
|
||||
|
||||
void ArduinoRP2040OTABackend::abort() { Update.end(); }
|
||||
|
||||
} // namespace ota
|
||||
} // namespace esphome
|
||||
|
||||
#endif // USE_RP2040
|
||||
#endif // USE_ARDUINO
|
27
esphome/components/ota/ota_backend_arduino_rp2040.h
Normal file
27
esphome/components/ota/ota_backend_arduino_rp2040.h
Normal file
|
@ -0,0 +1,27 @@
|
|||
#pragma once
|
||||
#include "esphome/core/defines.h"
|
||||
#ifdef USE_ARDUINO
|
||||
#ifdef USE_RP2040
|
||||
|
||||
#include "esphome/core/macros.h"
|
||||
#include "ota_backend.h"
|
||||
#include "ota_component.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace ota {
|
||||
|
||||
class ArduinoRP2040OTABackend : public OTABackend {
|
||||
public:
|
||||
OTAResponseTypes begin(size_t image_size) override;
|
||||
void set_update_md5(const char *md5) override;
|
||||
OTAResponseTypes write(uint8_t *data, size_t len) override;
|
||||
OTAResponseTypes end() override;
|
||||
void abort() override;
|
||||
bool supports_compression() override { return false; }
|
||||
};
|
||||
|
||||
} // namespace ota
|
||||
} // namespace esphome
|
||||
|
||||
#endif // USE_RP2040
|
||||
#endif // USE_ARDUINO
|
|
@ -2,6 +2,7 @@
|
|||
#include "ota_backend.h"
|
||||
#include "ota_backend_arduino_esp32.h"
|
||||
#include "ota_backend_arduino_esp8266.h"
|
||||
#include "ota_backend_arduino_rp2040.h"
|
||||
#include "ota_backend_esp_idf.h"
|
||||
|
||||
#include "esphome/core/log.h"
|
||||
|
@ -35,6 +36,9 @@ std::unique_ptr<OTABackend> make_ota_backend() {
|
|||
#ifdef USE_ESP_IDF
|
||||
return make_unique<IDFOTABackend>();
|
||||
#endif // USE_ESP_IDF
|
||||
#ifdef USE_RP2040
|
||||
return make_unique<ArduinoRP2040OTABackend>();
|
||||
#endif // USE_RP2040
|
||||
}
|
||||
|
||||
OTAComponent::OTAComponent() { global_ota_component = this; }
|
||||
|
|
|
@ -33,6 +33,7 @@ enum OTAResponseTypes {
|
|||
OTA_RESPONSE_ERROR_ESP32_NOT_ENOUGH_SPACE = 137,
|
||||
OTA_RESPONSE_ERROR_NO_UPDATE_PARTITION = 138,
|
||||
OTA_RESPONSE_ERROR_MD5_MISMATCH = 139,
|
||||
OTA_RESPONSE_ERROR_RP2040_NOT_ENOUGH_SPACE = 140,
|
||||
OTA_RESPONSE_ERROR_UNKNOWN = 255,
|
||||
};
|
||||
|
||||
|
|
157
esphome/components/rp2040/__init__.py
Normal file
157
esphome/components/rp2040/__init__.py
Normal file
|
@ -0,0 +1,157 @@
|
|||
import logging
|
||||
|
||||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import (
|
||||
CONF_BOARD,
|
||||
CONF_FRAMEWORK,
|
||||
CONF_SOURCE,
|
||||
CONF_VERSION,
|
||||
KEY_CORE,
|
||||
KEY_FRAMEWORK_VERSION,
|
||||
KEY_TARGET_FRAMEWORK,
|
||||
KEY_TARGET_PLATFORM,
|
||||
)
|
||||
from esphome.core import CORE, coroutine_with_priority
|
||||
|
||||
from .const import KEY_BOARD, KEY_RP2040, rp2040_ns
|
||||
|
||||
# force import gpio to register pin schema
|
||||
from .gpio import rp2040_pin_to_code # noqa
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
CODEOWNERS = ["@jesserockz"]
|
||||
AUTO_LOAD = []
|
||||
|
||||
|
||||
def set_core_data(config):
|
||||
CORE.data[KEY_RP2040] = {}
|
||||
CORE.data[KEY_CORE][KEY_TARGET_PLATFORM] = "rp2040"
|
||||
CORE.data[KEY_CORE][KEY_TARGET_FRAMEWORK] = "arduino"
|
||||
CORE.data[KEY_CORE][KEY_FRAMEWORK_VERSION] = cv.Version.parse(
|
||||
config[CONF_FRAMEWORK][CONF_VERSION]
|
||||
)
|
||||
CORE.data[KEY_RP2040][KEY_BOARD] = config[CONF_BOARD]
|
||||
|
||||
return config
|
||||
|
||||
|
||||
def _format_framework_arduino_version(ver: cv.Version) -> str:
|
||||
# format the given arduino (https://github.com/earlephilhower/arduino-pico/releases) version to
|
||||
# a PIO earlephilhower/framework-arduinopico value
|
||||
# List of package versions: https://api.registry.platformio.org/v3/packages/earlephilhower/tool/framework-arduinopico
|
||||
return f"~1.{ver.major}{ver.minor:02d}{ver.patch:02d}.0"
|
||||
|
||||
|
||||
# NOTE: Keep this in mind when updating the recommended version:
|
||||
# * The new version needs to be thoroughly validated before changing the
|
||||
# recommended version as otherwise a bunch of devices could be bricked
|
||||
# * For all constants below, update platformio.ini (in this repo)
|
||||
# and platformio.ini/platformio-lint.ini in the esphome-docker-base repository
|
||||
|
||||
# The default/recommended arduino framework version
|
||||
# - https://github.com/earlephilhower/arduino-pico/releases
|
||||
# - https://api.registry.platformio.org/v3/packages/earlephilhower/tool/framework-arduinopico
|
||||
RECOMMENDED_ARDUINO_FRAMEWORK_VERSION = cv.Version(2, 4, 0)
|
||||
|
||||
# The platformio/raspberrypi version to use for arduino frameworks
|
||||
# - https://github.com/platformio/platform-raspberrypi/releases
|
||||
# - https://api.registry.platformio.org/v3/packages/platformio/platform/raspberrypi
|
||||
ARDUINO_PLATFORM_VERSION = cv.Version(1, 7, 0)
|
||||
|
||||
|
||||
def _arduino_check_versions(value):
|
||||
value = value.copy()
|
||||
lookups = {
|
||||
"dev": (cv.Version(2, 4, 0), "https://github.com/earlephilhower/arduino-pico"),
|
||||
"latest": (cv.Version(2, 4, 0), None),
|
||||
"recommended": (RECOMMENDED_ARDUINO_FRAMEWORK_VERSION, None),
|
||||
}
|
||||
|
||||
if value[CONF_VERSION] in lookups:
|
||||
if CONF_SOURCE in value:
|
||||
raise cv.Invalid(
|
||||
"Framework version needs to be explicitly specified when custom source is used."
|
||||
)
|
||||
|
||||
version, source = lookups[value[CONF_VERSION]]
|
||||
else:
|
||||
version = cv.Version.parse(cv.version_number(value[CONF_VERSION]))
|
||||
source = value.get(CONF_SOURCE, None)
|
||||
|
||||
value[CONF_VERSION] = str(version)
|
||||
value[CONF_SOURCE] = source or _format_framework_arduino_version(version)
|
||||
|
||||
value[CONF_PLATFORM_VERSION] = value.get(
|
||||
CONF_PLATFORM_VERSION, _parse_platform_version(str(ARDUINO_PLATFORM_VERSION))
|
||||
)
|
||||
|
||||
if version != RECOMMENDED_ARDUINO_FRAMEWORK_VERSION:
|
||||
_LOGGER.warning(
|
||||
"The selected Arduino framework version is not the recommended one."
|
||||
)
|
||||
|
||||
return value
|
||||
|
||||
|
||||
def _parse_platform_version(value):
|
||||
try:
|
||||
# if platform version is a valid version constraint, prefix the default package
|
||||
cv.platformio_version_constraint(value)
|
||||
return f"platformio/raspberrypi @ {value}"
|
||||
except cv.Invalid:
|
||||
return value
|
||||
|
||||
|
||||
CONF_PLATFORM_VERSION = "platform_version"
|
||||
|
||||
ARDUINO_FRAMEWORK_SCHEMA = cv.All(
|
||||
cv.Schema(
|
||||
{
|
||||
cv.Optional(CONF_VERSION, default="recommended"): cv.string_strict,
|
||||
cv.Optional(CONF_SOURCE): cv.string_strict,
|
||||
cv.Optional(CONF_PLATFORM_VERSION): _parse_platform_version,
|
||||
}
|
||||
),
|
||||
_arduino_check_versions,
|
||||
)
|
||||
|
||||
CONFIG_SCHEMA = cv.All(
|
||||
cv.Schema(
|
||||
{
|
||||
cv.Required(CONF_BOARD): cv.string_strict,
|
||||
cv.Optional(CONF_FRAMEWORK, default={}): ARDUINO_FRAMEWORK_SCHEMA,
|
||||
}
|
||||
),
|
||||
set_core_data,
|
||||
)
|
||||
|
||||
|
||||
@coroutine_with_priority(1000)
|
||||
async def to_code(config):
|
||||
cg.add(rp2040_ns.setup_preferences())
|
||||
|
||||
cg.add_platformio_option("board", config[CONF_BOARD])
|
||||
cg.add_build_flag("-DUSE_RP2040")
|
||||
cg.add_define("ESPHOME_BOARD", config[CONF_BOARD])
|
||||
cg.add_define("ESPHOME_VARIANT", "RP2040")
|
||||
|
||||
conf = config[CONF_FRAMEWORK]
|
||||
cg.add_platformio_option("framework", "arduino")
|
||||
cg.add_build_flag("-DUSE_ARDUINO")
|
||||
cg.add_build_flag("-DUSE_RP2040_FRAMEWORK_ARDUINO")
|
||||
# cg.add_build_flag("-DPICO_BOARD=pico_w")
|
||||
cg.add_platformio_option("platform", conf[CONF_PLATFORM_VERSION])
|
||||
cg.add_platformio_option(
|
||||
"platform_packages",
|
||||
[f"earlephilhower/framework-arduinopico @ {conf[CONF_SOURCE]}"],
|
||||
)
|
||||
|
||||
cg.add_platformio_option("board_build.core", "earlephilhower")
|
||||
cg.add_platformio_option("board_build.filesystem_size", "0.5m")
|
||||
|
||||
ver: cv.Version = CORE.data[KEY_CORE][KEY_FRAMEWORK_VERSION]
|
||||
cg.add_define(
|
||||
"USE_ARDUINO_VERSION_CODE",
|
||||
cg.RawExpression(f"VERSION_CODE({ver.major}, {ver.minor}, {ver.patch})"),
|
||||
)
|
7
esphome/components/rp2040/boards.py
Normal file
7
esphome/components/rp2040/boards.py
Normal file
|
@ -0,0 +1,7 @@
|
|||
RP2040_BASE_PINS = {}
|
||||
|
||||
RP2040_BOARD_PINS = {
|
||||
"pico": {"LED": 25},
|
||||
"rpipico": "pico",
|
||||
"rpipicow": {},
|
||||
}
|
6
esphome/components/rp2040/const.py
Normal file
6
esphome/components/rp2040/const.py
Normal file
|
@ -0,0 +1,6 @@
|
|||
import esphome.codegen as cg
|
||||
|
||||
KEY_BOARD = "board"
|
||||
KEY_RP2040 = "rp2040"
|
||||
|
||||
rp2040_ns = cg.esphome_ns.namespace("rp2040")
|
32
esphome/components/rp2040/core.cpp
Normal file
32
esphome/components/rp2040/core.cpp
Normal file
|
@ -0,0 +1,32 @@
|
|||
#ifdef USE_RP2040
|
||||
|
||||
#include "core.h"
|
||||
#include "esphome/core/hal.h"
|
||||
#include "esphome/core/helpers.h"
|
||||
|
||||
#include "hardware/watchdog.h"
|
||||
|
||||
namespace esphome {
|
||||
|
||||
void IRAM_ATTR HOT yield() { ::yield(); }
|
||||
uint32_t IRAM_ATTR HOT millis() { return ::millis(); }
|
||||
void IRAM_ATTR 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); }
|
||||
void arch_restart() {
|
||||
while (true) { // NOLINT(clang-diagnostic-unreachable-code)
|
||||
yield();
|
||||
}
|
||||
}
|
||||
void arch_init() { watchdog_enable(0x7fffff, false); }
|
||||
void IRAM_ATTR HOT arch_feed_wdt() { watchdog_update(); }
|
||||
|
||||
uint8_t progmem_read_byte(const uint8_t *addr) {
|
||||
return pgm_read_byte(addr); // NOLINT
|
||||
}
|
||||
uint32_t IRAM_ATTR HOT arch_get_cpu_cycle_count() { return ulMainGetRunTimeCounterValue(); }
|
||||
uint32_t arch_get_cpu_freq_hz() { return RP2040::f_cpu(); }
|
||||
|
||||
} // namespace esphome
|
||||
|
||||
#endif // USE_RP2040
|
14
esphome/components/rp2040/core.h
Normal file
14
esphome/components/rp2040/core.h
Normal file
|
@ -0,0 +1,14 @@
|
|||
#pragma once
|
||||
|
||||
#ifdef USE_RP2040
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <pico.h>
|
||||
|
||||
extern "C" unsigned long ulMainGetRunTimeCounterValue();
|
||||
|
||||
namespace esphome {
|
||||
namespace rp2040 {} // namespace rp2040
|
||||
} // namespace esphome
|
||||
|
||||
#endif // USE_RP2040
|
103
esphome/components/rp2040/gpio.cpp
Normal file
103
esphome/components/rp2040/gpio.cpp
Normal file
|
@ -0,0 +1,103 @@
|
|||
#ifdef USE_RP2040
|
||||
|
||||
#include "gpio.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace rp2040 {
|
||||
|
||||
static const char *const TAG = "rp2040";
|
||||
|
||||
static int IRAM_ATTR flags_to_mode(gpio::Flags flags, uint8_t pin) {
|
||||
if (flags == gpio::FLAG_INPUT) { // NOLINT(bugprone-branch-clone)
|
||||
return INPUT;
|
||||
} else if (flags == gpio::FLAG_OUTPUT) {
|
||||
return OUTPUT;
|
||||
} else if (flags == (gpio::FLAG_INPUT | gpio::FLAG_PULLUP)) {
|
||||
return INPUT_PULLUP;
|
||||
} else if (flags == (gpio::FLAG_INPUT | gpio::FLAG_PULLDOWN)) {
|
||||
return INPUT_PULLDOWN;
|
||||
// } else if (flags == (gpio::FLAG_OUTPUT | gpio::FLAG_OPEN_DRAIN)) {
|
||||
// return OpenDrain;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
struct ISRPinArg {
|
||||
uint8_t pin;
|
||||
bool inverted;
|
||||
};
|
||||
|
||||
ISRInternalGPIOPin RP2040GPIOPin::to_isr() const {
|
||||
auto *arg = new ISRPinArg{}; // NOLINT(cppcoreguidelines-owning-memory)
|
||||
arg->pin = pin_;
|
||||
arg->inverted = inverted_;
|
||||
return ISRInternalGPIOPin((void *) arg);
|
||||
}
|
||||
|
||||
void RP2040GPIOPin::attach_interrupt(void (*func)(void *), void *arg, gpio::InterruptType type) const {
|
||||
PinStatus arduino_mode = LOW;
|
||||
switch (type) {
|
||||
case gpio::INTERRUPT_RISING_EDGE:
|
||||
arduino_mode = inverted_ ? FALLING : RISING;
|
||||
break;
|
||||
case gpio::INTERRUPT_FALLING_EDGE:
|
||||
arduino_mode = inverted_ ? RISING : FALLING;
|
||||
break;
|
||||
case gpio::INTERRUPT_ANY_EDGE:
|
||||
arduino_mode = CHANGE;
|
||||
break;
|
||||
case gpio::INTERRUPT_LOW_LEVEL:
|
||||
arduino_mode = inverted_ ? HIGH : LOW;
|
||||
break;
|
||||
case gpio::INTERRUPT_HIGH_LEVEL:
|
||||
arduino_mode = inverted_ ? LOW : HIGH;
|
||||
break;
|
||||
}
|
||||
|
||||
attachInterrupt(pin_, func, arduino_mode, arg);
|
||||
}
|
||||
void RP2040GPIOPin::pin_mode(gpio::Flags flags) {
|
||||
pinMode(pin_, flags_to_mode(flags, pin_)); // NOLINT
|
||||
}
|
||||
|
||||
std::string RP2040GPIOPin::dump_summary() const {
|
||||
char buffer[32];
|
||||
snprintf(buffer, sizeof(buffer), "GPIO%u", pin_);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
bool RP2040GPIOPin::digital_read() {
|
||||
return bool(digitalRead(pin_)) != inverted_; // NOLINT
|
||||
}
|
||||
void RP2040GPIOPin::digital_write(bool value) {
|
||||
digitalWrite(pin_, value != inverted_ ? 1 : 0); // NOLINT
|
||||
}
|
||||
void RP2040GPIOPin::detach_interrupt() const { detachInterrupt(pin_); }
|
||||
|
||||
} // namespace rp2040
|
||||
|
||||
using namespace rp2040;
|
||||
|
||||
bool IRAM_ATTR ISRInternalGPIOPin::digital_read() {
|
||||
auto *arg = reinterpret_cast<ISRPinArg *>(arg_);
|
||||
return bool(digitalRead(arg->pin)) != arg->inverted; // NOLINT
|
||||
}
|
||||
void IRAM_ATTR ISRInternalGPIOPin::digital_write(bool value) {
|
||||
auto *arg = reinterpret_cast<ISRPinArg *>(arg_);
|
||||
digitalWrite(arg->pin, value != arg->inverted ? 1 : 0); // NOLINT
|
||||
}
|
||||
void IRAM_ATTR ISRInternalGPIOPin::clear_interrupt() {
|
||||
// TODO: implement
|
||||
// auto *arg = reinterpret_cast<ISRPinArg *>(arg_);
|
||||
// GPIO_REG_WRITE(GPIO_STATUS_W1TC_ADDRESS, 1UL << arg->pin);
|
||||
}
|
||||
void IRAM_ATTR ISRInternalGPIOPin::pin_mode(gpio::Flags flags) {
|
||||
auto *arg = reinterpret_cast<ISRPinArg *>(arg_);
|
||||
pinMode(arg->pin, flags_to_mode(flags, arg->pin)); // NOLINT
|
||||
}
|
||||
|
||||
} // namespace esphome
|
||||
|
||||
#endif // USE_RP2040
|
38
esphome/components/rp2040/gpio.h
Normal file
38
esphome/components/rp2040/gpio.h
Normal file
|
@ -0,0 +1,38 @@
|
|||
#pragma once
|
||||
|
||||
#ifdef USE_RP2040
|
||||
|
||||
#include <Arduino.h>
|
||||
#include "esphome/core/hal.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace rp2040 {
|
||||
|
||||
class RP2040GPIOPin : 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 rp2040
|
||||
} // namespace esphome
|
||||
|
||||
#endif // USE_RP2040
|
91
esphome/components/rp2040/gpio.py
Normal file
91
esphome/components/rp2040/gpio.py
Normal file
|
@ -0,0 +1,91 @@
|
|||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
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.core import CORE
|
||||
from esphome import pins
|
||||
|
||||
from . import boards
|
||||
from .const import KEY_BOARD, KEY_RP2040, rp2040_ns
|
||||
|
||||
RP2040GPIOPin = rp2040_ns.class_("RP2040GPIOPin", cg.InternalGPIOPin)
|
||||
|
||||
|
||||
def _lookup_pin(value):
|
||||
board = CORE.data[KEY_RP2040][KEY_BOARD]
|
||||
board_pins = boards.RP2040_BOARD_PINS.get(board, {})
|
||||
|
||||
while isinstance(board_pins, str):
|
||||
board_pins = boards.RP2040_BOARD_PINS[board_pins]
|
||||
|
||||
if value in board_pins:
|
||||
return board_pins[value]
|
||||
if value in boards.RP2040_BASE_PINS:
|
||||
return boards.RP2040_BASE_PINS[value]
|
||||
raise cv.Invalid(f"Cannot resolve pin name '{value}' for board {board}.")
|
||||
|
||||
|
||||
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 _lookup_pin(value)
|
||||
|
||||
|
||||
def validate_gpio_pin(value):
|
||||
value = _translate_pin(value)
|
||||
if value < 0 or value > 29:
|
||||
raise cv.Invalid(f"RP2040: Invalid pin number: {value}")
|
||||
return value
|
||||
|
||||
|
||||
CONF_ANALOG = "analog"
|
||||
|
||||
RP2040_PIN_SCHEMA = cv.All(
|
||||
cv.Schema(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(RP2040GPIOPin),
|
||||
cv.Required(CONF_NUMBER): validate_gpio_pin,
|
||||
cv.Optional(CONF_MODE, default={}): cv.Schema(
|
||||
{
|
||||
cv.Optional(CONF_ANALOG, default=False): cv.boolean,
|
||||
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("rp2040", RP2040_PIN_SCHEMA)
|
||||
async def rp2040_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
|
49
esphome/components/rp2040/preferences.cpp
Normal file
49
esphome/components/rp2040/preferences.cpp
Normal file
|
@ -0,0 +1,49 @@
|
|||
#ifdef USE_RP2040
|
||||
|
||||
#include "preferences.h"
|
||||
|
||||
#include <cstring>
|
||||
#include "esphome/core/helpers.h"
|
||||
#include "esphome/core/log.h"
|
||||
#include "esphome/core/preferences.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace rp2040 {
|
||||
|
||||
static const char *const TAG = "rp2040.preferences";
|
||||
|
||||
class RP2040PreferenceBackend : public ESPPreferenceBackend {
|
||||
public:
|
||||
bool save(const uint8_t *data, size_t len) override { return true; }
|
||||
bool load(uint8_t *data, size_t len) override { return false; }
|
||||
};
|
||||
|
||||
class RP2040Preferences : public ESPPreferences {
|
||||
public:
|
||||
ESPPreferenceObject make_preference(size_t length, uint32_t type, bool in_flash) override {
|
||||
auto *pref = new RP2040PreferenceBackend(); // NOLINT(cppcoreguidelines-owning-memory)
|
||||
return ESPPreferenceObject(pref);
|
||||
}
|
||||
|
||||
ESPPreferenceObject make_preference(size_t length, uint32_t type) override {
|
||||
auto *pref = new RP2040PreferenceBackend(); // NOLINT(cppcoreguidelines-owning-memory)
|
||||
return ESPPreferenceObject(pref);
|
||||
}
|
||||
|
||||
bool sync() override { return true; }
|
||||
|
||||
bool reset() override { return true; }
|
||||
};
|
||||
|
||||
void setup_preferences() {
|
||||
auto *prefs = new RP2040Preferences(); // NOLINT(cppcoreguidelines-owning-memory)
|
||||
global_preferences = prefs;
|
||||
}
|
||||
|
||||
} // namespace rp2040
|
||||
|
||||
ESPPreferences *global_preferences; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
|
||||
} // namespace esphome
|
||||
|
||||
#endif // USE_RP2040
|
13
esphome/components/rp2040/preferences.h
Normal file
13
esphome/components/rp2040/preferences.h
Normal file
|
@ -0,0 +1,13 @@
|
|||
#pragma once
|
||||
|
||||
#ifdef USE_RP2040
|
||||
|
||||
namespace esphome {
|
||||
namespace rp2040 {
|
||||
|
||||
void setup_preferences();
|
||||
|
||||
} // namespace rp2040
|
||||
} // namespace esphome
|
||||
|
||||
#endif // USE_RP2040
|
0
esphome/components/rp2040_pwm/__init__.py
Normal file
0
esphome/components/rp2040_pwm/__init__.py
Normal file
55
esphome/components/rp2040_pwm/output.py
Normal file
55
esphome/components/rp2040_pwm/output.py
Normal file
|
@ -0,0 +1,55 @@
|
|||
from esphome import pins, automation
|
||||
from esphome.components import output
|
||||
import esphome.config_validation as cv
|
||||
import esphome.codegen as cg
|
||||
from esphome.const import (
|
||||
CONF_FREQUENCY,
|
||||
CONF_ID,
|
||||
CONF_PIN,
|
||||
)
|
||||
|
||||
CODEOWNERS = ["@jesserockz"]
|
||||
DEPENDENCIES = ["rp2040"]
|
||||
|
||||
|
||||
rp2040_pwm_ns = cg.esphome_ns.namespace("rp2040_pwm")
|
||||
RP2040PWM = rp2040_pwm_ns.class_("RP2040PWM", output.FloatOutput, cg.Component)
|
||||
SetFrequencyAction = rp2040_pwm_ns.class_("SetFrequencyAction", automation.Action)
|
||||
validate_frequency = cv.All(cv.frequency, cv.Range(min=1.0e-6))
|
||||
|
||||
CONFIG_SCHEMA = output.FLOAT_OUTPUT_SCHEMA.extend(
|
||||
{
|
||||
cv.Required(CONF_ID): cv.declare_id(RP2040PWM),
|
||||
cv.Required(CONF_PIN): pins.internal_gpio_output_pin_schema,
|
||||
cv.Optional(CONF_FREQUENCY, default="1kHz"): validate_frequency,
|
||||
}
|
||||
).extend(cv.COMPONENT_SCHEMA)
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
await cg.register_component(var, config)
|
||||
await output.register_output(var, config)
|
||||
|
||||
pin = await cg.gpio_pin_expression(config[CONF_PIN])
|
||||
cg.add(var.set_pin(pin))
|
||||
|
||||
cg.add(var.set_frequency(config[CONF_FREQUENCY]))
|
||||
|
||||
|
||||
@automation.register_action(
|
||||
"output.rp2040_pwm.set_frequency",
|
||||
SetFrequencyAction,
|
||||
cv.Schema(
|
||||
{
|
||||
cv.Required(CONF_ID): cv.use_id(RP2040PWM),
|
||||
cv.Required(CONF_FREQUENCY): cv.templatable(validate_frequency),
|
||||
}
|
||||
),
|
||||
)
|
||||
async def rp2040_set_frequency_to_code(config, action_id, template_arg, args):
|
||||
paren = await cg.get_variable(config[CONF_ID])
|
||||
var = cg.new_Pvariable(action_id, template_arg, paren)
|
||||
template_ = await cg.templatable(config[CONF_FREQUENCY], args, float)
|
||||
cg.add(var.set_frequency(template_))
|
||||
return var
|
45
esphome/components/rp2040_pwm/rp2040_pwm.cpp
Normal file
45
esphome/components/rp2040_pwm/rp2040_pwm.cpp
Normal file
|
@ -0,0 +1,45 @@
|
|||
#ifdef USE_RP2040
|
||||
|
||||
#include "rp2040_pwm.h"
|
||||
#include "esphome/core/defines.h"
|
||||
#include "esphome/core/helpers.h"
|
||||
#include "esphome/core/log.h"
|
||||
#include "esphome/core/macros.h"
|
||||
|
||||
#include <PinNames.h>
|
||||
|
||||
namespace esphome {
|
||||
namespace rp2040_pwm {
|
||||
|
||||
static const char *const TAG = "rp2040_pwm";
|
||||
|
||||
void RP2040PWM::setup() {
|
||||
ESP_LOGCONFIG(TAG, "Setting up RP2040 PWM Output...");
|
||||
this->pin_->setup();
|
||||
this->pwm_ = new mbed::PwmOut((PinName) this->pin_->get_pin());
|
||||
this->turn_off();
|
||||
}
|
||||
void RP2040PWM::dump_config() {
|
||||
ESP_LOGCONFIG(TAG, "RP2040 PWM:");
|
||||
LOG_PIN(" Pin: ", this->pin_);
|
||||
ESP_LOGCONFIG(TAG, " Frequency: %.1f Hz", this->frequency_);
|
||||
LOG_FLOAT_OUTPUT(this);
|
||||
}
|
||||
void HOT RP2040PWM::write_state(float state) {
|
||||
this->last_output_ = state;
|
||||
|
||||
// Also check pin inversion
|
||||
if (this->pin_->is_inverted()) {
|
||||
state = 1.0f - state;
|
||||
}
|
||||
|
||||
auto total_time_us = static_cast<uint32_t>(roundf(1e6f / this->frequency_));
|
||||
|
||||
this->pwm_->period_us(total_time_us);
|
||||
this->pwm_->write(state);
|
||||
}
|
||||
|
||||
} // namespace rp2040_pwm
|
||||
} // namespace esphome
|
||||
|
||||
#endif
|
57
esphome/components/rp2040_pwm/rp2040_pwm.h
Normal file
57
esphome/components/rp2040_pwm/rp2040_pwm.h
Normal file
|
@ -0,0 +1,57 @@
|
|||
#pragma once
|
||||
|
||||
#ifdef USE_RP2040
|
||||
|
||||
#include "esphome/components/output/float_output.h"
|
||||
#include "esphome/core/automation.h"
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/core/hal.h"
|
||||
|
||||
#include "drivers/PwmOut.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace rp2040_pwm {
|
||||
|
||||
class RP2040PWM : public output::FloatOutput, public Component {
|
||||
public:
|
||||
void set_pin(InternalGPIOPin *pin) { pin_ = pin; }
|
||||
void set_frequency(float frequency) { this->frequency_ = frequency; }
|
||||
/// Dynamically update frequency
|
||||
void update_frequency(float frequency) override {
|
||||
this->set_frequency(frequency);
|
||||
this->write_state(this->last_output_);
|
||||
}
|
||||
|
||||
/// Initialize pin
|
||||
void setup() override;
|
||||
void dump_config() override;
|
||||
/// HARDWARE setup_priority
|
||||
float get_setup_priority() const override { return setup_priority::HARDWARE; }
|
||||
|
||||
protected:
|
||||
void write_state(float state) override;
|
||||
|
||||
InternalGPIOPin *pin_;
|
||||
mbed::PwmOut *pwm_;
|
||||
float frequency_{1000.0};
|
||||
/// Cache last output level for dynamic frequency updating
|
||||
float last_output_{0.0};
|
||||
};
|
||||
|
||||
template<typename... Ts> class SetFrequencyAction : public Action<Ts...> {
|
||||
public:
|
||||
SetFrequencyAction(RP2040PWM *parent) : parent_(parent) {}
|
||||
TEMPLATABLE_VALUE(float, frequency);
|
||||
|
||||
void play(Ts... x) {
|
||||
float freq = this->frequency_.value(x...);
|
||||
this->parent_->update_frequency(freq);
|
||||
}
|
||||
|
||||
RP2040PWM *parent_;
|
||||
};
|
||||
|
||||
} // namespace rp2040_pwm
|
||||
} // namespace esphome
|
||||
|
||||
#endif // USE_RP2040
|
|
@ -13,6 +13,7 @@ CONFIG_SCHEMA = cv.Schema(
|
|||
CONF_IMPLEMENTATION,
|
||||
esp8266=IMPLEMENTATION_LWIP_TCP,
|
||||
esp32=IMPLEMENTATION_BSD_SOCKETS,
|
||||
rp2040=IMPLEMENTATION_LWIP_TCP,
|
||||
): cv.one_of(
|
||||
IMPLEMENTATION_LWIP_TCP, IMPLEMENTATION_BSD_SOCKETS, lower=True, space="_"
|
||||
),
|
||||
|
|
|
@ -91,7 +91,7 @@ struct iovec {
|
|||
size_t iov_len;
|
||||
};
|
||||
|
||||
#ifdef USE_ESP8266
|
||||
#if defined(USE_ESP8266) || defined(USE_RP2040)
|
||||
// arduino-esp8266 declares a global vars called INADDR_NONE/ANY which are invalid with the define
|
||||
#ifdef INADDR_ANY
|
||||
#undef INADDR_ANY
|
||||
|
|
|
@ -46,9 +46,7 @@ async def to_code(config):
|
|||
mosi = await cg.gpio_pin_expression(config[CONF_MOSI_PIN])
|
||||
cg.add(var.set_mosi(mosi))
|
||||
|
||||
if CORE.is_esp32 and CORE.using_arduino:
|
||||
cg.add_library("SPI", None)
|
||||
if CORE.is_esp8266:
|
||||
if CORE.using_arduino:
|
||||
cg.add_library("SPI", None)
|
||||
|
||||
|
||||
|
|
|
@ -105,7 +105,11 @@ class SPIComponent : public Component {
|
|||
void write_byte(uint8_t data) {
|
||||
#ifdef USE_SPI_ARDUINO_BACKEND
|
||||
if (this->hw_spi_ != nullptr) {
|
||||
#ifdef USE_RP2040
|
||||
this->hw_spi_->transfer(data);
|
||||
#else
|
||||
this->hw_spi_->write(data);
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
#endif // USE_SPI_ARDUINO_BACKEND
|
||||
|
@ -116,7 +120,11 @@ class SPIComponent : public Component {
|
|||
void write_byte16(const uint16_t data) {
|
||||
#ifdef USE_SPI_ARDUINO_BACKEND
|
||||
if (this->hw_spi_ != nullptr) {
|
||||
#ifdef USE_RP2040
|
||||
this->hw_spi_->transfer16(data);
|
||||
#else
|
||||
this->hw_spi_->write16(data);
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
#endif // USE_SPI_ARDUINO_BACKEND
|
||||
|
@ -130,7 +138,11 @@ class SPIComponent : public Component {
|
|||
#ifdef USE_SPI_ARDUINO_BACKEND
|
||||
if (this->hw_spi_ != nullptr) {
|
||||
for (size_t i = 0; i < length; i++) {
|
||||
#ifdef USE_RP2040
|
||||
this->hw_spi_->transfer16(data[i]);
|
||||
#else
|
||||
this->hw_spi_->write16(data[i]);
|
||||
#endif
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
@ -145,7 +157,11 @@ class SPIComponent : public Component {
|
|||
#ifdef USE_SPI_ARDUINO_BACKEND
|
||||
if (this->hw_spi_ != nullptr) {
|
||||
auto *data_c = const_cast<uint8_t *>(data);
|
||||
#ifdef USE_RP2040
|
||||
this->hw_spi_->transfer(data_c, length);
|
||||
#else
|
||||
this->hw_spi_->writeBytes(data_c, length);
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
#endif // USE_SPI_ARDUINO_BACKEND
|
||||
|
@ -178,7 +194,11 @@ class SPIComponent : public Component {
|
|||
if (this->miso_ != nullptr) {
|
||||
this->hw_spi_->transfer(data, length);
|
||||
} else {
|
||||
#ifdef USE_RP2040
|
||||
this->hw_spi_->transfer(data, length);
|
||||
#else
|
||||
this->hw_spi_->writeBytes(data, length);
|
||||
#endif
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -267,7 +267,7 @@ CONFIG_SCHEMA = cv.All(
|
|||
CONF_REBOOT_TIMEOUT, default="15min"
|
||||
): cv.positive_time_period_milliseconds,
|
||||
cv.SplitDefault(
|
||||
CONF_POWER_SAVE_MODE, esp8266="none", esp32="light"
|
||||
CONF_POWER_SAVE_MODE, esp8266="none", esp32="light", rp2040="light"
|
||||
): cv.enum(WIFI_POWER_SAVE_MODES, upper=True),
|
||||
cv.Optional(CONF_FAST_CONNECT, default=False): cv.boolean,
|
||||
cv.Optional(CONF_USE_ADDRESS): cv.string_strict,
|
||||
|
@ -386,6 +386,8 @@ async def to_code(config):
|
|||
cg.add_library("ESP8266WiFi", None)
|
||||
elif CORE.is_esp32 and CORE.using_arduino:
|
||||
cg.add_library("WiFi", None)
|
||||
elif CORE.is_rp2040:
|
||||
cg.add_library("WiFi", None)
|
||||
|
||||
if CORE.is_esp32 and CORE.using_esp_idf:
|
||||
if config[CONF_ENABLE_BTM] or config[CONF_ENABLE_RRM]:
|
||||
|
|
|
@ -710,6 +710,8 @@ int8_t WiFiScanResult::get_rssi() const { return this->rssi_; }
|
|||
bool WiFiScanResult::get_with_auth() const { return this->with_auth_; }
|
||||
bool WiFiScanResult::get_is_hidden() const { return this->is_hidden_; }
|
||||
|
||||
bool WiFiScanResult::operator==(const WiFiScanResult &rhs) const { return this->bssid_ == rhs.bssid_; }
|
||||
|
||||
WiFiComponent *global_wifi_component; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
|
||||
} // namespace wifi
|
||||
|
|
|
@ -24,6 +24,16 @@ extern "C" {
|
|||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef USE_RP2040
|
||||
extern "C" {
|
||||
#include "cyw43.h"
|
||||
#include "cyw43_country.h"
|
||||
#include "pico/cyw43_arch.h"
|
||||
}
|
||||
|
||||
#include <WiFi.h>
|
||||
#endif
|
||||
|
||||
namespace esphome {
|
||||
namespace wifi {
|
||||
|
||||
|
@ -138,6 +148,8 @@ class WiFiScanResult {
|
|||
float get_priority() const { return priority_; }
|
||||
void set_priority(float priority) { priority_ = priority; }
|
||||
|
||||
bool operator==(const WiFiScanResult &rhs) const;
|
||||
|
||||
protected:
|
||||
bool matches_{false};
|
||||
bssid_t bssid_;
|
||||
|
@ -310,6 +322,11 @@ class WiFiComponent : public Component {
|
|||
void wifi_process_event_(IDFWiFiEvent *data);
|
||||
#endif
|
||||
|
||||
#ifdef USE_RP2040
|
||||
static int s_wifi_scan_result(void *env, const cyw43_ev_scan_result_t *result);
|
||||
void wifi_scan_result(void *env, const cyw43_ev_scan_result_t *result);
|
||||
#endif
|
||||
|
||||
std::string use_address_;
|
||||
std::vector<WiFiAP> sta_;
|
||||
std::vector<WiFiSTAPriority> sta_priorities_;
|
||||
|
|
204
esphome/components/wifi/wifi_component_pico_w.cpp
Normal file
204
esphome/components/wifi/wifi_component_pico_w.cpp
Normal file
|
@ -0,0 +1,204 @@
|
|||
|
||||
#include "wifi_component.h"
|
||||
|
||||
#ifdef USE_RP2040
|
||||
|
||||
#include "lwip/dns.h"
|
||||
#include "lwip/err.h"
|
||||
#include "lwip/netif.h"
|
||||
|
||||
#include "esphome/core/application.h"
|
||||
#include "esphome/core/hal.h"
|
||||
#include "esphome/core/helpers.h"
|
||||
#include "esphome/core/log.h"
|
||||
#include "esphome/core/util.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace wifi {
|
||||
|
||||
static const char *const TAG = "wifi_pico_w";
|
||||
|
||||
bool WiFiComponent::wifi_mode_(optional<bool> sta, optional<bool> ap) {
|
||||
if (sta.has_value()) {
|
||||
if (sta.value()) {
|
||||
cyw43_wifi_set_up(&cyw43_state, CYW43_ITF_STA, true, CYW43_COUNTRY_WORLDWIDE);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WiFiComponent::wifi_apply_power_save_() {
|
||||
uint32_t pm;
|
||||
switch (this->power_save_) {
|
||||
case WIFI_POWER_SAVE_NONE:
|
||||
pm = CYW43_PERFORMANCE_PM;
|
||||
break;
|
||||
case WIFI_POWER_SAVE_LIGHT:
|
||||
pm = CYW43_DEFAULT_PM;
|
||||
break;
|
||||
case WIFI_POWER_SAVE_HIGH:
|
||||
pm = CYW43_AGGRESSIVE_PM;
|
||||
break;
|
||||
}
|
||||
int ret = cyw43_wifi_pm(&cyw43_state, pm);
|
||||
return ret == 0;
|
||||
}
|
||||
|
||||
// TODO: The driver doesnt seem to have an API for this
|
||||
bool WiFiComponent::wifi_apply_output_power_(float output_power) { return true; }
|
||||
|
||||
bool WiFiComponent::wifi_sta_connect_(const WiFiAP &ap) {
|
||||
if (!this->wifi_sta_ip_config_(ap.get_manual_ip()))
|
||||
return false;
|
||||
|
||||
auto ret = WiFi.begin(ap.get_ssid().c_str(), ap.get_password().c_str());
|
||||
if (ret != WL_CONNECTED)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WiFiComponent::wifi_sta_pre_setup_() { return this->wifi_mode_(true, {}); }
|
||||
|
||||
bool WiFiComponent::wifi_sta_ip_config_(optional<ManualIP> manual_ip) {
|
||||
if (!manual_ip.has_value()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
IPAddress ip_address = IPAddress(manual_ip->static_ip);
|
||||
IPAddress gateway = IPAddress(manual_ip->gateway);
|
||||
IPAddress subnet = IPAddress(manual_ip->subnet);
|
||||
|
||||
IPAddress dns = IPAddress(manual_ip->dns1);
|
||||
|
||||
WiFi.config(ip_address, dns, gateway, subnet);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WiFiComponent::wifi_apply_hostname_() {
|
||||
WiFi.setHostname(App.get_name().c_str());
|
||||
return true;
|
||||
}
|
||||
const char *get_auth_mode_str(uint8_t mode) {
|
||||
// TODO:
|
||||
return "UNKNOWN";
|
||||
}
|
||||
const char *get_disconnect_reason_str(uint8_t reason) {
|
||||
// TODO:
|
||||
return "UNKNOWN";
|
||||
}
|
||||
|
||||
WiFiSTAConnectStatus WiFiComponent::wifi_sta_connect_status_() {
|
||||
int status = cyw43_tcpip_link_status(&cyw43_state, CYW43_ITF_STA);
|
||||
switch (status) {
|
||||
case CYW43_LINK_JOIN:
|
||||
case CYW43_LINK_NOIP:
|
||||
return WiFiSTAConnectStatus::CONNECTING;
|
||||
case CYW43_LINK_UP:
|
||||
return WiFiSTAConnectStatus::CONNECTED;
|
||||
case CYW43_LINK_FAIL:
|
||||
case CYW43_LINK_BADAUTH:
|
||||
return WiFiSTAConnectStatus::ERROR_CONNECT_FAILED;
|
||||
case CYW43_LINK_NONET:
|
||||
return WiFiSTAConnectStatus::ERROR_NETWORK_NOT_FOUND;
|
||||
}
|
||||
return WiFiSTAConnectStatus::IDLE;
|
||||
}
|
||||
|
||||
int WiFiComponent::s_wifi_scan_result(void *env, const cyw43_ev_scan_result_t *result) {
|
||||
global_wifi_component->wifi_scan_result(env, result);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void WiFiComponent::wifi_scan_result(void *env, const cyw43_ev_scan_result_t *result) {
|
||||
bssid_t bssid;
|
||||
std::copy(result->bssid, result->bssid + 6, bssid.begin());
|
||||
std::string ssid(reinterpret_cast<const char *>(result->ssid));
|
||||
WiFiScanResult res(bssid, ssid, result->channel, result->rssi, result->auth_mode != CYW43_AUTH_OPEN, ssid.empty());
|
||||
if (std::find(this->scan_result_.begin(), this->scan_result_.end(), res) == this->scan_result_.end()) {
|
||||
this->scan_result_.push_back(res);
|
||||
}
|
||||
}
|
||||
|
||||
bool WiFiComponent::wifi_scan_start_() {
|
||||
this->scan_result_.clear();
|
||||
this->scan_done_ = false;
|
||||
cyw43_wifi_scan_options_t scan_options = {0};
|
||||
int err = cyw43_wifi_scan(&cyw43_state, &scan_options, nullptr, &s_wifi_scan_result);
|
||||
if (err) {
|
||||
ESP_LOGV(TAG, "cyw43_wifi_scan failed!");
|
||||
}
|
||||
return err == 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WiFiComponent::wifi_ap_ip_config_(optional<ManualIP> manual_ip) {
|
||||
// TODO:
|
||||
return false;
|
||||
}
|
||||
|
||||
bool WiFiComponent::wifi_start_ap_(const WiFiAP &ap) {
|
||||
if (!this->wifi_mode_({}, true))
|
||||
return false;
|
||||
|
||||
if (ap.get_channel().has_value()) {
|
||||
cyw43_wifi_ap_set_channel(&cyw43_state, ap.get_channel().value());
|
||||
}
|
||||
|
||||
const char *ssid = ap.get_ssid().c_str();
|
||||
|
||||
cyw43_wifi_ap_set_ssid(&cyw43_state, strlen(ssid), (const uint8_t *) ssid);
|
||||
|
||||
if (!ap.get_password().empty()) {
|
||||
const char *password = ap.get_password().c_str();
|
||||
cyw43_wifi_ap_set_password(&cyw43_state, strlen(password), (const uint8_t *) password);
|
||||
cyw43_wifi_ap_set_auth(&cyw43_state, CYW43_AUTH_WPA2_MIXED_PSK);
|
||||
} else {
|
||||
cyw43_wifi_ap_set_auth(&cyw43_state, CYW43_AUTH_OPEN);
|
||||
}
|
||||
cyw43_wifi_set_up(&cyw43_state, CYW43_ITF_AP, true, CYW43_COUNTRY_WORLDWIDE);
|
||||
|
||||
return true;
|
||||
}
|
||||
network::IPAddress WiFiComponent::wifi_soft_ap_ip() { return {WiFi.localIP()}; }
|
||||
|
||||
bool WiFiComponent::wifi_disconnect_() {
|
||||
int err = cyw43_wifi_leave(&cyw43_state, CYW43_ITF_STA);
|
||||
return err == 0;
|
||||
}
|
||||
// NOTE: The driver does not provide an interface to get this
|
||||
bssid_t WiFiComponent::wifi_bssid() {
|
||||
bssid_t bssid{};
|
||||
uint8_t raw_bssid[6];
|
||||
WiFi.BSSID(raw_bssid);
|
||||
for (size_t i = 0; i < bssid.size(); i++)
|
||||
bssid[i] = raw_bssid[i];
|
||||
return bssid;
|
||||
}
|
||||
// NOTE: The driver does not provide an interface to get this
|
||||
std::string WiFiComponent::wifi_ssid() { return WiFi.SSID(); }
|
||||
// NOTE: The driver does not provide an interface to get this
|
||||
int8_t WiFiComponent::wifi_rssi() { return WiFi.RSSI(); }
|
||||
// NOTE: The driver does not provide an interface to get this
|
||||
int32_t WiFiComponent::wifi_channel_() { return 0; }
|
||||
network::IPAddress WiFiComponent::wifi_sta_ip() { return {WiFi.localIP()}; }
|
||||
network::IPAddress WiFiComponent::wifi_subnet_mask_() { return {WiFi.subnetMask()}; }
|
||||
network::IPAddress WiFiComponent::wifi_gateway_ip_() { return {WiFi.gatewayIP()}; }
|
||||
network::IPAddress WiFiComponent::wifi_dns_ip_(int num) {
|
||||
const ip_addr_t *dns_ip = dns_getserver(num);
|
||||
return {dns_ip->addr};
|
||||
}
|
||||
|
||||
void WiFiComponent::wifi_loop_() {
|
||||
if (this->state_ == WIFI_COMPONENT_STATE_STA_SCANNING && !cyw43_wifi_scan_active(&cyw43_state)) {
|
||||
this->scan_done_ = true;
|
||||
ESP_LOGV(TAG, "Scan done!");
|
||||
}
|
||||
}
|
||||
|
||||
void WiFiComponent::wifi_pre_setup_() {}
|
||||
|
||||
} // namespace wifi
|
||||
} // namespace esphome
|
||||
|
||||
#endif
|
|
@ -1440,6 +1440,7 @@ class SplitDefault(Optional):
|
|||
esp32=vol.UNDEFINED,
|
||||
esp32_arduino=vol.UNDEFINED,
|
||||
esp32_idf=vol.UNDEFINED,
|
||||
rp2040=vol.UNDEFINED,
|
||||
):
|
||||
super().__init__(key)
|
||||
self._esp8266_default = vol.default_factory(esp8266)
|
||||
|
@ -1449,6 +1450,7 @@ class SplitDefault(Optional):
|
|||
self._esp32_idf_default = vol.default_factory(
|
||||
esp32_idf if esp32 is vol.UNDEFINED else esp32
|
||||
)
|
||||
self._rp2040_default = vol.default_factory(rp2040)
|
||||
|
||||
@property
|
||||
def default(self):
|
||||
|
@ -1458,6 +1460,8 @@ class SplitDefault(Optional):
|
|||
return self._esp32_arduino_default
|
||||
if CORE.is_esp32 and CORE.using_esp_idf:
|
||||
return self._esp32_idf_default
|
||||
if CORE.is_rp2040:
|
||||
return self._rp2040_default
|
||||
raise NotImplementedError
|
||||
|
||||
@default.setter
|
||||
|
|
|
@ -6,8 +6,9 @@ ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_"
|
|||
|
||||
PLATFORM_ESP32 = "esp32"
|
||||
PLATFORM_ESP8266 = "esp8266"
|
||||
PLATFORM_RP2040 = "rp2040"
|
||||
|
||||
TARGET_PLATFORMS = [PLATFORM_ESP32, PLATFORM_ESP8266]
|
||||
TARGET_PLATFORMS = [PLATFORM_ESP32, PLATFORM_ESP8266, PLATFORM_RP2040]
|
||||
|
||||
SOURCE_FILE_EXTENSIONS = {".cpp", ".hpp", ".h", ".c", ".tcc", ".ino"}
|
||||
HEADER_FILE_EXTENSIONS = {".h", ".hpp", ".tcc"}
|
||||
|
|
|
@ -594,6 +594,10 @@ class EsphomeCore:
|
|||
def is_esp32(self):
|
||||
return self.target_platform == "esp32"
|
||||
|
||||
@property
|
||||
def is_rp2040(self):
|
||||
return self.target_platform == "rp2040"
|
||||
|
||||
@property
|
||||
def target_framework(self):
|
||||
return self.data[KEY_CORE][KEY_TARGET_FRAMEWORK]
|
||||
|
|
|
@ -21,6 +21,8 @@
|
|||
#include "esp_system.h"
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <freertos/portmacro.h>
|
||||
#elif defined(USE_RP2040) && defined(USE_WIFI)
|
||||
#include <WiFi.h>
|
||||
#endif
|
||||
|
||||
#ifdef USE_ESP32_IGNORE_EFUSE_MAC_CRC
|
||||
|
@ -91,6 +93,8 @@ uint32_t random_uint32() {
|
|||
return esp_random();
|
||||
#elif defined(USE_ESP8266)
|
||||
return os_random();
|
||||
#elif defined(USE_RP2040)
|
||||
return ((uint32_t) rand()) << 16 + ((uint32_t) rand());
|
||||
#else
|
||||
#error "No random source available for this configuration."
|
||||
#endif
|
||||
|
@ -102,6 +106,8 @@ bool random_bytes(uint8_t *data, size_t len) {
|
|||
return true;
|
||||
#elif defined(USE_ESP8266)
|
||||
return os_get_random(data, len) == 0;
|
||||
#elif defined(USE_RP2040)
|
||||
return false;
|
||||
#else
|
||||
#error "No random source available for this configuration."
|
||||
#endif
|
||||
|
@ -409,6 +415,8 @@ void get_mac_address_raw(uint8_t *mac) {
|
|||
#endif
|
||||
#elif defined(USE_ESP8266)
|
||||
wifi_get_macaddr(STATION_IF, mac);
|
||||
#elif defined(USE_RP2040) && defined(USE_WIFI)
|
||||
WiFi.macAddress(mac);
|
||||
#endif
|
||||
}
|
||||
std::string get_mac_address() {
|
||||
|
|
|
@ -428,21 +428,27 @@ class DownloadBinaryRequestHandler(BaseHandler):
|
|||
def get(self, configuration=None):
|
||||
type = self.get_argument("type", "firmware.bin")
|
||||
|
||||
if type == "firmware.bin":
|
||||
storage_path = ext_storage_path(settings.config_dir, configuration)
|
||||
storage_json = StorageJSON.load(storage_path)
|
||||
if storage_json is None:
|
||||
self.send_error(404)
|
||||
return
|
||||
storage_path = ext_storage_path(settings.config_dir, configuration)
|
||||
storage_json = StorageJSON.load(storage_path)
|
||||
if storage_json is None:
|
||||
self.send_error(404)
|
||||
return
|
||||
|
||||
if storage_json.target_platform.lower() == const.PLATFORM_RP2040:
|
||||
filename = f"{storage_json.name}.uf2"
|
||||
path = storage_json.firmware_bin_path.replace(
|
||||
"firmware.bin", "firmware.uf2"
|
||||
)
|
||||
|
||||
elif storage_json.target_platform.lower() == const.PLATFORM_ESP8266:
|
||||
filename = f"{storage_json.name}.bin"
|
||||
path = storage_json.firmware_bin_path
|
||||
|
||||
elif type == "firmware.bin":
|
||||
filename = f"{storage_json.name}.bin"
|
||||
path = storage_json.firmware_bin_path
|
||||
|
||||
elif type == "firmware-factory.bin":
|
||||
storage_path = ext_storage_path(settings.config_dir, configuration)
|
||||
storage_json = StorageJSON.load(storage_path)
|
||||
if storage_json is None:
|
||||
self.send_error(404)
|
||||
return
|
||||
filename = f"{storage_json.name}-factory.bin"
|
||||
path = storage_json.firmware_bin_path.replace(
|
||||
"firmware.bin", "firmware-factory.bin"
|
||||
|
|
|
@ -119,16 +119,16 @@ class StorageJSON:
|
|||
)
|
||||
|
||||
@staticmethod
|
||||
def from_wizard(name: str, address: str, esp_platform: str) -> "StorageJSON":
|
||||
def from_wizard(name: str, address: str, platform: str) -> "StorageJSON":
|
||||
return StorageJSON(
|
||||
storage_version=1,
|
||||
name=name,
|
||||
comment=None,
|
||||
esphome_version=const.__version__,
|
||||
esphome_version=None,
|
||||
src_version=1,
|
||||
address=address,
|
||||
web_port=None,
|
||||
target_platform=esp_platform,
|
||||
target_platform=platform,
|
||||
build_path=None,
|
||||
firmware_bin_path=None,
|
||||
loaded_integrations=[],
|
||||
|
|
|
@ -81,11 +81,20 @@ esp32:
|
|||
type: esp-idf
|
||||
"""
|
||||
|
||||
RP2040_CONFIG = """
|
||||
rp2040:
|
||||
board: {board}
|
||||
framework:
|
||||
# Required until https://github.com/platformio/platform-raspberrypi/pull/36 is merged
|
||||
platform_version: https://github.com/maxgerhardt/platform-raspberrypi.git
|
||||
"""
|
||||
|
||||
HARDWARE_BASE_CONFIGS = {
|
||||
"ESP8266": ESP8266_CONFIG,
|
||||
"ESP32": ESP32_CONFIG,
|
||||
"ESP32S2": ESP32S2_CONFIG,
|
||||
"ESP32C3": ESP32C3_CONFIG,
|
||||
"RP2040": RP2040_CONFIG,
|
||||
}
|
||||
|
||||
|
||||
|
@ -164,6 +173,7 @@ captive_portal:
|
|||
|
||||
def wizard_write(path, **kwargs):
|
||||
from esphome.components.esp8266 import boards as esp8266_boards
|
||||
from esphome.components.rp2040 import boards as rp2040_boards
|
||||
|
||||
name = kwargs["name"]
|
||||
board = kwargs["board"]
|
||||
|
@ -173,9 +183,13 @@ def wizard_write(path, **kwargs):
|
|||
kwargs[key] = sanitize_double_quotes(kwargs[key])
|
||||
|
||||
if "platform" not in kwargs:
|
||||
kwargs["platform"] = (
|
||||
"ESP8266" if board in esp8266_boards.ESP8266_BOARD_PINS else "ESP32"
|
||||
)
|
||||
if board in esp8266_boards.ESP8266_BOARD_PINS:
|
||||
platform = "ESP8266"
|
||||
elif board in rp2040_boards.RP2040_BOARD_PINS:
|
||||
platform = "RP2040"
|
||||
else:
|
||||
platform = "ESP32"
|
||||
kwargs["platform"] = platform
|
||||
hardware = kwargs["platform"]
|
||||
|
||||
write_file(path, wizard_file(**kwargs))
|
||||
|
|
|
@ -146,6 +146,24 @@ build_flags =
|
|||
-DUSE_ESP32_FRAMEWORK_ESP_IDF
|
||||
extra_scripts = post:esphome/components/esp32/post_build.py.script
|
||||
|
||||
; These are common settings for the RP2040 using Arduino.
|
||||
[common:rp2040-arduino]
|
||||
extends = common:arduino
|
||||
board_build.core = earlephilhower
|
||||
board_build.filesystem_size = 0.5m
|
||||
|
||||
platform = https://github.com/maxgerhardt/platform-raspberrypi.git
|
||||
platform_packages =
|
||||
earlephilhower/framework-arduinopico @ ~1.20400.0
|
||||
|
||||
framework = arduino
|
||||
lib_deps =
|
||||
${common:arduino.lib_deps}
|
||||
build_flags =
|
||||
${common:arduino.build_flags}
|
||||
-DUSE_RP2040
|
||||
-DUSE_RP2040_FRAMEWORK_ARDUINO
|
||||
|
||||
; All the actual environments are defined below.
|
||||
[env:esp8266-arduino]
|
||||
extends = common:esp8266-arduino
|
||||
|
@ -222,3 +240,10 @@ board_build.esp-idf.sdkconfig_path = .temp/sdkconfig-esp32s2-idf-tidy
|
|||
build_flags =
|
||||
${common:esp32-idf.build_flags}
|
||||
${flags:clangtidy.build_flags}
|
||||
|
||||
[env:rp2040-pico-arduino]
|
||||
extends = common:rp2040-arduino
|
||||
board = pico
|
||||
build_flags =
|
||||
${common:rp2040-arduino.build_flags}
|
||||
${flags:runtime.build_flags}
|
||||
|
|
|
@ -534,6 +534,7 @@ def lint_relative_py_import(fname):
|
|||
"esphome/components/socket/headers.h",
|
||||
"esphome/components/esp32/core.cpp",
|
||||
"esphome/components/esp8266/core.cpp",
|
||||
"esphome/components/rp2040/core.cpp",
|
||||
],
|
||||
)
|
||||
def lint_namespace(fname, content):
|
||||
|
|
|
@ -24,3 +24,4 @@ Current test_.yaml file contents.
|
|||
| test3.yaml | ESP8266 | wifi | N/A
|
||||
| test4.yaml | ESP32 | ethernet | None
|
||||
| test5.yaml | ESP32 | wifi | ble_server
|
||||
| test6.yaml | RP2040 | wifi | N/A
|
||||
|
|
|
@ -13,8 +13,9 @@ using namespace esphome;
|
|||
|
||||
void setup() {
|
||||
App.pre_setup("livingroom", __DATE__ ", " __TIME__, false);
|
||||
auto *log = new logger::Logger(115200, 512, logger::UART_SELECTION_UART0); // NOLINT
|
||||
auto *log = new logger::Logger(115200, 512); // NOLINT
|
||||
log->pre_setup();
|
||||
log->set_uart_selection(logger::UART_SELECTION_UART0);
|
||||
App.register_component(log);
|
||||
|
||||
auto *wifi = new wifi::WiFiComponent(); // NOLINT
|
||||
|
|
39
tests/test6.yaml
Normal file
39
tests/test6.yaml
Normal file
|
@ -0,0 +1,39 @@
|
|||
---
|
||||
esphome:
|
||||
name: test6
|
||||
project:
|
||||
name: esphome.test6_project
|
||||
version: "1.0.0"
|
||||
|
||||
rp2040:
|
||||
board: rpipicow
|
||||
framework:
|
||||
# Waiting for https://github.com/platformio/platform-raspberrypi/pull/36
|
||||
platform_version: https://github.com/maxgerhardt/platform-raspberrypi.git
|
||||
|
||||
|
||||
wifi:
|
||||
networks:
|
||||
- ssid: "MySSID"
|
||||
password: "password1"
|
||||
|
||||
api:
|
||||
|
||||
ota:
|
||||
|
||||
logger:
|
||||
|
||||
binary_sensor:
|
||||
- platform: gpio
|
||||
pin: GPIO5
|
||||
id: pin_5_button
|
||||
|
||||
output:
|
||||
- platform: gpio
|
||||
pin: GPIO4
|
||||
id: pin_4
|
||||
|
||||
switch:
|
||||
- platform: output
|
||||
output: pin_4
|
||||
id: pin_4_switch
|
Loading…
Reference in a new issue