English | Simplified Chinese
Version Number | Change Type | Description |
---|---|---|
1.1.5 | Function enhancement | Update OTA upgrade transmission format |
1.1.4 | Function enhancement | Fixed the issue of SSH remote where packet reporting times out. |
1.1.3 | Function enhancement | update conf\rootcert.pem |
1.1.2 | New feature | Device rules, M2M, GN compilation file, anomaly detection, timestamp printed in logs, MQTT_DEBUG, Chinese cryptographic algorithm, remote configuration, and device-cloud secure communication (soft bus) were added. |
1.1.1 | New feature | SSH remote O&M function was added. |
1.1.0 | New feature | MQTT 5.0 was supported. The code was optimized to resolve the memory overflow issue. |
1.0.1 | Function enhancement | The application scenarios where MQTTS does not verify the platform public key, using TLS version is V1.2, and adding message storage examples were added. |
0.9.0 | New feature | The API for the gateway to update the child device status was added. |
0.8.0 | Function enhancement | The access domain name (iot-mqtts.cn-north-4.myhuaweicloud.com) and root certificate were added. If the device uses the old domain name (iot-acc.cn-north-4.myhuaweicloud.com) for access, use the v0.5.0 SDK. |
0.5.0 | Function enhancement | The device access address and the CA certificate matching IoTDA were preset in the SDK. |
-
Generic-protocol access was added.
-
Bootstrap scenario was added.
-
Encapsulation of SDK downstream payload structure was added.
-
Compressed message and property reporting were added.
-
Implicit subscription was added.
-
Context callback parameters were added.
-
English description was added.
-
The functions of adding and deleting a child device on a gateway were added.
-
Time synchronization function was added.
-
The API for reporting device logs was added.
-
The API for reporting device information was added.
-
The compatibility description of certificates of different versions was added.
-
Configurable timestamp verification was added.
-
The .so library file can be automatically generated.
-
The application scenario where MQTTS does not verify the platform public key was added.
-
The TLS version is V1.2.
-
Message storage examples were added.
-
File uploading/downloading were added.
-
MQTT5.0 was supported.
-
Device-side rules were supported.
-
SSH remote login function was added.
-
The M2M function was added.
-
The GN compilation file was added.
-
The device information abstraction layer was added.
-
Anomaly detection was added.
-
The function of printing timestamps in logs was added.
-
MQTT_DEBUG was added.
-
Remote configuration was added.
-
Device-cloud secure communication (soft bus) was added.
-
Update server ca conf/rootcert.pem
-
Optimize the timeout period for SSH remote reporting packets.
Sept 5, 2023
This document uses an example to describe how to use **huaweicloud-iot-device-sdk-c** (SDK for short) to quickly connect MQTT devices to the Huawei Cloud IoT platform. The SDK is designed for embedded devices with powerful computing and storage capabilities. You can call SDK APIs to implement communication between devices and the platform. The SDK currently supports:-
Product model: reporting device messages/properties/events and receiving the platform's commands/messages/events/requests for setting properties
-
Child device message forwarding and child device management
-
Over-the-air (OTA) upgrades
-
Secret authentication and certificate authentication for device access
-
Topic customization
-
Device shadow query
-
Custom log collection
-
Device-side rules
-
SSH remote login
-
Interconnection with edge M2M devices
SDK Directory Structure
The SDK must run on the Linux OS, and GCC (version 4.8 or later is recommended) must be installed. The SDK depends on the OpenSSL and Paho libraries. If you have your own compilation chains, compile these library files. For details about the common GCC compilation procedure in Linux, see section 3.2 and 3.3.-
Visit the OpenSSL website (https://www.openssl.org/source/), download the latest OpenSSL version (for example, openssl-1.1.1d.tar.gz), upload it to the Linux compiler (for example, in the directory /home/test), and run the following command to decompress the package:
tar -zxvf openssl-1.1.1d.tar.gz
-
Generate a Makefile. Run the following command to access the OpenSSL source code directory:
cd openssl-1.1.1d
Run the following configuration command:
./config shared --prefix=/home/test/openssl --openssldir=/home/test/openssl/ssl
prefix is a custom installation directory, openssldir is a custom directory that stores configuration files, and shared is used to generate a dynamic link library (.so library).
If an exception occurs during the compilation, add no-asm to the configuration command (indicating that the assembly code is not used).
./config no-asm shared --prefix=/home/test/openssl --openssldir=/home/test/openssl/ssl
-
Generate library files. In the OpenSSL source code directory, run the following command to add dependencies:
make depend
Run the following command to start compilation:
make
Run the following command to install:
make install
In the OpenSSL installation directory home/test/openssl, find the lib folder.
Find the library files (libcrypto.so.1.1 and libssl.so.1.1) and soft links (libcrypto.so and libssl.so). Copy these files to the lib folder of the SDK and copy the openssl folder in the home/test/openssl/include directory to the include folder of the SDK.
-
To use the TLS for Chinese cryptographic algorithm, download the corresponding version of OpenSSL. The installation method is the same as that of native OpenSSL. Based on OpenSSL 1.1.1s, this version is compatible with various native OpenSSL APIs and supports international TLS.
-
Visit https://github.com/eclipse/paho.mqtt.c to download the paho.mqtt.c source code. The files of version 1.3.9 or earlier in the release version are recommended. For the latest version, the number of lines in the file and the header files to be copied may be changed.
-
Decompress the package and upload it to the Linux compiler.
-
Edit the Makefile.
-
Run the following command to edit the Makefile:
vim Makefile
-
Run the following command to display the number of lines:
:set nu
-
Add the following two lines after DOXYGEN_COMMAND (locations of the OpenSSL header files and library files customized in 3.2):
CFLAGS += -I/home/test/openssl/include LDFLAGS += -L/home/test/openssl/lib -lrt
-
Add the header file reference of the OpenSSL to the CCFLAGS_SO and add library file references to LDFLAGS_CS, LDFLAGS_AS, and FlagS_EXES. (The reference is added by default in some updated versions of Paho libraries. In this case, you do not need to modify the reference.)
-
-
Compile the code.
-
Run the following command:
make clean
-
Run the following command:
make
-
-
After the compilation is complete, you can view the libraries that are compiled in the build/output directory.
-
Copy Paho library files. Currently, the SDK uses only libpaho-mqtt3as. Copy the libpaho-mqtt3as.so、libpaho-mqtt3as.so.1 and libpaho-mqtt3as.so.1.3 files to the lib folder of the SDK, and copy the header files (MQTTAsync.h, MQTTClient.h, MQTTClientPersistence.h, MQTTProperties.h, MQTTReasonCodes.h, and MQTTSubscribeOpts.h) in the src folder of the Paho source code directory to the include/base directory of the SDK. Alternatively, you can add all MQTT-related header files to the directory, as some versions of Paho libraries have the MQTTExportDeclarations.h header file.
-
Download the zlib source code package. Run the following command to decompress the package:
unzip zlib-1.2.11.zip
-
Run the following command to access the source code directory:
cd zlib-1.2.11
-
Generate a Makefile.
./configure
-
Run the Makefile.
make
-
Copy .so library files. Copy the libz.so, libz.so.1, and libz.so.1.2.11 files generated in the source code directory to the lib folder of the SDK.
-
Download the secure_c source code package.
-
Access the source code directory and run the Makefile with the following command:
make
-
Copy .so library files. Copy the libboundscheck.so files generated in the source code of lib directory to the lib folder of the SDK.
-
Download the libssh source code package. Run the following command to decompress the package:
tar xvf libssh-0.10.4.tar.xz
-
Run the following command to access the source code directory:
cd libssh-0.10.4
-
Run the following commands to compile the library files:
mkdir build cd build cmake .. make
-
Run the following command to install the lib:
sudo make install
-
Copy .so library files and header files. Copy the libssh.so, libssh.so.4, and libssh.so.4.9.4 files generated in the source code directory build/lib to the lib folder of the SDK. Copy the libssh folder in the usr/local/include directory to the include folder of the SDK.
-
Download the libnopoll source code package. Run the following command to decompress the package:
tar xzvf nopoll-0.4.8.b429.tar.gz
-
Run the following command to access the source code directory:
cd nopoll-0.4.8.b429
-
Run the following commands to compile and install the library files:
./configure make sudo make install pkg-config nopoll --cflags pkg-config nopoll --libs
-
Copy .so library files. Copy the libnopoll.so, libnopoll.so.0, and libnopoll.so.0.0.0 files generated in the source code directory src/.libs to the lib folder of the SDK. Copy the nopoll folder in the usr/local/include directory to the include folder of the SDK.
-
To import the developed profile (product model) to the console, choose Products in the navigation pane, click Model Definition on the displayed page, click Import from Local, select Add File, select a file, and click OK.
-
Choose Device > All Devices in the navigation pane, click Individual Register in the upper right corner, select the resource space where the product is located, select the product, enter the node ID (IMEI or MAC address), and customize the device name. If you do not customize the secret, the value is automatically generated by the platform. After all parameters are set, click OK.
-
You can copy the device secret and click Save & Close. The device ID and device secret are automatically downloaded to the local host in TXT format.
-
Choose Devices > All Devices in the navigation pane and you can see that the status of the newly created device is inactive.
-
Copy the SDK package to the Linux environment and run the following command to decompress the package:
unzip huaweicloud-iot-device-sdk-c-master.zip
-
Run the following command to go to the decompressed folder:
cd huaweicloud-iot-device-sdk-c-master
-
Modify configuration. You need to modify the following parameters in the src/device_demo/device_demo.c file: g_serverIp: southbound IP address of IoTDA, which can be viewed on the overview page of the console. g_username: parameter required for using MQTT. The default value is device ID, which is returned during device registration. g_password: device secret, which is returned during device registration.
-
Run the make command to start compilation, It is recommended to use Makefile to compile.
4.1 Run the following command to compile the file using Makefile:
make (Delete -m64 from the Makefile in a 32-bit OS.)
4.2 Run the following command to compile the file using GN, first install gn and ninja tools:
gn gen -C out ninja -C out cd out
-
Run.
5.1 Run the following command to load library files:
export LD_LIBRARY_PATH=./lib/
5.2 Run the following command:
./MQTT_Demo
You can view many printed logs on the console. login success indicates that the device authentication is successful.
MqttBase_onSubscribeSuccess indicates that the subscription is successful.
MqttBase_onPublishSuccess indicates that the data has been published.
-
Check the running status of the device.
-
Device-side Rules
7.1 Delivering Rules to a Device
Log in to the IoTDA console, choose Rules > Device Linkage, and click
Create Rule
.Enter the rule name, select the Device Side mode, and select a device.
On the console, when the property is reported and the value of
PhV_phsA
is 9, the rule on the device is triggered and theHandleCommandRequest
is invoked.If a command is executed across devices, the callback function IOTA_SetDeviceRuleSendMsgCallback needs to be invoked. You need to implement the HandleDeviceRuleSendMsg function to parse and execute the command.
7.2 Storing the Device-side Rules Locally
After the device is restarted and no longer connected to the network, the configured device-side rules cannot be obtained from the cloud. Store the rules in a file so that they can still be read after the device restarts. To use this feature, the device must support the file system. You only need to provide a path for the IOTA_EnableDeviceRuleStorage API (invoked in src/device_demo/device_demo.c) to locally store rules on the device. Change the value testdata.txt of the macro definition DEVICE_RULE_FILE_PATH in include/agentlite/iota_datatrans.h to the target file storage path.
-
SSH Remote Login
Before using the SSH remote login function, compile the libssh and libnopoll libraries by referring to 3.5 Compiling the libssh Library and 3.6 Compiling the libnopoll Library. In addition, ensure that the device is online.
Log in to the IoTDA console, choose O&M > Remote Login in the navigation pane, select an online device, enter the user name and password, and click OK.
The ssh remote login can be realized after the operations. Now you can enter commands for interactive operation.
-
Connecting to the Edge M2M
Currently, the SDK can be used to connect to the edge IoTEdge. The edge node forwards messages to the target device to implement the M2M function. Procedure:
-
- Set up an edge node, create a product, and add edge devices. For details, see Best Practices. https://support.huaweicloud.com/bestpractice-iotedge/iotedge_bestpractice_0050.html
-
- Use the required certificate. For details, see Best Practices. https://support.huaweicloud.com/bestpractice-iotedge/iotedge_bestpractice_0052.html
Download the plt-device-ca certificate file, copy the certificate content, and use it to replace the content in the conf/rootcert.perm file in the sdk directory.
This certificate is used by the device to verify the identity of the edge node.
-
- Replace port number. In the directory of include/base/mqtt_base.h, replace
#define MQTT_PORT "1883" #define MQTTS_PORT "8883"
with
#define MQTT_PORT "7882" #define MQTTS_PORT "7883"
-
- Test the demo. Replace the following information in src/device_demo/device_demo.c.
char *g_serverIp = "xx.xx.xx.xx"; // IP address of the edge node. int g_port= 7883; // MQTTS port number. Currently, the IoT Edge uses this port number by default. char *g_username = "tunnelDeviceA"; // The value is set in step 1. char *g_password = "xxxx"; // The value is set in step 1.
Assume that the IDs of source device A and target device B are tunnelDeviceA and tunnelDeviceB respectively. Device A sends a hello world message to device B. The following code is invoked on device A (the demo can be invoked in the main function):
void Test_M2MSendMsg() {
char *to = "deviceB";
char *from = username;
char *content = "hello deviceB";
char *requestId = "demoIdToDeviceB";
int messageId = IOTA_M2MSendMsg(to, from, content, requestId, NULL);
....
At the receiving end (device B), the following received message is printed with the callback function HandleM2mMessageDown:
void HandleM2mMessageDown(EN_IOTA_M2M_MESSAGE *rsp) { ...
PrintfLog(EN_LOG_LEVEL_INFO, "device_demo: HandleM2mMessageDown(), requestId: %s\n", rsp->request_id);
PrintfLog(EN_LOG_LEVEL_INFO, "device_demo: HandleM2mMessageDown(), to: %s\n", rsp->to);
PrintfLog(EN_LOG_LEVEL_INFO, "device_demo: HandleM2mMessageDown(), from: %s\n", rsp->from);
PrintfLog(EN_LOG_LEVEL_INFO, "device_demo: HandleM2mMessageDown(), content: %s\n", rsp->content);
// You can add the service processing logic here, for example, the subsequent processing after the content is received.
// do sth
}
The following information can be printed in terminal logs: Device A:
DEBUG device_demo: this is m2m demo
DEBUG iota_datatrans: IOTA_M2MSendMsg() with payload ==> {
"request_id": "demoIdToDeviceB",
"to": "deviceB",
"from": "deviceA",
"content": "hello deviceB"
}
DEBUG device_demo: Test_M2MSendMsg() ok, messageId 0
Device B:
INFO device_demo: HandleM2mMessageDown(), requestId: demoIdToDeviceB
INFO device_demo: HandleM2mMessageDown(), to: deviceB
INFO device_demo: HandleM2mMessageDown(), from: deviceA
INFO device_demo: HandleM2mMessageDown(), content: hello deviceB
- Device-cloud Secure Communication (with the OpenHarmony Soft Bus)
This section describes how to use some APIs. For more information about APIs, see the API documentation.
- Setting the log callback function
The SDK provides log callback functions. You can call the IOTA_SetPrintLogCallback function as required. For details, see the API documentation. Refer to IOTA_SetPrintLogCallback called by the main() function in the device_demo (device_demo.c in the src/device_demo folder, hereinafter referred to as demo).
void IOTA_SetPrintLogCallback(PFN_LOG_CALLBACK_HANDLER pfnLogCallbackHandler)
-
To print logs on the console, refer to vprintf(format, args) set in the myPrintLog function in the demo.
-
To print logs to the system log file, refer to the vsyslog(level, format, args) set in the myPrintLog function in the demo. In addition, #include "syslog.h" and #define _SYS_LOG should be contained.
-
Initializing resources
Before initiating services, call the IOTA_Init() API to initialize AgentLite resources. For details, see the API documentation. Refer to IOTA_Init() called by the main() function in the demo.
IOTA_Init(HW_CHAR *workPath)
- Configuring device binding
Before connecting a device to the platform, set the IP address, port, device ID, and secret. Refer to setAuthConfig() called by the main() function in the demo.
void setAuthConfig(){
IOTA_ConfigSetStr(EN_IOTA_CFG_MQTT_ADDR, g_serverIp);
IOTA_ConfigSetUint(EN_IOTA_CFG_MQTT_PORT, g_port);
IOTA_ConfigSetStr(EN_IOTA_CFG_DEVICEID, g_username);
IOTA_ConfigSetStr(EN_IOTA_CFG_DEVICESECRET, g_password);
IOTA_ConfigSetUint(EN_IOTA_CFG_AUTH_MODE, EN_IOTA_CFG_AUTH_MODE_SECRET); // Secret mode
IOTA_ConfigSetUint(EN_IOTA_CFG_CHECK_STAMP_METHOD, EN_IOTA_CFG_CHECK_STAMP_OFF);
/**
* Configuration is required in certificate mode:
*
* IOTA_ConfigSetUint(EN_IOTA_CFG_AUTH_MODE, EN_IOTA_CFG_AUTH_MODE_CERT);
* IOTA_ConfigSetStr(EN_MQTT_CFG_PRIVATE_KEY_PASSWORD, "yourPassword");
* */
#ifdef _SYS_LOG
//IOTA_ConfigSetUint(EN_IOTA_CFG_LOG_LOCAL_NUMBER, LOG_LOCAL7);
IOTA_ConfigSetUint(EN_IOTA_CFG_LOG_LEVEL, LOG_INFO);
#endif
}
The IP (EN_IOTA_CFG_MQTT_ADDR) and port (EN_IOTA_CFG_MQTT_PORT) of the platform can be obtained from the application interconnection information on the SP portal.
The device ID (EN_IOTA_CFG_DEVICEID) and device secret (EN_IOTA_CFG_DEVICESECRET) are returned during device registration.
The timestamp verification mode (EN_IOTA_CFG_CHECK_STAMP_METHOD) can be set to EN_IOTA_CFG_CHECK_STAMP_OFF. The sha256 (EN_IOTA_CFG_CHECK_STAMP_SHA256) or sm3 (EN_IOTA_CFG_CHECK_STAMP_SM3) can be used for hashing algorithm verification.
When _SYS_LOG is defined (printing a log in the system file), the facility type (EN_IOTA_CFG_LOG_LOCAL_NUMBER) and display level (EN_IOTA_CFG_LOG_LEVEL) of the log can be customized as required.
- Configuring callback function
You can set callback functions for actions such as device authentication success/failure, device disconnection success/failure, device message subscription success/failure, device message publishment success/failure, and device message/command receiving. Refer to setMyCallbacks() called by main() function in the demo.
void setMyCallbacks(){
IOTA_SetProtocolCallback(EN_IOTA_CALLBACK_CONNECT_SUCCESS, HandleConnectSuccess);
IOTA_SetProtocolCallback(EN_IOTA_CALLBACK_CONNECT_FAILURE, HandleConnectFailure);
IOTA_SetProtocolCallback(EN_IOTA_CALLBACK_DISCONNECT_SUCCESS, HandleDisConnectSuccess);
IOTA_SetProtocolCallback(EN_IOTA_CALLBACK_DISCONNECT_FAILURE, HandleDisConnectFailure);
IOTA_SetProtocolCallback(EN_IOTA_CALLBACK_CONNECTION_LOST, HandleConnectionLost);
IOTA_SetProtocolCallback(EN_IOTA_CALLBACK_SUBSCRIBE_SUCCESS, HandleSubscribesuccess);
IOTA_SetProtocolCallback(EN_IOTA_CALLBACK_SUBSCRIBE_FAILURE, HandleSubscribeFailure);
IOTA_SetProtocolCallback(EN_IOTA_CALLBACK_PUBLISH_SUCCESS, HandlePublishSuccess);
IOTA_SetProtocolCallback(EN_IOTA_CALLBACK_PUBLISH_FAILURE, HandlePublishFailure);
IOTA_SetMessageCallback(HandleMessageDown);
IOTA_SetUserTopicMsgCallback(HandleUserTopicMessageDown);
IOTA_SetCmdCallback(HandleCommandRequest);
IOTA_SetPropSetCallback(HandlePropertiesSet);
IOTA_SetPropGetCallback(HandlePropertiesGet);
IOTA_SetEventCallback(HandleEventsDown);
IOTA_SetShadowGetCallback(HandleDeviceShadowRsp);
}
- The **HandleConnectSuccess** function is called when a device is authenticated.
- The **HandleConnectFailure** function is called when a device fails to be authenticated.
- The **HandleDisConnectSuccess** function is called when a device proactively disconnects from the platform.
- The **HandleDisConnectFailure** function is called when a device fails to proactively disconnect from the platform.
- The **HandleConnectionLost** function is called when the connection between a device and the platform is lost.
- The **HandleSubscribesuccess** function is called when a device subscribes to a message.
- The **HandleSubscribeFailure** function is called when a device fails to subscribe to a message.
- The **HandlePublishSuccess** function is called when device data is published.
- The **HandlePublishFailure** function is called when device data fails to be published.
- The **HandleMessageDown** function is called when a device receives the unparsed data message (default topic) from the platform.
- The **HandleUserTopicMessageDown** function is called when a device receives the unparsed data message (custom topic) from the platform.
- The **HandleCommandRequest** function is called when a device receives a command.
- The **HandlePropertiesSet** function is called when a device receives the request for setting device properties.
- The **HandlePropertiesGet** function is called when a device receives the request for querying device properties.
- The **HandleEventsDown** function is called when a device receives the notification of an event (a child device addition or deletion event or an OTA event).
- The **HandleDeviceShadowRsp** function is called when a device receives the device shadow data.
-
Device authentication
After callback functions are set, call the authentication function. For details, see the invoking of this API in the demo:
HW_INT IOTA_Connect()
After the API is called, the message login success is displayed. It is recommended that you report data several seconds after a device is authenticated, or process services via the invoked callback function. The gateway is online. You can press Ctrl+C to stop the program. After that, the device is offline.
-
Reporting device messages/properties
After a device is authenticated, a gateway can call SDK APIs to report device messages and properties. In addition, the gateway can report the command execution result, property setting result, and property query result. It is recommended that the interval for reporting data is greater than or equal to hundreds of milliseconds.
-
API for reporting a device message:
HW_API_FUNC HW_INT IOTA_MessageReport(HW_CHAR *object_device_id, HW_CHAR *name, HW_CHAR *id, HW_CHAR *content, HW_CHAR *topicParas, HW_INT compressFlag, void *context);
The data reported via this API is not parsed by the platform. It can be forwarded to other services or pushed to the application server. object_device_id indicates the device to be reported. name indicates the message name. id indicates the message ID. content indicates the content to be reported. topicParas indicates the parameters of the custom topic. If this parameter is set to NULL, the default topic of the platform is used to report data. Refer to the Test_MessageReport function in the demo. For details about the parameters, see the API documentation.
-
static void Test_MessageReport()
{
// default topic
int messageId = IOTA_MessageReport(NULL, "data123", "123", "hello123123123123", NULL, 0, NULL);
// user topic
// int messageId = IOTA_MessageReport(NULL, "data123", "123", "hello", "devMsg", 0, NULL);
if (messageId != 0) {
PrintfLog(EN_LOG_LEVEL_ERROR, "device_demo: Test_MessageReport() failed, messageId %d\n", messageId);
}
}
-
API for reporting device properties:
HW_INT IOTA_PropertiesReport(ST_IOTA_SERVICE_DATA_INFO pServiceData[], HW_INT serviceNum)
The data reported via this API is parsed by the platform and the data in the structure must be the same as the properties defined in the product model. ST_IOTA_SERVICE_DATA_INFO indicates a structure array. A gateway can report multiple services at the same time. serviceNum indicates the number of services to be reported. Refer to the Test_propertiesReport function in the demo. For details about the parameters, see the API documentation.
void Test_propertiesReport() {
int serviceNum = 2; // Number of services to be reported by a gateway
ST_IOTA_SERVICE_DATA_INFO services[serviceNum];
//---------------the data of service1-------------------------------
char *service1 = "{\"Load\":\"5\",\"ImbA_strVal\":\"6\"}";
// services[0].event_time = GetEventTimesStamp(); //you need to free the services[0].event_time
services[0].event_time = NULL;
services[0].service_id = "parameter";
services[0].properties = service1;
//---------------the data of service2-------------------------------
char *service2 = "{\"PhV_phsA\":\"4\",\"PhV_phsB\":9}";
// services[1].event_time = GetEventTimesStamp(); //you need to free the services[1].event_time
services[0].event_time = NULL;
services[1].service_id = "analog";
services[1].properties = service2;
int messageId = IOTA_PropertiesReport(services, serviceNum);
if(messageId != 0) {
PrintfLog(EN_LOG_LEVEL_ERROR, "device_demo: Test_batchPropertiesReport() failed, messageId %d\n", messageId);
}
}
-
Receiving device messages/commands/properties
After a device is authenticated and the callback function is set, it can receive commands from the platform. (The SDK automatically subscribes to related topics.) The following commands are available: message delivery, command delivery, device property setting, device property query, child device addition, and child device deletion. (For details, see the API documentation.) Note: IoTDA uses implicit subscription. Devices do not need to subscribe to the downstream system topic. Devices subscribe to the system topic whose QoS is 0 by default. Call the API to subscribe to another topic whose QoS is 1.
-
A device receives a data message. After receiving a message, the device processes it via a callback function. For details, see the HandleMessageDown function in the demo. (Set the callback function in advance.)
-
A device receives the command as defined in the product model.
HW_API_FUNC HW_VOID IOTA_SetCmdCallback(PFN_CMD_CALLBACK_HANDLER callbackHandler)
This API is used to set the command callback function. When a command is delivered from the cloud or a device-side rule is triggered,callbackHandler
is called.void HandleCommandRequest(EN_IOTA_COMMAND *command) { if (command == NULL) { return; } PrintfLog(EN_LOG_LEVEL_INFO, "device_demo: HandleCommandRequest(), messageId %d\n", command->mqtt_msg_info->messageId); PrintfLog(EN_LOG_LEVEL_INFO, "device_demo: HandleCommandRequest(), object_device_id %s\n", command->object_device_id); PrintfLog(EN_LOG_LEVEL_INFO, "device_demo: HandleCommandRequest(), service_id %s\n", command->service_id); PrintfLog(EN_LOG_LEVEL_INFO, "device_demo: HandleCommandRequest(), command_name %s\n", command->command_name); PrintfLog(EN_LOG_LEVEL_INFO, "device_demo: HandleCommandRequest(), paras %s\n", command->paras); PrintfLog(EN_LOG_LEVEL_INFO, "device_demo: HandleCommandRequest(), request_id %s\n", command->request_id); // Specific command can be implemented here. Test_CommandResponse(command->request_id); // response command }
Registers a callback.
IOTA_SetCmdCallback(HandleCommandRequest);
-
A device receives the request for setting device properties. You can use the callback function to process the command. After receiving the request for setting device properties, call the IOTA_PropertiesSetResponse API to report the response. For details, see the HandlePropertiesSet function in the demo. (Set the callback function in advance.)
-
A device receives the request for querying device properties. You can use the callback function to process the command. After receiving the request for querying device properties, call the IOTA_PropertiesGetResponse API to report the response. For details, see the HandlePropertiesGet function in the demo. (Set the callback function in advance.)
-
The platform notifies a gateway of child device addition. You can use the callback function to process the command. After receiving the notification, call the IOTA_BatchPropertiesReport API to report child device data (view the reported data on the console). For details, see the HandleEventsDown function in the demo. (Set the callback function in advance.)
-
The platform notifies a gateway of child device deletion.
-
-
Reporting device data (child device)
A child device can report data in batch via the following API:
HW_INT IOTA_BatchPropertiesReport(ST_IOTA_DEVICE_DATA_INFO pDeviceData[], HW_INT deviceNum, HW_INT serviceLenList[])
The data reported via this API is parsed by the platform. The data in the structure must be the same as that defined in the product model. ST_IOTA_DEVICE_DATA_INFO indicates a structure array. Multiple child devices can report data at the same time and each child device can report multiple services. deviceNum indicates the number of child devices to report data. serviceLenList indicates the number of services reported by a child device. Refer to the Test_batchPropertiesReport function in the demo. For details about parameters, see the API documentation.
void Test_BatchPropertiesReport() {
int deviceNum = 1; // Number of child devices to report data.
ST_IOTA_DEVICE_DATA_INFO devices[deviceNum]; // Array hosting the structure of data to be reported by child devices.
int serviceList[deviceNum]; // Number of services to be reported by each child device.
serviceList[0] = 2; // **Device 1** needs to report two services.
// serviceList[1] = 1; // **Device 2** needs to report one service.
char *device1_service1 = "{\"Load\":\"1\",\"ImbA_strVal\":\"3\"}"; // Properties to be reported by **service 1** (in JSON format).
char *device1_service2 = "{\"PhV_phsA\":\"2\",\"PhV_phsB\":\"4\"}";// Properties to be reported by **service 2** (in JSON format).
devices[0].device_id = g_subDeviceId;
devices[0].services[0].event_time = "20191209T081212Z";
devices[0].services[0].service_id = "parameter";
devices[0].services[0].properties = device1_service1;
devices[0].services[1].event_time = "20191209T081212Z";
devices[0].services[1].service_id = "analog";
devices[0].services[1].properties = device1_service2;
// char *device2_service1 = "{\"AA\":\"2\",\"BB\":\"4\"}";
// devices[1].device_id = "subDevices22222";
// devices[1].services[0].event_time = "d2s1";
// devices[1].services[0].service_id = "device2_service11111111";
// devices[1].services[0].properties = device2_service1;
int messageId = IOTA_BatchPropertiesReport(devices, deviceNum, serviceList);
if(messageId != 0) {
printfLog(EN_LOG_LEVEL_ERROR, "device_demo: Test_BatchPropertiesReport() failed, messageId %d\n", messageId);
}
}
-
Accessing using a certificate
The platform supports device access authentication using X.509 certificates. The access procedures are as follows:
- For details about how to create and upload a certificate, visit https://support.huaweicloud.com/usermanual-iothub/iot_01_0055.html.
- Set the following parameters in the SDK:
- Change the certificate and secret file names to deviceCert.pem and deviceCert.key respectively, and place the two files in the conf directory of the SDK.
- Refer to the SetAuthConfig function in the demo to set the mode for the device to access using a certificate.
void SetAuthConfig() {
IOTA_ConfigSetStr(EN_IOTA_CFG_MQTT_ADDR, g_serverIp);
IOTA_ConfigSetUint(EN_IOTA_CFG_MQTT_PORT, g_port);
IOTA_ConfigSetStr(EN_IOTA_CFG_DEVICEID, g_username);
// IOTA_ConfigSetStr(EN_IOTA_CFG_DEVICESECRET, g_password);
// IOTA_ConfigSetUint(EN_IOTA_CFG_AUTH_MODE, EN_IOTA_CFG_AUTH_MODE_SECRET);
IOTA_ConfigSetUint(EN_IOTA_CFG_AUTH_MODE, EN_IOTA_CFG_AUTH_MODE_CERT);
IOTA_ConfigSetStr(EN_MQTT_CFG_PRIVATE_KEY_PASSWORD, "yourPassword");
}
-
TLS accessing using Chinese cryptographic algorithm
Currently, the SDK supports TLS access and data transmission using Chinese cryptographic algorithm. To use the Chinese cryptographic algorithm for communication, you need to download the corresponding version of OpenSSL. For details, see 3.2 Compiling the OpenSSL Library.
To use TLS, you need to install a patch for the paho.mqtt.c-1.3.9/src/SSLSocket.c file in the paho_mqtt open-source library. The patch installation method is as follows:
-
Run the following command to decompress ./generatingLib/gmtls.patch.zip and obtain the patch file gmtls.patch:
unzip ./generatingLib/gmtls.patch.zip
-
Run the following command to patch paho.mqtt.c-1.3.9/src/SSLSocket.c:
patch -b ./generatingLib/paho.mqtt.c-1.3.9/src/SSLSocket.c ./generatingLib/gmtls.patch
// Use the absolute path to read the certificate. const char *signCert = "/volume/tassl/iot-device-sdk-c/conf/gmcert_s/CS.cert.pem"; const char *signKey = "/volume/tassl/iot-device-sdk-c/conf/gmcert_s/CS.key.pem"; const char *encCert = "/volume/tassl/iot-device-sdk-c/conf/gmcert_e/CE.cert.pem"; const char *encKey = "/volume/tassl/iot-device-sdk-c/conf/gmcert_e/CE.key.pem"; const char *caPath = "/volume/tassl/iot-device-sdk-c/conf/GMCert_GMCA01.cert.pem"; const char *passWord = NULL;
After the patch is installed, you can check whether the preceding certificate path fields exist in paho.mqtt.c-1.3.9/src/SSLSocket.c. With password login mode, you do not need to enter the signature certificate, private key, encryption certificate, and private key path. But with certificate login mode, you need to enter the absolute path to access the file. After the modification, recompile the Paho library.
-
-
Customizing topic
For details, see the API documentation in the home directory.
-
Conducting OTA upgrade
For details, see the API documentation in the home directory.
-
Querying device shadow
For details, see the API documentation in the home directory.
-
Accessing with generic protocol
-
Bootstrap accessing You can provision devices to different regions with IoTDA. For details, go to https://support.huaweicloud.com/qs-iotps/iot_03_0006.html for more access examples. For details, see the user guide. You need to enable device_demo.o and bootstrap_demo.o in OBJS in Makefile in the home directory of the SDK.
Currently, IoTDA uses certificates issued by two authoritative CAs: DigiCert Global Root CA. and GlobalSign Root CA - R3. By default, the certificate in the conf directory is bound to the domain name of IoTDA basic edition. For details about how to switch to another IoTDA version, see Obtaining Resources.
- Compile and run the program.
-
Copy the huaweicloud-iot-device-sdk-c-master.zip package to the Linux environment and run the following command to decompress the package:
unzip huaweicloud-iot-device-sdk-c-master.zip
-
Go to the following folder:
cd huaweicloud-iot-device-sdk-c-master
-
Run the following command to perform compilation:
make
-
Run the following SDK demo:
./MQTT_Demo
-
Generating SDK library files
To generate a .so file, modify the Makefile. You can open the file using Notepad or run the vim Makefile to modify the file in the Linux environment. Press i to edit the file, and then run wq! to save the modification.
-
Storing data in abnormal scenarios
IoTDA provides code to store, report, and resend property messages in abnormal scenarios. You can use the Test_PropertiesStoreData() function. STORE_DATA_SWITCH specifies whether to enable this function. By default, it is disabled with the #define added. You can delete the #define to enable the function. The new code is sample code. You can use the default dynamic two-dimensional arrays or other data structures as needed. It is recommended that the device stores the collected data and resends the data when the device link is normal. The running logic of this API is as follows:
-
Storing the sensor data before reporting data. (Array is used by default, but you can use other data structures.)
-
If a publish success response is received, the message is deleted from the container. Or the message will be sent again later.
-
Using MQTT5.0
If you want to use MQTT5.0 (not the default MQTT3.1.1), uncomment #define MQTTV5 in the ./include/util/mqttv5_util.h file.
-
Using MQTT_DEBUG
To use the MQTT_DEBUG function, change the value of MQTT_TRACE_ON to 1 in the ./include/util/mqtt_base.h file. MQTT_TRACE_LEVEL indicates the log level. The default value is MQTTASYNC_TRACE_MAXIMUM, which is the highest level.
Log levels are described as follows:
Log levels significance MQTTASYNC_TRACE_MAXIMUM only print log contains fatal error MQTTASYNC_TRACE_MEDIUM only print log has severe error MQTTASYNC_TRACE_MINIMUM lesser log, only print when error happened MQTTASYNC_TRACE_PROTOCOL only print log about mqtt protocol MQTTASYNC_TRACE_ERROR print all MQTTASYNC_TRACE_SEVERE print all MQTTASYNC_TRACE_FATAL print all, include memory allocation and free When LOG_FILE_ENABLE is set to 1, logs are exported to the file path specified by LOG_FILE_NAME. When LOG_FILE_ENABLE is set to 0, logs are directly output.
-
Performing remote configuration
Configuration parameters can be remotely delivered. The SDK receives related information through event/down. You can register a customized callback hook function through IOTA_SetDeviceConfigCallback. The function processes related data after receiving configuration information.
-
Using soft bus
You can group and connect devices with the soft bus function of IoTDA. IoTDA can manage security groups and deliver credit identifiers for communication between group members.
Log in to the IoTDA console, choose Devices > Groups in the navigation pane, create a child group, create a Harmony soft bus, and customize its name.
To bind a device, click Bind, select the device to be bound to the group, and synchronize the soft bus information.
You can call IOTA_GetLatestSoftBusInfo to obtain the soft bus information of the device. You can obtain the version of the updated soft bus from the callback function HandlePropertiesSet delivered by the device shadow.
-
Automatic reconnection
src/device_demo/device_demo.c
provides two reconnection modes that cannot be used at the same time. You can use the macroCUSTOM_RECONNECT_SWITCH
to choose one mode.-
The
CUSTOM_RECONNECT_SWITCH
macro is not defined. In this case, reconnection is performed with the callback functions ofHandleConnectFailure
andHandleConnectionLost
. The MQTT disconnection will be detected in a timely manner. This method is recommended. -
The macro
CUSTOM_RECONNECT_SWITCH
is defined. In this case, a thread is used to simulate a timer. TheIOTA_IsConnected()
API is invoked every 30 seconds to check the connection status. If a disconnection is detected, reconnection is performed.
The system uses the first mode by default, where the macro
CUSTOM_RECONNECT_SWITCH
is not defined.void HandleConnectFailure(EN_IOTA_MQTT_PROTOCOL_RSP *rsp) { PrintfLog(EN_LOG_LEVEL_ERROR, "device_demo: HandleConnectFailure() error, messageId %d, code %d, messsage %s\n", rsp->mqtt_msg_info->messageId, rsp->mqtt_msg_info->code, rsp->message); PrintfLog(EN_LOG_LEVEL_ERROR, "device_demo: HandleConnectFailure() login again\n"); #ifndef CUSTOM_RECONNECT_SWITCH // Backoff reconnection connect_failed_times++; if (connect_failed_times < 10) { TimeSleep(50); } else if (connect_failed_times < 50) { TimeSleep(2500); } int ret = IOTA_Connect(); if (ret != 0) { PrintfLog(EN_LOG_LEVEL_ERROR, "device_demo: HandleAuthFailure() error, login again failed, result %d\n", ret); } #endif }
To use the second mode, find the following code and uncomment the macro
CUSTOM_RECONNECT_SWITCH
.// #define CUSTOM_RECONNECT_SWITCH
You can use the
IOTA_IsConnected()
API to read the connection status for reconnection.static void ReconnectRoutine(void *args) { (void)args; while (1) { PrintfLog(EN_LOG_LEVEL_DEBUG, "device_demo: need to reconnect: %s \n", IOTA_IsConnected() ? "no" : "yes"); if (!IOTA_IsConnected()) {// Check the connection status. int ret = IOTA_Connect(); if (ret != 0) { PrintfLog(EN_LOG_LEVEL_ERROR, "device_demo: IOTA_Connect() error, result %d\n", ret); } } TimeSleep(30 * 1000); // 30s } } static void ReconnectDemo() { if (pthread_create(&reconnectThread, NULL, ReconnectRoutine, NULL) != NULL) { PrintfLog(EN_LOG_LEVEL_ERROR, "device_demo: create ReconnectionThread failed! ===================>\n"); } }
-
- In compliance with the BSD-3 open source license agreement