esphome: name: uart-mock-ld2412-test host: api: batch_delay: 0ms # Disable batching to receive all state updates logger: level: VERBOSE external_components: - source: type: local path: EXTERNAL_COMPONENT_PATH # Dummy uart entry to satisfy ld2412's DEPENDENCIES = ["uart"] # The actual UART bus used is the uart_mock component below uart: baud_rate: 115200 port: /dev/null uart_mock: id: mock_uart baud_rate: 256000 auto_start: false injections: # Phase 1 (t=100ms): Valid LD2412 normal mode data frame - happy path # The buffer is clean at this point, so this frame should parse correctly. # Moving target: 100cm, energy 50 # Still target: 120cm, energy 25 # Target state: 0x03 (moving + still) # detection_distance = 100 (LD2412 computes from moving target when MOVE_BITMASK set) # # Frame layout (24 bytes): # [0-3] F4 F3 F2 F1 = data frame header # [4-5] 0D 00 = length 13 # [6] 02 = data type (normal) # [7] AA = data header marker # [8] 03 = target states (moving+still) # [9-10] 64 00 = moving distance 100 (0x0064) # [11] 32 = moving energy 50 # [12-13] 78 00 = still distance 120 (0x0078) # [14] 19 = still energy 25 # [15-16] 64 00 = detect distance bytes (ignored by LD2412 code) # [17] 00 = padding # [18] 55 = data footer marker # [19] 00 = CRC/check # [20-23] F8 F7 F6 F5 = data frame footer - delay: 100ms inject_rx: [ 0xF4, 0xF3, 0xF2, 0xF1, 0x0D, 0x00, 0x02, 0xAA, 0x03, 0x64, 0x00, 0x32, 0x78, 0x00, 0x19, 0x64, 0x00, 0x00, 0x55, 0x00, 0xF8, 0xF7, 0xF6, 0xF5, ] # Phase 2 (t=300ms): Garbage bytes # LD2412's parser rejects bytes that don't match the frame header at # position 0 (must start with F4 or FD), so buffer stays empty. - delay: 200ms inject_rx: [0xDE, 0xAD, 0xBE, 0xEF, 0x00, 0x11, 0x22] # Phase 3 (t=400ms): Truncated frame (header + partial data, no footer) # Starts with valid data frame header so parser accepts it. # After this, buffer_pos_ = 8. - delay: 100ms inject_rx: [0xF4, 0xF3, 0xF2, 0xF1, 0x0D, 0x00, 0x02, 0xAA] # Phase 4 (t=600ms): Overflow - inject 60 bytes of 0xFF (MAX_LINE_LENGTH=54) # Buffer has 8 bytes from phase 3 (garbage in phase 2 was rejected). # Overflow math: buffer_pos_ starts at 8, overflow triggers when # buffer_pos_ reaches 53 (MAX_LINE_LENGTH - 1). Need 45 more bytes to # fill positions 8-52, then byte 46 triggers overflow. After overflow, # buffer_pos_ = 0 and remaining 0xFF bytes are rejected (don't match header). - delay: 200ms inject_rx: [ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, ] # Phase 5 (t=700ms): Valid frame after overflow - recovery test # Buffer was reset by overflow. This valid frame should parse correctly. # Moving target: 50cm, energy 100 # Still target: 75cm, energy 80 # detection_distance = 50 (moving target distance, since MOVE_BITMASK set) - delay: 100ms inject_rx: [ 0xF4, 0xF3, 0xF2, 0xF1, 0x0D, 0x00, 0x02, 0xAA, 0x03, 0x32, 0x00, 0x64, 0x4B, 0x00, 0x50, 0x32, 0x00, 0x00, 0x55, 0x00, 0xF8, 0xF7, 0xF6, 0xF5, ] ld2412: id: ld2412_dev uart_id: mock_uart sensor: - platform: ld2412 ld2412_id: ld2412_dev moving_distance: name: "Moving Distance" filters: - timeout: timeout: 50ms value: last - throttle_with_priority: 50ms still_distance: name: "Still Distance" filters: - timeout: timeout: 50ms value: last - throttle_with_priority: 50ms moving_energy: name: "Moving Energy" filters: - timeout: timeout: 50ms value: last - throttle_with_priority: 50ms still_energy: name: "Still Energy" filters: - timeout: timeout: 50ms value: last - throttle_with_priority: 50ms detection_distance: name: "Detection Distance" filters: - timeout: timeout: 50ms value: last - throttle_with_priority: 50ms binary_sensor: - platform: ld2412 ld2412_id: ld2412_dev has_target: name: "Has Target" filters: - settle: 50ms has_moving_target: name: "Has Moving Target" filters: - settle: 50ms has_still_target: name: "Has Still Target" filters: - settle: 50ms button: - platform: template name: "Start Scenario" id: start_scenario_btn on_press: - lambda: 'id(mock_uart).start_scenario();'