Things used in this project

Hardware components:
Microsoft Lumia 950XL
You can use also other windows 10 devices
×1
Microsoft Xbox One Controller (bluetooth)
×1
Pi 3 02
Raspberry Pi 3 Model B
Also a raspberry pi 2 is compatible with this project
×1
Drone Frame
This frame has the power distribution board built in
×1
AfroFlight Naze32 Rev6 Flight Controller
×1
Hobby King 30A ESC 3A UBEC
×1
Turnigy D3530/14 1100KV Brushless Outrunner Motor
×1
Hobbyking Slowfly Propeller 10x4.5 Blue (CW/CCW) (4pcs)
×1
Turnigy 3300mAh 3S 30C Lipo Pack
×1
Gold Plated Spring Connector 3.5mm
You need only 12 female connector to connect ESCs to Motor
×1
HXT 4mm Gold Connector
You need just one of this to connect the battery to the power distribution board
×1
Mini Ublox NEO-M8N GPS for Naze32 / Flip32
GPS module for Naze 32 flight controller
×1
Vodafone K5150 4G/LTE USB
4G/LTE modem for web socket connection - Compatible with Windows 10 IoT core
×1
Wireless 300N USB 2.0 Dongle
This wifi dongle is compatible with Windows 10 IoT core, i choose this one because it has the removible external antenna
×1
Micro USB data cable
This short cable is required to connect RPi to the flight controller
×1
Software apps and online services:
10
Microsoft Windows 10 IoT Core
Hand tools and fabrication machines:
09507 01
Soldering iron (generic)

Schematics

RPi and fligh controller connection
1 kixi4u4imk

Code

MainPage.csC#
using System;
using System.Diagnostics;
using Windows.Devices.Gpio;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.Gaming.Input;
using System.Threading.Tasks;
using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Xaml.Input;
using Windows.Devices.Geolocation;
using Windows.UI.Xaml.Controls.Maps;
using Windows.Foundation;

namespace Drone
{
    public sealed partial class MainPage : Page
    {
        //private static string defaultHostName = "minwinpc";
        private static string defaultHostName = "192.168.137.1";
        public static string hostName = defaultHostName;
        private DispatcherTimer timer;
        private Gamepad controller;
        public static bool isGamepadConnected;
        public static bool isLandCompleted;
        public static bool isSignalLost;
        public static bool isTakeOffCompleted;
        private static Geopoint dronePosition;
        private static Image droneIcon;

        private const int LED_PIN = 26;
        private static GpioPin ledPin;
        private static GpioPinValue ledPinValue;

        private static ushort aux1, aux2, aux3, aux4;
        private static double throttle, yaw, roll, pitch;
        private static int throttleTakeOffRange;
        private const int MAX_THROTTLE_TAKEOFF_FACTOR = 400;
        private const int THROTTLE_TAKEOFF_FACTOR = 250;
        private const int LANDING_DISTANCE = 50;
        private const double THUMBSTICK_FACTOR = 0.1;
        private int countButton, flightModeCount, gpsDataCount;

        Camera camera;

        public MainPage()
        {
            this.InitializeComponent();

            isGamepadConnected = false;
            isTakeOffCompleted = false;
            isLandCompleted = false;
            isSignalLost = false;

            throttle = 0;
            roll = 1500;
            pitch = 1500;
            yaw = 1500;
            aux1 = 1500;
            aux2 = 1500;
            aux3 = 1500;
            aux4 = 1500;
            throttleTakeOffRange = 0;
            countButton = 0;
            flightModeCount = 0;
            gpsDataCount = 0;

            armToggle.IsEnabled = false;
            armToggle.IsTabStop = false;
            horizonToggle.IsEnabled = false;
            horizonToggle.IsTabStop = false;
            takeOffLandToggle.IsEnabled = false;
            takeOffLandToggle.IsTabStop = false;
            plusButton.IsEnabled = false;
            plusButton.IsTabStop = false;
            minusButton.IsEnabled = false;
            minusButton.IsTabStop = false;

            droneIcon = new Image();
            droneIcon.Source = new Windows.UI.Xaml.Media.Imaging.BitmapImage(new Uri("ms-appx:///Assets/Icons/droneIcon.png"));
            droneIcon.Width = 30;
            droneIcon.Height = 33;
            map.ZoomLevel = 17;
            map.Children.Add(droneIcon);
            map.MapServiceToken = App.MapServiceToken;

            InitGPIO();
            //InitNetwork();

            if (App.isRPi)
            {    
                InitNaze32();
            }

            Gamepad.GamepadAdded += Gamepad_GamepadAdded;
            Gamepad.GamepadRemoved += Gamepad_GamepadRemoved;

            timer = new DispatcherTimer();
            timer.Interval = TimeSpan.FromMilliseconds(20);
            timer.Tick += timer_Tick;
            timer.Start();
        }

        private async void Gamepad_GamepadRemoved(object sender, Gamepad e)
        {
            Debug.WriteLine("--------- CONTROLLER DISCONNECTED ---------");
            controller = null;
            isGamepadConnected = false;

            //Safe();
            
            await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
			{
                gamepadStatus.Opacity = 0.2;
                armToggle.IsEnabled = false;
                horizonToggle.IsEnabled = false;
                takeOffLandToggle.IsEnabled = false;
            });
        }

        private async void Gamepad_GamepadAdded(object sender, Gamepad e)
        {
            Debug.WriteLine("--------- CONTROLLER CONNECTED ---------");
            controller = e;
            isGamepadConnected = true;

			await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
			{
				gamepadStatus.Opacity = 1;
                armToggle.IsEnabled = true;
                horizonToggle.IsEnabled = true;
			});
        }

        private void droneStatus_Tapped(object sender, TappedRoutedEventArgs e)
        {
            if (droneStatus.Opacity < 0.3)
                FlyoutBase.ShowAttachedFlyout(sender as FrameworkElement);
        }

        private void connectButton_Click(object sender, RoutedEventArgs e)
        {
            StartupFlyout.Hide();

            if (!string.IsNullOrWhiteSpace(deviceName.Text))
                hostName = deviceName.Text;
            NetworkCmd.NetworkInit(hostName);
        }

        private void InitGPIO()
        {
            var gpio = GpioController.GetDefault();

            if (gpio == null)
            {
                App.isRPi = false;
                return;
            }

            //Set led
            ledPin = gpio.OpenPin(LED_PIN);
            ledPinValue = GpioPinValue.High;
            ledPin.Write(ledPinValue);
            ledPin.SetDriveMode(GpioPinDriveMode.Output);
        }

        public void InitNetwork()
        {
            if (App.isRPi)      
                hostName = "";

            NetworkCmd.NetworkInit(hostName);
        }

        public async void InitNaze32()
        {
            await MultiWii.Init();
            TurnOnLED();
        }

        public async void InitCamera()
        {
            camera = new Camera();
            await camera.InitCameraAsync();
            camera.StartStreaming();
        }

        private void timer_Tick(object sender, object e)
        {
            // RPi + Win 10 Device + Xbox One Controller
            //if (!App.isRPi && isGamepadConnected && NetworkCmd.socketIsConnected)
            //{
            //    // Read data from controller
            //    var reading = controller.GetCurrentReading();

            //    // Set RC data
            //    SetThrottle(reading.RightTrigger, reading.LeftTrigger);
            //    SetYaw(reading.LeftThumbstickX);
            //    SetPitch(reading.RightThumbstickY);
            //    SetRoll(reading.RightThumbstickX);
            //    CheckButtons(reading.Buttons);

            //    pitchLabel.Text = $"PITCH: {(ushort)pitch}";
            //    rollLabel.Text = $"ROLL: {(ushort)roll}";
            //    yawLabel.Text = $"YAW: {(ushort)yaw}";
            //    throttleLabel.Text = $"THROTTLE: {(ushort)throttle}";
            //    takeOffRangeLabel.Text = $"TAKE OFF RANGE: {throttleTakeOffRange}";

            //    //Send data to RPI
            //    //var bytes = MultiWii.Calculate_MSP_SET_RAW_RC((ushort)roll, (ushort)pitch, (ushort)yaw, (ushort)throttle, aux1, aux2, aux3, aux4); //Baseflight
            //    //var bytes = MultiWii.Calculate_MSP_SET_RAW_RC((ushort)roll, (ushort)pitch, (ushort)throttle, (ushort)yaw, aux1, aux2, aux3, aux4); //Cleanflight - AETR1234
            //    var bytes = MultiWii.Calculate_MSP_SET_RAW_RC((ushort)throttle, (ushort)roll, (ushort)pitch, (ushort)yaw, aux1, aux2, aux3, aux4); //Cleanflight - TAER1234
            //    NetworkCmd.SendDataToHost(bytes);

            //    //MultiWii.MSP_SET_RAW_RC((ushort)roll, (ushort)pitch, (ushort)yaw, (ushort)throttle, aux1, aux2, aux3, aux4); //BaseFlight
            //    //MultiWii.MSP_SET_RAW_RC((ushort)roll, (ushort)pitch, (ushort)throttle, (ushort)yaw, aux1, aux2, aux3, aux4); //Cleanflight - AETR1234
            //    //MultiWii.MSP_SET_RAW_RC((ushort)throttle, (ushort)roll, (ushort)pitch, (ushort)yaw, aux1, aux2, aux3, aux4); //Cleanflight - TAER1234

            //    if (countButton < 5)
            //        countButton++;
            //}

            // RPi + Xbox 360 controller
            if (App.isRPi && isGamepadConnected)
            {
                //Read data from controller
                var reading = controller.GetCurrentReading();

                //Set RC data
                SetThrottle(reading.RightTrigger, reading.LeftTrigger);
                SetYaw(reading.LeftThumbstickX);
                SetPitch(reading.RightThumbstickY);
                SetRoll(reading.RightThumbstickX);
                CheckButtons(reading.Buttons);

                pitchLabel.Text = $"PITCH: {(ushort)pitch}";
                rollLabel.Text = $"ROLL: {(ushort)roll}";
                yawLabel.Text = $"YAW: {(ushort)yaw}";
                throttleLabel.Text = $"THROTTLE: {(ushort)throttle}";
                takeOffRangeLabel.Text = $"TAKE OFF RANGE: {throttleTakeOffRange}";

                //Send data to RPI
                MultiWii.MSP_SET_RAW_RC((ushort)throttle, (ushort)roll, (ushort)pitch, (ushort)yaw, aux1, aux2, aux3, aux4); //Cleanflight - TAER1234

                if (countButton < 5)
                    countButton++;
            }

            if (gpsDataCount == 20)
            {
                if (App.isRPi)
                    MultiWii.GetGPSData();
                else
                    SetGPSInfo();

                gpsDataCount = 0;
            }

            gpsDataCount++;
        }

        private void SetThrottle(double rightTrigger, double leftTrigger)
        {
            // Incremental split of throttle on left and right trigger
            if (isTakeOffCompleted)
            {
                if (rightTrigger == 1)
                {
                    throttle = throttleTakeOffRange + THROTTLE_TAKEOFF_FACTOR + (rightTrigger * (500 - THROTTLE_TAKEOFF_FACTOR - throttleTakeOffRange)) + (leftTrigger * 500) + 1000;
                }
                else
                {
                    throttle = throttleTakeOffRange + THROTTLE_TAKEOFF_FACTOR + (rightTrigger * (500 - THROTTLE_TAKEOFF_FACTOR - throttleTakeOffRange)) + 1000;
                }
            }
        }

        private void SetYaw(double leftThumbstickX)
        {
            if (leftThumbstickX > THUMBSTICK_FACTOR || leftThumbstickX < -THUMBSTICK_FACTOR)
            {
                yaw = (leftThumbstickX * 500) + 1500;
            }
            else
            {
                yaw = 1500;
            }
        }

        private void SetPitch(double rightThumbstickY)
        {
            if(rightThumbstickY > THUMBSTICK_FACTOR || rightThumbstickY < -THUMBSTICK_FACTOR)
            {
                pitch = (rightThumbstickY * 500) + 1500;
            }
            else
            {
                pitch = 1500;
            }
        }

        private void SetRoll(double rightThumbstickX)
        {
            if (rightThumbstickX > THUMBSTICK_FACTOR || rightThumbstickX < -THUMBSTICK_FACTOR)
            {
                roll = (rightThumbstickX * 500) + 1500;
            }
            else
            {
                roll = 1500;
            }
        }

        private void CheckButtons(GamepadButtons buttons)
        {
            if (countButton == 5 && buttons == GamepadButtons.View)
            {
                if (armToggle.IsChecked == true)
                    armToggle.IsChecked = false;
                else
                    armToggle.IsChecked = true;

                countButton = 0;
            }
            else if (countButton == 5 && buttons == GamepadButtons.Menu)
            {
                if (horizonToggle.IsChecked == true)
                {
                    switch (flightModeCount)
                    {
                        case 0:
                            horizonToggle.Content = "Angle";
                            aux2 = 1000;
                            flightModeCount++;
                            break;
                        //case 1:
                        //    horizonToggle.Content = "Air";
                        //    aux2 = 2000;
                        //    flightModeCount++;
                        //    break;
                        case 1:
                            flightModeCount = 0;
                            horizonToggle.IsChecked = false;
                            break;
                    }
                }
                else
                    horizonToggle.IsChecked = true;

                countButton = 0;
            }
            else if (armToggle.IsChecked == true && countButton == 5 && buttons == GamepadButtons.DPadDown)
            {
                if (takeOffLandToggle.IsChecked == true)
                    takeOffLandToggle.IsChecked = false;

                countButton = 0;
            }
            else if (armToggle.IsChecked == true && countButton == 5 && buttons == GamepadButtons.DPadUp)
            {
                if (takeOffLandToggle.IsChecked == false)
                    takeOffLandToggle.IsChecked = true;

                countButton = 0;
            }
            else if (armToggle.IsChecked == true && countButton == 5 && buttons == GamepadButtons.LeftShoulder)
            {
                if (isTakeOffCompleted && (THROTTLE_TAKEOFF_FACTOR + throttleTakeOffRange) > THROTTLE_TAKEOFF_FACTOR)
                    throttleTakeOffRange -= 5;

                countButton = 0;
            }
            else if (armToggle.IsChecked == true && countButton == 5 && buttons == GamepadButtons.RightShoulder)
            {
                if (isTakeOffCompleted && (THROTTLE_TAKEOFF_FACTOR + throttleTakeOffRange) < MAX_THROTTLE_TAKEOFF_FACTOR)
                    throttleTakeOffRange += 5;

                countButton = 0;
            }
        }

        private void arm_Checked(object sender, RoutedEventArgs e)
        {
            aux1 = 2000;
            Debug.WriteLine("--------- ARMED ---------");

            takeOffLandToggle.IsEnabled = true;
        }

        private void arm_Unchecked(object sender, RoutedEventArgs e)
        {
            aux1 = 1000;
            Debug.WriteLine("--------- DISARMED ---------");

            takeOffLandToggle.IsChecked = false;
            takeOffLandToggle.IsEnabled = false;
        }

        private void horizonToggle_Checked(object sender, RoutedEventArgs e)
        {
            flightModeCount = 0;
            aux2 = 1200;
            Debug.WriteLine("--------- HORIZON: ON ---------");
        }

        private void horizonToggle_Unchecked(object sender, RoutedEventArgs e)
        {
            horizonToggle.Content = "Horizon";
            aux2 = 1500;
            Debug.WriteLine("--------- HORIZON: OFF ---------");
        }

        private void takeOffLandToggle_Checked(object sender, RoutedEventArgs e)
        {
            TakeOff();
            plusButton.IsEnabled = true;
            minusButton.IsEnabled = true;
        }

        private void takeOffLandToggle_Unchecked(object sender, RoutedEventArgs e)
        {
            Land();
            plusButton.IsEnabled = false;
            minusButton.IsEnabled = false;
        }

        private void plusButton_Click(object sender, RoutedEventArgs e)
        {
            if (isTakeOffCompleted && (THROTTLE_TAKEOFF_FACTOR + throttleTakeOffRange) < MAX_THROTTLE_TAKEOFF_FACTOR)
                throttleTakeOffRange += 5;
        }

        private void minusButton_Click(object sender, RoutedEventArgs e)
        {
            if (isTakeOffCompleted && (THROTTLE_TAKEOFF_FACTOR + throttleTakeOffRange) > THROTTLE_TAKEOFF_FACTOR)
                throttleTakeOffRange -= 5;
        }

        public void SetGPSInfo()
        {
            //Set gps icon --> gpsFix
            //Set gps num sat label --> gpsNumSat
            //Set speed --> if gpsFix > 0 => speed convert cm/s to km/h

            dronePosition = new Geopoint(new BasicGeoposition() { Latitude = MultiWii.gpsLatitude, Longitude = MultiWii.gpsLongitude, Altitude = MultiWii.gpsAltitude });
            map.Center = dronePosition;

            if (MultiWii.gpsFix > 0 && MultiWii.gpsLatitude > 0 && MultiWii.gpsLongitude > 0)
            {
                MapControl.SetLocation(droneIcon, dronePosition);
                MapControl.SetNormalizedAnchorPoint(droneIcon, new Point(0.5, 1));
            }
        }

        public static void TurnOnLED()
        {
            if (!App.isRPi)
                return;

            if (ledPinValue == GpioPinValue.High)
            {
                ledPinValue = GpioPinValue.Low;
                ledPin.Write(ledPinValue);
            }
        }

        public static void TurnOffLED()
        {
            if (!App.isRPi)
                return;

            if (ledPinValue == GpioPinValue.Low)
            {
                ledPinValue = GpioPinValue.High;
                ledPin.Write(ledPinValue);
            }
        }

        public static void FlipLED()
        {
            if (!App.isRPi)
                return;

            if (ledPinValue == GpioPinValue.High)
            {
                ledPinValue = GpioPinValue.Low;
                ledPin.Write(ledPinValue);
            }
            else
            {
                ledPinValue = GpioPinValue.High;
                ledPin.Write(ledPinValue);
            }
        }

        public static async void BlinkLED(int ms, int times)
        {
            if (!App.isRPi)
                return;

            times = times * 2;

            if (ledPinValue == GpioPinValue.High)
            {
                while (times != 0)
                {
                    FlipLED();
                    await Task.Delay(ms);
                    times--;
                }

                TurnOffLED();
            }
            else
            {
                while (times != 0)
                {
                    FlipLED();
                    await Task.Delay(ms);
                    times--;
                }

                TurnOnLED();
            }
        }

        private void TakeOff()
        {
            takeOffLandToggle.Content = "Land";
            isTakeOffCompleted = true;
            isLandCompleted = false;
        }

        private void Land()
        {
            takeOffLandToggle.Content = "Take Off";
            throttle = 0;
            throttleTakeOffRange = 0;
            isTakeOffCompleted = false;
            isLandCompleted = true;

            //await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
            //{
            //    armToggle.IsEnabled = true;
            //    horizonToggle.IsEnabled = true;
            //    takeOffLandToggle.IsEnabled = true;
            //});
        }

        public static void Safe()
        {
            throttle = 1000 + THROTTLE_TAKEOFF_FACTOR;
            roll = 1500;
            pitch = 1500;
            yaw = 1500;
            throttleTakeOffRange = 0;
            //MultiWii.MSP_SET_RAW_RC((ushort)roll, (ushort)pitch, (ushort)yaw, (ushort)throttle, aux1, aux2, aux3, aux4); Baseflight - AERT1234
            MultiWii.MSP_SET_RAW_RC((ushort)throttle, (ushort)roll, (ushort)pitch, (ushort)yaw, aux1, aux2, aux3, aux4); //Cleanflight - TAER1234
        }
    }
}
MultiWii.csC#
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
using Windows.Devices.Enumeration;
using Windows.Devices.SerialCommunication;
using Windows.Storage.Streams;

namespace Drone
{
    public static class MultiWii
    { 
        private static SerialDevice serialPort = null;
        private static List<DeviceInformation> listOfDevices = new List<DeviceInformation>();

        private const string MSP_HEADER = "$M<";

        public static int version, multiType, mspVersion, capability;
        public static int roll, pitch, yaw, throttle, aux1, aux2, aux3, aux4;
        public static int gpsFix, gpsNumSat, gpsSpeed, gpsGroundCourse;
        public static double gpsLatitude, gpsLongitude, gpsAltitude;
        public static float angx, angy, head, headfree, alt;

        private enum MSPConst
        {
            MSP_IDENT = 100,
            MSP_STATUS = 101,
            MSP_RAW_IMU = 102,
            MSP_SERVO = 103,
            MSP_MOTOR = 104,
            MSP_RC = 105,
            MSP_RAW_GPS = 106,
            MSP_COMP_GPS = 107,
            MSP_ATTITUDE = 108,
            MSP_ALTITUDE = 109,
            MSP_BAT = 110,
            MSP_RC_TUNING = 111,
            MSP_PID = 112,
            MSP_BOX = 113,
            MSP_MISC = 114,
            MSP_MOTOR_PINS = 115,
            MSP_BOXNAMES = 116,
            MSP_PIDNAMES = 117,
            MSP_SUPERRAW_IMU = 119,

            MSP_SET_RAW_SENSORS = 199,
            MSP_SET_RAW_RC = 200,
            MSP_SET_RAW_GPS = 201,
            MSP_SET_PID = 202,
            MSP_SET_BOX = 203,
            MSP_SET_RC_TUNING = 204,
            MSP_ACC_CALIBRATION = 205,
            MSP_MAG_CALIBRATION = 206,
            MSP_SET_MISC = 207,
            MSP_RESET_CONF = 208,

            MSP_EEPROM_WRITE = 250,

            MSP_DEBUG = 254
        }

        private const int
          IDLE = 0,
          HEADER_START = 1,
          HEADER_M = 2,
          HEADER_ARROW = 3,
          HEADER_SIZE = 4,
          HEADER_CMD = 5,
          HEADER_ERR = 6;

        private static byte[] inBuf = new byte[256];
        private static int c_state = IDLE;
        private static bool err_rcvd = false;

        private static byte checksum = 0;
        private static byte cmd;
        private static int offset = 0, dataSize = 0;

        public static async Task Init()
        {
            try
            {
                ListAvailablePorts();
                var deviceName = "CP2104 USB to UART Bridge Controller";
                //var deviceName = "Silicon Labs CP210x USB to UART Bridge (COM3)";
                var device = listOfDevices.Where(x => x.Name == deviceName).FirstOrDefault();
                if (device != null)
                    await OpenConnection(device.Id).AsAsyncAction();
                else
                    throw new Exception($"Device {deviceName} not found!");

                Listen();
            }
            catch(Exception ex)
            {
                MainPage.BlinkLED(250, 4);
                Debug.WriteLine(ex.Message);
            }
        }

        private static async void ListAvailablePorts()
        {
            try
            {
                string aqs = SerialDevice.GetDeviceSelector();

                var dis = await DeviceInformation.FindAllAsync(aqs);

                for (int i = 0; i < dis.Count; i++)
                {
                    listOfDevices.Add(dis[i]);
                }

                if (listOfDevices.Count < 1)
                    throw new Exception("Error finding usb connected devices");
            }
            catch (Exception ex)
            {
                MainPage.BlinkLED(250, 3);
                Debug.WriteLine(ex.Message);
            }
        }

        private static async Task OpenConnection(string deviceID)
        {
            CloseConnection();
            serialPort = await SerialDevice.FromIdAsync(deviceID).AsTask();
            serialPort.BaudRate = 115200;
            serialPort.WriteTimeout = TimeSpan.FromMilliseconds(10);
            serialPort.ReadTimeout = TimeSpan.FromMilliseconds(10);
            serialPort.Parity = SerialParity.None;
            serialPort.StopBits = SerialStopBitCount.One;
            serialPort.DataBits = 8;
            serialPort.IsRequestToSendEnabled = false;
            serialPort.IsDataTerminalReadyEnabled = false;
        }

        private static void CloseConnection()
        {
            if (serialPort != null)
            {
                serialPort.Dispose();
            }

            serialPort = null;
        }

        private static int p;

        private static int read32() { return (Int32)((inBuf[p++] & 0xff) + ((inBuf[p++] & 0xff) << 8) + ((inBuf[p++] & 0xff) << 16) + ((inBuf[p++] & 0xff) << 24)); }
        private static int read16() { return (Int16)((inBuf[p++] & 0xff) + ((inBuf[p++]) << 8)); }
        private static int read8() { return inBuf[p++] & 0xff; }

        //send msp without payload
        private static List<Byte> requestMSP(int msp)
        {
            return requestMSP(msp, null);
        }

        //send multiple msp without payload
        private static List<Byte> requestMSP(int[] msps)
        {
            List<Byte> s = new List<Byte>();
            foreach (int m in msps)
            {
                s.AddRange(requestMSP(m, null));
            }
            return s;
        }

        //send msp with payload
        private static List<Byte> requestMSP(int msp, byte[] payload)
        {
            if (msp < 0)
            {
                return null;
            }
            List<Byte> bf = new List<Byte>();
            foreach (byte c in MSP_HEADER.ToCharArray())
            {
                bf.Add(c);
            }

            byte checksum = 0;
            byte pl_size = (byte)((payload != null ? (int)(payload.Length) : 0) & 0xFF);
            bf.Add(pl_size);
            checksum ^= (byte)(pl_size & 0xFF);

            bf.Add((byte)(msp & 0xFF));
            checksum ^= (byte)(msp & 0xFF);

            if (payload != null)
            {
                foreach (byte b in payload)
                {
                    bf.Add((byte)(b & 0xFF));
                    checksum ^= (byte)(b & 0xFF);
                }
            }
            bf.Add(checksum);
            return (bf);
        }

        public static void sendRequestMSP(List<Byte> msp)
        {
            byte[] arr = new byte[msp.Count];
            int i = 0;
            foreach (byte b in msp)
            {
                arr[i++] = b;
            }

            Write(arr); // send the complete byte sequence in one go
        }

        public static void parseByte(byte c) {
            if (c_state == IDLE)
            {
                c_state = (c == '$') ? HEADER_START : IDLE;
            }
            else if (c_state == HEADER_START)
            {
                c_state = (c == 'M') ? HEADER_M : IDLE;
            }
            else if (c_state == HEADER_M)
            {
                if (c == '>')
                {
                    c_state = HEADER_ARROW;
                }
                else if (c == '!')
                {
                    c_state = HEADER_ERR;
                }
                else
                {
                    c_state = IDLE;
                }
            }
            else if (c_state == HEADER_ARROW || c_state == HEADER_ERR)
            {
                /* is this an error message? */
                err_rcvd = (c_state == HEADER_ERR);        /* now we are expecting the payload size */
                dataSize = (c & 0xFF);
                /* reset index variables */
                p = 0;
                offset = 0;
                checksum = 0;
                checksum ^= (byte)(c & 0xFF);
                /* the command is to follow */
                c_state = HEADER_SIZE;
            }
            else if (c_state == HEADER_SIZE)
            {
                cmd = (byte)(c & 0xFF);
                checksum ^= (byte)(c & 0xFF);
                c_state = HEADER_CMD;
            }
            else if (c_state == HEADER_CMD && offset < dataSize)
            {
                checksum ^= (byte)(c & 0xFF);
                inBuf[offset++] = (byte)(c & 0xFF);
            }
            else if (c_state == HEADER_CMD && offset >= dataSize)
            {
                /* compare calculated and transferred checksum */
                if ((checksum & 0xFF) == (c & 0xFF))
                {
                    if (err_rcvd)
                    {
                        //System.err.println("Copter did not understand request type "+c);
                        Debug.WriteLine("Copter did not understand request type " + c);
                    }
                    else
                    {
                        /* we got a valid response packet, evaluate it */
                        evaluateCommand(cmd, dataSize);
                    }
                }
                else
                {
                    Debug.WriteLine("invalid checksum for command " + ((int)(cmd & 0xFF)) + ": " + (checksum & 0xFF) + " expected, got " + (int)(c & 0xFF));
                }
                c_state = IDLE;
            }
        }

        private static async void Listen()
        {
            try
            {
                if (serialPort != null)
                {
                    // Create the DataReader object and attach to InputStream 
                    DataReader reader = new DataReader(serialPort.InputStream);

                    while (true)
                    {
                        //Launch the ReadBytesAsync task to perform the read
                        await ReadBytesAsync(reader);
                    }
                }
            }
            catch (Exception ex)
            {
                Debug.WriteLine(ex.Message);
            }
        }

        private static async void Read()
        {
            try
            {
                if (serialPort != null)
                {
                    DataReader reader = new DataReader(serialPort.InputStream);
                    await ReadBytesAsync(reader);
                    reader.DetachStream();
                    reader = null;
                }
            }
            catch (Exception ex)
            {              
                Debug.WriteLine(ex.Message);
            }
        }

        private static async Task ReadBytesAsync(DataReader reader)
        {
            uint ReadBufferLength = (uint)inBuf.Length;

            // Set InputStreamOptions to complete the asynchronous read operation when one or more bytes is available
            reader.InputStreamOptions = InputStreamOptions.Partial;

            var data = new List<byte>();

            // Launch the task and wait
            var bytesRead = await reader.LoadAsync(ReadBufferLength);         
            while (bytesRead > 0)
            {
                var b = reader.ReadByte();
                data.Add(b);
                parseByte(b);
                bytesRead--;
            }

            if (App.isRPi)
                NetworkCmd.SendDataToHost(data.ToArray());
        }

        private static async void Write(byte[] value)
        {
            try
            {
                if (serialPort != null)
                {
                    DataWriter writer = new DataWriter(serialPort.OutputStream);
                    await WriteBytesAsync(writer, value);
                    writer.DetachStream();
                    writer = null;
                }
            }
            catch(Exception ex)
            {
                Debug.WriteLine(ex.Message);
            }
        }

        private static async Task WriteBytesAsync(DataWriter writer, byte[] value)
        {
            writer.WriteBytes(value);
            await writer.StoreAsync();
        }

        public static void evaluateResponseMSP(List<byte> data)
        {
            foreach (byte b in data)
            {
                parseByte(b);
            }
        }

        private static void evaluateCommand(byte cmd, int dataSize)
        {
            int icmd = (int)(cmd & 0xFF);
            switch (icmd)
            {
                case (int)MSPConst.MSP_IDENT:
                    version = read8();
                    multiType = read8();
                    mspVersion = read8();
                    capability = read32();
                    Debug.WriteLine($"Version: {version} Type: {multiType} MspVersion: {mspVersion} Capability: {capability}");
                    break;

                case (int)MSPConst.MSP_ATTITUDE:
                    angx = read16() / 10f;
                    angy = read16() / 10f;
                    head = read16();
                    headfree = read16();
                    Debug.WriteLine($"angx: {angx} angy: {angy} head: {head} headfree: {headfree}");
                    break;

                case (int)MSPConst.MSP_ALTITUDE:
                    alt = read32();
                    Debug.WriteLine($"altitude: {alt}");
                    break;

                case (int)MSPConst.MSP_RC:
                    roll = read16();
                    pitch = read16();
                    yaw = read16();
                    throttle = read16();
                    aux1 = read16();
                    aux2 = read16();
                    aux3 = read16();
                    aux4 = read16();
                    Debug.WriteLine($"MSP_RC: [{roll},{pitch},{yaw},{throttle},{aux1},{aux2},{aux3},{aux4}]");
                    break;

                case (int)MSPConst.MSP_SET_RAW_RC:
                    //if (App.isRPi)
                    //    GetRC();
                    //Debug.WriteLine("MSP_RC: Received!");
                    break;

                case (int)MSPConst.MSP_RAW_GPS:
                    gpsFix = read8();
                    gpsNumSat = read8();
                    gpsLatitude = read32() / 10000000d;
                    gpsLongitude = read32() / 10000000d;
                    gpsAltitude = read16();
                    gpsSpeed = read16();
                    gpsGroundCourse = read16();
                    Debug.WriteLine($"GPS Signal: {gpsFix} GPS num sat: {gpsNumSat} Latitude: {gpsLatitude} Longitude: {gpsLongitude} Altitude: {gpsAltitude} Speed: {gpsSpeed}");
                    break;

                default:
                    Debug.WriteLine("Command not implemented: " + icmd);
                    break;
            }
        }

        // ROLL/PITCH/YAW/THROTTLE/AUX1/AUX2/AUX3/AUX4
        // Range [1000;2000]
        // This request is used to inject RC channel via MSP. Each chan overrides legacy RX as long as it is refreshed at least every second. See UART radio projects for more details.
        // Command Code = 200
        public static void MSP_SET_RAW_RC(UInt16 ch1, UInt16 ch2, UInt16 ch3, UInt16 ch4, UInt16 ch5, UInt16 ch6, UInt16 ch7, UInt16 ch8)
        {
            // Send
            List<byte> data = new List<byte>();
            data.AddRange(BitConverter.GetBytes(ch1));
            data.AddRange(BitConverter.GetBytes(ch2));
            data.AddRange(BitConverter.GetBytes(ch3));
            data.AddRange(BitConverter.GetBytes(ch4));
            data.AddRange(BitConverter.GetBytes(ch5));
            data.AddRange(BitConverter.GetBytes(ch6));
            data.AddRange(BitConverter.GetBytes(ch7));
            data.AddRange(BitConverter.GetBytes(ch8));

            sendRequestMSP(requestMSP((int)MSPConst.MSP_SET_RAW_RC, data.ToArray()));
        }

        public static byte[] Calculate_MSP_SET_RAW_RC(UInt16 ch1, UInt16 ch2, UInt16 ch3, UInt16 ch4, UInt16 ch5, UInt16 ch6, UInt16 ch7, UInt16 ch8)
        {
            // Send
            List<byte> data = new List<byte>();
            data.AddRange(BitConverter.GetBytes(ch1));
            data.AddRange(BitConverter.GetBytes(ch2));
            data.AddRange(BitConverter.GetBytes(ch3));
            data.AddRange(BitConverter.GetBytes(ch4));
            data.AddRange(BitConverter.GetBytes(ch5));
            data.AddRange(BitConverter.GetBytes(ch6));
            data.AddRange(BitConverter.GetBytes(ch7));
            data.AddRange(BitConverter.GetBytes(ch8));

            return requestMSP((int)MSPConst.MSP_SET_RAW_RC, data.ToArray()).ToArray();
        }

        public static void GetRC()
        {
            sendRequestMSP(requestMSP((int)MSPConst.MSP_RC));
        }

        public static void GetGPSData()
        {
            sendRequestMSP(requestMSP((int)MSPConst.MSP_RAW_GPS));
        }

        public static void GetBoardVersion()
        {
            sendRequestMSP(requestMSP((int)MSPConst.MSP_IDENT));
        }

        public static void GetAltitude()
        {
            sendRequestMSP(requestMSP((int)MSPConst.MSP_ALTITUDE));
        }

        public static void GetAttitude()
        {
            sendRequestMSP(requestMSP((int)MSPConst.MSP_ATTITUDE));
        }
    }
}
NetworkCmd.csC#
using System;
using System.Diagnostics;
using Windows.Networking.Sockets;
using Windows.Storage.Streams;
using Windows.Networking;
using Windows.UI.Popups;
using System.Collections.Generic;
using Windows.Foundation;
using System.Threading.Tasks;
using Windows.ApplicationModel.Core;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml;
using System.IO;

namespace Drone
{
	public static class NetworkCmd
	{
		private static string hostName = "";
		private const string hostPort = "8027";
        private static StreamSocketListener listener;
        private static DataReader readPacket;
        private static int readBuff = 64;

        public static bool socketIsConnected = false;

        public static async void NetworkInit(string host)
		{
			ClearPrevious();

			hostName = host;
			Debug.WriteLine("NetworkInit() host = " + hostName + " port = " + hostPort);

            // if no host, be client, otherwise be a host
            if (!string.IsNullOrWhiteSpace(hostName))
            {
                await InitConnectionToHost(hostName, hostPort);
            }
            else
            {
                if (listener == null) await StartListener(hostPort);
            }
		}

        #region ----- helpers -----	
        public static void SendDataToHost(string stringToSend)
        {
            if (socketIsConnected) PostSocketWrite(stringToSend);
        }

        static async void PostSocketWrite(string writeStr)
        {
            if (socket == null || !socketIsConnected)
            {
                Debug.WriteLine("Wr: Socket not connected yet.");
                return;
            }

            try
            {
                DataWriter writer = new DataWriter(socket.OutputStream);
                writer.WriteString(writeStr);
                await writer.StoreAsync();
                await writer.FlushAsync();
                writer.DetachStream();
                writer = null;
            }
            catch (Exception ex)
            {
                Debug.WriteLine("Failed to Write - " + ex.Message);
            }
        }

        public static void SendDataToHost(byte[] bytes)
        {
            if (socketIsConnected) PostSocketWrite(bytes);
        }

        static async void PostSocketWrite(byte[] bytes)
        {
            if (socket == null || !socketIsConnected)
            {
                Debug.WriteLine("Wr: Socket not connected yet.");
                return;
            }

            try
            {
                DataWriter writer = new DataWriter(socket.OutputStream);
                writer.WriteBytes(bytes);
                await writer.StoreAsync();
                await writer.FlushAsync();
                writer.DetachStream();
                writer = null;
            }
            catch (Exception ex)
            {
                Debug.WriteLine("Failed to Write - " + ex.Message);
                socketIsConnected = false;

                if (App.isRPi)
                {
                    Debug.WriteLine("Controller signal lost!");
                    MainPage.isSignalLost = true;
                    //MainPage.Safe();
                }

                if (!App.isRPi)
                {
                    Debug.WriteLine("Drone signal lost!");
                    MainPage.isSignalLost = true;

                    await CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
                    {
                        var currentPage = ((ContentControl)Window.Current.Content).Content as Page;
                        var droneStatus = currentPage.FindName("droneStatus") as Image;
                        droneStatus.Opacity = 0.2;
                    });
                }
            }
        }

        static void PostSocketRead(int length)
        {
            if (socket == null || !socketIsConnected)
            {
                Debug.WriteLine("Rd: Socket not connected yet.");
                return;
            }

            try
            {
                var readBuf = new Windows.Storage.Streams.Buffer((uint)length);
                var readOp = socket.InputStream.ReadAsync(readBuf, (uint)length, InputStreamOptions.Partial);
                readOp.Completed = (IAsyncOperationWithProgress<IBuffer, uint> asyncAction, AsyncStatus asyncStatus) =>
                {
                    switch (asyncStatus)
                    {
                        case AsyncStatus.Completed:
                        case AsyncStatus.Error:
                            try
                            {
                                IBuffer localBuf = asyncAction.GetResults();
                                uint bytesRead = localBuf.Length;
                                readPacket = DataReader.FromBuffer(localBuf);
                                OnDataReadCompletion(bytesRead, readPacket);
                            }
                            catch (Exception ex)
                            {
                                Debug.WriteLine("Read operation failed:  " + ex.Message);
                            }
                            break;
                        case AsyncStatus.Canceled:
                            Debug.WriteLine("Read operation cancelled");
                            break;
                    }
                };
            }
            catch (Exception ex)
            {
                Debug.WriteLine("Failed to post a Read - " + ex.Message);
            }
        }

        public static void OnDataReadCompletion(uint bytesRead, DataReader readPacket)
        {
            try
            {
                if (readPacket == null)
                {
                    Debug.WriteLine("DataReader is null");
                    return;
                }

                uint buffLen = readPacket.UnconsumedBufferLength;

                if (buffLen == 0)
                {
                    Debug.WriteLine("Buffer is empty");          
                    return;
                }

                Debug.WriteLine($"Network Received (b={bytesRead},l={buffLen})");

                List<byte> bytes = new List<byte>();
                while (buffLen > 0)
                {
                    byte b = readPacket.ReadByte();
                    bytes.Add(b);
                    buffLen--;
                }

                if (App.isRPi)
                {
                    MultiWii.sendRequestMSP(bytes);
                    PostSocketRead(readBuff);
                }
                else
                {
                    MultiWii.evaluateResponseMSP(bytes);
                    PostSocketRead(readBuff);
                }
            }
            catch(Exception ex)
            {
                Debug.WriteLine("OnDataReadCompletion() - " + ex.Message);
            }
        }

        private static void ClearPrevious()
		{
			if (socket != null)
			{
				socket.Dispose();
				socket = null;
				socketIsConnected = false;
			}
		}
		#endregion

		#region ----- host connection ----
		public static bool listenerHasStarted;
		private static async Task StartListener(string port)
		{
			try
			{
				listener = new StreamSocketListener();
				listener.ConnectionReceived += OnConnection;
				await listener.BindServiceNameAsync(port).AsTask();
				Debug.WriteLine("Listening on port " + port);
				listenerHasStarted = true;
			}
			catch (Exception e)
			{
				Debug.WriteLine("StartListener() - Unable to bind listener. " + e.Message);
                MainPage.BlinkLED(250, 2);
            }
		}

		private static void OnConnection(StreamSocketListener sender, StreamSocketListenerConnectionReceivedEventArgs args)
		{
			try
			{
				if (App.isRPi)
				{
                    MainPage.BlinkLED(250, 1);
                    
                    socket = args.Socket;
                    if (socket != null)
                    {
                        socketIsConnected = true;
                        
                        // Read controller commands
                        PostSocketRead(readBuff);
                    }
                }
            }
            catch (Exception ex)
			{
				Debug.WriteLine("OnConnection() - " + ex.Message);
			}
		}
		#endregion

		#region ----- client connection -----
		static StreamSocket socket;
		private static async Task InitConnectionToHost(string host, string port)
		{
			try
			{
                ClearPrevious();
                socket = new StreamSocket();

                HostName hostNameObj = new HostName(host);
				await socket.ConnectAsync(hostNameObj, port).AsTask();
				Debug.WriteLine("Connected to " + hostNameObj + ":" + port);
				socketIsConnected = true;

                if (!App.isRPi)
                {
                    await CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
                    {
                        var currentPage = ((ContentControl)Window.Current.Content).Content as Page;
                        var droneStatus = currentPage.FindName("droneStatus") as Image;
                        droneStatus.Opacity = 1;
                    });
                }
            }
			catch (Exception ex)
			{
				Debug.WriteLine("InitConnectionToHost() - " + ex.Message);
				if (!App.isRPi)
					await new MessageDialog("InitConnectionToHost() - " + ex.Message).ShowAsync();
			}
		}		
		#endregion		
	}
}

Credits

Replications

Did you replicate this project? Share it!

Love this project? Think it could be improved? Tell us what you think!

Give feedback

Comments

Similar projects you might like

KIDS personal assistant
Advanced
  • 171
  • 3

Work in progress

This is a kid PA project similar to Alexa and Google home. It can answer anything about a person or place, tells meaning & spells any word.

Amazon DRS Weight Sensor
Advanced
  • 1,199
  • 5

Full instructions

This device tracks the use of any product and automatically reorders it from Amazon when it is about to run out without your interaction.

SHRED Template
Advanced
  • 363
  • 1

Work in progress

Submission format for SHRED reports.

SHRED Template

Team MassiveIO

Pool Controller
Advanced
  • 26,428
  • 80

Full instructions

Windows 10 IoT Core project to control pool components for example pool pump, waterfall and solar heater.

Windows IOT - Automate your power outlets
Advanced
  • 15,592
  • 43

Full instructions

Control your home power outlet from anywhere in the world using raspberry pi, zigbee and arduino (Azure enabled)

RFID Register
Advanced
  • 11,090
  • 48

Work in progress

Clock in and out of locations with Mifare RFID cards for an accurate register of movements on and off site locations.

RFID Register

Team Homeworld

Add projectSign up / Login
Respect project