diff --git a/examples/wakaama/Makefile b/examples/wakaama/Makefile new file mode 100644 index 000000000000..f212634638df --- /dev/null +++ b/examples/wakaama/Makefile @@ -0,0 +1,116 @@ +# name of your application +APPLICATION = wakaama + +# If no BOARD is found in the environment, use this default: +BOARD ?= native + +# This has to be the absolute path to the RIOT base directory: +RIOTBASE ?= $(CURDIR)/../.. + +# TinyDTLS only has support for 32-bit architectures ATM +BOARD_BLACKLIST := arduino-duemilanove arduino-mega2560 arduino-uno chronos \ + msb-430 msb-430h telosb waspmote-pro wsn430-v1_3b wsn430-v1_4 \ + z1 + +BOARD_INSUFFICIENT_MEMORY := airfy-beacon b-l072z-lrwan1 bluepill calliope-mini \ + cc2650-launchpad cc2650stk maple-mini \ + microbit nrf51dongle nrf6310 nucleo32-f031 \ + nucleo32-f042 nucleo32-f303 nucleo32-l031 nucleo-f030 \ + nucleo-f070 nucleo-f072 nucleo-f103 nucleo-f302 nucleo-f334 \ + nucleo-l053 nucleo-l073 opencm904 \ + spark-core stm32f0discovery yunjia-nrf51822 + + +# Include packages that pull up and auto-init the link layer. +# NOTE: 6LoWPAN will be included if IEEE802.15.4 devices are present +USEMODULE += gnrc_netdev_default +USEMODULE += auto_init_gnrc_netif +# Specify the mandatory networking modules for IPv6 and sUDP +USEMODULE += gnrc_ipv6_router_default +USEMODULE += gnrc_sock_udp +# Additional networking modules that can be dropped if not needed +USEMODULE += gnrc_icmpv6_echo +# Add also the shell, some shell commands +USEMODULE += shell +USEMODULE += shell_commands +USEMODULE += ps + +# NOTE: Required by object_float_sensor.c +USEMODULE += saul_reg +# NOTE: Uncomment to automatically add any supported devices to the lwm2m client +#USEMODULE += saul_default +# NOTE: Uncomment to automatically add all GPIO pins from the SAUL registry to the lwm2m client +#USEMODULE += saul_gpio + +# NOTE: Load module for memory allocator +USEMODULE += memarray + +# Comment this out to disable code in RIOT that does safety checking +# which is not needed in a production environment but helps in the +# development process: +DEVELHELP ?= 1 + +# Specific the server URI address (NOTE: Domain names not supported yet) +SERVER_URI ?= '"coap://[fd00:dead:beef::1]"' + +ifneq (,$(SERVER_URI)) + CFLAGS += -DLWM2M_SERVER_URI=$(SERVER_URI) +endif + +ifneq (,$(findstring coaps,$(SERVER_URI))) + $(info Enabling tinyDTLS) + # NOTE: Add the package for TinyDTLS + USEPKG += tinydtls + + # (temporary fix) TinyDTLS <= 0.8.6 requires around 426 bytes in RAM. + CFLAGS += -DTHREAD_STACKSIZE_MAIN=\(3*THREAD_STACKSIZE_DEFAULT\) + + #TinyDTLs (crypto.c) made use of pthread + ifneq ($(BOARD),native) + USEMODULE += pthread + endif + + # NOTE: Those are taken from TinyDTLS. As the original Makefiles are + # overwitten is a good idea to preserve them here. + CFLAGS += -DDTLSv12 -DWITH_SHA256 + + # NOTE: This adds support for TLS_PSK_WITH_AES_128_CCM_8 + CFLAGS += -DDTLS_PSK + + # NOTE: Without ECC, this defaults to 100 for RIOT which is not enough. + CFLAGS += -DDTLS_MAX_BUF=200 + + # NOTE: The configuration for socket or non-socket communication in TinyDTLS. + # (This can be removed once PR #7615 is merged) + CFLAGS += -DWITH_RIOT_GNRC + + # NOTE: This adds support for TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8; UNTESTED!! + #CFLAGS += -DDTLS_ECC + + # NOTE: If enabled TinyDTLS' log are disabled (if memory is a issue). + # WARNING: Sometimes the log leads to Stack pointer corrupted. + # The reason is not identified yet. + # If said issue appears, enable this line. + #CFLAGS += -DNDEBUG + + # NOTE: Set the TinyDTLS log level + # (see lwm2mconfig.h or log_t in dtls_debug.h in the TinyDTLS source + # tree for possible values.) + #CFLAGS += -DDTLS_LOG_LEVEL=DTLS_LOG_DEBUG +else + # Any special variable required to take TinyDTLS place. +endif + +# NOTE: Add the package for wakaama +USEPKG += wakaama + +# NOTE: Enabled Wakaama debug log +#CFLAGS += -DLWM2M_WITH_LOGS + +# NOTE: Use wakaama in client mode with bootstrapping enabled +CFLAGS += -DLWM2M_CLIENT_MODE -DLWM2M_BOOTSTRAP + +# Change this to 0 show compiler invocation lines by default: +QUIET ?= 1 + +include $(RIOTBASE)/Makefile.include diff --git a/examples/wakaama/README.md b/examples/wakaama/README.md new file mode 100644 index 000000000000..12797a8cc317 --- /dev/null +++ b/examples/wakaama/README.md @@ -0,0 +1,204 @@ +Eclipse Wakaama Example Client +============================== + +This application is a port of the Eclipse Wakaama [example client +code](https://github.com/eclipse/wakaama/tree/master/examples/client) +to RIOT/GNRC. + +Usage +===== + +Edit the defines in `lwm2mconfig.h` according to your setup (see the +comments for what they do). + +By setting `LWM2M_SERVER_PSK_ID` and `LWM2M_SERVER_PSK_KEY` to NULL in +`lwm2mconfig.h`, CoAP will be used for communication instead of CoAPS. + +Build, flash and start the application: +``` +export BOARD=your_board +make all flash term +``` + +(Or Skip `make flash` if you're building for `BOARD=native`.) + +The `term` make target starts a terminal emulator for your board. It +connects to a default port so you can interact with the shell, usually +that is `/dev/ttyUSB0`. If your port is named differently, the +`PORT=/dev/yourport` (not to be confused with the UDP port) variable can +be used to override this. + + +Available LwM2M Objects +======================= + +At this time, only the mandatory objects for a client are present. The +*server*, *security* and *access control* objects are taken as is from +the example client in the Wakaama source tree. The *device* object was +re-implemented for RIOT (see `object_device.c`). + +Adding more objects should be as easy as implmenting a "contructor" +and then incrementing the `OBJ_COUNT` in `lwm2mclient.c` and extending +`run_server()` to call the constructor and adding the instance(s) to +`objArray`. + + +LwM2M Test Server +================= + +[Eclipse Leshan](https://github.com/eclipse/leshan#test-leshan-demos-locally) +is good for local testing. Just run it on the link local address of +the bridge interface created by `dist/tools/tapsetup/tapsetup` and add +the credentials set in `lwm2mconfig.h` via the web interface. Since the +example bootstrap server doesn't seem to support setting DTLS credentials +for the server itself via the web interface, you'll have to create a +new client config, edit `data/bootstrap.json` and set `securityMode`, +`publicKeyOrId`, `secretKey` and maybe `serverPublicKey` and restart +the server. + +The LwM2M demo server supports setting DTLS credentials on the *Security* +tab. + +Example (Linux): +``` +wget https://hudson.eclipse.org/leshan/job/leshan/lastSuccessfulBuild/artifact/leshan-server-demo.jar + +ADDR=$(ip address show dev tapbr0 | grep inet6 |\ + head -1 | awk '{print $2}' | cut -d'/' -f1) + +java -jar ./leshan-server-demo.jar \ + --coapshost ${ADDR} \ + --coaphost ${ADDR} +``` + +Example bootstrap.json +---------------------- + +An example `bootstrap.json` file for the Leshan bootstrap server can be +found in data `data/`. + +`testRIOTDevice.servers` contains a list of LwM2M +server objects that should be installed on the client +*testRIOTDevice*. `testRIOTDevice.servers.*.shortId` must match a +corresponding `testRIOTDevice.security.*.shortId`. + +`testRIOTDevice.security` contains a list of LwM2M +security objects. `testRIOTDevice.security.0` in this +example specifies the bootstrap server itself and its +DTLS PSK. `testRIOTDevice.security.0.publicKeyOrId` and +`testRIOTDevice.security.0.secretKey` in this case are equivalent to +`fooTestRIOT` and `39fe6611deb7713c6069`. + +`testRIOTDevice.security.1` matches the server to install on the +client. As mentioned, it's `shortId` must match the corresponding entry +in `testRIOTDevice.servers`. For simplicity's sake its PSK is identical +to the bootstrap server. + + +Example output +============== + +For this example, set: +* `ENABLE_DEBUG=(1)` in `lwm2mconfig.h` +* `CFLAGS += -DLWM2M_WITH_LOGS` in `Makefile` +* `CFLAGS += -DDTLS_LOG_LEVEL=DTLS_LOG_CRIT` in `Makefile` + +When registering directly to an LwM2M server, with DTLS, without +boostrapping, this should output something similar to this: +``` +main(): This is RIOT! (Version: 2018.01-devel-943-g987f6-calamity-track1/wakaama-integration) +[lwm2m_init:64] Entering +[lwm2m_configure:264] endpointName: "testRIOTDevice", msisdn: "(null)", altPath: "(null)", numObject: 4 + -> State: STATE_INITIAL +[lwm2m_step:372] timeoutP: -616140018120916932 +[lwm2m_step:377] State: STATE_INITIAL +[object_getServers:741] Entering +[lwm2m_data_new:143] size: 3 +[lwm2m_data_encode_bool:406] value: false +[lwm2m_data_encode_int:270] value: 10 +[lwm2m_data_encode_int:270] value: 10 +[lwm2m_data_decode_bool:416] Entering +[lwm2m_data_decode_bool:467] result: 1, value: false +[lwm2m_data_decode_int:280] Entering +[lwm2m_data_decode_int:335] result: 1, value: 10 +[lwm2m_data_new:143] size: 1 +[lwm2m_data_encode_int:270] value: 10 +[lwm2m_data_decode_int:280] Entering +[lwm2m_data_decode_int:335] result: 1, value: 10 +[lwm2m_data_free:161] size: 1 +[lwm2m_data_new:143] size: 2 +[lwm2m_data_encode_int:270] value: 300 +[lwm2m_data_encode_string:195] "U" +[lwm2m_data_decode_int:280] Entering +[lwm2m_data_decode_int:335] result: 1, value: 300 +[lwm2m_data_free:161] size: 2 +[lwm2m_data_free:161] size: 3 +[registration_start:477] State: STATE_REGISTER_REQUIRED +[object_getRegisterPayloadBufferLength:508] Entering +[object_getRegisterPayload:579] Entering +[lwm2m_data_new:143] size: 1 +[lwm2m_data_encode_string:195] "coaps://[fe80::585d:b5ff:fe1e:75]" +[lwm2m_data_free:161] size: 1 +[lwm2m_data_new:143] size: 1 +[lwm2m_data_encode_int:270] value: 0 +[lwm2m_data_decode_int:280] Entering +[lwm2m_data_decode_int:335] result: 1, value: 0 +[lwm2m_data_free:161] size: 1 +[transaction_new:156] method: 2, altPath: "(null)", mID: 30050, token_len: 4 +[transaction_new:157] NULL +[transaction_new:235] Exiting on success +[transaction_send:351] Entering +Success: started DTLS server on port 61618 +> [observe_step:482] Entering +[registration_step:1303] State: STATE_REGISTERING +[transaction_step:433] Entering +[lwm2m_step:480] Final timeoutP: -616140018120916990 +[lwm2m_step:482] Final state: STATE_REGISTERING + -> State: STATE_REGISTERING +[lwm2m_data_new:143] size: 1 +[lwm2m_data_encode_opaque:232] length: 11 +[lwm2m_data_free:161] size: 1 +[lwm2m_data_new:143] size: 1 +[lwm2m_data_encode_opaque:232] length: 10 +[lwm2m_data_free:161] size: 1 +decrypt_verify(): found 24 bytes cleartext + -> State: STATE_REGISTERING +[lwm2m_step:372] timeoutP: 679896346613776444 +[lwm2m_step:377] State: STATE_REGISTERING +[registration_getStatus:506] State: STATE_REGISTERING +[registration_getStatus:513] targetP->status: STATE_REG_PENDING +[registration_getStatus:536] reg_status: STATE_REG_PENDING +[observe_step:482] Entering +[registration_step:1303] State: STATE_REGISTERING +[transaction_step:433] Entering +[transaction_send:351] Entering +[lwm2m_step:480] Final timeoutP: 679896346613776387 +[lwm2m_step:482] Final state: STATE_REGISTERING + -> State: STATE_REGISTERING +decrypt_verify(): found 22 bytes cleartext +[lwm2m_handle_packet:214] Entering +[lwm2m_handle_packet:219] Parsed: ver 1, type 2, tkl 4, code 2.01, mid 30050, Content type: 0 +[lwm2m_handle_packet:220] Payload: +[transaction_handleResponse:274] Entering +[prv_handleRegistrationReply:212] Registration successful +[transaction_remove:260] Entering +[transaction_free:246] Entering + -> State: STATE_REGISTERING +[lwm2m_step:372] timeoutP: 679896346613776444 +[lwm2m_step:377] State: STATE_REGISTERING +[registration_getStatus:506] State: STATE_REGISTERING +[registration_getStatus:513] targetP->status: STATE_REGISTERED +[registration_getStatus:536] reg_status: STATE_REGISTERED +[observe_step:482] Entering +[registration_step:1303] State: STATE_READY +[transaction_step:433] Entering +[lwm2m_step:480] Final timeoutP: 679896346613776444 +[lwm2m_step:482] Final state: STATE_READY +``` + + +Limitations +=========== + +* The host part of any URI **MUST** be a valid IPv6 address, as the + client can't resolve host names at this time. diff --git a/examples/wakaama/connection.c b/examples/wakaama/connection.c new file mode 100644 index 000000000000..95cf40c0cd7e --- /dev/null +++ b/examples/wakaama/connection.c @@ -0,0 +1,620 @@ +/******************************************************************************* +* +* Copyright (c) 2015 Intel Corporation and others. +* All rights reserved. This program and the accompanying materials +* are made available under the terms of the Eclipse Public License v1.0 +* and Eclipse Distribution License v1.0 which accompany this distribution. +* +* The Eclipse Public License is available at +* http://www.eclipse.org/legal/epl-v10.html +* The Eclipse Distribution License is available at +* http://www.eclipse.org/org/documents/edl-v10.php. +* +* Contributors: +* David Navarro, Intel Corporation - initial API and implementation +* Christian Renz - Please refer to git log +* Christian Manal - Ported to RIOT OS +* +*******************************************************************************/ + +/** + * @ingroup examples + * @{ + * + * @file + * @brief Handles DTLS/networking of the Wakaama example client + * + * @author Christian Manal + * + * @} + */ + +#include +#include +#include +#include "connection.h" +#include "memarray_limits.h" +#include "xtimer.h" + +/* + * RIOT debugging + */ +#define ENABLE_DEBUG (0) +#include "debug.h" + +#define URI_LENGTH 256 + +#ifdef MODULE_TINYDTLS +static dtls_context_t *dtls_context; +#endif /* MODULE_TINYDTLS */ + +/********************* Security Obj Helpers **********************/ +static char *security_get_uri(lwm2m_object_t *obj, int instance_id, char *uri_buffer, + int buffer_size) +{ + int size = 1; + lwm2m_data_t *data = lwm2m_data_new(size); + + data->id = 0; /* security server uri */ + + obj->readFunc(instance_id, &size, &data, obj); + if (data != NULL && data->type == LWM2M_TYPE_STRING && + data->value.asBuffer.length > 0) { + if ((size_t)buffer_size > data->value.asBuffer.length) { + strncpy(uri_buffer, (char *)data->value.asBuffer.buffer, + data->value.asBuffer.length); + lwm2m_data_free(size, data); + return uri_buffer; + } + } + lwm2m_data_free(size, data); + return NULL; +} + +#ifdef MODULE_TINYDTLS +static int64_t security_get_mode(lwm2m_object_t *obj, int instance_id) +{ + int64_t mode; + int size = 1; + lwm2m_data_t *data = lwm2m_data_new(size); + + data->id = 2; /* security mode */ + + obj->readFunc(instance_id, &size, &data, obj); + if (0 != lwm2m_data_decode_int(data, &mode)) { + lwm2m_data_free(size, data); + return mode; + } + + lwm2m_data_free(size, data); + fprintf(stderr, "Unable to get security mode : use not secure mode"); + return LWM2M_SECURITY_MODE_NONE; +} + +static char *security_get_key(lwm2m_object_t *obj, int key_type, int instance_id, int *length) +{ + int size = 1; + + /* + * 3 = Public Key or ID + * 4 = Server Public Key or ID + * 5 = Secret Key + */ + if (key_type < 3 || key_type > 5) { + DEBUG("Invalid key type\n"); + return NULL; + } + + lwm2m_data_t *data = lwm2m_data_new(size); + data->id = key_type; + + obj->readFunc(instance_id, &size, &data, obj); + if (data != NULL && data->type == LWM2M_TYPE_OPAQUE) { + char *buff; + + buff = (char *)lwm2m_malloc(data->value.asBuffer.length); + if (buff != NULL) { + memcpy(buff, data->value.asBuffer.buffer, + data->value.asBuffer.length); + *length = data->value.asBuffer.length; + } + lwm2m_data_free(size, data); + + return buff; + } + else { + return NULL; + } +} +#endif /* MODULE_TINYDTLS */ + +/********************* Security Obj Helpers Ends **********************/ + +static int send_data(lwm2m_connection_t *conn, uint8_t *buffer, size_t length) +{ + ssize_t bytes_sent = sock_udp_send(&(conn->sock), buffer, length, NULL); + + /* TODO Error handling */ + if (bytes_sent <= 0) { + DEBUG("%s: Error code for sock_udp_send %i", __func__, bytes_sent); + return -1; + } + conn->last_send = lwm2m_gettime(); + return 0; +} + +/************************** TinyDTLS Callbacks ************************/ +#ifdef MODULE_TINYDTLS + +#ifdef DTLS_PSK +/* This function is the "key store" for tinyDTLS. It is called to + * retrieve a key for the given identity within this particular + * session. */ +static int get_psk_info(struct dtls_context_t *ctx, const session_t *session, + dtls_credentials_type_t type, const unsigned char *id, + size_t id_len, unsigned char *result, + size_t result_length) +{ + (void)session; + lwm2m_connection_t *conn = (lwm2m_connection_t *)ctx->app; + lwm2m_connection_t *cnx = lwm2m_connection_find((lwm2m_connection_t *)ctx->app, + &(conn->sock.remote)); + + (void)id; + (void)id_len; + + if (cnx == NULL) { + printf("GET PSK session not found\n"); + return dtls_alert_fatal_create(DTLS_ALERT_INTERNAL_ERROR); + } + + switch (type) { + case DTLS_PSK_IDENTITY: { + int idLen; + char *id; + id = security_get_key(cnx->security_obj, 3, cnx->security_inst_id, + &idLen); + if (result_length < (size_t)idLen) { + printf("cannot set psk_identity -- buffer too small\n"); + return dtls_alert_fatal_create(DTLS_ALERT_INTERNAL_ERROR); + } + + memcpy(result, id, idLen); + lwm2m_free(id); + return idLen; + } + case DTLS_PSK_KEY: { + int keyLen; + char *key; + key = security_get_key(cnx->security_obj, 5, cnx->security_inst_id, + &keyLen); + + if (result_length < (size_t)keyLen) { + printf("cannot set psk -- buffer too small\n"); + return dtls_alert_fatal_create(DTLS_ALERT_INTERNAL_ERROR); + } + + memcpy(result, key, keyLen); + lwm2m_free(key); + return keyLen; + } + default: + printf("unsupported request type: %d\n", type); + } + + return dtls_alert_fatal_create(DTLS_ALERT_INTERNAL_ERROR); +} +#endif /* DTLS_PSK */ + +#ifdef DTLS_ECC +static int get_ecdsa_key(struct dtls_context_t *ctx, const session_t *session, + const dtls_ecdsa_key_t **result) +{ + int keyLen; + + lwm2m_connection_t *conn = (lwm2m_connection_t *)ctx->app; + lwm2m_connection_t *cnx = lwm2m_connection_find((lwm2m_connection_t *)ctx->app, + &(conn->sock.remote)); + + if (cnx == NULL) { + printf("GET ECDSA KEY session not found\n"); + return dtls_alert_fatal_create(DTLS_ALERT_INTERNAL_ERROR); + } + + dtls_ecdsa_key_t *res = lwm2m_malloc(sizeof(dtls_ecdsa_key_t)); + if (res == NULL) { + printf("malloc() error\n"); + return dtls_alert_fatal_create(DTLS_ALERT_INTERNAL_ERROR); + } + + res->priv_key = (const unsigned char *)security_get_key( + cnx->security_obj, 3, cnx->security_inst_id, &keyLen); + + res->pub_key_x = (const unsigned char *)security_get_key( + cnx->security_obj, 4, cnx->security_inst_id, &keyLen); + + res->pub_key_y = (const unsigned char *)security_get_key( + cnx->security_obj, 5, cnx->security_inst_id, &keyLen); + + *result = res; + + return 0; +} + +/* TODO: Actually verify keys... */ +static int verify_ecdsa_key(struct dtls_context_t *ctx, + const session_t *session, + const unsigned char *other_pub_x, + const unsigned char *other_pub_y, + size_t key_size) +{ + (void)ctx; + (void)session; + (void)other_pub_x; + (void)other_pub_y; + (void)key_size; + return 0; +} +#endif /* DTLS_ECC */ + + +static int send_to_peer(struct dtls_context_t *ctx, session_t *session, + uint8 *data, size_t len) +{ + (void)session; + lwm2m_connection_t *conn = (lwm2m_connection_t *)ctx->app; + lwm2m_connection_t *cnx = lwm2m_connection_find((lwm2m_connection_t *)ctx->app, + &(conn->sock.remote)); + DEBUG("send_to_peer(%d)\n", len); + if (cnx != NULL) { + int err = send_data(cnx, data, len); + if (COAP_NO_ERROR != err) { + return -1; + } + return len; + } + return -1; +} + +static int read_from_peer(struct dtls_context_t *ctx, session_t *session, + uint8 *data, size_t len) +{ + (void)session; + DEBUG("read_from_peer(%d)\n", len); + lwm2m_connection_t *conn = (lwm2m_connection_t *)ctx->app; + lwm2m_connection_t *cnx = lwm2m_connection_find((lwm2m_connection_t *)ctx->app, + &(conn->sock.remote)); + if (cnx != NULL) { + lwm2m_handle_packet(cnx->lwm2mH, (uint8_t *)data, len, (void *)cnx); + return 0; + } + return -1; +} +/************************** TinyDTLS Callbacks Ends ************************/ + +static dtls_handler_t cb = { + .write = send_to_peer, + .read = read_from_peer, + .event = NULL, + #ifdef DTLS_PSK + .get_psk_info = get_psk_info, + #endif /* DTLS_PSK */ + #ifdef DTLS_ECC + .get_ecdsa_key = get_ecdsa_key, + .verify_ecdsa_key = verify_ecdsa_key + #endif /* DTLS_ECC */ +}; + +static dtls_context_t *get_dtls_context(lwm2m_connection_t *conn_list) +{ + if (dtls_context == NULL) { + dtls_context = dtls_new_context(conn_list); + if (dtls_context == NULL) { + fprintf(stderr, "Failed to create the DTLS context\r\n"); + } + dtls_set_handler(dtls_context, &cb); + } + else { + dtls_context->app = conn_list; + } + return dtls_context; +} +#endif /* MODULE_TINYDTLS */ + +lwm2m_connection_t *lwm2m_connection_find(lwm2m_connection_t *conn_list, + const sock_udp_ep_t *remote) +{ + lwm2m_connection_t *conn; + + conn = conn_list; + while (conn != NULL) { + if (conn->sock.remote.port == remote->port && + memcmp(&conn->sock.remote.addr.ipv6, &remote->addr.ipv6, sizeof(remote->addr.ipv6)) == 0) { + return conn; + } + + conn = conn->next; + } + + return conn; +} + +static lwm2m_connection_t *connection_new_incoming(lwm2m_connection_t *conn_list, + const sock_udp_ep_t *remote) +{ + lwm2m_connection_t *conn; + sock_udp_ep_t local = SOCK_IPV6_EP_ANY; + ssize_t res; + + conn = lwm2m_malloc(sizeof(lwm2m_connection_t)); + if (conn != NULL) { + memset(conn, 0, sizeof(lwm2m_connection_t)); + conn->next = conn_list; + + local.port = LWM2M_DTLS_PORT; + res = sock_udp_create(&(conn->sock), &local, remote, 0); + if (res < 0) { + fprintf(stderr, "ERROR: sock_udp_create failed\n"); + goto free_conn; + } + conn->last_send = lwm2m_gettime(); + +#ifdef MODULE_TINYDTLS + conn->no_sec = false; + conn->dtls_session = lwm2m_malloc(sizeof(session_t)); + memset(conn->dtls_session, 0, sizeof(session_t)); + if (memcpy(&(conn->dtls_session->addr), remote->addr.ipv6, (sizeof(uint8_t) * 16)) == NULL) { + fprintf(stderr, "ERROR: memcpu failed\n"); + goto free_conn; + } + conn->dtls_session->size = sizeof(uint8_t) * 16 + sizeof(unsigned short); + conn->dtls_session->port = remote->port; +#endif /* MODULE_TINYDTLS */ + } + + return conn; + +free_conn: + lwm2m_free(conn); + return NULL; +} + +lwm2m_connection_t *lwm2m_connection_create(lwm2m_connection_t *conn_list, + lwm2m_object_t *security_obj, + int instance_id, lwm2m_context_t *lwm2mH) +{ + lwm2m_connection_t *conn = NULL; + char uri_buf[URI_LENGTH+1]; + char *uri; + char *host; + char *tmp; + char *port; + char *defaultport; + sock_udp_ep_t remote = SOCK_IPV6_EP_ANY; + + memset(uri_buf, 0, URI_LENGTH+1); + + uri = security_get_uri(security_obj, instance_id, uri_buf, URI_LENGTH); + if (uri == NULL) { + fprintf(stderr, "lwm2m_connection_create(): uri == NULL\n"); + return NULL; + } + + /* parse uri in the form "coaps://[host]:[port]" */ + if (0 == strncmp(uri, "coaps://", strlen("coaps://"))) { + host = uri + strlen("coaps://"); + defaultport = LWM2M_DTLS_PORT_STR; + } + else if (0 == strncmp(uri, "coap://", strlen("coap://"))) { + host = uri + strlen("coap://"); + defaultport = LWM2M_STANDARD_PORT_STR; + } + else { + fprintf(stderr, "lwm2m_connection_create(): Invalid protocol in URI\n"); + return NULL; + } + + tmp = host; + DEBUG("lwm2m_connection_create(): getting port from %s\n", host); + if (tmp[0] == '[') { + DEBUG("lwm2m_connection_create(): getting port: IPv6 address\n"); + host++; + tmp = strrchr(tmp, ']'); + } + + port = strrchr(tmp, ':'); + if (port == NULL) { + *tmp = '\0'; + DEBUG("lwm2m_connection_create(): using defaultport %s\n", + defaultport); + port = defaultport; + } + else { + *(port - 1) = '\0'; + port++; + } + + DEBUG("lwm2m_connection_create(): ipv6_addr_from_str(&addr, %s)\n", host); + if (ipv6_addr_from_str((ipv6_addr_t *)&remote.addr, host) == NULL) { + fprintf(stderr, "lwm2m_connection_create(): IPv6 address malformed\n"); + return NULL; + } + + if (ipv6_addr_is_unspecified((const ipv6_addr_t *)&remote.addr)) { + fprintf(stderr, "lwm2m_connection_create(): Invalid remote address ([::])\n"); + return NULL; + } + + remote.port = atoi(port); + + /* If the address is a link-local one and we only have one netif, default to that. + * Otherwise throw an error, so we don't perma-clog the send queue. + */ + if (ipv6_addr_is_link_local((ipv6_addr_t *)&remote.addr.ipv6)) { + if (gnrc_netif_numof() == 1) { + gnrc_netif_t *netif = gnrc_netif_iter(NULL); + remote.netif = netif->pid; + } + else { + fprintf(stderr, "lwm2m_connection_create(): Can't use link-local address without interface number\n"); + } + } + + DEBUG("lwm2m_connection_create(): connection_new_incoming(%s, %d)\n", host, remote.port); + conn = connection_new_incoming(conn_list, &remote); + + /* do we need to start tinydtls? */ + if (conn != NULL) { + conn->lwm2mH = lwm2mH; +#ifdef MODULE_TINYDTLS + conn->security_obj = security_obj; + conn->security_inst_id = instance_id; + + if (security_get_mode(conn->security_obj, conn->security_inst_id) != + LWM2M_SECURITY_MODE_NONE) { + conn->dtls_context = get_dtls_context(conn); + } + else { + /* no dtls session */ + conn->no_sec = true; + } +#endif /* MODULE_TINYDTLS */ + } + + return conn; +} + +void lwm2m_connection_free(lwm2m_connection_t *conn_list) +{ +#ifdef MODULE_TINYDTLS + dtls_free_context(dtls_context); + dtls_context = NULL; +#endif /* MODULE_TINYDTLS */ + while (conn_list != NULL) { + lwm2m_connection_t *next; + + next = conn_list->next; + sock_udp_close(&conn_list->sock); +#ifdef MODULE_TINYDTLS + lwm2m_free(conn_list->dtls_session); +#endif /* MODULE_TINYDTLS */ + lwm2m_free(conn_list); + + conn_list = next; + } +} + +static int connection_send(lwm2m_connection_t *conn, uint8_t *buffer, size_t length) +{ + DEBUG("connection_send(%d)\n", length); +#ifdef MODULE_TINYDTLS + if (conn->no_sec) { +#endif /* MODULE_TINYDTLS */ + /* no security */ + if (0 != send_data(conn, buffer, length)) { + return -1; + } +#ifdef MODULE_TINYDTLS + } + else { + ssize_t bw = 0; + if (DTLS_NAT_TIMEOUT > 0 && + (lwm2m_gettime() - conn->last_send) > DTLS_NAT_TIMEOUT) { + /* we need to rehandhake because our source IP/port probably changed + * for the server + */ + if (lwm2m_connection_rehandshake(conn, true) != 0) { + printf("can't send due to rehandshake error\n"); + return -1; + } + } + DEBUG("dtls_write(%d)\n", length); + bw = dtls_write(conn->dtls_context, conn->dtls_session, buffer, length); + if (bw <= 0) { + DEBUG("dtls_write() failed (%d)\n", bw); + return -1; + } + DEBUG("dtls_write() wrote %d bytes\n", bw); + } +#endif /* MODULE_TINYDTLS */ + + return 0; +} + +int lwm2m_connection_handle_packet(lwm2m_connection_t *conn, uint8_t *buffer, + size_t num_bytes) +{ +#ifdef MODULE_TINYDTLS + if (!conn->no_sec) { + /* Let liblwm2m respond to the query depending on the context */ + DEBUG("lwm2m_connection_handle_packet() -> dtls_handle_message()\n"); + int result = dtls_handle_message(conn->dtls_context, conn->dtls_session, + buffer, num_bytes); + if (result != 0) { + printf("error dtls handling message %d\n", result); + } + return result; + } + else { +#endif /* MODULE_TINYDTLS */ + /* no security, just give the plaintext buffer to liblwm2m */ + DEBUG("lwm2m_connection_handle_packet() -> lwm2m_handle_packet()\n"); + lwm2m_handle_packet(conn->lwm2mH, buffer, num_bytes, (void *)conn); + return 0; +#ifdef MODULE_TINYDTLS +} +#endif /* MODULE_TINYDTLS */ +} + +#ifdef MODULE_TINYDTLS +int lwm2m_connection_rehandshake(lwm2m_connection_t *conn, bool send_close_notify) +{ + /* if not a dtls connection we do nothing */ + if (conn->no_sec) { + return 0; + } + + /* reset current session */ + dtls_peer_t *peer = dtls_get_peer(conn->dtls_context, conn->dtls_session); + if (peer != NULL) { + if (!send_close_notify) { + peer->state = DTLS_STATE_CLOSED; + } + dtls_reset_peer(conn->dtls_context, peer); + } + + /* start a fresh handshake */ + int result = dtls_connect(conn->dtls_context, conn->dtls_session); + if (result != 0) { + printf("error dtls reconnection %d\n", result); + } + return result; +} +#endif /* MODULE_TINYDTLS */ + +uint8_t lwm2m_buffer_send(void *sessionH, uint8_t *buffer, size_t length, + void *userdata) +{ + lwm2m_connection_t *conn = (lwm2m_connection_t *)sessionH; + + (void)userdata; + + if (conn == NULL) { + fprintf(stderr, "#> failed sending %zu bytes, missing connection\r\n", + length); + return COAP_500_INTERNAL_SERVER_ERROR; + } + + if (-1 == connection_send(conn, buffer, length)) { + fprintf(stderr, "#> failed sending %zu bytes\r\n", length); + return COAP_500_INTERNAL_SERVER_ERROR; + } + + return COAP_NO_ERROR; +} + +bool lwm2m_session_is_equal(void *session1, void *session2, void *user_data) +{ + (void)user_data; + return (session1 == session2); +} diff --git a/examples/wakaama/connection.h b/examples/wakaama/connection.h new file mode 100644 index 000000000000..981642982e60 --- /dev/null +++ b/examples/wakaama/connection.h @@ -0,0 +1,148 @@ +/******************************************************************************* +* +* Copyright (c) 2015 Intel Corporation and others. +* All rights reserved. This program and the accompanying materials +* are made available under the terms of the Eclipse Public License v1.0 +* and Eclipse Distribution License v1.0 which accompany this distribution. +* +* The Eclipse Public License is available at +* http://www.eclipse.org/legal/epl-v10.html +* The Eclipse Distribution License is available at +* http://www.eclipse.org/org/documents/edl-v10.php. +* +* Contributors: +* Simon Bernard - initial API and implementation +* Christian Renz - Please refer to git log +* Christian Manal - Ported to RIOT OS +* +*******************************************************************************/ + +/** + * @ingroup examples + * @{ + * + * @file + * @brief Header for Wakaama example client DTLS/networking code. + * + * @author Christian Manal + */ + + +#ifndef CONNECTION_H +#define CONNECTION_H + +#include +#include +#include +#include +#include +#include + +#include "net/sock/udp.h" +#include "net/ipv6/addr.h" +#include "net/af.h" + +#include "timex.h" +#include "utlist.h" +#include "xtimer.h" + +#include "lwm2mconfig.h" + +#ifdef MODULE_TINYDTLS +#include "tinydtls.h" +#include "dtls_debug.h" +#include "dtls.h" +#endif /* MODULE_TINYDTLS */ + +#include "liblwm2m.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* Seconds of inactivity after which to close and re-open DTLS connections */ +#define DTLS_NAT_TIMEOUT 0 + +typedef struct _lwm2m_connection_t { + struct _lwm2m_connection_t *next; + sock_udp_t sock; + lwm2m_context_t *lwm2mH; +#ifdef MODULE_TINYDTLS + session_t *dtls_session; + lwm2m_object_t *security_obj; + int security_inst_id; + dtls_context_t *dtls_context; + bool no_sec; +#endif /* MODULE_TINYDTLS */ + /* last time a data was sent to the server (used for NAT timeouts) */ + time_t last_send; +} lwm2m_connection_t; + +/** + * @brief Search for an open connection by remote endpoint. + * + * @param[in] conn_list The connection list to search. + * @param[in] remote The remote endpoint to search for. + * + * @return Pointer to connection, if one was found. + * @return NULL, if no connection was found. + */ +lwm2m_connection_t *lwm2m_connection_find(lwm2m_connection_t *conn_list, + const sock_udp_ep_t *remote); + +/** + * @brief Create a new lwm2m connection. + * + * @param[in] conn_list Connection list to add the new connection to. + * @param[in] security_obj The lwm2m security object to get the remote endpoint from. + * @param[in] lwm2mH The lwm2m context to add the connection to. + * + * @return Pointer to connection on success. + * @return NULL on error. + */ +lwm2m_connection_t *lwm2m_connection_create(lwm2m_connection_t *conn_list, + lwm2m_object_t *security_obj, + int instance_id, + lwm2m_context_t *lwm2mH); + +/** + * @brief Release resources of an lwm2m connection. + * + * @param[in] conn_list The connection (list) to free. + */ +void lwm2m_connection_free(lwm2m_connection_t *conn_list); + +/** + * @brief Handles incoming lwm2m network packages. + * + * @param[in] connP The connection the packet came on in. + * @param[in] buffer The packet buffer. + * @param[in] length The length of @p buffer. + * + * @return 0 on success + * @return <0 on error + */ +int lwm2m_connection_handle_packet(lwm2m_connection_t *connP, uint8_t *buffer, + size_t length); + +#ifdef MODULE_TINYDTLS +/** + * @brief Re-handshakes on open DTLS connection. Useful when your NAT + * timed out and your client has a new IP/PORT. + * + * @param[in] connP The connection to re-handshake. + * @param[in] send_close_notify Set to true if DTLS should send a close + * notification before re-handshaking. + * + * @return 0 on success + * @return <0 on error + */ +int lwm2m_connection_rehandshake(lwm2m_connection_t *connP, bool send_close_notify); +#endif /* MODULE_TINYDTLS */ + +#ifdef __cplusplus +} +#endif + +/** @} */ +#endif /* CONNECTION_H */ diff --git a/examples/wakaama/data/bootstrap.json b/examples/wakaama/data/bootstrap.json new file mode 100644 index 000000000000..4103132535b3 --- /dev/null +++ b/examples/wakaama/data/bootstrap.json @@ -0,0 +1,97 @@ +{ + "testRIOTDevice": { + "servers": { + "0": { + "shortId": 10, + "lifetime": 20, + "defaultMinPeriod": 1, + "notifIfDisabled": true, + "binding": "U" + } + }, + "security": { + "0": { + "uri": "coaps://localhost:5685", + "bootstrapServer": true, + "securityMode": "PSK", + "publicKeyOrId": [ + 67, + 108, + 105, + 101, + 110, + 116, + 95, + 105, + 100, + 101, + 110, + 116, + 105, + 116, + 121 + ], + "serverPublicKey": [], + "secretKey": [ + 115, + 101, + 99, + 114, + 101, + 116, + 80, + 83, + 75 + ], + "smsSecurityMode": "NO_SEC", + "smsBindingKeyParam": [], + "smsBindingKeySecret": [], + "serverSmsNumber": "", + "serverId": 111, + "clientOldOffTime": 1, + "bootstrapServerAccountTimeout": 0 + }, + "1": { + "uri": "coaps://[fd00:dead:beef::1]", + "bootstrapServer": false, + "securityMode": "PSK", + "publicKeyOrId": [ + 67, + 108, + 105, + 101, + 110, + 116, + 95, + 105, + 100, + 101, + 110, + 116, + 105, + 116, + 121 + ], + "serverPublicKey": [], + "secretKey": [ + 115, + 101, + 99, + 114, + 101, + 116, + 80, + 83, + 75 + ], + "smsSecurityMode": "NO_SEC", + "smsBindingKeyParam": [], + "smsBindingKeySecret": [], + "serverSmsNumber": "", + "serverId": 10, + "clientOldOffTime": 1, + "bootstrapServerAccountTimeout": 0 + } + } + } +} diff --git a/examples/wakaama/lwm2mclient.c b/examples/wakaama/lwm2mclient.c new file mode 100644 index 000000000000..b3daf58a0421 --- /dev/null +++ b/examples/wakaama/lwm2mclient.c @@ -0,0 +1,597 @@ +/******************************************************************************* +* +* Copyright (c) 2013, 2014 Intel Corporation and others. +* All rights reserved. This program and the accompanying materials +* are made available under the terms of the Eclipse Public License v1.0 +* and Eclipse Distribution License v1.0 which accompany this distribution. +* +* The Eclipse Public License is available at +* http://www.eclipse.org/legal/epl-v10.html +* The Eclipse Distribution License is available at +* http://www.eclipse.org/org/documents/edl-v10.php. +* +* Contributors: +* David Navarro, Intel Corporation - initial API and implementation +* Benjamin Cabé - Please refer to git log +* Fabien Fleutot - Please refer to git log +* Simon Bernard - Please refer to git log +* Julien Vermillard - Please refer to git log +* Axel Lorente - Please refer to git log +* Toby Jaffey - Please refer to git log +* Bosch Software Innovations GmbH - Please refer to git log +* Pascal Rieux - Please refer to git log +* Christian Renz - Please refer to git log +* Ricky Liu - Please refer to git log +* Christian Manal - Ported to RIOT OS +* +*******************************************************************************/ + +/* + Copyright (c) 2013, 2014 Intel Corporation + + Redistribution and use in source and binary forms, with or without + modification, + are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + * Neither the name of Intel Corporation nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. + IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + OR + OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + OF + THE POSSIBILITY OF SUCH DAMAGE. + + David Navarro + Bosch Software Innovations GmbH - Please refer to git log + + */ + +/** + * @ingroup examples + * @{ + * + * @file + * @brief Main loop for Wakaama example client. + * + * @author Christian Manal + * + * @} + */ + +#include "memarray_limits.h" + +#include "connection.h" +#include "liblwm2m.h" +#include "lwm2mclient.h" +#include "lwm2mconfig.h" + +#include "periph/pm.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * RIOT debugging + */ +#define ENABLE_DEBUG (0) +#include "debug.h" + +#define MAX_PACKET_SIZE 1024 +#define DEFAULT_SERVER_IPV6 "[::1]" +#define DEFAULT_SERVER_IPV4 "127.0.0.1" + +/* Taken from examples/dtls-echo/dtls-server.c */ +#define READER_QUEUE_SIZE (8U) + +#ifdef MODULE_TINYDTLS +#define RCVD_SIZE DTLS_MAX_BUF +#else /* MODULE_TINYDTLS */ +#define RCVD_SIZE 200 +#endif /* MODULE_TINYDTLS */ + +/* Time in seconds to reboot after from server request */ +#define REBOOT_TIME 5 + +/* Time in seconds to wait for a server response. + * Also controls the main loop max interval, + * which might be relevant for sensor polling. + */ +#define RESPONSE_TIMEOUT 20; + +static int g_quit = 0; + +#define OBJ_COUNT 4 +lwm2m_object_t *obj_array[OBJ_COUNT]; + +/* only backup security and server objects */ +#define BACKUP_OBJECT_COUNT 2 +lwm2m_object_t *backup_object_array[BACKUP_OBJECT_COUNT]; + +typedef struct { + lwm2m_object_t *security_obj; + lwm2m_object_t *server_object; + sock_udp_t sock; + lwm2m_connection_t *conn_list; + lwm2m_context_t *lwm2mH; + int address_family; +} client_data_t; + +#ifdef BOARD_NATIVE +void handle_sigint(int signum) +{ + (void)signum; + exit(0); +} +#endif /* BOARD_NATIVE */ + +void *lwm2m_connect_server(uint16_t sec_obj_inst_id, void *user_data) +{ + client_data_t *data; + lwm2m_list_t *instance; + lwm2m_connection_t *new_conn = NULL; + + data = (client_data_t *)user_data; + lwm2m_object_t *security_obj = data->security_obj; + + instance = LWM2M_LIST_FIND(data->security_obj->instanceList, sec_obj_inst_id); + if (instance == NULL) { + fprintf(stderr, "LWM2M_LIST_FIND() failed\n"); + return NULL; + } + + new_conn = lwm2m_connection_create(data->conn_list, security_obj, instance->id, + data->lwm2mH); + if (new_conn == NULL) { + fprintf(stderr, "Connection creation failed.\n"); + return NULL; + } + + data->conn_list = new_conn; + return (void *)new_conn; +} + +void lwm2m_close_connection(void *sessionH, void *user_data) +{ + client_data_t *app_data; + lwm2m_connection_t *target; + + app_data = (client_data_t *)user_data; + target = (lwm2m_connection_t *)sessionH; + + if (target == app_data->conn_list) { + app_data->conn_list = target->next; + } + else { + lwm2m_connection_t *parent; + + parent = app_data->conn_list; + while (parent != NULL && parent->next != target) { + parent = parent->next; + } + if (parent != NULL) { + parent->next = target->next; + lwm2m_free(target); + } + } +} + +#ifdef LWM2M_BOOTSTRAP + +static void prv_backup_objects(lwm2m_context_t *context) +{ + uint16_t i; + + for (i = 0; i < BACKUP_OBJECT_COUNT; i++) { + if (NULL != backup_object_array[i]) { + switch (backup_object_array[i]->objID) { + case LWM2M_SECURITY_OBJECT_ID: + clean_security_object(backup_object_array[i]); + lwm2m_free(backup_object_array[i]); + break; + case LWM2M_SERVER_OBJECT_ID: + clean_server_object(backup_object_array[i]); + lwm2m_free(backup_object_array[i]); + break; + default: + break; + } + } + backup_object_array[i] = + (lwm2m_object_t *)lwm2m_malloc(sizeof(lwm2m_object_t)); + memset(backup_object_array[i], 0, sizeof(lwm2m_object_t)); + } + + /* + * Backup content of objects 0 (security) and 1 (server) + */ + copy_security_object(backup_object_array[0], + (lwm2m_object_t *)LWM2M_LIST_FIND( + context->objectList, LWM2M_SECURITY_OBJECT_ID)); + copy_server_object(backup_object_array[1], + (lwm2m_object_t *)LWM2M_LIST_FIND( + context->objectList, LWM2M_SERVER_OBJECT_ID)); +} + +void prv_restore_objects(lwm2m_context_t *context) +{ + lwm2m_object_t *target; + + /* + * Restore content of objects 0 (security) and 1 (server) + */ + target = (lwm2m_object_t *)LWM2M_LIST_FIND(context->objectList, + LWM2M_SECURITY_OBJECT_ID); + /* first delete internal content */ + clean_security_object(target); + /* then restore previous object */ + copy_security_object(target, backup_object_array[0]); + + target = (lwm2m_object_t *)LWM2M_LIST_FIND(context->objectList, + LWM2M_SERVER_OBJECT_ID); + /* first delete internal content */ + clean_server_object(target); + /* then restore previous object */ + copy_server_object(target, backup_object_array[1]); + + /* restart the old servers */ + fprintf(stdout, "[BOOTSTRAP] ObjectList restored\r\n"); +} + +static void update_bootstrap_info(lwm2m_client_state_t *previous_bootstrap_state, + lwm2m_context_t *context) +{ + if (*previous_bootstrap_state != context->state) { + *previous_bootstrap_state = context->state; + switch (context->state) { + case STATE_BOOTSTRAPPING: +#ifdef WITH_LOGS + fprintf(stdout, + "[BOOTSTRAP] backup security and server objects\r\n"); +#endif + prv_backup_objects(context); + break; + default: + break; + } + } +} + +static void close_backup_object(void) +{ + int i; + + for (i = 0; i < BACKUP_OBJECT_COUNT; i++) { + if (NULL != backup_object_array[i]) { + switch (backup_object_array[i]->objID) { + case LWM2M_SECURITY_OBJECT_ID: + clean_security_object(backup_object_array[i]); + lwm2m_free(backup_object_array[i]); + break; + case LWM2M_SERVER_OBJECT_ID: + clean_server_object(backup_object_array[i]); + lwm2m_free(backup_object_array[i]); + break; + default: + break; + } + } + } +} +#endif + +void *lwm2m_run_server(void *arg) +{ + (void)arg; + client_data_t data; + int result; + lwm2m_context_t *lwm2mH = NULL; + char *server_uri = LWM2M_SERVER_URI; + int server_id = LWM2M_SERVER_ID; + int lifetime = LWM2M_DEVICE_TTL; + time_t reboot_time = 0; + sock_udp_ep_t local = SOCK_IPV6_EP_ANY; + +#ifdef LWM2M_BOOTSTRAP + bool bootstrapRequested = LWM2M_SERVER_IS_BOOTSTRAP; + lwm2m_client_state_t previousState = STATE_INITIAL; +#endif + +#ifdef MODULE_TINYDTLS + char *psk_id = LWM2M_SERVER_PSK_ID; + char *psk = LWM2M_SERVER_PSK_KEY; +#else /* MODULE_TINYDTLS */ + char *psk_id = NULL; +#endif /* MODULE_TINYDTLS */ + uint16_t psk_len = -1; + char *psk_buffer = NULL; + + msg_t _reader_queue[READER_QUEUE_SIZE]; + DEBUG("msg_init_queue()\r\n"); + msg_init_queue(_reader_queue, READER_QUEUE_SIZE); + + memset(&data, 0, sizeof(client_data_t)); + + local.port = LWM2M_DTLS_PORT; + if (sock_udp_create(&data.sock, &local, NULL, 0) != 0) { + fprintf(stderr, "ERROR: Can't create server socket\n"); + return NULL; + } + + + /* + * Now the main function fill an array with each object, this list will be + * later + * passed to liblwm2m. + * Those functions are located in their respective object file. + */ +#ifdef MODULE_TINYDTLS + if (psk != NULL) { + psk_len = strlen(psk) / 2; + psk_buffer = lwm2m_malloc(psk_len); + + if (NULL == psk_buffer) { + fprintf(stderr, "Failed to create PSK binary buffer\r\n"); + return NULL; + } + /* Hex string to binary */ + char *h = psk; + char *b = psk_buffer; + + for (; *h; h += 2, ++b) { + char tmp[2]; + tmp[0] = *h; + tmp[1] = *(h + 1); + + *b = (char)strtol(tmp, NULL, 16); + } + } +#endif /* MODULE_TINYDTLS */ + + DEBUG("get_security_object(%d, %s, %s, %s, %d, ...)\n", server_id, server_uri, + psk_id, psk_buffer, psk_len); +#ifdef LWM2M_BOOTSTRAP + obj_array[0] = get_security_object(server_id, server_uri, psk_id, psk_buffer, + psk_len, bootstrapRequested); +#else + obj_array[0] = get_security_object(server_id, server_uri, psk_id, psk_buffer, + psk_len, false); +#endif + if (NULL == obj_array[0]) { + fprintf(stderr, "Failed to create security object\r\n"); + return NULL; + } + data.security_obj = obj_array[0]; + + obj_array[1] = get_server_object(server_id, "U", lifetime, false); + if (NULL == obj_array[1]) { + fprintf(stderr, "Failed to create server object\r\n"); + return NULL; + } + + obj_array[2] = lwm2m_get_object_device(); + if (NULL == obj_array[2]) { + fprintf(stderr, "Failed to create Device object\r\n"); + return NULL; + } + + int instId = 0; + obj_array[3] = acc_ctrl_create_object(); + if (NULL == obj_array[3]) { + fprintf(stderr, "Failed to create Access Control object\r\n"); + return NULL; + } + else if (acc_ctrl_obj_add_inst(obj_array[3], instId, 3, 0, server_id) == + false) { + fprintf(stderr, "Failed to create Access Control object instance\r\n"); + return NULL; + } + else if (acc_ctrl_oi_add_ac_val(obj_array[3], instId, 0, 15) == + false) { /* 0b000000000001111 */ + fprintf(stderr, + "Failed to create Access Control ACL default resource\r\n"); + return NULL; + } + else if (acc_ctrl_oi_add_ac_val(obj_array[3], instId, 999, 1) == + false) { /* 0b000000000000001 */ + fprintf( + stderr, + "Failed to create Access Control ACL resource for server_id: 999\r\n"); + return NULL; + } + /* + * The liblwm2m library is now initialized with the functions that will be in + * charge of communication + */ + DEBUG("lwm2m_init()\r\n"); + lwm2mH = lwm2m_init(&data); + if (NULL == lwm2mH) { + fprintf(stderr, "lwm2m_init() failed\r\n"); + return NULL; + } + + data.lwm2mH = lwm2mH; + + /* + * We configure the liblwm2m library with the name of the client - which + * shall be unique for each client - + * the number of objects we will be passing through and the objects array + */ + DEBUG("lwm2m_configure()\r\n"); + result = lwm2m_configure(lwm2mH, LWM2M_DEVICE_NAME, NULL, NULL, OBJ_COUNT, + obj_array); + if (result != 0) { + fprintf(stderr, "lwm2m_configure() failed: 0x%X\r\n", result); + return NULL; + } + + /* This will auto-add all supported devices currently in the SAUL registry */ + lwm2m_auto_add_float_sensors(lwm2mH); + lwm2m_auto_add_bool_sensors(lwm2mH); + lwm2m_auto_add_bool_actuators(lwm2mH); + +#ifdef BOARD_NATIVE + signal(SIGINT, handle_sigint); +#endif /* BOARD_NATIVE */ + + /* + * We now enter in a while loop that will handle the communications from the + * server + */ + while (0 == g_quit) { + time_t tv; + uint8_t packet_rcvd[RCVD_SIZE]; + ssize_t pckt_rcvd_size = RCVD_SIZE; + sock_udp_ep_t remote; + + lwm2m_memarray_print_stats(); + + if (lwm2m_device_reboot_requested()) { + time_t tv_sec; + + tv_sec = lwm2m_gettime(); + + if (0 == reboot_time) { + fprintf(stderr, "reboot requested; rebooting in %u seconds\r\n", REBOOT_TIME); + reboot_time = tv_sec + REBOOT_TIME; + } + if (reboot_time < tv_sec) { + /* + * Message should normally be lost with reboot ... + */ + fprintf(stderr, "reboot time expired, rebooting ..."); + pm_reboot(); + } + else { + tv = reboot_time - tv_sec; + } + } + else { + tv = RESPONSE_TIMEOUT; + } + + /* Update sensor values. */ + lwm2m_poll_float_sensors(lwm2mH); + lwm2m_poll_bool_sensors(lwm2mH); + lwm2m_poll_bool_actuators(lwm2mH); + + /* + * This function does two things: + * - first it does the work needed by liblwm2m (eg. (re)sending some + * packets). + * - Secondly it adjusts the timeout value (default 60s) depending on the + * state of the transaction + * (eg. retransmission) and the time between the next operation + */ + DEBUG("lwm2m_step()\n"); + result = lwm2m_step(lwm2mH, &tv); + fprintf(stdout, " -> State: "); + switch (lwm2mH->state) { + case STATE_INITIAL: + fprintf(stdout, "STATE_INITIAL\r\n"); + break; + case STATE_BOOTSTRAP_REQUIRED: + fprintf(stdout, "STATE_BOOTSTRAP_REQUIRED\r\n"); + break; + case STATE_BOOTSTRAPPING: + fprintf(stdout, "STATE_BOOTSTRAPPING\r\n"); + break; + case STATE_REGISTER_REQUIRED: + fprintf(stdout, "STATE_REGISTER_REQUIRED\r\n"); + break; + case STATE_REGISTERING: + fprintf(stdout, "STATE_REGISTERING\r\n"); + break; + case STATE_READY: + fprintf(stdout, "STATE_READY\ren"); + break; + default: + fprintf(stdout, "Unknown...\r\n"); + break; + } + if (result != 0) { + fprintf(stderr, "lwm2m_step() failed: 0x%X\r\n", result); +#ifdef LWM2M_BOOTSTRAP + if (previousState == STATE_BOOTSTRAPPING) { +#ifdef WITH_LOGS + fprintf(stdout, + "[BOOTSTRAP] restore security and server objects\r\n"); +#endif /* WITH_LOGS */ + prv_restore_objects(lwm2mH); + lwm2mH->state = STATE_INITIAL; + } + else +#endif /* LWM2M_BOOTSTRAP */ + { + g_quit = 1; + continue; + } + } +#ifdef LWM2M_BOOTSTRAP + DEBUG("update_bootstrap_info()\r\n"); + update_bootstrap_info(&previousState, lwm2mH); +#endif + + pckt_rcvd_size = sock_udp_recv(&data.sock, &packet_rcvd, RCVD_SIZE, tv * US_PER_SEC, &remote); + DEBUG("sock_udp_recv()\r\n"); + if (pckt_rcvd_size > 0) { + lwm2m_connection_t *conn = lwm2m_connection_find(data.conn_list, &remote); + if (conn != NULL) { + DEBUG("lwm2m_connection_handle_packet(%d)\r\n", pckt_rcvd_size); + int result = lwm2m_connection_handle_packet(conn, packet_rcvd, pckt_rcvd_size); + if (0 != result) { + printf("error handling message %d\n", result); + } + } + } + else if ((pckt_rcvd_size < 0) && + ((pckt_rcvd_size != -EAGAIN) && (pckt_rcvd_size != -ETIMEDOUT))) { + DEBUG("Unexpected sock_udp_recv error code %i\n", pckt_rcvd_size); + } + } + + /* + * Finally when the loop is left smoothly - asked by user in the command line + * interface - we unregister our client from it + */ + if (psk_buffer != NULL) { + lwm2m_free(psk_buffer); + } + +#ifdef LWM2M_BOOTSTRAP + close_backup_object(); +#endif + lwm2m_close(lwm2mH); + sock_udp_close(&data.sock); + lwm2m_connection_free(data.conn_list); + + clean_security_object(obj_array[0]); + lwm2m_free(obj_array[0]); + clean_server_object(obj_array[1]); + lwm2m_free(obj_array[1]); + lwm2m_free_object_device(obj_array[2]); + acl_ctrl_free_object(obj_array[3]); + + return NULL; +} diff --git a/examples/wakaama/lwm2mclient.h b/examples/wakaama/lwm2mclient.h new file mode 100644 index 000000000000..6f48e5fb841a --- /dev/null +++ b/examples/wakaama/lwm2mclient.h @@ -0,0 +1,448 @@ +/******************************************************************************* +* +* Copyright (c) 2014 Bosch Software Innovations GmbH, Germany. +* All rights reserved. This program and the accompanying materials +* are made available under the terms of the Eclipse Public License v1.0 +* and Eclipse Distribution License v1.0 which accompany this distribution. +* +* The Eclipse Public License is available at +* http://www.eclipse.org/legal/epl-v10.html +* The Eclipse Distribution License is available at +* http://www.eclipse.org/org/documents/edl-v10.php. +* +* Contributors: +* Bosch Software Innovations GmbH - Please refer to git log +* Christian Manal - Ported to RIOT OS +* +*******************************************************************************/ +/* + * lwm2mclient.h + * + * General functions of lwm2m test client. + * + * Created on: 22.01.2015 + * Author: Achim Kraus + * Copyright (c) 2015 Bosch Software Innovations GmbH, Germany. All rights reserved. + */ + +/** + * @ingroup examples + * @{ + * + * @file + * @brief Header for main loop for Wakaama example client. + * + * @author Christian Manal + * + */ + +#ifndef LWM2MCLIENT_H +#define LWM2MCLIENT_H + +#include "liblwm2m.h" +#include "saul_reg.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @name LWM2M object IDs of supported devices + * + * Also see IPSO Alliance Smart Object definitions: + * https://github.com/IPSO-Alliance/pub + * @{ + */ +#define LWM2M_GENERIC_SENSOR_ID (3300u) +/* Sensors that return decimal values */ +#define LWM2M_ILLUMINANCE_SENSOR_ID (3301u) +#define LWM2M_ANALOG_SENSOR_ID (3202u) +#define LWM2M_TEMPERATURE_SENSOR_ID (3303u) +#define LWM2M_HUMIDITY_SENSOR_ID (3304u) +#define LWM2M_BAROMETER_SENSOR_ID (3315u) +#define LWM2M_VOLTAGE_SENSOR_ID (3316u) +#define LWM2M_CURRENT_SENSOR_ID (3317u) +#define LWM2M_FREQUENCY_SENSOR_ID (3318u) +#define LWM2M_DEPTH_SENSOR_ID (3319u) +#define LWM2M_PERCENTAGE_SENSOR_ID (3320u) +#define LWM2M_ALTITUDE_SENSOR_ID (3321u) +#define LWM2M_LOAD_SENSOR_ID (3322u) +#define LWM2M_PRESSURE_SENSOR_ID (3323u) +#define LWM2M_LOUDNESS_SENSOR_ID (3324u) +#define LWM2M_CONCENTRATION_SENSOR_ID (3325u) +#define LWM2M_ACIDITY_SENSOR_ID (3326u) +#define LWM2M_CONDUCTIVITY_SENSOR_ID (3327u) +#define LWM2M_POWER_SENSOR_ID (3328u) +#define LWM2M_POWER_FACTOR_SENSOR_ID (3329u) +#define LWM2M_DISTANCE_SENSOR_ID (3330u) +#define LWM2M_RATE_SENSOR_ID (3346u) +/* Sensors that return digital values */ +#define LWM2M_DIGITAL_SENSOR_ID (3200u) +/* Actuators */ +#define LWM2M_DIGITAL_ACTUATOR_ID (3201u) +/** @} */ + +/** + * @name IDs of used LwM2M attribute types. + * + * Also see IPSO Alliance Smart Object definitions: + * https://github.com/IPSO-Alliance/pub + * @{ + */ +/* Float sensors */ +#define LWM2M_SENSOR_VALUE_ATTR (5700u) /* R Float */ +#define LWM2M_SENSOR_UNITS_ATTR (5701u) /* R Float */ +#define LWM2M_SENSOR_ANALOG_INPUT_ATTR (5600u) /* R Float */ +#define LWM2M_MIN_MEASURED_ATTR (5601u) /* R Float */ +#define LWM2M_MAX_MEASURED_ATTR (5602u) /* R Float */ +#define LWM2M_MIN_RANGE_ATTR (5603u) /* R Float */ +#define LWM2M_MAX_RANGE_ATTR (5604u) /* R Float */ +#define LWM2M_RESET_MIN_MAX_MEASURED_ATTR (5605u) /* Execute */ +/* Digital sensors */ +#define LWM2M_DINPUT_ATTR (5500u) /* R Bool */ +#define LWM2M_DINPUT_COUNTER_ATTR (5501u) /* R Int */ +#define LWM2M_DINPUT_POLARITY_ATTR (5502u) /* RW Bool */ +#define LWM2M_DINPUT_DEBOUNCE_ATTR (5503u) /* RW Int */ +#define LWM2M_DINPUT_EDGESELECT_ATTR (5504u) /* RW INT (1-3) */ +#define LWM2M_DINPUT_COUNTER_RESET_ATTR (5505u) /* Execute */ +/* Digital actuator */ +#define LWM2M_DOUTPUT_ATTR (5550u) /* RW Bool */ +#define LWM2M_DOUTPUT_POLARITY_ATTR (5551u) /* RW Bool */ +/* Misc */ +#define LWM2M_APP_TYPE_ATTR (5750u) /* RW String */ +#define LWM2M_SENSOR_TYPE_ATTR (5751u) /* R String */ +/** @} */ + +/* + * lwm2mutil.c + */ + +/** + * @brief Translates a SAUL device type into a corresponding LWM2M object ID. + * + * @param[in] saul_type A SAUL device type ID. + * + * @return -1 if no LWM2M object ID could be found + * @return one of the LWM2M_*_SENSOR_ID values otherwise + */ +int lwm2m_saul_type_to_obj_id(uint8_t saul_type); + +/** + * @brief Converts an LWM2M object ID into a string. + * + * @param[in] id ID of an LWM2M object. + * + * @return Name of the object if @p id is defined. + * @return NULL otherwise. + */ +const char *lwm2m_object_id_to_str(const uint16_t id); + +/* + * object_digital_actuator.c + */ + +/** + * @brief Creates a new object instance for a digital actuator object and optionally adds + * it to an existing instance list. + * + * @param[in/out] object Pointer to pointer of a base sensor object. If the + * inner pointer is NULL, a new base object will be allocated. + * @param[in] dev Pointer to the SAUL device driver for the sensor. + * + * @return 0 on success + * @return -EINVAL if @p object == NULL + * @return -EINVAL if @p dev == NULL + * @return -EINVAL if @p objId of @p object does not equal LWM2M_DIGITIAL_ACTUATOR_ID + * @return -ENOMEM if no memory could be allocated for the base object or the instance. + * @return -EEXIST if an instance for @p dev already exists in @p object. + */ +int lwm2m_get_bool_actuator(lwm2m_object_t **object, saul_reg_t *dev); + +/** + * @brief Release memory of a base sensor object and all its instances. + * + * @param[in] obj Pointer to the object to release. + */ +void lwm2m_free_bool_actuator(lwm2m_object_t *obj); + +/** + * @brief Automatically adds objects all supported devices in the SAUL registry + * to an LWM2M client instance. + * + * @param[in] context The LWM2M client context to add objects to. + * + * @return The number of objects added. + */ +int lwm2m_auto_add_bool_actuators(lwm2m_context_t *context); + +/** + * @brief Notify observers about value changes. + * + * @param[in] lwm2mH LWM2M context + */ +void lwm2m_poll_bool_actuators(lwm2m_context_t *lwm2mH); + +/* + * object_digital_sensor.c + */ + +/** + * @brief Creates a new object instance for a digital sensor object and optionally adds + * it to an existing instance list. + * + * @param[in/out] object Pointer to pointer of a base sensor object. If the + * inner pointer is NULL, a new base object will be allocated. + * @param[in] dev Pointer to the SAUL device driver for the sensor. + * + * @return 0 on success + * @return -EINVAL if @p object == NULL + * @return -EINVAL if @p dev == NULL + * @return -EINVAL if @p objId of @p object does not equal LWM2M_DIGITAL_SENSOR_ID + * @return -ENOMEM if no memory could be allocated for the base object or the instance. + * @return -EEXIST if an instance for @p dev already exists in @p object. + */ +int lwm2m_get_bool_sensor(lwm2m_object_t **object, saul_reg_t *dev); + +/** + * @brief Release memory of a base sensor object and all its instances. + * + * @param[in] obj Pointer to the object to release. + */ +void lwm2m_free_bool_sensor(lwm2m_object_t *obj); + +/** + * @brief Automatically adds objects all supported devices in the SAUL registry + * to an LWM2M client instance. + * + * @param[in] context The LWM2M client context to add objects to. + * + * @return The number of objects added. + */ +int lwm2m_auto_add_bool_sensors(lwm2m_context_t *context); + +/** + * @update Update the measured values for all supported sensor objects and notify + * observers if values changed. + * + * @param[in] lwm2mH LWM2M context + */ +void lwm2m_poll_bool_sensors(lwm2m_context_t *lwm2mH); + +/* + * object_float_sensor.c + */ + +/** + * @brief Creates a new object instance for an analog sensor object and optionally adds + * it to an existing instance list. + * + * @param[in/out] object Pointer to pointer of a base sensor object. If the + * inner pointer is NULL, a new base object will be allocated. + * @param[in] sensor_id ID of the sensor type to use (See LWM2M_*_SENSOR_ID defines). + * @param[in] dev Pointer to the SAUL device driver for the sensor. + * + * @return 0 on success + * @return -EINVAL if @p object == NULL + * @return -EINVAL if @p sensor_id is an unknown ID + * @return -EINVAL if @p dev == NULL + * @return -EINVAL if @p sensor_id does not match the objId of @p object. + * @return -ENOMEM if no memory could be allocated for the base object or the instance. + * @return -EEXIST if an instance for @p dev already exists in @p object. + */ +int lwm2m_get_float_sensor(lwm2m_object_t **object, int sensor_id, saul_reg_t *dev); + +/** + * @brief Release memory of a base sensor object and all its instances. + * + * @param[in] obj Pointer to the object to release. + */ +void lwm2m_free_float_sensor(lwm2m_object_t *obj); + +/** + * @brief Automatically adds objects all supported devices in the SAUL registry + * to an LWM2M client instance. + * + * @param[in] context The LWM2M client context to add objects to. + * + * @return The number of objects added. + */ +int lwm2m_auto_add_float_sensors(lwm2m_context_t *context); + +/** + * @update Update the measured values for all supported sensor objects and notify + * observers if values changed. + * + * @param[in] lwm2mH LWM2M context + */ +void lwm2m_poll_float_sensors(lwm2m_context_t *lwm2mH); + +/* + * object_device.c + */ + +/** + * @brief Instantiate an lwm2m device object. + * + * @return Pointer to object on success. + * @param NULL on error. + */ +lwm2m_object_t *lwm2m_get_object_device(void); + +/** + * @brief Release resouces of an lwm2m device object. + * + * @param[in] object Pointer to the object. + */ +void lwm2m_free_object_device(lwm2m_object_t *object); + +/** + * @brief Indicates when a reboot has been requested via an lwm2m device object. + * + * @return true When reboot has been requested. + * @return false Otherwise + */ +bool lwm2m_device_reboot_requested(void); + +/* + * object_server.c + */ + +/** + * @brief Instantiate an lwm2m server object. + * + * @return Pointer to object on success. + * @param NULL on error. + */ +lwm2m_object_t *get_server_object(int server_id, const char *binding, int lifetime, bool storing); + +/** + * @brief Release resouces of an lwm2m server object. + * + * @param[in] object Pointer to the object. + */ +void clean_server_object(lwm2m_object_t *object); + +/** + * @brief Prints a string representation of an lwm2m server object to stdout. + * + * @param[in] object Pointer to the object. + */ +void display_server_object(lwm2m_object_t *object); + +/** + * @brief Creates a copy of an lwm2m server object. + * + * @param[out] object_dest Destination object to write to. + * @param[in] object_src Source object to copy from. + */ +void copy_server_object(lwm2m_object_t *object_dest, lwm2m_object_t *object_src); + +/* + * object_access_control.c + */ + +/** + * @brief Instantiate an lwm2m access control object. + * + * @return Pointer to object on success. + * @param NULL on error. + */ +lwm2m_object_t *acc_ctrl_create_object(void); + +/** + * @brief Release resouces of an lwm2m access control object. + * + * @param[in] object Pointer to the object. + */ +void acl_ctrl_free_object(lwm2m_object_t *object); + +/** + * @brief Add an instance of an lwm2m access control object to a base object. + * + * @param[out] accCtrlObjP The access control base object to add an instance to. + * @param[in] instId The instance ID of the new instance. + * @param[in] acObjectId The object ID of the target object. + * @param[in] acObjInstId The instance ID of the target object. + * @param[in] acOwner The owner + * + * @return true When the instance was successfully created. + * @return false Otherwise. + */ +bool acc_ctrl_obj_add_inst(lwm2m_object_t *accCtrlObjP, uint16_t instId, + uint16_t acObjectId, uint16_t acObjInstId, uint16_t acOwner); + +/** + * @brief Add an access control list entry to an lwm2m access control object instance. + * + * @param[out] accCtrlObjP The access control base object to modify. + * @param[in] instId The instance ID of the object to modify. + * @param[in] aclResId The resource ID to control access to. + * @param[in] acValue The permissions to set. + */ +bool acc_ctrl_oi_add_ac_val(lwm2m_object_t *accCtrlObjP, uint16_t instId, + uint16_t aclResId, uint16_t acValue); +/* + * object_security.c + */ + +/** + * @brief Instantiate an lwm2m security object. + * + * @param[in] server_id Short ID of the server + * @param[in] server_uri URI of the server + * @param[in] bs_psk_id PSK identity + * @param[in] psk PSK + * @param[in] psk_len Length of @p psk + * @param[in] is_bootstrap Server is bootstrap-server + * + * @return Pointer to object on success. + * @return NULL on error. + */ +lwm2m_object_t *get_security_object(int server_id, const char *server_uri, char *bs_psk_id, char *psk, uint16_t psk_len, bool is_bootstrap); + +/** + * @brief Release resouces of an lwm2m security object. + * + * @param[in] object Pointer to the object. + */ +void clean_security_object(lwm2m_object_t *object); + +/** + * @brief Get the URI of the corresponding server from an lwm2m security object. + * + * @param[in] object The security base object to read from. + * @param[in] sec_obj_inst_id The instance ID of the object to read from. + * + * @return URI string On success. + * @retuen NULL On error. + */ +char *get_server_uri(lwm2m_object_t *object, uint16_t sec_obj_inst_id); + +/** + * @brief Prints a string representation of an lwm2m security object to stdout. + * + * @param[in] object Pointer to the object. + */ +void display_security_object(lwm2m_object_t *object); + +/** + * @brief Creates a copy of an lwm2m security object. + * + * @param[out] object_dest Destination object to write to. + * @param[in] object_src Source object to copy from. + */ +void copy_security_object(lwm2m_object_t *object_dest, lwm2m_object_t *object_src); + +/* + * Run the wakaama process. + * + * @param[in] arg Ignored. + */ +void *lwm2m_run_server(void *arg); + +#ifdef __cplusplus +} +#endif + +/** @} */ +#endif /* LWM2MCLIENT_H */ diff --git a/examples/wakaama/lwm2mconfig.h b/examples/wakaama/lwm2mconfig.h new file mode 100644 index 000000000000..30c4b8daaffd --- /dev/null +++ b/examples/wakaama/lwm2mconfig.h @@ -0,0 +1,155 @@ +/* + * Copyright (C) 2018 Beduino Master Projekt - University of Bremen + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup examples + * @{ + * + * @file + * @brief Configurable parameters for Wakaama example client. + * + * @author Christian Manal + * + * @} + */ + + +#ifndef LWM2MCONFIG_H +#define LWM2MCONFIG_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Default ports + */ +#define LWM2M_STANDARD_PORT_STR "5683" +#define LWM2M_STANDARD_PORT 5683 +#define LWM2M_DTLS_PORT_STR "5684" +#define LWM2M_DTLS_PORT 5684 +#define LWM2M_BSSERVER_PORT_STR "5685" +#define LWM2M_BSSERVER_PORT 5685 + +/* + * The name the device registers at the LwM2M server. + */ +#define LWM2M_DEVICE_NAME "testRIOTDevice" + +/* + * Device object lifetime on the server. + */ +#define LWM2M_DEVICE_TTL 300 + +/* + * LwM2M server URI to register/bootstrap with. + * The host part of the URI MUST be a valid IPv6 address. Host names + * can not be resolved at this time. + */ +#ifndef LWM2M_SERVER_URI +#define LWM2M_SERVER_URI "coaps://[fd00:dead:beef::1]" +#endif /* LWM2M_SERVER_URI */ + +/* + * Numeric ID of LWM2M_SERVER_URI + */ +#define LWM2M_SERVER_ID 10 + +/* + * Set to a true value if LWM2M_SERVER_URI is a bootstrap server. + */ +#define LWM2M_SERVER_IS_BOOTSTRAP 0 + + +#ifdef MODULE_TINYDTLS + +/* + * DTLS debug log level. One of the following: + * DTLS_LOG_EMERG + * DTLS_LOG_ALERT + * DTLS_LOG_CRIT + * DTLS_LOG_WARN + * DTLS_LOG_NOTICE + * DTLS_LOG_INFO + * DTLS_LOG_DEBUG + */ +#ifndef DTLS_LOG_LEVEL +#define DTLS_LOG_LEVEL DTLS_LOG_CRIT +#endif + +/* + * PSK ID of LWM2M_SERVER_URI. + */ +#define LWM2M_SERVER_PSK_ID "Client_identity" + +/* + * PSK KEY of LWM2M_SERVER_URI in hexadecimal. + * Set to NULL to not encrypt traffic. + */ +#define LWM2M_SERVER_PSK_KEY "73656372657450534b" + +#endif /* MODULE_TINYDTLS */ + +/* + * The following defines set descriptor strings as defined for the + * mandatory LwM2M device object. See appendix E. of the OMA LwM2M technical + * specification. + */ + +/* + * Manufacturer string. + * Defaults to "A RIOT maker" + */ +/* #define LWM2M_DEVICE_MANUFACTURER */ + +/* + * Model string. + * Defaults to the RIOT_BOARD define. + */ +/* #define LWM2M_DEVICE_MODEL */ + +/* + * Serial number (string). + * Defaults to "undefined" + */ +/* #define LWM2M_DEVICE_SERIAL */ + +/* + * Firmware version string. + * Defaults to the RIOT_VERSION define. + */ +/* #define LWM2M_DEVICE_FW_VERSION */ + +/* + * LwM2M device bindings (see specification for more information). + * Defaults to "U" (unqueued UDP). + */ +/* #define LWM2M_DEVICE_BINDINGS */ + +/* + * Device type string. + * Defaults to "RIOT device" + */ +/* #define LWM2M_DEVICE_TYPE */ + +/* + * Hardware version string. + * Defaults to the RIOT_BOARD define. + */ +/* #define LWM2M_DEVICE_HW_VERSION */ + +/* + * Software version string. + * Defaults to the RIOT_VERSION define. + */ +/* #define LWM2M_DEVICE_SW_VERSION */ + +#ifdef __cplusplus +} +#endif +#endif /* LWM2MCONFIG_H */ diff --git a/examples/wakaama/lwm2mutil.c b/examples/wakaama/lwm2mutil.c new file mode 100644 index 000000000000..b905203e3d18 --- /dev/null +++ b/examples/wakaama/lwm2mutil.c @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2018 Beduino Master Projekt - University of Bremen + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +#include "lwm2mclient.h" + +/* + * RIOT debugging + */ +#define ENABLE_DEBUG (0) +#include "debug.h" + +int lwm2m_saul_type_to_obj_id(uint8_t saul_type) +{ + switch (saul_type) { + case SAUL_SENSE_ANY: + case SAUL_SENSE_COUNT: + return LWM2M_GENERIC_SENSOR_ID; + case SAUL_SENSE_OBJTEMP: + case SAUL_SENSE_TEMP: + return LWM2M_TEMPERATURE_SENSOR_ID; + case SAUL_SENSE_HUM: + return LWM2M_HUMIDITY_SENSOR_ID; + case SAUL_SENSE_UV: + case SAUL_SENSE_LIGHT: + return LWM2M_ILLUMINANCE_SENSOR_ID; + case SAUL_SENSE_PRESS: + return LWM2M_PRESSURE_SENSOR_ID; + case SAUL_SENSE_ANALOG: + return LWM2M_ANALOG_SENSOR_ID; + case SAUL_SENSE_DISTANCE: + return LWM2M_DISTANCE_SENSOR_ID; + case SAUL_SENSE_BTN: + return LWM2M_DIGITAL_SENSOR_ID; + case SAUL_SENSE_CO2: + return LWM2M_GENERIC_SENSOR_ID; + + /* Need data types other than float or bool */ + case SAUL_SENSE_ACCEL: + case SAUL_SENSE_MAG: + case SAUL_SENSE_GYRO: + case SAUL_SENSE_COLOR: + + /* Actuators aren't in yet */ + case SAUL_ACT_ANY: + case SAUL_ACT_LED_RGB: + case SAUL_ACT_SERVO: + case SAUL_ACT_MOTOR: + case SAUL_ACT_SWITCH: + return LWM2M_DIGITAL_ACTUATOR_ID; + case SAUL_ACT_DIMMER: + + case SAUL_CLASS_ANY: + case SAUL_CLASS_UNDEF: + default: + return -1; + } +} + +const char *lwm2m_object_id_to_str(const uint16_t id) +{ + switch (id) { + case LWM2M_GENERIC_SENSOR_ID: return "GENERIC_SENSOR"; + case LWM2M_ILLUMINANCE_SENSOR_ID: return "ILLUMINANCE_SENSOR"; + case LWM2M_ANALOG_SENSOR_ID: return "ANALOG_SENSOR"; + case LWM2M_TEMPERATURE_SENSOR_ID: return "TEMPERATURE_SENSOR"; + case LWM2M_HUMIDITY_SENSOR_ID: return "HUMIDITY_SENSOR"; + case LWM2M_BAROMETER_SENSOR_ID: return "BAROMETER_SENSOR"; + case LWM2M_VOLTAGE_SENSOR_ID: return "VOLTAGE_SENSOR"; + case LWM2M_CURRENT_SENSOR_ID: return "CURRENT_SENSOR"; + case LWM2M_FREQUENCY_SENSOR_ID: return "FREQUENCY_SENSOR"; + case LWM2M_DEPTH_SENSOR_ID: return "DEPTH_SENSOR"; + case LWM2M_PERCENTAGE_SENSOR_ID: return "PERCENTAGE_SENSOR"; + case LWM2M_ALTITUDE_SENSOR_ID: return "ALTITUDE_SENSOR"; + case LWM2M_LOAD_SENSOR_ID: return "LOAD_SENSOR"; + case LWM2M_PRESSURE_SENSOR_ID: return "PRESSURE_SENSOR"; + case LWM2M_LOUDNESS_SENSOR_ID: return "LOUDNESS_SENSOR"; + case LWM2M_CONCENTRATION_SENSOR_ID: return "CONCENTRATION_SENSOR"; + case LWM2M_ACIDITY_SENSOR_ID: return "ACIDITY_SENSOR"; + case LWM2M_CONDUCTIVITY_SENSOR_ID: return "CONDUCTIVITY_SENSOR"; + case LWM2M_POWER_SENSOR_ID: return "POWER_SENSOR"; + case LWM2M_POWER_FACTOR_SENSOR_ID: return "POWER_FACTOR_SENSOR"; + case LWM2M_DISTANCE_SENSOR_ID: return "DISTANCE_SENSOR"; + case LWM2M_RATE_SENSOR_ID: return "RATE_SENSOR"; + case LWM2M_DIGITAL_SENSOR_ID: return "DIGITAL_SENSOR"; + case LWM2M_DIGITAL_ACTUATOR_ID: return "DIGITAL_ACTUATOR"; + default: return "UNDEFINED_OBJECT"; + } +} diff --git a/examples/wakaama/main.c b/examples/wakaama/main.c new file mode 100644 index 000000000000..431fe2ac4587 --- /dev/null +++ b/examples/wakaama/main.c @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2018 Beduino Master Projekt - University of Bremen + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup examples + * @{ + * + * @file + * @brief Example application for Eclipse Wakaama LwM2M Client + * + * @author Christian Manal + * + * @} + */ + +#include +#include + +#include "msg.h" +#include "shell.h" + +#include "memarray_limits.h" + +#include "connection.h" +#include "lwm2mclient.h" + +#define READER_QUEUE_SIZE (8U) +#define SHELL_QUEUE_SIZE (8U) +char _server_stack[THREAD_STACKSIZE_MAIN + THREAD_EXTRA_STACKSIZE_PRINTF]; + +static kernel_pid_t _wakaama_kernel_pid = KERNEL_PID_UNDEF; + +/* + * Workaround for stuck mainloop, since run_server() apparently doesn't + * clean up properly yet. + */ +#ifdef BOARD_NATIVE +static int exit_cmd(int argc, char **argv) +{ + (void)argc; + (void)argv; + exit(0); +} +#endif /* BOARD_NATIVE */ + +static const shell_command_t shell_commands[] = { +#ifdef BOARD_NATIVE + { "exit", "Exit the shell", exit_cmd }, +#endif /* BOARD_NATIVE */ + { NULL, NULL, NULL } +}; + +int main(void) +{ + lwm2m_memarray_init(); + +#ifdef MODULE_TINYDTLS + dtls_init(); + dtls_set_log_level(DTLS_LOG_LEVEL); +#endif /* MODULE_TINYDTLS */ + + /* Only one instance of the server */ + if (_wakaama_kernel_pid != KERNEL_PID_UNDEF) { + printf("Error: server already running\n"); + return -1; + } + + _wakaama_kernel_pid = thread_create( + _server_stack, sizeof(_server_stack), THREAD_PRIORITY_MAIN - 1, + THREAD_CREATE_STACKTEST, lwm2m_run_server, NULL, "wakaama client"); + + msg_t _shell_queue[SHELL_QUEUE_SIZE]; + msg_init_queue(_shell_queue, SHELL_QUEUE_SIZE); + + char line_buf[SHELL_DEFAULT_BUFSIZE]; + shell_run(shell_commands, line_buf, SHELL_DEFAULT_BUFSIZE); + + return 0; +} diff --git a/examples/wakaama/memarray_limits.h b/examples/wakaama/memarray_limits.h new file mode 100644 index 000000000000..0829f9d4d24d --- /dev/null +++ b/examples/wakaama/memarray_limits.h @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2018 Beduino Master Projekt - University of Bremen + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + + +/** + * @ingroup examples + * @{ + * + * @file + * @brief Header for Wakaama example client memarray pre-allocation limits. + * + * @author Christian Manal + */ + + +#ifndef MEMARRAY_LIMITS_H +#define MEMARRAY_LIMITS_H + +#include "memarray.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** Maximum number of 16 byte blocks. */ +#define MAX_16BYTE_BLOCKS (25U) +/** Maximum number of 32 byte blocks. */ +#define MAX_32BYTE_BLOCKS (15U) +/** Maximum number of 64 byte blocks. */ +#define MAX_64BYTE_BLOCKS (20U) +/** Maximum number of 192 byte blocks. */ +#define MAX_192BYTE_BLOCKS (10U) +/** Maximum number of 512 byte blocks. */ +#define MAX_512BYTE_BLOCKS (1U) + +/** + * @brief Initializes memarray blocks for the lwm2m example client. + */ +void lwm2m_memarray_init(void); + +/** + * @brief Prints allocation and de-allocation statistics for + * memarray. Requires debugging to be enabled on platform.c. + */ +void lwm2m_memarray_print_stats(void); + +#ifdef __cplusplus +} +#endif + +/** @} */ +#endif /* MEMARRAY_LIMITS_H */ diff --git a/examples/wakaama/object_device.c b/examples/wakaama/object_device.c new file mode 100644 index 000000000000..edc55fa0b442 --- /dev/null +++ b/examples/wakaama/object_device.c @@ -0,0 +1,432 @@ +/* + * Copyright (C) 2018 Beduino Master Projekt - University of Bremen + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +#include +#include +#include + +#include "liblwm2m.h" +#include "lwm2mconfig.h" + +#ifndef LWM2M_DEVICE_MANUFACTURER +#define LWM2M_DEVICE_MANUFACTURER "A RIOT maker" +#endif + +#ifndef LWM2M_DEVICE_MODEL +#define LWM2M_DEVICE_MODEL RIOT_BOARD +#endif + +#ifndef LWM2M_DEVICE_SERIAL +#define LWM2M_DEVICE_SERIAL "undefined" +#endif + +#ifndef LWM2M_DEVICE_FW_VERSION +#define LWM2M_DEVICE_FW_VERSION RIOT_VERSION +#endif + +/* U (UDP) or UQ (UDP queue) and S (SMS) or SQ (SMS queue) */ +#ifndef LWM2M_DEVICE_BINDINGS +#define LWM2M_DEVICE_BINDINGS "U" +#endif + +#ifndef LWM2M_DEVICE_TYPE +#define LWM2M_DEVICE_TYPE "RIOT device" +#endif + +#ifndef LWM2M_DEVICE_HW_VERSION +#define LWM2M_DEVICE_HW_VERSION RIOT_BOARD +#endif + +#ifndef LWM2M_DEVICE_SW_VERSION +#define LWM2M_DEVICE_SW_VERSION RIOT_VERSION +#endif + +enum { + LWM2M_RES_MANUFACTURER = 0, + LWM2M_RES_MODEL_NO, + LWM2M_RES_SERIAL, + LWM2M_RES_FW_VER, + LWM2M_RES_REBOOT, /* exec */ + LWM2M_RES_FRESET, /* exec */ + LWM2M_RES_POWER_SRC, /* multiplea */ + LWM2M_RES_POWER_VOL, /* multiple */ + LWM2M_RES_POWER_AMP, /* multiple */ + LWM2M_RES_BATTERY_LEVEL, /* 0-100 (percentage) */ + LWM2M_RES_MEM_FREE, /* kB */ + LWM2M_RES_ERROR_CODE, /* 0-8; multiple */ + LWM2M_RES_ERROR_CODE_RESET, /* exec */ + LWM2M_RES_TIME, /* rw, string */ + LWM2M_RES_TIME_OFFSET, /* rw, string; from UTC */ + LWM2M_RES_TIME_ZONE, /* rw, string */ + LWM2M_RES_BINDINGS, + LWM2M_RES_TYPE, + LWM2M_RES_HW_VERSION, + LWM2M_RES_SW_VERSION, + LWM2M_RES_BATTERY_STATUS, + LWM2M_RES_MEM_TOTAL, /* kB */ + LWM2M_RES_EXT_DEV_INFO, /* objlink; multiple */ +}; + +typedef struct { + uint8_t *power_sources; /* 0-7 */ + uint16_t *power_voltage; /* mV */ + uint16_t *power_current; /* mA */ + uint8_t battery_status; /* 0-6 */ + uint32_t mem_total; /* kB */ + uint16_t(*ext_dev_info)[2]; /* Network byte order; most significant half -> + * object, least significant -> instance + */ + uint8_t ext_dev_info_len; + /* + * Possible values (according to LwM2M specification): + * 0 No error + * 1 Low battery power + * 2 External power supply off + * 3 GPS module failure + * 4 Low received signal strength + * 5 Out of memory + * 6 SMS failure + * 7 IP connectivity failure + * 8 Peripheral malfunction + */ + uint8_t error_code[7]; + /* How many values used in error_code[] */ + uint8_t error_code_used; +} dev_data_t; + +/* Set to true if reboot requested. */ +static bool reboot; + +static uint8_t prv_device_discover(uint16_t instance_id, int *num_dataP, + lwm2m_data_t **data_arrayP, + lwm2m_object_t *objectP) +{ + uint8_t result; + int i; + + (void)objectP; + + if (instance_id != 0) { + return COAP_404_NOT_FOUND; + } + + result = COAP_205_CONTENT; + + if (*num_dataP == 0) { + /* This list must contain all available resources */ + uint16_t res[] = { + LWM2M_RES_MANUFACTURER, LWM2M_RES_MODEL_NO, LWM2M_RES_SERIAL, + LWM2M_RES_FW_VER, LWM2M_RES_REBOOT, LWM2M_RES_ERROR_CODE, + /* LWM2M_RES_ERROR_CODE_RESET, TODO */ + LWM2M_RES_BINDINGS, LWM2M_RES_TYPE, LWM2M_RES_HW_VERSION, + LWM2M_RES_SW_VERSION, + }; + int len = sizeof(res) / sizeof(uint16_t); + + *data_arrayP = lwm2m_data_new(len); + if (*data_arrayP == NULL) { + return COAP_500_INTERNAL_SERVER_ERROR; + } + *num_dataP = len; + for (i = 0; i < len; i++) { + (*data_arrayP)[i].id = res[i]; + } + } + else { + /* Check if each given resource is present */ + for (i = 0; i < *num_dataP && result == COAP_205_CONTENT; i++) { + switch ((*data_arrayP)[i].id) { + case LWM2M_RES_MANUFACTURER: + case LWM2M_RES_MODEL_NO: + case LWM2M_RES_SERIAL: + case LWM2M_RES_FW_VER: + case LWM2M_RES_REBOOT: + case LWM2M_RES_ERROR_CODE: + /* case LWM2M_RES_ERROR_CODE_RESET: TODO */ + case LWM2M_RES_BINDINGS: + case LWM2M_RES_TYPE: + case LWM2M_RES_HW_VERSION: + case LWM2M_RES_SW_VERSION: + break; + default: + result = COAP_404_NOT_FOUND; + } + } + } + + return result; +} + +static uint8_t prv_device_read(uint16_t instance_id, int *num_dataP, + lwm2m_data_t **data_arrayP, + lwm2m_object_t *objectP) +{ + int i; + uint8_t result; + dev_data_t *data = (dev_data_t *)objectP->userData; + + (void)data; + + /* Single instance object */ + if (instance_id != 0) { + return COAP_404_NOT_FOUND; + } + + /* Full object requested */ + if (*num_dataP == 0) { + /* This list must contain all readable resources */ + uint16_t resList[] = { + LWM2M_RES_MANUFACTURER, LWM2M_RES_MODEL_NO, LWM2M_RES_SERIAL, + LWM2M_RES_FW_VER, LWM2M_RES_HW_VERSION, LWM2M_RES_SW_VERSION, + LWM2M_RES_BINDINGS, LWM2M_RES_TYPE, LWM2M_RES_ERROR_CODE, + }; + int cnt = sizeof(resList) / sizeof(uint16_t); + *data_arrayP = lwm2m_data_new(cnt); + if (*data_arrayP == NULL) { + return COAP_500_INTERNAL_SERVER_ERROR; + } + *num_dataP = cnt; + for (i = 0; i < cnt; i++) { + (*data_arrayP)[i].id = resList[i]; + } + } + + for (i = 0; i < *num_dataP; i++) { + switch ((*data_arrayP)[i].id) { + /* Exec resources */ + case LWM2M_RES_REBOOT: + case LWM2M_RES_FRESET: + case LWM2M_RES_ERROR_CODE_RESET: + result = COAP_405_METHOD_NOT_ALLOWED; + break; + /* Statically defined resources */ + case LWM2M_RES_MANUFACTURER: + lwm2m_data_encode_string(LWM2M_DEVICE_MANUFACTURER, + *data_arrayP + i); + result = COAP_205_CONTENT; + break; + case LWM2M_RES_MODEL_NO: + lwm2m_data_encode_string(LWM2M_DEVICE_MODEL, *data_arrayP + i); + result = COAP_205_CONTENT; + break; + case LWM2M_RES_SERIAL: + lwm2m_data_encode_string(LWM2M_DEVICE_SERIAL, *data_arrayP + i); + result = COAP_205_CONTENT; + break; + case LWM2M_RES_FW_VER: + lwm2m_data_encode_string(LWM2M_DEVICE_FW_VERSION, *data_arrayP + i); + result = COAP_205_CONTENT; + break; + case LWM2M_RES_HW_VERSION: + lwm2m_data_encode_string(LWM2M_DEVICE_HW_VERSION, *data_arrayP + i); + result = COAP_205_CONTENT; + break; + case LWM2M_RES_SW_VERSION: + lwm2m_data_encode_string(LWM2M_DEVICE_SW_VERSION, *data_arrayP + i); + result = COAP_205_CONTENT; + break; + case LWM2M_RES_BINDINGS: + lwm2m_data_encode_string(LWM2M_DEVICE_BINDINGS, *data_arrayP + i); + result = COAP_205_CONTENT; + break; + case LWM2M_RES_TYPE: + lwm2m_data_encode_string(LWM2M_DEVICE_TYPE, *data_arrayP + i); + result = COAP_205_CONTENT; + break; + /* Error code is mandatory */ + case LWM2M_RES_ERROR_CODE: + /* TODO: Do this properly */ + lwm2m_data_encode_int(0, *data_arrayP + i); + result = COAP_205_CONTENT; + break; + /* Time resources */ + case LWM2M_RES_TIME: + case LWM2M_RES_TIME_OFFSET: + case LWM2M_RES_TIME_ZONE: + /* What to do with this? */ + case LWM2M_RES_POWER_SRC: + case LWM2M_RES_POWER_VOL: + case LWM2M_RES_POWER_AMP: + case LWM2M_RES_BATTERY_LEVEL: + case LWM2M_RES_MEM_FREE: + case LWM2M_RES_BATTERY_STATUS: + case LWM2M_RES_MEM_TOTAL: + case LWM2M_RES_EXT_DEV_INFO: + default: + result = COAP_404_NOT_FOUND; + } + + if (result != COAP_205_CONTENT) { + break; + } + } + + return result; +} + +static uint8_t prv_device_write(uint16_t instance_id, int num_data, + lwm2m_data_t *data_array, + lwm2m_object_t *objectP) +{ + int i; + uint8_t result = COAP_404_NOT_FOUND; + dev_data_t *data = (dev_data_t *)objectP->userData; + + (void)data; + + (void)instance_id; + (void)num_data; + (void)data_array; + + for (i = 0; i < num_data; i++) { + switch (data_array[i].id) { + /* Writable */ + case LWM2M_RES_TIME: + case LWM2M_RES_TIME_OFFSET: + case LWM2M_RES_TIME_ZONE: + /* TODO: Update stuff here. */ + /* Non-Writable */ + case LWM2M_RES_MANUFACTURER: + case LWM2M_RES_MODEL_NO: + case LWM2M_RES_SERIAL: + case LWM2M_RES_FW_VER: + case LWM2M_RES_REBOOT: + case LWM2M_RES_FRESET: + case LWM2M_RES_POWER_SRC: + case LWM2M_RES_POWER_VOL: + case LWM2M_RES_POWER_AMP: + case LWM2M_RES_BATTERY_LEVEL: + case LWM2M_RES_MEM_FREE: + case LWM2M_RES_ERROR_CODE: + case LWM2M_RES_ERROR_CODE_RESET: + case LWM2M_RES_BINDINGS: + case LWM2M_RES_TYPE: + case LWM2M_RES_HW_VERSION: + case LWM2M_RES_SW_VERSION: + case LWM2M_RES_BATTERY_STATUS: + case LWM2M_RES_MEM_TOTAL: + case LWM2M_RES_EXT_DEV_INFO: + result = COAP_405_METHOD_NOT_ALLOWED; + break; + default: + result = COAP_404_NOT_FOUND; + } + } + + return result; +} + +static uint8_t prv_device_execute(uint16_t instance_id, uint16_t resource_id, + uint8_t *buffer, int length, + lwm2m_object_t *objectP) +{ + uint8_t result; + dev_data_t *data = (dev_data_t *)objectP->userData; + + (void)data; + + (void)buffer; + (void)length; + (void)objectP; + + /* single instance object */ + if (instance_id != 0) { + result = COAP_404_NOT_FOUND; + goto err_out; + } + + if (length != 0) { + result = COAP_400_BAD_REQUEST; + goto err_out; + } + + switch (resource_id) { + case LWM2M_RES_REBOOT: + reboot = true; + result = COAP_204_CHANGED; + break; + case LWM2M_RES_ERROR_CODE_RESET: + /* TODO */ + case LWM2M_RES_FRESET: + /* TODO Callback? */ + default: + result = COAP_405_METHOD_NOT_ALLOWED; + } + +err_out: + return result; +} + +/* + * Call this from the main loop to check whether a reboot was requested. + */ +bool lwm2m_device_reboot_requested(void) +{ + return reboot; +} + +lwm2m_object_t *lwm2m_get_object_device(void) +{ + lwm2m_object_t *obj; + + obj = (lwm2m_object_t *)lwm2m_malloc(sizeof(lwm2m_object_t)); + + if (obj == NULL) { + goto err_out; + } + + memset(obj, 0, sizeof(lwm2m_object_t)); + obj->instanceList = (lwm2m_list_t *)lwm2m_malloc(sizeof(lwm2m_list_t)); + + if (obj->instanceList == NULL) { + goto free_obj; + } + + memset(obj->instanceList, 0, sizeof(lwm2m_list_t)); + + obj->objID = LWM2M_DEVICE_OBJECT_ID; + + obj->readFunc = prv_device_read; + obj->writeFunc = prv_device_write; + obj->executeFunc = prv_device_execute; + obj->discoverFunc = prv_device_discover; + + /* Don't allocate memory for stuff that isn't used at the moment */ + /* obj->userData = lwm2m_malloc(sizeof(dev_data_t)); */ + /* if (obj->userData == NULL) { */ + /* goto free_ilist; */ + /* } */ + /* */ + /* memset(obj->userData, 0, sizeof(dev_data_t)); */ + /* INT USER DATA HERE */ + + return obj; + +/* free_ilist: */ +/* lwm2m_free(obj->instanceList); */ + +free_obj: + lwm2m_free(obj); + +err_out: + return NULL; +} + +void lwm2m_free_object_device(lwm2m_object_t *obj) +{ + if (obj == NULL) { + return; + } + if (obj->userData) { + lwm2m_free(obj->userData); + } + if (obj->instanceList) { + lwm2m_free(obj->instanceList); + } + lwm2m_free(obj); +} diff --git a/examples/wakaama/object_digital_actuator.c b/examples/wakaama/object_digital_actuator.c new file mode 100644 index 000000000000..fed7ae3fa848 --- /dev/null +++ b/examples/wakaama/object_digital_actuator.c @@ -0,0 +1,426 @@ +/* + * Copyright (C) 2018 Beduino Master Projekt - University of Bremen + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +#include +#include +#include +#include +#include +#include "lwm2mclient.h" +#include "xtimer.h" + +/* + * RIOT debugging + */ +#define ENABLE_DEBUG (0) +#include "debug.h" + +#define NOTIFY_VALUE (0x01) +#define NOTIFY_POLARITY (0x02) +#define NOTIFY_APP_TYPE (0x03) + +typedef struct _actuator_data_t { + struct _actuator_data_t *next; /* matches lwm2m_list_t::next */ + uint16_t instance_id; /* matches lwm2m_list_t::id */ + saul_reg_t *dev; /* The device for this sensor */ + uint8_t notify; /* Bitmask for observer notifications */ + bool value; /* Digitial output state (RW) */ + bool polarity; /* 0 = Normal, 1= Reversed (RW) */ + char *app_type; /* Application type (RW) */ +} actuator_data_t; + +typedef struct notification_tuple { + uint8_t mask; + uint16_t id; +} notification_tuple_t; + +static int prv_actuator_write_bool(actuator_data_t *target) +{ + phydat_t data = { + .val[0] = (int16_t)target->value, + .val[1] = 0, + .val[2] = 0, + .scale = 0, + .unit = UNIT_NONE, + }; + + int ret = saul_reg_write(target->dev, &data); + if (ret != 1) { + DEBUG("%s: saul_reg_write() error\n", __func__); + } + return ret; +} + +static bool prv_actuator_read_bool(actuator_data_t *target) +{ + phydat_t ret; + + if (saul_reg_read(target->dev, &ret) < 0) { + DEBUG("%s: saul_reg_read() error\n", __func__); + return false; + } + return (ret.val[0]) ? !target->polarity : target->polarity; +} + +static void priv_update_actuator_value(actuator_data_t *target) +{ + bool old_value = target->value; + target->value = prv_actuator_read_bool(target); + if (target->value != old_value) { + target->notify |= NOTIFY_VALUE; + } +} + +static void prv_init_instance(actuator_data_t *actuator_instance, saul_reg_t *dev) +{ + actuator_instance->dev = dev; + actuator_instance->polarity = false; + actuator_instance->value = prv_actuator_read_bool(actuator_instance); + actuator_instance->app_type = lwm2m_strdup(dev->name); +} + + +static uint8_t prv_get_value(lwm2m_data_t *data, actuator_data_t *target) +{ + switch (data->id) { + case LWM2M_DOUTPUT_ATTR: + lwm2m_data_encode_bool(target->value, data); + return COAP_205_CONTENT; + case LWM2M_DOUTPUT_POLARITY_ATTR: + lwm2m_data_encode_bool(target->polarity, data); + return COAP_205_CONTENT; + case LWM2M_APP_TYPE_ATTR: + lwm2m_data_encode_string(target->app_type, data); + return COAP_205_CONTENT; + } + return COAP_404_NOT_FOUND; +} + +static uint8_t prv_bool_read(uint16_t instance_id, int *num_data, + lwm2m_data_t **data_array, + lwm2m_object_t *object) +{ + int i; + uint8_t result; + actuator_data_t *target = + (actuator_data_t *)lwm2m_list_find(object->instanceList, instance_id); + + if (NULL == target) { + return COAP_404_NOT_FOUND; + } + + /* Server is asking for everything. */ + if (*num_data == 0) { + uint16_t res_list[] = { + LWM2M_DOUTPUT_ATTR, LWM2M_DOUTPUT_POLARITY_ATTR, + LWM2M_APP_TYPE_ATTR, + }; + int nb_res = sizeof(res_list) / sizeof(uint16_t); + + *data_array = lwm2m_data_new(nb_res); + if (*data_array == NULL) { + return COAP_500_INTERNAL_SERVER_ERROR; + } + *num_data = nb_res; + for (i = 0; i < nb_res; i++) { + (*data_array)[i].id = res_list[i]; + } + } + + i = 0; + do { + result = prv_get_value((*data_array) + i, target); + i++; + } while (i < *num_data && result == COAP_205_CONTENT); + + return result; +} + +static uint8_t prv_bool_write(uint16_t instance_id, int num_data, + lwm2m_data_t *data_array, + lwm2m_object_t *object) +{ + int i = 0; + uint8_t result; + bool value; + actuator_data_t *target = + (actuator_data_t *)lwm2m_list_find(object->instanceList, instance_id); + + if (NULL == target) { + return COAP_404_NOT_FOUND; + } + + do { + switch (data_array[i].id) { + case LWM2M_DOUTPUT_POLARITY_ATTR: + if (lwm2m_data_decode_bool(&data_array[i], &value) != 1) { + result = COAP_400_BAD_REQUEST; + } else { + target->notify |= NOTIFY_VALUE; + target->polarity = value; + result = COAP_204_CHANGED; + } + break; + case LWM2M_DOUTPUT_ATTR: + if (lwm2m_data_decode_bool(&data_array[i], &value) != 1) { + result = COAP_400_BAD_REQUEST; + } else { + target->notify |= NOTIFY_POLARITY; + target->value = (value) ? !target->polarity : target->polarity; + prv_actuator_write_bool(target); + result = COAP_204_CHANGED; + } + break; + case LWM2M_APP_TYPE_ATTR: + if (data_array[i].value.asBuffer.length == 0) { + if (target->app_type != NULL) { + lwm2m_free(target->app_type); + } + target->app_type = NULL; + target->notify |= NOTIFY_APP_TYPE; + result = COAP_204_CHANGED; + } else { + char *new = lwm2m_malloc(data_array[i].value.asBuffer.length+1); + if (new == NULL) { + result = COAP_500_INTERNAL_SERVER_ERROR; + } else { + memset(new, '\0', data_array[i].value.asBuffer.length+1); + memcpy(new, (char*)data_array[i].value.asBuffer.buffer, data_array[i].value.asBuffer.length); + if (target->app_type != NULL) { + lwm2m_free(target->app_type); + } + target->app_type = new; + target->notify |= NOTIFY_APP_TYPE; + result = COAP_204_CHANGED; + } + } + break; + default: + result = COAP_404_NOT_FOUND; + break; + } + i++; + } while (i < num_data && result == COAP_204_CHANGED); + + return result; +} + +static uint8_t prv_bool_execute(uint16_t instance_id, uint16_t resource_id, + uint8_t *buffer, int length, + lwm2m_object_t *object) +{ + (void)buffer; + (void)length; + (void)object; + (void)instance_id; + + switch (resource_id) { + case LWM2M_DOUTPUT_POLARITY_ATTR: + case LWM2M_DOUTPUT_ATTR: + case LWM2M_APP_TYPE_ATTR: + return COAP_405_METHOD_NOT_ALLOWED; + } + return COAP_404_NOT_FOUND; +} + +static uint8_t prv_bool_discover(uint16_t instance_id, int *num_data, + lwm2m_data_t **data_array, + lwm2m_object_t *object) +{ + uint8_t result; + int i; + + (void)instance_id; + (void)object; + + result = COAP_205_CONTENT; + + /* Server is asking for everything. */ + if (*num_data == 0) { + uint16_t res_list[] = { + LWM2M_DOUTPUT_POLARITY_ATTR, LWM2M_DOUTPUT_ATTR, + LWM2M_APP_TYPE_ATTR, + }; + int nb_res = sizeof(res_list) / sizeof(uint16_t); + + *data_array = lwm2m_data_new(nb_res); + if (*data_array == NULL) { + return COAP_500_INTERNAL_SERVER_ERROR; + } + *num_data = nb_res; + for (i = 0; i < nb_res; i++) { + (*data_array)[i].id = res_list[i]; + } + } + else { + for (i = 0; i < *num_data && result == COAP_205_CONTENT; i++) { + switch ((*data_array)[i].id) { + case LWM2M_DOUTPUT_POLARITY_ATTR: + case LWM2M_DOUTPUT_ATTR: + case LWM2M_APP_TYPE_ATTR: + break; + default: + result = COAP_404_NOT_FOUND; + } + } + } + + return result; +} + +int lwm2m_get_bool_actuator(lwm2m_object_t **object, saul_reg_t *dev) +{ + actuator_data_t *actuator_instance; + bool self_alloc = false; + + if (object == NULL) { + DEBUG("%s: EINVAL object == NULL\n", __func__); + return -EINVAL; + } + + if (dev == NULL) { + DEBUG("%s: EINVAL dev == NULL\n", __func__); + return -EINVAL; + } + + /* Allocate new object if we didn't get an existing one to add an instance to. */ + if ((*object) == NULL) { + (*object) = (lwm2m_object_t *)lwm2m_malloc(sizeof(lwm2m_object_t)); + if ((*object) == NULL) { + DEBUG("%s: couldn't allocate memory for lwm2m_object_t\n", __func__); + return -ENOMEM; + } + self_alloc = true; + + (*object)->objID = LWM2M_DIGITAL_ACTUATOR_ID; + + (*object)->readFunc = prv_bool_read; + (*object)->writeFunc = prv_bool_write; + (*object)->executeFunc = prv_bool_execute; + (*object)->discoverFunc = prv_bool_discover; + } + else { + /* Check for mismatching sensor IDs */ + if ((*object)->objID != LWM2M_DIGITAL_ACTUATOR_ID) { + DEBUG("%s: mismatching sensor ids\n", __func__); + return -EINVAL; + } + /* If we got an existing object, check whether an instance for this dev already exists */ + lwm2m_list_t *target; + for (target = (*object)->instanceList; target != NULL; target = target->next) { + if (((actuator_data_t *)target)->dev == dev) { + DEBUG("%s: sensor instance for dev already exists\n", __func__); + return -EEXIST; + } + } + } + + actuator_instance = (actuator_data_t *)lwm2m_malloc(sizeof(actuator_data_t)); + if (actuator_instance == NULL) { + DEBUG("%s: couldn't allocate memoru for actuator_data_t\n", __func__); + /* Only free object if we allocated it ourselves + * TODO/FIXME: Maybe never free ever and let the caller worry about that? + */ + if (self_alloc) { + DEBUG("%s: freeing self-allocated lwm2m_object_t\n", __func__); + lwm2m_free((*object)); + } + return -ENOMEM; + } + memset(actuator_instance, 0, sizeof(actuator_data_t)); + + actuator_instance->instance_id = lwm2m_list_newId((*object)->instanceList); + prv_init_instance(actuator_instance, dev); + (*object)->instanceList = LWM2M_LIST_ADD((*object)->instanceList, actuator_instance); + + return 0; +} + +void lwm2m_free_bool_actuator(lwm2m_object_t *obj) +{ + if (obj == NULL) { + return; + } + if (obj->userData) { + lwm2m_free(obj->userData); + } + while (obj->instanceList != NULL) { + actuator_data_t *instance = (actuator_data_t *)obj->instanceList; + obj->instanceList = obj->instanceList->next; + lwm2m_free(instance); + } + lwm2m_free(obj); +} + +int lwm2m_auto_add_bool_actuators(lwm2m_context_t *context) +{ + int added = 0; + int id = LWM2M_DIGITAL_ACTUATOR_ID; + int added_tmp = 0; + saul_reg_t *reg; + lwm2m_object_t *object = NULL; + + /* Collect all devices for the current object ID */ + for (reg = saul_reg; reg != NULL; reg = reg->next) { + if (lwm2m_saul_type_to_obj_id(reg->driver->type) == id) { + DEBUG("%s: adding instance of %s\n", __func__, + lwm2m_object_id_to_str(id)); + if (lwm2m_get_bool_actuator(&object, reg) == 0) { + added_tmp++; + } + } + } + if (object != NULL) { + if (lwm2m_add_object(context, object) == COAP_NO_ERROR) { + added += added_tmp; + } else { + DEBUG("%s: lwm2m_add_object() failed for %s\n", __func__, + lwm2m_object_id_to_str(id)); + } + } + + return added; +} + +void lwm2m_poll_bool_actuators(lwm2m_context_t *lwm2mH) +{ + lwm2m_object_t *object = (lwm2m_object_t *)lwm2mH->objectList; + + DEBUG("%s: updating sensor values", __func__); + + for (; object != NULL; object = object->next) { + if (object->objID == LWM2M_DIGITAL_ACTUATOR_ID) { + lwm2m_uri_t uri; + uri.flag = LWM2M_URI_FLAG_OBJECT_ID | LWM2M_URI_FLAG_INSTANCE_ID | LWM2M_URI_FLAG_RESOURCE_ID; + uri.objectId = object->objID; + actuator_data_t *instance = (actuator_data_t *)object->instanceList; + + for (; instance != NULL; instance = instance->next) { + uint8_t i; + notification_tuple_t notify_array[] = { + {NOTIFY_VALUE, LWM2M_DOUTPUT_ATTR}, + {NOTIFY_POLARITY, LWM2M_DOUTPUT_POLARITY_ATTR}, + {NOTIFY_APP_TYPE, LWM2M_DOUTPUT_ATTR}, + }; + priv_update_actuator_value(instance); + + for (i = 0; i < sizeof(notify_array) / sizeof(notification_tuple_t); i++) { + if (instance->notify & notify_array[i].mask) { + instance->notify &= ~notify_array[i].mask; + uri.resourceId = notify_array[i].id; + lwm2m_resource_value_changed(lwm2mH, &uri); + DEBUG("%s: sensor value changed for %"PRIu16"/%"PRIu16"/%"PRIu16"\n", + __func__, uri.objectId, uri.instanceId, + uri.resourceId); + } + } + } + } + } +} + diff --git a/examples/wakaama/object_digital_sensor.c b/examples/wakaama/object_digital_sensor.c new file mode 100644 index 000000000000..396d437b80c1 --- /dev/null +++ b/examples/wakaama/object_digital_sensor.c @@ -0,0 +1,494 @@ +/* + * Copyright (C) 2018 Beduino Master Projekt - University of Bremen + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +#include +#include +#include +#include +#include +#include "lwm2mclient.h" +#include "xtimer.h" + +#ifdef MODULE_SAUL_GPIO +#include "saul/periph.h" +#endif /* MODULE_SAUL_GPIO */ + +/* + * RIOT debugging + */ +#define ENABLE_DEBUG (0) +#include "debug.h" + +/* + * Bit mask entries for observer notifications + */ +#define NOTIFY_VALUE (0x01) +#define NOTIFY_COUNT (0x02) +#define NOTIFY_POLARITY (0x04) +#define NOTIFY_DEBOUNCE (0x08) +#define NOTIFY_EDGE_SELECT (0x10) + +typedef struct _sensor_data_t { + struct _sensor_data_t *next; /* matches lwm2m_list_t::next */ + uint16_t instance_id; /* matches lwm2m_list_t::id */ + saul_reg_t *dev; /* The device for this sensor */ + uint8_t notify; /* Bitmask for observer notifications */ + bool value; /* Most recently measured value (RO) */ + uint16_t count; /* Cumulative value of active state detected (RO) */ + bool polarity; /* 0 = Normal, 1= Reversed (RW) */ + uint32_t debounce; /* Debounce value in ms; (internally stored as us) (RW) */ + uint32_t debounce_timer; /* Timestamp since last measurement for debouncing (internal) */ + uint8_t edge_select; /* 1 == falling, 2 == rising, 3 == both; (RW) */ + const char *sensor_type; /* Sensor type (RO) */ +} sensor_data_t; + +typedef struct notification_tuple { + uint8_t mask; + uint16_t id; +} notification_tuple_t; + +static bool prv_sensor_read_bool(sensor_data_t *target) +{ + phydat_t ret; + uint32_t now = xtimer_now_usec(); + + /* Skip measuring until debounce time expired */ + if (now - target->debounce_timer < target->debounce) { + return target->value; + } + + target->debounce_timer = now; + + if (saul_reg_read(target->dev, &ret) < 0) { + DEBUG("%s: saul_reg_read() error\n", __func__); + return false; + } + return (ret.val[0]) ? !target->polarity : target->polarity; +} + +static void prv_update_bool_sensor(sensor_data_t *target) +{ + bool newval = prv_sensor_read_bool(target); + if (newval && !target->value) { + target->count++; + target->notify |= NOTIFY_COUNT; + } + if (newval != target->value) { + target->notify |= NOTIFY_VALUE; + } + target->value = newval; +} + +#ifdef MODULE_SAUL_GPIO +static void gpio_isr(void *arg) +{ + prv_update_bool_sensor((sensor_data_t*)arg); +} + +static uint32_t prv_edge_select(uint8_t es) { + switch (es) { + case 1: + return GPIO_FALLING; + case 2: + return GPIO_RISING; + case 3: + default: + return GPIO_BOTH; + } +} + +static void prv_set_interrupt(sensor_data_t *target) +{ + /* Register an interrupt to update sensor value */ + const saul_gpio_params_t *sdev = (const saul_gpio_params_t *)target->dev->dev; + if (gpio_init_int(sdev->pin, sdev->mode, + prv_edge_select(target->edge_select), gpio_isr, + (void*)target)) { + DEBUG("%s: Error registering gpio interrupt.\n", __func__); + } +} +#else /* MODULE_SAUL_GPIO */ +static void prv_set_interrupt(sensor_data_t *target) +{ + (void)target; +} +#endif /* MODULE_SAUL_GPIO */ + +static void prv_init_instance(sensor_data_t *sensor_instance, saul_reg_t *dev) +{ + sensor_instance->dev = dev; + sensor_instance->notify = 0; + sensor_instance->count = 0; + sensor_instance->polarity = false; + sensor_instance->debounce = 0; + sensor_instance->debounce_timer = 0; + sensor_instance->edge_select = 3; + sensor_instance->sensor_type = dev->name; + prv_update_bool_sensor(sensor_instance); + prv_set_interrupt(sensor_instance); +} + + +static uint8_t prv_get_value(lwm2m_data_t *data, sensor_data_t *target) +{ + switch (data->id) { + case LWM2M_DINPUT_ATTR: + lwm2m_data_encode_bool(target->value, data); + return COAP_205_CONTENT; + case LWM2M_DINPUT_COUNTER_ATTR: + lwm2m_data_encode_int(target->count, data); + return COAP_205_CONTENT; + case LWM2M_DINPUT_POLARITY_ATTR: + lwm2m_data_encode_bool(target->polarity, data); + return COAP_205_CONTENT; + case LWM2M_DINPUT_DEBOUNCE_ATTR: + lwm2m_data_encode_int(target->debounce / US_PER_MS, data); + return COAP_205_CONTENT; + case LWM2M_DINPUT_EDGESELECT_ATTR: + lwm2m_data_encode_int(target->edge_select, data); + return COAP_205_CONTENT; + case LWM2M_SENSOR_TYPE_ATTR: + lwm2m_data_encode_string(target->sensor_type, data); + return COAP_205_CONTENT; + case LWM2M_DINPUT_COUNTER_RESET_ATTR: + return COAP_405_METHOD_NOT_ALLOWED; + } + return COAP_404_NOT_FOUND; +} + +static uint8_t prv_bool_read(uint16_t instance_id, int *num_data, + lwm2m_data_t **data_array, + lwm2m_object_t *object) +{ + int i; + uint8_t result; + sensor_data_t *target = + (sensor_data_t *)lwm2m_list_find(object->instanceList, instance_id); + + if (NULL == target) { + return COAP_404_NOT_FOUND; + } + + /* Server is asking for everything. */ + if (*num_data == 0) { + uint16_t res_list[] = { + LWM2M_DINPUT_ATTR, LWM2M_DINPUT_COUNTER_ATTR, + LWM2M_DINPUT_POLARITY_ATTR, LWM2M_DINPUT_DEBOUNCE_ATTR, + LWM2M_DINPUT_EDGESELECT_ATTR, LWM2M_DINPUT_COUNTER_RESET_ATTR, + }; + int nb_res = sizeof(res_list) / sizeof(uint16_t); + + *data_array = lwm2m_data_new(nb_res); + if (*data_array == NULL) { + return COAP_500_INTERNAL_SERVER_ERROR; + } + *num_data = nb_res; + for (i = 0; i < nb_res; i++) { + (*data_array)[i].id = res_list[i]; + } + } + + i = 0; + do { + result = prv_get_value((*data_array) + i, target); + i++; + } while (i < *num_data && result == COAP_205_CONTENT); + + return result; +} + +static uint8_t prv_bool_write(uint16_t instance_id, int num_data, + lwm2m_data_t *data_array, + lwm2m_object_t *object) +{ + int i = 0; + uint8_t result; + union { + bool b; + int64_t i; + } value; + sensor_data_t *target = + (sensor_data_t *)lwm2m_list_find(object->instanceList, instance_id); + + if (NULL == target) { + return COAP_404_NOT_FOUND; + } + + do { + switch (data_array[i].id) { + case LWM2M_DINPUT_ATTR: + case LWM2M_DINPUT_COUNTER_ATTR: + case LWM2M_DINPUT_COUNTER_RESET_ATTR: + return COAP_405_METHOD_NOT_ALLOWED; + case LWM2M_DINPUT_POLARITY_ATTR: + if (lwm2m_data_decode_bool(&data_array[i], &value.b) != 1) { + result = COAP_400_BAD_REQUEST; + } else { + target->notify |= NOTIFY_POLARITY; + target->polarity = value.b; + result = COAP_204_CHANGED; + } + case LWM2M_DINPUT_DEBOUNCE_ATTR: + if (lwm2m_data_decode_int(&data_array[i], &value.i) != 1) { + result = COAP_400_BAD_REQUEST; + } else { + target->notify |= NOTIFY_DEBOUNCE; + target->debounce = (uint32_t)value.i * US_PER_MS; + result = COAP_204_CHANGED; + } + case LWM2M_DINPUT_EDGESELECT_ATTR: + if (lwm2m_data_decode_int(&data_array[i], &value.i) != 1) { + result = COAP_400_BAD_REQUEST; + } else if (value.i < 1 || value.i > 3) { + result = COAP_400_BAD_REQUEST; + } else { + target->notify |= NOTIFY_EDGE_SELECT; + target->edge_select = (uint32_t)value.i; + prv_set_interrupt(target); + result = COAP_204_CHANGED; + } + default: + result = COAP_404_NOT_FOUND; + break; + } + i++; + } while (i < num_data && result == COAP_204_CHANGED); + + return result; +} + +static uint8_t prv_bool_execute(uint16_t instance_id, uint16_t resource_id, + uint8_t *buffer, int length, + lwm2m_object_t *object) +{ + (void)buffer; + (void)length; + sensor_data_t *target = + (sensor_data_t *)lwm2m_list_find(object->instanceList, instance_id); + if (NULL == target) { + return COAP_404_NOT_FOUND; + } + + switch (resource_id) { + case LWM2M_DINPUT_ATTR: + case LWM2M_DINPUT_COUNTER_ATTR: + case LWM2M_DINPUT_POLARITY_ATTR: + case LWM2M_DINPUT_DEBOUNCE_ATTR: + case LWM2M_DINPUT_EDGESELECT_ATTR: + case LWM2M_SENSOR_TYPE_ATTR: + return COAP_405_METHOD_NOT_ALLOWED; + case LWM2M_DINPUT_COUNTER_RESET_ATTR: + target->count = 0; + return COAP_204_CHANGED; + } + return COAP_404_NOT_FOUND; +} + +static uint8_t prv_bool_discover(uint16_t instance_id, int *num_data, + lwm2m_data_t **data_array, + lwm2m_object_t *object) +{ + uint8_t result; + int i; + + (void)instance_id; + (void)object; + + result = COAP_205_CONTENT; + + /* Server is asking for everything. */ + if (*num_data == 0) { + uint16_t res_list[] = { + LWM2M_DINPUT_ATTR, LWM2M_DINPUT_COUNTER_ATTR, + LWM2M_DINPUT_POLARITY_ATTR, LWM2M_DINPUT_DEBOUNCE_ATTR, + LWM2M_DINPUT_EDGESELECT_ATTR, LWM2M_DINPUT_COUNTER_RESET_ATTR, + }; + int nb_res = sizeof(res_list) / sizeof(uint16_t); + + *data_array = lwm2m_data_new(nb_res); + if (*data_array == NULL) { + return COAP_500_INTERNAL_SERVER_ERROR; + } + *num_data = nb_res; + for (i = 0; i < nb_res; i++) { + (*data_array)[i].id = res_list[i]; + } + } + else { + for (i = 0; i < *num_data && result == COAP_205_CONTENT; i++) { + switch ((*data_array)[i].id) { + case LWM2M_DINPUT_ATTR: + case LWM2M_DINPUT_COUNTER_ATTR: + case LWM2M_DINPUT_POLARITY_ATTR: + case LWM2M_DINPUT_DEBOUNCE_ATTR: + case LWM2M_DINPUT_EDGESELECT_ATTR: + case LWM2M_SENSOR_TYPE_ATTR: + case LWM2M_DINPUT_COUNTER_RESET_ATTR: + break; + default: + result = COAP_404_NOT_FOUND; + } + } + } + + return result; +} + +int lwm2m_get_bool_sensor(lwm2m_object_t **object, saul_reg_t *dev) +{ + sensor_data_t *sensor_instance; + bool self_alloc = false; + + if (object == NULL) { + DEBUG("%s: EINVAL object == NULL\n", __func__); + return -EINVAL; + } + + if (dev == NULL) { + DEBUG("%s: EINVAL dev == NULL\n", __func__); + return -EINVAL; + } + + /* Allocate new object if we didn't get an existing one to add an instance to. */ + if ((*object) == NULL) { + (*object) = (lwm2m_object_t *)lwm2m_malloc(sizeof(lwm2m_object_t)); + if ((*object) == NULL) { + DEBUG("%s: couldn't allocate memory for lwm2m_object_t\n", __func__); + return -ENOMEM; + } + self_alloc = true; + + (*object)->objID = LWM2M_DIGITAL_SENSOR_ID; + + (*object)->readFunc = prv_bool_read; + (*object)->writeFunc = prv_bool_write; + (*object)->executeFunc = prv_bool_execute; + (*object)->discoverFunc = prv_bool_discover; + } + else { + /* Check for mismatching sensor IDs */ + if ((*object)->objID != LWM2M_DIGITAL_SENSOR_ID) { + DEBUG("%s: mismatching sensor ids\n", __func__); + return -EINVAL; + } + /* If we got an existing object, check whether an instance for this dev already exists */ + lwm2m_list_t *target; + for (target = (*object)->instanceList; target != NULL; target = target->next) { + if (((sensor_data_t *)target)->dev == dev) { + DEBUG("%s: sensor instance for dev already exists\n", __func__); + return -EEXIST; + } + } + } + + sensor_instance = (sensor_data_t *)lwm2m_malloc(sizeof(sensor_data_t)); + if (sensor_instance == NULL) { + DEBUG("%s: couldn't allocate memoru for sensor_data_t\n", __func__); + /* Only free object if we allocated it ourselves + * TODO/FIXME: Maybe never free ever and let the caller worry about that? + */ + if (self_alloc) { + DEBUG("%s: freeing self-allocated lwm2m_object_t\n", __func__); + lwm2m_free((*object)); + } + return -ENOMEM; + } + memset(sensor_instance, 0, sizeof(sensor_data_t)); + + sensor_instance->instance_id = lwm2m_list_newId((*object)->instanceList); + prv_init_instance(sensor_instance, dev); + (*object)->instanceList = LWM2M_LIST_ADD((*object)->instanceList, sensor_instance); + + return 0; +} + +void lwm2m_free_bool_sensor(lwm2m_object_t *obj) +{ + if (obj == NULL) { + return; + } + if (obj->userData) { + lwm2m_free(obj->userData); + } + while (obj->instanceList != NULL) { + sensor_data_t *instance = (sensor_data_t *)obj->instanceList; + obj->instanceList = obj->instanceList->next; + lwm2m_free(instance); + } + lwm2m_free(obj); +} + +int lwm2m_auto_add_bool_sensors(lwm2m_context_t *context) +{ + int added = 0; + int id = LWM2M_DIGITAL_SENSOR_ID; + int added_tmp = 0; + saul_reg_t *reg; + lwm2m_object_t *object = NULL; + + /* Collect all devices for the current object ID */ + for (reg = saul_reg; reg != NULL; reg = reg->next) { + if (lwm2m_saul_type_to_obj_id(reg->driver->type) == id) { + DEBUG("%s: adding instance of %s\n", __func__, + lwm2m_object_id_to_str(id)); + if (lwm2m_get_bool_sensor(&object, reg) == 0) { + added_tmp++; + } + } + } + if (object != NULL) { + if (lwm2m_add_object(context, object) == COAP_NO_ERROR) { + added += added_tmp; + } else { + DEBUG("%s: lwm2m_add_object() failed for %s\n", __func__, + lwm2m_object_id_to_str(id)); + } + } + + return added; +} + +void lwm2m_poll_bool_sensors(lwm2m_context_t *lwm2mH) +{ + lwm2m_object_t *object = (lwm2m_object_t *)lwm2mH->objectList; + + DEBUG("%s: updating sensor values", __func__); + + for (; object != NULL; object = object->next) { + if (object->objID == LWM2M_DIGITAL_SENSOR_ID) { + lwm2m_uri_t uri; + uri.flag = LWM2M_URI_FLAG_OBJECT_ID | LWM2M_URI_FLAG_INSTANCE_ID | LWM2M_URI_FLAG_RESOURCE_ID; + uri.objectId = object->objID; + sensor_data_t *instance = (sensor_data_t *)object->instanceList; + + for (; instance != NULL; instance = instance->next) { + uint8_t i; + notification_tuple_t notify_array[] = { + {NOTIFY_VALUE, LWM2M_DINPUT_ATTR}, + {NOTIFY_COUNT, LWM2M_DINPUT_COUNTER_ATTR}, + {NOTIFY_POLARITY, LWM2M_DINPUT_COUNTER_ATTR}, + {NOTIFY_DEBOUNCE, LWM2M_DINPUT_DEBOUNCE_ATTR}, + {NOTIFY_EDGE_SELECT, LWM2M_DINPUT_EDGESELECT_ATTR}, + }; + + prv_update_bool_sensor(instance); + + for (i = 0; i < sizeof(notify_array) / sizeof(notification_tuple_t); i++) { + if (instance->notify & notify_array[i].mask) { + instance->notify &= ~notify_array[i].mask; + uri.resourceId = notify_array[i].id; + lwm2m_resource_value_changed(lwm2mH, &uri); + DEBUG("%s: sensor value changed for %"PRIu16"/%"PRIu16"/%"PRIu16"\n", + __func__, uri.objectId, uri.instanceId, + uri.resourceId); + } + } + } + } + } +} diff --git a/examples/wakaama/object_float_sensor.c b/examples/wakaama/object_float_sensor.c new file mode 100644 index 000000000000..a4de48ffdc03 --- /dev/null +++ b/examples/wakaama/object_float_sensor.c @@ -0,0 +1,478 @@ +/* + * Copyright (C) 2018 Beduino Master Projekt - University of Bremen + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +#include +#include +#include +#include "lwm2mclient.h" + +/* + * RIOT debugging + */ +#define ENABLE_DEBUG (0) +#include "debug.h" + +#define min(a, b) (((a) < (b)) ? (a) : (b)) +#define max(a, b) (((a) > (b)) ? (a) : (b)) + +typedef struct _sensor_data_t { + struct _sensor_data_t *next; /* matches lwm2m_list_t::next */ + uint16_t instance_id; /* matches lwm2m_list_t::id */ + saul_reg_t *dev; /* The device for this sensor */ + const char *unit; /* Unit the sensor returns (RO) */ + float current_measured; /* Most recently measured value (RO) */ + float min_measured; /* Smallest measured value (RO) */ + float max_measured; /* Largest measured value (RO) */ + const char *sensor_type; /* The sensor type (RO) */ + /*char *app_type; * Application type (RW)* + float min_range; * Lowest measurable value (RO) * + float max_range; * Highest measurable value (RO) */ +} sensor_data_t; + +static float prv_sensor_read_float(saul_reg_t *dev) +{ + phydat_t ret; + + if (saul_reg_read(dev, &ret) < 0) { + DEBUG("%s: saul_reg_read() error\n", __func__); + return 0; + } + return (float)ret.val[0] * powf(10, ret.scale); +} + +static const char *prv_sensor_get_unit(saul_reg_t *dev) +{ + phydat_t ret; + + if (saul_reg_read(dev, &ret) < 0) { + DEBUG("%s: saul_reg_read() error\n", __func__); + return NULL; + } + return phydat_unit_to_str(ret.unit); +} + +static void prv_update_float_sensor(sensor_data_t *target) +{ + target->current_measured = prv_sensor_read_float(target->dev); + target->max_measured = max(target->current_measured, target->max_measured); + target->min_measured = min(target->current_measured, target->min_measured); + DEBUG("%s: measured new sensor value %f\n", __func__, target->current_measured); +} + +static void prv_init_instance(sensor_data_t *sensor_instance, saul_reg_t *dev) +{ + sensor_instance->dev = dev; + sensor_instance->sensor_type = dev->name; + sensor_instance->unit = prv_sensor_get_unit(dev); + sensor_instance->current_measured = prv_sensor_read_float(sensor_instance->dev); + sensor_instance->max_measured = sensor_instance->current_measured; + sensor_instance->min_measured = sensor_instance->current_measured; +} + +static uint8_t prv_get_value(lwm2m_data_t *data, sensor_data_t *target) +{ + switch (data->id) { + case LWM2M_SENSOR_VALUE_ATTR: + case LWM2M_SENSOR_ANALOG_INPUT_ATTR: + lwm2m_data_encode_float(target->current_measured, data); + return COAP_205_CONTENT; + case LWM2M_SENSOR_UNITS_ATTR: + lwm2m_data_encode_string(target->unit, data); + return COAP_205_CONTENT; + case LWM2M_MIN_MEASURED_ATTR: + lwm2m_data_encode_float(target->min_measured, data); + return COAP_205_CONTENT; + case LWM2M_MAX_MEASURED_ATTR: + lwm2m_data_encode_float(target->max_measured, data); + return COAP_205_CONTENT; + /*case LWM2M_MIN_RANGE_ATTR: + lwm2m_data_encode_float(target->min_range, data); + return COAP_205_CONTENT; + case LWM2M_MAX_RANGE_ATTR: + lwm2m_data_encode_float(target->max_range, data); + return COAP_205_CONTENT;*/ + case LWM2M_RESET_MIN_MAX_MEASURED_ATTR: + return COAP_405_METHOD_NOT_ALLOWED; + case LWM2M_SENSOR_TYPE_ATTR: + lwm2m_data_encode_string(target->sensor_type, data); + return COAP_205_CONTENT; + } + return COAP_404_NOT_FOUND; +} + +static uint8_t prv_float_read(uint16_t instance_id, int *num_data, + lwm2m_data_t **data_array, + lwm2m_object_t *object) +{ + int i; + uint8_t result; + sensor_data_t *target = + (sensor_data_t *)lwm2m_list_find(object->instanceList, instance_id); + + if (NULL == target) { + return COAP_404_NOT_FOUND; + } + + /* Server is asking for everything. */ + if (*num_data == 0) { + uint16_t res_list[] = { + LWM2M_SENSOR_ANALOG_INPUT_ATTR, + LWM2M_SENSOR_VALUE_ATTR, LWM2M_SENSOR_UNITS_ATTR, LWM2M_MIN_MEASURED_ATTR, + LWM2M_MAX_MEASURED_ATTR, LWM2M_SENSOR_TYPE_ATTR, + /*LWM2M_MIN_RANGE_ATTR, LWM2M_MAX_RANGE_ATTR,*/ + }; + int nb_res = sizeof(res_list) / sizeof(uint16_t); + + *data_array = lwm2m_data_new(nb_res); + if (*data_array == NULL) { + return COAP_500_INTERNAL_SERVER_ERROR; + } + *num_data = nb_res; + for (i = 0; i < nb_res; i++) { + (*data_array)[i].id = res_list[i]; + } + } + + i = 0; + do { + result = prv_get_value((*data_array) + i, target); + i++; + } while (i < *num_data && result == COAP_205_CONTENT); + + return result; +} + +static uint8_t prv_float_write(uint16_t instance_id, int num_data, + lwm2m_data_t *data_array, + lwm2m_object_t *object) +{ + (void)instance_id; + (void)object; + int i; + + for (i = 0; i < num_data; i++) { + switch (data_array[i].id) { + case LWM2M_SENSOR_VALUE_ATTR: + case LWM2M_SENSOR_ANALOG_INPUT_ATTR: + case LWM2M_SENSOR_UNITS_ATTR: + case LWM2M_MIN_MEASURED_ATTR: + case LWM2M_MAX_MEASURED_ATTR: + /* case LWM2M_MIN_RANGE_ATTR: + case LWM2M_MAX_RANGE_ATTR: */ + case LWM2M_RESET_MIN_MAX_MEASURED_ATTR: + case LWM2M_SENSOR_TYPE_ATTR: + return COAP_405_METHOD_NOT_ALLOWED; + } + } + return COAP_404_NOT_FOUND; +} + +static uint8_t prv_float_execute(uint16_t instance_id, uint16_t resource_id, + uint8_t *buffer, int length, + lwm2m_object_t *object) +{ + (void)buffer; + (void)length; + float value; + sensor_data_t *target = + (sensor_data_t *)lwm2m_list_find(object->instanceList, instance_id); + if (NULL == target) { + return COAP_404_NOT_FOUND; + } + + switch (resource_id) { + case LWM2M_SENSOR_VALUE_ATTR: + case LWM2M_SENSOR_ANALOG_INPUT_ATTR: + case LWM2M_SENSOR_UNITS_ATTR: + case LWM2M_MIN_MEASURED_ATTR: + case LWM2M_MAX_MEASURED_ATTR: + case LWM2M_SENSOR_TYPE_ATTR: + /* case LWM2M_MIN_RANGE_ATTR: + case LWM2M_MAX_RANGE_ATTR: */ + return COAP_405_METHOD_NOT_ALLOWED; + case LWM2M_RESET_MIN_MAX_MEASURED_ATTR: + value = prv_sensor_read_float(target->dev); + target->max_measured = value; + target->min_measured = value; + return COAP_204_CHANGED; + } + return COAP_404_NOT_FOUND; +} + +static uint8_t prv_float_discover(uint16_t instance_id, int *num_data, + lwm2m_data_t **data_array, + lwm2m_object_t *object) +{ + uint8_t result; + int i; + + (void)instance_id; + (void)object; + + result = COAP_205_CONTENT; + + /* Server is asking for everything. */ + if (*num_data == 0) { + uint16_t res_list[] = { + LWM2M_SENSOR_ANALOG_INPUT_ATTR, + LWM2M_SENSOR_VALUE_ATTR, LWM2M_SENSOR_UNITS_ATTR, LWM2M_MIN_MEASURED_ATTR, + LWM2M_MAX_MEASURED_ATTR, /*LWM2M_MIN_RANGE_ATTR, LWM2M_MAX_RANGE_ATTR,*/ + }; + int nb_res = sizeof(res_list) / sizeof(uint16_t); + + *data_array = lwm2m_data_new(nb_res); + if (*data_array == NULL) { + return COAP_500_INTERNAL_SERVER_ERROR; + } + *num_data = nb_res; + for (i = 0; i < nb_res; i++) { + (*data_array)[i].id = res_list[i]; + } + } + else { + for (i = 0; i < *num_data && result == COAP_205_CONTENT; i++) { + switch ((*data_array)[i].id) { + case LWM2M_SENSOR_ANALOG_INPUT_ATTR: + case LWM2M_SENSOR_VALUE_ATTR: + case LWM2M_SENSOR_UNITS_ATTR: + case LWM2M_MIN_MEASURED_ATTR: + case LWM2M_MAX_MEASURED_ATTR: + /* case LWM2M_MIN_RANGE_ATTR: + case LWM2M_MAX_RANGE_ATTR: */ + case LWM2M_RESET_MIN_MAX_MEASURED_ATTR: + case LWM2M_SENSOR_TYPE_ATTR: + break; + default: + result = COAP_404_NOT_FOUND; + } + } + } + + return result; +} + +static bool prv_sensor_supported(int sensor_id) +{ + switch (sensor_id) { + case LWM2M_GENERIC_SENSOR_ID: + case LWM2M_ILLUMINANCE_SENSOR_ID: + case LWM2M_ANALOG_SENSOR_ID: + case LWM2M_TEMPERATURE_SENSOR_ID: + case LWM2M_HUMIDITY_SENSOR_ID: + case LWM2M_BAROMETER_SENSOR_ID: + case LWM2M_VOLTAGE_SENSOR_ID: + case LWM2M_CURRENT_SENSOR_ID: + case LWM2M_FREQUENCY_SENSOR_ID: + case LWM2M_DEPTH_SENSOR_ID: + case LWM2M_PERCENTAGE_SENSOR_ID: + case LWM2M_ALTITUDE_SENSOR_ID: + case LWM2M_LOAD_SENSOR_ID: + case LWM2M_PRESSURE_SENSOR_ID: + case LWM2M_LOUDNESS_SENSOR_ID: + case LWM2M_CONCENTRATION_SENSOR_ID: + case LWM2M_ACIDITY_SENSOR_ID: + case LWM2M_CONDUCTIVITY_SENSOR_ID: + case LWM2M_POWER_SENSOR_ID: + case LWM2M_POWER_FACTOR_SENSOR_ID: + case LWM2M_DISTANCE_SENSOR_ID: + case LWM2M_RATE_SENSOR_ID: + return true; + default: + return false; + } +} + +int lwm2m_get_float_sensor(lwm2m_object_t **object, int sensor_id, saul_reg_t *dev) +{ + sensor_data_t *sensor_instance; + bool self_alloc = false; + + if (object == NULL) { + DEBUG("%s: EINVAL object == NULL\n", __func__); + return -EINVAL; + } + + if (!prv_sensor_supported(sensor_id)) { + DEBUG("%s: EINVAL invalid sensor_id\n", __func__); + return -EINVAL; + } + + if (dev == NULL) { + DEBUG("%s: EINVAL dev == NULL\n", __func__); + return -EINVAL; + } + + /* Allocate new object if we didn't get an existing one to add an instance to. */ + if ((*object) == NULL) { + (*object) = (lwm2m_object_t *)lwm2m_malloc(sizeof(lwm2m_object_t)); + if ((*object) == NULL) { + DEBUG("%s: couldn't allocate memory for lwm2m_object_t\n", __func__); + return -ENOMEM; + } + self_alloc = true; + + (*object)->objID = sensor_id; + + (*object)->readFunc = prv_float_read; + (*object)->writeFunc = prv_float_write; + (*object)->executeFunc = prv_float_execute; + (*object)->discoverFunc = prv_float_discover; + } + else { + /* Check for mismatching sensor IDs */ + if ((*object)->objID != sensor_id) { + DEBUG("%s: mismatching sensor ids\n", __func__); + return -EINVAL; + } + + /* If we got an existing object, check whether an instance for this dev already exists */ + lwm2m_list_t *target; + for (target = (*object)->instanceList; target != NULL; target = target->next) { + if (((sensor_data_t *)target)->dev == dev) { + DEBUG("%s: sensor instance for dev already exists\n", __func__); + return -EEXIST; + } + } + } + + sensor_instance = (sensor_data_t *)lwm2m_malloc(sizeof(sensor_data_t)); + if (sensor_instance == NULL) { + DEBUG("%s: couldn't allocate memoru for sensor_data_t\n", __func__); + /* Only free object if we allocated it ourselves + * TODO/FIXME: Maybe never free ever and let the caller worry about that? + */ + if (self_alloc) { + DEBUG("%s: freeing self-allocated lwm2m_object_t\n", __func__); + lwm2m_free((*object)); + } + return -ENOMEM; + } + memset(sensor_instance, 0, sizeof(sensor_data_t)); + + sensor_instance->instance_id = lwm2m_list_newId((*object)->instanceList); + prv_init_instance(sensor_instance, dev); + (*object)->instanceList = LWM2M_LIST_ADD((*object)->instanceList, sensor_instance); + + return 0; +} + +void lwm2m_free_float_sensor(lwm2m_object_t *obj) +{ + if (obj == NULL) { + return; + } + if (obj->userData) { + lwm2m_free(obj->userData); + } + while (obj->instanceList != NULL) { + sensor_data_t *instance = (sensor_data_t *)obj->instanceList; + obj->instanceList = obj->instanceList->next; + lwm2m_free(instance); + } + lwm2m_free(obj); +} + +int lwm2m_auto_add_float_sensors(lwm2m_context_t *context) +{ + int added = 0; + int i; + + uint16_t ids[] = { + LWM2M_GENERIC_SENSOR_ID, LWM2M_ILLUMINANCE_SENSOR_ID, + LWM2M_ANALOG_SENSOR_ID, LWM2M_TEMPERATURE_SENSOR_ID, + LWM2M_HUMIDITY_SENSOR_ID, LWM2M_BAROMETER_SENSOR_ID, + LWM2M_VOLTAGE_SENSOR_ID, LWM2M_CURRENT_SENSOR_ID, + LWM2M_FREQUENCY_SENSOR_ID, LWM2M_DEPTH_SENSOR_ID, + LWM2M_PERCENTAGE_SENSOR_ID, LWM2M_ALTITUDE_SENSOR_ID, + LWM2M_LOAD_SENSOR_ID, LWM2M_PRESSURE_SENSOR_ID, + LWM2M_LOUDNESS_SENSOR_ID, LWM2M_CONCENTRATION_SENSOR_ID, + LWM2M_ACIDITY_SENSOR_ID, LWM2M_CONDUCTIVITY_SENSOR_ID, + LWM2M_POWER_SENSOR_ID, LWM2M_POWER_FACTOR_SENSOR_ID, + LWM2M_DISTANCE_SENSOR_ID, LWM2M_RATE_SENSOR_ID, + }; + + /* Iterate over all supported object IDs */ + for (i = 0; i < (int)(sizeof(ids) / sizeof(uint16_t)); i++) { + int added_tmp = 0; + saul_reg_t *reg; + lwm2m_object_t *object = NULL; + /* Collect all devices for the current object ID */ + for (reg = saul_reg; reg != NULL; reg = reg->next) { + if ((int)ids[i] == lwm2m_saul_type_to_obj_id(reg->driver->type)) { + DEBUG("%s: adding instance of %s\n", __func__, + lwm2m_object_id_to_str(ids[i])); + if (lwm2m_get_float_sensor(&object, ids[i], reg) == 0) { + added_tmp++; + } + } + } + if (object != NULL) { + if (lwm2m_add_object(context, object) == COAP_NO_ERROR) { + added += added_tmp; + } else { + DEBUG("%s: lwm2m_add_object() failed for %s\n", __func__, + lwm2m_object_id_to_str(ids[i])); + } + } + } + + return added; +} + +void lwm2m_poll_float_sensors(lwm2m_context_t *lwm2mH) +{ + lwm2m_object_t *object = (lwm2m_object_t *)lwm2mH->objectList; + + DEBUG("%s: updating sensor values", __func__); + + for (; object != NULL; object = object->next) { + if (prv_sensor_supported(object->objID)) { + lwm2m_uri_t uri; + uri.flag = LWM2M_URI_FLAG_OBJECT_ID | LWM2M_URI_FLAG_INSTANCE_ID | LWM2M_URI_FLAG_RESOURCE_ID; + uri.objectId = object->objID; + sensor_data_t *instance = (sensor_data_t *)object->instanceList; + + for (; instance != NULL; instance = instance->next) { + float current = instance->current_measured; + float max = instance->max_measured; + float min = instance->min_measured; + + uri.instanceId = instance->instance_id; + + prv_update_float_sensor(instance); + + if (current != instance->current_measured) { + uri.resourceId = LWM2M_SENSOR_VALUE_ATTR; + lwm2m_resource_value_changed(lwm2mH, &uri); + DEBUG("%s: sensor value changed for %"PRIu16"/%"PRIu16"/%"PRIu16"\n", + __func__, uri.objectId, uri.instanceId, + uri.resourceId); + uri.resourceId = LWM2M_SENSOR_ANALOG_INPUT_ATTR; + lwm2m_resource_value_changed(lwm2mH, &uri); + DEBUG("%s: sensor value changed for %"PRIu16"/%"PRIu16"/%"PRIu16"\n", + __func__, uri.objectId, uri.instanceId, + uri.resourceId); + } + if (max != instance->max_measured) { + uri.resourceId = LWM2M_MAX_MEASURED_ATTR; + lwm2m_resource_value_changed(lwm2mH, &uri); + DEBUG("%s: max measured changed for %"PRIu16"/%"PRIu16"/%"PRIu16"\n", + __func__, uri.objectId, uri.instanceId, + uri.resourceId); + } + if (min != instance->min_measured) { + uri.resourceId = LWM2M_MIN_MEASURED_ATTR; + lwm2m_resource_value_changed(lwm2mH, &uri); + DEBUG("%s: min measured changed for %"PRIu16"/%"PRIu16"/%"PRIu16"\n", + __func__, uri.objectId, uri.instanceId, + uri.resourceId); + } + } + } + } +} diff --git a/examples/wakaama/platform.c b/examples/wakaama/platform.c new file mode 100644 index 000000000000..0ae4657347c6 --- /dev/null +++ b/examples/wakaama/platform.c @@ -0,0 +1,228 @@ +/******************************************************************************* +* +* Copyright (c) 2013, 2014, 2015 Intel Corporation and others. +* All rights reserved. This program and the accompanying materials +* are made available under the terms of the Eclipse Public License v1.0 +* and Eclipse Distribution License v1.0 which accompany this distribution. +* +* The Eclipse Public License is available at +* http://www.eclipse.org/legal/epl-v10.html +* The Eclipse Distribution License is available at +* http://www.eclipse.org/org/documents/edl-v10.php. +* +* Contributors: +* David Navarro, Intel Corporation - initial API and implementation +* Christian Manal - Ported to RIOT OS +*******************************************************************************/ + +#include +#include +#include +#include +#include +#include + +#include "lwm2mconfig.h" +#include "memarray_limits.h" + +/* + * RIOT debugging + */ +#define ENABLE_DEBUG (0) +#include "debug.h" + +/* + * Raise statistics about memory allocation. + * (Uncomment to enable). + */ +#if ENABLE_DEBUG == 1 +/* #define DEBUG_MALLOC */ +#endif /* ENABLE_DEBUG */ + +#ifndef LWM2M_MEMORY_TRACE + +#ifdef DEBUG_MALLOC +#define max(a, b) ((a) > (b)) ? (a) : (b) +uint16_t cnt16 = 0; +uint16_t cnt16_max = 0; +uint16_t cnt32 = 0; +uint16_t cnt32_max = 0; +uint16_t cnt64 = 0; +uint16_t cnt64_max = 0; +uint16_t cnt192 = 0; +uint16_t cnt192_max = 0; +uint16_t cnt512 = 0; +uint16_t cnt512_max = 0; +#endif /* DEBUG_MALLOC */ + +uint32_t b16_data[4][MAX_16BYTE_BLOCKS]; +uint32_t b32_data[8][MAX_32BYTE_BLOCKS]; +uint32_t b64_data[16][MAX_64BYTE_BLOCKS]; +uint32_t b192_data[48][MAX_192BYTE_BLOCKS]; +uint32_t b512_data[128][MAX_512BYTE_BLOCKS]; +memarray_t b16_storage; +memarray_t b32_storage; +memarray_t b64_storage; +memarray_t b192_storage; +memarray_t b512_storage; + +void lwm2m_memarray_print_stats(void) +{ +#ifdef DEBUG_MALLOC + DEBUG("cnt16: %"PRIu16"/%u max: %"PRIu16"\n", cnt16, MAX_16BYTE_BLOCKS, cnt16_max); + DEBUG("cnt32: %"PRIu16"/%u max: %"PRIu16"\n", cnt32, MAX_32BYTE_BLOCKS, cnt32_max); + DEBUG("cnt64: %"PRIu16"/%u max: %"PRIu16"\n", cnt64, MAX_64BYTE_BLOCKS, cnt64_max); + DEBUG("cnt192: %"PRIu16"/%u max: %"PRIu16"\n", cnt192, MAX_192BYTE_BLOCKS, cnt192_max); + DEBUG("cnt512: %"PRIu16"/%u max: %"PRIu16"\n", cnt512, MAX_512BYTE_BLOCKS, cnt512_max); +#endif /* DEBUG_MALLOC */ + return; +} + +void lwm2m_memarray_init(void) +{ + memarray_init(&b16_storage, b16_data, 16, MAX_16BYTE_BLOCKS); + memarray_init(&b32_storage, b32_data, 32, MAX_32BYTE_BLOCKS); + memarray_init(&b64_storage, b64_data, 64, MAX_64BYTE_BLOCKS); + memarray_init(&b192_storage, b192_data, 192, MAX_192BYTE_BLOCKS); + memarray_init(&b512_storage, b512_data, 512, MAX_512BYTE_BLOCKS); +} + +void *lwm2m_malloc(size_t s) +{ + if (s <= 16) { +#ifdef DEBUG_MALLOC + cnt16++; + cnt16_max = max(cnt16, cnt16_max); +#endif /* DEBUG_MALLOC */ + return memarray_alloc(&b16_storage); + } + else if (s <= 32) { +#ifdef DEBUG_MALLOC + cnt32++; + cnt32_max = max(cnt32, cnt32_max); +#endif /* DEBUG_MALLOC */ + return memarray_alloc(&b32_storage); + } + else if (s <= 64) { +#ifdef DEBUG_MALLOC + cnt64++; + cnt64_max = max(cnt64, cnt64_max); +#endif /* DEBUG_MALLOC */ + return memarray_alloc(&b64_storage); + } + else if (s <= 192) { +#ifdef DEBUG_MALLOC + cnt192++; + cnt192_max = max(cnt192, cnt192_max); +#endif /* DEBUG_MALLOC */ + return memarray_alloc(&b192_storage); + } + else if (s <= 512) { +#ifdef DEBUG_MALLOC + cnt512++; + cnt512_max = max(cnt512, cnt512_max); +#endif /* DEBUG_MALLOC */ + return memarray_alloc(&b512_storage); + } + DEBUG("lwm2m_malloc(): Requested %u bytes of memory but got no pool for this size.\n", s); + return NULL; +} + +void lwm2m_free(void *p) +{ + uintptr_t start = (uintptr_t)b16_data; + uintptr_t end = start + (uintptr_t)sizeof(b16_data); + uintptr_t pi = (uintptr_t)p; + + if (pi >= start && pi < end) { +#ifdef DEBUG_MALLOC + cnt16--; +#endif /* DEBUG_MALLOC */ + memarray_free(&b16_storage, p); + return; + } + + start = (uintptr_t)b32_data; + end = start + (uintptr_t)sizeof(b32_data); + if (pi >= start && pi < end) { +#ifdef DEBUG_MALLOC + cnt32--; +#endif /* DEBUG_MALLOC */ + memarray_free(&b32_storage, p); + return; + } + + start = (uintptr_t)b64_data; + end = start + (uintptr_t)sizeof(b64_data); + if (pi >= start && pi < end) { +#ifdef DEBUG_MALLOC + cnt64--; +#endif /* DEBUG_MALLOC */ + memarray_free(&b64_storage, p); + return; + } + + start = (uintptr_t)b192_data; + end = start + (uintptr_t)sizeof(b192_data); + if (pi >= start && pi < end) { +#ifdef DEBUG_MALLOC + cnt192--; +#endif /* DEBUG_MALLOC */ + memarray_free(&b192_storage, p); + return; + } + + start = (uintptr_t)b512_data; + end = start + (uintptr_t)sizeof(b512_data); + if (pi >= start && pi < end) { +#ifdef DEBUG_MALLOC + cnt512--; +#endif /* DEBUG_MALLOC */ + memarray_free(&b512_storage, p); + return; + } + + DEBUG("lwm2m_free(): Tried to free memory block that doesn't belong to a known pool\n"); + return; +} + +char *lwm2m_strdup(const char *str) +{ + size_t len = strlen(str) + 1; + void *new = lwm2m_malloc(len); + + if (new == NULL) { + return NULL; + } + + return strncpy(new, str, len); +} + +#endif /* LWM2M_MEMORY_TRACE */ + +int lwm2m_strncmp(const char *s1, const char *s2, size_t n) +{ + return strncmp(s1, s2, n); +} + +time_t lwm2m_gettime(void) +{ + struct timeval tv; + + if (0 != gettimeofday(&tv, NULL)) { + return -1; + } + + return tv.tv_sec; +} + +void lwm2m_printf(const char *format, ...) +{ + va_list ap; + + va_start(ap, format); + + vfprintf(stderr, format, ap); + + va_end(ap); +} diff --git a/pkg/wakaama/Makefile b/pkg/wakaama/Makefile index 2079cf4e5a63..68c7cce29171 100644 --- a/pkg/wakaama/Makefile +++ b/pkg/wakaama/Makefile @@ -1,6 +1,6 @@ PKG_NAME=wakaama PKG_URL=https://github.com/eclipse/wakaama.git -PKG_VERSION=ee80c224622684ee47c17c57918904cffd00c4d2 +PKG_VERSION=40663176dc647146e8d318dbe7cbf7e5aff27ac8 PKG_LICENSE=EDL-1.0,EPL-1.0 .PHONY: all @@ -12,6 +12,9 @@ patch: git-download mkdir -p "$(PKG_BUILDDIR)/riotbuild" cp $(PKG_BUILDDIR)/core/*.c $(PKG_BUILDDIR)/core/*.h $(PKG_BUILDDIR)/riotbuild cp $(PKG_BUILDDIR)/core/er-coap-13/*.c $(PKG_BUILDDIR)/core/er-coap-13/*.h $(PKG_BUILDDIR)/riotbuild + cp $(PKG_BUILDDIR)/examples/client/object_server.c $(PKG_BUILDDIR)/riotbuild + cp $(PKG_BUILDDIR)/examples/client/object_security.c $(PKG_BUILDDIR)/riotbuild + cp $(PKG_BUILDDIR)/examples/client/object_access_control.c $(PKG_BUILDDIR)/riotbuild echo 'MODULE:=wakaama' > $(PKG_BUILDDIR)/riotbuild/Makefile echo 'include $$(RIOTBASE)/Makefile.base' >> $(PKG_BUILDDIR)/riotbuild/Makefile diff --git a/pkg/wakaama/Makefile.include b/pkg/wakaama/Makefile.include index 1bfb90a6553b..7c3de093bbfa 100644 --- a/pkg/wakaama/Makefile.include +++ b/pkg/wakaama/Makefile.include @@ -1 +1 @@ -INCLUDES += -I$(RIOTPKG)/wakaama/wakaama +INCLUDES += -I$(PKGDIRBASE)/wakaama/riotbuild diff --git a/pkg/wakaama/patches/0009-fixed-warnings-in-observe.c.patch b/pkg/wakaama/patches/0009-fixed-warnings-in-observe.c.patch index 177220af9316..8e39b6f6fe1f 100644 --- a/pkg/wakaama/patches/0009-fixed-warnings-in-observe.c.patch +++ b/pkg/wakaama/patches/0009-fixed-warnings-in-observe.c.patch @@ -1,25 +1,25 @@ -From 9c30634e5458827e8e398ca56a4f5294a61b1b92 Mon Sep 17 00:00:00 2001 +From 13db42af916c31f9b6407a4f6f8ff800f3ac769b Mon Sep 17 00:00:00 2001 From: Christian Manal -Date: Wed, 29 Nov 2017 15:33:46 +0100 -Subject: [PATCH 09/12] fixed warnings in observe.c +Date: Mon, 7 May 2018 18:26:08 +0200 +Subject: [PATCH] fixed warnings in observe.c --- core/observe.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/core/observe.c b/core/observe.c -index 711c1d1..87c42b9 100644 +index 0f2f1dd..8858a68 100644 --- a/core/observe.c +++ b/core/observe.c -@@ -163,6 +163,7 @@ uint8_t observe_handleRequest(lwm2m_context_t * contextP, - { +@@ -164,6 +164,7 @@ uint8_t observe_handleRequest(lwm2m_context_t * contextP, + lwm2m_observed_t * observedP; lwm2m_watcher_t * watcherP; uint32_t count; + (void)size; LOG_ARG("Code: %02X, server status: %s", message->code, STR_STATUS(serverP->status)); LOG_URI(uriP); -@@ -649,7 +650,7 @@ void observe_step(lwm2m_context_t * contextP, +@@ -653,7 +654,7 @@ void observe_step(lwm2m_context_t * contextP, { LOG_ARG("Checking minimal period (%d s)", watcherP->parameters->minPeriod); @@ -28,7 +28,7 @@ index 711c1d1..87c42b9 100644 { // Minimum Period did not elapse yet interval = watcherP->lastTime + watcherP->parameters->minPeriod - currentTime; -@@ -671,7 +672,7 @@ void observe_step(lwm2m_context_t * contextP, +@@ -675,7 +676,7 @@ void observe_step(lwm2m_context_t * contextP, { LOG_ARG("Checking maximal period (%d s)", watcherP->parameters->maxPeriod); diff --git a/pkg/wakaama/patches/0013-fixed-warnings-in-example-objects.patch b/pkg/wakaama/patches/0013-fixed-warnings-in-example-objects.patch new file mode 100644 index 000000000000..280f6e6c2fb7 --- /dev/null +++ b/pkg/wakaama/patches/0013-fixed-warnings-in-example-objects.patch @@ -0,0 +1,101 @@ +From 2ab2f722a18025be55f12f482975b66548ce307b Mon Sep 17 00:00:00 2001 +From: Christian Manal +Date: Tue, 9 Jan 2018 10:27:14 +0100 +Subject: [PATCH] fixed warnings in example objects + +--- + examples/client/object_access_control.c | 2 +- + examples/client/object_security.c | 16 ---------------- + examples/client/object_server.c | 22 ++++++---------------- + 3 files changed, 7 insertions(+), 33 deletions(-) + +diff --git a/examples/client/object_access_control.c b/examples/client/object_access_control.c +index 053546d..4a02438 100644 +--- a/examples/client/object_access_control.c ++++ b/examples/client/object_access_control.c +@@ -261,7 +261,7 @@ static uint8_t prv_write_resources(uint16_t instanceId, int numData, + acc_ctrl_ri_t* acValListSave = accCtrlOiP->accCtrlValList; + accCtrlOiP->accCtrlValList = NULL; + +- int ri; ++ unsigned int ri; + lwm2m_data_t* subTlvArray = tlvArray[i].value.asChildren.array; + + if (tlvArray[i].value.asChildren.count == 0) +diff --git a/examples/client/object_security.c b/examples/client/object_security.c +index 901f92e..bfb5f85 100644 +--- a/examples/client/object_security.c ++++ b/examples/client/object_security.c +@@ -479,22 +479,6 @@ void copy_security_object(lwm2m_object_t * objectDest, lwm2m_object_t * objectSr + } + } + +-void display_security_object(lwm2m_object_t * object) +-{ +-#ifdef WITH_LOGS +- fprintf(stdout, " /%u: Security object, instances:\r\n", object->objID); +- security_instance_t * instance = (security_instance_t *)object->instanceList; +- while (instance != NULL) +- { +- fprintf(stdout, " /%u/%u: instanceId: %u, uri: %s, isBootstrap: %s, shortId: %u, clientHoldOffTime: %u\r\n", +- object->objID, instance->instanceId, +- instance->instanceId, instance->uri, instance->isBootstrap ? "true" : "false", +- instance->shortID, instance->clientHoldOffTime); +- instance = (security_instance_t *)instance->next; +- } +-#endif +-} +- + void clean_security_object(lwm2m_object_t * objectP) + { + while (objectP->instanceList != NULL) +diff --git a/examples/client/object_server.c b/examples/client/object_server.c +index 2f0531e..d762c9b 100644 +--- a/examples/client/object_server.c ++++ b/examples/client/object_server.c +@@ -150,6 +150,9 @@ static uint8_t prv_server_discover(uint16_t instanceId, + uint8_t result; + int i; + ++ (void)instanceId; ++ (void)objectP; ++ + result = COAP_205_CONTENT; + + // is the server asking for the full object ? +@@ -339,6 +342,9 @@ static uint8_t prv_server_execute(uint16_t instanceId, + { + server_instance_t * targetP; + ++ (void)buffer; ++ (void)length; ++ + targetP = (server_instance_t *)lwm2m_list_find(objectP->instanceList, instanceId); + if (NULL == targetP) return COAP_404_NOT_FOUND; + +@@ -428,22 +434,6 @@ void copy_server_object(lwm2m_object_t * objectDest, lwm2m_object_t * objectSrc) + } + } + +-void display_server_object(lwm2m_object_t * object) +-{ +-#ifdef WITH_LOGS +- fprintf(stdout, " /%u: Server object, instances:\r\n", object->objID); +- server_instance_t * serverInstance = (server_instance_t *)object->instanceList; +- while (serverInstance != NULL) +- { +- fprintf(stdout, " /%u/%u: instanceId: %u, shortServerId: %u, lifetime: %u, storing: %s, binding: %s\r\n", +- object->objID, serverInstance->instanceId, +- serverInstance->instanceId, serverInstance->shortServerId, serverInstance->lifetime, +- serverInstance->storing ? "true" : "false", serverInstance->binding); +- serverInstance = (server_instance_t *)serverInstance->next; +- } +-#endif +-} +- + lwm2m_object_t * get_server_object(int serverId, + const char* binding, + int lifetime, +-- +2.1.4 + diff --git a/pkg/wakaama/patches/0014-Use-lwm2m_strdup-instead-of-strdup.patch b/pkg/wakaama/patches/0014-Use-lwm2m_strdup-instead-of-strdup.patch new file mode 100644 index 000000000000..ca125ed85443 --- /dev/null +++ b/pkg/wakaama/patches/0014-Use-lwm2m_strdup-instead-of-strdup.patch @@ -0,0 +1,25 @@ +From 47d78e80ceb80a06e0cb9b0b7f7c959d04bd2c22 Mon Sep 17 00:00:00 2001 +From: Christian Manal +Date: Mon, 5 Feb 2018 15:52:10 +0100 +Subject: [PATCH] Use lwm2m_strdup() instead of strdup() + +--- + examples/client/object_security.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/examples/client/object_security.c b/examples/client/object_security.c +index bfb5f85..d91f6e5 100644 +--- a/examples/client/object_security.c ++++ b/examples/client/object_security.c +@@ -540,7 +540,7 @@ lwm2m_object_t * get_security_object(int serverId, + targetP->securityMode = LWM2M_SECURITY_MODE_PRE_SHARED_KEY; + if (bsPskId) + { +- targetP->publicIdentity = strdup(bsPskId); ++ targetP->publicIdentity = lwm2m_strdup(bsPskId); + targetP->publicIdLen = strlen(bsPskId); + } + if (pskLen > 0) +-- +2.1.4 + diff --git a/pkg/wakaama/patches/0015-fixed-alignment-problem-in-lwm2m_data_t.patch b/pkg/wakaama/patches/0015-fixed-alignment-problem-in-lwm2m_data_t.patch new file mode 100644 index 000000000000..f2006b1c7119 --- /dev/null +++ b/pkg/wakaama/patches/0015-fixed-alignment-problem-in-lwm2m_data_t.patch @@ -0,0 +1,32 @@ +From 5d0fd8783b734e8adeec3b9393e84dc8f4f7eb1e Mon Sep 17 00:00:00 2001 +From: Christian Manal +Date: Wed, 16 May 2018 11:30:02 +0200 +Subject: [PATCH] fixed alignment problem in lwm2m_data_t + +--- + core/liblwm2m.h | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/core/liblwm2m.h b/core/liblwm2m.h +index 3f16a6d..53616bf 100644 +--- a/core/liblwm2m.h ++++ b/core/liblwm2m.h +@@ -299,7 +299,6 @@ typedef struct _lwm2m_data_t lwm2m_data_t; + + struct _lwm2m_data_t + { +- lwm2m_data_type_t type; + uint16_t id; + union + { +@@ -322,6 +321,7 @@ struct _lwm2m_data_t + uint16_t objectInstanceId; + } asObjLink; + } value; ++ lwm2m_data_type_t type; + }; + + typedef enum +-- +2.1.4 +