Hardware components | ||||||
| × | 1 | ||||
| × | 1 | ||||
| × | 1 | ||||
Software apps and online services | ||||||
|
Good news: If you are looking to replicate this project or use hardware, drop me your quote and receive your own ready-to-go kit.
This project is about building a HTML page, then you can add your control to make your application become live.
You can then use almost any other devices to have access to it using web browser.
Firstly, I have based on different resources from:
- Blinky webserver from Windows IoT: https://www.hackster.io/windowsiot/blinky-webserver-sample-44ea34
- Create and consume an app service : https://msdn.microsoft.com/en-gb/windows/uwp/launch-resume/how-to-create-and-consume-an-app-service
The project may be required some knowledges in:
- HTML webpage
- Setup Blinky Sample from Windows IoT
- Setup ULN2003 driver: https://www.hackster.io/gaubuali/control-stepper-motor-5944b2
Open Visual Studio, create new Project
Delete StartupTask.cs. Add Reference Windows IoT Extensions for the UWP. Create new class named BackgroundTask.cs
Open Package.appxmanifest tab. Select Capabilities, check Internet (Client & Server)
Open Declarations tab. Remove Background Task then add App Service.
Add properties details
Build and Deploy only to Remote Machine.
Now, create and run a normal application.
public sealed class WebServerBGTask : IBackgroundTask
{
public void Run(IBackgroundTaskInstance taskInstance)
{
// Associate a cancellation handler with the background task.
taskInstance.Canceled += OnCanceled;
// Get the deferral object from the task instance
serviceDeferral = taskInstance.GetDeferral();
var appService = taskInstance.TriggerDetails as AppServiceTriggerDetails;
if (appService != null &&
appService.Name == "AppComService")
{
appServiceConnection = appService.AppServiceConnection;
appServiceConnection.RequestReceived += OnRequestReceived;
}
}
private async void OnRequestReceived(AppServiceConnection sender, AppServiceRequestReceivedEventArgs args)
{
var message = args.Request.Message;
string command = message["Command"] as string;
switch (command)
{
case "Initialize":
{
var messageDeferral = args.GetDeferral();
//Set a result to return to the caller
var returnMessage = new ValueSet();
HttpServer server = new HttpServer(8003, appServiceConnection);
IAsyncAction asyncAction = Windows.System.Threading.ThreadPool.RunAsync(
(workItem) =>
{
server.StartServer();
});
returnMessage.Add("Status", "Success");
var responseStatus = await args.Request.SendResponseAsync(returnMessage);
messageDeferral.Complete();
break;
}
case "Quit":
{
//Service was asked to quit. Give us service deferral
//so platform can terminate the background task
serviceDeferral.Complete();
break;
}
}
}
private void OnCanceled(IBackgroundTaskInstance sender, BackgroundTaskCancellationReason reason)
{
//Clean up and get ready to exit
}
BackgroundTaskDeferral serviceDeferral;
AppServiceConnection appServiceConnection;
}
public sealed class HttpServer : IDisposable
{
string offHtmlString = "<html><head><title>DC Motor Demo</title></head><body><form action=\"dcmotor.html\" method=\"GET\"><h1>DC Motor Demo</h1><p>Active DC Motor</p><input type=\"radio\" name=\"state\" value=\"on\" onclick=\"this.form.submit()\"> On<br><input type=\"radio\" name=\"state\" value=\"off\" checked onclick=\"this.form.submit()\"> Off</form></body></html>";
string onHtmlString = "<html><head><title>DC Motor Demo</title></head><body><form action=\"dcmotor.html\" method=\"GET\"><h1>DC Motor Demo</h1><p>Active DC Motor</p><input type=\"radio\" name=\"state\" value=\"on\" checked onclick=\"this.form.submit()\"> On<br><input type=\"radio\" name=\"state\" value=\"off\" onclick=\"this.form.submit()\"> Off</form></body></html>";
private const uint BufferSize = 8192;
private int port = 8003;
private readonly StreamSocketListener listener;
private AppServiceConnection appServiceConnection;
public HttpServer(int serverPort, AppServiceConnection connection)
{
listener = new StreamSocketListener();
port = serverPort;
appServiceConnection = connection;
listener.ConnectionReceived += (s, e) => ProcessRequestAsync(e.Socket);
}
public void StartServer()
{
#pragma warning disable CS4014
listener.BindServiceNameAsync(port.ToString());
#pragma warning restore CS4014
}
public void Dispose()
{
listener.Dispose();
}
private async void ProcessRequestAsync(StreamSocket socket)
{
// this works for text only
StringBuilder request = new StringBuilder();
using (IInputStream input = socket.InputStream)
{
byte[] data = new byte[BufferSize];
IBuffer buffer = data.AsBuffer();
uint dataRead = BufferSize;
while (dataRead == BufferSize)
{
await input.ReadAsync(buffer, BufferSize, InputStreamOptions.Partial);
request.Append(Encoding.UTF8.GetString(data, 0, data.Length));
dataRead = buffer.Length;
}
}
using (IOutputStream output = socket.OutputStream)
{
string requestMethod = request.ToString().Split('\n')[0];
string[] requestParts = requestMethod.Split(' ');
if (requestParts[0] == "GET")
await WriteResponseAsync(requestParts[1], output);
else
throw new InvalidDataException("HTTP method not supported: "
+ requestParts[0]);
}
}
private async Task WriteResponseAsync(string request, IOutputStream os)
{
// See if the request is for blinky.html, if yes get the new state
string state = "Unspecified";
bool stateChanged = false;
if (request.Contains("dcmotor.html?state=on"))
{
state = "On";
stateChanged = true;
}
else if (request.Contains("dcmotor.html?state=off"))
{
state = "Off";
stateChanged = true;
}
if (stateChanged)
{
var updateMessage = new ValueSet();
updateMessage.Add("State", state);
var responseStatus = await appServiceConnection.SendMessageAsync(updateMessage);
}
string html = state == "On" ? onHtmlString : offHtmlString;
// Show the html
using (Stream resp = os.AsStreamForWrite())
{
// Look in the Data subdirectory of the app package
byte[] bodyArray = Encoding.UTF8.GetBytes(html);
MemoryStream stream = new MemoryStream(bodyArray);
string header = String.Format("HTTP/1.1 200 OK\r\n" +
"Content-Length: {0}\r\n" +
"Connection: close\r\n\r\n",
stream.Length);
byte[] headerArray = Encoding.UTF8.GetBytes(header);
await resp.WriteAsync(headerArray, 0, headerArray.Length);
await stream.CopyToAsync(resp);
await resp.FlushAsync();
}
}
}
public sealed partial class MainPage : Page
{
public MainPage()
{
this.InitializeComponent();
InitGPIO();
InitAppSvc();
}
AppServiceConnection appServiceConnection;
private int LEDStatus = 0;
private const int LED_PIN = 8;
private GpioPin pin;
private SolidColorBrush redBrush = new SolidColorBrush(Windows.UI.Colors.Red);
private SolidColorBrush grayBrush = new SolidColorBrush(Windows.UI.Colors.LightGray);
private void InitGPIO()
{
var gpio = GpioController.GetDefault();
// Show an error if there is no GPIO controller
if (gpio == null)
{
pin = null;
return;
}
pin = gpio.OpenPin(LED_PIN);
pin.Write(GpioPinValue.Low);
pin.SetDriveMode(GpioPinDriveMode.Output);
}
private async void InitAppSvc()
{
var listing = await AppServiceCatalog.FindAppServiceProvidersAsync("AppComService");
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;
}
// Initialize the AppServiceConnection
appServiceConnection = new AppServiceConnection();
appServiceConnection.PackageFamilyName = packageName;
appServiceConnection.AppServiceName = "AppComService";
// Send a initialize request
var res = await appServiceConnection.OpenAsync();
if (res == AppServiceConnectionStatus.Success)
{
var message = new ValueSet();
message.Add("Command", "Initialize");
var response = await appServiceConnection.SendMessageAsync(message);
if (response.Status != AppServiceResponseStatus.Success)
{
throw new Exception("Failed to send message");
}
appServiceConnection.RequestReceived += OnMessageReceived;
}
}
private async void OnMessageReceived(AppServiceConnection sender, AppServiceRequestReceivedEventArgs args)
{
var message = args.Request.Message;
string newState = message["State"] as string;
switch (newState)
{
case "On":
{
await Dispatcher.RunAsync(
CoreDispatcherPriority.High,
() =>
{
TurnOnDcMotor();
});
break;
}
case "Off":
{
await Dispatcher.RunAsync(
CoreDispatcherPriority.High,
() =>
{
TurnOffDcMotor();
});
break;
}
case "Unspecified":
default:
{
// Do nothing
break;
}
}
}
private void TurnOffDcMotor()
{
if (LEDStatus == 1)
{
FlipGpio();
}
}
private void TurnOnDcMotor()
{
if (LEDStatus == 0)
{
FlipGpio();
}
}
private void FlipGpio()
{
if (LEDStatus == 0)
{
LEDStatus = 1;
if (pin != null)
{
// to turn on the LED, we need to push the pin 'low'
pin.Write(GpioPinValue.High);
}
}
else
{
LEDStatus = 0;
if (pin != null)
{
pin.Write(GpioPinValue.Low);
}
}
}
}
Comments