ESP32-C3 with integrated GC9A01 - cheap touch controller

So I’ve documented my journey in being able to control the ducts of my home heating/cooling system using esphome, some relays, and e-ink displays here. It’s working well, but the wall control panels are time consuming to create and cost more than I’d like.

I’m now looking at creating a thermostat control that is small, inexpensive, and simple to setup. Although my use case as a room thermostat is fairly specific, I think that what I am trying to do is something that could easily be used for many other tasks around the house (such as light switches) so thought I’d document it here. To that end I have found these guys:

Found in Aliexpress under the title “ESP32 Arduino LVGL WIFI&Bluetooth Development Board 1.28” 240*240 IPS Smart Display Screen 1.28 inch IPS LCD TFT Module touch"

These fit my requirements almost exactly. They come with an ESP32 built in, can be purchased with a case (black or white), and don’t cost much (about AUD$25/USD$16 delivered). They have a USB-C socket to configure and provide power, but also have separate sockets for power or even external sensors. The screen may be just a little small (think of a medium size smart watch screen), but the screen is bright and crisp so I think it will do quite well.

I ordered one to experiment with, and this is what I’ve found so far.
The device is an ESP32-2424S012 (Single Core 32bit)
1.28-inch 240*240 IPS TFT screen with Capacitive touch

When it arrived, on plugging it in it already had the demo image installed - it allows you to swipe (left/right/up/down) between screens, tap on an area marked ‘home’ to go back to the home, on a screen with a (pretend) music player you can toggle pause/play as well as change volume with a slider around the edge.

Sweet. Looks good. Time to break it.

I installed esphome via webtools, which worked to a point - can reset and install code but fails when trying to configure WiFi as it does not appear to support Improv WiFi serial. Could install the code directly if plugged into my esphome server but I have migrated over to running everything in containers which makes that tricky. Dang. So the screen was dead. I then tried to reinstall the original demo code via Arduino IDE. Followed instructions but got errors - resolved most of them but still had a few left so eventually gave up as I don’t want to use Arduino IDE anyhow.

Went to https://web.esphome.io and uploaded the 1.28DEMO.bin file (from the support docs) - all good. Back to being able to pretend to play music. At least I knew I hadn’t borked the device.

So. I wiped the device again. I created a small esphome config on esphome, downloaded the file locally, then uploaded the bin file to the device. Success! Created some text sensors such as uptime and all fine. Nothing on the screen of course, but now I could test my code using esphome over wifi.

Checked demo code, then compared with the documentation, and verified what the GPIO pins etc were being used. I also confirmed from that, that the device is a GC9A01 with a CST816D touch screen.

I then found a discussion talking about support for the GC9A01, and I tried that code. It installed happily, but still no image on the screen itself. Need to put this to one side for a little while until I have some time to do a bit more debugging.

As an aside the device in theory supports bluetooth - I tried installing the BLE tracker and initially thought the device had locked up, but in reality it just took a lot longer to start up. I think that the unit is pretty low powered, and I also suspect that it’s either not supported or the antenna is not fantastic - so probably don’t assume you may be able to use these for presence detection.

I’ll upload my current code, with comments, and will update if/as I progress.

substitutions:
  devicename: wallwatch01
  friendname: WallWatch01
  location: master
  board: esp32-c3-devkitm-1

esphome:
  name: $devicename
  friendly_name: $friendname

esp32:
  board: $board
  framework:
    type: arduino

# Enable logging
logger:

# Enable Home Assistant API
api:
  encryption:
    key: "thisisakey"

ota:
  password: "thisisapassword"

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

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

captive_portal:

time:
  - platform: homeassistant
    timezone: "Australia/Melbourne"
    id: esptime

# Test - see if the thing picks up BLE info
#esp32_ble_tracker:
#nope - locks up
#SpokeToSoon - actually does work, kinda. Slows down the startup significantly then claims to scan but nothing is found
#Suspect that it is really weak

sensor:
  - platform: uptime
    name: "$devicename Uptime"
  - platform: wifi_signal
    name: "$devicename WiFi Signal"
    update_interval: 60s    

external_components:
#  - source: github://pr#3625
#    components: [ gc9a01 ]
  - source: github://4cello/esphome@gc9a01
    components: ["gc9a01"]
# Arduino code shows screen is a GC9A01 with CST816D touch screen
# Neither above work - screen blank

spi:
  mosi_pin: GPIO7
  clk_pin: GPIO6
# Arduino IDE Code says
#      cfg.pin_sclk = 6
#      cfg.pin_mosi = 7
# ESP32-2424S012-V1.0.PNG agrees
# ESP32-2424S012-V1.0.PNG (LCM diagram) agrees - SCL and SDA


font:
  - file: 'fonts/GoogleSans-Medium.ttf'
    id: font_16
    size: 16

display:
#  - platform: ili9xxx
#    model: gc9a01
# Above is for when or if this is merged into the ili9xxx platform
  - platform: gc9a01
    id: watchface

#    reset_pin: GPIO26 # code has this disabled - could be GPI08 or 9?
# ESP32-2424S012-V1.0.PNG (LCM diagram) shows REST goes to REST - most likely the physical reset buttton
# Which is connected to EN on the ESP32 - physical pin 8 - enable pin
# Note GPIO9 is a strapping pin, so not great to use
# ESP32-2424S012-V1.0.PNG says GPIO9 is boot. Might disable for now.
#    reset_pin: GPIO9


    cs_pin: GPIO10
# Arduino IDE Code says
#     cfg.pin_cs = 10
# ESP32-2424S012-V1.0.PNG agrees
# ESP32-2424S012-V1.0.PNG (LCM diagram) agrees
 
    dc_pin: GPIO2
# Arduino IDE Code says
#        cfg.pin_dc = 2
# ESP32-2424S012-V1.0.PNG (ESP32 diagram) says this is SPIMISO
# ESP32-2424S012-V1.0.PNG (LCM diagram) shows D/C goes to SPIMISO which is GPI02
# Note GPIO2 is a strapping pin, so not great to use

    rotation: 90
    lambda: |-
      it.strftime(120,80, id(font_16), TextAlign::CENTER, "%A %b %w", id(esptime).now());
11 Likes

Win! I tried all sorts of things then found some technical data sheets for what appeared to be the right screen. Depending on how it is configured it may or may not have a BLK pin to play with the backlight. I then thought that maybe I needed to somehow enable the backlight. I then found that the LED anode was being routed to GPIO3. I enabled that and bang I now have words on the screen. Huge leap forward.

The magic code was:

output:
  - platform: ledc
    pin: GPIO3
    id: gpio_3_backlight_pwm
light:
  - platform: monochromatic
    output: gpio_3_backlight_pwm
    name: "Display Backlight"
    id: back_light
    restore_mode: ALWAYS_ON
2 Likes

So yay. Have the display working. Have removed most of my scratch notes from my code as no longer required, and next step will be to get the touch side of things working.

The code for those playing along at home:

substitutions:
  devicename: wallwatch01
  friendname: WallWatch01
  location: master
  board: esp32-c3-devkitm-1
  repin: GPIO1
  dcpin: GPIO2
  bkpin: GPIO3
  clpin: GPIO6
  mopin: GPIO7
  cspin: GPIO10

esphome:
  name: $devicename
  friendly_name: $friendname

esp32:
  board: $board
  framework:
    type: arduino

# Enable logging
logger:

# Enable Home Assistant API
api:
  encryption:
    key: !secret esphome_encryption_key

ota:
  password: !secret ota_password

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

  # Enable fallback hotspot (captive portal) in case wifi connection fails
  ap:
    ssid: "Wallwatch01 Fallback Hotspot"
    password: !secret fallback_password

captive_portal:

time:
  - platform: homeassistant
    timezone: "Australia/Melbourne"
    id: esptime

# Test - see if the thing picks up BLE info
#esp32_ble_tracker:
#nope - locks up
#SpokeTooSoon - actually does work, kinda. Slows down the startup significantly then claims to scan but nothing is found
#Either is not supported or antenna is really weak

sensor:
  - platform: uptime
    name: "$devicename Uptime"
  - platform: wifi_signal
    name: "$devicename WiFi Signal"
    update_interval: 60s    
  - platform: homeassistant
    id: outdoor_temperature
    entity_id: sensor.gw1000_v1_7_6_outdoor_temperature

external_components:
#  - source: github://pr#3625
#    components: [ gc9a01 ]
  - source: github://4cello/esphome@gc9a01
    components: ["gc9a01"]
# Arduino code shows screen is a GC9A01 with CST816D touch screen

spi:
  mosi_pin: $mopin
  clk_pin: $clpin
#mosi = Master Out Slave In
#miso = Master In Slave Out or fermented bean paste. In this case, most likely the former rather than the latter. Doesn't matter as not used.

# Need to turn on backlight as by default is not on
output:
  - platform: ledc
    pin: $bkpin
    id: gpio_3_backlight_pwm
light:
  - platform: monochromatic
    output: gpio_3_backlight_pwm
    name: "Display Backlight"
    id: back_light
    restore_mode: ALWAYS_ON

font:
  - file: 'fonts/GoogleSans-Medium.ttf'
    id: font_16
    size: 16
  - file: 'fonts/GoogleSans-Medium.ttf'
    id: font_32
    size: 32

color:
  - id: my_red
    red: 100%
    green: 3%
    blue: 5%
  - id: my_green
    red: 3%
    green: 100%
    blue: 5%
  - id: my_blue
    red: 3%
    green: 5%
    blue: 100%

display:
#  - platform: ili9xxx
#    model: gc9a01
# Above is for when or if this is merged into the ili9xxx platform
  - platform: gc9a01
    id: watchface
    reset_pin: $repin
    cs_pin: $cspin
    dc_pin: $dcpin

# Rotate the screen so usb socket is pointing down
    rotation: 90

# Print the date on one line in blue
# Print the current time on the next line, but in a bigger font but default white
# Print the outside temperature in green
# Surround the lot by a red circle with centre at 120, 120 and a radius of 115 because why not
    lambda: |-
      it.strftime(120,80, id(font_16), id(my_blue), TextAlign::CENTER, "%A %b %d", id(esptime).now());
      it.strftime(120,120, id(font_32), TextAlign::CENTER, "%I:%M %p", id(esptime).now());
      it.printf(120, 170, id(font_32), id(my_green), TextAlign::CENTER, "Now: %.1f°", id(outdoor_temperature).state);
      it.circle(120, 120, 115, id(my_red));
6 Likes

Thanks for sharing your progress.
I saw another one with knob controls, did you get your hands on that version as well?

There are a number of various ones out there. I suspect you are talking about the one I was tempted to get that is larger (2.1 inch) and has a surround you can physically rotate in a fashion similar to a Nest thermostat. I liked the concept, but it was significantly more expensive (roughly three times the cost) and there are reports of it not being particularly accurate - ie you spin the ‘knob’ and it doesn’t register. You can also push the screen and it registers that via a physical button - great, but again some reports of it not working all the time. So costs more, and may have some quality issues, so I thought I’d try this thing.

Dang. Might have hit a bit of a roadblock. The touchscreen is a CST816, with the code referring to CST816D which I assume is a variant. This is currently not directly supported in esphome. There is some discussion about it here and a library here, but I haven’t found any discussion about it in the esphome community. Will have to sit down and think about it… not sure I have the time or capability to create a driver for it…

That would be this one, slightly smaller panel.

I see, wasn’t expecting those issues. Where can I read more on that?

Interesting - I hadn’t seen that one. It is still more expensive, plus you still have to make a case for it. I might check it out if I can’t get this thing to work. the one I was thinking you meant was this.

Off hand I can’t point you to anything - they were just some comments I saw whilst trolling through various discussions on them, and could always simply be bad luck with those people getting faulty units. The cost was the killer for me though as it was getting close to what I already had created - using an e-ink display - that frankly I think was/is better in a number of ways.

I just got one of these today and testing it out at home.

I get a complie error when its trying to run your code, point me in the right direction??

Latest esphome version at the moment 2023.8.3.

  File "/config/esphome/.esphome/external_components/1ffebe30/esphome/components/gc9a01/display.py", line 63, in setup_gc9a01
    config[CONF_LAMBDA], [(display.DisplayBufferRef, "it")], return_type=cg.void
AttributeError: module 'esphome.components.display' has no attribute 'DisplayBufferRef'

EDIT:
I followed the folder and edited the display.py to use DisplayRef which was a work around.

1 Like

First suggestion is unless you have downloaded the external component locally, make sure that you have this in your code:

external_components:
  - source: github://4cello/esphome@gc9a01
    components: ["gc9a01"]

Second suggestion would be to try doing a clean build

1 Like

The PR needs a change on line 63 of display.py, should be
config[CONF_LAMBDA], [(display.DisplayRef, "it")], return_type=cg.void

Got the demo working, going to be watch for touch working :stuck_out_tongue:

Glad to hear! I’m currently avoiding thinking about getting the touch side working - instead I distracted myself by creating an automation to toggle the screen on/off using a motion sensor. Standard procrastination 101. I’ll look at the touch side of thing later this week.

1 Like

Also have the demo working. Just wanted to say thanks!

1 Like

Excellent. Seems to work really well for me as well - boots up super fast and the image is nice and clear. Still haven’t had the time to check out the touch side of things though. I suspect will need to take the arduino library and convert it to a format that esphome can use, so we can then use it as an external component. Definitely outside my comfort zone but willing to give it a go - but need to find the time… sigh…

Unfortunately openhasp seems to support the GC9A01 screen but not the CST816D (or CST816) so that’s a bummer.

Yeah. The glimmer of hope is there is sample code to get it running in the Arduino IDE so in theory I’m assuming that should be able to be leveraged to provide the support in esphome. Will see. As it is it’s a nice little display with integrated esp32 and case, but definitely would be good to enable the touch as well.

Hi Thanks for all the info.
Made my day.
Let’s stay in touch for evolutions

@zagnuts I am having trouble having the device recognized.
It is connected via cable.
Both on Mac M1 and a Windows PC it is not recognized.
Tried also to reset after connecting.
No joy.
Any tips?
Another thing that I noticed is that there is no code at the 4cello github repo link…

See if you can try a different cable. Check to see if it is recognised as existing when you plug it in (eg from Windows open up device manager) but it is quite possible that a pin on the cable is damaged so it allows power but not comms. Sometimes you may also need to install drivers to support the device - it is possible I already had that installed on my (M1 mac) laptop as it worked first time for me.

Or is a power only cable.

1 Like