Playing Around With the AGENT SDK to Create a Custom Watch Face

June 23, 2013 — 1 Comment

2013-06-23_14h26_25On Friday June 21st, the AGENT SDK v0.1.1 (June 2013, Preview Release) was released. On Thursday June 20th I backed the “AGENT: The World’s Smartest Watch” Kickstarter project. Not being able to wait for the watch to arrive to start coding, I started to play around with Visual Studio 2012, the AGENT Emulator and the Microsoft .NET Micro Framework.

To be honest, I have experience with various flavors of .Net, but I never took the time to explore the .NET Micro Framework. The feeling I got when I started to play with it, was that it felt odd… I have the VAR but no Generics !?! No lambdas, no LINQ..

This is the moment I realized that I was working with a mix of .Net 2.0 style with a slight hint of .net 4.0.

I also had not worked with Bitmaps for a while so I pulled out a stack of paper and started mapping out my screen.

When you have 128px by 128px to work with, you have to ask your self some important questions like “What can I live without?” and “What should be on screen?

Development for the watch is straight forward. When you create the AGENT Watch Face project, you get a nice program that is able update the display with the current time. The following code was slightly modified to accommodate my tastes.

public class Program
{
    static Bitmap display;
    static Timer updateClockTimer;
    private static WatchFaceUpdater wc;

    public static void Main()
    {
        // initialize our display buffer
        display = new Bitmap(Bitmap.MaxWidth, Bitmap.MaxHeight);

        wc = new WatchFaceUpdater(display);

        // display the time immediately
        UpdateTime(null);

        // obtain the current time
        var currentTime = DateTime.Now;
        // set up timer to refresh time every minute
        // start timer at beginning of next second
        var dueTime = new TimeSpan(0, 0, 0, 0, 1000 - currentTime.Millisecond);
        // update time every second
        var period = new TimeSpan(0, 0, 0, 1, 0);
        // start our update timer
        updateClockTimer = new Timer(UpdateTime, null, dueTime, period); 

        // go to sleep; time updates will happen automatically every minute
        Thread.Sleep(Timeout.Infinite);
    }

    static void UpdateTime(object state)
    {
        wc.Update();
    }
}

Then I set out to create my WatchFaceUpdater. I wanted to encapsulate all my logic within a class that could be instantiated because this will come in handy in future blog posts about Agent development.

The goal of my experiment was to show the current time with the previous time on top and the next time on the bottom. I’m by no means any good with UIs and that is why I usually stick to backend development…

Placing things around took some trial and error, a sheet of paper and a pen! Yes I know… I used paper, but it helped me keep track of x y coordinates when I was placing everything on the screen.

Changing font sizes isn’t implemented in the .NET Micro Framework. In order to display text of different sizes, you will need to export fonts into the TinyFont format and add them as resources to your project. The TinyFont format has the font size embedded in it, so it’s important to name them properly because you might get lost is you have too many sizes.

Jan Kučera has a great tool to help convert Fonts to TinyFonts. In my opinion, it beats using a command line =)

TinyFontToolGUI

Using this tool I created Cambria16.tinyfnt and Cambria20.tinyfnt and embedded them into my project.

2013-06-23_15h04_21

The WatchFaceUpdater calculates the values to display and places them on screen. This call should probably be split up into two classes, one for the bitmap related code and one for the values.

internal class WatchFaceUpdater
{
    private readonly Bitmap display;
    private DateTime currentTime;
    private readonly Font fontNinaB;
    private readonly Font biggerFond;

    private const int CenterText = 64 - 16;
    private const int YCenterTopLine = 64 - 12;
    private const int YCenterLowLine = 64 + 12;
    private const int XRightBound = 64 + 50;
        
    private const int XLeftBound = 64 - 50;

    internal WatchFaceUpdater(Bitmap display)
    {
        this.display = display;
        fontNinaB = Resources.GetFont(Resources.FontResources.Cambria16);
        biggerFond = Resources.GetFont(Resources.FontResources.Cambria20);    
    }

    internal void Update()
    {
        currentTime = DateTime.Now;

        var hours = GetHour();
        var minutes = GetMinutes();
        var seconds = GetSeconds();

        display.Clear();
            
        DrawSeperators();
            
        DrawPreviousValues(hours, minutes, seconds);
        DrawCurrentTime(hours, minutes, seconds);
        DrawNestValues(hours, minutes, seconds);

        display.Flush();
    }

    private void DrawNestValues(int[] hours, int[] minutes, int[] seconds)
    {
        var text = hours[2].ToString("D2") + 
                    ":" + 
                    minutes[2].ToString("D2") + 
                    ":" + 
                    seconds[2].ToString("D2");

        display.DrawText(text, 
                            fontNinaB, 
                            Color.White, 
                            XLeftBound+16, 
                            CenterText + 30);
    }

    private void DrawCurrentTime(int[] hours, int[] minutes, int[] seconds)
    {
        var text = hours[1].ToString("D2") + 
                    ":" + 
                    minutes[1].ToString("D2") + 
                    ":" + 
                    seconds[1].ToString("D2");

        display.DrawText(text, 
                            biggerFond, 
                            Color.White, 
                            XLeftBound-2, 
                            CenterText);
    }

    private void DrawPreviousValues(int[] hours, int[] minutes, int[] seconds)
    {
        var text = hours[0].ToString("D2") + 
                    ":" + 
                    minutes[0].ToString("D2") + 
                    ":" + 
                    seconds[0].ToString("D2");

        display.DrawText(text, 
                            fontNinaB, 
                            Color.White, 
                            XLeftBound+16, 
                            CenterText - 25);
    }
        
    private void DrawSeperators()
    {
        display.DrawLine(Color.White, 
                            1, 
                            XLeftBound, 
                            YCenterTopLine, 
                            XRightBound, 
                            YCenterTopLine);

        display.DrawLine(Color.White, 
                            1, 
                            XLeftBound, 
                            YCenterLowLine, 
                            XRightBound, 
                            YCenterLowLine);
    }

    internal int[] GetSeconds()
    {
        var toDisplay = new[]
        {
            currentTime.Subtract(new TimeSpan(0,0,0,1)).Second,
            currentTime.Second,
            currentTime.AddSeconds(1).Second,
        };
        return toDisplay;
    }

    internal int[] GetMinutes()
    {
        var toDisplay = new[]
        {
            currentTime.Subtract(new TimeSpan(0,0,0,1)).Minute,
            currentTime.Minute,
            currentTime.AddSeconds(1).Minute,
        };
        return toDisplay;
    }

    internal int[] GetHour()
    {
        var toDisplay = new[]
        {
            currentTime.Subtract(new TimeSpan(0,0,0,1)).Hour,
            currentTime.Hour,
            currentTime.AddSeconds(1).Hour,
        };
        return toDisplay;
    }
}

The general feeling I get from this first experience with .NET Micro Framework and the AGENT SDK v0.1.1 (June 2013, Preview Release) is that I just can’t wait to get my watch!

The next step in this adventure for me is to find ways to pull information about my Windows Azure Cloud Services and create new services for the AGENT Smartwatch.

The possibilities are endless! Imagine getting notified through your watch that something has gone wrong with your Windows Azure deployments!

Do you have any ideas for the new AGENT Smartwatch?

Trackbacks and Pingbacks:

  1. Dew Drop – June 24, 2013 (#1,571) | Alvin Ashcraft's Morning Dew - June 24, 2013

    […] Playing Around With the AGENT SDK to Create a Custom Watch Face (Alexandre Brisebois) […]

    Like

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.