我在地下室有一个不错的小项目,包括连接到Raspberry Pi 3的LED。相当复杂,是的,我知道。除此之外,这个Raspberry Pi运行的是Windows 10 IoT Core,我的目标是通过Azure Iot Hub服务中的Direct方法关闭和打开该LED。
除了一个奇怪的UI问题,我将在一个单独的问题中询问,这个系统或多或少地正常工作。我已经写了一个UWP项目,它可以很好地切换LED。回到用户界面工作时,就在某个时刻,我可以用一个可点击的按钮切换灯光。再次,非常复杂。无论如何,Azure IoT Hub已启动并正在运行,Raspberry Pi已正确配置,并且直接方法已在设备上设置。我打算使用Azure Functions来使用API调用来调用Direct方法,但在遇到问题后我将其简化了,现在正在通过Azure Portal内置的“Direct Method”对话框进行测试。 / 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 Hub的直接方法调用?也许某种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
答案 0 :(得分:1)
我已经使用您提供的代码测试了该问题,即使它不完整,我稍作修改以便可以运行。方法 ToggleLED 可以从Azure IoT Hub调用。在您的代码中,方法 WriteMessage 需要使用Dispatcher来更新TextBox,因为当调用direct方法时,它在新线程中运行(而不是在UI线程中)。对于您的问题,Direct方法遵循请求 - 响应模式,用于需要立即确认其结果的通信,通常是设备的交互式控制,例如打开风扇。我对持久性的含义有点困惑连接。使用MQTT连接Azure IoT Hub时,如果没有关闭操作,或者网络保持活动状态,则连接将不会关闭,还有其他一些异常。此外,IoT Hub不支持QoS 2消息。如果设备应用程序发布带有QoS 2的消息,IoT Hub将关闭网络连接。
<强>代码强>:
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";
});
}