Custom Component for Waveshare ESP32-S3 4.2" RLCD (ST7305)

I also get the pop sound. I have not investigated further. Here is how I am setting the default volume. 75% is fine for me with the speaker taped to the back of the unit, above that there is too much distortion IMO.

  on_boot:
    priority: 800
    then:
      # turn on PA before initializing es8311
      - switch.turn_on: audio_pa_ctrl
      - delay: 100ms
      - speaker.volume_set:
          id: rlcd_speaker_hw
          volume: 75%

I think I fixed the pop sound

  on_boot:
    priority: 800
    then:
      - switch.turn_on: audio_pa_ctrl
      - delay: 300ms   # increase this
      - speaker.volume_set:
          id: rlcd_speaker_hw
          volume: 0%

when playing sound

- speaker.volume_set:
    id: rlcd_speaker_hw
    volume: 75%

Have you tried displaying a image?

I tried


http_request:

online_image:
  - url: http://homeassistant.local:8123/local/snapshots/frontdoor.jpg
    id: door_snapshot
    format: JPEG
    update_interval: never
    type: BINARY

then

it.image(0, 0, id(door_snapshot));

but nothing shows

hey,
i’ve tried several configs, but my screen it only showing some snow on my device and in 2 parts (but not equally). On the left side mostly white, on the right mostly black. any ideas?
refresh changing to 1s doesn’t change

esphome:
  name: rlcd
  friendly_name: RLCD

esp32:
  board: esp32-s3-devkitc-1
  framework:
    type: esp-idf

# CRITICAL: Enables the 8MB Octal PSRAM required for the display buffer
psram:
  mode: octal
  speed: 40MHz

logger:

api:
  encryption:
    key: ""

ota:
  - platform: esphome
    password: ""

# ===== Wi‑Fi =====
wifi:
  ssid: ""
  password: ""

  # Fallback AP; handig bij Wi‑Fi issues
  ap:
    ssid: "Esp32S3 Fallback Hotspot"
    password: "CxWcHMh6SjcC"

# Captive portal voor fallback-AP
captive_portal:
spi:
  clk_pin: GPIO11  
  mosi_pin: GPIO12
  #miso_pin: GPIO8      # Added dummy pin to satisfy SPI initialization

# External component
external_components:
  - source: github://kylehase/ESPHome-ST7305-RLCD
    components: [ st7305_rlcd ]
    refresh: 0s
   

font:
  - file: "gfonts://Roboto"
    id: roboto
    size: 40



display:
  - platform: st7305_rlcd
    model: WAVESHARE_400X300
    rotation: 0
    id: my_display
    width: 400
    height: 300
    cs_pin: GPIO40
    dc_pin: GPIO5
    #te_pin: GPIO6
    reset_pin: GPIO41
    data_rate: 1MHz
    update_interval: never
    show_test_card: false
    lambda: |-
      // Get dimensions
      int w = it.get_width();
      int h = it.get_height();
      id(my_display).display_on();

      // Print "Hello World!" in the center of the screen
      // Arguments: x, y, font_id, color, alignment, text
      it.printf(w / 2, h / 2, id(roboto), COLOR_ON, TextAlign::CENTER, "Hello World!");
      id(my_display).low_power_mode();

you may want to draw a full rectangle on the display after initialisation (with a delay of 100ms or so) to ensure the buffer is fully initialized. I get random pixels/noise on boot-up too until the display is redrawn.

I was able to display an image that I included locally. I did use a PNG though.

Code (abridged):

image:
  - file: "images/image480.png"
    id: rlcd_photo
    type: BINARY
    resize: 198x198

and in the display lambda (some placement tricks to make it fit into an unused test area):

     // Right-hand image area (200x220 box from x=200..399, y=40..260)
      const int img_w = 198;
      const int img_h = 198;

      // Center the 200x200 image in that box
      const int right_x = left_w + ((w - left_w - img_w) / 2) +1;          // 201, leave center bar alone
      const int right_y = content_top + ((content_bottom - content_top - img_h) / 2);  // 50

      it.image(right_x, right_y, id(rlcd_photo), COLOR_OFF, COLOR_ON);

Hi
You probably need to ajust the refresh and the update intervals
See Custom Component for Waveshare ESP32-S3 4.2" RLCD (ST7305) - #15 by eponra

Hi

This is my config which uses some HA sensors
Thanks to kylehase and oliof for their incredible work!

esphome:
  name: rlcd
  friendly_name: RLCD

globals:
  - id: is_inverted
    type: bool
    initial_value: 'false'

esp32:
  board: esp32-s3-devkitc-1
  framework:
    type: esp-idf

# CRITICAL: Enables the 8MB Octal PSRAM required for the display buffer
psram:
  mode: octal
  speed: 40MHz

logger:

api:
  encryption:
    key: "xxxxxxxxxxxx"

ota:
  - platform: esphome
    password: "xxxxxxxxxxxx"

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

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

captive_portal:

spi:
  clk_pin: GPIO11  
  mosi_pin: GPIO12

i2c:
  sda: GPIO13
  scl: GPIO14
  scan: true
  id: bus_a

external_components:
  - source: github://kylehase/ESPHome-ST7305-RLCD
    components: [ st7305_rlcd ]
    refresh: 6s # Cache the component unless you are actively developing it

font:
  - file: "gfonts://Roboto"
    id: roboto
    size: 24


sensor:
  - platform: adc
    id: bat_voltage
    name: "Battery Voltage"
    pin: GPIO4   # This is the correct pin for RLCD-4.2
    attenuation: 12db
    update_interval: 60s
    filters:
      # The divider is 100k+100k (1/2) or 200k+100k (1/3)
      # Start with 2.0. If it reads ~2.1V for a full battery, change to 3.0.
      - multiply: 3.0 
      # Smooth out the jitter
      - sliding_window_moving_average:
          window_size: 10
          send_every: 1

      # Optional: Convert voltage to percentage
  - platform: copy
    source_id: bat_voltage
    id: bat_level
    name: "Battery Level"
    unit_of_measurement: "%"
    filters:
      - calibrate_linear:
          - 2.5 -> 0.0
          - 4.2 -> 100.0
      - clamp:
          min_value: 0
          max_value: 100
  - platform: shtcx
    id: my_sensor
    temperature:
      name: "Ambient Temperature"
      id: rlcd_temp
    humidity:
      name: "Ambient Humidity"
      id: rlcd_hum
    address: 0x70  # Standard SHTC3 address
    update_interval: 60s
    i2c_id: bus_a
    
text_sensor:
  # Text Sensors (Detected from Sensor Text)
  - platform: homeassistant
    id: couleur_actuelle_txt
    entity_id: sensor.rte_tempo_couleur_actuelle
    #internal: true
  - platform: homeassistant
    id: prochaine_couleur_txt
    entity_id: sensor.rte_tempo_prochaine_couleur
    #internal: true
  - platform: homeassistant
    id: ltarf_txt
    entity_id: sensor.lixee_current_price
    #internal: true

time:
  - platform: homeassistant
    id: ha_time

display:
  - platform: st7305_rlcd
    model: WAVESHARE_400X300
    rotation: 0
    id: my_display
    width: 400
    height: 300
    cs_pin: GPIO40
    dc_pin: GPIO5
    #te_pin: GPIO6
    reset_pin: GPIO41
    data_rate: 1MHz
    update_interval: 5s
    show_test_card: false
    lambda: |-
      int w = it.get_width();
      int h = it.get_height();

      // 1. Determine Colors based on the global toggle
      auto bg = id(is_inverted) ? COLOR_ON : COLOR_OFF;  // Swap Background
      auto fg = id(is_inverted) ? COLOR_OFF : COLOR_ON;  // Swap Text Color
      
      // 2. Wake up
      id(my_display).display_on();
      it.filled_rectangle(0, 0, w, h, bg); // Clear previous screen

      // 2. Draw "Hello World"
      it.printf(w / 2, h / 2, id(roboto), fg, TextAlign::CENTER, "A %s  | D %s", id(couleur_actuelle_txt).state.c_str(), id(prochaine_couleur_txt).state.c_str());
      it.printf(w / 2, h / 3, id(roboto), fg, TextAlign::CENTER, "%s", id(ltarf_txt).state.c_str());
      it.strftime(w / 2, h * 2 / 3, id(roboto), TextAlign::CENTER, "%H:%M:", id(ha_time).now());

      // 3. Sensors (Temp, Humidity, Battery Level)
      if (id(rlcd_temp).has_state() && id(rlcd_hum).has_state()) {
        it.printf(w / 2, 30, id(roboto), fg, TextAlign::CENTER, 
                  "T %.1f°C  |  H %.1f%% | B %.0f%%", id(rlcd_temp).state, id(rlcd_hum).state, id(bat_level).state);
      } else {
         it.printf(w / 2, 30, id(roboto), fg, TextAlign::CENTER, "Loading Sensors...");
      }
      
      // 4. Power Save
      id(my_display).low_power_mode();

### buttons
#
# left: User Button
#   Bug: GPIO18
# center: Power Button. 
#   Long press to turn off, short press to turn on. 
#   Bug: When turned on while on battery, display not initialized correctly
# right: Boot Button (GPIO0)

binary_sensor:
  # 1. The "BOOT" Button (Top side button)
  - platform: gpio
    name: "Boot Button"
    id: button_boot
    pin:
      number: GPIO0
      inverted: true
      mode: INPUT
    # Example Action: Force a screen refresh when clicked
    on_press:
      - component.update: my_display
  - platform: gpio
    name: "Button Key (GPIO18)"
    pin:
      number: GPIO18
      inverted: true
      mode: INPUT # Try PULLUP. If fails, remove this line (some boards have HW pullup)
    on_press:
      - logger.log: "KEY BUTTON PRESSED"
      # 1. Toggle the variable (true -> false -> true)
      - globals.set:
          id: is_inverted
          value: !lambda 'return !id(is_inverted);'
      
      # 2. Force the display to redraw immediately with new colors
      - component.update: my_display

This screen is a great alternative to e-paper, I had a waveshare e-paper display before which I used as a clock, partial refreshing every second, I know I wasn’t suppose to do that, hence it died after 2 years.

With this LCD screen I’ve programmed it as a Clock/Weather Station, taking sensor data from HA, showing weather forecast, hourly and daily. Plus temp/humidity from sensor around the house.
Also when I’m doing 3D print, instead of showing weather data, it shows the progress of the 3D print, when print is complete, it plays a chime to let me know it’s done. When I turn the printer off, it reverts back to weather.

Very happy with this screen/hardware.

Unfortunately what the screen can’t do is maintain the display in deep sleep. I am trying to explicitly manage power for the audio and wifi components, but I haven’t been too successful yet.

Thanks for the great work here.
Is there a way to configure waveshare rlcd 4.2 display in ESPHome Designer to simplify writing lambdas or LVGL?

Thank you in advance