【发布时间】:2018-01-12 03:46:25
【问题描述】:
我在地下室有一个不错的小项目,由一个连接到 Raspberry Pi 3 的 LED 组成。非常复杂,是的,我知道。除此之外,这个 Raspberry Pi 正在运行 Windows 10 IoT Core,我的目标是通过 Azure IoT Hub 服务中的直接方法来关闭和打开 LED。
除了我将在一个单独的问题中询问的一个奇怪的 UI 问题之外,这个系统或多或少地工作正常。我编写了一个 UWP 项目,它可以很好地切换 LED。回到 UI 正常工作的时候,在某一时刻,我可以通过一个可点击的按钮来切换灯光。再次,非常复杂。无论如何,Azure IoT Hub 已启动并正在运行,Raspberry Pi 已正确配置,并且直接方法已在设备上设置。我打算使用 Azure Functions 来使用 API 调用来调用 Direct Method,但在遇到问题后我将其简化,现在正在通过 Azure Portal 内置的“Direct Method”对话框进行测试。
我的问题是:当我从 Azure 门户的工具调用直接方法时(顺便说一句,使用起来有点烦人),然后等待 20 秒等待 5-seconds-and-then-it's-gone 结果弹出,我收到一条消息,说呼叫“等待连接时超时”。具体错误如下:
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;
});
}
}
}
谢谢! 卢卡斯·尼沃纳
【问题讨论】:
-
您在Azure Portal中测试调用直接方法时,您是否运行了为命名方法注册了委托的设备客户端应用程序?如果您运行过,请提供相关代码吗?
-
@Lucas Direct Method 要求在设备和 Azure IoT Hub 之间使用面向连接的协议,例如 mqtt 或 amqp。换句话说,您的设备必须连接才能进行设备方法调用。使用 azure 门户查询资源管理器查询包含其 connectionState 属性的所有设备。注意,结果页面没有刷新按钮,所以在下一次执行查询之前关闭页面。
-
@Lucas 出于测试目的,您可以使用 MQTTBox 客户端 workswithweb.com/mqttbox.html 或 Azure IoT Hub Tester codeproject.com/Articles/1173356/Azure-IoT-Hub-Tester 来模拟具有 MQTT 协议的设备。
-
@MichaelXu-MSFT...我已经添加了代码
-
@RomanKiss 是的,我可以理解必须连接设备才能接受基于网络的直接方法调用(看起来非常合乎逻辑)。我的麻烦在于实际上打开了连接。 Azure 不能直接这样做,因为我的本地 Internet 连接没有面向公众的 IP 地址,因此必须以某种方式建立连接,作为从 Raspberry Pi 到世界其他地方的某种持久隧道。我的问题(措辞更具体一点)是:如何通过某种 SSH(或其他)隧道为 MQTT 创建持久连接?
标签: azure raspberry-pi3 windows-10-iot-core azure-iot-hub