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 notification observation mode (confirmable and non-confirmable)
The client can configure a resource notification to be non-confirmable instead of the default behavior of confimable CoAP messaging.
To set a resource to be non-confirmable, use the set_confirmable(bool confirmable);
API:
/**
* @brief Sets how the notification is sent. By default confirmable message type is used.
*
* @param confirmable True means confirmable message, False means non-confirmable message.
*/
void set_confirmable(bool confirmable);
When the client configures a resource to send non-confirmable notifications, the optional message_delivery_status_cb
returns MESSAGE_STATUS_SENT
as final result, instead of MESSAGE_STATUS_DELIVERED
, which signifies server-side acknowledgement.
Setting the value of a dynamic Resource or Resource Instance
You can set the value of a dynamic Resource or Resource Instance, so 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 message delivery status callback
You can use this callback function to track the message status 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_message_delivery_status_cb(message_delivery_status_cb callback, void *client_args);
void message_status_callback(const M2MBase &object,
const M2MBase::MessageDeliveryStatus status,
const M2MBase::MessageType /*type*/)
{
switch (status) {
case M2MBase::MESSAGE_STATUS_BUILD_ERROR:
printf("Message status callback: (%s) error when building CoAP message\r\n", object.uri_path());
break;
case M2MBase::MESSAGE_STATUS_RESEND_QUEUE_FULL:
printf("Message status callback: (%s) CoAP resend queue full\r\n", object.uri_path());
break;
case M2MBase::MESSAGE_STATUS_SENT:
printf("Message status callback: (%s) Message sent to server\r\n", object.uri_path());
break;
case M2MBase::MESSAGE_STATUS_DELIVERED:
printf("Message status callback: (%s) Message delivered\r\n", object.uri_path());
break;
case M2MBase::MESSAGE_STATUS_SEND_FAILED:
printf("Message status callback: (%s) Message sending failed\r\n", object.uri_path());
break;
case M2MBase::MESSAGE_STATUS_SUBSCRIBED:
printf("Message status callback: (%s) subscribed\r\n", object.uri_path());
break;
case M2MBase::MESSAGE_STATUS_UNSUBSCRIBED:
printf("Message status callback: (%s) subscription removed\r\n", object.uri_path());
break;
case M2MBase::MESSAGE_STATUS_REJECTED:
printf("Message status callback: (%s) server has rejected the message\r\n", object.uri_path());
break;
default:
break;
}
}
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
}