<?xml version="1.0" encoding="utf-8"?><rss version="2.0"><channel><title>Edi Wang</title><description>Microsoft MVP for Azure</description><link>https://edi.wang/</link><pubDate>2026-11-08T14:38:03Z</pubDate><copyright>© 2009 - 2026 edi.wang</copyright><generator>Moonglade v16.10.0-preview.1</generator><language>en-US</language><item><title>Azure Remote Controlled Light with .NET Core 3.0 on Raspberry Pi</title><link>https://edi.wang/post/2019/10/6/azure-remote-controlled-light-with-net-core-30-on-raspberry-pi</link><description>&lt;p&gt;3 years ago, I wrote a blog post "&lt;a href="/post/2016/3/10/windows-10-iot-azure-remote-light" target="_blank" rel="noopener"&gt;Azure Remote Controlled Light with Windows 10 IoT Core&lt;/a&gt;", which is an experiment to sending a signal from your home and it will travel around the world then come back to your home to turn on an LED light on your Raspberry Pi. 3 years later, Windows 10 IoT Core and UWP seem not living so well. Microsoft doesn't have a new build of Windows 10 to support the latest Raspberry Pi 4 currently. I have a Pi 4, so I have no choice but to use .NET Core on Linux. Microsoft and the community are rapidly developing the .NET IoT libraries &lt;a href="https://github.com/dotnet/iot" target="_blank" rel="noopener"&gt;here&lt;/a&gt;, so it's very easy to move my old project from UWP to .NET Core 3.0 on Linux.&lt;/p&gt;
&lt;p&gt;Before we begin, please check out "&lt;a href="/post/2019/9/29/setup-net-core-30-runtime-and-sdk-on-raspberry-pi-4" target="_blank" rel="noopener"&gt;Setup .NET Core 3.0 Runtime and SDK on Raspberry Pi 4&lt;/a&gt;" if you haven't installed .NET Core on your Raspberry Pi.&lt;/p&gt;
&lt;h2&gt;Overview&lt;/h2&gt;
&lt;hr&gt;
&lt;p&gt;We are going to send messages from our computer to Azure IoT Hub, and Raspberry Pi will listen to the message and turn on/off the LED.&lt;/p&gt;
&lt;p&gt;&lt;img src="/image/img-d21677ea-8365-4720-a1c7-51fb8f88de32.png" border="0" loading="lazy"&gt;&lt;/p&gt;
&lt;h2&gt;Create Azure IoT Hub&lt;/h2&gt;
&lt;hr&gt;
&lt;p&gt;Refer to &lt;a href="https://docs.microsoft.com/en-us/azure/iot-hub/quickstart-send-telemetry-dotnet?WT.mc_id=AZ-MVP-5002809" target="_blank" rel="noopener"&gt;Azure document&lt;/a&gt; to create an IoT Hub. Copy your connection string, it looks like this:&lt;/p&gt;
&lt;pre class="language-bash"&gt;&lt;code&gt;HostName=&amp;lt;Your Hub Name&amp;gt;.azure-devices.net;SharedAccessKeyName=iothubowner;SharedAccessKey=&amp;lt;Your Key&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Download and install Azure Device Explorer from &lt;a href="https://aka.ms/aziotdevexp"&gt;https://aka.ms/aziotdevexp&lt;/a&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Copy your IoT Hub connection string and paste it into the main tab. Then hit the "&lt;strong&gt;Update&lt;/strong&gt;" button.&lt;/p&gt;
&lt;p&gt;&lt;img src="/image/img-27289818-7823-482e-aef3-c044acc37449.png" border="0" loading="lazy"&gt;&lt;/p&gt;
&lt;p&gt;Then go to the "&lt;strong&gt;Management&lt;/strong&gt;" tab, click "&lt;strong&gt;Create&lt;/strong&gt;", enter your device name for your Raspberry Pi and select "&lt;strong&gt;Auto Generate Keys&lt;/strong&gt;"&lt;/p&gt;
&lt;p&gt;&lt;img src="/image/img-57015707-bec5-4c74-bf7b-bdea2ed39d96.png" border="0" loading="lazy"&gt;&lt;/p&gt;
&lt;p&gt;After the registration, you would see the result listed in the grid view.&lt;/p&gt;
&lt;h2 data-hash="A3jO"&gt;Physical Connection on the Pi&lt;/h2&gt;
&lt;hr&gt;
&lt;p id="toc-2--physical-connection-on-the-pi-1" data-hash="A3jO"&gt;Connect an LED to your Raspberry Pi with:&lt;/p&gt;
&lt;ul&gt;
&lt;li data-hash="A3jO"&gt;Long led to GPIO 17&lt;/li&gt;
&lt;li data-hash="A3jO"&gt;Short leg to GROUND&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-hash="A3jO"&gt;Create AzureLightControl Project&lt;/h2&gt;
&lt;hr&gt;
&lt;p data-hash="A3jO"&gt;Create a new .NET Core 3.0 Console project. Add &lt;code&gt;Microsoft.Azure.Devices.Client&lt;/code&gt; and &lt;code&gt;System.Device.Gpio&lt;/code&gt; NuGet packages. &lt;code&gt;Microsoft.Azure.Devices.Client&lt;/code&gt; talks to Azure IoT Hub, while &lt;code&gt;System.Device.Gpio&lt;/code&gt; controls the GPIO for switching the LED. The project file looks like this:&lt;/p&gt;
&lt;pre class="language-markup"&gt;&lt;code&gt;&amp;lt;Project Sdk="Microsoft.NET.Sdk"&amp;gt;

  &amp;lt;PropertyGroup&amp;gt;
    &amp;lt;OutputType&amp;gt;Exe&amp;lt;/OutputType&amp;gt;
    &amp;lt;TargetFramework&amp;gt;netcoreapp3.0&amp;lt;/TargetFramework&amp;gt;
  &amp;lt;/PropertyGroup&amp;gt;

  &amp;lt;ItemGroup&amp;gt;
    &amp;lt;PackageReference Include="Microsoft.Azure.Devices.Client" Version="1.21.1" /&amp;gt;
    &amp;lt;PackageReference Include="System.Device.Gpio" Version="1.0.0" /&amp;gt;
  &amp;lt;/ItemGroup&amp;gt;

&amp;lt;/Project&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Replace Program.cs with the following content. Replace the const string content with your own value from Azure.&lt;/p&gt;
&lt;pre class="language-csharp"&gt;&lt;code&gt;using System;
using System.Device.Gpio;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Azure.Devices.Client;
using Microsoft.Azure.Devices.Client.Exceptions;

namespace AzureLightControl
{
    class Program
    {
        private const string IotHubUri = "&amp;lt;Your Hub Name&amp;gt;.azure-devices.net";
        private const string DeviceKey = "&amp;lt;Your Key&amp;gt;";
        private const string DeviceId = "&amp;lt;Your Device ID&amp;gt;";
        private const int Pin = 17;

        private static CancellationToken _ct;

        static async Task Main(string[] args)
        {
            Console.WriteLine("------------------------------");
            Console.WriteLine(" Azure IoT Hub Light Control");
            Console.WriteLine("------------------------------");

            var cts = new CancellationTokenSource();
            _ct = cts.Token;

            Console.CancelKeyPress += (sender, eventArgs) =&amp;gt;
            {
                Console.WriteLine($"{DateTime.Now} &amp;gt; Cancelling...");
                cts.Cancel();

                eventArgs.Cancel = true;
            };
            
            try
            {
                var t = Task.Run(Run, cts.Token);
                await t;
            }
            catch (IotHubCommunicationException)
            {
                Console.WriteLine($"{DateTime.Now} &amp;gt; Operation has been canceled.");
            }
            catch (OperationCanceledException)
            {
                Console.WriteLine($"{DateTime.Now} &amp;gt; Operation has been canceled.");
            }
            finally
            {
                cts.Dispose();
            }

            Console.ReadKey();
        }

        private static async Task Run()
        {
            using var deviceClient = DeviceClient.Create(IotHubUri, new DeviceAuthenticationWithRegistrySymmetricKey(DeviceId, DeviceKey));
            using var controller = new GpioController();
            controller.OpenPin(Pin, PinMode.Output);

            Console.WriteLine($"{DateTime.Now} &amp;gt; Connected to the best cloud on the planet.");
            Console.WriteLine($"Azure IoT Hub: {IotHubUri}");
            Console.WriteLine($"Device ID: {DeviceId}");

            Console.WriteLine($"{DateTime.Now} &amp;gt; GPIO pin enabled for use: {Pin}");

            while (!_ct.IsCancellationRequested)
            {
                Console.WriteLine($"{DateTime.Now} &amp;gt; Waiting new message from Azure...");
                var receivedMessage = await deviceClient.ReceiveAsync(_ct);
                if (receivedMessage == null) continue;

                var msg = Encoding.ASCII.GetString(receivedMessage.GetBytes());
                Console.WriteLine($"{DateTime.Now} &amp;gt; Received message: {msg}");

                switch (msg)
                {
                    case "on":
                        Console.WriteLine($"{DateTime.Now} &amp;gt; Turn on the light.");
                        controller.Write(Pin, PinValue.High);
                        break;
                    case "off":
                        Console.WriteLine($"{DateTime.Now} &amp;gt; Turn off the light.");
                        controller.Write(Pin, PinValue.Low);
                        break;
                    default:
                        Console.WriteLine($"Unknown command: {msg}");
                        break;
                }

                await deviceClient.CompleteAsync(receivedMessage, _ct);
            }
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In this example, &lt;code&gt;deviceClient.ReceiveAsync()&lt;/code&gt; method will listen for any new message from Azure IoT Hub. Process it, and then call &lt;code&gt;CompleteAsync()&lt;/code&gt; to tell Azure a message has been processed, so it won't be processed again if the device reconnects to Azure.&lt;/p&gt;
&lt;p&gt;Processing the message is very straight forward, read the message as string content, if it says "on", write a GPIO HIGH to pin 17, this can light up the LED. If the message content is "off", write a GPIO LOW to pin 17 to turn off the LED. The GPIO operation for controlling an LED is &lt;code&gt;PinMode.Output&lt;/code&gt;, if you work with sensors, it is usually &lt;code&gt;PinMode.Input&lt;/code&gt;.&amp;nbsp;&lt;/p&gt;
&lt;h2&gt;Deploy and Run&lt;/h2&gt;
&lt;hr&gt;
&lt;p&gt;Copy the source code or publish it to Raspberry Pi.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;On your Raspberry Pi: Use dotnet CLI to run this project. (If you don't know how, please refer to "&lt;a href="/post/2019/9/29/setup-net-core-30-runtime-and-sdk-on-raspberry-pi-4" target="_blank" rel="noopener"&gt;Setup .NET Core 3.0 Runtime and SDK on Raspberry Pi 4&lt;/a&gt;")&lt;/p&gt;
&lt;p&gt;On your PC: Send message content "&lt;strong&gt;on&lt;/strong&gt;" or "&lt;strong&gt;off&lt;/strong&gt;" to your Raspberry Pi in Device Explorer.&lt;/p&gt;
&lt;p&gt;&lt;img src="/image/img-269556e7-6ddc-4e8c-b872-d4bc005b32a1.png" border="0" loading="lazy"&gt;&lt;/p&gt;
&lt;p&gt;You have the magic to control a light over the internet now:&lt;/p&gt;
&lt;p&gt;&lt;img src="/image/img-29a2510f-58ff-406d-a2e4-28e8686c0c0e.jpg" border="0" alt="" width="2048" height="1535" loading="lazy"&gt;&lt;/p&gt;</description><category>.NET</category><category>Internet of Things</category><category>Microsoft Azure</category><guid isPermaLink="false">d313fcf1-1792-4fcb-bcda-22f874418497</guid><pubDate>2019-10-06T15:59:35Z</pubDate><updated xmlns="http://www.w3.org/2005/Atom">2019-10-06T15:59:35Z</updated></item><item><title>Setup .NET Runtime and SDK on Raspberry Pi 4</title><link>https://edi.wang/post/2019/9/29/setup-net-core-30-runtime-and-sdk-on-raspberry-pi-4</link><description>&lt;p&gt;I recently bought a Raspberry Pi 4 with 4GB RAM. I've managed to run an ASP.NET Core application on this Raspberry Pi 4.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;05/13/2022 Update content for .NET 6.0 LTS for ARM64. Original post is for .NET Core 3.1 ARM32.&lt;/p&gt;
&lt;h2&gt;ARM32 or ARM64?&lt;/h2&gt;
&lt;hr&gt;
&lt;p&gt;You can't run ARM64 version of .NET Core runtime or SDK on a Raspberry Pi 4 with the official Raspbian system today. Although Raspberry Pi 4's CPU supports 64 bit, its official operating system "Raspbian" isn't 64 bit. &lt;strong&gt;However, &lt;a href="https://ubuntu.com/download/raspberry-pi" target="_blank" rel="noopener"&gt;Ubuntu for Raspberry Pi 4&lt;/a&gt; can install ARM64 .NET 6.0 SDK&lt;/strong&gt;. It's up to you to choose ARM32 or ARM64. The setup steps are the same.&amp;nbsp;&lt;/p&gt;
&lt;h2&gt;Prepare&lt;/h2&gt;
&lt;hr&gt;
&lt;p&gt;Required: An Internet connection for downloading .NET SDK and Runtime.&lt;/p&gt;
&lt;p&gt;Optional: Visual Studio Code / Visual Studio on your desktop machine to create a sample project&lt;/p&gt;
&lt;p&gt;Update your Raspbian:&lt;/p&gt;
&lt;pre class="language-bash"&gt;&lt;code&gt;sudo apt-get update
sudo apt-get upgrade&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I also suggest using a monitor for your Raspberry Pi or a VNC connection, so that you can test your ASP.NET Core websites at the final step without messing up the port and network settings.&lt;/p&gt;
&lt;h2&gt;Download .NET SDK and Runtime&lt;/h2&gt;
&lt;hr&gt;
&lt;p&gt;On your desktop machine, open &lt;a href="https://dotnet.microsoft.com/download/dotnet-core/6.0"&gt;https://dotnet.microsoft.com/download/dotnet-core/6.0&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Find Linux distributions for ARM32/64 SDK and ASP.NET Core Runtime, click into it, you can then copy the &lt;strong&gt;Direct Link&lt;/strong&gt;. These 2 links are what we use in Raspbian / Ubuntu to download the latest SDK and Runtime. For example:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;.NET Core 6.0 SDK (Linux/ARM64)&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://download.visualstudio.microsoft.com/download/pr/7c62b503-4ede-4ff2-bc38-50f250a86d89/3b5e9db04cbe0169e852cb050a0dffce/dotnet-sdk-6.0.300-linux-arm64.tar.gz"&gt;https://download.visualstudio.microsoft.com/download/pr/7c62b503-4ede-4ff2-bc38-50f250a86d89/3b5e9db04cbe0169e852cb050a0dffce/dotnet-sdk-6.0.300-linux-arm64.tar.gz&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;ASP.NET Core 3.1 Runtime (Linux/ARM64)&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://download.visualstudio.microsoft.com/download/pr/8ba7087e-4513-41e5-8359-a4bcd2a3661f/e6828f0d8cf1ecc63074c9ff57685e27/aspnetcore-runtime-6.0.5-linux-arm64.tar.gz"&gt;https://download.visualstudio.microsoft.com/download/pr/8ba7087e-4513-41e5-8359-a4bcd2a3661f/e6828f0d8cf1ecc63074c9ff57685e27/aspnetcore-runtime-6.0.5-linux-arm64.tar.gz&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Use &lt;code&gt;wget&lt;/code&gt; on your Raspbian to download .NET SDK and ASP.NET Core runtime, for example, Linux ARM64:&lt;/p&gt;
&lt;pre class="language-bash"&gt;&lt;code&gt;wget https://download.visualstudio.microsoft.com/download/pr/7c62b503-4ede-4ff2-bc38-50f250a86d89/3b5e9db04cbe0169e852cb050a0dffce/dotnet-sdk-6.0.300-linux-arm64.tar.gz
wget https://download.visualstudio.microsoft.com/download/pr/8ba7087e-4513-41e5-8359-a4bcd2a3661f/e6828f0d8cf1ecc63074c9ff57685e27/aspnetcore-runtime-6.0.5-linux-arm64.tar.gz&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;em&gt;Please always check for updated URLs for a newer version of SDK and Runtime.&lt;/em&gt;&lt;/p&gt;
&lt;h2&gt;Install .NET SDK and Runtime&lt;/h2&gt;
&lt;hr&gt;
&lt;p&gt;Create a folder named for example "&lt;strong&gt;dotnet-arm64&lt;/strong&gt;" and unzip them into it.&amp;nbsp;&lt;/p&gt;
&lt;pre class="language-bash"&gt;&lt;code&gt;mkdir dotnet-arm64
tar zxf dotnet-sdk-6.0.300-linux-arm64.tar.gz -C $HOME/dotnet-arm64
tar zxf aspnetcore-runtime-6.0.5-linux-arm64.tar.gz -C $HOME/dotnet-arm64&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;So far you can only run .NET commands inside the&lt;strong&gt; dotnet-arm64&lt;/strong&gt;&amp;nbsp;folder. To make "dotnet" command can be run everywhere, you have to make something like environment variables, which will link &lt;strong&gt;dotnet-arm64&lt;/strong&gt;&amp;nbsp;folder to system level.&lt;/p&gt;
&lt;pre class="language-bash"&gt;&lt;code&gt;export DOTNET_ROOT=$HOME/dotnet-arm64
export PATH=$PATH:$HOME/dotnet-arm64&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now, you can run &lt;code&gt;dotnet --info&lt;/code&gt; to have a quick test:&lt;/p&gt;
&lt;p&gt;&lt;img src="/image/img-d451ad4b-4f5e-4bf0-9e65-b6bdb3c00599.png" border="0" loading="lazy"&gt;&lt;/p&gt;
&lt;h2&gt;Create .NET Applications&lt;/h2&gt;
&lt;hr&gt;
&lt;p&gt;Now that we have the SDK, we don't have to use another machine to develop and publish the App. We can just do it directly on our Raspberry Pi.&amp;nbsp;&lt;/p&gt;
&lt;pre class="language-bash"&gt;&lt;code&gt;mkdir hello-dotnet
cd hello-dotnet
dotnet new console&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;After the project files are created and NuGet restore completes. You can run the project using "&lt;code&gt;dotnet run&lt;/code&gt;" command.&lt;/p&gt;
&lt;p&gt;&lt;img src="/image/img-66c6bd54-4f08-4588-b380-e256468fc5fa.png" border="0" loading="lazy"&gt;&lt;/p&gt;
&lt;p&gt;You can even use nano as a basic code editor.&lt;/p&gt;
&lt;pre class="language-markup"&gt;&lt;code&gt;sudo nano Program.cs&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src="/image/img-57146326-fd0e-4a2c-b2cd-fb98f59f4f38.png" border="0" loading="lazy"&gt;&lt;/p&gt;
&lt;h2&gt;Deploy and Run ASP.NET Core Applications&lt;/h2&gt;
&lt;hr&gt;
&lt;p&gt;In a more real-world scenario. You just can't develop complex .NET applications without an IDE on Raspbain. We usually develop and test our App on desktop machines first and then publish it to Raspberry Pi to run it.&lt;/p&gt;
&lt;p&gt;To run an ASP.NET Core application. You need to first publish it. For example, from Visual Studio 2022. What I have is an empty ASP.NET Core application with a little more output information in Startup.cs&lt;/p&gt;
&lt;pre class="language-csharp"&gt;&lt;code&gt;await context.Response.WriteAsync($"Empower every person and every organization on the planet to achieve more{Environment.NewLine}" +
                                  $".NET Core {Environment.Version}{Environment.NewLine}" +
                                  $"Environment.OSVersion: {Environment.OSVersion}{Environment.NewLine}" +
                                  $"Environment.Is64BitOperatingSystem: {Environment.Is64BitOperatingSystem}{Environment.NewLine}" +
                                  $"Environment.Is64BitProcess: {Environment.Is64BitProcess}", Encoding.UTF8);&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Publish it using &lt;strong&gt;Framework-Dependent (FDD)&lt;/strong&gt; and &lt;strong&gt;Portable&lt;/strong&gt;. Because we have the native runtime on Raspberry Pi. &lt;span style="color: #e03e2d;"&gt;&lt;strong&gt;We DON'T have to build it into linux-arm RID! Portable will run just fine!&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src="/image/img-4195a7ce-27c3-49df-9bd0-35c73ea55438.png" border="0" loading="lazy"&gt;&lt;/p&gt;
&lt;p&gt;Copy the published directory to Raspberry Pi.&lt;/p&gt;
&lt;p&gt;&lt;img src="/image/img-0fd76800-533d-4928-81ab-189e93a43195.png" border="0" loading="lazy"&gt;&lt;/p&gt;
&lt;p&gt;Now you can use "&lt;code&gt;dotnet &amp;lt;Your DLL Name&amp;gt;.dll&lt;/code&gt;" to run your ASP.NET Core application.&lt;/p&gt;
&lt;p&gt;&lt;img src="/image/img-b260f547-b9b3-45c5-9e8b-b6aba68e883b.png" border="0" loading="lazy"&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src="/image/img-57c835dd-4545-4535-871a-de7b54d8fc22.png" border="0" loading="lazy"&gt;&lt;/p&gt;
&lt;h2&gt;"Auto Start" .NET Environment&lt;/h2&gt;
&lt;hr&gt;
&lt;p&gt;Every time you restart your Raspberry Pi, you'll have to re-configure the &lt;strong&gt;DOTNET_ROOT&lt;/strong&gt; and &lt;strong&gt;PATH&lt;/strong&gt; environment variables or .NET CLI won't work. To make them auto start with the system, we can modify the ".profile".&lt;/p&gt;
&lt;pre class="language-bash"&gt;&lt;code&gt;sudo nano .profile&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Add those lines at the end of this file&lt;/p&gt;
&lt;pre class="language-bash"&gt;&lt;code&gt;# set .NET SDK and Runtime path
export DOTNET_ROOT=$HOME/dotnet-arm64
export PATH=$PATH:$HOME/dotnet-arm64&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Looks like this&lt;/p&gt;
&lt;p&gt;&lt;img src="/image/img-82cf3652-2b87-49f6-b0c3-065ef569167d.png" border="0" loading="lazy"&gt;&lt;/p&gt;
&lt;p&gt;Save the file and restart your Raspbian system. Now you can run "&lt;code&gt;dotnet --info&lt;/code&gt;" directly without manually setting the environment variables again.&lt;/p&gt;
&lt;h2&gt;Access ASP.NET Core Website from LAN&lt;/h2&gt;
&lt;hr&gt;
&lt;p&gt;So far your ASP.NET Core application can only be visited on Raspberry Pi itself using "localhost". It would be much more useful if we expose it to LAN. There are two ways to do that.&lt;/p&gt;
&lt;h6&gt;Host by Kestrel itself&lt;/h6&gt;
&lt;p&gt;Just give &lt;code&gt;dotnet&lt;/code&gt; command a &lt;code&gt;--urls&lt;/code&gt; parameter, it would listen on the domain and port you provided. I don't want to limit to any domain name, so I use * instead.&lt;/p&gt;
&lt;pre class="language-bash"&gt;&lt;code&gt;dotnet Empower.dll --urls "http://*:8080"&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now you will be able to access your Raspberry Pi via LAN.&lt;/p&gt;
&lt;p&gt;&lt;img src="/image/img-e6db2b1e-345f-4c01-b1d8-b769345e7de4.png" border="0" loading="lazy"&gt;&lt;/p&gt;
&lt;p&gt;But this hosting model has its disadvantages, for example, when you have an exception in your code, the &lt;code&gt;dotnet&lt;/code&gt; process will end and you have to manually restart it. Let's check out a more real-world hosting model.&lt;/p&gt;
&lt;h6&gt;Host by Nginx with auto-restart capability&lt;/h6&gt;
&lt;p&gt;First, install and start Nginx:&lt;/p&gt;
&lt;pre class="language-bash"&gt;&lt;code&gt;sudo apt-get install nginx
sudo /etc/init.d/nginx start&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Open Nginx config file:&lt;/p&gt;
&lt;pre class="language-bash"&gt;&lt;code&gt;sudo nano /etc/nginx/sites-available/default&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Replace its content with:&lt;/p&gt;
&lt;pre class="language-bash"&gt;&lt;code&gt;server {
    listen        80 default_server;
    server_name   _;
    location / {
        proxy_pass         http://localhost:5000;
        proxy_http_version 1.1;
        proxy_set_header   Upgrade $http_upgrade;
        proxy_set_header   Connection keep-alive;
        proxy_set_header   Host $host;
        proxy_cache_bypass $http_upgrade;
        proxy_set_header   X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header   X-Forwarded-Proto $scheme;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;server_name&lt;/code&gt; is set to&lt;code&gt; _;&lt;/code&gt; means I don't limit domain names, just accept any hostnames. &lt;code&gt;proxy_pass&lt;/code&gt; is the default Kestrel HTTP endpoint, which runs our ASP.NET Core website.&lt;/p&gt;
&lt;p&gt;Check and apply the config file:&lt;/p&gt;
&lt;pre class="language-bash"&gt;&lt;code&gt;sudo nginx -t
sudo nginx -s reload&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now, start your ASP.NET Core website, you will be able to access your Raspberry Pi using port 80.&lt;/p&gt;
&lt;pre class="language-bash"&gt;&lt;code&gt;dotnet Empower.dll&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src="/image/img-0805ca38-d5b6-4f0d-b39f-2aa3eae26765.png" border="0" loading="lazy"&gt;&lt;/p&gt;
&lt;p&gt;Now we have one last step to go, make &lt;code&gt;dotnet&lt;/code&gt; process auto-restart in case your code accidentally blow up. To do that, we will create a &lt;code&gt;systemd&lt;/code&gt; service.&lt;/p&gt;
&lt;pre class="language-bash"&gt;&lt;code&gt;sudo nano /etc/systemd/system/kestrel-empowerapp.service&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With the following content:&lt;/p&gt;
&lt;pre class="language-bash"&gt;&lt;code&gt;[Unit]
Description=ASP.NET Core App - Empower

[Service]
WorkingDirectory=/home/pi/dotnet-playground/empower/portable-fdd
ExecStart=/home/pi/dotnet-arm64/dotnet /home/pi/dotnet-playground/empower/portable-fdd/Empower.dll
Restart=always
# Restart service after 10 seconds if the dotnet service crashes:
RestartSec=10
KillSignal=SIGINT
SyslogIdentifier=dotnet-empower
User=pi
Environment=ASPNETCORE_ENVIRONMENT=Production
Environment=DOTNET_PRINT_TELEMETRY_MESSAGE=false

[Install]
WantedBy=multi-user.target&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Note, we can only use absolute path in &lt;code&gt;systemd&lt;/code&gt; configuation.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Register and start the service:&lt;/p&gt;
&lt;pre class="language-bash"&gt;&lt;code&gt;sudo systemctl enable kestrel-empowerapp.service
sudo systemctl start kestrel-empowerapp.service
sudo systemctl status kestrel-empowerapp.service&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src="/image/img-38efe694-a240-4c05-a084-05f3623b6cb0.png" border="0" alt="" loading="lazy"&gt;&lt;/p&gt;
&lt;p&gt;Now, you have the auto start and failure restart capability for your ASP.NET Core 3.0 application running on Raspberry Pi 4. Try to reboot your Raspberry Pi, your website will auto start! It is a web server now!&lt;/p&gt;
&lt;p&gt;For more configuration details, you can refer to Microsoft official document &lt;a href="https://docs.microsoft.com/en-us/aspnet/core/host-and-deploy/linux-nginx?WT.mc_id=AZ-MVP-5002809" target="_blank" rel="noopener"&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Want Docker?&lt;/h2&gt;
&lt;hr&gt;
&lt;p&gt;There are Microsoft official docker images supporting ARM32 on Raspberry Pi 4, if you managed to install docker on your Raspberry Pi 4, have fun :)&lt;/p&gt;
&lt;p&gt;&lt;img src="/image/img-62cb1ad1-419a-45fa-ba73-bf04ea467b17.png" border="0" loading="lazy"&gt;&lt;/p&gt;
&lt;p&gt;Reference: &lt;a href="https://www.hanselman.com/blog/InstallingTheNETCore2xSDKOnARaspberryPiAndBlinkingAnLEDWithSystemDeviceGpio.aspx" target="_blank" rel="noopener"&gt;https://www.hanselman.com/blog/InstallingTheNETCore2xSDKOnARaspberryPiAndBlinkingAnLEDWithSystemDeviceGpio.aspx&lt;/a&gt;&lt;/p&gt;
&lt;div class="ms-editor-squiggler" style="color: initial; font: initial; font-feature-settings: initial; font-kerning: initial; font-optical-sizing: initial; font-variation-settings: initial; forced-color-adjust: initial; text-orientation: initial; text-rendering: initial; -webkit-font-smoothing: initial; -webkit-locale: initial; -webkit-text-orientation: initial; -webkit-writing-mode: initial; writing-mode: initial; zoom: initial; place-content: initial; place-items: initial; place-self: initial; alignment-baseline: initial; animation: initial; appearance: initial; aspect-ratio: initial; backdrop-filter: initial; backface-visibility: initial; background: initial; background-blend-mode: initial; baseline-shift: initial; block-size: initial; border-block: initial; border: initial; border-radius: initial; border-collapse: initial; border-inline: initial; inset: initial; box-shadow: initial; box-sizing: initial; break-after: initial; break-before: initial; break-inside: initial; buffered-rendering: initial; caption-side: initial; caret-color: initial; clear: initial; clip: initial; clip-path: initial; clip-rule: initial; color-interpolation: initial; color-interpolation-filters: initial; color-rendering: initial; color-scheme: initial; columns: initial; column-fill: initial; gap: initial; column-rule: initial; column-span: initial; contain: initial; contain-intrinsic-size: initial; content: initial; content-visibility: initial; counter-increment: initial; counter-reset: initial; counter-set: initial; cursor: initial; cx: initial; cy: initial; d: initial; display: block; dominant-baseline: initial; empty-cells: initial; fill: initial; fill-opacity: initial; fill-rule: initial; filter: initial; flex: initial; flex-flow: initial; float: initial; flood-color: initial; flood-opacity: initial; grid: initial; grid-area: initial; height: initial; hyphens: initial; image-orientation: initial; image-rendering: initial; inline-size: initial; inset-block: initial; inset-inline: initial; isolation: initial; letter-spacing: initial; lighting-color: initial; line-break: initial; list-style: initial; margin-block: initial; margin: initial; margin-inline: initial; marker: initial; mask: initial; mask-type: initial; max-block-size: initial; max-height: initial; max-inline-size: initial; max-width: initial; min-block-size: initial; min-height: initial; min-inline-size: initial; min-width: initial; mix-blend-mode: initial; object-fit: initial; object-position: initial; offset: initial; opacity: initial; order: initial; origin-trial-test-property: initial; orphans: initial; outline: initial; outline-offset: initial; overflow-anchor: initial; overflow-wrap: initial; overflow: initial; overscroll-behavior-block: initial; overscroll-behavior-inline: initial; overscroll-behavior: initial; padding-block: initial; padding: initial; padding-inline: initial; page: initial; page-orientation: initial; paint-order: initial; perspective: initial; perspective-origin: initial; pointer-events: initial; position: initial; quotes: initial; r: initial; resize: initial; ruby-position: initial; rx: initial; ry: initial; scroll-behavior: initial; scroll-margin-block: initial; scroll-margin: initial; scroll-margin-inline: initial; scroll-padding-block: initial; scroll-padding: initial; scroll-padding-inline: initial; scroll-snap-align: initial; scroll-snap-stop: initial; scroll-snap-type: initial; shape-image-threshold: initial; shape-margin: initial; shape-outside: initial; shape-rendering: initial; size: initial; speak: initial; stop-color: initial; stop-opacity: initial; stroke: initial; stroke-dasharray: initial; stroke-dashoffset: initial; stroke-linecap: initial; stroke-linejoin: initial; stroke-miterlimit: initial; stroke-opacity: initial; stroke-width: initial; tab-size: initial; table-layout: initial; text-align: initial; text-align-last: initial; text-anchor: initial; text-combine-upright: initial; text-decoration: initial; text-decoration-skip-ink: initial; text-indent: initial; text-overflow: initial; text-shadow: initial; text-size-adjust: initial; text-transform: initial; text-underline-offset: initial; text-underline-position: initial; touch-action: initial; transform: initial; transform-box: initial; transform-origin: initial; transform-style: initial; transition: initial; user-select: initial; vector-effect: initial; vertical-align: initial; visibility: initial; -webkit-app-region: initial; border-spacing: initial; -webkit-border-image: initial; -webkit-box-align: initial; -webkit-box-decoration-break: initial; -webkit-box-direction: initial; -webkit-box-flex: initial; -webkit-box-ordinal-group: initial; -webkit-box-orient: initial; -webkit-box-pack: initial; -webkit-box-reflect: initial; -webkit-highlight: initial; -webkit-hyphenate-character: initial; -webkit-line-break: initial; -webkit-line-clamp: initial; -webkit-mask-box-image: initial; -webkit-mask: initial; -webkit-mask-composite: initial; -webkit-perspective-origin-x: initial; -webkit-perspective-origin-y: initial; -webkit-print-color-adjust: initial; -webkit-rtl-ordering: initial; -webkit-ruby-position: initial; -webkit-tap-highlight-color: initial; -webkit-text-combine: initial; -webkit-text-decorations-in-effect: initial; -webkit-text-emphasis: initial; -webkit-text-emphasis-position: initial; -webkit-text-fill-color: initial; -webkit-text-security: initial; -webkit-text-stroke: initial; -webkit-transform-origin-x: initial; -webkit-transform-origin-y: initial; -webkit-transform-origin-z: initial; -webkit-user-drag: initial; -webkit-user-modify: initial; white-space: initial; widows: initial; width: initial; will-change: initial; word-break: initial; word-spacing: initial; x: initial; y: initial; z-index: initial;"&gt;&amp;nbsp;&lt;/div&gt;</description><category>.NET</category><category>Internet of Things</category><guid isPermaLink="false">088fe884-ccf9-438f-986d-401478bc862e</guid><pubDate>2019-09-29T09:29:07Z</pubDate><updated xmlns="http://www.w3.org/2005/Atom">2019-09-29T09:29:07Z</updated></item><item><title>Raspberry Pi Surveillance Camera with Linux and Azure</title><link>https://edi.wang/post/2016/8/11/raspi-azure-camera</link><description>&lt;p&gt;Because Windows 10 IoT Core&amp;nbsp;is not supporting Pi Camera, I can only use Linux instead. These days, I made a surveillance camera, the functionality is simple, when a person passed by the camera, it will take a picture and upload to Microsoft Azure Storage. It also got a website to remotely view the photos.&lt;/p&gt;
&lt;h2&gt;1. Hardware&lt;/h2&gt;
&lt;hr&gt;
&lt;p&gt;A PIR Sensor, a Pi Camera, a couple of jump wires. Connect the Singal port of the PIR sensor to GPIO 04, install the camera into CSI interface on the Raspberry Pi.&lt;/p&gt;
&lt;p&gt;&lt;img src="/image/img-5930a796-bae7-4f32-8a85-8449d83fa07a.png" border="0" alt="" width="438" height="590" loading="lazy"&gt;&lt;/p&gt;
&lt;p&gt;For installing the camera, please refer to official document here:&amp;nbsp;&lt;a href="https://www.raspberrypi.org/documentation/raspbian/applications/camera.md"&gt;https://www.raspberrypi.org/documentation/raspbian/applications/camera.md&lt;/a&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2&gt;2. Working with PIR Sensor and Camera&lt;/h2&gt;
&lt;hr&gt;
&lt;p&gt;SSH into Raspbian, create a new python file:&lt;/p&gt;
&lt;pre&gt;sudo nano pircam.py&lt;/pre&gt;
&lt;p&gt;The API for the PIR&amp;nbsp;sensor is built in Raspbian system. We only need to use it directly:&lt;/p&gt;
&lt;pre&gt;from gpiozero import MotionSensor
pir = MotionSensor(4)&lt;/pre&gt;
&lt;p&gt;To test if a person is passing by the PIR sensor:&lt;/p&gt;
&lt;pre&gt;pir.wait_for_motion()&lt;/pre&gt;
&lt;p&gt;To test if no one is standing in front of the PIR Sensor:&lt;/p&gt;
&lt;pre&gt;pir.wait_for_no_motion()&lt;/pre&gt;
&lt;p&gt;The camera API is also already in place.&lt;/p&gt;
&lt;pre&gt;from picamera import PiCamera
camera = PiCamera()&lt;/pre&gt;
&lt;p&gt;To take a photo:&lt;/p&gt;
&lt;pre&gt;camera.capture(filename)&lt;/pre&gt;
&lt;p&gt;So, to take a photo when a person is passing by the PIR sensor, we could do it this way:&lt;/p&gt;
&lt;pre&gt;from gpiozero import MotionSensor
from picamera import PiCamera
from datetime import datetime

camera = PiCamera()
pir = MotionSensor(4)

while True:
    pir.wait_for_motion()
    filename = "pircam-" +  datetime.now().strftime("%Y-%m-%d_%H.%M.%S.jpg")
    camera.capture(filename)
    pir.wait_for_no_motion()&lt;/pre&gt;
&lt;p&gt;Save the python file, and execute the script:&lt;/p&gt;
&lt;pre&gt;sudo python pircam.py&lt;/pre&gt;
&lt;p&gt;If everything is good, you can cross your fingers before the PIR sensor, the camera will take a photo. You can use "ls" command to verify if the photo has been saved to disk.&lt;/p&gt;
&lt;h2&gt;3. Configuration of Azure Storage Account and How to Upload Photo&lt;/h2&gt;
&lt;hr&gt;
&lt;p&gt;Please refer to my post &lt;em&gt;&lt;a href="/post/2016/8/8/raspbian-picamera-upload-azure" target="_blank" rel="noopener noreferrer"&gt;在树莓派上使用PiCamera拍照并上传到Azure&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Finally the working code is:&lt;/p&gt;
&lt;pre&gt;from gpiozero import MotionSensor
from picamera import PiCamera
from datetime import datetime
from azure.storage.blob import BlockBlobService
from azure.storage.blob import ContentSettings

camera = PiCamera()
pir = MotionSensor(4)
block_blob_service = BlockBlobService(account_name='YOURACCOUNT', account_key='YOURKEY')

while True:
    pir.wait_for_motion()
    filename = "pircam-" +  datetime.now().strftime("%Y-%m-%d_%H.%M.%S.jpg")
    camera.capture(filename)
    pir.wait_for_no_motion()
    block_blob_service.create_blob_from_path(
    'YOURCONTAINER',
    filename,
    filename,
    content_settings=ContentSettings(content_type='image/jpeg'))&lt;/pre&gt;
&lt;p&gt;If nothing goes wrong, after execute the script, you will see the photos in your zure Portal:&lt;/p&gt;
&lt;p&gt;&lt;img src="/image/img-9fc788bc-804f-422a-b456-b4103263fb2b.png" border="0" alt="" width="590" height="497" loading="lazy"&gt;&lt;/p&gt;
&lt;h2&gt;4. Build the Website&lt;/h2&gt;
&lt;hr&gt;
&lt;p&gt;Create a new ASP.NET MVC5 project with VS2015. Naming it like "PirCamWatcher", update all nuget packages to latest version. Install Azure Storage SDK:&lt;/p&gt;
&lt;pre&gt;PM &amp;gt; Install-Package WindowsAzure.Storage&lt;/pre&gt;
&lt;p&gt;Modify web.config, add the following keys into appSettings node:&lt;/p&gt;
&lt;pre&gt;    &amp;lt;add key="StorageConnectionString" value="DefaultEndpointsProtocol=https;AccountName=YOURACCOUNT;AccountKey=YOURKEY" /&amp;gt;
    &amp;lt;add key="AzureStorageAccountContainer" value="YOURCONTAINER" /&amp;gt;
    &amp;lt;add key="AuthUserName" value="YOURUSERNAME" /&amp;gt;
    &amp;lt;add key="AuthPassword" value="YOURPASSWORD"/&amp;gt;&lt;/pre&gt;
&lt;p&gt;Please modify the values according to your own Azure envrionment.&lt;/p&gt;
&lt;p&gt;The next thing is to make an authentication module. Because we are publishing it to the Internet, we can't allow everyone to see our photos. I choose Basic Authentication because it's the most simple way to do. To get it done in ASP.NET, we need to create an HttpModule first:&lt;/p&gt;
&lt;p&gt;e.g. BasicAuthHttpModule&lt;/p&gt;
&lt;pre&gt;using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http.Headers;
using System.Security.Principal;
using System.Text;
using System.Threading;
using System.Web;
using System.Web.Configuration;

namespace PirCamWatcher.IISModules
{
    public class BasicAuthHttpModule : IHttpModule
    {
        private const string Realm = "PirCam";

        public void Init(HttpApplication context)
        {
            // Register event handlers
            context.AuthenticateRequest += OnApplicationAuthenticateRequest;
            context.EndRequest += OnApplicationEndRequest;
        }

        private static void SetPrincipal(IPrincipal principal)
        {
            Thread.CurrentPrincipal = principal;
            if (HttpContext.Current != null)
            {
                HttpContext.Current.User = principal;
            }
        }

        private static bool CheckPassword(string username, string password)
        {
            var u = WebConfigurationManager.AppSettings["AuthUserName"];
            var p = WebConfigurationManager.AppSettings["AuthPassword"];
            return username == u &amp;amp;&amp;amp; password == p;
        }

        private static void AuthenticateUser(string credentials)
        {
            try
            {
                var encoding = Encoding.GetEncoding("iso-8859-1");
                credentials = encoding.GetString(Convert.FromBase64String(credentials));

                int separator = credentials.IndexOf(':');
                string name = credentials.Substring(0, separator);
                string password = credentials.Substring(separator + 1);

                if (CheckPassword(name, password))
                {
                    var identity = new GenericIdentity(name);
                    SetPrincipal(new GenericPrincipal(identity, null));
                }
                else
                {
                    // Invalid username or password.
                    HttpContext.Current.Response.StatusCode = 401;
                }
            }
            catch (FormatException)
            {
                // Credentials were not formatted correctly.
                HttpContext.Current.Response.StatusCode = 401;
            }
        }

        private static void OnApplicationAuthenticateRequest(object sender, EventArgs e)
        {
            var request = HttpContext.Current.Request;
            var authHeader = request.Headers["Authorization"];
            if (authHeader != null)
            {
                var authHeaderVal = AuthenticationHeaderValue.Parse(authHeader);

                // RFC 2617 sec 1.2, "scheme" name is case-insensitive
                if (authHeaderVal.Scheme.Equals("basic",
                        StringComparison.OrdinalIgnoreCase) &amp;amp;&amp;amp;
                    authHeaderVal.Parameter != null)
                {
                    AuthenticateUser(authHeaderVal.Parameter);
                }
            }
        }

        // If the request was unauthorized, add the WWW-Authenticate header 
        // to the response.
        private static void OnApplicationEndRequest(object sender, EventArgs e)
        {
            var response = HttpContext.Current.Response;
            if (response.StatusCode == 401)
            {
                response.Headers.Add("WWW-Authenticate",
                    string.Format("Basic realm=\"{0}\"", Realm));
            }
        }

        public void Dispose()
        {
        }
    }
}&lt;/pre&gt;
&lt;p&gt;And then, configure it into Web.config:&lt;/p&gt;
&lt;pre&gt;  &amp;lt;system.webServer&amp;gt;
    &amp;lt;modules&amp;gt;
      &amp;lt;add name="BasicAuthHttpModule"
        type="PirCamWatcher.IISModules.BasicAuthHttpModule, PirCamWatcher"/&amp;gt;
    &amp;lt;/modules&amp;gt;
  &amp;lt;/system.webServer&amp;gt;&lt;/pre&gt;
&lt;p&gt;Open IIS, disable "Anonymous Authentication"&lt;/p&gt;
&lt;p&gt;&lt;img src="/image/img-aee9e375-cdf8-4dab-854b-cc8ec24ee20a.png" border="0" alt="" width="889" height="256" loading="lazy"&gt;&lt;/p&gt;
&lt;p&gt;Now, try to access the website, it shoud ask you for a username and password&lt;/p&gt;
&lt;p&gt;&lt;img src="/image/img-3de8fffa-45a8-4351-ba2c-76bf94946418.png" border="0" alt="" width="885" height="374" loading="lazy"&gt;&lt;/p&gt;
&lt;p&gt;Finally, because we are deploying it to Azure. We can not operate the IIS on Azure&amp;nbsp;directly, so we need a tricky way to do it:&lt;/p&gt;
&lt;p&gt;Modify Web.Release.config, add the following code:&lt;/p&gt;
&lt;pre&gt;  &amp;lt;system.webServer&amp;gt;
    &amp;lt;security xdt:Transform="Insert"&amp;gt;
      &amp;lt;authentication&amp;gt;
        &amp;lt;anonymousAuthentication enabled="false" /&amp;gt;
      &amp;lt;/authentication&amp;gt;
    &amp;lt;/security&amp;gt;
  &amp;lt;/system.webServer&amp;gt;&lt;/pre&gt;
&lt;p&gt;Open HomeController.cs, replace the code with this:&lt;/p&gt;
&lt;pre&gt;using System.Linq;
using System.Web.Configuration;
using System.Web.Mvc;
using Microsoft.WindowsAzure.Storage;
using Microsoft.WindowsAzure.Storage.Blob;

namespace PirCamWatcher.Controllers
{
    public class HomeController : Controller
    {
        public static CloudBlobContainer BlobContainer { get; set; }

        public HomeController()
        {
            BlobContainer = GetBlobContainer();
        }

        public ActionResult Index()
        {
            return View();
        }

        public JsonResult GetImages()
        {
            var listBlobProperties = (from item in BlobContainer.ListBlobs(null, false)
                                      where item.GetType() == typeof(CloudBlockBlob)
                                      select (CloudBlockBlob)item
                                      into blob
                                      select new Models.PirCamBlobProperties(blob.Properties.LastModified, blob.Uri))
                                     .OrderByDescending(p =&amp;gt; p.DateModified)
                                     .ToList();

            return Json(listBlobProperties, JsonRequestBehavior.AllowGet);
        }

        private static CloudBlobContainer GetBlobContainer()
        {
            string connectionString = WebConfigurationManager.AppSettings["StorageConnectionString"];
            CloudStorageAccount storageAccount = CloudStorageAccount.Parse(connectionString);
            CloudBlobClient blobClient = storageAccount.CreateCloudBlobClient();
            CloudBlobContainer container =
                blobClient.GetContainerReference(WebConfigurationManager.AppSettings["AzureStorageAccountContainer"]);
            return container;
        }
    }
}&lt;/pre&gt;
&lt;p&gt;Add a "PirCamBlobProperties.cs" into Models&lt;/p&gt;
&lt;pre&gt;using System;

namespace PirCamWatcher.Models
{
    public class PirCamBlobProperties
    {
        public DateTimeOffset? DateModified;

        public Uri Uri;

        public PirCamBlobProperties(DateTimeOffset? dateModified, Uri uri)
        {
            DateModified = dateModified; Uri = uri;
        }
    }
}&lt;/pre&gt;
&lt;p&gt;Open Home\Index.cshtml, replace the code with&lt;/p&gt;
&lt;pre&gt;@{
    ViewBag.Title = "Home";
}

@section scripts{
    &amp;lt;script&amp;gt;
        function showImage(uri) {
            $(".img-prev-lg").html("&amp;lt;img src='" + uri + "' class='img-responsive' /&amp;gt;");
            $("#img-modal").modal();
        }

        $(function () {
            $.getJSON("Home/GetImages",
                function (data) {
                    var items = [];
                    $.each(data, function (k, v) {
                        items.push(
                            "&amp;lt;div class='sv-img' data-imgurl='" + v.Uri + "'&amp;gt;" +
                            "&amp;lt;img src='" + v.Uri + "' class='img-responsive' /&amp;gt;" +
                            "&amp;lt;div class='sv-date'&amp;gt;" + new Date(parseInt(v.DateModified.substr(6))) + "&amp;lt;/div&amp;gt;" +
                            "&amp;lt;/div&amp;gt;");
                    });

                    $("&amp;lt;div /&amp;gt;", {
                        "class": "img-container",
                        html: items.join("")
                    }).appendTo(".gallery-display");
                }).done(function () {
                    $(".sv-img").click(function () {
                        var uri = $(this).data('imgurl');
                        showImage(uri);
                    });
                });
        });
    &amp;lt;/script&amp;gt;
}

&amp;lt;h1 class="page-header"&amp;gt;
    Azure Home Surveillance
    &amp;lt;small&amp;gt;Raspberry Pi PIR Motion Camera&amp;lt;/small&amp;gt;
&amp;lt;/h1&amp;gt;

&amp;lt;section class="gallery-display"&amp;gt;&amp;lt;/section&amp;gt;

&amp;lt;div id="img-modal" class="modal fade" tabindex="-1" role="dialog"&amp;gt;
    &amp;lt;div class="modal-dialog modal-lg" role="document"&amp;gt;
        &amp;lt;div class="modal-content img-prev-lg"&amp;gt;

        &amp;lt;/div&amp;gt;
    &amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;&lt;/pre&gt;
&lt;p&gt;Modify site.css, add code:&lt;/p&gt;
&lt;pre&gt;.sv-img {
    display: inline-block;
    width: 275px;
    margin: 0 10px 10px 0;
    border: 1px solid #CCC;
    position: relative;
    cursor: pointer;
}

.sv-date {
    color: #FFF;
    background-color: #000;
    -ms-opacity: 0.5;
    opacity: 0.5;
    position: absolute;
    bottom: 0;
    padding: 5px;
}
&lt;/pre&gt;
&lt;p&gt;Publish the website to Azure, and you are good to go!&lt;/p&gt;
&lt;p&gt;&lt;img src="/image/img-97a1095c-ec5d-444c-a138-826f4da9403b.png" border="0" alt="" width="1301" height="871" loading="lazy"&gt;&lt;/p&gt;
&lt;p&gt;The entire project is on my github:&lt;/p&gt;
&lt;p&gt;&lt;a href="https://github.com/EdiWang/RaspiCameraSurveillance-Azure"&gt;https://github.com/EdiWang/RaspiCameraSurveillance-Azure&lt;/a&gt;&amp;nbsp;&lt;/p&gt;</description><category>Internet of Things</category><category>Microsoft Azure</category><guid isPermaLink="false">122ad138-5ff2-494e-a46d-fd5e47b76fd3</guid><pubDate>2016-08-11T08:47:49Z</pubDate><updated xmlns="http://www.w3.org/2005/Atom">2016-08-11T08:47:49Z</updated></item><item><title>Uploading PiCamera images to Azure</title><link>https://edi.wang/post/2016/8/8/raspbian-picamera-upload-azure</link><description>&lt;p&gt;Recently Windows 10 IoT Core Build 14393 is coming, but still without PiCamera support, WTF. So I can only choose Linux for my project. When I was using Windows, I use C# + Azure SDK to upload photos to Microsoft Azure. So how to do it on Linux?&lt;/p&gt;
&lt;p&gt;After some research, here's how to do it.&lt;/p&gt;
&lt;p&gt;My system is Raspbian Linux. First, we need to install Azure SDK in SSH Terminal:&lt;/p&gt;
&lt;pre&gt;sudo pip install --pre azure&lt;/pre&gt;
&lt;p&gt;Remember to execute the command as "sudo". Or you will not have permission to install it.&lt;/p&gt;
&lt;p&gt;After installation, Python will support Azure. You will need an Azure storage account, such as mine, called "edirpi3", with a container named "webcamupload" in it.&lt;/p&gt;
&lt;p&gt;&lt;img src="/image/img-03efaafa-8eb0-42e2-beca-f593b6e1d25d.png" border="0" alt="" width="590" height="278" loading="lazy"&gt;&lt;/p&gt;
&lt;p&gt;Then, create a new py file:&lt;/p&gt;
&lt;pre&gt;sudo nano testazureblob.py&lt;/pre&gt;
&lt;p&gt;Acccessing Azure Container from python:&lt;/p&gt;
&lt;pre&gt;from azure.storage.blob import BlockBlobService
block_blob_service = BlockBlobService(account_name='edirpi3', account_key='YOURKEY')&lt;/pre&gt;
&lt;p&gt;the "account_name" and "account_key" is in your azure portal:&lt;/p&gt;
&lt;p&gt;&lt;img src="/image/img-32209a65-e6cb-4e06-8acd-bd09b2f1779a.png" border="0" alt="" width="1173" height="581" loading="lazy"&gt;&lt;/p&gt;
&lt;p&gt;To upload file:&lt;/p&gt;
&lt;pre&gt;from azure.storage.blob import ContentSettings
block_blob_service.create_blob_from_path(
    'webcamupload',
    'firstblood.jpg',
    'image1.jpg',
    content_settings=ContentSettings(content_type='image/jpeg'))&lt;/pre&gt;
&lt;p&gt;To take picture:&lt;/p&gt;
&lt;pre&gt;import picamera

camera = picamera.PiCamera()
camera.capture('image1.jpg')
&lt;/pre&gt;
&lt;p&gt;Integrate those two functions, we get the code for upload picture to Azure Blob:&lt;/p&gt;
&lt;pre&gt;from azure.storage.blob import BlockBlobService
from azure.storage.blob import ContentSettings
import picamera

camera = picamera.PiCamera()
camera.capture('image1.jpg')

block_blob_service = BlockBlobService(account_name='edirpi3', account_key='YOURKEY')

block_blob_service.create_blob_from_path(
    'webcamupload',
    'firstblood.jpg',
    'image1.jpg',
    content_settings=ContentSettings(content_type='image/jpeg'))&lt;/pre&gt;
&lt;p&gt;&lt;img src="/image/img-8313b99a-2379-4392-bcd0-317f5dadd414.png" border="0" alt="" width="759" height="443" loading="lazy"&gt;&lt;/p&gt;
&lt;p&gt;Execute python file with "sudo python":&lt;/p&gt;
&lt;p&gt;&lt;img src="/image/img-756d55b9-abef-4138-8d26-17afe90e5906.png" border="0" alt="" width="759" height="443" loading="lazy"&gt;&lt;/p&gt;
&lt;p&gt;If everything is good, you will be able to see the picture in Azure portal:&lt;/p&gt;
&lt;p&gt;&lt;img src="/image/img-eef19396-cab8-4a4c-8267-3f0a4301d63f.png" border="0" alt="" width="936" height="341" loading="lazy"&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src="/image/img-f1173800-b07c-4a08-a7ae-a03d0abd6b57.png" border="0" alt="" width="1629" height="835" loading="lazy"&gt;&lt;/p&gt;</description><category>Internet of Things</category><guid isPermaLink="false">0d5064e4-0862-4f0a-909c-aacfe9104b6d</guid><pubDate>2016-08-08T12:15:14Z</pubDate><updated xmlns="http://www.w3.org/2005/Atom">2016-08-08T12:15:14Z</updated></item><item><title>Raspberry Pi 3 and Windows 10 IoT Core Basics</title><link>https://edi.wang/post/2016/8/7/raspberry-pi-3-windows-10-iot-basic</link><description>&lt;p&gt;最近有时间，总结了一下关于树莓派3及Windows 10 IoT的基础知识。适合没有接触过硬件但有一定编程经验的读者入门。我自己刚刚买树莓派的时候花了很长时间入门，搞清楚各种概念和属于。很多网上的帖子都是分散在各个角落的，并且是英文的，有些还是过时的，有些是爆的，所以我觉得有必要总结一篇基础入门帖子。&lt;strong&gt;这篇文章讲的都是抛砖引玉的概念，并不包括End-To-End的Case，想要做一个完整实验的文章，请看我博客的物联网开发系列文章：&lt;a href="/category/list/iot"&gt;http://edi.wang/category/list/iot&lt;/a&gt;&amp;nbsp;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;如果有错误和补充，欢迎在评论里指出。&lt;/p&gt;
&lt;h2&gt;一、树莓派3&lt;/h2&gt;
&lt;hr&gt;
&lt;p&gt;树莓派是基于ARM芯片的卡片电脑，大小和一张信用卡一样。今年推出了第三代，集成了板载蓝牙和WIFI。之前的树莓派1和2都是需要用USB WIFI和蓝牙实现的，所以你要是购买树莓派，买3是最方便的，可以省去USB WIFI和蓝牙的配件。并且Windows 10 IoT的最新版已经支持了树莓派3的板载WIFI和蓝牙，使用没有问题。&lt;/p&gt;
&lt;p&gt;物联网开发板有好几种，树莓派只是其中一种比较流行的。我也推荐使用树莓派入门，毕竟资料和周边配件都是很全的。&lt;/p&gt;
&lt;p&gt;树莓派3相对2有如下改进：&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;A 1.2GHz 64-bit quad-core ARMv8 CPU&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;802.11n Wireless LAN&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Bluetooth 4.1&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Bluetooth Low Energy (BLE)&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;1GB RAM&lt;/p&gt;
&lt;p&gt;4 USB ports&lt;/p&gt;
&lt;p&gt;40 GPIO pins&lt;/p&gt;
&lt;p&gt;Full HDMI port&lt;/p&gt;
&lt;p&gt;Ethernet port&lt;/p&gt;
&lt;p&gt;Combined 3.5mm audio jack and composite video&lt;/p&gt;
&lt;p&gt;Camera interface (CSI)&lt;/p&gt;
&lt;p&gt;Display interface (DSI)&lt;/p&gt;
&lt;p&gt;Micro SD card slot (now push-pull rather than push-push)&lt;/p&gt;
&lt;p&gt;VideoCore IV 3D graphics core&lt;/p&gt;
&lt;p&gt;&lt;img src="/image/img-9d772f0a-6b73-4eef-8298-6acbd8ae97d2.png" border="0" alt="" width="600" height="390" loading="lazy"&gt;&lt;/p&gt;
&lt;p&gt;购买价格大概在200-300之间，不同代理商价格不一样。买回来的产品只含有一块主板和说明书，不包括SD卡。关于SD卡的选购文章后面会有讲解。SD卡的作用就相当于树莓派这台电脑的硬盘，操作系统必须烧录在SD卡里才能启动树莓派。你可以简单的理解为，你买的树莓派就是个不带硬盘的电脑主机，当然也不包括显示器。&lt;/p&gt;
&lt;p&gt;树莓派3的接口布局如下：&lt;/p&gt;
&lt;p&gt;&lt;img src="/image/img-a2604e30-2ba2-44a7-ae27-7bd2fc78731c.png" border="0" alt="" width="1166" height="656" loading="lazy"&gt;&lt;/p&gt;
&lt;p&gt;其中，USB是2.0的，因为成本问题，树莓派3不能卖的太贵，用不上3.0。CSI接口是树莓派专用的摄像头接口，你也可以使用USB摄像头。但是USB摄像头的帧率只有5fps，CSI可以达到30fps。同样，Display也是树莓派专用的显示器接口。然而，这个接口所搭配的显示器需要占用GPIO端口，我们针对树莓派上所连接的硬件编程就是通过GPIO的，所以并不推荐选购这个接口的屏幕，比如官方的。树莓派有HDMI接口，推荐使用HDMI，不会占用GPIO。如果要触控，有些HDMI屏幕可以通过USB实现触控，只会占用一个USB端口。&lt;/p&gt;
&lt;p&gt;网线接口插入的是标准网线（RJ-45），为什么我们有WIFI了还要网线呢，一是因为操作系统第一次初始化的时候并不知道你的WIFI SID和账号密码，需要用户去指定，然而物联网开发的很多场景下你并不能直接像操作电脑一样操作树莓派，通常是远程通过IP操作，所以必须用有线网络。插入网线就能获得局域网IP。另一个原因是有线网络的速度远大于WIFI，对调试程序比较有利。&lt;/p&gt;
&lt;h2&gt;二、面包板的使用&lt;/h2&gt;
&lt;hr&gt;
&lt;p&gt;面包板是用来给你插拔电子元件的，可以随便购买，因为都是通用的，不是树莓派专用的配件。做任何电子实验都一样。一般买来的面包板长这样：&lt;/p&gt;
&lt;p&gt;&lt;img src="/image/img-4f558d26-57d6-42f1-856f-6c3db12749ae.png" border="0" alt="" width="1756" height="637" loading="lazy"&gt;&lt;/p&gt;
&lt;p&gt;其中，最上面和最下面的两对红蓝线，一般做电源线使用，这两条线背后的孔是&lt;strong&gt;水平联通&lt;/strong&gt;的，一般都是从他们引出电源的正极和负极给板子中间的洞洞眼使用。而中间的洞洞（A-E, F-J）的背后是&lt;strong&gt;垂直联通&lt;/strong&gt;的，这个一定要注意，不然可能会插爆元件，造成短路。&lt;/p&gt;
&lt;p&gt;正确的电路示意图如下：&lt;/p&gt;
&lt;p&gt;&lt;img src="/image/img-318b787f-2d6a-4bbd-9576-9fdfa8170020.png" border="0" alt="" width="925" height="720" loading="lazy"&gt;&lt;/p&gt;
&lt;p&gt;其中，绿色覆盖的洞洞表示通电的孔，可以看到，底端是水平的，而A-E是垂直的。&lt;/p&gt;
&lt;p&gt;面包板有好几种规格，根据不同的场合购买不同的面包板。推荐大家买一块Full size的，几块mini的，基本就够用了。&lt;/p&gt;
&lt;p&gt;&lt;img src="/image/img-d640afeb-9238-4c9b-bdff-3176c093da6a.png" border="0" alt="" width="1544" height="860" loading="lazy"&gt;&lt;/p&gt;
&lt;p&gt;如果你不清楚面包板的排线规律，建议先用干电池和LED做联通实验，不要直接接入树莓派。如果短路，树莓派默认是会重启系统的，但电流过大也很有可能损坏树莓派。&lt;/p&gt;
&lt;p&gt;面包板的背面是3M贴纸，用来把面包板固定在实验台上。&lt;/p&gt;
&lt;h2&gt;三、杜邦线&lt;/h2&gt;
&lt;hr&gt;
&lt;p&gt;用来连接树莓派和电子元件的电线叫做杜邦线，长这样：&lt;/p&gt;
&lt;p&gt;&lt;img src="/image/img-ebfe898e-14c5-43c4-accf-f013462d6e5e.png" border="0" alt="" width="997" height="997" loading="lazy"&gt;&lt;/p&gt;
&lt;p&gt;根据两端接口的不同，分为三种：&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;公对公&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;公对母&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;母对母&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;图上就是公对母的线。公的意思就是插别人的，母的意思就是被人插的，所以一看图就明白了。在平时的实验中基本这三种线都要用到，所以建议一次性购买。&lt;/p&gt;
&lt;h2&gt;四、 树莓派3的GPIO端口&lt;/h2&gt;
&lt;hr&gt;
&lt;p&gt;GPIO的意思是General Purpose Input Output （通用输入/输出），可以插人，也可以被插。我们做物联网开发使用的传感器都是通过GPIO操作的。树莓派3的GPIO布局如下：&lt;/p&gt;
&lt;p&gt;&lt;img src="/image/img-de85ebba-f7e2-41bf-ac7a-e181d5799ede.png" border="0" alt="" width="880" height="918" loading="lazy"&gt;&lt;/p&gt;
&lt;p&gt;对应主板上的布局如下：&lt;/p&gt;
&lt;p&gt;&lt;img src="/image/img-337daf27-7699-4ddf-9298-9c202cb1166a.png" border="0" alt="" width="801" height="548" loading="lazy"&gt;&lt;/p&gt;
&lt;p&gt;其中，3.3V，5V表示电源输出的正级，这些端口不能通过编程操作，只是简单的供电。GND是电源负极。其他端口都可以通过编程操作。GPIO是通用端口，SPI和I2C是特殊端口，操作方式和GPIO不一样。稍后会介绍。&lt;/p&gt;
&lt;p&gt;所以，如果要通过GPIO点亮一个LED灯泡，在不要求程序的情况下，可以直接接入3.3V输出及GND，也就相当于接了干电池：&lt;/p&gt;
&lt;p&gt;&lt;img src="/image/img-5573d457-9a8c-45ce-bd51-adb98f851291.png" border="0" alt="" width="1289" height="818" loading="lazy"&gt;&lt;/p&gt;
&lt;p&gt;如果要通过编程方式操作灯泡的开关，就得先了解GPIO的操作方式。GPIO模式分为输入和输出，值是高电平和低电平。比如，你的LED负极接入了GND，那么你要点亮LED，就必须给它的正极通电对吧？那么对于LED来说，正极是输入电流的，所以对应的GPIO端口应该是输出，GPIO值是高电平，这样就能点亮LED。这里我用的是通俗易懂的说法，并不严谨，如果你有电路基本知识就知道不是这样的。。。。&lt;/p&gt;
&lt;p&gt;简单粗暴的理解，就是，GPIO High表示电源正极，GPIO Low表示电源负极。你买的传感器都有说明，哪个针脚输入高电平会产生什么操作，传感器遇到什么情况会在哪个针脚输出低或者高电平等等，这就是物联网开发的基本原理了。&lt;/p&gt;
&lt;p&gt;&lt;img src="/image/img-1fd1e157-f340-44fb-ab78-0e8084105823.png" border="0" alt="" width="1087" height="847" loading="lazy"&gt;&lt;/p&gt;
&lt;h2&gt;五、电子元件和传感器&lt;/h2&gt;
&lt;hr&gt;
&lt;p&gt;树莓派的外设和电脑一样，可以简单分为输入设备和输出设备。比如，LED灯泡、蜂鸣器、电机、电机驱动板就属于输出设备，那么他们对应的GPIO都应该用Output Mode。而温度湿度传感器、人体红外传感器、光线强度、声音、酒精等就属于输入设备，那么他们对应的GPIO就应该用Input Mode。&lt;/p&gt;
&lt;p&gt;不同厂家生产的传感器定义不一样。比如蜂鸣器，通常有3个针脚，VCC代表正极，接树莓派的DC 3.3V，GND接GND，还有个Signal针脚，就接GPIO，等待输入信号。&lt;/p&gt;
&lt;p&gt;有的蜂鸣器是低电平触发的，所以只有GPIO输出Low才会响。有些是高电平触发的，所以GPIO得输出High才会响。至于怎么用代码操作GPIO后面会讲解。&lt;/p&gt;
&lt;p&gt;而对于输入设备，比如声音传感器，在遇到声音时候，有些厂家的输出低电平，有些输出高电平。那么在代码里，就得检测对应GPIO端口有没有输入，然后进行逻辑操作。&lt;/p&gt;
&lt;p&gt;总之，传感器都不一样，购买和使用必须仔细阅读说明，不然可能会损坏传感器或树莓派。尤其是对电压有要求的传感器，比如HC-SR04这种，输入5V输出5V的，GPIO只能接受3.3v，就必须加电阻分压才能使用。&lt;/p&gt;
&lt;p&gt;&lt;img src="/image/img-2254ffc9-11cc-4487-a2ce-890aeecd24f6.png" border="0" alt="" width="1588" height="872" loading="lazy"&gt;&lt;/p&gt;
&lt;p&gt;推荐大家购买传感器的同时，&lt;strong&gt;买一个常用电阻包&lt;/strong&gt;，某宝有很多打包好的，不同阻值的电阻，几百个，才几块钱，足够用了。&lt;/p&gt;
&lt;h2&gt;六、Windows 10 IoT Core&lt;/h2&gt;
&lt;hr&gt;
&lt;p&gt;树莓派的官方操作系统是Linux的一个分支，叫做Raspbian。作为软狗，我们怎么可以用Linux？所以必须用Windows 10来装逼。在装逼之前，首先得保证你的SD卡能承受这个逼格。据我亲测，Windows 10 IoT兼容最好的，是三星的EVO和EVO+系列SD卡：&lt;/p&gt;
&lt;p&gt;&lt;img src="/image/img-3e719e54-e2e8-4483-b324-b2e117fa456b.png" border="0" alt="" width="828" height="300" loading="lazy"&gt;&lt;/p&gt;
&lt;p&gt;32G，64G都是可以的，推荐大家买32G的，便宜不少也足够用。&lt;/p&gt;
&lt;p&gt;然后你需要做这些步骤：&lt;/p&gt;
&lt;p style="margin: 0in; font-size: 11.0pt;"&gt;&lt;strong&gt;&lt;span lang="zh-CN" style="font-family: 微软雅黑;"&gt;下载&lt;/span&gt;&lt;span lang="en-US" style="font-family: Calibri;"&gt;Windows 10 IOT Dashboard&lt;/span&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;p style="margin: 0in; font-family: Calibri; font-size: 11.0pt;"&gt;&lt;a href="https://developer.microsoft.com/en-us/windows/iot/Downloads.htm"&gt;https://developer.microsoft.com/en-us/windows/iot/Downloads.htm&lt;/a&gt;&lt;/p&gt;
&lt;p style="margin: 0in; font-family: Calibri; font-size: 11.0pt;"&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style="margin: 0in; font-size: 11.0pt;"&gt;&lt;strong&gt;&lt;span lang="zh-CN" style="font-family: 微软雅黑;"&gt;下载&lt;/span&gt;&lt;span lang="en-US" style="font-family: Calibri;"&gt;Windows:&lt;/span&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;p style="margin: 0in; font-family: Calibri; font-size: 11.0pt;"&gt;&lt;a href="http://ms-iot.github.io/content/en-US/win10/GetStarted/SetUpYourDeviceManually.htm"&gt;http://ms-iot.github.io/content/en-US/win10/GetStarted/SetUpYourDeviceManually.htm&lt;/a&gt;&lt;/p&gt;
&lt;p style="margin: 0in; font-family: Calibri; font-size: 11.0pt;"&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style="margin: 0in; font-family: Calibri; font-size: 11.0pt;"&gt;&lt;strong&gt;如果你需要看Release Note:&lt;/strong&gt;&lt;/p&gt;
&lt;p lang="zh-CN" style="margin: 0in; font-family: Calibri; font-size: 11.0pt;"&gt;&lt;a href="https://developer.microsoft.com/en-us/windows/iot/docs/releasenotesinsiderpreview"&gt;https://developer.microsoft.com/en-us/windows/iot/docs/releasenotesinsiderpreview&lt;/a&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;之后，就用IOT Dashboard把Windows刷入SD卡，这个官网下载页面有非常详细的图文介绍，我就不多说了。&lt;/p&gt;
&lt;p&gt;&lt;img src="/image/img-cf9a63a2-d81b-42f5-8c1b-4bd8c9a399f3.png" border="0" alt="" width="822" height="678" loading="lazy"&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src="/image/img-75c21581-6722-477f-a1b7-e1045357645c.png" border="0" alt="" width="570" height="249" loading="lazy"&gt;&lt;/p&gt;
&lt;p&gt;需要指出的是，刷完SD卡，根目录里会有个config.txt，这个是树莓派专用的，不是Windows的文件，所以得按照官网的文档来配置。文档在这里：&lt;/p&gt;
&lt;p lang="x-none"&gt;&lt;a href="https://www.raspberrypi.org/documentation/configuration/config-txt.md"&gt;https://www.raspberrypi.org/documentation/configuration/config-txt.md&lt;/a&gt;&lt;/p&gt;
&lt;p lang="x-none"&gt;默认情况下不需要改任何东西，一般只有你的屏幕和Windows 10不兼容才要看着改。比如我用的微雪7寸屏，就需要改成：&lt;/p&gt;
&lt;pre&gt;max_usb_current=1
hdmi_group=2
hdmi_mode=1
hdmi_mode=87
hdmi_cvt 1024 600 60 6 0 0 0&lt;/pre&gt;
&lt;p&gt;不然Windows就会花瓶。。。&lt;/p&gt;
&lt;p&gt;第一次启动Windows后，插入网线，然后打开电脑浏览器，输入树莓派的IP地址(可以在树莓派的屏幕上找到)：&lt;strong&gt;http://树莓派IP:8080&amp;nbsp;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;然后用默认账号登陆：&lt;strong&gt;Administrator&lt;/strong&gt;, &lt;strong&gt;p@ssw0rd&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;第一次登录后记得改密码，不然局域网里任何人都能操作你的树莓派了。&lt;/p&gt;
&lt;h2&gt;七、写代码&lt;/h2&gt;
&lt;hr&gt;
&lt;p&gt;Windows 10 IoT Core上跑的是UWP的应用程序，所以API和UWP的手机、PC开发完全一致。所以，你需要的工具依旧是VS2015+Windows 10 PC。&lt;/p&gt;
&lt;p&gt;唯一的差别就是得添加Windows IoT Extensions for the UWP的引用，因为操作GPIO的API都在这个里面。。。&lt;/p&gt;
&lt;p&gt;&lt;img src="/image/img-f9d5cd31-7d9b-452f-9041-3a2280c1d3b4.png" border="0" alt="" width="1365" height="743" loading="lazy"&gt;&lt;/p&gt;
&lt;p&gt;针对GPIO操作的样例代码如下：&lt;/p&gt;
&lt;pre&gt;using Windows.Devices.Gpio;
 
public void GPIO()
{
    // Get the default GPIO controller on the system
    GpioController gpio = GpioController.GetDefault();
    if (gpio == null)
        return; // GPIO not available on this system
 
    // Open GPIO 5
    using (GpioPin pin = gpio.OpenPin(5))
    {
        // Latch HIGH value first. This ensures a default value when the pin is set as output
        pin.Write(GpioPinValue.High);
     
        // Set the IO direction as output
        pin.SetDriveMode(GpioPinDriveMode.Output);
 
    } // Close pin - will revert to its power-on state 
}&lt;/pre&gt;
&lt;p&gt;这段代码就是向GPIO 05端口输出高电平的代码。如果你有个LED的正极是接在GPIO 05上的，负极接在GND上，那么这段代码是可以点亮你的LED的。&lt;/p&gt;
&lt;p&gt;部署UWP应用到树莓派的方式一般是通过网络，VS可以自动部署到Remote Machine，或者用Windows 10 IoT的网页portal上传appx部署。这些微软物联网开发官网都有非常详细和友好的介绍，请看：&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;a href="http://windowsondevices.com/" target="_blank" rel="noopener noreferrer"&gt;http://windowsondevices.com/&lt;/a&gt;&amp;nbsp;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;微软官方有个很好的例子库，在这里，强烈推荐大家学习参考：&lt;/p&gt;
&lt;p style="margin: 0in; font-family: Calibri; font-size: 11.0pt;"&gt;&lt;a href="https://github.com/ms-iot/samples" target="_blank" rel="noopener noreferrer"&gt;https://github.com/ms-iot/samples&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;八、其他&lt;/h2&gt;
&lt;hr&gt;
&lt;p&gt;&lt;strong&gt;树莓派壳的选购&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;有一点要注意：壳子的高度是否兼容散热片，如果你买了散热片，有些compact的盒子盖不上的，会爆。最好选0.5mm高度的散热片，基本能兼容市面上的任何外壳。&lt;/p&gt;
&lt;p&gt;对于散热，大部分情况根本用不到风扇，散热片足够了。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;你还可能需要的工具和配件有：&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;电烙铁、焊锡、焊锡膏、老虎钳、美工刀、迷你手电钻、元件收纳盒、实验台垫板、M2.5（树莓派主板的孔径是M2.5）, M3（大部分传感器的孔径是M3）铜柱及螺丝螺母、木板、有机玻璃板等等，这些都是常用的做物联网产品的东西，建议一次性购买。&lt;/p&gt;</description><category>Internet of Things</category><guid isPermaLink="false">8fccb47b-dc32-4926-afa9-a695372b9440</guid><pubDate>2016-08-07T07:46:43Z</pubDate><updated xmlns="http://www.w3.org/2005/Atom">2016-08-07T07:46:43Z</updated></item><item><title>How to use Windows RDP to connect Raspbian Linux</title><link>https://edi.wang/post/2016/8/6/rdp-into-raspbian</link><description>&lt;p&gt;Because Windows 10 IoT Core&amp;nbsp;is not yet ready to replace Raspbian, in some case, we have to use Linux. The remote operation on Linux&amp;nbsp;is usually done by SSH. If you want remote desktop like the one on Windows. There are two options: tightvnc or xrdp. I used to &amp;nbsp;use tightvnc, it does not have any problem with daily use. But I have to install a tightvnc client on my PC. However, XRDP can be supported by Windows MSTSC. So I tried XRDP today, it's easy to setup.&lt;/p&gt;
&lt;p&gt;First, SSH into your Raspberry Pi. Execute Command:&lt;/p&gt;
&lt;pre class="language-bash"&gt;&lt;code&gt;sudo apt-get -y install xrdp&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src="/image/img-10d26ff9-5ad8-4837-994a-6f662c62eb61.png" border="0" alt="" width="670" height="460" loading="lazy"&gt;&lt;/p&gt;
&lt;p&gt;After you are done, there is one more option, to resolve the IP by hostname using "ping" command. To make Windows CMD pingable to your Raspberry Pi, install this package:&lt;/p&gt;
&lt;pre class="language-bash"&gt;&lt;code&gt;sudo apt-get -y install samba&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It will take some time to install.&lt;/p&gt;
&lt;p&gt;After it's done, you can ping your Raspberry Pi from a Windows machine:&lt;/p&gt;
&lt;p&gt;&lt;img src="/image/img-6217c71a-63b0-4569-81c4-273d450df338.png" border="0" alt="" width="556" height="349" loading="lazy"&gt;&lt;/p&gt;
&lt;p&gt;Now, try mstsc on Windows PC, you will be able to connect by IP or hostname.&lt;/p&gt;
&lt;p&gt;&lt;img src="/image/img-0172c548-3d0a-4e70-acb7-c94ad0e3e8d7.png" border="0" alt="" width="407" height="253" loading="lazy"&gt;&lt;/p&gt;
&lt;p&gt;Use your linux credential to login.&lt;/p&gt;
&lt;p&gt;&lt;img src="/image/img-0bccd9a0-1437-4097-b27c-76f624779f44.png" border="0" alt="" width="786" height="593" loading="lazy"&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src="/image/img-c26c49f5-c7b3-4f5f-905e-993a22d7d8c5.png" border="0" alt="" width="786" height="593" loading="lazy"&gt;&lt;/p&gt;</description><category>Internet of Things</category><guid isPermaLink="false">cf4e92f5-93ed-46fb-b9de-e113cb610c0b</guid><pubDate>2016-08-06T08:01:36Z</pubDate><updated xmlns="http://www.w3.org/2005/Atom">2016-08-06T08:01:36Z</updated></item><item><title>Windows 10 IoT: How to Drive an SSD1306 I2C Display</title><link>https://edi.wang/post/2016/8/2/windows-10-iot-ssd-1306-i2c-display</link><description>&lt;p&gt;I bought a 128x64 0.96 inch OLED display, like this one:&lt;/p&gt;
&lt;p&gt;&lt;img src="/image/img-faf18876-3211-4f11-81ad-50ae5c8b30b7.png" border="0" alt="" width="735" height="381" loading="lazy"&gt;&lt;/p&gt;
&lt;p&gt;The driver chip for this screen is called SSD 1306, typically, this chip will have 2 layouts, SPI or I2C. However, the examples&amp;nbsp;fo how to drive I2C with SSD 1306&amp;nbsp;is very rare, and they run on Raspbian not Windows. Luckly, with the help from a Microsoft Staff in US, we finally got the code to run the screen on Windows 10 IoT Core.&lt;/p&gt;
&lt;h2&gt;1. Physical Connection&lt;/h2&gt;
&lt;hr&gt;
&lt;p&gt;Just like any other I2C device, connect GND to ground, VCC to DC 3.3V, SCL to clock, SDA to data.&lt;/p&gt;
&lt;p&gt;&lt;img src="/image/img-cd580c76-aa75-4be5-897e-51e35119bd67.png" border="0" alt="" width="1647" height="990" loading="lazy"&gt;&lt;/p&gt;
&lt;h2&gt;2. Coding&lt;/h2&gt;
&lt;hr&gt;
&lt;p&gt;The official sample gallery from Microsoft is only supporting SPI&amp;nbsp;by SSD1306. The I2C version is here, but never merged to master branch, I don't know what's the hold up.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://github.com/stefangordon/IoTCore-SSD1306-Driver" target="_blank" rel="noopener noreferrer"&gt;https://github.com/stefangordon/IoTCore-SSD1306-Driver&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Just download and deploy to your Raspberry Pi.&lt;/p&gt;
&lt;p&gt;This project is an UWP&amp;nbsp;background app, so there will be no UI when the app is running. VS won't attach either, but the screen will work. The entrance point of the program is "StartupTask.cs"&lt;/p&gt;
&lt;p&gt;To control the display, use "DisplayManager.cs"&lt;/p&gt;
&lt;p&gt;The 128x64 LED&amp;nbsp;can display 4 lines of text, use this method to output the text:&lt;/p&gt;
&lt;pre&gt;public void WriteLineDisplayBuf(String Line, UInt32 Col, UInt32 Row)&lt;/pre&gt;
&lt;p&gt;Row&amp;nbsp;range is between 0-3, which match line 1-4 on the screen. If you want to align text to left, set "Col" to 0. Line is the content of the text, which is type of string.&lt;/p&gt;
&lt;p&gt;To display an image, use this method:&lt;/p&gt;
&lt;pre&gt;public UInt32 WriteImageDisplayBuf(DisplayImage img, UInt32 Col, UInt32 Row)&lt;/pre&gt;
&lt;p&gt;The default example in the project is to display a "WIFI Connected" picture on line 1, "Hello" on line 2, "World" on line 3, so the code is:&lt;/p&gt;
&lt;pre&gt;        static void DrawBody()
        {
            // Row 0, and image
            display.WriteImageDisplayBuf(DisplayImages.Connected, 0, 0);

            // Row 1 - 3
            display.WriteLineDisplayBuf("Hello", 0, 1);
            display.WriteLineDisplayBuf("World", 0, 2);
        }     &lt;/pre&gt;
&lt;h2&gt;3. Running the Program&lt;/h2&gt;
&lt;hr&gt;
&lt;p&gt;The result is like this&lt;/p&gt;
&lt;p&gt;&lt;img src="/image/img-48c4be12-8397-4a10-b96c-725edeb00379.jpg" border="0" alt="" width="1280" height="720" loading="lazy"&gt;&lt;/p&gt;</description><category>Internet of Things</category><guid isPermaLink="false">dc6d5293-3607-4653-bcdb-89ec7012a514</guid><pubDate>2016-08-02T06:05:05Z</pubDate><updated xmlns="http://www.w3.org/2005/Atom">2016-08-02T06:05:05Z</updated></item><item><title>315MHz Remote Door Control by Windows 10 IoT</title><link>https://edi.wang/post/2016/7/21/windows-10-iot-remote-door-control</link><description>&lt;p&gt;In the past few days, I have implemented a device that mimics the remote control door of a garage with a Raspberry Pi and share it with you.&lt;/p&gt;
&lt;h2&gt;1. Remote control module&lt;/h2&gt;
&lt;hr&gt;
&lt;p&gt;First of all, I will introduce the remote control and receiver board I use, this kind of remote control module can be bought everywhere in China, and it basically looks like this. Many roller shutter doors and garage doors use this remote control module. For radio experiments, the non-locking version is purchased, so note that it is best to purchase the antenna at the same time. The antenna is a 50 ohm single-core wire, in fact, it is a copper wire, pay attention to the frequency when buying, the radio license frequency in Chinese mainland is 315MHz, and 443MHz is also used abroad, and the remote control I bought is 315MHz. Note that the transmitting frequency and receiving frequency of the remote control should match. The remote control receiver chip is PT2272-M4, M4 means jog mode, that is, press and hold the remote control button to output a high level, release and stop the output. The ANT side of the remote control board is welded with the antenna in a vertical upward direction (relative to your device).&lt;/p&gt;
&lt;p&gt;&lt;img src="/image/img-4bdaabb5-2361-445c-aafd-3985008a26a7.jpg" border="0" alt="" width="1024" height="828" loading="lazy"&gt;&lt;/p&gt;
&lt;p&gt;There are 7 interfaces on the remote control board:&lt;/p&gt;
&lt;table&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td width="277"&gt;
&lt;p&gt;&lt;strong&gt;PIN&lt;/strong&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td width="277"&gt;
&lt;p&gt;&lt;strong&gt;Description&lt;/strong&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td width="277"&gt;
&lt;p&gt;GND&lt;/p&gt;
&lt;/td&gt;
&lt;td width="277"&gt;
&lt;p&gt;Ground&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td width="277"&gt;
&lt;p&gt;5V&lt;/p&gt;
&lt;/td&gt;
&lt;td width="277"&gt;
&lt;p&gt;5V power input&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td width="277"&gt;
&lt;p&gt;D0&lt;/p&gt;
&lt;/td&gt;
&lt;td width="277"&gt;
&lt;p&gt;Correspondence remote control buttons&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td width="277"&gt;
&lt;p&gt;D1&lt;/p&gt;
&lt;/td&gt;
&lt;td width="277"&gt;
&lt;p&gt;Correspondence remote control buttons&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td width="277"&gt;
&lt;p&gt;D2&lt;/p&gt;
&lt;/td&gt;
&lt;td width="277"&gt;
&lt;p&gt;Correspondence remote control buttons&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td width="277"&gt;
&lt;p&gt;D3&lt;/p&gt;
&lt;/td&gt;
&lt;td width="277"&gt;
&lt;p&gt;Correspondence remote control buttons&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td width="277"&gt;
&lt;p&gt;VT&lt;/p&gt;
&lt;/td&gt;
&lt;td width="277"&gt;
&lt;p&gt;Effective signal output&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Among them, D0-D3 corresponds to the four buttons A, B, C, D on the remote control. As for which is which, the module of each model is different, and you need to experiment by yourself. VT outputs high when there is a signal in D0-D3. That is, press any button on the remote control, and the VT will output.&lt;/p&gt;
&lt;p&gt;It is important to note that the voltages of D0-D3, as well as VT outputs, are all 5V. However, the GPIO port of the Raspberry Pi receives a voltage of 3.3V, so it is necessary to divide the voltage to 3.3V with 2 resistors according to the voltage distribution law.&lt;/p&gt;
&lt;p&gt;The law of voltage distribution refers to the fact that in a series circuit, the voltage distributed to each load is proportional to its impedance. The circuit implemented according to this principle is also called a voltage divider.&lt;/p&gt;
&lt;p&gt;&lt;img src="/image/img-efa9bd6f-e5f4-40f5-90fd-be6dd0e338c3.png" border="0" alt="" width="178" height="168" loading="lazy"&gt;&lt;/p&gt;
&lt;p&gt;In a series circuit with only two impedances, the voltage distribution law formulas as follows:&lt;/p&gt;
&lt;p&gt;&lt;img src="/image/img-12a3cd89-fa5d-4bc1-a480-0c73356d40bb.png" border="0" alt="" width="165" height="45" loading="lazy"&gt;&lt;/p&gt;
&lt;p&gt;So, I'm taking a 4.7K resistor and a 10K resistor, substituting the formula:&lt;/p&gt;
&lt;p&gt;Vout = 5 x (10000 / (4700 + 10000))&lt;/p&gt;
&lt;p&gt;It is equivalent to about 3.4v, which is acceptable for a Raspberry Pi.&lt;/p&gt;
&lt;h2&gt;2. Stepper motor&lt;/h2&gt;
&lt;hr&gt;
&lt;p&gt;The stepper motor driver board I use is ULN2003, the stepper motor model is 28BYJ-48, and I have written an article on the blog before that ULN2003 drive the stepper motor, you can &lt;a href="/post/2016/4/4/windows-10-iot-stepper-motor" target="_blank" rel="noopener"&gt;click here to read&lt;/a&gt;. So I won't talk about how to use it here.&lt;/p&gt;
&lt;h2&gt;3. Physical connection&lt;/h2&gt;
&lt;hr&gt;
&lt;p&gt;In a nutshell, IN1-4 of the ULN2003 remote control board corresponds to GPIO ports 5, 6, 13, and 19. The VT of the remote control board corresponds to GPIO04. The 2 5V outputs on the Raspberry Pi power the remote control board and stepper motor, respectively. The connection is as follows:&lt;/p&gt;
&lt;p&gt;&lt;img src="/image/img-5f1aa116-b86a-4c04-a040-a8dece9282ae.png" border="0" alt="" width="1046" height="787" loading="lazy"&gt;&lt;/p&gt;
&lt;p&gt;The stepper motor needs to make a bracket by itself, and I used an M3 caliber drill to make holes in the ice cream stick.&lt;/p&gt;
&lt;h2&gt;4. Write code&lt;/h2&gt;
&lt;hr&gt;
&lt;p&gt;First, let's take one of the Uln2003 driver classes from &lt;a href="/post/2016/4/4/windows-10-iot-stepper-motor" target="_blank" rel="noopener"&gt;this article&lt;/a&gt; and introduce it into the project.&lt;/p&gt;
&lt;p&gt;Define an enumeration type to indicate the opening and closing state of the gate, and also define a bool type to indicate whether the motor is in operation or not.&lt;/p&gt;
&lt;pre class="language-csharp"&gt;&lt;code&gt;public bool IsBusy { get; set; }

private DoorStatus Status { get; set; }

enum DoorStatus
{
    Open,
    Closed
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Define GPIO controllers, ULN2003 stepper motor drivers, and GPIO ports on the VT side of the remote controller&lt;/p&gt;
&lt;pre&gt;public GpioController Controller { get; set; }

public GpioPin PinVT { get; set; }

public Uln2003Driver Uln2003Driver { get; set; }

public CancellationTokenSource Cts { get; private set; }&lt;/pre&gt;
&lt;p&gt;Define the method of opening and closing the door:&lt;/p&gt;
&lt;pre&gt;private async Task OpenDoorAsync()
{
    Cts = new CancellationTokenSource();

    await LogMessageAsync("Opening Door...");
    await Uln2003Driver.TurnAsync(90, TurnDirection.Left, Cts.Token);
    Status = DoorStatus.Open;
    await LogMessageAsync("Door is Open.");
}

private async Task CloseDoorAsync()
{
    Cts = new CancellationTokenSource();

    await LogMessageAsync("Closing Door...");
    await Uln2003Driver.TurnAsync(90, TurnDirection.Right, Cts.Token);
    Status = DoorStatus.Closed;
    await LogMessageAsync("Door is Closed.");
}&lt;/pre&gt;
&lt;p&gt;Main logic:&lt;/p&gt;
&lt;pre&gt;Controller = GpioController.GetDefault();
if (null != Controller)
{
    TxtMessage.Text += "[OK] GPIO Controller Initialized." + Environment.NewLine;

    Uln2003Driver = new Uln2003Driver(Controller, 5, 6, 13, 19);
    TxtMessage.Text += "[OK] Uln2003Driver Initialized on 5,6,13,19." + Environment.NewLine;

    Status = DoorStatus.Closed;

    PinVT = Controller.OpenPin(4);
    PinVT.SetDriveMode(GpioPinDriveMode.Input);
    TxtMessage.Text += "[OK] VT Initialized on GPIO 04." + Environment.NewLine;
    PinVT.ValueChanged += async (sender, args) =&amp;gt;
    {
        if (!IsBusy &amp;amp;&amp;amp; PinVT.Read() == GpioPinValue.High &amp;amp;&amp;amp; Status != DoorStatus.Open)
        {
            IsBusy = true;
            await LogMessageAsync("Remote Signal Received on VT.");
            await OpenDoorAsync();
            await Task.Delay(2000);
            await CloseDoorAsync();
            IsBusy = false;
        }
    };
}&lt;/pre&gt;
&lt;h2&gt;5. Operation&lt;/h2&gt;
&lt;hr&gt;
&lt;p&gt;Press any button on the remote control and the ice cream stick will rise. It automatically lowers after 2 seconds.&lt;/p&gt;
&lt;p&gt;&lt;img src="/image/img-a8dbc23c-f079-4c94-a83a-886411ebc064.jpg" border="0" alt="" width="1280" height="720" loading="lazy"&gt;&lt;/p&gt;</description><category>Internet of Things</category><guid isPermaLink="false">fef2c445-449c-47d9-a48b-3b5233b095b9</guid><pubDate>2016-07-21T10:00:43Z</pubDate><updated xmlns="http://www.w3.org/2005/Atom">2016-07-21T10:00:43Z</updated></item><item><title>Reed Controlled Light by Windows 10 IoT Core</title><link>https://edi.wang/post/2016/6/10/windows-10-iot-reed-light-control</link><description>&lt;p&gt;Reed switch is an electronic component to connect / disconnect the switch by magnetic field. When it touchs magnetic field, the two iron sheets inside the tube will clip together, so the switch is connected.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The application of Reed Swith：&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Door light controller: When you open the door, turn the light on. When you close the door, turn off the light. This is simply install a reed switch on your door handler, and install a magnetite on the doorframe.&lt;/p&gt;
&lt;p&gt;Of course, you can also make an alarm and install it on your door.&lt;/p&gt;
&lt;p&gt;There are many other scenarios that uses a reed swtich. This article talks about how to turn on/off a light by the reed switch.&amp;nbsp;&lt;/p&gt;
&lt;h2&gt;1. Physical Connection&lt;/h2&gt;
&lt;hr&gt;
&lt;p&gt;Connect Reed's VCC to DC3.3, GND toGROUND, DO to GPIO 05&lt;/p&gt;
&lt;p&gt;Connect LED positive pin to DC3.3, nagtive pin to GPIO 06&lt;/p&gt;
&lt;p&gt;&lt;img src="/image/img-16acef1a-35ba-404b-a7a7-8f75a266dc66.png" border="0" alt="" width="1647" height="1110" loading="lazy"&gt;&lt;/p&gt;
&lt;h2&gt;2. Coding&lt;/h2&gt;
&lt;hr&gt;
&lt;p&gt;Define GPIO Controller, &amp;nbsp;Reed switch's GPIO pin, LED's GPIO pin and LED's on/off flag.&lt;/p&gt;
&lt;pre&gt;public GpioController GpioController { get; set; }

public GpioPin ReedPin { get; set; }

public GpioPin LedPin { get; set; }

public bool IsLightOn { get; set; }&lt;/pre&gt;
&lt;p&gt;Initiate GPIO controller&lt;/p&gt;
&lt;pre&gt;GpioController = GpioController.GetDefault();
if (null != GpioController)
{

}
&lt;/pre&gt;
&lt;p&gt;Rememeber to check "null" first to ensure your program doesn't blow up on non-GPIO devices.&lt;/p&gt;
&lt;p&gt;Open the reed switch's GPIO pin:&lt;/p&gt;
&lt;pre&gt;ReedPin = GpioController.OpenPin(5);&lt;/pre&gt;
&lt;p&gt;Set it to input mode:&lt;/p&gt;
&lt;pre&gt;ReedPin.SetDriveMode(GpioPinDriveMode.Input);&lt;/pre&gt;
&lt;p&gt;Similarly, open LED pin, and set to output.&lt;/p&gt;
&lt;pre&gt; LedPin = GpioController.OpenPin(6);
 LedPin.SetDriveMode(GpioPinDriveMode.Output);
&lt;/pre&gt;
&lt;p&gt;By default, the LED is turned off, so it requires a GPIO.HIGH at first.&lt;/p&gt;
&lt;pre&gt;LedPin.Write(GpioPinValue.High);&lt;/pre&gt;
&lt;p&gt;Then, listen to the ValueChanged event on the reed's pin.&lt;/p&gt;
&lt;pre&gt;ReedPin.ValueChanged += (sender, args) =&amp;gt;
{

};
&lt;/pre&gt;
&lt;p&gt;In this event handler, check if the reed signal is GPIO.LOW, if it is LOW, that means it's currently in a magnetic field, so turn on the light. If not, turn off the light.&lt;/p&gt;
&lt;pre&gt;if (ReedPin.Read() == GpioPinValue.Low)
{
    IsLightOn = !IsLightOn;

    LedPin.Write(IsLightOn ? GpioPinValue.Low : GpioPinValue.High);
}
&lt;/pre&gt;
&lt;p&gt;Final code:&lt;/p&gt;
&lt;pre&gt;using Windows.Devices.Gpio;
using Windows.UI.Xaml.Controls;

namespace WindowsIoTReedLight
{
    public sealed partial class MainPage : Page
    {
        public GpioController GpioController { get; set; }

        public GpioPin ReedPin { get; set; }

        public GpioPin LedPin { get; set; }

        public bool IsLightOn { get; set; }

        public MainPage()
        {
            this.InitializeComponent();

            GpioController = GpioController.GetDefault();
            if (null != GpioController)
            {
                ReedPin = GpioController.OpenPin(5);
                ReedPin.SetDriveMode(GpioPinDriveMode.Input);
                ReedPin.ValueChanged += (sender, args) =&amp;gt;
                {
                    if (ReedPin.Read() == GpioPinValue.Low)
                    {
                        IsLightOn = !IsLightOn;

                        LedPin.Write(IsLightOn ? GpioPinValue.Low : GpioPinValue.High);
                    }
                };

                LedPin = GpioController.OpenPin(6);
                LedPin.SetDriveMode(GpioPinDriveMode.Output);
                LedPin.Write(GpioPinValue.High);
            }
        }
    }
}
&lt;/pre&gt;
&lt;h2&gt;3. Running&lt;/h2&gt;
&lt;hr&gt;
&lt;p&gt;Deploy the project to raspberry pi. Take a magnetite and closing in the reed switch, you will see the light will be turned on. Move away the magnetite, the light will be turned off.&lt;/p&gt;
&lt;p&gt;&lt;img src="/image/img-8efcfacc-f5f9-454a-bb16-a5eecc8dbc01.jpg" border="0" alt="" width="1024" height="576" loading="lazy"&gt;&lt;/p&gt;</description><category>Windows Development</category><category>Internet of Things</category><guid isPermaLink="false">68c3d134-17c3-4797-8a73-1acfd2635076</guid><pubDate>2016-06-10T11:34:53Z</pubDate><updated xmlns="http://www.w3.org/2005/Atom">2016-06-10T11:34:53Z</updated></item><item><title>74HC595 4 Digits LED Tube with Windows 10</title><link>https://edi.wang/post/2016/4/29/windows-10-iot-74hc595-led-digital-tube</link><description>&lt;p&gt;One of the commonly used LED Tube driver chip is 74HC595, by using the Chip, you can save GPIO ports. I bought an LED Tube with 595 chip, it only needs 3 GPIO ports to display 4 digits. However, there is no existing posts about how to drive the 74HC595 LED Tube from Windows 10. So, I can only try for myself.&lt;/p&gt;
&lt;h2&gt;1. Physical Connection&lt;/h2&gt;
&lt;hr&gt;
&lt;p&gt;VCC to DC3.3 power, GND to Ground, DIO to GPIO27 (you can change it, and change the program also). SCLK to gpio 5 (you can also change), RCLK to GPIO 06.&lt;/p&gt;
&lt;p&gt;As shown below:&lt;/p&gt;
&lt;p&gt;&lt;img src="/image/img-9991012f-65f0-4e97-aee1-544fbe1892e2.png" border="0" alt="" width="1347" height="990" loading="lazy"&gt;&lt;/p&gt;
&lt;h2&gt;2. Coding&lt;/h2&gt;
&lt;hr&gt;
&lt;p&gt;The shop give me a C++ code on Arduino:&lt;/p&gt;
&lt;pre&gt;/*******************************************************************************	
* Software Author:	HQ	
* Creation Date:        2015-2-10 
* Software History:	2015-3-20
* Version:              2.0 
* Sales address：       http://qifeidz.taobao.com/
********************************************************************************/

    unsigned char LED_0F[] = 
    {// 0	 1	  2	   3	4	 5	  6	   7	8	 9	  A	   b	C    d	  E    F    -
    	0xC0,0xF9,0xA4,0xB0,0x99,0x92,0x82,0xF8,0x80,0x90,0x8C,0xBF,0xC6,0xA1,0x86,0xFF,0xbf
    };
    unsigned char LED[4];	//用于LED的4位显示缓存
    int SCLK = 2;
    int RCLK = 1;
    int DIO = 0; //这里定义了那三个脚
    void setup ()
    {
      pinMode(SCLK,OUTPUT);
      pinMode(RCLK,OUTPUT);
      pinMode(DIO,OUTPUT); //让三个脚都是输出状态
    }
    void loop()
    {
        LED[0]=1;
    	LED[1]=2;
    	LED[2]=3;
    	LED[3]=4;
    	while(1)
    	{
    		LED4_Display ();
    	} 
      
    }
    
    void LED4_Display (void)
    {
    	unsigned char *led_table;          // 查表指针
    	unsigned char i;
    	//显示第1位
    	led_table = LED_0F + LED[0];
    	i = *led_table;
    	LED_OUT(i);			
    	LED_OUT(0x01);		
        digitalWrite(RCLK,LOW);
        digitalWrite(RCLK,HIGH);
    	//显示第2位
    	led_table = LED_0F + LED[1];
    	i = *led_table;
    	LED_OUT(i);		
    	LED_OUT(0x02);		
        digitalWrite(RCLK,LOW);
        digitalWrite(RCLK,HIGH);
    	//显示第3位
    	led_table = LED_0F + LED[2];
    	i = *led_table;
    	LED_OUT(i);			
    	LED_OUT(0x04);	
        digitalWrite(RCLK,LOW);
        digitalWrite(RCLK,HIGH);
    	//显示第4位
    	led_table = LED_0F + LED[3];
    	i = *led_table;
    	LED_OUT(i);			
    	LED_OUT(0x08);		
        digitalWrite(RCLK,LOW);
        digitalWrite(RCLK,HIGH);
    }
    
    void LED_OUT(unsigned char X)
    {
    	unsigned char i;
    	for(i=8;i&amp;gt;=1;i--)
    	{
    		if (X&amp;amp;0x80) 
                {
                  digitalWrite(DIO,HIGH);
                 }  
                else 
                {
                  digitalWrite(DIO,LOW);
                }
    		X&amp;lt;&amp;lt;=1;
                digitalWrite(SCLK,LOW);
                digitalWrite(SCLK,HIGH);
    	}
    }
    &lt;/pre&gt;
&lt;p&gt;We only need to display Numbers. So we only need 0-9 from the LED_0F array, the C# definition is this:&lt;/p&gt;
&lt;pre&gt;byte[] LEDDIGITS =
{
    // 0    1    2    3    4	5	 6	  7	   8    9
    0xC0,0xF9,0xA4,0xB0,0x99,0x92,0x82,0xF8,0x80,0x90
};&lt;/pre&gt;
&lt;p&gt;GPIO Pins:&lt;/p&gt;
&lt;pre&gt;public GpioPin PinDIO { get; set; }     // Serial Digital Input
public GpioPin PinRCLK { get; set; }   // Register Clock
public GpioPin PinSCLK { get; set; }   // Serial Clock&lt;/pre&gt;
&lt;p&gt;Initiate GPIO Controller and Pins.&amp;nbsp;&lt;/p&gt;
&lt;pre&gt;public LED74HC595Driver(int dioPin, int rclkPin, int sclkPin)
{
    var gpio = GpioController.GetDefault();

    // setup the pins
    PinDIO = gpio.OpenPin(dioPin);
    PinDIO.SetDriveMode(GpioPinDriveMode.Output);

    PinRCLK = gpio.OpenPin(rclkPin);
    PinRCLK.SetDriveMode(GpioPinDriveMode.Output);

    PinSCLK = gpio.OpenPin(sclkPin);
    PinSCLK.SetDriveMode(GpioPinDriveMode.Output);

    // initialize the pins to low
    PinDIO.Write(GpioPinValue.Low);
    PinRCLK.Write(GpioPinValue.Low);
    PinSCLK.Write(GpioPinValue.Low);
}&lt;/pre&gt;
&lt;p&gt;C# translation of "void LED_OUT(unsigned char X)"&lt;/p&gt;
&lt;pre&gt;// Serial-In-Parallel-Out
void WriteSIPO(byte b)
{
    for (int i = 8; i &amp;gt;= 1; i--)
    {
        PinDIO.Write((b &amp;amp; 0x80) &amp;gt; 0 ? GpioPinValue.High : GpioPinValue.Low);
        b &amp;lt;&amp;lt;= 1;
        PulseSCLK();
    }
}&lt;/pre&gt;
&lt;p&gt;In order to use it more handy, add 2 helper methods&lt;/p&gt;
&lt;pre&gt;// Pulse Register Clock
void PulseRCLK()
{
    PinRCLK.Write(GpioPinValue.Low);
    PinRCLK.Write(GpioPinValue.High);
}

// Pulse Serial Clock
void PulseSCLK()
{
    PinSCLK.Write(GpioPinValue.Low);
    PinSCLK.Write(GpioPinValue.High);
}&lt;/pre&gt;
&lt;p&gt;Now we can refact the logic of "void LED4_Display (void)". Create a new method, to display a single number on a particular tube.&lt;/p&gt;
&lt;pre&gt;public void DisplayChar(int index, byte b)
{
    if (index &amp;lt; 0 || index &amp;gt; 3)
    {
        throw new ArgumentOutOfRangeException(nameof(index), "index just be between 0-3.");
    }

    int i = 0x01;
    i = i &amp;lt;&amp;lt; index;

    WriteSIPO(b);
    WriteSIPO((byte)i);
    PulseRCLK();
}&lt;/pre&gt;
&lt;p&gt;The index stands for different tubes from right to left. 0 is the 1st tube, 2 is the second, 3 is the third, 4 is the last. Because it is 4 digits, it can not excceed 9999, so remember to check the range. I didn't consider the nagetive numbers, so it will be 0-9999.&lt;/p&gt;
&lt;p&gt;For example, you want to display "8" on the second tube, like _ _ 8 _&lt;/p&gt;
&lt;pre&gt;DisplayChar(1, 0x80)&lt;/pre&gt;
&lt;p&gt;the number "8" is already defined in LEDDIGITS.&lt;/p&gt;
&lt;p&gt;In order to be easy to use, write a method, take int type number, then convert to byte to display.&lt;/p&gt;
&lt;pre&gt;public void DisplayDigits(int number)
{
    if (number &amp;lt; 0 || number &amp;gt; 9999)
    {
        throw new ArgumentOutOfRangeException(nameof(number), "number must be between 0-9999");
    }

    // 老司机写法
    var numStr = number.ToString();
    for (int i = 0; i &amp;lt; numStr.Length; i++)
    {
        var pos = numStr[numStr.Length - i - 1] - '0';
        DisplayChar(i, LEDDIGITS[pos]);
    }

    // 新司机写法
    //// 个位
    //var ge = number % 10;

    //// 十位
    //var shi = number / 10 % 10;

    //// 百位
    //var bai = number / 100 % 10;

    //// 千位
    //var qian = number / 1000 % 10;

    //DisplayChar(0, LEDDIGITS[ge]);
    //DisplayChar(1, LEDDIGITS[shi]);
    //DisplayChar(2, LEDDIGITS[bai]);
    //DisplayChar(3, LEDDIGITS[qian]);
}&lt;/pre&gt;
&lt;p&gt;Then is the difficult part. How the LED Tube works is, by refreshing the characters on each tube for a very fast rate, human eye can not see the refresh, it will show like there are 4 digits at the same time. So that's why there are a while(1) loop in that C++ code.&lt;/p&gt;
&lt;p&gt;Translating to C#, in UWP, you will need a background worker so your app start page don't stuck.&lt;/p&gt;
&lt;pre&gt;public sealed partial class MainPage : Page
{
    public LED74HC595Driver Led74Hc595Driver { get; set; }

    public MainPage()
    {
        InitializeComponent();

        Led74Hc595Driver = new LED74HC595Driver(26, 6, 5);

        BackgroundWorker worker = new BackgroundWorker { WorkerSupportsCancellation = true };
        worker.DoWork += (sender, args) =&amp;gt;
        {
            while (true)
            {
                Led74Hc595Driver.DisplayDigits(1024);
            }
        };

        Loaded += (sender, args) =&amp;gt;
        {
            worker.RunWorkerAsync();
        };
    }
}&lt;/pre&gt;
&lt;p&gt;Finally, the complete code:&lt;/p&gt;
&lt;pre&gt;using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Windows.Devices.Gpio;

namespace LEDTube74HC595
{
    public class LED74HC595Driver
    {
        byte[] LEDDIGITS =
        {
            // 0    1    2    3    4	5	 6	  7	   8    9
    	    0xC0,0xF9,0xA4,0xB0,0x99,0x92,0x82,0xF8,0x80,0x90
        };

        private byte[] LEDCHARS =
        {
            // A     b     C     d     E     F     -
            0x8C, 0xBF, 0xC6, 0xA1, 0x86, 0xFF, 0xbf
        };

        public GpioPin PinDIO { get; set; }     // Serial Digital Input
        public GpioPin PinRCLK { get; set; }   // Register Clock
        public GpioPin PinSCLK { get; set; }   // Serial Clock

        public LED74HC595Driver(int dioPin, int rclkPin, int sclkPin)
        {
            var gpio = GpioController.GetDefault();

            // setup the pins
            PinDIO = gpio.OpenPin(dioPin);
            PinDIO.SetDriveMode(GpioPinDriveMode.Output);

            PinRCLK = gpio.OpenPin(rclkPin);
            PinRCLK.SetDriveMode(GpioPinDriveMode.Output);

            PinSCLK = gpio.OpenPin(sclkPin);
            PinSCLK.SetDriveMode(GpioPinDriveMode.Output);

            // initialize the pins to low
            PinDIO.Write(GpioPinValue.Low);
            PinRCLK.Write(GpioPinValue.Low);
            PinSCLK.Write(GpioPinValue.Low);
        }

        public void DisplayChar(int index, byte b)
        {
            if (index &amp;lt; 0 || index &amp;gt; 3)
            {
                throw new ArgumentOutOfRangeException(nameof(index), "index just be between 0-3.");
            }

            int i = 0x01;
            i = i &amp;lt;&amp;lt; index;

            WriteSIPO(b);
            WriteSIPO((byte)i);
            PulseRCLK();
        }

        public void DisplayDigits(int number)
        {
            if (number &amp;lt; 0 || number &amp;gt; 9999)
            {
                throw new ArgumentOutOfRangeException(nameof(number), "number must be between 0-9999");
            }

            // 老司机写法
            var numStr = number.ToString();
            for (int i = 0; i &amp;lt; numStr.Length; i++)
            {
                var pos = numStr[numStr.Length - i - 1] - '0';
                DisplayChar(i, LEDDIGITS[pos]);
            }

            // 新司机写法
            //// 个位
            //var ge = number % 10;

            //// 十位
            //var shi = number / 10 % 10;

            //// 百位
            //var bai = number / 100 % 10;

            //// 千位
            //var qian = number / 1000 % 10;

            //DisplayChar(0, LEDDIGITS[ge]);
            //DisplayChar(1, LEDDIGITS[shi]);
            //DisplayChar(2, LEDDIGITS[bai]);
            //DisplayChar(3, LEDDIGITS[qian]);
        }

        // Serial-In-Parallel-Out
        void WriteSIPO(byte b)
        {
            for (int i = 8; i &amp;gt;= 1; i--)
            {
                PinDIO.Write((b &amp;amp; 0x80) &amp;gt; 0 ? GpioPinValue.High : GpioPinValue.Low);
                b &amp;lt;&amp;lt;= 1;
                PulseSCLK();
            }
        }

        // Pulse Register Clock
        void PulseRCLK()
        {
            PinRCLK.Write(GpioPinValue.Low);
            PinRCLK.Write(GpioPinValue.High);
        }

        // Pulse Serial Clock
        void PulseSCLK()
        {
            PinSCLK.Write(GpioPinValue.Low);
            PinSCLK.Write(GpioPinValue.High);
        }
    }
}
&lt;/pre&gt;
&lt;h2&gt;3. Running&lt;/h2&gt;
&lt;hr&gt;
&lt;p&gt;&lt;img src="/image/img-bd88a3b8-4be7-4d13-b06f-fce384a38560.jpg" border="0" alt="" width="1280" height="720" loading="lazy"&gt;&lt;/p&gt;</description><category>Windows Development</category><category>Internet of Things</category><guid isPermaLink="false">4b38e96c-ec8d-4e07-8a8d-fbc5acd8a4e1</guid><pubDate>2016-04-29T08:15:01Z</pubDate><updated xmlns="http://www.w3.org/2005/Atom">2016-04-29T08:15:01Z</updated></item><item><title>Using stepper motor in Windows 10 IoT Core</title><link>https://edi.wang/post/2016/4/4/windows-10-iot-stepper-motor</link><description>&lt;p&gt;If you need to precisely control the rotation angle of the equipment, ordinary motors can't do it, usually we use stepper motors. For example, the 28BYJ-48 model is easy to buy.&lt;/p&gt;
&lt;p&gt;&lt;img src="/image/img-6d27c3e6-126c-4883-a061-5ac071104ba3.jpg" border="0" alt="" width="457" height="449" loading="lazy"&gt;&lt;/p&gt;
&lt;p&gt;The stepper motor has to be used with the driver board, and the most commonly used is the driver board ULN2003 the chip, as shown in the figure below. However, note that the driver board you buy may not look the same, but it doesn't matter, as long as the chip is written on the ULN2003, you can use it, and their interfaces are the same. For the principle of stepper motor, you can see this: &lt;a href="https://en.wikipedia.org/wiki/Stepper_motor" target="_blank" rel="noopener"&gt;https://en.wikipedia.org/wiki/Stepper_motor&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;After getting the driver board and stepper motor, insert the motor into the white slot of the driver board, this interface has a foolproof design, so it will not be plugged in backwards.&lt;/p&gt;
&lt;p&gt;&lt;img src="/image/img-004d4d78-b043-4b45-af26-6b0bbf70611a.jpg" border="0" alt="" width="1024" height="886" loading="lazy"&gt;&lt;/p&gt;
&lt;p&gt;There's a great article in English about how Windows 10 IoT drives stepper motors:&lt;/p&gt;
&lt;p&gt;&lt;a href="https://www.hackster.io/erickbp/stepper-motor-and-windows-10-iot-core-d3c5d6"&gt;https://www.hackster.io/erickbp/stepper-motor-and-windows-10-iot-core-d3c5d6&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;My example is an improvement and addition based on the article above.&lt;/p&gt;
&lt;h2&gt;1. Physical Connection&lt;/h2&gt;
&lt;hr&gt;
&lt;p&gt;First of all, it is not recommended to connect the 5V power supply of the stepper motor driver board to the 5V output of the Raspberry Pi, the Raspberry Pi will report a low voltage prompt when it is running, if you have any other devices connected to the Raspberry Pi, it is likely to cause the machine to restart. Therefore, it is recommended that you use an external 5v power supply, the positive and negative poles can be completely independent, and the negative pole can not be connected to the GND of the Raspberry Pi, which is not the same as what is said in the English information. I don't know if it will explode, though. I didn't blow up anyway.&lt;/p&gt;
&lt;p&gt;The external power supply I use is a waste USB mouse cable, and the output of USB is 5v, which is the most convenient.&lt;/p&gt;
&lt;p&gt;After connecting the power supply, use 4 wires to connect IN1-IN4 to the GPIO port of Raspberry Pi, the correspondence is as follows (of course, you can change it yourself, and the program should also be modified accordingly):&lt;/p&gt;
&lt;table class="table table-bordered"&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;th&gt;Driver board port&lt;/th&gt;
&lt;th&gt;Raspberry Pi port&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;IN1&lt;/td&gt;
&lt;td&gt;GPIO 26&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;IN2&lt;/td&gt;
&lt;td&gt;GPIO 13&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;IN3&amp;nbsp;&lt;/td&gt;
&lt;td&gt;GPIO 6&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;IN4&lt;/td&gt;
&lt;td&gt;GPIO 5&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;img src="/image/img-4042b46f-7f4c-4c88-bfc9-05f8b4351017.jpg" border="0" alt="" width="1024" height="1027" loading="lazy"&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src="/image/img-6982494b-ec5c-429e-bb7f-188545626133.jpg" border="0" alt="" width="1024" height="576" loading="lazy"&gt;&lt;/p&gt;
&lt;h2&gt;2. Write Code&lt;/h2&gt;
&lt;hr&gt;
&lt;p&gt;The original code is here: &lt;a href="https://github.com/erickbp/IoT/blob/master/Stepper%20Motor/Stepper%20Motor/Uln2003Driver.cs"&gt;https://github.com/erickbp/IoT/blob/master/Stepper%20Motor/Stepper%20Motor/Uln2003Driver.cs&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;I made some improvements. Post the full code first:&lt;/p&gt;
&lt;pre class="language-csharp"&gt;&lt;code&gt;using System;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
using Windows.Devices.Gpio;

namespace Uln2003StepMotor
{
    public class Uln2003Driver : IDisposable
    {
        public int IntervalMs { get; set; }

        private readonly GpioPin[] _gpioPins = new GpioPin[4];

        private readonly GpioPinValue[][] _waveDriveSequence =
        {
            new[] {GpioPinValue.High, GpioPinValue.Low, GpioPinValue.Low, GpioPinValue.Low},
            new[] {GpioPinValue.Low, GpioPinValue.High, GpioPinValue.Low, GpioPinValue.Low},
            new[] {GpioPinValue.Low, GpioPinValue.Low, GpioPinValue.High, GpioPinValue.Low},
            new[] {GpioPinValue.Low, GpioPinValue.Low, GpioPinValue.Low, GpioPinValue.High}
        };

        private readonly GpioPinValue[][] _fullStepSequence =
        {
            new[] {GpioPinValue.High, GpioPinValue.Low, GpioPinValue.Low, GpioPinValue.High},
            new[] {GpioPinValue.High, GpioPinValue.High, GpioPinValue.Low, GpioPinValue.Low},
            new[] {GpioPinValue.Low, GpioPinValue.High, GpioPinValue.High, GpioPinValue.Low},
            new[] {GpioPinValue.Low, GpioPinValue.Low, GpioPinValue.High, GpioPinValue.High }

        };

        private readonly GpioPinValue[][] _halfStepSequence =
        {
            new[] {GpioPinValue.High, GpioPinValue.High, GpioPinValue.Low, GpioPinValue.Low, GpioPinValue.Low, GpioPinValue.Low, GpioPinValue.Low, GpioPinValue.High},
            new[] {GpioPinValue.Low, GpioPinValue.High, GpioPinValue.High, GpioPinValue.High, GpioPinValue.Low, GpioPinValue.Low, GpioPinValue.Low, GpioPinValue.Low},
            new[] {GpioPinValue.Low, GpioPinValue.Low, GpioPinValue.Low, GpioPinValue.High, GpioPinValue.High, GpioPinValue.High, GpioPinValue.Low, GpioPinValue.Low},
            new[] {GpioPinValue.Low, GpioPinValue.Low, GpioPinValue.Low, GpioPinValue.Low, GpioPinValue.Low, GpioPinValue.High, GpioPinValue.High, GpioPinValue.High }
        };

        public Uln2003Driver(GpioController gpioController,
            int wireIn1, int wireIn2, int wireIn3, int wireIn4,
            GpioSharingMode sharingMode = GpioSharingMode.Exclusive, int intervalMs = 5)
        {
            var gpio = gpioController ?? GpioController.GetDefault();

            _gpioPins[0] = gpio.OpenPin(wireIn1, sharingMode);
            _gpioPins[1] = gpio.OpenPin(wireIn2, sharingMode);
            _gpioPins[2] = gpio.OpenPin(wireIn3, sharingMode);
            _gpioPins[3] = gpio.OpenPin(wireIn4, sharingMode);

            foreach (var gpioPin in _gpioPins)
            {
                gpioPin.Write(GpioPinValue.Low);
                gpioPin.SetDriveMode(GpioPinDriveMode.Output);
            }

            IntervalMs = intervalMs;
        }

        public async Task TurnAsync(TurnDirection direction, CancellationToken ct,
            DrivingMethod drivingMethod = DrivingMethod.FullStep)
        {
            bool stop = false;
            GpioPinValue[][] methodSequence;
            switch (drivingMethod)
            {
                case DrivingMethod.WaveDrive:
                    methodSequence = _waveDriveSequence;
                    break;
                case DrivingMethod.FullStep:
                    methodSequence = _fullStepSequence;
                    break;
                case DrivingMethod.HalfStep:
                    methodSequence = _halfStepSequence;
                    break;
                default:
                    throw new ArgumentOutOfRangeException(nameof(drivingMethod), drivingMethod, null);
            }
            while (!stop)
            {
                for (var j = 0; j &amp;lt; methodSequence[0].Length; j++)
                {
                    for (var i = 0; i &amp;lt; 4; i++)
                    {
                        _gpioPins[i].Write(methodSequence[direction == TurnDirection.Right ? i : 3 - i][j]);
                    }

                    // don't pass cancellation token, will blow up.
                    await Task.Delay(IntervalMs);

                    if (ct.IsCancellationRequested)
                    {
                        Debug.WriteLine("Cancel Requested, stop now.");
                        stop = true;
                        break;
                    }
                }
            }

            Stop();
        }

        public async Task TurnAsync(int degree, TurnDirection direction, CancellationToken ct,
            DrivingMethod drivingMethod = DrivingMethod.FullStep)
        {
            var steps = 0;
            GpioPinValue[][] methodSequence;
            switch (drivingMethod)
            {
                case DrivingMethod.WaveDrive:
                    methodSequence = _waveDriveSequence;
                    steps = (int)Math.Ceiling(degree / 0.1767478397486253);
                    break;
                case DrivingMethod.FullStep:
                    methodSequence = _fullStepSequence;
                    steps = (int)Math.Ceiling(degree / 0.1767478397486253);
                    break;
                case DrivingMethod.HalfStep:
                    methodSequence = _halfStepSequence;
                    steps = (int)Math.Ceiling(degree / 0.0883739198743126);
                    break;
                default:
                    throw new ArgumentOutOfRangeException(nameof(drivingMethod), drivingMethod, null);
            }
            var counter = 0;
            while (counter &amp;lt; steps)
            {
                for (var j = 0; j &amp;lt; methodSequence[0].Length; j++)
                {
                    for (var i = 0; i &amp;lt; 4; i++)
                    {
                        _gpioPins[i].Write(methodSequence[direction == TurnDirection.Right ? i : 3 - i][j]);
                    }

                    // don't pass cancellation token, will blow up.
                    await Task.Delay(IntervalMs);

                    if (ct.IsCancellationRequested)
                    {
                        Debug.WriteLine("Cancel Requested, stop now.");
                        counter = steps;
                    }
                    else
                    {
                        counter++;
                    }
                    
                    if (counter == steps)
                    {
                        break;
                    }
                }
            }

            Stop();
        }

        public void Stop()
        {
            foreach (var gpioPin in _gpioPins)
            {
                gpioPin.Write(GpioPinValue.Low);
            }
        }

        public void Dispose()
        {
            foreach (var gpioPin in _gpioPins)
            {
                gpioPin.Write(GpioPinValue.Low);
                gpioPin.Dispose();
            }
        }
    }

    public enum DrivingMethod
    {
        WaveDrive,
        FullStep,
        HalfStep
    }

    public enum TurnDirection
    {
        Left,
        Right
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The improvements are:&lt;/p&gt;
&lt;p&gt;1. &lt;code&gt;CancellationToken&lt;/code&gt; is added to the &lt;code&gt;TurnAsync&lt;/code&gt; method, which can be force stop the rotation.&lt;/p&gt;
&lt;pre class="language-csharp"&gt;&lt;code&gt;public async Task TurnAsync(int degree, TurnDirection direction, CancellationToken ct,
    DrivingMethod drivingMethod = DrivingMethod.FullStep)&lt;/code&gt;&lt;/pre&gt;
&lt;pre class="language-csharp"&gt;&lt;code&gt;// don't pass cancellation token, will blow up.
await Task.Delay(IntervalMs);

if (ct.IsCancellationRequested)
{
    Debug.WriteLine("Cancel Requested, stop now.");
    counter = steps;
}
else
{
    counter++;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;2. &lt;code&gt;TurnAsync &lt;/code&gt;adds a reload for rotating in one direction without specifying an angle. Then stop with a &lt;code&gt;CancellationToken&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;public async Task TurnAsync(TurnDirection direction, CancellationToken ct,
    DrivingMethod drivingMethod = DrivingMethod.FullStep)
&lt;/pre&gt;
&lt;h3&gt;Usage&lt;/h3&gt;
&lt;p&gt;XAML&amp;nbsp;&lt;/p&gt;
&lt;pre class="language-xml"&gt;&lt;code&gt;&amp;lt;Button x:Name="BtnLeftForever" Content="Trun Left Forever" Click="BtnLeftForever_OnClick" /&amp;gt;

&amp;lt;Button x:Name="BtnLeft90" Content="Turn Left 90" Click="BtnLeft90_OnClick" Margin="0,10,0,0" /&amp;gt;
&amp;lt;Button x:Name="BtnRight90" Content="Turn Right 90" Margin="0,10,0,0" Click="BtnRight90_OnClick" /&amp;gt;
&amp;lt;Button x:Name="BtnStop" Content="Stop" Margin="0,10,0,0" Click="BtnStop_OnClick"/&amp;gt;

&amp;lt;TextBox Text="10" Header="Degree" x:Name="TxtDegree" /&amp;gt;
&amp;lt;Button x:Name="TurnDegree" Click="TurnDegree_OnClick" Content="Trun Left" /&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Backend&lt;/p&gt;
&lt;pre class="language-csharp"&gt;&lt;code&gt;public sealed partial class MainPage : Page
{
    public CancellationTokenSource Cts { get; private set; }

    public Uln2003Driver Uln2003Driver { get; set; }

    public MainPage()
    {
        this.InitializeComponent();
        var controller = GpioController.GetDefault();
        Uln2003Driver = new Uln2003Driver(controller, 26, 13, 6, 5);
    }

    private async Task TurnMotor(int degree, TurnDirection direction)
    {
        Cts = new CancellationTokenSource();
        await Uln2003Driver.TurnAsync(degree, direction, Cts.Token);
    }

    private async void BtnLeft90_OnClick(object sender, RoutedEventArgs e)
    {
        await TurnMotor(90, TurnDirection.Left);
    }

    private async void BtnRight90_OnClick(object sender, RoutedEventArgs e)
    {
        await TurnMotor(90, TurnDirection.Right);
    }

    private async void TurnDegree_OnClick(object sender, RoutedEventArgs e)
    {
        await TurnMotor(int.Parse(TxtDegree.Text), TurnDirection.Left);
    }

    private void BtnStop_OnClick(object sender, RoutedEventArgs e)
    {
        Cts.Cancel();
    }

    private async void BtnLeftForever_OnClick(object sender, RoutedEventArgs e)
    {
        Cts = new CancellationTokenSource();
        await Uln2003Driver.TurnAsync(TurnDirection.Left, Cts.Token);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;3. Run&lt;/h2&gt;
&lt;hr&gt;
&lt;p&gt;&lt;img src="/image/img-4e0eb985-848a-40f5-bbc8-8413e2ec5e31.jpg" border="0" alt="" width="1024" height="576" loading="lazy"&gt;&lt;/p&gt;</description><category>Windows Development</category><category>Internet of Things</category><guid isPermaLink="false">a6adf188-3826-440b-8634-6b7e86d1a380</guid><pubDate>2016-04-04T01:15:00Z</pubDate><updated xmlns="http://www.w3.org/2005/Atom">2016-04-04T01:15:00Z</updated></item><item><title>Sound Controlled Light by Windows 10 IoT Core</title><link>https://edi.wang/post/2016/4/3/windows-10-iot-sound-light</link><description>&lt;p&gt;In many buildings, there are sound controlled light in use. When you clap your hands, the light turns on. Now, with Raspberry Pi 3 and Windows 10 IoT, we can make a sound controlled light ourselves.&lt;/p&gt;
&lt;p&gt;The sound sensor I use is FC-04. As other sensors, the first step is calibration.&lt;/p&gt;
&lt;h2&gt;1. Calibration for FC-04 Sound Sensor&lt;/h2&gt;
&lt;hr&gt;
&lt;p&gt;&lt;span style="color: #ff0000;"&gt;&lt;strong&gt;In quiet environment. &lt;/strong&gt;&lt;span style="color: #000000;"&gt;Connect&amp;nbsp;&lt;/span&gt;&lt;/span&gt;VCC to DC 3.3V，GND to Ground. At that time, the power LED on the FC-04 will turn on.&lt;/p&gt;
&lt;p&gt;If the switch LED is also on, that means the sensitivity of the sensor is too high, that will need calibration.&lt;/p&gt;
&lt;p&gt;&lt;img src="/image/img-4a436d9f-737a-4c3b-8bf3-bfbab12c8d53.jpg" border="0" alt="" width="1024" height="416" loading="lazy"&gt;&lt;/p&gt;
&lt;p&gt;Turns the blue potentiometer on the FC04, and make some sound, until the moment that the switch LED is off in quiet environment and on when you make sound.&lt;/p&gt;
&lt;p&gt;&lt;img src="/image/img-a34240a0-0076-4f17-9794-8fa5ac018c67.jpg" border="0" alt="" width="1240" height="577" loading="lazy"&gt;&lt;/p&gt;
&lt;h2&gt;2. Connecting Raspberry Pi&lt;/h2&gt;
&lt;hr&gt;
&lt;p&gt;This sensro has 2 output pins. DO and AO. However, if you bought the FC-04 with only DO, that will also do, we are not using AO at this project.&lt;/p&gt;
&lt;p&gt;Connect DO to &amp;nbsp;GPIO 06.&lt;/p&gt;
&lt;p&gt;Connect an LED, short leg to GROUND, long leg to GPIO 05.&lt;/p&gt;
&lt;h2&gt;3. Coding&lt;/h2&gt;
&lt;hr&gt;
&lt;p&gt;First, we have 2 devices on GPIO pins, they are FC-04 and an LED. So, we need two properties for them to represent their GPIO pins.&lt;/p&gt;
&lt;p&gt;In MainPage.xaml.cs add these 2 lines of code:&lt;/p&gt;
&lt;pre&gt;public GpioPin LedPin { get; set; }

public GpioPin SoundPin { get; set; }
&lt;/pre&gt;
&lt;p&gt;We also need a flag to store the on/off status of the LED:&lt;/p&gt;
&lt;pre&gt;public bool IsLightOn { get; set; }&lt;/pre&gt;
&lt;p&gt;Register Loaded event:&lt;/p&gt;
&lt;pre&gt;public MainPage()
{
    this.InitializeComponent();
    Loaded += OnLoaded;
}
&lt;/pre&gt;
&lt;p&gt;Initiate GPIO controller:&lt;/p&gt;
&lt;pre&gt;private void OnLoaded(object sender, RoutedEventArgs routedEventArgs)
{
    var controller = GpioController.GetDefault();
    if (null != controller)
    {

    }
}&lt;/pre&gt;
&lt;p&gt;GPIO.High will light up the LED, so when first run, we need to set it to off.&lt;/p&gt;
&lt;pre&gt;private void OnLoaded(object sender, RoutedEventArgs routedEventArgs)
{
    var controller = GpioController.GetDefault();
    if (null != controller)
    {
        LedPin = controller.OpenPin(5);
        LedPin.SetDriveMode(GpioPinDriveMode.Output);
        LedPin.Write(GpioPinValue.Low); // set light to off at start up
    }
}&lt;/pre&gt;
&lt;p&gt;The GPIO 06 for FC-04 is input pin, so code is:&lt;/p&gt;
&lt;pre&gt;SoundPin = controller.OpenPin(6);
SoundPin.SetDriveMode(GpioPinDriveMode.Input);
&lt;/pre&gt;
&lt;p&gt;So how do we know there's a signal coming from GPIO 06? There are two ways to do it, one is by event:&lt;/p&gt;
&lt;pre&gt;//
// Summary:
//     Occurs when the value of the general-purpose I/O (GPIO) pin changes, either because
//     of an external stimulus when the pin is configured as an input, or when a value
//     is written to the pin when the pin in configured as an output.
public event TypedEventHandler&amp;lt;GpioPin, GpioPinValueChangedEventArgs&amp;gt; ValueChanged;
&lt;/pre&gt;
&lt;p&gt;Another is to use a DispatcherTimer. I will use the "event" to do the project. Because Timer can not see the signal in real time. If you set interval too large, the light will become stupid. Only continuous sound will make it on and off. If interval is too small, then your light will go on and off in a very short time, it is useless.&lt;/p&gt;
&lt;pre&gt;SoundPin.ValueChanged += (pin, args) =&amp;gt;
{
    var pinValue = SoundPin.Read();
    if (pinValue == GpioPinValue.Low)
    {
        Debug.WriteLine("Sound Detected!");
        LedPin.Write(IsLightOn ? GpioPinValue.Low : GpioPinValue.High);
        IsLightOn = !IsLightOn;
    }
};
&lt;/pre&gt;
&lt;p&gt;Here is a trick, do not use the &lt;strong&gt;args.Edge&lt;/strong&gt; in the &lt;strong&gt;args&lt;/strong&gt; parameter. It's not same as &lt;strong&gt;Read()&lt;/strong&gt; method. The correct real-time way is to use &lt;strong&gt;ValueChanged&lt;/strong&gt; event with &lt;strong&gt;Read()&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre&gt;using System.Diagnostics;
using Windows.Devices.Gpio;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;

namespace SoundLight
{
    public sealed partial class MainPage : Page
    {
        public GpioPin LedPin { get; set; }

        public GpioPin SoundPin { get; set; }

        public bool IsLightOn { get; set; }

        public MainPage()
        {
            this.InitializeComponent();
            Loaded += OnLoaded;
        }

        private void OnLoaded(object sender, RoutedEventArgs routedEventArgs)
        {
            var controller = GpioController.GetDefault();
            if (null != controller)
            {
                LedPin = controller.OpenPin(5);
                LedPin.SetDriveMode(GpioPinDriveMode.Output);
                LedPin.Write(GpioPinValue.Low); // set light to off at start up

                SoundPin = controller.OpenPin(6);
                SoundPin.SetDriveMode(GpioPinDriveMode.Input);

                SoundPin.ValueChanged += (pin, args) =&amp;gt;
                {
                    var pinValue = SoundPin.Read();
                    if (pinValue == GpioPinValue.Low)
                    {
                        Debug.WriteLine("Sound Detected!");
                        LedPin.Write(IsLightOn ? GpioPinValue.Low : GpioPinValue.High);
                        IsLightOn = !IsLightOn;
                    }
                };
            }
        }
    }
}
&lt;/pre&gt;
&lt;h2&gt;4. Running&lt;/h2&gt;
&lt;hr&gt;
&lt;p&gt;Deploy and run the program. Clap your hand, the light turns on. Clap again, the light turns off.&lt;/p&gt;
&lt;p&gt;&lt;img src="/image/img-be4ac9a6-3d77-4950-a53d-28ff496e0991.jpg" border="0" alt="" width="1024" height="576" loading="lazy"&gt;&lt;/p&gt;
&lt;p&gt;When Clapping your hand, the indicator light on the FC-04 will also lights on.&lt;/p&gt;
&lt;p&gt;&lt;img src="/image/img-c4594a52-7734-42a3-b5ed-038b0f8cbc9f.jpg" border="0" alt="" width="1024" height="576" loading="lazy"&gt;&lt;/p&gt;</description><category>Windows Development</category><category>Internet of Things</category><guid isPermaLink="false">713dae50-82b6-4cbb-bd9c-c6c7de6f3a9a</guid><pubDate>2016-04-03T04:05:09Z</pubDate><updated xmlns="http://www.w3.org/2005/Atom">2016-04-03T04:05:09Z</updated></item><item><title>Windows 10 IoT: Detect Range with HC-04 Ultrasonic Sensor on Raspberry Pi 3</title><link>https://edi.wang/post/2016/4/2/windows-10-iot-hc04-ultra-sonic-distance</link><description>&lt;p&gt;HC-04是树莓派上用的比较广泛的超声波传感器，可以用来测量设备和物体之间的距离。可惜网上的资料很少有针对Windows 10的，几乎都是Python和C在Linux上的例子。&lt;/p&gt;
&lt;p&gt;hackster.io上有&lt;a href="https://www.hackster.io/AnuragVasanwala/windows-10-iot-core-ultrasonic-distance-mapper-d94d63" target="_blank" rel="noopener"&gt;一篇用HC-04做雷达的例子&lt;/a&gt;，但是设计的部件太多了，对单纯学习HC-04传感器的使用来说这个例子比较复杂。搜了一圈，英文资料最好的是这篇：&lt;a href="http://www.guruumeditation.net/en/digital-io-with-windows-10-iot-raspberry-pi-2-and-the-ultrasonic-ranging-module-hc-sr04/"&gt;http://www.guruumeditation.net/en/digital-io-with-windows-10-iot-raspberry-pi-2-and-the-ultrasonic-ranging-module-hc-sr04/&lt;/a&gt;&amp;nbsp;国内中文资料介绍Windows 10上HC-04传感器的文章似乎还没有，所以就简单总结一下步骤。&lt;/p&gt;
&lt;p&gt;&lt;img src="/image/img-8501bfc7-ff16-409b-afb4-276a10e8d661.jpg" border="0" alt="" width="1024" height="751" loading="lazy"&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;一、物理连接&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;对于没有多少物理和电子知识基础的人（比如我）来说，第一步连线是比较难的。很多文章都一笔带过说&amp;ldquo;这很简单，没啥好说的&amp;rdquo;，尼玛。。。&lt;/p&gt;
&lt;p&gt;其实要解决的问题是这样的：HC-04需要接入5V电源才能工作，它的响应输出是ECHO，这个要接到树莓派的GPIO端口上使用，但是ECHO的输出是5V，GPIO只能接受3.3V左右的输入，5V会烧掉树莓派，所以必须加电阻来爆一下，至于怎么加，我总结了一个最直观的示意图，如下：&lt;/p&gt;
&lt;p&gt;&lt;img src="/image/img-c5af195c-3912-4e7f-ad0f-27eb3283bc5f.png" border="0" alt="" width="632" height="590" loading="lazy"&gt;&lt;/p&gt;
&lt;p&gt;简单来说，你需要2个电阻，一个4.7K欧，一个10K欧。为什么要这样呢，是因为根据&lt;a href="https://zh.wikipedia.org/zh-cn/%E9%9B%BB%E5%A3%93%E5%88%86%E9%85%8D%E5%AE%9A%E5%89%87" target="_blank" rel="noopener"&gt;电压分配定律&lt;/a&gt;，如上图所示，4.7K和10K电阻的组合可以得到3.4V的输出。这对GPIO来说是可以接受的。如果你有别的值的电阻可以让输出正好3.3V也是可以的。我在淘宝买的常用电阻包没有办法配出正好3.3V，所以只能用4.7K和10K的组合。（上图的4.3K R1标错了）&lt;/p&gt;
&lt;p&gt;&lt;img src="/image/img-d9ca6072-3b1d-4897-b93d-c47f17d97e2b.png" border="0" alt="" width="2580" height="990" loading="lazy"&gt;&lt;/p&gt;
&lt;p&gt;然后，传感器和树莓派的连接也可以看上面这张图来连。VCC接树莓派的5V输出，TRIG接GPIO 23，ECHO通过一个4.3K电阻之后接GPIO 24，GND接Ground。&lt;/p&gt;
&lt;p&gt;&lt;img src="/image/img-b8f77f7c-8ba4-4d6d-a3ed-b2e855bde04d.jpg" border="0" alt="" width="1024" height="576" loading="lazy"&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;二、代码&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;爆代码就可以用一个现成的类，这是国外的&amp;nbsp;&lt;a href="http://www.guruumeditation.net/en/digital-io-with-windows-10-iot-raspberry-pi-2-and-the-ultrasonic-ranging-module-hc-sr04/"&gt;http://www.guruumeditation.net/en/digital-io-with-windows-10-iot-raspberry-pi-2-and-the-ultrasonic-ranging-module-hc-sr04/&lt;/a&gt;&amp;nbsp; 这篇文章里抄过来的。（稍微改进过）&lt;/p&gt;
&lt;pre&gt;using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Windows.Devices.Gpio;

namespace HC04
{
    public class HC04UltrasonicDistanceSensor
    {
        private GpioPin Pin_Trig, Pin_Echo;
        public double? Distance { get; }

        /// &amp;lt;summary&amp;gt;
        /// Available Gpio Pins. Refer: https://ms-iot.github.io/content/en-US/win10/samples/PinMappingsRPi2.htm
        /// &amp;lt;/summary&amp;gt;
        public enum AvailableGpioPin : int
        {
            /// &amp;lt;summary&amp;gt;
            /// Raspberry Pi 2 - Header Pin Number : 29
            /// &amp;lt;/summary&amp;gt;
            GpioPin_5 = 5,
            /// &amp;lt;summary&amp;gt;
            /// Raspberry Pi 2 - Header Pin Number : 31
            /// &amp;lt;/summary&amp;gt;
            GpioPin_6 = 6,
            /// &amp;lt;summary&amp;gt;
            /// Raspberry Pi 2 - Header Pin Number : 32
            /// &amp;lt;/summary&amp;gt;
            GpioPin_12 = 12,
            /// &amp;lt;summary&amp;gt;
            /// Raspberry Pi 2 - Header Pin Number : 33
            /// &amp;lt;/summary&amp;gt;
            GpioPin_13 = 13,
            /// &amp;lt;summary&amp;gt;
            /// Raspberry Pi 2 - Header Pin Number : 36
            /// &amp;lt;/summary&amp;gt;
            GpioPin_16 = 16,
            /// &amp;lt;summary&amp;gt;
            /// Raspberry Pi 2 - Header Pin Number : 12
            /// &amp;lt;/summary&amp;gt;
            GpioPin_18 = 18,
            /// &amp;lt;summary&amp;gt;
            /// Raspberry Pi 2 - Header Pin Number : 15
            /// &amp;lt;/summary&amp;gt;
            GpioPin_22 = 22,
            /// &amp;lt;summary&amp;gt;
            /// Raspberry Pi 2 - Header Pin Number : 16
            /// &amp;lt;/summary&amp;gt;
            GpioPin_23 = 23,
            /// &amp;lt;summary&amp;gt;
            /// Raspberry Pi 2 - Header Pin Number : 18
            /// &amp;lt;/summary&amp;gt;
            GpioPin_24 = 24,
            /// &amp;lt;summary&amp;gt;
            /// Raspberry Pi 2 - Header Pin Number : 22
            /// &amp;lt;/summary&amp;gt;
            GpioPin_25 = 25,
            /// &amp;lt;summary&amp;gt;
            /// Raspberry Pi 2 - Header Pin Number : 37
            /// &amp;lt;/summary&amp;gt;
            GpioPin_26 = 26,
            /// &amp;lt;summary&amp;gt;
            /// Raspberry Pi 2 - Header Pin Number : 13
            /// &amp;lt;/summary&amp;gt;
            GpioPin_27 = 27
        }

        public HC04UltrasonicDistanceSensor(AvailableGpioPin trigPin, AvailableGpioPin echoPin)
        {
            var gpio = GpioController.GetDefault();

            Pin_Trig = gpio.OpenPin((int)trigPin);
            Pin_Trig.SetDriveMode(GpioPinDriveMode.Output);
            Pin_Trig.Write(GpioPinValue.Low);

            Pin_Echo = gpio.OpenPin((int)echoPin);
            Pin_Echo.SetDriveMode(GpioPinDriveMode.Input);
        }

        public double GetDistance()
        {
            var mre = new ManualResetEventSlim(false);

            //Send a 10&amp;micro;s pulse to start the measurement
            Pin_Trig.Write(GpioPinValue.High);
            mre.Wait(TimeSpan.FromMilliseconds(0.01));
            Pin_Trig.Write(GpioPinValue.Low);

            var time = PulseIn(Pin_Echo, GpioPinValue.High, 500);

            // multiply by speed of sound in milliseconds (34000) divided by 2 (cause pulse make rountrip)
            var distance = time * 17000;
            return distance;
        }

        private double PulseIn(GpioPin pin, GpioPinValue value, ushort timeout)
        {
            var sw = new Stopwatch();
            var swTimeout = new Stopwatch();

            swTimeout.Start();

            // Wait for pulse
            while (pin.Read() != value)
            {
                if (swTimeout.ElapsedMilliseconds &amp;gt; timeout)
                    return 3.5;
            }
            sw.Start();

            // Wait for pulse end
            while (pin.Read() == value)
            {
                if (swTimeout.ElapsedMilliseconds &amp;gt; timeout)
                    return 3.4;
            }
            sw.Stop();

            return sw.Elapsed.TotalSeconds;
        }
    }
}
&lt;/pre&gt;
&lt;p&gt;使用方法如下：&lt;/p&gt;
&lt;pre&gt;public sealed partial class MainPage : Page
{
    public HC04UltrasonicDistanceSensor Hc04UltrasonicDistanceSensor { get; set; }
    private DispatcherTimer _timer;

    public MainPage()
    {
        this.InitializeComponent();
        Hc04UltrasonicDistanceSensor = new HC04UltrasonicDistanceSensor(
            HC04UltrasonicDistanceSensor.AvailableGpioPin.GpioPin_23, 
            HC04UltrasonicDistanceSensor.AvailableGpioPin.GpioPin_24);
    }

    protected override void OnNavigatedTo(NavigationEventArgs e)
    {
        base.OnNavigatedTo(e);

        _timer = new DispatcherTimer();

        _timer.Tick += (sender, o) =&amp;gt;
        {
            var distance = Hc04UltrasonicDistanceSensor.GetDistance();
            var s = $"Distance : {distance} cm";
            TxtDistance.Text = s;
            Debug.WriteLine(s);
        };

        _timer.Interval = TimeSpan.FromSeconds(1);

        _timer.Start();
    }
}
&lt;/pre&gt;
&lt;p&gt;简单解释一下：&lt;/p&gt;
&lt;p&gt;MainPage里的timer是用来定时每一秒取一次HC-04传感器返回的距离的，这个值可以自己改。这个距离是public double GetDistance()这个方法计算的。计算方法见代码注释。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;三、运行&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src="/image/img-3cc9e9d6-8f9e-45c6-9b46-47e95ac37752.jpg" border="0" alt="" width="1024" height="576" loading="lazy"&gt;&lt;/p&gt;</description><category>Windows Development</category><category>Internet of Things</category><guid isPermaLink="false">51923e12-9597-4c4e-beee-291eb5d9373e</guid><pubDate>2016-04-02T10:31:43Z</pubDate><updated xmlns="http://www.w3.org/2005/Atom">2016-04-02T10:31:43Z</updated></item><item><title>Windows 10 IoT Development: Moisture Sensor for Raspberry Pi 3</title><link>https://edi.wang/post/2016/3/28/windows-10-iot-development-moisture-sensor-for-raspberry-pi-3</link><description>&lt;p&gt;I bought a moisture sensor like this recently.&lt;/p&gt;
&lt;p&gt;&lt;img src="/image/img-23c56aa1-cdba-4c1b-b751-d823b8d730ba.png" alt="" width="400" height="259" /&gt;&lt;/p&gt;
&lt;p&gt;After some research, there seem no articles to indicate how to use it in Windows 10. So I figured out myself.&lt;/p&gt;
&lt;p&gt;This sensor has 4 Pins, which are:&lt;/p&gt;
&lt;table class="table table-bordered"&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;PIN&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Usage&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AO&lt;/td&gt;
&lt;td&gt;Analog Out&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DO&amp;nbsp;&lt;/td&gt;
&lt;td&gt;Digital Out&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GND&amp;nbsp;&lt;/td&gt;
&lt;td&gt;Ground&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;VCC&amp;nbsp;&lt;/td&gt;
&lt;td&gt;Power&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;So, let's connect VCC to DC3.3v on RPi, I use PIN 01. Connect GND to any ground header, which I use PIN 09. DO to any free GPIO header, I use 21. A complete reference of the Raspberry Pi 3 GPIO Headers is shown in the below picture.&lt;/p&gt;
&lt;p&gt;&lt;img src="/image/img-22babf92-16c6-4411-8a57-d50f1345bbe8.png" /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src="/image/img-77c352dd-63ce-4750-9837-b42023631992.png" alt="" width="939" height="1272" /&gt;&lt;/p&gt;
&lt;p&gt;The completed wire connection is this:&lt;/p&gt;
&lt;p&gt;&lt;img src="/image/img-b3f01787-1b75-4409-adf9-81fcd60823de.jpg" alt="" width="825" height="464" /&gt;&lt;/p&gt;
&lt;p&gt;After power on the Raspberry Pi, we should see the power LED turns on:&lt;/p&gt;
&lt;p&gt;&lt;img src="/image/img-39a929c4-af8d-4bd3-92d7-e7b263ad03b0.jpg" alt="" width="845" height="475" /&gt;&lt;/p&gt;
&lt;p&gt;Adjust the blue knob to calibrate the sensor not to be triggered (DO LED is not on) in dry air.&lt;/p&gt;
&lt;p&gt;Use a cup of water to test if DO-LED can be triggered. If it is on as soon as the sensor touches water, then calibration is done, we can go on to write code.&lt;/p&gt;
&lt;p&gt;&lt;img src="/image/img-82386ca1-aeed-4e8c-88b8-fcd3dbbca4cf.jpg" alt="" width="850" height="478" /&gt;&lt;/p&gt;
&lt;p&gt;The sensor will output GPIO High in dry air, and GPIO Low in water. Based on this fact, my code will be:&amp;nbsp;&lt;/p&gt;
&lt;pre class="language-csharp"&gt;&lt;span class="token keyword"&gt;public&lt;/span&gt; GpioPin MoistureSensorOutputPin &lt;span class="token punctuation"&gt;{&lt;/span&gt; &lt;span class="token keyword"&gt;get&lt;/span&gt;&lt;span class="token punctuation"&gt;;&lt;/span&gt; &lt;span class="token keyword"&gt;set&lt;/span&gt;&lt;span class="token punctuation"&gt;;&lt;/span&gt; &lt;span class="token punctuation"&gt;}&lt;/span&gt;

&lt;span class="token keyword"&gt;public&lt;/span&gt; DispatcherTimer DispatcherTimer &lt;span class="token punctuation"&gt;{&lt;/span&gt; &lt;span class="token keyword"&gt;get&lt;/span&gt;&lt;span class="token punctuation"&gt;;&lt;/span&gt; &lt;span class="token keyword"&gt;set&lt;/span&gt;&lt;span class="token punctuation"&gt;;&lt;/span&gt; &lt;span class="token punctuation"&gt;}&lt;/span&gt;

&lt;span class="token keyword"&gt;public&lt;/span&gt; &lt;span class="token function"&gt;MainPage&lt;/span&gt;&lt;span class="token punctuation"&gt;(&lt;/span&gt;&lt;span class="token punctuation"&gt;)&lt;/span&gt;
&lt;span class="token punctuation"&gt;{&lt;/span&gt;
    &lt;span class="token keyword"&gt;this&lt;/span&gt;&lt;span class="token punctuation"&gt;.&lt;/span&gt;&lt;span class="token function"&gt;InitializeComponent&lt;/span&gt;&lt;span class="token punctuation"&gt;(&lt;/span&gt;&lt;span class="token punctuation"&gt;)&lt;/span&gt;&lt;span class="token punctuation"&gt;;&lt;/span&gt;
    Loaded &lt;span class="token operator"&gt;+&lt;/span&gt;&lt;span class="token operator"&gt;=&lt;/span&gt; OnLoaded&lt;span class="token punctuation"&gt;;&lt;/span&gt;
&lt;span class="token punctuation"&gt;}&lt;/span&gt;

&lt;span class="token keyword"&gt;private&lt;/span&gt; &lt;span class="token keyword"&gt;async&lt;/span&gt; &lt;span class="token keyword"&gt;void&lt;/span&gt; &lt;span class="token function"&gt;OnLoaded&lt;/span&gt;&lt;span class="token punctuation"&gt;(&lt;/span&gt;&lt;span class="token keyword"&gt;object&lt;/span&gt; sender&lt;span class="token punctuation"&gt;,&lt;/span&gt; RoutedEventArgs routedEventArgs&lt;span class="token punctuation"&gt;)&lt;/span&gt;
&lt;span class="token punctuation"&gt;{&lt;/span&gt;
    &lt;span class="token keyword"&gt;var&lt;/span&gt; ctl &lt;span class="token operator"&gt;=&lt;/span&gt; &lt;span class="token keyword"&gt;await&lt;/span&gt; GpioController&lt;span class="token punctuation"&gt;.&lt;/span&gt;&lt;span class="token function"&gt;GetDefaultAsync&lt;/span&gt;&lt;span class="token punctuation"&gt;(&lt;/span&gt;&lt;span class="token punctuation"&gt;)&lt;/span&gt;&lt;span class="token punctuation"&gt;;&lt;/span&gt;
    MoistureSensorOutputPin &lt;span class="token operator"&gt;=&lt;/span&gt; ctl&lt;span class="token operator"&gt;?&lt;/span&gt;&lt;span class="token punctuation"&gt;.&lt;/span&gt;&lt;span class="token function"&gt;OpenPin&lt;/span&gt;&lt;span class="token punctuation"&gt;(&lt;/span&gt;&lt;span class="token number"&gt;21&lt;/span&gt;&lt;span class="token punctuation"&gt;)&lt;/span&gt;&lt;span class="token punctuation"&gt;;&lt;/span&gt;
    &lt;span class="token keyword"&gt;if&lt;/span&gt; &lt;span class="token punctuation"&gt;(&lt;/span&gt;MoistureSensorOutputPin &lt;span class="token operator"&gt;!=&lt;/span&gt; &lt;span class="token keyword"&gt;null&lt;/span&gt;&lt;span class="token punctuation"&gt;)&lt;/span&gt;
    &lt;span class="token punctuation"&gt;{&lt;/span&gt;
        MoistureSensorOutputPin&lt;span class="token punctuation"&gt;.&lt;/span&gt;&lt;span class="token function"&gt;SetDriveMode&lt;/span&gt;&lt;span class="token punctuation"&gt;(&lt;/span&gt;GpioPinDriveMode&lt;span class="token punctuation"&gt;.&lt;/span&gt;Input&lt;span class="token punctuation"&gt;)&lt;/span&gt;&lt;span class="token punctuation"&gt;;&lt;/span&gt;
        DispatcherTimer &lt;span class="token operator"&gt;=&lt;/span&gt; &lt;span class="token keyword"&gt;new&lt;/span&gt; &lt;span class="token class-name"&gt;DispatcherTimer&lt;/span&gt;&lt;span class="token punctuation"&gt;(&lt;/span&gt;&lt;span class="token punctuation"&gt;)&lt;/span&gt;
        &lt;span class="token punctuation"&gt;{&lt;/span&gt;
            Interval &lt;span class="token operator"&gt;=&lt;/span&gt; TimeSpan&lt;span class="token punctuation"&gt;.&lt;/span&gt;&lt;span class="token function"&gt;FromSeconds&lt;/span&gt;&lt;span class="token punctuation"&gt;(&lt;/span&gt;&lt;span class="token number"&gt;1&lt;/span&gt;&lt;span class="token punctuation"&gt;)&lt;/span&gt;
        &lt;span class="token punctuation"&gt;}&lt;/span&gt;&lt;span class="token punctuation"&gt;;&lt;/span&gt;
        DispatcherTimer&lt;span class="token punctuation"&gt;.&lt;/span&gt;Tick &lt;span class="token operator"&gt;+&lt;/span&gt;&lt;span class="token operator"&gt;=&lt;/span&gt; &lt;span class="token punctuation"&gt;(&lt;/span&gt;O_O&lt;span class="token punctuation"&gt;,&lt;/span&gt; n_n&lt;span class="token punctuation"&gt;)&lt;/span&gt; &lt;span class="token operator"&gt;=&lt;/span&gt;&lt;span class="token operator"&gt;&amp;amp;&lt;/span&gt;gt&lt;span class="token punctuation"&gt;;&lt;/span&gt;
        &lt;span class="token punctuation"&gt;{&lt;/span&gt;
            &lt;span class="token keyword"&gt;var&lt;/span&gt; pinValue &lt;span class="token operator"&gt;=&lt;/span&gt; MoistureSensorOutputPin&lt;span class="token punctuation"&gt;.&lt;/span&gt;&lt;span class="token function"&gt;Read&lt;/span&gt;&lt;span class="token punctuation"&gt;(&lt;/span&gt;&lt;span class="token punctuation"&gt;)&lt;/span&gt;&lt;span class="token punctuation"&gt;;&lt;/span&gt;
            &lt;span class="token keyword"&gt;if&lt;/span&gt; &lt;span class="token punctuation"&gt;(&lt;/span&gt;pinValue &lt;span class="token operator"&gt;==&lt;/span&gt; GpioPinValue&lt;span class="token punctuation"&gt;.&lt;/span&gt;High&lt;span class="token punctuation"&gt;)&lt;/span&gt;
            &lt;span class="token punctuation"&gt;{&lt;/span&gt;
                Debug&lt;span class="token punctuation"&gt;.&lt;/span&gt;&lt;span class="token function"&gt;WriteLine&lt;/span&gt;&lt;span class="token punctuation"&gt;(&lt;/span&gt;&lt;span class="token string"&gt;"Dry"&lt;/span&gt;&lt;span class="token punctuation"&gt;)&lt;/span&gt;&lt;span class="token punctuation"&gt;;&lt;/span&gt;
            &lt;span class="token punctuation"&gt;}&lt;/span&gt;
            &lt;span class="token keyword"&gt;else&lt;/span&gt;
            &lt;span class="token punctuation"&gt;{&lt;/span&gt;
                Debug&lt;span class="token punctuation"&gt;.&lt;/span&gt;&lt;span class="token function"&gt;WriteLine&lt;/span&gt;&lt;span class="token punctuation"&gt;(&lt;/span&gt;&lt;span class="token string"&gt;"Water Detected!"&lt;/span&gt;&lt;span class="token punctuation"&gt;)&lt;/span&gt;&lt;span class="token punctuation"&gt;;&lt;/span&gt;
            &lt;span class="token punctuation"&gt;}&lt;/span&gt;
        &lt;span class="token punctuation"&gt;}&lt;/span&gt;&lt;span class="token punctuation"&gt;;&lt;/span&gt;
        DispatcherTimer&lt;span class="token punctuation"&gt;.&lt;/span&gt;&lt;span class="token function"&gt;Start&lt;/span&gt;&lt;span class="token punctuation"&gt;(&lt;/span&gt;&lt;span class="token punctuation"&gt;)&lt;/span&gt;&lt;span class="token punctuation"&gt;;&lt;/span&gt;
    &lt;span class="token punctuation"&gt;}&lt;/span&gt;
&lt;span class="token punctuation"&gt;}&lt;/span&gt;&lt;/pre&gt;
&lt;p&gt;Please pay attention not to use this event handler:&lt;/p&gt;
&lt;pre class="language-csharp"&gt;&lt;span class="token keyword"&gt;public&lt;/span&gt; &lt;span class="token keyword"&gt;event&lt;/span&gt; TypedEventHandler&lt;span class="token operator"&gt;&amp;lt;&lt;/span&gt;GpioPin&lt;span class="token punctuation"&gt;,&lt;/span&gt; GpioPinValueChangedEventArgs&lt;span class="token operator"&gt;&amp;gt;&lt;/span&gt; ValueChanged&lt;span class="token punctuation"&gt;;&lt;/span&gt;&lt;/pre&gt;
&lt;p&gt;Because this will output the result multiple times like this:&lt;/p&gt;
&lt;p&gt;&lt;img src="/image/img-2628a53f-2585-4e94-b0ae-ebd7ee9853b3.png" alt="" width="222" height="245" /&gt;&lt;/p&gt;</description><category>Windows Development</category><category>Internet of Things</category><guid isPermaLink="false">015d1407-4fef-4f2a-b82f-2442e142201e</guid><pubDate>2016-03-28T11:38:14Z</pubDate><updated xmlns="http://www.w3.org/2005/Atom">2016-03-28T11:38:14Z</updated></item><item><title>GY-30 (BH1750FVI) Light Sensor with Windows 10 IoT</title><link>https://edi.wang/post/2016/3/26/windows-10-iot-gy-30-light-sensor</link><description>&lt;p&gt;I purchased a GY-30 light sensor recently, this model is also known as "BH1550FVI". There seems no sample for this sensor on Windows 10 yet, so I tried to make one.&lt;/p&gt;
&lt;p&gt;The light sensor looks like this:&lt;/p&gt;
&lt;p&gt;&lt;img src="/image/img-40b0896b-b6f0-4dc3-8ae1-d076732c40da.jpg" alt="" width="650" height="650" /&gt;&lt;/p&gt;
&lt;p&gt;My board is a Raspberry Pi 3, the GPIO layout is shown in this table:&lt;/p&gt;
&lt;p&gt;&lt;img src="/image/pi3_gpio.png" alt="" width="920" height="960" /&gt;&lt;/p&gt;
&lt;p&gt;Because GY-30 is an I2C device, so we can not use GPIO to drive it directly, we must use the I2C ports. In addition, because of it's an I2C device, it does not support hot plug. We should power off the Raspberry Pi, connect the sensor and then power on the Raspberry Pi, so that the system can identify the sensor.&lt;/p&gt;
&lt;p&gt;The physical connections are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;VCC - PIN01 - 3.3V&lt;/li&gt;
&lt;/ul&gt;
&lt;ul&gt;
&lt;li&gt;ADO - [NONE] - Not using&lt;/li&gt;
&lt;/ul&gt;
&lt;ul&gt;
&lt;li&gt;SDA - PIN 03- SDA1 I2C&lt;/li&gt;
&lt;/ul&gt;
&lt;ul&gt;
&lt;li&gt;SCL - PIN 05 - SCL1 I2C&lt;/li&gt;
&lt;/ul&gt;
&lt;ul&gt;
&lt;li&gt;GND - PIN 06 - Ground&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;br /&gt;&lt;img src="/image/img-e8ffba18-7f1a-433c-be39-56c417c9d4bb.png" alt="" width="1638" height="1044" /&gt;&lt;/p&gt;
&lt;p&gt;And then we could start coding. The official sample of I2C operations on Windows 10 IoT website is this:&lt;/p&gt;
&lt;pre&gt;I2C
        Pin 3 - I2C1 SDA
        Pin 5 - I2C1 SCL

using Windows.Devices.Enumeration;
using Windows.Devices.I2c;
 
public async void I2C()
{
    // Get a selector string for bus "I2C1"
    string aqs = I2cDevice.GetDeviceSelector("I2C1");
     
    // Find the I2C bus controller with our selector string
    var dis = await DeviceInformation.FindAllAsync(aqs);
    if (dis.Count == 0)
        return; // bus not found
     
    // 0x40 is the I2C device address
    var settings = new I2cConnectionSettings(0x40);
     
    // Create an I2cDevice with our selected bus controller and I2C settings
    using (I2cDevice device = await I2cDevice.FromIdAsync(dis[0].Id, settings))
    {
        byte[] writeBuf = { 0x01, 0x02, 0x03, 0x04 };
        device.Write(writeBuf);
    }
}&lt;/pre&gt;
&lt;p&gt;There's one thing interesting in the code, that is:&lt;/p&gt;
&lt;pre&gt;    // 0x40 is the I2C device address
    var settings = new I2cConnectionSettings(0x40);&lt;/pre&gt;
&lt;p&gt;So how do we get this address of "0x40"? I did some research and found every I2C devices are using different addresses. Generally, you will find it in their product manual. If you can not find it, &amp;nbsp;you can use this method in Raspbian system:&lt;/p&gt;
&lt;p&gt;&lt;a href="http://www.raspberrypi-spy.co.uk/2014/11/enabling-the-i2c-interface-on-the-raspberry-pi/" target="_blank"&gt;http://www.raspberrypi-spy.co.uk/2014/11/enabling-the-i2c-interface-on-the-raspberry-pi/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;My GY-30 sensor's I2C address is 0x23, if I am right, every GY-30 sensor that connecting using DC3.3v power will have the address of 0x23.&lt;/p&gt;
&lt;p&gt;My complete code for the&amp;nbsp;GY30LightSensor (This code is&amp;nbsp;referring the Ardunio version &lt;a href="http://blog.simtronyx.de/en/measurement-of-illuminance-with-a-bh1750fvi-breakout-board-gy-30-and-an-arduino-uno/"&gt;here&lt;/a&gt;):&lt;/p&gt;
&lt;pre&gt;using System;
using System.Diagnostics;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using Windows.Devices.Enumeration;
using Windows.Devices.I2c;

namespace GY30LightSensor
{
    public class GY30LightSensorEventArgs : EventArgs
    {
        public int? Lux { get; set; }

        public GY30LightSensorEventArgs(int? lux)
        {
            Lux = lux;
        }
    }

    public class GY30LightSensor
    {
        public int Bh1750Address =&amp;gt; 0x23;

        public I2cDevice I2CLightSensor { get; private set; }

        private Timer PeriodicTimer { get; set; }

        public int TimerIntervalMs { get; set; }

        public event ReadingEventHandler Reading;

        public delegate void ReadingEventHandler(object sender, GY30LightSensorEventArgs e);

        private void OnReading(int lux)
        {
            Reading?.Invoke(lux, new GY30LightSensorEventArgs(lux));
        }

        public GY30LightSensor(int timerIntervalMs = 100)
        {
            TimerIntervalMs = timerIntervalMs;
        }

        public async Task InitLightSensorAsync()
        {
            string aqs = I2cDevice.GetDeviceSelector();
            /* Get a selector string that will return all I2C controllers on the system */
            var dis = await DeviceInformation.FindAllAsync(aqs);
            /* Find the I2C bus controller device with our selector string           */
            if (dis.Count == 0)
            {
                throw new FileNotFoundException("No I2C controllers were found on the system");
            }

            var settings = new I2cConnectionSettings(Bh1750Address)
            {
                BusSpeed = I2cBusSpeed.FastMode
            };

            I2CLightSensor = await I2cDevice.FromIdAsync(dis[0].Id, settings);
            /* Create an I2cDevice with our selected bus controller and I2C settings */
            if (I2CLightSensor == null)
            {
                throw new UnauthorizedAccessException(string.Format("Slave address {0} on I2C Controller {1} is currently in use by " +
                                 "another application. Please ensure that no other applications are using I2C.", settings.SlaveAddress, dis[0].Id));
            }

            /* Write the register settings */
            try
            {
                I2CLightSensor.Write(new byte[] { 0x10 }); // 1 [lux] aufloesung
            }
            /* If the write fails display the error and stop running */
            catch (Exception ex)
            {
                Debug.WriteLine("Failed to communicate with device: " + ex.Message);
                throw;
            }

            PeriodicTimer = new Timer(this.TimerCallback, null, 0, TimerIntervalMs);
        }

        private void TimerCallback(object state)
        {
            var lux = ReadI2CLux();
            OnReading(lux);
        }

        private int ReadI2CLux()
        {
            byte[] regAddrBuf = new byte[] { 0x23 };
            byte[] readBuf = new byte[2];
            I2CLightSensor.WriteRead(regAddrBuf, readBuf);

            // is this calculation correct?
            var valf = ((readBuf[0] &amp;lt;&amp;lt; 8) | readBuf[1]) / 1.2; 

            return (int)valf;
        }
    }
}
&lt;/pre&gt;
&lt;p&gt;Here is how to use it:&lt;/p&gt;
&lt;pre&gt;public GY30LightSensor Gy30LightSensor { get; set; }

public MainPage()
{
    this.InitializeComponent();
    Gy30LightSensor = new GY30LightSensor();

    Loaded += OnLoaded;
}

private async void OnLoaded(object sender, RoutedEventArgs routedEventArgs)
{
    await Gy30LightSensor.InitLightSensorAsync();
    Gy30LightSensor.Reading += (o, args) =&amp;gt;
    {
        var task = this.Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =&amp;gt;
        {
            TxtLux.Text = args.Lux + " lx";
        });
    };
}
&lt;/pre&gt;
&lt;p&gt;Now, if you have some light shining on the sensor, you will get Lx value.&lt;/p&gt;
&lt;p&gt;P.S. I have not verified if the calculation of the Lx is correct:&lt;/p&gt;
&lt;pre&gt;var valf = ((readBuf[0] &amp;lt;&amp;lt; 8) | readBuf[1]) / 1.2; &lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;</description><category>Windows Development</category><category>Internet of Things</category><guid isPermaLink="false">636f8804-e280-4084-9d18-e1ce755d3947</guid><pubDate>2016-03-26T10:31:27Z</pubDate><updated xmlns="http://www.w3.org/2005/Atom">2016-03-26T10:31:27Z</updated></item><item><title>Windows 10 IoT开发：UWP应用调用关机和重启命令</title><link>https://edi.wang/post/2016/3/24/windows-10-iot-uwp-shutdown</link><description>&lt;p&gt;UWP通常是没有让设备关机和重启的权限的。但是在Windows 10 IoT Core中是有办法实现的。安装Windows 10 Iot Core之后默认启动的那个欢迎页面右上角是有关机和重启按钮的。其实就是个UWP，它的源代码在：&lt;a href="https://github.com/ms-iot/samples/tree/develop/IoTCoreDefaultApp"&gt;https://github.com/ms-iot/samples/tree/develop/IoTCoreDefaultApp&lt;/a&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;分析源代码可以发现，关机和重启是通过ShutDownManager这个class实现的。&lt;/p&gt;
&lt;pre class="language-csharp"&gt;&lt;code&gt;#region Assembly Windows.System.SystemManagementContract, Version=2.0.0.0, Culture=neutral, PublicKeyToken=null, ContentType=WindowsRuntime
// C:\Program Files (x86)\Windows Kits\10\References\Windows.System.SystemManagementContract\2.0.0.0\Windows.System.SystemManagementContract.winmd
#endregion

using System;
using Windows.Foundation.Metadata;

namespace Windows.System
{
    [ContractVersion(typeof(SystemManagementContract), 65536)]
    [Static(typeof(IShutdownManagerStatics), 65536, "Windows.System.SystemManagementContract")]
    [Threading(ThreadingModel.Both)]
    public static class ShutdownManager
    {
        public static void BeginShutdown(ShutdownKind shutdownKind, TimeSpan timeout);
        public static void CancelShutdown();
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;其中ShutdownKind的枚举类型定义如下：&lt;/p&gt;
&lt;pre class="language-csharp"&gt;&lt;code&gt;#region Assembly Windows.System.SystemManagementContract, Version=2.0.0.0, Culture=neutral, PublicKeyToken=null, ContentType=WindowsRuntime
// C:\Program Files (x86)\Windows Kits\10\References\Windows.System.SystemManagementContract\2.0.0.0\Windows.System.SystemManagementContract.winmd
#endregion

using Windows.Foundation.Metadata;

namespace Windows.System
{
    [ContractVersion(typeof(SystemManagementContract), 65536)]
    public enum ShutdownKind
    {
        Shutdown = 0,
        Restart = 1
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Shutdown就是关机，Restart就是重启。&lt;/p&gt;
&lt;p&gt;然而，如果我们要在自己的IoT应用里面调用，会碰到个巨坑。首先，代码是这样的：&lt;/p&gt;
&lt;pre class="language-csharp"&gt;&lt;code&gt;private void ShutdownHelper(ShutdownKind kind)
{
    ShutdownManager.BeginShutdown(kind, TimeSpan.FromSeconds(0.5));
}

private void BtnRestart_OnClick(object sender, RoutedEventArgs e)
{
    ShutdownHelper(ShutdownKind.Restart);
}

private void BtnShutdown_OnClick(object sender, RoutedEventArgs e)
{
    ShutdownHelper(ShutdownKind.Shutdown);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;运行就会爆炸！&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;抛出异常：没有权限&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;通常在UWP里，调用没有权限的API会爆炸的解决办法是在应用的manifest文件里勾选对应的权限。&lt;span style="color: #ff0000;"&gt;&lt;strong&gt;然而这个关机和重启，在VS的界面里是没有选项的。&lt;/strong&gt;&lt;/span&gt;我们需要用XML编辑器打开&lt;/p&gt;
&lt;p&gt;Package.appxmanifest，然后手工加权限：&lt;/p&gt;
&lt;p&gt;在Package节点的属性里加个命名空间&lt;/p&gt;
&lt;pre&gt;xmlns:iot="http://schemas.microsoft.com/appx/manifest/iot/windows10"&lt;/pre&gt;
&lt;p&gt;然后再Capabilities节点里加这个：&lt;/p&gt;
&lt;pre&gt;&amp;lt;iot:Capability Name="systemManagement" /&amp;gt;&lt;/pre&gt;
&lt;p&gt;最后看起来就像是这样：&lt;/p&gt;
&lt;pre&gt;&amp;lt;?xml version="1.0" encoding="utf-8"?&amp;gt;
&amp;lt;Package xmlns="http://schemas.microsoft.com/appx/manifest/foundation/windows10" 
         xmlns:mp="http://schemas.microsoft.com/appx/2014/phone/manifest" 
         xmlns:uap="http://schemas.microsoft.com/appx/manifest/uap/windows10" 
         xmlns:iot="http://schemas.microsoft.com/appx/manifest/iot/windows10"
         IgnorableNamespaces="uap mp"&amp;gt;
	...
  &amp;lt;Capabilities&amp;gt;
	...
    &amp;lt;iot:Capability Name="systemManagement" /&amp;gt;
	...
  &amp;lt;/Capabilities&amp;gt;
&amp;lt;/Package&amp;gt;&lt;/pre&gt;
&lt;p&gt;现在再运行就OK了&amp;hellip;&amp;hellip;&lt;/p&gt;</description><category>Windows Development</category><category>Internet of Things</category><guid isPermaLink="false">176811df-4d41-4753-8198-cc3156bd39c3</guid><pubDate>2016-03-24T07:19:58Z</pubDate><updated xmlns="http://www.w3.org/2005/Atom">2016-03-24T07:19:58Z</updated></item></channel></rss>