Building a Windows 10 IoT app running on Raspberry Pi2 - Part 2

Posted by: Shoban Kumar , on 5/1/2016, in Category Windows Store Apps
Views: 11777
Abstract: A Windows 10 IoT app running on Raspberry Pi2 to show the next Bus schedule.

This article is an extension to the first part HomePi - A Windows 10 & Raspberry Pi 2 IoT app [bitly.com/win10pi2].

This article is published from the DNC Magazine for Developers and Architects. Download this magazine from here [Zip PDF] or Subscribe to this magazine for FREE and download all previous and current editions

Previously, we looked at the parts required, added Display/Touch drivers and also added a small code snippet to display current weather information. This is part two of our quest to build our Home Pi Windows 10 IoT project. In this series we will add the remaining features and make our app interactive. Here is the complete app displaying the next Bus schedule.

windows10-iot-app

Let us jump into the code right away. To get some background of the app we have built so far, visit http://www.dotnetcurry.com/windowsapp/1240/homepi-windows-10-iot-app-raspberrypi-2

Windows 10 IOT Project

MainPage.xaml

Add the following code to the main Grid:

In the above code, we have added a new Media Player element and this will be used for Audio play back. MediaEnded event will automatically play the next track once the current track is complete.

Even though Home Pi has four screens (Next Bus, Weather, Music Player and Power Control), we will use a single XAML file to manage all four screens. Add the following enum values to the class.

enum CurrentPage
{
    Weather,
    NextBus,
    MusicPlayer,
    Power
};

Whenever a menu icon is pressed, we will use the enum values to identify the current page and display information accordingly.

This page also has some methods that are detailed here. Please read this article along with the source code to understand these methods in detail.

Timers

appTimer: This timer is used to display the count down for the next bus, and runs every minute.

touchTimer: This timer executes every 50 milli seconds and constantly checks for Touch Inputs.

TouchTimer_Tick

private void TouchTimer_Tick(object sender, object e)
{
    TSC2046.CheckTouch();

    int x = TSC2046.getTouchX();
    int y = TSC2046.getTouchY();
    int p = TSC2046.getPressure();

    if (p > 5)
    {
        CheckAction(TSC2046.getDispX(), TSC2046.getDispY());
    }
}

This method gets the x, y coordinates and the pressure value of the touch input using getTouchX (), getTouchY() and getPressure() methods in Touch processor class (refer previous article about Touch and Display processors). To avoid processing accidental touch inputs, only pressure values greater than 5 are processed and rest are ignored. CheckAction method is used to process the touch input. This process is repeated every 50 milli seconds.

CheckAction

 private void CheckAction(int x, int y)
{
    Windows.Foundation.Point touchPoint = new Windows.Foundation.Point(x, y);
    Rect rect;
    bool isControlpoint;

    switch (currenPage)
    {
        case CurrentPage.Power:
            rect = new Rect(64, 53, 108, 110); //Power button is within this rectangle area
            isControlpoint = rect.Contains(touchPoint);
            if (isControlpoint)
            {
                ShutDown();
            }
            break;
    }

    //Menu controls
    rect = new Rect(275, 20, 40, 40); //Next bus Icon is within this rectangle area
    isControlpoint = rect.Contains(touchPoint);
    if (isControlpoint)
    {
        GetNextBus();
    }

    rect = new Rect(275, 72, 40, 40); //Music player Icon is within this rectangle area
    isControlpoint = rect.Contains(touchPoint);
    if (isControlpoint)
    {
        PlayMusic();
    }
    rect = new Rect(275, 125, 40, 40); //Weather Icon is within this rectangle area
    isControlpoint = rect.Contains(touchPoint);
    if (isControlpoint)
    {
        GetWeather();
    }

    rect = new Rect(275, 179, 40, 40);
    isControlpoint = rect.Contains(touchPoint); //Power button is within this rectangle area
    if (isControlpoint)
    {
        ShowPowerPage();
    }

}

If the input pressure is greater than 5, then this method is called. Next step is to check if touch point falls under any of the menu controls. Menu items are nothing but just one whole Image in the project. Basic idea is to check if the x and y points lie somewhere within each x and y points of every pixel in the image. One way is to iterate through all pixels within the menu area and then compare it with x and y. A simpler approach is to use the inbuilt .NET classes and this can be achieved using the Rect structure and Contains method. I used Photoshop to find out the x, y coordinates of each menu icon and used Rect structure to build a Rectangle area. Then used it to check if the touch point lies within this area using Contains method. Inputs in any other areas are ignored:

Shutdown

private void ShutDown()
{
    ShutdownManager.BeginShutdown(ShutdownKind.Shutdown, TimeSpan.FromSeconds(0.5));
}

This method shuts down Raspberry Pi using the ShutdownManager class.

 

PlayMusic

private async void PlayMusic()
{
    currenPage = CurrentPage.MusicPlayer;
    //paint Menu
    await ILI9341.LoadBitmap(display1, 0, 0, 240, 320, 
                       "ms-appx:///assets/Music_Home.png");
    likes = await Utilities.GetLikes();
    if (likes.Count > 0)
    {
        LoadTrack(likes[nowPlaying]);
    }
}

PlayMusic() method loads a list of likes from Sound Cloud and starts playing the first track in the list. Utilities.GetLikes method is explained shortly.

GetNextBus

private async void GetNextBus()
{
    currenPage = CurrentPage.NextBus;
    await ILI9341.LoadBitmap(display1, 0, 0, 240, 320, "ms-appx:///assets/Bus_Home.png");
    nextBus = await Utilities.GetNextBus();
    ILI9341.setCursor(display1, 30, 15);
    string s = nextBus.ToString() + " min";
    ILI9341.write(display1, s.ToCharArray(), 6, 0xDB69);
}

This method sets the currentPage value, loads a new background Image and gets the schedule for next bus in minutes to displays it on screen. Number of minutes is updated every minute using AppTimer which runs every one minute.

AppTimer_Tick

private async void AppTimer_Tick(object sender, object e)
{
    //Run only if the current page is Next Bus
    switch (currenPage)
    {
        case CurrentPage.NextBus:
            //Erase screen
            ILI9341.fillRect(display1, 0, 0, 240, 70, 0xC616);
            nextBus -= 1;
            if (nextBus <= 2)
            {
                nextBus = await Utilities.GetNextBus();
            }

            ILI9341.setCursor(display1, 30, 15);
            string s = nextBus.ToString() + " min";
            ILI9341.write(display1, s.ToCharArray(), 6, 0xDB69);
            break;
    }
}

This method executes every minute and updates the minute value. This is done only if the current page is NextBus. To avoid reaching API limits with my local transport provider, I check for updates only if the next bus arrives in less than 2 minutes. Why 2 minutes and not the last minute? It takes two minutes to walk to my nearest bus stop J

ShowPowerPage

This method displays an image with a Power icon and touching the power icon will shut down the Raspberry Pi using Shutdown() method. CheckAction() method takes care of processing touch input and checks if it lies within the Power Icon area.

LoadTrack

private void LoadTrack(SoundCloudTrack currentTrack)
{
    //Stop player, set new stream uri and play track
    mPlayer.Stop();
    Uri streamUri = new Uri(currentTrack.stream_url + "?client_id=YOUR_CLIENT_ID_HERE");
    mPlayer.Source = streamUri;
    mPlayer.Play();
}

This method loads a new track in the Media Player and starts playing the current track.

mplayer_MediaEnded

private void mPlayer_MediaEnded(System.Object sender, RoutedEventArgs e)
{
    nowPlaying += 1;
    if (nowPlaying > likes.Count) //Reset to first track
        nowPlaying = 0;
    LoadTrack(likes[nowPlaying]);
}

This method gets called automatically when the track has finished playing. Global counter nowPlaying is updated and next track is loaded. It will also reset the counter to 0 to start playing again from the first track, if there are no more tracks to play.

Utilities.cs

This file has some extra methods and classes that are used for Sound Cloud and Next bus service calls.

GetNextBus

public static async Task GetNextBus()
{

    int nextbusIn = 0;
    try
    {
        var response = await GetjsonStream("http://www.wienerlinien.at/ogd_routing/XML_TRIP_REQUEST2?locationServerActive=1&outputFormat=JSON&type_origin=stopid&name_origin=60200884&type_destination=stopid&name_destination=60200641");
        RootObject obj = JsonConvert.DeserializeObject(response);

        foreach (Trip trip in obj.trips)
        {
            BUSDateTime nxt = trip.trip.legs[0].points[0].dateTime;
            DateTime nextBus = DateTime.ParseExact(nxt.time, "HH:mm", System.Globalization.CultureInfo.InvariantCulture);

            if (DateTime.Compare(DateTime.Now, nextBus) == -1) //Return the first next bus
            {
                TimeSpan diff = nextBus - DateTime.Now;
                int minutes = Convert.ToInt16(Math.Round(diff.TotalMinutes, 0));
                if (minutes > 2)
                {
                    nextbusIn = minutes;
                    return nextbusIn;
                }
            }
        }
    }
    catch (Exception)
    {
        //Handle error
    }
    return nextbusIn;
}

In this method, I make a call to my local transport API (Vienna, Austria) to get a list of buses from one point to another. I have hard coded the values for origin and destination. This API returns the next 5 buses as JSON string and I use Newtonsoft JSON library to get the first next bus and check if it arrives in less than 2 minutes. If so, return the second bus. Remember, it takes 2 minutes to walk to my nearest bus station. You can replace this with your own transport API or any other service for which you need a countdown for.

GetLikes

This method returns a list of favourites from your Sound Cloud profile. This is a simplified version of Music Player detailed in Build a Windows 10 Universal App – First Look

 

homepi-2

Future Enhancements

Home Pi App is good enough for a hobby project but you can make it better with few suggestions:

1. Better error handling

2. Restart option in Power Page

3. More media player controls for Music Player

4. Add different UI to MainPage.xaml so that you can have different UI for HDMI output and LCD screen

Download the entire source code of this article over here bit.ly/dncm23-win10-pi2-iot (Github)

Was this article worth reading? Share it with fellow developers too. Thanks!
Share on LinkedIn
Share on Google+
Further Reading - Articles You May Like!


Page copy protected against web site content infringement 	by Copyscape




Feedback - Leave us some adulation, criticism and everything in between!

Categories

JOIN OUR COMMUNITY

POPULAR ARTICLES

FREE .NET MAGAZINES

Free DNC .NET Magazine

Tags

JQUERY COOKBOOK

jQuery CookBook