* initial commit
This commit is contained in:
commit
b67069fc45
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
.idea
|
||||
cmake-build-debug
|
||||
33
CMakeLists.txt
Normal file
33
CMakeLists.txt
Normal file
@ -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 $<$<CONFIG:Debug>:WITH_LOGS>)
|
||||
|
||||
SOURCE_GROUP(wakaama FILES ${WAKAAMA_SOURCES})
|
||||
10
library.properties
Normal file
10
library.properties
Normal file
@ -0,0 +1,10 @@
|
||||
name=Wakaama Client
|
||||
version=0.1.1
|
||||
author=DavidDanyi,Ericsson
|
||||
maintainer=David Danyi <david.danyi@ericsson.com>
|
||||
sentence=Eclipse Wakaama client
|
||||
paragraph=
|
||||
category=Communication
|
||||
url=
|
||||
architectures=samd
|
||||
includes=wakaama-client.h
|
||||
307
src/object_device.c
Normal file
307
src/object_device.c
Normal file
@ -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 <david.navarro@intel.com>
|
||||
|
||||
*/
|
||||
|
||||
/*
|
||||
* 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 <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
|
||||
|
||||
#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);
|
||||
}
|
||||
253
src/object_security.c
Normal file
253
src/object_security.c
Normal file
@ -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 <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
|
||||
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;
|
||||
}
|
||||
425
src/object_server.c
Normal file
425
src/object_server.c
Normal file
@ -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 <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
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);
|
||||
}
|
||||
20
src/serialout.h
Normal file
20
src/serialout.h
Normal file
@ -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
|
||||
391
src/test_object.c
Normal file
391
src/test_object.c
Normal file
@ -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 <david.navarro@intel.com>
|
||||
|
||||
*/
|
||||
|
||||
/*
|
||||
* 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 <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <limits.h>
|
||||
|
||||
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);
|
||||
}
|
||||
258
src/wakaama-client.cpp
Normal file
258
src/wakaama-client.cpp
Normal file
@ -0,0 +1,258 @@
|
||||
#include "wakaama-client.h"
|
||||
#include <liblwm2m.h>
|
||||
#include <USB/USBAPI.h>
|
||||
#include <Dns.h>
|
||||
#include <Ethernet.h>
|
||||
|
||||
|
||||
#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);
|
||||
}
|
||||
53
src/wakaama-client.h
Normal file
53
src/wakaama-client.h
Normal file
@ -0,0 +1,53 @@
|
||||
#ifndef WAKAAMA_CLIENT_H_
|
||||
#define WAKAAMA_CLIENT_H_
|
||||
|
||||
#include <Udp.h>
|
||||
#include <EthernetUdp.h>
|
||||
#include <connection.h>
|
||||
#include <serialout.h>
|
||||
|
||||
#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
|
||||
Loading…
x
Reference in New Issue
Block a user