C Edge SDK Manual
October 21, 2024
Environment
-
C version
- Standard C99
-
GNU C Compiler
- GNU/Linux, MinGW(Windows), Unix
- calling /usr/bin/gcc or /usr/bin/c99
-
CMake >= 3.10
-
vcpkg
- Required Package
- mosquitto
- curl
- sqlite3
- openssl
- Install Scripts
vcpkg install mosquitto sqlite3 curl openssl
- Required Package
-
EdgeHub Edge C SDK
Build
Open a terminal/console/command prompt, navigate to the directory where Processing was cloned, and type the following:
-
Configure CMake
Run CMake to configure the build. Make sure that vcpkg is used as the toolchain file for external dependencies:
cmake -B build -DCMAKE_TOOLCHAIN_FILE=~/path-to-vcpkg/scripts/buildsystems/vcpkg.cmake -
build the project
Build both the shared library and the sample executable:
cmake --build build -
Running the sample
Running the sample ExecutableOnce the build process completes successfully, you can run the sample executable:
./build/sample
EdgeAgent
Load Library
Load a dynamic library and include “DatahubEdge.h”. Follow the definition below when using an API.
- EDGE_AGENT_OPTION.h: Define the construct function structure
- EDGE_CONFIG.h: Define UploadConfig structure
- EDGE_DATA.h: Define SendData structure
- EDGE_DEVICE_STATUS.h: Define SendDeviceStatus structure
/* load library */
#include "DatahubEdge.h"
char *error;
void *handle;
handle = dlopen ("./DatahubEdge.so.1.0.4", RTLD_LAZY);
if (!handle) {
fputs (dlerror(), stderr);
exit(1);
}
Load all of the EdgeAgent function declarations
void (*SetConnectEvent)();
void (*SetDisconnectEvent)();
void (*SetMessageReceived)();
void (*Constructor)(TOPTION_STRUCT);
void (*Connect)();
void (*Disconnect)();
int (*UploadConfig)(ActionType, TSCADA_CONFIG_STRUCT);
int (*SendData)(TEDGE_DATA_STRUCT);
int (*SendDeviceStatus)(TEDGE_DEVICE_STATUS_STRUCT);
SetConnectEvent = dlsym(handle, "SetConnectEvent");
SetDisconnectEvent = dlsym(handle, "SetDisconnectEvent");
SetMessageReceived = dlsym(handle, "SetMessageReceived");
Constructor = dlsym(handle, "Constructor");
Connect = dlsym(handle, "Connect");
Disconnect = dlsym(handle, "Disconnect");
UploadConfig = dlsym(handle, "UploadConfig");
SendData = dlsym(handle, "SendData");
SendDeviceStatus = dlsym(handle, "SendDeviceStatus");
if ((error = dlerror()) != NULL) {
fputs(error, stderr);
exit(1);
}
Constructor (TOPTION_STRUCT option)
Construct EdgeAgent object by using a symbolic structure which refer to TOPTION_STRUCT
/* Set Construct */
TOPTION_STRUCT options;
inc/EDGE_AGENT_OPTION.h
typedef struct OPTION_STRUCT {
bool AutoReconnect;
int ReconnectInterval;
char * NodeId;
char * DeviceId;
EdgeType Type;
int Heartbeat;
bool DataRecover;
ConnectType ConnectType;
bool UseSecure;
char * OvpnPath;
TMQTT_OPTION_STRUCT MQTT;
TDCCS_OPTION_STRUCT DCCS;
} TOPTION_STRUCT, * PTOPTION_STRUCT;
Declare Connection Variable
options.AutoReconnect = true;
options.ReconnectInterval = 1000;
options.NodeId = "YOUR_NODE_ID"; // getting from portal
options.Heartbeat = 60; // default is 60 seconds
options.DataRecover = true; // need to recover data or not when disconnected
options.ConnectType = MQTT; // set your connect type: DCCS or MQTT
options.Type = Gatway; // Choice your edge is Gateway or Device, Default is Gateway
options.UseSecure = false;
options.OvpnPath = "Your_OVPN_FILE_PATH//YOUR_OVPN_FILE_NAME";
TMQTT_OPTION_STRUCT mqtt;
switch (options.ConnectType){
case DCCS: // If ConnectType is DCCS, must fill this options
options.DCCS.CredentialKey = ""; // Credential Key
options.DCCS.APIUrl = ""; // DCCS API Url
break;
case MQTT: // If ConnectType is MQTT, must fill this options
options.MQTT.HostName = "127.0.0.1";
options.MQTT.Port = 1883;
options.MQTT.Username = "admin";
options.MQTT.Password = "admin";
options.MQTT.ProtocolType = TCP;
break;
case AzureIotHub:
options.AzureIotHub.ConnectionString = "";
break;
}
Constructor(options);
Azure IoT Hub
Due to the connection limit of the Azure IoT Hub SDK, the C# SDK can only connect to the Azure IoT Hub using a connection string. Therefore, you can only connect to the Edge Hub using a EdgeLink device.
Using OpenVPN Client to connect ()
To connect a VPN connection, start OpenVPN Connect, select an imported .ovpn file You can use the property called “options.OvpnPath” to determine whether the OpenVPN Connect or not.
TOPTION_STRUCT options; // your edge object
options.OvpnPath = "Your_OVPN_FILE_PATH//YOUR_OVPN_FILE.ovpn"; // use openvpn connect
Event
EdgeAgent has three subscription events.
- Connected: When EdgeAgent is connected to the IoTHub.
- Disconnected: When EdgeAgent is disconnected from the IoTHub.
- MessageReceived: When EdgeAgent receives an MQTT message from the cloud. The message types are as follows:
- WriteValue: Change the tag value from the cloud.
- ConfigAck: Response to a config upload from the edge to the cloud.
void edgeAgent_Connected() {
printf("Connect success\n");
IsConnected = true;
}
void edgeAgent_Disconnected() {
printf("Disconnected\n");
IsConnected = false;
}
void edgeAgent_Receive(char *cmd, char *val) {
if(strcmp(cmd, WriteValueCommand) == 0) {
printf("write value: %s\n", val);
}
else if(strcmp(cmd, AckConfig) == 0) {
printf("config ack result: %s\n", val);
}
}
/* Set Event */
SetConnectEvent(edgeAgent_Connected);
SetDisconnectEvent(edgeAgent_Disconnected);
SetMessageReceived(edgeAgent_Receive);
Connect ()
Connect to the IoTHub. When successfully connected, a connected event will be triggered.
Connect();
Disconnect ()
Disconnect from the IoTHub. When successfully disconnected, a disconnected event will be triggered.
Disconnect();
UploadConfig (ActionType action, TNODE_CONFIG_STRUCT edgeConfig)
Upload Node/Device/Tag Config with Action Type (Create/Update/Delete).
TNODE_CONFIG_STRUCT config;
ActionType action = Create; // Create, Update or Delete
/* Set Edge Config here */
// ---- Ref Set node config
// |---- Ref Set device config
// |----Ref Set analog tag config
// |----Ref Set discrete tag config
// |----Ref Set text tag config
bool result = UploadConfig(action, config);
Node Config:
TNODE_CONFIG_STRUCT config;
Device Config:
PTDEVICE_CONFIG_STRUCT device = malloc(sizeof(struct DEVICE_CONFIG_STRUCT));
device.Name = "Device1";
device.Type = "Sensor";
device.Description = "Voltage Sensor";
config.DeviceNumber = 1;
config.DeviceList = device;
Analog Tag Config:
PTANALOG_TAG_CONFIG analogTag = malloc(sizeof(struct ANALOG_TAG_CONFIG));
analogTag.Name = "ATag1";
analogTag.Description = "Analog Tag 1";
analogTag.ReadOnly = false;
analogTag.ArraySize = 0;
analogTag.SpanHigh = 1000;
analogTag.SpanLow = 0;
analogTag.EngineerUnit = "V";
analogTag.FractionDisplayFormat = 2;
Discrete Tag Config:
PTDISCRETE_TAG_CONFIG discreteTag = malloc(sizeof(struct DISCRETE_TAG_CONFIG));
discreteTag.Name = "DTag1"
discreteTag.Description = "Discrete Tag 1";
discreteTag.ReadOnly = false;
discreteTag.ArraySize = 0;
discreteTag.State0 = "0";
discreteTag.State1 = "1";
//discreteTag.State2 = "";
//discreteTag.State3 = "";
//discreteTag.State4 = "";
//discreteTag.State5 = "";
//discreteTag.State6 = "";
//discreteTag.State7 = "";
Text Tag Config:
PTTEXT_TAG_CONFIG textTag = malloc(sizeof(struct TEXT_TAG_CONFIG));
textTag.Name = "TTag1";
textTag.Description = "Text Tag 1";
textTag.ReadOnly = false;
textTag.ArraySize = 0;
Block Config
Create block config:
PTBLOCK_CONFIG blockConfig = malloc(sizeof(struct BLOCK_CONFIG));
blockConfig->AnalogNumber = 1;
blockConfig->AnalogTagList = malloc(blockConfig->AnalogNumber * sizeof(struct ANALOG_TAG_CONFIG));
blockConfig->DiscreteNumber = 1;
blockConfig->DiscreteTagList = malloc(blockConfig->DiscreteNumber * sizeof(struct DISCRETE_TAG_CONFIG));
blockConfig->TextNumber = 1;
blockConfig->TextTagList = malloc(blockConfig->TextNumber * sizeof(struct TEXT_TAG_CONFIG));
blockConfig->BlockType = "Pump";
for (int i = 0; i AnalogNumber; i++) {
blockConfig->AnalogTagList[i].Name = "ATag";
blockConfig->AnalogTagList[i].ArraySize = blockConfig->AnalogNumber; /* used for array tag */
}
for (int i = 0; i DiscreteNumber; i++) {
blockConfig->DiscreteTagList[i].Name = "DTag";
blockConfig->DiscreteTagList[i].ArraySize = blockConfig->DiscreteNumber; /* used for array tag */
}
for (int i = 0; i TextNumber; i++) {
blockConfig->TextTagList[i].Name = "TTag";
blockConfig->TextTagList[i].ArraySize = blockConfig->TextNumber; /* used for array tag */
}
This block config contain 3 tag, and the block type is Pump, using this block config create two block:
AddBlock(&deviceList[0], "Pump01", blockConfig);
AddBlock(&deviceList[0], "Pump02", blockConfig);
This will add 6 tag to your device, the first 3 tag will have a prefix of Pump01 and the last 3 tag will have a prefix of Pump02, for example:
SendData (EdgeData data)
Send the tag value to the cloud.
Send Tag Data
According to the tag type, the properties can be used as follows:
| EdgeData Structure | Tag Type Structure | Property | Data Type | Description |
|---|---|---|---|---|
| TEDGE_DATA_STRUCT | AnalogTagList | Value | double | analog tag value |
| TEDGE_DATA_STRUCT | DiscreteTagList | Value | unsigned integer | discrete tag value |
| TEDGE_DATA_STRUCT | TextTagList | Value | pointer to character | text tag value |
Use TEDGE_DATA_STRUCT structure to construct a data object.
- Send Analog Tag Data
TEDGE_DATA_STRUCT data;
PTEDGE_DEVICE_STRUCT data_device = malloc(device_num * sizeof(struct EDGE_DEVICE_STRUCT));
int analog_tag_num = 1;
PTEDGE_ANALOG_TAG_STRUCT analog_data_tag = malloc(analog_tag_num * sizeof(struct EDGE_ANALOG_TAG_STRUCT));
analog_data_tag.Name = "ATag1";
analog_data_tag.Value = 3.14
data_device.AnalogTagNumber = analog_tag_num;
data_device.AnalogTagList = analog_data_tag;
data_device.Id = "Device1";
data.DeviceNumber = 1;
data.DeviceList = data_device;
bool result = SendData(data);
- Send Discrete Tag Data
TEDGE_DATA_STRUCT data;
PTEDGE_DEVICE_STRUCT data_device = malloc(device_num * sizeof(struct EDGE_DEVICE_STRUCT));
int discrete_tag_num = 1;
PTEDGE_DISCRETE_TAG_STRUCT discrete_data_tag = malloc(discrete_tag_num * sizeof(struct EDGE_DISCRETE_TAG_STRUCT));
discrete_data_tag.Name = "DTage1";
discrete_data_tag.Value = 1;
data_device.DiscreteTagNumber = discrete_tag_num;
data_device.DiscreteTagList = discrete_data_tag;
data_device.Id = "Device1";
data.DeviceNumber = 1;
data.DeviceList = data_device;
bool result = SendData(data);
- Send Text Tag Data
TEDGE_DATA_STRUCT data;
PTEDGE_DEVICE_STRUCT data_device = malloc(device_num * sizeof(struct EDGE_DEVICE_STRUCT));
int discrete_tag_num = 1;
PTEDGE_TEXT_TAG_STRUCT text_data_tag = malloc(text_tag_num * sizeof(struct EDGE_TEXT_TAG_STRUCT));
text_data_tag.Name = "TTag1";
text_data_tag.Value = "Normal";
data_device.TextTagNumber = text_tag_num;
data_device.TextTagList = text_data_tag;
data_device.Id = "Device1";
data.DeviceNumber = 1;
data.DeviceList = data_device;
bool result = SendData(data);
Send Array Tag Data
Similar to allocating tag value for normal tags, an array tag value requires the creation of a new array data instance (PTEDGE_ARRAY_TAG_STRUCT) and referencing in another tag instance (PTEDGE_TAG_STRUCT)
- Send Analog Array Tag Data
int YOUR_ARRAY_SIZE = 10;
PTEDG_ANALOG_ARRAY_TAG_STRUCT analog_data_array_tag = malloc(YOUR_ARRAY_SIZE * sizeof(struct EDGE_ANALOG_ARRAY_TAG_STRUCT));
for(int idx = 0; idx < YOUR_ARRAY_SIZE; idx++){
analog_data_array_tag[idx].Index = idx;
analog_data_array_tag[idx].Value = 53.87;
}
analog_data_tag[YOUR_TAG_NUMBER].ArraySize = array_size;
analog_data_tag[YOUR_TAG_NUMBER].ArrayList = analog_data_array_tag;
- Send Discrete Array Tag Data
int YOUR_ARRAY_SIZE = 10;
PTEDG_DISCRETE_ARRAY_TAG_STRUCT discrete_data_array_tag = malloc(YOUR_ARRAY_SIZE * sizeof(struct EDGE_DISCRETE_ARRAY_TAG_STRUCT));
for(int idx = 0; idx < YOUR_ARRAY_SIZE; idx++){
discrete_data_array_tag[idx].Index = idx;
discrete_data_array_tag[idx].Value = 1;
}
discrete_data_tag[YOUR_TAG_NUMBER].ArraySize = array_size;
discrete_data_tag[YOUR_TAG_NUMBER].ArrayList = discrete_data_array_tag;
- Send Text Array Tag Data
int YOUR_ARRAY_SIZE = 10;
PTEDG_TEXT_ARRAY_TAG_STRUCT text_data_array_tag = malloc(YOUR_ARRAY_SIZE * sizeof(struct EDGE_TEXT_ARRAY_TAG_STRUCT));
for(int idx = 0; idx < YOUR_ARRAY_SIZE; idx++){
text_data_array_tag[idx].Index = idx;
text_data_array_tag[idx].Value = "Test";
}
text_data_array_tag[YOUR_TAG_NUMBER].ArraySize = array_size;
text_data_array_tag[YOUR_TAG_NUMBER].ArrayList = text_data_array_tag;
SendDeviceStatus (TEDGE_DEVICE_STATUS_STRUCT deviceStatus)
Send Device status to cloud when status changed.
TEDGE_DEVICE_STATUS_STRUCT status;
status.DeviceNumber = device_num;
PTDEVICE_LIST_STRUCT dev_list = malloc(sizeof(struct DEVICE_LIST_STRUCT));
dev_list.Id = "Device1";
dev_list.Status = 1;
status.DeviceList = dev_list;
bool result = SendDeviceStatus(status);
Property
| Property Name | Data Type | Description |
|---|---|---|
| IsConnected | boolean | Connection status (read only) |
Sample Code
- GitHub
Release Note
- [1.0.2] - 2024-10-21
- Add
- Support Azure IoT Hub device connection which is created from EdgeHub
- Add
- [1.0.1] - 2024-09-09
- Add
- Support
BlockTypethat is defined in WISE-PaaS MQTT v1.0.16
- Support
- Add
- [1.0.0] - 2024-08-08
- Add
EdgeSync360 EdgeHub EdgeSDKProject initialized, derived from WISE-PaaS DataHub EdgeSDK
- Add
Release Note (Archived: WISE-PaaS DataHub EdgeSDK)
- [1.0.5] - 2021-10-15
- Updated
- Remove unused parameters of config struct.
- Added
- Add ConfigAck event
- Updated
- [1.0.4] - 2021-09-10
- Updated
- Remove the strategy of splitting a huge data message into several smaller packets.
- Updated
- [1.0.3] - 2020-10-14
- Added
- Set the tls version into mosquitto library
- Add a new script in makefile to generate certificate authority (CA) for self-signing
- Added
- [1.0.2] - 2020-03-30
- Added
- Support OpenVPN
- Add a “RetentionPolicyName” property to the DeviceConfig class
- Updated
- Heartbeat frequency can be 0 for low-power devices
- Adjust the structure of the SendData struct
- Added
- [1.0.1] - 2020-02-17
- Updated
- Project renamed to DataHub
- Updated
- [1.0.0] - 2019-10-18