ESP32C3 Smartwatch: Implementing Default Sleep Mode and Touch Display

The previous article “Implementation and Experience of Pointer Dial Function for ESP32C3 Smartwatch” shared the functionality of the pointer dial on the ESP32C3 smartwatch. In the video, the Redmi Band 2 was used for comparison, which shows the main screen after touch and goes to sleep after 5 seconds. We have created a default sleep and touch display effect based on the Redmi Band 2.

This similar functionality can also be ported to Lichuang Practical.

1. Introduction to the Smartwatch

This smartwatch is controlled by the ESP32C3 and is equipped with a1.69-inch touchscreen with a resolution of240x320. The display driver used isST7789, and the touch driver isCST816T.

2. Touch Driver

There is no relevant Python driver forCST816T found on Github, so I rewrote the C language driver into aMicropython driver. The download link is: https://gitee.com/py2012/cst816t

3. Network Connection and Time Retrieval

The time displayed in the previous article is the system time. Once the USB connection is disconnected, the time resets to midnight. To get real-time, we need to connect to the internet. The relevant function code is as follows:

def do_connect():        wlan = network.WLAN(network.STA_IF)    wlan.active(True)        if not wlan.isconnected():                print('Connecting to WIFI...')        tft.text(font, 'connecting to network...',0,0)# no Chinese character library        wlan.connect('SSID', 'password')# Replace SSID and password with WIFI account password                 i = 1        while not wlan.isconnected():  # Wait for wireless connection            print("Connecting...{}".format(i))            tft.text(font, "Connecting...{}".format(i),0,16)            i += 1            utime.sleep(1)            print('network config:', wlan.ifconfig())    tft.text(font, 'network config:{}'.format(wlan.ifconfig()),0,16)
def sync_ntp():    print("Starting to sync network time")    tft.text(font, "loading",0,32)    try:        ntptime.NTP_DELTA = 3155644800  # Default        ntptime.host = 'pool.ntp.org'  # NTP server, default is "pool.ntp.org"         ntptime.settime()  # Modify device time    except:        sync_ntp()

4. Touch Display

TheCST816T driver contains aFingerIndex parameter, which confirms whether the screen is touched by judging theFingerIndex parameter. The default state is sleep (screen displays black), and when the screen is touched, it shows the time dial, and goes to sleep again after 6 seconds. The reference code is as follows:

"""PY Learning Notes"""from machine import Pin, SPIimport utimeimport mathimport st7789import ntptimeimport networkimport vga1_bold_16x16 as fontfrom cst816t import CST816T
'''Relevant functions omitted, see above for details'''# Initialize display screentft = config()tft.init()tft.fill(st7789.BLACK)# Initialize touch screent = CST816T(sda_pin=11, scl_pin=7, rst_pin=10, int_pin=9)
def main():    # Connect to the internet and get time    do_connect()    sync_ntp()        width = tft.width()    height = tft.height()    radius = min(width, height)             ofs = (width - radius) // 2             center_x = radius // 2 + ofs - 1        center_y = radius // 2 - 1
    # Draw dial background    face = "face_320x240_3.jpg"# Change to black background    tft.jpg(face, 0, 0, st7789.SLOW)
    # Create polygons for hour, minute, and second hands    # The polygon must be a closed convex polygon, otherwise there will be problems.    second_len = int(radius * 0.65 / 2)    second_poly = hand_polygon(second_len, 2)    minute_len = int(radius * 0.6 / 2)    minute_poly = hand_polygon(minute_len, 2)    hour_len = int(radius * 0.5 / 2)    hour_poly = hand_polygon(hour_len, 3)
    # Calculate the constants for the angles of the hands.    pi_div_6 = math.pi/6    pi_div_30 = math.pi/30    pi_div_360 = math.pi/360    pi_div_2160 = math.pi/2160
    # Initialize boundary rectangle variables for drawing hour, minute, and second hands.    # Calling bounding with True will reset the boundary, calling with False will disable the boundary    tft.bounding(True)    hour_bound = tft.bounding(True)    minute_bound = tft.bounding(True)    second_bound = tft.bounding(True)
    last_touch_time = 0  # Record last touch time    screen_timeout = 6   # Screen timeout (seconds)    screen_on = False    # Screen display status
    # Initial state is black screen    tft.fill(st7789.BLACK)
    while True:        current_time = utime.time()        FingerIndex,x,y,_ = t.getTouch()                # Detect touch events        if FingerIndex != 0:  # When there is touch            if not screen_on:  # If the screen is off                # Redisplay dial background                tft.jpg(face, 0, 0, st7789.SLOW)                screen_on = True            last_touch_time = current_time                # Check if the screen needs to be turned off        if screen_on and (current_time - last_touch_time > screen_timeout):            tft.fill(st7789.BLACK)  # Fill black to achieve sleep            screen_on = False                # Only update the clock when the screen is on        if screen_on:            # Save the current time (seconds) to determine when to update the display.            last = current_time
            # Get current hour, minute, and second            _, _, _, hour, minute, second, _, _ = utime.localtime()                        # Convert to Beijing time            hour = (hour + 8) % 24              # Convert 24-hour format to 12-hour format            hour = hour % 12 if hour % 12 != 0 else 12
            # Calculate the angle (radians) of the hour hand            # Every hour turns 30 degrees(π/6), every minute turns 0.5 degrees(π/360), every second turns(1/120) degrees(π/21600)            hour_ang = (                (hour * pi_div_6) +                (minute * pi_div_360) +                (second * math.pi/21600)) + math.pi  # Add 180 degrees to compensate for screen rotation
            # Calculate the angle (radians) of the minute hand            # Every minute turns 6 degrees(π/30), every second turns 0.1 degrees(π/1800)            minute_ang = (                (minute * pi_div_30) +                (second * math.pi/1800)) + math.pi  # Add 180 degrees to compensate for screen rotation
            # Calculate the angle (radians) of the second hand            second_ang = (second * pi_div_30) + math.pi  # Add 180 degrees to compensate for screen rotation
            # Erase the last drawn boundary area of the hour hand            x1, y1, x2, y2 = hour_bound            tft.fill_rect(x1, y1, x2, y2, st7789.BLACK)
            # Erase the last drawn boundary area of the minute hand            x1, y1, x2, y2 = minute_bound            tft.fill_rect(x1, y1, x2, y2, st7789.BLACK)
            # Erase the last drawn boundary area of the second hand            x1, y1, x2, y2 = second_bound            tft.fill_rect(x1, y1, x2, y2, st7789.BLACK)
            # Draw the center point after erasing the boundary area to reduce flicker            tft.fill_circle(center_x, center_y, 5, st7789.RED)
            tft.bounding(True)      # Clear boundary rectangle
            # Draw and fill the hour hand polygon rotated to hour_ang            tft.fill_polygon(hour_poly, center_x, center_y, st7789.WHITE, hour_ang)
            # Get the boundary rectangle drawn by the hour polygon and reset the boundary box for the next polygon            hour_bound = tft.bounding(True, True)
            # Draw and fill the minute hand polygon rotated to minute_ang            tft.fill_polygon(minute_poly, center_x, center_y, st7789.WHITE, minute_ang)
            # Get the boundary rectangle drawn by the minute polygon and reset the boundary box for the next polygon            minute_bound = tft.bounding(True, True)
            # Draw and fill the second hand polygon rotated to second_ang            tft.fill_polygon(second_poly, center_x, center_y, st7789.RED, second_ang)
            # Get the boundary rectangle drawn by the second polygon and reset the boundary box for the next polygon            second_bound = tft.bounding(True, True)
            # Draw the center point again to cover the second hand            tft.fill_circle(center_x, center_y, 5, st7789.RED)
            # Wait until the current second changes            while last == utime.time() and screen_on:                FingerIndex,x,y,_ = t.getTouch()                if FingerIndex != 0:                    last_touch_time = utime.time()                if utime.time() - last_touch_time > screen_timeout:                    screen_on = False                    tft.fill(st7789.BLACK)                    break                utime.sleep_ms(50)        else:            utime.sleep_ms(100)  # Reduce polling frequency when the screen is off
main()

ESP32C3 Smartwatch: Implementing Default Sleep Mode and Touch Display

1. The ESP32C3 smartwatch is connected to 3.3V and is always on by default, which cannot be adjusted. Therefore, black filling is used instead of sleep (however, it does not save power).

2. The dial is not finely polished, and there may be some blurriness next to the scale, which is not noticeable in reality but is quite obvious in the video. Please ignore it.

Recommended Reading:

  • High-precision voltage measurement experimental device based on ADS1115 (4. Capacitor charging and discharging experiment)

  • Make a camera-controlled car for less than 100 yuan (Part 2)

  • Lichuang Practical ESP32S3 Practical: How to Display Camera Images in Real-time on the Screen

  • Sky Star STM32 Solder a Battery Holder: Create a Smart Clock

  • ESP32-C3 E-ink Screen Electronic Clock (Based on Micropython)

  • Micro Weather Station Based on RP2040 and MicroPython

ESP32C3 Smartwatch: Implementing Default Sleep Mode and Touch Display

Leave a Comment

×