Develop a Smart Level App


Objective

Create a digital smart level application using Tizen .NET on your Galaxy Watch and use it for measuring an object’s level.

Overview

In this Code Lab, you will learn how simple it is to expand Galaxy Watch's existing features to leveling objects. This app measures the slope of an object simply and accurately.

Set up your environment

You will need the following:

  • Visual Studio 2017 or Visual Studio 2019 (including Xamarin package/component)

    • It is recommended that you use Visual Studio 2017
  • Visual Studio Tools for Tizen (Visual Studio extension)

To test the application, you may need:

  • Samsung Galaxy Watch or Emulator (Tizen 4.0↑)

Create a new project

Open Visual Studio IDE to create a new Tizen Watch Face App.

  1. Go to File > New > Project to open the New Project window.

  2. In the New Project window do the following steps:

    a. Select Installed > Visual C# > Tizen 4.0.

    b. Select Tizen Watchface App project type.

    c. Specify the project Name (Watchface).

    d. Set the project Location.

    e. Click the OK button to start creating a new project.

  3. Project structure should look like the following (Solution Explorer window).

Add assets

  1. Download and unpack the assets for the application.

    The assets pack consists of two folders...

    ... which contain the following set of images:

    • Background image file (res/background.png)

    • Level indicator image file (res/ball.png)

    • Custom application icon - existing file should be replaced (shared/res/SmartLevel.png). Notice that application icon filename will be different if you choose your own name for the project. In such case, make sure you update icon tag in tizen-manifest.xml file.

  2. Select the unpacked folders, res and shared, and copy them using context menu or press Ctrl + C . Next, make a left mouse click on the project name SmartLevel in Solution Explorer and press Ctrl + V to paste copied folders directly to the SmartLevel project.

  3. Confirm the following pop-ups to replace the content of the res and shared folders of the project.

Create the view

  1. Go to Solution Explorer window and open the MainPage.xaml file.

  2. Remove the StackLayout element, which has been prepared by default, and add an empty AbsoluteLayout element:

    
    <?xml version="1.0" encoding="utf-8" ?>
    <c:CirclePage xmlns="http://xamarin.com/schemas/2014/forms"
                    xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
                    xmlns:c="clr-namespace:Tizen.Wearable.CircularUI.Forms;assembly=Tizen.Wearable.CircularUI.Forms"
                    x:Class="SmartLevel.MainPage">
        <c:CirclePage.Content>
            <AbsoluteLayout>
    
            </AbsoluteLayout>
        </c:CirclePage.Content>
    </c:CirclePage>
    
    
  3. Add the content of the page with two image elements positioned in the Absolute Layout:

    • Image element for the application background (background.png)
    • Image element for the level ball indication (ball.png)
    
    <?xml version="1.0" encoding="utf-8" ?>
    <c:CirclePage xmlns="http://xamarin.com/schemas/2014/forms"
                    xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
                    xmlns:c="clr-namespace:Tizen.Wearable.CircularUI.Forms;assembly=Tizen.Wearable.CircularUI.Forms"
                      x:Class="SmartLevel.MainPage">
        <c:CirclePage.Content>
            <AbsoluteLayout>
                <Image Source="background.png"
                        AbsoluteLayout.LayoutFlags="PositionProportional"
                        AbsoluteLayout.LayoutBounds="0.5, 0.5, 360, 360" />
                <Image Source="ball.png"
                        AbsoluteLayout.LayoutFlags="PositionProportional"
                        AbsoluteLayout.LayoutBounds="0.5, 0.5, 40, 40"
                        Aspect="AspectFit"
                        x:Name="ball" />
            </AbsoluteLayout>
        </c:CirclePage.Content>
    </c:CirclePage>
    
    

Notice that you have set the name ball for the image which displays level ball indication, which would be used in the code in the next steps.

The application should now look like the one below:

Read data from accelerometer sensor

  1. Go to Solution Explorer window and open the MainPage.xaml.cs file.

  2. Add two lines of code:

    • A using statement for the Tizen.Sensor namespace.
    • A private field, which will store an instance of the Accelerometer class.
    
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    
    using Xamarin.Forms;
    using Xamarin.Forms.Xaml;
    using Tizen.Wearable.CircularUI.Forms;
    
    using Tizen.Sensor;
    
    namespace SmartLevel
    {
        [XamlCompilation(XamlCompilationOptions.Compile)]
        public partial class MainPage : CirclePage
        {
            private Accelerometer _accelerometer;
    
            public MainPage()
            {
                InitializeComponent();
            }
        }
    }
    
    
  3. Add InitializeAccelerometer() method, which creates instance of Accelerometer class, initializes it and starts reading data from the accelerometer. Use this method in the MainPage constructor:

    
    public partial class MainPage : CirclePage
    {
        private Accelerometer _accelerometer;
    
        public MainPage()
        {
            InitializeComponent();
            InitializeAccelerometer();
        }
    
        private void InitializeAccelerometer()
        {
            _accelerometer = new Accelerometer();
            _accelerometer.Interval = 20;
            _accelerometer.DataUpdated += OnAccelerometerDataUpdated;
            _accelerometer.Start();
        }
    }
    
    
  4. Add OnAccelerometerDataUpdated method. It will handle DataUpdated event of the accelerometer instance by updating position of the level ball indicator:

    
    public partial class MainPage : CirclePage
    {
        private Accelerometer _accelerometer;
    
        public MainPage()
        {
            InitializeComponent();
            InitializeAccelerometer();
        }
    
        private void InitializeAccelerometer()
        {
            _accelerometer = new Accelerometer();
            _accelerometer.Interval = 20;
            _accelerometer.DataUpdated += OnAccelerometerDataUpdated;
            _accelerometer.Start();
        }
    
        private void OnAccelerometerDataUpdated(object sender, AccelerometerDataUpdatedEventArgs e)
        {
            double x = (e.X + 10) / 20;
            double y = (e.Y + 10) / 20;
    
            var position = new Rectangle(x, 1 - y, 40, 40);
            AbsoluteLayout.SetLayoutBounds(ball, position);
        }
    }
    
    

At this point, you have an application which moves the level ball indicator every 20 milliseconds. In the next steps, you will learn how to easily average the recent accelerometer data to smooth level ball indicator movement.

Try to pitch and roll your watch device to see the level ball moving on the screen.

Calculate average position based on latest data

  1. Open the MainPage.xaml.cs file. Add generic Queue collection called _positions, which stores tuple - pair of two double values. Those values are the recent positions of the level ball indicator on the watch screen:

    
    public partial class MainPage : CirclePage
    {
        private Accelerometer _accelerometer;
        private Queue<(double x, double y)> _positions = new Queue<(double x, double y)>();
    
        public MainPage()
        {
            InitializeComponent();
            InitializeAccelerometer();
        }
    
        // (...)
    }
    
    
  2. Inside OnAcceletometerDataUpdated method add code which:

    • Adds (Enqueue) the latest data sample read from the accelerometer.

    • Removes (Dequeue) the oldest data sample, but only if there are more than 25 samples in the queue _positions.

    
    private void OnAccelerometerDataUpdated(object sender, AccelerometerDataUpdatedEventArgs e)
    {
        double x = (e.X + 10) / 20;
        double y = (e.Y + 10) / 20;
    
        _positions.Enqueue((x, y));
        if (_positions.Count > 25)
            _positions.Dequeue();
    
        var position = new Rectangle(x, 1 - y, 40, 40);
        AbsoluteLayout.SetLayoutBounds(ball, position);
    }
    
    
  3. Use LINQ method Average() to calculate average value of the position based on x and y values stored in the _positions collection:

    
    private void OnAccelerometerDataUpdated(object sender, AccelerometerDataUpdatedEventArgs e)
    {
        double x = (e.X + 10) / 20;
        double y = (e.Y + 10) / 20;
    
        _positions.Enqueue((x, y));
        if (_positions.Count > 25)
            _positions.Dequeue();
    
        double xAverage = _positions.Average(item => item.x);
        double yAverage = _positions.Average(item => item.y);
    
        var position = new Rectangle(x, 1 - y, 40, 40);
        AbsoluteLayout.SetLayoutBounds(ball, position);
    }
    
    
  4. Use newly calculated xAverage and yAverage values (instead of the latest x and y) to create new position object:

    
    private void OnAccelerometerDataUpdated(object sender, AccelerometerDataUpdatedEventArgs e)
    {
        double x = (e.X + 10) / 20;
        double y = (e.Y + 10) / 20;
    
        _positions.Enqueue((x, y));
        if (_positions.Count > 25)
            _positions.Dequeue();
    
        double xAverage = _positions.Average(item => item.x);
        double yAverage = _positions.Average(item => item.y);
    
        var position = new Rectangle(xAverage, 1 - yAverage, 40, 40);
        AbsoluteLayout.SetLayoutBounds(ball, position);
    }
    
    

At this point, you have an application which displays a level ball indicator based on latest 25 data samples read from accelerometer. Notice that the level ball indicator moves slowly and smoothly when the watch is rolled or pitched.

Try to pitch and roll your watch device to see level ball indication moving on the screen. Notice that now the level ball indicator moves are smooth.

You're done!

Congratulations! You have successfully achieved the goal of this Code Lab. Now, you can create a smart level app for your Galaxy Watch by yourself! If you're having trouble, you may download this file:

Smart Level Complete Code
(1.53MB)