New ESP32 based LCD screen - waveshare-esp32-s3-touch-lcd-7B

I just got a new ESP32 based screen from Waveshare. Looks really nice

They added control for the backlight which was an issue with the waveshare-esp32-s3-touch-lcd-7 and the also increased the screen resolution to 1024x600.

The problem is the older model used a ch422g_hub to access a bunch of pins including the backlight. The new screen uses a proprietary Waveshare chip for this.

Accessing that chip via i2c is the only way to control the back light.

Anybody interested in writing a drive for it. It looks very similar to the and the drive for it is simple.

/*****************************************************************************
 * | File         :   io_extension.h
 * | Author       :   Waveshare team
 * | Function     :   GPIO control using io extension via I2C interface
 * | Info         :
 * |                 Header file for controlling GPIO pins via the io extension
 * |                 chip using I2C communication. This file defines the
 * |                 necessary I2C addresses, commands, and GPIO pin control
 * |                 functions.
 * ----------------
 * | This version :   V1.0
 * | Date         :   2024-11-19
 * | Info         :   Basic version
 *
 ******************************************************************************/

 #ifndef __IO_EXTENSION_H
 #define __IO_EXTENSION_H
 
 #include "i2c.h"  // Include I2C header for I2C communication functions
 
 /* 
  * IO EXTENSION GPIO control via I2C - Register and Command Definitions
  *
  *
  * Example usage:
  * 1. Set the working mode by writing to the register at address 0x24
  * 2. Send function commands to control the GPIO pins and modes
  */
 
 /* IO EXTENSION Function Register Addresses */
 #define IO_EXTENSION_ADDR          0x24  // Slave address for mode configuration register
 
 /* Mode control flags (from the chip manual) */
 #define IO_EXTENSION_Mode             0x02 // 
 #define IO_EXTENSION_IO_OUTPUT_ADDR   0x03 // 
 #define IO_EXTENSION_IO_INPUT_ADDR    0x04 // 
 #define IO_EXTENSION_PWM_ADDR         0x05 // 
 #define IO_EXTENSION_ADC_ADDR         0x06 // 
 
 /* Specific IO pin assignments */
 #define IO_EXTENSION_IO_0          0x00  // IO0 
 #define IO_EXTENSION_IO_1          0x01  // IO1 (used for touch reset)
 #define IO_EXTENSION_IO_2          0x02  // IO2 (backlight control)
 #define IO_EXTENSION_IO_3          0x03  // IO3 (used for lcd reset)
 #define IO_EXTENSION_IO_4          0x04  // IO4 (SD card CS pin)
 #define IO_EXTENSION_IO_5          0x05  // IO5 (Select communication interface: 0 for USB, 1 for CAN)
 #define IO_EXTENSION_IO_6          0x06  // IO6
 #define IO_EXTENSION_IO_7          0x07  // IO7
 
 /* Structure to represent the IO EXTENSION device */
 typedef struct _io_extension_obj_t {
     i2c_master_dev_handle_t addr;      // Handle for mode configuration
     uint8_t Last_io_value;
     uint8_t Last_od_value;
 } io_extension_obj_t;
 
 
 /* Function declarations */
 void IO_EXTENSION_Init();                     // Initialize the IO_EXTENSION device
 void IO_EXTENSION_Output(uint8_t pin, uint8_t value);     // Set IO pin output (high/low)
 uint8_t IO_EXTENSION_Input(uint8_t pin);   // Read IO pin input state
 void IO_EXTENSION_Pwm_Output(uint8_t Value);
 uint16_t IO_EXTENSION_Adc_Input();
 
 #endif  // __IO_EXTENSION_H
 
 /*****************************************************************************
 * | File         :   io_extension.c
 * | Author       :   Waveshare team
 * | Function     :   IO_EXTENSION GPIO control via I2C interface
 * | Info         :
 * |                 I2C driver code for controlling GPIO pins using IO_EXTENSION chip.
 * ----------------
 * | This version :   V1.0
 * | Date         :   2024-11-27
 * | Info         :   Basic version, includes functions to read and write 
 * |                 GPIO pins using I2C communication with IO_EXTENSION.
 *
 ******************************************************************************/
#include "io_extension.h"  // Include IO_EXTENSION driver header for GPIO functions
 
io_extension_obj_t IO_EXTENSION;  // Define the global IO_EXTENSION object

/**
 * @brief Set the IO mode for the specified pins.
 * 
 * This function sets the specified pins to input or output mode by writing to the mode register.
 * 
 * @param pin An 8-bit value where each bit represents a pin (0 = input, 1 = output).
 */
void IO_EXTENSION_IO_Mode(uint8_t pin) 
{
    uint8_t data[2] = {IO_EXTENSION_Mode, pin}; // Prepare the data to write to the mode register
    // Write the 8-bit value to the IO mode register
    DEV_I2C_Write_Nbyte(IO_EXTENSION.addr, data, 2);
}

/**
 * @brief Initialize the IO_EXTENSION device.
 * 
 * This function configures the slave addresses for different registers of the
 * IO_EXTENSION chip via I2C, and sets the control flags for input/output modes.
 */
void IO_EXTENSION_Init()
{
    // Set the I2C slave address for the IO_EXTENSION device
    DEV_I2C_Set_Slave_Addr(&IO_EXTENSION.addr, IO_EXTENSION_ADDR);

    IO_EXTENSION_IO_Mode(0xff); // Set all pins to output mode

    // Initialize control flags for IO output enable and open-drain output mode
    IO_EXTENSION.Last_io_value = 0xFF; // All pins are initially set to high (output mode)
    IO_EXTENSION.Last_od_value = 0xFF; // All pins are initially set to high (open-drain mode)
}

/**
 * @brief Set the value of the IO output pins on the IO_EXTENSION device.
 * 
 * This function writes an 8-bit value to the IO output register. The value
 * determines the high or low state of the pins.
 * 
 * @param pin The pin number to set (0-7).
 * @param value The value to set on the specified pin (0 = low, 1 = high).
 */
void IO_EXTENSION_Output(uint8_t pin, uint8_t value) 
{
    // Update the output value based on the pin and value
    if (value == 1)
        IO_EXTENSION.Last_io_value |= (1 << pin); // Set the pin high
    else
        IO_EXTENSION.Last_io_value &= (~(1 << pin)); // Set the pin low

    uint8_t data[2] = {IO_EXTENSION_IO_OUTPUT_ADDR, IO_EXTENSION.Last_io_value}; // Prepare the data to write to the output register
    // Write the 8-bit value to the IO output register
    DEV_I2C_Write_Nbyte(IO_EXTENSION.addr, data, 2);
}

/**
 * @brief Read the value from the IO input pins on the IO_EXTENSION device.
 * 
 * This function reads the value of the IO input register and returns the state
 * of the specified pins.
 * 
 * @param pin The bit mask to specify which pin to read (e.g., 0x01 for the first pin).
 * @return The value of the specified pin(s) (0 = low, 1 = high).
 */
uint8_t IO_EXTENSION_Input(uint8_t pin) 
{
    uint8_t value = 0;

    // Read the value of the input pins
    DEV_I2C_Read_Nbyte(IO_EXTENSION.addr, IO_EXTENSION_IO_INPUT_ADDR, &value, 1);
    // Return the value of the specific pin(s) by masking with the provided bit mask
    return ((value & (1 << pin)) > 0);
}

/**
 * @brief Set the PWM output value on the IO_EXTENSION device.
 * 
 * This function sets the PWM output value, which controls the duty cycle of the PWM signal.
 * The duty cycle is calculated based on the input value and the resolution (12 bits).
 * 
 * @param Value The input value to set the PWM duty cycle (0-100).
 */
void IO_EXTENSION_Pwm_Output(uint8_t Value)
{
    // Prevent the screen from completely turning off
    if (Value >= 97)
    {
        Value = 97;
    }

    uint8_t data[2] = {IO_EXTENSION_PWM_ADDR, Value}; // Prepare the data to write to the PWM register
    // Calculate the duty cycle based on the resolution (12 bits)
    data[1] = Value * (255 / 100.0);
    // Write the 8-bit value to the PWM output register
    DEV_I2C_Write_Nbyte(IO_EXTENSION.addr, data, 2);
}

/**
 * @brief Read the ADC input value from the IO_EXTENSION device.
 * 
 * This function reads the ADC input value from the IO_EXTENSION device.
 * 
 * @return The ADC input value.
 */
uint16_t IO_EXTENSION_Adc_Input()
{
    // Read the ADC input value from the IO_EXTENSION device
    return DEV_I2C_Read_Word(IO_EXTENSION.addr, IO_EXTENSION_ADC_ADDR);
}
1 Like

i started writing a driver for that extension chip.
please give a try.

i dont have this device though. it was lost during delivery.

Looks good. I’ll be able to test this over the weekend when my screen shows up.

1 Like

I also just got my hands on this screen, and I’m happy to report this driver works perfectly for my needs. This is still very much a work in progress, but so far I have not encountered any issues with it.

This screen is working well with the new code for the special Waveshare MUX provided by @latonita.

But I have a new problem. It was working well with 2025.7.5 but when I upgrade to 2025.8.0 the screen stops working and is black (no network). If I look at the logs it is getting the dreaded “Invalid image block, can’t boot”.

I have done all the usually troubleshooting. Delete my .esphome folder and recompiled etc. But it doesn’t seem to work at all. When I go back to 2025.7.5 it’s fine.

Any ideas on this?

Looks like it doesn’t support cpu_frequency: 240MHz . As soon as I removed that it start working. Weird as this working fine in 2025.7.5

Hi Andrew, do you have a working YAML for the Waveshare ESP32-S3 Touch LCD 7B? Thanks.

Yes the version on my git works great.

This is a good demo that is working well at the moment.

1 Like

Thanks for sharing, and for all the work you’ve put into your GitHub library!

Do you have one of these yet? I would be interested to know if it works. What are you going to use it for?

Yes, I pasted your hardware YAML into an existing YAML and it all worked fine. I’ve been using the Waveshare 800x480 display and have been happy with them. I had a spare one that I planned on gifting to a pal, so I took the opportunity to get one of these 16MB flash 1024x600 displays with the adjustable backlight, especially since AliExpress had a great offer on them.

After about 15 minutes of using it, I realised it just wasn’t for me, so I gave it to my pal and he’s super happy with it. He plans on creating an alarm panel type thing by the front door with sticky note style popups for leaving family members notes.

The only thing I couldn’t get along with, and probably someone brighter than me could possibly resolve, was that even though the brightness can be adjusted, at its brightest it wasn’t as bright as the 800x480 model. That’s just a personal preference thing. The second issue was that although I set the PSRAM to run at 120MHz and page switching was just as smooth as the 800x480 version, just a lot slower. My guess is maybe the higher resolution is causing this.

All that being said, if I hadn’t had the 800x480 to compare it with, I would have been more than happy with it. For what they are, these things are so cheap.

Agreed, it’s a bit slow but the higher resolution is nice. The brightness is a bit lower.

The 800x480 does not have adjustable brightness so not good for night mode although there is a hardware hack for this.

Yes, that’s what I did. It’s super easy, even for a beginner like myself. One thing I didn’t pick up on is that the 1024×600 chip runs really hot. I noticed the same on the Waveshare ESP32-S3 4.3-inch 800×480. I wasn’t comfortable with that device and ended up returning it.

I don’t think it’s an actual issue, since I believe these chips are designed to run at very high temperatures. It was just that mine are mounted in the wall with a flat bezel over the top. My pal isn’t overly concerned, as he’s going to mount his in a picture frame, and it’ll be on his exposed brickwork wall.

How does your esphome: and esp32: code look now? Im having the invalid image block" loop on my waveshare 7" 800x480 1.2 and commenting out cpu_frequency: fixes it, it boots, but the screen is black.

There have been quite a few changes in the code lately. But the latest on my github is working with 2025.8.4.

I’m hoping the code for the backlight will be in the next release of ESPhome.

anyone having issues with a slight flickering with this display, kinda like a backlight frequency issue?
for me it flickers, the old display did not have this issue.

any ideas?

had the brightness set to 100, yup this took me hours to find, we live and we learn.

I am struggling to get the screen to turn fully off and back on again. Have tried various code fragments for controlling the chip that manages the backlight. Does anyone have a working yaml fragment that performs these functions and that is for the 7B variant? Thanks