mirror of
https://github.com/esphome/esphome.git
synced 2026-09-28 15:30:21 +00:00
[tfluna] New component for the TF-Luna Low Cost LiDAR Distance Sensor (#9199)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Co-authored-by: J. Nick Koston <nick+github@koston.org> Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com> Co-authored-by: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com> Co-authored-by: J. Nick Koston <nick@koston.org>
This commit is contained in:
co-authored by
Copilot
J. Nick Koston
pre-commit-ci-lite[bot]
Jonathan Swoboda
J. Nick Koston
parent
64388318e8
commit
d5d0901e2c
@@ -562,6 +562,7 @@ esphome/components/template/datetime/* @rfdarter
|
||||
esphome/components/template/event/* @nohat
|
||||
esphome/components/template/fan/* @ssieb
|
||||
esphome/components/text/* @mauritskorse
|
||||
esphome/components/tfluna/* @candrews
|
||||
esphome/components/thermopro_ble/* @sittner
|
||||
esphome/components/thermostat/* @kbx81
|
||||
esphome/components/time/* @esphome/core
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
import esphome.codegen as cg
|
||||
from esphome.components import i2c
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import CONF_ID
|
||||
from esphome.types import ConfigType
|
||||
|
||||
CODEOWNERS = ["@candrews"]
|
||||
DEPENDENCIES = ["i2c"]
|
||||
MULTI_CONF = True
|
||||
|
||||
tfluna_ns = cg.esphome_ns.namespace("tfluna")
|
||||
|
||||
TFLunaComponent = tfluna_ns.class_("TFLuna", cg.PollingComponent, i2c.I2CDevice)
|
||||
|
||||
CONF_TFLUNA_ID = "tfluna_id"
|
||||
CONF_TIMESTAMP = "timestamp"
|
||||
|
||||
FACTORY_DEFAULT_ADDRESS = 0x10
|
||||
|
||||
CONFIG_SCHEMA = (
|
||||
cv.Schema(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(TFLunaComponent),
|
||||
}
|
||||
)
|
||||
.extend(cv.polling_component_schema("1s"))
|
||||
.extend(i2c.i2c_device_schema(FACTORY_DEFAULT_ADDRESS))
|
||||
)
|
||||
|
||||
|
||||
async def to_code(config: ConfigType) -> None:
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
await cg.register_component(var, config)
|
||||
await i2c.register_i2c_device(var, config)
|
||||
@@ -0,0 +1,72 @@
|
||||
import esphome.codegen as cg
|
||||
from esphome.components import button
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import (
|
||||
CONF_ADDRESS,
|
||||
CONF_FACTORY_RESET,
|
||||
CONF_RESTART,
|
||||
DEVICE_CLASS_RESTART,
|
||||
ENTITY_CATEGORY_CONFIG,
|
||||
ENTITY_CATEGORY_DIAGNOSTIC,
|
||||
ICON_RESTART,
|
||||
ICON_RESTART_ALERT,
|
||||
)
|
||||
import esphome.final_validate as fv
|
||||
from esphome.types import ConfigType
|
||||
|
||||
from .. import CONF_TFLUNA_ID, FACTORY_DEFAULT_ADDRESS, TFLunaComponent, tfluna_ns
|
||||
|
||||
DEPENDENCIES = ["tfluna"]
|
||||
|
||||
ResetButton = tfluna_ns.class_("ResetButton", button.Button)
|
||||
RestartButton = tfluna_ns.class_("RestartButton", button.Button)
|
||||
|
||||
CONFIG_SCHEMA = {
|
||||
cv.GenerateID(CONF_TFLUNA_ID): cv.use_id(TFLunaComponent),
|
||||
cv.Optional(CONF_FACTORY_RESET): button.button_schema(
|
||||
ResetButton,
|
||||
device_class=DEVICE_CLASS_RESTART,
|
||||
entity_category=ENTITY_CATEGORY_CONFIG,
|
||||
icon=ICON_RESTART_ALERT,
|
||||
),
|
||||
cv.Optional(CONF_RESTART): button.button_schema(
|
||||
RestartButton,
|
||||
device_class=DEVICE_CLASS_RESTART,
|
||||
entity_category=ENTITY_CATEGORY_DIAGNOSTIC,
|
||||
icon=ICON_RESTART,
|
||||
),
|
||||
}
|
||||
|
||||
|
||||
def _validate_factory_default_address(config: ConfigType) -> ConfigType:
|
||||
if config.get(CONF_ADDRESS) != FACTORY_DEFAULT_ADDRESS:
|
||||
raise cv.Invalid(
|
||||
f"'{CONF_FACTORY_RESET}' requires the TF-Luna to use its factory default "
|
||||
f"I2C address 0x{FACTORY_DEFAULT_ADDRESS:02X}, as a factory reset "
|
||||
"restores that address",
|
||||
path=[CONF_ADDRESS],
|
||||
)
|
||||
return config
|
||||
|
||||
|
||||
def _final_validate(config: ConfigType) -> ConfigType:
|
||||
if CONF_FACTORY_RESET in config:
|
||||
fv.id_declaration_match_schema(_validate_factory_default_address)(
|
||||
config[CONF_TFLUNA_ID]
|
||||
)
|
||||
return config
|
||||
|
||||
|
||||
FINAL_VALIDATE_SCHEMA = _final_validate
|
||||
|
||||
|
||||
async def to_code(config: ConfigType) -> None:
|
||||
tfluna_component = await cg.get_variable(config[CONF_TFLUNA_ID])
|
||||
if factory_reset_config := config.get(CONF_FACTORY_RESET):
|
||||
b = await button.new_button(factory_reset_config)
|
||||
await cg.register_parented(b, config[CONF_TFLUNA_ID])
|
||||
cg.add(tfluna_component.set_reset_button(b))
|
||||
if restart_config := config.get(CONF_RESTART):
|
||||
b = await button.new_button(restart_config)
|
||||
await cg.register_parented(b, config[CONF_TFLUNA_ID])
|
||||
cg.add(tfluna_component.set_restart_button(b))
|
||||
@@ -0,0 +1,7 @@
|
||||
#include "reset_button.h"
|
||||
|
||||
namespace esphome::tfluna {
|
||||
|
||||
void ResetButton::press_action() { this->parent_->factory_reset(); }
|
||||
|
||||
} // namespace esphome::tfluna
|
||||
@@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/components/button/button.h"
|
||||
#include "../tfluna.h"
|
||||
|
||||
namespace esphome::tfluna {
|
||||
|
||||
class ResetButton : public button::Button, public Parented<TFLuna> {
|
||||
protected:
|
||||
void press_action() override;
|
||||
};
|
||||
|
||||
} // namespace esphome::tfluna
|
||||
@@ -0,0 +1,7 @@
|
||||
#include "restart_button.h"
|
||||
|
||||
namespace esphome::tfluna {
|
||||
|
||||
void RestartButton::press_action() { this->parent_->restart(); }
|
||||
|
||||
} // namespace esphome::tfluna
|
||||
@@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/components/button/button.h"
|
||||
#include "../tfluna.h"
|
||||
|
||||
namespace esphome::tfluna {
|
||||
|
||||
class RestartButton : public button::Button, public Parented<TFLuna> {
|
||||
protected:
|
||||
void press_action() override;
|
||||
};
|
||||
|
||||
} // namespace esphome::tfluna
|
||||
@@ -0,0 +1,75 @@
|
||||
import esphome.codegen as cg
|
||||
from esphome.components import sensor
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import (
|
||||
CONF_DISTANCE,
|
||||
CONF_SIGNAL_STRENGTH,
|
||||
CONF_TEMPERATURE,
|
||||
DEVICE_CLASS_DISTANCE,
|
||||
DEVICE_CLASS_DURATION,
|
||||
DEVICE_CLASS_SIGNAL_STRENGTH,
|
||||
DEVICE_CLASS_TEMPERATURE,
|
||||
ENTITY_CATEGORY_DIAGNOSTIC,
|
||||
ICON_ARROW_EXPAND_VERTICAL,
|
||||
ICON_SIGNAL,
|
||||
ICON_THERMOMETER,
|
||||
ICON_TIMER,
|
||||
STATE_CLASS_MEASUREMENT,
|
||||
UNIT_CELSIUS,
|
||||
UNIT_CENTIMETER,
|
||||
UNIT_MILLISECOND,
|
||||
)
|
||||
from esphome.types import ConfigType
|
||||
|
||||
from . import CONF_TFLUNA_ID, CONF_TIMESTAMP, TFLunaComponent
|
||||
|
||||
DEPENDENCIES = ["tfluna"]
|
||||
|
||||
CONFIG_SCHEMA = {
|
||||
cv.GenerateID(CONF_TFLUNA_ID): cv.use_id(TFLunaComponent),
|
||||
cv.Optional(CONF_DISTANCE): sensor.sensor_schema(
|
||||
unit_of_measurement=UNIT_CENTIMETER,
|
||||
icon=ICON_ARROW_EXPAND_VERTICAL,
|
||||
accuracy_decimals=0,
|
||||
device_class=DEVICE_CLASS_DISTANCE,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(
|
||||
unit_of_measurement=UNIT_CELSIUS,
|
||||
icon=ICON_THERMOMETER,
|
||||
accuracy_decimals=2,
|
||||
device_class=DEVICE_CLASS_TEMPERATURE,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
entity_category=ENTITY_CATEGORY_DIAGNOSTIC,
|
||||
),
|
||||
cv.Optional(CONF_SIGNAL_STRENGTH): sensor.sensor_schema(
|
||||
icon=ICON_SIGNAL,
|
||||
accuracy_decimals=0,
|
||||
device_class=DEVICE_CLASS_SIGNAL_STRENGTH,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
entity_category=ENTITY_CATEGORY_DIAGNOSTIC,
|
||||
),
|
||||
cv.Optional(CONF_TIMESTAMP): sensor.sensor_schema(
|
||||
icon=ICON_TIMER,
|
||||
accuracy_decimals=0,
|
||||
unit_of_measurement=UNIT_MILLISECOND,
|
||||
device_class=DEVICE_CLASS_DURATION,
|
||||
entity_category=ENTITY_CATEGORY_DIAGNOSTIC,
|
||||
),
|
||||
}
|
||||
|
||||
|
||||
async def to_code(config: ConfigType) -> None:
|
||||
tfluna_component = await cg.get_variable(config[CONF_TFLUNA_ID])
|
||||
if distance_config := config.get(CONF_DISTANCE):
|
||||
sens = await sensor.new_sensor(distance_config)
|
||||
cg.add(tfluna_component.set_distance_sensor(sens))
|
||||
if temperature_config := config.get(CONF_TEMPERATURE):
|
||||
sens = await sensor.new_sensor(temperature_config)
|
||||
cg.add(tfluna_component.set_temperature_sensor(sens))
|
||||
if signal_strength_config := config.get(CONF_SIGNAL_STRENGTH):
|
||||
sens = await sensor.new_sensor(signal_strength_config)
|
||||
cg.add(tfluna_component.set_signal_strength_sensor(sens))
|
||||
if timestamp_config := config.get(CONF_TIMESTAMP):
|
||||
sens = await sensor.new_sensor(timestamp_config)
|
||||
cg.add(tfluna_component.set_timestamp_sensor(sens))
|
||||
@@ -0,0 +1,23 @@
|
||||
import esphome.codegen as cg
|
||||
from esphome.components import text_sensor
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import CONF_VERSION, ENTITY_CATEGORY_DIAGNOSTIC, ICON_CHIP
|
||||
from esphome.types import ConfigType
|
||||
|
||||
from . import CONF_TFLUNA_ID, TFLunaComponent
|
||||
|
||||
DEPENDENCIES = ["tfluna"]
|
||||
|
||||
CONFIG_SCHEMA = {
|
||||
cv.GenerateID(CONF_TFLUNA_ID): cv.use_id(TFLunaComponent),
|
||||
cv.Optional(CONF_VERSION): text_sensor.text_sensor_schema(
|
||||
entity_category=ENTITY_CATEGORY_DIAGNOSTIC, icon=ICON_CHIP
|
||||
),
|
||||
}
|
||||
|
||||
|
||||
async def to_code(config: ConfigType) -> None:
|
||||
tfluna_component = await cg.get_variable(config[CONF_TFLUNA_ID])
|
||||
if version_config := config.get(CONF_VERSION):
|
||||
sens = await text_sensor.new_text_sensor(version_config)
|
||||
cg.add(tfluna_component.set_version_text_sensor(sens))
|
||||
@@ -0,0 +1,158 @@
|
||||
#include "tfluna.h"
|
||||
#include <cstddef>
|
||||
#include "esphome/core/log.h"
|
||||
#include "esphome/core/helpers.h"
|
||||
|
||||
namespace esphome::tfluna {
|
||||
|
||||
// see https://files.waveshare.com/upload/a/ac/SJ-PM-TF-Luna_A05_Product_Manual.pdf
|
||||
static constexpr uint8_t SAVE_REGISTER = 0x20;
|
||||
static constexpr uint8_t VERSION_REVISION_REGISTER = 0x0A;
|
||||
static constexpr uint8_t DISTANCE_LOW_REGISTER = 0x00;
|
||||
static constexpr uint8_t RESTORE_FACTORY_DEFAULTS_REGISTER = 0x29;
|
||||
static constexpr uint8_t SHUTDOWN_REBOOT_REGISTER = 0x21;
|
||||
static constexpr uint8_t MODE_REGISTER = 0x23;
|
||||
static constexpr uint8_t MODE_TRIGGER = 0x01;
|
||||
static constexpr uint8_t TRIGGER_ONESHOT_REGISTER = 0x24;
|
||||
static constexpr uint8_t MAX_READ_ATTEMPTS = 5;
|
||||
static constexpr uint8_t READ_RETRY_MS = 5;
|
||||
static const char *const TAG = "tfluna";
|
||||
static const char *const READ_RETRY_TIMEOUT = "read_retry";
|
||||
|
||||
void TFLuna::dump_config() {
|
||||
ESP_LOGCONFIG(TAG, "TF-Luna (i2c):");
|
||||
LOG_I2C_DEVICE(this);
|
||||
LOG_UPDATE_INTERVAL(this);
|
||||
ESP_LOGCONFIG(TAG, " Firmware: %s", this->version_);
|
||||
|
||||
#ifdef USE_SENSOR
|
||||
LOG_SENSOR(" ", "Distance:", this->distance_sensor_);
|
||||
LOG_SENSOR(" ", "Temperature:", this->temperature_sensor_);
|
||||
LOG_SENSOR(" ", "Signal Strength:", this->signal_strength_sensor_);
|
||||
LOG_SENSOR(" ", "Timestamp:", this->timestamp_sensor_);
|
||||
#endif
|
||||
#ifdef USE_TEXT_SENSOR
|
||||
LOG_TEXT_SENSOR(" ", "Version:", this->version_text_sensor_);
|
||||
#endif
|
||||
#ifdef USE_BUTTON
|
||||
LOG_BUTTON(" ", "Factory Reset:", this->reset_button_);
|
||||
LOG_BUTTON(" ", "Restart:", this->restart_button_);
|
||||
#endif
|
||||
}
|
||||
|
||||
void TFLuna::setup() {
|
||||
uint8_t mode;
|
||||
if (!this->read_byte(MODE_REGISTER, &mode)) {
|
||||
ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL);
|
||||
this->mark_failed();
|
||||
return;
|
||||
}
|
||||
if (mode != MODE_TRIGGER) {
|
||||
if (!this->write_byte(MODE_REGISTER, MODE_TRIGGER)) {
|
||||
ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL);
|
||||
this->mark_failed();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this->write_byte(SAVE_REGISTER, 1)) {
|
||||
ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL);
|
||||
this->mark_failed();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t raw_version[3];
|
||||
if (!this->read_bytes(VERSION_REVISION_REGISTER, raw_version, sizeof(raw_version))) {
|
||||
this->status_set_warning(ESP_LOG_MSG_COMM_FAIL);
|
||||
return;
|
||||
} else {
|
||||
snprintf(this->version_, sizeof(this->version_), "%d.%d.%d", raw_version[2], raw_version[1], raw_version[0]);
|
||||
#ifdef USE_TEXT_SENSOR
|
||||
if (this->version_text_sensor_ != nullptr) {
|
||||
this->version_text_sensor_->publish_state(version_);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
bool TFLuna::read_data_() {
|
||||
uint8_t buf[8];
|
||||
if (!this->read_bytes(DISTANCE_LOW_REGISTER, buf, sizeof(buf))) {
|
||||
this->status_set_warning(ESP_LOG_MSG_COMM_FAIL);
|
||||
return false;
|
||||
}
|
||||
// Layout:
|
||||
// buf[0..1] = distance (LE), buf[2..3] = signal (LE),
|
||||
// buf[4..5] = temperature (LE), buf[6..7] = timestamp (LE)
|
||||
uint16_t distance = encode_uint16(buf[1], buf[0]);
|
||||
uint16_t signal_strength = encode_uint16(buf[3], buf[2]);
|
||||
int16_t temperature_raw = encode_uint16(buf[5], buf[4]);
|
||||
uint16_t timestamp = encode_uint16(buf[7], buf[6]);
|
||||
|
||||
if (timestamp == this->previous_timestamp_) {
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifdef USE_SENSOR
|
||||
if (this->timestamp_sensor_ != nullptr) {
|
||||
this->timestamp_sensor_->publish_state(timestamp);
|
||||
}
|
||||
if (this->distance_sensor_ != nullptr) {
|
||||
// distance is unreliable when strength is below 100 or saturated at 0xFFFF
|
||||
this->distance_sensor_->publish_state(signal_strength < 100 || signal_strength == 0xFFFF ? NAN : distance);
|
||||
}
|
||||
|
||||
if (this->temperature_sensor_ != nullptr) {
|
||||
this->temperature_sensor_->publish_state(temperature_raw / (float) 100);
|
||||
}
|
||||
|
||||
if (this->signal_strength_sensor_ != nullptr) {
|
||||
this->signal_strength_sensor_->publish_state(signal_strength);
|
||||
}
|
||||
#endif
|
||||
this->status_clear_warning();
|
||||
this->previous_timestamp_ = timestamp;
|
||||
return true;
|
||||
}
|
||||
|
||||
void TFLuna::read_data_timeout_() {
|
||||
if (this->read_data_()) {
|
||||
this->attempt_ = 0;
|
||||
} else {
|
||||
if (this->attempt_ < MAX_READ_ATTEMPTS) {
|
||||
this->attempt_++;
|
||||
this->set_timeout(READ_RETRY_TIMEOUT, READ_RETRY_MS, [this]() { this->read_data_timeout_(); });
|
||||
} else {
|
||||
this->status_set_warning("Hung device, restarting...");
|
||||
this->restart();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TFLuna::update() {
|
||||
this->cancel_timeout(READ_RETRY_TIMEOUT);
|
||||
this->attempt_ = 0;
|
||||
if (!this->write_byte(TRIGGER_ONESHOT_REGISTER, 0x01)) {
|
||||
this->status_set_warning(ESP_LOG_MSG_COMM_FAIL);
|
||||
return;
|
||||
}
|
||||
this->read_data_timeout_();
|
||||
}
|
||||
|
||||
void TFLuna::factory_reset() {
|
||||
if (!this->write_byte(RESTORE_FACTORY_DEFAULTS_REGISTER, 1)) {
|
||||
ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL);
|
||||
return;
|
||||
}
|
||||
this->status_set_warning("Factory reset issued; waiting for device to become ready");
|
||||
this->set_timeout("_setup", 100, [this]() { this->setup(); });
|
||||
}
|
||||
|
||||
void TFLuna::restart() {
|
||||
if (!this->write_byte(SHUTDOWN_REBOOT_REGISTER, 0x02)) {
|
||||
ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace esphome::tfluna
|
||||
@@ -0,0 +1,57 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/core/defines.h"
|
||||
#include "esphome/core/component.h"
|
||||
#ifdef USE_SENSOR
|
||||
#include "esphome/components/sensor/sensor.h"
|
||||
#endif
|
||||
#ifdef USE_TEXT_SENSOR
|
||||
#include "esphome/components/text_sensor/text_sensor.h"
|
||||
#endif
|
||||
#ifdef USE_BUTTON
|
||||
#include "esphome/components/button/button.h"
|
||||
#endif
|
||||
#include "esphome/components/i2c/i2c.h"
|
||||
|
||||
namespace esphome::tfluna {
|
||||
|
||||
class TFLuna : public i2c::I2CDevice, public PollingComponent {
|
||||
#ifdef USE_SENSOR
|
||||
SUB_SENSOR(distance)
|
||||
SUB_SENSOR(temperature)
|
||||
SUB_SENSOR(signal_strength)
|
||||
SUB_SENSOR(timestamp)
|
||||
#endif
|
||||
#ifdef USE_TEXT_SENSOR
|
||||
SUB_TEXT_SENSOR(version)
|
||||
#endif
|
||||
#ifdef USE_BUTTON
|
||||
SUB_BUTTON(reset)
|
||||
SUB_BUTTON(restart)
|
||||
#endif
|
||||
public:
|
||||
void setup() override;
|
||||
|
||||
float get_setup_priority() const override { return setup_priority::LATE; }
|
||||
|
||||
void dump_config() override;
|
||||
|
||||
void update() override;
|
||||
|
||||
void factory_reset();
|
||||
|
||||
void restart();
|
||||
|
||||
protected:
|
||||
char version_[12] = "unknown";
|
||||
|
||||
uint16_t previous_timestamp_ = 0;
|
||||
|
||||
[[nodiscard]] bool read_data_();
|
||||
|
||||
void read_data_timeout_();
|
||||
|
||||
uint8_t attempt_ = 0;
|
||||
};
|
||||
|
||||
} // namespace esphome::tfluna
|
||||
@@ -0,0 +1,24 @@
|
||||
tfluna:
|
||||
|
||||
sensor:
|
||||
- platform: tfluna
|
||||
distance:
|
||||
name: "Distance"
|
||||
signal_strength:
|
||||
name: TF-Luna Signal Strength
|
||||
temperature:
|
||||
name: TF-Luna Temperature
|
||||
timestamp:
|
||||
name: TF-Luna Timestamp
|
||||
|
||||
text_sensor:
|
||||
- platform: tfluna
|
||||
version:
|
||||
name: "TF-Luna firmware version"
|
||||
|
||||
button:
|
||||
- platform: tfluna
|
||||
factory_reset:
|
||||
name: "factory reset"
|
||||
restart:
|
||||
name: "restart"
|
||||
@@ -0,0 +1,4 @@
|
||||
packages:
|
||||
i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml
|
||||
|
||||
<<: !include common.yaml
|
||||
@@ -0,0 +1,4 @@
|
||||
packages:
|
||||
i2c: !include ../../test_build_components/common/i2c/esp8266-ard.yaml
|
||||
|
||||
<<: !include common.yaml
|
||||
@@ -0,0 +1,4 @@
|
||||
packages:
|
||||
i2c: !include ../../test_build_components/common/i2c/rp2040-ard.yaml
|
||||
|
||||
<<: !include common.yaml
|
||||
Reference in New Issue
Block a user