Hello stack community.
I'm trying to send a simple message from a device to the Iot Hub, and then store this message on Blob.
To do so:
on the IoT Hub, i created a Router with the following property:
level='storage';
on the IoT Hub, i created an endpoint;
Everything works fine, except i get this warning when i inspect the Blob through the Azure platform showing me some bad characters:
The file 'xxxxxxxxxxx' may not render correctly as it contains an unrecognized extension.
The snippet to send the message from the device to the IoT Hub is the following:
var temperature = 20 + (Math.random() * 15);
var humidity = 60 + (Math.random() * 20);
var data = JSON.stringify({ deviceId: 'myFirstNodeDevice', temperature: temperature, humidity: humidity });
var message = new Message(data);
message.properties.add('level','storage');
console.log("Sending message: " + message.getData());
client.sendEvent(message, printResultFor('send'));
I can't figure out what's the real problem. Thank you in advance!
I haven't seen that message in before. I think this could be caused on how the route was configured. Use the following link for the full setup and integration of OIT hub messages with Azure storage to blob containers: https://learn.microsoft.com/en-us/azure/iot-hub/iot-hub-store-data-in-azure-table-storage
Related
Basically I want to be able to send a push notification for a specific registered device (using a tag) to the Azure Notification Hub which will then send that notification out to the device. I do have some c# code that will do this but I'd like to make use of an API if possible.
I found an article (computer restarted tho so I don't have the link) that explained how to send notifications using an API but it looks like it sends the notification to every device registered with the hub so I want to see if it would be easy to modify the body or even the pre-request script to include a specific device tag.
Or if any of you smart people know of an easier way to do this through an API then I'm all ears.
Here's my code (taken from Postman) that I currently have that's sending the notification to all hub devices.
POST endpoint: https://{namespace_name}.servicebus.windows.net/{hub_name}/messages/?api-version=2015-01
Headers:
Authorization - {{azure-authorization}}
Content-Type - application/json
ServiceBusNotification-Format - gcm
Request body:
{"data":
{
"gcm.notification.body":"Hi",
"gcm.notification.title":"Hi",
}
}
Pre-request Script:
function getAuthHeader(resourceUri, keyName, key) {
var d = new Date();
var sinceEpoch = Math.round(d.getTime() / 1000);
var expiry = (sinceEpoch + 3600);
var stringToSign = encodeURIComponent(resourceUri) + '\n' + expiry;
var hash = CryptoJS.HmacSHA256(stringToSign, key);
var hashInBase64 = CryptoJS.enc.Base64.stringify(hash);
var sasToken = 'SharedAccessSignature sr=' + encodeURIComponent(resourceUri) + '&sig=' + encodeURIComponent(hashInBase64) + '&se=' + expiry + '&skn=' + keyName;
console.log(sasToken);
return sasToken;
}
postman.setEnvironmentVariable('azure-authorization', getAuthHeader(request.url,"DefaultFullSharedAccessSignature", "<full_shared_access_signature>"));
postman.setEnvironmentVariable('current-date',new Date().toUTCString());
The REST API for sending notifications is a simple POST on /yourHub/messages, with special headers. When sending notifications in a platform-native format, the body is the platform-specific body to be sent. The additional headers are:
ServiceBusNotification-Format: Specifies that the platform (if
sending a native notification) or “template” is to send a template
notification.
ServiceBusNotification-Tags (optional): Specifies the tag (or tag
expression) defining the targeted set of registrations. If this
header is not present, the notification hub broadcasts to all
registrations.
Other headers are supported for platform-specific functionality, as specified in the Notification Hubs REST APIs documentation.
Refer: Use REST APIs from a backend
I want to connect a 'real device' with Azure IoT Central and connect a local source application to it using MQTT. I use this code for the connection and replace.
However, I cannot find any information on how to provide the timestamp. This thread suggests to set "iothub-creation-time-utc" as a "property" - I am not sure how to do that however. Is there any documentation on this?
add the property to the message:
message.properties.add('iothub-creation-time-utc', utcDT);
Based on the links in your question I assume you are using Node.js to develop your device code. There is a sample code snippet that shows how to set the creation time property here:
https://learn.microsoft.com/en-us/azure/iot-accelerators/iot-accelerators-connecting-pi-node
function sendTelemetry(data, schema) {
if (deviceOnline) {
var d = new Date();
var payload = JSON.stringify(data);
var message = new Message(payload);
message.properties.add('iothub-creation-time-utc', d.toISOString());
message.properties.add('iothub-message-schema', schema);
console.log('Sending device message data:\n' + payload);
client.sendEvent(message, printErrorFor('send event'));
} else {
console.log('Offline, not sending telemetry');
}
}
I am fairly new to React Native, but I created a simple app that has a button and a text that changes when the button is clicked. However, I want to connect to the IoT hub and receive information from the hub.
I am having issues currently with the connection. Here is my code:
import React from 'react';
import {StyleSheet, Dimensions, Alert, Text, View, Image, Button} from 'react-native';
const { width, height } = Dimensions.get("window");
'use strict';
var iothub = require('azure-iothub');
var connectionString = 'connection string';
var Mqtt = require('azure-iot-device-mqtt').Mqtt;
var DeviceClient = require('azure-iot-device').Client
var Message = require('azure-iot-device').Message;
var client = DeviceClient.fromConnectionString(connectionString, Mqtt);
// Create a message and send it to the IoT hub every second
setInterval(function(){
// Simulate telemetry.
var temperature = 20 + (Math.random() * 15);
var message = new Message(JSON.stringify({
temperature: temperature,
humidity: 60 + (Math.random() * 20)
}));
// Add a custom application property to the message.
// An IoT hub can filter on these properties without access to the message body.
message.properties.add('temperatureAlert', (temperature > 30) ? 'true' : 'false');
console.log('Sending message: ' + message.getData());
// Send the message.
client.sendEvent(message, function (err) {
if (err) {
console.error('send error: ' + err.toString());
} else {
console.log('message sent');
}
});
}, 1000);
The above part is a Node.js code, but I want to use it in a React application. How do I use Node packages in React?
I am getting the following error: Could not connect to development server.
Thanks.
As in your other question, I would suggest you take a look at Azure IoT Starter Kit Companion, which is a sample React Native application that helps you get your IoT device connected to an IoT Hub on iOS, Android and Windows.
Hope it helps!
Small point, you should NOT publish your connection strings. This is a rather large security issue.
As mentioned in your other question, the Azure IoT Hub Device SDK needs a Node.js runtime, which does not exists within the React Native app.
There is a plugin called nodejs-mobile-react-native for React Native that brings in the Node.js runtime and enables you to run Node.js apps alongside your React Native application.
I have written a detailed blog post about this topic outlining the steps necessary to achieve this.
I have followed all the instruction for setting up a "Downstream Device" to send messages through IoT Edge running in Transparent Gateway. I believe my routing rules are correct, but my Function module is not receiving any of the Messages through the message flow.
These are the instruction I've followed:
https://learn.microsoft.com/en-us/azure/iot-edge/how-to-create-transparent-gateway-linux
I am using 2 Linxu VMs (ubuntu 16.04.5).
IoT Edge Transparent Gateway VM is configured with all the certs properly setup, configured and verified. I've been able to using the openssl tool from the
openssl s_client -connect {my-gateway-machine-name-dns-name}.centralus.cloudapp.azure.com:8883 -CAfile /certs/certs/azure-iot-test-only.root.ca.cert.pem -showcerts
Downstream device running on Linux VM with Certs installed and verified. My connection string is as follows:
HostName={IoTHubName}.azure-devices.net;DeviceId=TC51_EdgeDownStreamDevice01;SharedAccessKey={My-Shared-Access-Key}=GatewayHostName={my-gateway-machine-name-dns-name}.centralus.cloudapp.azure.com
a. I have verified I get a successful verification of the SSL cert using the openssl tool.
b. I'm using the the following in my downstream device for my connection using the NodeJS SDK
var client = DeviceClient.fromConnectionString(connectionString, Mqtt);
c. I can see the messages showing up at the Azure IoT Hub in the Cloud, but I can't get my module running on the IoT Edge Transparent Gateway to be hit.
Here are my routing rules configured for the edgeHub as specified in "Routing messages from downstream devices" in the sample doc page.
This is what the example docs show:
{ "routes":{ "sensorToAIInsightsInput1":"FROM /messages/* WHERE NOT IS_DEFINED($connectionModuleId) INTO BrokeredEndpoint(\"/modules/ai_insights/inputs/input1\")", "AIInsightsToIoTHub":"FROM /messages/modules/ai_insights/outputs/output1 INTO $upstream" } }
This is what my routing configuration is set to:
"routes": {
"downstreamBatterySensorToBatteryDataFunctionInput1": "FROM /* WHERE NOT IS_DEFINED($connectionModuleId) INTO BrokeredEndpoint(\"/modules/BatteryDataFunctionModule/inputs/input1\")",
"BatteryDataFunctionModuleToIoTHub": "FROM /messages/modules/BatteryDataFunctionModule/outputs/* INTO $upstream"
}
** Note that I've used by "FROM /* WHERE NOT IS_DEFINED" and "FROM /messages/* WHERE NOT IS_DEFINED"
My module on the IoT Edge is setup as a Function. When I use the out of the box example where the simulator device is another module running on the IoT Edge, then my function is hit correctly. Its only when I'm trying to use a "Downstream Device" that the module is not being triggered.
I have enabled "Debug Logging for the IoT Edge Service" running on my Transparent Gateway.
This is the basic Run method for the Function module:
#r "Microsoft.Azure.Devices.Client"
#r "Newtonsoft.Json"
using System.IO;
using Microsoft.Azure.Devices.Client;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
// Filter messages based on the temperature value in the body of the message and the temperature threshold value.
public static async Task Run(Message messageReceived, IAsyncCollector<Message> output, TraceWriter log)
{
How can I figure out how to get my Module running in IoT Edge to be hit/triggered from a Downstream device?
So, you say you are seeing messages show up in IoT Hub, but not in Edge... A couple of things:
you posted this as your connection string in your node app:
HostName={IoTHubName}.azure-devices.net;DeviceId=TC51_EdgeDownStreamDevice01;SharedAccessKey={My-Shared-Access-Key}=GatewayHostName={my-gateway-machine-name-dns-name}.centralus.cloudapp.azure.com
Did you copy/paste this exactly? the reason I ask is that, between the shared access key and the word "GatewayHostName", you have an equals sign and not a semi-colon..
it should be:
HostName={IoTHubName}.azure-devices.net;DeviceId=TC51_EdgeDownStreamDevice01;SharedAccessKey={My-Shared-Access-Key};GatewayHostName={my-gateway-machine-name-dns-name}.centralus.cloudapp.azure.com
(note the ';' before GatewayHostName… if you really did have an equals sign there instead of a semicolon, there's no telling what kind of chaos that would cause :-)
Secondly, in your route, you call your module BatteryDataFunctionModule.. just want to make sure that module name is exact, including being case-sensitive. You probably know that, but don't want to assume..
Finally, if the two things above check out, can you add an addition debugging route that sends the 'incoming data' to IoTHub as well..
"FROM /* WHERE NOT IS_DEFINED($connectionModuleId) INTO $upstream"
so we can make sure the messages are actually making it through iot edge.
There are 2 problems that needed to be addressed to get the Downstream Device to communication
Thanks to #Steve-Busby-Msft I needed to have a semi-colon (;) at the end of the SharedAccessKey and before the GatewayHostName
you posted this as your connection string in your node app: HostName={IoTHubName}.azure-devices.net;DeviceId=TC51_EdgeDownStreamDevice01;SharedAccessKey={My-Shared-Access-Key}=GatewayHostName={my-gateway-machine-name-dns-name}.centralus.cloudapp.azure.com
The NodeJS application Downstream Device also has to load up the cert correctly at the 'Application level'.
Notice the section of code for
var edge_ca_cert_path = '[Path to Edge CA certificate]';
Node JS Downstream Application
'use strict';
var fs = require('fs');
var Protocol = require('azure-iot-device-mqtt').Mqtt;
// Uncomment one of these transports and then change it in fromConnectionString to test other transports
// var Protocol = require('azure-iot-device-http').Http;
// var Protocol = require('azure-iot-device-amqp').Amqp;
var Client = require('azure-iot-device').Client;
var Message = require('azure-iot-device').Message;
// 1) Obtain the connection string for your downstream device and to it
// append this string GatewayHostName=<edge device hostname>;
// 2) The edge device hostname is the hostname set in the config.yaml of the Edge device
// to which this sample will connect to.
//
// The resulting string should look like the following
// "HostName=<iothub_host_name>;DeviceId=<device_id>;SharedAccessKey=<device_key>;GatewayHostName=<edge device hostname>"
var connectionString = '[Downstream device IoT Edge connection string]';
// Path to the Edge "owner" root CA certificate
var edge_ca_cert_path = '[Path to Edge CA certificate]';
// fromConnectionString must specify a transport constructor, coming from any transport package.
var client = Client.fromConnectionString(connectionString, Protocol);
var connectCallback = function (err) {
if (err) {
console.error('Could not connect: ' + err.message);
} else {
console.log('Client connected');
client.on('message', function (msg) {
console.log('Id: ' + msg.messageId + ' Body: ' + msg.data);
// When using MQTT the following line is a no-op.
client.complete(msg, printResultFor('completed'));
// The AMQP and HTTP transports also have the notion of completing, rejecting or abandoning the message.
// When completing a message, the service that sent the C2D message is notified that the message has been processed.
// When rejecting a message, the service that sent the C2D message is notified that the message won't be processed by the device. the method to use is client.reject(msg, callback).
// When abandoning the message, IoT Hub will immediately try to resend it. The method to use is client.abandon(msg, callback).
// MQTT is simpler: it accepts the message by default, and doesn't support rejecting or abandoning a message.
});
// Create a message and send it to the IoT Hub every second
var sendInterval = setInterval(function () {
var windSpeed = 10 + (Math.random() * 4); // range: [10, 14]
var temperature = 20 + (Math.random() * 10); // range: [20, 30]
var humidity = 60 + (Math.random() * 20); // range: [60, 80]
var data = JSON.stringify({ deviceId: 'myFirstDownstreamDevice', windSpeed: windSpeed, temperature: temperature, humidity: humidity });
var message = new Message(data);
message.properties.add('temperatureAlert', (temperature > 28) ? 'true' : 'false');
console.log('Sending message: ' + message.getData());
client.sendEvent(message, printResultFor('send'));
}, 2000);
client.on('error', function (err) {
console.error(err.message);
});
client.on('disconnect', function () {
clearInterval(sendInterval);
client.removeAllListeners();
client.open(connectCallback);
});
}
};
// Provide the Azure IoT device client via setOptions with the X509
// Edge root CA certificate that was used to setup the Edge runtime
var options = {
ca : fs.readFileSync(edge_ca_cert_path, 'utf-8'),
};
client.setOptions(options, function(err) {
if (err) {
console.log('SetOptions Error: ' + err);
} else {
client.open(connectCallback);
}
});
I am attempting to use a node-red function to read Azure IoT Hub messages using AMQP. I have imported the azure-iothub module.
The code below connects ok and getFeedbackReceiver returns an AmqpReceiver object (I can see the object output to debug tab) which acts as an event emitter. However, the emitter loop (msgReceiver.on) doesn't seem to run. I don't even get a null output to debug tab.
Any help appreciated.
var azureamqp = global.get('azureamqp');
var iothub = azureamqp.Client.fromConnectionString(cnct);
try
{
iothub.open(function()
{
node.send({type: "connection", payload: util.inspect(iothub)});
node.status({ fill: "green", shape: "ring", text: "listening" });
iothub.getFeedbackReceiver(function(err,msgReceiver)
{
if (!err)
{
node.send({type: "receiver", payload: util.inspect(msgReceiver)});
msgReceiver.on('message',function(message)
{
node.send({payload:message});
});
}
else
{
node.send({payload: err});
}
});
node.send({type: "streamend"});
node.status({ fill: "red", shape: "ring", text: "disconnected" });
});
}
catch (err)
{}
Ok I think I see what's happening thanks to the additional comments: First of, some reference docs about messaging with IoT Hub:
https://learn.microsoft.com/en-us/azure/iot-hub/iot-hub-devguide-messaging
There are 2 types of messages that are sent through IoT Hub, depending on the direction in which they go:
Cloud to Device (C2D, or commands): these messages are sent by a cloud app to one or more devices. They are stored in a device-specific queue, and delivered to the device as soon as the device connects and starts listening for messages. Once received by the device, the device can choose to send feedback about these to IoT Hub. This feedback is received using the azure-iothub.Client.getFeedbackReceiver() API. I've added more about this at the end of the answer.
Device-to-Cloud (D2C, or telemetry): these messages are sent by the devices to the cloud application. These messages are stored in Event Hubs partitions and can be read from these partitions. This happens with the Event Hubs SDK
From the question and the comments it looks like you're trying to receive D2C messages (telemetry) using the feedback API - and it's not working, because it's not meant to work that way. It's good feedback for (bad?) API design and lack of docs though.
How to receive messages sent by devices:
This sample on github shows how to set up an Event Hubs client and can be used to listen to messages sent by devices to IoT Hub.This command in iothub-explorer is also an easy reference.
Please find below a simple example:
'use strict';
var EventHubClient = require('azure-event-hubs').Client;
var Promise = require('bluebird');
var connectionString = '[IoT Hub Connection String]';
var client = EventHubClient.fromConnectionString(connectionString);
var receiveAfterTime = Date.now() - 5000;
var printError = function (err) {
console.error(err.message);
};
var printEvent = function (ehEvent) {
console.log('Event Received: ');
console.log(JSON.stringify(ehEvent.body));
console.log('');
};
client.open()
.then(client.getPartitionIds.bind(client))
.then(function (partitionIds) {
return Promise.map(partitionIds, function (partitionId) {
return client.createReceiver('$Default', partitionId, { 'startAfterTime' : receiveAfterTime}).then(function(receiver) {
receiver.on('errorReceived', printError);
receiver.on('message', printEvent);
});
});
}).catch(printError);
A little more about that code sample:
Basically, the Event Hubs client provide an AMQP connection can be used to open receivers on each partition of the Event Hub. Partitions are used to store messages sent by devices. Each partition gets its own receiver, and each receiver has a message event. Hence the need to open one receiver per partition to never miss any message from any devices. Here's a little bit more about Event Hubs and the nature of partitions: https://learn.microsoft.com/en-us/azure/event-hubs/event-hubs-what-is-event-hubs
More about C2D Feedback:
There are 3 types of feedback that a device can send about a C2D message:
accept (or complete) means the message is taken care of by the device and the IoT Hub will remove this message from the device queue
reject means that the device doesn't want this message (maybe because it's malformed, or irrelevant, that's up to you to device) and the IoT Hub will remove the message from the queue.
abandon means that the device cannot "take care" of this message right now and wants IoT Hub to resend it later. The message remains in the queue.
accept and reject are both going to be observed using the getFeedbackReceiver API, as well as timeouts if the message is never received or is abandonned too many times.