LwM2M client utils
The LwM2M client utils library enables an nRF9160-based device to connect to an LwM2M server such as Leshan Demo Server using the Lightweight Machine to Machine (LwM2M) protocol over LTE. Once the device is connected, the library supports the querying the device to retrieve location data or information about the modem. This library builds on top of Zephyr’s Lightweight M2M (LWM2M) client.
Overview
The purpose of the library is to provide a basic combination of LwM2M objects, which forms a common communication framework for applications that communicate with an LwM2M server. Following are the fixed set of readily initialized objects that are available to the users:
Firmware Update object
Connectivity Monitoring object
Device object
Location object
LwM2M Security object
Signal Measurement Information object
Proprietary Location Assistance object
These objects do not indicate a complete set of resources that a device is expected to support. Based on the use case, a user application can, and is expected to, define additional set of resources based on the capabilities of the device.
By default, the library uses LTE-M for connecting and it does not utilize a bootstrap server. The library does not use the LwM2M Queue mode either. To use NB-IoT, a bootstrap server, or the queue mode, follow the implementation details described in the Cellular: LwM2M Client sample.
Configuration
Enable the following parameters to use this library:
Support for the objects is enabled by default, but they can be set individually.
Disable the CONFIG_LWM2M_CLIENT_UTILS_DEVICE_OBJ_SUPPORT
Kconfig option only if you are implementing a Reboot
resource on your application because of a mandatory requirement.
If you are using the Firmware Update object and require downloading of firmware images from TLS enabled services like HTTPS, configure CONFIG_LWM2M_CLIENT_UTILS_DOWNLOADER_SEC_TAG
to specify the security tag that has root certificate for the target server.
Additional configuration
The CONFIG_LWM2M_CLIENT_UTILS_RAI
Kconfig option enables Release Assistance Indication (RAI) for access stratum (AS).
When AS RAI is configured, the device might indicate that no further data is expected in the near future and the connection can be released.
AS RAI was introduced in the 3GPP Release 14 and needs to be supported by the network.
The CONFIG_LWM2M_CLIENT_UTILS_LTE_CONNEVAL
Kconfig option enables the connection pre-evaluation feature that requests information about a cell that is likely to be used for data transmission.
Based on the received estimation of the energy efficiency, the application can decide when the actual data transmission is started.
The application sets the maximum delay for data transmission, the threshold for energy consumption, and the poll period for evaluation.
If the estimated energy usage is above the configured threshold value, the connection pre-evaluation pauses the LwM2M engine and waits for a better signal state that meets the energy efficiency threshold, then resumes data transmission.
But if this waiting period is longer than the maximum delay set for the data transmission, the connection pre-evaluation resumes the LwM2M engine.
Note
Connection pre-evaluation consumes a small amount of energy every time it requests information about a cell.
The CONFIG_LWM2M_CLIENT_UTILS_DTLS_CID
Kconfig option enables DTLS Connection Identifier functionality in handshake.
Enabling this feature requires that either the CONFIG_LWM2M_RD_CLIENT_STOP_POLLING_AT_IDLE
or CONFIG_LWM2M_RD_CLIENT_LISTEN_AT_IDLE
Kconfig option is selected.
Defining custom objects
In addition to the basic objects, you can also create custom LwM2M objects. For most of the applications, the business logic might be implemented inside custom objects, which are either proprietary to the application or following an external specification like IPSO objects. In any case, the application can extend the resource tree by defining specific objects in addition to the objects that are already defined by LwM2M.
Before defining proprietary objects, check if a similar functionality is already defined in LwM2M Object and Resource Registry and use it instead of defining custom objects.
Note
Zephyr’s Lightweight M2M (LWM2M) library has only a limited support for some IPSO objects. To extend the functionality beyond the supported objects, you must make changes to the internal engine as well.
To define custom objects, complete the following steps:
Determine the object ID of the object.
Identify the resources corresponding to the object.
Determine the resource ID for a resource that must be customized.
Form the resource path for the resource in the
object ID/instance/resource ID
format.Create a structure for storing the resource value.
Define a read function that responds to the read requests for the resource value from the server.
Pass the resource information to the LwM2M client utils library to register callbacks for the resource and to publish the sensor data.
The following example describes how you can define an object that follows the Generic Sensor definition from IPSO.
To enable the support for Generic Sensor, set the Kconfig option CONFIG_LWM2M_IPSO_GENERIC_SENSOR
to y
.
To define an object that follows the Generic Sensor definition, complete the following steps:
Determine the object ID of the object:
Refer LwM2M Object and Resource Registry list and observe that the object ID of the Generic Sensor object is
3300
.Identify the resources corresponding to the object:
Click on the entry for the object ID(
3300
in this example) in the LwM2M Object and Resource Registry list to open a raw XML file or open the user friendly LwM2M editor to determine the resources that are defined for the specific the object ID. The following table shows the resource list corresponding to the Generic Sensor object:ID
Name
Operations
Instances
Mandatory
Type
Description
5700
Sensor Value
R
Single
Mandatory
Float
Last or Current Measured Value from the Sensor.
5701
Sensor Units
R
Single
Optional
String
Measurement Units Definition.
5601
Min Measured Value
R
Single
Optional
Float
The minimum value measured by the sensor since power ON or reset.
5602
Max Measured Value
R
Single
Optional
Float
The maximum value measured by the sensor since power ON or reset.
Determine the resource ID for a resource that must be customized:
This example changes only the single resource that is marked
Mandatory
in the above table, which is Sensor Value. You can see that the resource ID for the Sensor Value resource is5700
.Form the resource path for the resource in the
object ID/instance/resource ID
format:LwM2M uses resource paths in the
object ID/instance/resource ID
format. The object ID in the example is3300
and since it is the first instance of the object, the instance value is0
. Therefore, the full path for the sensor value will be3300/0/5700
. You must use this path in the LwM2M client utils library API.Define a read function that responds to the read requests for the resource value from the server:
static double sensor_value = 1.0; static void *read_cb(uint16_t obj_inst_id, uint16_t res_id, uint16_t res_inst_id, size_t *data_len) { /* Only object instance 0 is currently used */ if (obj_inst_id != 0) { *data_len = 0; return NULL; } /* Demo: change the sensor value */ sensor_value += 0.1; /* Return sensor value for the LwM2M library */ lwm2m_set_f64(&LWM2M_OBJ(3300, 0, 5700), sensor_value); *data_len = sizeof(sensor_value); return &sensor_value; }
Pass the resource information to the LwM2M client utils library to register callbacks for the resource and to publish the sensor data:
int init_resource(void) { lwm2m_create_obj_inst(&LWM2M_OBJ(3300, 0); lwm2m_register_read_callback(&LWM2M_OBJ(3300, 0, 5700), read_cb); return 0; }
The above code registers the object instance and passes the resource information to the library to register the read callback.
At this stage, the generic sensor is fully functional. For defining outputs, the process is very much similar but instead of read callback, write callback is defined.
Registering a read callback is optional and is recommended if you want to read the data directly from a sensor on each read operation.
If the value of a readable resource is modified on an event, a read callback need not be registered.
An example is the Push Button object.
On receipt of an event that is triggered by button press or release, the value is updated through the lwm2m_engine with lwm2m_set_bool()
.
When a read operation is issued by the server, the engine obtains the button value directly from the object’s internal data instead of the read callback.
This causes the internal engine to allocate memory and store all the resources that are defined for the IPSO object ID.
Extending the library with new object types
If the library is not supporting the object type that you need, it is possible to extend the support by introducing completely new object types. This is currently possible only by using an internal API from the LwM2M engine.
Before creating new object types, see the existing implementation of IPSO objects from zephyr/subsys/net/lib/lwm2m
directory.
Select one of the existing object types, for example Push Button, and refactor it according to your need.
The following example shows how to create a new object type that follows the IPSO Digital Output definition:
Define the following IDs:
#define IPSO_DIGITAL_OUTPUT_ID 3201 #define OUTPUT_DIGITAL_STATE_ID 5550 #define RESOURCE_PATH &LWM2M_OBJ(IPSO_DIGITAL_OUTPUT_ID, 0, OUTPUT_DIGITAL_STATE_ID)
Define the storage for the output type:
/* resource state */ struct output_data { bool state; }; static struct output_data output_data[MAX_INSTANCE_COUNT];
Define the resources for the object type:
#define N_RESOURCES 1 static struct lwm2m_engine_obj_field fields[] = { OBJ_FIELD(OUTPUT_DIGITAL_STATE_ID, RW, BOOL), };
The above code defines only a single resource.
Define the structures that are required by the engine:
static struct lwm2m_engine_obj output_obj; static struct lwm2m_engine_obj_inst inst[MAX_INSTANCE_COUNT]; static struct lwm2m_engine_res res[MAX_INSTANCE_COUNT][N_RESOURCES]; static struct lwm2m_engine_res_inst res_inst[MAX_INSTANCE_COUNT][N_RESOURCES];
Pass the information about how you want to create the object to the LwM2M engine:
static struct lwm2m_engine_obj_inst *output_create(uint16_t id) { /* Check that there is no other instance with this ID */ /* It is assumed that the instance ID is same as the index in the array */ if (inst[id].obj) { LOG_ERR("Cannot create instance - already existing: %u", id); return NULL; } if (id >= MAX_INSTANCE_COUNT) { LOG_ERR("Cannot create instance - no more room: %u", id); return NULL; } /* Set default values */ (void)memset(&output_data[id], 0, sizeof(output_data[id])); (void)memset(res[id], 0, sizeof(res[id][0]) * ARRAY_SIZE(res[id])); init_res_instance(res_inst[id], ARRAY_SIZE(res_inst[id])); /* initialize instance resource data */ int i = 0, j = 0; INIT_OBJ_RES(OUTPUT_DIGITAL_STATE_ID, res[id], i, res_inst[id], j, 1, false, true, &output_data[id].state, sizeof(output_data[id].state), NULL, NULL, NULL, NULL); inst[id].resources = res[id]; inst[id].resource_count = i; LOG_DBG("Created IPSO Output instance: %d", id); return &inst[id]; }
Register the new object type with the engine:
int ipso_output_init() { output_obj.obj_id = IPSO_DIGITAL_OUTPUT_ID; output_obj.fields = fields; output_obj.field_count = ARRAY_SIZE(fields); output_obj.max_instance_count = ARRAY_SIZE(inst); output_obj.create_cb = output_create; lwm2m_register_obj(&output_obj); lwm2m_create_obj_inst(&LWM2M_OBJ(3201, 0)); lwm2m_register_post_write_callback(RESOURCE_PATH, on_off_cb); return 0; }
As shown in the above code, the instance is created, and a callback is attached to it. The content of the callback is similar as in the Generic Sensor example. Some details are left out in these examples and for more information, see the existing IPSO objects from the LwM2M engine.
API documentation
include/net/lwm2m_client_utils.h
subsys/net/lib/lwm2m_client_utils/lwm2m
- group lwm2m_client_utils
LwM2M Client utilities to build an application.
The client provides APIs for:
connecting to a remote server
setting up default resources
Firmware
Connection monitoring
Device
Location
Security
Defines
-
LWM2M_OBJECT_ADV_FIRMWARE_ID
-
RESULT_ADV_FOTA_CANCELLED
-
RESULT_ADV_FOTA_DEFERRED
-
RESULT_ADV_CONFLICT_STATE
-
RESULT_ADV_DEPENDENCY_ERR
-
REBOOT_SOURCE_DEVICE_OBJ
-
REBOOT_SOURCE_FOTA_OBJ
-
LWM2M_FOTA_PACKAGE_ID
-
LWM2M_FOTA_PACKAGE_URI_ID
-
LWM2M_FOTA_UPDATE_ID
-
LWM2M_FOTA_STATE_ID
-
LWM2M_FOTA_UPDATE_RESULT_ID
-
LWM2M_FOTA_PACKAGE_NAME_ID
-
LWM2M_FOTA_PACKAGE_VERSION_ID
-
LWM2M_FOTA_UPDATE_PROTO_SUPPORT_ID
-
LWM2M_FOTA_UPDATE_DELIV_METHOD_ID
-
LWM2M_FOTA_CANCEL_ID
-
LWM2M_FOTA_SEVERITY_ID
-
LWM2M_FOTA_LAST_STATE_CHANGE_TIME_ID
-
LWM2M_FOTA_MAXIMUM_DEFERRED_PERIOD_ID
-
LWM2M_ADV_FOTA_COMPONENT_NAME_ID
-
LWM2M_ADV_FOTA_CURRENT_VERSION_ID
-
LWM2M_ADV_FOTA_LINKED_INSTANCES_ID
-
LWM2M_ADV_FOTA_CONFLICTING_INSTANCES_ID
Typedefs
-
typedef int (*modem_mode_cb_t)(enum lte_lc_func_mode new_mode, void *user_data)
Callback to request a modem state change, being it powering off, flight mode etc.
- Return:
0 if mode was set successfully
- Return:
positive value to indicate seconds before retry
- Return:
negative error code in case of a failure
-
typedef int (*lwm2m_firmware_event_cb_t)(struct lwm2m_fota_event *event)
Firmware update event callback.
This callback is used for indicating firmware update states, to prepare the application for an update, and to request for reconnecting the modem after the firmware update.
Event handler is getting event callback when Firmware update utils library change states.
LWM2M_FOTA_DOWNLOAD_START: Indicate that download or upload of a new image is started.
LWM2M_FOTA_DOWNLOAD_FINISHED: Indicate that Image is delivered.
LWM2M_FOTA_UPDATE_IMAGE_REQ: Request a permission to update an image. Callback handler may return zero to grant permission for update. Otherwise it may return negative error code to cancel the update or positive value to indicate that update should be postponed that amount of seconds.
LWM2M_FOTA_UPDATE_MODEM_RECONNECT_REQ: Request a reconnect and initialize the modem after a firmware update. The callback handler may return 0 to indicate that the application is handling the reconnect. Modem initialization and LwM2M client re-connection needs to be handled outside of the callback context. Otherwise, return -1 to indicate that the device is rebooting
LWM2M_FOTA_UPDATE_ERROR: Indicate that FOTA process have failed or cancelled.
- Param event:
[in] LwM2M Firmware Update object event structure
- Return:
zero indicating OK or negative error code indicating an failure and will mark the whole FOTA process to failed. Positive return code will postpone the request, but can only be used in LWM2M_FOTA_UPDATE_IMAGE_REQ event.
Enums
-
enum lwm2m_fota_event_id
Firmware update callback events.
Values:
-
enumerator LWM2M_FOTA_DOWNLOAD_START
Download process started
-
enumerator LWM2M_FOTA_DOWNLOAD_FINISHED
Download process finished
-
enumerator LWM2M_FOTA_UPDATE_IMAGE_REQ
Request for update new image
-
enumerator LWM2M_FOTA_UPDATE_MODEM_RECONNECT_REQ
Request to reconnect the modem and LwM2M client
-
enumerator LWM2M_FOTA_UPDATE_ERROR
Fota process fail or cancelled
-
enumerator LWM2M_FOTA_DOWNLOAD_START
Functions
-
int lwm2m_init_security(struct lwm2m_ctx *ctx, char *endpoint, struct modem_mode_change *mmode)
Initialize Security object support for nrf91.
This wrapper will install hooks that allows device to do a proper bootstrap and store received server settings to permanent storage using Zephyr settings API. Credential are stored to modem and no keys would enter the flash.
Note
This API calls settings_subsys_init() and should only be called after the settings backend (Flash or FS) is ready.
-
int lwm2m_security_set_psk(uint16_t sec_obj_inst, const void *psk, int psk_len, bool psk_is_hex, const char *psk_id)
Set security object to PSK mode.
Any pointer can be given as a NULL, which means that data related to this field is set to zero length in the engine. Effectively, it causes that relative data is not written into the modem. This can be used if the given data is already provisioned to the modem.
- Parameters:
sec_obj_inst – Security object ID to modify.
psk – Pointer to PSK key, either in HEX or binary format.
psk_len – Length of data in PSK pointer. If PSK is HEX string, should include string terminator.
psk_is_hex – True if PSK points to data in HEX format. False if the data is binary.
psk_id – PSK key ID in string format.
- Returns:
Zero if success, negative error code otherwise.
-
int lwm2m_security_set_certificate(uint16_t sec_obj_inst, const void *cert, int cert_len, const void *private_key, int key_len, const void *ca_chain, int ca_len)
Set security object to certificate mode.
Any pointer can be given as a NULL, which means that data related to this field is set to zero length in the engine. Effectively, it causes that relative data is not written into the modem. This can be used if the given data is already provisioned to the modem.
- Parameters:
sec_obj_inst – Security object ID to modify.
cert – Pointer to certificate.
cert_len – Certificate length.
private_key – Pointer to private key.
key_len – Private key length.
ca_chain – Pointer to CA certificate or server certificate.
ca_len – CA chain length.
- Returns:
Zero if success, negative error code otherwise.
-
bool lwm2m_security_needs_bootstrap(void)
Check if the client credentials are already stored.
- Returns:
true If bootstrap is needed.
- Returns:
false If client credentials are already available.
-
int lwm2m_init_device(void)
Initialize Device object.
-
int lwm2m_device_reboot_cb(uint16_t obj_inst_id, uint8_t *args, uint16_t args_len)
Reboot handler for a device object.
All arguments are ignored.
- Parameters:
obj_inst_id – Device object instance.
args – Argument pointer’s
args_len – Length of argument’s
- Returns:
Zero if success, negative error code otherwise.
-
int lwm2m_init_location(void)
Initialize Location object.
-
void *firmware_read_cb(uint16_t obj_inst_id, size_t *data_len)
Firmware read callback.
-
int lwm2m_init_firmware(void)
Initialize Firmware update utils library.
- Returns:
Zero if success, negative error code otherwise.
-
int lwm2m_init_firmware_cb(lwm2m_firmware_event_cb_t cb)
Initialize Firmware update utils library with callback.
- Parameters:
cb – [in] A callback function to receive firmware update state changes.
- Returns:
Zero if success, negative error code otherwise.
-
int lwm2m_init_image(void)
Initialize Image Update object.
- Returns:
Zero if success, negative error code otherwise.
-
int lwm2m_init_connmon(void)
Initialize Connectivity Monitoring object. Called in SYS_INIT.
- Returns:
Zero if success, negative error code otherwise.
-
int lwm2m_init_rai(void)
Initialize release assistance indication (RAI) module.
- Returns:
Zero if success, negative error code otherwise.
-
int lwm2m_rai_no_data(void)
Set socket option SO_RAI_NO_DATA to bypass RRC Inactivity period and immediately switch to Idle mode.
- Returns:
Zero if success, negative error code otherwise.
-
int lwm2m_rai_last(void)
Set socket option SO_RAI_LAST and send dummy packet to bypass RRC Inactivity period and immediately switch to Idle mode.
- Returns:
Zero if success, negative error code otherwise.
-
int lwm2m_rai_get(enum lwm2m_rai_mode *mode)
Get the RAI mode.
- Parameters:
mode – Pointer to RAI mode variable.
- Returns:
Zero if success, negative error code otherwise.
-
int lwm2m_rai_req(enum lwm2m_rai_mode mode)
Function for requesting modem to enable or disable use of AS RAI.
- Parameters:
mode – Requested RAI mode.
- Returns:
Zero if success, negative error code otherwise.
-
int lwm2m_utils_enable_conneval(enum lte_lc_energy_estimate min_energy_estimate, uint64_t maximum_delay_s, uint64_t poll_period_ms)
Enable connection pre-evaluation module.
- Parameters:
min_energy_estimate – Minimum estimated energy consumption when data transmission is started.
maximum_delay_s – Maximum time in seconds to delay data transmission.
poll_period_ms – Time period in milliseconds before new energy estimation.
- Returns:
Zero if success, negative error code otherwise.
-
void lwm2m_utils_disable_conneval(void)
Disable connection pre-evaluation.
-
int lwm2m_utils_conneval(struct lwm2m_ctx *client, enum lwm2m_rd_client_event *client_event)
Start connection pre-evaluation.
This evaluation may block or alter the ongoing event to prevent LwM2M engine from initiating transfers when network conditions are poor.
- Parameters:
client – Pointer to LwM2M context
client_event – pointer to LwM2M RD client events
- Returns:
Zero if success, negative error code otherwise.
-
void lwm2m_utils_connection_manage(struct lwm2m_ctx *client, enum lwm2m_rd_client_event *client_event)
LwM2M utils connection event handler.
This function should be called from an event handler registered to lwm2m_rd_client_start() before normal event handler part.
- Parameters:
client – client A pointer to LwM2M context.
client_event – A pointer to LwM2M RD client events.
-
uint8_t lwm2m_adv_firmware_get_update_state(uint16_t obj_inst_id)
-
void lwm2m_adv_firmware_set_update_state(uint16_t obj_inst_id, uint8_t state)
-
uint8_t lwm2m_adv_firmware_get_update_result(uint16_t obj_inst_id)
-
void lwm2m_adv_firmware_set_update_result(uint16_t obj_inst_id, uint8_t result)
-
void lwm2m_adv_firmware_set_write_cb(uint16_t obj_inst_id, lwm2m_engine_set_data_cb_t cb)
-
lwm2m_engine_set_data_cb_t lwm2m_adv_firmware_get_write_cb(uint16_t obj_inst_id)
-
void lwm2m_adv_firmware_set_update_cb(uint16_t obj_inst_id, lwm2m_engine_execute_cb_t cb)
-
lwm2m_engine_execute_cb_t lwm2m_adv_firmware_get_update_cb(uint16_t obj_inst_id)
-
int lwm2m_adv_firmware_create_inst(const char *component, lwm2m_engine_set_data_cb_t write_callback, lwm2m_engine_execute_cb_t update_callback)
-
struct modem_mode_change
- #include <lwm2m_client_utils.h>
Callback used for querying permission from the app to proceed when modem’s state changes.
- Param cb:
The callback function
- Param user_data:
App specific data to be fed to the callback once it’s called
-
struct lwm2m_fota_download_start
- #include <lwm2m_client_utils.h>
Event data for id LWM2M_FOTA_DOWNLOAD_START.
Public Members
-
uint16_t obj_inst_id
Object Instance id for event
-
uint16_t obj_inst_id
-
struct lwm2m_fota_download_finished
- #include <lwm2m_client_utils.h>
Event data for id LWM2M_FOTA_DOWNLOAD_FINISHED.
-
struct lwm2m_fota_update_request
- #include <lwm2m_client_utils.h>
Event data for id LWM2M_FOTA_UPDATE_IMAGE_REQ.
-
struct lwm2m_fota_reconnect
- #include <lwm2m_client_utils.h>
Event data for ID LWM2M_FOTA_UPDATE_MODEM_RECONNECT_REQ.
Public Members
-
uint16_t obj_inst_id
Object Instance ID for event
-
uint16_t obj_inst_id
-
struct lwm2m_fota_update_failure
- #include <lwm2m_client_utils.h>
Event data for ID LWM2M_FOTA_UPDATE_ERROR.
-
struct lwm2m_fota_event
- #include <lwm2m_client_utils.h>
Public Members
-
enum lwm2m_fota_event_id id
Fota event ID and indicate used Data structure
-
struct lwm2m_fota_download_start download_start
LWM2M_FOTA_DOWNLOAD_START
-
struct lwm2m_fota_download_finished download_ready
LWM2M_FOTA_DOWNLOAD_FINISHED
-
struct lwm2m_fota_update_request update_req
LWM2M_FOTA_UPDATE_IMAGE_REQ
-
struct lwm2m_fota_reconnect reconnect_req
LWM2M_FOTA_UPDATE_MODEM_RECONNECT_REQ
-
struct lwm2m_fota_update_failure failure
LWM2M_FOTA_UPDATE_ERROR
-
enum lwm2m_fota_event_id id