# LwM2M Gateway for the EMQX Broker. [The LwM2M Specifications](http://www.openmobilealliance.org/release/LightweightM2M) is a Lightweight Machine to Machine protocol. With `emqx_lwm2m`, user is able to send LwM2M commands(READ/WRITE/EXECUTE/...) and get LwM2M response in MQTT way. `emqx_lwm2m` transforms data between MQTT and LwM2M protocol. emqx_lwm2m needs object definitions to parse data from lwm2m devices. Object definitions are declared by organizations in XML format, you could find those XMLs from [LwM2MRegistry](http://www.openmobilealliance.org/wp/OMNA/LwM2M/LwM2MRegistry.html), download and put them into the directory specified by `lwm2m.xml_dir`. If no associated object definition is found, response from device will be discarded and report an error message in log. ## Load emqx_lwm2m ``` ./bin/emqx_ctl plugins load emqx_lwm2m ``` ## Test emqx-lwm2m using *wakaama* [wakaama](https://github.com/eclipse/wakaama) is an easy-to-use lwm2m client command line tool. Start *lwm2mclient* using an endpoint name `ep1`: ``` ./lwm2mclient -n ep1 -h 127.0.0.1 -p 5683 -4 ``` To send an LwM2M DISCOVER command to *lwm2mclient*, publish an MQTT message to topic `lwm2m//dn` (where `` is the endpoint name of the client), with following payload: ```json { "reqID": "2", "msgType": "discover", "data": { "path": "/3/0" } } ``` The MQTT message will be translated to an LwM2M DISCOVER command and sent to the *lwm2mclient*. Then the response of *lwm2mclient* will be in turn translated to an MQTT message, with topic `lwm2m//up/resp`, with following payload: ```json { "reqID": "2", "msgType": "discover", "data": { "code":"2.05", "codeMsg": "content", "content": [ ";dim=8", "", "", "", "" ] } } ``` ## LwM2M <--> MQTT Mapping ### Register/Update (LwM2M Client Registration Interface) - **LwM2M Register and Update message will be converted to following MQTT message:** - **Method:** PUBLISH - **Topic:** `lwm2m/{?EndpointName}/up/resp` (configurable) - **Payload**: - MsgType **register** and **update**: ```json { "msgType": {?MsgType}, "data": { "ep": {?EndpointName}, "lt": {?LifeTime}, "sms": {?MSISDN}, "lwm2m": {?Lwm2mVersion}, "b": {?Binding}, "alternatePath": {?AlternatePath}, "objectList": {?ObjectList} } } ``` - {?EndpointName}: String, the endpoint name of the LwM2M client - {?MsgType}: String, could be: - "register": LwM2M Register - "update": LwM2M Update - "data" contains the query options and the object-list of the register message - The *update* message is only published if the object-list changed. ### Downlink Command and Uplink Response (LwM2M Device Management & Service Enablement Interface) - **To send a downlink command to device, publish following MQTT message:** - **Method:** PUBLISH - **Topic:** `lwm2m/{?EndpointName}/dn` - **Request Payload**: ```json { "reqID": {?ReqID}, "msgType": {?MsgType}, "data": {?Data} } ``` - {?ReqID}: Integer, request-id, used for matching the response to the request - {?MsgType}: String, can be one of the following: - "read": LwM2M Read - "discover": LwM2M Discover - "write": LwM2M Write - "write-attr": LwM2M Write Attributes - "execute": LwM2M Execute - "create": LwM2M Create - "delete": LwM2M Delete - {?Data}: Json Object, its value depends on the {?MsgType}: - **If {?MsgType} = "read" or "discover"**: ```json { "path": {?ResourcePath} } ``` - {?ResourcePath}: String, LwM2M full resource path. e.g. "3/0", "/3/0/0", "/3/0/6/0" - **If {?MsgType} = "write" (single write)**: ```json { "path": {?ResourcePath}, "type": {?ValueType}, "value": {?Value} } ``` - {?ValueType}: String, can be: "Time", "String", "Integer", "Float", "Boolean", "Opaque", "Objlnk" - {?Value}: Value of the resource, depends on "type". - **If {?MsgType} = "write" (batch write)**: ```json { "basePath": {?BasePath}, "content": [ { "path": {?ResourcePath}, "type": {?ValueType}, "value": {?Value} } ] } ``` - The full path is concatenation of "basePath" and "path". - **If {?MsgType} = "write-attr"**: ```json { "path": {?ResourcePath}, "pmin": {?PeriodMin}, "pmax": {?PeriodMax}, "gt": {?GreaterThan}, "lt": {?LessThan}, "st": {?Step} } ``` - {?PeriodMin}: Number, LwM2M Notification Class Attribute - Minimum Period. - {?PeriodMax}: Number, LwM2M Notification Class Attribute - Maximum Period. - {?GreaterThan}: Number, LwM2M Notification Class Attribute - Greater Than. - {?LessThan}: Number, LwM2M Notification Class Attribute - Less Than. - {?Step}: Number, LwM2M Notification Class Attribute - Step. - **If {?MsgType} = "execute"**: ```json { "path": {?ResourcePath}, "args": {?Arguments} } ``` - {?Arguments}: String, LwM2M Execute Arguments. - **If {?MsgType} = "create"**: ```json { "basePath": "/{?ObjectID}", "content": [ { "path": {?ResourcePath}, "type": {?ValueType}, "value": {?Value} } ] } ``` - {?ObjectID}: Integer, LwM2M Object ID - **If {?MsgType} = "delete"**: ```json { "path": "{?ObjectID}/{?ObjectInstanceID}" } ``` - {?ObjectInstanceID}: Integer, LwM2M Object Instance ID - **The response of LwM2M will be converted to following MQTT message:** - **Method:** PUBLISH - **Topic:** `"lwm2m/{?EndpointName}/up/resp"` - **Response Payload:** ```json { "reqID": {?ReqID}, "imei": {?IMEI}, "imsi": {?IMSI}, "msgType": {?MsgType}, "data": {?Data} } ``` - {?MsgType}: String, can be: - "read": LwM2M Read - "discover": LwM2M Discover - "write": LwM2M Write - "write-attr": LwM2M Write Attributes - "execute": LwM2M Execute - "create": LwM2M Create - "delete": LwM2M Delete - **"ack"**: [CoAP Empty ACK](https://tools.ietf.org/html/rfc7252#section-5.2.2) - {?Data}: Json Object, its value depends on {?MsgType}: - **If {?MsgType} = "write", "write-attr", "execute", "create", "delete", or "read"(when response without content)**: ```json { "code": {?StatusCode}, "codeMsg": {?CodeMsg}, "reqPath": {?RequestPath} } ``` - {?StatusCode}: String, LwM2M status code, e.g. "2.01", "4.00", etc. - {?CodeMsg}: String, LwM2M response message, e.g. "content", "bad_request" - {?RequestPath}: String, the requested "path" or "basePath" - **If {?MsgType} = "discover"**: ```json { "code": {?StatusCode}, "codeMsg": {?CodeMsg}, "reqPath": {?RequestPath}, "content": [ {?Link}, ... ] } ``` - {?Link}: String(LwM2M link format) e.g. `""`, `"<3/0/1>;dim=8"` - **If {?MsgType} = "read"(when response with content)**: ```json { "code": {?StatusCode}, "codeMsg": {?CodeMsg}, "content": {?Content} } ``` - {?Content} ```json [ { "path": {?ResourcePath}, "value": {?Value} } ] ``` - **If {?MsgType} = "ack", "data" does not exists** ### Observe (Information Reporting Interface - Observe/Cancel-Observe) - **To observe/cancel-observe LwM2M client, send following MQTT PUBLISH:** - **Method:** PUBLISH - **Topic:** `lwm2m/{?EndpointName}/dn` - **Request Payload**: ```json { "reqID": {?ReqID}, "msgType": {?MsgType}, "data": { "path": {?ResourcePath} } } ``` - {?ResourcePath}: String, the LwM2M resource to be observed/cancel-observed. - {?MsgType}: String, can be: - "observe": LwM2M Observe - "cancel-observe": LwM2M Cancel Observe - {?ReqID}: Integer, request-id, is the {?ReqID} in the request - **Responses will be converted to following MQTT message:** - **Method:** PUBLISH - **Topic:** `lwm2m/{?EndpointName}/up/resp` - **Response Payload**: ```json { "reqID": {?ReqID}, "msgType": {?MsgType}, "data": { "code": {?StatusCode}, "codeMsg": {?CodeMsg}, "reqPath": {?RequestPath}, "content": [ { "path": {?ResourcePath}, "value": {?Value} } ] } } ``` - {?MsgType}: String, can be: - "observe": LwM2M Observe - "cancel-observe": LwM2M Cancel Observe - **"ack"**: [CoAP Empty ACK](https://tools.ietf.org/html/rfc7252#section-5.2.2) ### Notification (Information Reporting Interface - Notify) - **The notifications from LwM2M clients will be converted to MQTT PUBLISH:** - **Method:** PUBLISH - **Topic:** `lwm2m/{?EndpiontName}/up/notify` - **Notification Payload**: ```json { "reqID": {?ReqID}, "msgType": {?MsgType}, "seqNum": {?ObserveSeqNum}, "data": { "code": {?StatusCode}, "codeMsg": {?CodeMsg}, "reqPath": {?RequestPath}, "content": [ { "path": {?ResourcePath}, "value": {?Value} } ] } } ``` - {?MsgType}: String, must be "notify" - {?ObserveSeqNum}: Number, value of "Observe" option in CoAP message - "content": same to the "content" field contains in the response of "read" command ## Feature limitations - emqx_lwm2m implements LwM2M gateway to EMQX, not a full-featured and independent LwM2M server. - emqx_lwm2m does not include LwM2M bootstrap server. - emqx_lwm2m supports UDP binding, no SMS binding yet. - Firmware object is not fully supported now since mqtt to coap block-wise transfer is not available. - Object Versioning is not supported now. ## DTLS emqx-lwm2m support DTLS to secure UDP data. Please config lwm2m.certfile and lwm2m.keyfile in emqx_lwm2m.conf. If certfile or keyfile are invalid, DTLS will be turned off and you could read a error message in the log. ## License Apache License Version 2.0 ## Author EMQX-Men Team.