all new yaml for 1u, use ext lib.
This commit is contained in:
parent
836b01cb5a
commit
175f467622
|
|
@ -1,45 +1,67 @@
|
||||||
esphome:
|
esphome:
|
||||||
name: screek-humen-dectet-1u
|
name: screek-humen-sensor-1u
|
||||||
comment: Screek Human Presence Sensor 24GHz PS-HPS 1U
|
comment: Screek Human Presence Sensor 1U
|
||||||
friendly_name: Screek Human Presence Sensor 1U
|
friendly_name: Screek Human Presence Sensor 1U
|
||||||
name_add_mac_suffix: True
|
name_add_mac_suffix: True
|
||||||
platformio_options:
|
platformio_options:
|
||||||
board_build.flash_mode: dio
|
board_build.flash_mode: dio
|
||||||
min_version: 2023.3.2
|
board_build.f_cpu: 80000000L
|
||||||
|
on_boot:
|
||||||
|
priority: 900
|
||||||
|
then:
|
||||||
|
lambda: |-
|
||||||
|
// id(wake_up_reason) = esp_sleep_get_wakeup_cause();
|
||||||
|
id(cpu_speed) = ESP.getCpuFreqMHz() ;
|
||||||
project:
|
project:
|
||||||
name: Screek.Human_Presence_Sensor
|
name: Screek.Human_Presence_Sensor
|
||||||
version: 1U
|
version: 1U
|
||||||
|
min_version: 2023.6.1
|
||||||
|
|
||||||
|
globals:
|
||||||
|
- id: cpu_speed
|
||||||
|
type: int
|
||||||
|
restore_value: no
|
||||||
|
initial_value: '0'
|
||||||
|
|
||||||
external_components:
|
external_components:
|
||||||
- source: screek-mod-components
|
# - source: github://screek-workshop/custom_components_support_for_screek_1u
|
||||||
components: [ ld2410, uart ]
|
- source:
|
||||||
|
type: git
|
||||||
|
url: https://github.com/screek-workshop/custom_components_support_for_screek_1u
|
||||||
|
ref: master
|
||||||
|
# components: [ ld2410, uart ]
|
||||||
|
|
||||||
esp32:
|
esp32:
|
||||||
board: lolin_s2_mini
|
board: lolin_s2_mini
|
||||||
framework:
|
framework:
|
||||||
type: arduino
|
type: arduino
|
||||||
version: 2.0.7
|
version: 2.0.9
|
||||||
platform_version: 6.0.1
|
platform_version: 6.3.2
|
||||||
|
|
||||||
improv_serial:
|
improv_serial:
|
||||||
|
|
||||||
logger:
|
logger:
|
||||||
hardware_uart: uart0
|
hardware_uart: uart0
|
||||||
|
|
||||||
api:
|
api:
|
||||||
|
# encryption:
|
||||||
|
# key: "YOUR-API-PASSWORD"
|
||||||
|
|
||||||
ota:
|
ota:
|
||||||
password: "YOUR_OTA_PASSWORD"
|
# This won't work for the original firmware(we use diffrent ota password),
|
||||||
|
# so if you want to turn it into your own OTA,
|
||||||
|
# make sure to completely flash your custom yaml in the firmware once.
|
||||||
|
# after that, you may always ota with your own password.
|
||||||
|
password: "all-things-in-their-being-are-good-for-something" # words by Socrates
|
||||||
|
|
||||||
wifi:
|
wifi:
|
||||||
# ssid: !secret wifi_ssid
|
# power_save_mode: NONE
|
||||||
# password: !secret wifi_password
|
output_power: 15dB
|
||||||
# fast_connect: True
|
power_save_mode: LIGHT
|
||||||
|
reboot_timeout: 10min
|
||||||
power_save_mode: NONE
|
|
||||||
ap:
|
ap:
|
||||||
ssid: "SCREEK HUMAN-SENSOR"
|
ssid: "SCREEK HUMAN-SENSOR"
|
||||||
|
# password: "YOUR-AP-PASSWORD"
|
||||||
|
|
||||||
captive_portal:
|
captive_portal:
|
||||||
|
|
||||||
|
|
@ -59,23 +81,42 @@ binary_sensor:
|
||||||
name: Still Target
|
name: Still Target
|
||||||
|
|
||||||
sensor:
|
sensor:
|
||||||
|
- platform: template
|
||||||
|
name: "ESP Cpu Speed"
|
||||||
|
accuracy_decimals: 0
|
||||||
|
unit_of_measurement: Mhz
|
||||||
|
lambda: |-
|
||||||
|
return (id(cpu_speed));
|
||||||
|
entity_category: "diagnostic"
|
||||||
|
|
||||||
|
# - platform: internal_temperature
|
||||||
|
# id: sys_esp_temperature
|
||||||
|
# name: ESP Temperature
|
||||||
|
# unit_of_measurement: °C
|
||||||
|
# device_class: TEMPERATURE
|
||||||
|
# update_interval: 1s
|
||||||
|
# entity_category: "diagnostic"
|
||||||
|
|
||||||
- platform: template
|
- platform: template
|
||||||
id: sys_esp_temperature
|
id: sys_esp_temperature
|
||||||
name: ESP Temperature
|
name: ESP Temperature
|
||||||
lambda: return temperatureRead();
|
lambda: return temperatureRead();
|
||||||
unit_of_measurement: °C
|
unit_of_measurement: °C
|
||||||
device_class: TEMPERATURE
|
device_class: TEMPERATURE
|
||||||
update_interval: 30s
|
update_interval: 5s
|
||||||
entity_category: "diagnostic"
|
entity_category: "diagnostic"
|
||||||
|
|
||||||
- platform: uptime
|
- platform: uptime
|
||||||
name: Uptime
|
name: Uptime
|
||||||
id: sys_uptime
|
id: sys_uptime
|
||||||
update_interval: 10s
|
update_interval: 10s
|
||||||
|
|
||||||
- platform: wifi_signal
|
- platform: wifi_signal
|
||||||
name: RSSI
|
name: RSSI
|
||||||
id: wifi_signal_db
|
id: wifi_signal_db
|
||||||
update_interval: 1s
|
update_interval: 5s
|
||||||
entity_category: "diagnostic"
|
entity_category: "diagnostic"
|
||||||
|
|
||||||
- platform: template
|
- platform: template
|
||||||
id: esp_memory
|
id: esp_memory
|
||||||
icon: mdi:memory
|
icon: mdi:memory
|
||||||
|
|
@ -84,6 +125,7 @@ sensor:
|
||||||
unit_of_measurement: 'kB'
|
unit_of_measurement: 'kB'
|
||||||
state_class: measurement
|
state_class: measurement
|
||||||
entity_category: "diagnostic"
|
entity_category: "diagnostic"
|
||||||
|
|
||||||
- platform: ld2410
|
- platform: ld2410
|
||||||
moving_distance:
|
moving_distance:
|
||||||
name : Moving Distance
|
name : Moving Distance
|
||||||
|
|
@ -109,12 +151,10 @@ light:
|
||||||
|
|
||||||
time:
|
time:
|
||||||
- platform: sntp
|
- platform: sntp
|
||||||
id: time_now
|
id: time_sntp
|
||||||
servers:
|
|
||||||
- ntp.aliyun.com
|
|
||||||
|
|
||||||
uart:
|
uart:
|
||||||
id: uart1
|
id: uart_ld2410
|
||||||
tx_pin: GPIO18
|
tx_pin: GPIO18
|
||||||
rx_pin: GPIO33
|
rx_pin: GPIO33
|
||||||
baud_rate: 256000
|
baud_rate: 256000
|
||||||
|
|
@ -124,22 +164,46 @@ uart:
|
||||||
ld2410:
|
ld2410:
|
||||||
timeout: 150s
|
timeout: 150s
|
||||||
id: ld2410_radar
|
id: ld2410_radar
|
||||||
|
# max_move_distance : 6m
|
||||||
|
# max_still_distance: 0.75m
|
||||||
|
# g0_move_threshold: 10
|
||||||
|
# g0_still_threshold: 20
|
||||||
|
# g1_move_threshold: 10
|
||||||
|
# g1_still_threshold: 20
|
||||||
|
# g2_move_threshold: 20
|
||||||
|
# g2_still_threshold: 21
|
||||||
|
# g3_move_threshold: 30
|
||||||
|
# g3_still_threshold: 31
|
||||||
|
# g4_move_threshold: 40
|
||||||
|
# g4_still_threshold: 41
|
||||||
|
# g5_move_threshold: 50
|
||||||
|
# g5_still_threshold: 51
|
||||||
|
# g6_move_threshold: 60
|
||||||
|
# g6_still_threshold: 61
|
||||||
|
# g7_move_threshold: 70
|
||||||
|
# g7_still_threshold: 71
|
||||||
|
# g8_move_threshold: 80
|
||||||
|
# g8_still_threshold: 81
|
||||||
|
|
||||||
button:
|
button:
|
||||||
- platform: template
|
- platform: template
|
||||||
name: "Enable LD2410 BLE"
|
name: "Enable LD2410 BLE"
|
||||||
|
# disabled_by_default: True
|
||||||
entity_category: "config"
|
entity_category: "config"
|
||||||
icon: mdi:bluetooth
|
icon: mdi:bluetooth
|
||||||
on_press:
|
on_press:
|
||||||
lambda: |-
|
lambda: |-
|
||||||
id(ld2410_radar) -> ble_control(true);
|
id(ld2410_radar) -> ble_control(true);
|
||||||
|
|
||||||
- platform: template
|
- platform: template
|
||||||
name: "Disable LD2410 BLE"
|
name: "Disable LD2410 BLE"
|
||||||
|
# disabled_by_default: True
|
||||||
entity_category: "config"
|
entity_category: "config"
|
||||||
icon: mdi:bluetooth-off
|
icon: mdi:bluetooth-off
|
||||||
on_press:
|
on_press:
|
||||||
lambda: |-
|
lambda: |-
|
||||||
id(ld2410_radar) -> ble_control(false);
|
id(ld2410_radar) -> ble_control(false);
|
||||||
|
|
||||||
- platform: template
|
- platform: template
|
||||||
name: "LD2410 Reboot"
|
name: "LD2410 Reboot"
|
||||||
icon: mdi:radar
|
icon: mdi:radar
|
||||||
|
|
@ -149,9 +213,11 @@ button:
|
||||||
// auto* radar = LD2410Component::get(ld2410);
|
// auto* radar = LD2410Component::get(ld2410);
|
||||||
// radar -> roboot();
|
// radar -> roboot();
|
||||||
id(ld2410_radar) -> reboot();
|
id(ld2410_radar) -> reboot();
|
||||||
|
|
||||||
- platform: restart
|
- platform: restart
|
||||||
icon: mdi:power-cycle
|
icon: mdi:power-cycle
|
||||||
name: "ESP Reboot"
|
name: "ESP Reboot"
|
||||||
|
|
||||||
- platform: factory_reset
|
- platform: factory_reset
|
||||||
disabled_by_default: True
|
disabled_by_default: True
|
||||||
name: Factory Reset
|
name: Factory Reset
|
||||||
|
|
@ -1,158 +0,0 @@
|
||||||
esphome:
|
|
||||||
name: screek-humen-dectet-1u
|
|
||||||
comment: Screek Human Presence Sensor 24GHz PS-HPS 1U
|
|
||||||
friendly_name: Screek Human Presence Sensor 1U
|
|
||||||
name_add_mac_suffix: True
|
|
||||||
platformio_options:
|
|
||||||
board_build.flash_mode: dio
|
|
||||||
board_build.extra_flags:
|
|
||||||
- "-DARDUINO_USB_CDC_ON_BOOT=0"
|
|
||||||
project:
|
|
||||||
name: Screek.Human_Presence_Sensor
|
|
||||||
version: 1U
|
|
||||||
|
|
||||||
external_components:
|
|
||||||
- source: screek-mod-components
|
|
||||||
|
|
||||||
esp32:
|
|
||||||
board: lolin_s2_mini
|
|
||||||
framework:
|
|
||||||
type: arduino
|
|
||||||
version: 2.0.7
|
|
||||||
platform_version: 6.0.1
|
|
||||||
|
|
||||||
improv_serial:
|
|
||||||
|
|
||||||
logger:
|
|
||||||
# hardware_uart: uart0
|
|
||||||
|
|
||||||
api:
|
|
||||||
|
|
||||||
|
|
||||||
ota:
|
|
||||||
password: "YOUR_OTA_PASSWORD"
|
|
||||||
|
|
||||||
wifi:
|
|
||||||
# ssid: !secret wifi_ssid
|
|
||||||
# password: !secret wifi_password
|
|
||||||
# fast_connect: True
|
|
||||||
|
|
||||||
power_save_mode: NONE
|
|
||||||
ap:
|
|
||||||
ssid: "SCREEK HUMAN-SENSOR"
|
|
||||||
|
|
||||||
captive_portal:
|
|
||||||
|
|
||||||
web_server:
|
|
||||||
port: 80
|
|
||||||
|
|
||||||
binary_sensor:
|
|
||||||
- platform: status
|
|
||||||
name: Online
|
|
||||||
id: ink_ha_connected
|
|
||||||
- platform: ld2410
|
|
||||||
has_target:
|
|
||||||
name: Presence
|
|
||||||
has_moving_target:
|
|
||||||
name: Moving Target
|
|
||||||
has_still_target:
|
|
||||||
name: Still Target
|
|
||||||
|
|
||||||
sensor:
|
|
||||||
- platform: template
|
|
||||||
id: sys_esp_temperature
|
|
||||||
name: ESP Temperature
|
|
||||||
lambda: return temperatureRead();
|
|
||||||
unit_of_measurement: °C
|
|
||||||
device_class: TEMPERATURE
|
|
||||||
update_interval: 30s
|
|
||||||
entity_category: "diagnostic"
|
|
||||||
- platform: uptime
|
|
||||||
name: Uptime
|
|
||||||
id: sys_uptime
|
|
||||||
update_interval: 10s
|
|
||||||
- platform: wifi_signal
|
|
||||||
name: RSSI
|
|
||||||
id: wifi_signal_db
|
|
||||||
update_interval: 1s
|
|
||||||
entity_category: "diagnostic"
|
|
||||||
- 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"
|
|
||||||
- platform: ld2410
|
|
||||||
moving_distance:
|
|
||||||
name : Moving Distance
|
|
||||||
id: moving_distance
|
|
||||||
still_distance:
|
|
||||||
name: Still Distance
|
|
||||||
id: still_distance
|
|
||||||
moving_energy:
|
|
||||||
name: Move Energy
|
|
||||||
still_energy:
|
|
||||||
name: Still Energy
|
|
||||||
detection_distance:
|
|
||||||
name: Detection Distance
|
|
||||||
light:
|
|
||||||
name: Sun Light
|
|
||||||
|
|
||||||
light:
|
|
||||||
- platform: status_led
|
|
||||||
name: sys_status
|
|
||||||
pin: GPIO15
|
|
||||||
internal: True
|
|
||||||
restore_mode: ALWAYS_OFF
|
|
||||||
|
|
||||||
time:
|
|
||||||
- platform: sntp
|
|
||||||
id: time_now
|
|
||||||
servers:
|
|
||||||
- ntp.aliyun.com
|
|
||||||
|
|
||||||
uart:
|
|
||||||
id: uart1
|
|
||||||
tx_pin: GPIO18
|
|
||||||
rx_pin: GPIO33
|
|
||||||
baud_rate: 256000
|
|
||||||
parity: NONE
|
|
||||||
stop_bits: 1
|
|
||||||
|
|
||||||
ld2410:
|
|
||||||
timeout: 150s
|
|
||||||
id: ld2410_radar
|
|
||||||
|
|
||||||
button:
|
|
||||||
- platform: template
|
|
||||||
name: "Enable LD2410 BLE"
|
|
||||||
entity_category: "config"
|
|
||||||
icon: mdi:bluetooth
|
|
||||||
on_press:
|
|
||||||
lambda: |-
|
|
||||||
id(ld2410_radar) -> ble_control(true);
|
|
||||||
- platform: template
|
|
||||||
name: "Disable LD2410 BLE"
|
|
||||||
entity_category: "config"
|
|
||||||
icon: mdi:bluetooth-off
|
|
||||||
on_press:
|
|
||||||
lambda: |-
|
|
||||||
id(ld2410_radar) -> ble_control(false);
|
|
||||||
- platform: template
|
|
||||||
name: "LD2410 Reboot"
|
|
||||||
icon: mdi:radar
|
|
||||||
entity_category: "config"
|
|
||||||
on_press:
|
|
||||||
lambda: |-
|
|
||||||
// auto* radar = LD2410Component::get(ld2410);
|
|
||||||
// radar -> roboot();
|
|
||||||
id(ld2410_radar) -> reboot();
|
|
||||||
- platform: restart
|
|
||||||
icon: mdi:power-cycle
|
|
||||||
name: "ESP Reboot"
|
|
||||||
- platform: factory_reset
|
|
||||||
disabled_by_default: True
|
|
||||||
name: Factory Reset
|
|
||||||
id: factory_reset_all
|
|
||||||
|
|
@ -1,158 +0,0 @@
|
||||||
import esphome.codegen as cg
|
|
||||||
import esphome.config_validation as cv
|
|
||||||
from esphome.components import uart
|
|
||||||
from esphome.const import CONF_ID, CONF_TIMEOUT
|
|
||||||
from esphome import automation
|
|
||||||
from esphome.automation import maybe_simple_id
|
|
||||||
|
|
||||||
DEPENDENCIES = ["uart"]
|
|
||||||
CODEOWNERS = ["@sebcaps"]
|
|
||||||
MULTI_CONF = True
|
|
||||||
|
|
||||||
ld2410_ns = cg.esphome_ns.namespace("ld2410")
|
|
||||||
LD2410Component = ld2410_ns.class_("LD2410Component", cg.Component, uart.UARTDevice)
|
|
||||||
LD2410Restart = ld2410_ns.class_("LD2410Restart", automation.Action)
|
|
||||||
CONF_LD2410_ID = "ld2410_id"
|
|
||||||
CONF_MAX_MOVE_DISTANCE = "max_move_distance"
|
|
||||||
CONF_MAX_STILL_DISTANCE = "max_still_distance"
|
|
||||||
CONF_G0_MOVE_THRESHOLD = "g0_move_threshold"
|
|
||||||
CONF_G0_STILL_THRESHOLD = "g0_still_threshold"
|
|
||||||
CONF_G1_MOVE_THRESHOLD = "g1_move_threshold"
|
|
||||||
CONF_G1_STILL_THRESHOLD = "g1_still_threshold"
|
|
||||||
CONF_G2_MOVE_THRESHOLD = "g2_move_threshold"
|
|
||||||
CONF_G2_STILL_THRESHOLD = "g2_still_threshold"
|
|
||||||
CONF_G3_MOVE_THRESHOLD = "g3_move_threshold"
|
|
||||||
CONF_G3_STILL_THRESHOLD = "g3_still_threshold"
|
|
||||||
CONF_G4_MOVE_THRESHOLD = "g4_move_threshold"
|
|
||||||
CONF_G4_STILL_THRESHOLD = "g4_still_threshold"
|
|
||||||
CONF_G5_MOVE_THRESHOLD = "g5_move_threshold"
|
|
||||||
CONF_G5_STILL_THRESHOLD = "g5_still_threshold"
|
|
||||||
CONF_G6_MOVE_THRESHOLD = "g6_move_threshold"
|
|
||||||
CONF_G6_STILL_THRESHOLD = "g6_still_threshold"
|
|
||||||
CONF_G7_MOVE_THRESHOLD = "g7_move_threshold"
|
|
||||||
CONF_G7_STILL_THRESHOLD = "g7_still_threshold"
|
|
||||||
CONF_G8_MOVE_THRESHOLD = "g8_move_threshold"
|
|
||||||
CONF_G8_STILL_THRESHOLD = "g8_still_threshold"
|
|
||||||
|
|
||||||
DISTANCES = [0.75, 1.5, 2.25, 3, 3.75, 4.5, 5.25, 6]
|
|
||||||
|
|
||||||
CONFIG_SCHEMA = cv.All(
|
|
||||||
cv.Schema(
|
|
||||||
{
|
|
||||||
cv.GenerateID(): cv.declare_id(LD2410Component),
|
|
||||||
cv.Optional(CONF_MAX_MOVE_DISTANCE, default="4.5m"): cv.All(
|
|
||||||
cv.distance, cv.one_of(*DISTANCES, float=True)
|
|
||||||
),
|
|
||||||
cv.Optional(CONF_MAX_STILL_DISTANCE, default="4.5m"): cv.All(
|
|
||||||
cv.distance, cv.one_of(*DISTANCES, float=True)
|
|
||||||
),
|
|
||||||
cv.Optional(CONF_TIMEOUT, default="5s"): cv.All(
|
|
||||||
cv.positive_time_period_seconds,
|
|
||||||
cv.Range(max=cv.TimePeriod(seconds=32767)),
|
|
||||||
),
|
|
||||||
cv.Optional(CONF_G0_MOVE_THRESHOLD, default=50): cv.int_range(
|
|
||||||
min=0, max=100
|
|
||||||
),
|
|
||||||
cv.Optional(CONF_G0_STILL_THRESHOLD, default=0): cv.int_range(
|
|
||||||
min=0, max=100
|
|
||||||
),
|
|
||||||
cv.Optional(CONF_G1_MOVE_THRESHOLD, default=50): cv.int_range(
|
|
||||||
min=0, max=100
|
|
||||||
),
|
|
||||||
cv.Optional(CONF_G1_STILL_THRESHOLD, default=0): cv.int_range(
|
|
||||||
min=0, max=100
|
|
||||||
),
|
|
||||||
cv.Optional(CONF_G2_MOVE_THRESHOLD, default=40): cv.int_range(
|
|
||||||
min=0, max=100
|
|
||||||
),
|
|
||||||
cv.Optional(CONF_G2_STILL_THRESHOLD, default=40): cv.int_range(
|
|
||||||
min=0, max=100
|
|
||||||
),
|
|
||||||
cv.Optional(CONF_G3_MOVE_THRESHOLD, default=40): cv.int_range(
|
|
||||||
min=0, max=100
|
|
||||||
),
|
|
||||||
cv.Optional(CONF_G3_STILL_THRESHOLD, default=40): cv.int_range(
|
|
||||||
min=0, max=100
|
|
||||||
),
|
|
||||||
cv.Optional(CONF_G4_MOVE_THRESHOLD, default=40): cv.int_range(
|
|
||||||
min=0, max=100
|
|
||||||
),
|
|
||||||
cv.Optional(CONF_G4_STILL_THRESHOLD, default=40): cv.int_range(
|
|
||||||
min=0, max=100
|
|
||||||
),
|
|
||||||
cv.Optional(CONF_G5_MOVE_THRESHOLD, default=40): cv.int_range(
|
|
||||||
min=0, max=100
|
|
||||||
),
|
|
||||||
cv.Optional(CONF_G5_STILL_THRESHOLD, default=40): cv.int_range(
|
|
||||||
min=0, max=100
|
|
||||||
),
|
|
||||||
cv.Optional(CONF_G6_MOVE_THRESHOLD, default=30): cv.int_range(
|
|
||||||
min=0, max=100
|
|
||||||
),
|
|
||||||
cv.Optional(CONF_G6_STILL_THRESHOLD, default=15): cv.int_range(
|
|
||||||
min=0, max=100
|
|
||||||
),
|
|
||||||
cv.Optional(CONF_G7_MOVE_THRESHOLD, default=30): cv.int_range(
|
|
||||||
min=0, max=100
|
|
||||||
),
|
|
||||||
cv.Optional(CONF_G7_STILL_THRESHOLD, default=15): cv.int_range(
|
|
||||||
min=0, max=100
|
|
||||||
),
|
|
||||||
cv.Optional(CONF_G8_MOVE_THRESHOLD, default=30): cv.int_range(
|
|
||||||
min=0, max=100
|
|
||||||
),
|
|
||||||
cv.Optional(CONF_G8_STILL_THRESHOLD, default=15): cv.int_range(
|
|
||||||
min=0, max=100
|
|
||||||
),
|
|
||||||
}
|
|
||||||
)
|
|
||||||
.extend(uart.UART_DEVICE_SCHEMA)
|
|
||||||
.extend(cv.COMPONENT_SCHEMA)
|
|
||||||
)
|
|
||||||
|
|
||||||
FINAL_VALIDATE_SCHEMA = uart.final_validate_device_schema(
|
|
||||||
"ld2410",
|
|
||||||
baud_rate=256000,
|
|
||||||
require_tx=True,
|
|
||||||
require_rx=True,
|
|
||||||
parity="NONE",
|
|
||||||
stop_bits=1,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
async def to_code(config):
|
|
||||||
var = cg.new_Pvariable(config[CONF_ID])
|
|
||||||
await cg.register_component(var, config)
|
|
||||||
await uart.register_uart_device(var, config)
|
|
||||||
cg.add(var.set_timeout(config[CONF_TIMEOUT]))
|
|
||||||
cg.add(var.set_max_move_distance(int(config[CONF_MAX_MOVE_DISTANCE] / 0.75)))
|
|
||||||
cg.add(var.set_max_still_distance(int(config[CONF_MAX_STILL_DISTANCE] / 0.75)))
|
|
||||||
cg.add(
|
|
||||||
var.set_range_config(
|
|
||||||
config[CONF_G0_MOVE_THRESHOLD],
|
|
||||||
config[CONF_G0_STILL_THRESHOLD],
|
|
||||||
config[CONF_G1_MOVE_THRESHOLD],
|
|
||||||
config[CONF_G1_STILL_THRESHOLD],
|
|
||||||
config[CONF_G2_MOVE_THRESHOLD],
|
|
||||||
config[CONF_G2_STILL_THRESHOLD],
|
|
||||||
config[CONF_G3_MOVE_THRESHOLD],
|
|
||||||
config[CONF_G3_STILL_THRESHOLD],
|
|
||||||
config[CONF_G4_MOVE_THRESHOLD],
|
|
||||||
config[CONF_G4_STILL_THRESHOLD],
|
|
||||||
config[CONF_G5_MOVE_THRESHOLD],
|
|
||||||
config[CONF_G5_STILL_THRESHOLD],
|
|
||||||
config[CONF_G6_MOVE_THRESHOLD],
|
|
||||||
config[CONF_G6_STILL_THRESHOLD],
|
|
||||||
config[CONF_G7_MOVE_THRESHOLD],
|
|
||||||
config[CONF_G7_STILL_THRESHOLD],
|
|
||||||
config[CONF_G8_MOVE_THRESHOLD],
|
|
||||||
config[CONF_G8_STILL_THRESHOLD],
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
CALIBRATION_ACTION_SCHEMA = maybe_simple_id(
|
|
||||||
{
|
|
||||||
cv.Required(CONF_ID): cv.use_id(LD2410Component),
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
@ -1,36 +0,0 @@
|
||||||
import esphome.codegen as cg
|
|
||||||
from esphome.components import binary_sensor
|
|
||||||
import esphome.config_validation as cv
|
|
||||||
from esphome.const import DEVICE_CLASS_MOTION, DEVICE_CLASS_OCCUPANCY
|
|
||||||
from . import CONF_LD2410_ID, LD2410Component
|
|
||||||
|
|
||||||
DEPENDENCIES = ["ld2410"]
|
|
||||||
CONF_HAS_TARGET = "has_target"
|
|
||||||
CONF_HAS_MOVING_TARGET = "has_moving_target"
|
|
||||||
CONF_HAS_STILL_TARGET = "has_still_target"
|
|
||||||
|
|
||||||
CONFIG_SCHEMA = {
|
|
||||||
cv.GenerateID(CONF_LD2410_ID): cv.use_id(LD2410Component),
|
|
||||||
cv.Optional(CONF_HAS_TARGET): binary_sensor.binary_sensor_schema(
|
|
||||||
device_class=DEVICE_CLASS_OCCUPANCY
|
|
||||||
),
|
|
||||||
cv.Optional(CONF_HAS_MOVING_TARGET): binary_sensor.binary_sensor_schema(
|
|
||||||
device_class=DEVICE_CLASS_MOTION
|
|
||||||
),
|
|
||||||
cv.Optional(CONF_HAS_STILL_TARGET): binary_sensor.binary_sensor_schema(
|
|
||||||
device_class=DEVICE_CLASS_OCCUPANCY
|
|
||||||
),
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
async def to_code(config):
|
|
||||||
ld2410_component = await cg.get_variable(config[CONF_LD2410_ID])
|
|
||||||
if CONF_HAS_TARGET in config:
|
|
||||||
sens = await binary_sensor.new_binary_sensor(config[CONF_HAS_TARGET])
|
|
||||||
cg.add(ld2410_component.set_target_sensor(sens))
|
|
||||||
if CONF_HAS_MOVING_TARGET in config:
|
|
||||||
sens = await binary_sensor.new_binary_sensor(config[CONF_HAS_MOVING_TARGET])
|
|
||||||
cg.add(ld2410_component.set_moving_target_sensor(sens))
|
|
||||||
if CONF_HAS_STILL_TARGET in config:
|
|
||||||
sens = await binary_sensor.new_binary_sensor(config[CONF_HAS_STILL_TARGET])
|
|
||||||
cg.add(ld2410_component.set_still_target_sensor(sens))
|
|
||||||
|
|
@ -1,416 +0,0 @@
|
||||||
#include "ld2410.h"
|
|
||||||
|
|
||||||
#define highbyte(val) (uint8_t)((val) >> 8)
|
|
||||||
#define lowbyte(val) (uint8_t)((val) &0xff)
|
|
||||||
|
|
||||||
namespace esphome {
|
|
||||||
namespace ld2410 {
|
|
||||||
|
|
||||||
static const char *const TAG = "ld2410";
|
|
||||||
|
|
||||||
void LD2410Component::dump_config() {
|
|
||||||
ESP_LOGCONFIG(TAG, "LD2410:");
|
|
||||||
#ifdef USE_BINARY_SENSOR
|
|
||||||
LOG_BINARY_SENSOR(" ", "HasTargetSensor", this->target_binary_sensor_);
|
|
||||||
LOG_BINARY_SENSOR(" ", "MovingSensor", this->moving_binary_sensor_);
|
|
||||||
LOG_BINARY_SENSOR(" ", "StillSensor", this->still_binary_sensor_);
|
|
||||||
#endif
|
|
||||||
#ifdef USE_SENSOR
|
|
||||||
LOG_SENSOR(" ", "Moving Distance", this->moving_target_distance_sensor_);
|
|
||||||
LOG_SENSOR(" ", "Still Distance", this->still_target_distance_sensor_);
|
|
||||||
LOG_SENSOR(" ", "Moving Energy", this->moving_target_energy_sensor_);
|
|
||||||
LOG_SENSOR(" ", "Still Energy", this->still_target_energy_sensor_);
|
|
||||||
LOG_SENSOR(" ", "Detection Distance", this->detection_distance_sensor_);
|
|
||||||
|
|
||||||
LOG_SENSOR(" ", "Light", this->light_sensor_);
|
|
||||||
#endif
|
|
||||||
this->set_config_mode_(true);
|
|
||||||
this->get_version_();
|
|
||||||
this->set_config_mode_(false);
|
|
||||||
ESP_LOGCONFIG(TAG, " Firmware Version : %u.%u.%u%u%u%u", this->version_[0], this->version_[1], this->version_[2],
|
|
||||||
this->version_[3], this->version_[4], this->version_[5]);
|
|
||||||
}
|
|
||||||
|
|
||||||
void LD2410Component::setup() {
|
|
||||||
ESP_LOGCONFIG(TAG, "Setting up LD2410...");
|
|
||||||
// ESP_LOGCONFIG(TAG, "Apply screek patch...");
|
|
||||||
this->set_config_mode_(true);
|
|
||||||
|
|
||||||
/*
|
|
||||||
this->set_max_distances_timeout_(this->max_move_distance_, this->max_still_distance_, this->timeout_);
|
|
||||||
// Configure Gates sensitivity
|
|
||||||
this->set_gate_threshold_(0, this->rg0_move_threshold_, this->rg0_still_threshold_);
|
|
||||||
this->set_gate_threshold_(1, this->rg1_move_threshold_, this->rg1_still_threshold_);
|
|
||||||
this->set_gate_threshold_(2, this->rg2_move_threshold_, this->rg2_still_threshold_);
|
|
||||||
this->set_gate_threshold_(3, this->rg3_move_threshold_, this->rg3_still_threshold_);
|
|
||||||
this->set_gate_threshold_(4, this->rg4_move_threshold_, this->rg4_still_threshold_);
|
|
||||||
this->set_gate_threshold_(5, this->rg5_move_threshold_, this->rg5_still_threshold_);
|
|
||||||
this->set_gate_threshold_(6, this->rg6_move_threshold_, this->rg6_still_threshold_);
|
|
||||||
this->set_gate_threshold_(7, this->rg7_move_threshold_, this->rg7_still_threshold_);
|
|
||||||
this->set_gate_threshold_(8, this->rg8_move_threshold_, this->rg8_still_threshold_);
|
|
||||||
*/
|
|
||||||
this->get_version_();
|
|
||||||
this->set_config_mode_(false);
|
|
||||||
|
|
||||||
// this->factory_mode(true);
|
|
||||||
|
|
||||||
ESP_LOGCONFIG(TAG, "Firmware Version : %u.%u.%u%u%u%u", this->version_[0], this->version_[1], this->version_[2],
|
|
||||||
this->version_[3], this->version_[4], this->version_[5]);
|
|
||||||
ESP_LOGCONFIG(TAG, "LD2410 setup complete.");
|
|
||||||
}
|
|
||||||
|
|
||||||
void LD2410Component::loop() {
|
|
||||||
const int max_line_length = 80;
|
|
||||||
static uint8_t buffer[max_line_length];
|
|
||||||
|
|
||||||
while (available()) {
|
|
||||||
this->readline_(read(), buffer, max_line_length);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void LD2410Component::send_command_(uint8_t command, uint8_t *command_value, int command_value_len) {
|
|
||||||
// lastCommandSuccess->publish_state(false);
|
|
||||||
|
|
||||||
// frame start bytes
|
|
||||||
this->write_array(CMD_FRAME_HEADER, 4);
|
|
||||||
// length bytes
|
|
||||||
int len = 2;
|
|
||||||
if (command_value != nullptr)
|
|
||||||
len += command_value_len;
|
|
||||||
this->write_byte(lowbyte(len));
|
|
||||||
this->write_byte(highbyte(len));
|
|
||||||
|
|
||||||
// command
|
|
||||||
this->write_byte(lowbyte(command));
|
|
||||||
this->write_byte(highbyte(command));
|
|
||||||
|
|
||||||
// command value bytes
|
|
||||||
if (command_value != nullptr) {
|
|
||||||
for (int i = 0; i < command_value_len; i++) {
|
|
||||||
this->write_byte(command_value[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// frame end bytes
|
|
||||||
this->write_array(CMD_FRAME_END, 4);
|
|
||||||
// FIXME to remove
|
|
||||||
delay(50); // NOLINT
|
|
||||||
}
|
|
||||||
|
|
||||||
long map(long x, long in_min, long in_max, long out_min, long out_max) {
|
|
||||||
return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
|
|
||||||
}
|
|
||||||
|
|
||||||
void LD2410Component::handle_periodic_data_(uint8_t *buffer, int len) {
|
|
||||||
if (len < 12)
|
|
||||||
return; // 4 frame start bytes + 2 length bytes + 1 data end byte + 1 crc byte + 4 frame end bytes
|
|
||||||
if (buffer[0] != 0xF4 || buffer[1] != 0xF3 || buffer[2] != 0xF2 || buffer[3] != 0xF1) // check 4 frame start bytes
|
|
||||||
return;
|
|
||||||
if (buffer[7] != HEAD || buffer[len - 6] != END || buffer[len - 5] != CHECK) // Check constant values
|
|
||||||
return; // data head=0xAA, data end=0x55, crc=0x00
|
|
||||||
|
|
||||||
/*
|
|
||||||
Data Type: 6th
|
|
||||||
0x01: Engineering mode
|
|
||||||
0x02: Normal mode
|
|
||||||
*/
|
|
||||||
// char data_type = buffer[DATA_TYPES];
|
|
||||||
/*
|
|
||||||
Target states: 9th
|
|
||||||
0x00 = No target
|
|
||||||
0x01 = Moving targets
|
|
||||||
0x02 = Still targets
|
|
||||||
0x03 = Moving+Still targets
|
|
||||||
*/
|
|
||||||
#ifdef USE_BINARY_SENSOR
|
|
||||||
char target_state = buffer[TARGET_STATES];
|
|
||||||
if (this->target_binary_sensor_ != nullptr) {
|
|
||||||
this->target_binary_sensor_->publish_state(target_state != 0x00);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/*
|
|
||||||
Reduce data update rate to prevent home assistant database size grow fast
|
|
||||||
*/
|
|
||||||
int32_t current_millis = millis();
|
|
||||||
if (current_millis - last_periodic_millis < 1000)
|
|
||||||
return;
|
|
||||||
last_periodic_millis = current_millis;
|
|
||||||
|
|
||||||
#ifdef USE_BINARY_SENSOR
|
|
||||||
if (this->moving_binary_sensor_ != nullptr) {
|
|
||||||
this->moving_binary_sensor_->publish_state(CHECK_BIT(target_state, 0));
|
|
||||||
}
|
|
||||||
if (this->still_binary_sensor_ != nullptr) {
|
|
||||||
this->still_binary_sensor_->publish_state(CHECK_BIT(target_state, 1));
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
/*
|
|
||||||
Moving target distance: 10~11th bytes
|
|
||||||
Moving target energy: 12th byte
|
|
||||||
Still target distance: 13~14th bytes
|
|
||||||
Still target energy: 15th byte
|
|
||||||
Detect distance: 16~17th bytes
|
|
||||||
*/
|
|
||||||
#ifdef USE_SENSOR
|
|
||||||
if (this->moving_target_distance_sensor_ != nullptr) {
|
|
||||||
int new_moving_target_distance = this->two_byte_to_int_(buffer[MOVING_TARGET_LOW], buffer[MOVING_TARGET_HIGH]);
|
|
||||||
if (this->moving_target_distance_sensor_->get_state() != new_moving_target_distance)
|
|
||||||
this->moving_target_distance_sensor_->publish_state(new_moving_target_distance);
|
|
||||||
}
|
|
||||||
if (this->moving_target_energy_sensor_ != nullptr) {
|
|
||||||
int new_moving_target_energy = buffer[MOVING_ENERGY];
|
|
||||||
if (this->moving_target_energy_sensor_->get_state() != new_moving_target_energy)
|
|
||||||
this->moving_target_energy_sensor_->publish_state(new_moving_target_energy);
|
|
||||||
}
|
|
||||||
if (this->still_target_distance_sensor_ != nullptr) {
|
|
||||||
int new_still_target_distance = this->two_byte_to_int_(buffer[STILL_TARGET_LOW], buffer[STILL_TARGET_HIGH]);
|
|
||||||
if (this->still_target_distance_sensor_->get_state() != new_still_target_distance)
|
|
||||||
this->still_target_distance_sensor_->publish_state(new_still_target_distance);
|
|
||||||
}
|
|
||||||
if (this->still_target_energy_sensor_ != nullptr) {
|
|
||||||
int new_still_target_energy = buffer[STILL_ENERGY];
|
|
||||||
if (this->still_target_energy_sensor_->get_state() != new_still_target_energy)
|
|
||||||
this->still_target_energy_sensor_->publish_state(new_still_target_energy);
|
|
||||||
}
|
|
||||||
if (this->detection_distance_sensor_ != nullptr) {
|
|
||||||
int new_detect_distance = this->two_byte_to_int_(buffer[DETECT_DISTANCE_LOW], buffer[DETECT_DISTANCE_HIGH]);
|
|
||||||
if (this->detection_distance_sensor_->get_state() != new_detect_distance)
|
|
||||||
this->detection_distance_sensor_->publish_state(new_detect_distance);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this->light_sensor_ != nullptr) {
|
|
||||||
int data_type = buffer[6];
|
|
||||||
int new_light = -1;
|
|
||||||
if (data_type == 0x01){ // 0x01 = 工程模式!
|
|
||||||
new_light = buffer[37];
|
|
||||||
|
|
||||||
new_light = map(new_light, 85, 255, 0, 100);
|
|
||||||
if (new_light < 0){
|
|
||||||
new_light = 0;
|
|
||||||
}
|
|
||||||
if (new_light > 100){
|
|
||||||
new_light = 100;
|
|
||||||
}
|
|
||||||
|
|
||||||
ESP_LOGD(TAG,"LD2410 Sun Light: %d%%", new_light);
|
|
||||||
}else{
|
|
||||||
int32_t now_millis = millis();
|
|
||||||
|
|
||||||
if (now_millis - last_change_fatory_mode_millis > 2000){
|
|
||||||
ESP_LOGD(TAG,"Normal mode no light, change to factory mode");
|
|
||||||
|
|
||||||
this->factory_mode(true);
|
|
||||||
last_change_fatory_mode_millis = now_millis;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (this->light_sensor_->get_state() != new_light){
|
|
||||||
this->light_sensor_->publish_state(new_light);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void LD2410Component::handle_ack_data_(uint8_t *buffer, int len) {
|
|
||||||
ESP_LOGV(TAG, "Handling ACK DATA for COMMAND");
|
|
||||||
if (len < 10) {
|
|
||||||
ESP_LOGE(TAG, "Error with last command : incorrect length");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (buffer[0] != 0xFD || buffer[1] != 0xFC || buffer[2] != 0xFB || buffer[3] != 0xFA) { // check 4 frame start bytes
|
|
||||||
ESP_LOGE(TAG, "Error with last command : incorrect Header");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (buffer[COMMAND_STATUS] != 0x01) {
|
|
||||||
ESP_LOGE(TAG, "Error with last command : status != 0x01");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (this->two_byte_to_int_(buffer[8], buffer[9]) != 0x00) {
|
|
||||||
ESP_LOGE(TAG, "Error with last command , last buffer was: %u , %u", buffer[8], buffer[9]);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (buffer[COMMAND]) {
|
|
||||||
case lowbyte(CMD_ENABLE_CONF):
|
|
||||||
ESP_LOGV(TAG, "Handled Enable conf command");
|
|
||||||
break;
|
|
||||||
case lowbyte(CMD_DISABLE_CONF):
|
|
||||||
ESP_LOGV(TAG, "Handled Disabled conf command");
|
|
||||||
break;
|
|
||||||
case lowbyte(CMD_VERSION):
|
|
||||||
ESP_LOGV(TAG, "FW Version is: %u.%u.%u%u%u%u", buffer[13], buffer[12], buffer[17], buffer[16], buffer[15],
|
|
||||||
buffer[14]);
|
|
||||||
this->version_[0] = buffer[13];
|
|
||||||
this->version_[1] = buffer[12];
|
|
||||||
this->version_[2] = buffer[17];
|
|
||||||
this->version_[3] = buffer[16];
|
|
||||||
this->version_[4] = buffer[15];
|
|
||||||
this->version_[5] = buffer[14];
|
|
||||||
|
|
||||||
break;
|
|
||||||
case lowbyte(CMD_GATE_SENS):
|
|
||||||
ESP_LOGV(TAG, "Handled sensitivity command");
|
|
||||||
break;
|
|
||||||
case lowbyte(CMD_QUERY): // Query parameters response
|
|
||||||
{
|
|
||||||
if (buffer[10] != 0xAA)
|
|
||||||
return; // value head=0xAA
|
|
||||||
/*
|
|
||||||
Moving distance range: 13th byte
|
|
||||||
Still distance range: 14th byte
|
|
||||||
*/
|
|
||||||
// TODO
|
|
||||||
// maxMovingDistanceRange->publish_state(buffer[12]);
|
|
||||||
// maxStillDistanceRange->publish_state(buffer[13]);
|
|
||||||
/*
|
|
||||||
Moving Sensitivities: 15~23th bytes
|
|
||||||
Still Sensitivities: 24~32th bytes
|
|
||||||
*/
|
|
||||||
for (int i = 0; i < 9; i++) {
|
|
||||||
moving_sensitivities[i] = buffer[14 + i];
|
|
||||||
}
|
|
||||||
for (int i = 0; i < 9; i++) {
|
|
||||||
still_sensitivities[i] = buffer[23 + i];
|
|
||||||
}
|
|
||||||
/*
|
|
||||||
None Duration: 33~34th bytes
|
|
||||||
*/
|
|
||||||
// noneDuration->publish_state(this->two_byte_to_int_(buffer[32], buffer[33]));
|
|
||||||
} break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void LD2410Component::readline_(int readch, uint8_t *buffer, int len) {
|
|
||||||
static int pos = 0;
|
|
||||||
|
|
||||||
if (readch >= 0) {
|
|
||||||
if (pos < len - 1) {
|
|
||||||
buffer[pos++] = readch;
|
|
||||||
buffer[pos] = 0;
|
|
||||||
} else {
|
|
||||||
pos = 0;
|
|
||||||
}
|
|
||||||
if (pos >= 4) {
|
|
||||||
if (buffer[pos - 4] == 0xF8 && buffer[pos - 3] == 0xF7 && buffer[pos - 2] == 0xF6 && buffer[pos - 1] == 0xF5) {
|
|
||||||
ESP_LOGV(TAG, "Will handle Periodic Data");
|
|
||||||
this->handle_periodic_data_(buffer, pos);
|
|
||||||
pos = 0; // Reset position index ready for next time
|
|
||||||
} else if (buffer[pos - 4] == 0x04 && buffer[pos - 3] == 0x03 && buffer[pos - 2] == 0x02 &&
|
|
||||||
buffer[pos - 1] == 0x01) {
|
|
||||||
ESP_LOGV(TAG, "Will handle ACK Data");
|
|
||||||
this->handle_ack_data_(buffer, pos);
|
|
||||||
pos = 0; // Reset position index ready for next time
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void LD2410Component::set_config_mode_(bool enable) {
|
|
||||||
uint8_t cmd = enable ? CMD_ENABLE_CONF : CMD_DISABLE_CONF;
|
|
||||||
uint8_t cmd_value[2] = {0x01, 0x00};
|
|
||||||
this->send_command_(cmd, enable ? cmd_value : nullptr, 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
void LD2410Component::query_parameters_() { this->send_command_(CMD_QUERY, nullptr, 0); }
|
|
||||||
void LD2410Component::get_version_() { this->send_command_(CMD_VERSION, nullptr, 0); }
|
|
||||||
|
|
||||||
void LD2410Component::set_max_distances_timeout_(uint8_t max_moving_distance_range, uint8_t max_still_distance_range,
|
|
||||||
uint16_t timeout) {
|
|
||||||
uint8_t value[18] = {0x00,
|
|
||||||
0x00,
|
|
||||||
lowbyte(max_moving_distance_range),
|
|
||||||
highbyte(max_moving_distance_range),
|
|
||||||
0x00,
|
|
||||||
0x00,
|
|
||||||
0x01,
|
|
||||||
0x00,
|
|
||||||
lowbyte(max_still_distance_range),
|
|
||||||
highbyte(max_still_distance_range),
|
|
||||||
0x00,
|
|
||||||
0x00,
|
|
||||||
0x02,
|
|
||||||
0x00,
|
|
||||||
lowbyte(timeout),
|
|
||||||
highbyte(timeout),
|
|
||||||
0x00,
|
|
||||||
0x00};
|
|
||||||
this->send_command_(CMD_MAXDIST_DURATION, value, 18);
|
|
||||||
this->query_parameters_();
|
|
||||||
}
|
|
||||||
void LD2410Component::set_gate_threshold_(uint8_t gate, uint8_t motionsens, uint8_t stillsens) {
|
|
||||||
// reference
|
|
||||||
// https://drive.google.com/drive/folders/1p4dhbEJA3YubyIjIIC7wwVsSo8x29Fq-?spm=a2g0o.detail.1000023.17.93465697yFwVxH
|
|
||||||
// Send data: configure the motion sensitivity of distance gate 3 to 40, and the static sensitivity of 40
|
|
||||||
// 00 00 (gate)
|
|
||||||
// 03 00 00 00 (gate number)
|
|
||||||
// 01 00 (motion sensitivity)
|
|
||||||
// 28 00 00 00 (value)
|
|
||||||
// 02 00 (still sensitivtiy)
|
|
||||||
// 28 00 00 00 (value)
|
|
||||||
uint8_t value[18] = {0x00, 0x00, lowbyte(gate), highbyte(gate), 0x00, 0x00,
|
|
||||||
0x01, 0x00, lowbyte(motionsens), highbyte(motionsens), 0x00, 0x00,
|
|
||||||
0x02, 0x00, lowbyte(stillsens), highbyte(stillsens), 0x00, 0x00};
|
|
||||||
this->send_command_(CMD_GATE_SENS, value, 18);
|
|
||||||
}
|
|
||||||
|
|
||||||
void LD2410Component::factoryReset()
|
|
||||||
{
|
|
||||||
this->set_config_mode_(true);
|
|
||||||
|
|
||||||
uint8_t cmd = 0x00A2;
|
|
||||||
this -> send_command_(cmd, nullptr, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
void LD2410Component::reboot()
|
|
||||||
{
|
|
||||||
ESP_LOGD(TAG, "reboot ld2410b...");
|
|
||||||
this->set_config_mode_(true);
|
|
||||||
|
|
||||||
uint8_t cmd = 0x00A3;
|
|
||||||
this -> send_command_(cmd, nullptr, 0);
|
|
||||||
// not need to exit config mode because the ld2410 will reboot automatically
|
|
||||||
}
|
|
||||||
|
|
||||||
void LD2410Component::ble_control(bool enable)
|
|
||||||
{
|
|
||||||
this->set_config_mode_(true);
|
|
||||||
uint8_t CMD_BLE_CONF = 0x00A4;
|
|
||||||
uint8_t CMD_REBOOT = 0x00A3;
|
|
||||||
|
|
||||||
uint8_t cmd_value[2] = {0x00, 0x00};
|
|
||||||
|
|
||||||
if (enable){
|
|
||||||
cmd_value[0] = 0x01;
|
|
||||||
|
|
||||||
ESP_LOGD(TAG, "Enable BLE...");
|
|
||||||
}else{
|
|
||||||
ESP_LOGD(TAG, "Disable BLE...");
|
|
||||||
}
|
|
||||||
|
|
||||||
this -> send_command_(CMD_BLE_CONF, cmd_value, 2);
|
|
||||||
|
|
||||||
//this -> send_command_(CMD_REBOOT, nullptr, 0);
|
|
||||||
|
|
||||||
this->set_config_mode_(false);
|
|
||||||
|
|
||||||
this -> reboot();
|
|
||||||
}
|
|
||||||
|
|
||||||
void LD2410Component::factory_mode(bool enable){
|
|
||||||
uint8_t CMD_FACTORY_ON = 0x0062;
|
|
||||||
uint8_t CMD_FACTORY_OFF = 0x0063;
|
|
||||||
this->set_config_mode_(true);
|
|
||||||
|
|
||||||
uint8_t cmd = enable ? CMD_FACTORY_ON: CMD_FACTORY_OFF;
|
|
||||||
|
|
||||||
this->send_command_(cmd, nullptr, 0);
|
|
||||||
|
|
||||||
this->set_config_mode_(false);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace ld2410
|
|
||||||
} // namespace esphome
|
|
||||||
|
|
@ -1,158 +0,0 @@
|
||||||
#pragma once
|
|
||||||
#include "esphome/core/defines.h"
|
|
||||||
#include "esphome/core/component.h"
|
|
||||||
#ifdef USE_BINARY_SENSOR
|
|
||||||
#include "esphome/components/binary_sensor/binary_sensor.h"
|
|
||||||
#endif
|
|
||||||
#ifdef USE_SENSOR
|
|
||||||
#include "esphome/components/sensor/sensor.h"
|
|
||||||
#endif
|
|
||||||
#include "esphome/components/uart/uart.h"
|
|
||||||
#include "esphome/core/automation.h"
|
|
||||||
#include "esphome/core/helpers.h"
|
|
||||||
|
|
||||||
namespace esphome {
|
|
||||||
namespace ld2410 {
|
|
||||||
|
|
||||||
#define CHECK_BIT(var, pos) (((var) >> (pos)) & 1)
|
|
||||||
|
|
||||||
// Commands
|
|
||||||
static const uint8_t CMD_ENABLE_CONF = 0x00FF;
|
|
||||||
static const uint8_t CMD_DISABLE_CONF = 0x00FE;
|
|
||||||
static const uint8_t CMD_MAXDIST_DURATION = 0x0060;
|
|
||||||
static const uint8_t CMD_QUERY = 0x0061;
|
|
||||||
static const uint8_t CMD_GATE_SENS = 0x0064;
|
|
||||||
static const uint8_t CMD_VERSION = 0x00A0;
|
|
||||||
|
|
||||||
// Commands values
|
|
||||||
static const uint8_t CMD_MAX_MOVE_VALUE = 0x0000;
|
|
||||||
static const uint8_t CMD_MAX_STILL_VALUE = 0x0001;
|
|
||||||
static const uint8_t CMD_DURATION_VALUE = 0x0002;
|
|
||||||
// Command Header & Footer
|
|
||||||
static const uint8_t CMD_FRAME_HEADER[4] = {0xFD, 0xFC, 0xFB, 0xFA};
|
|
||||||
static const uint8_t CMD_FRAME_END[4] = {0x04, 0x03, 0x02, 0x01};
|
|
||||||
// Data Header & Footer
|
|
||||||
static const uint8_t DATA_FRAME_HEADER[4] = {0xF4, 0xF3, 0xF2, 0xF1};
|
|
||||||
static const uint8_t DATA_FRAME_END[4] = {0xF8, 0xF7, 0xF6, 0xF5};
|
|
||||||
/*
|
|
||||||
Data Type: 6th byte
|
|
||||||
Target states: 9th byte
|
|
||||||
Moving target distance: 10~11th bytes
|
|
||||||
Moving target energy: 12th byte
|
|
||||||
Still target distance: 13~14th bytes
|
|
||||||
Still target energy: 15th byte
|
|
||||||
Detect distance: 16~17th bytes
|
|
||||||
*/
|
|
||||||
enum PeriodicDataStructure : uint8_t {
|
|
||||||
DATA_TYPES = 5,
|
|
||||||
TARGET_STATES = 8,
|
|
||||||
MOVING_TARGET_LOW = 9,
|
|
||||||
MOVING_TARGET_HIGH = 10,
|
|
||||||
MOVING_ENERGY = 11,
|
|
||||||
STILL_TARGET_LOW = 12,
|
|
||||||
STILL_TARGET_HIGH = 13,
|
|
||||||
STILL_ENERGY = 14,
|
|
||||||
DETECT_DISTANCE_LOW = 15,
|
|
||||||
DETECT_DISTANCE_HIGH = 16,
|
|
||||||
};
|
|
||||||
// 上报数据的固定结构。(23年3月13日_16时54分_)
|
|
||||||
enum PeriodicDataValue : uint8_t { HEAD = 0XAA, END = 0x55, CHECK = 0x00 };
|
|
||||||
|
|
||||||
enum AckDataStructure : uint8_t { COMMAND = 6, COMMAND_STATUS = 7 };
|
|
||||||
|
|
||||||
// char cmd[2] = {enable ? 0xFF : 0xFE, 0x00};
|
|
||||||
class LD2410Component : public Component, public uart::UARTDevice {
|
|
||||||
#ifdef USE_SENSOR
|
|
||||||
SUB_SENSOR(moving_target_distance)
|
|
||||||
SUB_SENSOR(still_target_distance)
|
|
||||||
SUB_SENSOR(moving_target_energy)
|
|
||||||
SUB_SENSOR(still_target_energy)
|
|
||||||
SUB_SENSOR(detection_distance)
|
|
||||||
SUB_SENSOR(light)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
public:
|
|
||||||
void setup() override;
|
|
||||||
void dump_config() override;
|
|
||||||
void loop() override;
|
|
||||||
|
|
||||||
#ifdef USE_BINARY_SENSOR
|
|
||||||
void set_target_sensor(binary_sensor::BinarySensor *sens) { this->target_binary_sensor_ = sens; };
|
|
||||||
void set_moving_target_sensor(binary_sensor::BinarySensor *sens) { this->moving_binary_sensor_ = sens; };
|
|
||||||
void set_still_target_sensor(binary_sensor::BinarySensor *sens) { this->still_binary_sensor_ = sens; };
|
|
||||||
#endif
|
|
||||||
|
|
||||||
void set_timeout(uint16_t value) { this->timeout_ = value; };
|
|
||||||
void set_max_move_distance(uint8_t value) { this->max_move_distance_ = value; };
|
|
||||||
void set_max_still_distance(uint8_t value) { this->max_still_distance_ = value; };
|
|
||||||
void set_range_config(int rg0_move, int rg0_still, int rg1_move, int rg1_still, int rg2_move, int rg2_still,
|
|
||||||
int rg3_move, int rg3_still, int rg4_move, int rg4_still, int rg5_move, int rg5_still,
|
|
||||||
int rg6_move, int rg6_still, int rg7_move, int rg7_still, int rg8_move, int rg8_still) {
|
|
||||||
this->rg0_move_threshold_ = rg0_move;
|
|
||||||
this->rg0_still_threshold_ = rg0_still;
|
|
||||||
this->rg1_move_threshold_ = rg1_move;
|
|
||||||
this->rg1_still_threshold_ = rg1_still;
|
|
||||||
this->rg2_move_threshold_ = rg2_move;
|
|
||||||
this->rg2_still_threshold_ = rg2_still;
|
|
||||||
this->rg3_move_threshold_ = rg3_move;
|
|
||||||
this->rg3_still_threshold_ = rg3_still;
|
|
||||||
this->rg4_move_threshold_ = rg4_move;
|
|
||||||
this->rg4_still_threshold_ = rg4_still;
|
|
||||||
this->rg5_move_threshold_ = rg5_move;
|
|
||||||
this->rg5_still_threshold_ = rg5_still;
|
|
||||||
this->rg6_move_threshold_ = rg6_move;
|
|
||||||
this->rg6_still_threshold_ = rg6_still;
|
|
||||||
this->rg7_move_threshold_ = rg7_move;
|
|
||||||
this->rg7_still_threshold_ = rg7_still;
|
|
||||||
this->rg8_move_threshold_ = rg8_move;
|
|
||||||
this->rg8_still_threshold_ = rg8_still;
|
|
||||||
};
|
|
||||||
int moving_sensitivities[9] = {0};
|
|
||||||
int still_sensitivities[9] = {0};
|
|
||||||
|
|
||||||
int32_t last_periodic_millis = millis();
|
|
||||||
|
|
||||||
int32_t last_change_fatory_mode_millis = 0;
|
|
||||||
|
|
||||||
void factoryReset();
|
|
||||||
|
|
||||||
void reboot();
|
|
||||||
|
|
||||||
void ble_control(bool enable);
|
|
||||||
|
|
||||||
void factory_mode(bool enable);
|
|
||||||
|
|
||||||
protected:
|
|
||||||
#ifdef USE_BINARY_SENSOR
|
|
||||||
binary_sensor::BinarySensor *target_binary_sensor_{nullptr};
|
|
||||||
binary_sensor::BinarySensor *moving_binary_sensor_{nullptr};
|
|
||||||
binary_sensor::BinarySensor *still_binary_sensor_{nullptr};
|
|
||||||
#endif
|
|
||||||
|
|
||||||
std::vector<uint8_t> rx_buffer_;
|
|
||||||
int two_byte_to_int_(char firstbyte, char secondbyte) { return (int16_t)(secondbyte << 8) + firstbyte; }
|
|
||||||
void send_command_(uint8_t command_str, uint8_t *command_value, int command_value_len);
|
|
||||||
|
|
||||||
void set_max_distances_timeout_(uint8_t max_moving_distance_range, uint8_t max_still_distance_range,
|
|
||||||
uint16_t timeout);
|
|
||||||
void set_gate_threshold_(uint8_t gate, uint8_t motionsens, uint8_t stillsens);
|
|
||||||
void set_config_mode_(bool enable);
|
|
||||||
void handle_periodic_data_(uint8_t *buffer, int len);
|
|
||||||
void handle_ack_data_(uint8_t *buffer, int len);
|
|
||||||
void readline_(int readch, uint8_t *buffer, int len);
|
|
||||||
void query_parameters_();
|
|
||||||
void get_version_();
|
|
||||||
|
|
||||||
uint16_t timeout_;
|
|
||||||
uint8_t max_move_distance_;
|
|
||||||
uint8_t max_still_distance_;
|
|
||||||
|
|
||||||
uint8_t version_[6];
|
|
||||||
uint8_t rg0_move_threshold_, rg0_still_threshold_, rg1_move_threshold_, rg1_still_threshold_, rg2_move_threshold_,
|
|
||||||
rg2_still_threshold_, rg3_move_threshold_, rg3_still_threshold_, rg4_move_threshold_, rg4_still_threshold_,
|
|
||||||
rg5_move_threshold_, rg5_still_threshold_, rg6_move_threshold_, rg6_still_threshold_, rg7_move_threshold_,
|
|
||||||
rg7_still_threshold_, rg8_move_threshold_, rg8_still_threshold_;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace ld2410
|
|
||||||
} // namespace esphome
|
|
||||||
|
|
@ -1,70 +0,0 @@
|
||||||
import esphome.codegen as cg
|
|
||||||
from esphome.components import sensor
|
|
||||||
import esphome.config_validation as cv
|
|
||||||
from esphome.const import (
|
|
||||||
DEVICE_CLASS_DISTANCE,
|
|
||||||
DEVICE_CLASS_ENERGY,
|
|
||||||
UNIT_CENTIMETER,
|
|
||||||
UNIT_PERCENT,
|
|
||||||
STATE_CLASS_NONE,
|
|
||||||
ICON_BRIGHTNESS_5,
|
|
||||||
UNIT_EMPTY,
|
|
||||||
UNIT_LUX,
|
|
||||||
|
|
||||||
)
|
|
||||||
from . import CONF_LD2410_ID, LD2410Component
|
|
||||||
|
|
||||||
DEPENDENCIES = ["ld2410"]
|
|
||||||
CONF_MOVING_DISTANCE = "moving_distance"
|
|
||||||
CONF_STILL_DISTANCE = "still_distance"
|
|
||||||
CONF_MOVING_ENERGY = "moving_energy"
|
|
||||||
CONF_STILL_ENERGY = "still_energy"
|
|
||||||
CONF_DETECTION_DISTANCE = "detection_distance"
|
|
||||||
CONF_LIGHT = "light"
|
|
||||||
|
|
||||||
CONFIG_SCHEMA = {
|
|
||||||
cv.GenerateID(CONF_LD2410_ID): cv.use_id(LD2410Component),
|
|
||||||
cv.Optional(CONF_MOVING_DISTANCE): sensor.sensor_schema(
|
|
||||||
device_class=DEVICE_CLASS_DISTANCE, unit_of_measurement=UNIT_CENTIMETER
|
|
||||||
),
|
|
||||||
cv.Optional(CONF_STILL_DISTANCE): sensor.sensor_schema(
|
|
||||||
device_class=DEVICE_CLASS_DISTANCE, unit_of_measurement=UNIT_CENTIMETER
|
|
||||||
),
|
|
||||||
cv.Optional(CONF_MOVING_ENERGY): sensor.sensor_schema(
|
|
||||||
device_class=DEVICE_CLASS_ENERGY, unit_of_measurement=UNIT_PERCENT
|
|
||||||
),
|
|
||||||
cv.Optional(CONF_STILL_ENERGY): sensor.sensor_schema(
|
|
||||||
device_class=DEVICE_CLASS_ENERGY, unit_of_measurement=UNIT_PERCENT
|
|
||||||
),
|
|
||||||
cv.Optional(CONF_DETECTION_DISTANCE): sensor.sensor_schema(
|
|
||||||
device_class=DEVICE_CLASS_DISTANCE, unit_of_measurement=UNIT_CENTIMETER
|
|
||||||
),
|
|
||||||
|
|
||||||
cv.Optional(CONF_LIGHT): sensor.sensor_schema(
|
|
||||||
device_class=STATE_CLASS_NONE,
|
|
||||||
icon=ICON_BRIGHTNESS_5,
|
|
||||||
unit_of_measurement=UNIT_PERCENT
|
|
||||||
),
|
|
||||||
}
|
|
||||||
|
|
||||||
async def to_code(config):
|
|
||||||
ld2410_component = await cg.get_variable(config[CONF_LD2410_ID])
|
|
||||||
if CONF_MOVING_DISTANCE in config:
|
|
||||||
sens = await sensor.new_sensor(config[CONF_MOVING_DISTANCE])
|
|
||||||
cg.add(ld2410_component.set_moving_target_distance_sensor(sens))
|
|
||||||
if CONF_STILL_DISTANCE in config:
|
|
||||||
sens = await sensor.new_sensor(config[CONF_STILL_DISTANCE])
|
|
||||||
cg.add(ld2410_component.set_still_target_distance_sensor(sens))
|
|
||||||
if CONF_MOVING_ENERGY in config:
|
|
||||||
sens = await sensor.new_sensor(config[CONF_MOVING_ENERGY])
|
|
||||||
cg.add(ld2410_component.set_moving_target_energy_sensor(sens))
|
|
||||||
if CONF_STILL_ENERGY in config:
|
|
||||||
sens = await sensor.new_sensor(config[CONF_STILL_ENERGY])
|
|
||||||
cg.add(ld2410_component.set_still_target_energy_sensor(sens))
|
|
||||||
if CONF_DETECTION_DISTANCE in config:
|
|
||||||
sens = await sensor.new_sensor(config[CONF_DETECTION_DISTANCE])
|
|
||||||
cg.add(ld2410_component.set_detection_distance_sensor(sens))
|
|
||||||
|
|
||||||
if CONF_LIGHT in config:
|
|
||||||
sens = await sensor.new_sensor(config[CONF_LIGHT])
|
|
||||||
cg.add(ld2410_component.set_light_sensor(sens))
|
|
||||||
|
|
@ -1,158 +0,0 @@
|
||||||
import esphome.codegen as cg
|
|
||||||
import esphome.config_validation as cv
|
|
||||||
from esphome.components import uart
|
|
||||||
from esphome.const import CONF_ID, CONF_TIMEOUT
|
|
||||||
from esphome import automation
|
|
||||||
from esphome.automation import maybe_simple_id
|
|
||||||
|
|
||||||
DEPENDENCIES = ["uart"]
|
|
||||||
CODEOWNERS = ["@sebcaps"]
|
|
||||||
MULTI_CONF = True
|
|
||||||
|
|
||||||
ld2410_ns = cg.esphome_ns.namespace("ld2410")
|
|
||||||
LD2410Component = ld2410_ns.class_("LD2410Component", cg.Component, uart.UARTDevice)
|
|
||||||
LD2410Restart = ld2410_ns.class_("LD2410Restart", automation.Action)
|
|
||||||
CONF_LD2410_ID = "ld2410_id"
|
|
||||||
CONF_MAX_MOVE_DISTANCE = "max_move_distance"
|
|
||||||
CONF_MAX_STILL_DISTANCE = "max_still_distance"
|
|
||||||
CONF_G0_MOVE_THRESHOLD = "g0_move_threshold"
|
|
||||||
CONF_G0_STILL_THRESHOLD = "g0_still_threshold"
|
|
||||||
CONF_G1_MOVE_THRESHOLD = "g1_move_threshold"
|
|
||||||
CONF_G1_STILL_THRESHOLD = "g1_still_threshold"
|
|
||||||
CONF_G2_MOVE_THRESHOLD = "g2_move_threshold"
|
|
||||||
CONF_G2_STILL_THRESHOLD = "g2_still_threshold"
|
|
||||||
CONF_G3_MOVE_THRESHOLD = "g3_move_threshold"
|
|
||||||
CONF_G3_STILL_THRESHOLD = "g3_still_threshold"
|
|
||||||
CONF_G4_MOVE_THRESHOLD = "g4_move_threshold"
|
|
||||||
CONF_G4_STILL_THRESHOLD = "g4_still_threshold"
|
|
||||||
CONF_G5_MOVE_THRESHOLD = "g5_move_threshold"
|
|
||||||
CONF_G5_STILL_THRESHOLD = "g5_still_threshold"
|
|
||||||
CONF_G6_MOVE_THRESHOLD = "g6_move_threshold"
|
|
||||||
CONF_G6_STILL_THRESHOLD = "g6_still_threshold"
|
|
||||||
CONF_G7_MOVE_THRESHOLD = "g7_move_threshold"
|
|
||||||
CONF_G7_STILL_THRESHOLD = "g7_still_threshold"
|
|
||||||
CONF_G8_MOVE_THRESHOLD = "g8_move_threshold"
|
|
||||||
CONF_G8_STILL_THRESHOLD = "g8_still_threshold"
|
|
||||||
|
|
||||||
DISTANCES = [0.75, 1.5, 2.25, 3, 3.75, 4.5, 5.25, 6]
|
|
||||||
|
|
||||||
CONFIG_SCHEMA = cv.All(
|
|
||||||
cv.Schema(
|
|
||||||
{
|
|
||||||
cv.GenerateID(): cv.declare_id(LD2410Component),
|
|
||||||
cv.Optional(CONF_MAX_MOVE_DISTANCE, default="4.5m"): cv.All(
|
|
||||||
cv.distance, cv.one_of(*DISTANCES, float=True)
|
|
||||||
),
|
|
||||||
cv.Optional(CONF_MAX_STILL_DISTANCE, default="4.5m"): cv.All(
|
|
||||||
cv.distance, cv.one_of(*DISTANCES, float=True)
|
|
||||||
),
|
|
||||||
cv.Optional(CONF_TIMEOUT, default="5s"): cv.All(
|
|
||||||
cv.positive_time_period_seconds,
|
|
||||||
cv.Range(max=cv.TimePeriod(seconds=32767)),
|
|
||||||
),
|
|
||||||
cv.Optional(CONF_G0_MOVE_THRESHOLD, default=50): cv.int_range(
|
|
||||||
min=0, max=100
|
|
||||||
),
|
|
||||||
cv.Optional(CONF_G0_STILL_THRESHOLD, default=0): cv.int_range(
|
|
||||||
min=0, max=100
|
|
||||||
),
|
|
||||||
cv.Optional(CONF_G1_MOVE_THRESHOLD, default=50): cv.int_range(
|
|
||||||
min=0, max=100
|
|
||||||
),
|
|
||||||
cv.Optional(CONF_G1_STILL_THRESHOLD, default=0): cv.int_range(
|
|
||||||
min=0, max=100
|
|
||||||
),
|
|
||||||
cv.Optional(CONF_G2_MOVE_THRESHOLD, default=40): cv.int_range(
|
|
||||||
min=0, max=100
|
|
||||||
),
|
|
||||||
cv.Optional(CONF_G2_STILL_THRESHOLD, default=40): cv.int_range(
|
|
||||||
min=0, max=100
|
|
||||||
),
|
|
||||||
cv.Optional(CONF_G3_MOVE_THRESHOLD, default=40): cv.int_range(
|
|
||||||
min=0, max=100
|
|
||||||
),
|
|
||||||
cv.Optional(CONF_G3_STILL_THRESHOLD, default=40): cv.int_range(
|
|
||||||
min=0, max=100
|
|
||||||
),
|
|
||||||
cv.Optional(CONF_G4_MOVE_THRESHOLD, default=40): cv.int_range(
|
|
||||||
min=0, max=100
|
|
||||||
),
|
|
||||||
cv.Optional(CONF_G4_STILL_THRESHOLD, default=40): cv.int_range(
|
|
||||||
min=0, max=100
|
|
||||||
),
|
|
||||||
cv.Optional(CONF_G5_MOVE_THRESHOLD, default=40): cv.int_range(
|
|
||||||
min=0, max=100
|
|
||||||
),
|
|
||||||
cv.Optional(CONF_G5_STILL_THRESHOLD, default=40): cv.int_range(
|
|
||||||
min=0, max=100
|
|
||||||
),
|
|
||||||
cv.Optional(CONF_G6_MOVE_THRESHOLD, default=30): cv.int_range(
|
|
||||||
min=0, max=100
|
|
||||||
),
|
|
||||||
cv.Optional(CONF_G6_STILL_THRESHOLD, default=15): cv.int_range(
|
|
||||||
min=0, max=100
|
|
||||||
),
|
|
||||||
cv.Optional(CONF_G7_MOVE_THRESHOLD, default=30): cv.int_range(
|
|
||||||
min=0, max=100
|
|
||||||
),
|
|
||||||
cv.Optional(CONF_G7_STILL_THRESHOLD, default=15): cv.int_range(
|
|
||||||
min=0, max=100
|
|
||||||
),
|
|
||||||
cv.Optional(CONF_G8_MOVE_THRESHOLD, default=30): cv.int_range(
|
|
||||||
min=0, max=100
|
|
||||||
),
|
|
||||||
cv.Optional(CONF_G8_STILL_THRESHOLD, default=15): cv.int_range(
|
|
||||||
min=0, max=100
|
|
||||||
),
|
|
||||||
}
|
|
||||||
)
|
|
||||||
.extend(uart.UART_DEVICE_SCHEMA)
|
|
||||||
.extend(cv.COMPONENT_SCHEMA)
|
|
||||||
)
|
|
||||||
|
|
||||||
FINAL_VALIDATE_SCHEMA = uart.final_validate_device_schema(
|
|
||||||
"ld2410",
|
|
||||||
baud_rate=256000,
|
|
||||||
require_tx=True,
|
|
||||||
require_rx=True,
|
|
||||||
parity="NONE",
|
|
||||||
stop_bits=1,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
async def to_code(config):
|
|
||||||
var = cg.new_Pvariable(config[CONF_ID])
|
|
||||||
await cg.register_component(var, config)
|
|
||||||
await uart.register_uart_device(var, config)
|
|
||||||
cg.add(var.set_timeout(config[CONF_TIMEOUT]))
|
|
||||||
cg.add(var.set_max_move_distance(int(config[CONF_MAX_MOVE_DISTANCE] / 0.75)))
|
|
||||||
cg.add(var.set_max_still_distance(int(config[CONF_MAX_STILL_DISTANCE] / 0.75)))
|
|
||||||
cg.add(
|
|
||||||
var.set_range_config(
|
|
||||||
config[CONF_G0_MOVE_THRESHOLD],
|
|
||||||
config[CONF_G0_STILL_THRESHOLD],
|
|
||||||
config[CONF_G1_MOVE_THRESHOLD],
|
|
||||||
config[CONF_G1_STILL_THRESHOLD],
|
|
||||||
config[CONF_G2_MOVE_THRESHOLD],
|
|
||||||
config[CONF_G2_STILL_THRESHOLD],
|
|
||||||
config[CONF_G3_MOVE_THRESHOLD],
|
|
||||||
config[CONF_G3_STILL_THRESHOLD],
|
|
||||||
config[CONF_G4_MOVE_THRESHOLD],
|
|
||||||
config[CONF_G4_STILL_THRESHOLD],
|
|
||||||
config[CONF_G5_MOVE_THRESHOLD],
|
|
||||||
config[CONF_G5_STILL_THRESHOLD],
|
|
||||||
config[CONF_G6_MOVE_THRESHOLD],
|
|
||||||
config[CONF_G6_STILL_THRESHOLD],
|
|
||||||
config[CONF_G7_MOVE_THRESHOLD],
|
|
||||||
config[CONF_G7_STILL_THRESHOLD],
|
|
||||||
config[CONF_G8_MOVE_THRESHOLD],
|
|
||||||
config[CONF_G8_STILL_THRESHOLD],
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
CALIBRATION_ACTION_SCHEMA = maybe_simple_id(
|
|
||||||
{
|
|
||||||
cv.Required(CONF_ID): cv.use_id(LD2410Component),
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
@ -1,36 +0,0 @@
|
||||||
import esphome.codegen as cg
|
|
||||||
from esphome.components import binary_sensor
|
|
||||||
import esphome.config_validation as cv
|
|
||||||
from esphome.const import DEVICE_CLASS_MOTION, DEVICE_CLASS_OCCUPANCY
|
|
||||||
from . import CONF_LD2410_ID, LD2410Component
|
|
||||||
|
|
||||||
DEPENDENCIES = ["ld2410"]
|
|
||||||
CONF_HAS_TARGET = "has_target"
|
|
||||||
CONF_HAS_MOVING_TARGET = "has_moving_target"
|
|
||||||
CONF_HAS_STILL_TARGET = "has_still_target"
|
|
||||||
|
|
||||||
CONFIG_SCHEMA = {
|
|
||||||
cv.GenerateID(CONF_LD2410_ID): cv.use_id(LD2410Component),
|
|
||||||
cv.Optional(CONF_HAS_TARGET): binary_sensor.binary_sensor_schema(
|
|
||||||
device_class=DEVICE_CLASS_OCCUPANCY
|
|
||||||
),
|
|
||||||
cv.Optional(CONF_HAS_MOVING_TARGET): binary_sensor.binary_sensor_schema(
|
|
||||||
device_class=DEVICE_CLASS_MOTION
|
|
||||||
),
|
|
||||||
cv.Optional(CONF_HAS_STILL_TARGET): binary_sensor.binary_sensor_schema(
|
|
||||||
device_class=DEVICE_CLASS_OCCUPANCY
|
|
||||||
),
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
async def to_code(config):
|
|
||||||
ld2410_component = await cg.get_variable(config[CONF_LD2410_ID])
|
|
||||||
if CONF_HAS_TARGET in config:
|
|
||||||
sens = await binary_sensor.new_binary_sensor(config[CONF_HAS_TARGET])
|
|
||||||
cg.add(ld2410_component.set_target_sensor(sens))
|
|
||||||
if CONF_HAS_MOVING_TARGET in config:
|
|
||||||
sens = await binary_sensor.new_binary_sensor(config[CONF_HAS_MOVING_TARGET])
|
|
||||||
cg.add(ld2410_component.set_moving_target_sensor(sens))
|
|
||||||
if CONF_HAS_STILL_TARGET in config:
|
|
||||||
sens = await binary_sensor.new_binary_sensor(config[CONF_HAS_STILL_TARGET])
|
|
||||||
cg.add(ld2410_component.set_still_target_sensor(sens))
|
|
||||||
|
|
@ -1,416 +0,0 @@
|
||||||
#include "ld2410.h"
|
|
||||||
|
|
||||||
#define highbyte(val) (uint8_t)((val) >> 8)
|
|
||||||
#define lowbyte(val) (uint8_t)((val) &0xff)
|
|
||||||
|
|
||||||
namespace esphome {
|
|
||||||
namespace ld2410 {
|
|
||||||
|
|
||||||
static const char *const TAG = "ld2410";
|
|
||||||
|
|
||||||
void LD2410Component::dump_config() {
|
|
||||||
ESP_LOGCONFIG(TAG, "LD2410:");
|
|
||||||
#ifdef USE_BINARY_SENSOR
|
|
||||||
LOG_BINARY_SENSOR(" ", "HasTargetSensor", this->target_binary_sensor_);
|
|
||||||
LOG_BINARY_SENSOR(" ", "MovingSensor", this->moving_binary_sensor_);
|
|
||||||
LOG_BINARY_SENSOR(" ", "StillSensor", this->still_binary_sensor_);
|
|
||||||
#endif
|
|
||||||
#ifdef USE_SENSOR
|
|
||||||
LOG_SENSOR(" ", "Moving Distance", this->moving_target_distance_sensor_);
|
|
||||||
LOG_SENSOR(" ", "Still Distance", this->still_target_distance_sensor_);
|
|
||||||
LOG_SENSOR(" ", "Moving Energy", this->moving_target_energy_sensor_);
|
|
||||||
LOG_SENSOR(" ", "Still Energy", this->still_target_energy_sensor_);
|
|
||||||
LOG_SENSOR(" ", "Detection Distance", this->detection_distance_sensor_);
|
|
||||||
|
|
||||||
LOG_SENSOR(" ", "Light", this->light_sensor_);
|
|
||||||
#endif
|
|
||||||
this->set_config_mode_(true);
|
|
||||||
this->get_version_();
|
|
||||||
this->set_config_mode_(false);
|
|
||||||
ESP_LOGCONFIG(TAG, " Firmware Version : %u.%u.%u%u%u%u", this->version_[0], this->version_[1], this->version_[2],
|
|
||||||
this->version_[3], this->version_[4], this->version_[5]);
|
|
||||||
}
|
|
||||||
|
|
||||||
void LD2410Component::setup() {
|
|
||||||
ESP_LOGCONFIG(TAG, "Setting up LD2410...");
|
|
||||||
// ESP_LOGCONFIG(TAG, "Apply screek patch...");
|
|
||||||
this->set_config_mode_(true);
|
|
||||||
|
|
||||||
/*
|
|
||||||
this->set_max_distances_timeout_(this->max_move_distance_, this->max_still_distance_, this->timeout_);
|
|
||||||
// Configure Gates sensitivity
|
|
||||||
this->set_gate_threshold_(0, this->rg0_move_threshold_, this->rg0_still_threshold_);
|
|
||||||
this->set_gate_threshold_(1, this->rg1_move_threshold_, this->rg1_still_threshold_);
|
|
||||||
this->set_gate_threshold_(2, this->rg2_move_threshold_, this->rg2_still_threshold_);
|
|
||||||
this->set_gate_threshold_(3, this->rg3_move_threshold_, this->rg3_still_threshold_);
|
|
||||||
this->set_gate_threshold_(4, this->rg4_move_threshold_, this->rg4_still_threshold_);
|
|
||||||
this->set_gate_threshold_(5, this->rg5_move_threshold_, this->rg5_still_threshold_);
|
|
||||||
this->set_gate_threshold_(6, this->rg6_move_threshold_, this->rg6_still_threshold_);
|
|
||||||
this->set_gate_threshold_(7, this->rg7_move_threshold_, this->rg7_still_threshold_);
|
|
||||||
this->set_gate_threshold_(8, this->rg8_move_threshold_, this->rg8_still_threshold_);
|
|
||||||
*/
|
|
||||||
this->get_version_();
|
|
||||||
this->set_config_mode_(false);
|
|
||||||
|
|
||||||
// this->factory_mode(true);
|
|
||||||
|
|
||||||
ESP_LOGCONFIG(TAG, "Firmware Version : %u.%u.%u%u%u%u", this->version_[0], this->version_[1], this->version_[2],
|
|
||||||
this->version_[3], this->version_[4], this->version_[5]);
|
|
||||||
ESP_LOGCONFIG(TAG, "LD2410 setup complete.");
|
|
||||||
}
|
|
||||||
|
|
||||||
void LD2410Component::loop() {
|
|
||||||
const int max_line_length = 80;
|
|
||||||
static uint8_t buffer[max_line_length];
|
|
||||||
|
|
||||||
while (available()) {
|
|
||||||
this->readline_(read(), buffer, max_line_length);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void LD2410Component::send_command_(uint8_t command, uint8_t *command_value, int command_value_len) {
|
|
||||||
// lastCommandSuccess->publish_state(false);
|
|
||||||
|
|
||||||
// frame start bytes
|
|
||||||
this->write_array(CMD_FRAME_HEADER, 4);
|
|
||||||
// length bytes
|
|
||||||
int len = 2;
|
|
||||||
if (command_value != nullptr)
|
|
||||||
len += command_value_len;
|
|
||||||
this->write_byte(lowbyte(len));
|
|
||||||
this->write_byte(highbyte(len));
|
|
||||||
|
|
||||||
// command
|
|
||||||
this->write_byte(lowbyte(command));
|
|
||||||
this->write_byte(highbyte(command));
|
|
||||||
|
|
||||||
// command value bytes
|
|
||||||
if (command_value != nullptr) {
|
|
||||||
for (int i = 0; i < command_value_len; i++) {
|
|
||||||
this->write_byte(command_value[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// frame end bytes
|
|
||||||
this->write_array(CMD_FRAME_END, 4);
|
|
||||||
// FIXME to remove
|
|
||||||
delay(50); // NOLINT
|
|
||||||
}
|
|
||||||
|
|
||||||
long map(long x, long in_min, long in_max, long out_min, long out_max) {
|
|
||||||
return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
|
|
||||||
}
|
|
||||||
|
|
||||||
void LD2410Component::handle_periodic_data_(uint8_t *buffer, int len) {
|
|
||||||
if (len < 12)
|
|
||||||
return; // 4 frame start bytes + 2 length bytes + 1 data end byte + 1 crc byte + 4 frame end bytes
|
|
||||||
if (buffer[0] != 0xF4 || buffer[1] != 0xF3 || buffer[2] != 0xF2 || buffer[3] != 0xF1) // check 4 frame start bytes
|
|
||||||
return;
|
|
||||||
if (buffer[7] != HEAD || buffer[len - 6] != END || buffer[len - 5] != CHECK) // Check constant values
|
|
||||||
return; // data head=0xAA, data end=0x55, crc=0x00
|
|
||||||
|
|
||||||
/*
|
|
||||||
Data Type: 6th
|
|
||||||
0x01: Engineering mode
|
|
||||||
0x02: Normal mode
|
|
||||||
*/
|
|
||||||
// char data_type = buffer[DATA_TYPES];
|
|
||||||
/*
|
|
||||||
Target states: 9th
|
|
||||||
0x00 = No target
|
|
||||||
0x01 = Moving targets
|
|
||||||
0x02 = Still targets
|
|
||||||
0x03 = Moving+Still targets
|
|
||||||
*/
|
|
||||||
#ifdef USE_BINARY_SENSOR
|
|
||||||
char target_state = buffer[TARGET_STATES];
|
|
||||||
if (this->target_binary_sensor_ != nullptr) {
|
|
||||||
this->target_binary_sensor_->publish_state(target_state != 0x00);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/*
|
|
||||||
Reduce data update rate to prevent home assistant database size grow fast
|
|
||||||
*/
|
|
||||||
int32_t current_millis = millis();
|
|
||||||
if (current_millis - last_periodic_millis < 1000)
|
|
||||||
return;
|
|
||||||
last_periodic_millis = current_millis;
|
|
||||||
|
|
||||||
#ifdef USE_BINARY_SENSOR
|
|
||||||
if (this->moving_binary_sensor_ != nullptr) {
|
|
||||||
this->moving_binary_sensor_->publish_state(CHECK_BIT(target_state, 0));
|
|
||||||
}
|
|
||||||
if (this->still_binary_sensor_ != nullptr) {
|
|
||||||
this->still_binary_sensor_->publish_state(CHECK_BIT(target_state, 1));
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
/*
|
|
||||||
Moving target distance: 10~11th bytes
|
|
||||||
Moving target energy: 12th byte
|
|
||||||
Still target distance: 13~14th bytes
|
|
||||||
Still target energy: 15th byte
|
|
||||||
Detect distance: 16~17th bytes
|
|
||||||
*/
|
|
||||||
#ifdef USE_SENSOR
|
|
||||||
if (this->moving_target_distance_sensor_ != nullptr) {
|
|
||||||
int new_moving_target_distance = this->two_byte_to_int_(buffer[MOVING_TARGET_LOW], buffer[MOVING_TARGET_HIGH]);
|
|
||||||
if (this->moving_target_distance_sensor_->get_state() != new_moving_target_distance)
|
|
||||||
this->moving_target_distance_sensor_->publish_state(new_moving_target_distance);
|
|
||||||
}
|
|
||||||
if (this->moving_target_energy_sensor_ != nullptr) {
|
|
||||||
int new_moving_target_energy = buffer[MOVING_ENERGY];
|
|
||||||
if (this->moving_target_energy_sensor_->get_state() != new_moving_target_energy)
|
|
||||||
this->moving_target_energy_sensor_->publish_state(new_moving_target_energy);
|
|
||||||
}
|
|
||||||
if (this->still_target_distance_sensor_ != nullptr) {
|
|
||||||
int new_still_target_distance = this->two_byte_to_int_(buffer[STILL_TARGET_LOW], buffer[STILL_TARGET_HIGH]);
|
|
||||||
if (this->still_target_distance_sensor_->get_state() != new_still_target_distance)
|
|
||||||
this->still_target_distance_sensor_->publish_state(new_still_target_distance);
|
|
||||||
}
|
|
||||||
if (this->still_target_energy_sensor_ != nullptr) {
|
|
||||||
int new_still_target_energy = buffer[STILL_ENERGY];
|
|
||||||
if (this->still_target_energy_sensor_->get_state() != new_still_target_energy)
|
|
||||||
this->still_target_energy_sensor_->publish_state(new_still_target_energy);
|
|
||||||
}
|
|
||||||
if (this->detection_distance_sensor_ != nullptr) {
|
|
||||||
int new_detect_distance = this->two_byte_to_int_(buffer[DETECT_DISTANCE_LOW], buffer[DETECT_DISTANCE_HIGH]);
|
|
||||||
if (this->detection_distance_sensor_->get_state() != new_detect_distance)
|
|
||||||
this->detection_distance_sensor_->publish_state(new_detect_distance);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this->light_sensor_ != nullptr) {
|
|
||||||
int data_type = buffer[6];
|
|
||||||
int new_light = -1;
|
|
||||||
if (data_type == 0x01){ // 0x01 = 工程模式!
|
|
||||||
new_light = buffer[37];
|
|
||||||
|
|
||||||
new_light = map(new_light, 85, 255, 0, 100);
|
|
||||||
if (new_light < 0){
|
|
||||||
new_light = 0;
|
|
||||||
}
|
|
||||||
if (new_light > 100){
|
|
||||||
new_light = 100;
|
|
||||||
}
|
|
||||||
|
|
||||||
ESP_LOGD(TAG,"LD2410 Sun Light: %d%%", new_light);
|
|
||||||
}else{
|
|
||||||
int32_t now_millis = millis();
|
|
||||||
|
|
||||||
if (now_millis - last_change_fatory_mode_millis > 2000){
|
|
||||||
ESP_LOGD(TAG,"Normal mode no light, change to factory mode");
|
|
||||||
|
|
||||||
this->factory_mode(true);
|
|
||||||
last_change_fatory_mode_millis = now_millis;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (this->light_sensor_->get_state() != new_light){
|
|
||||||
this->light_sensor_->publish_state(new_light);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void LD2410Component::handle_ack_data_(uint8_t *buffer, int len) {
|
|
||||||
ESP_LOGV(TAG, "Handling ACK DATA for COMMAND");
|
|
||||||
if (len < 10) {
|
|
||||||
ESP_LOGE(TAG, "Error with last command : incorrect length");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (buffer[0] != 0xFD || buffer[1] != 0xFC || buffer[2] != 0xFB || buffer[3] != 0xFA) { // check 4 frame start bytes
|
|
||||||
ESP_LOGE(TAG, "Error with last command : incorrect Header");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (buffer[COMMAND_STATUS] != 0x01) {
|
|
||||||
ESP_LOGE(TAG, "Error with last command : status != 0x01");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (this->two_byte_to_int_(buffer[8], buffer[9]) != 0x00) {
|
|
||||||
ESP_LOGE(TAG, "Error with last command , last buffer was: %u , %u", buffer[8], buffer[9]);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (buffer[COMMAND]) {
|
|
||||||
case lowbyte(CMD_ENABLE_CONF):
|
|
||||||
ESP_LOGV(TAG, "Handled Enable conf command");
|
|
||||||
break;
|
|
||||||
case lowbyte(CMD_DISABLE_CONF):
|
|
||||||
ESP_LOGV(TAG, "Handled Disabled conf command");
|
|
||||||
break;
|
|
||||||
case lowbyte(CMD_VERSION):
|
|
||||||
ESP_LOGV(TAG, "FW Version is: %u.%u.%u%u%u%u", buffer[13], buffer[12], buffer[17], buffer[16], buffer[15],
|
|
||||||
buffer[14]);
|
|
||||||
this->version_[0] = buffer[13];
|
|
||||||
this->version_[1] = buffer[12];
|
|
||||||
this->version_[2] = buffer[17];
|
|
||||||
this->version_[3] = buffer[16];
|
|
||||||
this->version_[4] = buffer[15];
|
|
||||||
this->version_[5] = buffer[14];
|
|
||||||
|
|
||||||
break;
|
|
||||||
case lowbyte(CMD_GATE_SENS):
|
|
||||||
ESP_LOGV(TAG, "Handled sensitivity command");
|
|
||||||
break;
|
|
||||||
case lowbyte(CMD_QUERY): // Query parameters response
|
|
||||||
{
|
|
||||||
if (buffer[10] != 0xAA)
|
|
||||||
return; // value head=0xAA
|
|
||||||
/*
|
|
||||||
Moving distance range: 13th byte
|
|
||||||
Still distance range: 14th byte
|
|
||||||
*/
|
|
||||||
// TODO
|
|
||||||
// maxMovingDistanceRange->publish_state(buffer[12]);
|
|
||||||
// maxStillDistanceRange->publish_state(buffer[13]);
|
|
||||||
/*
|
|
||||||
Moving Sensitivities: 15~23th bytes
|
|
||||||
Still Sensitivities: 24~32th bytes
|
|
||||||
*/
|
|
||||||
for (int i = 0; i < 9; i++) {
|
|
||||||
moving_sensitivities[i] = buffer[14 + i];
|
|
||||||
}
|
|
||||||
for (int i = 0; i < 9; i++) {
|
|
||||||
still_sensitivities[i] = buffer[23 + i];
|
|
||||||
}
|
|
||||||
/*
|
|
||||||
None Duration: 33~34th bytes
|
|
||||||
*/
|
|
||||||
// noneDuration->publish_state(this->two_byte_to_int_(buffer[32], buffer[33]));
|
|
||||||
} break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void LD2410Component::readline_(int readch, uint8_t *buffer, int len) {
|
|
||||||
static int pos = 0;
|
|
||||||
|
|
||||||
if (readch >= 0) {
|
|
||||||
if (pos < len - 1) {
|
|
||||||
buffer[pos++] = readch;
|
|
||||||
buffer[pos] = 0;
|
|
||||||
} else {
|
|
||||||
pos = 0;
|
|
||||||
}
|
|
||||||
if (pos >= 4) {
|
|
||||||
if (buffer[pos - 4] == 0xF8 && buffer[pos - 3] == 0xF7 && buffer[pos - 2] == 0xF6 && buffer[pos - 1] == 0xF5) {
|
|
||||||
ESP_LOGV(TAG, "Will handle Periodic Data");
|
|
||||||
this->handle_periodic_data_(buffer, pos);
|
|
||||||
pos = 0; // Reset position index ready for next time
|
|
||||||
} else if (buffer[pos - 4] == 0x04 && buffer[pos - 3] == 0x03 && buffer[pos - 2] == 0x02 &&
|
|
||||||
buffer[pos - 1] == 0x01) {
|
|
||||||
ESP_LOGV(TAG, "Will handle ACK Data");
|
|
||||||
this->handle_ack_data_(buffer, pos);
|
|
||||||
pos = 0; // Reset position index ready for next time
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void LD2410Component::set_config_mode_(bool enable) {
|
|
||||||
uint8_t cmd = enable ? CMD_ENABLE_CONF : CMD_DISABLE_CONF;
|
|
||||||
uint8_t cmd_value[2] = {0x01, 0x00};
|
|
||||||
this->send_command_(cmd, enable ? cmd_value : nullptr, 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
void LD2410Component::query_parameters_() { this->send_command_(CMD_QUERY, nullptr, 0); }
|
|
||||||
void LD2410Component::get_version_() { this->send_command_(CMD_VERSION, nullptr, 0); }
|
|
||||||
|
|
||||||
void LD2410Component::set_max_distances_timeout_(uint8_t max_moving_distance_range, uint8_t max_still_distance_range,
|
|
||||||
uint16_t timeout) {
|
|
||||||
uint8_t value[18] = {0x00,
|
|
||||||
0x00,
|
|
||||||
lowbyte(max_moving_distance_range),
|
|
||||||
highbyte(max_moving_distance_range),
|
|
||||||
0x00,
|
|
||||||
0x00,
|
|
||||||
0x01,
|
|
||||||
0x00,
|
|
||||||
lowbyte(max_still_distance_range),
|
|
||||||
highbyte(max_still_distance_range),
|
|
||||||
0x00,
|
|
||||||
0x00,
|
|
||||||
0x02,
|
|
||||||
0x00,
|
|
||||||
lowbyte(timeout),
|
|
||||||
highbyte(timeout),
|
|
||||||
0x00,
|
|
||||||
0x00};
|
|
||||||
this->send_command_(CMD_MAXDIST_DURATION, value, 18);
|
|
||||||
this->query_parameters_();
|
|
||||||
}
|
|
||||||
void LD2410Component::set_gate_threshold_(uint8_t gate, uint8_t motionsens, uint8_t stillsens) {
|
|
||||||
// reference
|
|
||||||
// https://drive.google.com/drive/folders/1p4dhbEJA3YubyIjIIC7wwVsSo8x29Fq-?spm=a2g0o.detail.1000023.17.93465697yFwVxH
|
|
||||||
// Send data: configure the motion sensitivity of distance gate 3 to 40, and the static sensitivity of 40
|
|
||||||
// 00 00 (gate)
|
|
||||||
// 03 00 00 00 (gate number)
|
|
||||||
// 01 00 (motion sensitivity)
|
|
||||||
// 28 00 00 00 (value)
|
|
||||||
// 02 00 (still sensitivtiy)
|
|
||||||
// 28 00 00 00 (value)
|
|
||||||
uint8_t value[18] = {0x00, 0x00, lowbyte(gate), highbyte(gate), 0x00, 0x00,
|
|
||||||
0x01, 0x00, lowbyte(motionsens), highbyte(motionsens), 0x00, 0x00,
|
|
||||||
0x02, 0x00, lowbyte(stillsens), highbyte(stillsens), 0x00, 0x00};
|
|
||||||
this->send_command_(CMD_GATE_SENS, value, 18);
|
|
||||||
}
|
|
||||||
|
|
||||||
void LD2410Component::factoryReset()
|
|
||||||
{
|
|
||||||
this->set_config_mode_(true);
|
|
||||||
|
|
||||||
uint8_t cmd = 0x00A2;
|
|
||||||
this -> send_command_(cmd, nullptr, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
void LD2410Component::reboot()
|
|
||||||
{
|
|
||||||
ESP_LOGD(TAG, "reboot ld2410b...");
|
|
||||||
this->set_config_mode_(true);
|
|
||||||
|
|
||||||
uint8_t cmd = 0x00A3;
|
|
||||||
this -> send_command_(cmd, nullptr, 0);
|
|
||||||
// not need to exit config mode because the ld2410 will reboot automatically
|
|
||||||
}
|
|
||||||
|
|
||||||
void LD2410Component::ble_control(bool enable)
|
|
||||||
{
|
|
||||||
this->set_config_mode_(true);
|
|
||||||
uint8_t CMD_BLE_CONF = 0x00A4;
|
|
||||||
uint8_t CMD_REBOOT = 0x00A3;
|
|
||||||
|
|
||||||
uint8_t cmd_value[2] = {0x00, 0x00};
|
|
||||||
|
|
||||||
if (enable){
|
|
||||||
cmd_value[0] = 0x01;
|
|
||||||
|
|
||||||
ESP_LOGD(TAG, "Enable BLE...");
|
|
||||||
}else{
|
|
||||||
ESP_LOGD(TAG, "Disable BLE...");
|
|
||||||
}
|
|
||||||
|
|
||||||
this -> send_command_(CMD_BLE_CONF, cmd_value, 2);
|
|
||||||
|
|
||||||
//this -> send_command_(CMD_REBOOT, nullptr, 0);
|
|
||||||
|
|
||||||
this->set_config_mode_(false);
|
|
||||||
|
|
||||||
this -> reboot();
|
|
||||||
}
|
|
||||||
|
|
||||||
void LD2410Component::factory_mode(bool enable){
|
|
||||||
uint8_t CMD_FACTORY_ON = 0x0062;
|
|
||||||
uint8_t CMD_FACTORY_OFF = 0x0063;
|
|
||||||
this->set_config_mode_(true);
|
|
||||||
|
|
||||||
uint8_t cmd = enable ? CMD_FACTORY_ON: CMD_FACTORY_OFF;
|
|
||||||
|
|
||||||
this->send_command_(cmd, nullptr, 0);
|
|
||||||
|
|
||||||
this->set_config_mode_(false);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace ld2410
|
|
||||||
} // namespace esphome
|
|
||||||
|
|
@ -1,158 +0,0 @@
|
||||||
#pragma once
|
|
||||||
#include "esphome/core/defines.h"
|
|
||||||
#include "esphome/core/component.h"
|
|
||||||
#ifdef USE_BINARY_SENSOR
|
|
||||||
#include "esphome/components/binary_sensor/binary_sensor.h"
|
|
||||||
#endif
|
|
||||||
#ifdef USE_SENSOR
|
|
||||||
#include "esphome/components/sensor/sensor.h"
|
|
||||||
#endif
|
|
||||||
#include "esphome/components/uart/uart.h"
|
|
||||||
#include "esphome/core/automation.h"
|
|
||||||
#include "esphome/core/helpers.h"
|
|
||||||
|
|
||||||
namespace esphome {
|
|
||||||
namespace ld2410 {
|
|
||||||
|
|
||||||
#define CHECK_BIT(var, pos) (((var) >> (pos)) & 1)
|
|
||||||
|
|
||||||
// Commands
|
|
||||||
static const uint8_t CMD_ENABLE_CONF = 0x00FF;
|
|
||||||
static const uint8_t CMD_DISABLE_CONF = 0x00FE;
|
|
||||||
static const uint8_t CMD_MAXDIST_DURATION = 0x0060;
|
|
||||||
static const uint8_t CMD_QUERY = 0x0061;
|
|
||||||
static const uint8_t CMD_GATE_SENS = 0x0064;
|
|
||||||
static const uint8_t CMD_VERSION = 0x00A0;
|
|
||||||
|
|
||||||
// Commands values
|
|
||||||
static const uint8_t CMD_MAX_MOVE_VALUE = 0x0000;
|
|
||||||
static const uint8_t CMD_MAX_STILL_VALUE = 0x0001;
|
|
||||||
static const uint8_t CMD_DURATION_VALUE = 0x0002;
|
|
||||||
// Command Header & Footer
|
|
||||||
static const uint8_t CMD_FRAME_HEADER[4] = {0xFD, 0xFC, 0xFB, 0xFA};
|
|
||||||
static const uint8_t CMD_FRAME_END[4] = {0x04, 0x03, 0x02, 0x01};
|
|
||||||
// Data Header & Footer
|
|
||||||
static const uint8_t DATA_FRAME_HEADER[4] = {0xF4, 0xF3, 0xF2, 0xF1};
|
|
||||||
static const uint8_t DATA_FRAME_END[4] = {0xF8, 0xF7, 0xF6, 0xF5};
|
|
||||||
/*
|
|
||||||
Data Type: 6th byte
|
|
||||||
Target states: 9th byte
|
|
||||||
Moving target distance: 10~11th bytes
|
|
||||||
Moving target energy: 12th byte
|
|
||||||
Still target distance: 13~14th bytes
|
|
||||||
Still target energy: 15th byte
|
|
||||||
Detect distance: 16~17th bytes
|
|
||||||
*/
|
|
||||||
enum PeriodicDataStructure : uint8_t {
|
|
||||||
DATA_TYPES = 5,
|
|
||||||
TARGET_STATES = 8,
|
|
||||||
MOVING_TARGET_LOW = 9,
|
|
||||||
MOVING_TARGET_HIGH = 10,
|
|
||||||
MOVING_ENERGY = 11,
|
|
||||||
STILL_TARGET_LOW = 12,
|
|
||||||
STILL_TARGET_HIGH = 13,
|
|
||||||
STILL_ENERGY = 14,
|
|
||||||
DETECT_DISTANCE_LOW = 15,
|
|
||||||
DETECT_DISTANCE_HIGH = 16,
|
|
||||||
};
|
|
||||||
// 上报数据的固定结构。(23年3月13日_16时54分_)
|
|
||||||
enum PeriodicDataValue : uint8_t { HEAD = 0XAA, END = 0x55, CHECK = 0x00 };
|
|
||||||
|
|
||||||
enum AckDataStructure : uint8_t { COMMAND = 6, COMMAND_STATUS = 7 };
|
|
||||||
|
|
||||||
// char cmd[2] = {enable ? 0xFF : 0xFE, 0x00};
|
|
||||||
class LD2410Component : public Component, public uart::UARTDevice {
|
|
||||||
#ifdef USE_SENSOR
|
|
||||||
SUB_SENSOR(moving_target_distance)
|
|
||||||
SUB_SENSOR(still_target_distance)
|
|
||||||
SUB_SENSOR(moving_target_energy)
|
|
||||||
SUB_SENSOR(still_target_energy)
|
|
||||||
SUB_SENSOR(detection_distance)
|
|
||||||
SUB_SENSOR(light)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
public:
|
|
||||||
void setup() override;
|
|
||||||
void dump_config() override;
|
|
||||||
void loop() override;
|
|
||||||
|
|
||||||
#ifdef USE_BINARY_SENSOR
|
|
||||||
void set_target_sensor(binary_sensor::BinarySensor *sens) { this->target_binary_sensor_ = sens; };
|
|
||||||
void set_moving_target_sensor(binary_sensor::BinarySensor *sens) { this->moving_binary_sensor_ = sens; };
|
|
||||||
void set_still_target_sensor(binary_sensor::BinarySensor *sens) { this->still_binary_sensor_ = sens; };
|
|
||||||
#endif
|
|
||||||
|
|
||||||
void set_timeout(uint16_t value) { this->timeout_ = value; };
|
|
||||||
void set_max_move_distance(uint8_t value) { this->max_move_distance_ = value; };
|
|
||||||
void set_max_still_distance(uint8_t value) { this->max_still_distance_ = value; };
|
|
||||||
void set_range_config(int rg0_move, int rg0_still, int rg1_move, int rg1_still, int rg2_move, int rg2_still,
|
|
||||||
int rg3_move, int rg3_still, int rg4_move, int rg4_still, int rg5_move, int rg5_still,
|
|
||||||
int rg6_move, int rg6_still, int rg7_move, int rg7_still, int rg8_move, int rg8_still) {
|
|
||||||
this->rg0_move_threshold_ = rg0_move;
|
|
||||||
this->rg0_still_threshold_ = rg0_still;
|
|
||||||
this->rg1_move_threshold_ = rg1_move;
|
|
||||||
this->rg1_still_threshold_ = rg1_still;
|
|
||||||
this->rg2_move_threshold_ = rg2_move;
|
|
||||||
this->rg2_still_threshold_ = rg2_still;
|
|
||||||
this->rg3_move_threshold_ = rg3_move;
|
|
||||||
this->rg3_still_threshold_ = rg3_still;
|
|
||||||
this->rg4_move_threshold_ = rg4_move;
|
|
||||||
this->rg4_still_threshold_ = rg4_still;
|
|
||||||
this->rg5_move_threshold_ = rg5_move;
|
|
||||||
this->rg5_still_threshold_ = rg5_still;
|
|
||||||
this->rg6_move_threshold_ = rg6_move;
|
|
||||||
this->rg6_still_threshold_ = rg6_still;
|
|
||||||
this->rg7_move_threshold_ = rg7_move;
|
|
||||||
this->rg7_still_threshold_ = rg7_still;
|
|
||||||
this->rg8_move_threshold_ = rg8_move;
|
|
||||||
this->rg8_still_threshold_ = rg8_still;
|
|
||||||
};
|
|
||||||
int moving_sensitivities[9] = {0};
|
|
||||||
int still_sensitivities[9] = {0};
|
|
||||||
|
|
||||||
int32_t last_periodic_millis = millis();
|
|
||||||
|
|
||||||
int32_t last_change_fatory_mode_millis = 0;
|
|
||||||
|
|
||||||
void factoryReset();
|
|
||||||
|
|
||||||
void reboot();
|
|
||||||
|
|
||||||
void ble_control(bool enable);
|
|
||||||
|
|
||||||
void factory_mode(bool enable);
|
|
||||||
|
|
||||||
protected:
|
|
||||||
#ifdef USE_BINARY_SENSOR
|
|
||||||
binary_sensor::BinarySensor *target_binary_sensor_{nullptr};
|
|
||||||
binary_sensor::BinarySensor *moving_binary_sensor_{nullptr};
|
|
||||||
binary_sensor::BinarySensor *still_binary_sensor_{nullptr};
|
|
||||||
#endif
|
|
||||||
|
|
||||||
std::vector<uint8_t> rx_buffer_;
|
|
||||||
int two_byte_to_int_(char firstbyte, char secondbyte) { return (int16_t)(secondbyte << 8) + firstbyte; }
|
|
||||||
void send_command_(uint8_t command_str, uint8_t *command_value, int command_value_len);
|
|
||||||
|
|
||||||
void set_max_distances_timeout_(uint8_t max_moving_distance_range, uint8_t max_still_distance_range,
|
|
||||||
uint16_t timeout);
|
|
||||||
void set_gate_threshold_(uint8_t gate, uint8_t motionsens, uint8_t stillsens);
|
|
||||||
void set_config_mode_(bool enable);
|
|
||||||
void handle_periodic_data_(uint8_t *buffer, int len);
|
|
||||||
void handle_ack_data_(uint8_t *buffer, int len);
|
|
||||||
void readline_(int readch, uint8_t *buffer, int len);
|
|
||||||
void query_parameters_();
|
|
||||||
void get_version_();
|
|
||||||
|
|
||||||
uint16_t timeout_;
|
|
||||||
uint8_t max_move_distance_;
|
|
||||||
uint8_t max_still_distance_;
|
|
||||||
|
|
||||||
uint8_t version_[6];
|
|
||||||
uint8_t rg0_move_threshold_, rg0_still_threshold_, rg1_move_threshold_, rg1_still_threshold_, rg2_move_threshold_,
|
|
||||||
rg2_still_threshold_, rg3_move_threshold_, rg3_still_threshold_, rg4_move_threshold_, rg4_still_threshold_,
|
|
||||||
rg5_move_threshold_, rg5_still_threshold_, rg6_move_threshold_, rg6_still_threshold_, rg7_move_threshold_,
|
|
||||||
rg7_still_threshold_, rg8_move_threshold_, rg8_still_threshold_;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace ld2410
|
|
||||||
} // namespace esphome
|
|
||||||
|
|
@ -1,70 +0,0 @@
|
||||||
import esphome.codegen as cg
|
|
||||||
from esphome.components import sensor
|
|
||||||
import esphome.config_validation as cv
|
|
||||||
from esphome.const import (
|
|
||||||
DEVICE_CLASS_DISTANCE,
|
|
||||||
DEVICE_CLASS_ENERGY,
|
|
||||||
UNIT_CENTIMETER,
|
|
||||||
UNIT_PERCENT,
|
|
||||||
STATE_CLASS_NONE,
|
|
||||||
ICON_BRIGHTNESS_5,
|
|
||||||
UNIT_EMPTY,
|
|
||||||
UNIT_LUX,
|
|
||||||
|
|
||||||
)
|
|
||||||
from . import CONF_LD2410_ID, LD2410Component
|
|
||||||
|
|
||||||
DEPENDENCIES = ["ld2410"]
|
|
||||||
CONF_MOVING_DISTANCE = "moving_distance"
|
|
||||||
CONF_STILL_DISTANCE = "still_distance"
|
|
||||||
CONF_MOVING_ENERGY = "moving_energy"
|
|
||||||
CONF_STILL_ENERGY = "still_energy"
|
|
||||||
CONF_DETECTION_DISTANCE = "detection_distance"
|
|
||||||
CONF_LIGHT = "light"
|
|
||||||
|
|
||||||
CONFIG_SCHEMA = {
|
|
||||||
cv.GenerateID(CONF_LD2410_ID): cv.use_id(LD2410Component),
|
|
||||||
cv.Optional(CONF_MOVING_DISTANCE): sensor.sensor_schema(
|
|
||||||
device_class=DEVICE_CLASS_DISTANCE, unit_of_measurement=UNIT_CENTIMETER
|
|
||||||
),
|
|
||||||
cv.Optional(CONF_STILL_DISTANCE): sensor.sensor_schema(
|
|
||||||
device_class=DEVICE_CLASS_DISTANCE, unit_of_measurement=UNIT_CENTIMETER
|
|
||||||
),
|
|
||||||
cv.Optional(CONF_MOVING_ENERGY): sensor.sensor_schema(
|
|
||||||
device_class=DEVICE_CLASS_ENERGY, unit_of_measurement=UNIT_PERCENT
|
|
||||||
),
|
|
||||||
cv.Optional(CONF_STILL_ENERGY): sensor.sensor_schema(
|
|
||||||
device_class=DEVICE_CLASS_ENERGY, unit_of_measurement=UNIT_PERCENT
|
|
||||||
),
|
|
||||||
cv.Optional(CONF_DETECTION_DISTANCE): sensor.sensor_schema(
|
|
||||||
device_class=DEVICE_CLASS_DISTANCE, unit_of_measurement=UNIT_CENTIMETER
|
|
||||||
),
|
|
||||||
|
|
||||||
cv.Optional(CONF_LIGHT): sensor.sensor_schema(
|
|
||||||
device_class=STATE_CLASS_NONE,
|
|
||||||
icon=ICON_BRIGHTNESS_5,
|
|
||||||
unit_of_measurement=UNIT_PERCENT
|
|
||||||
),
|
|
||||||
}
|
|
||||||
|
|
||||||
async def to_code(config):
|
|
||||||
ld2410_component = await cg.get_variable(config[CONF_LD2410_ID])
|
|
||||||
if CONF_MOVING_DISTANCE in config:
|
|
||||||
sens = await sensor.new_sensor(config[CONF_MOVING_DISTANCE])
|
|
||||||
cg.add(ld2410_component.set_moving_target_distance_sensor(sens))
|
|
||||||
if CONF_STILL_DISTANCE in config:
|
|
||||||
sens = await sensor.new_sensor(config[CONF_STILL_DISTANCE])
|
|
||||||
cg.add(ld2410_component.set_still_target_distance_sensor(sens))
|
|
||||||
if CONF_MOVING_ENERGY in config:
|
|
||||||
sens = await sensor.new_sensor(config[CONF_MOVING_ENERGY])
|
|
||||||
cg.add(ld2410_component.set_moving_target_energy_sensor(sens))
|
|
||||||
if CONF_STILL_ENERGY in config:
|
|
||||||
sens = await sensor.new_sensor(config[CONF_STILL_ENERGY])
|
|
||||||
cg.add(ld2410_component.set_still_target_energy_sensor(sens))
|
|
||||||
if CONF_DETECTION_DISTANCE in config:
|
|
||||||
sens = await sensor.new_sensor(config[CONF_DETECTION_DISTANCE])
|
|
||||||
cg.add(ld2410_component.set_detection_distance_sensor(sens))
|
|
||||||
|
|
||||||
if CONF_LIGHT in config:
|
|
||||||
sens = await sensor.new_sensor(config[CONF_LIGHT])
|
|
||||||
cg.add(ld2410_component.set_light_sensor(sens))
|
|
||||||
|
|
@ -1,351 +0,0 @@
|
||||||
from typing import Optional
|
|
||||||
|
|
||||||
import esphome.codegen as cg
|
|
||||||
import esphome.config_validation as cv
|
|
||||||
import esphome.final_validate as fv
|
|
||||||
from esphome.yaml_util import make_data_base
|
|
||||||
from esphome import pins, automation
|
|
||||||
from esphome.const import (
|
|
||||||
CONF_BAUD_RATE,
|
|
||||||
CONF_ID,
|
|
||||||
CONF_NUMBER,
|
|
||||||
CONF_RX_PIN,
|
|
||||||
CONF_TX_PIN,
|
|
||||||
CONF_UART_ID,
|
|
||||||
CONF_DATA,
|
|
||||||
CONF_RX_BUFFER_SIZE,
|
|
||||||
CONF_INVERTED,
|
|
||||||
CONF_INVERT,
|
|
||||||
CONF_TRIGGER_ID,
|
|
||||||
CONF_SEQUENCE,
|
|
||||||
CONF_TIMEOUT,
|
|
||||||
CONF_DEBUG,
|
|
||||||
CONF_DIRECTION,
|
|
||||||
CONF_AFTER,
|
|
||||||
CONF_BYTES,
|
|
||||||
CONF_DELIMITER,
|
|
||||||
CONF_DUMMY_RECEIVER,
|
|
||||||
CONF_DUMMY_RECEIVER_ID,
|
|
||||||
CONF_LAMBDA,
|
|
||||||
)
|
|
||||||
from esphome.core import CORE
|
|
||||||
|
|
||||||
CODEOWNERS = ["@esphome/core"]
|
|
||||||
uart_ns = cg.esphome_ns.namespace("uart")
|
|
||||||
UARTComponent = uart_ns.class_("UARTComponent")
|
|
||||||
|
|
||||||
IDFUARTComponent = uart_ns.class_("IDFUARTComponent", UARTComponent, cg.Component)
|
|
||||||
ESP32ArduinoUARTComponent = uart_ns.class_(
|
|
||||||
"ESP32ArduinoUARTComponent", UARTComponent, cg.Component
|
|
||||||
)
|
|
||||||
ESP8266UartComponent = uart_ns.class_(
|
|
||||||
"ESP8266UartComponent", UARTComponent, cg.Component
|
|
||||||
)
|
|
||||||
RP2040UartComponent = uart_ns.class_("RP2040UartComponent", UARTComponent, cg.Component)
|
|
||||||
|
|
||||||
UARTDevice = uart_ns.class_("UARTDevice")
|
|
||||||
UARTWriteAction = uart_ns.class_("UARTWriteAction", automation.Action)
|
|
||||||
UARTDebugger = uart_ns.class_("UARTDebugger", cg.Component, automation.Action)
|
|
||||||
UARTDummyReceiver = uart_ns.class_("UARTDummyReceiver", cg.Component)
|
|
||||||
MULTI_CONF = True
|
|
||||||
|
|
||||||
|
|
||||||
def validate_raw_data(value):
|
|
||||||
if isinstance(value, str):
|
|
||||||
return value.encode("utf-8")
|
|
||||||
if isinstance(value, str):
|
|
||||||
return value
|
|
||||||
if isinstance(value, list):
|
|
||||||
return cv.Schema([cv.hex_uint8_t])(value)
|
|
||||||
raise cv.Invalid(
|
|
||||||
"data must either be a string wrapped in quotes or a list of bytes"
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def validate_rx_pin(value):
|
|
||||||
value = pins.internal_gpio_input_pin_schema(value)
|
|
||||||
if CORE.is_esp8266 and value[CONF_NUMBER] >= 16:
|
|
||||||
raise cv.Invalid("Pins GPIO16 and GPIO17 cannot be used as RX pins on ESP8266.")
|
|
||||||
return value
|
|
||||||
|
|
||||||
|
|
||||||
def validate_invert_esp32(config):
|
|
||||||
if (
|
|
||||||
CORE.is_esp32
|
|
||||||
and CONF_TX_PIN in config
|
|
||||||
and CONF_RX_PIN in config
|
|
||||||
and config[CONF_TX_PIN][CONF_INVERTED] != config[CONF_RX_PIN][CONF_INVERTED]
|
|
||||||
):
|
|
||||||
raise cv.Invalid(
|
|
||||||
"Different invert values for TX and RX pin are not (yet) supported for ESP32."
|
|
||||||
)
|
|
||||||
return config
|
|
||||||
|
|
||||||
|
|
||||||
def _uart_declare_type(value):
|
|
||||||
if CORE.is_esp8266:
|
|
||||||
return cv.declare_id(ESP8266UartComponent)(value)
|
|
||||||
if CORE.is_esp32:
|
|
||||||
if CORE.using_arduino:
|
|
||||||
return cv.declare_id(ESP32ArduinoUARTComponent)(value)
|
|
||||||
if CORE.using_esp_idf:
|
|
||||||
return cv.declare_id(IDFUARTComponent)(value)
|
|
||||||
if CORE.is_rp2040:
|
|
||||||
return cv.declare_id(RP2040UartComponent)(value)
|
|
||||||
raise NotImplementedError
|
|
||||||
|
|
||||||
|
|
||||||
UARTParityOptions = uart_ns.enum("UARTParityOptions")
|
|
||||||
UART_PARITY_OPTIONS = {
|
|
||||||
"NONE": UARTParityOptions.UART_CONFIG_PARITY_NONE,
|
|
||||||
"EVEN": UARTParityOptions.UART_CONFIG_PARITY_EVEN,
|
|
||||||
"ODD": UARTParityOptions.UART_CONFIG_PARITY_ODD,
|
|
||||||
}
|
|
||||||
|
|
||||||
CONF_STOP_BITS = "stop_bits"
|
|
||||||
CONF_DATA_BITS = "data_bits"
|
|
||||||
CONF_PARITY = "parity"
|
|
||||||
|
|
||||||
UARTDirection = uart_ns.enum("UARTDirection")
|
|
||||||
UART_DIRECTIONS = {
|
|
||||||
"RX": UARTDirection.UART_DIRECTION_RX,
|
|
||||||
"TX": UARTDirection.UART_DIRECTION_TX,
|
|
||||||
"BOTH": UARTDirection.UART_DIRECTION_BOTH,
|
|
||||||
}
|
|
||||||
|
|
||||||
# The reason for having CONF_BYTES at 150 by default:
|
|
||||||
#
|
|
||||||
# The log message buffer size is 512 bytes by default. About 35 bytes are
|
|
||||||
# used for the log prefix. That leaves us with 477 bytes for logging data.
|
|
||||||
# The default log output is hex, which uses 3 characters per represented
|
|
||||||
# byte (2 hex chars + 1 separator). That means that 477 / 3 = 159 bytes
|
|
||||||
# can be represented in a single log line. Using 150, because people love
|
|
||||||
# round numbers.
|
|
||||||
AFTER_DEFAULTS = {CONF_BYTES: 150, CONF_TIMEOUT: "100ms"}
|
|
||||||
|
|
||||||
# By default, log in hex format when no specific sequence is provided.
|
|
||||||
DEFAULT_DEBUG_OUTPUT = "UARTDebug::log_hex(direction, bytes, ':');"
|
|
||||||
DEFAULT_SEQUENCE = [{CONF_LAMBDA: make_data_base(DEFAULT_DEBUG_OUTPUT)}]
|
|
||||||
|
|
||||||
|
|
||||||
def maybe_empty_debug(value):
|
|
||||||
if value is None:
|
|
||||||
value = {}
|
|
||||||
return DEBUG_SCHEMA(value)
|
|
||||||
|
|
||||||
|
|
||||||
DEBUG_SCHEMA = cv.Schema(
|
|
||||||
{
|
|
||||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(UARTDebugger),
|
|
||||||
cv.Optional(CONF_DIRECTION, default="BOTH"): cv.enum(
|
|
||||||
UART_DIRECTIONS, upper=True
|
|
||||||
),
|
|
||||||
cv.Optional(CONF_AFTER, default=AFTER_DEFAULTS): cv.Schema(
|
|
||||||
{
|
|
||||||
cv.Optional(
|
|
||||||
CONF_BYTES, default=AFTER_DEFAULTS[CONF_BYTES]
|
|
||||||
): cv.validate_bytes,
|
|
||||||
cv.Optional(
|
|
||||||
CONF_TIMEOUT, default=AFTER_DEFAULTS[CONF_TIMEOUT]
|
|
||||||
): cv.positive_time_period_milliseconds,
|
|
||||||
cv.Optional(CONF_DELIMITER): cv.templatable(validate_raw_data),
|
|
||||||
}
|
|
||||||
),
|
|
||||||
cv.Optional(
|
|
||||||
CONF_SEQUENCE, default=DEFAULT_SEQUENCE
|
|
||||||
): automation.validate_automation(),
|
|
||||||
cv.Optional(CONF_DUMMY_RECEIVER, default=False): cv.boolean,
|
|
||||||
cv.GenerateID(CONF_DUMMY_RECEIVER_ID): cv.declare_id(UARTDummyReceiver),
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
CONFIG_SCHEMA = cv.All(
|
|
||||||
cv.Schema(
|
|
||||||
{
|
|
||||||
cv.GenerateID(): _uart_declare_type,
|
|
||||||
cv.Required(CONF_BAUD_RATE): cv.int_range(min=1),
|
|
||||||
cv.Optional(CONF_TX_PIN): pins.internal_gpio_output_pin_schema,
|
|
||||||
cv.Optional(CONF_RX_PIN): validate_rx_pin,
|
|
||||||
cv.Optional(CONF_RX_BUFFER_SIZE, default=256): cv.validate_bytes,
|
|
||||||
cv.Optional(CONF_STOP_BITS, default=1): cv.one_of(1, 2, int=True),
|
|
||||||
cv.Optional(CONF_DATA_BITS, default=8): cv.int_range(min=5, max=8),
|
|
||||||
cv.Optional(CONF_PARITY, default="NONE"): cv.enum(
|
|
||||||
UART_PARITY_OPTIONS, upper=True
|
|
||||||
),
|
|
||||||
cv.Optional(CONF_INVERT): cv.invalid(
|
|
||||||
"This option has been removed. Please instead use invert in the tx/rx pin schemas."
|
|
||||||
),
|
|
||||||
cv.Optional(CONF_DEBUG): maybe_empty_debug,
|
|
||||||
}
|
|
||||||
).extend(cv.COMPONENT_SCHEMA),
|
|
||||||
cv.has_at_least_one_key(CONF_TX_PIN, CONF_RX_PIN),
|
|
||||||
validate_invert_esp32,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
async def debug_to_code(config, parent):
|
|
||||||
trigger = cg.new_Pvariable(config[CONF_TRIGGER_ID], parent)
|
|
||||||
await cg.register_component(trigger, config)
|
|
||||||
for action in config[CONF_SEQUENCE]:
|
|
||||||
await automation.build_automation(
|
|
||||||
trigger,
|
|
||||||
[(UARTDirection, "direction"), (cg.std_vector.template(cg.uint8), "bytes")],
|
|
||||||
action,
|
|
||||||
)
|
|
||||||
cg.add(trigger.set_direction(config[CONF_DIRECTION]))
|
|
||||||
after = config[CONF_AFTER]
|
|
||||||
cg.add(trigger.set_after_bytes(after[CONF_BYTES]))
|
|
||||||
cg.add(trigger.set_after_timeout(after[CONF_TIMEOUT]))
|
|
||||||
if CONF_DELIMITER in after:
|
|
||||||
data = after[CONF_DELIMITER]
|
|
||||||
if isinstance(data, bytes):
|
|
||||||
data = list(data)
|
|
||||||
for byte in after[CONF_DELIMITER]:
|
|
||||||
cg.add(trigger.add_delimiter_byte(byte))
|
|
||||||
if config[CONF_DUMMY_RECEIVER]:
|
|
||||||
dummy = cg.new_Pvariable(config[CONF_DUMMY_RECEIVER_ID], parent)
|
|
||||||
await cg.register_component(dummy, {})
|
|
||||||
cg.add_define("USE_UART_DEBUGGER")
|
|
||||||
|
|
||||||
|
|
||||||
async def to_code(config):
|
|
||||||
cg.add_global(uart_ns.using)
|
|
||||||
var = cg.new_Pvariable(config[CONF_ID])
|
|
||||||
await cg.register_component(var, config)
|
|
||||||
|
|
||||||
cg.add(var.set_baud_rate(config[CONF_BAUD_RATE]))
|
|
||||||
|
|
||||||
if CONF_TX_PIN in config:
|
|
||||||
tx_pin = await cg.gpio_pin_expression(config[CONF_TX_PIN])
|
|
||||||
cg.add(var.set_tx_pin(tx_pin))
|
|
||||||
if CONF_RX_PIN in config:
|
|
||||||
rx_pin = await cg.gpio_pin_expression(config[CONF_RX_PIN])
|
|
||||||
cg.add(var.set_rx_pin(rx_pin))
|
|
||||||
cg.add(var.set_rx_buffer_size(config[CONF_RX_BUFFER_SIZE]))
|
|
||||||
cg.add(var.set_stop_bits(config[CONF_STOP_BITS]))
|
|
||||||
cg.add(var.set_data_bits(config[CONF_DATA_BITS]))
|
|
||||||
cg.add(var.set_parity(config[CONF_PARITY]))
|
|
||||||
|
|
||||||
if CONF_DEBUG in config:
|
|
||||||
await debug_to_code(config[CONF_DEBUG], var)
|
|
||||||
|
|
||||||
|
|
||||||
# A schema to use for all UART devices, all UART integrations must extend this!
|
|
||||||
UART_DEVICE_SCHEMA = cv.Schema(
|
|
||||||
{
|
|
||||||
cv.GenerateID(CONF_UART_ID): cv.use_id(UARTComponent),
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
KEY_UART_DEVICES = "uart_devices"
|
|
||||||
|
|
||||||
|
|
||||||
def final_validate_device_schema(
|
|
||||||
name: str,
|
|
||||||
*,
|
|
||||||
baud_rate: Optional[int] = None,
|
|
||||||
require_tx: bool = False,
|
|
||||||
require_rx: bool = False,
|
|
||||||
parity: Optional[str] = None,
|
|
||||||
stop_bits: Optional[int] = None,
|
|
||||||
):
|
|
||||||
def validate_baud_rate(value):
|
|
||||||
if value != baud_rate:
|
|
||||||
raise cv.Invalid(
|
|
||||||
f"Component {name} requires baud rate {baud_rate} for the uart bus"
|
|
||||||
)
|
|
||||||
return value
|
|
||||||
|
|
||||||
def validate_pin(opt, device):
|
|
||||||
def validator(value):
|
|
||||||
if opt in device:
|
|
||||||
raise cv.Invalid(
|
|
||||||
f"The uart {opt} is used both by {name} and {device[opt]}, "
|
|
||||||
f"but can only be used by one. Please create a new uart bus for {name}."
|
|
||||||
)
|
|
||||||
device[opt] = name
|
|
||||||
return value
|
|
||||||
|
|
||||||
return validator
|
|
||||||
|
|
||||||
def validate_parity(value):
|
|
||||||
if value != parity:
|
|
||||||
raise cv.Invalid(
|
|
||||||
f"Component {name} requires parity {parity} for the uart bus"
|
|
||||||
)
|
|
||||||
return value
|
|
||||||
|
|
||||||
def validate_stop_bits(value):
|
|
||||||
if value != stop_bits:
|
|
||||||
raise cv.Invalid(
|
|
||||||
f"Component {name} requires stop bits {stop_bits} for the uart bus"
|
|
||||||
)
|
|
||||||
return value
|
|
||||||
|
|
||||||
def validate_hub(hub_config):
|
|
||||||
hub_schema = {}
|
|
||||||
uart_id = hub_config[CONF_ID]
|
|
||||||
devices = fv.full_config.get().data.setdefault(KEY_UART_DEVICES, {})
|
|
||||||
device = devices.setdefault(uart_id, {})
|
|
||||||
|
|
||||||
if require_tx:
|
|
||||||
hub_schema[
|
|
||||||
cv.Required(
|
|
||||||
CONF_TX_PIN,
|
|
||||||
msg=f"Component {name} requires this uart bus to declare a tx_pin",
|
|
||||||
)
|
|
||||||
] = validate_pin(CONF_TX_PIN, device)
|
|
||||||
if require_rx:
|
|
||||||
hub_schema[
|
|
||||||
cv.Required(
|
|
||||||
CONF_RX_PIN,
|
|
||||||
msg=f"Component {name} requires this uart bus to declare a rx_pin",
|
|
||||||
)
|
|
||||||
] = validate_pin(CONF_RX_PIN, device)
|
|
||||||
if baud_rate is not None:
|
|
||||||
hub_schema[cv.Required(CONF_BAUD_RATE)] = validate_baud_rate
|
|
||||||
if parity is not None:
|
|
||||||
hub_schema[cv.Required(CONF_PARITY)] = validate_parity
|
|
||||||
if stop_bits is not None:
|
|
||||||
hub_schema[cv.Required(CONF_STOP_BITS)] = validate_stop_bits
|
|
||||||
return cv.Schema(hub_schema, extra=cv.ALLOW_EXTRA)(hub_config)
|
|
||||||
|
|
||||||
return cv.Schema(
|
|
||||||
{cv.Required(CONF_UART_ID): fv.id_declaration_match_schema(validate_hub)},
|
|
||||||
extra=cv.ALLOW_EXTRA,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
async def register_uart_device(var, config):
|
|
||||||
"""Register a UART device, setting up all the internal values.
|
|
||||||
|
|
||||||
This is a coroutine, you need to await it with a 'yield' expression!
|
|
||||||
"""
|
|
||||||
parent = await cg.get_variable(config[CONF_UART_ID])
|
|
||||||
cg.add(var.set_uart_parent(parent))
|
|
||||||
|
|
||||||
|
|
||||||
@automation.register_action(
|
|
||||||
"uart.write",
|
|
||||||
UARTWriteAction,
|
|
||||||
cv.maybe_simple_value(
|
|
||||||
{
|
|
||||||
cv.GenerateID(): cv.use_id(UARTComponent),
|
|
||||||
cv.Required(CONF_DATA): cv.templatable(validate_raw_data),
|
|
||||||
},
|
|
||||||
key=CONF_DATA,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
async def uart_write_to_code(config, action_id, template_arg, args):
|
|
||||||
var = cg.new_Pvariable(action_id, template_arg)
|
|
||||||
await cg.register_parented(var, config[CONF_ID])
|
|
||||||
data = config[CONF_DATA]
|
|
||||||
if isinstance(data, bytes):
|
|
||||||
data = list(data)
|
|
||||||
|
|
||||||
if cg.is_template(data):
|
|
||||||
templ = await cg.templatable(data, args, cg.std_vector.template(cg.uint8))
|
|
||||||
cg.add(var.set_data_template(templ))
|
|
||||||
else:
|
|
||||||
cg.add(var.set_data_static(data))
|
|
||||||
return var
|
|
||||||
|
|
@ -1,38 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "uart.h"
|
|
||||||
#include "esphome/core/automation.h"
|
|
||||||
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
namespace esphome {
|
|
||||||
namespace uart {
|
|
||||||
|
|
||||||
template<typename... Ts> class UARTWriteAction : public Action<Ts...>, public Parented<UARTComponent> {
|
|
||||||
public:
|
|
||||||
void set_data_template(std::function<std::vector<uint8_t>(Ts...)> func) {
|
|
||||||
this->data_func_ = func;
|
|
||||||
this->static_ = false;
|
|
||||||
}
|
|
||||||
void set_data_static(const std::vector<uint8_t> &data) {
|
|
||||||
this->data_static_ = data;
|
|
||||||
this->static_ = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void play(Ts... x) override {
|
|
||||||
if (this->static_) {
|
|
||||||
this->parent_->write_array(this->data_static_);
|
|
||||||
} else {
|
|
||||||
auto val = this->data_func_(x...);
|
|
||||||
this->parent_->write_array(val);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected:
|
|
||||||
bool static_{false};
|
|
||||||
std::function<std::vector<uint8_t>(Ts...)> data_func_{};
|
|
||||||
std::vector<uint8_t> data_static_{};
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace uart
|
|
||||||
} // namespace esphome
|
|
||||||
|
|
@ -1,37 +0,0 @@
|
||||||
import esphome.codegen as cg
|
|
||||||
import esphome.config_validation as cv
|
|
||||||
from esphome.components import switch, uart
|
|
||||||
from esphome.const import CONF_DATA, CONF_SEND_EVERY
|
|
||||||
from esphome.core import HexInt
|
|
||||||
from .. import uart_ns, validate_raw_data
|
|
||||||
|
|
||||||
DEPENDENCIES = ["uart"]
|
|
||||||
|
|
||||||
UARTSwitch = uart_ns.class_("UARTSwitch", switch.Switch, uart.UARTDevice, cg.Component)
|
|
||||||
|
|
||||||
|
|
||||||
CONFIG_SCHEMA = (
|
|
||||||
switch.switch_schema(UARTSwitch, block_inverted=True)
|
|
||||||
.extend(
|
|
||||||
{
|
|
||||||
cv.Required(CONF_DATA): validate_raw_data,
|
|
||||||
cv.Optional(CONF_SEND_EVERY): cv.positive_time_period_milliseconds,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
.extend(uart.UART_DEVICE_SCHEMA)
|
|
||||||
.extend(cv.COMPONENT_SCHEMA)
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
async def to_code(config):
|
|
||||||
var = await switch.new_switch(config)
|
|
||||||
await cg.register_component(var, config)
|
|
||||||
await uart.register_uart_device(var, config)
|
|
||||||
|
|
||||||
data = config[CONF_DATA]
|
|
||||||
if isinstance(data, bytes):
|
|
||||||
data = [HexInt(x) for x in data]
|
|
||||||
cg.add(var.set_data(data))
|
|
||||||
|
|
||||||
if CONF_SEND_EVERY in config:
|
|
||||||
cg.add(var.set_send_every(config[CONF_SEND_EVERY]))
|
|
||||||
|
|
@ -1,47 +0,0 @@
|
||||||
#include "uart_switch.h"
|
|
||||||
#include "esphome/core/log.h"
|
|
||||||
|
|
||||||
namespace esphome {
|
|
||||||
namespace uart {
|
|
||||||
|
|
||||||
static const char *const TAG = "uart.switch";
|
|
||||||
|
|
||||||
void UARTSwitch::loop() {
|
|
||||||
if (this->state && this->send_every_) {
|
|
||||||
const uint32_t now = millis();
|
|
||||||
if (now - this->last_transmission_ > this->send_every_) {
|
|
||||||
this->write_command_();
|
|
||||||
this->last_transmission_ = now;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void UARTSwitch::write_command_() {
|
|
||||||
ESP_LOGD(TAG, "'%s': Sending data...", this->get_name().c_str());
|
|
||||||
this->write_array(this->data_.data(), this->data_.size());
|
|
||||||
}
|
|
||||||
|
|
||||||
void UARTSwitch::write_state(bool state) {
|
|
||||||
if (!state) {
|
|
||||||
this->publish_state(false);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this->publish_state(true);
|
|
||||||
this->write_command_();
|
|
||||||
|
|
||||||
if (this->send_every_ == 0) {
|
|
||||||
this->publish_state(false);
|
|
||||||
} else {
|
|
||||||
this->last_transmission_ = millis();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
void UARTSwitch::dump_config() {
|
|
||||||
LOG_SWITCH("", "UART Switch", this);
|
|
||||||
if (this->send_every_) {
|
|
||||||
ESP_LOGCONFIG(TAG, " Send Every: %u", this->send_every_);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace uart
|
|
||||||
} // namespace esphome
|
|
||||||
|
|
@ -1,30 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "esphome/core/component.h"
|
|
||||||
#include "esphome/components/uart/uart.h"
|
|
||||||
#include "esphome/components/switch/switch.h"
|
|
||||||
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
namespace esphome {
|
|
||||||
namespace uart {
|
|
||||||
|
|
||||||
class UARTSwitch : public switch_::Switch, public UARTDevice, public Component {
|
|
||||||
public:
|
|
||||||
void loop() override;
|
|
||||||
|
|
||||||
void set_data(const std::vector<uint8_t> &data) { data_ = data; }
|
|
||||||
void set_send_every(uint32_t send_every) { this->send_every_ = send_every; }
|
|
||||||
|
|
||||||
void dump_config() override;
|
|
||||||
|
|
||||||
protected:
|
|
||||||
void write_command_();
|
|
||||||
void write_state(bool state) override;
|
|
||||||
std::vector<uint8_t> data_;
|
|
||||||
uint32_t send_every_;
|
|
||||||
uint32_t last_transmission_;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace uart
|
|
||||||
} // namespace esphome
|
|
||||||
|
|
@ -1,46 +0,0 @@
|
||||||
#include "uart.h"
|
|
||||||
#include "esphome/core/log.h"
|
|
||||||
#include "esphome/core/helpers.h"
|
|
||||||
#include "esphome/core/application.h"
|
|
||||||
#include "esphome/core/defines.h"
|
|
||||||
|
|
||||||
namespace esphome {
|
|
||||||
namespace uart {
|
|
||||||
|
|
||||||
static const char *const TAG = "uart";
|
|
||||||
|
|
||||||
void UARTDevice::check_uart_settings(uint32_t baud_rate, uint8_t stop_bits, UARTParityOptions parity,
|
|
||||||
uint8_t data_bits) {
|
|
||||||
if (this->parent_->get_baud_rate() != baud_rate) {
|
|
||||||
ESP_LOGE(TAG, " Invalid baud_rate: Integration requested baud_rate %u but you have %u!", baud_rate,
|
|
||||||
this->parent_->get_baud_rate());
|
|
||||||
}
|
|
||||||
if (this->parent_->get_stop_bits() != stop_bits) {
|
|
||||||
ESP_LOGE(TAG, " Invalid stop bits: Integration requested stop_bits %u but you have %u!", stop_bits,
|
|
||||||
this->parent_->get_stop_bits());
|
|
||||||
}
|
|
||||||
if (this->parent_->get_data_bits() != data_bits) {
|
|
||||||
ESP_LOGE(TAG, " Invalid number of data bits: Integration requested %u data bits but you have %u!", data_bits,
|
|
||||||
this->parent_->get_data_bits());
|
|
||||||
}
|
|
||||||
if (this->parent_->get_parity() != parity) {
|
|
||||||
ESP_LOGE(TAG, " Invalid parity: Integration requested parity %s but you have %s!",
|
|
||||||
LOG_STR_ARG(parity_to_str(parity)), LOG_STR_ARG(parity_to_str(this->parent_->get_parity())));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const LogString *parity_to_str(UARTParityOptions parity) {
|
|
||||||
switch (parity) {
|
|
||||||
case UART_CONFIG_PARITY_NONE:
|
|
||||||
return LOG_STR("NONE");
|
|
||||||
case UART_CONFIG_PARITY_EVEN:
|
|
||||||
return LOG_STR("EVEN");
|
|
||||||
case UART_CONFIG_PARITY_ODD:
|
|
||||||
return LOG_STR("ODD");
|
|
||||||
default:
|
|
||||||
return LOG_STR("UNKNOWN");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace uart
|
|
||||||
} // namespace esphome
|
|
||||||
|
|
@ -1,72 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <vector>
|
|
||||||
#include "esphome/core/component.h"
|
|
||||||
#include "esphome/core/hal.h"
|
|
||||||
#include "esphome/core/log.h"
|
|
||||||
#include "uart_component.h"
|
|
||||||
|
|
||||||
namespace esphome {
|
|
||||||
namespace uart {
|
|
||||||
|
|
||||||
class UARTDevice {
|
|
||||||
public:
|
|
||||||
UARTDevice() = default;
|
|
||||||
UARTDevice(UARTComponent *parent) : parent_(parent) {}
|
|
||||||
|
|
||||||
void set_uart_parent(UARTComponent *parent) { this->parent_ = parent; }
|
|
||||||
|
|
||||||
void write_byte(uint8_t data) { this->parent_->write_byte(data); }
|
|
||||||
|
|
||||||
void write_array(const uint8_t *data, size_t len) { this->parent_->write_array(data, len); }
|
|
||||||
void write_array(const std::vector<uint8_t> &data) { this->parent_->write_array(data); }
|
|
||||||
template<size_t N> void write_array(const std::array<uint8_t, N> &data) {
|
|
||||||
this->parent_->write_array(data.data(), data.size());
|
|
||||||
}
|
|
||||||
|
|
||||||
void write_str(const char *str) { this->parent_->write_str(str); }
|
|
||||||
|
|
||||||
bool read_byte(uint8_t *data) { return this->parent_->read_byte(data); }
|
|
||||||
bool peek_byte(uint8_t *data) { return this->parent_->peek_byte(data); }
|
|
||||||
|
|
||||||
bool read_array(uint8_t *data, size_t len) { return this->parent_->read_array(data, len); }
|
|
||||||
template<size_t N> optional<std::array<uint8_t, N>> read_array() { // NOLINT
|
|
||||||
std::array<uint8_t, N> res;
|
|
||||||
if (!this->read_array(res.data(), N)) {
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
int available() { return this->parent_->available(); }
|
|
||||||
|
|
||||||
void flush() { return this->parent_->flush(); }
|
|
||||||
|
|
||||||
// Compat APIs
|
|
||||||
int read() {
|
|
||||||
uint8_t data;
|
|
||||||
if (!this->read_byte(&data))
|
|
||||||
return -1;
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
size_t write(uint8_t data) {
|
|
||||||
this->write_byte(data);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
int peek() {
|
|
||||||
uint8_t data;
|
|
||||||
if (!this->peek_byte(&data))
|
|
||||||
return -1;
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Check that the configuration of the UART bus matches the provided values and otherwise print a warning
|
|
||||||
void check_uart_settings(uint32_t baud_rate, uint8_t stop_bits = 1,
|
|
||||||
UARTParityOptions parity = UART_CONFIG_PARITY_NONE, uint8_t data_bits = 8);
|
|
||||||
|
|
||||||
protected:
|
|
||||||
UARTComponent *parent_{nullptr};
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace uart
|
|
||||||
} // namespace esphome
|
|
||||||
|
|
@ -1,24 +0,0 @@
|
||||||
#include "uart_component.h"
|
|
||||||
|
|
||||||
namespace esphome {
|
|
||||||
namespace uart {
|
|
||||||
|
|
||||||
static const char *const TAG = "uart";
|
|
||||||
|
|
||||||
bool UARTComponent::check_read_timeout_(size_t len) {
|
|
||||||
if (this->available() >= int(len))
|
|
||||||
return true;
|
|
||||||
|
|
||||||
uint32_t start_time = millis();
|
|
||||||
while (this->available() < int(len)) {
|
|
||||||
if (millis() - start_time > 100) {
|
|
||||||
ESP_LOGE(TAG, "Reading from UART timed out at byte %u!", this->available());
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
yield();
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace uart
|
|
||||||
} // namespace esphome
|
|
||||||
|
|
@ -1,89 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <vector>
|
|
||||||
#include <cstring>
|
|
||||||
#include "esphome/core/defines.h"
|
|
||||||
#include "esphome/core/component.h"
|
|
||||||
#include "esphome/core/hal.h"
|
|
||||||
#include "esphome/core/log.h"
|
|
||||||
#ifdef USE_UART_DEBUGGER
|
|
||||||
#include "esphome/core/automation.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace esphome {
|
|
||||||
namespace uart {
|
|
||||||
|
|
||||||
enum UARTParityOptions {
|
|
||||||
UART_CONFIG_PARITY_NONE,
|
|
||||||
UART_CONFIG_PARITY_EVEN,
|
|
||||||
UART_CONFIG_PARITY_ODD,
|
|
||||||
};
|
|
||||||
|
|
||||||
#ifdef USE_UART_DEBUGGER
|
|
||||||
enum UARTDirection {
|
|
||||||
UART_DIRECTION_RX,
|
|
||||||
UART_DIRECTION_TX,
|
|
||||||
UART_DIRECTION_BOTH,
|
|
||||||
};
|
|
||||||
#endif
|
|
||||||
|
|
||||||
const LogString *parity_to_str(UARTParityOptions parity);
|
|
||||||
|
|
||||||
class UARTComponent {
|
|
||||||
public:
|
|
||||||
void write_array(const std::vector<uint8_t> &data) { this->write_array(&data[0], data.size()); }
|
|
||||||
void write_byte(uint8_t data) { this->write_array(&data, 1); };
|
|
||||||
void write_str(const char *str) {
|
|
||||||
const auto *data = reinterpret_cast<const uint8_t *>(str);
|
|
||||||
this->write_array(data, strlen(str));
|
|
||||||
};
|
|
||||||
|
|
||||||
virtual void write_array(const uint8_t *data, size_t len) = 0;
|
|
||||||
|
|
||||||
bool read_byte(uint8_t *data) { return this->read_array(data, 1); };
|
|
||||||
virtual bool peek_byte(uint8_t *data) = 0;
|
|
||||||
virtual bool read_array(uint8_t *data, size_t len) = 0;
|
|
||||||
|
|
||||||
/// Return available number of bytes.
|
|
||||||
virtual int available() = 0;
|
|
||||||
/// Block until all bytes have been written to the UART bus.
|
|
||||||
virtual void flush() = 0;
|
|
||||||
|
|
||||||
void set_tx_pin(InternalGPIOPin *tx_pin) { this->tx_pin_ = tx_pin; }
|
|
||||||
void set_rx_pin(InternalGPIOPin *rx_pin) { this->rx_pin_ = rx_pin; }
|
|
||||||
void set_rx_buffer_size(size_t rx_buffer_size) { this->rx_buffer_size_ = rx_buffer_size; }
|
|
||||||
size_t get_rx_buffer_size() { return this->rx_buffer_size_; }
|
|
||||||
|
|
||||||
void set_stop_bits(uint8_t stop_bits) { this->stop_bits_ = stop_bits; }
|
|
||||||
uint8_t get_stop_bits() const { return this->stop_bits_; }
|
|
||||||
void set_data_bits(uint8_t data_bits) { this->data_bits_ = data_bits; }
|
|
||||||
uint8_t get_data_bits() const { return this->data_bits_; }
|
|
||||||
void set_parity(UARTParityOptions parity) { this->parity_ = parity; }
|
|
||||||
UARTParityOptions get_parity() const { return this->parity_; }
|
|
||||||
void set_baud_rate(uint32_t baud_rate) { baud_rate_ = baud_rate; }
|
|
||||||
uint32_t get_baud_rate() const { return baud_rate_; }
|
|
||||||
|
|
||||||
#ifdef USE_UART_DEBUGGER
|
|
||||||
void add_debug_callback(std::function<void(UARTDirection, uint8_t)> &&callback) {
|
|
||||||
this->debug_callback_.add(std::move(callback));
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
protected:
|
|
||||||
virtual void check_logger_conflict() = 0;
|
|
||||||
bool check_read_timeout_(size_t len = 1);
|
|
||||||
|
|
||||||
InternalGPIOPin *tx_pin_;
|
|
||||||
InternalGPIOPin *rx_pin_;
|
|
||||||
size_t rx_buffer_size_;
|
|
||||||
uint32_t baud_rate_;
|
|
||||||
uint8_t stop_bits_;
|
|
||||||
uint8_t data_bits_;
|
|
||||||
UARTParityOptions parity_;
|
|
||||||
#ifdef USE_UART_DEBUGGER
|
|
||||||
CallbackManager<void(UARTDirection, uint8_t)> debug_callback_{};
|
|
||||||
#endif
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace uart
|
|
||||||
} // namespace esphome
|
|
||||||
|
|
@ -1,184 +0,0 @@
|
||||||
#ifdef USE_ESP32_FRAMEWORK_ARDUINO
|
|
||||||
#include "esphome/core/application.h"
|
|
||||||
#include "esphome/core/defines.h"
|
|
||||||
#include "esphome/core/helpers.h"
|
|
||||||
#include "esphome/core/log.h"
|
|
||||||
#include "uart_component_esp32_arduino.h"
|
|
||||||
|
|
||||||
#ifdef USE_LOGGER
|
|
||||||
#include "esphome/components/logger/logger.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace esphome {
|
|
||||||
namespace uart {
|
|
||||||
static const char *const TAG = "uart.arduino_esp32";
|
|
||||||
|
|
||||||
static const uint32_t UART_PARITY_EVEN = 0 << 0;
|
|
||||||
static const uint32_t UART_PARITY_ODD = 1 << 0;
|
|
||||||
static const uint32_t UART_PARITY_ENABLE = 1 << 1;
|
|
||||||
static const uint32_t UART_NB_BIT_5 = 0 << 2;
|
|
||||||
static const uint32_t UART_NB_BIT_6 = 1 << 2;
|
|
||||||
static const uint32_t UART_NB_BIT_7 = 2 << 2;
|
|
||||||
static const uint32_t UART_NB_BIT_8 = 3 << 2;
|
|
||||||
static const uint32_t UART_NB_STOP_BIT_1 = 1 << 4;
|
|
||||||
static const uint32_t UART_NB_STOP_BIT_2 = 3 << 4;
|
|
||||||
static const uint32_t UART_TICK_APB_CLOCK = 1 << 27;
|
|
||||||
|
|
||||||
uint32_t ESP32ArduinoUARTComponent::get_config() {
|
|
||||||
uint32_t config = 0;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* All bits numbers below come from
|
|
||||||
* framework-arduinoespressif32/cores/esp32/esp32-hal-uart.h
|
|
||||||
* And more specifically conf0 union in uart_dev_t.
|
|
||||||
*
|
|
||||||
* Below is bit used from conf0 union.
|
|
||||||
* <name>:<bits position> <values>
|
|
||||||
* parity:0 0:even 1:odd
|
|
||||||
* parity_en:1 Set this bit to enable uart parity check.
|
|
||||||
* bit_num:2-4 0:5bits 1:6bits 2:7bits 3:8bits
|
|
||||||
* stop_bit_num:4-6 stop bit. 1:1bit 2:1.5bits 3:2bits
|
|
||||||
* tick_ref_always_on:27 select the clock.1:apb clock:ref_tick
|
|
||||||
*/
|
|
||||||
|
|
||||||
if (this->parity_ == UART_CONFIG_PARITY_EVEN) {
|
|
||||||
config |= UART_PARITY_EVEN | UART_PARITY_ENABLE;
|
|
||||||
} else if (this->parity_ == UART_CONFIG_PARITY_ODD) {
|
|
||||||
config |= UART_PARITY_ODD | UART_PARITY_ENABLE;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (this->data_bits_) {
|
|
||||||
case 5:
|
|
||||||
config |= UART_NB_BIT_5;
|
|
||||||
break;
|
|
||||||
case 6:
|
|
||||||
config |= UART_NB_BIT_6;
|
|
||||||
break;
|
|
||||||
case 7:
|
|
||||||
config |= UART_NB_BIT_7;
|
|
||||||
break;
|
|
||||||
case 8:
|
|
||||||
config |= UART_NB_BIT_8;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this->stop_bits_ == 1) {
|
|
||||||
config |= UART_NB_STOP_BIT_1;
|
|
||||||
} else {
|
|
||||||
config |= UART_NB_STOP_BIT_2;
|
|
||||||
}
|
|
||||||
|
|
||||||
config |= UART_TICK_APB_CLOCK;
|
|
||||||
|
|
||||||
return config;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ESP32ArduinoUARTComponent::setup() {
|
|
||||||
ESP_LOGCONFIG(TAG, "Setting up UART...");
|
|
||||||
// Use Arduino HardwareSerial UARTs if all used pins match the ones
|
|
||||||
// preconfigured by the platform. For example if RX disabled but TX pin
|
|
||||||
// is 1 we still want to use Serial.
|
|
||||||
bool is_default_tx, is_default_rx;
|
|
||||||
#ifdef CONFIG_IDF_TARGET_ESP32C3
|
|
||||||
is_default_tx = tx_pin_ == nullptr || tx_pin_->get_pin() == 21;
|
|
||||||
is_default_rx = rx_pin_ == nullptr || rx_pin_->get_pin() == 20;
|
|
||||||
|
|
||||||
#else
|
|
||||||
is_default_tx = tx_pin_ == nullptr || tx_pin_->get_pin() == 1;
|
|
||||||
is_default_rx = rx_pin_ == nullptr || rx_pin_->get_pin() == 3;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// 一个临时补丁,让USB-CDC串口和UART串口共存!(*23年3月7日*17时15分)
|
|
||||||
#if defined(USE_ARDUINO) && defined(ARDUINO_USB_CDC_ON_BOOT) && ((defined(USE_ESP32_VARIANT_ESP32C3) && defined(ARDUINO_USB_MODE)) || defined(USE_ESP32_VARIANT_ESP32S2))
|
|
||||||
|
|
||||||
static uint8_t next_uart_num = 1;
|
|
||||||
|
|
||||||
this->number_ = next_uart_num;
|
|
||||||
this->hw_serial_ = new HardwareSerial(next_uart_num++); // NOLINT(cppcoreguidelines-owning-memory)
|
|
||||||
|
|
||||||
#else
|
|
||||||
// 传统方式,进行检查,进行判断代码。(*23年3月7日*17时22分)
|
|
||||||
if (is_default_tx && is_default_rx) {
|
|
||||||
this->hw_serial_ = &Serial;
|
|
||||||
} else {
|
|
||||||
static uint8_t next_uart_num = 1;
|
|
||||||
this->number_ = next_uart_num;
|
|
||||||
this->hw_serial_ = new HardwareSerial(next_uart_num++); // NOLINT(cppcoreguidelines-owning-memory)
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
int8_t tx = this->tx_pin_ != nullptr ? this->tx_pin_->get_pin() : -1;
|
|
||||||
int8_t rx = this->rx_pin_ != nullptr ? this->rx_pin_->get_pin() : -1;
|
|
||||||
bool invert = false;
|
|
||||||
if (tx_pin_ != nullptr && tx_pin_->is_inverted())
|
|
||||||
invert = true;
|
|
||||||
if (rx_pin_ != nullptr && rx_pin_->is_inverted())
|
|
||||||
invert = true;
|
|
||||||
this->hw_serial_->begin(this->baud_rate_, get_config(), rx, tx, invert);
|
|
||||||
this->hw_serial_->setRxBufferSize(this->rx_buffer_size_);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ESP32ArduinoUARTComponent::dump_config() {
|
|
||||||
ESP_LOGCONFIG(TAG, "UART Bus %d:", this->number_);
|
|
||||||
LOG_PIN(" TX Pin: ", tx_pin_);
|
|
||||||
LOG_PIN(" RX Pin: ", rx_pin_);
|
|
||||||
if (this->rx_pin_ != nullptr) {
|
|
||||||
ESP_LOGCONFIG(TAG, " RX Buffer Size: %u", this->rx_buffer_size_);
|
|
||||||
}
|
|
||||||
ESP_LOGCONFIG(TAG, " Baud Rate: %u baud", this->baud_rate_);
|
|
||||||
ESP_LOGCONFIG(TAG, " Data Bits: %u", this->data_bits_);
|
|
||||||
ESP_LOGCONFIG(TAG, " Parity: %s", LOG_STR_ARG(parity_to_str(this->parity_)));
|
|
||||||
ESP_LOGCONFIG(TAG, " Stop bits: %u", this->stop_bits_);
|
|
||||||
this->check_logger_conflict();
|
|
||||||
}
|
|
||||||
|
|
||||||
void ESP32ArduinoUARTComponent::write_array(const uint8_t *data, size_t len) {
|
|
||||||
this->hw_serial_->write(data, len);
|
|
||||||
#ifdef USE_UART_DEBUGGER
|
|
||||||
for (size_t i = 0; i < len; i++) {
|
|
||||||
this->debug_callback_.call(UART_DIRECTION_TX, data[i]);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ESP32ArduinoUARTComponent::peek_byte(uint8_t *data) {
|
|
||||||
if (!this->check_read_timeout_())
|
|
||||||
return false;
|
|
||||||
*data = this->hw_serial_->peek();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ESP32ArduinoUARTComponent::read_array(uint8_t *data, size_t len) {
|
|
||||||
if (!this->check_read_timeout_(len))
|
|
||||||
return false;
|
|
||||||
this->hw_serial_->readBytes(data, len);
|
|
||||||
#ifdef USE_UART_DEBUGGER
|
|
||||||
for (size_t i = 0; i < len; i++) {
|
|
||||||
this->debug_callback_.call(UART_DIRECTION_RX, data[i]);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
int ESP32ArduinoUARTComponent::available() { return this->hw_serial_->available(); }
|
|
||||||
void ESP32ArduinoUARTComponent::flush() {
|
|
||||||
ESP_LOGVV(TAG, " Flushing...");
|
|
||||||
this->hw_serial_->flush();
|
|
||||||
}
|
|
||||||
|
|
||||||
void ESP32ArduinoUARTComponent::check_logger_conflict() {
|
|
||||||
#ifdef USE_LOGGER
|
|
||||||
if (this->hw_serial_ == nullptr || logger::global_logger->get_baud_rate() == 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this->hw_serial_ == logger::global_logger->get_hw_serial()) {
|
|
||||||
ESP_LOGW(TAG, " You're using the same serial port for logging and the UART component. Please "
|
|
||||||
"disable logging over the serial port by setting logger->baud_rate to 0.");
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace uart
|
|
||||||
} // namespace esphome
|
|
||||||
#endif // USE_ESP32_FRAMEWORK_ARDUINO
|
|
||||||
|
|
@ -1,41 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#ifdef USE_ESP32_FRAMEWORK_ARDUINO
|
|
||||||
|
|
||||||
#include <HardwareSerial.h>
|
|
||||||
#include <vector>
|
|
||||||
#include "esphome/core/component.h"
|
|
||||||
#include "esphome/core/hal.h"
|
|
||||||
#include "esphome/core/log.h"
|
|
||||||
#include "uart_component.h"
|
|
||||||
|
|
||||||
namespace esphome {
|
|
||||||
namespace uart {
|
|
||||||
|
|
||||||
class ESP32ArduinoUARTComponent : public UARTComponent, public Component {
|
|
||||||
public:
|
|
||||||
void setup() override;
|
|
||||||
void dump_config() override;
|
|
||||||
float get_setup_priority() const override { return setup_priority::BUS; }
|
|
||||||
|
|
||||||
void write_array(const uint8_t *data, size_t len) override;
|
|
||||||
|
|
||||||
bool peek_byte(uint8_t *data) override;
|
|
||||||
bool read_array(uint8_t *data, size_t len) override;
|
|
||||||
|
|
||||||
int available() override;
|
|
||||||
void flush() override;
|
|
||||||
|
|
||||||
uint32_t get_config();
|
|
||||||
|
|
||||||
protected:
|
|
||||||
void check_logger_conflict() override;
|
|
||||||
|
|
||||||
HardwareSerial *hw_serial_{nullptr};
|
|
||||||
uint8_t number_{0};
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace uart
|
|
||||||
} // namespace esphome
|
|
||||||
|
|
||||||
#endif // USE_ESP32_FRAMEWORK_ARDUINO
|
|
||||||
|
|
@ -1,304 +0,0 @@
|
||||||
#ifdef USE_ESP8266
|
|
||||||
#include "uart_component_esp8266.h"
|
|
||||||
#include "esphome/core/application.h"
|
|
||||||
#include "esphome/core/defines.h"
|
|
||||||
#include "esphome/core/helpers.h"
|
|
||||||
#include "esphome/core/log.h"
|
|
||||||
|
|
||||||
#ifdef USE_LOGGER
|
|
||||||
#include "esphome/components/logger/logger.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace esphome {
|
|
||||||
namespace uart {
|
|
||||||
|
|
||||||
static const char *const TAG = "uart.arduino_esp8266";
|
|
||||||
bool ESP8266UartComponent::serial0_in_use = false; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
|
|
||||||
|
|
||||||
uint32_t ESP8266UartComponent::get_config() {
|
|
||||||
uint32_t config = 0;
|
|
||||||
|
|
||||||
if (this->parity_ == UART_CONFIG_PARITY_NONE) {
|
|
||||||
config |= UART_PARITY_NONE;
|
|
||||||
} else if (this->parity_ == UART_CONFIG_PARITY_EVEN) {
|
|
||||||
config |= UART_PARITY_EVEN;
|
|
||||||
} else if (this->parity_ == UART_CONFIG_PARITY_ODD) {
|
|
||||||
config |= UART_PARITY_ODD;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (this->data_bits_) {
|
|
||||||
case 5:
|
|
||||||
config |= UART_NB_BIT_5;
|
|
||||||
break;
|
|
||||||
case 6:
|
|
||||||
config |= UART_NB_BIT_6;
|
|
||||||
break;
|
|
||||||
case 7:
|
|
||||||
config |= UART_NB_BIT_7;
|
|
||||||
break;
|
|
||||||
case 8:
|
|
||||||
config |= UART_NB_BIT_8;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this->stop_bits_ == 1) {
|
|
||||||
config |= UART_NB_STOP_BIT_1;
|
|
||||||
} else {
|
|
||||||
config |= UART_NB_STOP_BIT_2;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this->tx_pin_ != nullptr && this->tx_pin_->is_inverted())
|
|
||||||
config |= BIT(22);
|
|
||||||
if (this->rx_pin_ != nullptr && this->rx_pin_->is_inverted())
|
|
||||||
config |= BIT(19);
|
|
||||||
|
|
||||||
return config;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ESP8266UartComponent::setup() {
|
|
||||||
ESP_LOGCONFIG(TAG, "Setting up UART bus...");
|
|
||||||
// Use Arduino HardwareSerial UARTs if all used pins match the ones
|
|
||||||
// preconfigured by the platform. For example if RX disabled but TX pin
|
|
||||||
// is 1 we still want to use Serial.
|
|
||||||
SerialConfig config = static_cast<SerialConfig>(get_config());
|
|
||||||
|
|
||||||
if (!ESP8266UartComponent::serial0_in_use && (tx_pin_ == nullptr || tx_pin_->get_pin() == 1) &&
|
|
||||||
(rx_pin_ == nullptr || rx_pin_->get_pin() == 3)
|
|
||||||
#ifdef USE_LOGGER
|
|
||||||
// we will use UART0 if logger isn't using it in swapped mode
|
|
||||||
&& (logger::global_logger->get_hw_serial() == nullptr ||
|
|
||||||
logger::global_logger->get_uart() != logger::UART_SELECTION_UART0_SWAP)
|
|
||||||
#endif
|
|
||||||
) {
|
|
||||||
this->hw_serial_ = &Serial;
|
|
||||||
this->hw_serial_->begin(this->baud_rate_, config);
|
|
||||||
this->hw_serial_->setRxBufferSize(this->rx_buffer_size_);
|
|
||||||
ESP8266UartComponent::serial0_in_use = true;
|
|
||||||
} else if (!ESP8266UartComponent::serial0_in_use && (tx_pin_ == nullptr || tx_pin_->get_pin() == 15) &&
|
|
||||||
(rx_pin_ == nullptr || rx_pin_->get_pin() == 13)
|
|
||||||
#ifdef USE_LOGGER
|
|
||||||
// we will use UART0 swapped if logger isn't using it in regular mode
|
|
||||||
&& (logger::global_logger->get_hw_serial() == nullptr ||
|
|
||||||
logger::global_logger->get_uart() != logger::UART_SELECTION_UART0)
|
|
||||||
#endif
|
|
||||||
) {
|
|
||||||
this->hw_serial_ = &Serial;
|
|
||||||
this->hw_serial_->begin(this->baud_rate_, config);
|
|
||||||
this->hw_serial_->setRxBufferSize(this->rx_buffer_size_);
|
|
||||||
this->hw_serial_->swap();
|
|
||||||
ESP8266UartComponent::serial0_in_use = true;
|
|
||||||
} else if ((tx_pin_ == nullptr || tx_pin_->get_pin() == 2) && (rx_pin_ == nullptr || rx_pin_->get_pin() == 8)) {
|
|
||||||
this->hw_serial_ = &Serial1;
|
|
||||||
this->hw_serial_->begin(this->baud_rate_, config);
|
|
||||||
this->hw_serial_->setRxBufferSize(this->rx_buffer_size_);
|
|
||||||
} else {
|
|
||||||
this->sw_serial_ = new ESP8266SoftwareSerial(); // NOLINT
|
|
||||||
this->sw_serial_->setup(tx_pin_, rx_pin_, this->baud_rate_, this->stop_bits_, this->data_bits_, this->parity_,
|
|
||||||
this->rx_buffer_size_);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void ESP8266UartComponent::dump_config() {
|
|
||||||
ESP_LOGCONFIG(TAG, "UART Bus:");
|
|
||||||
LOG_PIN(" TX Pin: ", tx_pin_);
|
|
||||||
LOG_PIN(" RX Pin: ", rx_pin_);
|
|
||||||
if (this->rx_pin_ != nullptr) {
|
|
||||||
ESP_LOGCONFIG(TAG, " RX Buffer Size: %u", this->rx_buffer_size_); // NOLINT
|
|
||||||
}
|
|
||||||
ESP_LOGCONFIG(TAG, " Baud Rate: %u baud", this->baud_rate_);
|
|
||||||
ESP_LOGCONFIG(TAG, " Data Bits: %u", this->data_bits_);
|
|
||||||
ESP_LOGCONFIG(TAG, " Parity: %s", LOG_STR_ARG(parity_to_str(this->parity_)));
|
|
||||||
ESP_LOGCONFIG(TAG, " Stop bits: %u", this->stop_bits_);
|
|
||||||
if (this->hw_serial_ != nullptr) {
|
|
||||||
ESP_LOGCONFIG(TAG, " Using hardware serial interface.");
|
|
||||||
} else {
|
|
||||||
ESP_LOGCONFIG(TAG, " Using software serial");
|
|
||||||
}
|
|
||||||
this->check_logger_conflict();
|
|
||||||
}
|
|
||||||
|
|
||||||
void ESP8266UartComponent::check_logger_conflict() {
|
|
||||||
#ifdef USE_LOGGER
|
|
||||||
if (this->hw_serial_ == nullptr || logger::global_logger->get_baud_rate() == 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this->hw_serial_ == logger::global_logger->get_hw_serial()) {
|
|
||||||
ESP_LOGW(TAG, " You're using the same serial port for logging and the UART component. Please "
|
|
||||||
"disable logging over the serial port by setting logger->baud_rate to 0.");
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
void ESP8266UartComponent::write_array(const uint8_t *data, size_t len) {
|
|
||||||
if (this->hw_serial_ != nullptr) {
|
|
||||||
this->hw_serial_->write(data, len);
|
|
||||||
} else {
|
|
||||||
for (size_t i = 0; i < len; i++)
|
|
||||||
this->sw_serial_->write_byte(data[i]);
|
|
||||||
}
|
|
||||||
#ifdef USE_UART_DEBUGGER
|
|
||||||
for (size_t i = 0; i < len; i++) {
|
|
||||||
this->debug_callback_.call(UART_DIRECTION_TX, data[i]);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
bool ESP8266UartComponent::peek_byte(uint8_t *data) {
|
|
||||||
if (!this->check_read_timeout_())
|
|
||||||
return false;
|
|
||||||
if (this->hw_serial_ != nullptr) {
|
|
||||||
*data = this->hw_serial_->peek();
|
|
||||||
} else {
|
|
||||||
*data = this->sw_serial_->peek_byte();
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
bool ESP8266UartComponent::read_array(uint8_t *data, size_t len) {
|
|
||||||
if (!this->check_read_timeout_(len))
|
|
||||||
return false;
|
|
||||||
if (this->hw_serial_ != nullptr) {
|
|
||||||
this->hw_serial_->readBytes(data, len);
|
|
||||||
} else {
|
|
||||||
for (size_t i = 0; i < len; i++)
|
|
||||||
data[i] = this->sw_serial_->read_byte();
|
|
||||||
}
|
|
||||||
#ifdef USE_UART_DEBUGGER
|
|
||||||
for (size_t i = 0; i < len; i++) {
|
|
||||||
this->debug_callback_.call(UART_DIRECTION_RX, data[i]);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
int ESP8266UartComponent::available() {
|
|
||||||
if (this->hw_serial_ != nullptr) {
|
|
||||||
return this->hw_serial_->available();
|
|
||||||
} else {
|
|
||||||
return this->sw_serial_->available();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
void ESP8266UartComponent::flush() {
|
|
||||||
ESP_LOGVV(TAG, " Flushing...");
|
|
||||||
if (this->hw_serial_ != nullptr) {
|
|
||||||
this->hw_serial_->flush();
|
|
||||||
} else {
|
|
||||||
this->sw_serial_->flush();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
void ESP8266SoftwareSerial::setup(InternalGPIOPin *tx_pin, InternalGPIOPin *rx_pin, uint32_t baud_rate,
|
|
||||||
uint8_t stop_bits, uint32_t data_bits, UARTParityOptions parity,
|
|
||||||
size_t rx_buffer_size) {
|
|
||||||
this->bit_time_ = F_CPU / baud_rate;
|
|
||||||
this->rx_buffer_size_ = rx_buffer_size;
|
|
||||||
this->stop_bits_ = stop_bits;
|
|
||||||
this->data_bits_ = data_bits;
|
|
||||||
this->parity_ = parity;
|
|
||||||
if (tx_pin != nullptr) {
|
|
||||||
gpio_tx_pin_ = tx_pin;
|
|
||||||
gpio_tx_pin_->setup();
|
|
||||||
tx_pin_ = gpio_tx_pin_->to_isr();
|
|
||||||
tx_pin_.digital_write(true);
|
|
||||||
}
|
|
||||||
if (rx_pin != nullptr) {
|
|
||||||
gpio_rx_pin_ = rx_pin;
|
|
||||||
gpio_rx_pin_->setup();
|
|
||||||
rx_pin_ = gpio_rx_pin_->to_isr();
|
|
||||||
rx_buffer_ = new uint8_t[this->rx_buffer_size_]; // NOLINT
|
|
||||||
gpio_rx_pin_->attach_interrupt(ESP8266SoftwareSerial::gpio_intr, this, gpio::INTERRUPT_FALLING_EDGE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
void IRAM_ATTR ESP8266SoftwareSerial::gpio_intr(ESP8266SoftwareSerial *arg) {
|
|
||||||
uint32_t wait = arg->bit_time_ + arg->bit_time_ / 3 - 500;
|
|
||||||
const uint32_t start = arch_get_cpu_cycle_count();
|
|
||||||
uint8_t rec = 0;
|
|
||||||
// Manually unroll the loop
|
|
||||||
for (int i = 0; i < arg->data_bits_; i++)
|
|
||||||
rec |= arg->read_bit_(&wait, start) << i;
|
|
||||||
|
|
||||||
/* If parity is enabled, just read it and ignore it. */
|
|
||||||
/* TODO: Should we check parity? Or is it too slow for nothing added..*/
|
|
||||||
if (arg->parity_ == UART_CONFIG_PARITY_EVEN || arg->parity_ == UART_CONFIG_PARITY_ODD)
|
|
||||||
arg->read_bit_(&wait, start);
|
|
||||||
|
|
||||||
// Stop bit
|
|
||||||
arg->wait_(&wait, start);
|
|
||||||
if (arg->stop_bits_ == 2)
|
|
||||||
arg->wait_(&wait, start);
|
|
||||||
|
|
||||||
arg->rx_buffer_[arg->rx_in_pos_] = rec;
|
|
||||||
arg->rx_in_pos_ = (arg->rx_in_pos_ + 1) % arg->rx_buffer_size_;
|
|
||||||
// Clear RX pin so that the interrupt doesn't re-trigger right away again.
|
|
||||||
arg->rx_pin_.clear_interrupt();
|
|
||||||
}
|
|
||||||
void IRAM_ATTR HOT ESP8266SoftwareSerial::write_byte(uint8_t data) {
|
|
||||||
if (this->gpio_tx_pin_ == nullptr) {
|
|
||||||
ESP_LOGE(TAG, "UART doesn't have TX pins set!");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
bool parity_bit = false;
|
|
||||||
bool need_parity_bit = true;
|
|
||||||
if (this->parity_ == UART_CONFIG_PARITY_EVEN) {
|
|
||||||
parity_bit = false;
|
|
||||||
} else if (this->parity_ == UART_CONFIG_PARITY_ODD) {
|
|
||||||
parity_bit = true;
|
|
||||||
} else {
|
|
||||||
need_parity_bit = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
InterruptLock lock;
|
|
||||||
uint32_t wait = this->bit_time_;
|
|
||||||
const uint32_t start = arch_get_cpu_cycle_count();
|
|
||||||
// Start bit
|
|
||||||
this->write_bit_(false, &wait, start);
|
|
||||||
for (int i = 0; i < this->data_bits_; i++) {
|
|
||||||
bool bit = data & (1 << i);
|
|
||||||
this->write_bit_(bit, &wait, start);
|
|
||||||
if (need_parity_bit)
|
|
||||||
parity_bit ^= bit;
|
|
||||||
}
|
|
||||||
if (need_parity_bit)
|
|
||||||
this->write_bit_(parity_bit, &wait, start);
|
|
||||||
// Stop bit
|
|
||||||
this->write_bit_(true, &wait, start);
|
|
||||||
if (this->stop_bits_ == 2)
|
|
||||||
this->wait_(&wait, start);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
void IRAM_ATTR ESP8266SoftwareSerial::wait_(uint32_t *wait, const uint32_t &start) {
|
|
||||||
while (arch_get_cpu_cycle_count() - start < *wait)
|
|
||||||
;
|
|
||||||
*wait += this->bit_time_;
|
|
||||||
}
|
|
||||||
bool IRAM_ATTR ESP8266SoftwareSerial::read_bit_(uint32_t *wait, const uint32_t &start) {
|
|
||||||
this->wait_(wait, start);
|
|
||||||
return this->rx_pin_.digital_read();
|
|
||||||
}
|
|
||||||
void IRAM_ATTR ESP8266SoftwareSerial::write_bit_(bool bit, uint32_t *wait, const uint32_t &start) {
|
|
||||||
this->tx_pin_.digital_write(bit);
|
|
||||||
this->wait_(wait, start);
|
|
||||||
}
|
|
||||||
uint8_t ESP8266SoftwareSerial::read_byte() {
|
|
||||||
if (this->rx_in_pos_ == this->rx_out_pos_)
|
|
||||||
return 0;
|
|
||||||
uint8_t data = this->rx_buffer_[this->rx_out_pos_];
|
|
||||||
this->rx_out_pos_ = (this->rx_out_pos_ + 1) % this->rx_buffer_size_;
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
uint8_t ESP8266SoftwareSerial::peek_byte() {
|
|
||||||
if (this->rx_in_pos_ == this->rx_out_pos_)
|
|
||||||
return 0;
|
|
||||||
return this->rx_buffer_[this->rx_out_pos_];
|
|
||||||
}
|
|
||||||
void ESP8266SoftwareSerial::flush() {
|
|
||||||
// Flush is a NO-OP with software serial, all bytes are written immediately.
|
|
||||||
}
|
|
||||||
int ESP8266SoftwareSerial::available() {
|
|
||||||
int avail = int(this->rx_in_pos_) - int(this->rx_out_pos_);
|
|
||||||
if (avail < 0)
|
|
||||||
return avail + this->rx_buffer_size_;
|
|
||||||
return avail;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace uart
|
|
||||||
} // namespace esphome
|
|
||||||
#endif // USE_ESP8266
|
|
||||||
|
|
@ -1,79 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#ifdef USE_ESP8266
|
|
||||||
|
|
||||||
#include <HardwareSerial.h>
|
|
||||||
#include <vector>
|
|
||||||
#include "esphome/core/component.h"
|
|
||||||
#include "esphome/core/hal.h"
|
|
||||||
#include "esphome/core/log.h"
|
|
||||||
#include "uart_component.h"
|
|
||||||
|
|
||||||
namespace esphome {
|
|
||||||
namespace uart {
|
|
||||||
|
|
||||||
class ESP8266SoftwareSerial {
|
|
||||||
public:
|
|
||||||
void setup(InternalGPIOPin *tx_pin, InternalGPIOPin *rx_pin, uint32_t baud_rate, uint8_t stop_bits,
|
|
||||||
uint32_t data_bits, UARTParityOptions parity, size_t rx_buffer_size);
|
|
||||||
|
|
||||||
uint8_t read_byte();
|
|
||||||
uint8_t peek_byte();
|
|
||||||
|
|
||||||
void flush();
|
|
||||||
|
|
||||||
void write_byte(uint8_t data);
|
|
||||||
|
|
||||||
int available();
|
|
||||||
|
|
||||||
protected:
|
|
||||||
static void gpio_intr(ESP8266SoftwareSerial *arg);
|
|
||||||
|
|
||||||
void wait_(uint32_t *wait, const uint32_t &start);
|
|
||||||
bool read_bit_(uint32_t *wait, const uint32_t &start);
|
|
||||||
void write_bit_(bool bit, uint32_t *wait, const uint32_t &start);
|
|
||||||
|
|
||||||
uint32_t bit_time_{0};
|
|
||||||
uint8_t *rx_buffer_{nullptr};
|
|
||||||
size_t rx_buffer_size_;
|
|
||||||
volatile size_t rx_in_pos_{0};
|
|
||||||
size_t rx_out_pos_{0};
|
|
||||||
uint8_t stop_bits_;
|
|
||||||
uint8_t data_bits_;
|
|
||||||
UARTParityOptions parity_;
|
|
||||||
InternalGPIOPin *gpio_tx_pin_{nullptr};
|
|
||||||
ISRInternalGPIOPin tx_pin_;
|
|
||||||
InternalGPIOPin *gpio_rx_pin_{nullptr};
|
|
||||||
ISRInternalGPIOPin rx_pin_;
|
|
||||||
};
|
|
||||||
|
|
||||||
class ESP8266UartComponent : public UARTComponent, public Component {
|
|
||||||
public:
|
|
||||||
void setup() override;
|
|
||||||
void dump_config() override;
|
|
||||||
float get_setup_priority() const override { return setup_priority::BUS; }
|
|
||||||
|
|
||||||
void write_array(const uint8_t *data, size_t len) override;
|
|
||||||
|
|
||||||
bool peek_byte(uint8_t *data) override;
|
|
||||||
bool read_array(uint8_t *data, size_t len) override;
|
|
||||||
|
|
||||||
int available() override;
|
|
||||||
void flush() override;
|
|
||||||
|
|
||||||
uint32_t get_config();
|
|
||||||
|
|
||||||
protected:
|
|
||||||
void check_logger_conflict() override;
|
|
||||||
|
|
||||||
HardwareSerial *hw_serial_{nullptr};
|
|
||||||
ESP8266SoftwareSerial *sw_serial_{nullptr};
|
|
||||||
|
|
||||||
private:
|
|
||||||
static bool serial0_in_use; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace uart
|
|
||||||
} // namespace esphome
|
|
||||||
|
|
||||||
#endif // USE_ESP8266
|
|
||||||
|
|
@ -1,206 +0,0 @@
|
||||||
#ifdef USE_ESP_IDF
|
|
||||||
|
|
||||||
#include "uart_component_esp_idf.h"
|
|
||||||
#include "esphome/core/application.h"
|
|
||||||
#include "esphome/core/defines.h"
|
|
||||||
#include "esphome/core/helpers.h"
|
|
||||||
#include "esphome/core/log.h"
|
|
||||||
|
|
||||||
#ifdef USE_LOGGER
|
|
||||||
#include "esphome/components/logger/logger.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace esphome {
|
|
||||||
namespace uart {
|
|
||||||
static const char *const TAG = "uart.idf";
|
|
||||||
|
|
||||||
uart_config_t IDFUARTComponent::get_config_() {
|
|
||||||
uart_parity_t parity = UART_PARITY_DISABLE;
|
|
||||||
if (this->parity_ == UART_CONFIG_PARITY_EVEN) {
|
|
||||||
parity = UART_PARITY_EVEN;
|
|
||||||
} else if (this->parity_ == UART_CONFIG_PARITY_ODD) {
|
|
||||||
parity = UART_PARITY_ODD;
|
|
||||||
}
|
|
||||||
|
|
||||||
uart_word_length_t data_bits;
|
|
||||||
switch (this->data_bits_) {
|
|
||||||
case 5:
|
|
||||||
data_bits = UART_DATA_5_BITS;
|
|
||||||
break;
|
|
||||||
case 6:
|
|
||||||
data_bits = UART_DATA_6_BITS;
|
|
||||||
break;
|
|
||||||
case 7:
|
|
||||||
data_bits = UART_DATA_7_BITS;
|
|
||||||
break;
|
|
||||||
case 8:
|
|
||||||
data_bits = UART_DATA_8_BITS;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
data_bits = UART_DATA_BITS_MAX;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
uart_config_t uart_config;
|
|
||||||
uart_config.baud_rate = this->baud_rate_;
|
|
||||||
uart_config.data_bits = data_bits;
|
|
||||||
uart_config.parity = parity;
|
|
||||||
uart_config.stop_bits = this->stop_bits_ == 1 ? UART_STOP_BITS_1 : UART_STOP_BITS_2;
|
|
||||||
uart_config.flow_ctrl = UART_HW_FLOWCTRL_DISABLE;
|
|
||||||
uart_config.source_clk = UART_SCLK_APB;
|
|
||||||
uart_config.rx_flow_ctrl_thresh = 122;
|
|
||||||
|
|
||||||
return uart_config;
|
|
||||||
}
|
|
||||||
|
|
||||||
void IDFUARTComponent::setup() {
|
|
||||||
static uint8_t next_uart_num = 0;
|
|
||||||
#ifdef USE_LOGGER
|
|
||||||
if (logger::global_logger->get_uart_num() == next_uart_num)
|
|
||||||
next_uart_num++;
|
|
||||||
#endif
|
|
||||||
if (next_uart_num >= UART_NUM_MAX) {
|
|
||||||
ESP_LOGW(TAG, "Maximum number of UART components created already.");
|
|
||||||
this->mark_failed();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this->uart_num_ = next_uart_num++;
|
|
||||||
ESP_LOGCONFIG(TAG, "Setting up UART %u...", this->uart_num_);
|
|
||||||
|
|
||||||
this->lock_ = xSemaphoreCreateMutex();
|
|
||||||
|
|
||||||
xSemaphoreTake(this->lock_, portMAX_DELAY);
|
|
||||||
|
|
||||||
uart_config_t uart_config = this->get_config_();
|
|
||||||
esp_err_t err = uart_param_config(this->uart_num_, &uart_config);
|
|
||||||
if (err != ESP_OK) {
|
|
||||||
ESP_LOGW(TAG, "uart_param_config failed: %s", esp_err_to_name(err));
|
|
||||||
this->mark_failed();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
err = uart_driver_install(this->uart_num_, this->rx_buffer_size_, 0, 0, nullptr, 0);
|
|
||||||
if (err != ESP_OK) {
|
|
||||||
ESP_LOGW(TAG, "uart_driver_install failed: %s", esp_err_to_name(err));
|
|
||||||
this->mark_failed();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
int8_t tx = this->tx_pin_ != nullptr ? this->tx_pin_->get_pin() : -1;
|
|
||||||
int8_t rx = this->rx_pin_ != nullptr ? this->rx_pin_->get_pin() : -1;
|
|
||||||
|
|
||||||
err = uart_set_pin(this->uart_num_, tx, rx, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE);
|
|
||||||
if (err != ESP_OK) {
|
|
||||||
ESP_LOGW(TAG, "uart_set_pin failed: %s", esp_err_to_name(err));
|
|
||||||
this->mark_failed();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t invert = 0;
|
|
||||||
if (this->tx_pin_ != nullptr && this->tx_pin_->is_inverted())
|
|
||||||
invert |= UART_SIGNAL_TXD_INV;
|
|
||||||
if (this->rx_pin_ != nullptr && this->rx_pin_->is_inverted())
|
|
||||||
invert |= UART_SIGNAL_RXD_INV;
|
|
||||||
|
|
||||||
err = uart_set_line_inverse(this->uart_num_, invert);
|
|
||||||
if (err != ESP_OK) {
|
|
||||||
ESP_LOGW(TAG, "uart_set_line_inverse failed: %s", esp_err_to_name(err));
|
|
||||||
this->mark_failed();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
xSemaphoreGive(this->lock_);
|
|
||||||
}
|
|
||||||
|
|
||||||
void IDFUARTComponent::dump_config() {
|
|
||||||
ESP_LOGCONFIG(TAG, "UART Bus:");
|
|
||||||
ESP_LOGCONFIG(TAG, " Number: %u", this->uart_num_);
|
|
||||||
LOG_PIN(" TX Pin: ", tx_pin_);
|
|
||||||
LOG_PIN(" RX Pin: ", rx_pin_);
|
|
||||||
if (this->rx_pin_ != nullptr) {
|
|
||||||
ESP_LOGCONFIG(TAG, " RX Buffer Size: %u", this->rx_buffer_size_);
|
|
||||||
}
|
|
||||||
ESP_LOGCONFIG(TAG, " Baud Rate: %u baud", this->baud_rate_);
|
|
||||||
ESP_LOGCONFIG(TAG, " Data Bits: %u", this->data_bits_);
|
|
||||||
ESP_LOGCONFIG(TAG, " Parity: %s", LOG_STR_ARG(parity_to_str(this->parity_)));
|
|
||||||
ESP_LOGCONFIG(TAG, " Stop bits: %u", this->stop_bits_);
|
|
||||||
this->check_logger_conflict();
|
|
||||||
}
|
|
||||||
|
|
||||||
void IDFUARTComponent::write_array(const uint8_t *data, size_t len) {
|
|
||||||
xSemaphoreTake(this->lock_, portMAX_DELAY);
|
|
||||||
uart_write_bytes(this->uart_num_, data, len);
|
|
||||||
xSemaphoreGive(this->lock_);
|
|
||||||
#ifdef USE_UART_DEBUGGER
|
|
||||||
for (size_t i = 0; i < len; i++) {
|
|
||||||
this->debug_callback_.call(UART_DIRECTION_TX, data[i]);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
bool IDFUARTComponent::peek_byte(uint8_t *data) {
|
|
||||||
if (!this->check_read_timeout_())
|
|
||||||
return false;
|
|
||||||
xSemaphoreTake(this->lock_, portMAX_DELAY);
|
|
||||||
if (this->has_peek_) {
|
|
||||||
*data = this->peek_byte_;
|
|
||||||
} else {
|
|
||||||
int len = uart_read_bytes(this->uart_num_, data, 1, 20 / portTICK_RATE_MS);
|
|
||||||
if (len == 0) {
|
|
||||||
*data = 0;
|
|
||||||
} else {
|
|
||||||
this->has_peek_ = true;
|
|
||||||
this->peek_byte_ = *data;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
xSemaphoreGive(this->lock_);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool IDFUARTComponent::read_array(uint8_t *data, size_t len) {
|
|
||||||
size_t length_to_read = len;
|
|
||||||
if (!this->check_read_timeout_(len))
|
|
||||||
return false;
|
|
||||||
xSemaphoreTake(this->lock_, portMAX_DELAY);
|
|
||||||
if (this->has_peek_) {
|
|
||||||
length_to_read--;
|
|
||||||
*data = this->peek_byte_;
|
|
||||||
data++;
|
|
||||||
this->has_peek_ = false;
|
|
||||||
}
|
|
||||||
if (length_to_read > 0)
|
|
||||||
uart_read_bytes(this->uart_num_, data, length_to_read, 20 / portTICK_RATE_MS);
|
|
||||||
xSemaphoreGive(this->lock_);
|
|
||||||
#ifdef USE_UART_DEBUGGER
|
|
||||||
for (size_t i = 0; i < len; i++) {
|
|
||||||
this->debug_callback_.call(UART_DIRECTION_RX, data[i]);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
int IDFUARTComponent::available() {
|
|
||||||
size_t available;
|
|
||||||
|
|
||||||
xSemaphoreTake(this->lock_, portMAX_DELAY);
|
|
||||||
uart_get_buffered_data_len(this->uart_num_, &available);
|
|
||||||
if (this->has_peek_)
|
|
||||||
available++;
|
|
||||||
xSemaphoreGive(this->lock_);
|
|
||||||
|
|
||||||
return available;
|
|
||||||
}
|
|
||||||
|
|
||||||
void IDFUARTComponent::flush() {
|
|
||||||
ESP_LOGVV(TAG, " Flushing...");
|
|
||||||
xSemaphoreTake(this->lock_, portMAX_DELAY);
|
|
||||||
uart_wait_tx_done(this->uart_num_, portMAX_DELAY);
|
|
||||||
xSemaphoreGive(this->lock_);
|
|
||||||
}
|
|
||||||
|
|
||||||
void IDFUARTComponent::check_logger_conflict() {}
|
|
||||||
|
|
||||||
} // namespace uart
|
|
||||||
} // namespace esphome
|
|
||||||
|
|
||||||
#endif // USE_ESP32
|
|
||||||
|
|
@ -1,39 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#ifdef USE_ESP_IDF
|
|
||||||
|
|
||||||
#include <driver/uart.h>
|
|
||||||
#include "esphome/core/component.h"
|
|
||||||
#include "uart_component.h"
|
|
||||||
|
|
||||||
namespace esphome {
|
|
||||||
namespace uart {
|
|
||||||
|
|
||||||
class IDFUARTComponent : public UARTComponent, public Component {
|
|
||||||
public:
|
|
||||||
void setup() override;
|
|
||||||
void dump_config() override;
|
|
||||||
float get_setup_priority() const override { return setup_priority::BUS; }
|
|
||||||
|
|
||||||
void write_array(const uint8_t *data, size_t len) override;
|
|
||||||
|
|
||||||
bool peek_byte(uint8_t *data) override;
|
|
||||||
bool read_array(uint8_t *data, size_t len) override;
|
|
||||||
|
|
||||||
int available() override;
|
|
||||||
void flush() override;
|
|
||||||
|
|
||||||
protected:
|
|
||||||
void check_logger_conflict() override;
|
|
||||||
uart_port_t uart_num_;
|
|
||||||
uart_config_t get_config_();
|
|
||||||
SemaphoreHandle_t lock_;
|
|
||||||
|
|
||||||
bool has_peek_{false};
|
|
||||||
uint8_t peek_byte_;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace uart
|
|
||||||
} // namespace esphome
|
|
||||||
|
|
||||||
#endif // USE_ESP_IDF
|
|
||||||
|
|
@ -1,184 +0,0 @@
|
||||||
#ifdef USE_RP2040
|
|
||||||
#include "uart_component_rp2040.h"
|
|
||||||
#include "esphome/core/application.h"
|
|
||||||
#include "esphome/core/defines.h"
|
|
||||||
#include "esphome/core/helpers.h"
|
|
||||||
#include "esphome/core/log.h"
|
|
||||||
|
|
||||||
#include <hardware/uart.h>
|
|
||||||
|
|
||||||
#ifdef USE_LOGGER
|
|
||||||
#include "esphome/components/logger/logger.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace esphome {
|
|
||||||
namespace uart {
|
|
||||||
|
|
||||||
static const char *const TAG = "uart.arduino_rp2040";
|
|
||||||
|
|
||||||
uint16_t RP2040UartComponent::get_config() {
|
|
||||||
uint16_t config = 0;
|
|
||||||
|
|
||||||
if (this->parity_ == UART_CONFIG_PARITY_NONE) {
|
|
||||||
config |= UART_PARITY_NONE;
|
|
||||||
} else if (this->parity_ == UART_CONFIG_PARITY_EVEN) {
|
|
||||||
config |= UART_PARITY_EVEN;
|
|
||||||
} else if (this->parity_ == UART_CONFIG_PARITY_ODD) {
|
|
||||||
config |= UART_PARITY_ODD;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (this->data_bits_) {
|
|
||||||
case 5:
|
|
||||||
config |= SERIAL_DATA_5;
|
|
||||||
break;
|
|
||||||
case 6:
|
|
||||||
config |= SERIAL_DATA_6;
|
|
||||||
break;
|
|
||||||
case 7:
|
|
||||||
config |= SERIAL_DATA_7;
|
|
||||||
break;
|
|
||||||
case 8:
|
|
||||||
config |= SERIAL_DATA_8;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this->stop_bits_ == 1) {
|
|
||||||
config |= SERIAL_STOP_BIT_1;
|
|
||||||
} else {
|
|
||||||
config |= SERIAL_STOP_BIT_2;
|
|
||||||
}
|
|
||||||
|
|
||||||
return config;
|
|
||||||
}
|
|
||||||
|
|
||||||
void RP2040UartComponent::setup() {
|
|
||||||
ESP_LOGCONFIG(TAG, "Setting up UART bus...");
|
|
||||||
|
|
||||||
uint16_t config = get_config();
|
|
||||||
|
|
||||||
constexpr uint32_t valid_tx_uart_0 = __bitset({0, 12, 16, 28});
|
|
||||||
constexpr uint32_t valid_tx_uart_1 = __bitset({4, 8, 20, 24});
|
|
||||||
|
|
||||||
constexpr uint32_t valid_rx_uart_0 = __bitset({1, 13, 17, 29});
|
|
||||||
constexpr uint32_t valid_rx_uart_1 = __bitset({5, 9, 21, 25});
|
|
||||||
|
|
||||||
int8_t tx_hw = -1;
|
|
||||||
int8_t rx_hw = -1;
|
|
||||||
|
|
||||||
if (this->tx_pin_ != nullptr) {
|
|
||||||
if (this->tx_pin_->is_inverted()) {
|
|
||||||
ESP_LOGD(TAG, "An inverted TX pin %u can only be used with SerialPIO", this->tx_pin_->get_pin());
|
|
||||||
} else {
|
|
||||||
if (((1 << this->tx_pin_->get_pin()) & valid_tx_uart_0) != 0) {
|
|
||||||
tx_hw = 0;
|
|
||||||
} else if (((1 << this->tx_pin_->get_pin()) & valid_tx_uart_1) != 0) {
|
|
||||||
tx_hw = 1;
|
|
||||||
} else {
|
|
||||||
ESP_LOGD(TAG, "TX pin %u can only be used with SerialPIO", this->tx_pin_->get_pin());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this->rx_pin_ != nullptr) {
|
|
||||||
if (this->rx_pin_->is_inverted()) {
|
|
||||||
ESP_LOGD(TAG, "An inverted RX pin %u can only be used with SerialPIO", this->rx_pin_->get_pin());
|
|
||||||
} else {
|
|
||||||
if (((1 << this->rx_pin_->get_pin()) & valid_rx_uart_0) != 0) {
|
|
||||||
rx_hw = 0;
|
|
||||||
} else if (((1 << this->rx_pin_->get_pin()) & valid_rx_uart_1) != 0) {
|
|
||||||
rx_hw = 1;
|
|
||||||
} else {
|
|
||||||
ESP_LOGD(TAG, "RX pin %u can only be used with SerialPIO", this->rx_pin_->get_pin());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef USE_LOGGER
|
|
||||||
if (tx_hw == rx_hw && logger::global_logger->get_uart() == tx_hw) {
|
|
||||||
ESP_LOGD(TAG, "Using SerialPIO as UART%d is taken by the logger", tx_hw);
|
|
||||||
tx_hw = -1;
|
|
||||||
rx_hw = -1;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (tx_hw == -1 || rx_hw == -1 || tx_hw != rx_hw) {
|
|
||||||
ESP_LOGV(TAG, "Using SerialPIO");
|
|
||||||
pin_size_t tx = this->tx_pin_ == nullptr ? SerialPIO::NOPIN : this->tx_pin_->get_pin();
|
|
||||||
pin_size_t rx = this->rx_pin_ == nullptr ? SerialPIO::NOPIN : this->rx_pin_->get_pin();
|
|
||||||
auto *serial = new SerialPIO(tx, rx, this->rx_buffer_size_); // NOLINT(cppcoreguidelines-owning-memory)
|
|
||||||
serial->begin(this->baud_rate_, config);
|
|
||||||
if (this->tx_pin_ != nullptr && this->tx_pin_->is_inverted())
|
|
||||||
gpio_set_outover(tx, GPIO_OVERRIDE_INVERT);
|
|
||||||
if (this->rx_pin_ != nullptr && this->rx_pin_->is_inverted())
|
|
||||||
gpio_set_inover(rx, GPIO_OVERRIDE_INVERT);
|
|
||||||
this->serial_ = serial;
|
|
||||||
} else {
|
|
||||||
ESP_LOGV(TAG, "Using Hardware Serial");
|
|
||||||
SerialUART *serial;
|
|
||||||
if (tx_hw == 0) {
|
|
||||||
serial = &Serial1;
|
|
||||||
} else {
|
|
||||||
serial = &Serial2;
|
|
||||||
}
|
|
||||||
serial->setTX(this->tx_pin_->get_pin());
|
|
||||||
serial->setRX(this->rx_pin_->get_pin());
|
|
||||||
serial->setFIFOSize(this->rx_buffer_size_);
|
|
||||||
serial->begin(this->baud_rate_, config);
|
|
||||||
this->serial_ = serial;
|
|
||||||
this->hw_serial_ = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void RP2040UartComponent::dump_config() {
|
|
||||||
ESP_LOGCONFIG(TAG, "UART Bus:");
|
|
||||||
LOG_PIN(" TX Pin: ", tx_pin_);
|
|
||||||
LOG_PIN(" RX Pin: ", rx_pin_);
|
|
||||||
if (this->rx_pin_ != nullptr) {
|
|
||||||
ESP_LOGCONFIG(TAG, " RX Buffer Size: %u", this->rx_buffer_size_);
|
|
||||||
}
|
|
||||||
ESP_LOGCONFIG(TAG, " Baud Rate: %u baud", this->baud_rate_);
|
|
||||||
ESP_LOGCONFIG(TAG, " Data Bits: %u", this->data_bits_);
|
|
||||||
ESP_LOGCONFIG(TAG, " Parity: %s", LOG_STR_ARG(parity_to_str(this->parity_)));
|
|
||||||
ESP_LOGCONFIG(TAG, " Stop bits: %u", this->stop_bits_);
|
|
||||||
if (this->hw_serial_) {
|
|
||||||
ESP_LOGCONFIG(TAG, " Using hardware serial");
|
|
||||||
} else {
|
|
||||||
ESP_LOGCONFIG(TAG, " Using SerialPIO");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void RP2040UartComponent::write_array(const uint8_t *data, size_t len) {
|
|
||||||
this->serial_->write(data, len);
|
|
||||||
#ifdef USE_UART_DEBUGGER
|
|
||||||
for (size_t i = 0; i < len; i++) {
|
|
||||||
this->debug_callback_.call(UART_DIRECTION_TX, data[i]);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
bool RP2040UartComponent::peek_byte(uint8_t *data) {
|
|
||||||
if (!this->check_read_timeout_())
|
|
||||||
return false;
|
|
||||||
*data = this->serial_->peek();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
bool RP2040UartComponent::read_array(uint8_t *data, size_t len) {
|
|
||||||
if (!this->check_read_timeout_(len))
|
|
||||||
return false;
|
|
||||||
this->serial_->readBytes(data, len);
|
|
||||||
#ifdef USE_UART_DEBUGGER
|
|
||||||
for (size_t i = 0; i < len; i++) {
|
|
||||||
this->debug_callback_.call(UART_DIRECTION_RX, data[i]);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
int RP2040UartComponent::available() { return this->serial_->available(); }
|
|
||||||
void RP2040UartComponent::flush() {
|
|
||||||
ESP_LOGVV(TAG, " Flushing...");
|
|
||||||
this->serial_->flush();
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace uart
|
|
||||||
} // namespace esphome
|
|
||||||
|
|
||||||
#endif // USE_RP2040
|
|
||||||
|
|
@ -1,43 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#ifdef USE_RP2040
|
|
||||||
|
|
||||||
#include <SerialPIO.h>
|
|
||||||
#include <SerialUART.h>
|
|
||||||
|
|
||||||
#include <vector>
|
|
||||||
#include "esphome/core/component.h"
|
|
||||||
#include "esphome/core/hal.h"
|
|
||||||
#include "esphome/core/log.h"
|
|
||||||
#include "uart_component.h"
|
|
||||||
|
|
||||||
namespace esphome {
|
|
||||||
namespace uart {
|
|
||||||
|
|
||||||
class RP2040UartComponent : public UARTComponent, public Component {
|
|
||||||
public:
|
|
||||||
void setup() override;
|
|
||||||
void dump_config() override;
|
|
||||||
float get_setup_priority() const override { return setup_priority::BUS; }
|
|
||||||
|
|
||||||
void write_array(const uint8_t *data, size_t len) override;
|
|
||||||
|
|
||||||
bool peek_byte(uint8_t *data) override;
|
|
||||||
bool read_array(uint8_t *data, size_t len) override;
|
|
||||||
|
|
||||||
int available() override;
|
|
||||||
void flush() override;
|
|
||||||
|
|
||||||
uint16_t get_config();
|
|
||||||
|
|
||||||
protected:
|
|
||||||
void check_logger_conflict() override {}
|
|
||||||
bool hw_serial_{false};
|
|
||||||
|
|
||||||
HardwareSerial *serial_{nullptr};
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace uart
|
|
||||||
} // namespace esphome
|
|
||||||
|
|
||||||
#endif // USE_RP2040
|
|
||||||
|
|
@ -1,202 +0,0 @@
|
||||||
#include "esphome/core/defines.h"
|
|
||||||
#ifdef USE_UART_DEBUGGER
|
|
||||||
|
|
||||||
#include <vector>
|
|
||||||
#include "uart_debugger.h"
|
|
||||||
#include "esphome/core/helpers.h"
|
|
||||||
#include "esphome/core/log.h"
|
|
||||||
|
|
||||||
namespace esphome {
|
|
||||||
namespace uart {
|
|
||||||
|
|
||||||
static const char *const TAG = "uart_debug";
|
|
||||||
|
|
||||||
UARTDebugger::UARTDebugger(UARTComponent *parent) {
|
|
||||||
parent->add_debug_callback([this](UARTDirection direction, uint8_t byte) {
|
|
||||||
if (!this->is_my_direction_(direction) || this->is_recursive_()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this->trigger_after_direction_change_(direction);
|
|
||||||
this->store_byte_(direction, byte);
|
|
||||||
this->trigger_after_delimiter_(byte);
|
|
||||||
this->trigger_after_bytes_();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
void UARTDebugger::loop() { this->trigger_after_timeout_(); }
|
|
||||||
|
|
||||||
bool UARTDebugger::is_my_direction_(UARTDirection direction) {
|
|
||||||
return this->for_direction_ == UART_DIRECTION_BOTH || this->for_direction_ == direction;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool UARTDebugger::is_recursive_() { return this->is_triggering_; }
|
|
||||||
|
|
||||||
void UARTDebugger::trigger_after_direction_change_(UARTDirection direction) {
|
|
||||||
if (this->has_buffered_bytes_() && this->for_direction_ == UART_DIRECTION_BOTH &&
|
|
||||||
this->last_direction_ != direction) {
|
|
||||||
this->fire_trigger_();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void UARTDebugger::store_byte_(UARTDirection direction, uint8_t byte) {
|
|
||||||
this->bytes_.push_back(byte);
|
|
||||||
this->last_direction_ = direction;
|
|
||||||
this->last_time_ = millis();
|
|
||||||
}
|
|
||||||
|
|
||||||
void UARTDebugger::trigger_after_delimiter_(uint8_t byte) {
|
|
||||||
if (this->after_delimiter_.empty() || !this->has_buffered_bytes_()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (this->after_delimiter_[this->after_delimiter_pos_] != byte) {
|
|
||||||
this->after_delimiter_pos_ = 0;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this->after_delimiter_pos_++;
|
|
||||||
if (this->after_delimiter_pos_ == this->after_delimiter_.size()) {
|
|
||||||
this->fire_trigger_();
|
|
||||||
this->after_delimiter_pos_ = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void UARTDebugger::trigger_after_bytes_() {
|
|
||||||
if (this->has_buffered_bytes_() && this->after_bytes_ > 0 && this->bytes_.size() >= this->after_bytes_) {
|
|
||||||
this->fire_trigger_();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void UARTDebugger::trigger_after_timeout_() {
|
|
||||||
if (this->has_buffered_bytes_() && this->after_timeout_ > 0 && millis() - this->last_time_ >= this->after_timeout_) {
|
|
||||||
this->fire_trigger_();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool UARTDebugger::has_buffered_bytes_() { return !this->bytes_.empty(); }
|
|
||||||
|
|
||||||
void UARTDebugger::fire_trigger_() {
|
|
||||||
this->is_triggering_ = true;
|
|
||||||
trigger(this->last_direction_, this->bytes_);
|
|
||||||
this->bytes_.clear();
|
|
||||||
this->is_triggering_ = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void UARTDummyReceiver::loop() {
|
|
||||||
// Reading up to a limited number of bytes, to make sure that this loop()
|
|
||||||
// won't lock up the system on a continuous incoming stream of bytes.
|
|
||||||
uint8_t data;
|
|
||||||
int count = 50;
|
|
||||||
while (this->available() && count--) {
|
|
||||||
this->read_byte(&data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// In the upcoming log functions, a delay was added after all log calls.
|
|
||||||
// This is done to allow the system to ship the log lines via the API
|
|
||||||
// TCP connection(s). Without these delays, debug log lines could go
|
|
||||||
// missing when UART devices block the main loop for too long.
|
|
||||||
|
|
||||||
void UARTDebug::log_hex(UARTDirection direction, std::vector<uint8_t> bytes, uint8_t separator) {
|
|
||||||
std::string res;
|
|
||||||
if (direction == UART_DIRECTION_RX) {
|
|
||||||
res += "<<< ";
|
|
||||||
} else {
|
|
||||||
res += ">>> ";
|
|
||||||
}
|
|
||||||
size_t len = bytes.size();
|
|
||||||
char buf[5];
|
|
||||||
for (size_t i = 0; i < len; i++) {
|
|
||||||
if (i > 0) {
|
|
||||||
res += separator;
|
|
||||||
}
|
|
||||||
sprintf(buf, "%02X", bytes[i]);
|
|
||||||
res += buf;
|
|
||||||
}
|
|
||||||
ESP_LOGD(TAG, "%s", res.c_str());
|
|
||||||
delay(10);
|
|
||||||
}
|
|
||||||
|
|
||||||
void UARTDebug::log_string(UARTDirection direction, std::vector<uint8_t> bytes) {
|
|
||||||
std::string res;
|
|
||||||
if (direction == UART_DIRECTION_RX) {
|
|
||||||
res += "<<< \"";
|
|
||||||
} else {
|
|
||||||
res += ">>> \"";
|
|
||||||
}
|
|
||||||
size_t len = bytes.size();
|
|
||||||
char buf[5];
|
|
||||||
for (size_t i = 0; i < len; i++) {
|
|
||||||
if (bytes[i] == 7) {
|
|
||||||
res += "\\a";
|
|
||||||
} else if (bytes[i] == 8) {
|
|
||||||
res += "\\b";
|
|
||||||
} else if (bytes[i] == 9) {
|
|
||||||
res += "\\t";
|
|
||||||
} else if (bytes[i] == 10) {
|
|
||||||
res += "\\n";
|
|
||||||
} else if (bytes[i] == 11) {
|
|
||||||
res += "\\v";
|
|
||||||
} else if (bytes[i] == 12) {
|
|
||||||
res += "\\f";
|
|
||||||
} else if (bytes[i] == 13) {
|
|
||||||
res += "\\r";
|
|
||||||
} else if (bytes[i] == 27) {
|
|
||||||
res += "\\e";
|
|
||||||
} else if (bytes[i] == 34) {
|
|
||||||
res += "\\\"";
|
|
||||||
} else if (bytes[i] == 39) {
|
|
||||||
res += "\\'";
|
|
||||||
} else if (bytes[i] == 92) {
|
|
||||||
res += "\\\\";
|
|
||||||
} else if (bytes[i] < 32 || bytes[i] > 127) {
|
|
||||||
sprintf(buf, "\\x%02X", bytes[i]);
|
|
||||||
res += buf;
|
|
||||||
} else {
|
|
||||||
res += bytes[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
res += '"';
|
|
||||||
ESP_LOGD(TAG, "%s", res.c_str());
|
|
||||||
delay(10);
|
|
||||||
}
|
|
||||||
|
|
||||||
void UARTDebug::log_int(UARTDirection direction, std::vector<uint8_t> bytes, uint8_t separator) {
|
|
||||||
std::string res;
|
|
||||||
size_t len = bytes.size();
|
|
||||||
if (direction == UART_DIRECTION_RX) {
|
|
||||||
res += "<<< ";
|
|
||||||
} else {
|
|
||||||
res += ">>> ";
|
|
||||||
}
|
|
||||||
for (size_t i = 0; i < len; i++) {
|
|
||||||
if (i > 0) {
|
|
||||||
res += separator;
|
|
||||||
}
|
|
||||||
res += to_string(bytes[i]);
|
|
||||||
}
|
|
||||||
ESP_LOGD(TAG, "%s", res.c_str());
|
|
||||||
delay(10);
|
|
||||||
}
|
|
||||||
|
|
||||||
void UARTDebug::log_binary(UARTDirection direction, std::vector<uint8_t> bytes, uint8_t separator) {
|
|
||||||
std::string res;
|
|
||||||
size_t len = bytes.size();
|
|
||||||
if (direction == UART_DIRECTION_RX) {
|
|
||||||
res += "<<< ";
|
|
||||||
} else {
|
|
||||||
res += ">>> ";
|
|
||||||
}
|
|
||||||
char buf[20];
|
|
||||||
for (size_t i = 0; i < len; i++) {
|
|
||||||
if (i > 0) {
|
|
||||||
res += separator;
|
|
||||||
}
|
|
||||||
sprintf(buf, "0b" BYTE_TO_BINARY_PATTERN " (0x%02X)", BYTE_TO_BINARY(bytes[i]), bytes[i]);
|
|
||||||
res += buf;
|
|
||||||
}
|
|
||||||
ESP_LOGD(TAG, "%s", res.c_str());
|
|
||||||
delay(10);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace uart
|
|
||||||
} // namespace esphome
|
|
||||||
#endif
|
|
||||||
|
|
@ -1,101 +0,0 @@
|
||||||
#pragma once
|
|
||||||
#include "esphome/core/defines.h"
|
|
||||||
#ifdef USE_UART_DEBUGGER
|
|
||||||
|
|
||||||
#include <vector>
|
|
||||||
#include "esphome/core/component.h"
|
|
||||||
#include "esphome/core/automation.h"
|
|
||||||
#include "uart.h"
|
|
||||||
#include "uart_component.h"
|
|
||||||
|
|
||||||
namespace esphome {
|
|
||||||
namespace uart {
|
|
||||||
|
|
||||||
/// The UARTDebugger class adds debugging support to a UART bus.
|
|
||||||
///
|
|
||||||
/// It accumulates bytes that travel over the UART bus and triggers one or
|
|
||||||
/// more actions that can log the data at an appropriate time. What
|
|
||||||
/// 'appropriate time' means exactly, is determined by a number of
|
|
||||||
/// configurable constraints. E.g. when a given number of bytes is gathered
|
|
||||||
/// and/or when no more data has been seen for a given time interval.
|
|
||||||
class UARTDebugger : public Component, public Trigger<UARTDirection, std::vector<uint8_t>> {
|
|
||||||
public:
|
|
||||||
explicit UARTDebugger(UARTComponent *parent);
|
|
||||||
void loop() override;
|
|
||||||
|
|
||||||
/// Set the direction in which to inspect the bytes: incoming, outgoing
|
|
||||||
/// or both. When debugging in both directions, logging will be triggered
|
|
||||||
/// when the direction of the data stream changes.
|
|
||||||
void set_direction(UARTDirection direction) { this->for_direction_ = direction; }
|
|
||||||
|
|
||||||
/// Set the maximum number of bytes to accumulate. When the number of bytes
|
|
||||||
/// is reached, logging will be triggered.
|
|
||||||
void set_after_bytes(size_t size) { this->after_bytes_ = size; }
|
|
||||||
|
|
||||||
/// Set a timeout for the data stream. When no new bytes are seen during
|
|
||||||
/// this timeout, logging will be triggered.
|
|
||||||
void set_after_timeout(uint32_t timeout) { this->after_timeout_ = timeout; }
|
|
||||||
|
|
||||||
/// Add a delimiter byte. This can be called multiple times to setup a
|
|
||||||
/// multi-byte delimiter (a typical example would be '\r\n').
|
|
||||||
/// When the constructed byte sequence is found in the data stream,
|
|
||||||
/// logging will be triggered.
|
|
||||||
void add_delimiter_byte(uint8_t byte) { this->after_delimiter_.push_back(byte); }
|
|
||||||
|
|
||||||
protected:
|
|
||||||
UARTDirection for_direction_;
|
|
||||||
UARTDirection last_direction_{};
|
|
||||||
std::vector<uint8_t> bytes_{};
|
|
||||||
size_t after_bytes_;
|
|
||||||
uint32_t after_timeout_;
|
|
||||||
uint32_t last_time_{};
|
|
||||||
std::vector<uint8_t> after_delimiter_{};
|
|
||||||
size_t after_delimiter_pos_{};
|
|
||||||
bool is_triggering_{false};
|
|
||||||
|
|
||||||
bool is_my_direction_(UARTDirection direction);
|
|
||||||
bool is_recursive_();
|
|
||||||
void store_byte_(UARTDirection direction, uint8_t byte);
|
|
||||||
void trigger_after_direction_change_(UARTDirection direction);
|
|
||||||
void trigger_after_delimiter_(uint8_t byte);
|
|
||||||
void trigger_after_bytes_();
|
|
||||||
void trigger_after_timeout_();
|
|
||||||
bool has_buffered_bytes_();
|
|
||||||
void fire_trigger_();
|
|
||||||
};
|
|
||||||
|
|
||||||
/// This UARTDevice is used by the serial debugger to read data from a
|
|
||||||
/// serial interface when the 'dummy_receiver' option is enabled.
|
|
||||||
/// The data are not stored, nor processed. This is most useful when the
|
|
||||||
/// debugger is used to reverse engineer a serial protocol, for which no
|
|
||||||
/// specific UARTDevice implementation exists (yet), but for which the
|
|
||||||
/// incoming bytes must be read to drive the debugger.
|
|
||||||
class UARTDummyReceiver : public Component, public UARTDevice {
|
|
||||||
public:
|
|
||||||
UARTDummyReceiver(UARTComponent *parent) : UARTDevice(parent) {}
|
|
||||||
void loop() override;
|
|
||||||
};
|
|
||||||
|
|
||||||
/// This class contains some static methods, that can be used to easily
|
|
||||||
/// create a logging action for the debugger.
|
|
||||||
class UARTDebug {
|
|
||||||
public:
|
|
||||||
/// Log the bytes as hex values, separated by the provided separator
|
|
||||||
/// character.
|
|
||||||
static void log_hex(UARTDirection direction, std::vector<uint8_t> bytes, uint8_t separator);
|
|
||||||
|
|
||||||
/// Log the bytes as string values, escaping unprintable characters.
|
|
||||||
static void log_string(UARTDirection direction, std::vector<uint8_t> bytes);
|
|
||||||
|
|
||||||
/// Log the bytes as integer values, separated by the provided separator
|
|
||||||
/// character.
|
|
||||||
static void log_int(UARTDirection direction, std::vector<uint8_t> bytes, uint8_t separator);
|
|
||||||
|
|
||||||
/// Log the bytes as '<binary> (<hex>)' values, separated by the provided
|
|
||||||
/// separator.
|
|
||||||
static void log_binary(UARTDirection direction, std::vector<uint8_t> bytes, uint8_t separator);
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace uart
|
|
||||||
} // namespace esphome
|
|
||||||
#endif
|
|
||||||
Loading…
Reference in New Issue
Block a user