[EN] How to make the stopwatch?

From the article Create a clock that displays an analog display through a color display, this time, it has been modified to make it work as a timer or stopwatch by using the ESP32-CAM board connected to the TFT display and using a switch from pin GPIO0 used as a mode switch or program chip when booting the system or supply power to board ESP32-CAM as shown in Figure 1 and programming still uses Python language with MicroPython as always

Figure 1 dCore RED, esp32cam+REDTAB

Draw a clock

The improvements from the previous article are to change the style of the pins on the watch face. By drawing a change from drawing a circle from the calculated midpoint of each position to draw in a straight line. The working processes of this section are

  • calculate for newX, newY
  • calculate newX2 และ newY2
  • draw straight line from (newX, newY) to (newX2, newY2)

The result is as shown in Figure 2, and the code for drawing the watch face is as follows.

screenCenterX = 40
screenCenterY = 40
...
def drawClock():
    midPointCircleDraw(screenCenterX, screenCenterY, 39, tft.color(232,232,232))
    n12x = screenCenterX
    n12y = 2
    n12y2 = 8
    for i in range(12):
        newX,newY = rotate( n12x, n12y, 30*i)
        newX2,newY2 = rotate( n12x, n12y2, 30*i)
        tft.line((newX,newY),(newX2, newY2),tft.color(232, 232, 64))
Figure 2 Display when timer began

Example Code

Circuit

Equipment

  1.  ST7735 GREENTAB80x160 display
  2. ESP32 , we use ESP32CAM

Connecting from display to ESP32 using channel 2 SPI bus

  1. SCK of display connect to GPIO14 of ESP32
  2. MOSI of display connect to GPIO12 of ESP32
  3. DC of display connect to GPIO15 of ESP32
  4. RST of display connect to GPIO13 of ESP32
  5. CS of display connect to GPIO2 of ESP32

Connecting the switch to the GPIO0 pin

Code

The working principles of the sample program are

  • Set the working load to 0.
  • Draw a dial.
  • if switch was pressed
    • if state = 0
      • start timer
      • change state to 1
    • if state = 1
      • stop timer
      • show hour, minute, second
      • clear the variables to default

Code

from ST7735 import TFT
import machine as mc
from machine import SPI,Pin, Timer
import time
import math

#################################################################
###### setting ##################################################
#################################################################
mc.freq(240000000)
spi = SPI(2, baudrate=26000000,
          sck=Pin(14), mosi=Pin(12),
          polarity=0, phase=0)
sw = Pin(0, Pin.IN, Pin.PULL_UP)
# dc, rst, cs
tft=TFT(spi,15,13,2)
tft.fill(tft.BLACK)
tft.swap()
screenCenterX = 40
screenCenterY = 40
second = 0
minute = 0
hour = 0
state = 0

#################################################################
###### sub modules ##############################################
#################################################################
def midPointCircleDraw(x_centre, y_centre, r, c):
    x = r
    y = 0

    tft.setPixel(x + x_centre,y + y_centre, c)

    # When radius is zero only a single
    # point be printed
    if (r > 0) :
        tft.setPixel(x + x_centre,-y + y_centre, c)
        tft.setPixel(y + x_centre,x + y_centre, c)
        tft.setPixel(-y + x_centre,x + y_centre, c)
     
    # Initialising the value of P
    P = 1 - r
 
    while x > y:
        y += 1
         
        # Mid-point inside or on the perimeter
        if P <= 0:
            P = P + 2 * y + 1
             
        # Mid-point outside the perimeter
        else:        
            x -= 1
            P = P + 2 * y - 2 * x + 1
         
        # All the perimeter points have
        # already been printed
        if (x < y):
            break
         
        # Printing the generated point its reflection
        # in the other octants after translation
        tft.setPixel(x + x_centre,y + y_centre, c)
        tft.setPixel(-x + x_centre, y + y_centre, c)
        tft.setPixel( x + x_centre,-y + y_centre, c)
        tft.setPixel( -x + x_centre,-y + y_centre, c)
         
        # If the generated point on the line x = y then
        # the perimeter points have already been printed
        if x != y:
            tft.setPixel(y + x_centre, x + y_centre, c)
            tft.setPixel(-y + x_centre, x + y_centre, c)
            tft.setPixel(y + x_centre, -x + y_centre, c)
            tft.setPixel(-y + x_centre, -x + y_centre, c)

def rotate(pX,pY,angle):
    rad = math.radians(angle)
    pX -= screenCenterX
    pY -= screenCenterY
    xCos = pX*math.cos(rad)
    ySin = pY*math.sin(rad)
    xSin = pX*math.sin(rad)
    yCos = pY*math.cos(rad)
    newX = xCos - ySin + screenCenterX
    newY = xSin + yCos + screenCenterY
    return (int(newX), int(newY))

def drawClock():
    midPointCircleDraw(screenCenterX, screenCenterY, 39, tft.color(232,232,232))
    n12x = screenCenterX
    n12y = 2
    n12y2 = 8
    for i in range(12):
        newX,newY = rotate( n12x, n12y, 30*i)
        newX2,newY2 = rotate( n12x, n12y2, 30*i)
        tft.line((newX,newY),(newX2, newY2),tft.color(232, 232, 64))
        
def drawSecond( sec ):
    deg = sec*6
    n12x = screenCenterX
    n12y = 10
    newX,newY = rotate( n12x, n12y, deg)
    tft.line((screenCenterX, screenCenterY), (newX, newY), tft.color(232, 64, 232))

def cbSecond(x):
    global second, minute, hour
    second += 1
    if (second == 60):
        second = 0
        minute += 1
        if (minute == 60):
            minute = 0
            hour += 1
            if (hour == 12):
                hour = 0
    tft.fill(tft.BLACK)
    drawClock()
    drawSecond(second)
    tft.swap()
    
#################################################################
###### main program #############################################
#################################################################
tft.fill(tft.BLACK)
drawClock()
drawSecond(0)
tft.swap()
secTmr = Timer(0)
while True:
    if (sw.value()==0):
        if state == 0:
            secTmr.init( period=1000, mode=Timer.PERIODIC, callback=cbSecond)
            state = 1 # start
        else:
            state = 0
            secTmr.deinit()
            tft.fill(tft.BLACK)
            drawClock()
            drawSecond(0)
            tft.text("H:{}".format(hour),(96,24),tft.color(232, 192, 194))
            tft.text("M:{}".format(minute),(96,34),tft.color(192, 232, 194))
            tft.text("S:{}".format(second),(96,44),tft.color(194, 192, 232))
            tft.swap()
            second = 0
            minute = 0
            hour = 0
        time.sleep(0.25)


The result is as shown it Figure 3

Figure 3 Result of the code

Conclusion

From this article, we can see that we can improve the function of the watch as a timer by modifying the working procedure. Making the original equipment more capable means the hardware that we use will work very well, besides the hardware design being good, it also depends on the software we write. We hope that the examples in this article should serve as a guide for improving functionality or combine it with the previous article to support more functionality and finally, have fun with programming.

(C) 2020-2022, By Jarut Busarathid and Danai Jedsadathitikul
Updated 2022-01-15