Can't receive c2d messages from Azure Function App(Node) - azure

I am building an IoT system using Azure IoT Hub, 2 ESP32 devices(one is to send sensor values and another is to receive c2d message and activate an actuator) and an Azure Function to store the values to a database.
To send c2d messages from an Azure function app, I used the following code.(This Azure Function is triggered when IoT Hub gets a mqtt message from my ESP32 device.)
But I couldn't receive them on my mosquitto_subscribe script running on my laptop.
code in the function app
import { AzureFunction, Context } from "#azure/functions"
const Client = require('azure-iothub').Client;
const Message = require('azure-iot-common').Message;
const connectionString = 'my connection string';
const targetDevice = 'my device id';
const serviceClient = Client.fromConnectionString(connectionString);
const IoTHubTrigger: AzureFunction = async function (context: Context, message: {type:string;isPressed:boolean;}): Promise<void> {
context.log(`Eventhub trigger function called for message : ${JSON.stringify(message)}`);
context.log(message)
context.log(message.isPressed)
if(message.isPressed){
try {
await serviceClient.open()
context.log('Service client connected');
// serviceClient.getFeedbackReceiver(receiveFeedback);
const csdMessage = new Message('This is a C2D message');
csdMessage.ack = 'full';
csdMessage.messageId = "My Message ID";
context.log('Sending message: ' + csdMessage.getData());
await serviceClient.send(targetDevice, csdMessage);
}catch(err){
context.log('Could not connect: ' + err.message);
}
}
context.done();
};
export default IoTHubTrigger;
mosquitto code
mosquitto_sub -d -q 1 \
-V mqttv311 \
-p 8883 \
-h my-iot-hub-name.azure-devices.net \
-i esp32\
-u "my-iot-hub-name.azure-devices.net/my-decice-name/api-version=2016-11-14" \
-P "$SAS" \
-t "devices/my-device-name/messages/devicebound/#"
According to the execution log, the connection between IoT Hub and the Function was successfully established and the message was sent.
execution log
What confuses me is that the mosquitto script can receive c2d messages when I run the local node script which is almost the same as the one in Function App.
I wrote the following script referring to this document.
https://learn.microsoft.com/en-us/azure/iot-hub/iot-hub-node-node-c2d#send-a-cloud-to-device-message
'use strict';
var Client = require('azure-iothub').Client;
var Message = require('azure-iot-common').Message;
var connectionString = 'my connection string';
var targetDevice = 'my device id';
var serviceClient = Client.fromConnectionString(connectionString);
(async()=>{
try {
await serviceClient.open()
console.log('Service client connected');
// serviceClient.getFeedbackReceiver(receiveFeedback);
const csdMessage = new Message('This is a C2D message');
csdMessage.ack = 'full';
csdMessage.messageId = "My Message ID";
console.log('Sending message: ' + csdMessage.getData());
await serviceClient.send(targetDevice, csdMessage);
}catch(err){
console.log('Could not connect: ' + err.message);
}
})();
Why can't c2d messages reach mosquitto_sub only when they are from function app even though the function to send a message is successfully executed ?
Can anyone help me?
Thank you in advance.

Related

Function execution took 540029 ms, finished with status: 'timeout'

I have created a cloud function that connects to an MQTT broker I have used a third-party MQTT broker (Mosquito MQTT broker), and sends the data to the Firebase real-time database every time the MQTT broker receives data from the machine. I am using the GCP console for writing and deploying the function. I successfully deployed the function without any errors, however, when I test it from the GCP console, it starts sending data but stops after the time specified in the timeout. I have tried timeout values from 60 to 540 seconds, but it still stops after the specified time. I have also increased the allocated memory, but it hasn't resolved the issue and I keep getting the same timeout error.
This is my code
const Admin = require("firebase-admin");
const functions = require("firebase-functions");
const mqtt = require('mqtt');
const clientId = 'mqtt_googleserver_********7'
const topic = '#'
const serviceAccount = require("./service.json");
Admin.initializeApp({
credential: Admin.credential.cert(serviceAccount),
databaseURL: "https://***************firebaseio.com/"
});
exports.rtdb_mains = functions.https.onRequest((_request, _response) => {
const client = mqtt.connect('mqtt://**.**.**.****.***',{
clientId,
clean: true,
connectTimeout: 4000,
username: '******',
password: '********',
reconnectPeriod: 1000,
});
const db = Admin.database();
client.addListener('connect', () => {
console.log('Connected');
client.subscribe([topic], { qos: 1 });
console.log(`Subscribe to topic '${topic}'`);
});
client.on('message', async (topic, payload) => {
console.log('Received Message:', topic, payload.toString());
if (payload.toString() !== "" && topic !== "") {
const ref = db.ref("All_machines");
const childref = ref.child(topic.toString());
await childref.set(payload.toString());
const topicDetails = topic.split("/");
const machineId = topicDetails[1];
const machineParameter = topicDetails[2];
if (machineParameter === "BoardID") {
const ref = db.ref(machineParameter);
await ref.set(machineId);
}
}
});
});
can anyone please help me with this problem.
You don't need to specify a service.json if you push the CF on firebase. You can directly use the default configuration.
You can do directly this :
admin.initializeApp();
Secondly, the way you use your MQTT implementation and the cloud function are not correct.
You are listenning and waiting for a message in a function that is trigger only by a POST or GET request.
I suggest to use the pub/sub api for doing such a thing and have a good implementation for sending / receiving messages.
In case of you really need to listen for message in your MQTT implementation, you will need another provider than Cloud Function or calling the native MQTT of Cloud Function
https://cloud.google.com/functions/docs/calling/pubsub
https://www.googlecloudcommunity.com/gc/Serverless/Can-a-cloud-function-subscribe-to-an-MQTT-topic/m-p/402965

How to pass on an updated variable outside of MQTT event to use it otherwise as condition? (Hyperledger Fabric & MQTT)

First, we are quite new in the area Hyperledger Fabric and MQTT. We are building a prototype with a Blockchain platform (Hyperledger Fabric) written in nodeJS and a raspberry pi for transmitting the IoT data (working with mqtt). In our nodeJS file we create a MQTT client that subscribes to a topic and gets as a message a JSON object. The parameters of that object have to be passed on to the smart contract (submitContract). We realized that the gateway for Hyperledger Fabric disconnects before it runs the MQTT event functions (client.on) and therefore we thought we let the gateway disconnect on the condition of a submitted contract (see below code). In order to do so we want to get the success variable out of the client.on("message") function so that we can use it for the if statement at the end. However, the success variable doesn't seem to get updated to success=true by simply using return within client.on() so that eventually the program never exits (aka gateway is not disconnected). Below, you see parts of the code (the relevant parts). Does anyone know how to pass on properly an updated variable out of the MQTT event function?
'use strict';
const { FileSystemWallet, Gateway } = require('fabric-network');
const mqtt = require("mqtt");
async function main() {
try {
// Create a new gateway for connecting to our peer node.
const gateway = new Gateway();
await gateway.connect(ccpPath, { wallet, identity: 'user1', discovery: { enabled: true, asLocalhost: true } });
// Get the network (channel) our contract is deployed to.
const network = await gateway.getNetwork('mychannel');
// Get the contract from the network.
const contract = network.getContract('fabcar');
//MQTT client connection & contract submission
console.log("connecting to broker");
const client = mqtt.connect("mqtt://192.168.43.217");
var success = false;
client.on("connect", () =>{
console.log("Subscribing");
client.subscribe("rfidData");
console.log("Please hold your tag on the RFID reader. Wait...");
});
client.on("message", (topic, message) =>{
var rfidPayload = JSON.parse(message.toString());
var carKeyIn = rfidPayload.carKey;
var renterIDIn = rfidPayload.renterID;
var timestampIn = rfidPayload.timestamp;
console.log(rfidPayload);
contract.submitTransaction('openCar', carKeyIn, renterIDIn, timestampIn);
success = true;
console.log("Success? " + success);
return success;
});
client.stream.on('error', (err) => {
console.log('errorMessage', err);
client.end()
});
client.on("offline",() =>{
console.log("offline");
});
client.on("reconnect", ()=>{
console.log("reconnect");
});
// Disconnect from the gateway.
if (success === true){
client.end();
gateway.disconnect();
};
} catch (error) {
console.error(`Failed to submit transaction: ${error}`);
process.exit(1);
}
}
main();
You need to take on the concept of asynchronous execution and event driven systems. The application you have written does not execute in the order it is written.
The on('message',....) call back will only be executed when a message is delivered to client. This function also does not return any values (it is not actually called by any of your code, it is called by the underlying MQTT client code) so it will not "return" the value of success.
If you want the application to disconnect after it has received the first message then the easiest thing to do is to move the code that disconnects the MQTT client and the Fabric gateway to inside the callback. e.g.
client.on("message", (topic, message) =>{
var rfidPayload = JSON.parse(message.toString());
var carKeyIn = rfidPayload.carKey;
var renterIDIn = rfidPayload.renterID;
var timestampIn = rfidPayload.timestamp;
console.log(rfidPayload);
contract.submitTransaction('openCar', carKeyIn, renterIDIn, timestampIn);
gateway.disconnect();
client.end();
});
EDIT:
Looking at the doc it appears that submitTransaction() is flagged as being an async function, so you should be able to use await to block until it completes
...
console.log(rfidPayload);
await contract.submitTransaction('openCar', carKeyIn, renterIDIn, timestampIn);
gateway.disconnect();
client.end();
});

How to get activate device in X509 self assigned Device type in Azure IOT hub?

I am new to IOT and X509 Authentication provisioning.
I am able to register device using node.js in Azure IOT hub (Self Assigned X509 Device type).
The pipeline is,
1. Download certificate of the registered device type.
2. convert into key and cert pem files
3. pass register id as device id
register_X509.js
'use strict';
var fs = require('fs');
var Client = require('azure-iot-device').Client;
var Message = require('azure-iot-device').Message;
var X509AuthenticationProvider = require('azure-iot-device').X509AuthenticationProvider;
var Protocol = require('azure-iot-device-mqtt').Mqtt;
var Transport = require('azure-iot-provisioning-device-mqtt').Mqtt;
var X509Security = require('azure-iot-security-x509').X509Security;
var ProvisioningDeviceClient = require('azure-iot-provisioning-device').ProvisioningDeviceClient;
var provisioningHost = 'my-provisioning-host';
var idScope = 'id-scope';
var registrationId = 'registration-id';
var deviceCert = {
cert: fs.readFileSync('cert.pem').toString(),
key: fs.readFileSync('key.pem').toString()
};
var transport = new Transport();
var securityClient = new X509Security(registrationId, deviceCert);
var deviceClient = ProvisioningDeviceClient.create(provisioningHost, idScope, transport, securityClient);
// Register the device. Do not force a re-registration.
deviceClient.register(function(err, result) {
if (err) {
console.log("error registering device: " + err);
} else {
console.log('registration succeeded');
console.log('assigned hub=' + result.assignedHub);
console.log('deviceId=' + result.deviceId);
var client = Client.fromAuthenticationProvider(X509AuthenticationProvider.fromX509Options(
result.deviceId, result.assignedHub, deviceCert
), Protocol);
var connectCallback = function (err) {
if (err) {
console.error('Could not connect: ' + err.message);
} else {
console.log('Client connected');
// Create device Twin
client.getTwin(function (err, twin) {
if (err) {
console.error('could not get twin');
} else {
console.log('twin created');
console.log('twin contents:');
console.log(twin.properties);
// Send Event message
setTimeout(function () {
var data = JSON.stringify({
'did': result.deviceId,
'dts': new Date().toISOString(),
'desiredStatus': 'Activated',
'reportedStatus': 'Activating',
'msg': []
});
var message = new Message(data);
console.log('Sending message: ' + message.getData());
client.sendEvent(message, printResultFor('send'));
}, 1000);
}
});
client.on('error', function (err) {
console.error(err.message);
});
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.
});
}
};
client.open(connectCallback);
}
});
// Helper function to print results in the console
function printResultFor(op) {
return function printResult(err, res) {
if (err) console.log(op + ' error: ' + err.toString());
if (res) console.log(op + ' status: ' + res.constructor.name);
};
}
At the end of this output, my device is registed under IOT hub and I am able to make connection as well using following code.
The output of the above script is,
registration succeeded
assigned hub=my-end-point.net
deviceId=my-device-id
Client connected
twin created
twin contents:null: Object {reported: Object, desired: Object}
Sending message: {"did":"my-device-id","dts":"2020-01-30T07:18:38.471Z","desiredStatus":"Activated","reportedStatus":"Activating","msg":[]}
send status: MessageEnqueued
it says messages is in queue but I am unable to activate the device.
I'm stuck at this stage, Can anyone help me to get rid of this?
any idea why it's not working or Am I going in the right approach would be welcome.

where to place the for-loop in existing node.js scrtip

I have a existing script from Microsoft that generates data for an IoT hub. Script works like a charm and generates telemetry data every second. Now I want to add an Id to the output of the code. Nothing fancy just starting at 1 and progress with every message up to 1000000
I search for the for loop and found an easy example on W3 schools. But when I use this piece of code the way I think it should be used, it does not generate code every second anymore, but continuously. and the Id field starts at 1000000 and does not add up.
The sample code from Microsoft:
'''var connectionString = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx';
// Using the Node.js Device SDK for IoT Hub:
// https://github.com/Azure/azure-iot-sdk-node
// The sample connects to a device-specific MQTT endpoint on your IoT Hub.
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 way I used the for-loop:
'''
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
'use strict';
// The device connection string to authenticate the device with your IoT hub.
//
// NOTE:
// For simplicity, this sample sets the connection string in code.
// In a production environment, the recommended approach is to use
// an environment variable to make it available to your application
// or use an HSM or an x509 certificate.
// https://learn.microsoft.com/azure/iot-hub/iot-hub-devguide-security
//
// Using the Azure CLI:
// az iot hub device-identity show-connection-string --hub-name {YourIoTHubName} --device-id MyNodeDevice --output table
var connectionString = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx';
// Using the Node.js Device SDK for IoT Hub:
// https://github.com/Azure/azure-iot-sdk-node
// The sample connects to a device-specific MQTT endpoint on your IoT Hub.
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);
**var id
for (id = 0; id < 1000000; id++) {**
// 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({
id: id,
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);
**};**
'''
How to use the for loop in the existing script so I can Add an ID to the output that starts at 1 progresses with 1 every single record up to 1000000
Remove the for loop, just ad id var outside the setInterval in increase it each fire:
// Using the Node.js Device SDK for IoT Hub:
// https://github.com/Azure/azure-iot-sdk-node
// The sample connects to a device-specific MQTT endpoint on your IoT Hub.
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);
**var id
var id = 0;
// Create a message and send it to the IoT hub every second
setInterval(function(){
id++;
// Simulate telemetry.
var temperature = 20 + (Math.random() * 15);
var message = new Message(JSON.stringify({
id: id,
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);
'''

send data from Rfid rc522 to azure iot hub using nodejs

//THIS IS THE CODE TO READ SERIAL NUMBER AND SEND DATA TO AZURE IOT HUB
var rc522=require("rc522/build/Release/rc522");
var Async = require('async');
var Protocol = require('azure-iot-device-http').Http;
var Client = require('azure-iot-device').Client;
var ConnectionString = require('azure-iot-device').ConnectionString;
var Message = require('azure-iot-device').Message;
// Enter Device Connection String: AZURE--> IOTHub --> Devices--> select Device--> Connection String.
var connectionString = '';
var deviceId = ConnectionString.parse(connectionString).DeviceId;
var client = Client.fromConnectionString(connectionString, Protocol);
var connectCallback=function(err){
if(err){
console.error('could not open connection' +err);
}
else{
console.log('client connected');
rc522(function(serial){
console.log(serial);
});
var readings = { Id:serial};
var message = new Message(JSON.stringify(readings));
client.sendEvent(message, function (error) {
if (error)
{
console.log(error.toString());
}
else
{
console.log("Data sent on %s...", new Date());
}
});
}
}
client.open(connectCallback);
I cannot send rfid rc522 serial number to Azure IOT hub with Nodejs.I am able to connect to client and display serial number on console but cannot see any received messages in azure iot hub.I wrote function app and sent iothub message to tablestorage.
Below is my code and output
iothub and table storage output,
console output for rfid nodejs,
Can somepone please explain me how to send serial number to azure IOT hub.
I have searched for resources about this but could not find any in nodejs.
Many Thanks in advance
What happens when you log JSON.stringify(readings) to the console? I am not a node.js expert but I think serial is undefined there since it is used outside the scope of rc522(function(serial){ ..} where it is defined
try
rc522(function(serial){
console.log(serial);
var readings = { Id:serial};
var message = new Message(JSON.stringify(readings));
client.sendEvent(message, function (error) {
// code here
});
});

Resources