This page contains detailed information about the LWM2M service in the Tartabit IoT Bridge. The embedded LWM2M server is fully compliant with the LWM2M 1.0 specification, and partially compliant with the LWM2M 1.1 specification.
thumbprint based
authentication is supported where each endpoint has the thumbprint for the certificate that the device will present. This allows fine control, at the expense of having to manage individual certificates for each endpoint.CA based
authentication is also supported where a CA certificate is uploaded to the server and all clients connecting must use a certificate signed by the CA to be authenticated.The LWM2M service serves as an anchor for defined endpoints. It contains common information related to settings such as bootstrap and CoAP timeouts. You must have a LWM2M service defined before you can connect any LWM2M devices.
Time before declared missing
field in the endpoint at the time the endpoint is created.The LWM2M endpoint contains information about the specific client that is connecting. Most importantly it holds the endpoint name, identity, and pre-shared key used for authentication. It is important to note that clients can bootstrap or register with the Tartabit IoT Bridge using the defined credentials, if the client bootstraps, it will be provided unique credentials to use during its subsequent registration.
This event fires any time a LWM2M device successfully registers to the platform.
{
"binding": "U",
"credentials": "primary",
"remoteAddr": "1.2.3.4:34221",
"version": "1.1",
"discovery": {
"children": {
"1": {
"children": {
"0": {}
}
},
"2": {},
"3": {
"children": {
"0": {}
}
},
"5": {
"children": {
"0": {}
}
},
"6": {
"children": {
"0": {}
}
},
}
}
}
U
or UQ
.primary
, alternate
, or none
This event fires any time a LWM2M device update is received by the platform.
same as lwm2m.register
This event fires when a LWM2M device bootstraps to the platform. If no bootstrap trigger is executed, then the device will be automatically bootstrapped. To use this event, you must reply with a bootstrap configuration using the trigger.reply()
function.
{
"version":"1.1"
}
Below is a sample trigger showing the bootstrap configuration:
var bootstrap = {
clearSecurityObjects: true,
clearServerObjects: true,
clearAclObjects: true,
servers: [
{
shortServerId: 99,
bootstrap: false,
self: true,
//secMode: 'nosec',
secMode: 'psk',
lifetime: 60,
disableTimeout: 86400,
binding: 'UQ',
}
],
acls: [
{
objectId: 4,
instanceId: 0,
rules: {
'99': 'rwe'
},
owner: 65535
}
],
objects: {
"3441": {"1": null},
"3441": {"10": { "105": "bootstrap value"}}
}
}
trigger.reply(bootstrap)
bootstrap: true
and self: true
.nosec
and psk
are supported.dtls
).dtls
).dtls
).r
, w
, e
, c
, d
to allow specific functionality.This event fires any time data is received from the LWM2M device, data is received as a result of a "notify" packet sent as part of an observation, or a reply to an asynchronous read request.
{
"source": "read",
"data": {
"device": {
"0": {
"battery_level": 0,
"battery_status": "normal",
"current_time": "2020-12-10T00:58:18Z",
"device_type": "Device",
"error_code": {
"0": "no error"
},
"firmware_version": "1.0.5.111",
"hardware_version": "Rev 0",
"manufacturer": "Tartabit",
"memory_free": 12459472,
"memory_total": 12864892,
"model_number": "Not Set",
"serial_number": "Not Set",
"software_version": "1.2.2",
"supported_binding_and_modes": "UQ",
"timezone": "EST",
"utc_offset": "-04:00"
}
}
},
"context": {
"requestId":"12345678"
}
}
This event fires when the firmware update process completes (whether a success or failure) for a client.
{
"success": true,
"result": "Firmware updated successfully",
"dateStarted": "2021-02-16T12:21:25.7839634Z",
"dateDownloading": "2021-02-16T12:21:25.8329683Z",
"dateDownloaded": "2021-02-16T12:21:25.8329683Z",
"dateUpdating": "2021-02-16T12:21:25.8329683Z",
"dateComplete": "2021-02-16T12:21:35.8363563Z",
"duration: 10.0343,
"context": {
"replyId": "INRXVhm76rhB9o6Z"
}
}
This event fires when an asynchronous function is called, and a context was specified.
// synchronous, so will not generate an event
var obj = lwm2m.read('mydevice',['3/0'],{async: false})
// asynchronous, but no context defined. Will not generate an event
lwm2m.read('mydevice',['3/0'])
// will generate an event
lwm2m.read('mydevice',['3/0'], {context: {op: 'read', msgId: '1234'}})
// successful example
{
"context": {
"op": "read",
"msgId": "1234"
},
"path": "3/0/14,3441/0/110",
"source": "observe-composite",
"success": true
}
// failed example
{
"context": {
"op": "read",
"msgId": "1234"
},
"error": "tlv: resource definition not found",
"path": "3/0/100",
"source": "write",
"success": false
}
// partial failure
{
"context": {
"op": "read",
"msgId": "1234"
},
"error": "partial success",
"partialErrors": {
"3/0/100": "coap: not found"
},
"path": "3/0/14,3/0/100",
"source": "read",
"success": false
}
true
, will be true
if the result is an intermediate result (like putting an item on the queue), and will not be present otherwise.read
. write
, writeMulti
, exec
, create
, etc.This event fires any time a lwm2m.read() call fails.
{
"source": "read",
"error": "device did not respond to the request"
"context": {
"requestId":"12345678"
}
}
read
. write
, writeMulti
, exec
, create
{
"encoding":"tlv",
"composite": false,
"context": {},
"async": true
}
Create an instance of an object.
// Create instance 0 of object 19 with resource 0 set to 5 and resource 1 set to 'ABC'
lwm2m.create('mydevice','19/0', {'0':5, '1':'ABC'})
Execute a resource of an object.
// Execute resource 3/0/4 to reboot the device.
lwm2m.exec('mydevice','3/0/4')
Execute a resource of an object and pass arguments with the execution.
// Execute resource 3/0/4 to reboot the device.
lwm2m.execWithArgs('mydevice','3/0/4', {'0': 'abc123'})
0='abc123'
.Execute a firmware update using object 5 on a client. Updates regarding the progress of a firmware update are available in the log viewer. When an update is complete, whether successful or failed, a LWM2M FOTA Complete
event will be generated. It is possible to initiate a firmware update on a non-standard instance by specifying instance: "1"
in the options configuration.
// Initiate a firmware update on 'mydevice' with a firmware file hosted externally.
lwm2m.firmwareUpdate('mydevice','https://<your url here>/fwv102.bin')
// Initiate a firmware update on 'mydevice' with a firmware file hosted in the inventory. URI will be written assuming an HTTPS download.
lwm2m.firmwareUpdate('mydevice','fwv102.bin')
// Initiate a firmware update on 'mydevice' with a firmware file hosted in the inventory. The download will occur using COAP over UDP.
lwm2m.firmwareUpdate('mydevice','fwv102.bin', {protocol:'coap'})
// Initiate a firmware update on 'mydevice', and supply a context that will be available in the 'LWM2M FOTA Complete' event for matching the result.
lwm2m.firmwareUpdate('mydevice','fwv102.bin', {context: {a:1, b:2})
// Initiate a firmware update on 'mydevice' with a firmware file hosted in the inventory. The file will be pushed to the device by writing to the 5/0/0 resource.
lwm2m.firmwareUpdate('mydevice','fwv102.bin', {delivery:'push'})
LWM2M Inventory
. If a file from the inventory is specified, then the URI will be automatically set based on the options.http
, https
, coap
, or coaps
depending on your desired transfer method. Default is http
.observe
is supported, meaning that an observation will be placed on 5/0/3 to monitor the state of the firmware update. In the future poll
will be supported for devices that do not support observations.pull
is supported, set to push
to send the file by writing 5/0/0.http
or https
protocols, a query parameter epid=<endpointid>
will be added to the URL to allow the server to track when the device actually starts downloading the firmware file. Setting noEpid
to true will suppress this query parameter from the URL.true
or false
, and defaults to false
. When set, prior to initiating a firmware update a blank string will be written to 5/0/1
to reset the device's state machine.text
, can be text
, or tlv
to control the encoding used to write the blank string to reset the firmware state machine if resetUri
is set to true.0
.Cancels a firmware update in process, the state machine will be set to cancelled. If a firmware update is not in process, no action will be performed.
// Cancel a firmware update in progress.
lwm2m.firmwareCancel('mydevice')
Retrieve the last known values of all resources for a given endpoint. The return matches the same formatting as the result of a lwm2m.read() call. The time that each resource was last updated is presented in a set of additional objects prefixed by ~
, this allows you to know when each value was populated.
// Retrieve the last known data for a device.
var data = lwm2m.lastKnown('mydevice')
var manufacturer = data['3']['0']['0']
var manufacturerUpdatedTimestamp = data['~3']['0']['0']
// Retrieve the last known data for a device, and don't include the ~ objects with the timestamps.
var data = lwm2m.lastKnown('mydevice',{includeUpdateTimes: false})
var data = lwm2m.lastKnown('mydevice', {translateIds:'strings'})
var manufacturer = data['device']['0']['manufacturer']
Observe an instance, or resource. Typically this function should by called from the lwm2m.register event to setup observations when the device connects. After a successful observation, device notifications will be received and generate lwm2m.data events that can be captured and processed. If used with {async: false}
then an object will be returned with an object called tokens
containing the observation tokens for each observed instance or resource so that they can be cancelled in the future.
// Observe resource 3/0/13 to get notifications on the current time of the device.
lwm2m.observe('mydevice','3/0/13')
// Observe instance 3/0 to get notifications anytime the device object changes.
lwm2m.observe('mydevice','3/0')
// Observe multiple resources using a composite observation.
lwm2m.observe('mydevice',['3/0/11','6/0'],{composite:true})
// Observe synchronously and retreive the token
var rslt = lwm2m.observe('mydevice',['3/0/11','6/0'],{async: false})
// rslt.tokens will be a map such as { '3/0/11': 'abdz9sdf23', '6/0': 'd0xa930xa'} containing the tokens for each
// observation, if the observation is a composite, it will have a single key named composite { 'composite': '190s9xzdfa0'}
Cancel an existing observation by its token. You must have saved the token from a previous call to lwm2m.observe()
to be able to cancel it. This function is always asynchronous.
// observe two resources and save the tokens to the cache
var rslt = lwm2m.observe('mydevice',['3/0/11','6/0'],{async: false})
cache.set('observes-mydevice',JSON.stringify(rslt.tokens))
// get the tokens and cancel them (presumably in another trigger)
var tokens = JSON.parse(cache.get('observes-mydevice'))
lwm2m.observeCancel('mydevice', tokens['3/0/11'])
lwm2m.observeCancel('mydevice', tokens['6/0'])
Read one or more instances or resources. You can read multiple instances or resources by specifying an array of paths. The reads are asynchronous because there could be a long delay between the request and the response for the device. As such, the replies come in the form of a lwm2m.data event with the results of the read request. If the device uses LWM2M 1.0, then each instance or resource will be issued as a separate read packet, if using LWM2M 1.1 then the server will attempt to use composite reads.
// Read instance 3/0, reply will be returned via the lwm2m.data event.
lwm2m.read('mydevice',['3/0'])
// Read multiple instances.
lwm2m.read('mydevice',['3/0','6/0/0','6/0/1'])
// Read resource with text encoding
lwm2m.read('mydevice',['3/0/14'],{encoding: 'text'})
// Read instance and pass context to lwm2m.data event.
lwm2m.read('mydevice',['3/0'], {context:{reply: 'abc123'})
// read multiple resources using a composite read.
lwm2m.read('myevice',['3/0/11','6/0'],{composite:true})
Write a single instance or resource. This function is convenient as the data field is a simple value or object, whereas the writeMulti() call requires a more complex object. Note that this function does not support composite operations, for composite write, use writeMulti().
// Write resource 3/0/15 (timezone) to the value 'UTC'.
lwm2m.write('mydevice','3/0/15','UTC')
// Write instance 3/0 with two values.
lwm2m.write('mydevice','3/0',{'14':'+4:00', '15':'EDT'})
// Write resource 3/0/15 (timezone) to the value 'UTC' with text encoding.
lwm2m.write('mydevice','3/0/15','UTC', {encoding: 'text'})
Write multiple instances or resources. If the client supports LWM2M 1.1, then this function will attempt to perform a composite write of all data, if the client supports LWM2M 1.0, then each instance or resource will be written individually. If you write a single resource in an instance, it will be sent as a instance write, if you write more than one resource, and utilize an encoding that supports multi-resource operations (ie TLV or SENML_CBOR) it will be sent as an instance write, otherwise each individual resource will be written.
// Write resource 3/0/15 (timezone) to the value 'UTC'.
lwm2m.writeMulti('mydevice',{'3':{'0':{'15':'UTC'}}})
// Write multiple resource on different instances.
lwm2m.writeMulti('mydevice',{'3':{'0':{'15':'UTC'}}, '1':{'0':{'7': 86400}}})
// Write multiple resource on different instances using a composite write.
lwm2m.writeMulti('mydevice',{'3':{'0':{'15':'UTC'}}, '1':{'0':{'7': 86400}}},{composite:true})
All actions can either be invoked asyncrhonously or synchronously, the difference can have significant impact on your application, so care must be taken.
By default, all actions are asynchronous, meaning they return immeditely, and any error or data is published via another event. In the options for an action, you can specify "async: false" to force the action to act synchronously.
LWM2M Error
events.lwm2m.read()
returns the data from the read, and throws an exception on failure.var data = lwm2m.read('mydevice', ['3/0'], {async: false})
// data contains the same object that is generated in the LWM2M Data event.
try {
lwm2m.write('mydevice', '5/0/2', {async: false})
} catch(e) {
//handle error
}
Custom objects can be managed by navigating to Advanced -> LWM2M Objects in the Control Center. From here you can easily upload new objects and delete objects that are no longer needed. Note that objects must be valid XML definitions as specified by OMA. Objects defined in an account will override the default objects provided by the system, as such, if you have a device that needs a custom implementation of a standard object it can be imported here.
The LWM2M Inventory allows you to upload files that can be distributed through firmware updates (and software updates in the future). Files that are uploaded can be accessed by PULL
mode FOTA requests via HTTP, HTTPS, COAP, and COAPS. For COAPS you must use the same DTLS credentials as you use for your application server connection.
The files uploaded to the LWM2M Inventory must be smaller than 16MB. Larger files must be hosted externally.
The LWM2M Browser can be found by clicking the browser icon on an endpoint in the endpoint list. The browser works for any connected device to enable simple interactions with instances and resources in your client.
The following events are considered billable:
The following events are NOT considered billable:
Endpoints will show connected if the client is registered to the server, this does not mean that the client is reachable. If the device is connecting on the public internet, then it is highly possible that it is behind a NAT. In general UDP NATs expire after 1-3 minutes, meaning there is only a 60-180 second window after each update for the server to initiate communications with the client.
To debug the firmware state machine edit your LWM2M Service and add a tag
_trace_
. Additional messages will be logged to the log viewer.
Due to the inconsistent implementation of LWM2M 1.1 in clients, features such as composites are not enabled by default, you must use the {composite:true} flag in the options of the request to initiate a composite operation. This is subject to change in the future as consistent implementation improves.