Chapter 3 Functional Description of LWM2M Client

This chapter contains a functional description of LWM2M Client.

LWM2M Client Initialization

The LWM2M Client is initialized by calling nx_lwm2m_client_create service. The LWM2M Client runs in its own thread and can report some events to the application through the use of callbacks, or by calling methods of the custom objects implemented by the application.

In addition, LWM2M Client Sessions must be created by calling nx_lwm2m_client_session_create for enabling communication with one or more servers. A session can communicate with two different types of servers: a Bootstrap Server or a LWM2M Server (Device Management).

Bootstrap Server Session

A communication session with a Bootstrap Server is used to provision essential information into the LWM2M Client to enable the LWM2M Client to perform the operation “Register” with one or more LWM2M Servers. This type of server is used during the Client Initiated and Server Initiated Bootstrap modes.

The application can start a Bootstrap session by calling nx_lwm2m_client_session_bootstrap or nx_lwm2m_client_session_bootstrap_dtls, it must provide the IP address and port number of the server, and an optional Security Object Instance ID. The nx_lwm2m_client_session_bootstrap function uses insecure communication, whereas nx_lwm2m_client_session_bootstrap_dtls establishes a secure DTLS connection with the server.

If the Bootstrap operation is successful, the Bootstrap server should have created Security Object Instance(s) for the Bootstrap and LWM2M Servers, and Server Object Instance(s) for the LWM2M Servers. The application can call nx_lwm2m_client_session_register_info_get to get LWM2M Server(s) information and use this information for establishing a session with the LWM2M Server(s).

The Bootstrap data should be saved to non-volatile memory by the application to configure the LWM2M Client at the next reboot of the device.

LWM2M Server Session

A communication session with a LWM2M Server is used for Registration, Device Management and Service Enablement.

The application can register the LWM2M Client to a server by calling nx_lwm2m_client_session_register or nx_lwm2m_client_session_register_dtls, it must provide the IP address and port number of the server, and the Short Server ID which corresponds to an existing Server Object Instance. The nx_lwm2m_client_session_register function uses insecure communication, whereas nx_lwm2m_client_session_register_dtls establishes a secure DTLS connection with the server.

The application can de-register the LWM2M Client by calling nx_lwm2m_client_session_deregister, and ask the client to send an ‘Update’ message by calling nx_lwm2m_client_session_update.

Session State Callback

The application registers a callback at creation of a session which is called when the state of the session is updated, the callback function NX_LWM2M_CLIENT_SESSION_STATE_CALLBACK has the following prototype.

typedef VOID (*NX_LWM2M_CLIENT_SESSION_STATE_CALLBACK)(NX_LWM2M_CLIENT_SESSION *session_ptr,UINT state);

The following states are defined.

Session State Description
NX_LWM2M_CLIENT_SESSION_INIT The initial state after session creation.
NX_LWM2M_CLIENT_SESSION_BOOTSTRAP_WAITING The client is waiting for the expiration of the ‘Hold Off’ timer or Server Initiated Bootstrap.
NX_LWM2M_CLIENT_SESSION_BOOTSTRAP_REQUESTING The client has sent a ‘Request’ message to the Bootstrap Server (Client Initiated Bootstrap).
NX_LWM2M_CLIENT_SESSION_BOOTSTRAP_INITIATED The client is receiving data from the Bootstrap Server.
NX_LWM2M_CLIENT_SESSION_BOOTSTRAP_FINISHED The Bootstrap Server has sent a ‘Finished’ message.
NX_LWM2M_CLIENT_SESSION_BOOTSTRAP_ERROR The bootstrap session has failed.
NX_LWM2M_CLIENT_SESSION_REGISTERING The client has sent a ‘Register’ message to the LWM2M Server.
NX_LWM2M_CLIENT_SESSION_REGISTERED The client is registered to the LWM2M Server.
NX_LWM2M_CLIENT_SESSION_UPDATING The client has sent an ‘Update’ message to the LWM2M Server.
NX_LWM2M_CLIENT_SESSION_DEREGISTERING The client has sent an ‘De-register’ message to the LWM2M Server.
NX_LWM2M_CLIENT_SESSION_DEREGISTERED The client is de-registered from the LWM2M Server.
NX_LWM2M_CLIENT_SESSION_DISABLED The LWM2M Server is disabled. A ‘Register’ will be sent after the expiration of the disable timer.
NX_LWM2M_CLIENT_SESSION_ERROR The registration or update operation to the LWM2M Server has failed.
NX_LWM2M_CLIENT_SESSION_DELETED The Server Object Instance corresponding to the LWM2M Server has been deleted.

In case of error the application can retrieve the cause of the error by calling nx_lwm2m_client_session_error_get.

Local Device Management

The application can access the Objects of the LWM2M Client by using the local device management functions. The following functions are provided.

Function Name Description
nx_lwm2m_client_object_read Read resources from an Object Instance.
nx_lwm2m_client_object_discover Get the list of the resources of an Object Instance.
nx_lwm2m_client_object_write Write resources to an Object Instance.
nx_lwm2m_client_object_execute Perform the Execute operation on a resource of an Object Instance.
nx_lwm2m_client_object_create Create a new Object Instance.
nx_lwm2m_client_object_delete Delete an existing Object Instance.
nx_lwm2m_client_object_next_get Get the next Object ID by the LWM2M Client.
nx_lwm2m_client_object_instance_next_get Get the next Instance of an Object.

Resource Information

When reading from and writing to Objects a Resource is represented by the NX_LWM2M_CLIENT_RESOURCE structure. This structure contains the ID of the Resource/Instance and its value.

The following functions are provided for setting resource info and value.

Function Name Description
nx_lwm2m_client_resource_info_set Set resource info: resource id and operation: READ, WRITE, EXECUTABLE.
nx_lwm2m_client_resource_string_set Set the resource value as string.
nx_lwm2m_client_resource_integer32_set Set the resource value as 32-Bit integer.
nx_lwm2m_client_resource_integer64_set Set the resource value as 64-Bit integer.
nx_lwm2m_client_resource_float32_set Set the resource value as 32-Bit float.
nx_lwm2m_client_resource_float64_set Set the resource value as 64-Bit float.
nx_lwm2m_client_resource_boolean_set Set the resource value as boolean.
nx_lwm2m_client_resource_objlnk_set Set the resource value as object link.
nx_lwm2m_client_resource_opaque_set Set the resource value as opaque.
nx_lwm2m_client_resource_instance_set Set the resource value as instance for multiple resource.
nx_lwm2m_client_resource_dim_set Set the resource dimension for multiple resource for discover.

The following functions are provided for getting resource info and value.

Function Name Description
nx_lwm2m_client_resource_info_get Get resource info: resource id and operation: READ, WRITE, EXECUTABLE.
nx_lwm2m_client_resource_string_get Get the value of a string resource.
nx_lwm2m_client_resource_integer32_get Get the value of a 32-Bit integer resource.
nx_lwm2m_client_resource_integer64_get Get the value of a b4-Bit integer resource.
nx_lwm2m_client_resource_float32_get Get the value of a 32-Bit float resource.
nx_lwm2m_client_resource_float64_get Get the value of a 64-Bit float resource.
nx_lwm2m_client_resource_boolean_get Get the value of a Boolean resource.
nx_lwm2m_client_resource_objlnk_get Get the value of an object link resource.
nx_lwm2m_client_resource_opaque_get Get the value of an opaque resource.
nx_lwm2m_client_resource_instance_get Get the resource instance for multiple resource.
nx_lwm2m_client_resource_dim_get Get the resource dimension for multiple resource.

Object Implementation

The LWM2M Client implements the mandatory OMA LWM2M Objects: Security (0), Server (1), Access Control (2) and Device (3). Other device specific objects must be implemented by the application.

Two data structures are used to define an Object: the NX_LWM2M_CLIENT_OBJECT structure defines the Object implementation, including the Object ID and the object methods, and the NX_LWM2M_CLIENT_OBJECT_INSTANCE structure contains the data of an Object Instance.

The following functions are provided.

Function Name Description
nx_lwm2m_client_object_add Add object implementation to the LwM2M Client.
nx_lwm2m_client_object_remove Remove object implementation from the LwM2M Client.
nx_lwm2m_client_object_instance_add Add object instance to the Object.
nx_lwm2m_client_object_instance_remove Remove object instance from the Object.

The object method callback function has signature shown below.

typedef UINT (*NX_LWM2M_CLIENT_OBJECT_OPERATION_CALLBACK)(
    UINT operation, 
    NX_LWM2M_CLIENT_OBJECT *object_ptr,
    NX_LWM2M_CLIENT_OBJECT_INSTANCE *object_instance_ptr,
    NX_LWM2M_CLIENT_RESOURCE *resource,
    UINT *resource_count,
    VOID *args_ptr,
    UINT args_length);

The ‘Read’ Method

The ‘Read’ method is used to read Resource values from an Object Instance. The parameters are defined as follows.

Parameter Description
operation NX_LWM2M_CLIENT_OBJECT_READ
object_ptr Pointer to the Object implementation.
instance_ptr Pointer to the Object Instance.
resource Pointer to an array of NX_LWM2M_CLIENT_RESOURCE containing the IDs of the resources to read. On return the array is filled with corresponding types and values.
resource_count Pointer to the number of resources to read.
args_ptr Unused parameter for read.
args_length Unused parameter for read.

The ‘Discover’ Method

The ‘Discover’ method is used to retrieve the list of all Resources implemented by an Object. The parameters are defined as follows.

Parameter Description
operation NX_LWM2M_CLIENT_OBJECT_DISCOVER
object_ptr Pointer to the Object implementation.
instance_ptr Pointer to the Object Instance.
resource Pointer to an array of NX_LWM2M_CLIENT_RESOURCE. On return the array is filled with corresponding resource info.
resource_count Pointer to the number of resources to discover. On return this parameter must be updated as the true value.
args_ptr Unused parameter for discover.
args_length Unused parameter for discover.

The ‘Write’ Method

The ‘Write’ method is used to update or replace resources of an Object Instance. The parameters are defined as follows.

Parameter Description
operation NX_LWM2M_CLIENT_OBJECT_WRITE
object_ptr Pointer to the Object implementation.
instance_ptr Pointer to the Object Instance.
resource Pointer to an array of NX_LWM2M_CLIENT_RESOURCE containing the IDs of the resources to read. On return the array is filled with corresponding types and values.
resource_count Pointer to the number of resources to discover.
args_ptr Write flags.
args_length The length of the arguments.

The following write operations can be specified to the flag parameter.

Operation Action Description
0 Partial Update Adds or updates Resources provided in the new value and leaves other existing Resources unchanged.
NX_LWM2M_CLIENT_OBJECT_WRITE_REPLACE_INSTANCE Replace Instance Replaces the Object Instance with the new provided resource values.
NX_LWM2M_CLIENT_OBJECT_WRITE_REPLACE_RESOURCE Replace Resource Replaces the Resources with the new provided resource values (used to replace Multiple Resources).
NX_LWM2M_CLIENT_OBJECT_WRITE_CREATE Create Instance Initializes the newly created Object Instance with the provided resource values (called from the nx_lwm2m_object_create method).
NX_LWM2M_CLIENT_OBJECT_WRITE_BOOTSTRAP Bootstrap Write Called during Bootstrap sequence.

The ‘Execute’ Method

The ‘Execute’ method implements the execution of an Object Resource.

The input parameters are defined as follows.

Parameter Description
operation NX_LWM2M_CLIENT_OBJECT_EXECUTE
object_ptr Pointer to the Object implementation.
instance_ptr Pointer to the Object Instance.
resource Pointer to an array of NX_LWM2M_CLIENT_RESOURCE containing the IDs of the resources to read. On return the array is filled with corresponding types and values.
resource_count Pointer to the number of resources to discover.
args_ptr Pointer to the arguments.
args_length The length of the arguments.

The function must return NX_LWM2M_CLIENT_NOT_FOUND if the Resource ID doesn’t exist, or NX_LWM2M_CLIENT_METHOD_NOT_ALLOWED if it doesn’t support execution.

The ‘Create’ Method

The ‘Create’ method implements the creation of a new Object Instance.

The input parameters are defined as follows.

Parameter Description
operation NX_LWM2M_CLIENT_OBJECT_CREATE
object_ptr Pointer to the Object implementation.
instance_ptr Unused parameter.
resource Pointer to an array of NX_LWM2M_CLIENT_RESOURCE containing the IDs of the resources to read. On return the array is filled with corresponding types and values.
resource_count Pointer to the number of resources to discover.
args_ptr Object instance id.
args_length The length of the arguments.

The ‘Delete’ Method

The ‘Delete’ method implements the deletion of an object instance, the input parameters are defined as follows.

Parameter Description
operation NX_LWM2M_CLIENT_OBJECT_DELETE
object_ptr Pointer to the Object implementation.
instance_ptr Pointer to the Object Instance.
resource Unused parameter.
resource_count Unused parameter.
args_ptr Unused parameter.
args_length Unused parameter.

On success the Object must release the Object Instance data and any other resource allocated by the instance.

Example of LWM2M Client Application

The following code is an example of a simple LWM2M Client Application which implements a custom device consisting of a temperature sensor and a light switch.

The device allows a server to read the value of the temperature sensor and the boolean state of the light switch, and to set the light switch to on/off.

#include "nx_lwm2m_client.h"


/* Custom Object implementation. */

/* Temperature Object ID and resource IDs. */
#define IPSO_TEMPERATURE_OBJECT_ID   3303

#define IPSO_RESOURCE_MIN_VALUE      5601
#define IPSO_RESOURCE_MAX_VALUE      5602
#define IPSO_RESOURCE_RESET_MINMAX   5605
#define IPSO_RESOURCE_VALUE          5700
#define IPSO_RESOURCE_UNITS          5701

/* Actuation Object ID and resource IDs. */
#define IPSO_ACTUATION_OBJECT_ID     3306

#define IPSO_RESOURCE_ONOFF          5850

/* Define the Temperature Object Instance structure */
typedef struct
{
    /* The LWM2M Object Instance */
    NX_LWM2M_CLIENT_OBJECT_INSTANCE    object_instance;

    /* Resources Data */
    NX_LWM2M_FLOAT32                   temperature;
    NX_LWM2M_FLOAT32                   min_temperature;
    NX_LWM2M_FLOAT32                   max_temperature;

} IPSO_TEMPERATURE_INSTANCE;

/* Define the Actuation Object Instance structure */
typedef struct
{
    /* The LWM2M Object Instance */
    NX_LWM2M_CLIENT_OBJECT_INSTANCE    object_instance;

    /* Resources Data */
    NX_LWM2M_BOOL                      onoff;

} IPSO_ACTUATION_INSTANCE;

/* IPSO Temperature */
/* Define the 'Read' Method */
UINT ipso_temperature_read(NX_LWM2M_CLIENT_OBJECT *object_ptr, NX_LWM2M_CLIENT_OBJECT_INSTANCE *instance_ptr, NX_LWM2M_CLIENT_RESOURCE *resource, UINT resource_count)
{
IPSO_TEMPERATURE_INSTANCE *temp = ((IPSO_TEMPERATURE_INSTANCE *) instance_ptr);
UINT i;
NX_LWM2M_ID resource_id;

    for (i = 0 ; i < resource_count; i++)
    {
        nx_lwm2m_client_resource_info_get(&resource[i], &resource_id, NX_NULL);
        switch (resource_id)
        {

        case IPSO_RESOURCE_MIN_VALUE:

            /* return the minimum measured temperature value */
            nx_lwm2m_client_resource_float32_set(&resource[i], temp -> min_temperature);
            break;

        case IPSO_RESOURCE_MAX_VALUE:

            /* return the maximum measured temperature value */
            nx_lwm2m_client_resource_float32_set(&resource[i], temp -> max_temperature);
            break;

        case IPSO_RESOURCE_VALUE:

            /* return the temperature value */
            nx_lwm2m_client_resource_float32_set(&resource[i], temp -> temperature);
            break;

        case IPSO_RESOURCE_RESET_MINMAX:

            /* Not readable */
            return(NX_LWM2M_CLIENT_METHOD_NOT_ALLOWED);

        case IPSO_RESOURCE_UNITS:

            /* return the temperature units */
            nx_lwm2m_client_resource_string_set(&resource[i], "Cel", 3);
            break;

        default:

            /* unknown resource ID */
            return(NX_LWM2M_CLIENT_NOT_FOUND);
        }
    }

    return(NX_SUCCESS);
}

/* Define the 'Discover' method */
UINT ipso_temperature_discover(NX_LWM2M_CLIENT_OBJECT *object_ptr, NX_LWM2M_CLIENT_OBJECT_INSTANCE *instance_ptr, NX_LWM2M_CLIENT_RESOURCE *resources, UINT *resource_count)
{
    if (*resource_count < 5)
    {
        return(NX_LWM2M_CLIENT_BUFFER_TOO_SMALL);
    }

    /* return the list of supported resources IDs */
    *resource_count = 5;
    nx_lwm2m_client_resource_info_set(&resources[0], IPSO_RESOURCE_MIN_VALUE, NX_LWM2M_CLIENT_RESOURCE_OPERATION_READ);
    nx_lwm2m_client_resource_info_set(&resources[1], IPSO_RESOURCE_MAX_VALUE, NX_LWM2M_CLIENT_RESOURCE_OPERATION_READ);
    nx_lwm2m_client_resource_info_set(&resources[2], IPSO_RESOURCE_RESET_MINMAX, NX_LWM2M_CLIENT_RESOURCE_OPERATION_EXECUTABLE);
    nx_lwm2m_client_resource_info_set(&resources[3], IPSO_RESOURCE_VALUE, NX_LWM2M_CLIENT_RESOURCE_OPERATION_READ);
    nx_lwm2m_client_resource_info_set(&resources[4], IPSO_RESOURCE_UNITS, NX_LWM2M_CLIENT_RESOURCE_OPERATION_READ);

    return(NX_SUCCESS);
}

/* Define the 'Execute' method */
UINT ipso_temperature_execute(NX_LWM2M_CLIENT_OBJECT *object_ptr, NX_LWM2M_CLIENT_OBJECT_INSTANCE *instance_ptr, NX_LWM2M_CLIENT_RESOURCE *resource, const CHAR *args_ptr, UINT args_length)
{
IPSO_TEMPERATURE_INSTANCE *temp = ((IPSO_TEMPERATURE_INSTANCE *) instance_ptr);
NX_LWM2M_CLIENT_RESOURCE value;
NX_LWM2M_ID resource_id;

    /* Get resource id */
    nx_lwm2m_client_resource_info_get(resource, &resource_id, NX_NULL);

    switch (resource_id)
    {

    case IPSO_RESOURCE_MIN_VALUE:
    case IPSO_RESOURCE_MAX_VALUE:
    case IPSO_RESOURCE_VALUE:
    case IPSO_RESOURCE_UNITS:

        /* read-only resource */
        return(NX_LWM2M_CLIENT_METHOD_NOT_ALLOWED);

    case IPSO_RESOURCE_RESET_MINMAX:

        /* reset min/max values to current temperature */
        nx_lwm2m_client_resource_float32_set(&value, temp -> temperature);
        if (temp -> min_temperature != temp -> temperature)
        {
            temp -> min_temperature = temp -> temperature;
            nx_lwm2m_client_resource_info_set(&value, IPSO_RESOURCE_MIN_VALUE, NX_NULL);
            nx_lwm2m_client_object_resource_changed(object_ptr, instance_ptr, &value);
        }
        if (temp -> max_temperature != temp -> temperature)
        {
            temp -> max_temperature = temp -> temperature;
            nx_lwm2m_client_resource_info_set(&value, IPSO_RESOURCE_MAX_VALUE, NX_NULL);
            nx_lwm2m_client_object_resource_changed(object_ptr, instance_ptr, &value);
        }

        break;

    default:

        /* unknown resource ID */
        return(NX_LWM2M_CLIENT_NOT_FOUND);
    }

    return(NX_SUCCESS);
}

/* Define the operation callback function of Temperature Object */
UINT ipso_temperature_operation(UINT operation, NX_LWM2M_CLIENT_OBJECT *object_ptr, NX_LWM2M_CLIENT_OBJECT_INSTANCE *object_instance_ptr, NX_LWM2M_CLIENT_RESOURCE *resource, UINT *resource_count, VOID *args_ptr, UINT args_length)
{

    switch (operation)
    {
    case NX_LWM2M_CLIENT_OBJECT_READ:

        /* Call read function */
        return ipso_temperature_read(object_ptr, object_instance_ptr, resource, *resource_count);
    case NX_LWM2M_CLIENT_OBJECT_DISCOVER:

        /* Call discover function */
        return ipso_temperature_discover(object_ptr, object_instance_ptr, resource, resource_count);

    case NX_LWM2M_CLIENT_OBJECT_EXECUTE:

        /* Call execute function */
        return ipso_temperature_execute(object_ptr, object_instance_ptr, resource, args_ptr, args_length);
    default:

        /*Unsupported operation */
        return(NX_LWM2M_CLIENT_NOT_SUPPORTED);
    }
}


/* IPSO Actuation */
/* Define the 'Read' Method */
UINT ipso_actuation_read(NX_LWM2M_CLIENT_OBJECT *object_ptr, NX_LWM2M_CLIENT_OBJECT_INSTANCE *instance_ptr, NX_LWM2M_CLIENT_RESOURCE *resource, UINT resource_count)
{
IPSO_ACTUATION_INSTANCE *act = ((IPSO_ACTUATION_INSTANCE *) instance_ptr);
UINT i;
NX_LWM2M_ID resource_id;

    for (i = 0 ; i < resource_count; i++)
    {
        nx_lwm2m_client_resource_info_get(&resource[i], &resource_id, NX_NULL);
        switch (resource_id)
        {

        case IPSO_RESOURCE_ONOFF:

            /* return the on/off value */
            nx_lwm2m_client_resource_boolean_set(&resource[i], act -> onoff);
            break;

        default:

            /* unknown resource ID */
            return(NX_LWM2M_CLIENT_NOT_FOUND);
        }
    }

    return(NX_SUCCESS);
}


/* Define the 'Discover' method */
UINT ipso_actuation_discover(NX_LWM2M_CLIENT_OBJECT *object_ptr, NX_LWM2M_CLIENT_OBJECT_INSTANCE *instance_ptr, NX_LWM2M_CLIENT_RESOURCE *resources, UINT *resource_count)
{
    if (*resource_count < 1)
    {
        return(NX_LWM2M_CLIENT_BUFFER_TOO_SMALL);
    }

    /* return the list of supported resources IDs */
    *resource_count = 1;
    nx_lwm2m_client_resource_info_set(&resources[0], IPSO_RESOURCE_ONOFF, NX_LWM2M_CLIENT_RESOURCE_OPERATION_READ_WRITE);

    return(NX_SUCCESS);
}

/* Define the 'Write' method */
UINT ipso_actuation_write(NX_LWM2M_CLIENT_OBJECT *object_ptr, NX_LWM2M_CLIENT_OBJECT_INSTANCE *instance_ptr, NX_LWM2M_CLIENT_RESOURCE *resource, UINT resource_count, UINT flags)
{
IPSO_ACTUATION_INSTANCE *act = ((IPSO_ACTUATION_INSTANCE *) instance_ptr);
UINT ret;
NX_LWM2M_BOOL onoff;
UINT i;
NX_LWM2M_ID resource_id;

    for (i = 0 ; i < resource_count; i++)
    {
        nx_lwm2m_client_resource_info_get(&resource[i], &resource_id, NX_NULL);
        switch (resource_id)
        {

        case IPSO_RESOURCE_ONOFF:

            /* assign on/off boolean value */
            ret = nx_lwm2m_client_resource_boolean_get(&resource[i], &onoff);
            if (ret != NX_SUCCESS)
            {
                /* invalid value type */
                return(ret);
            }
            if (onoff != act->onoff)
            {
                act->onoff = onoff;

                printf("Set actuation switch %s\n", onoff ? "On" : "Off");
            }
            break;

        default:

            /* unknown resource ID */
            return(NX_LWM2M_CLIENT_NOT_FOUND);
        }
    }

    return(NX_SUCCESS);
}

/* Define the operation callback function of Actuation Object */
UINT ipso_actuation_operation(UINT operation, NX_LWM2M_CLIENT_OBJECT *object_ptr, NX_LWM2M_CLIENT_OBJECT_INSTANCE *object_instance_ptr, NX_LWM2M_CLIENT_RESOURCE *resource, UINT *resource_count, VOID *args_ptr, UINT args_length)
{
UINT write_op;

    switch (operation)
    {
    case NX_LWM2M_CLIENT_OBJECT_READ:

        /* Call read function */
        return ipso_actuation_read(object_ptr, object_instance_ptr, resource, *resource_count);
    case NX_LWM2M_CLIENT_OBJECT_DISCOVER:

        /* Call discover function */
        return ipso_actuation_discover(object_ptr, object_instance_ptr, resource, resource_count);
    case NX_LWM2M_CLIENT_OBJECT_WRITE:

        /* Get the type of write operation */
        write_op = *(UINT *)args_ptr;

        /* Call write function */
        return ipso_actuation_write(object_ptr, object_instance_ptr, resource, *resource_count, write_op);
    default:

        /* Unsupported operation */
        return(NX_LWM2M_CLIENT_NOT_SUPPORTED);
    }
}


/* NetX data.  */
NX_IP                   ip_0;
NX_PACKET_POOL          pool_0;

/* LWM2M Client data.  */
NX_LWM2M_CLIENT         client;
ULONG                   client_stack[4096 / sizeof(ULONG)];
NX_LWM2M_CLIENT_SESSION session;

/* Objects and instances.  */
NX_LWM2M_CLIENT_OBJECT      temperature_object;
NX_LWM2M_CLIENT_OBJECT      actuation_object;
IPSO_TEMPERATURE_INSTANCE   temperature_instance;
IPSO_ACTUATION_INSTANCE     actuation_instance;

/* Define the session state callback */
void session_callback(NX_LWM2M_CLIENT_SESSION *session_ptr, UINT state)
{
    printf("LWM2M Callback: -> %d\n", state);

    switch (state)
    {

    case NX_LWM2M_CLIENT_SESSION_BOOTSTRAP_REQUESTING:

        printf("Start client initiated bootstrap\n");
        break;

    case NX_LWM2M_CLIENT_SESSION_BOOTSTRAP_INITIATED:

        printf("Got message from boostrap server\n");
        break;

    case NX_LWM2M_CLIENT_SESSION_BOOTSTRAP_FINISHED:

         /* Bootstrap session done, we can register to the LWM2M Server */
        printf( "Boostrap finished.\n");
#ifdef BOOTSTRAP
        tx_semaphore_put(&semaphore_bootstarp_finish);
#endif
        break;

    case NX_LWM2M_CLIENT_SESSION_BOOTSTRAP_ERROR:

        /* Failed to Bootstrap the LWM2M Client. */
        printf( "Failed to boostrap device, error=%02x\n", nx_lwm2m_client_session_error_get(session_ptr));
        break;

    case NX_LWM2M_CLIENT_SESSION_REGISTERED:

        /* Registration to the LWM2M Client done. */
        printf( "LWM2M device registered.\n");
        break;

    case NX_LWM2M_CLIENT_SESSION_DISABLED:

        /* Registration to the LWM2M Client done. */
        printf( "LWM2M device disabled.\n");
        break;

    case NX_LWM2M_CLIENT_SESSION_DEREGISTERED:

        /* Registration to the LWM2M Client done. */
        printf( "LWM2M device deregistered.\n");
        break;

    case NX_LWM2M_CLIENT_SESSION_ERROR:

        /* Failed to register to the LWM2M Client. */
        printf( "Failed to register device, error=%02x\n", nx_lwm2m_client_session_error_get(session_ptr));
        break;
    }
}


/* Application main thread */
void application_thread(ULONG info)
{

UINT status;
NX_LWM2M_ID server_id = 0;
NXD_ADDRESS server_addr;
CHAR *server_uri = NX_NULL;
UINT server_uri_len = 0;
UCHAR security_mode = 0;
   
    /* Create the LWM2M client */
    status = nx_lwm2m_client_create(&client, &ip_0, &pool_0, "nxlwm2mclient", sizeof("nxlwm2mclient") - 1, NX_NULL, 0, NX_LWM2M_CLIENT_BINDING_U, client_stack, sizeof(client_stack), 4);
    if (status)
    {
        return;
    }

    /* Define our custom objects: */
    /* Add Temperature Object */
    status = nx_lwm2m_client_object_add(&client, &temperature_object, IPSO_TEMPERATURE_OBJECT_ID, ipso_temperature_operation);
    if (status)
    {
        return;
    }

    /* Define a single instance */
    temperature_instance.temperature = 22.5f;
    temperature_instance.min_temperature = temperature_instance.temperature;
    temperature_instance.max_temperature = temperature_instance.temperature;
    status = nx_lwm2m_client_object_instance_add(&temperature_object, &temperature_instance.object_instance, NX_NULL);
    if (status)
    {
        return;
    }

    /* Add Actuation Object */
    status = nx_lwm2m_client_object_add(&client, &actuation_object, IPSO_ACTUATION_OBJECT_ID, ipso_actuation_operation);
    if (status)
    {
        return;
    }

    /* Define a single instance */
    actuation_instance.onoff = NX_FALSE;
    status = nx_lwm2m_client_object_instance_add(&actuation_object, &actuation_instance.object_instance, NX_NULL);
    if (status)
    {
        return;
    }

    /* Create a session */
    status = nx_lwm2m_client_session_create(&session, &client, session_callback);
    if (status)
    {
        return;
}

    /* Set bootstrap server address.  */
    server_addr.nxd_ip_version = NX_IP_VERSION_V4;
    server_addr.nxd_ip_address.v4 = IP_ADDRESS(23, 97, 187, 154);

    printf("Start boostraping\r\n");
    status = nx_lwm2m_client_session_bootstrap(&session, 0, &server_addr, 5783);
    if (status)
    {
        return;
    }

    status = nx_lwm2m_client_session_register_info_get(&session, NX_LWM2M_CLIENT_RESERVED_ID, &server_id, &server_uri, &server_uri_len, &security_mode, NX_NULL, NX_NULL, NX_NULL, NX_NULL, NX_NULL, NX_NULL);
    if (status || (security_mode != NX_LWM2M_CLIENT_SECURITY_MODE_NOSEC))
    {
        return;
    }

printf("Register to LWM2M server\r\n");
status = nx_lwm2m_client_session_register(&session, server_id, &server_addr, 5683);

    if (status)
    {
        return;
    }

    /* Application main loop */
    while (1)
    {

        /* application code... */
        tx_thread_sleep(NX_IP_PERIODIC_RATE);
    }

    /* Terminate the LWM2M Client */
    nx_lwm2m_client_delete(&client);
}