Skip to main content

[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>

Back to the top