diff --git a/apps/ipso-objects/Makefile.ipso-objects b/apps/ipso-objects/Makefile.ipso-objects new file mode 100644 index 000000000..8de44f196 --- /dev/null +++ b/apps/ipso-objects/Makefile.ipso-objects @@ -0,0 +1,3 @@ +ipso-objects_src = ipso-temperature.c ipso-button.c ipso-leds-control.c \ + ipso-light-control.c ipso-objects.c +CFLAGS += -DWITH_IPSO=1 diff --git a/apps/ipso-objects/ipso-button.c b/apps/ipso-objects/ipso-button.c new file mode 100644 index 000000000..f40410ea0 --- /dev/null +++ b/apps/ipso-objects/ipso-button.c @@ -0,0 +1,163 @@ +/* + * Copyright (c) 2015, Yanzi Networks AB. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of the copyright holder 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 HOLDER 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 HOLDER 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. + */ + +/** + * \addtogroup ipso-objects + * @{ + */ + +/** + * \file + * Implementation of OMA LWM2M / IPSO button as a digital input + * \author + * Joakim Eriksson + * Niclas Finne + */ + +#include "contiki.h" +#include "lwm2m-object.h" +#include "lwm2m-engine.h" +#include "er-coap-engine.h" + +#define DEBUG 0 +#if DEBUG +#include +#define PRINTF(...) printf(__VA_ARGS__) +#else +#define PRINTF(...) +#endif + +#if PLATFORM_HAS_BUTTON +#include "dev/button-sensor.h" + +PROCESS(ipso_button_process, "ipso-button"); +#endif /* PLATFORM_HAS_BUTTON */ + +static int input_state = 0; +static int polarity = 0; +static int32_t counter = 0; +static int32_t edge_selection = 3; +static int32_t debounce_time = 10; +/*---------------------------------------------------------------------------*/ +static int +read_state(lwm2m_context_t *ctx, uint8_t *outbuf, size_t outsize) +{ + int value; + if(polarity == 0) { + value = input_state ? 1 : 0; + } else { + value = input_state ? 0 : 1; + } + PRINTF("Read button state (polarity=%d, state=%d): %d\n", + polarity, input_state, value); + return ctx->writer->write_boolean(ctx, outbuf, outsize, value); +} +/*---------------------------------------------------------------------------*/ +static int +reset_counter(lwm2m_context_t *ctx, const uint8_t *arg, size_t len, + uint8_t *outbuf, size_t outlen) +{ + counter = 0; + return 0; +} +/*---------------------------------------------------------------------------*/ +LWM2M_RESOURCES(button_resources, + LWM2M_RESOURCE_CALLBACK(5500, { read_state, NULL, NULL }), + LWM2M_RESOURCE_INTEGER_VAR(5501, &counter), + LWM2M_RESOURCE_BOOLEAN_VAR(5502, &polarity), + LWM2M_RESOURCE_INTEGER_VAR(5503, &debounce_time), + LWM2M_RESOURCE_INTEGER_VAR(5504, &edge_selection), + LWM2M_RESOURCE_CALLBACK(5505, { NULL, NULL, reset_counter }), + LWM2M_RESOURCE_STRING(5751, "Button") + ); +LWM2M_INSTANCES(button_instances, + LWM2M_INSTANCE(0, button_resources)); +LWM2M_OBJECT(button, 3200, button_instances); +/*---------------------------------------------------------------------------*/ +void +ipso_button_init(void) +{ + /* register this device and its handlers - the handlers automatically + sends in the object to handle */ + lwm2m_engine_register_object(&button); + +#if PLATFORM_HAS_BUTTON + process_start(&ipso_button_process, NULL); +#endif /* PLATFORM_HAS_BUTTON */ +} +/*---------------------------------------------------------------------------*/ +#if PLATFORM_HAS_BUTTON +PROCESS_THREAD(ipso_button_process, ev, data) +{ + static struct etimer timer; + int32_t time; + + PROCESS_BEGIN(); + + SENSORS_ACTIVATE(button_sensor); + + while(1) { + PROCESS_WAIT_EVENT(); + + if(ev == sensors_event && data == &button_sensor) { + if(!input_state) { + input_state = 1; + counter++; + if((edge_selection & 2) != 0) { + lwm2m_object_notify_observers(&button, "/0/5500"); + } + lwm2m_object_notify_observers(&button, "/0/5501"); + + time = (debounce_time * CLOCK_SECOND / 1000); + if(time < 1) { + time = 1; + } + etimer_set(&timer, (clock_time_t)time); + } + } else if(ev == PROCESS_EVENT_TIMER && data == &timer) { + if(!input_state) { + /* Button is not in pressed state */ + } else if(button_sensor.value(0) != 0) { + /* Button is still pressed */ + etimer_reset(&timer); + } else { + input_state = 0; + if((edge_selection & 1) != 0) { + lwm2m_object_notify_observers(&button, "/0/5500"); + } + } + } + } + + PROCESS_END(); +} +#endif /* PLATFORM_HAS_BUTTON */ +/*---------------------------------------------------------------------------*/ +/** @} */ diff --git a/apps/ipso-objects/ipso-leds-control.c b/apps/ipso-objects/ipso-leds-control.c new file mode 100644 index 000000000..578dac3d2 --- /dev/null +++ b/apps/ipso-objects/ipso-leds-control.c @@ -0,0 +1,236 @@ +/* + * Copyright (c) 2015, Yanzi Networks AB. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of the copyright holder 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 HOLDER 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 HOLDER 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. + */ + +/** + * \addtogroup ipso-objects + * @{ + * + */ + +/** + * \file + * Implementation of OMA LWM2M / IPSO Light Control for LEDs + * \author + * Joakim Eriksson + * Niclas Finne + */ + +#include "lwm2m-object.h" +#include "lwm2m-engine.h" +#include "er-coap-engine.h" +#include "dev/leds.h" +#include + +#define DEBUG 0 +#if DEBUG +#include +#define PRINTF(...) printf(__VA_ARGS__) +#else +#define PRINTF(...) +#endif + +#if LEDS_ALL & LEDS_BLUE || LEDS_ALL & LEDS_RED || LEDS_ALL & LEDS_BLUE +#define LEDS_CONTROL_NUMBER (((LEDS_ALL & LEDS_BLUE) ? 1 : 0) + ((LEDS_ALL & LEDS_RED) ? 1 : 0) + ((LEDS_ALL & LEDS_GREEN) ? 1 : 0)) +#else +#define LEDS_CONTROL_NUMBER 1 +#endif + +struct led_state { + unsigned long last_on_time; + uint32_t total_on_time; + uint8_t is_on; + uint8_t led_value; +}; + +static struct led_state states[LEDS_CONTROL_NUMBER]; +static lwm2m_instance_t leds_control_instances[LEDS_CONTROL_NUMBER]; +/*---------------------------------------------------------------------------*/ +static int +read_state(lwm2m_context_t *ctx, uint8_t *outbuf, size_t outsize) +{ + uint8_t idx = ctx->object_instance_index; + if(idx >= LEDS_CONTROL_NUMBER) { + return 0; + } + return ctx->writer->write_boolean(ctx, outbuf, outsize, + states[idx].is_on ? 1 : 0); +} +/*---------------------------------------------------------------------------*/ +static int +write_state(lwm2m_context_t *ctx, const uint8_t *inbuf, size_t insize, + uint8_t *outbuf, size_t outsize) +{ + int value; + size_t len; + + uint8_t idx = ctx->object_instance_index; + if(idx >= LEDS_CONTROL_NUMBER) { + return 0; + } + + len = ctx->reader->read_boolean(ctx, inbuf, insize, &value); + if(len > 0) { + if(value) { + if(!states[idx].is_on) { + states[idx].is_on = 1; + states[idx].last_on_time = clock_seconds(); +#if PLATFORM_HAS_LEDS + leds_on(states[idx].led_value); +#endif /* PLATFORM_HAS_LEDS */ + } + } else if(states[idx].is_on) { + states[idx].total_on_time += clock_seconds() - states[idx].last_on_time; + states[idx].is_on = 0; +#if PLATFORM_HAS_LEDS + leds_off(states[idx].led_value); +#endif /* PLATFORM_HAS_LEDS */ + } + } else { + PRINTF("IPSO leds control - ignored illegal write to on/off\n"); + } + return len; +} +/*---------------------------------------------------------------------------*/ +static char * +get_color(int value) { + switch(value) { + case LEDS_GREEN: + return "Green"; + case LEDS_RED: + return "Red"; + case LEDS_BLUE: + return "Blue"; + } + return "None"; +} + +static int +read_color(lwm2m_context_t *ctx, uint8_t *outbuf, size_t outsize) +{ + char *value; + uint8_t idx = ctx->object_instance_index; + if(idx >= LEDS_CONTROL_NUMBER) { + return 0; + } + value = get_color(states[idx].led_value); + return ctx->writer->write_string(ctx, outbuf, outsize, + value, strlen(value)); +} +/*---------------------------------------------------------------------------*/ +static int +read_on_time(lwm2m_context_t *ctx, uint8_t *outbuf, size_t outsize) +{ + unsigned long now; + uint8_t idx = ctx->object_instance_index; + if(idx >= LEDS_CONTROL_NUMBER) { + return 0; + } + + if(states[idx].is_on) { + /* Update the on time */ + now = clock_seconds(); + states[idx].total_on_time += now - states[idx].last_on_time; + states[idx].last_on_time = now; + } + return ctx->writer->write_int(ctx, outbuf, outsize, + (int32_t)states[idx].total_on_time); +} +/*---------------------------------------------------------------------------*/ +static int +write_on_time(lwm2m_context_t *ctx, + const uint8_t *inbuf, size_t insize, + uint8_t *outbuf, size_t outsize) +{ + int32_t value; + size_t len; + uint8_t idx = ctx->object_instance_index; + if(idx >= LEDS_CONTROL_NUMBER) { + return 0; + } + + len = ctx->reader->read_int(ctx, inbuf, insize, &value); + if(len > 0 && value == 0) { + PRINTF("IPSO leds control - reset On Time\n"); + states[idx].total_on_time = 0; + if(states[idx].is_on) { + states[idx].last_on_time = clock_seconds(); + } + } else { + PRINTF("IPSO leds control - ignored illegal write to On Time\n"); + } + return len; +} +/*---------------------------------------------------------------------------*/ +LWM2M_RESOURCES(leds_control_resources, + LWM2M_RESOURCE_CALLBACK(5850, { read_state, write_state, NULL }), + LWM2M_RESOURCE_CALLBACK(5706, { read_color, NULL, NULL }), + LWM2M_RESOURCE_CALLBACK(5852, { read_on_time, write_on_time, NULL }) + ); +LWM2M_OBJECT(leds_control, 3311, leds_control_instances); +/*---------------------------------------------------------------------------*/ +static int +bit_no(int bit) +{ + int i; + for(i = 0; i < 8; i++) { + if(LEDS_ALL & (1 << i)) { + if(bit == 0) { + /* matching bit */ + return 1 << i; + } else { + /* matching but used */ + bit--; + } + } + } + return 0; +} + +void +ipso_leds_control_init(void) +{ + lwm2m_instance_t template = LWM2M_INSTANCE(0, leds_control_resources); + int i; + + /* Initialize the instances */ + for(i = 0; i < LEDS_CONTROL_NUMBER; i++) { + leds_control_instances[i] = template; + leds_control_instances[i].id = i; + states[i].led_value = bit_no(i); + } + + /* register this device and its handlers - the handlers automatically + sends in the object to handle */ + lwm2m_engine_register_object(&leds_control); + PRINTF("IPSO leds control initialized with %u instances\n", + LEDS_CONTROL_NUMBER); +} +/*---------------------------------------------------------------------------*/ +/** @} */ diff --git a/apps/ipso-objects/ipso-light-control.c b/apps/ipso-objects/ipso-light-control.c new file mode 100644 index 000000000..02c4b9be2 --- /dev/null +++ b/apps/ipso-objects/ipso-light-control.c @@ -0,0 +1,202 @@ +/* + * Copyright (c) 2015, Yanzi Networks AB. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of the copyright holder 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 HOLDER 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 HOLDER 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. + */ + +/** + * \addtogroup ipso-objects + * @{ + * + */ + +/** + * \file + * Implementation of OMA LWM2M / IPSO Light Control + * \author + * Joakim Eriksson + * Niclas Finne + */ + +#include "ipso-objects.h" +#include "lwm2m-object.h" +#include "lwm2m-engine.h" + +#ifdef IPSO_LIGHT_CONTROL +extern const struct ipso_objects_actuator IPSO_LIGHT_CONTROL; +#endif /* IPSO_LIGHT_CONTROL */ +/*---------------------------------------------------------------------------*/ +static unsigned long last_on_time; +static uint32_t total_on_time; +static int dim_level = 0; +static uint8_t is_on = 0; +/*---------------------------------------------------------------------------*/ +static int +read_state(lwm2m_context_t *ctx, uint8_t *outbuf, size_t outsize) +{ + return ctx->writer->write_boolean(ctx, outbuf, outsize, is_on ? 1 : 0); +} +/*---------------------------------------------------------------------------*/ +static int +write_state(lwm2m_context_t *ctx, const uint8_t *inbuf, size_t insize, + uint8_t *outbuf, size_t outsize) +{ + int value; + size_t len; + + len = ctx->reader->read_boolean(ctx, inbuf, insize, &value); + if(len > 0) { + if(value) { + if(!is_on) { + is_on = 1; + last_on_time = clock_seconds(); + } + } else { + if(is_on) { + total_on_time += clock_seconds() - last_on_time; + is_on = 0; + } + } +#ifdef IPSO_LIGHT_CONTROL + if(IPSO_LIGHT_CONTROL.set_on) { + IPSO_LIGHT_CONTROL.set_on(value); + } else if(IPSO_LIGHT_CONTROL.set_dim_level) { + dim_level = value ? 100 : 0; + IPSO_LIGHT_CONTROL.set_dim_level(dim_level); + } +#endif /* IPSO_LIGHT_CONTROL */ + } + return len; +} +/*---------------------------------------------------------------------------*/ +static int +read_dim(lwm2m_context_t *ctx, uint8_t *outbuf, size_t outsize) +{ + return ctx->writer->write_int(ctx, outbuf, outsize, dim_level); +} +/*---------------------------------------------------------------------------*/ +static int +write_dim(lwm2m_context_t *ctx, const uint8_t *inbuf, size_t insize, + uint8_t *outbuf, size_t outsize) +{ + int32_t value; + size_t len; + + len = ctx->reader->read_int(ctx, inbuf, insize, &value); + if(len > 0) { + if(value < 0) { + value = 0; + } else if(value > 100) { + value = 100; + } + + dim_level = value; + if(value > 0) { + if(!is_on) { + is_on = 1; + last_on_time = clock_seconds(); + } + } else { + if(is_on) { + total_on_time += clock_seconds() - last_on_time; + is_on = 0; + } + } +#ifdef IPSO_LIGHT_CONTROL + if(IPSO_LIGHT_CONTROL.set_dim_level) { + IPSO_LIGHT_CONTROL.set_dim_level(dim_level); + } else if(IPSO_LIGHT_CONTROL.set_on) { + IPSO_LIGHT_CONTROL.set_on(is_on); + } +#endif /* IPSO_LIGHT_CONTROL */ + } + return len; +} +/*---------------------------------------------------------------------------*/ +static int +read_on_time(lwm2m_context_t *ctx, uint8_t *outbuf, size_t outsize) +{ + unsigned long now; + if(is_on) { + /* Update the on time */ + now = clock_seconds(); + total_on_time += now - last_on_time; + last_on_time = now; + } + return ctx->writer->write_int(ctx, outbuf, outsize, (int32_t)total_on_time); +} +/*---------------------------------------------------------------------------*/ +static int +write_on_time(lwm2m_context_t *ctx, + const uint8_t *inbuf, size_t insize, + uint8_t *outbuf, size_t outsize) +{ + int32_t value; + size_t len; + + len = ctx->reader->read_int(ctx, inbuf, insize, &value); + if(len > 0 && value == 0) { + total_on_time = 0; + if(is_on) { + last_on_time = clock_seconds(); + } + } + return len; +} +/*---------------------------------------------------------------------------*/ +LWM2M_RESOURCES(light_control_resources, + LWM2M_RESOURCE_CALLBACK(5850, { read_state, write_state, NULL }), + LWM2M_RESOURCE_CALLBACK(5851, { read_dim, write_dim, NULL }), + LWM2M_RESOURCE_CALLBACK(5852, { read_on_time, write_on_time, NULL }), + ); +LWM2M_INSTANCES(light_control_instances, + LWM2M_INSTANCE(0, light_control_resources)); +LWM2M_OBJECT(light_control, 3311, light_control_instances); +/*---------------------------------------------------------------------------*/ +void +ipso_light_control_init(void) +{ +#ifdef IPSO_LIGHT_CONTROL + if(IPSO_LIGHT_CONTROL.init) { + IPSO_LIGHT_CONTROL.init(); + } + if(IPSO_LIGHT_CONTROL.is_on) { + is_on = IPSO_LIGHT_CONTROL.is_on(); + } + if(IPSO_LIGHT_CONTROL.get_dim_level) { + dim_level = IPSO_LIGHT_CONTROL.get_dim_level(); + if(dim_level > 0 && IPSO_LIGHT_CONTROL.is_on == NULL) { + is_on = 1; + } + } +#endif /* IPSO_LIGHT_CONTROL */ + last_on_time = clock_seconds(); + + lwm2m_engine_register_object(&light_control); +} +/*---------------------------------------------------------------------------*/ +/** @} */ diff --git a/apps/ipso-objects/ipso-objects.c b/apps/ipso-objects/ipso-objects.c new file mode 100644 index 000000000..1e8243f94 --- /dev/null +++ b/apps/ipso-objects/ipso-objects.c @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2015, Yanzi Networks AB. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of the copyright holder 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 HOLDER 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 HOLDER 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. + */ + +/** + * \addtogroup oma-lwm2m + * @{ + */ + +/** + * \file + * Implementation of the IPSO Objects + * \author + * Joakim Eriksson + * Niclas Finne + */ + +#include "contiki.h" +#include "ipso-objects.h" +/*---------------------------------------------------------------------------*/ +void +ipso_objects_init(void) +{ + /* initialize any relevant object for the IPSO Objects */ +#ifdef IPSO_TEMPERATURE + ipso_temperature_init(); +#endif + +#if PLATFORM_HAS_BUTTON + ipso_button_init(); +#endif + +#ifdef IPSO_LIGHT_CONTROL + ipso_light_control_init(); +#elif PLATFORM_HAS_LEDS + ipso_leds_control_init(); +#endif +} +/*---------------------------------------------------------------------------*/ +/** @} */ diff --git a/apps/ipso-objects/ipso-objects.h b/apps/ipso-objects/ipso-objects.h new file mode 100644 index 000000000..13cfecae6 --- /dev/null +++ b/apps/ipso-objects/ipso-objects.h @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2015, Yanzi Networks AB. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of the copyright holder 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 HOLDER 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 HOLDER 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. + */ + +/** + * \addtogroup apps + * @{ + */ + +/** + * \defgroup ipso-objects An implementation of IPSO Objects + * @{ + * + * This application is an implementation of IPSO Objects for + * OMA Lightweight M2M. + */ + +/** + * \file + * Header file for the Contiki IPSO Objects for OMA LWM2M + * \author + * Joakim Eriksson + * Niclas Finne + */ + +#ifndef IPSO_OBJECTS_H_ +#define IPSO_OBJECTS_H_ + +#include "contiki-conf.h" + +void ipso_temperature_init(void); +void ipso_button_init(void); +void ipso_light_control_init(void); +void ipso_leds_control_init(void); + +/* the init function to register the IPSO objects */ +void ipso_objects_init(void); + +struct ipso_objects_actuator { + /** + * \brief Initialize the driver. + */ + void (* init)(void); + + /** + * \brief Check if the actuator is on or off. + * + * \return Zero if the actuator is off and non-zero otherwise. + */ + int (* is_on)(void); + + /** + * \brief Set the actuator to on or off. + * + * \param onoroff Zero to set the actuator to off and non-zero otherwise. + * \return Zero if ok and a non-zero error code otherwise. + */ + int (* set_on)(int onoroff); + + /** + * \brief Set the actuator to on or off. + * + * \param onoroff Zero to set the actuator to off and non-zero otherwise. + * \return Zero if ok and a non-zero error code otherwise. + */ + int (* get_dim_level)(void); + + /** + * \brief Set the dim level of the actuator. + * + * \param level The dim level between 0% and 100%. + * \return Zero if ok and a non-zero error code otherwise. + */ + int (* set_dim_level)(int level); +}; + +struct ipso_objects_sensor { + /** + * \brief Initialize the driver. + */ + void (* init)(void); + + /** + * \brief Read the sensor value in 1/1000 units. + * + * \param value A pointer to the variable to hold the sensor value. + * \return Zero if ok and a non-zero error code otherwise. + */ + int (* read_value)(int32_t *value); +}; + +#endif /* IPSO_OBJECTS_H_ */ +/** + * @} + * @} + */ diff --git a/apps/ipso-objects/ipso-temperature.c b/apps/ipso-objects/ipso-temperature.c new file mode 100644 index 000000000..61c87554a --- /dev/null +++ b/apps/ipso-objects/ipso-temperature.c @@ -0,0 +1,159 @@ +/* + * Copyright (c) 2015, Yanzi Networks AB. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of the copyright holder 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 HOLDER 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 HOLDER 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. + */ + +/** + * \addtogroup ipso-objects + * @{ + */ + +/** + * \file + * Implementation of OMA LWM2M / IPSO Temperature + * \author + * Joakim Eriksson + * Niclas Finne + */ + +#include +#include "ipso-objects.h" +#include "lwm2m-object.h" +#include "lwm2m-engine.h" +#include "er-coap-engine.h" + +#ifdef IPSO_TEMPERATURE +extern const struct ipso_objects_sensor IPSO_TEMPERATURE; +#endif /* IPSO_TEMPERATURE */ + +#ifndef IPSO_TEMPERATURE_MIN +#define IPSO_TEMPERATURE_MIN (-50 * LWM2M_FLOAT32_FRAC) +#endif + +#ifndef IPSO_TEMPERATURE_MAX +#define IPSO_TEMPERATURE_MAX (80 * LWM2M_FLOAT32_FRAC) +#endif + +static struct ctimer periodic_timer; +static int32_t min_temp; +static int32_t max_temp; +static int read_temp(int32_t *value); +/*---------------------------------------------------------------------------*/ +static int +temp(lwm2m_context_t *ctx, uint8_t *outbuf, size_t outsize) +{ + int32_t value; + if(read_temp(&value)) { + return ctx->writer->write_float32fix(ctx, outbuf, outsize, + value, LWM2M_FLOAT32_BITS); + } + return 0; +} +/*---------------------------------------------------------------------------*/ +LWM2M_RESOURCES(temperature_resources, + /* Temperature (Current) */ + LWM2M_RESOURCE_CALLBACK(5700, { temp, NULL, NULL }), + /* Units */ + LWM2M_RESOURCE_STRING(5701, "Celcius"), + /* Min Range Value */ + LWM2M_RESOURCE_FLOATFIX(5603, IPSO_TEMPERATURE_MIN), + /* Max Range Value */ + LWM2M_RESOURCE_FLOATFIX(5604, IPSO_TEMPERATURE_MAX), + /* Min Measured Value */ + LWM2M_RESOURCE_FLOATFIX_VAR(5601, &min_temp), + /* Max Measured Value */ + LWM2M_RESOURCE_FLOATFIX_VAR(5602, &max_temp), + ); +LWM2M_INSTANCES(temperature_instances, + LWM2M_INSTANCE(0, temperature_resources)); +LWM2M_OBJECT(temperature, 3303, temperature_instances); +/*---------------------------------------------------------------------------*/ +static int +read_temp(int32_t *value) +{ +#ifdef IPSO_TEMPERATURE + int32_t temp; + if(IPSO_TEMPERATURE.read_value == NULL || + IPSO_TEMPERATURE.read_value(&temp) != 0) { + return 0; + } + + /* Convert milliCelsius to fix float */ + *value = (temp * LWM2M_FLOAT32_FRAC) / 1000; + + if(*value < min_temp) { + min_temp = *value; + lwm2m_object_notify_observers(&temperature, "/0/5601"); + } + if(*value > max_temp) { + max_temp = *value; + lwm2m_object_notify_observers(&temperature, "/0/5602"); + } + return 1; +#else /* IPSO_TEMPERATURE */ + return 0; +#endif /* IPSO_TEMPERATURE */ +} +/*---------------------------------------------------------------------------*/ +static void +handle_periodic_timer(void *ptr) +{ + static int32_t last_value = IPSO_TEMPERATURE_MIN; + int32_t v; + + /* Only notify when the value has changed since last */ + if(read_temp(&v) && v != last_value) { + last_value = v; + lwm2m_object_notify_observers(&temperature, "/0/5700"); + } + ctimer_reset(&periodic_timer); +} +/*---------------------------------------------------------------------------*/ +void +ipso_temperature_init(void) +{ + int32_t v; + min_temp = IPSO_TEMPERATURE_MAX; + max_temp = IPSO_TEMPERATURE_MIN; + +#ifdef IPSO_TEMPERATURE + if(IPSO_TEMPERATURE.init) { + IPSO_TEMPERATURE.init(); + } +#endif /* IPSO_TEMPERATURE */ + + /* register this device and its handlers - the handlers automatically + sends in the object to handle */ + lwm2m_engine_register_object(&temperature); + + /* update temp and min/max + notify any listeners */ + read_temp(&v); + ctimer_set(&periodic_timer, CLOCK_SECOND * 10, handle_periodic_timer, NULL); +} +/*---------------------------------------------------------------------------*/ +/** @} */ diff --git a/apps/oma-lwm2m/Makefile.oma-lwm2m b/apps/oma-lwm2m/Makefile.oma-lwm2m new file mode 100644 index 000000000..8445530ed --- /dev/null +++ b/apps/oma-lwm2m/Makefile.oma-lwm2m @@ -0,0 +1,5 @@ +oma-lwm2m_src = lwm2m-object.c lwm2m-engine.c \ + lwm2m-device.c lwm2m-server.c lwm2m-security.c \ + oma-tlv.c oma-tlv-reader.c oma-tlv-writer.c \ + lwm2m-plain-text.c +CFLAGS += -DHAVE_OMA_LWM2M=1 diff --git a/apps/oma-lwm2m/lwm2m-device.c b/apps/oma-lwm2m/lwm2m-device.c new file mode 100644 index 000000000..a911c7c4a --- /dev/null +++ b/apps/oma-lwm2m/lwm2m-device.c @@ -0,0 +1,152 @@ +/* + * Copyright (c) 2015, Yanzi Networks AB. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of the copyright holder 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 HOLDER 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 HOLDER 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. + */ + +/** + * \addtogroup oma-lwm2m + * @{ + */ + +/** + * \file + * Implementation of the Contiki OMA LWM2M device + * \author + * Joakim Eriksson + * Niclas Finne + */ + +#include "lwm2m-object.h" +#include "lwm2m-device.h" +#include "lwm2m-engine.h" + +#define DEBUG 0 +#if DEBUG +#include +#define PRINTF(...) printf(__VA_ARGS__) +#else +#define PRINTF(...) +#endif + +static int32_t time_offset = 0; +/*---------------------------------------------------------------------------*/ +static int +read_lwtime(lwm2m_context_t *ctx, uint8_t *outbuf, size_t outsize) +{ + return ctx->writer->write_int(ctx, outbuf, outsize, + time_offset + clock_seconds()); +} +/*---------------------------------------------------------------------------*/ +static int +set_lwtime(lwm2m_context_t *ctx, const uint8_t *inbuf, size_t insize, + uint8_t *outbuf, size_t outsize) +{ + /* assume that this only read one TLV value */ + int32_t lw_time; + size_t len = ctx->reader->read_int(ctx, inbuf, insize, &lw_time); + if(len == 0) { + PRINTF("FAIL: could not read time '%*.s'\n", (int)insize, inbuf); + } else { + PRINTF("Got: time: %*.s => %" PRId32 "\n", (int)insize, inbuf, lw_time); + + time_offset = lw_time - clock_seconds(); + PRINTF("Write time...%" PRId32 " => offset = %" PRId32 "\n", + lw_time, time_offset); + } + /* return the number of bytes read */ + return len; +} +/*---------------------------------------------------------------------------*/ +#ifdef PLATFORM_REBOOT +static struct ctimer reboot_timer; +static void +do_the_reboot(void *ptr) +{ + PLATFORM_REBOOT(); +} +static int +reboot(lwm2m_context_t *ctx, const uint8_t *arg, size_t argsize, + uint8_t *outbuf, size_t outsize) +{ + PRINTF("Device will reboot!\n"); + ctimer_set(&reboot_timer, CLOCK_SECOND / 2, do_the_reboot, NULL); + return 0; +} +#endif /* PLATFORM_REBOOT */ +/*---------------------------------------------------------------------------*/ +#ifdef PLATFORM_FACTORY_DEFAULT +static int +factory_reset(lwm2m_context_t *ctx, const uint8_t *arg, size_t arg_size, + uint8_t *outbuf, size_t outsize) +{ + PRINTF("Device will do factory default!\n"); + PLATFORM_FACTORY_DEFAULT(); + return 0; +} +#endif /* PLATFORM_FACTORY_DEFAULT */ +/*---------------------------------------------------------------------------*/ +LWM2M_RESOURCES(device_resources, +#ifdef LWM2M_DEVICE_MANUFACTURER + LWM2M_RESOURCE_STRING(0, LWM2M_DEVICE_MANUFACTURER), +#endif /* LWM2M_DEVICE_MANUFACTURER */ +#ifdef LWM2M_DEVICE_TYPE + LWM2M_RESOURCE_STRING(17, LWM2M_DEVICE_TYPE), +#endif /* LWM2M_DEVICE_TYPE */ +#ifdef LWM2M_DEVICE_MODEL_NUMBER + LWM2M_RESOURCE_STRING(1, LWM2M_DEVICE_MODEL_NUMBER), +#endif /* LWM2M_DEVICE_MODEL_NUMBER */ +#ifdef LWM2M_DEVICE_SERIAL_NO + LWM2M_RESOURCE_STRING(2, LWM2M_DEVICE_SERIAL_NO), +#endif /* LWM2M_DEVICE_SERIAL_NO */ +#ifdef LWM2M_DEVICE_FIRMWARE_VERSION + LWM2M_RESOURCE_STRING(3, LWM2M_DEVICE_FIRMWARE_VERSION), +#endif /* LWM2M_DEVICE_FIRMWARE_VERSION */ +#ifdef PLATFORM_REBOOT + LWM2M_RESOURCE_CALLBACK(4, { NULL, NULL, reboot }), +#endif /* PLATFORM_REBOOT */ +#ifdef PLATFORM_FACTORY_DEFAULT + LWM2M_RESOURCE_CALLBACK(5, { NULL, NULL, factory_reset }), +#endif /* PLATFORM_FACTORY_DEFAULT */ + /* Current Time */ + LWM2M_RESOURCE_CALLBACK(13, { read_lwtime, set_lwtime, NULL }), + ); +LWM2M_INSTANCES(device_instances, LWM2M_INSTANCE(0, device_resources)); +LWM2M_OBJECT(device, 3, device_instances); +/*---------------------------------------------------------------------------*/ +void +lwm2m_device_init(void) +{ + /** + * Register this device and its handlers - the handlers + * automatically sends in the object to handle. + */ + PRINTF("*** Init lwm2m-device\n"); + lwm2m_engine_register_object(&device); +} +/*---------------------------------------------------------------------------*/ +/** @} */ diff --git a/apps/oma-lwm2m/lwm2m-device.h b/apps/oma-lwm2m/lwm2m-device.h new file mode 100644 index 000000000..5e80c786a --- /dev/null +++ b/apps/oma-lwm2m/lwm2m-device.h @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2015, Yanzi Networks AB. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of the copyright holder 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 HOLDER 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 HOLDER 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. + */ + +/** + * \addtogroup oma-lwm2m + * @{ + */ + +/** + * \file + * Header file for the Contiki OMA LWM2M device + * \author + * Joakim Eriksson + * Niclas Finne + */ + +#ifndef LWM2M_DEVICE_H_ +#define LWM2M_DEVICE_H_ + +#include "contiki-conf.h" + +#ifndef LWM2M_DEVICE_MODEL_NUMBER +#ifdef BOARD_STRING +#define LWM2M_DEVICE_MODEL_NUMBER BOARD_STRING +#endif /* BOARD_STRING */ +#endif /* LWM2M_DEVICE_MODEL_NUMBER */ + +#ifndef LWM2M_DEVICE_FIRMWARE_VERSION +#define LWM2M_DEVICE_FIRMWARE_VERSION CONTIKI_VERSION_STRING +#endif /* LWM2M_DEVICE_FIRMWARE_VERSION */ + +void lwm2m_device_init(void); + +#endif /* LWM2M_DEVICE_H_ */ +/** @} */ diff --git a/apps/oma-lwm2m/lwm2m-engine.c b/apps/oma-lwm2m/lwm2m-engine.c new file mode 100644 index 000000000..62f549db4 --- /dev/null +++ b/apps/oma-lwm2m/lwm2m-engine.c @@ -0,0 +1,1017 @@ +/* + * Copyright (c) 2015, Yanzi Networks AB. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of the copyright holder 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 HOLDER 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 HOLDER 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. + */ + +/** + * \addtogroup oma-lwm2m + * @{ + */ + +/** + * \file + * Implementation of the Contiki OMA LWM2M engine + * \author + * Joakim Eriksson + * Niclas Finne + */ + +#include "contiki.h" +#include "lwm2m-engine.h" +#include "lwm2m-object.h" +#include "lwm2m-device.h" +#include "lwm2m-plain-text.h" +#include "rest-engine.h" +#include "er-coap-constants.h" +#include "er-coap-engine.h" +#include "oma-tlv.h" +#include "oma-tlv-writer.h" +#include "net/ipv6/uip-ds6.h" +#include +#include +#include + +#if UIP_CONF_IPV6_RPL +#include "net/rpl/rpl.h" +#endif /* UIP_CONF_IPV6_RPL */ + +#define DEBUG DEBUG_NONE +#include "net/ip/uip-debug.h" + +#ifndef LWM2M_ENGINE_CLIENT_ENDPOINT_PREFIX +#ifdef LWM2M_DEVICE_MODEL_NUMBER +#define LWM2M_ENGINE_CLIENT_ENDPOINT_PREFIX LWM2M_DEVICE_MODEL_NUMBER +#else /* LWM2M_DEVICE_MODEL_NUMBER */ +#define LWM2M_ENGINE_CLIENT_ENDPOINT_PREFIX "Contiki-" +#endif /* LWM2M_DEVICE_MODEL_NUMBER */ +#endif /* LWM2M_ENGINE_CLIENT_ENDPOINT_PREFIX */ + +#ifdef LWM2M_ENGINE_CONF_MAX_OBJECTS +#define MAX_OBJECTS LWM2M_ENGINE_CONF_MAX_OBJECTS +#else /* LWM2M_ENGINE_CONF_MAX_OBJECTS */ +#define MAX_OBJECTS 10 +#endif /* LWM2M_ENGINE_CONF_MAX_OBJECTS */ + +#define REMOTE_PORT UIP_HTONS(COAP_DEFAULT_PORT) +#define BS_REMOTE_PORT UIP_HTONS(5685) + +static const lwm2m_object_t *objects[MAX_OBJECTS]; +static char endpoint[32]; +static char rd_data[128]; /* allocate some data for the RD */ + +PROCESS(lwm2m_rd_client, "LWM2M Engine"); + +static uip_ipaddr_t server_ipaddr; +static uint16_t server_port = REMOTE_PORT; +static uip_ipaddr_t bs_server_ipaddr; +static uint16_t bs_server_port = BS_REMOTE_PORT; + +static uint8_t use_bootstrap = 0; +static uint8_t has_bootstrap_server_info = 0; +static uint8_t use_registration = 0; +static uint8_t has_registration_server_info = 0; +static uint8_t registered = 0; +static uint8_t bootstrapped = 0; /* bootstrap made... */ + +void lwm2m_device_init(void); +void lwm2m_security_init(void); +void lwm2m_server_init(void); + +static const lwm2m_instance_t *get_first_instance_of_object(uint16_t id, lwm2m_context_t *context); +static const lwm2m_instance_t *get_instance(const lwm2m_object_t *object, lwm2m_context_t *context, int depth); +static const lwm2m_resource_t *get_resource(const lwm2m_instance_t *instance, lwm2m_context_t *context); +/*---------------------------------------------------------------------------*/ +static void +client_chunk_handler(void *response) +{ +#if (DEBUG) & DEBUG_PRINT + const uint8_t *chunk; + + int len = coap_get_payload(response, &chunk); + + PRINTF("|%.*s\n", len, (char *)chunk); +#endif /* (DEBUG) & DEBUG_PRINT */ +} +/*---------------------------------------------------------------------------*/ +static int +index_of(const uint8_t *data, int offset, int len, uint8_t c) +{ + if(offset < 0) { + return offset; + } + for(; offset < len; offset++) { + if(data[offset] == c) { + return offset; + } + } + return -1; +} +/*---------------------------------------------------------------------------*/ +static int +has_network_access(void) +{ +#if UIP_CONF_IPV6_RPL + if(rpl_get_any_dag() == NULL) { + return 0; + } +#endif /* UIP_CONF_IPV6_RPL */ + return 1; +} +/*---------------------------------------------------------------------------*/ +void +lwm2m_engine_use_bootstrap_server(int use) +{ + use_bootstrap = use != 0; + if(use_bootstrap) { + process_poll(&lwm2m_rd_client); + } +} +/*---------------------------------------------------------------------------*/ +void +lwm2m_engine_use_registration_server(int use) +{ + use_registration = use != 0; + if(use_registration) { + process_poll(&lwm2m_rd_client); + } +} +/*---------------------------------------------------------------------------*/ +void +lwm2m_engine_register_with_server(const uip_ipaddr_t *server, uint16_t port) +{ + uip_ipaddr_copy(&server_ipaddr, server); + if(port != 0) { + server_port = port; + } else { + server_port = REMOTE_PORT; + } + has_registration_server_info = 1; + registered = 0; + if(use_registration) { + process_poll(&lwm2m_rd_client); + } +} +/*---------------------------------------------------------------------------*/ +static int +update_registration_server(void) +{ + if(has_registration_server_info) { + return 1; + } + +#if UIP_CONF_IPV6_RPL + { + rpl_dag_t *dag; + + /* Use the DAG id as server address if no other has been specified */ + dag = rpl_get_any_dag(); + if(dag != NULL) { + uip_ipaddr_copy(&server_ipaddr, &dag->dag_id); + server_port = REMOTE_PORT; + return 1; + } + } +#endif /* UIP_CONF_IPV6_RPL */ + + return 0; +} +/*---------------------------------------------------------------------------*/ +void +lwm2m_engine_register_with_bootstrap_server(const uip_ipaddr_t *server, + uint16_t port) +{ + uip_ipaddr_copy(&bs_server_ipaddr, server); + if(port != 0) { + bs_server_port = port; + } else { + bs_server_port = BS_REMOTE_PORT; + } + has_bootstrap_server_info = 1; + bootstrapped = 0; + registered = 0; + if(use_bootstrap) { + process_poll(&lwm2m_rd_client); + } +} +/*---------------------------------------------------------------------------*/ +static int +update_bootstrap_server(void) +{ + if(has_bootstrap_server_info) { + return 1; + } + +#if UIP_CONF_IPV6_RPL + { + rpl_dag_t *dag; + + /* Use the DAG id as server address if no other has been specified */ + dag = rpl_get_any_dag(); + if(dag != NULL) { + uip_ipaddr_copy(&bs_server_ipaddr, &dag->dag_id); + bs_server_port = REMOTE_PORT; + return 1; + } + } +#endif /* UIP_CONF_IPV6_RPL */ + + return 0; +} +/*---------------------------------------------------------------------------*/ +PROCESS_THREAD(lwm2m_rd_client, ev, data) +{ + static coap_packet_t request[1]; /* This way the packet can be treated as pointer as usual. */ + static struct etimer et; + + PROCESS_BEGIN(); + + printf("RD Client started with endpoint '%s'\n", endpoint); + + etimer_set(&et, 15 * CLOCK_SECOND); + + while(1) { + PROCESS_YIELD(); + + if(etimer_expired(&et)) { + if(!has_network_access()) { + /* Wait until for a network to join */ + } else if(use_bootstrap && bootstrapped == 0) { + if(update_bootstrap_server()) { + /* prepare request, TID is set by COAP_BLOCKING_REQUEST() */ + coap_init_message(request, COAP_TYPE_CON, COAP_POST, 0); + coap_set_header_uri_path(request, "/bs"); + coap_set_header_uri_query(request, endpoint); + + printf("Registering ID with bootstrap server ["); + uip_debug_ipaddr_print(&bs_server_ipaddr); + printf("]:%u as '%s'\n", uip_ntohs(bs_server_port), endpoint); + + COAP_BLOCKING_REQUEST(&bs_server_ipaddr, bs_server_port, request, + client_chunk_handler); + bootstrapped++; + } + } else if(use_bootstrap && bootstrapped == 1) { + lwm2m_context_t context; + const lwm2m_instance_t *instance = NULL; + const lwm2m_resource_t *rsc; + const uint8_t *first; + int len; + + PRINTF("*** Bootstrap - checking for server info...\n"); + + /* get the security object */ + instance = get_first_instance_of_object(LWM2M_OBJECT_SECURITY_ID, &context); + if(instance != NULL) { + /* get the server URI */ + context.resource_id = LWM2M_SECURITY_SERVER_URI; + rsc = get_resource(instance, &context); + first = lwm2m_object_get_resource_string(rsc, &context); + len = lwm2m_object_get_resource_strlen(rsc, &context); + if(first != NULL && len > 0) { + int start, end; + uip_ipaddr_t addr; + int32_t port; + uint8_t secure = 0; + + PRINTF("**** Found security instance using: %.*s\n", len, first); + /* TODO Should verify it is a URI */ + + /* Check if secure */ + secure = strncmp((const char *)first, "coaps:", 6) == 0; + + /* Only IPv6 supported */ + start = index_of(first, 0, len, '['); + end = index_of(first, start, len, ']'); + if(start > 0 && end > start && + uiplib_ipaddrconv((const char *)&first[start], &addr)) { + if(first[end + 1] == ':' && + lwm2m_plain_text_read_int(first + end + 2, len - end - 2, &port)) { + } else if(secure) { + /** + * Secure CoAP should use a different port but for now + * the same port is used. + */ + port = COAP_DEFAULT_PORT; + } else { + port = COAP_DEFAULT_PORT; + } + PRINTF("Server address "); + PRINT6ADDR(&addr); + PRINTF(" port %" PRId32 "%s\n", port, secure ? " (secure)" : ""); + if(secure) { + printf("Secure CoAP requested but not supported - can not bootstrap\n"); + } else { + lwm2m_engine_register_with_server(&addr, + UIP_HTONS((uint16_t)port)); + bootstrapped++; + } + } else { + printf("** failed to parse URI %.*s\n", len, first); + } + } + } + + if(bootstrapped == 1) { + /* Not ready. Lets retry with the bootstrap server again */ + bootstrapped = 0; + } + + } else if(use_registration && !registered && + update_registration_server()) { + int pos; + int len, i, j; + registered = 1; + + /* prepare request, TID is set by COAP_BLOCKING_REQUEST() */ + coap_init_message(request, COAP_TYPE_CON, COAP_POST, 0); + coap_set_header_uri_path(request, "/rd"); + coap_set_header_uri_query(request, endpoint); + + /* generate the rd data */ + pos = 0; + for(i = 0; i < MAX_OBJECTS; i++) { + if(objects[i] != NULL) { + for(j = 0; j < objects[i]->count; j++) { + if(objects[i]->instances[j].flag & LWM2M_INSTANCE_FLAG_USED) { + len = snprintf(&rd_data[pos], sizeof(rd_data) - pos, + "%s<%d/%d>", pos > 0 ? "," : "", + objects[i]->id, objects[i]->instances[j].id); + if(len > 0 && len < sizeof(rd_data) - pos) { + pos += len; + } + } + } + } + } + + coap_set_payload(request, (uint8_t *)rd_data, pos); + + printf("Registering with ["); + uip_debug_ipaddr_print(&server_ipaddr); + printf("]:%u lwm2m endpoint '%s': '%.*s'\n", uip_ntohs(server_port), + endpoint, pos, rd_data); + COAP_BLOCKING_REQUEST(&server_ipaddr, server_port, request, + client_chunk_handler); + } + /* for now only register once... registered = 0; */ + etimer_set(&et, 15 * CLOCK_SECOND); + } + } + PROCESS_END(); +} +/*---------------------------------------------------------------------------*/ +void +lwm2m_engine_init(void) +{ +#ifdef LWM2M_ENGINE_CLIENT_ENDPOINT_NAME + + snprintf(endpoint, sizeof(endpoint) - 1, + "?ep=" LWM2M_ENGINE_CLIENT_ENDPOINT_NAME); + +#else /* LWM2M_ENGINE_CLIENT_ENDPOINT_NAME */ + + int len, i; + uint8_t state; + uip_ipaddr_t *ipaddr; + char client[sizeof(endpoint)]; + + len = strlen(LWM2M_ENGINE_CLIENT_ENDPOINT_PREFIX); + /* ensure that this fits with the hex-nums */ + if(len > sizeof(client) - 13) { + len = sizeof(client) - 13; + } + memcpy(client, LWM2M_ENGINE_CLIENT_ENDPOINT_PREFIX, len); + + /* pick an IP address that is PREFERRED or TENTATIVE */ + ipaddr = NULL; + for(i = 0; i < UIP_DS6_ADDR_NB; i++) { + state = uip_ds6_if.addr_list[i].state; + if(uip_ds6_if.addr_list[i].isused && + (state == ADDR_TENTATIVE || state == ADDR_PREFERRED)) { + ipaddr = &(uip_ds6_if.addr_list[i]).ipaddr; + break; + } + } + + if(ipaddr != NULL) { + for(i = 0; i < 6; i++) { + /* assume IPv6 for now */ + uint8_t b = ipaddr->u8[10 + i]; + client[len++] = (b >> 4) > 9 ? 'A' - 10 + (b >> 4) : '0' + (b >> 4); + client[len++] = (b & 0xf) > 9 ? 'A' - 10 + (b & 0xf) : '0' + (b & 0xf); + } + } + + /* a zero at end of string */ + client[len] = 0; + /* create endpoint */ + snprintf(endpoint, sizeof(endpoint) - 1, "?ep=%s", client); + +#endif /* LWM2M_ENGINE_CLIENT_ENDPOINT_NAME */ + + rest_init_engine(); + process_start(&lwm2m_rd_client, NULL); +} +/*---------------------------------------------------------------------------*/ +void +lwm2m_engine_register_default_objects(void) +{ + lwm2m_security_init(); + lwm2m_server_init(); + lwm2m_device_init(); +} +/*---------------------------------------------------------------------------*/ +static int +parse_next(const char **path, int *path_len, uint16_t *value) +{ + char c; + *value = 0; + /* printf("parse_next: %p %d\n", *path, *path_len); */ + if(*path_len == 0) { + return 0; + } + while(*path_len > 0) { + c = **path; + (*path)++; + *path_len = *path_len - 1; + if(c >= '0' && c <= '9') { + *value = *value * 10 + (c - '0'); + } else if(c == '/') { + return 1; + } else { + /* error */ + return -4; + } + } + return 1; +} +/*---------------------------------------------------------------------------*/ +int +lwm2m_engine_parse_context(const lwm2m_object_t *object, + const char *path, int path_len, + lwm2m_context_t *context) +{ + int ret; + if(context == NULL || object == NULL || path == NULL) { + return 0; + } + memset(context, 0, sizeof(lwm2m_context_t)); + /* get object id */ + ret = 0; + ret += parse_next(&path, &path_len, &context->object_id); + ret += parse_next(&path, &path_len, &context->object_instance_id); + ret += parse_next(&path, &path_len, &context->resource_id); + + /* Set default reader/writer */ + context->reader = &lwm2m_plain_text_reader; + context->writer = &oma_tlv_writer; + + return ret; +} +/*---------------------------------------------------------------------------*/ +const lwm2m_object_t * +lwm2m_engine_get_object(uint16_t id) +{ + int i; + for(i = 0; i < MAX_OBJECTS; i++) { + if(objects[i] != NULL && objects[i]->id == id) { + return objects[i]; + } + } + return NULL; +} +/*---------------------------------------------------------------------------*/ +int +lwm2m_engine_register_object(const lwm2m_object_t *object) +{ + int i; + int found = 0; + for(i = 0; i < MAX_OBJECTS; i++) { + if(objects[i] == NULL) { + objects[i] = object; + found = 1; + break; + } + } + rest_activate_resource(lwm2m_object_get_coap_resource(object), + (char *)object->path); + return found; +} +/*---------------------------------------------------------------------------*/ +static const lwm2m_instance_t * +get_first_instance_of_object(uint16_t id, lwm2m_context_t *context) +{ + const lwm2m_object_t *object; + int i; + + object = lwm2m_engine_get_object(id); + if(object == NULL) { + /* No object with the specified id found */ + return NULL; + } + + /* Initialize the context */ + memset(context, 0, sizeof(lwm2m_context_t)); + context->object_id = id; + + for(i = 0; i < object->count; i++) { + if(object->instances[i].flag & LWM2M_INSTANCE_FLAG_USED) { + context->object_instance_id = object->instances[i].id; + context->object_instance_index = i; + return &object->instances[i]; + } + } + return NULL; +} +/*---------------------------------------------------------------------------*/ +static const lwm2m_instance_t * +get_instance(const lwm2m_object_t *object, lwm2m_context_t *context, int depth) +{ + int i; + if(depth > 1) { + PRINTF("lwm2m: searching for instance %u\n", context->object_instance_id); + for(i = 0; i < object->count; i++) { + PRINTF(" Instance %d -> %u (used: %d)\n", i, object->instances[i].id, + (object->instances[i].flag & LWM2M_INSTANCE_FLAG_USED) != 0); + if(object->instances[i].id == context->object_instance_id && + object->instances[i].flag & LWM2M_INSTANCE_FLAG_USED) { + context->object_instance_index = i; + return &object->instances[i]; + } + } + } + return NULL; +} +/*---------------------------------------------------------------------------*/ +static const lwm2m_resource_t * +get_resource(const lwm2m_instance_t *instance, lwm2m_context_t *context) +{ + int i; + if(instance != NULL) { + PRINTF("lwm2m: searching for resource %u\n", context->resource_id); + for(i = 0; i < instance->count; i++) { + PRINTF(" Resource %d -> %u\n", i, instance->resources[i].id); + if(instance->resources[i].id == context->resource_id) { + context->resource_index = i; + return &instance->resources[i]; + } + } + } + return NULL; +} +/*---------------------------------------------------------------------------*/ +static int +write_rd_link_data(const lwm2m_object_t *object, + const lwm2m_instance_t *instance, + char *buffer, size_t size) +{ + const lwm2m_resource_t *resource; + int len, rdlen, i; + + PRINTF("<%d/%d>", object->id, instance->id); + rdlen = snprintf(buffer, size, "<%d/%d>", + object->id, instance->id); + if(rdlen < 0 || rdlen >= size) { + return -1; + } + + for(i = 0; i < instance->count; i++) { + resource = &instance->resources[i]; + PRINTF(",<%d/%d/%d>", object->id, instance->id, resource->id); + + len = snprintf(&buffer[rdlen], size - rdlen, + ",<%d/%d/%d>", object->id, instance->id, resource->id); + rdlen += len; + if(len < 0 || rdlen >= size) { + return -1; + } + } + return rdlen; +} +/*---------------------------------------------------------------------------*/ +static int +write_rd_json_data(const lwm2m_context_t *context, + const lwm2m_object_t *object, + const lwm2m_instance_t *instance, + char *buffer, size_t size) +{ + const lwm2m_resource_t *resource; + const char *s = ""; + int len, rdlen, i; + + PRINTF("{\"e\":["); + rdlen = snprintf(buffer, size, "{\"e\":["); + if(rdlen < 0 || rdlen >= size) { + return -1; + } + + for(i = 0, len = 0; i < instance->count; i++) { + resource = &instance->resources[i]; + len = 0; + if(lwm2m_object_is_resource_string(resource)) { + const uint8_t *value; + uint16_t slen; + value = lwm2m_object_get_resource_string(resource, context); + slen = lwm2m_object_get_resource_strlen(resource, context); + if(value != NULL) { + PRINTF("%s{\"n\":\"%u\",\"vs\":\"%.*s\"}", s, + resource->id, slen, value); + len = snprintf(&buffer[rdlen], size - rdlen, + "%s{\"n\":\"%u\",\"vs\":\"%.*s\"}", s, + resource->id, slen, value); + } + } else if(lwm2m_object_is_resource_int(resource)) { + int32_t value; + if(lwm2m_object_get_resource_int(resource, context, &value)) { + PRINTF("%s{\"n\":\"%u\",\"v\":%" PRId32 "}", s, + resource->id, value); + len = snprintf(&buffer[rdlen], size - rdlen, + "%s{\"n\":\"%u\",\"v\":%" PRId32 "}", s, + resource->id, value); + } + } else if(lwm2m_object_is_resource_floatfix(resource)) { + int32_t value; + if(lwm2m_object_get_resource_floatfix(resource, context, &value)) { + PRINTF("%s{\"n\":\"%u\",\"v\":%" PRId32 "}", s, resource->id, + value / LWM2M_FLOAT32_FRAC); + len = snprintf(&buffer[rdlen], size - rdlen, + "%s{\"n\":\"%u\",\"v\":", s, resource->id); + rdlen += len; + if(len < 0 || rdlen >= size) { + return -1; + } + + len = lwm2m_plain_text_write_float32fix((uint8_t *)&buffer[rdlen], + size - rdlen, + value, LWM2M_FLOAT32_BITS); + if(len == 0) { + return -1; + } + rdlen += len; + + if(rdlen < size) { + buffer[rdlen] = '}'; + } + len = 1; + } + } else if(lwm2m_object_is_resource_boolean(resource)) { + int value; + if(lwm2m_object_get_resource_boolean(resource, context, &value)) { + PRINTF("%s{\"n\":\"%u\",\"v\":%s}", s, resource->id, + value ? "true" : "false"); + len = snprintf(&buffer[rdlen], size - rdlen, + "%s{\"n\":\"%u\",\"v\":%s}", s, resource->id, + value ? "true" : "false"); + } + } + rdlen += len; + if(len < 0 || rdlen >= size) { + return -1; + } + if(len > 0) { + s = ","; + } + } + PRINTF("]}\n"); + len = snprintf(&buffer[rdlen], size - rdlen, "]}"); + rdlen += len; + if(len < 0 || rdlen >= size) { + return -1; + } + + return rdlen; +} +/*---------------------------------------------------------------------------*/ +void +lwm2m_engine_handler(const lwm2m_object_t *object, + void *request, void *response, + uint8_t *buffer, uint16_t preferred_size, + int32_t *offset) +{ + int len; + const char *url; + unsigned int format; + int depth; + lwm2m_context_t context; + rest_resource_flags_t method; + const lwm2m_instance_t *instance; +#if (DEBUG) & DEBUG_PRINT + const char *method_str; +#endif /* (DEBUG) & DEBUG_PRINT */ + + method = REST.get_method_type(request); + + len = REST.get_url(request, &url); + if(!REST.get_header_content_type(request, &format)) { + PRINTF("No format given. Assume text plain...\n"); + format = LWM2M_TEXT_PLAIN; + } else if(format == TEXT_PLAIN) { + /* CoAP content format text plain - assume LWM2M text plain */ + format = LWM2M_TEXT_PLAIN; + } + + depth = lwm2m_engine_parse_context(object, url, len, &context); + PRINTF("Context: %u/%u/%u found: %d\n", context.object_id, + context.object_instance_id, context.resource_id, depth); + +#if (DEBUG) & DEBUG_PRINT + /* for debugging */ + if(method == METHOD_GET) { + method_str = "GET"; + } else if(method == METHOD_POST) { + method_str = "POST"; + } else if(method == METHOD_PUT) { + method_str = "PUT"; + } else if(method == METHOD_DELETE) { + method_str = "DELETE"; + } else { + method_str = "UNKNOWN"; + } + PRINTF("%s Called Path:%.*s Format:%d ID:%d bsize:%u\n", method_str, len, + url, format, object->id, preferred_size); + if(format == LWM2M_TEXT_PLAIN) { + /* a string */ + const uint8_t *data; + int plen = REST.get_request_payload(request, &data); + if(plen > 0) { + PRINTF("Data: '%.*s'\n", plen, (char *)data); + } + } +#endif /* (DEBUG) & DEBUG_PRINT */ + + instance = get_instance(object, &context, depth); + + /* from POST */ + if(depth > 1 && instance == NULL) { + if(method != METHOD_PUT && method != METHOD_POST) { + PRINTF("Error - do not have instance %d\n", context.object_instance_id); + REST.set_response_status(response, NOT_FOUND_4_04); + return; + } else { + const uint8_t *data; + int i, len, plen, pos; + oma_tlv_t tlv; + PRINTF(">>> CREATE ? %d/%d\n", context.object_id, + context.object_instance_id); + + for(i = 0; i < object->count; i++) { + if((object->instances[i].flag & LWM2M_INSTANCE_FLAG_USED) == 0) { + /* allocate this instance */ + object->instances[i].flag |= LWM2M_INSTANCE_FLAG_USED; + object->instances[i].id = context.object_instance_id; + context.object_instance_index = i; + PRINTF("Created instance: %d\n", context.object_instance_id); + REST.set_response_status(response, CREATED_2_01); + instance = &object->instances[i]; + break; + } + } + + if(instance == NULL) { + /* could for some reason not create the instance */ + REST.set_response_status(response, NOT_ACCEPTABLE_4_06); + return; + } + + plen = REST.get_request_payload(request, &data); + if(plen == 0) { + /* do nothing more */ + return; + } + PRINTF("Payload: "); + for(i = 0; i < plen; i++) { + PRINTF("%02x", data[i]); + } + PRINTF("\n"); + + pos = 0; + do { + len = oma_tlv_read(&tlv, (uint8_t *)&data[pos], plen - pos); + PRINTF("Found TLV type=%u id=%u len=%lu\n", + tlv.type, tlv.id, (unsigned long)tlv.length); + /* here we need to do callbacks or write value */ + if(tlv.type == OMA_TLV_TYPE_RESOURCE) { + context.resource_id = tlv.id; + const lwm2m_resource_t *rsc = get_resource(instance, &context); + if(rsc != NULL) { + /* write the value to the resource */ + if(lwm2m_object_is_resource_string(rsc)) { + PRINTF(" new string value for /%d/%d/%d = %.*s\n", + context.object_id, context.object_instance_id, + context.resource_id, (int)tlv.length, tlv.value); + lwm2m_object_set_resource_string(rsc, &context, + tlv.length, tlv.value); + } else if(lwm2m_object_is_resource_int(rsc)) { + PRINTF(" new int value for /%d/%d/%d = %" PRId32 "\n", + context.object_id, context.object_instance_id, + context.resource_id, oma_tlv_get_int32(&tlv)); + lwm2m_object_set_resource_int(rsc, &context, + oma_tlv_get_int32(&tlv)); + } else if(lwm2m_object_is_resource_floatfix(rsc)) { + int32_t value; + if(oma_tlv_float32_to_fix(&tlv, &value, LWM2M_FLOAT32_BITS)) { + PRINTF(" new float value for /%d/%d/%d = %" PRId32 "\n", + context.object_id, context.object_instance_id, + context.resource_id, value >> LWM2M_FLOAT32_BITS); + lwm2m_object_set_resource_floatfix(rsc, &context, value); + } else { + PRINTF(" new float value for /%d/%d/%d: FAILED\n", + context.object_id, context.object_instance_id, + context.resource_id); + } + } else if(lwm2m_object_is_resource_boolean(rsc)) { + PRINTF(" new boolean value for /%d/%d/%d = %" PRId32 "\n", + context.object_id, context.object_instance_id, + context.resource_id, oma_tlv_get_int32(&tlv)); + lwm2m_object_set_resource_boolean(rsc, &context, + oma_tlv_get_int32(&tlv) != 0); + } + } + } + pos = pos + len; + } while(len > 0 && pos < plen); + } + return; + } + + if(depth == 3) { + const lwm2m_resource_t *resource = get_resource(instance, &context); + size_t tlvlen = 0; + if(resource == NULL) { + PRINTF("Error - do not have resource %d\n", context.resource_id); + REST.set_response_status(response, NOT_FOUND_4_04); + return; + } + /* HANDLE PUT */ + if(method == METHOD_PUT) { + if(lwm2m_object_is_resource_callback(resource)) { + if(resource->value.callback.write != NULL) { + /* pick a reader ??? */ + if(format == LWM2M_TEXT_PLAIN) { + /* a string */ + const uint8_t *data; + int plen = REST.get_request_payload(request, &data); + context.reader = &lwm2m_plain_text_reader; + PRINTF("PUT Callback with data: '%.*s'\n", plen, data); + /* no specific reader for plain text */ + tlvlen = resource->value.callback.write(&context, data, plen, + buffer, preferred_size); + PRINTF("tlvlen:%u\n", (unsigned int)tlvlen); + REST.set_response_status(response, CHANGED_2_04); + } else { + PRINTF("PUT callback with format %d\n", format); + REST.set_response_status(response, NOT_ACCEPTABLE_4_06); + } + } else { + PRINTF("PUT - no write callback\n"); + REST.set_response_status(response, METHOD_NOT_ALLOWED_4_05); + } + } else { + PRINTF("PUT on non-callback resource!\n"); + REST.set_response_status(response, METHOD_NOT_ALLOWED_4_05); + } + /* HANDLE GET */ + } else if(method == METHOD_GET) { + if(lwm2m_object_is_resource_string(resource)) { + const uint8_t *value; + uint16_t len; + value = lwm2m_object_get_resource_string(resource, &context); + len = lwm2m_object_get_resource_strlen(resource, &context); + if(value != NULL) { + PRINTF("Get string value: %.*s\n", (int)len, (char *)value); + /* TODO check format */ + REST.set_response_payload(response, value, len); + REST.set_header_content_type(response, LWM2M_TEXT_PLAIN); + /* Done */ + return; + } + } else if(lwm2m_object_is_resource_int(resource)) { + int32_t value; + if(lwm2m_object_get_resource_int(resource, &context, &value)) { + /* export INT as TLV */ + tlvlen = oma_tlv_write_int32(resource->id, value, buffer, preferred_size); + PRINTF("Exporting int as TLV: %" PRId32 ", len: %u\n", + value, (unsigned int)tlvlen); + } + } else if(lwm2m_object_is_resource_floatfix(resource)) { + int32_t value; + if(lwm2m_object_get_resource_floatfix(resource, &context, &value)) { + /* export FLOATFIX as TLV */ + PRINTF("Exporting %d-bit fix as float: %" PRId32 "\n", + LWM2M_FLOAT32_BITS, value); + tlvlen = oma_tlv_write_float32(resource->id, + value, LWM2M_FLOAT32_BITS, + buffer, preferred_size); + PRINTF("Exporting as TLV: len:%u\n", (unsigned int)tlvlen); + } + } else if(lwm2m_object_is_resource_callback(resource)) { + if(resource->value.callback.read != NULL) { + tlvlen = resource->value.callback.read(&context, + buffer, preferred_size); + } else { + REST.set_response_status(response, METHOD_NOT_ALLOWED_4_05); + return; + } + } + if(tlvlen > 0) { + REST.set_response_payload(response, buffer, tlvlen); + REST.set_header_content_type(response, LWM2M_TLV); + } else { + /* failed to produce output - it is an internal error */ + REST.set_response_status(response, INTERNAL_SERVER_ERROR_5_00); + } + /* Handle POST */ + } else if(method == METHOD_POST) { + if(lwm2m_object_is_resource_callback(resource)) { + if(resource->value.callback.exec != NULL) { + const uint8_t *data; + int plen = REST.get_request_payload(request, &data); + PRINTF("Execute Callback with data: '%.*s'\n", plen, data); + tlvlen = resource->value.callback.exec(&context, + data, plen, + buffer, preferred_size); + REST.set_response_status(response, CHANGED_2_04); + } else { + PRINTF("Execute callback - no exec callback\n"); + REST.set_response_status(response, METHOD_NOT_ALLOWED_4_05); + } + } else { + PRINTF("Resource post but no callback resource\n"); + REST.set_response_status(response, METHOD_NOT_ALLOWED_4_05); + } + } + } else if(depth == 2) { + /* produce an instance response */ + if(method != METHOD_GET) { + REST.set_response_status(response, METHOD_NOT_ALLOWED_4_05); + } else if(instance == NULL) { + REST.set_response_status(response, NOT_FOUND_4_04); + } else { + int rdlen; + if(format == APPLICATION_LINK_FORMAT) { + rdlen = write_rd_link_data(object, instance, + (char *)buffer, preferred_size); + } else { + rdlen = write_rd_json_data(&context, object, instance, + (char *)buffer, preferred_size); + } + if(rdlen < 0) { + PRINTF("Failed to generate instance response\n"); + REST.set_response_status(response, SERVICE_UNAVAILABLE_5_03); + return; + } + REST.set_response_payload(response, buffer, rdlen); + if(format == APPLICATION_LINK_FORMAT) { + REST.set_header_content_type(response, REST.type.APPLICATION_LINK_FORMAT); + } else { + REST.set_header_content_type(response, REST.type.APPLICATION_JSON); + } + } + } +} +/*---------------------------------------------------------------------------*/ +void +lwm2m_engine_delete_handler(const lwm2m_object_t *object, void *request, + void *response, uint8_t *buffer, + uint16_t preferred_size, int32_t *offset) +{ + int len; + const char *url; + lwm2m_context_t context; + + len = REST.get_url(request, &url); + PRINTF("*** DELETE URI:'%.*s' called... - responding with DELETED.\n", + len, url); + len = lwm2m_engine_parse_context(object, url, len, &context); + PRINTF("Context: %u/%u/%u found: %d\n", context.object_id, + context.object_instance_id, context.resource_id, len); + + REST.set_response_status(response, DELETED_2_02); +} +/*---------------------------------------------------------------------------*/ +/** @} */ diff --git a/apps/oma-lwm2m/lwm2m-engine.h b/apps/oma-lwm2m/lwm2m-engine.h new file mode 100644 index 000000000..5512a326d --- /dev/null +++ b/apps/oma-lwm2m/lwm2m-engine.h @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2015, Yanzi Networks AB. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of the copyright holder 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 HOLDER 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 HOLDER 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. + */ + +/** + * \addtogroup oma-lwm2m + * @{ + */ + +/** + * \file + * Header file for the Contiki OMA LWM2M engine + * \author + * Joakim Eriksson + * Niclas Finne + */ + +#ifndef LWM2M_ENGINE_H +#define LWM2M_ENGINE_H + +#include "lwm2m-object.h" + +#define LWM2M_FLOAT32_BITS 10 +#define LWM2M_FLOAT32_FRAC (1L << LWM2M_FLOAT32_BITS) + +/* LWM2M / CoAP Content-Formats */ +typedef enum { + LWM2M_TEXT_PLAIN = 1541, + LWM2M_TLV = 1542, + LWM2M_JSON = 1543, + LWM2M_OPAQUE = 1544 +} lwm2m_content_format_t; + +void lwm2m_engine_init(void); +void lwm2m_engine_register_default_objects(void); +void lwm2m_engine_use_bootstrap_server(int use); +void lwm2m_engine_use_registration_server(int use); +void lwm2m_engine_register_with_server(const uip_ipaddr_t *server, uint16_t port); +void lwm2m_engine_register_with_bootstrap_server(const uip_ipaddr_t *server, uint16_t port); + +const lwm2m_object_t *lwm2m_engine_get_object(uint16_t id); + +int lwm2m_engine_register_object(const lwm2m_object_t *object); + +void lwm2m_engine_handler(const lwm2m_object_t *object, + void *request, void *response, + uint8_t *buffer, uint16_t preferred_size, + int32_t *offset); + +void lwm2m_engine_delete_handler(const lwm2m_object_t *object, + void *request, void *response, + uint8_t *buffer, uint16_t preferred_size, + int32_t *offset); + +#endif /* LWM2M_ENGINE_H */ +/** @} */ diff --git a/apps/oma-lwm2m/lwm2m-object.c b/apps/oma-lwm2m/lwm2m-object.c new file mode 100644 index 000000000..2f5ddb6ed --- /dev/null +++ b/apps/oma-lwm2m/lwm2m-object.c @@ -0,0 +1,336 @@ +/* + * Copyright (c) 2015, Yanzi Networks AB. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of the copyright holder 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 HOLDER 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 HOLDER 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. + */ + +/** + * \addtogroup oma-lwm2m + * @{ + * + */ + +/** + * \file + * Implementation of the Contiki OMA LWM2M object API + * \author + * Joakim Eriksson + * Niclas Finne + */ + +#include "lwm2m-object.h" +#include +/*---------------------------------------------------------------------------*/ +int +lwm2m_object_is_resource_string(const lwm2m_resource_t *resource) +{ + if(resource == NULL) { + return 0; + } + if(resource->type == LWM2M_RESOURCE_TYPE_STR_VALUE || + resource->type == LWM2M_RESOURCE_TYPE_STR_VARIABLE || + resource->type == LWM2M_RESOURCE_TYPE_STR_VARIABLE_ARRAY) { + return 1; + } + return 0; +} +/*---------------------------------------------------------------------------*/ +const uint8_t * +lwm2m_object_get_resource_string(const lwm2m_resource_t *resource, + const lwm2m_context_t *context) +{ + if(resource == NULL || context == NULL) { + return NULL; + } + if(resource->type == LWM2M_RESOURCE_TYPE_STR_VALUE) { + return resource->value.string.value; + } + if(resource->type == LWM2M_RESOURCE_TYPE_STR_VARIABLE) { + return *(resource->value.stringvar.var); + } + if(resource->type == LWM2M_RESOURCE_TYPE_STR_VARIABLE_ARRAY) { + if(context->object_instance_index < resource->value.stringvararr.count) { + return resource->value.stringvararr.var + + resource->value.stringvararr.size * context->object_instance_index; + } + return NULL; + } + /* Not a string */ + return NULL; +} +/*---------------------------------------------------------------------------*/ +uint16_t +lwm2m_object_get_resource_strlen(const lwm2m_resource_t *resource, + const lwm2m_context_t *context) +{ + if(resource == NULL || context == NULL) { + return 0; + } + if(resource->type == LWM2M_RESOURCE_TYPE_STR_VALUE) { + return resource->value.string.len; + } + if(resource->type == LWM2M_RESOURCE_TYPE_STR_VARIABLE) { + return *(resource->value.stringvar.len); + } + if(resource->type == LWM2M_RESOURCE_TYPE_STR_VARIABLE_ARRAY) { + if(context->object_instance_index < resource->value.stringvararr.count) { + return resource->value.stringvararr.len[context->object_instance_index]; + } + return 0; + } + /* Not a string */ + return 0; +} +/*---------------------------------------------------------------------------*/ +int +lwm2m_object_set_resource_string(const lwm2m_resource_t *resource, + const lwm2m_context_t *context, + uint16_t len, const uint8_t *string) +{ + if(resource == NULL || context == NULL) { + return 0; + } + if(resource->type == LWM2M_RESOURCE_TYPE_STR_VARIABLE) { + if(len > resource->value.stringvar.size) { + /* Too large */ + return 0; + } + memcpy(resource->value.stringvar.var, string, len); + *(resource->value.stringvar.len) = len; + return 1; + } + if(resource->type == LWM2M_RESOURCE_TYPE_STR_VARIABLE_ARRAY) { + if(context->object_instance_index < resource->value.stringvararr.count && + len <= resource->value.stringvararr.size) { + memcpy(resource->value.stringvararr.var + + resource->value.stringvararr.size * context->object_instance_index, + string, len); + resource->value.stringvararr.len[context->object_instance_index] = len; + return 1; + } + return 0; + } + /* Not a string variable */ + return 0; +} +/*---------------------------------------------------------------------------*/ +int +lwm2m_object_is_resource_int(const lwm2m_resource_t *resource) +{ + if(resource == NULL) { + return 0; + } + if(resource->type == LWM2M_RESOURCE_TYPE_INT_VALUE || + resource->type == LWM2M_RESOURCE_TYPE_INT_VARIABLE || + resource->type == LWM2M_RESOURCE_TYPE_INT_VARIABLE_ARRAY) { + return 1; + } + return 0; +} +/*---------------------------------------------------------------------------*/ +int +lwm2m_object_get_resource_int(const lwm2m_resource_t *resource, + const lwm2m_context_t *context, + int32_t *value) +{ + if(resource == NULL || context == NULL || value == NULL) { + return 0; + } + if(resource->type == LWM2M_RESOURCE_TYPE_INT_VALUE) { + *value = resource->value.integer.value; + return 1; + } + if(resource->type == LWM2M_RESOURCE_TYPE_INT_VARIABLE) { + *value = *(resource->value.integervar.var); + return 1; + } + if(resource->type == LWM2M_RESOURCE_TYPE_INT_VARIABLE_ARRAY) { + if(context->object_instance_index < resource->value.integervararr.count) { + *value = resource->value.integervararr.var[context->object_instance_index]; + return 1; + } + return 0; + } + /* Not an integer */ + return 0; +} +/*---------------------------------------------------------------------------*/ +int +lwm2m_object_set_resource_int(const lwm2m_resource_t *resource, + const lwm2m_context_t *context, + int32_t value) +{ + if(resource == NULL || context == NULL) { + return 0; + } + if(resource->type == LWM2M_RESOURCE_TYPE_INT_VARIABLE) { + *(resource->value.integervar.var) = value; + return 1; + } + if(resource->type == LWM2M_RESOURCE_TYPE_INT_VARIABLE_ARRAY) { + if(context->object_instance_index < resource->value.integervararr.count) { + resource->value.integervararr.var[context->object_instance_index] = + value; + return 1; + } + return 0; + } + /* Not an integer variable */ + return 0; +} +/*---------------------------------------------------------------------------*/ +int +lwm2m_object_is_resource_floatfix(const lwm2m_resource_t *resource) +{ + if(resource == NULL) { + return 0; + } + if(resource->type == LWM2M_RESOURCE_TYPE_FLOATFIX_VALUE || + resource->type == LWM2M_RESOURCE_TYPE_FLOATFIX_VARIABLE || + resource->type == LWM2M_RESOURCE_TYPE_FLOATFIX_VARIABLE_ARRAY) { + return 1; + } + return 0; +} +/*---------------------------------------------------------------------------*/ +int +lwm2m_object_get_resource_floatfix(const lwm2m_resource_t *resource, + const lwm2m_context_t *context, + int32_t *value) +{ + if(resource == NULL || context == NULL || value == NULL) { + return 0; + } + if(resource->type == LWM2M_RESOURCE_TYPE_FLOATFIX_VALUE) { + *value = resource->value.floatfix.value; + return 1; + } + if(resource->type == LWM2M_RESOURCE_TYPE_FLOATFIX_VARIABLE) { + *value = *(resource->value.floatfixvar.var); + return 1; + } + if(resource->type == LWM2M_RESOURCE_TYPE_FLOATFIX_VARIABLE_ARRAY) { + if(context->object_instance_index < resource->value.floatfixvararr.count) { + *value = resource->value.floatfixvararr.var[context->object_instance_index]; + return 1; + } + return 0; + } + /* Not an float */ + return 0; +} +/*---------------------------------------------------------------------------*/ +int +lwm2m_object_set_resource_floatfix(const lwm2m_resource_t *resource, + const lwm2m_context_t *context, + int32_t value) +{ + if(resource == NULL || context == NULL) { + return 0; + } + if(resource->type == LWM2M_RESOURCE_TYPE_FLOATFIX_VARIABLE) { + *(resource->value.floatfixvar.var) = value; + return 1; + } + if(resource->type == LWM2M_RESOURCE_TYPE_FLOATFIX_VARIABLE_ARRAY) { + if(context->object_instance_index < resource->value.floatfixvararr.count) { + resource->value.floatfixvararr.var[context->object_instance_index] = + value; + return 1; + } + return 0; + } + /* Not an float variable */ + return 0; +} +/*---------------------------------------------------------------------------*/ +int +lwm2m_object_is_resource_boolean(const lwm2m_resource_t *resource) +{ + if(resource == NULL) { + return 0; + } + if(resource->type == LWM2M_RESOURCE_TYPE_BOOLEAN_VALUE || + resource->type == LWM2M_RESOURCE_TYPE_BOOLEAN_VARIABLE || + resource->type == LWM2M_RESOURCE_TYPE_BOOLEAN_VARIABLE_ARRAY) { + return 1; + } + return 0; +} +/*---------------------------------------------------------------------------*/ +int +lwm2m_object_get_resource_boolean(const lwm2m_resource_t *resource, + const lwm2m_context_t *context, + int *value) +{ + if(resource == NULL || context == NULL || value == NULL) { + return 0; + } + if(resource->type == LWM2M_RESOURCE_TYPE_BOOLEAN_VALUE) { + *value = resource->value.boolean.value; + return 1; + } + if(resource->type == LWM2M_RESOURCE_TYPE_BOOLEAN_VARIABLE) { + *value = *(resource->value.booleanvar.var); + return 1; + } + if(resource->type == LWM2M_RESOURCE_TYPE_BOOLEAN_VARIABLE_ARRAY) { + if(context->object_instance_index < resource->value.booleanvararr.count) { + *value = resource->value.booleanvararr.var[context->object_instance_index]; + return 1; + } + return 0; + } + /* Not a boolean */ + return 0; +} +/*---------------------------------------------------------------------------*/ +int +lwm2m_object_set_resource_boolean(const lwm2m_resource_t *resource, + const lwm2m_context_t *context, + int value) +{ + if(resource == NULL || context == NULL) { + return 0; + } + if(resource->type == LWM2M_RESOURCE_TYPE_BOOLEAN_VARIABLE) { + *(resource->value.booleanvar.var) = value; + return 1; + } + if(resource->type == LWM2M_RESOURCE_TYPE_BOOLEAN_VARIABLE_ARRAY) { + if(context->object_instance_index < resource->value.booleanvararr.count) { + resource->value.booleanvararr.var[context->object_instance_index] = + value; + return 1; + } + return 0; + } + /* Not a boolean variable */ + return 0; +} +/*---------------------------------------------------------------------------*/ +/** @} */ diff --git a/apps/oma-lwm2m/lwm2m-object.h b/apps/oma-lwm2m/lwm2m-object.h new file mode 100644 index 000000000..5c391cb41 --- /dev/null +++ b/apps/oma-lwm2m/lwm2m-object.h @@ -0,0 +1,359 @@ +/* + * Copyright (c) 2015, Yanzi Networks AB. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of the copyright holder 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 HOLDER 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 HOLDER 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. + */ + +/** + * \addtogroup apps + * @{ + */ + +/** + * \defgroup oma-lwm2m An implementation of OMA LWM2M + * @{ + * + * This application is an implementation of OMA Lightweight M2M. + */ + +/** + * \file + * Header file for the Contiki OMA LWM2M object API + * \author + * Joakim Eriksson + * Niclas Finne + */ + +#ifndef LWM2M_OBJECT_H_ +#define LWM2M_OBJECT_H_ + +#include "rest-engine.h" +#include "er-coap-observe.h" + +#define LWM2M_OBJECT_SECURITY_ID 0 +#define LWM2M_OBJECT_SERVER_ID 1 +#define LWM2M_OBJECT_ACCESS_CONTROL_ID 2 +#define LWM2M_OBJECT_DEVICE_ID 3 +#define LWM2M_OBJECT_CONNECTIVITY_MONITORING_ID 4 +#define LWM2M_OBJECT_FIRMWARE_ID 5 +#define LWM2M_OBJECT_LOCATION_ID 6 +#define LWM2M_OBJECT_CONNECTIVITY_STATISTICS_ID 7 + +#define LWM2M_SECURITY_SERVER_URI 0 +#define LWM2M_SECURITY_BOOTSTRAP_SERVER 1 +#define LWM2M_SECURITY_MODE 2 +#define LWM2M_SECURITY_CLIENT_PKI 3 +#define LWM2M_SECURITY_SERVER_PKI 4 +#define LWM2M_SECURITY_KEY 5 +#define LWM2M_SECURITY_SHORT_SERVER_ID 10 + +/* Pre-shared key mode */ +#define LWM2M_SECURITY_MODE_PSK 0 +/* Raw Public Key mode */ +#define LWM2M_SECURITY_MODE_RPK 1 +/* Certificate mode */ +#define LWM2M_SECURITY_MODE_CERTIFICATE 2 +/* NoSec mode */ +#define LWM2M_SECURITY_MODE_NOSEC 3 + +#define LWM2M_OBJECT_STR_HELPER(x) (uint8_t *) #x +#define LWM2M_OBJECT_STR(x) LWM2M_OBJECT_STR_HELPER(x) + +#define LWM2M_OBJECT_PATH_STR_HELPER(x) #x +#define LWM2M_OBJECT_PATH_STR(x) LWM2M_OBJECT_PATH_STR_HELPER(x) + +struct lwm2m_reader; +struct lwm2m_writer; +/* Data model for OMA LWM2M objects */ +typedef struct lwm2m_context { + uint16_t object_id; + uint16_t object_instance_id; + uint16_t resource_id; + uint8_t object_instance_index; + uint8_t resource_index; + /* TODO - add uint16_t resource_instance_id */ + + const struct lwm2m_reader *reader; + const struct lwm2m_writer *writer; +} lwm2m_context_t; + +/* LWM2M format writer for the various formats supported */ +typedef struct lwm2m_writer { + size_t (* write_int)(const lwm2m_context_t *ctx, uint8_t *outbuf, size_t outlen, int32_t value); + size_t (* write_string)(const lwm2m_context_t *ctx, uint8_t *outbuf, size_t outlen, const char *value, size_t strlen); + size_t (* write_float32fix)(const lwm2m_context_t *ctx, uint8_t *outbuf, size_t outlen, int32_t value, int bits); + size_t (* write_boolean)(const lwm2m_context_t *ctx, uint8_t *outbuf, size_t outlen, int value); +} lwm2m_writer_t; + +typedef struct lwm2m_reader { + size_t (* read_int)(const lwm2m_context_t *ctx, const uint8_t *inbuf, size_t len, int32_t *value); + size_t (* read_string)(const lwm2m_context_t *ctx, const uint8_t *inbuf, size_t len, uint8_t *value, size_t strlen); + size_t (* read_float32fix)(const lwm2m_context_t *ctx, const uint8_t *inbuf, size_t len, int32_t *value, int bits); + size_t (* read_boolean)(const lwm2m_context_t *ctx, const uint8_t *inbuf, size_t len, int *value); +} lwm2m_reader_t; + +typedef struct lwm2m_value_callback { + int (* read)(lwm2m_context_t *ctx, uint8_t *outbuf, size_t outlen); + int (* write)(lwm2m_context_t *ctx, + const uint8_t *buffer, size_t len, + uint8_t *outbuf, size_t outlen); + int (* exec)(lwm2m_context_t *ctx, const uint8_t *arg, size_t len, + uint8_t *outbuf, size_t outlen); +} lwm2m_value_callback_t; + +#define LWM2M_RESOURCE_TYPE_STR_VALUE 1 +#define LWM2M_RESOURCE_TYPE_STR_VARIABLE 2 +#define LWM2M_RESOURCE_TYPE_STR_VARIABLE_ARRAY 3 +#define LWM2M_RESOURCE_TYPE_INT_VALUE 4 +#define LWM2M_RESOURCE_TYPE_INT_VARIABLE 5 +#define LWM2M_RESOURCE_TYPE_INT_VARIABLE_ARRAY 6 +#define LWM2M_RESOURCE_TYPE_FLOATFIX_VALUE 7 +#define LWM2M_RESOURCE_TYPE_FLOATFIX_VARIABLE 8 +#define LWM2M_RESOURCE_TYPE_FLOATFIX_VARIABLE_ARRAY 9 +#define LWM2M_RESOURCE_TYPE_BOOLEAN_VALUE 10 +#define LWM2M_RESOURCE_TYPE_BOOLEAN_VARIABLE 11 +#define LWM2M_RESOURCE_TYPE_BOOLEAN_VARIABLE_ARRAY 12 +#define LWM2M_RESOURCE_TYPE_CALLBACK 16 +#define LWM2M_RESOURCE_TYPE_INSTANCES 17 + +typedef struct lwm2m_resource { + uint16_t id; + uint8_t type; /* indicate value type and multi-instance resource */ + union { + struct { + uint16_t len; + const uint8_t *value; + } string; + struct { + uint16_t size; + uint16_t *len; + uint8_t **var; + } stringvar; + struct { + uint16_t count; + uint16_t size; + /* string var array with counting entries */ + uint16_t *len; + uint8_t *var; + } stringvararr; + struct { + int32_t value; + } integer; + struct { + int32_t *var; + } integervar; + struct { + /* used for multiple instances (dynamic) NOTE: this is an index into + the instance so having two instances means that there is need for + allocation of two ints here */ + uint16_t count; + int32_t *var; /* used as an array? */ + } integervararr; + struct { + int32_t value; + } floatfix; + struct { + int32_t *var; + } floatfixvar; + struct { + uint16_t count; + int32_t *var; + } floatfixvararr; + struct { + int value; + } boolean; + struct { + int *var; + } booleanvar; + struct { + uint16_t count; + int *var; + } booleanvararr; + lwm2m_value_callback_t callback; + /* lwm2m_resource *resources[]; TO BE ADDED LATER*/ + } value; +} lwm2m_resource_t; + +#define LWM2M_INSTANCE_FLAG_USED 1 + +typedef struct lwm2m_instance { + uint16_t id; + uint16_t count; + uint16_t flag; + const lwm2m_resource_t *resources; +} lwm2m_instance_t; + +typedef struct lwm2m_object { + uint16_t id; + uint16_t count; + const char *path; + resource_t *coap_resource; + lwm2m_instance_t *instances; +} lwm2m_object_t; + +#define LWM2M_RESOURCES(name, ...) \ + static const lwm2m_resource_t name[] = { __VA_ARGS__ } + +#define LWM2M_RESOURCE_STRING(id, s) \ + { id, LWM2M_RESOURCE_TYPE_STR_VALUE, .value.string.len = sizeof(s) - 1, .value.string.value = (uint8_t *) s } + +#define LWM2M_RESOURCE_STRING_VAR(id, s, l, v) \ + { id, LWM2M_RESOURCE_TYPE_STR_VARIABLE, .value.stringvar.size = (s), .value.stringvar.len = (l), .value.stringvar.var = (v) } + +#define LWM2M_RESOURCE_STRING_VAR_ARR(id, c, s, l, v) \ + { id, LWM2M_RESOURCE_TYPE_STR_VARIABLE_ARRAY, .value.stringvararr.count = c, .value.stringvararr.size = s, .value.stringvararr.len = l, .value.stringvararr.var = (uint8_t *) v } + +#define LWM2M_RESOURCE_INTEGER(id, v) \ + { id, LWM2M_RESOURCE_TYPE_INT_VALUE, .value.integer.value = (v) } + +#define LWM2M_RESOURCE_INTEGER_VAR(id, v) \ + { id, LWM2M_RESOURCE_TYPE_INT_VARIABLE, .value.integervar.var = (v) } + +#define LWM2M_RESOURCE_INTEGER_VAR_ARR(id, c, v) \ + { id, LWM2M_RESOURCE_TYPE_INT_VARIABLE_ARRAY, .value.integervararr.count = (c), .value.integervararr.var = (v) } + +#define LWM2M_RESOURCE_FLOATFIX(id, v) \ + { id, LWM2M_RESOURCE_TYPE_FLOATFIX_VALUE, .value.floatfix.value = (v) } + +#define LWM2M_RESOURCE_FLOATFIX_VAR(id, v) \ + { id, LWM2M_RESOURCE_TYPE_FLOATFIX_VARIABLE, .value.floatfixvar.var = (v) } + +#define LWM2M_RESOURCE_FLOATFIX_VAR_ARR(id, c, v) \ + { id, LWM2M_RESOURCE_TYPE_FLOATFIX_VARIABLE_ARRAY, .value.floatfixvararr.count = (c), .value.floatfixvararr.var = (v) } + +#define LWM2M_RESOURCE_BOOLEAN(id, v) \ + { id, LWM2M_RESOURCE_TYPE_BOOLEAN_VALUE, .value.boolean.value = (v) } + +#define LWM2M_RESOURCE_BOOLEAN_VAR(id, v) \ + { id, LWM2M_RESOURCE_TYPE_BOOLEAN_VARIABLE, .value.booleanvar.var = (v) } + +#define LWM2M_RESOURCE_BOOLEAN_VAR_ARR(id, c, v) \ + { id, LWM2M_RESOURCE_TYPE_BOOLEAN_VARIABLE_ARRAY, .value.booleanvararr.count = (c), .value.booleanvararr.var = (v) } + +#define LWM2M_RESOURCE_CALLBACK(id, ...) \ + { id, LWM2M_RESOURCE_TYPE_CALLBACK, .value.callback = __VA_ARGS__ } + +#define LWM2M_INSTANCE(id, resources) \ + { id, sizeof(resources)/sizeof(lwm2m_resource_t), LWM2M_INSTANCE_FLAG_USED, resources } + +#define LWM2M_INSTANCE_UNUSED(id, resources) \ + { id, sizeof(resources)/sizeof(lwm2m_resource_t), 0, resources } + +#define LWM2M_INSTANCES(name, ...) \ + static lwm2m_instance_t name[] = { __VA_ARGS__ } + +#define LWM2M_OBJECT(name, id, instances) \ + static void lwm2m_get_h_##name(void *request, void *response, uint8_t *buffer, uint16_t preferred_size, int32_t *offset); \ + static void lwm2m_put_h_##name(void *request, void *response, uint8_t *buffer, uint16_t preferred_size, int32_t *offset); \ + static void lwm2m_post_h_##name(void *request, void *response, uint8_t *buffer, uint16_t preferred_size, int32_t *offset); \ + static void lwm2m_delete_h_##name(void *request, void *response, uint8_t *buffer, uint16_t preferred_size, int32_t *offset); \ + static resource_t rest_rsc_##name = { NULL, NULL, HAS_SUB_RESOURCES | IS_OBSERVABLE, NULL, lwm2m_get_h_##name, lwm2m_post_h_##name, lwm2m_put_h_##name, lwm2m_delete_h_##name, { NULL } }; \ + static const lwm2m_object_t name = { id, sizeof(instances)/sizeof(lwm2m_instance_t), LWM2M_OBJECT_PATH_STR(id), &rest_rsc_##name, instances}; \ + static void lwm2m_get_h_##name(void *request, void *response, uint8_t *buffer, uint16_t preferred_size, int32_t *offset) { \ + lwm2m_engine_handler(&name, request, response, buffer, preferred_size, offset); } \ + static void lwm2m_put_h_##name(void *request, void *response, uint8_t *buffer, uint16_t preferred_size, int32_t *offset) { \ + lwm2m_engine_handler(&name, request, response, buffer, preferred_size, offset); } \ + static void lwm2m_post_h_##name(void *request, void *response, uint8_t *buffer, uint16_t preferred_size, int32_t *offset) { \ + lwm2m_engine_handler(&name, request, response, buffer, preferred_size, offset); } \ + static void lwm2m_delete_h_##name(void *request, void *response, uint8_t *buffer, uint16_t preferred_size, int32_t *offset) { \ + lwm2m_engine_delete_handler(&name, request, response, buffer, preferred_size, offset); } + +/* how do we register attributes in the above resource here ??? */ + +int lwm2m_object_is_resource_string(const lwm2m_resource_t *resource); +int lwm2m_object_is_resource_int(const lwm2m_resource_t *resource); +int lwm2m_object_is_resource_floatfix(const lwm2m_resource_t *resource); +int lwm2m_object_is_resource_boolean(const lwm2m_resource_t *resource); + +static inline int +lwm2m_object_is_resource_callback(const lwm2m_resource_t *resource) +{ + return resource != NULL && resource->type == LWM2M_RESOURCE_TYPE_CALLBACK; +} + +const uint8_t * +lwm2m_object_get_resource_string(const lwm2m_resource_t *resource, + const lwm2m_context_t *context); + +uint16_t +lwm2m_object_get_resource_strlen(const lwm2m_resource_t *resource, + const lwm2m_context_t *context); + +int +lwm2m_object_set_resource_string(const lwm2m_resource_t *resource, + const lwm2m_context_t *context, + uint16_t len, const uint8_t *string); + +int +lwm2m_object_get_resource_int(const lwm2m_resource_t *resource, + const lwm2m_context_t *context, + int32_t *value); + +int +lwm2m_object_set_resource_int(const lwm2m_resource_t *resource, + const lwm2m_context_t *context, + int32_t value); + +int +lwm2m_object_get_resource_floatfix(const lwm2m_resource_t *resource, + const lwm2m_context_t *context, + int32_t *value); + +int +lwm2m_object_set_resource_floatfix(const lwm2m_resource_t *resource, + const lwm2m_context_t *context, + int32_t value); + +int +lwm2m_object_get_resource_boolean(const lwm2m_resource_t *resource, + const lwm2m_context_t *context, + int *value); + +int +lwm2m_object_set_resource_boolean(const lwm2m_resource_t *resource, + const lwm2m_context_t *context, + int value); + +static inline resource_t * +lwm2m_object_get_coap_resource(const lwm2m_object_t *object) +{ + return (resource_t *)object->coap_resource; +} + +static inline void +lwm2m_object_notify_observers(const lwm2m_object_t *object, char *path) +{ + coap_notify_observers_sub(lwm2m_object_get_coap_resource(object), path); +} + +#include "lwm2m-engine.h" + +#endif /* LWM2M_OBJECT_H_ */ +/** + * @} + * @} + */ diff --git a/apps/oma-lwm2m/lwm2m-plain-text.c b/apps/oma-lwm2m/lwm2m-plain-text.c new file mode 100644 index 000000000..34b7f2318 --- /dev/null +++ b/apps/oma-lwm2m/lwm2m-plain-text.c @@ -0,0 +1,248 @@ +/* + * Copyright (c) 2015, Yanzi Networks AB. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of the copyright holder 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 HOLDER 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 HOLDER 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. + */ + +/** + * \addtogroup oma-lwm2m + * @{ + */ + +/** + * \file + * Implementation of the Contiki OMA LWM2M plain text reader / writer + * \author + * Joakim Eriksson + * Niclas Finne + */ + +#include "lwm2m-object.h" +#include "lwm2m-plain-text.h" +#include +#include +#include + +#define DEBUG 0 +#if DEBUG +#define PRINTF(...) printf(__VA_ARGS__) +#else +#define PRINTF(...) +#endif + +/*---------------------------------------------------------------------------*/ +size_t +lwm2m_plain_text_read_int(const uint8_t *inbuf, size_t len, int32_t *value) +{ + int i, neg = 0; + *value = 0; + for(i = 0; i < len; i++) { + if(inbuf[i] >= '0' && inbuf[i] <= '9') { + *value = *value * 10 + (inbuf[i] - '0'); + } else if(inbuf[i] == '-' && i == 0) { + neg = 1; + } else { + break; + } + } + if(neg) { + *value = -*value; + } + return i; +} +/*---------------------------------------------------------------------------*/ +size_t +lwm2m_plain_text_read_float32fix(const uint8_t *inbuf, size_t len, + int32_t *value, int bits) +{ + int i, dot = 0, neg = 0; + int32_t counter, integerpart, frac; + + integerpart = 0; + counter = 0; + frac = 0; + for(i = 0; i < len; i++) { + if(inbuf[i] >= '0' && inbuf[i] <= '9') { + counter = counter * 10 + (inbuf[i] - '0'); + frac = frac * 10; + } else if(inbuf[i] == '.' && dot == 0) { + integerpart = counter; + counter = 0; + frac = 1; + dot = 1; + } else if(inbuf[i] == '-' && i == 0) { + neg = 1; + } else { + break; + } + } + *value = integerpart << bits; + if(frac > 1) { + *value += ((counter << bits) / frac); + } + PRINTF("READ FLOATFIX: \"%.*s\" => int(%ld) frac(%ld) f=%ld Value=%ld\n", + (int)len, (char *)inbuf, + (long)integerpart, + (long)counter, + (long)frac, + (long)*value); + if(neg) { + *value = -*value; + } + return i; +} +/*---------------------------------------------------------------------------*/ +size_t +lwm2m_plain_text_write_float32fix(uint8_t *outbuf, size_t outlen, + int32_t value, int bits) +{ + int64_t v; + unsigned long integer_part; + unsigned long frac_part; + int n, o = 0; + + if(outlen == 0) { + return 0; + } + if(value < 0) { + *outbuf++ = '-'; + outlen--; + o = 1; + value = -value; + } + + integer_part = (unsigned long)(value >> bits); + v = value - (integer_part << bits); + v = (v * 100) >> bits; + frac_part = (unsigned long)v; + + n = snprintf((char *)outbuf, outlen, "%lu.%02lu", integer_part, frac_part); + if(n < 0 || n >= outlen) { + return 0; + } + return n + o; +} +/*---------------------------------------------------------------------------*/ +static size_t +write_boolean(const lwm2m_context_t *ctx, uint8_t *outbuf, size_t outlen, + int value) +{ + if(outlen > 0) { + if(value) { + *outbuf = '1'; + } else { + *outbuf = '0'; + } + return 1; + } + return 0; +} +/*---------------------------------------------------------------------------*/ +static size_t +write_int(const lwm2m_context_t *ctx, uint8_t *outbuf, size_t outlen, + int32_t value) +{ + int n = snprintf((char *)outbuf, outlen, "%ld", (long)value); + if(n < 0 || n >= outlen) { + return 0; + } + return n; +} +/*---------------------------------------------------------------------------*/ +static size_t +write_float32fix(const lwm2m_context_t *ctx, uint8_t *outbuf, size_t outlen, + int32_t value, int bits) +{ + return lwm2m_plain_text_write_float32fix(outbuf, outlen, value, bits); +} +/*---------------------------------------------------------------------------*/ +static size_t +write_string(const lwm2m_context_t *ctx, uint8_t *outbuf, size_t outlen, + const char *value, size_t stringlen) +{ + int n = snprintf((char *)outbuf, outlen, "%.*s", (int) stringlen, value); + if(n < 0 || n >= outlen) { + return 0; + } + return n; +} +/*---------------------------------------------------------------------------*/ +const lwm2m_writer_t lwm2m_plain_text_writer = { + write_int, + write_string, + write_float32fix, + write_boolean +}; +/*---------------------------------------------------------------------------*/ +static size_t +read_int(const lwm2m_context_t *ctx, const uint8_t *inbuf, size_t len, + int32_t *value) +{ + return lwm2m_plain_text_read_int(inbuf, len, value); +} +/*---------------------------------------------------------------------------*/ +static size_t +read_string(const lwm2m_context_t *ctx, const uint8_t *inbuf, size_t len, + uint8_t *value, size_t stringlen) +{ + if(stringlen <= len) { + /* The outbuffer can not contain the full string including ending zero */ + return 0; + } + memcpy(value, inbuf, len); + value[len] = '\0'; + return len; +} +/*---------------------------------------------------------------------------*/ +static size_t +read_float32fix(const lwm2m_context_t *ctx, const uint8_t *inbuf, size_t len, + int32_t *value, int bits) +{ + return lwm2m_plain_text_read_float32fix(inbuf, len, value, bits); +} +/*---------------------------------------------------------------------------*/ +static size_t +read_boolean(const lwm2m_context_t *ctx, const uint8_t *inbuf, size_t len, + int *value) +{ + if(len > 0) { + if(*inbuf == '1' || *inbuf == '0') { + *value = *inbuf == '1' ? 1 : 0; + return 1; + } + } + return 0; +} +/*---------------------------------------------------------------------------*/ +const lwm2m_reader_t lwm2m_plain_text_reader = { + read_int, + read_string, + read_float32fix, + read_boolean +}; +/*---------------------------------------------------------------------------*/ +/** @} */ diff --git a/apps/oma-lwm2m/lwm2m-plain-text.h b/apps/oma-lwm2m/lwm2m-plain-text.h new file mode 100644 index 000000000..bf12a118c --- /dev/null +++ b/apps/oma-lwm2m/lwm2m-plain-text.h @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2015, Yanzi Networks AB. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of the copyright holder 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 HOLDER 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 HOLDER 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. + */ + +/** + * \addtogroup oma-lwm2m + * @{ + */ + +/** + * \file + * Header file for the Contiki OMA LWM2M plain text reader / writer + * \author + * Joakim Eriksson + * Niclas Finne + */ + +#ifndef LWM2M_PLAIN_TEXT_H_ +#define LWM2M_PLAIN_TEXT_H_ + +#include "lwm2m-object.h" + +extern const lwm2m_reader_t lwm2m_plain_text_reader; +extern const lwm2m_writer_t lwm2m_plain_text_writer; + +size_t lwm2m_plain_text_read_int(const uint8_t *inbuf, size_t len, + int32_t *value); + +size_t lwm2m_plain_text_read_float32fix(const uint8_t *inbuf, size_t len, + int32_t *value, int bits); + +size_t lwm2m_plain_text_write_float32fix(uint8_t *outbuf, size_t outlen, + int32_t value, int bits); + +#endif /* LWM2M_PLAIN_TEXT_H_ */ +/** @} */ diff --git a/apps/oma-lwm2m/lwm2m-security.c b/apps/oma-lwm2m/lwm2m-security.c new file mode 100644 index 000000000..953279b4d --- /dev/null +++ b/apps/oma-lwm2m/lwm2m-security.c @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2015, Yanzi Networks AB. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of the copyright holder 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 HOLDER 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 HOLDER 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. + */ + +/** + * \addtogroup oma-lwm2m + * @{ + * + */ + +/** + * \file + * Implementation of the Contiki OMA LWM2M security + * \author + * Joakim Eriksson + * Niclas Finne + */ + +#include +#include "lwm2m-object.h" +#include "lwm2m-engine.h" + +#define DEBUG 0 +#if DEBUG +#include +#define PRINTF(...) printf(__VA_ARGS__) +#else +#define PRINTF(...) +#endif + +#ifdef LWM2M_CONF_SERVER_MAX_COUNT +#define MAX_COUNT LWM2M_CONF_SERVER_MAX_COUNT +#else +#define MAX_COUNT 2 +#endif + +/* hoping that we do not get more than 64 bytes... */ +#define MAX_SIZE 64 + +static int32_t bs_arr[MAX_COUNT]; +static int32_t secmode_arr[MAX_COUNT]; +static int32_t sid_arr[MAX_COUNT]; + +static char server_uri[MAX_COUNT][MAX_SIZE]; +static uint16_t su_len[MAX_COUNT]; +static char client_id[MAX_COUNT][MAX_SIZE]; +static uint16_t client_id_len[MAX_COUNT]; +static char server_id[MAX_COUNT][MAX_SIZE]; +static uint16_t server_id_len[MAX_COUNT]; +static char psk_key[MAX_COUNT][MAX_SIZE]; +static uint16_t psk_key_len[MAX_COUNT]; +static lwm2m_instance_t security_instances[MAX_COUNT]; + +LWM2M_RESOURCES(security_resources, + LWM2M_RESOURCE_STRING_VAR_ARR(0, MAX_COUNT, MAX_SIZE, su_len, server_uri), + LWM2M_RESOURCE_INTEGER_VAR_ARR(1, MAX_COUNT, bs_arr), + LWM2M_RESOURCE_INTEGER_VAR_ARR(2, MAX_COUNT, secmode_arr), + LWM2M_RESOURCE_STRING_VAR_ARR(3, MAX_COUNT, MAX_SIZE, client_id_len, client_id), + LWM2M_RESOURCE_STRING_VAR_ARR(4, MAX_COUNT, MAX_SIZE, server_id_len, server_id), + /* TODO This should not be readable! */ + LWM2M_RESOURCE_STRING_VAR_ARR(5, MAX_COUNT, MAX_SIZE, psk_key_len, psk_key), + LWM2M_RESOURCE_INTEGER_VAR_ARR(10, MAX_COUNT, sid_arr) + ); +LWM2M_OBJECT(security, 0, security_instances); +/*---------------------------------------------------------------------------*/ +void +lwm2m_security_init(void) +{ + lwm2m_instance_t template = LWM2M_INSTANCE_UNUSED(0, security_resources); + int i; + + /* Initialize the instances */ + for(i = 0; i < MAX_COUNT; i++) { + security_instances[i] = template; + security_instances[i].id = i; + } + + /** + * Register this device and its handlers - the handlers + * automatically sends in the object to handle. + */ + PRINTF("*** Init lwm2m-security\n"); + lwm2m_engine_register_object(&security); +} +/*---------------------------------------------------------------------------*/ +/** @} */ diff --git a/apps/oma-lwm2m/lwm2m-server.c b/apps/oma-lwm2m/lwm2m-server.c new file mode 100644 index 000000000..9de678682 --- /dev/null +++ b/apps/oma-lwm2m/lwm2m-server.c @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2015, Yanzi Networks AB. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of the copyright holder 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 HOLDER 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 HOLDER 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. + */ + +/** + * \addtogroup oma-lwm2m + * @{ + * + */ + +/** + * \file + * Implementation of the Contiki OMA LWM2M server + * \author + * Joakim Eriksson + * Niclas Finne + */ + +#include +#include "lwm2m-object.h" +#include "lwm2m-engine.h" + +#define DEBUG 0 +#if DEBUG +#include +#define PRINTF(...) printf(__VA_ARGS__) +#else +#define PRINTF(...) +#endif + +#ifdef LWM2M_CONF_SERVER_MAX_COUNT +#define MAX_COUNT LWM2M_CONF_SERVER_MAX_COUNT +#else +#define MAX_COUNT 2 +#endif + +static int32_t sid_arr[MAX_COUNT]; +static int32_t lifetime_arr[MAX_COUNT]; +static lwm2m_instance_t server_instances[MAX_COUNT]; + +LWM2M_RESOURCES(server_resources, + LWM2M_RESOURCE_INTEGER_VAR_ARR(0, MAX_COUNT, sid_arr), + LWM2M_RESOURCE_INTEGER_VAR_ARR(1, MAX_COUNT, lifetime_arr), + ); +LWM2M_OBJECT(server, 1, server_instances); +/*---------------------------------------------------------------------------*/ +void +lwm2m_server_init(void) +{ + lwm2m_instance_t template = LWM2M_INSTANCE_UNUSED(0, server_resources); + int i; + + /* Initialize the instances */ + for(i = 0; i < MAX_COUNT; i++) { + server_instances[i] = template; + server_instances[i].id = i; + } + + /** + * Register this device and its handlers - the handlers + * automatically sends in the object to handle + */ + PRINTF("*** Init lwm2m-server\n"); + lwm2m_engine_register_object(&server); +} +/*---------------------------------------------------------------------------*/ +/** @} */ diff --git a/apps/oma-lwm2m/oma-tlv-reader.c b/apps/oma-lwm2m/oma-tlv-reader.c new file mode 100644 index 000000000..58e1dcc54 --- /dev/null +++ b/apps/oma-lwm2m/oma-tlv-reader.c @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2015, Yanzi Networks AB. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of the copyright holder 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 HOLDER 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 HOLDER 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. + */ + +/** + * \addtogroup oma-lwm2m + * @{ + * + */ + +/** + * \file + * Implementation of the Contiki OMA LWM2M TLV reader + * \author + * Joakim Eriksson + * Niclas Finne + */ + +#include "lwm2m-object.h" +#include "oma-tlv-reader.h" +#include "oma-tlv.h" + +/*---------------------------------------------------------------------------*/ +static size_t +read_int(const lwm2m_context_t *ctx, const uint8_t *inbuf, size_t len, + int32_t *value) +{ + oma_tlv_t tlv; + size_t size; + size = oma_tlv_read(&tlv, inbuf, len); + if(size > 0) { + *value = oma_tlv_get_int32(&tlv); + } + return size; +} +/*---------------------------------------------------------------------------*/ +static size_t +read_string(const lwm2m_context_t *ctx, const uint8_t *inbuf, size_t len, + uint8_t *value, size_t stringlen) +{ + oma_tlv_t tlv; + size_t size; + size = oma_tlv_read(&tlv, inbuf, len); + if(size > 0) { + if(stringlen <= tlv.length) { + /* The outbuffer can not contain the full string including ending zero */ + return 0; + } + memcpy(value, tlv.value, tlv.length); + value[tlv.length] = '\0'; + } + return size; +} +/*---------------------------------------------------------------------------*/ +static size_t +read_float32fix(const lwm2m_context_t *ctx, const uint8_t *inbuf, size_t len, + int32_t *value, int bits) +{ + oma_tlv_t tlv; + size_t size; + size = oma_tlv_read(&tlv, inbuf, len); + if(size > 0) { + oma_tlv_float32_to_fix(&tlv, value, bits); + } + return size; +} +/*---------------------------------------------------------------------------*/ +static size_t +read_boolean(const lwm2m_context_t *ctx, const uint8_t *inbuf, size_t len, + int *value) +{ + oma_tlv_t tlv; + size_t size; + size = oma_tlv_read(&tlv, inbuf, len); + if(size > 0) { + *value = oma_tlv_get_int32(&tlv) != 0; + } + return size; +} +/*---------------------------------------------------------------------------*/ +const lwm2m_reader_t oma_tlv_reader = { + read_int, + read_string, + read_float32fix, + read_boolean +}; +/*---------------------------------------------------------------------------*/ +/** @} */ diff --git a/apps/oma-lwm2m/oma-tlv-reader.h b/apps/oma-lwm2m/oma-tlv-reader.h new file mode 100644 index 000000000..7e6540c18 --- /dev/null +++ b/apps/oma-lwm2m/oma-tlv-reader.h @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2015, Yanzi Networks AB. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of the copyright holder 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 HOLDER 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 HOLDER 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. + */ + +/** \addtogroup oma-lwm2m + * @{ */ + +/** + * \file + * Header file for the Contiki OMA LWM2M TLV reader + * \author + * Joakim Eriksson + * Niclas Finne + */ + +#ifndef OMA_TLV_READER_H_ +#define OMA_TLV_READER_H_ + +#include "lwm2m-object.h" + +extern const lwm2m_reader_t oma_tlv_reader; + +#endif /* OMA_TLV_READER_H_ */ +/** @} */ diff --git a/apps/oma-lwm2m/oma-tlv-writer.c b/apps/oma-lwm2m/oma-tlv-writer.c new file mode 100644 index 000000000..2f26a69f6 --- /dev/null +++ b/apps/oma-lwm2m/oma-tlv-writer.c @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2015, Yanzi Networks AB. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of the copyright holder 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 HOLDER 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 HOLDER 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. + */ + +/** + * \addtogroup oma-lwm2m + * @{ + * + */ + +/** + * \file + * Implementation of the Contiki OMA LWM2M TLV writer + * \author + * Joakim Eriksson + * Niclas Finne + */ + +#include "lwm2m-object.h" +#include "oma-tlv.h" +/*---------------------------------------------------------------------------*/ +static size_t +write_boolean_tlv(const lwm2m_context_t *ctx, uint8_t *outbuf, size_t outlen, + int value) +{ + return oma_tlv_write_int32(ctx->resource_id, value != 0 ? 1 : 0, + outbuf, outlen); +} +/*---------------------------------------------------------------------------*/ +static size_t +write_int_tlv(const lwm2m_context_t *ctx, uint8_t *outbuf, size_t outlen, + int32_t value) +{ + return oma_tlv_write_int32(ctx->resource_id, value, outbuf, outlen); +} +/*---------------------------------------------------------------------------*/ +static size_t +write_float32fix_tlv(const lwm2m_context_t *ctx, uint8_t *outbuf, + size_t outlen, int32_t value, int bits) +{ + return oma_tlv_write_float32(ctx->resource_id, value, bits, outbuf, outlen); +} +/*---------------------------------------------------------------------------*/ +static size_t +write_string_tlv(const lwm2m_context_t *ctx, uint8_t *outbuf, size_t outlen, + const char *value, size_t stringlen) +{ + oma_tlv_t tlv; + tlv.type = OMA_TLV_TYPE_RESOURCE; + tlv.value = (uint8_t *) value; + tlv.length = (uint32_t) stringlen; + tlv.id = ctx->resource_id; + return oma_tlv_write(&tlv, outbuf, outlen); +} +/*---------------------------------------------------------------------------*/ +const lwm2m_writer_t oma_tlv_writer = { + write_int_tlv, + write_string_tlv, + write_float32fix_tlv, + write_boolean_tlv +}; +/*---------------------------------------------------------------------------*/ +/** @} */ diff --git a/apps/oma-lwm2m/oma-tlv-writer.h b/apps/oma-lwm2m/oma-tlv-writer.h new file mode 100644 index 000000000..6ae5edd14 --- /dev/null +++ b/apps/oma-lwm2m/oma-tlv-writer.h @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2015, Yanzi Networks AB. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of the copyright holder 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 HOLDER 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 HOLDER 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. + */ + +/** \addtogroup oma-lwm2m + * @{ */ + +/** + * \file + * Header file for the Contiki OMA LWM2M TLV writer + * \author + * Joakim Eriksson + * Niclas Finne + */ + +#ifndef OMA_TLV_WRITER_H_ +#define OMA_TLV_WRITER_H_ + +#include "lwm2m-object.h" + +extern const lwm2m_writer_t oma_tlv_writer; + +#endif /* OMA_TLV_WRITER_H_ */ +/** @} */ diff --git a/apps/oma-lwm2m/oma-tlv.c b/apps/oma-lwm2m/oma-tlv.c new file mode 100644 index 000000000..c07df31cc --- /dev/null +++ b/apps/oma-lwm2m/oma-tlv.c @@ -0,0 +1,296 @@ +/* + * Copyright (c) 2015, Yanzi Networks AB. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of the copyright holder 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 HOLDER 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 HOLDER 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. + */ + +/** + * \addtogroup oma-lwm2m + * @{ + * + */ + +/** + * \file + * Implementation of the Contiki OMA LWM2M TLV + * \author + * Joakim Eriksson + * Niclas Finne + */ + +#include +#include +#include "oma-tlv.h" + +#define DEBUG 0 +#if DEBUG +#include +#define PRINTF(...) printf(__VA_ARGS__) +#else +#define PRINTF(...) +#endif + +/*---------------------------------------------------------------------------*/ +static inline uint8_t +get_len_type(const oma_tlv_t *tlv) +{ + if(tlv->length < 8) { + return 0; + } else if(tlv->length < 256) { + return 1; + } else if(tlv->length < 0x10000) { + return 2; + } else { + return 3; + } +} +/*---------------------------------------------------------------------------*/ +size_t +oma_tlv_read(oma_tlv_t *tlv, const uint8_t *buffer, size_t len) +{ + uint8_t len_type; + uint8_t len_pos = 1; + size_t tlv_len; + + tlv->type = (buffer[0] >> 6) & 3; + len_type = (buffer[0] >> 3) & 3; + len_pos = 1 + (((buffer[0] & (1 << 5)) != 0) ? 2 : 1); + + tlv->id = buffer[1]; + /* if len_pos is larger than two it means that there is more ID to read */ + if(len_pos > 2) { + tlv->id = (tlv->id << 8) + buffer[2]; + } + + if(len_type == 0) { + tlv_len = buffer[0] & 7; + } else { + /* read the length */ + tlv_len = 0; + while(len_type > 0) { + tlv_len = tlv_len << 8 | buffer[len_pos++]; + len_type--; + } + } + /* and read out the data??? */ + tlv->length = tlv_len; + tlv->value = &buffer[len_pos]; + + return len_pos + tlv_len; +} +/*---------------------------------------------------------------------------*/ +size_t +oma_tlv_get_size(const oma_tlv_t *tlv) +{ + size_t size; + /* first hdr + len size */ + size = 1 + get_len_type(tlv); + /* id size */ + size += (tlv->id > 255) ? 2 : 1; + + /* and the length */ + size += tlv->length; + return size; +} +/*---------------------------------------------------------------------------*/ +size_t +oma_tlv_write(const oma_tlv_t *tlv, uint8_t *buffer, size_t len) +{ + int pos; + uint8_t len_type; + + /* len type is the same as number of bytes required for length */ + len_type = get_len_type(tlv); + pos = 1 + len_type; + /* ensure that we do not write too much */ + if(len < tlv->length + pos) { + PRINTF("OMA-TLV: Could not write the TLV - buffer overflow.\n"); + return 0; + } + + /* first type byte in TLV header */ + buffer[0] = (tlv->type << 6) | + (tlv->id > 255 ? (1 << 5) : 0) | + (len_type << 3) | + (len_type == 0 ? tlv->length : 0); + + pos = 1; + /* The ID */ + if(tlv->id > 255) { + buffer[pos++] = (tlv->id >> 8) & 0xff; + } + buffer[pos++] = tlv->id & 0xff; + /* Add length if needed - unrolled loop ? */ + if(len_type > 2) { + buffer[pos++] = (tlv->length >> 16) & 0xff; + } + if(len_type > 1) { + buffer[pos++] = (tlv->length >> 8) & 0xff; + } + if(len_type > 0) { + buffer[pos++] = tlv->length & 0xff; + } + + /* finally add the value */ + memcpy(&buffer[pos], tlv->value, tlv->length); + + if(DEBUG) { + int i; + PRINTF("TLV:"); + for(i = 0; i < pos + tlv->length; i++) { + PRINTF("%02x", buffer[i]); + } + PRINTF("\n"); + } + + return pos + tlv->length; +} +/*---------------------------------------------------------------------------*/ +int32_t +oma_tlv_get_int32(const oma_tlv_t *tlv) +{ + int i; + int32_t value = 0; + /* will probably need to handle MSB as a sign bit? */ + for(i = 0; i < tlv->length; i++) { + value = (value << 8) | tlv->value[i]; + } + return value; +} +/*---------------------------------------------------------------------------*/ +size_t +oma_tlv_write_int32(int16_t id, int32_t value, uint8_t *buffer, size_t len) +{ + oma_tlv_t tlv; + size_t tlvlen = 0; + uint8_t buf[4]; + int i; + PRINTF("Exporting int32 %d %ld ", id, (long)value); + + buf[3] = value & 0xff; + value = value >> 8; + for(i = 1; value > 0 && i < 4; i++) { + buf[3 - i] = value & 0xff; + value = value >> 8; + } + tlvlen = i; + + /* export INT as TLV */ + PRINTF("len: %zu\n", tlvlen); + tlv.type = OMA_TLV_TYPE_RESOURCE; + tlv.length = tlvlen; + tlv.value = &buf[3 - (tlvlen - 1)]; + tlv.id = id; + return oma_tlv_write(&tlv, buffer, len); +} +/*---------------------------------------------------------------------------*/ +/* convert fixpoint 32-bit to a IEEE Float in the byte array*/ +size_t +oma_tlv_write_float32(int16_t id, int32_t value, int bits, + uint8_t *buffer, size_t len) +{ + int i; + int e = 0; + int32_t val = 0; + int32_t v; + uint8_t b[4]; + oma_tlv_t tlv; + + v = value; + if(v < 0) { + v = -v; + } + + while(v > 1) { + val = (val >> 1); + if (v & 1) { + val = val | (1L << 22); + } + v = (v >> 1); + e++; + } + + PRINTF("Sign: %d, Fraction: %06lx 0b", value < 0, (long)val); + for(i = 0; i < 23; i++) { + PRINTF("%d", (int)((val >> (22 - i)) & 1)); + } + PRINTF("\nExp:%d\n", e); + + /* convert to the thing we should have */ + e = e - bits + 127; + + /* is this the right byte order? */ + b[0] = (value < 0 ? 0x80 : 0) | (e >> 1); + b[1] = ((e & 1) << 7) | ((val >> 16) & 0x7f); + b[2] = (val >> 8) & 0xff; + b[3] = val & 0xff; + + /* construct the TLV */ + tlv.type = OMA_TLV_TYPE_RESOURCE; + tlv.length = 4; + tlv.value = b; + tlv.id = id; + + return oma_tlv_write(&tlv, buffer, len); +} +/*---------------------------------------------------------------------------*/ +/* convert float to fixpoint */ +size_t +oma_tlv_float32_to_fix(const oma_tlv_t *tlv, int32_t *value, int bits) +{ + /* TLV needs to be 4 bytes */ + int e, i; + int32_t val; + int sign = (tlv->value[0] & 0x80) != 0; + e = ((tlv->value[0] << 1) & 0xff) | (tlv->value[1] >> 7); + val = (((long)tlv->value[1] & 0x7f) << 16) | (tlv->value[2] << 8) | tlv->value[3]; + + PRINTF("Sign: %d, Fraction: %06lx 0b", val < 0, (long)val); + for(i = 0; i < 23; i++) { + PRINTF("%d", (int)((val >> (22 - i)) & 1)); + } + PRINTF("\nExp:%d => %d\n", e, e - 127); + + e = e - 127 + bits; + + /* e corresponds to the number of times we need to roll the number */ + + PRINTF("Actual e=%d\n", e); + e = e - 23; + PRINTF("E after sub %d\n", e); + val = val | 1L << 23; + if(e > 0) { + val = val << e; + } else { + val = val >> -e; + } + + *value = sign ? -val : val; + return 4; +} +/*---------------------------------------------------------------------------*/ +/** @} */ diff --git a/apps/oma-lwm2m/oma-tlv.h b/apps/oma-lwm2m/oma-tlv.h new file mode 100644 index 000000000..1bd5fd94a --- /dev/null +++ b/apps/oma-lwm2m/oma-tlv.h @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2015, Yanzi Networks AB. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of the copyright holder 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 HOLDER 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 HOLDER 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. + */ + +/** \addtogroup oma-lwm2m + * @{ */ + +/** + * \file + * Header file for the Contiki OMA LWM2M TLV + * \author + * Joakim Eriksson + * Niclas Finne + */ + +#ifndef OAM_TLV_H_ +#define OAM_TLV_H_ + +#include "contiki.h" + +enum { + OMA_TLV_TYPE_OBJECT_INSTANCE = 0, + OMA_TLV_TYPE_RESOURCE_INSTANCE = 1, + OMA_TLV_TYPE_MULTI_RESOURCE = 2, + OMA_TLV_TYPE_RESOURCE = 3 +}; +typedef uint8_t oma_tlv_type_t; + +typedef enum { + OMA_TLV_LEN_TYPE_NO_LEN = 0, + OMA_TLV_LEN_TYPE_8BIT_LEN = 1, + OMA_TLV_LEN_TYPE_16BIT_LEN = 2, + OMA_TLV_LEN_TYPE_24BIT_LEN = 3 +} oma_tlv_len_type_t; + +typedef struct { + oma_tlv_type_t type; + uint16_t id; /* can be 8-bit or 16-bit when serialized */ + uint32_t length; + const uint8_t *value; +} oma_tlv_t; + +size_t oma_tlv_get_size(const oma_tlv_t *tlv); + +/* read a TLV from the buffer */ +size_t oma_tlv_read(oma_tlv_t *tlv, const uint8_t *buffer, size_t len); + +/* write a TLV to the buffer */ +size_t oma_tlv_write(const oma_tlv_t *tlv, uint8_t *buffer, size_t len); + +int32_t oma_tlv_get_int32(const oma_tlv_t *tlv); + +/* write a int as a TLV to the buffer */ +size_t oma_tlv_write_int32(int16_t id, int32_t value, uint8_t *buffer, size_t len); + +/* write a float converted from fixpoint as a TLV to the buffer */ +size_t oma_tlv_write_float32(int16_t id, int32_t value, int bits, uint8_t *buffer, size_t len); + +/* convert TLV with float32 to fixpoint */ +size_t oma_tlv_float32_to_fix(const oma_tlv_t *tlv, int32_t *value, int bits); + +#endif /* OAM_TLV_H_ */ +/** @} */ diff --git a/examples/ipso-objects/Makefile b/examples/ipso-objects/Makefile new file mode 100644 index 000000000..8f620741c --- /dev/null +++ b/examples/ipso-objects/Makefile @@ -0,0 +1,29 @@ +CONTIKI_PROJECT = example-ipso-objects + +CONTIKI_SOURCEFILES += serial-protocol.c example-ipso-temperature.c + +all: $(CONTIKI_PROJECT) + +CFLAGS += -DPROJECT_CONF_H=\"project-conf.h\" + +APPS += rest-engine +APPS += er-coap +APPS += oma-lwm2m +APPS += ipso-objects + +CONTIKI=../.. +CONTIKI_WITH_IPV6 = 1 +include $(CONTIKI)/Makefile.include + +# border router rules +$(CONTIKI)/tools/tunslip6: $(CONTIKI)/tools/tunslip6.c + (cd $(CONTIKI)/tools && $(MAKE) tunslip6) + +connect-router: $(CONTIKI)/tools/tunslip6 + sudo $(CONTIKI)/tools/tunslip6 aaaa::1/64 + +connect-router-cooja: $(CONTIKI)/tools/tunslip6 + sudo $(CONTIKI)/tools/tunslip6 -a 127.0.0.1 -p 60001 aaaa::1/64 + +connect-router-native: $(CONTIKI)/examples/ipv6/native-border-router/border-router.native + sudo $(CONTIKI)/exmples/ipv6/native-border-router/border-router.native -a 127.0.0.1 -p 60001 aaaa::1/64 diff --git a/examples/ipso-objects/README.md b/examples/ipso-objects/README.md new file mode 100644 index 000000000..6d1eb5871 --- /dev/null +++ b/examples/ipso-objects/README.md @@ -0,0 +1,48 @@ +IPSO Objects Example +============================================ + +This is an example of how to make use of the IPSO Object and LWM2M +implementation in Contiki. + +The LWM2M implementation is based on the Erbium CoAP implementation +and consists of two apps: lwm2m-engine and ipso-objects. The +lwm2m-engine handle the specifics of LWM2M including bootstrapping and +how read/writes of objects and resources are handled. The ipso-objects +contains implementations of some of the IPSO Smart Objects. + +The implementation was used during the IPSO Interop in May 2015, +Kista, Sweden, and was successfully tested with other +implementations. + +The examples use some of the basic IPSO object for controlling LEDs on +Contiki devices and for reading out temperature. + +##Testing IPSO-objects with Leshan + +First program a device with the examples/ipso-objects/example-ipso-objects.c + +```bash +>make example-ipso-objects.upload TARGET=zoul +>... +``` + +After that start up a native-border router or other border router on aaaa::1/64 +or another prefix - NOTE: if you use another prefix you will need to change LWM2M_SERVER_ADDRESS for which the device will register - in project-conf.h: +``` +#define LWM2M_SERVER_ADDRESS "aaaa::1" +``` + +Then when everything is setup you can download a Leshan and use that to +test controlling LEDs of the device. + +###Starting Leshan +```bash +wget https://hudson.eclipse.org/leshan/job/leshan/lastSuccessfulBuild/artifact/leshan-standalone.jar +java -jar ./leshan-standalone.jar +``` +Browse to leshans device page with http://127.0.0.1:8080 . + +When you have started the border-router and also Leshan you should now +start (or reboot) your IPSO Object enabled device. Within 30 seconds +you should be able to see it on the Leshan device page. + diff --git a/examples/ipso-objects/cooja-example-ipso-objects.csc b/examples/ipso-objects/cooja-example-ipso-objects.csc new file mode 100644 index 000000000..d8dc44876 --- /dev/null +++ b/examples/ipso-objects/cooja-example-ipso-objects.csc @@ -0,0 +1,171 @@ + + + [APPS_DIR]/mrm + [APPS_DIR]/mspsim + [APPS_DIR]/avrora + [APPS_DIR]/serial_socket + [APPS_DIR]/collect-view + [APPS_DIR]/powertracker + + LWM2M & IPSO Objects Example + 1.0 + 123456 + 1000000 + + org.contikios.cooja.radiomediums.UDGM + 50.0 + 100.0 + 1.0 + 1.0 + + + 40000 + + + org.contikios.cooja.mspmote.WismoteMoteType + wismote1 + Wismote Border Router #border-router + [CONTIKI_DIR]/examples/ipv6/rpl-border-router/border-router.c + make border-router.wismote TARGET=wismote DEFINES=NETSTACK_RDC=nullrdc_driver,NETSTACK_MAC=nullmac_driver + [CONTIKI_DIR]/examples/ipv6/rpl-border-router/border-router.wismote + org.contikios.cooja.interfaces.Position + org.contikios.cooja.interfaces.RimeAddress + org.contikios.cooja.interfaces.IPAddress + org.contikios.cooja.interfaces.Mote2MoteRelations + org.contikios.cooja.interfaces.MoteAttributes + org.contikios.cooja.mspmote.interfaces.MspClock + org.contikios.cooja.mspmote.interfaces.MspMoteID + org.contikios.cooja.mspmote.interfaces.MspButton + org.contikios.cooja.mspmote.interfaces.Msp802154Radio + org.contikios.cooja.mspmote.interfaces.MspDefaultSerial + org.contikios.cooja.mspmote.interfaces.MspLED + org.contikios.cooja.mspmote.interfaces.MspDebugOutput + + + org.contikios.cooja.mspmote.WismoteMoteType + wismote2 + Wismote IPSO Objects #ipso-example + [CONTIKI_DIR]/examples/ipso-objects/example-ipso-objects.c + make example-ipso-objects.wismote TARGET=wismote + [CONTIKI_DIR]/examples/ipso-objects/example-ipso-objects.wismote + org.contikios.cooja.interfaces.Position + org.contikios.cooja.interfaces.RimeAddress + org.contikios.cooja.interfaces.IPAddress + org.contikios.cooja.interfaces.Mote2MoteRelations + org.contikios.cooja.interfaces.MoteAttributes + org.contikios.cooja.mspmote.interfaces.MspClock + org.contikios.cooja.mspmote.interfaces.MspMoteID + org.contikios.cooja.mspmote.interfaces.MspButton + org.contikios.cooja.mspmote.interfaces.Msp802154Radio + org.contikios.cooja.mspmote.interfaces.MspDefaultSerial + org.contikios.cooja.mspmote.interfaces.MspLED + org.contikios.cooja.mspmote.interfaces.MspDebugOutput + + + + + org.contikios.cooja.interfaces.Position + 56.362361976162035 + 11.826023799100883 + 0.0 + + + org.contikios.cooja.mspmote.interfaces.MspClock + 1.0 + + + org.contikios.cooja.mspmote.interfaces.MspMoteID + 1 + + wismote1 + + + + + org.contikios.cooja.interfaces.Position + 60.1539674439426 + 11.827942168467365 + 0.0 + + + org.contikios.cooja.mspmote.interfaces.MspClock + 1.0 + + + org.contikios.cooja.mspmote.interfaces.MspMoteID + 2 + + wismote2 + + + + org.contikios.cooja.plugins.SimControl + 280 + 2 + 160 + 400 + 0 + + + org.contikios.cooja.plugins.Visualizer + + true + org.contikios.cooja.plugins.skins.IDVisualizerSkin + org.contikios.cooja.plugins.skins.TrafficVisualizerSkin + org.contikios.cooja.plugins.skins.UDGMVisualizerSkin + org.contikios.cooja.plugins.skins.LEDVisualizerSkin + org.contikios.cooja.plugins.skins.MoteTypeVisualizerSkin + 53.336918739504526 0.0 0.0 53.336918739504526 -2924.9161170527295 -473.3614543395965 + + 400 + 4 + 400 + 1 + 1 + + + org.contikios.cooja.plugins.LogListener + + ID:2 + + + + 1286 + 1 + 240 + 400 + 160 + + + org.contikios.cooja.plugins.Notes + + OMA LWM2M & IPSO Object example + +1. Start a LWM2M server, for example Leshan +2. Run the example and bridge Cooja using tunslip with the prefix aaaa::1/64: + (cd contiki/examples/ipso-objects && make connect-router-cooja) + +After a short time, the example node should register with the LWM2M server at [aaaa::1]:5683. + true + + 1006 + 0 + 160 + 680 + 0 + + + org.contikios.cooja.serialsocket.SerialSocketServer + 0 + + 60001 + true + + 362 + 3 + 116 + 1 + 399 + + + diff --git a/examples/ipso-objects/cooja-example-router-node.csc b/examples/ipso-objects/cooja-example-router-node.csc new file mode 100644 index 000000000..a261b1747 --- /dev/null +++ b/examples/ipso-objects/cooja-example-router-node.csc @@ -0,0 +1,187 @@ + + + [APPS_DIR]/mrm + [APPS_DIR]/mspsim + [APPS_DIR]/avrora + [APPS_DIR]/serial_socket + [APPS_DIR]/collect-view + [APPS_DIR]/powertracker + + OMA LWM2M and IPSO Object example + 2.0 + 123456 + 1000000 + + org.contikios.cooja.radiomediums.UDGM + 500.0 + 500.0 + 1.0 + 1.0 + + + 40000 + + + org.contikios.cooja.mspmote.WismoteMoteType + wismote1 + Wismote Router #wismote1 + [CONTIKI_DIR]/examples/ipso-objects/example-server.c + make example-server.wismote TARGET=wismote + [CONTIKI_DIR]/examples/ipso-objects/example-server.wismote + org.contikios.cooja.interfaces.Position + org.contikios.cooja.interfaces.RimeAddress + org.contikios.cooja.interfaces.IPAddress + org.contikios.cooja.interfaces.Mote2MoteRelations + org.contikios.cooja.interfaces.MoteAttributes + org.contikios.cooja.mspmote.interfaces.MspClock + org.contikios.cooja.mspmote.interfaces.MspMoteID + org.contikios.cooja.mspmote.interfaces.MspButton + org.contikios.cooja.mspmote.interfaces.Msp802154Radio + org.contikios.cooja.mspmote.interfaces.MspDefaultSerial + org.contikios.cooja.mspmote.interfaces.MspLED + org.contikios.cooja.mspmote.interfaces.MspDebugOutput + + + org.contikios.cooja.mspmote.WismoteMoteType + wismote2 + Wismote Mote Type #wismote2 + [CONTIKI_DIR]/examples/ipso-objects/example-ipso-objects.c + make example-ipso-objects.wismote TARGET=wismote + [CONTIKI_DIR]/examples/ipso-objects/example-ipso-objects.wismote + org.contikios.cooja.interfaces.Position + org.contikios.cooja.interfaces.RimeAddress + org.contikios.cooja.interfaces.IPAddress + org.contikios.cooja.interfaces.Mote2MoteRelations + org.contikios.cooja.interfaces.MoteAttributes + org.contikios.cooja.mspmote.interfaces.MspClock + org.contikios.cooja.mspmote.interfaces.MspMoteID + org.contikios.cooja.mspmote.interfaces.MspButton + org.contikios.cooja.mspmote.interfaces.Msp802154Radio + org.contikios.cooja.mspmote.interfaces.MspDefaultSerial + org.contikios.cooja.mspmote.interfaces.MspLED + org.contikios.cooja.mspmote.interfaces.MspDebugOutput + + + + + org.contikios.cooja.interfaces.Position + 30.243188653185154 + 29.963547412144486 + 0.0 + + + org.contikios.cooja.mspmote.interfaces.MspClock + 1.0 + + + org.contikios.cooja.mspmote.interfaces.MspMoteID + 1 + + + org.contikios.cooja.mspmote.interfaces.MspDefaultSerial + s aaaa::200:0:0:3 /3311/0/5850 0~;s aaaa::200:0:0:3 /3311/0/5850 1~;g aaaa::200:0:0:3 /3311/1/5850~;g aaaa::200:0:0:3 /3311/0/5850~;g aaaa::200:0:0:3 /3311/1/5850~;g aaaa::200:0:0:3 /3311/0/5850~;s aaaa::200:0:0:3 /3311/0/5850 1~;s aaaa::200:0:0:2 /3311/0/5850 1~;h~;s aaaa::200:0:0:2 /3311/0/5850 1~;s aaaa::200:0:0:2 /3311/0/5850 0~;g aaaa::200:0:0:2 /3311/0/5850~;g aaaa::200:0:0:2 /3311/1/5850~;g aaaa::200:0:0:2 /3311/2/5850~;l~; + + wismote1 + + + + + org.contikios.cooja.interfaces.Position + 59.75123136831088 + 29.84506209179908 + 0.0 + + + org.contikios.cooja.mspmote.interfaces.MspClock + 1.0 + + + org.contikios.cooja.mspmote.interfaces.MspMoteID + 2 + + wismote2 + + + + + org.contikios.cooja.interfaces.Position + 60.30742753391745 + 59.35092511889063 + 0.0 + + + org.contikios.cooja.mspmote.interfaces.MspClock + 1.0 + + + org.contikios.cooja.mspmote.interfaces.MspMoteID + 3 + + wismote2 + + + + org.contikios.cooja.plugins.SimControl + 280 + 0 + 160 + 400 + 0 + + + org.contikios.cooja.plugins.Visualizer + + true + org.contikios.cooja.plugins.skins.IDVisualizerSkin + org.contikios.cooja.plugins.skins.GridVisualizerSkin + org.contikios.cooja.plugins.skins.TrafficVisualizerSkin + org.contikios.cooja.plugins.skins.UDGMVisualizerSkin + org.contikios.cooja.plugins.skins.LEDVisualizerSkin + 4.593848158957425 0.0 0.0 4.593848158957425 13.734375417550426 -121.37641081710846 + + 400 + 3 + 400 + 1 + 1 + + + org.contikios.cooja.plugins.LogListener + + + + + + 959 + 2 + 447 + 400 + 160 + + + org.contikios.cooja.plugins.Notes + + Enter notes here + true + + 679 + 1 + 160 + 680 + 0 + + + org.contikios.cooja.plugins.MoteInterfaceViewer + 0 + + Serial port + 0,0 + + 579 + 4 + 300 + 49 + 414 + + + diff --git a/examples/ipso-objects/example-ipso-objects.c b/examples/ipso-objects/example-ipso-objects.c new file mode 100644 index 000000000..66a31a0bb --- /dev/null +++ b/examples/ipso-objects/example-ipso-objects.c @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2015, Yanzi Networks AB. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of the copyright holder 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 HOLDER 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 HOLDER 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. + * + */ + +/** + * \file + * OMA LWM2M and IPSO Objects example. + * \author + * Joakim Eriksson, joakime@sics.se + * Niclas Finne, nfi@sics.se + */ + +#include "contiki.h" +#include "lwm2m-engine.h" +#include "ipso-objects.h" + +#define DEBUG DEBUG_NONE +#include "net/ip/uip-debug.h" + +#ifndef REGISTER_WITH_LWM2M_BOOTSTRAP_SERVER +#define REGISTER_WITH_LWM2M_BOOTSTRAP_SERVER 0 +#endif + +#ifndef REGISTER_WITH_LWM2M_SERVER +#define REGISTER_WITH_LWM2M_SERVER 1 +#endif + +#ifndef LWM2M_SERVER_ADDRESS +#define LWM2M_SERVER_ADDRESS "aaaa::1" +#endif + +PROCESS(example_ipso_objects, "IPSO object example"); +AUTOSTART_PROCESSES(&example_ipso_objects); +/*---------------------------------------------------------------------------*/ +static void +setup_lwm2m_servers(void) +{ +#ifdef LWM2M_SERVER_ADDRESS + uip_ipaddr_t addr; + if(uiplib_ipaddrconv(LWM2M_SERVER_ADDRESS, &addr)) { + lwm2m_engine_register_with_bootstrap_server(&addr, 0); + lwm2m_engine_register_with_server(&addr, 0); + } +#endif /* LWM2M_SERVER_ADDRESS */ + + lwm2m_engine_use_bootstrap_server(REGISTER_WITH_LWM2M_BOOTSTRAP_SERVER); + lwm2m_engine_use_registration_server(REGISTER_WITH_LWM2M_SERVER); +} +/*---------------------------------------------------------------------------*/ +PROCESS_THREAD(example_ipso_objects, ev, data) +{ + PROCESS_BEGIN(); + + PROCESS_PAUSE(); + + PRINTF("Starting IPSO objects example\n"); + + /* Initialize the OMA LWM2M engine */ + lwm2m_engine_init(); + + /* Register default LWM2M objects */ + lwm2m_engine_register_default_objects(); + + /* Register default IPSO objects */ + ipso_objects_init(); + + setup_lwm2m_servers(); + + while(1) { + PROCESS_WAIT_EVENT(); + } + + PROCESS_END(); +} diff --git a/examples/ipso-objects/example-ipso-temperature.c b/examples/ipso-objects/example-ipso-temperature.c new file mode 100644 index 000000000..30f320640 --- /dev/null +++ b/examples/ipso-objects/example-ipso-temperature.c @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2015, Yanzi Networks AB. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of the copyright holder 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 HOLDER 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 HOLDER 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. + */ + +/** + * \file + * An dummy temperature driver as example + * \author + * Joakim Eriksson + * Niclas Finne + */ + +#include "ipso-objects.h" +#include "lib/random.h" + +static int32_t last_value = 27000; +/*---------------------------------------------------------------------------*/ +static int +read_value(int32_t *value) +{ + last_value = last_value + (random_rand() % 1000) - 500; + if(last_value < 18000) { + last_value = 18000; + } else if(last_value > 35000) { + last_value = 35000; + } + *value = last_value; + return 0; +} +/*---------------------------------------------------------------------------*/ +const struct ipso_objects_sensor example_ipso_temperature = { + .read_value = read_value +}; +/*---------------------------------------------------------------------------*/ diff --git a/examples/ipso-objects/example-server.c b/examples/ipso-objects/example-server.c new file mode 100644 index 000000000..2d48c738b --- /dev/null +++ b/examples/ipso-objects/example-server.c @@ -0,0 +1,392 @@ +/* + * Copyright (c) 2015, Yanzi Networks AB. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of the copyright holder 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 HOLDER 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 HOLDER 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. + * + */ + +/** + * \file + * IPSO Objects and OMA LWM2M example. + * \author + * Joakim Eriksson, joakime@sics.se + * Niclas Finne, nfi@sics.se + */ + +#include "contiki.h" +#include "net/ip/uip.h" +#include "net/rpl/rpl.h" +#include "net/netstack.h" +#include "er-coap-constants.h" +#include "er-coap-engine.h" +#include "lwm2m-engine.h" +#include "oma-tlv.h" +#include "dev/serial-line.h" +#include "serial-protocol.h" + +#if CONTIKI_TARGET_WISMOTE +#include "dev/uart1.h" +#endif + +#define DEBUG DEBUG_PRINT +#include "net/ip/uip-debug.h" + +#define REMOTE_PORT UIP_HTONS(COAP_DEFAULT_PORT) + +#define URL_WELL_KNOWN ".well-known/core" +#define URL_DEVICE_MODEL "/3/0/1" +#define URL_DEVICE_FIRMWARE_VERSION "/3/0/3" +#define URL_LIGHT_CONTROL "/3311/0/5850" +#define URL_POWER_CONTROL "/3312/0/5850" + +#define MAX_NODES 10 + +#define NODE_HAS_TYPE (1 << 0) + +struct node { + uip_ipaddr_t ipaddr; + char type[32]; + uint8_t flags; + uint8_t retries; +}; + +static struct node nodes[MAX_NODES]; +static uint8_t node_count; + +static struct node *current_target; +static char current_uri[32] = URL_LIGHT_CONTROL; +static char current_value[32] = "1"; +static int current_request = COAP_PUT; +static uint8_t fetching_type = 0; + +PROCESS(router_process, "router process"); +AUTOSTART_PROCESSES(&router_process); +/*---------------------------------------------------------------------------*/ +static struct node * +add_node(const uip_ipaddr_t *addr) +{ + int i; + for(i = 0; i < node_count; i++) { + if(uip_ipaddr_cmp(&nodes[i].ipaddr, addr)) { + /* Node already added */ + return &nodes[i]; + } + } + if(node_count < MAX_NODES) { + uip_ipaddr_copy(&nodes[node_count].ipaddr, addr); + return &nodes[node_count++]; + } + return NULL; +} +/*---------------------------------------------------------------------------*/ +void +set_value(const uip_ipaddr_t *addr, char *uri, char *value) +{ + int i; + printf("#set value "); + uip_debug_ipaddr_print(addr); + printf(" URI: %s Value: %s\n", uri, value); + + for(i = 0; i < node_count; i++) { + if(uip_ipaddr_cmp(&nodes[i].ipaddr, addr)) { + /* setup command */ + current_target = &nodes[i]; + current_request = COAP_PUT; + strncpy(current_uri, uri, sizeof(current_uri) - 1); + strncpy(current_value, value, sizeof(current_value) - 1); + process_poll(&router_process); + break; + } + } +} +/*---------------------------------------------------------------------------*/ +void +get_value(const uip_ipaddr_t *addr, char *uri) +{ + int i; + printf("#get value "); + uip_debug_ipaddr_print(addr); + printf(" URI: %s\n", uri); + + for(i = 0; i < node_count; i++) { + if(uip_ipaddr_cmp(&nodes[i].ipaddr, addr)) { + /* setup command */ + current_target = &nodes[i]; + current_request = COAP_GET; + strncpy(current_uri, uri, sizeof(current_uri) - 1); + current_value[0] = 0; + process_poll(&router_process); + break; + } + } +} +/*---------------------------------------------------------------------------*/ +void +print_node_list(void) +{ + int i; + int out = 0; + for(i = 0; i < node_count; i++) { + if(nodes[i].flags & NODE_HAS_TYPE) { + if(out++) { + printf(";"); + } + printf("%s,", nodes[i].type); + uip_debug_ipaddr_print(&nodes[i].ipaddr); + } + } + printf("\n"); +} +/*---------------------------------------------------------------------------*/ +/** + * This function is will be passed to COAP_BLOCKING_REQUEST() to + * handle responses. + */ +static void +client_chunk_handler(void *response) +{ + const uint8_t *chunk; + unsigned int format; + int len = coap_get_payload(response, &chunk); + coap_get_header_content_format(response, &format); + + /* if(len > 0) { */ + /* printf("|%.*s (%d,%d)", len, (char *)chunk, len, format); */ + /* } */ + if(current_target != NULL && fetching_type) { + if(len > sizeof(current_target->type) - 1) { + len = sizeof(current_target->type) - 1; + } + memcpy(current_target->type, chunk, len); + current_target->type[len] = 0; + current_target->flags |= NODE_HAS_TYPE; + + PRINTF("\nNODE "); + PRINT6ADDR(¤t_target->ipaddr); + PRINTF(" HAS TYPE %s\n", current_target->type); + } else { + /* otherwise update the current value */ + if(format == LWM2M_TLV) { + oma_tlv_t tlv; + /* we can only read int32 for now ? */ + if(oma_tlv_read(&tlv, chunk, len) > 0) { + /* printf("TLV.type=%d len=%d id=%d value[0]=%d\n", */ + /* tlv.type, tlv.length, tlv.id, tlv.value[0]); */ + + int value = oma_tlv_get_int32(&tlv); + snprintf(current_value, sizeof(current_value), "%d", value); + } + } else { + if(len > sizeof(current_value) - 1) { + len = sizeof(current_value) - 1; + } + memcpy(current_value, chunk, len); + current_value[len] = 0; + } + } +} +/*---------------------------------------------------------------------------*/ +static void +setup_network(void) +{ + uip_ipaddr_t ipaddr; + struct uip_ds6_addr *root_if; + rpl_dag_t *dag; + int i; + uint8_t state; + +#if CONTIKI_TARGET_WISMOTE + uart1_set_input(serial_line_input_byte); + serial_line_init(); +#endif + +#if UIP_CONF_ROUTER +/** + * The choice of server address determines its 6LoWPAN header compression. + * Obviously the choice made here must also be selected in udp-client.c. + * + * For correct Wireshark decoding using a sniffer, add the /64 prefix to the 6LowPAN protocol preferences, + * e.g. set Context 0 to aaaa::. At present Wireshark copies Context/128 and then overwrites it. + * (Setting Context 0 to aaaa::1111:2222:3333:4444 will report a 16 bit compressed address of aaaa::1111:22ff:fe33:xxxx) + * Note Wireshark's IPCMV6 checksum verification depends on the correct uncompressed addresses. + */ +#if 0 +/* Mode 1 - 64 bits inline */ + uip_ip6addr(&ipaddr, 0xaaaa, 0, 0, 0, 0, 0, 0, 1); +#elif 1 +/* Mode 2 - 16 bits inline */ + uip_ip6addr(&ipaddr, 0xaaaa, 0, 0, 0, 0, 0x00ff, 0xfe00, 1); +#else +/* Mode 3 - derived from link local (MAC) address */ + uip_ip6addr(&ipaddr, 0xaaaa, 0, 0, 0, 0, 0, 0, 0); + uip_ds6_set_addr_iid(&ipaddr, &uip_lladdr); +#endif + + uip_ds6_addr_add(&ipaddr, 0, ADDR_MANUAL); + root_if = uip_ds6_addr_lookup(&ipaddr); + if(root_if != NULL) { + dag = rpl_set_root(RPL_DEFAULT_INSTANCE, &ipaddr); + uip_ip6addr(&ipaddr, 0xaaaa, 0, 0, 0, 0, 0, 0, 0); + rpl_set_prefix(dag, &ipaddr, 64); + PRINTF("created a new RPL dag\n"); + } else { + PRINTF("failed to create a new RPL DAG\n"); + } +#endif /* UIP_CONF_ROUTER */ + + PRINTF("IPv6 addresses: "); + for(i = 0; i < UIP_DS6_ADDR_NB; i++) { + state = uip_ds6_if.addr_list[i].state; + if(state == ADDR_TENTATIVE || state == ADDR_PREFERRED) { + PRINT6ADDR(&uip_ds6_if.addr_list[i].ipaddr); + PRINTF("\n"); + /* hack to make address "final" */ + if (state == ADDR_TENTATIVE) { + uip_ds6_if.addr_list[i].state = ADDR_PREFERRED; + } + } + } +} +/*---------------------------------------------------------------------------*/ +PROCESS_THREAD(router_process, ev, data) +{ + /* This way the packet can be treated as pointer as usual. */ + static coap_packet_t request[1]; + static struct etimer timer; + uip_ds6_route_t *r; + uip_ipaddr_t *nexthop; + int n; + + PROCESS_BEGIN(); + + PROCESS_PAUSE(); + + /* receives all CoAP messages */ + coap_init_engine(); + + setup_network(); + + /* The data sink runs with a 100% duty cycle in order to ensure high + packet reception rates. */ + NETSTACK_MAC.off(1); + + while(1) { + etimer_set(&timer, CLOCK_SECOND * 5); + PROCESS_YIELD(); + + /* Handle serial line input */ + if(ev == serial_line_event_message) { + serial_protocol_input((char *) data); + } + + if(etimer_expired(&timer)) { + current_target = NULL; + n = 0; + for(r = uip_ds6_route_head(); r != NULL; r = uip_ds6_route_next(r)) { + current_target = add_node(&r->ipaddr); + if(current_target == NULL || + (current_target->flags & NODE_HAS_TYPE) != 0 || + current_target->retries > 5) { + continue; + } + PRINTF(" "); + PRINT6ADDR(&r->ipaddr); + PRINTF(" -> "); + nexthop = uip_ds6_route_nexthop(r); + if(nexthop != NULL) { + PRINT6ADDR(nexthop); + PRINTF("\n"); + } else { + PRINTF("-"); + } + PRINTF("\n"); + n++; + break; + } + } + + /* This is a node type discovery */ + if(current_target != NULL && + (current_target->flags & NODE_HAS_TYPE) == 0 && + current_target->retries < 6) { + + /* prepare request, TID is set by COAP_BLOCKING_REQUEST() */ + coap_init_message(request, COAP_TYPE_CON, COAP_GET, 0); + coap_set_header_uri_path(request, URL_DEVICE_MODEL); + + current_target->retries++; + + PRINTF("CoAP request to ["); + PRINT6ADDR(¤t_target->ipaddr); + PRINTF("]:%u (%u tx)\n", UIP_HTONS(REMOTE_PORT), + current_target->retries); + + fetching_type = 1; + COAP_BLOCKING_REQUEST(¤t_target->ipaddr, REMOTE_PORT, request, + client_chunk_handler); + fetching_type = 0; + strncpy(current_uri, URL_LIGHT_CONTROL, sizeof(current_uri)); + printf("\n--Done--\n"); + } + + /* If having a type this is another type of request */ + if(current_target != NULL && + (current_target->flags & NODE_HAS_TYPE) && strlen(current_uri) > 0) { + /* prepare request, TID is set by COAP_BLOCKING_REQUEST() */ + coap_init_message(request, COAP_TYPE_CON, current_request, 0); + coap_set_header_uri_path(request, current_uri); + + if(strlen(current_value) > 0) { + coap_set_payload(request, (uint8_t *)current_value, + strlen(current_value)); + } + + PRINTF("CoAP request to ["); + PRINT6ADDR(¤t_target->ipaddr); + PRINTF("]:%u %s\n", UIP_HTONS(REMOTE_PORT), current_uri); + + COAP_BLOCKING_REQUEST(¤t_target->ipaddr, REMOTE_PORT, request, + client_chunk_handler); + + /* print out result of command */ + if(current_request == COAP_PUT) { + printf("s "); + } else { + printf("g "); + } + uip_debug_ipaddr_print(¤t_target->ipaddr); + printf(" %s %s\n", current_uri, current_value); + + current_target = NULL; + current_uri[0] = 0; + current_value[0] = 0; + + } + } + + PROCESS_END(); +} +/*---------------------------------------------------------------------------*/ diff --git a/examples/ipso-objects/project-conf.h b/examples/ipso-objects/project-conf.h new file mode 100644 index 000000000..49e049bbd --- /dev/null +++ b/examples/ipso-objects/project-conf.h @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2015, Yanzi Networks AB. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of the copyright holder 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 HOLDER 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 HOLDER 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. + */ + +#ifndef PROJECT_CONF_H_ +#define PROJECT_CONF_H_ + +#ifdef BOARD_STRING +#define LWM2M_DEVICE_MODEL_NUMBER BOARD_STRING +#elif defined(CONTIKI_TARGET_WISMOTE) +#include "dev/watchdog.h" +#define LWM2M_DEVICE_MODEL_NUMBER "wismote" +#define LWM2M_DEVICE_MANUFACTURER "Arago Systems" +#define LWM2M_DEVICE_SERIAL_NO "001" +#define PLATFORM_REBOOT watchdog_reboot +#endif + +#define IPSO_TEMPERATURE example_ipso_temperature + +/** + * Disabling RDC and CSMA to save memory on constrained devices. + */ +#undef NETSTACK_CONF_RDC +#define NETSTACK_CONF_RDC nullrdc_driver + +#undef NETSTACK_CONF_MAC +#define NETSTACK_CONF_MAC nullmac_driver + +/* Disabling TCP on CoAP nodes. */ +#undef UIP_CONF_TCP +#define UIP_CONF_TCP 0 + +/* Increase rpl-border-router IP-buffer when using more than 64. */ +#undef REST_MAX_CHUNK_SIZE +#define REST_MAX_CHUNK_SIZE 64 + +/* Multiplies with chunk size, be aware of memory constraints. */ +#undef COAP_MAX_OPEN_TRANSACTIONS +#define COAP_MAX_OPEN_TRANSACTIONS 4 + +/* Filtering .well-known/core per query can be disabled to save space. */ +#undef COAP_LINK_FORMAT_FILTERING +#define COAP_LINK_FORMAT_FILTERING 0 +#undef COAP_PROXY_OPTION_PROCESSING +#define COAP_PROXY_OPTION_PROCESSING 0 + +/* Enable client-side support for COAP observe */ +#define COAP_OBSERVE_CLIENT 1 + +#endif /* PROJECT_CONF_H_ */ diff --git a/examples/ipso-objects/serial-protocol.c b/examples/ipso-objects/serial-protocol.c new file mode 100644 index 000000000..f0c2cb1a0 --- /dev/null +++ b/examples/ipso-objects/serial-protocol.c @@ -0,0 +1,126 @@ +/* + * Copyright (c) 2015, Yanzi Networks AB. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of the copyright holder 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 HOLDER 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 HOLDER 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. + */ + +/** + * \file + * Simple serial protocol to list and interact with devices + * \author + * Joakim Eriksson + * Niclas Finne + */ + +#include "contiki.h" +#include "net/ip/uip.h" +#include "net/ip/uiplib.h" +#include + +void print_node_list(void); +void set_value(const uip_ipaddr_t *addr, char *uri, char *value); +void get_value(const uip_ipaddr_t *addr, char *uri); +/*---------------------------------------------------------------------------*/ +int +find_next_sep(const char *str, char sep, int pos) +{ + char c; + while((c = str[pos]) != 0) { + if(c == sep) { + return pos + 1; + } + pos++; + } + return -1; +} +/*---------------------------------------------------------------------------*/ +/* + * l - list all discovered devices + * s - set + * d - get + */ +void +serial_protocol_input(char *data) +{ + /* We assume that we have a string here */ + char cmd = data[0]; + int pos = 0; + + switch(cmd) { + case 'l': + /* list devices */ + print_node_list(); + break; + case 's': { + uip_ip6addr_t ipaddr; + char *uri; + char *value; + pos = find_next_sep(data, ' ', pos); + if(pos > 0) { + /* start of IP */ + int start = pos; + pos = find_next_sep(data, ' ', pos); + if(pos == -1) { + return; + } + data[pos - 1] = 0; + if(uiplib_ip6addrconv(&data[start], &ipaddr) == 0) { + printf("* Error not valid IP\n"); + } + uri = &data[pos]; + pos = find_next_sep(data, ' ', pos); + if(pos == -1) return; + data[pos - 1] = 0; + value = &data[pos]; + /* set the value at the specified node */ + set_value(&ipaddr, uri, value); + } + break; + } + case 'g': { + uip_ip6addr_t ipaddr; + char *uri; + pos = find_next_sep(data, ' ', pos); + if(pos > 0) { + /* start of IP */ + int start = pos; + pos = find_next_sep(data, ' ', pos); + if(pos == -1) return; + data[pos - 1] = 0; + if(uiplib_ip6addrconv((const char *) &data[start], &ipaddr) == 0) { + printf("* Error not valid IP\n"); + } + uri = &data[pos]; + /* get the value at the specified node */ + get_value(&ipaddr, uri); + } + break; + } + default: + printf("Unknown command\n"); + } +} +/*---------------------------------------------------------------------------*/ diff --git a/examples/ipso-objects/serial-protocol.h b/examples/ipso-objects/serial-protocol.h new file mode 100644 index 000000000..a6dfcc129 --- /dev/null +++ b/examples/ipso-objects/serial-protocol.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2015, Yanzi Networks AB. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of the copyright holder 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 HOLDER 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 HOLDER 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. + */ + +/** + * \file + * Simple serial protocol to list and interact with devices + * \author + * Joakim Eriksson + * Niclas Finne + */ + +#ifndef SERIAL_PROTOCOL_H_ +#define SERIAL_PROTOCOL_H_ + +void serial_protocol_input(char *data); + +#endif /* SERIAL_PROTOCOL_H_ */ diff --git a/regression-tests/01-compile-base/Makefile b/regression-tests/01-compile-base/Makefile index 484880773..bbfbd0c6b 100644 --- a/regression-tests/01-compile-base/Makefile +++ b/regression-tests/01-compile-base/Makefile @@ -14,6 +14,7 @@ hello-world/z1 \ eeprom-test/native \ collect/sky \ er-rest-example/wismote \ +ipso-objects/wismote \ example-shell/native \ netperf/sky \ powertrace/sky \ diff --git a/regression-tests/18-compile-arm-ports/Makefile b/regression-tests/18-compile-arm-ports/Makefile index c314e8360..a292bb3e3 100644 --- a/regression-tests/18-compile-arm-ports/Makefile +++ b/regression-tests/18-compile-arm-ports/Makefile @@ -15,6 +15,7 @@ cc26xx/very-sleepy-demo/srf06-cc26xx \ hello-world/cc2538dk \ ipv6/rpl-border-router/cc2538dk \ er-rest-example/cc2538dk \ +ipso-objects/cc2538dk \ webserver-ipv6/cc2538dk \ cc2538dk/cc2538dk \ cc2538dk/udp-ipv6-echo-server/cc2538dk \ @@ -26,6 +27,7 @@ ipv6/multicast/cc2538dk \ zolertia/zoul/zoul \ zolertia/zoul/cc1200-demo/zoul \ er-rest-example/zoul \ +ipso-objects/zoul \ hello-world/zoul \ cc2538dk/mqtt-demo/zoul \ er-rest-example/stm32nucleo-spirit1 \