Device Management Agent : C based implementation

The DM client written on C is based on libcoap library.

Website : https://libcoap.net

GitHub : https://github.com/obgm/libcoap

For libcoap API used in DM : Please see the document section in libcoap library for better understanding of the libcoap API.

DM Architecture :

This is the architectural view of C based DM agent.

../../_images/dm_cagent_arch.png

Structure of C-DM Agent Project :

├── c_dm_linux_secure_stable_v1.0

└── libcoap_dtls_lib

c_dm_linux_secure_stable_v1.0 is the folder which contains DM agent & libcoap_dtls_lib is the libcoap library needed for DM agent

The DM agent source-code structure looks like the following:

├── coap_list.c

├── coap_list.h

├── dm_resource.h

├── dmz.h

├── dtls_dm.c

├── Makefile

├── resource_sys.c

└── resource_uri.h

Developing C-DM Agent :

1. Define URI : This is the path to access a resource. Follows LWM2M spec path format /object/object instance/resource for eg. /17/0/3

char uri_resource[] = {"/n/m/n"};

Note: LWM2M allows resource URL in /object/object instance/resource format. Though there is no mandatory resource URL but the standard prescribes certain resources called normative resource as per specific URL.

In TCUP all sensor resources are put under object 15 for e.g. a freeRAM under /15/0/6 etc. and Custom resources are advised to be put under object 17 for e.g. a restart command under /17/0/16 etc.

The resource values should have only alpha numeric characters.

2. Define name of resource :

const unsigned char resource_name[] = {"ResourceName"};

3. A variable to hold resource value:

char resource_value [n];

4. Notification check variable to notify if made observable :

struct coap_resource_t *check_resource = NULL;

5. Write Handler to GET the resource :

static void hnd_get_resource(coap_context_t *ctx UNUSED_PARAM, struct coap_resource_t *resource UNUSED_PARAM, const coap_endpoint_t *local_interface UNUSED_PARAM, coap_address_t *peer UNUSED_PARAM, coap_pdu_t *request UNUSED_PARAM, str *token UNUSED_PARAM, coap_pdu_t *response) {

    /*Function to be written to create GET handler*/

    unsigned char buf[3];

    response->hdr->code = COAP_RESPONSE_CODE(205); /*Check for CoAP response code*/

    if (coap_find_observer(resource, peer, token)) {
        printf("\n## Resource Subscribed for Observation\n");
        /* FIXME: need to check for resource->dirty? */
        coap_add_option(response, COAP_OPTION_OBSERVE, coap_encode_var_bytes(buf, ctx->observe), buf);
    }
    else {
        printf("\n## GET request for the resource");
    }

    /*CoAP content type*/
    coap_add_option(response, COAP_OPTION_CONTENT_TYPE, coap_encode_var_bytes(buf, COAP_MEDIATYPE_TEXT_PLAIN), buf);

    /*MAX AGE for the content*/
    coap_add_option(response, COAP_OPTION_MAXAGE, coap_encode_var_bytes(buf, 0x2ffff), buf);

    /*Function to retrieve value of the specific DM resource*/
    get_resource_value ();

    /*Add retrieved value to CoAP payload to be sent*/
    coap_add_data(response, strlen(resource_value), (unsigned char *)resource_value);


}

6. Initiate resource to be registered in leshan server :

static void init_resources(coap_context_t *ctx) {

    /*Add resource here to be registered with leshan server*/

    coap_resource_t r; /*Resource will be added in the structure*/

    /* ---- Begin Template : resource  to be added ---- */

    /*Initiate resource*/
    r = coap_resource_init((const unsigned char *)uri_resource, strlen(uri_resource), COAP_RESOURCE_FLAGS_NOTIFY_CON);

    /*Add resource handler*/
    coap_register_handler(r, COAP_REQUEST_GET, hnd_get_resource);

    /*call function to get resource value*/
    get_resource_value ();

    /*Make the resource observable*/
    r->observable = 1;

    /*Add initial values (not mandatory)*/
    coap_add_attr(r, (const unsigned char *)resource_value, strlen(resource_value), resource_name, strlen(resource_name), 0);

    /*Add resource to context*/
    coap_add_resource(ctx, r);

    check_resource = r;

    /* ---- End Template : resource  to be added ---- */

    /*Use the template to add more resources*/

}

7. Define CoAP URI :

You can pass your server IP address, endpoint ID (DM client name), lifetime (how many seconds you want to be registered) & security type of the DM client as parameter while running your DM client. How to run DM with parameters is provided at the end of the documentation and also in DM agent help.

/*checks whether the connection is with DTLS or not, you will have to pass parameter to set it while running dm agent*/
if (secure_check == 1) {
    sprintf(uripath, "coaps://%s/rd?ep=%s&lt=%ld&b=U", server_uri, endpoint_id, lifetime_sec);
}
else {
    sprintf(uripath, "coap://%s/rd?ep=%s&lt=%ld&b=U", server_uri, endpoint_id, lifetime_sec);
}

8. Change registration payload for CoAP (if needed) :

char registerPayload[MAX_REG_PAYLOAD];

9. Add resource on registration payload : Call the function with values filled on the arguments.

leshan_coap_form_payload( char *dest, const char *resourceUri, const char *title, const char *datatype, const char *isObservable, const char *unit, const char *infodesc, const char *resourceType, const char *isUpdatable ) {

}

9. To notify observation :

if (check_resource) {
    check_resource->dirty = 1;
}

10. Functions to retrieve resource value :

The functions which will retrieve/populate resource values can be defined in resource_sys.c file

11. Enabling/disabling resources which needs to connect with unrestricted internet :

To enable or disable the resource, you will need to edit dmz.h file inside the agent source code folder.

/* to turn on  off IP info*/
#define OPENNET 0 //define as 1 if not in Demilitarized Zone (DMZ) ie. in non-blocking internet infrastructure

12. Compiling DM application :

  • Go to directory c_dm_secure_r3_v2/

  • You need to compile both library and your DM Agent

  • Go to libcoap_dtls_lib/ext/tinydtls directory, do ./configure and do make

  • Go to libcoap_dtls_lib/ directory, do ./configure and do make

  • Now go to to DM Agent directory c_dm_linux_secure_stable_v1.0 and do make

  • Run DM Agent ./dtls_dm with required arguments

DM Agent Help:

After compiling the library and DM Agent, run ./dtls_dm -h to see list of command line arguments

Lightweight Agent for Device [LAD]

Help:

-c

endpoint listener port [default : random]

-p

destination server Port [default = 5683]

-i

server ip [default = 127.0.0.1]

-d

security option [ 0 = nonsecure (default), 1 = secure ]

-k

DM user api key

-s

PSK secret key

-u

DTLS identity

-e

endpoint id

-l

device registration lifetime (in second) [default 86400 sec]

-v

CoAP verbosity level (max 9) [default 0]

-h

help

Steps to Run:

  1. Preregister a device using Portal or API sandbox

  2. Generate PSK for the pre-registered device using the portal or API sandbox, DTLS identity and PSK will be generated

  3. Register the device by running the device agent with generated PSK and identity argument

Example Run:

./dtls_dm -e “cdtlsdev” -i “10.10.10.136” -k “t9od4WHCHv” -d 1 -u “7FlmUqBOkPGu1zc9” -s “OJzaJqXQ3SkeAASV” -p 5684