mirror of
https://github.com/esphome/esphome.git
synced 2026-05-10 05:37:55 +08:00
[emontx] emonTx component (#9027)
Co-authored-by: Claude <noreply@anthropic.com> Co-authored-by: J. Nick Koston <nick@koston.org> Co-authored-by: J. Nick Koston <nick@home-assistant.io>
This commit is contained in:
@@ -148,6 +148,7 @@ esphome/components/ee895/* @Stock-M
|
||||
esphome/components/ektf2232/touchscreen/* @jesserockz
|
||||
esphome/components/emc2101/* @ellull
|
||||
esphome/components/emmeti/* @E440QF
|
||||
esphome/components/emontx/* @FredM67 @glynhudson @TrystanLea
|
||||
esphome/components/ens160/* @latonita
|
||||
esphome/components/ens160_base/* @latonita @vincentscode
|
||||
esphome/components/ens160_i2c/* @latonita
|
||||
|
||||
@@ -0,0 +1,152 @@
|
||||
from dataclasses import dataclass, field
|
||||
|
||||
from esphome import automation
|
||||
import esphome.codegen as cg
|
||||
from esphome.components import uart
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import (
|
||||
CONF_COMMAND,
|
||||
CONF_ID,
|
||||
CONF_ON_DATA,
|
||||
CONF_RX_BUFFER_SIZE,
|
||||
CONF_UART_ID,
|
||||
)
|
||||
from esphome.core import CORE
|
||||
import esphome.final_validate as fv
|
||||
from esphome.types import ConfigType
|
||||
|
||||
AUTO_LOAD = ["json"]
|
||||
CODEOWNERS = ["@FredM67", "@TrystanLea", "@glynhudson"]
|
||||
DEPENDENCIES = ["uart"]
|
||||
|
||||
emontx_ns = cg.esphome_ns.namespace("emontx")
|
||||
EmonTx = emontx_ns.class_("EmonTx", cg.Component, uart.UARTDevice)
|
||||
|
||||
# Action to send command to emonTx
|
||||
EmonTxSendCommandAction = emontx_ns.class_("EmonTxSendCommandAction", automation.Action)
|
||||
|
||||
CONF_EMONTX_ID = "emontx_id"
|
||||
CONF_TAG_NAME = "tag_name"
|
||||
CONF_ON_JSON = "on_json"
|
||||
|
||||
DOMAIN = "emontx"
|
||||
|
||||
MINIMUM_RX_BUFFER_SIZE = 2048
|
||||
|
||||
|
||||
@dataclass
|
||||
class EmonTxData:
|
||||
sensor_counts: dict[str, int] = field(default_factory=dict)
|
||||
|
||||
|
||||
def _get_data() -> EmonTxData:
|
||||
if DOMAIN not in CORE.data:
|
||||
CORE.data[DOMAIN] = EmonTxData()
|
||||
return CORE.data[DOMAIN]
|
||||
|
||||
|
||||
# Main configuration schema
|
||||
CONFIG_SCHEMA = (
|
||||
cv.Schema(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(EmonTx),
|
||||
cv.Optional(CONF_ON_JSON): automation.validate_automation({}),
|
||||
cv.Optional(CONF_ON_DATA): automation.validate_automation({}),
|
||||
}
|
||||
)
|
||||
.extend(cv.COMPONENT_SCHEMA)
|
||||
.extend(uart.UART_DEVICE_SCHEMA)
|
||||
)
|
||||
|
||||
|
||||
def final_validate(config: ConfigType) -> ConfigType:
|
||||
full_config = fv.full_config.get()
|
||||
|
||||
# Count sensors registered to this hub (IDs are resolved at final_validate stage)
|
||||
hub_id = str(config[CONF_ID])
|
||||
sensor_count = sum(
|
||||
1
|
||||
for s in full_config.get("sensor", [])
|
||||
if s.get("platform") == "emontx" and str(s.get(CONF_EMONTX_ID)) == hub_id
|
||||
)
|
||||
_get_data().sensor_counts[hub_id] = sensor_count
|
||||
|
||||
# Ensure UART RX buffer size is large enough to handle data bursts from firmware
|
||||
for uart_conf in full_config["uart"]:
|
||||
if uart_conf[CONF_ID] == config[CONF_UART_ID]:
|
||||
current_buffer_size = uart_conf[CONF_RX_BUFFER_SIZE]
|
||||
if current_buffer_size < MINIMUM_RX_BUFFER_SIZE:
|
||||
raise cv.Invalid(
|
||||
f"Component emontx requires UART '{config[CONF_UART_ID]}' to have "
|
||||
f"rx_buffer_size of at least {MINIMUM_RX_BUFFER_SIZE} bytes "
|
||||
f"(currently set to {current_buffer_size} bytes). "
|
||||
f"Please add 'rx_buffer_size: {MINIMUM_RX_BUFFER_SIZE}' to your uart configuration.",
|
||||
path=[CONF_UART_ID],
|
||||
)
|
||||
break
|
||||
|
||||
# Validate UART settings
|
||||
schema = uart.final_validate_device_schema(
|
||||
"emontx",
|
||||
baud_rate=115200,
|
||||
require_tx=False,
|
||||
require_rx=True,
|
||||
data_bits=8,
|
||||
parity="NONE",
|
||||
stop_bits=1,
|
||||
)
|
||||
return schema(config)
|
||||
|
||||
|
||||
FINAL_VALIDATE_SCHEMA = final_validate
|
||||
|
||||
|
||||
_CALLBACK_AUTOMATIONS = (
|
||||
automation.CallbackAutomation(
|
||||
CONF_ON_JSON,
|
||||
"add_on_json_callback",
|
||||
[(cg.JsonObject, "json"), (cg.std_string, "raw_json")],
|
||||
),
|
||||
automation.CallbackAutomation(
|
||||
CONF_ON_DATA, "add_on_data_callback", [(cg.std_string, "data")]
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
async def to_code(config: ConfigType) -> None:
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
await cg.register_component(var, config)
|
||||
await uart.register_uart_device(var, config)
|
||||
|
||||
# Initialize sensor storage with count from final_validate
|
||||
sensor_count = _get_data().sensor_counts.get(str(config[CONF_ID]), 0)
|
||||
if sensor_count > 0:
|
||||
cg.add(var.init_sensors(sensor_count))
|
||||
|
||||
await automation.build_callback_automations(var, config, _CALLBACK_AUTOMATIONS)
|
||||
|
||||
|
||||
# Action: emontx.send_command
|
||||
|
||||
EMONTX_SEND_COMMAND_ACTION_SCHEMA = cv.Schema(
|
||||
{
|
||||
cv.GenerateID(): cv.use_id(EmonTx),
|
||||
cv.Required(CONF_COMMAND): cv.templatable(cv.string),
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@automation.register_action(
|
||||
"emontx.send_command",
|
||||
EmonTxSendCommandAction,
|
||||
EMONTX_SEND_COMMAND_ACTION_SCHEMA,
|
||||
synchronous=True,
|
||||
)
|
||||
async def emontx_send_command_action_to_code(
|
||||
config: ConfigType, action_id, template_arg, args
|
||||
) -> None:
|
||||
var = cg.new_Pvariable(action_id, template_arg)
|
||||
await cg.register_parented(var, config[CONF_ID])
|
||||
template_ = await cg.templatable(config[CONF_COMMAND], args, cg.std_string)
|
||||
cg.add(var.set_command(template_))
|
||||
return var
|
||||
@@ -0,0 +1,116 @@
|
||||
#include "emontx.h"
|
||||
#include "esphome/core/log.h"
|
||||
#include "esphome/components/json/json_util.h"
|
||||
|
||||
namespace esphome::emontx {
|
||||
|
||||
static const char *const TAG = "emontx";
|
||||
|
||||
void EmonTx::setup() { this->buffer_pos_ = 0; }
|
||||
|
||||
/**
|
||||
* @brief Implements the main loop for parsing data from the serial port.
|
||||
*
|
||||
* @details Continuously processes incoming UART data line-by-line:
|
||||
* 1. Fire on_data callbacks for all received lines
|
||||
* 2. If line starts with '{', parse as JSON and update sensors/callbacks
|
||||
*/
|
||||
void EmonTx::loop() {
|
||||
// Read all available data to prevent UART buffer overflow
|
||||
while (this->available() > 0) {
|
||||
uint8_t received = this->read();
|
||||
|
||||
if (received == '\r') {
|
||||
continue; // Ignore CR
|
||||
} else if (received == '\n') {
|
||||
// End of line - process the buffer
|
||||
if (this->buffer_pos_ > 0) {
|
||||
// Null-terminate for safe logging and c_str() use
|
||||
size_t len = this->buffer_pos_;
|
||||
this->buffer_[len] = '\0';
|
||||
this->buffer_pos_ = 0;
|
||||
|
||||
StringRef line(this->buffer_.data(), len);
|
||||
ESP_LOGD(TAG, "Received line: %s", line.c_str());
|
||||
|
||||
// Fire data callbacks for all received lines
|
||||
this->data_callbacks_.call(line);
|
||||
|
||||
// Check if this line is JSON (starts with '{')
|
||||
if (this->buffer_[0] == '{') {
|
||||
ESP_LOGV(TAG, "Line is JSON, parsing...");
|
||||
this->parse_json_(this->buffer_.data(), len);
|
||||
}
|
||||
}
|
||||
} else if (this->buffer_pos_ >= MAX_LINE_LENGTH) {
|
||||
ESP_LOGW(TAG, "Buffer overflow (>%zu bytes), discarding buffer", MAX_LINE_LENGTH);
|
||||
this->buffer_pos_ = 0;
|
||||
} else {
|
||||
this->buffer_[this->buffer_pos_++] = static_cast<char>(received);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void EmonTx::parse_json_(const char *data, size_t len) {
|
||||
bool success = json::parse_json(reinterpret_cast<const uint8_t *>(data), len, [this, data, len](JsonObject root) {
|
||||
#ifdef USE_SENSOR
|
||||
for (auto &sensor_pair : this->sensors_) {
|
||||
auto val = root[sensor_pair.first];
|
||||
if (val.is<JsonVariant>()) {
|
||||
float value = val;
|
||||
ESP_LOGV(TAG, "Updating sensor '%s' with value: %.2f", sensor_pair.first, value);
|
||||
sensor_pair.second->publish_state(value);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
this->json_callbacks_.call(root, StringRef(data, len));
|
||||
return true;
|
||||
});
|
||||
|
||||
if (!success) {
|
||||
ESP_LOGW(TAG, "Failed to parse JSON");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Logs the EmonTx component configuration details.
|
||||
*/
|
||||
void EmonTx::dump_config() {
|
||||
ESP_LOGCONFIG(TAG, "EmonTx:");
|
||||
|
||||
#ifdef USE_SENSOR
|
||||
ESP_LOGCONFIG(TAG, " Registered sensors: %zu", this->sensors_.size());
|
||||
for (const auto &sensor_pair : this->sensors_) {
|
||||
ESP_LOGCONFIG(TAG, " Sensor: %s", sensor_pair.first);
|
||||
}
|
||||
#else
|
||||
ESP_LOGCONFIG(TAG, " Sensor support: DISABLED");
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sends a command string to the emonTx device via UART.
|
||||
*
|
||||
* @param command The command string to send (LF will be appended automatically).
|
||||
*/
|
||||
void EmonTx::send_command(const std::string &command) {
|
||||
ESP_LOGD(TAG, "Sending command to emonTx: %s", command.c_str());
|
||||
this->write_str(command.c_str());
|
||||
this->write_byte('\n');
|
||||
}
|
||||
|
||||
#ifdef USE_SENSOR
|
||||
/**
|
||||
* @brief Registers a sensor to receive updates for a specific JSON tag.
|
||||
*
|
||||
* @param tag_name The JSON key to monitor for this sensor (must be a string literal).
|
||||
* @param sensor Pointer to the sensor that will receive value updates.
|
||||
*/
|
||||
void EmonTx::register_sensor(const char *tag_name, sensor::Sensor *sensor) {
|
||||
ESP_LOGCONFIG(TAG, "Registering sensor for tag: %s", tag_name);
|
||||
this->sensors_.emplace_back(tag_name, sensor);
|
||||
}
|
||||
#endif
|
||||
|
||||
} // namespace esphome::emontx
|
||||
@@ -0,0 +1,69 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/core/defines.h"
|
||||
#include "esphome/core/automation.h"
|
||||
#include "esphome/core/helpers.h"
|
||||
#include "esphome/core/string_ref.h"
|
||||
#include "esphome/components/uart/uart.h"
|
||||
#include "esphome/components/json/json_util.h"
|
||||
|
||||
#include <array>
|
||||
|
||||
#ifdef USE_SENSOR
|
||||
#include "esphome/components/sensor/sensor.h"
|
||||
#endif
|
||||
|
||||
namespace esphome::emontx {
|
||||
|
||||
/// Maximum line length in bytes (plus one byte reserved for null terminator)
|
||||
static constexpr size_t MAX_LINE_LENGTH = 1024;
|
||||
|
||||
/**
|
||||
* @class EmonTx
|
||||
* @brief Main class for the EmonTx component.
|
||||
*
|
||||
* The EmonTx processes incoming data frames via UART,
|
||||
* extracts tags and values, and publishes them to registered sensors.
|
||||
*/
|
||||
class EmonTx : public Component, public uart::UARTDevice {
|
||||
public:
|
||||
EmonTx() = default;
|
||||
|
||||
void loop() override;
|
||||
void setup() override;
|
||||
void dump_config() override;
|
||||
|
||||
template<typename F> void add_on_json_callback(F &&callback) { this->json_callbacks_.add(std::forward<F>(callback)); }
|
||||
|
||||
template<typename F> void add_on_data_callback(F &&callback) { this->data_callbacks_.add(std::forward<F>(callback)); }
|
||||
|
||||
// Send command to emonTx via UART
|
||||
void send_command(const std::string &command);
|
||||
|
||||
#ifdef USE_SENSOR
|
||||
void init_sensors(size_t count) { this->sensors_.init(count); }
|
||||
void register_sensor(const char *tag_name, sensor::Sensor *sensor);
|
||||
#endif
|
||||
|
||||
protected:
|
||||
void parse_json_(const char *data, size_t len);
|
||||
|
||||
#ifdef USE_SENSOR
|
||||
FixedVector<std::pair<const char *, sensor::Sensor *>> sensors_{};
|
||||
#endif
|
||||
LazyCallbackManager<void(JsonObject, StringRef)> json_callbacks_;
|
||||
LazyCallbackManager<void(StringRef)> data_callbacks_;
|
||||
uint16_t buffer_pos_{0};
|
||||
std::array<char, MAX_LINE_LENGTH + 1> buffer_{};
|
||||
};
|
||||
|
||||
// Action to send command to emonTx
|
||||
template<typename... Ts> class EmonTxSendCommandAction : public Action<Ts...>, public Parented<EmonTx> {
|
||||
public:
|
||||
TEMPLATABLE_VALUE(std::string, command)
|
||||
|
||||
void play(const Ts &...x) override { this->parent_->send_command(this->command_.value(x...)); }
|
||||
};
|
||||
|
||||
} // namespace esphome::emontx
|
||||
@@ -0,0 +1,133 @@
|
||||
import esphome.codegen as cg
|
||||
from esphome.components import sensor
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import (
|
||||
CONF_ACCURACY_DECIMALS,
|
||||
CONF_DEVICE_CLASS,
|
||||
CONF_ID,
|
||||
CONF_STATE_CLASS,
|
||||
CONF_UNIT_OF_MEASUREMENT,
|
||||
DEVICE_CLASS_CURRENT,
|
||||
DEVICE_CLASS_ENERGY,
|
||||
DEVICE_CLASS_POWER,
|
||||
DEVICE_CLASS_POWER_FACTOR,
|
||||
DEVICE_CLASS_TEMPERATURE,
|
||||
DEVICE_CLASS_VOLTAGE,
|
||||
STATE_CLASS_MEASUREMENT,
|
||||
STATE_CLASS_TOTAL_INCREASING,
|
||||
UNIT_AMPERE,
|
||||
UNIT_CELSIUS,
|
||||
UNIT_EMPTY,
|
||||
UNIT_PULSES,
|
||||
UNIT_VOLT,
|
||||
UNIT_WATT,
|
||||
UNIT_WATT_HOURS,
|
||||
)
|
||||
from esphome.types import ConfigType
|
||||
|
||||
from .. import CONF_EMONTX_ID, CONF_TAG_NAME, EmonTx, emontx_ns
|
||||
|
||||
EmonTxSensor = emontx_ns.class_("EmonTxSensor", sensor.Sensor, cg.Component)
|
||||
|
||||
# Define sensor type configurations by prefix
|
||||
SENSOR_CONFIGS = {
|
||||
"P": {
|
||||
CONF_UNIT_OF_MEASUREMENT: UNIT_WATT,
|
||||
CONF_DEVICE_CLASS: DEVICE_CLASS_POWER,
|
||||
CONF_STATE_CLASS: STATE_CLASS_MEASUREMENT,
|
||||
CONF_ACCURACY_DECIMALS: 0,
|
||||
},
|
||||
"E": {
|
||||
CONF_UNIT_OF_MEASUREMENT: UNIT_WATT_HOURS,
|
||||
CONF_DEVICE_CLASS: DEVICE_CLASS_ENERGY,
|
||||
CONF_STATE_CLASS: STATE_CLASS_TOTAL_INCREASING,
|
||||
CONF_ACCURACY_DECIMALS: 0,
|
||||
},
|
||||
"V": {
|
||||
CONF_UNIT_OF_MEASUREMENT: UNIT_VOLT,
|
||||
CONF_DEVICE_CLASS: DEVICE_CLASS_VOLTAGE,
|
||||
CONF_STATE_CLASS: STATE_CLASS_MEASUREMENT,
|
||||
CONF_ACCURACY_DECIMALS: 2,
|
||||
},
|
||||
"I": {
|
||||
CONF_UNIT_OF_MEASUREMENT: UNIT_AMPERE,
|
||||
CONF_DEVICE_CLASS: DEVICE_CLASS_CURRENT,
|
||||
CONF_STATE_CLASS: STATE_CLASS_MEASUREMENT,
|
||||
CONF_ACCURACY_DECIMALS: 2,
|
||||
},
|
||||
"T": {
|
||||
CONF_UNIT_OF_MEASUREMENT: UNIT_CELSIUS,
|
||||
CONF_DEVICE_CLASS: DEVICE_CLASS_TEMPERATURE,
|
||||
CONF_STATE_CLASS: STATE_CLASS_MEASUREMENT,
|
||||
CONF_ACCURACY_DECIMALS: 2,
|
||||
},
|
||||
}
|
||||
|
||||
# Pattern-based configurations
|
||||
PATTERN_CONFIGS = {
|
||||
"PULSE": {
|
||||
CONF_UNIT_OF_MEASUREMENT: UNIT_PULSES,
|
||||
CONF_DEVICE_CLASS: DEVICE_CLASS_ENERGY,
|
||||
CONF_ACCURACY_DECIMALS: 0,
|
||||
},
|
||||
"PF": {
|
||||
CONF_UNIT_OF_MEASUREMENT: UNIT_EMPTY,
|
||||
CONF_DEVICE_CLASS: DEVICE_CLASS_POWER_FACTOR,
|
||||
CONF_STATE_CLASS: STATE_CLASS_MEASUREMENT,
|
||||
CONF_ACCURACY_DECIMALS: 2,
|
||||
},
|
||||
}
|
||||
|
||||
# Create a base schema that's flexible for any tag
|
||||
BASE_SCHEMA = sensor.sensor_schema(
|
||||
EmonTxSensor,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
accuracy_decimals=0,
|
||||
).extend(
|
||||
{
|
||||
cv.GenerateID(CONF_EMONTX_ID): cv.use_id(EmonTx),
|
||||
cv.Required(CONF_TAG_NAME): cv.string,
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
def apply_tag_defaults(config: ConfigType) -> ConfigType:
|
||||
"""Apply defaults based on tag prefix if applicable, but don't restrict any tags."""
|
||||
tag = config[CONF_TAG_NAME]
|
||||
|
||||
# Skip if tag is too short
|
||||
if len(tag) < 2:
|
||||
return config
|
||||
|
||||
# Check if this tag starts with a known prefix
|
||||
tag_upper = tag.upper()
|
||||
|
||||
for pattern, pattern_config in PATTERN_CONFIGS.items():
|
||||
if tag_upper.startswith(pattern):
|
||||
# Apply pattern defaults if not overridden by user
|
||||
for key, value in pattern_config.items():
|
||||
if key not in config:
|
||||
config[key] = value
|
||||
return config
|
||||
|
||||
# Only apply defaults for known prefixes with numeric indices
|
||||
prefix = tag_upper[0]
|
||||
if prefix in SENSOR_CONFIGS and len(tag) > 1 and tag[1:].isdigit():
|
||||
# Apply defaults for known tag types, but only if not overridden by user
|
||||
defaults = SENSOR_CONFIGS[prefix]
|
||||
for key, value in defaults.items():
|
||||
if key not in config:
|
||||
config[key] = value
|
||||
|
||||
return config
|
||||
|
||||
|
||||
CONFIG_SCHEMA = cv.All(BASE_SCHEMA, apply_tag_defaults)
|
||||
|
||||
|
||||
async def to_code(config: ConfigType) -> None:
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
await cg.register_component(var, config)
|
||||
await sensor.register_sensor(var, config)
|
||||
hub = await cg.get_variable(config[CONF_EMONTX_ID])
|
||||
cg.add(hub.register_sensor(config[CONF_TAG_NAME], var))
|
||||
@@ -0,0 +1,10 @@
|
||||
#include "emontx_sensor.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome::emontx {
|
||||
|
||||
static const char *const TAG = "emontx_sensor";
|
||||
|
||||
void EmonTxSensor::dump_config() { LOG_SENSOR(" ", "EmonTx Sensor", this); }
|
||||
|
||||
} // namespace esphome::emontx
|
||||
@@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/components/sensor/sensor.h"
|
||||
#include "esphome/core/component.h"
|
||||
|
||||
namespace esphome::emontx {
|
||||
|
||||
class EmonTxSensor : public sensor::Sensor, public Component {
|
||||
public:
|
||||
void dump_config() override;
|
||||
};
|
||||
|
||||
} // namespace esphome::emontx
|
||||
@@ -0,0 +1,25 @@
|
||||
button:
|
||||
- platform: template
|
||||
name: Send command test
|
||||
on_press:
|
||||
- emontx.send_command:
|
||||
id: test_emontx
|
||||
command: "v"
|
||||
|
||||
emontx:
|
||||
id: test_emontx
|
||||
on_json:
|
||||
- then:
|
||||
- logger.log: "Got JSON"
|
||||
on_data:
|
||||
- then:
|
||||
- logger.log:
|
||||
format: "Got data: %s"
|
||||
args: [data.c_str()]
|
||||
|
||||
sensor:
|
||||
- platform: emontx
|
||||
name: Power
|
||||
tag_name: P1
|
||||
emontx_id: test_emontx
|
||||
unit_of_measurement: W
|
||||
@@ -0,0 +1,4 @@
|
||||
packages:
|
||||
uart: !include ../../test_build_components/common/uart_115200/esp32-idf.yaml
|
||||
|
||||
<<: !include common.yaml
|
||||
@@ -0,0 +1,4 @@
|
||||
packages:
|
||||
uart: !include ../../test_build_components/common/uart_115200/esp8266-ard.yaml
|
||||
|
||||
<<: !include common.yaml
|
||||
@@ -0,0 +1,4 @@
|
||||
packages:
|
||||
uart: !include ../../test_build_components/common/uart_115200/rp2040-ard.yaml
|
||||
|
||||
<<: !include common.yaml
|
||||
@@ -9,3 +9,4 @@ uart:
|
||||
tx_pin: ${tx_pin}
|
||||
rx_pin: ${rx_pin}
|
||||
baud_rate: 115200
|
||||
rx_buffer_size: 2048
|
||||
|
||||
@@ -9,3 +9,4 @@ uart:
|
||||
tx_pin: ${tx_pin}
|
||||
rx_pin: ${rx_pin}
|
||||
baud_rate: 115200
|
||||
rx_buffer_size: 2048
|
||||
|
||||
@@ -10,3 +10,4 @@ uart:
|
||||
tx_pin: ${tx_pin}
|
||||
rx_pin: ${rx_pin}
|
||||
baud_rate: 115200
|
||||
rx_buffer_size: 2048
|
||||
|
||||
@@ -10,3 +10,4 @@ uart:
|
||||
tx_pin: ${tx_pin}
|
||||
rx_pin: ${rx_pin}
|
||||
baud_rate: 115200
|
||||
rx_buffer_size: 2048
|
||||
|
||||
@@ -9,3 +9,4 @@ uart:
|
||||
tx_pin: ${tx_pin}
|
||||
rx_pin: ${rx_pin}
|
||||
baud_rate: 115200
|
||||
rx_buffer_size: 2048
|
||||
|
||||
@@ -9,3 +9,4 @@ uart:
|
||||
tx_pin: ${tx_pin}
|
||||
rx_pin: ${rx_pin}
|
||||
baud_rate: 115200
|
||||
rx_buffer_size: 2048
|
||||
|
||||
Reference in New Issue
Block a user