无法将Windows 10 IoT核心版设备从封闭式防火墙后面连接到Azure IoT中心 [英] Trouble connecting a Windows 10 IoT Core device to Azure IoT Hub from behind closed firewall

查看:107
本文介绍了无法将Windows 10 IoT核心版设备从封闭式防火墙后面连接到Azure IoT中心的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在地下室有一个不错的小项目,其中包括一个连接到Raspberry Pi 3的LED.相当复杂,是的,我知道.除此之外,该Raspberry Pi还运行Windows 10 IoT核心版,我的目标是通过Azure Iot Hub服务中的直接方法来启用和禁用该LED.

除了我将在一个单独的问题中询问的奇怪的UI问题之外,该系统或多或少都在正常工作.我已经编写了一个UWP项目,它可以很好地切换LED.回到UI工作时的状态,有一次,我可以使用可单击的按钮切换灯光.同样,非常复杂.无论如何,Azure IoT中心已启动并正在运行,已正确配置Raspberry Pi,并在设备上设置了直接方法.我打算使用Azure Functions使得可以使用API​​调用来调用直接方法,但是遇到问题后,我简化了这一点,现在通过Azure门户的内置直接方法"对话框进行测试./p>

我的问题是这样的:当我从Azure门户的工具中调用直接方法时(顺便说一句,这有点烦人),然后等待20秒以得到5秒钟然后又消失了弹出,我收到一条消息,提示呼叫等待连接时超时".确切的错误如下:

DeviceNotFoundException: Device {"Message":"{\"errorCode\":404103,\"trackingId\":\"9b39dbe7f22c4acda1abbaa1ccc4c410-G:3-TimeStamp:01/11/2018 22:31:55\",\"message\":\"Timed out waiting for device to connect.\",\"info\":{\"timeout\":\"00:00:00\"},\"timestampUtc\":\"2018-01-11T22:31:55.1883184Z\"}","ExceptionMessage":""} not registered

虽然我不能100%地确定未注册"部分,但我可以肯定地确定此问题是由我的互联网路由器引起的.不久之前,我的家庭切换到Hughesnet作为我们的ISP,因此我们失去了接受入站流量的能力(即,该网络现在对外部请求已关闭),并且我们无法设置NAT以在任一端口中转发一两个端口(没有可公开访问的IP地址,时段).虽然我很想抱怨HughesNet服务的多个方面,但这是另一回事.

对于与此项目类似的项目,我已经能够设置一种持久的解决方法,例如反向SSH隧道.困难在于,我不确定如何在该项目中做到这一点.

我的问题是:有没有办法让我的Raspberry Pi在这里接受来自Azure IoT中心的直接方法调用?某种SSH隧道,也许吗?

更新:代码

using System;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Media;
using Windows.Devices.Gpio;
using System.Text;
using Windows.UI;
using Microsoft.Azure.Devices.Client;
using System.Threading.Tasks;

// The Blank Page item template is documented at https://go.microsoft.com/fwlink/?LinkId=402352&clcid=0x409

namespace UltimateLED2
{
public sealed partial class MainPage : Page
{
    const string DeviceId = "**********";
    const string DeviceKey = "**************************************";
    const string HubEndpoint = "*****.azure-devices.net";
    const int LEDPinNumber = 5;
    GpioPin LEDPin;
    bool LEDPinState;
    Brush StatusNormalBrush;
    DeviceClient deviceClient;

    public MainPage()
    {
        this.InitializeComponent();
        StatusNormalBrush = StatusIndicator.Fill;
        if (!TryInitGPIO().Result)
        {
            WriteMessage("GPIO initialization failed");
        }
        deviceClient = DeviceClient.Create(HubEndpoint,
            AuthenticationMethodFactory.CreateAuthenticationWithRegistrySymmetricKey(DeviceId, DeviceKey), TransportType.Mqtt_WebSocket_Only); 
        deviceClient.SetMethodHandlerAsync("ToggleLED", new MethodCallback(ToggleLEDMethod), null);
    }

    private async Task<MethodResponse> ToggleLEDMethod(MethodRequest methodRequest, object userContext)
    {
        WriteMessage("Recieved Direct Request to toggle LED");
        LEDPinState = !LEDPinState;
        await UpdateLight();
        return new MethodResponse(Encoding.UTF8.GetBytes("{\"LightIs\":\"" + (LEDPinState ? "On" : "Off") + "\"}"), 200);
    }

    public async Task<bool> TryInitGPIO()
    {
        GpioController gpioController = GpioController.GetDefault();
        if (gpioController == null)
        {
            WriteMessage("This Device is not IoT friendly!  (No GPIO Controller found)", true);
            return false;
        }
        if (gpioController.TryOpenPin(LEDPinNumber, GpioSharingMode.Exclusive, out LEDPin, out GpioOpenStatus openStatus))
        {
            WriteMessage($"Output Pin ({LEDPinNumber}) Opened Successfully!!");
        }
        else
        {
            WriteMessage($"Output Pin ({LEDPinNumber}) Failed to Open", true);
            return false;
        }

        LEDPin.SetDriveMode(GpioPinDriveMode.Output);
        LEDPin.Write(GpioPinValue.High);
        LEDPinState = true;
        await UpdateLight();
        WriteMessage("Output Pin initialized and on");
        return true;
    }

    private void WriteMessage(string message, bool isError = false)
    {
        StringBuilder sb = new StringBuilder(OutputBox.Text);
        if (isError)
        {
            sb.AppendLine();
            sb.AppendLine("*************ERROR**************");
        }
        sb.AppendLine(message);
        if (isError)
        {
            sb.AppendLine("*************END ERROR**************");
            sb.AppendLine();
        }
        OutputBox.Text = sb.ToString();  //Upon reviewing my code before posting it here, I noticed that this line of code directly modifies a UI element, and yet no errors are thrown (that I can see), whereas changing the color of my little light indicator circle below threw a threading error when I attempted to change the UI from another thread.  This function can be called synchronously from async methods, that run on different threads... does that not mean this function would be called on the different thread it was called from? 
    }

    private async void ManualToggle_Click(object sender, RoutedEventArgs e)
    {
        WriteMessage("Recieved Manual Toggle");
        LEDPinState = !LEDPinState;
        await UpdateLight();
    }

    private async Task UpdateLight()
    {

        LEDPin.Write(LEDPinState ? GpioPinValue.High : GpioPinValue.Low);
        await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
         {
             StatusIndicator.Fill = LEDPinState ? new SolidColorBrush(Colors.Red) : StatusNormalBrush;
         });


    }
}
}

谢谢! 卢卡斯·尼沃纳(Lucas Niewohner)

解决方案

我已经使用您提供的代码测试了该问题,即使该代码不完整,我也对其进行了少许修改以便可以运行.方法 ToggleLED 可以从Azure IoT中心调用.在您的代码中,方法 WriteMessage 需要使用Dispatcher来更新TextBox,因为直接方法调用时,它在新线程(而不是UI线程)中运行.对于您的问题,直接方法遵循请求-响应模式,用于需要立即确认其结果的通信,通常是对设备的交互式控制,例如打开风扇.使用MQTT连接Azure IoT中心时,如果没有关闭操作或网络保持活动状态,连接将不会关闭,还有其他一些例外.此外,IoT中心不支持QoS 2消息.如果某个设备应用发布具有QoS 2的消息,则IoT中心将关闭网络连接.

代码:

public sealed partial class MainPage : Page
{
    const string DeviceId = "device1";
    const string DeviceKey = "<my-device-primarykey>";
    const string HubEndpoint = "<my-iot-hub>";
    const int LEDPinNumber = 5;
    GpioPin LEDPin;
    bool LEDPinState;
    Brush StatusNormalBrush;
    DeviceClient deviceClient;

    public MainPage()
    {
        this.InitializeComponent();

        if (!TryInitGPIO().Result)
        {
            WriteMessage("GPIO initialization failed");
        }

        deviceClient = DeviceClient.Create(HubEndpoint,
            AuthenticationMethodFactory.CreateAuthenticationWithRegistrySymmetricKey(DeviceId, DeviceKey), TransportType.Mqtt_WebSocket_Only);
        deviceClient.SetMethodHandlerAsync("ToggleLED", new MethodCallback(ToggleLEDMethod), null);
    }

    private Task<MethodResponse> ToggleLEDMethod(MethodRequest methodRequest, object userContext)
    {
        WriteMessage("Recieved Direct Request to toggle LED");
        LEDPinState = !LEDPinState;
        UpdateLight();
        return Task.FromResult(new MethodResponse(Encoding.UTF8.GetBytes("{\"LightIs\":\"" + (LEDPinState ? "On" : "Off") + "\"}"), 200));
    }

    public async Task<bool> TryInitGPIO()
    {
        GpioController gpioController = GpioController.GetDefault();
        if (gpioController == null)
        {
            WriteMessage("This Device is not IoT friendly!  (No GPIO Controller found)", true);
            return false;
        }
        if (gpioController.TryOpenPin(LEDPinNumber, GpioSharingMode.Exclusive, out LEDPin, out GpioOpenStatus openStatus))
        {
            WriteMessage($"Output Pin ({LEDPinNumber}) Opened Successfully!!");
        }
        else
        {
            WriteMessage($"Output Pin ({LEDPinNumber}) Failed to Open", true);
            return false;
        }

        LEDPin.SetDriveMode(GpioPinDriveMode.Output);
        LEDPin.Write(GpioPinValue.High);
        LEDPinState = true;
        UpdateLight();
        WriteMessage("Output Pin initialized and on");
        return true;
    }

    private async void WriteMessage(string message, bool isError = false)
    {
        await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
        {

            StringBuilder sb = new StringBuilder(OutputBox.Text);
            if (isError)
            {
                sb.AppendLine();
                sb.AppendLine("*************ERROR**************");
            }
            sb.AppendLine(message);
            if (isError)
            {
                sb.AppendLine("*************END ERROR**************");
                sb.AppendLine();
            }
            OutputBox.Text = sb.ToString();  //Upon reviewing my code before posting it here, I noticed that this line of code directly modifies a UI element, and yet no errors are thrown (that I can see), whereas changing the color of my little light indicator circle below threw a threading error when I attempted to change the UI from another thread.  This function can be called synchronously from async methods, that run on different threads... does that not mean this function would be called on the different thread it was called from? 
        });
    }

    private async void ManualToggle_Click(object sender, RoutedEventArgs e)
    {
        WriteMessage("Recieved Manual Toggle");
        LEDPinState = !LEDPinState;
        UpdateLight();
    }

    private async void UpdateLight()
    {

        LEDPin.Write(LEDPinState ? GpioPinValue.High : GpioPinValue.Low);

        await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
        {
            //StatusIndicator.Fill = LEDPinState ? new SolidColorBrush(Colors.Red) : StatusNormalBrush;
            OutputBox.Text = "UpdateLight\r\n";
        });
    }

I have a nice little project here in my basement, consisting of an LED wired into a Raspberry Pi 3. Quite complex, yes, I know. Beyond that, this Raspberry Pi is running Windows 10 IoT Core, and my goal is to make it possible to toggle that LED off and on by means of a Direct Method from the Azure Iot Hub service.

Asides from an odd UI issue that I'll be asking about in a separate question, this system is more or less working properly. I've written up a UWP project, and it toggles the LED just fine. Back when the UI was working, which it was, at one point, I was able to toggle the light with a clickable button. Again, very complex. Anyway, The Azure IoT Hub is up and running, the Raspberry Pi properly provisioned, and the Direct Method set up on the device. I was intending to use Azure Functions to make it possible to use an API call to call the Direct Method, but after experiencing problems I simplified that out, and am now testing by means of the Azure Portal's built in "Direct Method" dialog.

My problem is this: when I call the direct method from the Azure Portal's tool (which is somewhat annoying to use, by the way), and wait 20 seconds for the 5-seconds-and-then-it's-gone result pop up, I get a message saying that the call "Timed out while waiting to connect". The exact error is as follows:

DeviceNotFoundException: Device {"Message":"{\"errorCode\":404103,\"trackingId\":\"9b39dbe7f22c4acda1abbaa1ccc4c410-G:3-TimeStamp:01/11/2018 22:31:55\",\"message\":\"Timed out waiting for device to connect.\",\"info\":{\"timeout\":\"00:00:00\"},\"timestampUtc\":\"2018-01-11T22:31:55.1883184Z\"}","ExceptionMessage":""} not registered

While I'm not 100% certain about the "not registered" part, I'm fairly certain this problem stems from my internet router. Not too long ago, my household switched over to Hughesnet as our ISP, and in doing so we lost the ability to accept inbound traffic (i.e., the network is now closed to outside requests), and we have no ability to set up NAT to forward a port or two in either (there is no publicly accessible IP address, period). While I'd love to grumble about more than a few aspects of HughesNet's service, that's another matter.

For projects similar to this one, I have been able to set up a persistent workaround, such a reverse SSH tunnel. The difficulty is, I'm not particularly sure how to do that with this project.

My question is: Is there a way to get my Raspberry Pi here to accept a Direct Method call from the Azure IoT Hub? Some sort of SSH tunnel, perhaps?

Update: Code

using System;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Media;
using Windows.Devices.Gpio;
using System.Text;
using Windows.UI;
using Microsoft.Azure.Devices.Client;
using System.Threading.Tasks;

// The Blank Page item template is documented at https://go.microsoft.com/fwlink/?LinkId=402352&clcid=0x409

namespace UltimateLED2
{
public sealed partial class MainPage : Page
{
    const string DeviceId = "**********";
    const string DeviceKey = "**************************************";
    const string HubEndpoint = "*****.azure-devices.net";
    const int LEDPinNumber = 5;
    GpioPin LEDPin;
    bool LEDPinState;
    Brush StatusNormalBrush;
    DeviceClient deviceClient;

    public MainPage()
    {
        this.InitializeComponent();
        StatusNormalBrush = StatusIndicator.Fill;
        if (!TryInitGPIO().Result)
        {
            WriteMessage("GPIO initialization failed");
        }
        deviceClient = DeviceClient.Create(HubEndpoint,
            AuthenticationMethodFactory.CreateAuthenticationWithRegistrySymmetricKey(DeviceId, DeviceKey), TransportType.Mqtt_WebSocket_Only); 
        deviceClient.SetMethodHandlerAsync("ToggleLED", new MethodCallback(ToggleLEDMethod), null);
    }

    private async Task<MethodResponse> ToggleLEDMethod(MethodRequest methodRequest, object userContext)
    {
        WriteMessage("Recieved Direct Request to toggle LED");
        LEDPinState = !LEDPinState;
        await UpdateLight();
        return new MethodResponse(Encoding.UTF8.GetBytes("{\"LightIs\":\"" + (LEDPinState ? "On" : "Off") + "\"}"), 200);
    }

    public async Task<bool> TryInitGPIO()
    {
        GpioController gpioController = GpioController.GetDefault();
        if (gpioController == null)
        {
            WriteMessage("This Device is not IoT friendly!  (No GPIO Controller found)", true);
            return false;
        }
        if (gpioController.TryOpenPin(LEDPinNumber, GpioSharingMode.Exclusive, out LEDPin, out GpioOpenStatus openStatus))
        {
            WriteMessage($"Output Pin ({LEDPinNumber}) Opened Successfully!!");
        }
        else
        {
            WriteMessage($"Output Pin ({LEDPinNumber}) Failed to Open", true);
            return false;
        }

        LEDPin.SetDriveMode(GpioPinDriveMode.Output);
        LEDPin.Write(GpioPinValue.High);
        LEDPinState = true;
        await UpdateLight();
        WriteMessage("Output Pin initialized and on");
        return true;
    }

    private void WriteMessage(string message, bool isError = false)
    {
        StringBuilder sb = new StringBuilder(OutputBox.Text);
        if (isError)
        {
            sb.AppendLine();
            sb.AppendLine("*************ERROR**************");
        }
        sb.AppendLine(message);
        if (isError)
        {
            sb.AppendLine("*************END ERROR**************");
            sb.AppendLine();
        }
        OutputBox.Text = sb.ToString();  //Upon reviewing my code before posting it here, I noticed that this line of code directly modifies a UI element, and yet no errors are thrown (that I can see), whereas changing the color of my little light indicator circle below threw a threading error when I attempted to change the UI from another thread.  This function can be called synchronously from async methods, that run on different threads... does that not mean this function would be called on the different thread it was called from? 
    }

    private async void ManualToggle_Click(object sender, RoutedEventArgs e)
    {
        WriteMessage("Recieved Manual Toggle");
        LEDPinState = !LEDPinState;
        await UpdateLight();
    }

    private async Task UpdateLight()
    {

        LEDPin.Write(LEDPinState ? GpioPinValue.High : GpioPinValue.Low);
        await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
         {
             StatusIndicator.Fill = LEDPinState ? new SolidColorBrush(Colors.Red) : StatusNormalBrush;
         });


    }
}
}

Thanks! Lucas Niewohner

解决方案

I have tested the issue with the code you provided, even though it is incomplete, I modified slightly so that it can be run.The method ToggleLED can be called from Azure IoT Hub. In your code, the method WriteMessage need to use Dispatcher to update the TextBox,because when the direct method called,it runs in a new thread(not in UI thread). For your question, Direct methods follow a request-response pattern and are meant for communications that require immediate confirmation of their result, usually interactive control of the device, for example to turn on a fan.I am a little confused about the meaning of persistent connection.When you connect the Azure IoT Hub using MQTT, the connection will not be closed if there is no close action, or the network keeps alive,there is some other exception. In addition, IoT Hub does not support QoS 2 messages. If a device app publishes a message with QoS 2, IoT Hub closes the network connection.

Code:

public sealed partial class MainPage : Page
{
    const string DeviceId = "device1";
    const string DeviceKey = "<my-device-primarykey>";
    const string HubEndpoint = "<my-iot-hub>";
    const int LEDPinNumber = 5;
    GpioPin LEDPin;
    bool LEDPinState;
    Brush StatusNormalBrush;
    DeviceClient deviceClient;

    public MainPage()
    {
        this.InitializeComponent();

        if (!TryInitGPIO().Result)
        {
            WriteMessage("GPIO initialization failed");
        }

        deviceClient = DeviceClient.Create(HubEndpoint,
            AuthenticationMethodFactory.CreateAuthenticationWithRegistrySymmetricKey(DeviceId, DeviceKey), TransportType.Mqtt_WebSocket_Only);
        deviceClient.SetMethodHandlerAsync("ToggleLED", new MethodCallback(ToggleLEDMethod), null);
    }

    private Task<MethodResponse> ToggleLEDMethod(MethodRequest methodRequest, object userContext)
    {
        WriteMessage("Recieved Direct Request to toggle LED");
        LEDPinState = !LEDPinState;
        UpdateLight();
        return Task.FromResult(new MethodResponse(Encoding.UTF8.GetBytes("{\"LightIs\":\"" + (LEDPinState ? "On" : "Off") + "\"}"), 200));
    }

    public async Task<bool> TryInitGPIO()
    {
        GpioController gpioController = GpioController.GetDefault();
        if (gpioController == null)
        {
            WriteMessage("This Device is not IoT friendly!  (No GPIO Controller found)", true);
            return false;
        }
        if (gpioController.TryOpenPin(LEDPinNumber, GpioSharingMode.Exclusive, out LEDPin, out GpioOpenStatus openStatus))
        {
            WriteMessage($"Output Pin ({LEDPinNumber}) Opened Successfully!!");
        }
        else
        {
            WriteMessage($"Output Pin ({LEDPinNumber}) Failed to Open", true);
            return false;
        }

        LEDPin.SetDriveMode(GpioPinDriveMode.Output);
        LEDPin.Write(GpioPinValue.High);
        LEDPinState = true;
        UpdateLight();
        WriteMessage("Output Pin initialized and on");
        return true;
    }

    private async void WriteMessage(string message, bool isError = false)
    {
        await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
        {

            StringBuilder sb = new StringBuilder(OutputBox.Text);
            if (isError)
            {
                sb.AppendLine();
                sb.AppendLine("*************ERROR**************");
            }
            sb.AppendLine(message);
            if (isError)
            {
                sb.AppendLine("*************END ERROR**************");
                sb.AppendLine();
            }
            OutputBox.Text = sb.ToString();  //Upon reviewing my code before posting it here, I noticed that this line of code directly modifies a UI element, and yet no errors are thrown (that I can see), whereas changing the color of my little light indicator circle below threw a threading error when I attempted to change the UI from another thread.  This function can be called synchronously from async methods, that run on different threads... does that not mean this function would be called on the different thread it was called from? 
        });
    }

    private async void ManualToggle_Click(object sender, RoutedEventArgs e)
    {
        WriteMessage("Recieved Manual Toggle");
        LEDPinState = !LEDPinState;
        UpdateLight();
    }

    private async void UpdateLight()
    {

        LEDPin.Write(LEDPinState ? GpioPinValue.High : GpioPinValue.Low);

        await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
        {
            //StatusIndicator.Fill = LEDPinState ? new SolidColorBrush(Colors.Red) : StatusNormalBrush;
            OutputBox.Text = "UpdateLight\r\n";
        });
    }

这篇关于无法将Windows 10 IoT核心版设备从封闭式防火墙后面连接到Azure IoT中心的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆