ESPHome Nest thermostat clone on cheap rotary display

can I pinch your code please? I am struggling plus too many projects LOL

I’ll message you, it’s in no working state though.

I’ve finished rewriting the original nESP code to LVGL for the ZX2D10GE01R-V4848 device with my own twist. I may have went overboard in the GUI (it’s about 1000 lines long) but I’ve published it on GitHub:

There may be a whole bunch weather entities or zone cover entities which might not be relevant to your use case, so you might need to go and delete those widgets and home assistant entities. Most of the UI changes are controlled by esphome scripts which hide and show widgets and update labels. The hardware and GUI code are separated into their own files.

Thanks to @veli for the inspiration for this project and @clydebarrow for the quick lvgl bugfixes and answering my questions on Discord.

5 Likes

loving this thanks mate, you’ve done all the hard yards just updating a few items for my heatpump, 5 speed setting, etc :slight_smile: but awesome stuff thank you

1 Like

Thanks for the work done.
I may have a stupid question but which yaml should be loaded to the esp ? What’s the difference between them ? Cannot access to the discord


Thanks

Hi Brent,
Would you be able to give me an invite link to the discord server, I’ve been hitting a wall with trying to get my code working for the rotary encoder. Thanks!

If you mean the ESPHome discord server, it’s on https://esphome.io/, bottom of the menu tab, ā€œJoin the communityā€

Hi guys,

Trying to compile the code of @kto in ESPhome builder, but I’m getting following error:

> In file included from src/esphome/components/lvgl/lvgl_esphome.cpp:2:
> src/esphome/components/lvgl/lvgl_esphome.cpp: In function 'void* lv_custom_mem_alloc(size_t)':
> src/esphome/core/log.h:128:3: error: expected unqualified-id before '::' token
>   128 |   ::esphome::esp_log_printf_(ESPHOME_LOG_LEVEL_ERROR, tag, __LINE__, ESPHOME_LOG_FORMAT(format), ##__VA_ARGS__)
>       |   ^~
> src/esphome/core/log.h:151:28: note: in expansion of macro 'esph_log_e'
>   151 | #define ESP_LOGE(tag, ...) esph_log_e(tag, __VA_ARGS__)
>       |                            ^~~~~~~~~~
> src/esphome/components/lvgl/lvgl_esphome.cpp:400:14: note: in expansion of macro 'ESP_LOGE'
>   400 |     esphome::ESP_LOGE(esphome::lvgl::TAG, "Failed to allocate %zu bytes", size); 

Anyone had this issue? Don’t know where to start looking.

Thanks,

Nikola

I would start by making sure you are using the latest ESPHome, and removing any external component configs.

Thanks @clydebarrow. I got it compiled ok and running, but with two issues.

  1. With OTA flashing enabled, the screen loads only one time when I flash the code. After that, I can’t flash another firmware with OTA, until I reset the device. Once I reset the device, it goes into OTA flashing mode, not showing anything on the screen. I can’t wake it up anymore, but I can flash it. Once I disabled the OTA, and loaded the code with ESP WEB, I can normally reset the device and the screen shows every time.

  2. Main menu works ok. Scrolling between the icons is smooth, no issues. When I turn on the climate, turning the encoder takes 1-2 seconds to reflect the change in the arc widget, and the HA entity. The issue is with the climate entity that creates this lag. It is a Mitsubishi AC with mitsubishi2MQTT on ESP8266 (controlling from HA works without delay, so I still need to figure out where it is created). I updated it to the latest version, and it improved a bit, but still has a delay. The thing that also adds to the problem is due to the fact that the command to HA is issued on_clockwise or on_anticlockwise with each encoder movement. I can adjust the temperature by 0.5 degrees on each movement, experience a delay and then rotate again.
    I wanted to change this part, that I would issue command to HA once movement of encoder has stopped (meaning when I reach desired temperature on the dial), but I don’t see the encoder has this possibility.
    I would appreciate if anyone can give me some idea here.

In reply to my own question, issue #2, I’ve managed to solve it with the help of this post.
I’ve introduced a number that I increment or decrement with each move of the encoder, and then I’m executing a script with a delay. The script adds positive or negative value of the number to the set_temperature sensor value, and executes only if there was no movement of encoder for 500ms.

The original code is creating delays in my specific case because it has too many hops:
Encoder movement triggers HA.climate.set_temperature, then publishes to MQTT, to be received by ESP8266 that controls the HVAC unit, that needs to respond and send set_temperature all the way back through the same steps to update the set_temp_roller.

Instead, I now update the set_temp_roller on each encoder movement, and then wait for verification from the HVAC unit to be sent through HA, but the screen is not frozen meanwhile. If they are out of sync, the sensor set_temperature will correct the set_temp_roller as soon as it gets updated through HA.

@kto I believe that anyhow this should be the approach in your code. The UI should not be blocked until the climate entity responds. Maybe the solution I applied is not the best, but it lets the UI run smoothly.

I still have the problem with OTA update, the issue #1 in my previous post. Can anyone help?

Thanks

Fixed it. Wrong manual IP configuration :flushed:

Hi NikolamViser, what did you do to fix this error while compiling? i upgraded my esphome version to dev branch, but still get the same error while compiling?

Hi dekuith,

I’m not using the dev branch, just updated ESPhome to 2025.4.0.
I commented out the lvgl component load in external components and it worked for me.

Thanx for the reply. its compiling now! I did not coment out the complete LVGL component!

Circling back to this, I’ve added this device (zx2d10ge01r-v4848) to the upcoming mipi_rgb display driver as a pre-defined model, so the required yaml for the display is just as below.

Secondly, you can flash this over USB by bridging the USB pads and shorting GPIO0 to GND for first boot (the stock firmware doesn’t enable the USB-JTAG serial port.)

esphome:
  name: zx2d10ge01r-v4848

esp32:
  variant: esp32s3
  framework:
    type: esp-idf

external_components:
  - source: github://pr#9892
    components: [mipi, mipi_rgb]
    refresh: 1h

psram:
  mode: octal
  speed: 80MHz

logger:

spi:
  ### DISPLAY
  clk_pin:
    number: 47
    allow_other_uses: true
  mosi_pin:
    number: 41
    allow_other_uses: true


output:
  - platform: ledc
    id: display_led
    pin: 38
    zero_means_zero: true

light:
  - platform: monochromatic
    id: backlight
    output: display_led
    name: Backlight
    icon: mdi:brightness-percent
    default_transition_length: 500ms
    restore_mode: ALWAYS_ON

display:
  - platform: mipi_rgb
    model: zx2d10ge01r-v4848

lvgl:
4 Likes

Just wanted to send a big thanks to @veli and @kto for their great work. I’ve ported their code to a Waveshare 2.1" curved edge round touch display. Its standalone. It can use HA but its not dependant on it for operation. Removed my 2nd gen Nest yesterday and installed the ā€œNespā€.

4 Likes

In case it is of interest I’ve uploaded my code here : GitHub - wineds/neo-nesp: Nest Thermostat clone on a rotary display running @esphome

looks bloody nice, love the profile of the touch screen, may need to replace my chunky rotary device now :slight_smile:

1 Like

I’m trying to compile the neo-nesp project but consistently hit an error during ESP-IDF tools installation. The compilation fails with ERROR idf_tools.py installation failed when the download reaches 96-99%.

Environment

  • Device: ESP32-S3
  • OS: Windows 11
  • ESPHome Version: 2025.11.0-dev (also tried 2024.9.0, 2024.10.0)
  • Compilation Method: Local Windows install, Home Assistant add-on, Docker
  • ESP-IDF: Manually installed v5.2.6 to C:\Espressif

What I’ve Tried

  1. :white_check_mark: Compiled on 3 different devices/networks (Windows PC, Surface laptop, Home Assistant on Raspberry Pi)
  2. :white_check_mark: Manually installed ESP-IDF v5.2.6 using offline installer
  3. :white_check_mark: Set environment variables (IDF_PATH, IDF_TOOLS_PATH)
  4. :white_check_mark: Created junction from PlatformIO to installed ESP-IDF
  5. :white_check_mark: Cleared all build caches multiple times
  6. :white_check_mark: Tried multiple ESPHome versions
  7. :white_check_mark: Used Docker with ESPHome image

The Error

Every compilation attempt fails at the same point:

INFO Installing tools via idf_tools.py (this may take several minutes)...
ERROR idf_tools.py installation failed (rc=1). Tail:
96%
97%
98%
99%
Done
INFO Installing tools via idf_tools.py (this may take several minutes)...

The download reaches 96-99% then fails and retries indefinitely.

Current Status

The idf_tools.py installation failure continues to occur on every compilation attempt, regardless of:

  • Having ESP-IDF manually installed locally
  • Creating junctions from PlatformIO to the installed ESP-IDF
  • Using different networks or devices

The error appears to be related to a specific package download that consistently fails at 96-99% completion.

Questions

  1. Has anyone successfully compiled neo-nesp on Windows recently?
  2. Are there any known issues with ESP-IDF 5.4.x or 5.5.x that I should avoid?
  3. Should I be using a specific ESPHome version?
  4. Is there a pre-compiled firmware I could test with to verify my hardware works?

Configuration

Using the standard neo-nesp.yaml from the repository with no modifications.

Any guidance would be greatly appreciated! I’ve been working on this for several days and would love to get this display working.

Thank you!