[
Date Prev][
Date Next][
Thread Prev][
Thread Next][
Date Index][
Thread Index]
[
List Home]
[wakaama-dev] Wakaama Client with Multiple Resource Instances
|
Hi all,
I'm currently developing an LwM2M client using Wakaama library, and it would be nice to use Multiple Resource Instances in one of my Smart Object - an object what implemets a GeoFencing function. The .xml and .cpp files of this Object are attached to see what I'm talking about.
For the GPS functionality I use the "Sodaq_UBlox_GPS" library.
I've tried with Single Resources already, and it works fine - but with Multiple Resources I get this error message: "Invalid Response: Unable to decode response payload of request".
I'm trying to find the problem, but couldn't find it yet. I also couln't find any examples on the Internet, where Muliple Resources are used with Wakaama.
Do you have any experience with this topic? I would be grateful for every help!
Thanks for reading,
Regards,
Zsofi Papp
/*
* Implements a location object wiht GEOFENCING functionality.
*
*
* | | Multiple | |
* Object | ID | Instances | Mandatory |
* Test | 42701 | No | Yes |
*
*
* Resources: => Supported multiple!
*
* Name | ID | Op | Inst. |Mand.| Type | Range | Units | Description
* Latitude | 1 | R | Single | Yes | Float | | Deg | The decimal notation of latitude e.g. - 45.5723 [Worlds Geodetic System 1984].|
* Longitude | 2 | R | Single | Yes | Float | | Deg | The decimal notation of longitude e.g. - 153.21760 [Worlds Geodetic System 1984].|
* Geof. lat | 3 | R/W | Multiple| No | Float | | Deg |
* Geof. lon | 4 | R/W | Multiple| No | Float | | Deg |
* Geof. radius| 5 | R/W | Multiple| No | Float | | km | The value in the Radius Resource indicates the size in meters of a circular area |
* Timestamp | 6 | R | Single | Yes | Time | | s | The timestamp when GeoFencing border has been crossed. |
* State | 7 | R | Single | No | Integer | | | The actual state of Device => 4 states: C->A,C->B, A->C, B->C
*
*
*/
#include "wakaama-client.h"
#include "liblwm2m.h"
#include <Arduino.h>
#include <Wire.h>
#include <Sodaq_UBlox_GPS.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <limits.h>
#include <time.h>
//Debug Stream for GPS module
#define DEBUG_STREAM SerialUSB
#ifdef LWM2M_CLIENT_MODE
#define GEOFENCING_OBJECT_ID 42701
//Variables for READ only Resources --> lwm2m_resource_value_changed(lwm2mH, &uri_)
lwm2m_uri_t uri1, uri2, uri6, uri77; //lat,lon,timestamp,state
#define RES_M_LATITUDE 1
#define RES_M_LONGITUDE 2
#define RES_M_GEOFENCING_LAT 3
#define RES_M_GEOFENCING_LON 4
#define RES_M_GEOFENCING_RAD 5
#define RES_M_TIMESTAMP 6
#define RES_M_STATE 7
typedef struct
{
//Only SINGLE Object Instance can be used!! Multiple object instances wanted => uncomment the next two line for Instance list
//struct _prv_instance_ * next; // matches lwm2m_list_t::next
//uint16_t shortID; // matches lwm2m_list_t::id
double latitude;
double longitude;
double geofencing_lat[2];
double geofencing_lon[2];
double radius[2];
unsigned long timestamp;
uint8_t state;
} gps_geofencing_data_t;
static bool state_changed(gps_geofencing_data_t * data)
{
uint8_t actual_state;
static uint8_t last_state = 0;
double range = sodaq_gps.getRange((int32_t)(data->geofencing_lon[0]), (int32_t)(data->geofencing_lat[0]));
DEBUG_STREAM.println(String("Range from GeoFencing center point:") + String(range, 7) + String("km"));
//One circle GeoFence area
actual_state = 1; //Outside the circle
if(range < data->radius[0]) {
actual_state = 0; //Inside the circle
}
else actual_state = 1; //Outside the circle
//TODO: Two circle GeoFence area (5 states: 0...4)
/*
if(range_from1 < data->radius1) { //Inside of 1. circle
actual_state = 0;
}
else if (range_from2 < data->radius2) { //Inside of 2. circle
actual_state = 1;
}
else if (range_from1 > data->radius1 && range_from2 > data->radius2 && last_state == 0) {
actual_state = 2; //Outside, came from 1. circle
}
else if (range_from1 > data->radius1 && range_from2 > data->radius2 && last_state == 1) {
actual_state = 3; //Outside, came from 2. circle
}
else
actual_state = 4; //State not defined!
*/
if (last_state != actual_state)
{
data->state = actual_state;
last_state = actual_state;
return true;
}
return false;
}
//-----Reading the Resource values of the GeoFencing Object Instance
//
static uint8_t gps_read(uint16_t instanceId,
int * numDataP,
lwm2m_data_t ** dataArrayP,
lwm2m_object_t * objectP)
{
int i;
uint8_t result;
gps_geofencing_data_t * data = (gps_geofencing_data_t*)(objectP->userData);
// this is a single instance object (object ID can't be greater than 0)
if (instanceId != 0)
{
return COAP_404_NOT_FOUND;
}
if (*numDataP == 0)
{
*dataArrayP = lwm2m_data_new(7);
if (*dataArrayP == NULL) return COAP_500_INTERNAL_SERVER_ERROR;
*numDataP = 7;
(*dataArrayP)[0].id = 1; //RES_M_LATITUDE
(*dataArrayP)[1].id = 2; //RES_M_LONGITUDE
(*dataArrayP)[2].id = 3; //RES_M_GEOFENCING_LAT => array of lats
(*dataArrayP)[2].value.asChildren.array = lwm2m_data_new(2);
if ((*dataArrayP)[2].value.asChildren.array == NULL) return COAP_500_INTERNAL_SERVER_ERROR;
(*dataArrayP)[3].id = 4; //RES_M_GEOFENCING_LON => array of lons
(*dataArrayP)[3].value.asChildren.array = lwm2m_data_new(2);
if ((*dataArrayP)[3].value.asChildren.array == NULL) return COAP_500_INTERNAL_SERVER_ERROR;
(*dataArrayP)[4].id = 5; //RES_M_GEOFENCING_RAD => array of rads
(*dataArrayP)[4].value.asChildren.array = lwm2m_data_new(2);
if ((*dataArrayP)[4].value.asChildren.array == NULL) return COAP_500_INTERNAL_SERVER_ERROR;
(*dataArrayP)[5].id = 6; //RES_M_TIMESTAMP
(*dataArrayP)[6].id = 7; //RES_M_STATE
}
i = 0;
do
{
switch ((*dataArrayP)[i].id)
{
case RES_M_LATITUDE:
lwm2m_data_encode_float(data->latitude, *dataArrayP + i);
result = COAP_205_CONTENT;
break;
case RES_M_LONGITUDE:
lwm2m_data_encode_float(data->longitude, *dataArrayP + i);
result = COAP_205_CONTENT;
break;
case RES_M_GEOFENCING_LAT:
//lwm2m_data_encode_float(data->geofencing_lat, *dataArrayP + i);
lwm2m_data_encode_float(data->geofencing_lat[0], (*dataArrayP)[2].value.asChildren.array + 0);
lwm2m_data_encode_float(data->geofencing_lat[1], (*dataArrayP)[2].value.asChildren.array + 1);
lwm2m_data_encode_instances( (*dataArrayP)[2].value.asChildren.array, 2, *dataArrayP + i);
result = COAP_205_CONTENT;
break;
case RES_M_GEOFENCING_LON:
//lwm2m_data_encode_float(data->geofencing_lon, *dataArrayP + i);
lwm2m_data_encode_float(data->geofencing_lon[0], (*dataArrayP)[3].value.asChildren.array + 0);
lwm2m_data_encode_float(data->geofencing_lon[1], (*dataArrayP)[3].value.asChildren.array + 1);
lwm2m_data_encode_instances( (*dataArrayP)[3].value.asChildren.array, 2, *dataArrayP + i);
result = COAP_205_CONTENT;
break;
case RES_M_GEOFENCING_RAD:
//lwm2m_data_encode_float(data->radius, *dataArrayP + i);
lwm2m_data_encode_float(data->radius[0], (*dataArrayP)[4].value.asChildren.array + 0);
lwm2m_data_encode_float(data->radius[1], (*dataArrayP)[4].value.asChildren.array + 1);
lwm2m_data_encode_instances( (*dataArrayP)[4].value.asChildren.array, 2, *dataArrayP + i);
result = COAP_205_CONTENT;
break;
case RES_M_TIMESTAMP: //unsigned long
lwm2m_data_encode_int(data->timestamp, *dataArrayP + i);
result = COAP_205_CONTENT;
break;
case RES_M_STATE:
lwm2m_data_encode_int(data->state, *dataArrayP + i);
result = COAP_205_CONTENT;
break;
default:
result = COAP_404_NOT_FOUND;
}
i++;
} while (i < *numDataP && result == COAP_205_CONTENT);
return result;
}
//-----Writing the Resource values of the Electrometer Object Instance
static uint8_t gps_write(uint16_t instanceId,
int numData,
lwm2m_data_t * dataArray,
lwm2m_object_t * objectP)
{
int i;
uint8_t result;
gps_geofencing_data_t * data = (gps_geofencing_data_t*)(objectP->userData);
// this is a single instance object
if (instanceId != 0)
{
return COAP_404_NOT_FOUND;
}
i = 0;
do
{
switch (dataArray[i].id)
{
case RES_M_LATITUDE:
result = COAP_405_METHOD_NOT_ALLOWED;
break;
case RES_M_LONGITUDE:
result = COAP_405_METHOD_NOT_ALLOWED;
break;
case RES_M_GEOFENCING_LAT:
//if (lwm2m_data_decode_float(&dataArray[i], &data->geofencing_lat) == 1)
if ( lwm2m_data_decode_float( &(dataArray[2].value.asChildren.array[0]), &(data->geofencing_lat[0])) == 1 &&
lwm2m_data_decode_float( &(dataArray[2].value.asChildren.array[1]), &(data->geofencing_lat[1])) == 1 )
result = COAP_204_CHANGED;
break;
case RES_M_GEOFENCING_LON:
//if (lwm2m_data_decode_float(&dataArray[i], &data->geofencing_lon) == 1)
if ( lwm2m_data_decode_float( &(dataArray[3].value.asChildren.array[0]), &(data->geofencing_lon[0])) == 1 &&
lwm2m_data_decode_float( &(dataArray[3].value.asChildren.array[1]), &(data->geofencing_lon[1])) == 1 )
result = COAP_204_CHANGED;
break;
case RES_M_GEOFENCING_RAD:
//if (lwm2m_data_decode_float(&dataArray[i], &data->radius) == 1)
if ( lwm2m_data_decode_float( &(dataArray[4].value.asChildren.array[0]), &(data->radius[0])) == 1 &&
lwm2m_data_decode_float( &(dataArray[4].value.asChildren.array[1]), &(data->radius[1])) == 1 )
result = COAP_204_CHANGED;
break;
case RES_M_TIMESTAMP:
result = COAP_405_METHOD_NOT_ALLOWED;
break;
case RES_M_STATE:
result = COAP_405_METHOD_NOT_ALLOWED;
break;
default:
result = COAP_405_METHOD_NOT_ALLOWED;
}
i++;
} while (i < numData && result == COAP_204_CHANGED);
return result;
}
static uint8_t gps_execute(uint16_t instanceId,
uint16_t resourceId,
uint8_t * buffer,
int length,
lwm2m_object_t * objectP)
{
if (instanceId != 0)
{
return COAP_404_NOT_FOUND;
}
if (length != 0) return COAP_400_BAD_REQUEST;
return COAP_405_METHOD_NOT_ALLOWED;
}
lwm2m_object_t * get_object_gps_geofencing(void)
{
lwm2m_stringToUri("/42701/0/1", 10, &uri1);
lwm2m_stringToUri("/42701/0/2", 10, &uri2);
lwm2m_stringToUri("/42701/0/6", 10, &uri6);
lwm2m_stringToUri("/42701/0/7", 10, &uri77);
lwm2m_object_t * geofencingObj;
geofencingObj = (lwm2m_object_t *)lwm2m_malloc(sizeof(lwm2m_object_t));
if (NULL != geofencingObj)
{
memset(geofencingObj, 0, sizeof(lwm2m_object_t));
geofencingObj->objID = GEOFENCING_OBJECT_ID;
geofencingObj->instanceList = (lwm2m_list_t *)lwm2m_malloc(sizeof(lwm2m_list_t));
if (NULL != geofencingObj->instanceList)
{
memset(geofencingObj->instanceList, 0, sizeof(lwm2m_list_t));
}
else
{
lwm2m_free(geofencingObj);
return NULL;
}
geofencingObj->readFunc = gps_read;
geofencingObj->writeFunc = gps_write;
geofencingObj->executeFunc = gps_execute;
geofencingObj->userData = lwm2m_malloc(sizeof(gps_geofencing_data_t));
if (NULL != geofencingObj->userData)
{
((gps_geofencing_data_t*)geofencingObj->userData)->latitude = 47.870000;
((gps_geofencing_data_t*)geofencingObj->userData)->longitude = 19.063200;
((gps_geofencing_data_t*)geofencingObj->userData)->geofencing_lat[0] = 11.111111;
((gps_geofencing_data_t*)geofencingObj->userData)->geofencing_lon[0] = 33.333333;
((gps_geofencing_data_t*)geofencingObj->userData)->radius[0] = 200; // km
((gps_geofencing_data_t*)geofencingObj->userData)->geofencing_lat[1] = 12.121212;
((gps_geofencing_data_t*)geofencingObj->userData)->geofencing_lon[1] = 34.343434;
((gps_geofencing_data_t*)geofencingObj->userData)->radius[1] = 100; // km
((gps_geofencing_data_t*)geofencingObj->userData)->timestamp = time(NULL); // seconds since START
((gps_geofencing_data_t*)geofencingObj->userData)->state = 0; // state (integer) 0...4
}
else
{
lwm2m_free(geofencingObj);
geofencingObj = NULL;
}
}
return geofencingObj;
}
void free_object_gps_geofencing(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);
}
//Some delay before starting to scan gps information
void find_fix(uint32_t delay_until)
{
DEBUG_STREAM.println(String("delay ... ") + delay_until + String("ms"));
delay(delay_until);
uint32_t start = millis();
uint32_t timeout = 1L*2000;
DEBUG_STREAM.println(String("waiting for fix ..., timeout=") + timeout + String("ms"));
if (sodaq_gps.scan(true, timeout)) {
DEBUG_STREAM.println(String(" =================time to find fix: ") + (millis() - start) + String("ms"));
DEBUG_STREAM.println(String(" datetime = ") + sodaq_gps.getDateTimeString());
DEBUG_STREAM.println(String(" lat = ") + String(sodaq_gps.getLat(), 7));
DEBUG_STREAM.println(String(" lon = ") + String(sodaq_gps.getLon(), 7));
DEBUG_STREAM.println(String(" num sats = ") + String(sodaq_gps.getNumberOfSatellites()));
} else {
DEBUG_STREAM.println("No Fix");
}
}
void tick_object_gps_geofencing(lwm2m_object_t * objectP, lwm2m_context_t * lwm2mH)
{
static unsigned long lastReport = 0;
static unsigned long lastMeasurement = 0;
unsigned long now = millis();
const int measurementInterval = 2000;
const int readingInterval = 5000;
const int wait_ms = 0;
gps_geofencing_data_t * data = (gps_geofencing_data_t*)(objectP->userData);
if (now - lastMeasurement > measurementInterval) {
find_fix(wait_ms); //Takes 2sec! => loop time = 2sec+measurementInterval
double actualLat = sodaq_gps.getLat();
double actualLon = sodaq_gps.getLon();
struct tm t = {0}; // Initalize to all 0's
t.tm_year = sodaq_gps.getYear(); // This is year-1900, so 112 = 2012
t.tm_mon = sodaq_gps.getMonth();
t.tm_mday = sodaq_gps.getDay();
t.tm_hour = sodaq_gps.getHour();
t.tm_min = sodaq_gps.getMinute();
t.tm_sec = sodaq_gps.getSecond();
time_t actualTimeT = mktime(&t);
data->latitude = actualLat; //Resource ID=1
data->longitude = actualLon; //ID=2
data->timestamp = actualTimeT; //ID=6 (timestamp => unsigned long)
//data->state = actualInside; //ID=10 (this is updated in state_changed() function!)
if (state_changed(data)) {
lwm2m_resource_value_changed(lwm2mH, &uri6); //Time
lwm2m_resource_value_changed(lwm2mH, &uri77); //State
}
lastMeasurement = now;
}
if ((readingInterval > 0) &&
(now - lastReport > readingInterval))
{
lwm2m_resource_value_changed(lwm2mH, &uri1); //Latitude
lwm2m_resource_value_changed(lwm2mH, &uri2); //Longitude
lastReport = now;
}
}
#endif
<?xml version="1.0" encoding="utf-8"?>
<LWM2M xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://openmobilealliance.org/tech/profiles/LWM2M.xsd">
<Object ObjectType="MODefinition">
<Name>GeoFencingM</Name>
<Description1>This IPSO object represents GPS coordinates and two circle shaped GeoFencing area (with multiple resources), which can be modified.
</Description1>
<ObjectID>42702</ObjectID>
<ObjectURN>urn:oma:lwm2m:ext:42702</ObjectURN>
<MultipleInstances>Single</MultipleInstances>
<Mandatory>Optional</Mandatory>
<Resources>
<Item ID="1">
<Name>Latitude</Name>
<Operations>R</Operations>
<MultipleInstances>Single</MultipleInstances>
<Mandatory>Mandatory</Mandatory>
<Type>Float</Type>
<RangeEnumeration></RangeEnumeration>
<Units>Deg</Units>
<Description>The decimal notation of latitude, e.g. -43.5723 (World Geodetic System 1984).</Description>
</Item>
<Item ID="2">
<Name>Longitude</Name>
<Operations>R</Operations>
<MultipleInstances>Single</MultipleInstances>
<Mandatory>Mandatory</Mandatory>
<Type>Float</Type>
<RangeEnumeration></RangeEnumeration>
<Units>Deg</Units>
<Description>The decimal notation of longitude, e.g. 153.21760 (World Geodetic System 1984).</Description>
</Item>
<Item ID="3">
<Name>GeoFencing Latitude</Name>
<Operations>RW</Operations>
<MultipleInstances>Multiple</MultipleInstances>
<Mandatory>Mandatory</Mandatory>
<Type>Float</Type>
<RangeEnumeration></RangeEnumeration>
<Units>Deg</Units>
<Description>The decimal notation of latitude of GeoFencing center point, e.g. -43.5723 (World Geodetic System 1984).</Description>
</Item>
<Item ID="4">
<Name>GeoFencing Longitude</Name>
<Operations>RW</Operations>
<MultipleInstances>Multiple</MultipleInstances>
<Mandatory>Mandatory</Mandatory>
<Type>Float</Type>
<RangeEnumeration></RangeEnumeration>
<Units>Deg</Units>
<Description>The decimal notation of longitude of GeoFencing center point, e.g. -43.5723 (World Geodetic System 1984).</Description>
</Item>
<Item ID="5">
<Name>GeoFencing Radius</Name>
<Operations>RW</Operations>
<MultipleInstances>Single</MultipleInstances>
<Mandatory>Mandatory</Mandatory>
<Type>Float</Type>
<RangeEnumeration></RangeEnumeration>
<Units>km</Units>
<Description>The decimal notation of radius of GeoFencing area, in kilometers.</Description>
</Item>
<Item ID="6">
<Name>Timestamp</Name>
<Operations>R</Operations>
<MultipleInstances>Single</MultipleInstances>
<Mandatory>Mandatory</Mandatory>
<Type>Time</Type>
<RangeEnumeration></RangeEnumeration>
<Units></Units>
<Description>The timestamp of when the GeoFencing border has been crossed (both direction).</Description>
</Item>
<Item ID="7">
<Name>State</Name>
<Operations>R</Operations>
<MultipleInstances>Single</MultipleInstances>
<Mandatory>Mandatory</Mandatory>
<Type>Integer</Type>
<RangeEnumeration></RangeEnumeration>
<Units></Units>
<Description>The GeoFencing status.</Description>
</Item>
</Resources>
<Description2></Description2>
</Object>
</LWM2M>