You can use any GPIO-pin for UART on ESP32.
Alright after beating my head against the wall to get the custom UART pins to work on the Xiao ESP32C6, here is my working config.
Note: I am using the internal antenna, so I disable the external antenna in this config.
esphome:
name: basement-stairs-xiao
friendly_name: Basement Stairs XIAO
on_boot:
priority: 600
then:
# Enable internal antenna by default (FM8625H RF switch)
- output.turn_off: rf_switch_enable # LOW -> RF switch power ON
- output.turn_off: rf_antenna_select # LOW -> internal antenna (PCB/RF1)
esp32:
variant: esp32c6
board: seeed_xiao_esp32c6
framework:
type: esp-idf
sdkconfig_options:
CONFIG_SOC_WIFI_HE_SUPPORT: y
CONFIG_ESP_WIFI_11AX_SUPPORT: y
# Enable logging
logger:
hardware_uart: USB_SERIAL_JTAG
# Enable Home Assistant API
api:
encryption:
key: !secret api_encryption_key
ota:
- platform: esphome
password: !secret ota_password
wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
# Enable fallback hotspot (captive portal) in case wifi connection fails
ap:
ssid: "Basement-Lab-Presence-Rd-03D"
password: !secret wifi_password
captive_portal:
bluetooth_proxy:
active: true
web_server:
port: 80
version: 3
include_internal: true
time:
- platform: sntp
id: sntp_time
timezone: America/Chicago
servers:
- 0.pool.ntp.org
- 1.pool.ntp.org
- 2.pool.ntp.org
uart:
- id: my_uart_bus
rx_pin: GPIO20
tx_pin: GPIO18
baud_rate: 256000
parity: NONE
stop_bits: 1
# External antenna control using ESPHome 2025.12+ board aliases (FM8625H RF switch)
# RF_SWITCH_EN (GPIO3) = RF switch power, RF_ANT_SELECT (GPIO14) = antenna selector
output:
- platform: gpio
id: rf_switch_enable
pin: RF_SWITCH_EN
- platform: gpio
id: rf_antenna_select
pin: RF_ANT_SELECT
switch:
- platform: template
name: "External Antenna"
optimistic: true
restore_mode: ALWAYS_OFF
turn_on_action:
- output.turn_off: rf_switch_enable # LOW -> RF switch power ON
- output.turn_on: rf_antenna_select # HIGH -> external antenna (U.FL/RF2)
turn_off_action:
- output.turn_off: rf_switch_enable # LOW -> RF switch power ON
- output.turn_off: rf_antenna_select # LOW -> internal antenna (PCB/RF1)
- platform: ld2410
engineering_mode:
name: Engineering mode
bluetooth:
name: Control bluetooth
ld2410:
id: ld2410_radar
uart_id: my_uart_bus
button:
- platform: restart
icon: mdi:power-cycle
name: "ESP Reboot"
- platform: factory_reset
disabled_by_default: True
name: Factory Reset
id: factory_reset_all
internal: true
select:
- platform: ld2410
distance_resolution:
name: Distance resolution
baud_rate:
name: Baud rate
light_function:
name: Light function
out_pin_level:
name: Out pin level
globals:
- id: _wifi_disconnects_since_boot
type: int
restore_value: no
initial_value: '0'
# ========================================================
# Sensor
# ========================================================
number:
- platform: ld2410
timeout:
name: Timeout
light_threshold:
name: Light threshold
max_move_distance_gate:
name: Max move distance gate
max_still_distance_gate:
name: Max still distance gate
g0:
move_threshold:
name: G0 move threshold
still_threshold:
name: G0 still threshold
g1:
move_threshold:
name: G1 move threshold
still_threshold:
name: G1 still threshold
g2:
move_threshold:
name: G2 move threshold
still_threshold:
name: G2 still threshold
g3:
move_threshold:
name: G3 move threshold
still_threshold:
name: G3 still threshold
g4:
move_threshold:
name: G4 move threshold
still_threshold:
name: G4 still threshold
g5:
move_threshold:
name: G5 move threshold
still_threshold:
name: G5 still threshold
g6:
move_threshold:
name: G6 move threshold
still_threshold:
name: G6 still threshold
g7:
move_threshold:
name: G7 move threshold
still_threshold:
name: G7 still threshold
g8:
move_threshold:
name: G8 move threshold
still_threshold:
name: G8 still threshold
binary_sensor:
- platform: status
name: Online
id: ink_ha_connected
internal: true
- platform: ld2410
has_target:
name: Presence
has_moving_target:
name: Moving Target
has_still_target:
name: Still Target
out_pin_presence_status:
name: Out pin presence status
sensor:
- platform: internal_temperature
name: "Internal Temp"
entity_category: diagnostic
- platform: uptime
name: "Uptime"
unit_of_measurement: "h"
accuracy_decimals: 2
update_interval: 60s
filters:
- lambda: return x / 3600.0;
entity_category: diagnostic
- platform: wifi_signal
id: wifi_signal_dbm
name: "Wi‑Fi RSSI"
update_interval: 30s
entity_category: diagnostic
- platform: template
name: "Wi‑Fi Disconnects (since boot)"
id: wifi_disconnects_since_boot_sensor
entity_category: diagnostic
accuracy_decimals: 0
update_interval: 30s
lambda: |-
return id(_wifi_disconnects_since_boot);
- platform: template
id: esp_memory
icon: mdi:memory
name: ESP Free Memory
lambda: return heap_caps_get_free_size(MALLOC_CAP_INTERNAL) / 1024;
unit_of_measurement: 'kB'
state_class: measurement
entity_category: "diagnostic"
internal: true
- platform: ld2410
light:
name: Light
moving_distance:
name: Moving Distance
still_distance:
name: Still Distance
moving_energy:
name: Move Energy
still_energy:
name: Still Energy
detection_distance:
name: Detection Distance
g0:
move_energy:
name: G0 move energy
still_energy:
name: G0 still energy
g1:
move_energy:
name: G1 move energy
still_energy:
name: G1 still energy
g2:
move_energy:
name: G2 move energy
still_energy:
name: G2 still energy
g3:
move_energy:
name: G3 move energy
still_energy:
name: G3 still energy
g4:
move_energy:
name: G4 move energy
still_energy:
name: G4 still energy
g5:
move_energy:
name: G5 move energy
still_energy:
name: G5 still energy
g6:
move_energy:
name: G6 move energy
still_energy:
name: G6 still energy
g7:
move_energy:
name: G7 move energy
still_energy:
name: G7 still energy
g8:
move_energy:
name: G8 move energy
still_energy:
name: G8 still energy

