23289

Trouble connecting a Windows 10 IoT Core device to Azure IoT Hub from behind closed firewall

Question:

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?

<strong>Update</strong>: 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

Answer1:

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 <strong>ToggleLED</strong> can be called from Azure IoT Hub. In your code, the method <strong>WriteMessage</strong> 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.

<strong>Code</strong>:

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"; }); }

Recommend

  • Install py2exe for python 2.7 over pip: this package requires Python 3.3 or later
  • How to make a dhcp server listen for broadcast requests in a virtual box vm
  • How to setup VisualSVN Setup on Windows Azure Instance
  • iptables 1.4.11 on Android
  • Difference between devpi and pypi server
  • Proving if n = m and m = o, then n + m = m + o in Idris?
  • Get all derived types of a type
  • Google cloud datastore emulator init data
  • Browserify and Reactify source maps include full local path names
  • Two columns in subquery in where clause
  • Chrome breakpoint on radio doesn't fire
  • Google OAuth2 for an web application hosted behind NAT (intranet server without public IP)
  • Installing SSL on AWS EC2 Bitnami Mean Stack
  • Generating random numbers directly inside a .htaccess file
  • Rails 3.2 from SQLite locally to Postgres on Heroku
  • UML diagram generator in Visual Studio 2010
  • Does “internal” visibility modifier in Kotlin work yet?
  • How to resolve docker host names (/etc/hosts) in containers
  • XBee Linux Serial Port on Rasberry Pi
  • Graceful pod termination
  • Should I be afraid to use UDP to make a client/server broadcast talk?
  • Access the state of control in Winforms from another application
  • Mercurial: Identify file name after rename
  • MVC - @Html.CheckBoxFor
  • Tools for understanding HTML layout
  • node.js POST request fails
  • Replace last two characters in column
  • CORS with socket.io
  • Available space left on drive - WinAPI - Windows CE
  • File extension of zlib zipped html page?
  • Examples of how to a STS in .Net 4.5 using WCF
  • Prevent Tomcat from caching request during starup
  • Access Android Market through SSH tunnel
  • How to run “Deployd” on port 80 instead of port 5000 in webserver.
  • Meteor: Do Something On Email Verification Confirmation
  • Cannot resolve symbol 'MyApi'
  • How to get address from latitude and longitude android google map v2 [duplicate]
  • How to make Safari send if-modified-since header?
  • Web-crawler for facebook in python
  • using HTMLImports.whenReady not working in chrome