Add rmt_channel to remote_transmitter and remote_receiver (#6497)

* Add rmt_channel to remote_transmitter and remote_receiver

* Add codeowner

* Add tests
This commit is contained in:
Jesse Hills 2024-04-09 13:53:57 +12:00 committed by GitHub
parent 55c49281a2
commit c66b2c52c1
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
25 changed files with 383 additions and 33 deletions

View file

@ -115,6 +115,7 @@ esphome/components/esp32_ble_server/* @Rapsssito @clydebarrow @jesserockz
esphome/components/esp32_camera_web_server/* @ayufan esphome/components/esp32_camera_web_server/* @ayufan
esphome/components/esp32_can/* @Sympatron esphome/components/esp32_can/* @Sympatron
esphome/components/esp32_improv/* @jesserockz esphome/components/esp32_improv/* @jesserockz
esphome/components/esp32_rmt/* @jesserockz
esphome/components/esp32_rmt_led_strip/* @jesserockz esphome/components/esp32_rmt_led_strip/* @jesserockz
esphome/components/esp8266/* @esphome/core esphome/components/esp8266/* @esphome/core
esphome/components/ethernet_info/* @gtjadsonsantos esphome/components/ethernet_info/* @gtjadsonsantos

View file

@ -0,0 +1,55 @@
import esphome.config_validation as cv
import esphome.codegen as cg
from esphome.components import esp32
CODEOWNERS = ["@jesserockz"]
RMT_TX_CHANNELS = {
esp32.const.VARIANT_ESP32: [0, 1, 2, 3, 4, 5, 6, 7],
esp32.const.VARIANT_ESP32S2: [0, 1, 2, 3],
esp32.const.VARIANT_ESP32S3: [0, 1, 2, 3],
esp32.const.VARIANT_ESP32C3: [0, 1],
esp32.const.VARIANT_ESP32C6: [0, 1],
esp32.const.VARIANT_ESP32H2: [0, 1],
}
RMT_RX_CHANNELS = {
esp32.const.VARIANT_ESP32: [0, 1, 2, 3, 4, 5, 6, 7],
esp32.const.VARIANT_ESP32S2: [0, 1, 2, 3],
esp32.const.VARIANT_ESP32S3: [4, 5, 6, 7],
esp32.const.VARIANT_ESP32C3: [2, 3],
esp32.const.VARIANT_ESP32C6: [2, 3],
esp32.const.VARIANT_ESP32H2: [2, 3],
}
rmt_channel_t = cg.global_ns.enum("rmt_channel_t")
RMT_CHANNEL_ENUMS = {
0: rmt_channel_t.RMT_CHANNEL_0,
1: rmt_channel_t.RMT_CHANNEL_1,
2: rmt_channel_t.RMT_CHANNEL_2,
3: rmt_channel_t.RMT_CHANNEL_3,
4: rmt_channel_t.RMT_CHANNEL_4,
5: rmt_channel_t.RMT_CHANNEL_5,
6: rmt_channel_t.RMT_CHANNEL_6,
7: rmt_channel_t.RMT_CHANNEL_7,
}
def validate_rmt_channel(*, tx: bool):
rmt_channels = RMT_TX_CHANNELS if tx else RMT_RX_CHANNELS
def _validator(value):
cv.only_on_esp32(value)
value = cv.int_(value)
variant = esp32.get_esp32_variant()
if variant not in rmt_channels:
raise cv.Invalid(f"ESP32 variant {variant} does not support RMT.")
if value not in rmt_channels[variant]:
raise cv.Invalid(
f"RMT channel {value} does not support {'transmitting' if tx else 'receiving'} for ESP32 variant {variant}."
)
return cv.enum(RMT_CHANNEL_ENUMS)(value)
return _validator

View file

@ -3,7 +3,7 @@ from dataclasses import dataclass
import esphome.codegen as cg import esphome.codegen as cg
import esphome.config_validation as cv import esphome.config_validation as cv
from esphome import pins from esphome import pins
from esphome.components import esp32, light from esphome.components import esp32_rmt, light
from esphome.const import ( from esphome.const import (
CONF_CHIPSET, CONF_CHIPSET,
CONF_MAX_REFRESH_RATE, CONF_MAX_REFRESH_RATE,
@ -11,6 +11,7 @@ from esphome.const import (
CONF_OUTPUT_ID, CONF_OUTPUT_ID,
CONF_PIN, CONF_PIN,
CONF_RGB_ORDER, CONF_RGB_ORDER,
CONF_RMT_CHANNEL,
) )
CODEOWNERS = ["@jesserockz"] CODEOWNERS = ["@jesserockz"]
@ -57,27 +58,6 @@ CONF_BIT0_HIGH = "bit0_high"
CONF_BIT0_LOW = "bit0_low" CONF_BIT0_LOW = "bit0_low"
CONF_BIT1_HIGH = "bit1_high" CONF_BIT1_HIGH = "bit1_high"
CONF_BIT1_LOW = "bit1_low" CONF_BIT1_LOW = "bit1_low"
CONF_RMT_CHANNEL = "rmt_channel"
RMT_CHANNELS = {
esp32.const.VARIANT_ESP32: [0, 1, 2, 3, 4, 5, 6, 7],
esp32.const.VARIANT_ESP32S2: [0, 1, 2, 3],
esp32.const.VARIANT_ESP32S3: [0, 1, 2, 3],
esp32.const.VARIANT_ESP32C3: [0, 1],
esp32.const.VARIANT_ESP32C6: [0, 1],
esp32.const.VARIANT_ESP32H2: [0, 1],
}
def _validate_rmt_channel(value):
variant = esp32.get_esp32_variant()
if variant not in RMT_CHANNELS:
raise cv.Invalid(f"ESP32 variant {variant} does not support RMT.")
if value not in RMT_CHANNELS[variant]:
raise cv.Invalid(
f"RMT channel {value} is not supported for ESP32 variant {variant}."
)
return value
CONFIG_SCHEMA = cv.All( CONFIG_SCHEMA = cv.All(
@ -87,7 +67,7 @@ CONFIG_SCHEMA = cv.All(
cv.Required(CONF_PIN): pins.internal_gpio_output_pin_number, cv.Required(CONF_PIN): pins.internal_gpio_output_pin_number,
cv.Required(CONF_NUM_LEDS): cv.positive_not_null_int, cv.Required(CONF_NUM_LEDS): cv.positive_not_null_int,
cv.Required(CONF_RGB_ORDER): cv.enum(RGB_ORDERS, upper=True), cv.Required(CONF_RGB_ORDER): cv.enum(RGB_ORDERS, upper=True),
cv.Required(CONF_RMT_CHANNEL): _validate_rmt_channel, cv.Required(CONF_RMT_CHANNEL): esp32_rmt.validate_rmt_channel(tx=True),
cv.Optional(CONF_MAX_REFRESH_RATE): cv.positive_time_period_microseconds, cv.Optional(CONF_MAX_REFRESH_RATE): cv.positive_time_period_microseconds,
cv.Optional(CONF_CHIPSET): cv.one_of(*CHIPSETS, upper=True), cv.Optional(CONF_CHIPSET): cv.one_of(*CHIPSETS, upper=True),
cv.Optional(CONF_IS_RGBW, default=False): cv.boolean, cv.Optional(CONF_IS_RGBW, default=False): cv.boolean,

View file

@ -15,6 +15,9 @@ RemoteRMTChannel::RemoteRMTChannel(uint8_t mem_block_num) : mem_block_num_(mem_b
next_rmt_channel = rmt_channel_t(int(next_rmt_channel) + mem_block_num); next_rmt_channel = rmt_channel_t(int(next_rmt_channel) + mem_block_num);
} }
RemoteRMTChannel::RemoteRMTChannel(rmt_channel_t channel, uint8_t mem_block_num)
: channel_(channel), mem_block_num_(mem_block_num) {}
void RemoteRMTChannel::config_rmt(rmt_config_t &rmt) { void RemoteRMTChannel::config_rmt(rmt_config_t &rmt) {
if (rmt_channel_t(int(this->channel_) + this->mem_block_num_) > RMT_CHANNEL_MAX) { if (rmt_channel_t(int(this->channel_) + this->mem_block_num_) > RMT_CHANNEL_MAX) {
this->mem_block_num_ = int(RMT_CHANNEL_MAX) - int(this->channel_); this->mem_block_num_ = int(RMT_CHANNEL_MAX) - int(this->channel_);

View file

@ -3,10 +3,10 @@
#pragma once #pragma once
#include "esphome/components/binary_sensor/binary_sensor.h"
#include "esphome/core/automation.h"
#include "esphome/core/component.h" #include "esphome/core/component.h"
#include "esphome/core/hal.h" #include "esphome/core/hal.h"
#include "esphome/core/automation.h"
#include "esphome/components/binary_sensor/binary_sensor.h"
#ifdef USE_ESP32 #ifdef USE_ESP32
#include <driver/rmt.h> #include <driver/rmt.h>
@ -86,6 +86,7 @@ class RemoteComponentBase {
class RemoteRMTChannel { class RemoteRMTChannel {
public: public:
explicit RemoteRMTChannel(uint8_t mem_block_num = 1); explicit RemoteRMTChannel(uint8_t mem_block_num = 1);
explicit RemoteRMTChannel(rmt_channel_t channel, uint8_t mem_block_num = 1);
void config_rmt(rmt_config_t &rmt); void config_rmt(rmt_config_t &rmt);
void set_clock_divider(uint8_t clock_divider) { this->clock_divider_ = clock_divider; } void set_clock_divider(uint8_t clock_divider) { this->clock_divider_ = clock_divider; }

View file

@ -1,7 +1,7 @@
import esphome.codegen as cg import esphome.codegen as cg
import esphome.config_validation as cv import esphome.config_validation as cv
from esphome import pins from esphome import pins
from esphome.components import remote_base from esphome.components import remote_base, esp32_rmt
from esphome.const import ( from esphome.const import (
CONF_BUFFER_SIZE, CONF_BUFFER_SIZE,
CONF_DUMP, CONF_DUMP,
@ -11,6 +11,7 @@ from esphome.const import (
CONF_PIN, CONF_PIN,
CONF_TOLERANCE, CONF_TOLERANCE,
CONF_MEMORY_BLOCKS, CONF_MEMORY_BLOCKS,
CONF_RMT_CHANNEL,
) )
from esphome.core import CORE, TimePeriod from esphome.core import CORE, TimePeriod
@ -45,6 +46,7 @@ CONFIG_SCHEMA = remote_base.validate_triggers(
CONF_IDLE, default="10ms" CONF_IDLE, default="10ms"
): cv.positive_time_period_microseconds, ): cv.positive_time_period_microseconds,
cv.Optional(CONF_MEMORY_BLOCKS, default=3): cv.Range(min=1, max=8), cv.Optional(CONF_MEMORY_BLOCKS, default=3): cv.Range(min=1, max=8),
cv.Optional(CONF_RMT_CHANNEL): esp32_rmt.validate_rmt_channel(tx=False),
} }
).extend(cv.COMPONENT_SCHEMA) ).extend(cv.COMPONENT_SCHEMA)
) )
@ -53,7 +55,12 @@ CONFIG_SCHEMA = remote_base.validate_triggers(
async def to_code(config): async def to_code(config):
pin = await cg.gpio_pin_expression(config[CONF_PIN]) pin = await cg.gpio_pin_expression(config[CONF_PIN])
if CORE.is_esp32: if CORE.is_esp32:
var = cg.new_Pvariable(config[CONF_ID], pin, config[CONF_MEMORY_BLOCKS]) if (rmt_channel := config.get(CONF_RMT_CHANNEL, None)) is not None:
var = cg.new_Pvariable(
config[CONF_ID], pin, rmt_channel, config[CONF_MEMORY_BLOCKS]
)
else:
var = cg.new_Pvariable(config[CONF_ID], pin, config[CONF_MEMORY_BLOCKS])
else: else:
var = cg.new_Pvariable(config[CONF_ID], pin) var = cg.new_Pvariable(config[CONF_ID], pin)

View file

@ -1,7 +1,7 @@
#pragma once #pragma once
#include "esphome/core/component.h"
#include "esphome/components/remote_base/remote_base.h" #include "esphome/components/remote_base/remote_base.h"
#include "esphome/core/component.h"
#include <cinttypes> #include <cinttypes>
@ -38,6 +38,9 @@ class RemoteReceiverComponent : public remote_base::RemoteReceiverBase,
#ifdef USE_ESP32 #ifdef USE_ESP32
RemoteReceiverComponent(InternalGPIOPin *pin, uint8_t mem_block_num = 1) RemoteReceiverComponent(InternalGPIOPin *pin, uint8_t mem_block_num = 1)
: RemoteReceiverBase(pin), remote_base::RemoteRMTChannel(mem_block_num) {} : RemoteReceiverBase(pin), remote_base::RemoteRMTChannel(mem_block_num) {}
RemoteReceiverComponent(InternalGPIOPin *pin, rmt_channel_t channel, uint8_t mem_block_num = 1)
: RemoteReceiverBase(pin), remote_base::RemoteRMTChannel(channel, mem_block_num) {}
#else #else
RemoteReceiverComponent(InternalGPIOPin *pin) : RemoteReceiverBase(pin) {} RemoteReceiverComponent(InternalGPIOPin *pin) : RemoteReceiverBase(pin) {}
#endif #endif

View file

@ -1,8 +1,8 @@
import esphome.codegen as cg import esphome.codegen as cg
import esphome.config_validation as cv import esphome.config_validation as cv
from esphome import pins from esphome import pins
from esphome.components import remote_base from esphome.components import remote_base, esp32_rmt
from esphome.const import CONF_CARRIER_DUTY_PERCENT, CONF_ID, CONF_PIN from esphome.const import CONF_CARRIER_DUTY_PERCENT, CONF_ID, CONF_PIN, CONF_RMT_CHANNEL
AUTO_LOAD = ["remote_base"] AUTO_LOAD = ["remote_base"]
remote_transmitter_ns = cg.esphome_ns.namespace("remote_transmitter") remote_transmitter_ns = cg.esphome_ns.namespace("remote_transmitter")
@ -18,13 +18,17 @@ CONFIG_SCHEMA = cv.Schema(
cv.Required(CONF_CARRIER_DUTY_PERCENT): cv.All( cv.Required(CONF_CARRIER_DUTY_PERCENT): cv.All(
cv.percentage_int, cv.Range(min=1, max=100) cv.percentage_int, cv.Range(min=1, max=100)
), ),
cv.Optional(CONF_RMT_CHANNEL): esp32_rmt.validate_rmt_channel(tx=True),
} }
).extend(cv.COMPONENT_SCHEMA) ).extend(cv.COMPONENT_SCHEMA)
async def to_code(config): async def to_code(config):
pin = await cg.gpio_pin_expression(config[CONF_PIN]) pin = await cg.gpio_pin_expression(config[CONF_PIN])
var = cg.new_Pvariable(config[CONF_ID], pin) if (rmt_channel := config.get(CONF_RMT_CHANNEL, None)) is not None:
var = cg.new_Pvariable(config[CONF_ID], pin, rmt_channel)
else:
var = cg.new_Pvariable(config[CONF_ID], pin)
await cg.register_component(var, config) await cg.register_component(var, config)
cg.add(var.set_carrier_duty_percent(config[CONF_CARRIER_DUTY_PERCENT])) cg.add(var.set_carrier_duty_percent(config[CONF_CARRIER_DUTY_PERCENT]))

View file

@ -1,7 +1,7 @@
#pragma once #pragma once
#include "esphome/core/component.h"
#include "esphome/components/remote_base/remote_base.h" #include "esphome/components/remote_base/remote_base.h"
#include "esphome/core/component.h"
#include <vector> #include <vector>
@ -16,8 +16,15 @@ class RemoteTransmitterComponent : public remote_base::RemoteTransmitterBase,
#endif #endif
{ {
public: public:
explicit RemoteTransmitterComponent(InternalGPIOPin *pin) : remote_base::RemoteTransmitterBase(pin) {} #ifdef USE_ESP32
RemoteTransmitterComponent(InternalGPIOPin *pin, uint8_t mem_block_num = 1)
: remote_base::RemoteTransmitterBase(pin), remote_base::RemoteRMTChannel(mem_block_num) {}
RemoteTransmitterComponent(InternalGPIOPin *pin, rmt_channel_t channel, uint8_t mem_block_num = 1)
: remote_base::RemoteTransmitterBase(pin), remote_base::RemoteRMTChannel(channel, mem_block_num) {}
#else
explicit RemoteTransmitterComponent(InternalGPIOPin *pin) : remote_base::RemoteTransmitterBase(pin) {}
#endif
void setup() override; void setup() override;
void dump_config() override; void dump_config() override;

View file

@ -676,6 +676,7 @@ CONF_REVERSED = "reversed"
CONF_RGB_ORDER = "rgb_order" CONF_RGB_ORDER = "rgb_order"
CONF_RGBW = "rgbw" CONF_RGBW = "rgbw"
CONF_RISING_EDGE = "rising_edge" CONF_RISING_EDGE = "rising_edge"
CONF_RMT_CHANNEL = "rmt_channel"
CONF_ROTATION = "rotation" CONF_ROTATION = "rotation"
CONF_RS_PIN = "rs_pin" CONF_RS_PIN = "rs_pin"
CONF_RTD_NOMINAL_RESISTANCE = "rtd_nominal_resistance" CONF_RTD_NOMINAL_RESISTANCE = "rtd_nominal_resistance"

View file

@ -0,0 +1,18 @@
remote_receiver:
id: rcvr
pin: ${pin}
rmt_channel: ${rmt_channel}
dump: all
on_coolix:
then:
delay: !lambda "return x.first + x.second;"
on_rc_switch:
then:
delay: !lambda "return uint32_t(x.code) + x.protocol;"
binary_sensor:
- platform: remote_receiver
name: Panasonic Remote Input
panasonic:
address: 0x4004
command: 0x100BCBD

View file

@ -0,0 +1,6 @@
substitutions:
pin: GPIO2
rmt_channel: "2"
packages:
common: !include esp32-common.yaml

View file

@ -0,0 +1,6 @@
substitutions:
pin: GPIO2
rmt_channel: "2"
packages:
common: !include esp32-common.yaml

View file

@ -0,0 +1,6 @@
substitutions:
pin: GPIO2
rmt_channel: "2"
packages:
common: !include esp32-common.yaml

View file

@ -0,0 +1,6 @@
substitutions:
pin: GPIO38
rmt_channel: "5"
packages:
common: !include esp32-common.yaml

View file

@ -0,0 +1,6 @@
substitutions:
pin: GPIO2
rmt_channel: "2"
packages:
common: !include esp32-common.yaml

View file

@ -0,0 +1,17 @@
remote_receiver:
id: rcvr
pin: GPIO5
dump: all
on_coolix:
then:
delay: !lambda "return x.first + x.second;"
on_rc_switch:
then:
delay: !lambda "return uint32_t(x.code) + x.protocol;"
binary_sensor:
- platform: remote_receiver
name: Panasonic Remote Input
panasonic:
address: 0x4004
command: 0x100BCBD

View file

@ -0,0 +1,178 @@
button:
- platform: template
name: JVC Off
id: living_room_lights_on
on_press:
remote_transmitter.transmit_jvc:
data: 0x10EF
- platform: template
name: MagiQuest
on_press:
remote_transmitter.transmit_magiquest:
wand_id: 0x01234567
- platform: template
name: NEC
id: living_room_lights_off
on_press:
remote_transmitter.transmit_nec:
address: 0x4242
command: 0x8484
- platform: template
name: LG
on_press:
remote_transmitter.transmit_lg:
data: 4294967295
nbits: 28
- platform: template
name: Samsung
on_press:
remote_transmitter.transmit_samsung:
data: 0xABCDEF
- platform: template
name: Samsung36
on_press:
remote_transmitter.transmit_samsung36:
address: 0x0400
command: 0x000E00FF
- platform: template
name: ToshibaAC
on_press:
- remote_transmitter.transmit_toshiba_ac:
rc_code_1: 0xB24DBF4050AF
rc_code_2: 0xD5660001003C
- platform: template
name: Sony
on_press:
remote_transmitter.transmit_sony:
data: 0xABCDEF
nbits: 12
- platform: template
name: Panasonic
on_press:
remote_transmitter.transmit_panasonic:
address: 0x4004
command: 0x1000BCD
- platform: template
name: Pioneer
on_press:
- remote_transmitter.transmit_pioneer:
rc_code_1: 0xA556
rc_code_2: 0xA506
repeat:
times: 2
- platform: template
name: RC Switch Raw
on_press:
remote_transmitter.transmit_rc_switch_raw:
code: "00101001100111110101xxxx"
protocol: 1
- platform: template
name: RC Switch Type A
on_press:
remote_transmitter.transmit_rc_switch_type_a:
group: "11001"
device: "01000"
state: true
protocol:
pulse_length: 175
sync: [1, 31]
zero: [1, 3]
one: [3, 1]
inverted: false
- platform: template
name: RC Switch Type B
on_press:
remote_transmitter.transmit_rc_switch_type_b:
address: 4
channel: 2
state: true
- platform: template
name: RC Switch Type C
on_press:
remote_transmitter.transmit_rc_switch_type_c:
family: "a"
group: 1
device: 2
state: true
- platform: template
name: RC Switch Type D
on_press:
remote_transmitter.transmit_rc_switch_type_d:
group: "a"
device: 2
state: true
- platform: template
name: RC5
on_press:
remote_transmitter.transmit_rc5:
address: 0x00
command: 0x0B
- platform: template
name: RC5
on_press:
remote_transmitter.transmit_raw:
code: [1000, -1000]
- platform: template
name: AEHA
id: eaha_hitachi_climate_power_on
on_press:
remote_transmitter.transmit_aeha:
address: 0x8008
data:
[
0x00,
0x02,
0xFD,
0xFF,
0x00,
0x33,
0xCC,
0x49,
0xB6,
0xC8,
0x37,
0x16,
0xE9,
0x00,
0xFF,
0x00,
0xFF,
0x00,
0xFF,
0x00,
0xFF,
0x00,
0xFF,
0xCA,
0x35,
0x8F,
0x70,
0x00,
0xFF,
0x00,
0xFF,
0x00,
0xFF,
0x00,
0xFF,
]
- platform: template
name: Haier
on_press:
remote_transmitter.transmit_haier:
code:
[
0xA6,
0xDA,
0x00,
0x00,
0x40,
0x40,
0x00,
0x80,
0x00,
0x00,
0x00,
0x00,
0x05,
]

View file

@ -0,0 +1,8 @@
remote_transmitter:
id: rcvr
pin: ${pin}
rmt_channel: ${rmt_channel}
carrier_duty_percent: 50%
packages:
buttons: !include common-buttons.yaml

View file

@ -0,0 +1,6 @@
substitutions:
pin: GPIO2
rmt_channel: "1"
packages:
common: !include esp32-common.yaml

View file

@ -0,0 +1,6 @@
substitutions:
pin: GPIO2
rmt_channel: "1"
packages:
common: !include esp32-common.yaml

View file

@ -0,0 +1,6 @@
substitutions:
pin: GPIO2
rmt_channel: "2"
packages:
common: !include esp32-common.yaml

View file

@ -0,0 +1,6 @@
substitutions:
pin: GPIO38
rmt_channel: "3"
packages:
common: !include esp32-common.yaml

View file

@ -0,0 +1,6 @@
substitutions:
pin: GPIO2
rmt_channel: "2"
packages:
common: !include esp32-common.yaml

View file

@ -0,0 +1,7 @@
remote_transmitter:
id: trns
pin: GPIO5
carrier_duty_percent: 50%
packages:
buttons: !include common-buttons.yaml