6.8. EdgeLock 2GO Agent Examples¶
The usage of EdgeLock 2GO agent is demonstrated with the following example demo source code.
The source code example is taken from the following file
<SE05X_root_folder>/simw-top/nxp_iot_agent/ex/src/iot_agent_demo.c
.
6.8.1. Initializaton of context and updating device configuration¶
Initialization of contexts of EdgeLock 2GO agent its keystores and datastores
agent_status = iot_agent_init(&iot_agent_context);
AGENT_SUCCESS_OR_EXIT();
// print the uid of the device
agent_status = iot_agent_print_uid((sss_se05x_session_t*)&pCtx->session);
AGENT_SUCCESS_OR_EXIT();
/************* Register keystore*************/
agent_status = iot_agent_keystore_sss_se05x_init(&keystore, EDGELOCK2GO_KEYSTORE_ID, pCtx, true);
AGENT_SUCCESS_OR_EXIT();
agent_status = iot_agent_register_keystore(&iot_agent_context, &keystore);
AGENT_SUCCESS_OR_EXIT();
/************* Register datastore*************/
// This is the datastore that holds connection information on how to connect to
// the EdgeLock 2GO cloud service itself. Typically this would be a persistent
// datastore which supports transactions to allow for atomic updates of
// the URL/port/etc. If a filesystem is available, here, we use a file-based
// datastore.
// Note, that the ID of the datastore is a given.
#if DS_FS
agent_status = iot_agent_datastore_fs_init(&edgelock2go_datastore,
nxp_iot_DatastoreIdentifiers_DATASTORE_EDGELOCK2GO_ID, gszEdgeLock2GoDatastoreFilename,
&iot_agent_service_is_configuration_data_valid);
#elif DS_PLAIN
agent_status = iot_agent_datastore_plain_init(&edgelock2go_datastore,
nxp_iot_DatastoreIdentifiers_DATASTORE_EDGELOCK2GO_ID);
#endif
AGENT_SUCCESS_OR_EXIT();
// If the contents of the datastore for the connectin to the EdgeLock 2O datastore
// are not valid (e.g. on the first boot), fill the datastore with contents
// from the settings contained in nxp_iot_agent_config.h
if (!iot_agent_service_is_configuration_data_valid(&edgelock2go_datastore)) {
iot_agent_utils_write_edgelock2go_datastore(&keystore, &edgelock2go_datastore,
EDGELOCK2GO_HOSTNAME, EDGELOCK2GO_PORT, iot_agent_trusted_root_ca_certificates);
}
// For connecting to the EdgeLock 2GO cloud service, we also need to register the
// datastore that contains the information on how to connect there.
agent_status = iot_agent_set_edgelock2go_datastore(&iot_agent_context, &edgelock2go_datastore);
AGENT_SUCCESS_OR_EXIT();
#if DS_FS
agent_status = iot_agent_datastore_fs_init(&datastore, 0U, gszDatastoreFilename,
&iot_agent_service_is_configuration_data_valid);
AGENT_SUCCESS_OR_EXIT();
#endif
#if DS_PLAIN
agent_status = iot_agent_datastore_plain_init(&datastore, 0U);
AGENT_SUCCESS_OR_EXIT();
#endif
agent_status = iot_agent_register_datastore(&iot_agent_context, &datastore);
AGENT_SUCCESS_OR_EXIT();
Update device configuration:
agent_status = iot_agent_update_device_configuration(&iot_agent_context, &status_report);
// We have a status report which can have some details on the operations that happened.
// This gives the opportunity to programmatically react on errors/failures. For demonstration
// purpose, print that information to the console here.
if ((agent_status == IOT_AGENT_SUCCESS || agent_status == IOT_AGENT_UPDATE_FAILED) && status_report.has_status) {
print_status_report(&status_report);
}
AGENT_SUCCESS_OR_EXIT();
Iterate over configured services and access credentials and service configuration data:
if (!iot_agent_is_service_configuration_data_valid(&iot_agent_context))
{
EXIT_STATUS_MSG(IOT_AGENT_FAILURE, "Not all configuration data is valid");
}
// get total number of services
number_of_services = iot_agent_get_number_of_services(&iot_agent_context);
IOT_AGENT_INFO("Found configuration data for %d services.", (int) number_of_services);
6.8.2. MQTT connection tests with Cloud Onboarding Provisioning¶
The example shows how to trigger the MQTT connection tests to the services when they are provisioned
through the Cloud Onboaring Provisioning; the connection is triggered from following file:
<SE05X_root_folder>/simw-top/nxp_iot_agent/ex/src/iot_agent_demo.c
#if (AX_EMBEDDED && defined(USE_RTOS) && USE_RTOS == 1) || (SSS_HAVE_HOSTCRYPTO_OPENSSL)
agent_status = iot_agent_verify_mqtt_connection(&iot_agent_context);
AGENT_SUCCESS_OR_EXIT();
#endif
In case of FreeRTOS platform as LPC55S or FRDM_K64F the MQTT FreeRTOS client will be used
for connection; implementation details can be found under
<SE05X_root_folder>/simw-top/nxp_iot_agent/ex/src/utils/iot_agent_mqtt_freertos.c
:
iot_agent_status_t iot_agent_verify_mqtt_connection_for_service(iot_agent_context_t* iot_agent_context,
const nxp_iot_ServiceDescriptor* service_descriptor)
{
iot_agent_status_t agent_status = IOT_AGENT_SUCCESS;
agent_status = pubSub(iot_agent_context, service_descriptor);
AGENT_SUCCESS_OR_EXIT_MSG("MQTT connection test failed");
exit:
return agent_status;
}
iot_agent_status_t iot_agent_verify_mqtt_connection(iot_agent_context_t* iot_agent_context)
{
iot_agent_status_t agent_status = IOT_AGENT_SUCCESS;
size_t number_of_services = 0U;
nxp_iot_ServiceDescriptor service_descriptor = nxp_iot_ServiceDescriptor_init_default;
number_of_services = iot_agent_get_number_of_services(iot_agent_context);
for (size_t i = 0U; i < number_of_services; i++)
{
agent_status = iot_agent_select_service_by_index(iot_agent_context, i, &service_descriptor);
AGENT_SUCCESS_OR_EXIT();
agent_status = iot_agent_verify_mqtt_connection_for_service(iot_agent_context, &service_descriptor);
AGENT_SUCCESS_OR_EXIT();
}
exit:
iot_agent_free_service_descriptor(&service_descriptor);
return agent_status;
}
In case of Open SSL platform as iMX6 or iMX8 the MQTT Paho client will be used for connection;
implementation details can be found under
<SE05X_root_folder>/simw-top/nxp_iot_agent/ex/src/utils/iot_agent_mqtt_paho.c
:
iot_agent_status_t iot_agent_verify_mqtt_connection_for_service(iot_agent_context_t* iot_agent_context, const nxp_iot_ServiceDescriptor* service_descriptor)
{
iot_agent_status_t agent_status = IOT_AGENT_SUCCESS;
int mkdir_check = 0;
#if IOT_AGENT_MQTT_CONNECTION_TEST_ENABLE
mqtt_connection_params_t connection_params = { 0 };
iot_agent_status_t mqtt_status = IOT_AGENT_SUCCESS;
#endif
//write to files
char config_filename[256];
char certificate_filename[256];
char keyref_filename[256];
char server_cert_filename[256];
char metadata_filename[256];
const char* service_type_str;
char service_dir[64];
uint32_t keystore_id = service_descriptor->client_key_sss_ref.endpoint_id;
iot_agent_keystore_t* keystore = NULL;
agent_status = iot_agent_get_keystore_by_id(iot_agent_context, keystore_id, &keystore);
AGENT_SUCCESS_OR_EXIT();
agent_status = iot_agent_keystore_open_session(keystore);
AGENT_SUCCESS_OR_EXIT();
agent_status = iot_agent_service_get_service_type_as_string(service_descriptor, &service_type_str);
AGENT_SUCCESS_OR_EXIT();
if (ACCESS(goutput_directory, R_OK) != 0)
{
mkdir_check = DO_MKDIR(goutput_directory);
ASSERT_OR_EXIT(mkdir_check == 0);
}
snprintf(service_dir, sizeof(service_dir), "%s%c%s%c", goutput_directory, path_separator, service_type_str, path_separator);
if (ACCESS(service_dir, R_OK) != 0)
{
mkdir_check = DO_MKDIR(service_dir);
ASSERT_OR_EXIT(mkdir_check == 0);
}
snprintf(config_filename, sizeof(config_filename), "%s" SERVICE_CONFIGURATION_PATTERN, service_dir, service_descriptor->identifier);
snprintf(metadata_filename, sizeof(metadata_filename), "%s" SERVICE_METADATA_PATTERN, service_dir, service_descriptor->identifier);
snprintf(keyref_filename, sizeof(keyref_filename), "%s" SERVICE_KEYREF_PATTERN, service_dir, service_descriptor->identifier);
snprintf(certificate_filename, sizeof(certificate_filename), "%s" SERVICE_CERTIFICATE_PATTERN, service_dir, service_descriptor->identifier);
snprintf(server_cert_filename, sizeof(server_cert_filename), "%s" SERVICE_SERVER_CERT_PATTERN, service_dir, service_type_str, service_descriptor->identifier);
//create service files
char relative_certificate_filename[128];
char relative_keyref_filename[128];
char relative_server_cert_filename[128];
snprintf(relative_certificate_filename, sizeof(relative_certificate_filename), SERVICE_CERTIFICATE_PATTERN, service_descriptor->identifier);
snprintf(relative_keyref_filename, sizeof(relative_keyref_filename), SERVICE_KEYREF_PATTERN, service_descriptor->identifier);
snprintf(relative_server_cert_filename, sizeof(relative_server_cert_filename), SERVICE_SERVER_CERT_PATTERN, service_type_str, service_descriptor->identifier);
if (IOT_AGENT_SUCCESS != write_service_configuration(service_descriptor, config_filename,
relative_certificate_filename, relative_keyref_filename, relative_server_cert_filename))
{
write_error_logs(config_filename);
}
else
{
LOG_D("Created service configuration file [%s]", config_filename);
}
if (IOT_AGENT_SUCCESS != write_service_metadata(service_descriptor, metadata_filename))
{
write_error_logs(metadata_filename);
}
else
{
LOG_D("Created metadata file [%s]", metadata_filename);
}
if (service_descriptor->server_certificate != NULL) {
if (IOT_AGENT_SUCCESS != iot_agent_utils_write_certificate_pem(service_descriptor->server_certificate->bytes,
(size_t)service_descriptor->server_certificate->size, server_cert_filename))
{
write_error_logs(server_cert_filename);
}
else
{
LOG_D("Created server root certificate file [%s]", server_cert_filename);
}
}
if (service_descriptor->client_certificate != NULL) {
if (IOT_AGENT_SUCCESS != iot_agent_utils_write_certificate_pem(service_descriptor->client_certificate->bytes,
(size_t)service_descriptor->client_certificate->size, certificate_filename))
{
write_error_logs(certificate_filename);
}
else
{
LOG_D("Created service client certificate file [%s]", certificate_filename);
}
}
if (IOT_AGENT_SUCCESS != iot_agent_utils_write_key_ref_service_pem(iot_agent_context, keyref_filename))
{
write_error_logs(keyref_filename);
}
else
{
LOG_D("Created service keyref file [%s]", keyref_filename);
}
//write to files - end
#if IOT_AGENT_MQTT_CONNECTION_TEST_ENABLE
iot_agent_keystore_close_session(keystore);
connection_params.keypath = keyref_filename;
connection_params.devcert = certificate_filename;
connection_params.rootpath = server_cert_filename;
mqtt_status = iot_agent_mqtt_test(service_descriptor, &connection_params);
agent_status = iot_agent_keystore_open_session(keystore);
AGENT_SUCCESS_OR_EXIT();
#endif
exit:
if (mqtt_status != IOT_AGENT_SUCCESS) {
return mqtt_status;
}
return agent_status;
}
iot_agent_status_t iot_agent_verify_mqtt_connection(iot_agent_context_t* iot_agent_context)
{
iot_agent_status_t agent_status = IOT_AGENT_SUCCESS;
size_t number_of_services = 0U;
nxp_iot_ServiceDescriptor service_descriptor = nxp_iot_ServiceDescriptor_init_default;
number_of_services = iot_agent_get_number_of_services(iot_agent_context);
delete_old_service_files_folders(goutput_directory);
AGENT_SUCCESS_OR_EXIT();
for (size_t i = 0U; i < number_of_services; i++)
{
agent_status = iot_agent_select_service_by_index(iot_agent_context, i, &service_descriptor);
AGENT_SUCCESS_OR_EXIT();
agent_status = iot_agent_verify_mqtt_connection_for_service(iot_agent_context, &service_descriptor);
AGENT_SUCCESS_OR_EXIT();
}
exit:
iot_agent_free_service_descriptor(&service_descriptor);
return agent_status;
}
6.8.3. MQTT connection tests with Remote Trust Provisioning¶
The example shows how to trigger the MQTT connection tests to the services when they are provisioned
through the Remote Trust Provisioning; the connection is triggered from following file:
<SE05X_root_folder>/simw-top/nxp_iot_agent/ex/src/iot_agent_demo.c
#if ((AX_EMBEDDED && defined(USE_RTOS) && USE_RTOS == 1) || (SSS_HAVE_HOSTCRYPTO_OPENSSL)) && (IOT_AGENT_COS_OVER_RTP_ENABLE == 1)
// example code for MQTT conneciton when provisioning services as RTP objects
iot_agent_cleanup_mqtt_config_files_cos_over_rtp();
AGENT_SUCCESS_OR_EXIT();
// use this code for AWS services
// modify the connection parameters to allow connection to your AWS account
agent_status = iot_agent_get_mqtt_service_descriptor_for_aws(&iot_agent_context, &aws_service_descriptor);
AGENT_SUCCESS_OR_EXIT();
// in case of AWS device auto-registration the MQTT connection will fail on the first connection; in this
// use case add additional call to the funcion iot_agent_verify_mqtt_connection_cos_over_rtp
agent_status = iot_agent_verify_mqtt_connection_cos_over_rtp(&iot_agent_context, &aws_service_descriptor);
AGENT_SUCCESS_OR_EXIT();
// use this code for Azure services
agent_status = iot_agent_get_service_descriptor_for_azure(&iot_agent_context, &azure_service_descriptor);
AGENT_SUCCESS_OR_EXIT();
agent_status = iot_agent_verify_mqtt_connection_cos_over_rtp(&iot_agent_context, &azure_service_descriptor);
AGENT_SUCCESS_OR_EXIT();
#endif
To enable the MQTT connection for the RTP use case some manual adjustments needs to be done in the Agent code;
the use case can be enabled by setting the variable IOT_AGENT_COS_OVER_RTP_ENABLE to 1 in the file
<SE05X_root_folder>/simw-top/nxp_iot_agent/inc/nxp_iot_agent_config.h
The user has to setup some configuration parameters for the service to which he want to connect; this can be done
by changing some definitions in the file:
<SE05X_root_folder>/simw-top/nxp_iot_agent/ex/inc/iot_agent_config.h
// CHANGE THIS : fill with key pair and device certificate object IDs as defined on EL2GO when generating them
#define AWS_SERVICE_KEY_PAIR_ID 0x83000101
#define AWS_SERVICE_DEVICE_CERT_ID 0x83000102
// CHANGE THIS: the AWS hostname to which the device will connect
#define AWS_HOSTNAME "aw9969rp3sm22-ats.iot.eu-central-1.amazonaws.com"
// optional: the client ID will be by default set dynamically to the Common Name of the
// device leaf certficate; is possible to hardocode it, by uncommenting the below line and assign
// the desired value
//#define AWS_CLIENT_ID "awsrtptest-0000000000001e6e-0000"
// CHANGE THIS: set the desired service ID; is used internally by the MQTT client, should be unique
// for every service
#define AWS_SERVICE_ID 101
// CHANGE THIS : fill with key pair and device certificate object IDs as defined on EL2GO when generating them
#define AZURE_SERVICE_KEY_PAIR_ID 0x83000211
#define AZURE_SERVICE_DEVICE_CERT_ID 0x83000212
// CHANGE THIS : set the ID scope and the global endpoint as defined in the Azure DPS account
#define AZURE_ID_SCOPE "0ne004510C6"
#define AZURE_GLOBAL_DEVICE_ENDPOINT "global.azure-devices-provisioning.net"
// optional: the registration ID will be by default set dynamically to the Common Name of the
// device leaf certficate; is possible to hardocode it, by uncommenting the below line and assign
// the desired value
//#define AZURE_REGISTRATION_ID "azurertptest-0000000000001e51-0000"
// CHANGE THIS: set the desired service ID; is used internally by the MQTT client, should be unique
// for every service
#define AZURE_SERVICE_ID 102
Depending on the service type the Service Descriptor should be set to include the correct connection data
and object IDs of the Key Pair and X.509 device leaf certificate associated with the service. The following
functions should be modified in the file
<SE05X_root_folder>/simw-top/nxp_iot_agent/ex/src/iot_agent_demo.c
iot_agent_status_t iot_agent_get_mqtt_service_descriptor_for_aws(iot_agent_context_t* iot_agent_context,
nxp_iot_ServiceDescriptor* service_descriptor)
{
iot_agent_status_t agent_status = IOT_AGENT_SUCCESS;
ASSERT_OR_EXIT_MSG(service_descriptor != NULL, "Service descriptor is null");
// AWS Service type
service_descriptor->identifier = AWS_SERVICE_ID;
service_descriptor->has_service_type = true;
service_descriptor->service_type = nxp_iot_ServiceType_AWSSERVICE;
// service key pair
service_descriptor->has_client_key_sss_ref = true;
service_descriptor->client_key_sss_ref.object_id = AWS_SERVICE_KEY_PAIR_ID;
service_descriptor->client_key_sss_ref.has_type = false;
service_descriptor->client_key_sss_ref.type = nxp_iot_EndpointType_INVALID;
service_descriptor->client_key_sss_ref.has_endpoint_id = true;
service_descriptor->client_key_sss_ref.endpoint_id = EDGELOCK2GO_KEYSTORE_ID;
service_descriptor->client_key_sss_ref.has_object_id = true;
// client certificate
service_descriptor->has_client_certificate_sss_ref = true;
service_descriptor->client_certificate_sss_ref.object_id = AWS_SERVICE_DEVICE_CERT_ID;
service_descriptor->client_certificate_sss_ref.has_type = false;
service_descriptor->client_certificate_sss_ref.type = nxp_iot_EndpointType_INVALID;
service_descriptor->client_certificate_sss_ref.has_endpoint_id = true;
service_descriptor->client_certificate_sss_ref.endpoint_id = EDGELOCK2GO_KEYSTORE_ID;
service_descriptor->client_certificate_sss_ref.has_object_id = true;
// AWS server certificate
service_descriptor->server_certificate = (pb_bytes_array_t*)&aws_root_server_cert;
service_descriptor->has_server_certificate_sss_ref = false;
// AWS MQTT connection parameters
char hostname[] = AWS_HOSTNAME;
iot_agent_fill_service_char_array(&service_descriptor->hostname, hostname, sizeof(hostname));
service_descriptor->has_port = true;
service_descriptor->port = 8883;
service_descriptor->has_timeout_ms = true;
service_descriptor->timeout_ms = 0x4E20;
service_descriptor->has_protocol = true;
service_descriptor->protocol = nxp_iot_ServiceProtocolType_MQTTS;
#ifdef AWS_CLIENT_ID
char client_id[] = AWS_CLIENT_ID;
iot_agent_fill_service_char_array(&service_descriptor->client_id, client_id, sizeof(client_id));
#else
service_descriptor->client_id = malloc(COMMON_NAME_MAX_SIZE);
memset(service_descriptor->client_id, 0, COMMON_NAME_MAX_SIZE);
agent_status = iot_agent_utils_get_certificate_common_name(iot_agent_context, service_descriptor, service_descriptor->client_id, COMMON_NAME_MAX_SIZE);
AGENT_SUCCESS_OR_EXIT();
#endif
service_descriptor->username = NULL;
service_descriptor->password = NULL;
// fill metadata if any
service_descriptor->customer_metadata_json = NULL;
exit:
return agent_status;
}
iot_agent_status_t iot_agent_get_service_descriptor_for_azure(iot_agent_context_t* iot_agent_context,
nxp_iot_ServiceDescriptor* service_descriptor)
{
iot_agent_status_t agent_status = IOT_AGENT_SUCCESS;
ASSERT_OR_EXIT_MSG(service_descriptor != NULL, "Service descriptor is null");
// Azure Service type
service_descriptor->identifier = AZURE_SERVICE_ID;
service_descriptor->has_service_type = true;
service_descriptor->service_type = nxp_iot_ServiceType_AZURESERVICE;
// service key pair
service_descriptor->has_client_key_sss_ref = true;
service_descriptor->client_key_sss_ref.object_id = AZURE_SERVICE_KEY_PAIR_ID;
service_descriptor->client_key_sss_ref.has_type = false;
service_descriptor->client_key_sss_ref.type = nxp_iot_EndpointType_INVALID;
service_descriptor->client_key_sss_ref.has_endpoint_id = true;
service_descriptor->client_key_sss_ref.endpoint_id = EDGELOCK2GO_KEYSTORE_ID;
service_descriptor->client_key_sss_ref.has_object_id = true;
// service client certificate
service_descriptor->has_client_certificate_sss_ref = true;
service_descriptor->client_certificate_sss_ref.object_id = AZURE_SERVICE_DEVICE_CERT_ID;
service_descriptor->client_certificate_sss_ref.has_type = false;
service_descriptor->client_certificate_sss_ref.type = nxp_iot_EndpointType_INVALID;
service_descriptor->client_certificate_sss_ref.has_endpoint_id = true;
service_descriptor->client_certificate_sss_ref.endpoint_id = EDGELOCK2GO_KEYSTORE_ID;
service_descriptor->client_certificate_sss_ref.has_object_id = true;
// Azure server certificate
service_descriptor->server_certificate = (pb_bytes_array_t*)&azure_root_server_cert;
service_descriptor->has_server_certificate_sss_ref = false;
// Azure MQTT connection parameters
char azure_id_scope[] = AZURE_ID_SCOPE;
iot_agent_fill_service_char_array(&service_descriptor->azure_id_scope, azure_id_scope, sizeof(azure_id_scope));
// the registration ID will be by defualt set dynamically to the Common Name of the device leaf certficate;
// it is possible to hardocode it, by uncommenting the AZURE_REGISTRATION_ID in the configuration file
#ifdef AZURE_REGISTRATION_ID
char azure_registration_id[] = AZURE_REGISTRATION_ID;
iot_agent_fill_service_char_array(&service_descriptor->azure_registration_id, azure_registration_id, sizeof(azure_registration_id));
#else
service_descriptor->azure_registration_id = malloc(COMMON_NAME_MAX_SIZE);
memset(service_descriptor->azure_registration_id, 0, COMMON_NAME_MAX_SIZE);
agent_status = iot_agent_utils_get_certificate_common_name(iot_agent_context, service_descriptor, service_descriptor->azure_registration_id, COMMON_NAME_MAX_SIZE);
AGENT_SUCCESS_OR_EXIT();
#endif
char azure_global_device_endpoint[] = AZURE_GLOBAL_DEVICE_ENDPOINT;
iot_agent_fill_service_char_array(&service_descriptor->azure_global_device_endpoint, azure_global_device_endpoint, sizeof(azure_global_device_endpoint));
service_descriptor->has_port = false;
service_descriptor->port = 0;
service_descriptor->has_timeout_ms = true;
service_descriptor->timeout_ms = 0x4E20;
service_descriptor->has_protocol = false;
service_descriptor->username = NULL;
service_descriptor->password = NULL;
// fill metadata if any
service_descriptor->customer_metadata_json = NULL;
exit:
return agent_status;
}
In the case of AWS auto-registration the Intermediate certificate used to sign the device leaf certificate needs to be presented
to the cloud when doing the registration; EdgeLock 2GO offers the possibility to provision the intermediate CA together
with the leaf certificate in one combined object.
The MQTT Client on Embedded Linux devices is able to automatically load the combined object and execute the auto-registration.
On FreeRTOS devices the dynamic loading is not implemented and the intermediate certificate must be provided in the code; to achieve this,
the value of the #define keyJITR_DEVICE_CERTIFICATE_AUTHORITY_PEM in file <SE05X_root_folder>/simw-top/demos/ksdk/common/aws_clientcredential_keys.h
must be filled with the value of the intermediate certificate in PEM format.