Skip to main content

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
  • EdgeHub Edge C SDK

Build

Open a terminal/console/command prompt, navigate to the directory where Processing was cloned, and type the following:

  1. 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
  2. build the project

    Build both the shared library and the sample executable:

    cmake --build build
  3. 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 StructureTag Type StructurePropertyData TypeDescription
TEDGE_DATA_STRUCTAnalogTagListValuedoubleanalog tag value
TEDGE_DATA_STRUCTDiscreteTagListValueunsigned integerdiscrete tag value
TEDGE_DATA_STRUCTTextTagListValuepointer to charactertext 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 = &quot;Test&quot;;
}
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 NameData TypeDescription
IsConnectedbooleanConnection status (read only)

Sample Code

Release Note

  • [1.0.2] - 2024-10-21
    • Add
      • Support Azure IoT Hub device connection which is created from EdgeHub
  • [1.0.1] - 2024-09-09
    • Add
      • Support BlockType that is defined in WISE-PaaS MQTT v1.0.16
  • [1.0.0] - 2024-08-08
    • Add
      • EdgeSync360 EdgeHub EdgeSDK Project initialized, derived from WISE-PaaS DataHub EdgeSDK

Release Note (Archived: WISE-PaaS DataHub EdgeSDK)

  • [1.0.5] - 2021-10-15
    • Updated
      • Remove unused parameters of config struct.
    • Added
      • Add ConfigAck event
  • [1.0.4] - 2021-09-10
    • Updated
      • Remove the strategy of splitting a huge data message into several smaller packets.
  • [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
  • [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
  • [1.0.1] - 2020-02-17
    • Updated
      • Project renamed to DataHub
  • [1.0.0] - 2019-10-18