Analog Watch

Analog Watch

How do smart watches work? In this lesson you will learn how to build an analog-ish clock. This can run on BrainPad Pulse alone, but the results will be much cooler when combined with BrainPower.

Prerequisite

You must have basic understanding of coding and have completed the Drawing lesson.

The Face

The watch face is what you see behind the hands. We will build a simple face that consists of the numbers 1 to 12. We want those to be arranged in a circle. We also want the circle (oval) to have different width and height, to accommodate a non-square screens. The Math libraries include Sin() and Cos() we would need to calculate/draw circles.

In C# we’ll need to add ‘using System‘ to the top with our other using statements.

In Python we’ll need to add ‘import math‘ to the top of our code.

using System;
using static BrainPad.Controller;
using static BrainPad.Display;

void DrawFace(double x, double y, double width, double height){
    var h = 0;
    while (h < 12) {
        var a = (h / 6 * Math.PI) - (Math.PI / 2);
        Point(x + width * Math.Cos(a), y + height * Math.Sin(a), White);
        h = h + 1;
      }
}
Clear();
DrawFace(64,32,38,28);
Show();
from BrainPad import *
import math

def DrawFace(x, y, width, height):
    h = 0
    while (h < 12):
        a = (h / 6 * math.pi) - (math.pi / 2)
        Point(x + width * math.cos(a), y + height * math.sin(a), White)
        h = h + 1
        
Clear()
DrawFace(64,32,38,28)
Show()
 

In C# running the code above will only show 2 points. This is due to the compiler opting to use an int type when we created the h variable. The type int doesn’t have fractions, and so the math in the loop does not work properly as we lose all fractions.

In Python this isn’t an issue because the variable is dynamic type and it changes on the fly to whatever is necessary.

Variable Type Refresher

The Variables lesson covers variables in more detail. This section will highlight the difference between int and double when using C#.

When using var, the system is given the choice to select the appropriate variable type. We can enforce the use of fractions (double type) by assigning a fraction to the variable when it is first created. For example, if the initial value is 0, then set the variable to 0.0. Mathematically speaking, 0 and 0.0 are exactly the same. But in code, the use of 0.0 is telling the system that this is a variable the requires fractions and that any mathematical calculations must keep track of fractions.

Here is an example code showing the problem. What is 5 divided by 2?

var v = 5;
v = v / 2;
Print(v);

5 divided by 2 results in 2.5, but if you use integer instead of double, then the result is 2 not 2.5. This is the case in C# but Python is different as it will change the type automatically. and the results will be correct.

Now, modify the same code but use 5.0 when creating the v variable.

var v = 5.0;
v = v / 2;
Print(v);

We also have the choice to select the variable type to expect fractions (double type).

double v = 5;
v = v / 2;
Print(v);

Face Redo

Going back to our initial example, we set the h variable to 0.0, or use double instead of var.

In Python we’ll keep h = 0, since it handles variables differently.

void DrawFace(double x, double y, double width, double height){
    double h = 0.0;
    while (h < 12) {
        var a = (h / 6 * Math.PI) - (Math.PI / 2);
        Point(x + width * Math.Cos(a), y + height * Math.Sin(a), White);
        h = h + 1;
      }
}
Clear();
DrawFace(64,32,38,28);
Show();
def DrawFace(x, y, width, height):
    h = 0
    while (h < 12):
        a = (h / 6 * math.pi) - (math.pi / 2)
        Point(x + width * math.cos(a), y + height * math.sin(a), White)
        h = h + 1
        
Clear()
DrawFace(64,32,38,28)
Show()
 

And we finally have 12 points surrounding the “face”. Let’s take this a bit further and put numbers instead of points.

void DrawFace(double x, double y, double width, double height){
    double h = 0.0;
    while (h < 12) {
        var a = (h / 6 * Math.PI) - (Math.PI / 2);
        Text(h, x + width * Math.Cos(a), y + height * Math.Sin(a));
        h = h + 1;
      }
}
Clear();
DrawFace(64,32,38,28);
Show();
def DrawFace(x, y, width, height):
    h = 0
    while (h < 12):
        a = (h / 6 * math.pi) - (math.pi / 2)

        Text(h, x + width * math.cos(a), y + height * math.sin(a)
        h = h + 1
        
Clear()
DrawFace(64,32,38,28)
Show()

That is almost correct! We are used to seeing 12 on the top, but we now see 0. In fact, hour 0 and hour 12 are the same thing! Time is revolving and when time reached 12 it is simply going back to 0. Anyway, we still need to correct the face. We can fix this by adding an if statement where if the number is 0, then it modifies to 12. But since time is revolving and the continuing drawing of a circle will just redraw over the same circle, also revolving like time, we can start count at 1 instead of 0 and then draw all the way to 12.

void DrawFace(double x, double y, double width, double height){
    var h = 1.0;
    while (h <= 12) {
        var a = (h / 6 * Math.PI) - (Math.PI / 2);
        Text(h, x + width * Math.Cos(a), y + height * Math.Sin(a));
        h = h + 1;
      }
}
Clear();
DrawFace(64,32,38,28);
Show();
def DrawFace(x, y, width, height):
    h = 1
    while (h <= 12):
        a = (h / 6 * math.pi) - (math.pi / 2)
        Text(h, x + width * math.cos(a), y + height * math.sin(a))      
        h = h + 1
        
Clear()
DrawFace(64,32,38,28)
Show()
 

The Hands

The hand is simply a Line() that starts at the middle of the watch and points to a location around the face. The hour hand uses 1 to 12 numbers, but the minute and second hands are from 1 to 60. We will make one function that draws a hand. We will give it location and size, and then it will have a time and unit. Unit is 12 for hours and 60 for seconds and minutes.

void DrawHand(double x, double y, double width, double height, double time, double unit) {
    unit = unit / 2;
    var a = (time / unit * Math.PI) - (Math.PI/2);
    var ex = x + width * Math.Cos(a);
    var ey = y + height * Math.Sin(a);
    Line(x, y, ex, ey);
}
def DrawHand(x, y, width,height,time,unit):
    unit = unit / 2
    a = (time / unit * math.pi) - (math.pi/2)
    ex = x + width * math.cos(a)
    ey = y + height * math.sin(a)
    Line(x, y, ex, ey)

Let’s say the current time is 3:25 and 40 seconds. Oh, and don’t forget about the face!

DrawFace(64, 32, 38, 28);

DrawHand(64, 32, 20, 10, 3, 12);
DrawHand(64, 32, 28, 18, 25, 60);
DrawHand(64, 32, 30, 20, 40, 60);
Show();
DrawFace(64, 32, 38, 28)

DrawHand(64, 32, 20, 10, 3, 12)
DrawHand(64, 32, 28, 18, 25, 60)
DrawHand(64, 32, 30, 20, 40, 60)
Show()

Do you see how the hands do not seem perfectly centered? Actually, the hands are perfectly centered, but the face is not! This is because the text position is not in the middle of the character. It is in the top left corner of the characters. The characters are 8×6 points, so we need to move the face back by 4×3 points.

DrawFace(64 - 4, 32 - 3, 38, 28);

DrawHand(64, 32, 20, 10, 3, 12);
DrawHand(64, 32, 28, 18, 25, 60);
DrawHand(64, 32, 30, 20, 40, 60);
Show();
DrawFace(64 - 4, 32 - 3, 38, 28)

DrawHand(64, 32, 20, 10, 3, 12)
DrawHand(64, 32, 28, 18, 25, 60)
DrawHand(64, 32, 30, 20, 40, 60)
Show()

We can read the internal system time to show the appropriate time. This requires the use of services provided by the system to read time. Each language/system/OS has its own unique available services. For example, C# includes .NET services to handle time. Here is a time machine example, where we read the time, show it on the “face”, and then add one second.

var time = new DateTime(2021, 1, 1, 3, 30, 1);

while (true) {
    Clear();
    DrawFace(64 - 4, 32 - 3, 38, 28);

    DrawHand(64, 32, 20, 10, time.Hour, 12);
    DrawHand(64, 32, 28, 18, time.Minute, 60);
    DrawHand(64, 32, 30, 20, time.Second, 60);
    Show();
    time = time.Add(new TimeSpan(0, 0, +1));
    Wait(1);
}

On the other hand, MicroPython does not have a time service. The full Python does but not the scaled down MicroPython version. We will instead create a time counter. We now have generic code that can run on both.

We may have to adjust our Wait() function argument to keep more accurate time since we’re telling it to Wait(1) second and our code actually takes time to run.

var seconds = 0;
var minutes = 25;
var hours = 3;

while (true){
    Clear();
    DrawFace(64 - 4, 32 - 3, 38, 28);

    DrawHand(64, 32, 20, 10, hours, 12);
    DrawHand(64, 32, 28, 18, minutes, 60);
    DrawHand(64, 32, 30, 20, seconds, 60);
    
    seconds = seconds + 1;
    if (seconds >= 60) {
        seconds = 0;
        minutes = minutes + 1;
    }
    if (minutes >=60) {
        minutes = 0;
        hours = hours + 1;
    }
    if (hours > 12) {
        hours = 1;
    }
    Wait(1);
    Show();
}
seconds = 0
minutes = 25
hours = 3

while True:
    Clear()
    DrawFace(64 - 4, 32 - 3, 38, 28);

    DrawHand(64, 32, 20, 10, hours, 12);
    DrawHand(64, 32, 28, 18, minutes, 60);
    DrawHand(64, 32, 30, 20, seconds, 60);
    
    seconds = seconds + 1
    if seconds >= 60:
        seconds = 0
        minutes = minutes + 1
    if minutes >=60:
        minutes =0
        hours = hours + 1
    if hours > 12:
        hours = 1
    Wait(1)
    Show()
 
  
  
 

What’s Next?

Let’s use the buttons to set the hours and minutes on our new watch. This should be an easy addition given our current level of programming knowledge. First create the 2 button objects.

var btnA = Button(ButtonA,0);
var btnB = Button(ButtonB,0);
btnA = Button(ButtonA,0)
btnB = Button(ButtonB,0)

Finally add if statements in side our while loop to check each button an then increment the hours +1 when the A button is pressed, and increment the minutes + 1 when the B button is pressed.

var seconds = 0;
var minutes = 43;
var hours = 10;

var btnA = Button(ButtonA, 0);
var btnB = Button(ButtonB, 0);

while (true) {

    if (In(btnA) == 1) {
        minutes = minutes + 1;
    }

    if (In(btnB)== 1) {
        hours = hours + 1;
    }

    Clear();
    DrawFace(64 - 4, 32 - 3, 38, 28);

    DrawHand(64, 32, 20, 10, hours, 12);
    DrawHand(64, 32, 28, 18, minutes, 60);
    DrawHand(64, 32, 30, 20, seconds, 60);

    seconds = seconds + 1;
    if (seconds >= 60) {
        seconds = 0;
        minutes = minutes + 1;
    }
    if (minutes >= 60) {
        minutes = 0;
        hours = hours + 1;
    }
    if (hours > 12) {
        hours = 1;
    }
    Wait(1)
    Show()
}
seconds = 0
minutes = 43
hours = 10

btnA = Button(ButtonA, 0)
btnB = Button(ButtonB, 0)

while (true) {

    if (In(btnA) == 1):
        minutes = minutes + 1


    if (In(btnB)== 1): 
        hours = hours + 1


    Clear()
    DrawFace(64 - 4, 32 - 3, 38, 28)

    DrawHand(64, 32, 20, 10, hours, 12)
    DrawHand(64, 32, 28, 18, minutes, 60)
    DrawHand(64, 32, 30, 20, seconds, 60)

    seconds = seconds + 1
    if (seconds >= 60):
        seconds = 0
        minutes = minutes + 1
  
    if (minutes >= 60):
        minutes = 0
        hours = hours + 1
    
    if (hours > 12):
        hours = 1
    
    Wait(1)
    Show()
 


BrainStorm

How does a computer keep track of time when it is off? It actually does not! Once power is down, everything is reset internally. However, there is a small circuit inside caller RTC (Real Time Clock) and this circuit is extremely low power and it has its own tiny battery. When the computer is off, the RTC will continue to run and continue to keep track of time. The moment the computer is powered up, it will read the current time from the RTC,