JC3248W535 (Guition 3.5”) Config

Just wanted to share a basic working config for this device (JC3248W535) :slight_smile:

FYI much of this config is derived from here and I am only creating my own thread in the spirit of sharing the most minimal config possible for each device that I have on-hand.

Heads-up: I try to keep the configs in these posts updated but sometimes the configs in my repo are more up-to-date!

Here are the device-specific bits of the config separated out from everything else:

esphome:
  platformio_options:
    upload_speed: 921600
    board_build.flash_mode: dio
    board_build.f_flash: 80000000L
    board_build.f_cpu: 240000000L

esp32:
  board: esp32-s3-devkitc-1
  variant: esp32s3
  flash_size: 16MB
  framework:
    type: esp-idf
    sdkconfig_options:
      COMPILER_OPTIMIZATION_SIZE: y
      CONFIG_ESP32S3_DEFAULT_CPU_FREQ_240: y
      CONFIG_ESP32S3_DATA_CACHE_64KB: y
      CONFIG_ESP32S3_DATA_CACHE_LINE_64B: y
      CONFIG_SPIRAM_FETCH_INSTRUCTIONS: y
      CONFIG_SPIRAM_RODATA: y

psram:
  mode: octal
  speed: 80MHz # 120MHz is not supported and falls back to 40MHz

spi:
  - type: quad
    clk_pin: 47
    data_pins: [21, 48, 40, 39]

i2c:
  - sda: 4
    scl: 8

output:
  - id: gpio_backlight_pwm
    platform: ledc
    pin: 1

light:
  - id: backlight
    name: Backlight
    platform: monochromatic
    output: gpio_backlight_pwm
    restore_mode: ALWAYS_ON

display:
  - id: main_display
    platform: mipi_spi
    model: JC3248W535
    data_rate: 40MHz
    update_interval: never
    auto_clear_enabled: false

touchscreen:
  - id: main_touchscreen
    platform: axs15231
    # on_touch:
    #   - lambda: |-
    #         ESP_LOGI("cal", "x=%d, y=%d, x_raw=%d, y_raw=%0d",
    #           touch.x,
    #           touch.y,
    #           touch.x_raw,
    #           touch.y_raw
    #         );

I like to keep device-specific code like that its own file separate from everything else so that I can use the Packages component to import it into multiple relevant projects, rather than duplicate the whole thing into each one. In my case I save it into a file called templates/JC3248W535.yaml.

In the spirit of not duplicating code into multiple projects, I create another file that is not device-specific but is common/repetitive among all my projects. In my case I chose to call it templates/common.yaml and it has code like so:

esphome:
  name: "${device_name}"
  friendly_name: "${friendly_name}"

logger:
  level: INFO

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

web_server:
  port: 80

ota:
  - platform: esphome

api:

Finally, this is how I use both of the above files together in a project:

substitutions:
  device_name: guition-35-test-device
  friendly_name: Guition 3.5 Test Device

packages:
  common: !include templates/common.yaml
  device: !include templates/JC3248W535.yaml
  # dashboard: !include templates/dashboard_480x320.yaml

#sensor: ...

Note: The contents of the dashboard package referenced above aren’t in this post but I have it in the example to illustrate the power of setting up a config this way. In my case templates/dashboard_480x320.yaml is a file that I’ve created with HA sensors and LVGL pages in it, and it can be reused on any of my devices that support the same resolution as this one.

Concerns / Potential Improvements

  • TBD

Changes

  • Sept 29 2025: Changed driver and increased display performance massively.

More in my GitHub repo

3 Likes

That is nice! I will eventually start using the packages component, it does make things much cleaner.

How do you edit your ESPhome files? I just started using VS code and enjoying it so far. I’m compiling and installing them with the run command from separate terminal.

1 Like

Thanks!

I run ESPHome in Docker on a linux machine so I use VS Code with the Remote SSH plugin to edit my layouts and I usually just go back to the ESPHome dashboard in a browser to update devices, but sometimes I update from VS Code directly (either a task I setup or from the built-in terminal).

The task I have setup, if curious, is this:
.vscode/tasks.json

{
    // See https://go.microsoft.com/fwlink/?LinkId=733558
    // for the documentation about the tasks.json format
    "version": "2.0.0",
    "tasks": [
        {
            "label": "Build and Flash",
            "type": "shell",
            "command": "docker compose exec esphome esphome run --device OTA --no-logs ${fileBasename}",
            "problemMatcher": [],
            "group": {
                "kind": "build",
                "isDefault": true
            }
        }
    ]
}

This allows me to press CTRL+SHIFT+B while in a config and VSCode will run docker compose exec esphome esphome run --device OTA --no-logs ${fileBasename} for me. esphome is the name of the ESPHome service in my docker-compose.yaml which is why it shows up twice in the command.

1 Like

Thank you! Did anyone managed to get the speaker the work with ESPHome? It works with the demo program that the Chinese sellers put on these devices, but there is no info about which IO it’s connected to.

You are a hero!
You have just saved me so many headaches and gray hairs. Thank you! :pray: :pray: :pray: :pray: :pray: :pray:
ps. I only created an account here to let you know the above. :smiley:

1 Like

Glad to hear it!

Was it the device config or the VS Code tip that helped you? (You replied to the post with the VS Code tip which made me curious!

I came here for a basic config but ended up finding a working well structured project with lots of nice patterns and examples.
It’s going to push my ESPHome / Home Assistant knowledge by lightyears.
I’m still studying your work and being excited about it.
Thank you!
edit: not the vs-code tip, missclicked

1 Like

This has been extremely helpful so far, thank you! As a total novice it took me a while to learn exactly how this device/coding was all working, but persistence and a lot of trail and error has gotten me there, and your code has helped massively.

One tip I’m writing down here for other users, this might be really obvious to more experienced HA/ESPHome users but not obvious at all for newbies, and I didn’t see it detailed in any of the research I’ve done to get this setup. Once the device is added to ESPHome in HA, you need to enable the device to make service calls to HA, otherwise the buttons just won’t do anything when pressed. Settings> Devices & Services> Intergrations (tab)> ESP Home > the device is listed and click Configure > Select On the tickbox

2 Likes

在“让JC3248W535开发板显示LVGL组件”的问题上,我已经失去了很多头发。你的文章给了我希望,你能提供一个教程吗?I have lost a lot of hair on the issue of ‘making JC3248W535 development board display LVGL components’. Your article gave me hope, can you provide a tutorial?

I am also using the JC3248W535C display and want to share some code to get a quite decent ‘battery SOC’ reading. I am using a 2000mAh Lipo and the SOC reading is ‘calibrated’ to the voltage curve during discharge. Due to voltage losses the measurement value is way to high when charging. I am sure this can be improved, but maybe someone is willing to use this as a starting point:

sensor:
  - platform: adc
    pin: 5
    name: "Battery SOC"
    unit_of_measurement: "%"
    state_class: measurement
    device_class: battery
    attenuation: 12db
    accuracy_decimals: 0
    samples: 16
    filters:
      - multiply: 1.72
      - sliding_window_moving_average:
          window_size: 15
      - calibrate_linear:
          method: exact
          datapoints:
            - 3.00 ->   0.0
            - 3.35 ->  12.0
            - 3.40 ->  20.0
            - 3.60 ->  60.0
            - 4.00 ->  90.0
            - 4.10 -> 100.0
      - clamp: 
          min_value: 0.0
          max_value: 100.0
    update_interval: 4s
    on_value_range:
      - below: 20.0
        then:
          - lvgl.widget.show: battery_empty_label
      - above: 20.0
        then: 
          - lvgl.widget.hide: battery_empty_label
1 Like

Heads-up I’ve updated the display section of the config in the OP and Github as I found that there’s a newer display driver with support for this model and it increases performance hugely :slight_smile:

I’m really struggling to get this screen working.
Running esphome 2025.11.4

Tried a variety of different examples.

Backlight is definitely on pin 1 and I can adjust it.
No matter what I try I end up with the same result.

My current code to attempt to just write a single full colour

Any ideas? Any really obvious issues?
I’ve wasted too much time already!

esphome:
  name: esp32-screen-1
  friendly_name: ESP32-Screen-1
  platformio_options:
    upload_speed: 921600
    board_build.flash_mode: dio
    board_build.f_flash: 80000000L
    board_build.f_cpu: 240000000L

esp32:
  board: esp32-s3-devkitc-1
  variant: esp32s3
  flash_size: 16MB
  framework:
    type: esp-idf
    sdkconfig_options:
      COMPILER_OPTIMIZATION_SIZE: y
      CONFIG_ESP32S3_DEFAULT_CPU_FREQ_240: y
      CONFIG_ESP32S3_DATA_CACHE_64KB: y
      CONFIG_ESP32S3_DATA_CACHE_LINE_64B: y
      CONFIG_SPIRAM_FETCH_INSTRUCTIONS: y
      CONFIG_SPIRAM_RODATA: y

# Enable logging
logger:

# Enable Home Assistant API
api:
  encryption:
    key: "xxxxx="

ota:
  - platform: esphome
    password: "xxxxx"

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

# Enable fallback hotspot (captive portal) in case wifi connection fails
  ap:
    ssid: "Esp32-Screen-1 Fallback Hotspot"
    password: "xxxxx"

captive_portal:

web_server:
  port: 80

psram:
  mode: octal
  speed: 80MHz # 120MHz is not supported and falls back to 40MHz

spi:
  - type: quad
    clk_pin: 47
    data_pins: [21, 48, 40, 39]

output:
  - id: gpio_backlight_pwm
    platform: ledc
    pin: 1

light:
  - id: backlight
    name: Backlight
    platform: monochromatic
    output: gpio_backlight_pwm
    restore_mode: ALWAYS_ON

display:
  - id: main_display
    platform: mipi_spi
    model: JC3248W535
    data_rate: 40MHz
    update_interval: 0s
    auto_clear_enabled: false
    lambda: |-
      it.fill(Color(0, 255, 0));

You don’t want the platformio options or sdkconfig options, and you don’t want an update interval of 0s. But there is a working base config here:

or here:

1 Like

Thank you!
I was sure I’d tried your example, but probably only the spi and display sections.
Now working and time to build it into what I want.