Mistake on this page? Email us

Using Client Lite API for LwM2M resource management

This section explains how to use Client Lite C-API to create and configure different types of Objects, Object Instances, Resources and Resource Instances to comply with the OMA LwM2M specifications. Client Lite communicates them to Device Management.

You should define your Objects in your application before you connect the client. Standard Object definitions are available in OMA LwM2M Object and Resource Registry. Using standard definitions helps with interoperatibility.

Note: The API complies with the OMA LwM2M specification version 1.0.

As per the OMA LwM2M specification:

  • The client must have defined Objects, under which it can create Object Instances.
  • The client must have at least one Object Instance under each Object.
  • The client must have at least one Resource under each Object Instance.
  • The client may have Resource Instances under Resources.

Creating and configuring Objects and Resources

With this API, you can create and configure LwM2M Resources.

OMA alliance defines a schema for LwM2M Objects and Resources. You can pick one of the existing Objects or define your custom Object schema for your application.

The Object and Resource structure definitions are defined statically at build time and follow the LwM2M schema. You need to define your Object and Resource schema in the struct accordingly.

There are two ways to create your OMA Object and Resource struct for Client Lite.

Creating OMA-defined Objects and Resources automatically

The OMA LwM2M Object definitions are publicly available as XML documents, and their format is governed by an XML Schema definition.

The metadata functionality of the Object registry uses a condensed version of the Object definitions: C structs stored as static data with the code or created dynamically. You can define the metadata through a compile-time common pointer (OMA_LWM2M_OBJECT_DEFS).

The structures are defined in the lwm2m_registry_meta.h header file.

Client Lite provides a tool tools/oma_lwm2m_data_model_tool.py for verifying and converting OMA LwM2M Object definition XML documents into the format used internally. In the example below, this tool is used to generate source/user_oma_lwm2m_object_defs.h from four OMA LwM2M Object definitions validated with LWM2M.xsd. The generated file contains data and the USER_OMA_OBJECTS define directive that is injected to be part of Client Lite resource meta-definitions during compile time.

The example assumes you have OMA object definition .xml files (3200.xml 3201.xml 5000.xml 10000.xml) in your application's folder (lwm2m-resource) and mbed-cloud-client is one level up from this working directory.

$ python ../mbed-cloud-client/tools/oma_lwm2m_data_model_tool.py -s ../mbed-cloud-client/tools/LWM2M.xsd -i 3200.xml 3201.xml 5000.xml 10000.xml -c ../source/user_oma_lwm2m_object_defs.h -m ../mbed-cloud-client/tools/oma_lwm2m_object_user_template.h

You must inject the application resource file generated as above to be part of Client Lite's resource meta-definitions:

  1. Configure a pre-processor macro called USER_OMA_OBJECT_FILE that points to the generated file.

  2. Add this configuration to the mbed_cloud_client_user_config.h file using the syntax:

    #define USER_OMA_OBJECT_FILE "user_oma_lwm2m_object_defs.h"
    

Creating OMA-defined Objects and Resources manually

Objects

The process for the manual Object creation:

  1. Create Resources for your Objects.

    LWM2M_RESOURCE_DEFINITION is the macro for the Object structure following the LwM2M Resource schema.

    • The first parameter is the Resource ID. In this case it's 5501 as it is a button Resource.
    • The second parameter defines whether there can be one or multiple instances to that Resource.
    • The third parameter defines whether the Resource is mandatory or optional.
    • The fourth parameter is the server operation the Resource supports, for example GET(LWM2M_RESOURCE_OPERATIONS_R), GET-PUT(LWM2M_RESOURCE_OPERATIONS_RW), POST(LWM2M_RESOURCE_OPERATIONS_E) or none (LWM2M_RESOURCE_OPERATIONS_NONE).
    • The fifth parameter is the data type of the Resource.
    • The sixth parameter is a plain text description of the Resource.
    const lwm2m_resource_meta_definition_t OMA_LWM2M_RESOURCE_DEFS_OBJECT_3200[] = {
        LWM2M_RESOURCE_DEFINITION(5501, LWM2M_RESOURCE_SINGLE_INSTANCE, LWM2M_RESOURCE_MANDATORY, LWM2M_RESOURCE_OPERATIONS_R,     LWM2M_RESOURCE_TYPE_INTEGER, "button_resource") };
    
    
    const lwm2m_resource_meta_definition_t OMA_LWM2M_RESOURCE_DEFS_OBJECT_5000[] = {
        LWM2M_RESOURCE_DEFINITION(1, LWM2M_RESOURCE_SINGLE_INSTANCE, LWM2M_RESOURCE_MANDATORY, LWM2M_RESOURCE_OPERATIONS_E, LWM2M_RESOURCE_TYPE_INTEGER, "unregister") };
    
  2. Define your Object struct in the user_oma_lwm2m_object_defs.h file.

    LWM2M_OBJECT_DEFINITION is the macro for the Object structure following the LwM2M Object schema.

    • The first parameter is the Object ID. In this case it's 3200 and 5000.
    • The second parameter defines whether there can be one or multiple instances to that Object.
    • The third parameter defines whether the Object is mandatory or optional.
    • The fourth parameter is a plain text description of the Object.
    • The fifth parameter is the number of Resources under that Resource.
    • The sixth parameter is the structure for the Resource definitions of Resources under this Object.
  3. Add your meta definitions to the USER_OMA_OBJECTS define directive.

    #define USER_OMA_OBJECTS     LWM2M_OBJECT_DEFINITION(3200, LWM2M_OBJECT_SINGLE_INSTANCE, LWM2M_OBJECT_MANDATORY, "Button Object", 1, OMA_LWM2M_RESOURCE_DEFS_OBJECT_3200),\
    LWM2M_OBJECT_DEFINITION(5000, LWM2M_OBJECT_SINGLE_INSTANCE, LWM2M_OBJECT_MANDATORY, "Unregister Object", 1, OMA_LWM2M_RESOURCE_DEFS_OBJECT_5000)
    
  4. Every time you add a new Object, add it to the structure list.

  5. Inject the application Resource file generated as above to be part of Client Lite's resource meta-definitions.

  6. Configure a pre-processor macro USER_OMA_OBJECT_FILE that points to the generated file.

  7. Add this configuration to the mbed_cloud_client_user_config.h file using the syntax:

    #define USER_OMA_OBJECT_FILE "user_oma_lwm2m_object_defs.h"
    

Resources

You must define all Resources under a given Object in the same struct in user_oma_lwm2m_object_defs.h file as defined here.

Using Object and Resources from an application

Once you have defined your Object and Resources statically, register them with Client Lite to confirm your application will be using them.

For example:

#include "lwm2m_registry.h"

 registry_path_t path;
 registry_status_t ret;
 registry_t *registry = &simple_m2m_client_get_interface()->endpoint.registry;
 ret = registry_set_path(path, object, object_instance, resource, resource_instance, REGISTRY_PATH_RESOURCE);

  • object is the ID of your Object.
  • object_instance is the Object Instance ID of your Object.
  • resource is the ID of your Resource.
  • resource_instance is the Resource Instance ID of your Resource.

Usually, Object Instance ID and Resource Instance ID are set to 0.

You need to register all Resources under an Object individually.

Callback

You can set a function callback to each Resource individually to track the events taking place on that Resource. Set the callback mode so that they can inform your application of the following events defined under registry_callback_type_e.

For example:

#include "lwm2m_registry.h"

 registry_path_t path;
 registry_status_t ret;
 registry_t *registry = &simple_m2m_client_get_interface()->endpoint.registry;
 ret = registry_set_path(&path, object, object_instance, resource, resource_instance, REGISTRY_PATH_RESOURCE);
 ret = registry_set_callback(registry, &path, registry_callback_t callback);

callback is a function pointer of following type:

registry_status_t callback(registry_callback_type_t,
                           const registry_path_t *,
                           const registry_callback_token_t*,
                           const registry_object_value_t*,
                           const registry_notification_status_t,
                           registry_t *){}

To get back the callback Client Lite uses to report events, use the getter function:

registry_status_t registry_get_callback(const registry_t *registry, const registry_path_t *path, registry_callback_t* callback);

Setting and getting values

There is a pair of setter and getter APIs for each data type. Use them to set and get the data for a given Resource.

Data type Integer

  • Set the value:

    #include "lwm2m_registry.h"
    
     registry_path_t path;
     registry_status_t ret;
     int64_t value = 100;
     registry_t *registry = &simple_m2m_client_get_interface()->endpoint.registry;
     ret = registry_set_path(&path, object, object_instance, resource, resource_instance, REGISTRY_PATH_RESOURCE);
     ret = registry_set_value_int(registry, &path, value);
    
  • Get the value:

    registry_status_t registry_get_value_int(const registry_t *registry, const registry_path_t *path, int64_t *value);
    

Data type String

  • Set the value:

    #include "lwm2m_registry.h"
    
     registry_path_t path;
     registry_status_t ret;
     char *value = "abcdefg";
    
     registry_t *registry = &simple_m2m_client_get_interface()->endpoint.registry;
     ret = registry_set_path(&path, object, object_instance, resource, resource_instance, REGISTRY_PATH_RESOURCE);
     ret = registry_set_value_string(registry, &path, value, true);
    

    The registry_set_value_string API allows you to pass the data ownership to the client. In the above case, the client owns the data. If you pass false as the fourth parameter, the client will not own this data and your application will need to free this later.

    The registry_set_value_string_copy API allows the client to create a copy of the data so you can manage your own data pointer:

    ret = registry_set_value_string_copy(registry, &path, const uint8_t *value, size_t length);
    
  • Get the value:

    registry_status_t registry_get_value_string(const registry_t *registry, const registry_path_t *path, char **value);
    

Data type Opaque

  • Set the value:

    #include "lwm2m_registry.h"
    
    registry_path_t path;
    registry_status_t ret;
    char value[] = {'a','b'};
    registry_data_opaque_t opaque_value;
    opaque_value.data = value;
    opaque_value.size = sizeof(value);
    
    registry_t *registry = &simple_m2m_client_get_interface()->endpoint.registry;
    ret = registry_set_path(&path, object, object_instance, resource, resource_instance, REGISTRY_PATH_RESOURCE);
    ret = registry_set_value_opaque(registry, &path, &opaque_value, true);
    

    The registry_set_value_opaque API allows you to pass the data ownership to the client. In the above case, the client owns the data. If you pass false as the fourth parameter, the client will not own this data and your application will need to free this later.

    The registry_set_value_opaque_copy API allows the client to create a copy of the data so you can manage your own data pointer:

    ret = registry_set_value_opaque_copy(registry, path, const uint8_t *value, size_t length);
    
  • Get the value:

    registry_status_t registry_get_value_opaque(const registry_t *registry, const registry_path_t *path, registry_data_opaque_t **value);
    

Data type Time

  • Set the value:

    #include "lwm2m_registry.h"
    
    registry_path_t path;
    registry_status_t ret;
    int64_t value = 2323131213;
    registry_t *registry = &simple_m2m_client_get_interface()->endpoint.registry;
    ret = registry_set_path(&path, object, object_instance, resource, resource_instance, REGISTRY_PATH_RESOURCE);
    ret = registry_set_value_time(registry, &path, value);
    
  • Get the value:

    registry_status_t registry_get_value_time(const registry_t *registry, const registry_path_t *path, int64_t *value);
    

Data type Float

  • Set the value:

    #include "lwm2m_registry.h"
    
    registry_path_t path;
    registry_status_t ret;
    float value = 3.14159f;
    registry_t *registry = &simple_m2m_client_get_interface()->endpoint.registry;
    ret = registry_set_path(&path, object, object_instance, resource, resource_instance, REGISTRY_PATH_RESOURCE);
    ret = registry_set_value_float(registry, &path, value);
    
  • Get the value:

    registry_status_t registry_get_value_float(const registry_t *registry, const registry_path_t *path, float *value);
    

Data type Boolean

  • Set the value:

    #include "lwm2m_registry.h"
    
    registry_path_t path;
    registry_status_t ret;
    bool value = true;
    registry_t *registry = &simple_m2m_client_get_interface()->endpoint.registry;
    ret = registry_set_path(&path, object, object_instance, resource, resource_instance, REGISTRY_PATH_RESOURCE);
    ret = registry_set_value_boolean(registry, &path, value);
    
  • Get the value:

    registry_status_t registry_get_value_boolean(const registry_t *registry, const registry_path_t *path, bool *value);
    

Set empty value to any data type with a special function

```
#include "lwm2m_registry.h"

registry_path_t path;
registry_status_t ret;
bool empty = true;
registry_t *registry = &simple_m2m_client_get_interface()->endpoint.registry;
ret = registry_set_path(&path, object, object_instance, resource, resource_instance, REGISTRY_PATH_RESOURCE);
ret = registry_set_value_empty(registry, &path, empty);
```

Setting max age to Resources

You can set the maximum time-to-live (in seconds) parameter to Resources, to cache the values on Device Management for a given time. This is recommended especially for sleepy and network constrained devices, because you may not want your web application to always receive (GET) the value directly from the device, especially if the value has not changed.

registry_status_t registry_set_max_age(registry_t *registry, const registry_path_t *path, uint32_t max_age);

The subsequent getter API for the max-age parameter for each Resource:

registry_status_t registry_get_max_age(registry_t *registry, const registry_path_t *path, uint32_t *max_age);

If you have, for example, a temperature reading that would be read once every 15 minutes, it would make sense to set the max-age to (15*60) seconds to that Resource.