Mistake on this page? Email us

Resources and resource instances

Types

There are three types of resources:

  • Device object resources.
  • Security object resources.
  • Custom object resources.

For each of these types, the Resource and Resource Instances can be either static or dynamic:

  • Static: Resource and Resource Instances whose values do not change over time. These are not observable.
  • Dynamic: Resource and Resource Instances whose values can change; the value is fetched from setter APIs every time the server requests a read operation. You can make these observable.

Use the M2MResource class to create and configure object resources of all types.

Tip: The M2MResource class is derived from the M2MResourceInstance class, which in turn is derived from the M2MBase class, so you can use all the public methods from M2MBase or M2MResourceInstance and their derived classes.

Creating device object resources

There are direct APIs that create and set values for the device resources. You can create the required Resource and set values based on their data types.

  • For resources that take string values:

    M2MResource* create_resource(DeviceResource resource, const String &value);
    
  • For resources that take integer values:

    M2MResource* create_resource(DeviceResource resource, uint32_t value);
    
  • A few resources can have multiple instances. To create these resources:

    M2MResourceInstance* create_resource_instance(DeviceResource resource, uint32_t value, uint16_t instance_id);
    

    Where instance_id is the Resource Instance's ID, for example /3/0/11/0.

Working with Resource values

To find the supported enums for integer or string value types, read the M2MDevice API documentation.

You can use other APIs in the M2MDevice class to set, remove and modify new values for the resources.

Creating security object resources

Mandatory (automatic) resources

Creating an M2MSecurity object automatically creates most of the mandatory resources. You can set their values based on their data types.

Resource Data type
SecurityMode Integer
ShortServerID Integer
bool set_resource_value(SecurityResource resource, uint32_t value);

security_object->set_resource_value(M2MSecurity::SecurityMode, 1);
security_object->set_resource_value(M2MSecurity::ShortServerID, 1);
Resource Data type
M2MServerUri String
BootstrapServer String
bool set_resource_value(SecurityResource resource, const String &value);

security_object->set_resource_value(M2MSecurity::M2MServerUri, "coap://lwm2m.us-east-1.mbedcloud.com:5684");
Resource Data type
PublicKey Binary
ServerPublicKey Binary
Secretkey Binary
bool set_resource_value(SecurityResource resource, const uint8_t* value, const uint16_t length);

uint8_t key[] = {"key"};
security_object->set_resource_value(M2MSecurity::PublicKey, key, sizeof(key));

Optional resources

The security object defines three optional resources, which all take an integer value:

  • SMSSecurityMode.
  • M2MServerSMSNumber.
  • ClientHoldOffTime.

To create and set values for the optional resources:

M2MResource* create_resource(SecurityResource resource, uint32_t value);

security_object->create_resource(M2MSecurity::M2MServerSMSNumber, 123542323);

Working with Resource values

To find the supported enums for the integer, string or uint8_t* value types, read the M2MSecurity API documentation.

There are more APIs in the M2MSecurity class that you can use to set, remove and modify Resource values.

Creating custom object resources

For custom objects, you can create resources of two types:

  • M2MResource: a Resource with a single instance, for example /3303/0/5700.

  • M2MResourceInstance: a Resource with multiple instances, for example /3303/0/5700/0 and /3303/0/5700/1.

Creating dynamic and static single-instance resources

  • To create a single-instance Resource with a static value (/3303/0/5700):

    #include "mbed-client/m2mobject.h"
    #include "mbed-client/m2mobjectinstance.h"
    #include "mbed-client/m2mresource.h"
    _object = M2MInterfaceFactory::create_object("3303");
    if(_object) {
        M2MObjectInstance* inst = _object->create_object_instance();
        if(inst) {
    	    inst->create_static_resource("5700",
        	    		     "ResourceTest",
                                         STATIC_VALUE,
                                         sizeof(STATIC_VALUE)-1);
    
  • To create an observable, single-instance Resource with a dynamic value, which you can set later (/3303/0/5700):

    #include "mbed-client/m2mobject.h"
    #include "mbed-client/m2mobjectinstance.h"
    #include "mbed-client/m2mresource.h"
    _object = M2MInterfaceFactory::create_object("3303");
    if(_object) {
        M2MObjectInstance* inst = _object->create_object_instance();
        if(inst) {
        M2MResource* res = inst->create_dynamic_resource("5700", "ResourceTest", true);
            char buffer[20];
            int size = sprintf(buffer, "%d", _value);
            res->set_operation(M2MBase::GET_PUT_ALLOWED);
            res->set_value((const uint8_t*)buffer,
                                       (const uint32_t)size);
    

Creating dynamic and static multi-instance resources

  • To create a Resource Instance (/3303/0/5700/0) with a static value:

    M2MObject* object = M2MInterfaceFactory::create_object("3303");
    M2MObjectInstance* object_instance = object->create_object_instance(0);
    
    uint8_t value[] = {"value"};
    M2MResourceInstance* resource_instance = object_instance->create_static_resource_instance("5700", "sensor", M2MResourceInstance::INTEGER, value, sizeof(value), 0);
    
  • To create an observable Resource Instance with a dynamic value, which you can set later (/3303/0/5700):

    M2MObject* object = M2MInterfaceFactory::create_object("3303");
    M2MObjectInstance* object_instance = object->create_object_instance(0);
    
    uint8_t value[] = {"value"};
    M2MResourceInstance* resource_instance = object_instance->create_dynamic_resource_instance("5700", "sensor", M2MResourceInstance::INTEGER, true, 0);
    int64_t value = 1000;
    resource_instance->set_value(value);
    
    

Configuring the Resource and Resource Instance

Once you have created a Resource or Resource Instance (of any type), you can configure various parameters in it to control or modify communication with Device Management Connect.

Resource and Resource Instances can have many parameters; we review only the most important ones, which you must configure properly to work with the Resource or Resource Instances.

Setting the operation mode

Setting the operation mode of a Resource or Resource Instance determines which Device Management Connect requests it can handle: GET, PUT, POST and DELETE:

virtual void set_operation(M2MBase::Operation operation);

resource->set_operation(M2MBase::GET_PUT_POST_ALLOWED); // The REST operations that can be performed on this Resource. In this case, GET, PUT and POST are allowed; since DELETE is not listed, it is not allowed
resource_instance->set_operation(M2MBase::GET_PUT_DELTE_ALLOWED); // The REST operations that can be performed on this Resource Instance. In this case, GET, PUT and DELETE are allowed; since POST is not listed, it is not allowed

Setting the observable mode

By default, static resources and Resource Instances are not observable. You can set them to be observable, or change them back to not observable:

virtual void set_observable(bool observable);

For a dynamic Resource or Resource Instance, you can set the observable mode when creating the Resource or Resource Instance. You can change it later if you need to:

resource->set_observable(true); // Make an unobservable Resource or Resource Instance observable.
resource->set_observable(false); // Make an observable Resource or Resource Instance unobservable.

Setting the value of a dynamic Resource or Resource Instance

You can set the value of a dynamic Resource or Resource Instance so that it can be sent to Device Management Connect using GET requests:

virtual bool set_value(const uint8_t* value, const uint32_t value_length);
uint8_t value[] = {"value"};
resource->set_value(value, sizeof(value));

Setting an executable function

For dynamic resources, you can pass a function pointer to the Resource or Resource Instance. It will execute when Device Management Connect calls a POST method on that resource.

Note: The Resource or Resource Instance must support the POST operation mode for this feature to work.

To pass the function pointer:

virtual void set_execute_function(execute_callback callback);
void execute_function_example(void *) {
// Code
};
resource->set_execute_function(execute_callback(this, &execute_function_example));

If the execute_callback function is defined as a global function and is outside of your class scope, you can use an overloaded set_execute_function:

virtual void set_execute_function(execute_callback_2 callback);
static void c_style_function(void *) {
// Code
}
resource->set_execute_function(&c_style_function);

Notes:
- The callback handler execution time must be under 10 ms as it is called from the event loop that is shared with other system resources. This means that long callback handlers will block other (possibly critical) code execution.
- You must not call new Device Management Client APIs within the callback handler.

Setting the notification delivery status function

You can use this callback function to track the notification message state changes. For objects, object instances and resources, you can pass a function pointer that executes when the client sends a notification or receives a subscription event.

To pass the function pointer:

bool set_notification_delivery_status_cb(notification_delivery_status_cb callback, void *client_args);
void notification_status(const M2MBase& object, const NoticationDeliveryStatus status)
{
    switch(status) {
        case NOTIFICATION_STATUS_BUILD_ERROR:
            // Error when creating a notification message. Can happen if running out of memory.
            break;
        case NOTIFICATION_STATUS_RESEND_QUEUE_FULL:
            // Cannot create a message because the CoAP resend queue is full.
            break;
        case NOTIFICATION_STATUS_SENT:
            // Notification message created and sent to the server.
            break;
        case NOTIFICATION_STATUS_DELIVERED:
	    // Notification message delivered to the server.
            break;
        case NOTIFICATION_STATUS_SEND_FAILED:
            // Cannot send a notification message because there are no more CoAP retry attempts left.
            break;
        case NOTIFICATION_STATUS_SUBSCRIBED:
            // Observation has started.
            break;
        case NOTIFICATION_STATUS_UNSUBSCRIBED:
            // Observation removed.
            break;
        default:
            break;
    }
}

resource->set_notification_delivery_status_cb((void(*)(const M2MBase&, const NoticationDeliveryStatus, void*))notification_status, application_given_data);

Getter and remove functions

Additional APIs provide getter and remove functions for Resource and Resource Instances in the M2MResource and M2MResourceInstance classes. Read the API documentation for their usage.

Setting an external handler for block-wise messages

For dynamic resources, you can pass a function pointer to the Resource Instance. It will execute when Device Management Connect calls a PUT method on that Resource with a large payload using a block-wise operation. If the callback is set, the application receives notifications for every incoming block-wise message, and the Device Management Client side does not store the message anymore; it is then the application's responsibility to store every block-wise message and combine them when the last block has arrived. When the last block-wise message has arrived also the value_updated() callback is called. The Resource value is empty since the data is stored in the application.

Note:
- The Resource must support the PUT operation mode for this feature to work.
- This feature is used only for transfers that require a block-wise transfer. Meaning that the incoming payload size is larger than set in mbed-client.sn-coap-max-blockwise-payload-size.
- Due to a limitation in the mbed-client-c library, the size limit for a GET request is 65 kB.


Incoming messages

To pass the function pointer for an incoming block-wise message:

virtual void set_incoming_block_message_callback(incoming_block_message_callback callback);
void block_message_received(M2MBlockMessage* argument) {
// Code
}
resource->set_incoming_block_message_callback(incoming_block_message_callback(this, &block_message_received));

Outgoing messages

To pass the function pointer for an outgoing block-wise message:

virtual void set_outgoing_block_message_callback(outgoing_block_message_callback callback);
void block_message_requested(const String& resource, uint8_t*& data, uint32_t& len) {
// Code
}
resource->set_outgoing_block_message_callback(outgoing_block_message_callback(this, &block_message_requested));

Notes: You need to provide the outgoing message content in a single callback. This limits the payload size to 64 KiB.

Application-specific message size

Applications can define their own maximum incoming message size in bytes at build time.

  • For Mbed OS, create an mbed_app.json file at the application level:

    "target_overrides": {
            "*": {
                "mbed-client.sn-coap-max-incoming-message-size": 100000
            }