Display knob

How far away is this:

From running ESP Home and doing a knob controlled modal info display / control input?

It could go all kinds of ways, but for a simple concept I see the knob switching between modes, blue background displays for info, green background displays would include controls, and maybe spontaneous alerts could show with red background.

Ideally, I would want some kind of “easily” editable config file to define the modes so they could be updated / developed without breaking out the whole tool chain for each new refinement.

Now I am picturing a mode where you can rotate through “channel” selections for the TV and music player… I actually developed something like that for a http server that runs scripts on the HTPC, but hopping up to the HA level brings in a wider gamut of controls.

That display is not supported and probably won’t be for a while as it has 2 different ESP32 chips.

Get this one. It already has full support for ESPhome.

2 Likes

Quite close I guess. Not sure about the battery part:

1 Like

That is a whole rabbit-hole world of stuff over there. The Core 2 also looks nice, touchscreen only instead of dial, no RFID reader, but battery is included:

FWIW, I’ve got both the Waveshare and the M5 now (Waveshare only just arrived this evening).

The Waveshare is more of a complete product; it feels far more robust (and I’ve gone for the ones with a built-in battery). The M5Stack Dial is a much more exposed experimenter’s product. Overall, I prefer the anodised aluminium of the Waveshare - but they’re both pretty good.

Now I need to sort out my ESP dev env… I’ve been more focused on Pi Picos lately.

2 Likes

Got mine today……this was not a great first time non esphome project! I’m about 20 hours deep learning about vscode/libraries, bin files, firmware flashing, I’ve read every piece of documentation there is!
Have you had a chance to build custom “firmware” for this device? Would love to chat!

Hi,
i got it kind of working but im still not happy since a lot of things are still missing.

i also started here:

i will update in the next days the yaml there since i got a couple new things working like battery etc

There are probably other ppl there which are better than me, so feel free to post here improvements etc

3 Likes

The device(s) work fine in esphome, problem is that for unknown reasons they put audio out on the esp32 and mic + display on the esp32-s3, makes it hard to use it as i voice assistant. so i put it back in the drawer :laughing:

you can flash both esp’s from esphome, just flip the usb-c plug.

here is what i got for the esp32-s3 side before i put it back in the drawer:

substitutions:
  name: esphome-web-d1e9b4
  friendly_name: Xiaozhi Knob

esphome:
  name: ${name}
  friendly_name: ${friendly_name}
  min_version: 2025.5.0
  name_add_mac_suffix: false

esp32:
  board: esp32-s3-devkitc-1
  flash_size: 16MB
  cpu_frequency: 240MHz
  framework:
    type: esp-idf
    sdkconfig_options:
      CONFIG_ESP32S3_DEFAULT_CPU_FREQ_240: "y"
      CONFIG_ESP32S3_DATA_CACHE_64KB: "y"
      CONFIG_ESP32S3_DATA_CACHE_LINE_64B: "y"

psram:
  mode: octal
  speed: 80MHz

api:

ota:
  - platform: esphome
    id: ota_esphome

logger:
  level: DEBUG

wifi:
  ssid: !secret wifi_ssid
  password: !secret wifi_password
  ap:
    ssid: "Xiaozhi 3.5 Hotspot"
    password: "RZ7D3EzJdPM6"

captive_portal:

uart:
  id: audio_uart
  tx_pin: GPIO41
  rx_pin: GPIO40
  baud_rate: 115200

i2c:
  - id: bus_a
    sda: GPIO11
    scl: GPIO12
    scan: true

sensor:
  - platform: adc
    pin: 1
    name: "Battery Voltage"
    id: battery_voltage
    attenuation: 12db
    accuracy_decimals: 2
    update_interval: 1s
    unit_of_measurement: "V"
    icon: mdi:battery-medium
    filters:
      - multiply: 2.0
      - median:
          window_size: 7
          send_every: 7
          send_first_at: 7
      - throttle: 1min
    on_value:
      then:
        - component.update: battery_percentage

  - platform: rotary_encoder
    id: knob
    name: "Rotary Knob Raw"
    pin_a:
      number: GPIO8
      mode: INPUT_PULLUP
    pin_b:
      number: GPIO7
      mode: INPUT_PULLUP
    resolution: 2
    on_clockwise:
      then:
        - logger.log: "Knob turned → CLOCKWISE"
    on_anticlockwise:
      then:
        - logger.log: "Knob turned → COUNTER-CLOCKWISE"


  - platform: template
    id: battery_percentage
    name: "Battery Percentage"
    lambda: return id(battery_voltage).state;
    accuracy_decimals: 0
    unit_of_measurement: "%"
    icon: mdi:battery-medium
    filters:
      - calibrate_linear:
         method: exact
         datapoints:
          - 2.80 -> 0.0
          - 3.10 -> 10.0
          - 3.30 -> 20.0
          - 3.45 -> 30.0
          - 3.60 -> 40.0
          - 3.70 -> 50.0
          - 3.75 -> 60.0
          - 3.80 -> 70.0
          - 3.90 -> 80.0
          - 4.00 -> 90.0
          - 4.20 -> 100.0
      - lambda: |-
          if (x > 100) return 100;
          if (x < 0) return 0;
          return x;

switch:
  - platform: gpio
    name: "PA CTRL"
    pin: 48
    restore_mode: RESTORE_DEFAULT_ON

i2s_audio:
  - id: i2s_in
    i2s_lrclk_pin: GPIO45
    i2s_bclk_pin: GPIO42


microphone:
  - platform: i2s_audio
    id: i2s_microphone
    i2s_audio_id: i2s_in
    i2s_din_pin: GPIO46
    adc_type: external
    pdm: false
    channel: left
    sample_rate: 16000
    bits_per_sample: 32bit

time:
  - platform: sntp
    id: sntp_time
    timezone: "Europe/Berlin"

font:
  - file: "gfonts://Roboto"
    id: my_font
    size: 28
  - file: "gfonts://Roboto"
    id: roboto_52
    size: 52

binary_sensor:
  - platform: template
    name: "Touch Button"
    id: touch_input

# SCREEN & TOUCH

output:
  - platform: ledc
    pin: 47
    id: backlight_output

light:
  - platform: monochromatic
    id: Sled
    name: Screen
    icon: "mdi:television"
    entity_category: config
    output: backlight_output
    restore_mode: ALWAYS_ON
    default_transition_length: 250ms

external_components:
  - source: github://pr#10392
    components: [mipi_spi]
    refresh: 1d

spi:
  id: display_qspi
  type: quad
  clk_pin: 13
  data_pins: [15, 16, 17, 18]

touchscreen:
  - platform: cst816
    id: my_touchscreen
    interrupt_pin: GPIO9
    reset_pin: GPIO10
    display: main_display
    on_touch:
      then:
        - logger.log:
            format: "Touch at (%d, %d)"
            args: [touch.x, touch.y]
        - binary_sensor.template.publish:
            id: touch_input
            state: ON
    on_release:
      then:
        - binary_sensor.template.publish:
            id: touch_input
            state: OFF

display:
  - platform: mipi_spi
    id: main_display
    model: JC3636W518V2
    rotation: 180
    cs_pin: 14
    reset_pin: 21
    update_interval: 1s

    lambda: |-
      auto black = Color(0,0,0), red = Color(255,0,0), green = Color(0,255,0), blue = Color(0,0,255), yellow = Color(255,255,0), white = Color(255,255, 255), cyberlightgreen = Color(0,255,159), cyberlightblue = Color(0,184,255), cyberpink = Color(214,0,255);
      it.fill(black);
      it.strftime(180, 180, id(roboto_52), cyberlightgreen, TextAlign::CENTER, "%H:%M:%S", id(sntp_time).now());


3 Likes

Hi @RealDeco, thanks for that i changed some things on my code and it works.

i couldnt test the audio and mic stuff since i get an error that i need arduino framework for that.

the rotary encoder also doesnt work for me, probably because its not waveshare but an Guition device?
at least the custom rotary component i created seems to work: WaveShare-Knob-Esp32S3/components/rotary_encoder_custom at main · KrX3D/WaveShare-Knob-Esp32S3 · GitHub

did you get the other SD Card and Vibration motor DRV2605 working?

i stopped when i couldn’t get audo to work, and yes the guition is red, and the waveshare is blue and it looked like a promising device, but it’s a confusing device to work with in esphome :frowning:

Hi @RealDeco , I used your yaml configuration for Waveshare ESP32S3 1.8 rotary knob (blue). The touch works and the time is displayed. However when I rotate the knob it just displays the rotation direction once and does not log the direction no matter how many times the knob is rotated. Requesting your help to figure out the problem. Attaching log snapshot for your reference.

It is not directly comparable, but i use this component https://esphome.io/components/sensor/rotary_encoder/ with a M5Stack Dial.

# --- Rotary Encoder Sensor Component ---
sensor:
  # The platform name is 'rotary_encoder'
  - platform: rotary_encoder
    id: room_dial_sensor
    # The pins you provided for the ESP32-S3 Dial
    pin_a: 
      number: GPIO40
      mode:
        input: true
        pullup: true
    pin_b: 
      number: GPIO41
      mode:
        input: true
        pullup: true
    # The rotary encoder value will increase/decrease by 1 per click/detent
    name: "Dial Step Count"
    resolution: 1
    
    # NEW: Use lambdas to check boundaries before setting the next value
    on_clockwise:
      then:
        - lambda: |-
            const float MAX_TEMP = 28.0;
            const float STEP_SIZE = 0.5;
            
            float current_state = id(setpoint_number).state;
            
            // Only increment if the current state is less than the maximum value
            if (current_state < MAX_TEMP) {
              float new_value = current_state + STEP_SIZE;
              // Ensures we don't accidentally exceed MAX_TEMP due to floating point math
              if (new_value > MAX_TEMP) new_value = MAX_TEMP;
              
              id(setpoint_number).publish_state(new_value);
            }

    on_anticlockwise:
      then:
        - lambda: |-
            const float MIN_TEMP = 16.0;
            const float STEP_SIZE = 0.5;
            
            float current_state = id(setpoint_number).state;
            
            // Only decrement if the current state is greater than the minimum value
            if (current_state > MIN_TEMP) {
              float new_value = current_state - STEP_SIZE;
              // Ensures we don't accidentally drop below MIN_TEMP
              if (new_value < MIN_TEMP) new_value = MIN_TEMP;

              id(setpoint_number).publish_state(new_value);
            }

I hope it’s helpful.

1 Like

Thank you @juergenjw. I shall try this :saluting_face:

I also bought the Waveshare Knob and your work on github is very helpful. Thank you for sharing.

1 Like

Is there a consensus on waveshare vs m5stack?

I’m looking for a display for turning on my garage heater. I can see these being good for turning on, setting temp with the dial, setting a timer to turn off the heater with a dial.

Also could display current temp.

Not controlling the heater directly. Just as a UI.

M5stack is 1.8” so a little on the smaller side, but preference goes to whatever is more supported and ready to go out of the box…

I got the audio to work. Well the output anyway. My problem is it doesn’t have a built-in speaker. So you need a speaker also.
There are schematics here that show it’s a PCM5100, and here is an example ESPHome config for that chip, just set the dac_type to external.

The example below should get anyone most of the way there.

But since I want a speaker+microphone, I’ll be moving on to implementation with this thing. No screen or knob, but that’s not the part I want first…

substitutions:
  i2c_id: internal_i2c
  i2s_mclk_multiple: 256
  i2s_bps_spk: 16bit
  i2s_bps_mic: 16bit
  i2s_sample_rate_spk: 16000
  i2s_sample_rate_mic: 16000 # must be 16000 for voice_assistant to work(?)
  i2s_use_apll: true
  task_stack_in_psram: True

psram:
  mode: octal
  speed: 80MHz

switch:
  - platform: gpio
    name: "PA CTRL"
    pin: GPIO0
    restore_mode: RESTORE_DEFAULT_ON

i2s_audio:
  - id: i2s_output_id
    i2s_lrclk_pin: GPIO40
    i2s_bclk_pin: GPIO39

speaker:
  - platform: i2s_audio
    id: i2s_audio_speaker
    i2s_dout_pin: GPIO41
    i2s_audio_id: i2s_output_id
    dac_type: external
    timeout: never
    buffer_duration: 100ms
    channel: stereo

  - platform: mixer
    id: mixing_speaker
    task_stack_in_psram: ${task_stack_in_psram}
    output_speaker: i2s_audio_speaker
    num_channels: 2
    source_speakers:
      - id: announcement_spk_mixer_input
        timeout: never
      - id: media_spk_mixer_input
        timeout: never

  - platform: resampler
    id: media_spk_resampling_input
    task_stack_in_psram: ${task_stack_in_psram}
    output_speaker: media_spk_mixer_input

  - platform: resampler
    id: announcement_spk_resampling_input
    task_stack_in_psram: ${task_stack_in_psram}
    output_speaker: announcement_spk_mixer_input

media_player:
  - platform: speaker
    name: None
    id: speaker_media_player
    media_pipeline: 
      format: FLAC
      sample_rate: ${i2s_sample_rate_spk}
      speaker: media_spk_mixer_input
      num_channels: 2
    announcement_pipeline: 
      speaker: announcement_spk_mixer_input
      format: FLAC
      sample_rate: ${i2s_sample_rate_spk}
      num_channels: 1

there’s been some talk about the “knob” display in discord, turns out there’s a v1 and v2… v1 needs to be hardware hacked, v2 doesn’t. i assume only people who already have one could be having a v1 as it’s most likely discontinued