Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Ceiling fan control not working with KC868-AGv3
#11
The fan (CARRO USA Osborn) uses a rolling counter, so a captured frame can never be replayed. It is rc_switch protocol 1, 297us pulse, 32 bits:

   
Code:
[16-bit remote ID][12-bit command][4-bit counter]

The counter increments by one on every button press on the physical remote, whichever button it is, and the fan ignores a frame whose counter it has already seen. That is why capture and replay looks correct on the air but does nothing at the fan, and why bumping the repeat count does not help.

Commands for my remote (the 12 bits), remote ID 1110111110001111:


Code:
    off              000000010001
    speed up         000000110001
    speed down       000000110000
    forward/reverse  000000100001
    breeze/nature    000001000001
    light white      000001010000
    light yellow     000001100000
    light down       000001110000
    light up         000010000000

Two things were needed to make transmission work:

  1. Track the counter. The receiver watches the physical remote and stores the last counter it saw; every transmission sends the stored value plus one. That keeps the ESP32 and the remote in sync no matter which one is used.
  2. Send the repeats back to back. With `wait_time: 0ms` the fan responds. With a 40ms gap between repeats it ignores the burst entirely, no matter how many repeats are sent.

I gave up on KCS for this and used ESPHome, because the counter arithmetic has to happen on the device. Below is what I run on the AGv3. The fan logic is a package so each fan and remote pair is a few lines of configuration.

common/rf_fan.yaml:

Code:
# One 433MHz RF ceiling fan remote (rc_switch protocol 1).
#
# Frame: 16-bit remote ID, 12-bit command, 4-bit rolling counter. The counter
# increments on every press of any button and the fan rejects stale values, so
# we track it from the physical remote and send counter+1.
#
# Each new press on the physical remote also fires an esphome.rf_fan_button
# event in Home Assistant with fan_id and command (12-bit command, decimal).
# The remote repeats each frame several times; only the first frame with a new
# counter fires an event. Our own transmissions update the counter before
# sending, so they never fire events.
#
# Vars:
#   fan_id:       C++ identifier prefix, e.g. living_room
#   fan_name:     entity name prefix, e.g. "Living Room"
#   remote_id:    first 16 bits of the remote's frames, e.g. "0xEF8F"
#   pulse_length: base pulse in microseconds (optional)

defaults:
  pulse_length: "297"

remote_receiver:
  on_rc_switch:
    then:
      - if:
          condition:
            lambda: |-
              return x.protocol == 1 && (x.code >> 16) == ${remote_id} &&
                     (x.code & 0xF) != id(${fan_id}_counter);
          then:
            - lambda: 'id(${fan_id}_counter) = x.code & 0xF;'
            - homeassistant.event:
                event: esphome.rf_fan_button
                data:
                  fan_id: ${fan_id}
                  command: !lambda 'return to_string((x.code >> 4) & 0xFFF);'

globals:
  - id: ${fan_id}_counter
    type: uint8_t
    restore_value: true
    initial_value: "0"

script:
  - id: ${fan_id}_send
    mode: queued
    parameters:
      command: int
    then:
      - lambda: 'id(${fan_id}_counter) = (id(${fan_id}_counter) + 1) & 0xF;'
      - remote_transmitter.transmit_rc_switch_raw:
          transmitter_id: rf_tx
          code: !lambda |-
            uint32_t frame = ((uint32_t) ${remote_id} << 16) |
                             (((uint32_t) command & 0xFFF) << 4) |
                             id(${fan_id}_counter);
            std::string code;
            for (int i = 31; i >= 0; i--)
              code += ((frame >> i) & 1) ? '1' : '0';
            return code;
          protocol:
            pulse_length: ${pulse_length}
          repeat:
            times: 8
            wait_time: 0ms

button:
  - platform: template
    name: "${fan_name} Fan Off"
    on_press:
      - script.execute:
          id: ${fan_id}_send
          command: 0x011

  # Turns the fan on and increases speed; also exits Breeze/Nature mode.
  - platform: template
    name: "${fan_name} Fan Speed Up"
    on_press:
      - script.execute:
          id: ${fan_id}_send
          command: 0x031

  # Turns the fan on and decreases speed; also exits Breeze/Nature mode.
  - platform: template
    name: "${fan_name} Fan Speed Down"
    on_press:
      - script.execute:
          id: ${fan_id}_send
          command: 0x030

  # Toggles summer (counter-clockwise) and winter (clockwise); fan must be running.
  - platform: template
    name: "${fan_name} Fan Forward Reverse"
    on_press:
      - script.execute:
          id: ${fan_id}_send
          command: 0x021

  # Toggles between Breeze and Nature modes.
  - platform: template
    name: "${fan_name} Fan Breeze Nature"
    on_press:
      - script.execute:
          id: ${fan_id}_send
          command: 0x041

  - platform: template
    name: "${fan_name} Light White"
    on_press:
      - script.execute:
          id: ${fan_id}_send
          command: 0x050

  - platform: template
    name: "${fan_name} Light Yellow"
    on_press:
      - script.execute:
          id: ${fan_id}_send
          command: 0x060

  - platform: template
    name: "${fan_name} Light Down"
    on_press:
      - script.execute:
          id: ${fan_id}_send
          command: 0x070

  - platform: template
    name: "${fan_name} Light Up"
    on_press:
      - script.execute:
          id: ${fan_id}_send
          command: 0x080

kc868-agv3.yaml, the relevant parts:

Code:
remote_receiver:
  pin: GPIO9
  dump:
    - rc_switch
  tolerance: 50%
  filter: 4us
  idle: 4ms

remote_transmitter:
  - id: rf_tx
    pin: GPIO2
    carrier_duty_percent: 100%
    non_blocking: true

packages:
  living_room_fan: !include
    file: common/rf_fan.yaml
    vars:
      fan_id: living_room
      fan_name: "Living Room"
      remote_id: "0xEF8F"
  library_fan: !include
    file: common/rf_fan.yaml
    vars:
      fan_id: library
      fan_name: "Library"
      remote_id: "0x2FC8"

Six fans run this way now. To add one, capture the first 16 bits of its remote's frames from the rc_switch dump and add another package entry.

Two notes on the receiver settings. Do not raise `idle` to 10ms: at that setting the constant 433MHz background noise merges with real packets and nothing decodes at all. And the `raw` dump at these thresholds is pure noise, so it was no help in identifying an unknown protocol.

Each new press on the physical remote also fires an `esphome.rf_fan_button` event in Home Assistant with the fan ID and the command, so the remotes can drive other things. That needs "Allow the device to perform Home Assistant actions" enabled on the ESPHome integration for the device.
Reply
#12
thanks share your code.
Reply


Forum Jump:


Users browsing this thread: 1 Guest(s)