ESP32-C3 with integrated GC9A01 - cheap touch controller

Okay, i’ve been fighting with this a few days and struggle through the quircks of yaml and lambda. It isn’t the nicest solution but this isn’t the nicest language to write in too.
Anyway here is an example piece of code to go through several pages with swipes in 4 directions.

The screens are row1 has 3 screens, (page100-102), row 2 has 5 screens (page 1-5) and row 3 has 3 screens (page 200-202).
You swipe left and right to change the screen and swipe up/down to switch between rows

I left all the logging in place so you can follow what is happening and where.
I am very open to improvements.

# ESP32-2424S012 display
# by     : A.A. van Zoelen
# Version: 1.0.0
# Date   : 16 April 2024
# 
# This version is build on the shoulders of giants that did
# the groundwork to make this device accessable in Home Assistant
#
# https://github.com/zagnuts/Esphome_ESP32-C3_GC9A01
#
#
# To use, create a config in esphome with code. Change or remove references to sensors as required to suit your environment.
# This code refers to external image files - feel free to remove and/or change. For legal reasons I cannot share these.
# Pay close attention to the "substitutions" section and update to suit you
# Then compile and save the file locally
# Then go to https://web.esphome.io/ and connect the device to the pc and prepare it for first use
# Then upload the saved binary to the device
# Once installed, you will be able to use OTA updates

# Notes - feel free to remove
# - it is a single core processor running at up to 160Mhz, 400KB SRAM, 348KB ROM, 4MB flash
# - display resolution is 240*240 IPS
# - battery connector is a JST 1.25 2P. Note that this is for a 3V lithium battery (3.3V would be fine. It does have
#    over current protection but even so I would avoid 3.7V or greater just in case). 
# - Can power direct through the jst with DC but you need to initially provide power via USB 
#    before it recognises that there is power coming in via the battery port
# - it has a tiny click button called "switch" - unclear how to use. Have seen some images that refer to it as on/off 
#    but it does not seem to do that. The circuit diags seem to imply it is IO8 which is used for the screen so maybe
#    it could be used to toggle the screen on or off. It is deeply recessed and so tiny I doubt many people would use it anyhow.
# - it has two larger buttons for reset and boot
# - I/O interface connector is a SH 1.0 4P (GND, +3.3V, TX, RX) so UART - could for example use with
#    DS18B20 temp sensor (to measure the room temp)
#    URM07 distance sensor (to trigger the screen to wake up as someone approaches)
#    other options may include the following, but be aware many require more than 3V to be reliable, so may require separate power
#    SEN0395 or LD2410 mmWave for presense detection
#    MH-Z19C to measure CO2
#    SM300D2 to measure all sorts of things (CO2, formaldehyde, TVOC, laser PM2.5, PM10, temperature and humidity)
# - It has bluetooth, and can function as a ble tracker but memory and processor constraints means that it can
#    have issues (eg random rebooting) doing that AND running the display at the same time
# - processor is weak and memory low, so do not expect it to be able to handle animation too well 
# - specs say power consumption is 100mA - but varies depending on load and if display is active

substitutions:
  devicename: energie-monitor
  friendname: "Energie monitor"
  timez: Europe/Amsterdam
  board: esp32-c3-devkitm-1
# Display state on initial start
# Note that the screensaver only starts after the first touch of the display
# Change to ALWAYS_OFF if want the screen to be off after booting, ALWAYS_ON if want it on at start eg for initial troubleshooting
  screenstart: ALWAYS_ON
  # screenstart: ALWAYS_OFF
# Timeout for the screen
  screensaver: 1 min
#GPIO pins for the LCD screen
  repin: GPIO1
  dcpin: GPIO2
# Note - you may see an error on compilation "WARNING GPIO2 is a Strapping PIN and should be avoided" - ignore this as you have no choice
  bkpin: GPIO3
  clpin: GPIO6
  mopin: GPIO7
  cspin: GPIO10
# GPIO pins for the touch screen
  sdapin: GPIO4
  sclpin: GPIO5
  intpin: GPIO8  
# END OF SUBSITUTIONS

esphome:
  name: $devicename
  friendly_name: $friendname

esp32:
  board: $board
  framework:
    type: arduino

# Enable logging
# Change to avoid "Components should block for at most 20-30ms" warning messages in the log - an issue since 2023.7.0
# Not really a breaking change - it's an issue I suspect due to the device being slow and this error previously
# simply not being reported
logger:
  level: DEBUG #makes uart stream available in esphome logstream
  logs:
    component: ERROR
  #Turn off UART logging over RX/TX 
  baud_rate: 0


# Enable Home Assistant API
api:
  encryption:
    key: "YOUR ENCRYPTION KEY or remove this block"

ota:

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

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

captive_portal:


time:
  - platform: homeassistant
    timezone: "$timez"
    id: esptime


#Create a script to turn the screen off after a set period
script:
  - id: screentime
    mode: restart
    then:
      - light.turn_on: back_light
      - delay: $screensaver
      - light.turn_off: back_light


# 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: $screenstart


globals:
  # Used to determine swipes and direction
   - id: startvalue_x
     type: int
   - id: endvalue_x
     type: int     
   - id: startvalue_y
     type: int
   - id: endvalue_y
     type: int
   - id: swipe_direction
     type: int
     initial_value: '0'
   - id: swipe_up
     type: int
     initial_value: '1'
   - id: swipe_down
     type: int
     initial_value: '2'
   - id: swipe_left
     type: int
     initial_value: '3'
   - id: swipe_right
     type: int
     initial_value: '4'
   - id: page_row
     type: int
     initial_value: '2'
   - id: page_counter
     type: int
     initial_value: '100'


touchscreen:
  id: this_touch_screen
  platform: cst816
  interrupt_pin: GPIO0
  transform:
    swap_xy: true
    mirror_y: true

  on_touch:
    - lambda: |-
        ESP_LOGI("Single touch: ", "id=%d, x=%d, y=%d, x_raw=%d, y_raw=%0d",
            touch.id,
            touch.x,
            touch.y,
            touch.x_raw,
            touch.y_raw
        );
        id(startvalue_x) = touch.x;
        id(startvalue_y) = touch.y;


  on_update:
    - lambda: |-
          for (auto touch: touches)  {
              if (touch.state <= 2) {
                 ESP_LOGI("Touch points:", "id=%d x=%d, y=%d", touch.id, touch.x, touch.y);
              }
              id(endvalue_x) = touch.x;
              id(endvalue_y) = touch.y;
          }


  on_release:
    - script.execute: screentime
    - component.update: watchface
    - logger.log: "On_Touch released"         
    - logger.log:
        format: "Swipe: X start: %d - X end: %d -- Y start: %d - Y end: %d"
        args: [ 'id(startvalue_x)', 'id(endvalue_x)', 'id(startvalue_y)', 'id(endvalue_y)' ]

    - logger.log:
        format: "1 Swipe direction: %d -- Page row: %d -- page_counter: %d"
        args: [ 'id(swipe_direction)', 'id(page_row)', 'id(page_counter)' ] 

    # Here you can add your own logic on swiper
    - if:
        condition:
          lambda: 'return (id(startvalue_x) - id(endvalue_x)) > 50;'
        then:
          - logger.log: "Swipe DOWN"
          - lambda: |-
              id(swipe_direction) = id(swipe_down);
              id(page_row) = id(page_row) - 1;
              if (id(page_row) < 1) {
                    id(page_row) = 3;
                  } 
    - if:
        condition:
          lambda: 'return (id(startvalue_x) - id(endvalue_x)) < -50;'
        then:
          - logger.log: "Swipe UP"
          - lambda: |-
              id(swipe_direction) = id(swipe_up);
              id(page_row) = id(page_row) + 1;
              if (id(page_row) > 3) {
                    id(page_row) = 1;
                  } 
    - logger.log:
        format: "2 Swipe direction: %d -- Page row: %d -- page_counter: %d"
        args: [ 'id(swipe_direction)', 'id(page_row)', 'id(page_counter)' ] 

    # Swipe left or right
    - if:
        condition:
          lambda: 'return (id(startvalue_y) - id(endvalue_y)) > 50;'
        then:
          - logger.log: "Swipe LEFT"
          - lambda: |-
              id(swipe_direction) = id(swipe_left);
    - if:
        condition:
          lambda: 'return (id(startvalue_y) - id(endvalue_y)) < -50;'
        then:
          - logger.log: "Swipe RIGHT"
          - lambda: |-
              id(swipe_direction) = id(swipe_right);
    - logger.log:
        format: "3 Swipe direction: %d -- Page row: %d -- page_counter: %d"
        args: [ 'id(swipe_direction)', 'id(page_row)', 'id(page_counter)' ] 

    ##############################################
    # Now handle to logic to find the right page #
    ##############################################
    - logger.log:
        format: "4 Swipe direction: %d -- Page row: %d -- page_counter: %d"
        args: [ 'id(swipe_direction)', 'id(page_row)', 'id(page_counter)' ] 

    - if: # TO Row 1 - Swipe up/down
        condition:
          - lambda: 'return id(page_row) == 1;'
          - lambda: 'return ( (id(swipe_direction) == id(swipe_down)) || (id(swipe_direction) == id(swipe_up)) );'
        then:
          - lambda: |-
              id(page_counter) = 1;
    - if: # TO Row 2 - Swipe up/down
        condition:
          - lambda: 'return id(page_row) == 2;'
          - lambda: 'return ( (id(swipe_direction) == id(swipe_down)) || (id(swipe_direction) == id(swipe_up)) );'
        then:
          - lambda: |-
              id(page_counter) = 100;
    - if: # TO Row 1 - Swipe up/down
        condition:
          - lambda: 'return id(page_row) == 3;'
          - lambda: 'return ( (id(swipe_direction) == id(swipe_down)) || (id(swipe_direction) == id(swipe_up)) );'
        then:
          - lambda: |-
              id(page_counter) = 200;

    - if: # Row 1 - Swipe left
        condition:
          - lambda: 'return id(page_row) == 1;'
          - lambda: 'return id(swipe_direction) == id(swipe_left);'
        then:
          - lambda: |-
              id(page_counter) = id(page_counter) - 1;
              if (id(page_counter) < 1) {
                id(page_counter) = 5;
              }
              if (id(page_counter) > 5) {
                id(page_counter) = 5;
              }
    - if: # Row 1 - Swipe right
        condition:
          - lambda: 'return id(page_row) == 1;'
          - lambda: 'return id(swipe_direction) == id(swipe_right);'
        then:
          - lambda: |-
              id(page_counter) = id(page_counter) + 1;
              if (id(page_counter) > 5) {
                id(page_counter) = 1;
              }

    - logger.log:
        format: "5 Swipe direction: %d -- Page row: %d -- page_counter: %d"
        args: [ 'id(swipe_direction)', 'id(page_row)', 'id(page_counter)' ] 

    - if: # Row 2 - Swipe left
        condition:
          - lambda: 'return id(page_row) == 2;'
          - lambda: 'return id(swipe_direction) == id(swipe_left);'
        then:
          - lambda: |-
              id(page_counter) = id(page_counter) - 1;
              if (id(page_counter) < 100) {
                id(page_counter) = 102;
              }
              if (id(page_counter) > 102) {
                id(page_counter) = 100;
              }
    - if: # Row 2 - Swipe right
        condition:
          - lambda: 'return id(page_row) == 2;'
          - lambda: 'return id(swipe_direction) == id(swipe_right);'
        then:
          - lambda: |-
              id(page_counter) = id(page_counter) + 1;
              if (id(page_counter) > 102) {
                id(page_counter) = 100;
              }
              if (id(page_counter) < 100) {
                id(page_counter) = 100;
              }

    - logger.log:
        format: "6 Swipe direction: %d -- Page row: %d -- page_counter: %d"
        args: [ 'id(swipe_direction)', 'id(page_row)', 'id(page_counter)' ] 

    - if: # Row 3 - Swipe left
        condition:
          - lambda: 'return id(page_row) == 3;'
          - lambda: 'return id(swipe_direction) == id(swipe_left);'
        then:
          - lambda: |-
              id(page_counter) = id(page_counter) - 1;
              if (id(page_counter) < 200) {
                id(page_counter) = 202;
              }
    - if: # Row 3 - Swipe right
        condition:
          - lambda: 'return id(page_row) == 3;'
          - lambda: 'return id(swipe_direction) == id(swipe_right);'
        then:
          - lambda: |-
              id(page_counter) = id(page_counter) + 1;
              if (id(page_counter) < 200) {
                id(page_counter) = 200;
              }
              if (id(page_counter) > 202) {
                id(page_counter) = 200;
              }

    - logger.log:
        format: "7 Swipe direction: %d -- Page row: %d -- page_counter: %d"
        args: [ 'id(swipe_direction)', 'id(page_row)', 'id(page_counter)' ] 

    ############################## 
    # Now show the intended page #
    ############################## 
    # ROW 2
    - if:
        condition:
          lambda: 'return id(page_counter) == 1;'
        then:
          - display.page.show: page1
    - if:
        condition:
          lambda: 'return id(page_counter) == 2;'
        then:
          - display.page.show: page2
    - if:
        condition:
          lambda: 'return id(page_counter) == 3;'
        then:
          - display.page.show: page3
    - if:
        condition:
          lambda: 'return id(page_counter) == 4;'
        then:
          - display.page.show: page4
    - if:
        condition:
          lambda: 'return id(page_counter) == 5;'
        then:
          - display.page.show: page5
    # ROW 1
    - logger.log:
        format: "8 Swipe direction: %d -- Page row: %d -- page_counter: %d"
        args: [ 'id(swipe_direction)', 'id(page_row)', 'id(page_counter)' ] 

    - if:
        condition:
          lambda: 'return id(page_counter) == 100;'
        then:
          - display.page.show: page100
    - if:
        condition:
          lambda: 'return id(page_counter) == 101;'
        then:
          - display.page.show: page101
    - if:
        condition:
          lambda: 'return id(page_counter) == 102;'
        then:
          - display.page.show: page102
    # ROW 3
    - logger.log:
        format: "9 Swipe direction: %d -- Page row: %d -- page_counter: %d"
        args: [ 'id(swipe_direction)', 'id(page_row)', 'id(page_counter)' ] 

    - if:
        condition:
          lambda: 'return id(page_counter) == 200;'
        then:
          - display.page.show: page200
    - if:
        condition:
          lambda: 'return id(page_counter) == 201;'
        then:
          - display.page.show: page201
    - if:
        condition:
          lambda: 'return id(page_counter) == 202;'
        then:
          - display.page.show: page202

    - logger.log:
        format: "Swipe direction: %d -- Page row: %d -- page_counter: %d"
        args: [ 'id(swipe_direction)', 'id(page_row)', 'id(page_counter)' ] 
    # Clear the recorded values
    - component.update: watchface
    - lambda: |-
           id(startvalue_x) = 0;
           id(endvalue_x) = 0;
           id(startvalue_y) = 0;
           id(endvalue_y) = 0;
           id(swipe_direction) = 0;

    - component.update: watchface



sensor:
  # DIAGNOSTICS
  - platform: uptime
    name: "$devicename Uptime"
  - platform: wifi_signal
    name: "$devicename WiFi Signal"
    update_interval: 60s
  - platform: template
    name: $devicename free memory
    lambda: return heap_caps_get_free_size(MALLOC_CAP_INTERNAL);
    icon: "mdi:memory"
    entity_category: diagnostic
    state_class: measurement
    unit_of_measurement: "b"
    update_interval: 60s  
  # DEVICES and ENTITIES
  #- platform: homeassistant
  #  id: room_temperature
  #  entity_id: sensor.badkamer_thgr810_thgn800_temperature


# Needed for the display
spi:
  mosi_pin: $mopin
  clk_pin: $clpin


i2c:
  sda: $sdapin
  scl: $sclpin
  

# https://esphome.io/components/display/fonts.html
font:
  - file: "gfonts://Roboto"
    id: font_12
    size: 12
  - file: "gfonts://Roboto"
    id: font_16
    size: 16
  - file: "gfonts://Roboto"
    id: font_24
    size: 24
  - file: "gfonts://Roboto"
    id: font_32
    size: 32
  - file: "gfonts://Roboto"
    id: font_64
    size: 64

color:
  - id: my_red
    hex: FF0000
  - id: my_green
    hex: 008000
  - id: my_blue
    hex: 0000FF
  - id: my_yellow
    hex: FFFF00

qr_code:
  - id: wifi_qr
    value: MijnPaswoord

# https://esphome.io/components/display/index.html
display:
  - platform: ili9xxx
    model: GC9A01A
    id: watchface
    reset_pin: $repin
    cs_pin: $cspin
    dc_pin: 
      number: $dcpin
      ignore_strapping_warning: true
      # The above is to remove the strapping pin warning message
    # How often to refresh the display
    update_interval: 1s
    # Look and feel of each page below
    pages:
      - id: page1
        lambda: |-
          it.printf(140, 30, id(font_12), id(my_red), "page 1");
          it.printf(140, 200, id(font_12), id(my_green), "swipe");
          // Draw the QR-code at position [x=50,y=0] with white color and a 7x scale
          it.qr_code(50, 50, id(wifi_qr), Color(180,180,180), 7);
      - id: page2
        lambda: |-
          it.printf(140, 30, id(font_12), id(my_red), "page 2");
          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, 200, id(font_16), id(my_green), TextAlign::CENTER, "outside");
          it.circle(120, 120, 119, id(my_yellow));          
      - id: page3
        lambda: |-
          it.printf(140, 30, id(font_12), id(my_red), "page 3");
          it.strftime(120,80, id(font_16), id(my_blue), TextAlign::CENTER, "%A %b %d", id(esptime).now());
          it.printf(120,120, id(font_24), TextAlign::CENTER, "Today Min/Max:");
          it.printf(120, 200, id(font_16), id(my_green), TextAlign::CENTER, "weather");
          it.circle(120, 120, 119, id(my_blue));
      - id: page4
        lambda: |-
          it.printf(140, 30, id(font_12), id(my_red), "page 4");
          it.printf(120, 200, id(font_16), id(my_blue), TextAlign::CENTER, "lamp");
          it.circle(120, 120, 119, id(my_green));   
      - id: page5
        lambda: |-
          it.printf(140, 30, id(font_12), id(my_red), "page 5");
          it.printf(120,30, id(font_16), id(my_blue), TextAlign::CENTER, "help");
          it.printf(120,60, id(font_16), TextAlign::CENTER, "Swipe up/down");
          it.printf(120,80, id(font_16), TextAlign::CENTER, "to change temp");
          it.printf(120,110, id(font_16), id(my_yellow), TextAlign::CENTER, "Long press on thermostat");
          it.printf(120,130, id(font_16), id(my_yellow), TextAlign::CENTER, "to toggle off/cool/heat");          
          it.printf(120,160, id(font_16), TextAlign::CENTER, "Swipe L/R to change screens");
          it.printf(120,190, id(font_16), id(my_yellow), TextAlign::CENTER, "Tap to turn screen");
          it.printf(120,210, id(font_16), id(my_yellow), TextAlign::CENTER, "on/off");
          it.circle(120, 120, 119, id(my_green));     
      # SWIPE DOWN PAGES
      - id: page100
        lambda: |-
          it.printf(140, 30, id(font_12), id(my_red), "page 100");
          it.printf(140, 200, id(font_12), id(my_green), "default1");
      - id: page101
        lambda: |-
          it.printf(140, 30, id(font_12), id(my_red), "page 101");
      - id: page102
        lambda: |-
          it.printf(140, 30, id(font_12), id(my_red), "page 102");
      # SWIPE UP PAGES    
      - id: page200
        lambda: |-
          it.printf(140, 30, id(font_12), id(my_red), "page 200");
          it.printf(140, 200, id(font_12), id(my_green), "swipe");
          // Draw the QR-code at position [x=50,y=0] with white color and a 2x scale
          it.qr_code(75, 75, id(wifi_qr), Color(255,255,255), 2);
      - id: page201
        lambda: |-
          it.printf(140, 30, id(font_12), id(my_red), "page 201");
      - id: page202
        lambda: |-
          it.printf(140, 30, id(font_12), id(my_red), "page 202");
2 Likes

You don’t need the extra touch screen now either :wink: it is in main ESPhome now :slight_smile:

touchscreen:
  platform: cst816
  id: my_touchscreen
  interrupt_pin: 0
  reset_pin: 1
1 Like

It’s in the yaml example shown above and you can drop teh reset_pin definition also. That give you an extra pin.

This would be enough

touchscreen:
  id: this_touch_screen
  platform: cst816
  interrupt_pin: GPIO0

I made this at least for me, usefull display and control. It give me various info about the weather, time, EV and such and control over the media player and a few other entities

You can swipe up and down in any direction and a single touch to toggle/ activate a button or item.

2 Likes

About the unexpected reboots.
Sadly i have them too but I believe it is due some sort of memory leak or resources being eaten up

[17:21:14][D][sensor:094]: 'energie-monitor free memory': Sending state 52596.00000 b with 1 decimals of accuracy
..
[17:22:14][D][sensor:094]: 'energie-monitor free memory': Sending state 37796.00000 b with 1 decimals of accuracy
...
[17:23:14][D][sensor:094]: 'energie-monitor free memory': Sending state 22892.00000 b with 1 decimals of accuracy

Interesting. I have three now, and all three (two running with external screen and touch drivers, one with external touch driver) haven’t had an issue since I’ve a) put capacitors on the power (I suspect an issue specifically for me due to powering multiple devices off one power source) and b) not using the ble_tracker.

I see you have quite a few screens, many with images, so although they look really great maybe that’s eating in to the memory as well. Perhaps check those images and try to avoid the resize option - you could also disable some of those screens and see what impact re-enabling them has on the available memory. Mine (see the chart showing about 7 days of free memory tracking since the last restart for one of mine) tend to start with over 200,000 bytes free on initial starting then average around the 83,000 byte free mark, sometimes dropping to around the 81,000 bytes. I think if it drops below 60,000 bytes then you may start to run into trouble so perhaps aim for that?

I might also change one of mine to no longer use any external components to see if the standard touch driver is eating memory.

Here is my current version, I may have made it too busy but its hard to pick which data I want :rofl:

3 Likes

@zagnuts

You mention some interesting points but I might have found the problem that is eating up the resources.

      - id: page100 # World times
        lambda: |-
            it.strftime(120,40, id(font_16), id(my_blue), TextAlign::CENTER, "%A %b %d", id(localtimezone).now());
            it.strftime(120,70, id(font_32), TextAlign::CENTER, "%I:%M %p", id(localtimezone).now());

            it.printf(30, 95, id(font_16), id(my_green), "New York");
              setenv("TZ", "EST+5EDT,M3.2.0/2,M11.1.0/2", 1);
              tzset();
              auto thetime = id(localtimezone).now();
              if (thetime.is_valid())
                it.strftime(30,115, id(font_16), "%I:%M %p", thetime);

            it.printf(135, 95, id(font_16), id(my_green), "Auckland");
              setenv("TZ", "NZST+13NZDT,M3.4.4/26,M10.5.0", 1);
              tzset();
              auto thetime2 = id(localtimezone).now();
              if (thetime2.is_valid())
                it.strftime(130,115, id(font_16), "%I:%M %p", thetime2);

I use this piece of code to show the time in different cities. To make it work i have to set the time environment each time to a different time zone. This is eating up my resources. If i remove this part then the memory usage stays around rh 55K mark.

Now I only have to find a different solution to show different world times or just remove that part.

btw, i don’t use the image resize option.

Is that made with lvgl?

I removed the timezone code from esphome and move that to HA using the worldclock platform. After that I just imported the sensors. As far as I can see now then memory usage stays stable.

1 Like

Yes, code for it is here: GitHub - clowrey/esphome-esp32-2424s012-lvgl-powermeter

I have just wasted over half a day trying to flash Esphome firmware to a new ESP32-2424S012 module! I have flashed lots of ESP8266 and a few ESP32-C3 modules using the Linux esp-flasher program without issues, but using it to flash this device results in it boot looping with no display or wifi.

The esp-flasher was able to flash the factory 1.28DEMO.bin file successfully to the boot looping device to get it working again, so I assumed it would also be able to flash the Esphome bin files I had created.

I thought the problem was because I had made a mistake editing some of the settings in the example code given in the examples in this thread. But after trying most of them, and making a bare bones file with only the wifi and IP address settings in it, I came to the conclusion it must be due to the way it was being flashed.

As a last resort I used the https://web.esphome.io web flasher to flash the Modern format bin file and it worked first time! Then using the OTA flash from the Esphome dashboard works to upload changed code.

The first post does say they used the https://web.esphome.io web flasher but doesn’t actually say that you MUST use it for the first Esphome flash.

I’ve no idea why esp-flasher will flash other ESP32-C3 modules but not this one?

Hopefully this information will stop other people wasting their time getting the initial Esphome flashing to work.

2 Likes

I’m not sure on the exact reason either, but believe it is something with how it gets the module into boot mode. I also always use the https://web.esphome.io/ flasher for first time installs, then the OTA option, it always saves time and frustration :slight_smile:

Although I had Esphome able to flash the board OTA after first flashing using the https://web.esphome.ioboard web flasher. After remembering that I had previously had trouble flashing a Sonoff Basic R4, which also uses an Esp32-C3, using the Esp-flasher program, I experimented further.

To get the Esp-flasher program able to flash it I had to change the Esphome yaml code Framework Type from arduino to esp-idf and add the entry platformio_options: to the esphome: setting with 3 entries. See example below.

esphome:
  name: $devicename
  friendly_name: $friendname
  platformio_options:
    board_build.f_flash: 40000000L
    board_build.flash_mode: dio
    board_build.flash_size: 4MB
#
esp32:
  board: esp32-c3-devkitm-1
  variant: esp32c3
  framework:
#    type: arduino
 # used instead of arduino
    type: esp-idf

With these settings the Esp-flasher program can flash the Esphome bin file to a device running the factory Demo bin code.

But if you flash a bin file with the type esp-idf that is in Legacy format it works but Esphome Dashboard cannot OTA flash updates to it! It fails with a memory or similar error. If you flash the Modern format then the Esphome dashboard can do OTA updates.

Looking at the logs of the Esp-flasher program if you use a Legacy format bin file, or one with the arduino Framework Type, it looks like it flashes about 4 partitions and then it boot loops after being flashed. But if you flash a Modern format, or one with the esp-idf Framework Type, it looks like it flashes just one large partition. Flashing the factory 1.28DEMO.bin file, which works, the logs show the same type of one large partition.

So it appears that the partition scheme used is what is causing the problem of boot loops and the Esphome Dashboard being unable to do OTA updates.

But I went back to using the arduino Framework in the bin file and did the initial flash again using the web flasher as I later had some issues after making changes to the yaml code. I’m not sure if they were due to the esp-idf Framework type or not, but they didn’t reappear after going back to using the arduino type.

I hope this helps someone.

Do you guys know how to establish communication between the screen and an ESP32 receiver via UART or Bluetooth?

The screen is attached to the esp32 by spi.

I have put in what I believe are the settings for the UART socket on the device in my sample code ie:

#uart:
#  rx_pin: GPIO20
#  tx_pin: GPIO21
#  baud_rate: 9600
#  id: uart_bus

I’ve tried to use it with mixed results, but would be interested to hear if anyone is able to use it properly. Bluetooth I think would be possible, but it seems to use a lot of ram so can impact reliability - again feel free, but might not be worth it.

Did you manage to send and receive something via UART? I can’t seem to do it.

I think you are confused. The screen comes complete with a esp32.

Are you trying to communicate with a second esp32?

I was able to get the address from a Dallas ds18b20 so yes could read, but couldn’t get a sane temperature reading for an unknown reason. I didn’t spend much time on it as it was going to be neater for me to use separate sensors and query them via home assistant.