Carey Payette
Published © GPL3+

WinIoT: Inter-Application Communication using App Services

Learn how to establish bi-directional inter-application communication using App Services on Windows IoT Core.

IntermediateFull instructions provided3,514
WinIoT: Inter-Application Communication using App Services

Things used in this project

Hardware components

Adafruit Pi Cobbler Plus
×1
Raspberry Pi 2 Model B
Raspberry Pi 2 Model B
×1
Magnetic Door Sensor
×1
Green LED
×1
46 Ohm Resistor
×1

Software apps and online services

Visual Studio 2015
Microsoft Visual Studio 2015
Windows 10 IoT Core
Microsoft Windows 10 IoT Core

Story

Read more

Schematics

Wiring Diagram

LED Anode to Pin 23
One leg of resistor to LED Cathode
Second leg of resistor to GND
One wire of contact sensor to Pin 18
Second wire of contact sensor to GND

Code

PerimeterBreachAppService.cs Code Listing (documented inline)

C#
A background task that monitors a door sensor and reacts to client requests to turn on/off an LED
using System;
using System.Threading.Tasks;
using Windows.ApplicationModel.AppService;
using Windows.ApplicationModel.Background;
using Windows.Devices.Gpio;
using Windows.Foundation.Collections;

namespace BackgroundHeadlessApplication
{
    public sealed class PerimeterBreachAppService : IBackgroundTask
    {
        private BackgroundTaskDeferral _deferral;
        private AppServiceConnection _connection;
        private int _sensorPinNumber = 18;
        private GpioPin _sensorPin;
        private int _ledPinNumber = 23;
        private GpioPin _ledPin;

        public void Run(IBackgroundTaskInstance taskInstance)
        {
            //keep this background task alive
            _deferral = taskInstance.GetDeferral();
            taskInstance.Canceled += OnTaskCanceled;

            //execution triggered by another application requesting this App Service
            //assigns an event handler to fire when a message is received from the client
            var triggerDetails = taskInstance.TriggerDetails as AppServiceTriggerDetails;
            _connection = triggerDetails.AppServiceConnection;
            _connection.RequestReceived += Connection_RequestReceived;

            //initialize sensor and led pins
            GpioController controller = GpioController.GetDefault();
            _sensorPin = controller.OpenPin(_sensorPinNumber);
            _sensorPin.SetDriveMode(GpioPinDriveMode.InputPullUp); //high when door is open, low when closed
            _sensorPin.ValueChanged += SensorPin_ValueChanged; //assigns event handler to fire when 
            _ledPin = controller.OpenPin(_ledPinNumber);
            _ledPin.SetDriveMode(GpioPinDriveMode.Output);

            //send initial notification of the startup state of the sensor
            //toggle current value so initial notification is made
            NotifyClientsOfPerimeterState().Wait();
        }

        private async void SensorPin_ValueChanged(GpioPin sender, GpioPinValueChangedEventArgs args)
        {
            await NotifyClientsOfPerimeterState();
        }

        private async Task NotifyClientsOfPerimeterState()
        {
            var _sensorCurrentValue = _sensorPin.Read();
            var messages = new ValueSet(); //name value pair

            if (_sensorCurrentValue == GpioPinValue.High)
            {
                //send perimeter breached
                messages.Add("Perimeter Notification", "Breached");
            }
            else
            {
                //send perimeter secure
                messages.Add("Perimeter Notification", "Secure");
            }

            //send message to the client
            var response = await _connection.SendMessageAsync(messages);

            if (response.Status == AppServiceResponseStatus.Success)
            {
                var result = response.Message["Response"];
                //optionally log result from client
            }
        }


        private async void Connection_RequestReceived(AppServiceConnection sender, AppServiceRequestReceivedEventArgs args)
        {
            // if you are doing anything awaitable, you need to get a deferral
            var requestDeferral = args.GetDeferral();
            var returnMessage = new ValueSet();
            try
            {
                //obtain and react to the command passed in by the client
                var message = args.Request.Message["Request"] as string;
                switch (message)
                {
                    case "Turn LED On":
                        _ledPin.Write(GpioPinValue.High);
                        break;
                    case "Turn LED Off":
                        _ledPin.Write(GpioPinValue.Low);
                        break;
                }
                returnMessage.Add("Response", "OK");
            }
            catch (Exception ex)
            {
                returnMessage.Add("Response", "Failed: " + ex.Message);
            }

            await args.Request.SendResponseAsync(returnMessage);
 
            //let the OS know that the action is complete
            requestDeferral.Complete();
        }

        private void OnTaskCanceled(IBackgroundTaskInstance sender, BackgroundTaskCancellationReason reason)
        {
            if (_deferral != null)
            {
                _deferral.Complete();
            }
        }
    }
}

UI XAML Snippet

XML
UI definition to be pasted in the Grid element of MainPage.xaml
        <StackPanel Orientation="Vertical" Margin="0,30,0,0">
            <TextBlock FontSize="48" Foreground="Blue">Headed Application</TextBlock>
            <TextBlock FontSize="24" Foreground="Red" x:Name="txtStatus"></TextBlock>
            <StackPanel Orientation="Horizontal" Margin="0,10,0,0">
                <Button x:Name="btnLEDOn" Background="Aquamarine" Margin="0,0,10,0" Click="btnCommand_Click">Turn LED On</Button>
                <Button x:Name="btnLEDOff" Background="LightCoral" Click="btnCommand_Click">Turn LED Off</Button>
            </StackPanel>
        </StackPanel>

MainPage.xaml.cs Code Listing (documented inline)

C#
UI behavior implementation
using System;
using Windows.ApplicationModel.AppService;
using Windows.Foundation.Collections;
using Windows.UI;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Media;

namespace HeadedApplication
{
    
    public sealed partial class MainPage : Page
    {
        private AppServiceConnection _perimeterBreachService = null;
        public MainPage()
        {
            this.InitializeComponent();
            SetupAppService();
        }

        private async void SetupAppService()
        {
            // find the installed application(s) that expose the app service PerimeterBreachService
            var listing = await AppServiceCatalog.FindAppServiceProvidersAsync("PerimeterBreachService");
            var packageName = "";
            // there may be cases where other applications could expose the same App Service Name, in our case
            // we only have the one
            if (listing.Count == 1)
            {
                packageName = listing[0].PackageFamilyName;
            }
            _perimeterBreachService = new AppServiceConnection();
            _perimeterBreachService.AppServiceName = "PerimeterBreachService";
            _perimeterBreachService.PackageFamilyName = packageName;
            //open app service connection
            var status = await _perimeterBreachService.OpenAsync();

            if (status != AppServiceConnectionStatus.Success)
            {
                //something went wrong
                txtStatus.Text = "Could not connect to the App Service: " + status.ToString();
            }
            else
            {
                //add handler to receive app service messages (Perimiter messages)
                _perimeterBreachService.RequestReceived += PerimeterBreachService_RequestReceived;
            }
        }

        private async void PerimeterBreachService_RequestReceived(AppServiceConnection sender, AppServiceRequestReceivedEventArgs args)
        {

            await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, async () => 
            {
                // if you are doing anything awaitable, you need to get a deferral
                var requestDeferral = args.GetDeferral();
                var returnMessage = new ValueSet();
                var uiMessage = "";
                var uiMessageBrush = new SolidColorBrush(Colors.Black);

                try
                {
                    //determine message sent from the background task (perimeter notifications)
                    var message = args.Request.Message["Perimeter Notification"] as string;
                    switch (message)
                    {
                        case "Breached":
                            uiMessage = "Perimeter Breached!!! Alert!!!";
                            uiMessageBrush = new SolidColorBrush(Colors.Red);
                            break;
                        case "Secure":
                            uiMessage = "Perimeter Secured... Move along...";
                            break;
                    }
                    returnMessage.Add("Response", "OK");
                }
                catch (Exception ex)
                {
                    returnMessage.Add("Response", "Failed: " + ex.Message);
                }

                //return OK or Failed response back to the background task
                await args.Request.SendResponseAsync(returnMessage);


                //let the OS know that the action is complete
                requestDeferral.Complete();

                txtStatus.Text = uiMessage;
                txtStatus.Foreground = uiMessageBrush;
            });
        }

        private async void btnCommand_Click(object sender, RoutedEventArgs e)
        {
            Button btn = sender as Button;
            string command = "";
            var message = new ValueSet();
            var statusColor = new SolidColorBrush(Colors.Black);
            switch(btn.Name)
            {
                case "btnLEDOn":
                    command = "Turn LED On";
                    break;
                case "btnLEDOff":
                    command = "Turn LED Off";
                    break;
            }
            message.Add("Request", command);

            //use the app service connection to send LED Command (based on button pressed)
            var response = await _perimeterBreachService.SendMessageAsync(message);
            var result = "";
            //analyze response from the background task
            if(response.Status == AppServiceResponseStatus.Success)
            {
                result = response.Message["Response"] as string;
            }
            else
            {
                result = "Something went wrong: " + response.Status;
                statusColor = new SolidColorBrush(Colors.Red);
            }

            await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
            {
                txtStatus.Text = result;
                txtStatus.Foreground = statusColor;
            });
        }
    }
}

Source Code

GitHub Repository

Credits

Carey Payette

Carey Payette

13 projects • 134 followers
Sr. Software Engineer at Independent

Comments