Things used in this project

Hardware components:
Ultimate 4tronix Initio 4WD Robot Kit for Raspberry Pi
×1
Pi 3 02
Raspberry Pi 3 Model B
×1
ELP 2.8mm wide angle lens 1080p HD USB Camera Module (ELP-USBFHD01M-L28)
×1
Official Raspberry Pi WiFi dongle
×1
Energizer Extreme AA NiMH 2300mAh rechargable battery
×6
AA battery charger (one that can load 6x batteries at the same time) (generic)
×1
Adafruit DC & Stepper Motor HAT for Raspberry Pi
×1
Adafruit 16-Channel PWM / Servo HAT for Raspberry Pi
×1
Pololu 3.3V, 2.6A Step-Down Voltage Regulator D24V22F3
×2
(optional) Logitech Gamepad F710
×1
Microsoft (optional) Xbox 360 Wireless Controller for Windows & Xbox 360 Console
×1
Microsoft (optional) Xbox 360 Wireless Receiver for Windows
×1
Adafruit Brass M2.5 Standoffs for Pi HATs - Black Plated
×8
Adafruit GPIO Stacking Header for Pi A+/B+/Pi 2/Pi 3 - Extra-long 2x20 Pins
×1
Anker Micro USB cable with 20 AWG power lines
×1
M2,5 / 16mm screw (generic)
×6
M2,5 screw nut (generic)
×6
M2 / 12mm screw (generic)
×2
M2 screw nut (generic)
×16
Shrink tubes or hot glue gun (generic)
×1
20 AWG Jumper Wires (generic)
×15
Solder (generic)
×1
Software apps and online services:
10
Microsoft Windows 10 IoT Core
Vs2015logo
Microsoft Visual Studio 2015
Hand tools and fabrication machines:
09507 01
Soldering iron (generic)
Crosstip screwdriver PH000
Cordless screw driver with M2 and M2,5 drill head (generic)
Heat gun or hot glue gun (generic)
Side cutter (generic)
Small Flat pliers (generic)

Schematics

Wiring of the robot
Video controlled robot with windows iot core raspberry 3   wiring   schematics 9yoqbwwkty

Code

MotorController.csC#
The C# based motor controller class. This class controlles the "Adafruit DC & Stepper Motor HAT for Raspberry Pi" with help of the PwmController.cs class.
using System;
using System.Collections.Generic;
using System.Threading.Tasks;

namespace Initio_4tronix.CarController
{
    public class MotorController
    {
        private int _i2caddress;
        private int _frequency;
        public PwmController PwmController;
        public List<Motor> Motors;
        private DateTime _lastMotorCommandTime = DateTime.MinValue;
        private const int STOP_MOTOR_AFTER_TIMEOUT = 500;

        public async Task Initialize(byte addr = 0x60,
                                     int freq = 1600)
        {
            _i2caddress = addr;
            _frequency = freq;
            Motors = new List<Motor>
            {
                new Motor(this, 0),
                new Motor(this, 1),
                new Motor(this, 2),
                new Motor(this, 3)
            };

            PwmController = new PwmController(addr);
            await PwmController.Initialize();
            PwmController.SetDesiredFrequency(_frequency);

            Move(new MotorCommand
            {
                ForwardBackward = true,
                Speed = 0
            });
        }

        public void SetPin(int pin, int value)
        {
            if (pin < 0 || pin > 15)
            {
                throw new Exception("PWM pin must be between 0 and 15 inclusive");
            }

            if (value != 0 && value != 1)
            {
                throw new Exception("Pin value must be 0 or 1!");
            }

            if (value == 0)
            {
                PwmController.SetPwm((byte)pin, 4096, 0);
            }
            else if (value == 1)
            {
                PwmController.SetPwm((byte)pin, 0, 4096);
            }
        }

        public Motor GetMotor(int num)
        {
            if (num < 1 || num > 4)
            {
                throw new Exception("MotorHAT Motor must be between 1 and 4 inclusive");
            }

            return Motors[num - 1];
        }

        public void Move(MotorCommand motorCommand)
        {
            //Maximum speed from 0 to 255
            var maximumSpeed = 255;
            
            var motorLeft = GetMotor(4);
            var motorRight = GetMotor(1);

            //Stop
            if (motorCommand.Speed == 0)
            {
                motorLeft.SetSpeed(0);
                motorRight.SetSpeed(0);

                motorLeft.Run(MotorAction.RELEASE);
                motorRight.Run(MotorAction.RELEASE);
            }
            //Full right
            else if (motorCommand.FullRight)
            {
                var carSpeedFull = (int)Math.Round(motorCommand.Speed * maximumSpeed, 0);

                if (motorCommand.ForwardBackward)
                {
                    motorLeft.Run(MotorAction.FORWARD);
                    motorRight.Run(MotorAction.BACKWARD);

                    motorLeft.SetSpeed(carSpeedFull);
                    motorRight.SetSpeed(carSpeedFull);
                }
                else
                {
                    motorLeft.Run(MotorAction.BACKWARD);
                    motorRight.Run(MotorAction.FORWARD);

                    motorLeft.SetSpeed(carSpeedFull);
                    motorRight.SetSpeed(carSpeedFull);
                }
            }
            //Full left
            else if (motorCommand.FullLeft)
            {
                var carSpeedFull = (int)Math.Round(motorCommand.Speed * maximumSpeed, 0);

                if (motorCommand.ForwardBackward)
                {
                    motorLeft.Run(MotorAction.BACKWARD);
                    motorRight.Run(MotorAction.FORWARD);

                    motorLeft.SetSpeed(carSpeedFull);
                    motorRight.SetSpeed(carSpeedFull);
                }
                else
                {
                    motorLeft.Run(MotorAction.FORWARD);
                    motorRight.Run(MotorAction.BACKWARD);

                    motorLeft.SetSpeed(carSpeedFull);
                    motorRight.SetSpeed(carSpeedFull);
                }
            }
            //Slightly left
            else if (motorCommand.RightLeft < 0)
            {
                var carSpeedFull = (int)Math.Round(motorCommand.Speed * maximumSpeed, 0);
                var carSpeedSlow = (int)Math.Round(motorCommand.Speed * (1 - Math.Abs(motorCommand.RightLeft)) * maximumSpeed, 0);

                if (motorCommand.ForwardBackward)
                {
                    motorLeft.Run(MotorAction.FORWARD);
                    motorRight.Run(MotorAction.FORWARD);
                }
                else
                {
                    motorLeft.Run(MotorAction.BACKWARD);
                    motorRight.Run(MotorAction.BACKWARD);
                }

                motorLeft.SetSpeed(carSpeedSlow);
                motorRight.SetSpeed(carSpeedFull);
            }
            //Slightly right
            else if (motorCommand.RightLeft > 0)
            {
                var carSpeedFull = (int)Math.Round(motorCommand.Speed * maximumSpeed, 0);
                var carSpeedSlow = (int)Math.Round(motorCommand.Speed * (1 - Math.Abs(motorCommand.RightLeft)) * maximumSpeed, 0);

                if (motorCommand.ForwardBackward)
                {
                    motorLeft.Run(MotorAction.FORWARD);
                    motorRight.Run(MotorAction.FORWARD);
                }
                else
                {
                    motorLeft.Run(MotorAction.BACKWARD);
                    motorRight.Run(MotorAction.BACKWARD);
                }

                motorLeft.SetSpeed(carSpeedFull);
                motorRight.SetSpeed(carSpeedSlow);
            }
            //Forward or backward
            else if (motorCommand.RightLeft == 0)
            {
                var carSpeedFull = (int)Math.Round(motorCommand.Speed * maximumSpeed, 0);

                if (motorCommand.ForwardBackward)
                {
                    motorLeft.Run(MotorAction.FORWARD);
                    motorRight.Run(MotorAction.FORWARD);
                }
                else
                {
                    motorLeft.Run(MotorAction.BACKWARD);
                    motorRight.Run(MotorAction.BACKWARD);
                }

                motorLeft.SetSpeed(carSpeedFull);
                motorRight.SetSpeed(carSpeedFull);
            }
        }

        /// <summary>
        /// Move motors and stop motors after timeout when method is not called again within the timeout
        /// </summary>
        /// <param name="motorCommand"></param>
        public void MoveAndStopAfterTimeout(MotorCommand motorCommand)
        {
            Move(motorCommand);

            var motorCommandTime = DateTime.Now;
            _lastMotorCommandTime = motorCommandTime;

            Task.Run(async () => { await StopAfterTimeout(motorCommandTime); });
        }

        private async Task StopAfterTimeout(DateTime motorCommandTime)
        {
            await Task.Delay(STOP_MOTOR_AFTER_TIMEOUT);

            if (_lastMotorCommandTime <= motorCommandTime)
            {
                Move(new MotorCommand
                {
                    Speed = 0
                });
            }
        }
    }
}
ServoController.csC#
The C# based servo controller class. This class controlles the "Adafruit 16-Channel PWM / Servo HAT for Raspberry Pi" with help of the PwmController.cs class.
using System;
using System.Threading.Tasks;

namespace Initio_4tronix.CarController
{
    public class ServoController
    {
        private PwmController _pwmController;
        
        private const ushort SERVO_MIN_POSITION = 150;
        private const ushort SERVO_MAX_POSITION = 430;

        private ushort _tiltServoCurrentPosition = 290;
        
        public async Task Initialize()
        {
            _pwmController = new PwmController(0x40);
            await _pwmController.Initialize();
            _pwmController.SetDesiredFrequency(60);

            _pwmController.SetPwm(0, 0, 340);
            _pwmController.SetPwm(1, 0, _tiltServoCurrentPosition);
        }

        public void Move(ServoCommand servoCommand)
        {
            if (servoCommand.Up)
            {
                if (SERVO_MIN_POSITION == _tiltServoCurrentPosition)
                {
                    return;
                }

                _tiltServoCurrentPosition -= servoCommand.Speed;

                if (_tiltServoCurrentPosition < SERVO_MIN_POSITION)
                {
                    _tiltServoCurrentPosition = SERVO_MIN_POSITION;
                }
            }
            else if (servoCommand.Down)
            {
                if (SERVO_MAX_POSITION == _tiltServoCurrentPosition)
                {
                    return;
                }

                _tiltServoCurrentPosition += servoCommand.Speed;

                if (_tiltServoCurrentPosition > SERVO_MAX_POSITION)
                {
                    _tiltServoCurrentPosition = SERVO_MAX_POSITION;
                }
            }

            if (servoCommand.Up || servoCommand.Down)
            {
                _pwmController.SetPwm(1, 0, _tiltServoCurrentPosition);
            }
        }
    }
}
PwmController.csC#
This class controlls the "Adafruit DC & Stepper Motor HAT for Raspberry Pi" and the "Adafruit 16-Channel PWM / Servo HAT for Raspberry Pi" over I2C and is used from the MotorController.cs and ServoController.cs class.
using Initio_4tronix.Helper;
using System;
using System.Threading.Tasks;
using Windows.Devices.I2c;

namespace Initio_4tronix.CarController
{
    public class PwmController
    {
        private I2cDevice _pwmDevice;
        private readonly int _baseAddress;

        public PwmController(int baseAddress)
        {
            //For servo max 1000 frequency. For motor max 1600 frequency
            MaxFrequency = 1600;
            MinFrequency = 40;
            PinCount = 16;
            _baseAddress = baseAddress;
        }

        public async Task Initialize()
        {
            var settings = new I2cConnectionSettings(_baseAddress) { BusSpeed = I2cBusSpeed.FastMode, SharingMode = I2cSharingMode.Shared };

            var controller = await I2cController.GetDefaultAsync();
            _pwmDevice = controller.GetDevice(settings);

            Reset();
        }

        public void Reset()
        {
            QueuedLock.Enter();

            _pwmDevice.Write(new byte[] { (byte)Registers.MODE1, 0x0 }); // reset the device

            QueuedLock.Exit();

            SetAllPwm(4096, 0);
        }

        /// <summary>
        /// Set pwm values
        /// </summary>
        /// <param name="channel">The pin that should updated</param>
        /// <param name="on">The tick (between 0..4095) when the signal should change from low to high</param>
        /// <param name="off">the tick (between 0..4095) when the signal should change from high to low</param>
        public void SetPwm(byte channel, ushort on, ushort off)
        {
            QueuedLock.Enter();

            _pwmDevice.Write(new byte[] { (byte)(Registers.LED0_ON_L + 4 * channel), (byte)(on & 0xFF) });
            _pwmDevice.Write(new byte[] { (byte)(Registers.LED0_ON_H + 4 * channel), (byte)(on >> 8) });
            _pwmDevice.Write(new byte[] { (byte)(Registers.LED0_OFF_L + 4 * channel), (byte)(off & 0xFF) });
            _pwmDevice.Write(new byte[] { (byte)(Registers.LED0_OFF_H + 4 * channel), (byte)(off >> 8) });

            QueuedLock.Exit();
        }

        public void SetAllPwm(ushort on, ushort off)
        {
            QueuedLock.Enter();

            _pwmDevice.Write(new byte[] { (byte)Registers.ALL_LED_ON_L, (byte)(on & 0xFF) });
            _pwmDevice.Write(new byte[] { (byte)Registers.ALL_LED_ON_H, (byte)(on >> 8) });
            _pwmDevice.Write(new byte[] { (byte)Registers.ALL_LED_OFF_L, (byte)(off & 0xFF) });
            _pwmDevice.Write(new byte[] { (byte)Registers.ALL_LED_OFF_H, (byte)(off >> 8) });

            QueuedLock.Exit();
        }

        /// <summary>
        /// Set the frequency (defaults to 60Hz if not set). 1 Hz equals 1 full pwm cycle per second.
        /// </summary>
        /// <param name="frequency">Frequency in Hz</param>
        public double SetDesiredFrequency(double frequency)
        {
            if (frequency > MaxFrequency || frequency < MinFrequency)
            {
                throw new ArgumentOutOfRangeException(nameof(frequency), "Frequency must be between 40 and 1000hz");
            }

            frequency *= 0.9f; //Correct for overshoot in the frequency setting (see issue #11).
            double prescaleval = 25000000f;
            prescaleval /= 4096;
            prescaleval /= frequency;
            prescaleval -= 1;

            byte prescale = (byte)Math.Floor(prescaleval + 0.5f);

            QueuedLock.Enter();

            var readBuffer = new byte[1];
            _pwmDevice.WriteRead(new byte[] { (byte)Registers.MODE1 }, readBuffer);

            byte oldmode = readBuffer[0];
            byte newmode = (byte)((oldmode & 0x7F) | 0x10); //sleep
            _pwmDevice.Write(new byte[] { (byte)Registers.MODE1, newmode });
            _pwmDevice.Write(new byte[] { (byte)Registers.PRESCALE, prescale });
            _pwmDevice.Write(new byte[] { (byte)Registers.MODE1, oldmode });
            Task.Delay(TimeSpan.FromMilliseconds(5)).Wait();
            _pwmDevice.Write(new byte[] { (byte)Registers.MODE1, (byte)(oldmode | 0xa1) });

            QueuedLock.Exit();

            ActualFrequency = frequency;

            return ActualFrequency;

        }

        public double ActualFrequency { get; private set; }
        public double MaxFrequency { get; }
        public double MinFrequency { get; }
        public int PinCount { get; }
    }

    public enum Registers
    {
        MODE1 = 0x00,
        MODE2 = 0x01,
        SUBADR1 = 0x02,
        SUBADR2 = 0x03,
        SUBADR3 = 0x04,
        PRESCALE = 0xFE,
        LED0_ON_L = 0x06,
        LED0_ON_H = 0x07,
        LED0_OFF_L = 0x08,
        LED0_OFF_H = 0x09,
        ALL_LED_ON_L = 0xFA,
        ALL_LED_ON_H = 0xFB,
        ALL_LED_OFF_L = 0xFC,
        ALL_LED_OFF_H = 0xFD
    }

    public enum Bits
    {
        RESTART = 0x80,
        SLEEP = 0x10,
        ALLCALL = 0x01,
        INVRT = 0x10,
        OUTDRV = 0x04
    }
}
Gamepad.csC#
This class get the gamepad commands and move motors and servos accordingly.
using Initio_4tronix.CarController;
using System;
using System.Threading.Tasks;
using Windows.Gaming.Input;
using Windows.System;

namespace Initio_4tronix.Devices
{
    public class GamepadController
    {
        private Gamepad _gamepad;

        private bool _carStopped;
        private volatile bool _isGamepadReadingStopped = true;
        private volatile bool _isGamepadVibrationStopped = true;

        //Dependencies
        private MotorController _motorController;
        private ServoController _servoController;

        public GamepadController(MotorController motorController, ServoController servoController)
        {
            _motorController = motorController;
            _servoController = servoController;

            Gamepad.GamepadAdded += GamepadAdded;
            Gamepad.GamepadRemoved += GamepadRemoved;
        }

        private async void GamepadAdded(object sender, Gamepad gamepad)
        {
            _gamepad = gamepad;
            
            await StartGamepadReading(gamepad);
        }

        private async void GamepadRemoved(object sender, Gamepad gamepad)
        {
            _gamepad = null;

            while (!_isGamepadReadingStopped || !_isGamepadVibrationStopped)
            {
                await Task.Delay(10);
            }

            _motorController.Move(new MotorCommand
            {
                Speed = 0
            });
        }

        private async Task StartGamepadReading(Gamepad gamepad)
        {
            _isGamepadReadingStopped = false;

            var buttonDownTimeLong = TimeSpan.FromSeconds(5);
            var xRightShoulderButton = new GamepadButtonDown(buttonDownTimeLong, GamepadButtons.X, GamepadButtons.RightShoulder);

            while (_gamepad == gamepad)
            {
                var gamepadReadingTry = gamepad?.GetCurrentReading();
                if (!gamepadReadingTry.HasValue)
                    break;

                var gamepadReading = gamepadReadingTry.Value;
                
                var motorCommand = new MotorCommand(gamepadReading);
                var servoCommand = new ServoCommand(gamepadReading);

                if (!_carStopped || motorCommand.Speed != 0.0 || servoCommand.Speed != 0.0)
                {
                    _carStopped = false;

                    //Move motors and servos
                    _motorController.Move(motorCommand);
                    _servoController.Move(servoCommand);
                }

                if (motorCommand.Speed == 0 && servoCommand.Speed == 0)
                {
                    _carStopped = true;
                }

                //Shutdown
                var xRightShoulderButtonResult = xRightShoulderButton.UpdateGamepadButtonState(gamepadReading);
                if (xRightShoulderButtonResult.ButtonClicked)
                {
                    await ProcessLauncher.RunToCompletionAsync(@"CmdWrapper.exe", "\"shutdown -s -t 0\"");
                }

                await Task.Delay(25);
            }

            _isGamepadReadingStopped = true;
        }
    }
}
Camera.csC#
The C# camera class get the current frame from the webcam stream and write it as an byte array jpeg image in the Frame property.
using Initio_4tronix.Helper;
using System;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices.WindowsRuntime;
using System.Threading.Tasks;
using Windows.ApplicationModel.Core;
using Windows.Graphics.Imaging;
using Windows.Media.Capture;
using Windows.Media.Capture.Frames;
using Windows.Storage.Streams;
using Windows.UI.Core;

namespace Initio_4tronix.Devices
{
    /// <summary>
    /// Camera: ELP 2.8mm wide angle lens 1080p HD USB Camera Module (ELP-USBFHD01M-L28)
    /// </summary>
    public class Camera
    {
        public byte[] Frame { get; set; }
        private MediaCapture _mediaCapture;
        private MediaFrameReader _mediaFrameReader;

        //Check if camera support resolution and subtyp before change
        private const int VIDEO_WIDTH = 640;
        private const int VIDEO_HEIGHT = 480;
        private const string VIDEO_SUBTYP = "YUY2";
        private const double IMAGE_QUALITY_PERCENT = 0.8d;
        private BitmapPropertySet _imageQuality;

        public async Task Initialize()
        {
            await CoreApplication.MainView.CoreWindow.Dispatcher.RunAndAwaitAsync(CoreDispatcherPriority.Normal, async () =>
            {
                _imageQuality = new BitmapPropertySet();
                var imageQualityValue = new BitmapTypedValue(IMAGE_QUALITY_PERCENT, Windows.Foundation.PropertyType.Single);
                _imageQuality.Add("ImageQuality", imageQualityValue);

                _mediaCapture = new MediaCapture();

                var frameSourceGroups = await MediaFrameSourceGroup.FindAllAsync();

                var settings = new MediaCaptureInitializationSettings()
                {
                    SharingMode = MediaCaptureSharingMode.ExclusiveControl,

                    //With CPU the results contain always SoftwareBitmaps, otherwise with GPU
                    //they preferring D3DSurface
                    MemoryPreference = MediaCaptureMemoryPreference.Cpu,

                    //Capture only video, no audio
                    StreamingCaptureMode = StreamingCaptureMode.Video
                };

                await _mediaCapture.InitializeAsync(settings);

                var mediaFrameSource = _mediaCapture.FrameSources.First().Value;
                var videoDeviceController = mediaFrameSource.Controller.VideoDeviceController;

                videoDeviceController.DesiredOptimization = Windows.Media.Devices.MediaCaptureOptimization.Quality;
                videoDeviceController.PrimaryUse = Windows.Media.Devices.CaptureUse.Video;

                //Set backlight compensation to min (otherwise there are problems with strong light sources)
                if (videoDeviceController.BacklightCompensation.Capabilities.Supported)
                {
                    videoDeviceController.BacklightCompensation.TrySetValue(videoDeviceController.BacklightCompensation.Capabilities.Min);
                }

                //Set exposure (auto light adjustment)
                if (_mediaCapture.VideoDeviceController.Exposure.Capabilities.Supported
                    && _mediaCapture.VideoDeviceController.Exposure.Capabilities.AutoModeSupported)
                {
                    _mediaCapture.VideoDeviceController.Exposure.TrySetAuto(true);
                }

                //Set resolution, frame rate and video subtyp
                var videoFormat = mediaFrameSource.SupportedFormats.First(sf => sf.VideoFormat.Width == VIDEO_WIDTH
                                                                                && sf.VideoFormat.Height == VIDEO_HEIGHT
                                                                                && sf.Subtype == VIDEO_SUBTYP);

                await mediaFrameSource.SetFormatAsync(videoFormat);

                _mediaFrameReader = await _mediaCapture.CreateFrameReaderAsync(mediaFrameSource);

                //If debugger is attached you can't get frames from the camera, because the BitmapEncoder
                //has a bug and not dispose correctly. This results in an System.OutOfMemoryException
                if (!Debugger.IsAttached)
                {
                    _mediaFrameReader.FrameArrived += FrameArrived;

                    await _mediaFrameReader.StartAsync();
                }
            });
        }
        
        public void FrameArrived(MediaFrameReader sender, MediaFrameArrivedEventArgs eventArgs)
        {
            var frame = _mediaFrameReader.TryAcquireLatestFrame();

            if (frame == null
                || frame.VideoMediaFrame == null
                || frame.VideoMediaFrame.SoftwareBitmap == null)
                return;

            using (var stream = new InMemoryRandomAccessStream())
            {
                using (var bitmap = SoftwareBitmap.Convert(frame.VideoMediaFrame.SoftwareBitmap, BitmapPixelFormat.Rgba8, BitmapAlphaMode.Premultiplied))
                {
                    var imageTask = BitmapEncoder.CreateAsync(BitmapEncoder.JpegEncoderId, stream, _imageQuality).AsTask();
                    imageTask.Wait();
                    var encoder = imageTask.Result;
                    encoder.SetSoftwareBitmap(bitmap);

                    //Rotate image 180 degrees
                    var transform = encoder.BitmapTransform;
                    transform.Rotation = BitmapRotation.Clockwise180Degrees;

                    var flushTask = encoder.FlushAsync().AsTask();
                    flushTask.Wait();

                    using (var asStream = stream.AsStream())
                    {
                        asStream.Position = 0;

                        var image = new byte[asStream.Length];
                        asStream.Read(image, 0, image.Length);

                        Frame = image;

                        encoder = null;
                    }
                }
            }
        }
    }
}
Camera.jsJavaScript
The camera JavaScript file. The JavaScript function GetVideoFrame gets a jpeg image (the current video frame) with an ajax request and display it in the browser. The function recall itself endless.
//Maximum frames of the video stream per second. The less frames the less network traffic.
//(the current server side webcam stream delivers 30 frames, so I limit the frames to 30)
var maximumVideoFramesPerSecond = 30;

var maximumVideoFramesPerSecondTimeout = 1000.0 / maximumVideoFramesPerSecond;
var getVideoFrameTimeout = 2000;
var lastVideoFrameTime = new Date();

function GetVideoFrame() {

    lastVideoFrameTime = new Date();

    var xhr = new XMLHttpRequest();
    xhr.open("GET", "VideoFrame" + new Date().getTime().toString() + ".html", true);
    xhr.responseType = "blob";

    xhr.onreadystatechange = function () {
        if (xhr.readyState == 4) {
            if (xhr.status === 200) {
                var urlCreator = window.URL || window.webkitURL;
                var imageUrl = urlCreator.createObjectURL(xhr.response);
                document.querySelector("#videoFrame").src = imageUrl;
            }
        }
    }

    xhr.timeout = getVideoFrameTimeout;
    xhr.ontimeout = function () {
        GetVideoFrameAfterTimeout();
    }

    xhr.onerror = function () {
        GetVideoFrameAfterTimeout();
    }

    xhr.onload = function () {
        GetVideoFrameAfterTimeout();
    }

    xhr.send();
}

function GetVideoFrameAfterTimeout() {
    var videoFrameTimeToLastFrame = new Date() - lastVideoFrameTime;

    if (videoFrameTimeToLastFrame >= maximumVideoFramesPerSecondTimeout) {
        setTimeout(function () { GetVideoFrame(); }, 0);

    } else {
        var nextVideoFrameTimeout = maximumVideoFramesPerSecondTimeout - videoFrameTimeToLastFrame;
        setTimeout(function () { GetVideoFrame(); }, nextVideoFrameTimeout);
    }
}

GetVideoFrame();
HttpServer.csC#
The C# http server. It provides the html, css and javascript files. It returns also the current video frame (jpeg image) to the ajax request thats called from the Camera.js JavaScript file. It receive and executes the car move commands too.
using Initio_4tronix.CarController;
using Initio_4tronix.Devices;
using Initio_4tronix.Helper;
using System;
using System.Runtime.InteropServices.WindowsRuntime;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using Windows.Networking.Sockets;
using Windows.Storage.Streams;
using Windows.System;

namespace Initio_4tronix.Web
{
    public sealed class HttpServer
    {
        private const uint BUFFER_SIZE = 3024;
        private readonly StreamSocketListener _listener;

        //Dependency objects
        private MotorController _motorController;
        private ServoController _servoController;
        private Camera _camera;

        public HttpServer(MotorController motorController,
                          ServoController servoController,
                          Camera camera)
        {
            _motorController = motorController;
            _servoController = servoController;
            _camera = camera;

            _listener = new StreamSocketListener();
            _listener.ConnectionReceived += ProcessRequest;
            _listener.Control.KeepAlive = false;
            _listener.Control.NoDelay = false;
            _listener.Control.QualityOfService = SocketQualityOfService.LowLatency;
        }

        public async void Start()
        {
            await _listener.BindServiceNameAsync(80.ToString());
        }

        private async void ProcessRequest(StreamSocketListener streamSocktetListener, StreamSocketListenerConnectionReceivedEventArgs eventArgs)
        {
            try
            {
                var socket = eventArgs.Socket;

                //Read request
                var httpRequest = await ReadRequest(socket);

                //Write Response
                await WriteResponse(httpRequest, socket.OutputStream);

                socket.InputStream.Dispose();
                socket.OutputStream.Dispose();
                socket.Dispose();
            }
            catch (Exception) { }
        }

        private async Task<HttpRequest> ReadRequest(StreamSocket socket)
        {
            var request = string.Empty;

            using (var inputStream = socket.InputStream)
            {
                var data = new byte[BUFFER_SIZE];
                var buffer = data.AsBuffer();

                var startReadRequest = DateTime.Now;
                while (!HttpGetRequestHasUrl(request))
                {
                    if (DateTime.Now.Subtract(startReadRequest) >= TimeSpan.FromMilliseconds(5000))
                    {
                        throw new TaskCanceledException("Request timeout.");
                    }

                    var inputStreamReadTask = inputStream.ReadAsync(buffer, BUFFER_SIZE, InputStreamOptions.Partial);
                    var timeout = TimeSpan.FromMilliseconds(1000);
                    await TaskHelper.CancelTaskAfterTimeout(ct => inputStreamReadTask.AsTask(ct), timeout);

                    request += Encoding.UTF8.GetString(data, 0, data.Length);
                }
            }

            var requestMethod = request.Split('\n')[0];
            var requestParts = requestMethod.Split(' ');
            var relativeUrl = requestParts.Length > 1 ? requestParts[1] : string.Empty;

            return new HttpRequest { Request = request, RelativeUrl = relativeUrl.ToLowerInvariant() };
        }

        private async Task WriteResponse(HttpRequest httpRequest, IOutputStream outputStream)
        {
            //Get javascript files
            if (httpRequest.RelativeUrl.StartsWith("/javascript"))
            {
                await HttpResponse.WriteResponseFile(ToFolderPath(httpRequest.RelativeUrl), HttpContentType.JavaScript, outputStream);
            }
            //Get css style files
            else if (httpRequest.RelativeUrl.StartsWith("/styles"))
            {
                await HttpResponse.WriteResponseFile(ToFolderPath(httpRequest.RelativeUrl), HttpContentType.Css, outputStream);
            }
            //Get images
            else if (httpRequest.RelativeUrl.StartsWith("/images"))
            {
                await HttpResponse.WriteResponseFile(ToFolderPath(httpRequest.RelativeUrl), HttpContentType.Png, outputStream);
            }
            //Get current camera frame
            else if (httpRequest.RelativeUrl.StartsWith("/videoframe"))
            {
                if (_camera.Frame != null)
                {
                    HttpResponse.WriteResponseFile(_camera.Frame, HttpContentType.Jpeg, outputStream);
                }
                else
                {
                    HttpResponse.WriteResponseError("Not camera fram available. Maybe there is an error or camera is not started.", outputStream);
                }
            }
            //Shutdown
            else if (httpRequest.RelativeUrl.StartsWith("/shutdown"))
            {
                await ProcessLauncher.RunToCompletionAsync(@"CmdWrapper.exe", "\"shutdown -s -t 0\"");
            }
            //Motor and servo command
            else if (httpRequest.RelativeUrl.StartsWith("/motorservocommand"))
            {
                var httpRequestContent = HttpRequestContentHelper.GetContentAsJson(httpRequest.Request);
                if (!httpRequestContent.Success)
                {
                    HttpResponse.WriteResponseError("Request contains no content.", outputStream);
                }

                var content = httpRequestContent.Content;

                var forward = content["forward"].GetBoolean();
                var backward = content["backward"].GetBoolean();

                var motorCommand = new MotorCommand
                {
                    ForwardBackward = forward || !backward,
                    FullLeft = content["fullLeft"].GetBoolean(),
                    FullRight = content["fullRight"].GetBoolean(),
                    Speed = content["speed"].GetBoolean() ? 1 : 0
                };

                var servoCommand = new ServoCommand
                {
                    Up = content["up"].GetBoolean(),
                    Down = content["down"].GetBoolean(),
                    Speed = 7
                };

                _motorController.MoveAndStopAfterTimeout(motorCommand);
                _servoController.Move(servoCommand);

                HttpResponse.WriteResponseOk(outputStream);
            }
            //Get Desktop.html page
            else if (httpRequest.RelativeUrl.StartsWith("/desktop"))
            {
                await HttpResponse.WriteResponseFile(@"\Html\Desktop.html", HttpContentType.Html, outputStream);
            }
            //Get Mobile.html page
            else if (httpRequest.RelativeUrl.StartsWith("/mobile"))
            {
                await HttpResponse.WriteResponseFile(@"\Html\Mobile.html", HttpContentType.Html, outputStream);
            }
            //Get index.html page
            else
            {
                await HttpResponse.WriteResponseFile(@"\Html\Index.html", HttpContentType.Html, outputStream);
            }
        }

        private bool HttpGetRequestHasUrl(string httpRequest)
        {
            var regex = new Regex("^.*GET.*HTTP.*\\r\\n.*$", RegexOptions.Multiline);
            return regex.IsMatch(httpRequest.ToUpper());
        }
                
        private string ToFolderPath(string relativeUrl)
        {
            var folderPath = relativeUrl.Replace('/', '\\');
            return folderPath;
        }
    }
}
Video controlled Robot with Windows IoT Core Raspberry 3
The code for the robot is written as a UWP C# app.

Credits

812263169b0ee61fc6c4b75c03df8541
Sascha

.NET Developer

Replications

Did you replicate this project? Share it!

I made one

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

Give feedback

Comments

Similar projects you might like

SORT4U
Intermediate
  • 30
  • 2

Full instructions

It's the sorting machine that you've been waiting for. Just throw all your junk in it and let it sort for you.

SORT4U

Team The Avengers

Solar Smart Greenhouse Using Vertical Aquaponic Towers
Intermediate
  • 2,973
  • 43

Work in progress

Feed your family with 22 sqm greenhousing by combining permaculture, aquaponic vertical towers and IoT.

Drop of Life
Intermediate
  • 666
  • 17

Full instructions

A display to remind me when I can donate blood and save lives, powered by the Red Cross API and Particle.

Measure wind speed with Eltako windsensor and Win10 IoT Core
Intermediate
  • 574
  • 7

Full instructions

This Project will help you track wind Speeds using Windows 10 IoT Core and the Eltako Windsensor.

FM radio
Intermediate
  • 8,382
  • 38

Full instructions

Build a great sounding FM radio with a cool display using an Arduino Nano and the Sparkfun Si4703 FM tuner breakout

Raspberry Pi Facial Recognition
Intermediate
  • 5,256
  • 42

Work in progress

So you want to control entry to your secret lair, huh? This project will allow you to use facial recognition using a Pi and AWS.

ProjectsCommunitiesContestsLiveAppsBetaFree StoreBlogAdd projectSign up / Login
Respect project
Feedback