Exchange data with connected products using the AWS IoT Core broker.
With this connector, you can connect the DPS with your AWS IoT Core, allowing to:
Receive data of your products connected to the AWS IoT Core broker.
Send configuration parameter updates or execute commands passing through the AWS IoT Device Shadow service.
Perform firmware update by using the AWS IoT Core Jobs service.
For more details you can refer to these articles:
AWS IoT Core Configuration
Once you have enabled the connector, you must go to your AWS IoT core administration page and configure a Message Routing rule.
A rule requires specifying the event to be listened, by defining a select statement which is applied on the incoming messages.
Below you can find some examples of SQL, also with conditions.
// forward all messages
SELECT *, topic() as topic FROM '#'
// forward all messages with condition on topic
SELECT *, topic() as topic FROM '#' WHERE topic(1) = 'dev-001' or topic(1) = 'dev-002'
// forward all messages with condition on serial number within payload
SELECT *, topic() as topic FROM '#' WHERE serialNumber = 'sn001' or serialNumber = 'sn002'
// forward all messages with condition on metric value within payload
SELECT *, topic() as topic FROM '#' WHERE data.temp > 50
For more details about how to define SQL refer to this article.
Within the rule, you must configure an action, you can use the HTTPS Endpoint action, which forward the MQTT messages to an external endpoint.
To configure the action, you must provide the following information.
// Endpoint URL
https://aws-iot-core.servitly.com/data
// Headers
Authorization: Basic Base64(<API_KEY>:<SECRET_KEY>)
In the alternative to the Servitly endpoint you can specify a custom URL that can be used for adapting messages.
Note that, once the rule has been created, if the automatic confirmation is not supported by your endpoint, it must be manually confirmed, else it must be marked as active.
You can verify the status of the endpoint by going in the Message Routing / Destinations page.
You can retrieve the API_KEY and SECRET_KEY from the API key you have selected in the AWS IoT Core plugin settings.
Each time a message arrives to the AWS IoT Core, the endpoint is called by passing to it the AWS IoT Core vent body and authorization header.
AWS IoT Core - MQTT Test Client
You can use the Test Client to publish a message.
Below is the message that IoT Core will forward to the endpoint. As you can see, it also includes the topic that is added as defined in the SQL.
{
"ts": 1684165930999,
"topic": "dev-001/data",
"data": {
"temp": 25
}
}
The topic used to post messages must be consistent with the thing mapping, otherwise the message is discarded.
The topic must be in these formats:
<THING_ASSET_ID>/<METRIC_MAPPING_PATH>
<THING_ASSET_ID>/<THING_MAPPING_PATH>/<METRIC_MAPPING_PATH>
In the case, the topic or the payload are not compatible with Servitly, you can use an AWS Lambda function to convert them and finally invoke the Servitly data endpoint.
Adapting Messages
In the case, your devices are already publishing data to the AWS IoT Core, but the topic and payload formats are not compatible with Servitly.
Within the Message Routing rule, you can reference an endpoint (e.g. AWS Lambda function URL) which can convert the topic and payload in order to be compliant with Servitly.
Here is an example of AWS Lambda you can use as a starting point.
Note that, this example is based on NodeJS, and uses CommonJS modules, so in the AWS Lamda function you need to configure the index.js file instead of the *.mjs (which is based on ES modules).
const https = require('https');
exports.handler = async(event) => {
console.debug(JSON.stringify(event));
// extracts the authorization header
let authHeader;
try {
if (event.headers.authorization) {
authHeader= event.headers.authorization;
}
if (!authHeader) {
throw new Error();
}
} catch (err) {
return {
statusCode: 401,
body: JSON.stringify({
error: "Invalid authorization"
}),
};
}
// reads the AWS IoT Core event
let payload, topic, assetId, path;
try {
payload = JSON.parse(event.body);
topic = payload.topic;
assetId = topic.substring(0, topic.indexOf("/"));
path = topic.substring(topic.indexOf("/") + 1);
// TODO: place here your code to adapth the paylod
} catch (err) {
return {
statusCode: 400,
body: JSON.stringify({
error: 'Invalid JSON payload'
}),
};
}
function forwardData(assetId, path, payload) {
// console.debug("ASSET_ID " + assetId);
// console.debug("PATH " + path);
// console.debug("PAYLOAD " + JSON.stringify(payload));
const options = {
hostname: 'aws-iot-core.servitly.com',
path: '/data?assetId=' + assetId + "&path=" + encodeURIComponent(path),
method: 'POST',
port: 443,
headers: {
'Content-Type': 'application/json',
'Authorization': authHeader
},
};
return new Promise((resolve, reject) => {
const req = https.request(options, res => {
if (res.statusCode < 300) {
// console.debug("Data successfully forwarded");
resolve(res);
} else {
// console.debug("Data forward error");
let error = new Error();
error.message = res.statusCode == 401 ? "Invalid authorization" : "Data forward error";
error.code = res.statusCode;
reject(error);
}
});
req.on('error', err => {
reject(new Error(err));
});
req.write(JSON.stringify(payload));
req.end();
});
}
try {
await forwardData(assetId, path, payload);
return {
statusCode: 204,
body: null,
};
} catch (err) {
return {
statusCode: err.code || 500,
body: JSON.stringify({
error: 'Failed to forward data to the AWS IoT Connector endpoint',
details: err
})
};
}
};
Note that once you have created the routing rule that forward messages to a custom endpoint (e.g. function URL of an AWS Lambda), in the AWS IoT Core management page, you must access the page Message routing / Destinations, and confirm the endpoint.
For more details about endpoint confirmation you can refer to this article.
Automatic Endpoint Confirmation
Here you can find the piece of code that you can add on top of your AWS Lambda function to handle the endpoint confirmation automatically.
// handle endpoint confirmation
if (event.queryStringParameters && event.queryStringParameters.confirmationToken) {
console.debug("Confirmation request");
let body = JSON.parse(event.body);
function confirmEndpoint(enableUrl) {
return new Promise((resolve, reject) => {
https.get(enableUrl, (res) => {
let data = '';
res.on('data', (chunk) => {
data += chunk;
});
res.on('end', () => {
console.log('Confirmation Success:', data);
resolve();
});
}).on('error', (err) => {
console.error('Confirmation Error:', err.message);
reject();
});
});
}
try {
await confirmEndpoint(body.enableUrl);
return {
statusCode: 204
};
} catch (err) {
return {
statusCode: 500,
body: JSON.stringify({
error: 'Confirmation Error',
details: err
})
};
}
}
AWS IoT Core Access
In order to send data to your AWS IoT Core connected devices, you need an AWS Access Key (to be configured in the AWS IoT Core plugin).
In the AWS account, an AWS IAM User must be set up with the policies described below.
For the IAM User, you must create an Access Key.
In the AWS IoT Core plugin, you need to enter the ID and Secret of the Access Key you created.
For more details about IAM users management, you can refer to this AWS guide.
IAM Identity Policies
AWS IoT Device Shadow
This policy is required to allow the AWS IoT Core plugin to invoke the Device Shadow API when a configuration parameter is updated or a command is executed.
{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Action": "iot:UpdateThingShadow",
"Resource": "arn:aws:iot:*:<aws-account-id>:thing/*"
}
]
}
AWS IoT Core Jobs
This policy is required to allow the AWS IoT Core plugin to invoke the Jobs API when a firmware update must be performed on the remote devices.
{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Action": "iot:CreateJob",
"Resource": [
"arn:aws:iot:*:<aws-account-id>:thing/*",
"arn:aws:iot:*:<aws-account-id>:job/*"
]
}
]
}
Each plugin update may take a few minutes before it is propagated and used by the connector.
Plugin Configuration
To enable this plugin, you need to:
Go to the Integrations / Plugins page.
Select the IoT Connectors category.
Locate the AWS IoT Core card.
Click on the card switch to activate the plugin.
Configure the plugin properties and save.
In order to receive IoT messages you must select an API Key that will be used in the forward rule to authenticate the client.
The other settings are required only, if you need to send remote updates (parameters and command) or firmware updates.
Here is the list of all the properties that can be used in the plugin configuration.
PROPERTIES | |
---|---|
API Key | The Servitly API Key required to authenticate incoming HTTP requests from the AWS IoT Core forwarding rules or Lambda functions used for message adaptation. Incoming requests must have the Basic authorization header based on the public and secret key values. |
Access Key ID | The access key ID used to connect to the AWS IoT Core services. |
Secret Access Key | The secret access key used to connect to the AWS IoT Core services. |
Region | The AWS region where devices mapped to AWS IoT core are located. |
Base Device Shadow URL | The base URL used to make request to the AWS IoT Device Shadow service. Required for devices remote control. |
Shadow Payload Adapter | The URL of the Lambda function used to adapt configuration parameters and command messages to AWS IoT Device Shadow messages. If missing, a default adaptation is performed. |
Job Document Mode | The way the firmware file is included in the AWS IoT Job creation action. |