How to Control rotary encoder

Hello,
Is possible to mimic rotary encoder with ESP home I want to fully transition to ESP home firmware, but i have like 20 heater valves E-q3 Modified with ESP07 to be wifi capable.

Now i am usig this code to set temperature on them:

#if defined(ROTARY_ENC_PIN_1) && defined(ROTARY_ENC_PIN_2)
void setTemperature(float temp)
{
    int z = 0;
    // Set temperature to OFF
    for (uint8_t i = 0; i < 60; ++i)
    {
        digitalWrite(ROTARY_ENC_PIN_2, z);
        delay(30);
        digitalWrite(ROTARY_ENC_PIN_1, z);
        delay(30);

        z = (z + 1) % 2;
    }

    /* Substract 4.5 from the given temperature to compute the number of steps
        * required to set the desired temperature, as the temperature directly jumps
        * from OFF tp 5C. */
    for (uint8_t i = 0; i < (temp - 4.5) * 2; ++i)
    {
        digitalWrite(ROTARY_ENC_PIN_1, z);
        delay(30);
        digitalWrite(ROTARY_ENC_PIN_2, z);
        delay(30);

        z = (z + 1) % 2;
    }
}
#endif

So far i was unable to get same functionality with ESP HOME it there someone who can cuid me to desired outcome ?

Thank you in advance !

That is the problem i dont need senzor but a Output :slight_smile:
I need esep to act as rotary encoder not to read it :slight_smile:
But thanky for your time :slight_smile:

Got you now!

You would go about this basically the same way as in your original sketch.
The major difference is the delay() function. If you call that from a Lambda in ESPHome (which I think is possible) you will block ESPHome’s event loop. This is not what you want to do.

The better way to go is to use YAML actions instead of Lambdas. The YAML delay action is smart and non-blocking.

Here is a very rough proposal how you could try it in YAML:

output:
  - platform: gpio
    pin: D1
    id: gpio_d1
  - platform: gpio
    pin: D2
    id: gpio_d2

on_...:
  - repeat:
      count: 30
      then:
        - output.turn_on: gpio_d2
        - delay: 30ms
        - output.turn_on: gpio_d1
        - delay: 30ms
        - output.turn_off: gpio_d2
        - delay: 30ms
        - output.turn_off: gpio_d1
        - delay: 30ms
  - repeat:
      count: !lambda |-
          return id(some_sensor).state - 4.5;
      then:
        - output.turn_on: gpio_d1
        - delay: 30ms
        - output.turn_on: gpio_d2
        - delay: 30ms
        - output.turn_off: gpio_d1
        - delay: 30ms
        - output.turn_off: gpio_d2
        - delay: 30ms

Hi Again,
Tahk you :slight_smile: i will try to test it just a question is part vith on_… correct ? or how i can say tat i vant it to act like termosat unt and each point is celsius degree ?

Sorry for beeing souch a noob but i am trying to understand it :slight_smile:

I know nothing about which events shall trigger your process. This is like the caller of your function setTemperature(). It is not in your example so I can’t tell what will call it and at which time.

An example could be an ESPHome number entity that can be changed through HA. The number components provide an on_value event that you can use to trigger the above example.

This is where you will need to do some reading in the ESPHome docs.

hmm… i read documentation whole day an i am unable to get it working is seems like value is not provisioned to hass :frowning:

Show your code and as much error information as possible. Help us to help you.

esphome:
  name: vasek-heater

esp8266:
  board: esp01_1m

# Enable logging
logger:

# Enable Home Assistant API
api:

ota:
  password: ""

wifi:
  ssid: !secret wifi_ssid
  password: !secret wifi_password

  # Enable fallback hotspot (captive portal) in case wifi connection fails
  ap:
    ssid: "Vasek-Heater Fallback Hotspot"
    password: ""

captive_portal:

output:
   
switch:
  - platform: gpio
    pin: 5
    id: gpio_d1
  - platform: gpio
    pin: 4
    id: gpio_d2
    
    
 #climate:
 # - platform: thermostat
#    name: "Thermostat Climate Controller"
#    sensor: my_temperature_sensor
#    default_target_temperature_low: 20 °C
#    min_heating_off_time: 300s
#    min_heating_run_time: 300s
#    min_idle_time: 30s
#    heat_action:
#      - repeat:
#          count: 30
 #       then:
  #        - output.turn_on: gpio_d2
   #       - delay: 30ms
    ##      - output.turn_on: gpio_d1
      #    - delay: 30ms
       #   - output.turn_off: gpio_d2
       #   - delay: 30ms
       #   - output.turn_off: gpio_d1
      #    - delay: 30ms
      #- repeat:
      #    count: !lambda |-
      #      return id(some_sensor).state - 4.5;
      #  then:
      #    - output.turn_on: gpio_d1
      #    - delay: 30ms
      #    - output.turn_on: gpio_d2
      #    - delay: 30ms
      #    - output.turn_off: gpio_d1
      #    - delay: 30ms
      #    - output.turn_off: gpio_d2
      #    - delay: 30ms
    #idle_action:
    #  - repeat:
     #     count: 30
    ##    then:
     #     - output.turn_on: gpio_d2
      ##    - delay: 30ms
        #  - output.turn_on: gpio_d1
         # - delay: 30ms
        #  - output.turn_off: gpio_d2
         # - delay: 30ms
        #  - output.turn_off: gpio_d1
        #  - delay: 30ms#

I am not even able to Compile and upload it basicaly how it should vork is folowing

on seting the temperature shoud run foloving code first to null the encoder

- repeat:
#          count: 30
 #       then:
  #        - output.turn_on: gpio_d2
   #       - delay: 30ms
    ##      - output.turn_on: gpio_d1
      #    - delay: 30ms
       #   - output.turn_off: gpio_d2
       #   - delay: 30ms
       #   - output.turn_off: gpio_d1
      #    - delay: 30ms

and then actual set of the temperature

- repeat:
      #    count: !lambda |-
      #      return id(some_sensor).state - 4.5;
      #  then:
      #    - output.turn_on: gpio_d1
      #    - delay: 30ms
      #    - output.turn_on: gpio_d2
      #    - delay: 30ms
      #    - output.turn_off: gpio_d1
      #    - delay: 30ms
      #    - output.turn_off: gpio_d2
      #    - delay: 30ms

where if understand correctly id(some_sensor).state should represent actual value of Celsius degree

ins there some funxtion liken in js like this.state whitch just retur selected temperature ?

Use the logger component and it’s logger.log action:

:confused: i tryed like 3 hours no luck :frowning: i am sentence to frizze toi dead :frowning: ESP Home is actually harder than using plain code at least for me :frowning:

1 Like
esphome:
  name: vasek-heater

esp8266:
  board: esp01_1m

# Enable logging
logger:

# Enable Home Assistant API
api:

ota:
  password: ""

wifi:
  ssid: !secret wifi_ssid
  password: !secret wifi_password

  # Enable fallback hotspot (captive portal) in case wifi connection fails
  ap:
    ssid: "Vasek-Heater Fallback Hotspot"
    password: ""

captive_portal:

# Example configuration entry
globals:
  - id: ch1s
    type: float
    restore_value: false
    initial_value: "5.6"
    
sensor:
  - platform: template
    name: x
    id: x
    lambda: |-
      return float(id(ch1s));
    update_interval: 60s

output:
  - platform: gpio
    pin: 12
    id: gpio_d1
  - platform: gpio
    pin: 13
    id: gpio_d2
    
# Example single-point configuration entry (for heating only)
climate:
  - platform: thermostat
    name: "Thermostat Climate Controller"
    sensor: x
    default_target_temperature_low: 20 °C
    min_heating_off_time: 300s
    min_heating_run_time: 300s
    min_idle_time: 30s
    heat_action:
        repeat:
          count: !lambda |-
            return 30 - 4.5;
          then:
            - output.turn_on: gpio_d1
            - delay: 30ms
            - output.turn_on: gpio_d2
            - delay: 30ms
            - output.turn_off: gpio_d1
            - delay: 30ms
            - output.turn_off: gpio_d2
            - delay: 30ms
    idle_action:
        repeat:
          count: 30
          then:
          - output.turn_on: gpio_d2
          - delay: 30ms
          - output.turn_on: gpio_d1
          - delay: 30ms
          - output.turn_off: gpio_d2
          - delay: 30ms
          - output.turn_off: gpio_d1
          - delay: 30ms

I manage to get it working kinda :frowning: but i still need to get actual degreees set to heater action and to senzor global variable ch1s any advise ?

Little progress on code almost vorking but my variable ch1s is not set and it stays 0

# Example configuration entry
globals:
  - id: ch1s
    type: float
    restore_value: false
    initial_value: "5.6"
    
sensor:
  - platform: template
    name: x
    id: x
    lambda: |-
      return float(id(ch1s));
    update_interval: 30s

output:
  - platform: gpio
    pin: 12
    id: gpio_d1
  - platform: gpio
    pin: 13
    id: gpio_d2
    
# Example single-point configuration entry (for heating only)
climate:
  - platform: thermostat
    name: "Thermostat Climate Controller"
    id: temperature_sensor
    sensor: x
    default_target_temperature_low: 20 °C
    min_heating_off_time: 300s
    min_heating_run_time: 300s
    min_idle_time: 30s
    visual:
      min_temperature: 0.5 °C
      max_temperature: 30 °C
      temperature_step: 0.5 °C
    heat_action:
        - repeat:
            count: 30
            then:
              - output.turn_on: gpio_d2
              - delay: 30ms
              - output.turn_on: gpio_d1
              - delay: 30ms
              - output.turn_off: gpio_d2
              - delay: 30ms
              - output.turn_off: gpio_d1
              - delay: 30ms
        - repeat:
            count: !lambda |-
              return id(temperature_sensor).target_temperature - 4.5;
            then:
              - output.turn_on: gpio_d1
              - delay: 30ms
              - output.turn_on: gpio_d2
              - delay: 30ms
              - output.turn_off: gpio_d1
              - delay: 30ms
              - output.turn_off: gpio_d2
              - delay: 30ms
    idle_action:
        repeat:
          count: 30
          then:
          - output.turn_on: gpio_d2
          - delay: 30ms
          - output.turn_on: gpio_d1
          - delay: 30ms
          - output.turn_off: gpio_d2
          - delay: 30ms
          - output.turn_off: gpio_d1
          - delay: 30ms
    on_state:
      - logger.log:
          format: "The temperature set value %.1f virtual senzor %.1f"
          args: [ 'id(temperature_sensor).target_temperature', 'id(x).state']
      - globals.set:
          id: ch1s
          value: id(temperature_sensor).target_temperature

anny sugestion why ?

I am happy to see that you are homing in on a solution.

You don’t need a float() cast here. Your global is already of type float.
Just use

    lambda: |-
      return id(ch1s);

Hint:
When you would really need to parse a string to a float you would do: parse_number<float>("123.4")

1 Like

Hello :slight_smile:
Firmware is working but endocer is not setin any position and i dont know why is my loop implementation wron since i dont see log output from it ?

sensor:
  - platform: template
    id: x
    lambda: !lambda |-
      return float(id(variable_temp));

output:
  - platform: gpio
    pin: 12
    id: gpio_d1
  - platform: gpio
    pin: 13
    id: gpio_d2
    
# Example single-point configuration entry (for heating only)
climate:
  - platform: thermostat
    name: "Thermostat Climate Controller"
    id: temperature_sensor
    sensor: x
    default_target_temperature_low: 20 °C
    min_heating_off_time: 300s
    min_heating_run_time: 300s
    min_idle_time: 30s
    visual:
      min_temperature: 0.5 °C
      max_temperature: 30 °C
      temperature_step: 0.5 °C
    heat_action:
        - repeat:
            count: 60
            then:
              - output.turn_on: gpio_d2
              - delay: 30ms
              - output.turn_on: gpio_d1
              - delay: 30ms
        - repeat:
            count: !lambda |-
              return id(temperature_sensor).target_temperature - 4.5;
            then:
              - logger.log: "Idle"
              - output.turn_on: gpio_d1
              - delay: 30ms
              - output.turn_on: gpio_d2
              - delay: 30ms
    idle_action:
      - logger.log: "Idle"
    on_state:
      - logger.log:
          format: "The temperature set value %.1f virtual senzor %.1f global"
          args: [ 'id(temperature_sensor).target_temperature', 'id(x).state']
      - globals.set:
          id: variable_temp
          value: !lambda 'return id(temperature_sensor).target_temperature;'

also i still dont know how to utilize z (count) value as in my previouse sketch since it neeed to be count per loop :frowning:

EDIT:
I think i will need to alter it to have something as this but i am unable to compile then since there ate 2 acitons :frowning: any advice ?

image

EDIT:
Wouldent be better to use

output:
  - platform: esp8266_pwm
    id: pwm_output

instead of set level ?

Your last code example makes things unnecessary complex. Why do you want to work with modulo instead of simply switching on, waiting, switching off, waiting and repeating? Do you actually understand your original (= old) code or was it just a copy paste action? You need to understand what you do.

If nothing happens in your logs it might be simply because your actions don’t trigger. I am not really familiar with the climate and thermostat components. But it is possible that heat_action is only triggered once when the system goes from cooling to heating? Maybe you have to use climate.on_state instead. From the docs:

This trigger is activated each time the state of the climate device is updated (for example, if the current temperature measurement or the mode set by the users changes).

Hello yes I understand my original code, i write it ages ago, i am using digital write and seting certain value so in the end i am basically trying to generate the valve pattern similar what is done by encoder. But it starts to be really messy for me to write code in yamlI will try to move thinks to On_state but it seems to me that no signal is actually outputed out of board unfortunately I don’t have oscilloscope anymore. I tried original code you suggest on/off but it did not work so i am starting to try everything reasonable/not reasonable :frowning:

Why don’t you start small and easy, with a climate and thermostat component that only contains logging commands in their heat_action and in their on_state blocks. This way you can test when which action is triggered. You can find out which action happens whenever you change the target temperature. That is the one you want to use for simulating your rotary encoder.

From there you slowly build up. Keep the logging actions and gradually add more commands to it.

This may be late, or silly, but have you considered just (mechanically) connecting a servo or stepper motor to the rotary encoder you wish to operate, and then just coding to have the servo rotate to the specific positions needed to operate the rotary knob?

I already have this part working only think what is not working is generation of signal wave other thinks in code are working, i even mange to move encoder, bit other problem rised, now o have problem if i repeat 30 time i one direction ant 30 times in other only first repeat is ever working, like if i upload onli one repeat for example to up direction encoder is moved correctly 30 pints up, if i upload only other direction it is moved 30 times down as expected, but if i try to null it 30 times down direction and then 30 times up it only goes 30 time down for some reason. I till think that i mess up timing shomehow in esp home :frowning: i am really struggling to understand loop you suggested sinc you are doing 30 repeat instead of 50 and doing full signal wave instead only one site perchange :frowning: