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