Expert guidance for developing ESPHome YAML configurations and components for IoT microcontrollers (ESP32, ESP8266, RP2040, LibreTiny)
Expert guidance for developing ESPHome YAML configurations and custom components for IoT microcontrollers.
ESPHome is a system to configure microcontrollers (ESP32, ESP8266, RP2040, LibreTiny-based chips) using YAML configuration files. It generates C++ firmware that can be compiled and flashed to devices for home automation control.
1. **ESP32**: Multiple variants (Original, C2, C3, C5, C6, H2, P4, S2, S3) with ESP-IDF or Arduino
2. **ESP8266**: Arduino framework only (memory-constrained)
3. **RP2040**: Raspberry Pi Pico with Arduino and PIO support
4. **LibreTiny**: Realtek and Beken chip families
- Functions/methods/variables: `lower_snake_case`
- Classes/structs/enums: `UpperCamelCase`
- Top-level constants: `UPPER_SNAKE_CASE`
- Function-local constants: `lower_snake_case`
- Protected/private fields: `lower_snake_case_with_trailing_underscore_`
**Prefer `protected`** for most class fields to enable extensibility:
```cpp
class MyComponent : public Component {
protected:
std::string key_;
int param_{0};
};
```
**Use `private` only for safety-critical cases**:
1. **Pointer lifetime issues** (setters validate pointers from known lists)
2. **Invariant coupling** (fields must stay synchronized)
3. **Resource management** (setters perform cleanup/registration)
Provide `protected` accessor methods when derived classes need controlled access.
- Conditional compilation (`#ifdef`, `#ifndef`)
- Compile-time sizes from Python codegen (e.g., `std::array` dimensions via `cg.add_define()`)
```
components/[component_name]/
├── __init__.py # Configuration schema and code generation
├── [component].h # C++ header
├── [component].cpp # C++ implementation
└── [platform]/ # Platform-specific implementations
├── __init__.py
├── [platform].h
└── [platform].cpp
```
```python
DEPENDENCIES = ["uart"]
AUTO_LOAD = ["sensor"]
CONFLICTS_WITH = ["other_component"]
CODEOWNERS = ["@username"]
MULTI_CONF = True # Allow multiple instances
```
```python
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.const import CONF_ID
CONF_PARAM = "param" # New constant not in esphome/const.py
my_component_ns = cg.esphome_ns.namespace("my_component")
MyComponent = my_component_ns.class_("MyComponent", cg.Component)
CONFIG_SCHEMA = cv.Schema({
cv.GenerateID(): cv.declare_id(MyComponent),
cv.Required(CONF_KEY): cv.string,
cv.Optional(CONF_PARAM, default=42): cv.int_,
}).extend(cv.COMPONENT_SCHEMA)
async def to_code(config):
var = cg.new_Pvariable(config[CONF_ID])
await cg.register_component(var, config)
cg.add(var.set_key(config[CONF_KEY]))
cg.add(var.set_param(config[CONF_PARAM]))
```
```cpp
namespace esphome {
namespace my_component {
class MyComponent : public Component {
public:
void setup() override;
void loop() override;
void dump_config() override;
void set_key(const std::string &key) { this->key_ = key; }
void set_param(int param) { this->param_ = param; }
protected:
std::string key_;
int param_{0};
};
} // namespace my_component
} // namespace esphome
```
```python
from esphome.components import sensor
CONFIG_SCHEMA = sensor.sensor_schema(MySensor).extend(
cv.polling_component_schema("60s")
)
async def to_code(config):
var = await sensor.new_sensor(config)
await cg.register_component(var, config)
```
```python
from esphome.components import binary_sensor
CONFIG_SCHEMA = binary_sensor.binary_sensor_schema().extend({...})
async def to_code(config):
var = await binary_sensor.new_binary_sensor(config)
```
```python
from esphome.components import switch
CONFIG_SCHEMA = switch.switch_schema().extend({...})
async def to_code(config):
var = await switch.new_switch(config)
```
```python
cv.only_on(["esp32", "esp8266"])
esp32.only_on_variant(["esp32c3", "esp32s3"])
cv.only_with_framework(["arduino"])
```
```python
CONFIG_SCHEMA = cv.Schema({...})
.extend(cv.COMPONENT_SCHEMA)
.extend(uart.UART_DEVICE_SCHEMA)
.extend(i2c.i2c_device_schema(0x48))
.extend(spi.spi_device_schema(cs_pin_required=True))
```
Use Docker or create Python venv and install from `requirements_dev.txt`.
Use the environment wrapper:
```bash
python3 script/run-in-env.py <command>
python3 script/run-in-env.py pre-commit run
```
**Python tests**:
```bash
pytest
```
**C++ static analysis**:
```bash
clang-tidy
```
**Component compilation tests**:
```bash
script/test_build_components -c <component> -t <platform>
```
**Test all components together** (catch ID conflicts):
```bash
./script/test_component_grouping.py -e config --all
```
1. **Configuration System** (`esphome/config*.py`): YAML parsing, Voluptuous validation, multi-platform schemas
2. **Code Generation** (`esphome/codegen.py`): Python → C++ generation, templates, build flags
3. **Component System** (`esphome/components/`): Modular hardware/software components, platform-specific implementations
4. **Core Framework** (`esphome/core/`): Application lifecycle, hardware abstraction, component registration
5. **Dashboard** (`esphome/dashboard/`): Web interface for configuration and OTA updates
Leave a review
No reviews yet. Be the first to review this skill!
# Download SKILL.md from killerskills.ai/api/skills/esphome-configuration-development/raw