commit b67069fc45aabc7e6d2d2a781bd2361f38266cb2 Author: Danyi Dávid Date: Tue Oct 17 21:44:35 2017 +0200 * initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..8e24b65 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.idea +cmake-build-debug diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..9c7d59b --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,33 @@ +cmake_minimum_required (VERSION 3.0) + +project (lightclient) + +add_definitions(-DSerialOut=SerialUSB) + +if(DTLS) + message(FATAL_ERROR "DTLS option is not supported." ) +endif() + +include(${CMAKE_CURRENT_LIST_DIR}/../wakaama-core/src/wakaama.cmake) +include(${CMAKE_CURRENT_LIST_DIR}/../wakaama-shared/src/arduino-base.cmake) +include(${CMAKE_CURRENT_LIST_DIR}/../wakaama-shared/src/shared.cmake) + +add_definitions(-DLWM2M_CLIENT_MODE) +add_definitions(${SHARED_DEFINITIONS} ${WAKAAMA_DEFINITIONS}) + +include_directories (${WAKAAMA_SOURCES_DIR} ${SHARED_INCLUDE_DIRS} ${ARDUINO_INCLUDE_DIRS}) + +SET(SOURCES + ${CMAKE_CURRENT_LIST_DIR}/wakaama-client.cpp + ${CMAKE_CURRENT_LIST_DIR}/object_security.c + ${CMAKE_CURRENT_LIST_DIR}/object_server.c + ${CMAKE_CURRENT_LIST_DIR}/object_device.c + ${CMAKE_CURRENT_LIST_DIR}/test_object.c +) + +add_executable(${PROJECT_NAME} ${SOURCES} ${WAKAAMA_SOURCES} ${SHARED_SOURCES}) + +# Add WITH_LOGS to debug variant +set_property(TARGET ${PROJECT_NAME} APPEND PROPERTY COMPILE_DEFINITIONS $<$:WITH_LOGS>) + +SOURCE_GROUP(wakaama FILES ${WAKAAMA_SOURCES}) diff --git a/library.properties b/library.properties new file mode 100644 index 0000000..76644a3 --- /dev/null +++ b/library.properties @@ -0,0 +1,10 @@ +name=Wakaama Client +version=0.1.1 +author=DavidDanyi,Ericsson +maintainer=David Danyi +sentence=Eclipse Wakaama client +paragraph= +category=Communication +url= +architectures=samd +includes=wakaama-client.h diff --git a/src/object_device.c b/src/object_device.c new file mode 100644 index 0000000..5337b94 --- /dev/null +++ b/src/object_device.c @@ -0,0 +1,307 @@ +/******************************************************************************* + * + * Copyright (c) 2013, 2014, 2015 Intel Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Eclipse Distribution License v1.0 which accompany this distribution. + * + * The Eclipse Public License is available at + * http://www.eclipse.org/legal/epl-v10.html + * The Eclipse Distribution License is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * Contributors: + * David Navarro, Intel Corporation - initial API and implementation + * domedambrosio - Please refer to git log + * Fabien Fleutot - Please refer to git log + * Axel Lorente - Please refer to git log + * Bosch Software Innovations GmbH - Please refer to git log + * Pascal Rieux - Please refer to git log + * + *******************************************************************************/ + +/* + Copyright (c) 2013, 2014 Intel Corporation + + Redistribution and use in source and binary forms, with or without modification, + are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + * Neither the name of Intel Corporation nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + THE POSSIBILITY OF SUCH DAMAGE. + + David Navarro + +*/ + +/* + * This object is single instance only, and is mandatory to all LWM2M device as it describe the object such as its + * manufacturer, model, etc... + * + * Here we implement only some of the optional resources. + * + */ + +#include "liblwm2m.h" + +#include +#include +#include +#include + + +#define PRV_MANUFACTURER "Open Mobile Alliance" +#define PRV_MODEL_NUMBER "Lightweight M2M Client" +#define PRV_BINDING_MODE "U" + +// Resource Id's: +#define RES_O_MANUFACTURER 0 +#define RES_O_MODEL_NUMBER 1 +#define RES_O_SERIAL_NUMBER 2 +#define RES_O_FIRMWARE_VERSION 3 +#define RES_M_REBOOT 4 +#define RES_O_FACTORY_RESET 5 +#define RES_O_AVL_POWER_SOURCES 6 +#define RES_O_POWER_SOURCE_VOLTAGE 7 +#define RES_O_POWER_SOURCE_CURRENT 8 +#define RES_O_BATTERY_LEVEL 9 +#define RES_O_MEMORY_FREE 10 +#define RES_M_ERROR_CODE 11 +#define RES_O_RESET_ERROR_CODE 12 +#define RES_O_CURRENT_TIME 13 +#define RES_O_UTC_OFFSET 14 +#define RES_O_TIMEZONE 15 +#define RES_M_BINDING_MODES 16 +#define RES_O_DEVICE_TYPE 17 +#define RES_O_HARDWARE_VERSION 18 +#define RES_O_SOFTWARE_VERSION 19 +#define RES_O_BATTERY_STATUS 20 +#define RES_O_MEMORY_TOTAL 21 + + +static uint8_t prv_set_value(lwm2m_data_t * dataP) +{ + // a simple switch structure is used to respond at the specified resource asked + switch (dataP->id) + { + case RES_O_MANUFACTURER: + lwm2m_data_encode_string(PRV_MANUFACTURER, dataP); + return COAP_205_CONTENT; + + case RES_O_MODEL_NUMBER: + lwm2m_data_encode_string(PRV_MODEL_NUMBER, dataP); + return COAP_205_CONTENT; + + case RES_M_REBOOT: + return COAP_405_METHOD_NOT_ALLOWED; + + case RES_M_BINDING_MODES: + lwm2m_data_encode_string(PRV_BINDING_MODE, dataP); + return COAP_205_CONTENT; + + + default: + return COAP_404_NOT_FOUND; + } +} + +static uint8_t prv_device_read(uint16_t instanceId, + int * numDataP, + lwm2m_data_t ** dataArrayP, + lwm2m_object_t * objectP) +{ + uint8_t result; + int i; + + // this is a single instance object + if (instanceId != 0) + { + return COAP_404_NOT_FOUND; + } + + // is the server asking for the full object ? + if (*numDataP == 0) + { + uint16_t resList[] = { + RES_O_MANUFACTURER, + RES_O_MODEL_NUMBER, + RES_M_BINDING_MODES + }; + int nbRes = sizeof(resList)/sizeof(uint16_t); + + *dataArrayP = lwm2m_data_new(nbRes); + if (*dataArrayP == NULL) return COAP_500_INTERNAL_SERVER_ERROR; + *numDataP = nbRes; + for (i = 0 ; i < nbRes ; i++) + { + (*dataArrayP)[i].id = resList[i]; + } + } + + i = 0; + do + { + result = prv_set_value((*dataArrayP) + i); + i++; + } while (i < *numDataP && result == COAP_205_CONTENT); + + return result; +} + +static uint8_t prv_device_discover(uint16_t instanceId, + int * numDataP, + lwm2m_data_t ** dataArrayP, + lwm2m_object_t * objectP) +{ + uint8_t result; + int i; + + // this is a single instance object + if (instanceId != 0) + { + return COAP_404_NOT_FOUND; + } + + result = COAP_205_CONTENT; + + // is the server asking for the full object ? + if (*numDataP == 0) + { + uint16_t resList[] = { + RES_O_MANUFACTURER, + RES_O_MODEL_NUMBER, + RES_M_BINDING_MODES, + RES_M_REBOOT + }; + int nbRes = sizeof(resList)/sizeof(uint16_t); + + *dataArrayP = lwm2m_data_new(nbRes); + if (*dataArrayP == NULL) return COAP_500_INTERNAL_SERVER_ERROR; + *numDataP = nbRes; + for (i = 0 ; i < nbRes ; i++) + { + (*dataArrayP)[i].id = resList[i]; + } + } + else + { + for (i = 0; i < *numDataP && result == COAP_205_CONTENT; i++) + { + switch ((*dataArrayP)[i].id) + { + case RES_O_MANUFACTURER: + case RES_O_MODEL_NUMBER: + case RES_M_BINDING_MODES: + case RES_M_REBOOT: + break; + default: + result = COAP_404_NOT_FOUND; + } + } + } + + return result; +} + +static uint8_t prv_device_execute(uint16_t instanceId, + uint16_t resourceId, + uint8_t * buffer, + int length, + lwm2m_object_t * objectP) +{ + // this is a single instance object + if (instanceId != 0) + { + return COAP_404_NOT_FOUND; + } + + if (length != 0) return COAP_400_BAD_REQUEST; + + if (resourceId == RES_M_REBOOT) + { +// fprintf(stdout, "\n\t REBOOT\r\n\n"); + return COAP_204_CHANGED; + } + + return COAP_405_METHOD_NOT_ALLOWED; +} + +lwm2m_object_t * get_object_device() +{ + /* + * The get_object_device function create the object itself and return a pointer to the structure that represent it. + */ + lwm2m_object_t * deviceObj; + + deviceObj = (lwm2m_object_t *)lwm2m_malloc(sizeof(lwm2m_object_t)); + + if (NULL != deviceObj) + { + memset(deviceObj, 0, sizeof(lwm2m_object_t)); + + /* + * It assigns his unique ID + * The 3 is the standard ID for the mandatory object "Object device". + */ + deviceObj->objID = LWM2M_DEVICE_OBJECT_ID; + + /* + * and its unique instance + * + */ + deviceObj->instanceList = (lwm2m_list_t *)lwm2m_malloc(sizeof(lwm2m_list_t)); + if (NULL != deviceObj->instanceList) + { + memset(deviceObj->instanceList, 0, sizeof(lwm2m_list_t)); + } + else + { + lwm2m_free(deviceObj); + return NULL; + } + + /* + * And the private function that will access the object. + * Those function will be called when a read/write/execute query is made by the server. In fact the library don't need to + * know the resources of the object, only the server does. + */ + deviceObj->readFunc = prv_device_read; + deviceObj->executeFunc = prv_device_execute; + deviceObj->discoverFunc = prv_device_discover; + + } + + return deviceObj; +} + +void free_object_device(lwm2m_object_t * objectP) +{ + if (NULL != objectP->userData) + { + lwm2m_free(objectP->userData); + objectP->userData = NULL; + } + if (NULL != objectP->instanceList) + { + lwm2m_free(objectP->instanceList); + objectP->instanceList = NULL; + } + + lwm2m_free(objectP); +} diff --git a/src/object_security.c b/src/object_security.c new file mode 100644 index 0000000..fdd150a --- /dev/null +++ b/src/object_security.c @@ -0,0 +1,253 @@ +/******************************************************************************* + * + * Copyright (c) 2013, 2014, 2015 Intel Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Eclipse Distribution License v1.0 which accompany this distribution. + * + * The Eclipse Public License is available at + * http://www.eclipse.org/legal/epl-v10.html + * The Eclipse Distribution License is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * Contributors: + * David Navarro, Intel Corporation - initial API and implementation + * Bosch Software Innovations GmbH - Please refer to git log + * Pascal Rieux - Please refer to git log + * + *******************************************************************************/ + +/* + * Resources: + * + * Name | ID | Operations | Instances | Mandatory | Type | Range | Units | + * Server URI | 0 | | Single | Yes | String | | | + * Bootstrap Server | 1 | | Single | Yes | Boolean | | | + * Security Mode | 2 | | Single | Yes | Integer | 0-3 | | + * Public Key or ID | 3 | | Single | Yes | Opaque | | | + * Server Public Key or ID | 4 | | Single | Yes | Opaque | | | + * Secret Key | 5 | | Single | Yes | Opaque | | | + * SMS Security Mode | 6 | | Single | Yes | Integer | 0-255 | | + * SMS Binding Key Param. | 7 | | Single | Yes | Opaque | 6 B | | + * SMS Binding Secret Keys | 8 | | Single | Yes | Opaque | 32-48 B | | + * Server SMS Number | 9 | | Single | Yes | Integer | | | + * Short Server ID | 10 | | Single | No | Integer | 1-65535 | | + * Client Hold Off Time | 11 | | Single | Yes | Integer | | s | + * + */ + +/* + * Here we implement a very basic LWM2M Security Object which only knows NoSec security mode. + */ + +#include "liblwm2m.h" + +#include +#include +#include + + +typedef struct _security_instance_ +{ + struct _security_instance_ * next; // matches lwm2m_list_t::next + uint16_t instanceId; // matches lwm2m_list_t::id + char * uri; + bool isBootstrap; + uint16_t shortID; + uint32_t clientHoldOffTime; +} security_instance_t; + +static uint8_t prv_get_value(lwm2m_data_t * dataP, + security_instance_t * targetP) +{ + + switch (dataP->id) + { + case LWM2M_SECURITY_URI_ID: + lwm2m_data_encode_string(targetP->uri, dataP); + return COAP_205_CONTENT; + + case LWM2M_SECURITY_BOOTSTRAP_ID: + lwm2m_data_encode_bool(targetP->isBootstrap, dataP); + return COAP_205_CONTENT; + + case LWM2M_SECURITY_SECURITY_ID: + lwm2m_data_encode_int(LWM2M_SECURITY_MODE_NONE, dataP); + return COAP_205_CONTENT; + + case LWM2M_SECURITY_PUBLIC_KEY_ID: + // Here we return an opaque of 1 byte containing 0 + { + uint8_t value = 0; + + lwm2m_data_encode_opaque(&value, 1, dataP); + } + return COAP_205_CONTENT; + + case LWM2M_SECURITY_SERVER_PUBLIC_KEY_ID: + // Here we return an opaque of 1 byte containing 0 + { + uint8_t value = 0; + + lwm2m_data_encode_opaque(&value, 1, dataP); + } + return COAP_205_CONTENT; + + case LWM2M_SECURITY_SECRET_KEY_ID: + // Here we return an opaque of 1 byte containing 0 + { + uint8_t value = 0; + + lwm2m_data_encode_opaque(&value, 1, dataP); + } + return COAP_205_CONTENT; + + case LWM2M_SECURITY_SMS_SECURITY_ID: + lwm2m_data_encode_int(LWM2M_SECURITY_MODE_NONE, dataP); + return COAP_205_CONTENT; + + case LWM2M_SECURITY_SMS_KEY_PARAM_ID: + // Here we return an opaque of 6 bytes containing a buggy value + { + char * value = "12345"; + lwm2m_data_encode_opaque((uint8_t *)value, 6, dataP); + } + return COAP_205_CONTENT; + + case LWM2M_SECURITY_SMS_SECRET_KEY_ID: + // Here we return an opaque of 32 bytes containing a buggy value + { + char * value = "1234567890abcdefghijklmnopqrstu"; + lwm2m_data_encode_opaque((uint8_t *)value, 32, dataP); + } + return COAP_205_CONTENT; + + case LWM2M_SECURITY_SMS_SERVER_NUMBER_ID: + lwm2m_data_encode_int(0, dataP); + return COAP_205_CONTENT; + + case LWM2M_SECURITY_SHORT_SERVER_ID: + lwm2m_data_encode_int(targetP->shortID, dataP); + return COAP_205_CONTENT; + + case LWM2M_SECURITY_HOLD_OFF_ID: + lwm2m_data_encode_int(targetP->clientHoldOffTime, dataP); + return COAP_205_CONTENT; + + default: + return COAP_404_NOT_FOUND; + } +} + +static uint8_t prv_security_read(uint16_t instanceId, + int * numDataP, + lwm2m_data_t ** dataArrayP, + lwm2m_object_t * objectP) +{ + security_instance_t * targetP; + uint8_t result; + int i; + + targetP = (security_instance_t *)lwm2m_list_find(objectP->instanceList, instanceId); + if (NULL == targetP) return COAP_404_NOT_FOUND; + + // is the server asking for the full instance ? + if (*numDataP == 0) + { + uint16_t resList[] = {LWM2M_SECURITY_URI_ID, + LWM2M_SECURITY_BOOTSTRAP_ID, + LWM2M_SECURITY_SECURITY_ID, + LWM2M_SECURITY_PUBLIC_KEY_ID, + LWM2M_SECURITY_SERVER_PUBLIC_KEY_ID, + LWM2M_SECURITY_SECRET_KEY_ID, + LWM2M_SECURITY_SMS_SECURITY_ID, + LWM2M_SECURITY_SMS_KEY_PARAM_ID, + LWM2M_SECURITY_SMS_SECRET_KEY_ID, + LWM2M_SECURITY_SMS_SERVER_NUMBER_ID, + LWM2M_SECURITY_SHORT_SERVER_ID, + LWM2M_SECURITY_HOLD_OFF_ID}; + int nbRes = sizeof(resList)/sizeof(uint16_t); + + *dataArrayP = lwm2m_data_new(nbRes); + if (*dataArrayP == NULL) return COAP_500_INTERNAL_SERVER_ERROR; + *numDataP = nbRes; + for (i = 0 ; i < nbRes ; i++) + { + (*dataArrayP)[i].id = resList[i]; + } + } + + i = 0; + do + { + result = prv_get_value((*dataArrayP) + i, targetP); + i++; + } while (i < *numDataP && result == COAP_205_CONTENT); + + return result; +} + +lwm2m_object_t * get_security_object(const char * uri) +{ + lwm2m_object_t * securityObj; + + securityObj = (lwm2m_object_t *)lwm2m_malloc(sizeof(lwm2m_object_t)); + + if (NULL != securityObj) + { + security_instance_t * targetP; + + memset(securityObj, 0, sizeof(lwm2m_object_t)); + + securityObj->objID = 0; + + // Manually create an hardcoded instance + targetP = (security_instance_t *)lwm2m_malloc(sizeof(security_instance_t)); + if (NULL == targetP) + { + lwm2m_free(securityObj); + return NULL; + } + + memset(targetP, 0, sizeof(security_instance_t)); + targetP->instanceId = 0; + targetP->uri = strdup(uri); + targetP->isBootstrap = false; + targetP->shortID = 123; + targetP->clientHoldOffTime = 10; + + securityObj->instanceList = LWM2M_LIST_ADD(securityObj->instanceList, targetP); + + securityObj->readFunc = prv_security_read; + } + + return securityObj; +} + +void free_security_object(lwm2m_object_t * objectP) +{ + while (objectP->instanceList != NULL) + { + security_instance_t * securityInstance = (security_instance_t *)objectP->instanceList; + objectP->instanceList = objectP->instanceList->next; + if (NULL != securityInstance->uri) + { + lwm2m_free(securityInstance->uri); + } + lwm2m_free(securityInstance); + } + lwm2m_free(objectP); +} + +char * get_server_uri(lwm2m_object_t * objectP, + uint16_t secObjInstID) +{ + security_instance_t * targetP = (security_instance_t *)LWM2M_LIST_FIND(objectP->instanceList, secObjInstID); + + if (NULL != targetP) + { + return lwm2m_strdup(targetP->uri); + } + + return NULL; +} diff --git a/src/object_server.c b/src/object_server.c new file mode 100644 index 0000000..3aa0a80 --- /dev/null +++ b/src/object_server.c @@ -0,0 +1,425 @@ +/******************************************************************************* + * + * Copyright (c) 2013, 2014 Intel Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Eclipse Distribution License v1.0 which accompany this distribution. + * + * The Eclipse Public License is available at + * http://www.eclipse.org/legal/epl-v10.html + * The Eclipse Distribution License is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * Contributors: + * David Navarro, Intel Corporation - initial API and implementation + * Julien Vermillard, Sierra Wireless + * Bosch Software Innovations GmbH - Please refer to git log + * Pascal Rieux - Please refer to git log + * + *******************************************************************************/ + +/* + * Resources: + * + * Name | ID | Operations | Instances | Mandatory | Type | Range | Units | + * Short ID | 0 | R | Single | Yes | Integer | 1-65535 | | + * Lifetime | 1 | R/W | Single | Yes | Integer | | s | + * Default Min Period | 2 | R/W | Single | No | Integer | | s | + * Default Max Period | 3 | R/W | Single | No | Integer | | s | + * Disable | 4 | E | Single | No | | | | + * Disable Timeout | 5 | R/W | Single | No | Integer | | s | + * Notification Storing | 6 | R/W | Single | Yes | Boolean | | | + * Binding | 7 | R/W | Single | Yes | String | | | + * Registration Update | 8 | E | Single | Yes | | | | + * + */ + +#include "liblwm2m.h" + +#include +#include +#include + +typedef struct _server_instance_ +{ + struct _server_instance_ * next; // matches lwm2m_list_t::next + uint16_t instanceId; // matches lwm2m_list_t::id + uint16_t shortServerId; + uint32_t lifetime; + bool storing; + char binding[4]; +} server_instance_t; + +static uint8_t prv_get_value(lwm2m_data_t * dataP, + server_instance_t * targetP) +{ + switch (dataP->id) + { + case LWM2M_SERVER_SHORT_ID_ID: + lwm2m_data_encode_int(targetP->shortServerId, dataP); + return COAP_205_CONTENT; + + case LWM2M_SERVER_LIFETIME_ID: + lwm2m_data_encode_int(targetP->lifetime, dataP); + return COAP_205_CONTENT; + + case LWM2M_SERVER_DISABLE_ID: + return COAP_405_METHOD_NOT_ALLOWED; + + case LWM2M_SERVER_STORING_ID: + lwm2m_data_encode_bool(targetP->storing, dataP); + return COAP_205_CONTENT; + + case LWM2M_SERVER_BINDING_ID: + lwm2m_data_encode_string(targetP->binding, dataP); + return COAP_205_CONTENT; + + case LWM2M_SERVER_UPDATE_ID: + return COAP_405_METHOD_NOT_ALLOWED; + + default: + return COAP_404_NOT_FOUND; + } +} + +static uint8_t prv_server_read(uint16_t instanceId, + int * numDataP, + lwm2m_data_t ** dataArrayP, + lwm2m_object_t * objectP) +{ + server_instance_t * targetP; + uint8_t result; + int i; + + targetP = (server_instance_t *)lwm2m_list_find(objectP->instanceList, instanceId); + if (NULL == targetP) return COAP_404_NOT_FOUND; + + // is the server asking for the full instance ? + if (*numDataP == 0) + { + uint16_t resList[] = { + LWM2M_SERVER_SHORT_ID_ID, + LWM2M_SERVER_LIFETIME_ID, + LWM2M_SERVER_STORING_ID, + LWM2M_SERVER_BINDING_ID + }; + int nbRes = sizeof(resList)/sizeof(uint16_t); + + *dataArrayP = lwm2m_data_new(nbRes); + if (*dataArrayP == NULL) return COAP_500_INTERNAL_SERVER_ERROR; + *numDataP = nbRes; + for (i = 0 ; i < nbRes ; i++) + { + (*dataArrayP)[i].id = resList[i]; + } + } + + i = 0; + do + { + result = prv_get_value((*dataArrayP) + i, targetP); + i++; + } while (i < *numDataP && result == COAP_205_CONTENT); + + return result; +} + +static uint8_t prv_server_discover(uint16_t instanceId, + int * numDataP, + lwm2m_data_t ** dataArrayP, + lwm2m_object_t * objectP) +{ + uint8_t result; + int i; + + result = COAP_205_CONTENT; + + // is the server asking for the full object ? + if (*numDataP == 0) + { + uint16_t resList[] = { + LWM2M_SERVER_SHORT_ID_ID, + LWM2M_SERVER_LIFETIME_ID, + LWM2M_SERVER_MIN_PERIOD_ID, + LWM2M_SERVER_MAX_PERIOD_ID, + LWM2M_SERVER_DISABLE_ID, + LWM2M_SERVER_TIMEOUT_ID, + LWM2M_SERVER_STORING_ID, + LWM2M_SERVER_BINDING_ID, + LWM2M_SERVER_UPDATE_ID + }; + int nbRes = sizeof(resList)/sizeof(uint16_t); + + *dataArrayP = lwm2m_data_new(nbRes); + if (*dataArrayP == NULL) return COAP_500_INTERNAL_SERVER_ERROR; + *numDataP = nbRes; + for (i = 0 ; i < nbRes ; i++) + { + (*dataArrayP)[i].id = resList[i]; + } + } + else + { + for (i = 0; i < *numDataP && result == COAP_205_CONTENT; i++) + { + switch ((*dataArrayP)[i].id) + { + case LWM2M_SERVER_SHORT_ID_ID: + case LWM2M_SERVER_LIFETIME_ID: + case LWM2M_SERVER_MIN_PERIOD_ID: + case LWM2M_SERVER_MAX_PERIOD_ID: + case LWM2M_SERVER_DISABLE_ID: + case LWM2M_SERVER_TIMEOUT_ID: + case LWM2M_SERVER_STORING_ID: + case LWM2M_SERVER_BINDING_ID: + case LWM2M_SERVER_UPDATE_ID: + break; + default: + result = COAP_404_NOT_FOUND; + } + } + } + + return result; +} + +static uint8_t prv_set_int_value(lwm2m_data_t * dataArray, + uint32_t * data) +{ + uint8_t result; + int64_t value; + + if (1 == lwm2m_data_decode_int(dataArray, &value)) + { + if (value >= 0 && value <= 0xFFFFFFFF) + { + *data = value; + result = COAP_204_CHANGED; + } + else + { + result = COAP_406_NOT_ACCEPTABLE; + } + } + else + { + result = COAP_400_BAD_REQUEST; + } + return result; +} + +static uint8_t prv_server_write(uint16_t instanceId, + int numData, + lwm2m_data_t * dataArray, + lwm2m_object_t * objectP) +{ + server_instance_t * targetP; + int i; + uint8_t result; + + targetP = (server_instance_t *)lwm2m_list_find(objectP->instanceList, instanceId); + if (NULL == targetP) + { + return COAP_404_NOT_FOUND; + } + + i = 0; + do + { + switch (dataArray[i].id) + { + case LWM2M_SERVER_SHORT_ID_ID: + { + uint32_t value; + + result = prv_set_int_value(dataArray + i, &value); + if (COAP_204_CHANGED == result) + { + if (0 < value && value <= 0xFFFF) + { + targetP->shortServerId = value; + } + else + { + result = COAP_406_NOT_ACCEPTABLE; + } + } + } + break; + + case LWM2M_SERVER_LIFETIME_ID: + result = prv_set_int_value(dataArray + i, (uint32_t *)&(targetP->lifetime)); + break; + + case LWM2M_SERVER_DISABLE_ID: + result = COAP_405_METHOD_NOT_ALLOWED; + break; + + case LWM2M_SERVER_STORING_ID: + { + bool value; + + if (1 == lwm2m_data_decode_bool(dataArray + i, &value)) + { + targetP->storing = value; + result = COAP_204_CHANGED; + } + else + { + result = COAP_400_BAD_REQUEST; + } + } + break; + + case LWM2M_SERVER_BINDING_ID: + if ((dataArray[i].type == LWM2M_TYPE_STRING || dataArray[i].type == LWM2M_TYPE_OPAQUE) + && dataArray[i].value.asBuffer.length > 0 && dataArray[i].value.asBuffer.length <= 3 + && (strncmp((char*)dataArray[i].value.asBuffer.buffer, "U", dataArray[i].value.asBuffer.length) == 0 + || strncmp((char*)dataArray[i].value.asBuffer.buffer, "UQ", dataArray[i].value.asBuffer.length) == 0 + || strncmp((char*)dataArray[i].value.asBuffer.buffer, "S", dataArray[i].value.asBuffer.length) == 0 + || strncmp((char*)dataArray[i].value.asBuffer.buffer, "SQ", dataArray[i].value.asBuffer.length) == 0 + || strncmp((char*)dataArray[i].value.asBuffer.buffer, "US", dataArray[i].value.asBuffer.length) == 0 + || strncmp((char*)dataArray[i].value.asBuffer.buffer, "UQS", dataArray[i].value.asBuffer.length) == 0)) + { + strncpy(targetP->binding, (char*)dataArray[i].value.asBuffer.buffer, dataArray[i].value.asBuffer.length); + result = COAP_204_CHANGED; + } + else + { + result = COAP_400_BAD_REQUEST; + } + break; + + case LWM2M_SERVER_UPDATE_ID: + result = COAP_405_METHOD_NOT_ALLOWED; + break; + + default: + return COAP_404_NOT_FOUND; + } + i++; + } while (i < numData && result == COAP_204_CHANGED); + + return result; +} + +static uint8_t prv_server_execute(uint16_t instanceId, + uint16_t resourceId, + uint8_t * buffer, + int length, + lwm2m_object_t * objectP) + +{ + server_instance_t * targetP; + + targetP = (server_instance_t *)lwm2m_list_find(objectP->instanceList, instanceId); + if (NULL == targetP) return COAP_404_NOT_FOUND; + + switch (resourceId) + { + case LWM2M_SERVER_DISABLE_ID: + // executed in core, if COAP_204_CHANGED is returned + return COAP_204_CHANGED; + + case LWM2M_SERVER_UPDATE_ID: + // executed in core, if COAP_204_CHANGED is returned + return COAP_204_CHANGED; + + default: + return COAP_405_METHOD_NOT_ALLOWED; + } +} + +static uint8_t prv_server_delete(uint16_t id, + lwm2m_object_t * objectP) +{ + server_instance_t * serverInstance; + + objectP->instanceList = lwm2m_list_remove(objectP->instanceList, id, (lwm2m_list_t **)&serverInstance); + if (NULL == serverInstance) return COAP_404_NOT_FOUND; + + lwm2m_free(serverInstance); + + return COAP_202_DELETED; +} + +static uint8_t prv_server_create(uint16_t instanceId, + int numData, + lwm2m_data_t * dataArray, + lwm2m_object_t * objectP) +{ + server_instance_t * serverInstance; + uint8_t result; + + serverInstance = (server_instance_t *)lwm2m_malloc(sizeof(server_instance_t)); + if (NULL == serverInstance) return COAP_500_INTERNAL_SERVER_ERROR; + memset(serverInstance, 0, sizeof(server_instance_t)); + + serverInstance->instanceId = instanceId; + objectP->instanceList = LWM2M_LIST_ADD(objectP->instanceList, serverInstance); + + result = prv_server_write(instanceId, numData, dataArray, objectP); + + if (result != COAP_204_CHANGED) + { + (void)prv_server_delete(instanceId, objectP); + } + else + { + result = COAP_201_CREATED; + } + + return result; +} + +lwm2m_object_t * get_server_object() +{ + lwm2m_object_t * serverObj; + + serverObj = (lwm2m_object_t *)lwm2m_malloc(sizeof(lwm2m_object_t)); + + if (NULL != serverObj) + { + server_instance_t * serverInstance; + + memset(serverObj, 0, sizeof(lwm2m_object_t)); + + serverObj->objID = 1; + + // Manually create an hardcoded server + serverInstance = (server_instance_t *)lwm2m_malloc(sizeof(server_instance_t)); + if (NULL == serverInstance) + { + lwm2m_free(serverObj); + return NULL; + } + + memset(serverInstance, 0, sizeof(server_instance_t)); + serverInstance->instanceId = 0; + serverInstance->shortServerId = 123; + serverInstance->lifetime = 300; + serverInstance->storing = false; + serverInstance->binding[0] = 'U'; + serverObj->instanceList = LWM2M_LIST_ADD(serverObj->instanceList, serverInstance); + + serverObj->readFunc = prv_server_read; + serverObj->writeFunc = prv_server_write; + serverObj->createFunc = prv_server_create; + serverObj->deleteFunc = prv_server_delete; + serverObj->executeFunc = prv_server_execute; + serverObj->discoverFunc = prv_server_discover; + } + + return serverObj; +} + +void free_server_object(lwm2m_object_t * object) +{ + while (object->instanceList != NULL) + { + server_instance_t * serverInstance = (server_instance_t *)object->instanceList; + object->instanceList = object->instanceList->next; + lwm2m_free(serverInstance); + } + lwm2m_free(object); +} diff --git a/src/serialout.h b/src/serialout.h new file mode 100644 index 0000000..4297faf --- /dev/null +++ b/src/serialout.h @@ -0,0 +1,20 @@ +#ifndef LIGHTCLIENT_SERIALOUT_H +#define LIGHTCLIENT_SERIALOUT_H + +#if defined(ARDUINO_AVR_LEONARDO) +#define SerialOut Serial +#define UBLOX Serial1 + +#elif defined(ARDUINO_SAMD_ZERO) +#define SerialOut SerialUSB +#define UBLOX Serial + +#elif defined(ARDUINO_SAM_ZERO) +#define SerialOut SerialUSB +#define UBLOX Serial1 + +#else +#error "Add your board." +#endif + +#endif //LIGHTCLIENT_SERIALOUT_H diff --git a/src/test_object.c b/src/test_object.c new file mode 100644 index 0000000..38e7ab8 --- /dev/null +++ b/src/test_object.c @@ -0,0 +1,391 @@ +/******************************************************************************* + * + * Copyright (c) 2013, 2014 Intel Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Eclipse Distribution License v1.0 which accompany this distribution. + * + * The Eclipse Public License is available at + * http://www.eclipse.org/legal/epl-v10.html + * The Eclipse Distribution License is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * Contributors: + * David Navarro, Intel Corporation - initial API and implementation + * domedambrosio - Please refer to git log + * Fabien Fleutot - Please refer to git log + * Axel Lorente - Please refer to git log + * Achim Kraus, Bosch Software Innovations GmbH - Please refer to git log + * Pascal Rieux - Please refer to git log + * Ville Skyttä - Please refer to git log + * + *******************************************************************************/ + +/* + Copyright (c) 2013, 2014 Intel Corporation + + Redistribution and use in source and binary forms, with or without modification, + are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + * Neither the name of Intel Corporation nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + THE POSSIBILITY OF SUCH DAMAGE. + + David Navarro + +*/ + +/* + * Implements an object for testing purpose + * + * Multiple + * Object | ID | Instances | Mandatoty | + * Test | 31024 | Yes | No | + * + * Resources: + * Supported Multiple + * Name | ID | Operations | Instances | Mandatory | Type | Range | Units | Description | + * test | 1 | R/W | No | Yes | Integer | 0-255 | | | + * exec | 2 | E | No | Yes | | | | | + * dec | 3 | R/W | No | Yes | Float | | | | + * sig | 4 | R/W | No | Yes | Integer | | | 16-bit signed integer | + * + */ + +#include "liblwm2m.h" + +#include +#include +#include +#include +#include + +static void prv_output_buffer(uint8_t * buffer, + int length) +{ + int i; + uint8_t array[16]; + + i = 0; + while (i < length) + { + int j; + fprintf(stderr, " "); + + memcpy(array, buffer+i, 16); + + for (j = 0 ; j < 16 && i+j < length; j++) + { + fprintf(stderr, "%02X ", array[j]); + } + while (j < 16) + { + fprintf(stderr, " "); + j++; + } + fprintf(stderr, " "); + for (j = 0 ; j < 16 && i+j < length; j++) + { + if (isprint(array[j])) + fprintf(stderr, "%c ", array[j]); + else + fprintf(stderr, ". "); + } + fprintf(stderr, "\n"); + + i += 16; + } +} + +/* + * Multiple instance objects can use userdata to store data that will be shared between the different instances. + * The lwm2m_object_t object structure - which represent every object of the liblwm2m as seen in the single instance + * object - contain a chained list called instanceList with the object specific structure prv_instance_t: + */ +typedef struct _prv_instance_ +{ + /* + * The first two are mandatories and represent the pointer to the next instance and the ID of this one. The rest + * is the instance scope user data (uint8_t test in this case) + */ + struct _prv_instance_ * next; // matches lwm2m_list_t::next + uint16_t shortID; // matches lwm2m_list_t::id + uint8_t test; + double dec; + int16_t sig; +} prv_instance_t; + +static uint8_t prv_read(uint16_t instanceId, + int * numDataP, + lwm2m_data_t ** dataArrayP, + lwm2m_object_t * objectP) +{ + prv_instance_t * targetP; + int i; + + targetP = (prv_instance_t *)lwm2m_list_find(objectP->instanceList, instanceId); + if (NULL == targetP) return COAP_404_NOT_FOUND; + + if (*numDataP == 0) + { + *dataArrayP = lwm2m_data_new(3); + if (*dataArrayP == NULL) return COAP_500_INTERNAL_SERVER_ERROR; + *numDataP = 3; + (*dataArrayP)[0].id = 1; + (*dataArrayP)[1].id = 3; + (*dataArrayP)[2].id = 4; + } + + for (i = 0 ; i < *numDataP ; i++) + { + switch ((*dataArrayP)[i].id) + { + case 1: + lwm2m_data_encode_int(targetP->test, *dataArrayP + i); + break; + case 2: + return COAP_405_METHOD_NOT_ALLOWED; + case 3: + lwm2m_data_encode_float(targetP->dec, *dataArrayP + i); + break; + case 4: + lwm2m_data_encode_int(targetP->sig, *dataArrayP + i); + break; + default: + return COAP_404_NOT_FOUND; + } + } + + return COAP_205_CONTENT; +} + +static uint8_t prv_discover(uint16_t instanceId, + int * numDataP, + lwm2m_data_t ** dataArrayP, + lwm2m_object_t * objectP) +{ + int i; + + // is the server asking for the full object ? + if (*numDataP == 0) + { + *dataArrayP = lwm2m_data_new(4); + if (*dataArrayP == NULL) return COAP_500_INTERNAL_SERVER_ERROR; + *numDataP = 4; + (*dataArrayP)[0].id = 1; + (*dataArrayP)[1].id = 2; + (*dataArrayP)[2].id = 3; + (*dataArrayP)[3].id = 4; + } + else + { + for (i = 0; i < *numDataP; i++) + { + switch ((*dataArrayP)[i].id) + { + case 1: + case 2: + case 3: + case 4: + break; + default: + return COAP_404_NOT_FOUND; + } + } + } + return COAP_205_CONTENT; +} + +static uint8_t prv_write(uint16_t instanceId, + int numData, + lwm2m_data_t * dataArray, + lwm2m_object_t * objectP) +{ + prv_instance_t * targetP; + int i; + + targetP = (prv_instance_t *)lwm2m_list_find(objectP->instanceList, instanceId); + if (NULL == targetP) return COAP_404_NOT_FOUND; + + for (i = 0 ; i < numData ; i++) + { + switch (dataArray[i].id) + { + case 1: + { + int64_t value; + + if (1 != lwm2m_data_decode_int(dataArray + i, &value) || value < 0 || value > 0xFF) + { + return COAP_400_BAD_REQUEST; + } + targetP->test = (uint8_t)value; + } + break; + case 2: + return COAP_405_METHOD_NOT_ALLOWED; + case 3: + if (1 != lwm2m_data_decode_float(dataArray + i, &(targetP->dec))) + { + return COAP_400_BAD_REQUEST; + } + break; + case 4: + { + int64_t value; + + if (1 != lwm2m_data_decode_int(dataArray + i, &value) || value < INT16_MIN || value > INT16_MAX) + { + return COAP_400_BAD_REQUEST; + } + targetP->sig = (int16_t)value; + } + break; + default: + return COAP_404_NOT_FOUND; + } + } + + return COAP_204_CHANGED; +} + +static uint8_t prv_delete(uint16_t id, + lwm2m_object_t * objectP) +{ + prv_instance_t * targetP; + + objectP->instanceList = lwm2m_list_remove(objectP->instanceList, id, (lwm2m_list_t **)&targetP); + if (NULL == targetP) return COAP_404_NOT_FOUND; + + lwm2m_free(targetP); + + return COAP_202_DELETED; +} + +static uint8_t prv_create(uint16_t instanceId, + int numData, + lwm2m_data_t * dataArray, + lwm2m_object_t * objectP) +{ + prv_instance_t * targetP; + uint8_t result; + + + targetP = (prv_instance_t *)lwm2m_malloc(sizeof(prv_instance_t)); + if (NULL == targetP) return COAP_500_INTERNAL_SERVER_ERROR; + memset(targetP, 0, sizeof(prv_instance_t)); + + targetP->shortID = instanceId; + objectP->instanceList = LWM2M_LIST_ADD(objectP->instanceList, targetP); + + result = prv_write(instanceId, numData, dataArray, objectP); + + if (result != COAP_204_CHANGED) + { + (void)prv_delete(instanceId, objectP); + } + else + { + result = COAP_201_CREATED; + } + + return result; +} + +static uint8_t prv_exec(uint16_t instanceId, + uint16_t resourceId, + uint8_t * buffer, + int length, + lwm2m_object_t * objectP) +{ + + if (NULL == lwm2m_list_find(objectP->instanceList, instanceId)) return COAP_404_NOT_FOUND; + + switch (resourceId) + { + case 1: + return COAP_405_METHOD_NOT_ALLOWED; + case 2: +// fprintf(stdout, "\r\n-----------------\r\n" +// "Execute on %hu/%d/%d\r\n" +// " Parameter (%d bytes):\r\n", +// objectP->objID, instanceId, resourceId, length); + prv_output_buffer((uint8_t*)buffer, length); +// fprintf(stdout, "-----------------\r\n\r\n"); + return COAP_204_CHANGED; + case 3: + return COAP_405_METHOD_NOT_ALLOWED; + default: + return COAP_404_NOT_FOUND; + } +} + +lwm2m_object_t * get_test_object(void) +{ + lwm2m_object_t * testObj; + + testObj = (lwm2m_object_t *)lwm2m_malloc(sizeof(lwm2m_object_t)); + + if (NULL != testObj) + { + int i; + prv_instance_t * targetP; + + memset(testObj, 0, sizeof(lwm2m_object_t)); + + testObj->objID = 31024; + for (i=0 ; i < 3 ; i++) + { + targetP = (prv_instance_t *)lwm2m_malloc(sizeof(prv_instance_t)); + if (NULL == targetP) return NULL; + memset(targetP, 0, sizeof(prv_instance_t)); + targetP->shortID = 10 + i; + targetP->test = 20 + i; + targetP->dec = -30 + i + (double)i/100.0; + targetP->sig = 0 - i; + testObj->instanceList = LWM2M_LIST_ADD(testObj->instanceList, targetP); + } + /* + * From a single instance object, two more functions are available. + * - The first one (createFunc) create a new instance and filled it with the provided informations. If an ID is + * provided a check is done for verifying his disponibility, or a new one is generated. + * - The other one (deleteFunc) delete an instance by removing it from the instance list (and freeing the memory + * allocated to it) + */ + testObj->readFunc = prv_read; + testObj->writeFunc = prv_write; + testObj->executeFunc = prv_exec; + testObj->createFunc = prv_create; + testObj->deleteFunc = prv_delete; + testObj->discoverFunc = prv_discover; + } + + return testObj; +} + +void free_test_object(lwm2m_object_t * object) +{ + LWM2M_LIST_FREE(object->instanceList); + if (object->userData != NULL) + { + lwm2m_free(object->userData); + object->userData = NULL; + } + lwm2m_free(object); +} diff --git a/src/wakaama-client.cpp b/src/wakaama-client.cpp new file mode 100644 index 0000000..0b439b7 --- /dev/null +++ b/src/wakaama-client.cpp @@ -0,0 +1,258 @@ +#include "wakaama-client.h" +#include +#include +#include +#include + + +#ifndef LWM2M_WITH_LOGS +// Same usage as C89 printf() +extern void lwm2m_printf(const char * format, ...); +#endif + +/** + * "coap://localhost:5683" + * @param uri + * @todo init and use uri in security object + */ +void ArduinoClient::init() { + SerialOut.println(F("init():start")); + memset(&data, 0, sizeof(client_data_t)); + + const __FlashStringHelper * objFail = F("Failed to create object"); + + // create udp listener + data.udp = new EthernetUDP(); + data.udp->begin(localPort); + + // init objects + SerialOut.println(F("*object:security")); + objArray[0] = get_security_object(uri); + if (nullptr == objArray[0]) + { + SerialOut.println(objFail); + exit(0); + } + data.securityObjP = objArray[0]; + + SerialOut.println(F("*object:server")); + objArray[1] = get_server_object(); + if (nullptr == objArray[1]) + { + SerialOut.println(objFail); + exit(0); + } + + SerialOut.println(F("*object:device")); + objArray[2] = get_object_device(); + if (nullptr == objArray[2]) + { + SerialOut.println(objFail); + exit(0); + } + + SerialOut.println(F("*object:test")); + objArray[3] = get_test_object(); + if (nullptr == objArray[3]) + { + SerialOut.println(objFail); + exit(0); + } + + /* + * The liblwm2m library is now initialized with the functions that will be in + * charge of communication + */ + SerialOut.println(F("*lwm2m_init()")); + lwm2mH = lwm2m_init(&data); + if (NULL == lwm2mH) + { + SerialOut.println(F("lwm2m_init() failed")); + exit(0); + } + + /* + * We configure the liblwm2m library with the name of the client - which shall be unique for each client - + * the number of objects we will be passing through and the objects array + */ + SerialOut.println(F("*lwm2m_configure()")); + result = lwm2m_configure(lwm2mH, name, NULL, NULL, OBJ_COUNT, objArray); + if (result != 0) + { + SerialOut.println(F("lwm2m_configure() failed")); + exit(0); + } + SerialOut.println(F("init():done")); +} + + +void ArduinoClient::doWorkStep() { + SerialOut.println(F("doWorkStep():start")); + + /* + * This function does two things: + * - first it does the work needed by liblwm2m (eg. (re)sending some packets). + * - Secondly it adjusts the timeout value (default 60s) depending on the state of the transaction + * (eg. retransmission) and the time before the next operation + */ + SerialOut.println(F("lwm2m_step()")); + result = lwm2m_step(lwm2mH, &step_delay); + if (result != 0) + { + SerialOut.print(F("lwm2m_step() failed")); + exit(0); + } + + // wait for socket event + SerialOut.println(F("parsePacket()")); + int packetSize = data.udp->parsePacket(); + if (packetSize) { + int numBytes = data.udp->read(packetBuffer, UDP_TX_PACKET_MAX_SIZE); + + connection_t * connP; + connP = connection_find(data.connList, data.udp); + if (connP != nullptr) + { + /* + * Let liblwm2m respond to the query depending on the context + */ + lwm2m_handle_packet(lwm2mH, (uint8_t*)packetBuffer, numBytes, connP); + } + } + SerialOut.println(F("doWorkStep():done")); +} + +/** + * Destructor, actually this won't ever run on arduino... + */ +ArduinoClient::~ArduinoClient() { + lwm2m_close(lwm2mH); + data.udp->stop(); + connection_free(data.connList); + + free_security_object(objArray[0]); + free_server_object(objArray[1]); + free_object_device(objArray[2]); + free_test_object(objArray[3]); +} + +/** + * called from registration.c prv_register() + * + * @param secObjInstID + * @param userData + * @return + */ +void * lwm2m_connect_server(uint16_t secObjInstID, void *userData) { + client_data_t * dataP; + char * uri; + char * host; + char * port; + connection_t * newConnP = nullptr; + + dataP = (client_data_t *)userData; + + uri = get_server_uri(dataP->securityObjP, secObjInstID); + + if (uri == nullptr) return nullptr; + + SerialOut.print(F("Connecting to ")); + SerialOut.println(uri); + + // parse uri in the form "coaps://[host]:[port]" + if (0 == strncmp(uri, "coaps://", strlen("coaps://"))) + { + host = uri+strlen("coaps://"); + } + else if (0 == strncmp(uri, "coap://", strlen("coap://"))) + { + host = uri+strlen("coap://"); + } + else + { + lwm2m_free(uri); + return (void *)newConnP; + } + port = strrchr(host, ':'); + if (port == nullptr) { + lwm2m_free(uri); + return (void *)newConnP; + } + // remove brackets + if (host[0] == '[') + { + host++; + if (*(port - 1) == ']') + { + *(port - 1) = 0; + } + else { + lwm2m_free(uri); + return (void *)newConnP; + } + } + // split strings + *port = 0; + port++; + + auto * remoteIp = new IPAddress(); + if(!remoteIp->fromString(host)) { + DNSClient dns; + dns.begin(Ethernet.dnsServerIP()); + dns.getHostByName(host, *remoteIp); + } + + String portStr = port; + + newConnP = connection_create(dataP->connList, dataP->udp, remoteIp, portStr.toInt()); + if (newConnP == nullptr) { + SerialOut.println(F("Connection creation failed")); + } + else { + dataP->connList = newConnP; + } + SerialOut.println(F("Connection created")); + + lwm2m_free(uri); + return (void *)newConnP; +}; + +/** + * called from + * + * @param sessionH + * @param userData + */ +void lwm2m_close_connection(void *sessionH, void *userData) { + client_data_t * app_data; + connection_t * targetP; + + app_data = (client_data_t *)userData; + targetP = (connection_t *)sessionH; + + if (targetP == app_data->connList) + { + app_data->connList = targetP->next; + lwm2m_free(targetP); + } + else + { + connection_t * parentP; + + parentP = app_data->connList; + while (parentP != nullptr && parentP->next != targetP) + { + parentP = parentP->next; + } + if (parentP != nullptr) + { + parentP->next = targetP->next; + lwm2m_free(targetP); + } + } +}; + +void lwm2m_printf(const char * format, ...) +{ + SerialOut.println(format); +} diff --git a/src/wakaama-client.h b/src/wakaama-client.h new file mode 100644 index 0000000..4673579 --- /dev/null +++ b/src/wakaama-client.h @@ -0,0 +1,53 @@ +#ifndef WAKAAMA_CLIENT_H_ +#define WAKAAMA_CLIENT_H_ + +#include +#include +#include +#include + +#define OBJ_COUNT 4 + +extern "C" { +extern lwm2m_object_t * get_object_device(void); +extern void free_object_device(lwm2m_object_t * objectP); +extern lwm2m_object_t * get_server_object(void); +extern void free_server_object(lwm2m_object_t * object); +extern lwm2m_object_t * get_security_object(const char * uri); +extern void free_security_object(lwm2m_object_t * objectP); +extern char * get_server_uri(lwm2m_object_t * objectP, uint16_t secObjInstID); +extern lwm2m_object_t * get_test_object(void); +extern void free_test_object(lwm2m_object_t * object); +}; + +typedef struct +{ + lwm2m_object_t * securityObjP; + connection_t * connList; + EthernetUDP * udp; +} client_data_t; + + +class ArduinoClient { + +public: + ArduinoClient(const char *uri) : uri(uri) {}; + void init(); + ~ArduinoClient(); + void doWorkStep(); +private: + const char * uri; + const uint16_t localPort = 56830; + const char * name = "wakaamArduino"; + time_t step_delay = 60; + int result; + + char packetBuffer[UDP_TX_PACKET_MAX_SIZE]; + + client_data_t data; + lwm2m_context_t * lwm2mH = nullptr; + lwm2m_object_t * objArray[OBJ_COUNT]; + +}; + +#endif