Rotary dial + ESP32

For what it’s worth, a regular piece of ESPHome code (not a custom component, now deprecated) to make use of a the rotary dial of a vintage phone as a sensor. Can be used to trigger actions, etc.

Relevant parts of yaml below:

binary_sensor:
  - platform: gpio
    id: rotary_active
    pin:
      number: 37
      inverted: true
      mode:
        input: true
        pullup: true
    filters:
      delayed_on_off: 50ms
    on_press:
      then:
        - lambda: |-
            id(rotaryCount) = 0;
    on_release:
      then:
        - lambda: |-
            if( id(rotaryCount)>0 ){
              std::string s = id(dialed_number_string).c_str() ;
              int i = (int)id(rotaryCount) % 10 ;    // %10 : pulse count 10 corresponds to dialed number 0
              // publish the dialed number
              id(rotary_dial).publish_state( i ) ;
              // publish the entire dialed number
              s += to_string( i ) ;
              id(dialed_number_string) = s ;
              id(dialed_number).publish_state( id(dialed_number_string) ) ;
            }
        - script.execute: rotary_dial_timer_script

  - platform: gpio
    id: rotary_pulses
    pin:
      number: 39
      inverted: true
      mode:
        input: true
        pullup: true
    filters:
      delayed_on_off: 3ms
    on_press:
      then:
        - lambda: |-
            if( id(rotary_active).state ){
              id(rotaryCount)++;
            }

sensor:
  - platform: template
    name: Rotary Dial
    id: rotary_dial
    update_interval: never
    accuracy_decimals: 0

text_sensor:
  - platform: template
    name: Dialed number
    id: dialed_number
    icon: mdi:numeric
    lambda: |-
      return {};
    update_interval: never
    on_value: 
      then:
        - if:
            condition:
              and:
                - lambda: 'return id(dialed_number).state == "123" ;'
            then:
              - delay: 30ms
              - script.stop: rotary_dial_timer_script
              - script.execute: reset_dialed_number_script
              # insert here the action to trigger

globals:
  - id: dialed_number_string
    type: std::string
    initial_value: ""
    restore_value: false
  - id: rotaryCount
    type: int
    initial_value: '0'
    restore_value: false

script:
  - id: rotary_dial_timer_script
    mode: restart
    then:
      - delay: 12 sec #time-out
      - script.execute: reset_dialed_number_script

  - id: reset_dialed_number_script
    mode: restart
    then:
      - lambda: |-
          id(dialed_number_string) = "" ;
          id(dialed_number).publish_state( id(dialed_number_string) )  ;

This piece of code works with that kind of dial (here, a S63 phone dial).

  • red wire: rotary_pulses / red+white wire : gnd
  • blue wire: rotary_active / blue+white wire : gnd
4 Likes

This is a cool project. I tested it out with my own setup (same phone) and what I’m getting in the dialed number string is a series of 1’s instead of a count. So “111” should be “3”, “11111” = “5”, etc. Is the above your final working lambda template or am I missing something?

Yes this is the final piece of yaml code.
I would be able to try and find the glitch by looking at the code you implemented…

Nothing to see… my code is exactly the same, that’s why I’m stuck on it.

It may also be a mechanical or wiring issue. Given your description, it’s like if the rotary_active binary sensor closed each time there was a pulse from the rotary dial.
Maybe you could try first to test with a multimeter (beep mode) your hardware, see if it works as expected : the rotary dial should contact only when in standby position, and the rotary pulses contact only when the dial is turning and the corresponding number of times.

I have this S63 dial, and also a FeTaP (German) one. Works well with both.